tools/libs/guest/xc_core_arm.h
tools/libs/guest/xc_core_x86.h
tools/libs/guest/xc_private.h
+tools/libs/light/_*.[ch]
+tools/libs/light/*.pyc
+tools/libs/light/_libxl.api-for-check
+tools/libs/light/*.api-ok
+tools/libs/light/libxenlight.map
+tools/libs/light/libxl-save-helper
+tools/libs/light/dsdt*
+tools/libs/light/mk_dsdt
+tools/libs/light/ssdt*
+tools/libs/light/testidl
+tools/libs/light/testidl.c
+tools/libs/light/test_timedereg
+tools/libs/light/test_fdderegrace
+tools/libs/light/tmp.*
+tools/libs/light/xenlight.pc
+tools/libs/light/include/_*.h
tools/libs/stat/_paths.h
tools/libs/stat/headers.chk
tools/libs/stat/libxenstat.map
tools/include/xen-xsm/*
tools/include/xen-foreign/*.(c|h|size)
tools/include/xen-foreign/checker
-tools/libxl/_libxl.api-for-check
-tools/libxl/*.api-ok
tools/libxl/*.pc
-tools/libxl/dsdt*
tools/libxl/libxlu_cfg_y.output
-tools/libxl/mk_dsdt
-tools/libxl/ssdt*
-tools/libxl/testenum
-tools/libxl/testenum.c
-tools/libxl/tmp.*
tools/misc/cpuperf/cpuperf-perfcntr
tools/misc/cpuperf/cpuperf-xen
tools/misc/xc_shadow
tools/misc/xen-hptool
tools/misc/xen-mfndump
tools/libs/toolcore/include/_*.h
-tools/libxl/_*.[ch]
-tools/libxl/testidl
-tools/libxl/testidl.c
-tools/libxl/*.pyc
-tools/libxl/libxl-save-helper
-tools/libxl/test_timedereg
-tools/libxl/test_fdderegrace
tools/firmware/etherboot/eb-roms.h
tools/firmware/etherboot/gpxe-git-snapshot.tar.gz
tools/misc/xenhypfs
include $(XEN_ROOT)/tools/libs/uselibs.mk
-XEN_libxenlight = $(XEN_ROOT)/tools/libxl
-# Currently libxlutil lives in the same directory as libxenlight
-XEN_libxlutil = $(XEN_libxenlight)
+XEN_libxlutil = $(XEN_ROOT)/tools/libxl
CFLAGS_xeninclude = -I$(XEN_INCLUDE)
LDLIBS_libxenstore += -ldl
endif
+CFLAGS_libxenlight += $(CFLAGS_libxenctrl)
+
ifeq ($(debug),y)
# Disable optimizations
CFLAGS += -O0 -fno-omit-frame-pointer
CFLAGS += -O2 -fomit-frame-pointer
endif
-CFLAGS_libxenlight = -I$(XEN_libxenlight) $(CFLAGS_libxenctrl) $(CFLAGS_xeninclude)
-SHDEPS_libxenlight = $(SHLIB_libxenctrl) $(SHLIB_libxenstore) $(SHLIB_libxenhypfs) $(SHLIB_libxenguest)
-LDLIBS_libxenlight = $(SHDEPS_libxenlight) $(XEN_libxenlight)/libxenlight$(libextension)
-SHLIB_libxenlight = $(SHDEPS_libxenlight) -Wl,-rpath-link=$(XEN_libxenlight)
-
CFLAGS_libxlutil = -I$(XEN_libxlutil)
SHDEPS_libxlutil = $(SHLIB_libxenlight)
LDLIBS_libxlutil = $(SHDEPS_libxlutil) $(XEN_libxlutil)/libxlutil$(libextension)
PACKAGE_BUGREPORT='xen-devel@lists.xen.org'
PACKAGE_URL='https://www.xen.org/'
-ac_unique_file="libxl/libxl.c"
+ac_unique_file="libs/light/libxl.c"
# Factoring default headers for most tests.
ac_includes_default="\
#include <stdio.h>
AC_PREREQ([2.67])
AC_INIT([Xen Hypervisor Tools], m4_esyscmd([../version.sh ../xen/Makefile]),
[xen-devel@lists.xen.org], [xen], [https://www.xen.org/])
-AC_CONFIG_SRCDIR([libxl/libxl.c])
+AC_CONFIG_SRCDIR([libs/light/libxl.c])
AC_CONFIG_FILES([
../config/Tools.mk
hotplug/FreeBSD/rc.d/xencommons
GO ?= go
-LIBXL_SRC_DIR = ../../libxl
+LIBXL_SRC_DIR = $(XEN_ROOT)/tools/libs/light
.PHONY: all
all: build
SUBDIRS-y += store
SUBDIRS-y += stat
SUBDIRS-$(CONFIG_Linux) += vchan
+SUBDIRS-y += light
ifeq ($(CONFIG_RUMP),y)
SUBDIRS-y := toolcore
--- /dev/null
+LIBXENLIGHT CODING STYLE
+========================
+
+
+AN APOLOGY AND WARNING
+----------------------
+
+Much of the code in libxl does not yet follow this coding style
+document in every respect. However, new code is expected to conform.
+
+Patches to improve the style of existing code are welcome. Please
+separate these out from functional changes.
+
+If it is not feasible to conform fully to the style while patching old
+code, without doing substantial style reengineering first, we may
+accept patches which contain nonconformant elements, provided that
+they don't make the coding style problem worse overall.
+
+In this case, the new code should conform to the prevailing style in
+the area being touched.
+
+
+MEMORY ALLOCATION
+-----------------
+
+Memory allocation for libxl-internal purposes should normally be done
+with the provided gc mechanisms; there is then no need to free. See
+"libxl memory management" in libxl.h.
+
+
+CONVENTIONAL VARIABLE NAMES
+---------------------------
+
+The following local variable names should be used where applicable:
+
+ int rc; /* a libxl error code - and not anything else */
+ int r; /* the return value from a system call (or libxc call) */
+ bool ok; /* the success return value from a boolean function */
+
+ uint32_t domid;
+ libxl__gc *gc;
+ libxl__egc *egc;
+ libxl__ao *ao;
+
+ libxl_foo_bar_state *fbs; /* local variable */
+ libxl_foo_bar_state foo_bar; /* inside another state struct */
+
+
+CONVENIENCE MACROS
+------------------
+
+There are a number of convenience macros which shorten the program and
+avoid opportunity for mistakes. In some cases non-use of the macros
+produces functional bugs or incorrect error handling. Use the macros
+whenever they are applicable. For example:
+
+ Usually, don't use: Instead, use (see libxl_internal.h):
+ libxl__log[v] LOG, LOGE, LOGEV
+ libxl__sprintf GCSPRINTF
+ libxl__*alloc et al. GCNEW, GCNEW_ARRAY, GCREALLOC_ARRAY
+ isalnum etc. directly CTYPE
+ libxl__ctx_[un]lock CTX_LOCK, CTX_UNLOCK
+ gc=...; ao=...; EGC_GC, AO_GC, STATE_AO_GC
+ explicit gc creation GC_INIT, GC_FREE
+ memset(..,0,sizeof..) FILLZERO
+
+Instead of malloc et al one should (as an exception to the above) use
+libxl__{zalloc,calloc,realloc} etc but passing NOGC.
+
+ERROR HANDLING
+--------------
+
+Unless, there are good reasons to do otherwise, the following error
+handling and cleanup paradigm should be used:
+
+ * All local variables referring to resources which might need
+ cleaning up are declared at the top of the function, and
+ initialised to a sentinel value indicating "nothing allocated".
+ For example,
+ libxl_evgen_disk_eject *evg = NULL;
+ int nullfd = -1;
+
+ * If the function is to return a libxl error value, `rc' is
+ used to contain the error code, but it is NOT initialised:
+ int rc;
+
+ * There is only one error cleanup path out of the function. It
+ starts with a label `out:'. That error cleanup path checks for
+ each allocated resource and frees it iff necessary. It then
+ returns rc. For example,
+ out:
+ if (evg) libxl__evdisable_disk_eject(gc, evg);
+ if (nullfd >= 0) close(nullfd);
+ return rc;
+
+ * Function calls which might fail (ie most function calls) are
+ handled by putting the return/status value into a variable, and
+ then checking it in a separate statement:
+ char *dompath = libxl__xs_get_dompath(gc, bl->domid);
+ if (!dompath) { rc = ERROR_FAIL; goto out; }
+
+ * If a resource is freed in the main body of the function (for
+ example, in a loop), the corresponding variable has to be reset to
+ the sentinel at the point where it's freed.
+
+Whether to use the `out' path for successful returns as well as error
+returns is a matter of taste and convenience for the specific
+function. Not reusing the out path is fine if the duplicated function
+exit code is only `CTX_UNLOCK; GC_FREE;' (or similar).
+
+If you reuse the `out' path for successful returns, there may be
+resources which are to be returned to the caller rather than freed.
+In that case you have to reset the local variable to `nothing here',
+to avoid the resource being freed on the out path. That resetting
+should be done immediately after the resource value is stored at the
+applicable _r function parameter (or equivalent). Do not test `rc' in
+the out section, to discover whether to free things.
+
+The uses of the single-line formatting in the examples above are
+permitted exceptions to the usual libxl code formatting rules.
+
+
+
+IDEMPOTENT DATA STRUCTURE CONSTRUCTION/DESTRUCTION
+--------------------------------------------------
+
+Nontrivial data structures (in structs) should come with an idempotent
+_dispose function, which must free all resources associated with the
+data structure (but not free the struct itself).
+
+Such a struct should also come with an _init function which
+initialises the struct so that _dispose is a no-op.
+
+
+ASYNCHRONOUS/LONG-RUNNING OPERATIONS
+------------------------------------
+
+All long-running operations in libxl need to use the asynchronous
+operation machinery. Consult the programmer documentation in
+libxl_internal.h for details - search for "Machinery for asynchronous
+operations".
+
+The code for asynchronous operations should be laid out in
+chronological order. That is, where there is a chain of callback
+functions, each subsequent function should be, textually, the next
+function in the file. This will normally involve predeclaring the
+callback functions. Synchronous helper functions should be separated
+out into a section preceding the main callback chain.
+
+Control flow arrangements in asynchronous operations should be made as
+simple as possible, because it can otherwise be very hard to see
+through the tangle.
+
+
+When inventing a new sub-operation in asynchronous code, consider
+whether to structure it formally as a sub-operation with its own state
+structure. (See, for example, libxl__datacopier_*.)
+
+An ao-suboperation state structure should contain, in this order:
+ * fields that the caller must fill in, and which are,
+ effectively, the parameters to the operation, including:
+ - libxl__ao *ao
+ - the callback function pointer(s), which
+ should be named callback or callback_*.
+ * shared information fields or ones used for returning information
+ to the calling operation
+ * private fields
+These sections should be clearly demarcated by comments.
+
+An asynchronous operation should normally have an idempotent stop or
+cancel function. It should normally also have an _init function for
+its state struct, which arranges that the stop is a no-op.
+
+The permitted order of calls into your ao operation's methods must be
+documented in comments, if it is nontrivial.
+
+
+When using an ao sub-operation, you should normally:
+ * Physically include the sub-operation state struct in your
+ own state struct;
+ * Use CONTAINER_OF to find your own state struct at the start of
+ your implementations of the sub-operation callback functions;
+ * Unconditionally initialise the sub-operation's struct (with its
+ _init method) in your own _init method.
+ * Unconditionally cancel or destroy the sub-operation in your own
+ cancel or destroy method.
+
+
+FORMATTING AND NAMING
+---------------------
+
+Blatantly copied from qemu and linux with few modifications.
+
+
+1. Whitespace
+
+Of course, the most important aspect in any coding style is whitespace.
+Crusty old coders who have trouble spotting the glasses on their noses
+can tell the difference between a tab and eight spaces from a distance
+of approximately fifteen parsecs. Many a flamewar have been fought and
+lost on this issue.
+
+Libxenlight indents are four spaces. Tabs are never used, except in
+Makefiles where they have been irreversibly coded into the syntax.
+Spaces of course are superior to tabs because:
+
+ - You have just one way to specify whitespace, not two. Ambiguity breeds
+ mistakes.
+ - The confusion surrounding 'use tabs to indent, spaces to justify' is gone.
+ - Tab indents push your code to the right, making your screen seriously
+ unbalanced.
+ - Tabs will be rendered incorrectly on editors who are misconfigured not
+ to use tab stops of eight positions.
+ - Tabs are rendered badly in patches, causing off-by-one errors in almost
+ every line.
+ - It is the libxenlight coding style.
+
+Do not leave whitespace dangling off the ends of lines.
+
+
+2. Line width
+
+Lines are limited to 75 characters.
+
+Rationale:
+ - Some people like to tile their 24" screens with a 6x4 matrix of 80x24
+ xterms and use vi in all of them. The best way to punish them is to
+ let them keep doing it.
+ - In an 80 column terminal, some room needs to be left for > quoting
+ characters, +/- diff characters, and so on, in emails.
+ - Code and especially patches is much more readable if limited to a sane
+ line length. Eighty is traditional.
+ - It is the libxenlight coding style.
+
+
+3. Naming
+
+C is a Spartan language, and so should your naming be. Unlike Modula-2
+and Pascal programmers, C programmers do not use cute names like
+ThisVariableIsATemporaryCounter. A C programmer would call that
+variable "tmp", which is much easier to write, and not the least more
+difficult to understand.
+
+HOWEVER, while mixed-case names are frowned upon, descriptive names for
+global variables are a must. To call a global function "foo" is a
+shooting offense.
+
+GLOBAL variables (to be used only if you _really_ need them) need to
+have descriptive names, as do global functions. If you have a function
+that counts the number of active users, you should call that
+"count_active_users()" or similar, you should _not_ call it "cntusr()".
+
+Encoding the type of a function into the name (so-called Hungarian
+notation) is brain damaged - the compiler knows the types anyway and can
+check those, and it only confuses the programmer.
+
+LOCAL variable names should be short, and to the point. If you have
+some random integer loop counter, it should probably be called "i".
+Calling it "loop_counter" is non-productive, if there is no chance of it
+being mis-understood. Similarly, "tmp" can be just about any type of
+variable that is used to hold a temporary value.
+
+Local variables used to store return values should have descriptive name
+like "rc" or "ret". Following the same reasoning the label used as exit
+path should be called "out".
+
+Function arguments which are used to return values to the caller
+should be suffixed `_r' or `_out'.
+
+Variables, type names and function names are
+lower_case_with_underscores.
+Type names and function names use the prefix libxl__ when internal to
+libxenlight and libxl_ when exported in libxl.h.
+Xl should avoid using libxl_ and libxl__ as prefix for its own function
+names.
+
+When wrapping standard library functions, use the prefix libxl_ to alert
+readers that they are seeing a wrapped version; otherwise avoid this prefix.
+
+Typedefs are used to eliminate the redundant 'struct' keyword.
+It is the libxenlight coding style.
+
+
+4. Statements
+
+Don't put multiple statements on a single line.
+Don't put multiple assignments on a single line either.
+Error code paths with an if statement and a goto or a return on the same
+line are allowed. Examples:
+
+ if (rc) goto out;
+ if (rc < 0) return;
+
+Libxenlight coding style is super simple. Avoid tricky expressions.
+
+
+5. Block structure
+
+Every indented statement is braced, but blocks that contain just one
+statement may have the braces omitted. To avoid confusion, either all
+the blocks in an if...else chain have braces, or none of them do.
+
+The opening brace is on the line that contains the control flow
+statement that introduces the new block; the closing brace is on the
+same line as the else keyword, or on a line by itself if there is no
+else keyword. Examples:
+
+ if (a == 5) {
+ printf("a was 5.\n");
+ } else if (a == 6) {
+ printf("a was 6.\n");
+ } else {
+ printf("a was something else entirely.\n");
+ }
+
+ if (a == 5)
+ printf("a was 5.\n");
+
+An exception is the opening brace for a function; for reasons of tradition
+and clarity it comes on a line by itself:
+
+ void a_function(void)
+ {
+ do_something();
+ }
+
+Rationale: a consistent (except for functions...) bracing style reduces
+ambiguity and avoids needless churn when lines are added or removed.
+Furthermore, it is the libxenlight coding style.
+
--- /dev/null
+XEN_ROOT = $(CURDIR)/../../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+SRCS-y += osdeps.c
+SRCS-y += libxl_paths.c
+SRCS-y += libxl_bootloader.c
+SRCS-y += flexarray.c
+ifeq ($(CONFIG_LIBNL),y)
+SRCS-y += libxl_netbuffer.c
+else
+SRCS-y += libxl_nonetbuffer.c
+endif
+ifeq ($(CONFIG_X86),y)
+SRCS-y += libxl_convert_callout.c
+else
+SRCS-y += libxl_no_convert_callout.c
+endif
+SRCS-y += libxl_remus.c
+SRCS-y += libxl_checkpoint_device.c
+SRCS-y += libxl_remus_disk_drbd.c
+ifeq ($(CONFIG_LIBNL),y)
+SRCS-y += libxl_colo_restore.c
+SRCS-y += libxl_colo_save.c
+SRCS-y += libxl_colo_qdisk.c
+SRCS-y += libxl_colo_proxy.c
+SRCS-y += libxl_colo_nic.c
+else
+SRCS-y += libxl_no_colo.c
+endif
+
+ACPI_PATH = $(XEN_ROOT)/tools/libacpi
+DSDT_FILES-$(CONFIG_X86) = dsdt_pvh.c
+ACPI_OBJS = $(patsubst %.c,%.o,$(DSDT_FILES-y)) build.o static_tables.o
+ACPI_PIC_OBJS = $(patsubst %.o,%.opic,$(ACPI_OBJS))
+$(DSDT_FILES-y): acpi
+vpath build.c $(ACPI_PATH)/
+vpath static_tables.c $(ACPI_PATH)/
+
+.PHONY: acpi
+acpi:
+ $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) DSDT_FILES="$(DSDT_FILES-y)"
+
+SRCS-$(CONFIG_X86) += $(ACPI_OBJS:.o=.c)
+
+CFLAGS += -Wno-format-zero-length -Wmissing-declarations \
+ -Wno-declaration-after-statement -Wformat-nonliteral
+CFLAGS += -I.
+
+SRCS-$(CONFIG_X86) += libxl_cpuid.c
+SRCS-$(CONFIG_X86) += libxl_x86.c
+SRCS-$(CONFIG_X86) += libxl_psr.c
+SRCS-$(CONFIG_X86) += libxl_x86_acpi.c
+SRCS-$(CONFIG_ARM) += libxl_nocpuid.c
+SRCS-$(CONFIG_ARM) += libxl_arm.c
+SRCS-$(CONFIG_ARM) += libxl_libfdt_compat.c
+ifeq ($(CONFIG_ARM_64),y)
+DSDT_FILES-y = dsdt_anycpu_arm.c
+SRCS-y += libxl_arm_acpi.c
+SRCS-y += $(DSDT_FILES-y)
+dsdt_anycpu_arm.c:
+ $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) DSDT_FILES="$(DSDT_FILES-y)"
+else
+SRCS-$(CONFIG_ARM) += libxl_arm_no_acpi.c
+endif
+
+SRCS-OS-$(CONFIG_NetBSD) = libxl_netbsd.c
+SRCS-OS-$(CONFIG_Linux) = libxl_linux.c
+SRCS-OS-$(CONFIG_FreeBSD) = libxl_freebsd.c
+ifeq ($(SRCS-OS-y),)
+$(error Your Operating System is not supported by libxenlight, \
+please check libxl_linux.c and libxl_netbsd.c to see how to get it ported)
+endif
+SRCS-y += $(SRCS-OS-y)
+
+SRCS-y += libxl.c
+SRCS-y += libxl_create.c
+SRCS-y += libxl_dm.c
+SRCS-y += libxl_pci.c
+SRCS-y += libxl_dom.c
+SRCS-y += libxl_exec.c
+SRCS-y += libxl_xshelp.c
+SRCS-y += libxl_device.c
+SRCS-y += libxl_internal.c
+SRCS-y += libxl_utils.c
+SRCS-y += libxl_uuid.c
+SRCS-y += libxl_json.c
+SRCS-y += libxl_aoutils.c
+SRCS-y += libxl_numa.c
+SRCS-y += libxl_vnuma.c
+SRCS-y += libxl_stream_read.c
+SRCS-y += libxl_stream_write.c
+SRCS-y += libxl_save_callout.c
+SRCS-y += _libxl_save_msgs_callout.c
+SRCS-y += libxl_qmp.c
+SRCS-y += libxl_event.c
+SRCS-y += libxl_fork.c
+SRCS-y += libxl_dom_suspend.c
+SRCS-y += libxl_dom_save.c
+SRCS-y += libxl_usb.c
+SRCS-y += libxl_vtpm.c
+SRCS-y += libxl_nic.c
+SRCS-y += libxl_disk.c
+SRCS-y += libxl_console.c
+SRCS-y += libxl_cpupool.c
+SRCS-y += libxl_mem.c
+SRCS-y += libxl_sched.c
+SRCS-y += libxl_tmem.c
+SRCS-y += libxl_9pfs.c
+SRCS-y += libxl_domain.c
+SRCS-y += libxl_vdispl.c
+SRCS-y += libxl_pvcalls.c
+SRCS-y += libxl_vsnd.c
+SRCS-y += libxl_vkb.c
+SRCS-y += libxl_genid.c
+SRCS-y += _libxl_types.c
+SRCS-y += libxl_flask.c
+SRCS-y += _libxl_types_internal.c
+
+ifeq ($(CONFIG_LIBNL),y)
+CFLAGS_LIBXL += $(LIBNL3_CFLAGS)
+endif
+CFLAGS_LIBXL += -Wshadow
+ifeq ($(debug),y)
+CFLAGS_LIBXL += -DCONFIG_DEBUG
+endif
+
+CFLAGS += $(PTHREAD_CFLAGS)
+LDFLAGS += $(PTHREAD_LDFLAGS)
+
+LIBXL_TESTS += timedereg
+LIBXL_TESTS_PROGS = $(LIBXL_TESTS) fdderegrace
+LIBXL_TESTS_INSIDE = $(LIBXL_TESTS) fdevent
+
+# Each entry FOO in LIBXL_TESTS has two main .c files:
+# libxl_test_FOO.c "inside libxl" code to support the test case
+# test_FOO.c "outside libxl" code to exercise the test case
+# Conventionally there will also be:
+# libxl_test_FOO.h interface between the "inside" and "outside" parts
+# The "inside libxl" file is compiled exactly like a piece of libxl, and the
+# "outside libxl" file is compiled exactly like a piece of application
+# code. They must share information via explicit libxl entrypoints.
+# Unlike proper parts of libxl, it is permissible for libxl_test_FOO.c
+# to use private global variables for its state. Note that all the
+# "inside" parts are compiled into a single test library, so their
+# symbol names must be unique.
+#
+# To run these tests, either use LD_PRELOAD to get libxenlight_test.so
+# loaded, or rename it to libxenlight.so so it is the target of the
+# appropriate symlinks.
+
+LIBXL_TEST_OBJS += $(foreach t, $(LIBXL_TESTS_INSIDE),libxl_test_$t.opic)
+TEST_PROG_OBJS += $(foreach t, $(LIBXL_TESTS_PROGS),test_$t.o) test_common.o
+TEST_PROGS += $(foreach t, $(LIBXL_TESTS_PROGS),test_$t)
+
+AUTOINCS = _libxl_list.h _paths.h _libxl_save_msgs_callout.h _libxl_save_msgs_helper.h
+AUTOSRCS = _libxl_save_msgs_callout.c _libxl_save_msgs_helper.c
+
+CLIENTS = testidl libxl-save-helper
+
+SAVE_HELPER_OBJS = libxl_save_helper.o _libxl_save_msgs_helper.o
+
+LIBHEADER := libxl.h libxl_event.h libxl_json.h _libxl_types.h _libxl_types_json.h _libxl_list.h libxl_utils.h libxl_uuid.h
+
+NO_HEADERS_CHK := y
+
+include $(XEN_ROOT)/tools/libs/libs.mk
+
+$(PKG_CONFIG_LOCAL): PKG_CONFIG_INCDIR = $(CURDIR)
+$(PKG_CONFIG_LOCAL): PKG_CONFIG_CFLAGS_LOCAL = $(CFLAGS_xeninclude)
+
+LDUSELIBS-y += $(PTYFUNCS_LIBS)
+LDUSELIBS-$(CONFIG_LIBNL) += $(LIBNL3_LIBS)
+LDUSELIBS-$(CONFIG_Linux) += -luuid
+LDUSELIBS-$(CONFIG_Linux) += -lrt
+LDUSELIBS-$(CONFIG_ARM) += -lfdt
+LDUSELIBS-y += $(PTHREAD_LIBS)
+LDUSELIBS-y += -lyajl
+LDUSELIBS += $(LDUSELIBS-y)
+
+$(LIB_OBJS) $(PIC_OBJS) $(LIBXL_TEST_OBJS): CFLAGS += $(CFLAGS_LIBXL) -include $(XEN_ROOT)/tools/config.h
+$(ACPI_OBJS) $(ACPI_PIC_OBJS): CFLAGS += -I. -DLIBACPI_STDUTILS=\"$(CURDIR)/libxl_x86_acpi.h\"
+$(TEST_PROG_OBJS) _libxl.api-for-check: CFLAGS += $(CFLAGS_libxentoollog) $(CFLAGS_libxentoolcore)
+libxl_dom.o libxl_dom.opic: CFLAGS += -I$(XEN_ROOT)/tools # include libacpi/x86.h
+libxl_x86_acpi.o libxl_x86_acpi.opic: CFLAGS += -I$(XEN_ROOT)/tools
+$(SAVE_HELPER_OBJS): CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenevtchn) $(CFLAGS_libxenguest)
+
+testidl.o: CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenlight)
+testidl.c: libxl_types.idl gentest.py include/libxl.h $(AUTOINCS)
+ $(PYTHON) gentest.py libxl_types.idl testidl.c.new
+ mv testidl.c.new testidl.c
+
+build: $(CLIENTS) $(TEST_PROGS) $(AUTOSRCS) $(AUTOINCS)
+
+$(LIB_OBJS) $(PIC_OBJS) $(SAVE_HELPER_OBJS) $(LIBXL_TEST_OBJS) $(TEST_PROG_OBJS): $(AUTOINCS) libxl.api-ok
+
+genpath-target = $(call buildmakevars2header,_paths.h)
+$(eval $(genpath-target))
+
+libxl.api-ok: check-libxl-api-rules _libxl.api-for-check
+ $(PERL) $^
+ touch $@
+
+_%.api-for-check: include/%.h $(AUTOINCS)
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_$*.o) -c -E $< $(APPEND_CFLAGS) \
+ -DLIBXL_EXTERNAL_CALLERS_ONLY=LIBXL_EXTERNAL_CALLERS_ONLY \
+ >$@.new
+ mv -f $@.new $@
+
+_libxl_list.h: $(XEN_INCLUDE)/xen-external/bsd-sys-queue-h-seddery $(XEN_INCLUDE)/xen-external/bsd-sys-queue.h
+ $(PERL) $^ --prefix=libxl >$@.new
+ $(call move-if-changed,$@.new,$@)
+
+_libxl_save_msgs_helper.c _libxl_save_msgs_callout.c \
+_libxl_save_msgs_helper.h _libxl_save_msgs_callout.h: \
+ libxl_save_msgs_gen.pl
+ $(PERL) -w $< $@ >$@.new
+ $(call move-if-changed,$@.new,$@)
+
+include/libxl.h: _libxl_types.h _libxl_list.h
+include/libxl_json.h: _libxl_types_json.h
+libxl_internal.h: _libxl_types_internal.h _libxl_types_private.h _libxl_types_internal_private.h _paths.h
+libxl_internal_json.h: _libxl_types_internal_json.h
+xl.h: _paths.h
+
+$(LIB_OBJS) $(PIC_OBJS) $(LIBXL_TEST_OBJS) $(TEST_PROG_OBJS) $(SAVE_HELPER_OBJS): include/libxl.h
+$(LIB_OBJS) $(PIC_OBJS) $(LIBXL_TEST_OBJS): libxl_internal.h
+
+_libxl_type%.h _libxl_type%_json.h _libxl_type%_private.h _libxl_type%.c: libxl_type%.idl gentypes.py idl.py
+ $(eval stem = $(notdir $*))
+ $(PYTHON) gentypes.py libxl_type$(stem).idl __libxl_type$(stem).h __libxl_type$(stem)_private.h \
+ __libxl_type$(stem)_json.h __libxl_type$(stem).c
+ $(call move-if-changed,__libxl_type$(stem).h,_libxl_type$(stem).h)
+ $(call move-if-changed,__libxl_type$(stem)_private.h,_libxl_type$(stem)_private.h)
+ $(call move-if-changed,__libxl_type$(stem)_json.h,_libxl_type$(stem)_json.h)
+ $(call move-if-changed,__libxl_type$(stem).c,_libxl_type$(stem).c)
+
+include/_%.h: _%.h
+ cp $< $@
+
+libxenlight_test.so: $(PIC_OBJS) $(LIBXL_TEST_OBJS)
+ $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenlight.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LDUSELIBS) $(APPEND_LDFLAGS)
+
+test_%: test_%.o test_common.o libxenlight_test.so
+ $(CC) $(LDFLAGS) -o $@ $^ $(filter-out %libxenlight.so, $(LDLIBS_libxenlight)) $(LDLIBS_libxentoollog) $(LDLIBS_libxentoolcore) -lyajl $(APPEND_LDFLAGS)
+
+libxl-save-helper: $(SAVE_HELPER_OBJS) libxenlight.so
+ $(CC) $(LDFLAGS) -o $@ $(SAVE_HELPER_OBJS) $(LDLIBS_libxentoollog) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxentoolcore) $(APPEND_LDFLAGS)
+
+testidl: testidl.o libxenlight.so
+ $(CC) $(LDFLAGS) -o $@ testidl.o $(LDLIBS_libxenlight) $(LDLIBS_libxentoollog) $(LDLIBS_libxentoolcore) $(APPEND_LDFLAGS)
+
+install: installlocal $(LIBHEADERS)
+
+.PHONY: installlocal
+installlocal: libxl-save-helper
+ $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN)
+ $(INSTALL_PROG) libxl-save-helper $(DESTDIR)$(LIBEXEC_BIN)
+
+uninstall: uninstalllocal
+
+.PHONY: uninstalllocal
+uninstalllocal:
+ rm -f $(DESTDIR)$(LIBEXEC_BIN)/libxl-save-helper
+
+clean: cleanlocal
+
+.PHONY: cleanlocal
+cleanlocal:
+ $(RM) -f _*.h *.o $(CLIENTS)
+ $(RM) -f _*.c *.pyc _paths.*.tmp _*.api-for-check
+ $(RM) -f testidl.c.new testidl.c *.api-ok
+ $(RM) -f $(TEST_PROGS)
+ $(RM) -rf __pycache__
+ $(RM) -f include/_*.h
+ $(RM) -f libxenlight.map
+ $(RM) -f $(AUTOSRCS) $(AUTOINCS)
+ $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) clean
--- /dev/null
+#!/usr/bin/perl -w
+use strict;
+our $needed=0;
+our $speclineoffset=0;
+our $specfile;
+while (<>) {
+ if (m/^\# (\d+) \"(.*)\"$/) {
+ $speclineoffset = $1 - $. -1;
+ $specfile = $2;
+ }
+ my $file = defined($specfile) ? $specfile : $ARGV;
+ my $line = $speclineoffset + $.;
+ if (m/libxl_asyncop_how[^;]/) {
+ $needed=1;
+ }
+ if (m/LIBXL_EXTERNAL_CALLERS_ONLY/) {
+ $needed=0;
+ }
+ next unless $needed;
+ if (m/\;/) {
+ die "$file:$line:missing LIBXL_EXTERNAL_CALLERS_ONLY";
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_internal.h"
+#include <stdarg.h>
+
+/*
+ * It is safe to store gc in the struct because:
+ * - If it an actual gc, then the flexarray should not be used after the gc
+ * have been freed.
+ * - If it is a NOGC, then this point to a structure embedded in libxl_ctx,
+ * therefore will survive across several libxl calls.
+ */
+
+flexarray_t *flexarray_make(libxl__gc *gc, int size, int autogrow)
+{
+ flexarray_t *array;
+
+ GCNEW(array);
+ array->size = size;
+ array->autogrow = autogrow;
+ array->count = 0;
+ array->gc = gc;
+ GCNEW_ARRAY(array->data, size);
+
+ return array;
+}
+
+void flexarray_free(flexarray_t *array)
+{
+ assert(!libxl__gc_is_real(array->gc));
+ free(array->data);
+ free(array);
+}
+
+void flexarray_grow(flexarray_t *array, int extents)
+{
+ int newsize;
+ libxl__gc *gc = array->gc;
+
+ newsize = array->size + extents;
+ GCREALLOC_ARRAY(array->data, newsize);
+ array->size += extents;
+}
+
+int flexarray_set(flexarray_t *array, unsigned int idx, void *ptr)
+{
+ if (idx >= array->size) {
+ int newsize;
+ if (!array->autogrow)
+ return 1;
+ newsize = (array->size * 2 < idx) ? idx + 1 : array->size * 2;
+ flexarray_grow(array, newsize - array->size);
+ }
+ if ( idx + 1 > array->count )
+ array->count = idx + 1;
+ array->data[idx] = ptr;
+ return 0;
+}
+
+int flexarray_append(flexarray_t *array, void *ptr)
+{
+ return flexarray_set(array, array->count, ptr);
+}
+
+int flexarray_append_pair(flexarray_t *array, void *ptr1, void *ptr2)
+{
+ int rc = flexarray_append(array, ptr1);
+ if (!rc)
+ rc = flexarray_append(array, ptr2);
+ return rc;
+}
+
+int flexarray_vappend(flexarray_t *array, ...)
+{
+ va_list va;
+ void *ptr;
+ int ret;
+
+ va_start(va, array);
+ for(ret = 0; (ptr = va_arg(va, void *)); ret++) {
+ if ( flexarray_append(array, ptr) )
+ break;
+ }
+ va_end(va);
+ return ret;
+}
+
+int flexarray_get(flexarray_t *array, int idx, void **ptr)
+{
+ if (idx >= array->size)
+ return 1;
+ *ptr = array->data[idx];
+ return 0;
+}
+
+void **flexarray_contents(flexarray_t *array)
+{
+ void **data;
+ data = array->data;
+ if (!libxl__gc_is_real(array->gc))
+ free(array);
+ return data;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#ifndef FLEXARRAY_H
+#define FLEXARRAY_H
+
+struct libxl__gc;
+
+typedef struct flexarray {
+ int size;
+ int autogrow;
+ unsigned int count;
+ void **data; /* array of pointer */
+ struct libxl__gc *gc;
+} flexarray_t;
+
+/*
+ * NOGC can be used with flexarrays, but flexarray_free will need to be called
+ * to free the struct. The content of the flexarray will not be freed through
+ * flexarray_free.
+ */
+_hidden flexarray_t *flexarray_make(struct libxl__gc *gc_opt,
+ int size, int autogrow);
+_hidden void flexarray_free(flexarray_t *array);
+_hidden void flexarray_grow(flexarray_t *array, int extents);
+_hidden int flexarray_set(flexarray_t *array, unsigned int index, void *ptr);
+_hidden int flexarray_append(flexarray_t *array, void *ptr);
+_hidden int flexarray_append_pair(flexarray_t *array, void *ptr1, void *ptr2);
+_hidden int flexarray_vappend(flexarray_t *array, ...);
+_hidden int flexarray_get(flexarray_t *array, int index, void **ptr);
+
+_hidden void **flexarray_contents(flexarray_t *array);
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+#!/usr/bin/python
+
+from __future__ import print_function
+
+import os
+import sys
+import re
+import random
+
+import idl
+
+def randomize_char(c):
+ if random.random() < 0.5:
+ return str.lower(c)
+ else:
+ return str.upper(c)
+
+def randomize_case(s):
+ r = [randomize_char(c) for c in s]
+ return "".join(r)
+
+def randomize_enum(e):
+ return random.choice([v.name for v in e.values])
+
+handcoded = ["libxl_bitmap", "libxl_key_value_list",
+ "libxl_cpuid_policy_list", "libxl_string_list"]
+
+def gen_rand_init(ty, v, indent = " ", parent = None):
+ s = ""
+ if isinstance(ty, idl.Enumeration):
+ s += "%s = %s;\n" % (ty.pass_arg(v, parent is None), randomize_enum(ty))
+ elif isinstance(ty, idl.Array):
+ if parent is None:
+ raise Exception("Array type must have a parent")
+ s += "%s = test_rand(8);\n" % (parent + ty.lenvar.name)
+ s += "%s = calloc(%s, sizeof(*%s));\n" % \
+ (v, parent + ty.lenvar.name, v)
+ s += "assert(%s);\n" % (v, )
+ s += "{\n"
+ s += " int i;\n"
+ s += " for (i=0; i<%s; i++)\n" % (parent + ty.lenvar.name)
+ s += gen_rand_init(ty.elem_type, v+"[i]",
+ indent + " ", parent)
+ s += "}\n"
+ elif isinstance(ty, idl.KeyedUnion):
+ if parent is None:
+ raise Exception("KeyedUnion type must have a parent")
+ s += gen_rand_init(ty.keyvar.type, parent + ty.keyvar.name, indent, parent)
+ s += "switch (%s) {\n" % (parent + ty.keyvar.name)
+ for f in ty.fields:
+ (nparent,fexpr) = ty.member(v, f, parent is None)
+ s += "case %s:\n" % f.enumname
+ if f.type is not None:
+ s += gen_rand_init(f.type, fexpr, indent + " ", nparent)
+ s += " break;\n"
+ s += "}\n"
+ elif isinstance(ty, idl.Struct) \
+ and (parent is None or ty.json_gen_fn is None):
+ for f in [f for f in ty.fields if not f.const]:
+ (nparent,fexpr) = ty.member(v, f, parent is None)
+ s += gen_rand_init(f.type, fexpr, "", nparent)
+ elif hasattr(ty, "rand_init") and ty.rand_init is not None:
+ s += "%s(%s);\n" % (ty.rand_init,
+ ty.pass_arg(v, isref=parent is None,
+ passby=idl.PASS_BY_REFERENCE))
+ elif ty.typename in ["libxl_uuid", "libxl_mac", "libxl_hwcap", "libxl_ms_vm_genid"]:
+ s += "rand_bytes((uint8_t *)%s, sizeof(*%s));\n" % (v,v)
+ elif ty.typename in ["libxl_domid", "libxl_devid"] or isinstance(ty, idl.Number):
+ s += "%s = test_rand(sizeof(%s) * 8);\n" % \
+ (ty.pass_arg(v, parent is None),
+ ty.pass_arg(v, parent is None))
+ elif ty.typename in ["bool"]:
+ s += "%s = test_rand(2);\n" % v
+ elif ty.typename in ["libxl_defbool"]:
+ s += "libxl_defbool_set(%s, test_rand(2));\n" % v
+ elif ty.typename in ["char *"]:
+ s += "%s = rand_str();\n" % v
+ elif ty.private:
+ pass
+ elif ty.typename in handcoded:
+ raise Exception("Gen for handcoded %s" % ty.typename)
+ else:
+ raise Exception("Cannot randomly init %s" % ty.typename)
+
+ if s != "":
+ s = indent + s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+if __name__ == '__main__':
+ if len(sys.argv) < 3:
+ print("Usage: gentest.py <idl> <implementation>", file=sys.stderr)
+ sys.exit(1)
+
+ random.seed(os.getenv('LIBXL_TESTIDL_SEED'))
+
+ (builtins,types) = idl.parse(sys.argv[1])
+
+ impl = sys.argv[2]
+ f = open(impl, "w")
+ f.write("""
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "libxl.h"
+#include "libxl_utils.h"
+
+static int test_rand(unsigned max)
+{
+ /* We are not using rand() for its cryptographic properies. */
+ return rand() % max;
+}
+
+static char *rand_str(void)
+{
+ int i, sz = test_rand(32);
+ char *s = malloc(sz+1);
+ assert(s);
+ for (i=0; i<sz; i++)
+ s[i] = 'a' + test_rand(26);
+ s[i] = '\\0';
+ return s;
+}
+
+static void rand_bytes(uint8_t *p, size_t sz)
+{
+ int i;
+ for (i=0; i<sz; i++)
+ p[i] = test_rand(256);
+}
+
+static void libxl_bitmap_rand_init(libxl_bitmap *bitmap)
+{
+ int i;
+ bitmap->size = test_rand(16);
+ bitmap->map = calloc(bitmap->size, sizeof(*bitmap->map));
+ assert(bitmap->map);
+ libxl_for_each_bit(i, *bitmap) {
+ if (test_rand(2))
+ libxl_bitmap_set(bitmap, i);
+ else
+ libxl_bitmap_reset(bitmap, i);
+ }
+}
+
+static void libxl_key_value_list_rand_init(libxl_key_value_list *pkvl)
+{
+ int i, nr_kvp = test_rand(16);
+ libxl_key_value_list kvl = calloc(nr_kvp+1, 2*sizeof(char *));
+ assert(kvl);
+
+ for (i = 0; i<2*nr_kvp; i += 2) {
+ kvl[i] = rand_str();
+ if (test_rand(8))
+ kvl[i+1] = rand_str();
+ else
+ kvl[i+1] = NULL;
+ }
+ kvl[i] = NULL;
+ kvl[i+1] = NULL;
+ *pkvl = kvl;
+}
+
+static void libxl_cpuid_policy_list_rand_init(libxl_cpuid_policy_list *pp)
+{
+ int i, nr_policies = test_rand(16);
+ struct {
+ const char *n;
+ int w;
+ } options[] = {
+ /* A random selection from libxl_cpuid_parse_config */
+ {"maxleaf", 32},
+ {"family", 8},
+ {"model", 8},
+ {"stepping", 4},
+ {"localapicid", 8},
+ {"proccount", 8},
+ {"clflush", 8},
+ {"brandid", 8},
+ {"f16c", 1},
+ {"avx", 1},
+ {"osxsave", 1},
+ {"xsave", 1},
+ {"aes", 1},
+ {"popcnt", 1},
+ {"movbe", 1},
+ {"x2apic", 1},
+ {"sse4.2", 1},
+ {"sse4.1", 1},
+ {"dca", 1},
+ {"pdcm", 1},
+ {"procpkg", 6},
+ };
+ const int nr_options = sizeof(options)/sizeof(options[0]);
+ char buf[64];
+ libxl_cpuid_policy_list p = NULL;
+
+ for (i = 0; i < nr_policies; i++) {
+ int opt = test_rand(nr_options);
+ int val = test_rand(1<<options[opt].w);
+ snprintf(buf, 64, \"%s=%#x\", options[opt].n, val);
+ libxl_cpuid_parse_config(&p, buf);
+ }
+ *pp = p;
+}
+
+static void libxl_string_list_rand_init(libxl_string_list *p)
+{
+ int i, nr = test_rand(16);
+ libxl_string_list l = calloc(nr+1, sizeof(char *));
+ assert(l);
+
+ for (i = 0; i<nr; i++) {
+ l[i] = rand_str();
+ }
+ l[i] = NULL;
+ *p = l;
+}
+""")
+ for ty in builtins + types:
+ if isinstance(ty, idl.Number): continue
+ if ty.typename not in handcoded:
+ f.write("static void %s_rand_init(%s);\n" % \
+ (ty.typename,
+ ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+ f.write("static void %s_rand_init(%s)\n" % \
+ (ty.typename,
+ ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+ f.write("{\n")
+ f.write(gen_rand_init(ty, "p"))
+ f.write("}\n")
+ f.write("\n")
+ ty.rand_init = "%s_rand_init" % ty.typename
+
+ f.write("""
+int main(int argc, char **argv)
+{
+""")
+
+ for ty in types:
+ f.write(" %s %s_val, %s_val_new;\n" % \
+ (ty.typename, ty.typename, ty.typename))
+ f.write("""
+ int rc;
+ char *s, *new_s, *json_string;
+ xentoollog_logger_stdiostream *logger;
+ libxl_ctx *ctx;
+
+ logger = xtl_createlogger_stdiostream(stderr, XTL_DETAIL, 0);
+ if (!logger) exit(1);
+
+ if (libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, (xentoollog_logger*)logger)) {
+ fprintf(stderr, "cannot init xl context\\n");
+ exit(1);
+ }
+""")
+ f.write(" printf(\"Testing TYPE_to/from_json()\\n\");\n")
+ f.write(" printf(\"----------------------\\n\");\n")
+ f.write(" printf(\"\\n\");\n")
+ for ty in [t for t in types if t.json_gen_fn is not None]:
+ arg = ty.typename + "_val"
+ f.write(" %s_rand_init(%s);\n" % (ty.typename, \
+ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
+ if not isinstance(ty, idl.Enumeration):
+ iters = random.randrange(1,10)
+ while iters > 0:
+ f.write(" %s_init(%s_new);\n" % (ty.typename, \
+ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
+ iters -= 1
+ f.write(" s = %s_to_json(ctx, %s);\n" % \
+ (ty.typename, ty.pass_arg(arg, isref=False)))
+ f.write(" printf(\"%%s: %%s\\n\", \"%s\", s);\n" % ty.typename)
+ f.write(" if (s == NULL) abort();\n")
+ f.write(" rc = %s_from_json(ctx, &%s_val_new, s);\n" % \
+ (ty.typename, ty.typename))
+ f.write(" if (rc) abort();\n")
+ f.write(" new_s = %s_to_json(ctx, %s_new);\n" % \
+ (ty.typename, ty.pass_arg(arg, isref=False)))
+ f.write(" if (new_s == NULL) abort();\n")
+ f.write(" if (strcmp(s, new_s)) {\n")
+ f.write(" printf(\"Huh? Regenerated string different from original string.\\n\");\n")
+ f.write(" printf(\"Regenerated string: %s\\n\", new_s);\n")
+ f.write(" abort();\n")
+ f.write(" }\n")
+ f.write(" free(s);\n")
+ f.write(" free(new_s);\n")
+ if ty.dispose_fn is not None:
+ iters = random.randrange(1,10)
+ f.write(" %s(&%s_val);\n" % (ty.dispose_fn, ty.typename))
+ while iters > 0:
+ f.write(" %s(&%s_val_new);\n" % (ty.dispose_fn, ty.typename))
+ iters -= 1
+ f.write("\n")
+
+ f.write(" printf(\"Testing TYPE_copy()\\n\");\n")
+ f.write(" printf(\"----------------------\\n\");\n")
+ f.write(" printf(\"\\n\");\n")
+ for ty in [t for t in types if t.copy_fn is not None]:
+ f.write(" printf(\"Testing %s_copy, \");\n" % ty.typename)
+ arg = ty.typename + "_val"
+ f.write(" %s_init(%s);\n" % (ty.typename, \
+ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
+ f.write(" %s_rand_init(%s);\n" % (ty.typename, \
+ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
+ f.write(" %s_init(%s_new);\n" % (ty.typename, \
+ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
+ f.write(" %s_copy(ctx, %s_new, %s);\n" % (ty.typename, \
+ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE), \
+ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
+ f.write(" s = %s_to_json(ctx, %s);\n" % \
+ (ty.typename, ty.pass_arg(arg, isref=False)))
+ f.write(" if (s == NULL) abort();\n")
+ f.write(" new_s = %s_to_json(ctx, %s_new);\n" % \
+ (ty.typename, ty.pass_arg(arg, isref=False)))
+ f.write(" if (new_s == NULL) abort();\n")
+ f.write(" if (strcmp(s, new_s)) {\n")
+ f.write(" printf(\"Huh? Deep copy for %s failed. Regenerated string different from original string.\\n\");\n" \
+ % ty.typename)
+ f.write(" printf(\"Original string: %s\\n\", s);\n")
+ f.write(" printf(\"Regenerated string: %s\\n\", new_s);\n")
+ f.write(" abort();\n")
+ f.write(" }\n")
+ f.write(" free(s);\n")
+ f.write(" free(new_s);\n")
+ if ty.dispose_fn is not None:
+ f.write(" %s(&%s_val);\n" % (ty.dispose_fn, ty.typename))
+ f.write(" %s(&%s_val_new);\n" % (ty.dispose_fn, ty.typename))
+ f.write(" printf(\"done\\n\");\n")
+ f.write("\n")
+
+ f.write(" printf(\"\\n\");\n")
+ f.write(" printf(\"Testing Enumerations\\n\");\n")
+ f.write(" printf(\"--------------------\\n\");\n")
+ f.write(" printf(\"\\n\");\n")
+ for ty in [t for t in types if isinstance(t,idl.Enumeration)]:
+ f.write(" printf(\"%s -- to string:\\n\");\n" % (ty.typename))
+ for v in ty.values:
+ f.write(" printf(\"\\t%s = %%d = \\\"%%s\\\"\\n\", " \
+ "%s, %s_to_string(%s));\n" % \
+ (v.valuename, v.name, ty.typename, v.name))
+ f.write("\n")
+
+ f.write(" printf(\"%s -- to JSON:\\n\");\n" % (ty.typename))
+ for v in ty.values:
+ f.write(" json_string = %s_to_json(ctx, %s);\n" % \
+ (ty.typename, v.name))
+ f.write(" printf(\"\\t%s = %%d = %%s\", " \
+ "%s, json_string);\n" %\
+ (v.valuename, v.name))
+ f.write(" free(json_string);\n");
+ f.write(" json_string = NULL;\n");
+ f.write("\n")
+
+ f.write(" printf(\"%s -- from string:\\n\");\n" % (ty.typename))
+ for v in [v.valuename for v in ty.values] + ["AN INVALID VALUE"]:
+ n = randomize_case(v)
+ f.write(" %s_val = -1;\n" % (ty.typename))
+ f.write(" rc = %s_from_string(\"%s\", &%s_val);\n" %\
+ (ty.typename, n, ty.typename))
+
+ f.write(" printf(\"\\t%s = \\\"%%s\\\" = %%d (rc %%d)\\n\", " \
+ "\"%s\", %s_val, rc);\n" %\
+ (v, n, ty.typename))
+ f.write("\n")
+
+ f.write("""
+
+ libxl_ctx_free(ctx);
+ xtl_logger_destroy((xentoollog_logger*)logger);
+
+ return 0;
+}
+""")
--- /dev/null
+#!/usr/bin/python
+
+from __future__ import print_function
+
+import sys
+import re
+
+import idl
+
+def libxl_C_instance_of(ty, instancename):
+ if isinstance(ty, idl.Aggregate) and ty.typename is None:
+ if instancename is None:
+ return libxl_C_type_define(ty)
+ else:
+ return libxl_C_type_define(ty) + " " + instancename
+
+ s = ""
+ if isinstance(ty, idl.Array):
+ s += libxl_C_instance_of(ty.lenvar.type, ty.lenvar.name) + ";\n"
+
+ return s + ty.typename + " " + instancename
+
+def libxl_C_type_define(ty, indent = ""):
+ s = ""
+ if isinstance(ty, idl.Enumeration):
+ if ty.typename is None:
+ s += "enum {\n"
+ else:
+ s += "typedef enum %s {\n" % ty.typename
+
+ for v in ty.values:
+ x = "%s = %d" % (v.name, v.value)
+ x = x.replace("\n", "\n ")
+ s += " " + x + ",\n"
+ if ty.typename is None:
+ s += "}"
+ else:
+ s += "} %s" % ty.typename
+
+ elif isinstance(ty, idl.Aggregate):
+ if isinstance(ty, idl.KeyedUnion):
+ s += libxl_C_instance_of(ty.keyvar.type, ty.keyvar.name) + ";\n"
+
+ if ty.typename is None:
+ s += "%s {\n" % ty.kind
+ else:
+ s += "typedef %s %s {\n" % (ty.kind, ty.typename)
+
+ for f in ty.fields:
+ if isinstance(ty, idl.KeyedUnion) and f.type is None: continue
+
+ x = libxl_C_instance_of(f.type, f.name)
+ if f.const:
+ x = "const " + x
+ x = x.replace("\n", "\n ")
+ s += " " + x + ";\n"
+ if ty.typename is None:
+ s += "}"
+ else:
+ s += "} %s" % ty.typename
+ else:
+ raise NotImplementedError("%s" % type(ty))
+ return s.replace("\n", "\n%s" % indent)
+
+def libxl_C_type_dispose(ty, v, indent = " ", parent = None):
+ s = ""
+ if isinstance(ty, idl.KeyedUnion):
+ if parent is None:
+ raise Exception("KeyedUnion type must have a parent")
+ s += "switch (%s) {\n" % (parent + ty.keyvar.name)
+ for f in ty.fields:
+ (nparent,fexpr) = ty.member(v, f, parent is None)
+ s += "case %s:\n" % f.enumname
+ if f.type is not None:
+ s += libxl_C_type_dispose(f.type, fexpr, indent + " ", nparent)
+ s += " break;\n"
+ s += "}\n"
+ elif isinstance(ty, idl.Array):
+ if parent is None:
+ raise Exception("Array type must have a parent")
+ if ty.elem_type.dispose_fn is not None:
+ s += "{\n"
+ s += " int i;\n"
+ s += " for (i=0; i<%s; i++)\n" % (parent + ty.lenvar.name)
+ s += libxl_C_type_dispose(ty.elem_type, v+"[i]",
+ indent + " ", parent)
+ if ty.dispose_fn is not None:
+ if ty.elem_type.dispose_fn is not None:
+ s += " "
+ s += "%s(%s);\n" % (ty.dispose_fn, ty.pass_arg(v, parent is None))
+ if ty.elem_type.dispose_fn is not None:
+ s += "}\n"
+ elif isinstance(ty, idl.Struct) and (parent is None or ty.dispose_fn is None):
+ for f in [f for f in ty.fields if not f.const]:
+ (nparent,fexpr) = ty.member(v, f, parent is None)
+ s += libxl_C_type_dispose(f.type, fexpr, "", nparent)
+ else:
+ if ty.dispose_fn is not None:
+ s += "%s(%s);\n" % (ty.dispose_fn, ty.pass_arg(v, parent is None))
+
+ if s != "":
+ s = indent + s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_type_copy(ty, v, w, indent = " ", vparent = None, wparent = None):
+ s = ""
+
+ if vparent is None:
+ s += "GC_INIT(ctx);\n";
+
+ if isinstance(ty, idl.KeyedUnion):
+ if vparent is None or wparent is None:
+ raise Exception("KeyedUnion type must have a parent")
+ s += "%s = %s;\n" % ((vparent + ty.keyvar.name), (wparent + ty.keyvar.name))
+ s += "switch (%s) {\n" % (wparent + ty.keyvar.name)
+ for f in ty.fields:
+ (vnparent,vfexpr) = ty.member(v, f, vparent is None)
+ (wnparent,wfexpr) = ty.member(w, f, wparent is None)
+ s += "case %s:\n" % f.enumname
+ if f.type is not None:
+ s += libxl_C_type_copy(f.type, vfexpr, wfexpr, indent + " ",
+ vnparent, wnparent)
+ s += " break;\n"
+ s += "}\n"
+ elif isinstance(ty, idl.Array):
+ if vparent is None or wparent is None:
+ raise Exception("Array type must have a parent")
+ s += "%s = libxl__calloc(NOGC, %s, sizeof(*%s));\n" % (ty.pass_arg(v, vparent is None),
+ (wparent + ty.lenvar.name),
+ ty.pass_arg(w, wparent is None))
+ s += "%s = %s;\n" % ((vparent + ty.lenvar.name), (wparent + ty.lenvar.name))
+ s += "{\n"
+ s += " int i;\n"
+ s += " for (i=0; i<%s; i++)\n" % (wparent + ty.lenvar.name)
+ s += libxl_C_type_copy(ty.elem_type, v+"[i]", w+"[i]",
+ indent + " ", vparent, wparent)
+ s += "}\n"
+ elif isinstance(ty, idl.Struct) and ((vparent is None and wparent is None) or ty.copy_fn is None):
+ for f in [f for f in ty.fields if not f.const and not f.type.private]:
+ (vnparent,vfexpr) = ty.member(v, f, vparent is None)
+ (wnparent,wfexpr) = ty.member(w, f, wparent is None)
+ s += libxl_C_type_copy(f.type, vfexpr, wfexpr, "", vnparent, wnparent)
+ else:
+ if ty.copy_fn is not None:
+ s += "%s(ctx, %s, %s);\n" % (ty.copy_fn,
+ ty.pass_arg(v, vparent is None, passby=idl.PASS_BY_REFERENCE),
+ ty.pass_arg(w, wparent is None, passby=idl.PASS_BY_REFERENCE))
+
+ else:
+ s += "%s = %s;\n" % (ty.pass_arg(v, vparent is None, passby=idl.PASS_BY_VALUE),
+ ty.pass_arg(w, wparent is None, passby=idl.PASS_BY_VALUE))
+
+ if vparent is None:
+ s += "GC_FREE;\n"
+
+ if s != "":
+ s = indent + s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_init_members(ty, nesting = 0):
+ """Returns a list of members of ty which require a separate init"""
+
+ if isinstance(ty, idl.Aggregate):
+ return [f for f in ty.fields if not f.const and isinstance(f.type,idl.KeyedUnion)]
+ else:
+ return []
+
+def libxl_C_type_do_init(ty, pass_arg, need_zero=True, indent=" "):
+ s=indent
+ if ty.init_val is not None:
+ s+= "%s = %s;\n" % (pass_arg(idl.PASS_BY_VALUE), ty.init_val)
+ elif ty.init_fn is not None:
+ s+= "%s(%s);\n" % (ty.init_fn, pass_arg(idl.PASS_BY_REFERENCE))
+ elif need_zero:
+ ptr = pass_arg(idl.PASS_BY_REFERENCE)
+ s+= "memset(%s, 0, sizeof(*%s));\n" % (ptr, ptr)
+ else:
+ s=""
+ return s
+
+def _libxl_C_type_init(ty, v, indent = " ", parent = None, subinit=False):
+ s = ""
+ if isinstance(ty, idl.KeyedUnion):
+ if parent is None:
+ raise Exception("KeyedUnion type must have a parent")
+ if subinit:
+ s += "switch (%s) {\n" % (parent + ty.keyvar.name)
+ for f in ty.fields:
+ (nparent,fexpr) = ty.member(v, f, parent is None)
+ s += "case %s:\n" % f.enumname
+ if f.type is not None:
+ s += _libxl_C_type_init(f.type, fexpr, " ", nparent)
+ s += " break;\n"
+ s += "}\n"
+ else:
+ if ty.keyvar.init_val:
+ s += "%s = %s;\n" % (parent + ty.keyvar.name, ty.keyvar.init_val)
+ elif ty.keyvar.type.init_val:
+ s += "%s = %s;\n" % (parent + ty.keyvar.name, ty.keyvar.type.init_val)
+ elif isinstance(ty, idl.Struct) and (parent is None or ty.init_fn is None):
+ for f in [f for f in ty.fields if not f.const]:
+ (nparent,fexpr) = ty.member(v, f, parent is None)
+ if f.init_val is not None:
+ s += "%s = %s;\n" % (fexpr, f.init_val)
+ else:
+ s += _libxl_C_type_init(f.type, fexpr, "", nparent)
+ else:
+ if ty.init_val is not None:
+ s += "%s = %s;\n" % (ty.pass_arg(v, parent is None), ty.init_val)
+ elif ty.init_fn is not None:
+ s += "%s(%s);\n" % (ty.init_fn, ty.pass_arg(v, parent is None))
+
+ if s != "":
+ s = indent + s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_type_init(ty):
+ s = ""
+ s += "void %s(%s)\n" % (ty.init_fn, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE))
+ s += "{\n"
+ s += " memset(p, '\\0', sizeof(*p));\n"
+ s += _libxl_C_type_init(ty, "p")
+ s += "}\n"
+ s += "\n"
+ return s
+
+def libxl_C_type_member_init(ty, field):
+ if not isinstance(field.type, idl.KeyedUnion):
+ raise Exception("Only KeyedUnion is supported for member init")
+
+ ku = field.type
+
+ s = ""
+ s += "void %s(%s, %s)\n" % (ty.init_fn + "_" + ku.keyvar.name,
+ ty.make_arg("p", passby=idl.PASS_BY_REFERENCE),
+ ku.keyvar.type.make_arg(ku.keyvar.name))
+ s += "{\n"
+
+ if ku.keyvar.init_val is not None:
+ init_val = ku.keyvar.init_val
+ elif ku.keyvar.type.init_val is not None:
+ init_val = ku.keyvar.type.init_val
+ else:
+ init_val = None
+
+ (nparent,fexpr) = ty.member(ty.pass_arg("p"), ku.keyvar, isref=True)
+ if init_val is not None:
+ s += " assert(%s == %s);\n" % (fexpr, init_val)
+ else:
+ s += " assert(!%s);\n" % (fexpr)
+ s += " %s = %s;\n" % (fexpr, ku.keyvar.name)
+
+ (nparent,fexpr) = ty.member(ty.pass_arg("p"), field, isref=True)
+ s += _libxl_C_type_init(ku, fexpr, parent=nparent, subinit=True)
+ s += "}\n"
+ s += "\n"
+ return s
+
+def libxl_C_type_gen_map_key(f, parent, indent = ""):
+ s = ""
+ if isinstance(f.type, idl.KeyedUnion):
+ s += "switch (%s) {\n" % (parent + f.type.keyvar.name)
+ for x in f.type.fields:
+ v = f.type.keyvar.name + "." + x.name
+ s += "case %s:\n" % x.enumname
+ s += " s = yajl_gen_string(hand, (const unsigned char *)\"%s\", sizeof(\"%s\")-1);\n" % (v, v)
+ s += " if (s != yajl_gen_status_ok)\n"
+ s += " goto out;\n"
+ s += " break;\n"
+ s += "}\n"
+ else:
+ s += "s = yajl_gen_string(hand, (const unsigned char *)\"%s\", sizeof(\"%s\")-1);\n" % (f.name, f.name)
+ s += "if (s != yajl_gen_status_ok)\n"
+ s += " goto out;\n"
+ if s != "":
+ s = indent + s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_type_copy_deprecated(field, v, indent = " ", vparent = None):
+ s = ""
+
+ if isinstance(field.type, idl.KeyedUnion):
+ if vparent is None:
+ raise Exception("KeyedUnion type must have a parent")
+ s += "switch (%s) {\n" % (vparent + field.type.keyvar.name)
+ for f in [f for f in field.type.fields if not f.const]:
+ (vnparent,vfexpr) = ty.member(v, f, vparent is None)
+ s += "case %s:\n" % f.enumname
+ if f.type is not None:
+ s += libxl_C_type_copy_deprecated(f, vfexpr, indent, vnparent)
+ s+= " break;\n"
+ s+="}\n";
+ elif isinstance(field.type, idl.Array) and field.deprecated_by:
+ raise Exception("Array type is not supported for deprecation")
+ elif isinstance(field.type, idl.Struct) and field.type.copy_fn is None:
+ for f in [f for f in field.type.fields if not f.const]:
+ (vnparent,vfexpr) = ty.member(v, f, vparent is None)
+ s += libxl_C_type_copy_deprecated(f, vfexpr, "", vnparent)
+ elif field.deprecated_by is not None:
+ if field.type.check_default_fn is None:
+ raise Exception(
+"Deprecated field %s type doesn't have a default value checker" % field.name)
+ field_pass = lambda by: field.type.pass_arg(v, vparent is None,
+ passby=by)
+ field_val = field_pass(idl.PASS_BY_VALUE)
+ field_ptr = field_pass(idl.PASS_BY_REFERENCE)
+ s+= "if (!%s(&p->%s) && !%s(%s))\n" % (field.type.check_default_fn,
+ field.deprecated_by,
+ field.type.check_default_fn,
+ field_ptr)
+ s+= " return -EINVAL;\n"
+ s+="(void) (&p->%s == %s);\n" % (field.deprecated_by, field_ptr)
+ s+= "if (%s(&p->%s)) {\n" % (field.type.check_default_fn,
+ field.deprecated_by)
+ s+= " "
+ if field.type.copy_fn is not None:
+ s+= "%s(ctx, &p->%s, %s);\n" % (field.type.copy_fn,
+ field.deprecated_by, field_ptr)
+ else:
+ s+= "p->%s = %s;\n" % (field.deprecated_by, field_val)
+
+ if field.type.dispose_fn is not None:
+ s+= " %s(%s);\n" % (field.type.dispose_fn,
+ field.type.pass_arg(v, vparent is None))
+ s+=libxl_C_type_do_init(field.type, field_pass)
+ s+= "}\n"
+
+ if s != "":
+ s = indent + s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def get_init_val(f):
+ if f.init_val is not None:
+ return f.init_val
+ elif f.type.init_val is not None:
+ return f.type.init_val
+ return None
+
+def get_default_expr(f, nparent, fexpr):
+ if isinstance(f.type, idl.Aggregate):
+ return "1 /* always generate JSON output for aggregate type */"
+
+ if isinstance(f.type, idl.Array):
+ return "%s && %s" % (fexpr, nparent + f.type.lenvar.name)
+
+ init_val = get_init_val(f)
+ if init_val is not None:
+ return "%s != %s" % (fexpr, init_val)
+
+ if f.type.check_default_fn:
+ return "!%s(&%s)" % (f.type.check_default_fn, fexpr)
+
+ return "%s" % fexpr
+
+def libxl_C_type_gen_json(ty, v, indent = " ", parent = None):
+ s = ""
+ if parent is None:
+ s += "yajl_gen_status s;\n"
+
+ if isinstance(ty, idl.Array):
+ if parent is None:
+ raise Exception("Array type must have a parent")
+ s += "{\n"
+ s += " int i;\n"
+ s += " s = yajl_gen_array_open(hand);\n"
+ s += " if (s != yajl_gen_status_ok)\n"
+ s += " goto out;\n"
+ s += " for (i=0; i<%s; i++) {\n" % (parent + ty.lenvar.name)
+ s += libxl_C_type_gen_json(ty.elem_type, v+"[i]",
+ indent + " ", parent)
+ s += " }\n"
+ s += " s = yajl_gen_array_close(hand);\n"
+ s += " if (s != yajl_gen_status_ok)\n"
+ s += " goto out;\n"
+ s += "}\n"
+ elif isinstance(ty, idl.Enumeration):
+ s += "s = libxl__yajl_gen_enum(hand, %s_to_string(%s));\n" % (ty.typename, ty.pass_arg(v, parent is None))
+ s += "if (s != yajl_gen_status_ok)\n"
+ s += " goto out;\n"
+ elif isinstance(ty, idl.KeyedUnion):
+ if parent is None:
+ raise Exception("KeyedUnion type must have a parent")
+ s += "switch (%s) {\n" % (parent + ty.keyvar.name)
+ for f in ty.fields:
+ (nparent,fexpr) = ty.member(v, f, parent is None)
+ s += "case %s:\n" % f.enumname
+ if f.type is not None:
+ s += libxl_C_type_gen_json(f.type, fexpr, indent + " ", nparent)
+ else:
+ s += " s = yajl_gen_map_open(hand);\n"
+ s += " if (s != yajl_gen_status_ok)\n"
+ s += " goto out;\n"
+ s += " s = yajl_gen_map_close(hand);\n"
+ s += " if (s != yajl_gen_status_ok)\n"
+ s += " goto out;\n"
+ s += " break;\n"
+ s += "}\n"
+ elif isinstance(ty, idl.Struct) and (parent is None or ty.json_gen_fn is None):
+ s += "s = yajl_gen_map_open(hand);\n"
+ s += "if (s != yajl_gen_status_ok)\n"
+ s += " goto out;\n"
+ for f in [f for f in ty.fields if not f.const and not f.type.private]:
+ (nparent,fexpr) = ty.member(v, f, parent is None)
+ default_expr = get_default_expr(f, nparent, fexpr)
+ s += "if (%s) {\n" % default_expr
+
+ s += libxl_C_type_gen_map_key(f, nparent, " ")
+ s += libxl_C_type_gen_json(f.type, fexpr, " ", nparent)
+
+ s += "}\n"
+
+ s += "s = yajl_gen_map_close(hand);\n"
+ s += "if (s != yajl_gen_status_ok)\n"
+ s += " goto out;\n"
+ else:
+ if ty.json_gen_fn is not None:
+ s += "s = %s(hand, %s);\n" % (ty.json_gen_fn, ty.pass_arg(v, parent is None))
+ s += "if (s != yajl_gen_status_ok)\n"
+ s += " goto out;\n"
+
+ if parent is None:
+ s += "out:\n"
+ s += "return s;\n"
+
+ if s != "":
+ s = indent + s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_type_to_json(ty, v, indent = " "):
+ s = ""
+ gen = "(libxl__gen_json_callback)&%s_gen_json" % ty.typename
+ s += "return libxl__object_to_json(ctx, \"%s\", %s, (void *)%s);\n" % (ty.typename, gen, ty.pass_arg(v, passby=idl.PASS_BY_REFERENCE))
+
+ if s != "":
+ s = indent + s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_type_parse_json(ty, w, v, indent = " ", parent = None, discriminator = None):
+ s = ""
+ if parent is None:
+ s += "int rc = 0;\n"
+ s += "const libxl__json_object *x __attribute__((__unused__)) = o;\n"
+
+ if isinstance(ty, idl.Array):
+ if parent is None:
+ raise Exception("Array type must have a parent")
+ if discriminator is not None:
+ raise Exception("Only KeyedUnion can have discriminator")
+ lenvar = parent + ty.lenvar.name
+ s += "{\n"
+ s += " libxl__json_object *t;\n"
+ s += " int i;\n"
+ s += " if (!libxl__json_object_is_array(x)) {\n"
+ s += " rc = -1;\n"
+ s += " goto out;\n"
+ s += " }\n"
+ s += " %s = x->u.array->count;\n" % lenvar
+ s += " %s = libxl__calloc(NOGC, %s, sizeof(*%s));\n" % (v, lenvar, v)
+ s += " if (!%s && %s != 0) {\n" % (v, lenvar)
+ s += " rc = -1;\n"
+ s += " goto out;\n"
+ s += " }\n"
+ s += " for (i=0; (t=libxl__json_array_get(x,i)); i++) {\n"
+ s += libxl_C_type_do_init(ty.elem_type,
+ lambda by: ("&" if by == idl.PASS_BY_REFERENCE else "")+
+ ("%s[i]" % v),
+ need_zero=False, indent=indent+" ")
+ s += libxl_C_type_parse_json(ty.elem_type, "t", v+"[i]",
+ indent + " ", parent)
+ s += " }\n"
+ s += " if (i != %s) {\n" % lenvar
+ s += " rc = -1;\n"
+ s += " goto out;\n"
+ s += " }\n"
+ s += "}\n"
+ elif isinstance(ty, idl.Enumeration):
+ if discriminator is not None:
+ raise Exception("Only KeyedUnion can have discriminator")
+ s += "{\n"
+ s += " const char *enum_str;\n"
+ s += " if (!libxl__json_object_is_string(%s)) {\n" % w
+ s += " rc = -1;\n"
+ s += " goto out;\n"
+ s += " }\n"
+ s += " enum_str = libxl__json_object_get_string(%s);\n" % w
+ s += " rc = %s_from_string(enum_str, %s);\n" % (ty.typename, ty.pass_arg(v, parent is None, idl.PASS_BY_REFERENCE))
+ s += " if (rc)\n"
+ s += " goto out;\n"
+ s += "}\n"
+ elif isinstance(ty, idl.KeyedUnion):
+ if parent is None:
+ raise Exception("KeyedUnion type must have a parent")
+ if discriminator is None:
+ raise Excpetion("KeyedUnion type must have a discriminator")
+ for f in ty.fields:
+ if f.enumname != discriminator:
+ continue
+ (nparent,fexpr) = ty.member(v, f, parent is None)
+ if f.type is not None:
+ s += libxl_C_type_parse_json(f.type, w, fexpr, indent + " ", nparent)
+ elif isinstance(ty, idl.Struct) and (parent is None or ty.json_parse_fn is None):
+ if discriminator is not None:
+ raise Exception("Only KeyedUnion can have discriminator")
+ for f in [f for f in ty.fields if not f.const and not f.type.private]:
+ saved_var_name = "saved_%s" % f.name
+ s += "{\n"
+ s += " const libxl__json_object *%s = x;\n" % saved_var_name
+ if isinstance(f.type, idl.KeyedUnion):
+ for x in f.type.fields:
+ s += " x = libxl__json_map_get(\"%s\", %s, JSON_MAP);\n" % \
+ (f.type.keyvar.name + "." + x.name, w)
+ s += " if (x) {\n"
+ (nparent, fexpr) = ty.member(v, f.type.keyvar, parent is None)
+ s += " %s_init_%s(%s, %s);\n" % (ty.typename, f.type.keyvar.name, v, x.enumname)
+ (nparent,fexpr) = ty.member(v, f, parent is None)
+ s += libxl_C_type_parse_json(f.type, "x", fexpr, " ", nparent, x.enumname)
+ s += " }\n"
+ else:
+ s += " x = libxl__json_map_get(\"%s\", %s, %s);\n" % (f.name, w, f.type.json_parse_type)
+ s += " if (x) {\n"
+ (nparent,fexpr) = ty.member(v, f, parent is None)
+ s += libxl_C_type_parse_json(f.type, "x", fexpr, " ", nparent)
+ s += " }\n"
+ s += " x = %s;\n" % saved_var_name
+ s += "}\n"
+ else:
+ if discriminator is not None:
+ raise Exception("Only KeyedUnion can have discriminator")
+ if ty.json_parse_fn is not None:
+ s += "rc = %s(gc, %s, &%s);\n" % (ty.json_parse_fn, w, v)
+ s += "if (rc)\n"
+ s += " goto out;\n"
+
+ if parent is None:
+ s += "out:\n"
+ s += "return rc;\n"
+
+ if s != "":
+ s = indent +s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_type_from_json(ty, v, w, indent = " "):
+ s = ""
+ parse = "(libxl__json_parse_callback)&%s_parse_json" % (ty.namespace + "_" + ty.rawname)
+ s += "return libxl__object_from_json(ctx, \"%s\", %s, %s, %s);\n" % (ty.typename, parse, v, w)
+
+ if s != "":
+ s = indent + s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_enum_to_string(ty, e, indent = " "):
+ s = ""
+ s += "switch(%s) {\n" % e
+ for v in ty.values:
+ s += " case %s:\n" % (v.name)
+ s += " return \"%s\";\n" % (v.valuename.lower())
+ s += " default:\n "
+ s += " return NULL;\n"
+ s += "}\n"
+
+ if s != "":
+ s = indent + s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_enum_strings(ty, indent=""):
+ s = ""
+ s += "libxl_enum_string_table %s_string_table[] = {\n" % (ty.typename)
+ for v in ty.values:
+ s += " { .s = \"%s\", .v = %s },\n" % (v.valuename.lower(), v.name)
+ s += " { NULL, -1 },\n"
+ s += "};\n"
+ s += "\n"
+
+ if s != "":
+ s = indent + s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_enum_from_string(ty, str, e, indent = " "):
+ s = ""
+ s += "return libxl__enum_from_string(%s_string_table,\n" % ty.typename
+ s += " %s, (int *)%s);\n" % (str, e)
+
+ if s != "":
+ s = indent + s
+ return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+
+if __name__ == '__main__':
+ if len(sys.argv) != 6:
+ print("Usage: gentypes.py <idl> <header> <header-private> <header-json> <implementation>", file=sys.stderr)
+ sys.exit(1)
+
+ (_, idlname, header, header_private, header_json, impl) = sys.argv
+
+ (builtins,types) = idl.parse(idlname)
+
+ print("outputting libxl type definitions to %s" % header)
+
+ f = open(header, "w")
+
+ header_define = header.upper().replace('.','_')
+ f.write("""#ifndef %s
+#define %s
+
+/*
+ * DO NOT EDIT.
+ *
+ * This file is autogenerated by
+ * "%s"
+ */
+
+""" % (header_define, header_define, " ".join(sys.argv)))
+
+ for ty in types:
+ f.write(libxl_C_type_define(ty) + ";\n")
+ if ty.dispose_fn is not None:
+ f.write("%svoid %s(%s);\n" % (ty.hidden(), ty.dispose_fn, ty.make_arg("p")))
+ if ty.copy_deprecated_fn is not None:
+ f.write("%sint %s(libxl_ctx *ctx, %s);\n" % (ty.hidden(),
+ ty.copy_deprecated_fn,
+ ty.make_arg("p")))
+ if ty.copy_fn is not None:
+ f.write("%svoid %s(libxl_ctx *ctx, %s, const %s);\n" % (ty.hidden(), ty.copy_fn,
+ ty.make_arg("dst"), ty.make_arg("src")))
+ if ty.init_fn is not None:
+ f.write("%svoid %s(%s);\n" % (ty.hidden(), ty.init_fn, ty.make_arg("p")))
+ for field in libxl_init_members(ty):
+ if not isinstance(field.type, idl.KeyedUnion):
+ raise Exception("Only KeyedUnion is supported for member init")
+ ku = field.type
+ f.write("%svoid %s(%s, %s);\n" % (ty.hidden(), ty.init_fn + "_" + ku.keyvar.name,
+ ty.make_arg("p"),
+ ku.keyvar.type.make_arg(ku.keyvar.name)))
+ if ty.json_gen_fn is not None:
+ f.write("%schar *%s_to_json(libxl_ctx *ctx, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p")))
+ if ty.json_parse_fn is not None:
+ f.write("%sint %s_from_json(libxl_ctx *ctx, %s, const char *s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+ if isinstance(ty, idl.Enumeration):
+ f.write("%sconst char *%s_to_string(%s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p")))
+ f.write("%sint %s_from_string(const char *s, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("e", passby=idl.PASS_BY_REFERENCE)))
+ f.write("%sextern libxl_enum_string_table %s_string_table[];\n" % (ty.hidden(), ty.typename))
+ f.write("\n")
+
+ f.write("""#endif /* %s */\n""" % (header_define))
+ f.close()
+
+ print("outputting libxl JSON definitions to %s" % header_json)
+
+ f = open(header_json, "w")
+
+ header_json_define = header_json.upper().replace('.','_')
+ f.write("""#ifndef %s
+#define %s
+
+/*
+ * DO NOT EDIT.
+ *
+ * This file is autogenerated by
+ * "%s"
+ */
+
+""" % (header_json_define, header_json_define, " ".join(sys.argv)))
+
+ for ty in [ty for ty in types if ty.json_gen_fn is not None]:
+ f.write("%syajl_gen_status %s_gen_json(yajl_gen hand, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+
+ f.write("\n")
+ f.write("""#endif /* %s */\n""" % header_json_define)
+ f.close()
+
+ print("outputting libxl type internal definitions to %s" % header_private)
+
+ f = open(header_private, "w")
+
+ header_private_define = header_private.upper().replace('.','_')
+ f.write("""#ifndef %s
+#define %s
+
+/*
+ * DO NOT EDIT.
+ *
+ * This file is autogenerated by
+ * "%s"
+ */
+
+""" % (header_private_define, header_private_define, " ".join(sys.argv)))
+
+ for ty in [ty for ty in types if ty.json_parse_fn is not None]:
+ f.write("%sint %s_parse_json(libxl__gc *gc, const libxl__json_object *o, %s);\n" % \
+ (ty.hidden(), ty.namespace + "_" + ty.rawname,
+ ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+
+ f.write("\n")
+ f.write("""#endif /* %s */\n""" % header_json_define)
+ f.close()
+
+ print("outputting libxl type implementations to %s" % impl)
+
+ f = open(impl, "w")
+ f.write("""
+/* DO NOT EDIT.
+ *
+ * This file is autogenerated by
+ * "%s"
+ */
+
+#include "libxl_osdeps.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libxl_internal.h"
+
+
+""" % " ".join(sys.argv))
+
+ for ty in [t for t in types if t.dispose_fn is not None and t.autogenerate_dispose_fn]:
+ f.write("void %s(%s)\n" % (ty.dispose_fn, ty.make_arg("p")))
+ f.write("{\n")
+ f.write(" if (!p) return;\n")
+ f.write(libxl_C_type_dispose(ty, "p"))
+ f.write(" memset(p, 0, sizeof(*p));\n")
+ f.write("}\n")
+ f.write("\n")
+
+ for ty in [t for t in types if t.copy_fn and t.autogenerate_copy_fn]:
+ f.write("void %s(libxl_ctx *ctx, %s, const %s)\n" % (ty.copy_fn,
+ ty.make_arg("dst", passby=idl.PASS_BY_REFERENCE),
+ ty.make_arg("src", passby=idl.PASS_BY_REFERENCE)))
+ f.write("{\n")
+ f.write(libxl_C_type_copy(ty, "dst", "src"))
+ f.write("}\n")
+ f.write("\n")
+
+ for ty in [t for t in types if t.copy_deprecated_fn]:
+ f.write("int %s(libxl_ctx *ctx, %s)\n" % (ty.copy_deprecated_fn,
+ ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+ f.write("{\n")
+ for field in [field for field in ty.fields if not field.const]:
+ (vnparent,vfexpr) = ty.member("p", field, True)
+ f.write(libxl_C_type_copy_deprecated(field, vfexpr,
+ vparent = vnparent))
+ f.write(" return 0;\n")
+ f.write("}\n")
+ f.write("\n")
+
+ for ty in [t for t in types if t.init_fn is not None and t.autogenerate_init_fn]:
+ f.write(libxl_C_type_init(ty))
+ for field in libxl_init_members(ty):
+ f.write(libxl_C_type_member_init(ty, field))
+
+ for ty in [t for t in types if isinstance(t,idl.Enumeration)]:
+ f.write("const char *%s_to_string(%s e)\n" % (ty.typename, ty.typename))
+ f.write("{\n")
+ f.write(libxl_C_enum_to_string(ty, "e"))
+ f.write("}\n")
+ f.write("\n")
+
+ f.write(libxl_C_enum_strings(ty))
+
+ f.write("int %s_from_string(const char *s, %s *e)\n" % (ty.typename, ty.typename))
+ f.write("{\n")
+ f.write(libxl_C_enum_from_string(ty, "s", "e"))
+ f.write("}\n")
+ f.write("\n")
+
+ for ty in [t for t in types if t.json_gen_fn is not None]:
+ f.write("yajl_gen_status %s_gen_json(yajl_gen hand, %s)\n" % (ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+ f.write("{\n")
+ f.write(libxl_C_type_gen_json(ty, "p"))
+ f.write("}\n")
+ f.write("\n")
+
+ f.write("char *%s_to_json(libxl_ctx *ctx, %s)\n" % (ty.typename, ty.make_arg("p")))
+ f.write("{\n")
+ f.write(libxl_C_type_to_json(ty, "p"))
+ f.write("}\n")
+ f.write("\n")
+
+ for ty in [t for t in types if t.json_parse_fn is not None]:
+ f.write("int %s_parse_json(libxl__gc *gc, const libxl__json_object *%s, %s)\n" % \
+ (ty.namespace + "_" + ty.rawname,"o",ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+ f.write("{\n")
+ f.write(libxl_C_type_parse_json(ty, "o", "p"))
+ f.write("}\n")
+ f.write("\n")
+
+ f.write("int %s_from_json(libxl_ctx *ctx, %s, const char *s)\n" % (ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+ f.write("{\n")
+ if not isinstance(ty, idl.Enumeration):
+ f.write(" %s_init(p);\n" % ty.typename)
+ f.write(libxl_C_type_from_json(ty, "p", "s"))
+ f.write("}\n")
+ f.write("\n")
+
+ f.close()
--- /dev/null
+from __future__ import print_function
+
+import sys
+
+PASS_BY_VALUE = 1
+PASS_BY_REFERENCE = 2
+
+DIR_NONE = 0
+DIR_IN = 1
+DIR_OUT = 2
+DIR_BOTH = 3
+
+_default_namespace = ""
+def namespace(s):
+ if type(s) != str:
+ raise TypeError("Require a string for the default namespace.")
+ global _default_namespace
+ _default_namespace = s
+
+def _get_default_namespace():
+ global _default_namespace
+ return _default_namespace
+
+_default_hidden = False
+def hidden(b):
+ global _default_hidden
+ _default_hidden = b
+
+def _get_default_hidden():
+ global _default_hidden
+ return _default_hidden
+
+class Type(object):
+ def __init__(self, typename, **kwargs):
+ self.namespace = kwargs.setdefault('namespace',
+ _get_default_namespace())
+ self._hidden = kwargs.setdefault('hidden', _get_default_hidden())
+ self.dir = kwargs.setdefault('dir', DIR_BOTH)
+ if self.dir not in [DIR_NONE, DIR_IN, DIR_OUT, DIR_BOTH]:
+ raise ValueError
+
+ self.passby = kwargs.setdefault('passby', PASS_BY_VALUE)
+ if self.passby not in [PASS_BY_VALUE, PASS_BY_REFERENCE]:
+ raise ValueError
+
+ self.private = kwargs.setdefault('private', False)
+
+ if typename is None: # Anonymous type
+ self.typename = None
+ self.rawname = None
+ elif self.namespace is None: # e.g. system provided types
+ self.typename = typename
+ self.rawname = typename
+ else:
+ self.typename = self.namespace + typename
+ self.rawname = typename
+
+ if self.typename is not None:
+ self.dispose_fn = kwargs.setdefault('dispose_fn', self.typename + "_dispose")
+ else:
+ self.dispose_fn = kwargs.setdefault('dispose_fn', None)
+
+ self.autogenerate_dispose_fn = kwargs.setdefault('autogenerate_dispose_fn', True)
+
+ if self.typename is not None:
+ self.copy_fn = kwargs.setdefault('copy_fn', self.typename + "_copy")
+ else:
+ self.copy_fn = kwargs.setdefault('copy_fn', None)
+
+ self.autogenerate_copy_fn = kwargs.setdefault('autogenerate_copy_fn', True)
+
+ self.init_fn = kwargs.setdefault('init_fn', None)
+ self.init_val = kwargs.setdefault('init_val', None)
+ self.autogenerate_init_fn = kwargs.setdefault('autogenerate_init_fn', False)
+
+ self.check_default_fn = kwargs.setdefault('check_default_fn', None)
+ self.copy_deprecated_fn = kwargs.setdefault('copy_deprecated_fn',
+ None)
+
+ if self.typename is not None and not self.private:
+ self.json_gen_fn = kwargs.setdefault('json_gen_fn', self.typename + "_gen_json")
+ self.json_parse_type = kwargs.setdefault('json_parse_type', "JSON_ANY")
+ if self.namespace is not None:
+ self.json_parse_fn = kwargs.setdefault('json_parse_fn',
+ self.namespace + "_" + self.rawname + "_parse_json")
+ else:
+ self.json_parse_fn = kwargs.setdefault('json_parse_fn',
+ self.typename + "_parse_json")
+ else:
+ self.json_gen_fn = kwargs.setdefault('json_gen_fn', None)
+ self.json_parse_type = kwargs.setdefault('json_parse_type', None)
+ self.json_parse_fn = kwargs.setdefault('json_parse_fn', None)
+
+ self.autogenerate_json = kwargs.setdefault('autogenerate_json', True)
+
+ def marshal_in(self):
+ return self.dir in [DIR_IN, DIR_BOTH]
+ def marshal_out(self):
+ return self.dir in [DIR_OUT, DIR_BOTH]
+
+ def hidden(self):
+ if self._hidden:
+ return "_hidden "
+ else:
+ return ""
+
+ def make_arg(self, n, passby=None):
+ if passby is None: passby = self.passby
+
+ if passby == PASS_BY_REFERENCE:
+ return "%s *%s" % (self.typename, n)
+ else:
+ return "%s %s" % (self.typename, n)
+
+ def pass_arg(self, n, isref=None, passby=None):
+ if passby is None: passby = self.passby
+ if isref is None: isref = self.passby == PASS_BY_REFERENCE
+
+ if passby == PASS_BY_REFERENCE:
+ if isref:
+ return "%s" % (n)
+ else:
+ return "&%s" % (n)
+ else:
+ if isref:
+ return "*%s" % (n)
+ else:
+ return "%s" % (n)
+
+class Builtin(Type):
+ """Builtin type"""
+ def __init__(self, typename, **kwargs):
+ kwargs.setdefault('dispose_fn', None)
+ kwargs.setdefault('autogenerate_dispose_fn', False)
+ kwargs.setdefault('autogenerate_json', False)
+ Type.__init__(self, typename, **kwargs)
+
+class Number(Builtin):
+ def __init__(self, ctype, **kwargs):
+ kwargs.setdefault('namespace', None)
+ kwargs.setdefault('dispose_fn', None)
+ kwargs.setdefault('copy_fn', None)
+ kwargs.setdefault('signed', False)
+ kwargs.setdefault('json_gen_fn', "yajl_gen_integer")
+ kwargs.setdefault('json_parse_type', "JSON_INTEGER")
+ # json_parse_fn might be overriden on specific type
+ kwargs.setdefault('json_parse_fn', "libxl__int_parse_json")
+ self.signed = kwargs['signed']
+ Builtin.__init__(self, ctype, **kwargs)
+
+class UInt(Number):
+ def __init__(self, w, **kwargs):
+ kwargs.setdefault('namespace', None)
+ kwargs.setdefault('dispose_fn', None)
+ kwargs.setdefault('json_parse_fn', "libxl__uint%d_parse_json" % w)
+ kwargs.setdefault('copy_fn', None)
+ Number.__init__(self, "uint%d_t" % w, **kwargs)
+
+ self.width = w
+
+class EnumerationValue(object):
+ def __init__(self, enum, value, name, **kwargs):
+ self.enum = enum
+
+ self.valuename = str.upper(name)
+ self.rawname = str.upper(enum.rawname) + "_" + self.valuename
+ self.name = str.upper(enum.value_namespace) + self.rawname
+ self.value = value
+
+class Enumeration(Type):
+ def __init__(self, typename, values, **kwargs):
+ kwargs.setdefault('dispose_fn', None)
+ kwargs.setdefault('copy_fn', None)
+ kwargs.setdefault('json_parse_type', "JSON_STRING")
+ Type.__init__(self, typename, **kwargs)
+
+ self.value_namespace = kwargs.setdefault('value_namespace',
+ self.namespace)
+
+ self.values = []
+ for v in values:
+ # (value, name)
+ (num,name) = v
+ self.values.append(EnumerationValue(self, num, name,
+ typename=self.rawname))
+ def lookup(self, name):
+ for v in self.values:
+ if v.valuename == str.upper(name):
+ return v
+ return ValueError
+
+class Field(object):
+ """An element of an Aggregate type"""
+ def __init__(self, type, name, **kwargs):
+ self.type = type
+ self.name = name
+ self.const = kwargs.setdefault('const', False)
+ self.enumname = kwargs.setdefault('enumname', None)
+ self.init_val = kwargs.setdefault('init_val', None)
+ self.deprecated_by = kwargs.setdefault('deprecated_by', None)
+
+class Aggregate(Type):
+ """A type containing a collection of other types"""
+ def __init__(self, kind, typename, fields, **kwargs):
+ kwargs.setdefault('json_parse_type', "JSON_MAP")
+ Type.__init__(self, typename, **kwargs)
+
+ if self.typename is not None:
+ self.init_fn = kwargs.setdefault('init_fn', self.typename + "_init")
+ else:
+ self.init_fn = kwargs.setdefault('init_fn', None)
+
+ self.autogenerate_init_fn = kwargs.setdefault('autogenerate_init_fn', True)
+
+ self.kind = kind
+
+ self.fields = []
+ for f in fields:
+ # (name, type[, {kw args}])
+ if len(f) == 2:
+ n,t = f
+ kw = {}
+ elif len(f) == 3:
+ n,t,kw = f
+ else:
+ raise ValueError
+ if n is None:
+ raise ValueError
+ self.fields.append(Field(t,n,**kw))
+
+ # Returns a tuple (stem, field-expr)
+ #
+ # field-expr is a C expression for a field "f" within the struct
+ # "v".
+ #
+ # stem is the stem common to both "f" and any other sibbling field
+ # within the "v".
+ def member(self, v, f, isref):
+ if isref:
+ deref = v + "->"
+ else:
+ deref = v + "."
+
+ if f.name is None: # Anonymous
+ return (deref, deref)
+ else:
+ return (deref, deref + f.name)
+
+class Struct(Aggregate):
+ def __init__(self, name, fields, **kwargs):
+ kwargs.setdefault('passby', PASS_BY_REFERENCE)
+ Aggregate.__init__(self, "struct", name, fields, **kwargs)
+
+ def has_fields(self):
+ return len(self.fields) != 0
+
+class Union(Aggregate):
+ def __init__(self, name, fields, **kwargs):
+ # Generally speaking some intelligence is required to free a
+ # union therefore any specific instance of this class will
+ # need to provide an explicit destructor function.
+ kwargs.setdefault('passby', PASS_BY_REFERENCE)
+ kwargs.setdefault('dispose_fn', None)
+ Aggregate.__init__(self, "union", name, fields, **kwargs)
+
+class KeyedUnion(Aggregate):
+ """A union which is keyed of another variable in the parent structure"""
+ def __init__(self, name, keyvar_type, keyvar_name, fields, **kwargs):
+ Aggregate.__init__(self, "union", name, [], **kwargs)
+
+ if not isinstance(keyvar_type, Enumeration):
+ raise ValueError
+
+ kv_kwargs = dict([(x.lstrip('keyvar_'),y) for (x,y) in kwargs.items() if x.startswith('keyvar_')])
+
+ self.keyvar = Field(keyvar_type, keyvar_name, **kv_kwargs)
+
+ for f in fields:
+ # (name, enum, type)
+ e, ty = f
+ ev = keyvar_type.lookup(e)
+ en = ev.name
+ self.fields.append(Field(ty, e, enumname=en))
+
+#
+# Standard Types
+#
+
+void = Builtin("void *", namespace = None)
+bool = Builtin("bool", namespace = None,
+ copy_fn=None,
+ json_gen_fn = "yajl_gen_bool",
+ json_parse_type = "JSON_BOOL",
+ json_parse_fn = "libxl__bool_parse_json",
+ autogenerate_json = False)
+
+size_t = Number("size_t", namespace = None)
+
+integer = Number("int", namespace = None, signed = True)
+
+uint8 = UInt(8)
+uint16 = UInt(16)
+uint32 = UInt(32)
+uint64 = UInt(64, json_gen_fn = "libxl__uint64_gen_json")
+
+string = Builtin("char *", namespace = None, copy_fn = "libxl_string_copy", dispose_fn = "free",
+ json_gen_fn = "libxl__string_gen_json",
+ json_parse_type = "JSON_STRING | JSON_NULL",
+ json_parse_fn = "libxl__string_parse_json",
+ autogenerate_json = False,
+ check_default_fn="libxl__string_is_default")
+
+class Array(Type):
+ """An array of the same type"""
+ def __init__(self, elem_type, lenvar_name, **kwargs):
+ kwargs.setdefault('dispose_fn', 'free')
+ kwargs.setdefault('json_parse_type', 'JSON_ARRAY')
+ Type.__init__(self, namespace=elem_type.namespace, typename=elem_type.rawname + " *", **kwargs)
+
+ lv_kwargs = dict([(x.lstrip('lenvar_'),y) for (x,y) in kwargs.items() if x.startswith('lenvar_')])
+
+ self.lenvar = Field(integer, lenvar_name, **lv_kwargs)
+ self.elem_type = elem_type
+
+class OrderedDict(dict):
+ """A dictionary which remembers insertion order.
+
+ push to back on duplicate insertion"""
+
+ def __init__(self):
+ dict.__init__(self)
+ self.__ordered = []
+
+ def __setitem__(self, key, value):
+ try:
+ self.__ordered.remove(key)
+ except ValueError:
+ pass
+
+ self.__ordered.append(key)
+ dict.__setitem__(self, key, value)
+
+ def ordered_keys(self):
+ return self.__ordered
+ def ordered_values(self):
+ return [self[x] for x in self.__ordered]
+ def ordered_items(self):
+ return [(x,self[x]) for x in self.__ordered]
+
+def parse(f):
+ print("Parsing %s" % f, file=sys.stderr)
+
+ globs = {}
+ locs = OrderedDict()
+
+ for n,t in globals().items():
+ if isinstance(t, Type):
+ globs[n] = t
+ elif isinstance(t,type(object)) and issubclass(t, Type):
+ globs[n] = t
+ elif n in ['PASS_BY_REFERENCE', 'PASS_BY_VALUE',
+ 'DIR_NONE', 'DIR_IN', 'DIR_OUT', 'DIR_BOTH',
+ 'namespace', 'hidden']:
+ globs[n] = t
+
+ try:
+ exec(compile(open(f).read(), f, 'exec'), globs, locs)
+ except SyntaxError as e:
+ raise SyntaxError("Errors were found at line %d while processing %s:\n\t%s"
+ % (e.lineno, f, e.text))
+
+ types = [t for t in locs.ordered_values() if isinstance(t,Type)]
+
+ builtins = [t for t in types if isinstance(t,Builtin)]
+ types = [t for t in types if not isinstance(t,Builtin)]
+
+ return (builtins,types)
--- /dev/null
+libxl IDL
+---------
+
+Each type in the libxl interface is represented by an object of type
+idl.Type (or a subclass thereof). Every local variable defined by the
+.idl file must be an instance of idl.Type (e.g. you may not define
+Python functions or any other construct other than defining variables)
+
+The name of the type must be passed as the first argument to the
+constructor when defining a new type. The name given should not
+contain the initial namespace element (e.g. "libxl_"). See below for
+how to specify a namespace.
+
+The Type.typename contains the C name of the type _including_ the
+namespace element while Type.rawname is always set to the 'base' name
+of the type.
+
+The idl.Type base class has several other properties which apply to
+all types. The properties are set by passing a named parameter to the
+constructor.
+
+Type.namespace: (default: "libxl_")
+
+ The namespace in which the type resides. Usually this is "libxl_" but
+ system defined and builtin types may differ.
+
+ If the typename is not None then the namespace is prepended to the
+ type.
+
+Type.passby: (default: idl.PASS_BY_VALUE)
+
+ Defines the manner in which a type should be passed to C
+ functions. Valid values for this fields are:
+ idl.PASS_BY_VALUE
+ idl.PASS_BY_REFERENCE
+
+Type.dispose_fn: (default: typename + "_dispose" or None if type == None)
+
+ The name of the C function which will free all dynamically allocated
+ memory contained within this type (but not the type itself).
+
+Type.autogenerate_dispose_fn: (default: True)
+
+ Indicates if the above named Type.dispose_fn should be
+ autogenerated.
+
+Type.copy_fn: (default: typename + "_copy" or None if type == None)
+
+ The name of the C function which will deep copy all fields within
+ this type.
+
+Type.autogenerate_copy_fn: (default: True)
+
+ Indicates if the above named Type.copy_fn should be
+ autogenerated.
+
+Type.autogenerate_copy_fn
+
+Type.init_val: (default: None)
+
+ C expression for the value to initialise instances of this type to.
+
+ If present takes precendence over init_fn (see below).
+
+Type.init_fn: (default: typename + "_init" if dir in [IN, BOTH] and
+ type != None)
+
+ The name of the C function which will initialist Type.
+
+Type.autogenerate_init_fn: (default: True if dir in [IN, BOTH])
+
+ Indicates if the above named Type.init_fn should be
+ autogenerated.
+
+Type.json_gen_fn: (default: typename + "_gen_json" or None if type == None)
+
+ The name of the C function which will generate a YAJL data structure
+ representing this type.
+
+Type.json_parse_fn: (default: typename + "_parse_json" or None if type == None)
+
+ The name of the C function which will parse a libxl JSON structure
+ representing this type to C type.
+
+Type.autogenerate_json: (default: True)
+
+ Indicates if the above named Type.json_*_fn should be autogenerated.
+
+Type.check_default_fn:
+
+ If it's set then calling this function shall return true if this type
+ has been set to default value (internal libxl implementation).
+
+ If this is not set, that means we can check the type against init_val
+ (if it has one) or zero to determine whether the value is default
+ value.
+
+Other simple type-Classes
+-------------------------
+
+idl.Builtin
+
+ Instances of this class represent types which are predefined within
+ the system.
+
+idl.UInt
+
+ Instances of this class represent the standard uint<N>_t types.
+
+ The <N> for a given instance must be passed to the constructor and is
+ then available in UInt.width
+
+Complex type-Classes
+--------------------
+
+idl.Enumeration
+
+ A class representing an enumeration (named integer values).
+ This class has one property besides the ones defined for the Type
+ class:
+
+ Enumeration.value_namespace: (default: namespace)
+
+ The namespace in which the values of the Enumeration (see below) reside.
+ This prefix is prepended to the name of the value.
+
+ The values are available in the list Enumeration.values. Each
+ element in the list is of type idl.EnumerationValue.
+
+ Each EnumerationValue has the following properties:
+
+ EnumerationValue.enum Reference to containing Enumeration
+ EnumerationValue.name The C name of this value, including
+ the namespace and typename of the
+ containing Enumeration (e.g.
+ "LIBXL_FOOENUM_VALUE")
+ EnumerationValue.rawname The C name of this value, excluding
+ the namespace but including the
+ typename of the containing
+ Enumeration (e.g. "FOOENUM_VALUE")
+ EnumerationValue.valuename The name of this value, excluding the
+ name of the containing Enumeration
+ and any namespace (e.g. "VALUE")
+ EnumerationValue.value The integer value associated with this name.
+
+idl.Aggregate
+
+ Base class for type-Classes which contain a number of other types
+ (e.g. structs and unions).
+
+ The contained types are available in the list Aggregate.fields. Each
+ element in the list is of type idl.Field representing a member of the
+ aggregate.
+
+ Each field has the following properties:
+
+ Field.type The type of the member (a idl.Type).
+ Field.name The name of the member (can be None for anonymous
+ fields).
+ Field.const Boolean, true if the member is const.
+ Field.init_val The initialisation value for this field. Takes
+ precendence over both Field.type.init_val and
+ Field.type.init_fn.
+
+idl.Struct
+
+ A subclass of idl.Aggregate representing the C struct type.
+
+ Struct.kind == "struct"
+
+idl.Union
+
+ A subclass of idl.Aggregate representing the C union type.
+
+ Union.kind == "union"
+
+idl.KeyedUnion
+
+ A subclass of idl.Aggregate which represents the C union type
+ where the currently valid member of the union can be determined based
+ upon another member in the containing type. An idl.KeyedUnion must
+ always be a member of a containing idl.Aggregate type.
+
+ The KeyedUnion.keyvar contains an idl.Field, this is the member of
+ the containing type which determines the valid member of the
+ union. The idl.Field.type of the keyvar must be an Enumeration type.
+
+idl.Array
+
+ A class representing an array of similar elements. An idl.Array must
+ always be an idl.Field of a containing idl.Aggregate.
+
+ idl.Array.elem_type contains an idl.Type which is the type of each
+ element of the array.
+
+ idl.Array.len_var contains an idl.Field which is added to the parent
+ idl.Aggregate and will contain the length of the array. The field
+ MUST be named num_ARRAYNAME.
+
+Standard Types
+--------------
+
+Several standard types a predefined. They are
+
+void (void pointer type)
+bool
+size_t
+integer 24 bit signed integer.
+
+uint{8,16,32,64} uint{8,16,32,64}_t
+
+domid Domain ID
+
+string NULL terminated string
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+/*
+ * libxl API compatibility
+ *
+ * From Xen 4.2 onwards the API of libxl will be maintained in a
+ * stable manner. This means that it should be possible to write an
+ * application against the API provided by libxl in Xen 4.2 and expect
+ * that it will continue to compile against future versions of Xen
+ * without source modification.
+ *
+ * In order to make such compatibility possible it is required that
+ * application which want to be exposed to a particular API #define
+ * LIBXL_API_VERSION before including libxl.h or any other libxl
+ * header. The syntax of the LIBXL_API_VERSION is:
+ * 0xVVSSEE
+ * where ($(XEN_xxx) from xen/Makefile):
+ * VV is the Xen major release number, $(XEN_VERSION)
+ * SS is the Xen sub version number, $(XEN_SUBVERSION)
+ * EE is the Xen extra version digit, first numeric part of
+ * $(XEN_EXTRAVERSION) not including the leading "."
+ * For example the first stable API version, supported by Xen 4.2.0,
+ * is 0x040200.
+ *
+ * Lack of LIBXL_API_VERSION means "the latest" which will
+ * change. Specifying an unknown LIBXL_API_VERSION will result in a
+ * compile time error.
+ *
+ * Identical versions of the libxl API will represented by the version
+ * containing the earliest instance of that API. e.g. if 4.2.0 and
+ * 4.3.0 contain an identical libxl API then only LIBXL_API_VERSION
+ * 0x040200 will be valid.
+ *
+ * We will try especially hard to avoid changing the API during a
+ * stable series, i.e. it should be unusual for the last byte of
+ * LIBXL_API_VERSION to be non-zero.
+ *
+ * In the event that a change is required which cannot be made
+ * backwards compatible in this manner a #define of the form
+ * LIBXL_HAVE_<interface> will always be added in order to make it
+ * possible to write applications which build against any version of
+ * libxl. Such changes are expected to be exceptional and used as a
+ * last resort. The barrier for backporting such a change to a stable
+ * branch will be very high.
+ *
+ * These guarantees apply only to stable releases of Xen. When an
+ * incompatible change is made in the unstable tree then
+ * LIBXL_API_VERSION will be bumped to the next expected stable
+ * release number on the first such change only. Applications which
+ * want to support building against Xen unstable are expected to track
+ * API changes in that tree until it is released as a stable release.
+ *
+ * API compatibility will be maintained for all versions of Xen using
+ * the same $(XEN_VERSION) (e.g. throughout a major release).
+ */
+
+/* LIBXL_HAVE_PHYSINFO_CAP_PV
+ *
+ * If this is defined, libxl_physinfo has a "cap_pv" field.
+ */
+#define LIBXL_HAVE_PHYSINFO_CAP_PV 1
+
+/* LIBXL_HAVE_CONSOLE_NOTIFY_FD
+ *
+ * If this is defined, libxl_console_exec and
+ * libxl_primary_console_exe take a notify_fd parameter. That
+ * parameter will be used to notify the caller that the console is connected.
+ */
+#define LIBXL_HAVE_CONSOLE_NOTIFY_FD 1
+
+/* LIBXL_HAVE_CONST_COPY_AND_LENGTH_FUNCTIONS
+ *
+ * If this is defined, the copy functions have constified src parameter and the
+ * length functions accept constified parameter.
+ */
+#define LIBXL_HAVE_CONST_COPY_AND_LENGTH_FUNCTIONS 1
+
+/* LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONST_B_INFO
+ *
+ * If this is defined, libxl_domain_need_memory no longer modifies
+ * the b_info paseed in.
+ */
+#define LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONST_B_INFO 1
+
+/* LIBXL_HAVE_VNUMA
+ *
+ * If this is defined the type libxl_vnode_info exists, and a
+ * field 'vnuma_nodes' is present in libxl_domain_build_info.
+ */
+#define LIBXL_HAVE_VNUMA 1
+
+/* LIBXL_HAVE_USERDATA_UNLINK
+ *
+ * If it is defined, libxl has a library function called
+ * libxl_userdata_unlink.
+ */
+#define LIBXL_HAVE_USERDATA_UNLINK 1
+
+/* LIBXL_HAVE_CPUPOOL_QUALIFIER_TO_CPUPOOLID
+ *
+ * If this is defined, libxl has a library function called
+ * libxl_cpupool_qualifier_to_cpupoolid, which takes in a CPU pool
+ * qualifier in the form of number or string, then returns the ID of
+ * that CPU pool.
+ */
+#define LIBXL_HAVE_CPUPOOL_QUALIFIER_TO_CPUPOOLID 1
+
+/* LIBXL_HAVE_CPUPOOL_ADD_REM_CPUMAP
+ *
+ * If this is defined, libxl has two library functions called
+ * libxl_cpupool_cpuadd_cpumap and libxl_cpupool_cpuremove_cpumap,
+ * which allow to add to or remove from a cpupool all the cpus
+ * specified in a bitmap.
+ */
+#define LIBXL_HAVE_CPUPOOL_ADD_REM_CPUMAP 1
+
+/*
+ *
+ * LIBXL_HAVE_BITMAP_AND_OR
+ *
+ * If this is defined, libxl has two library functions, libxl_bitmap_and
+ * and libxl_bitmap_or to compute the logical and and or of two bitmaps
+ */
+#define LIBXL_HAVE_BITMAP_AND_OR 1
+
+/*
+ * LIBXL_HAVE_FIRMWARE_PASSTHROUGH indicates the feature for
+ * passing in SMBIOS and ACPI firmware to HVM guests is present
+ * in the library.
+ */
+#define LIBXL_HAVE_FIRMWARE_PASSTHROUGH 1
+
+/*
+ * LIBXL_HAVE_DOMAIN_NODEAFFINITY indicates that a 'nodemap' field
+ * (of libxl_bitmap type) is present in libxl_domain_build_info,
+ * containing the node-affinity for the domain.
+ */
+#define LIBXL_HAVE_DOMAIN_NODEAFFINITY 1
+
+/*
+ * LIBXL_HAVE_PVUSB indicates functions for plugging in USB devices
+ * through pvusb -- both hotplug and at domain creation time..
+ */
+#define LIBXL_HAVE_PVUSB 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE indicates that the
+ * libxl_vendor_device field is present in the hvm sections of
+ * libxl_domain_build_info. This field tells libxl which
+ * flavour of xen-pvdevice to enable in QEMU.
+ */
+#define LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE 1
+
+/*
+ * The libxl_domain_build_info has the event_channels field.
+ */
+#define LIBXL_HAVE_BUILDINFO_EVENT_CHANNELS 1
+
+/*
+ * libxl_domain_build_info has the u.hvm.ms_vm_genid field.
+ */
+#define LIBXL_HAVE_BUILDINFO_HVM_MS_VM_GENID 1
+
+/*
+ * LIBXL_HAVE_VCPUINFO_SOFT_AFFINITY indicates that a 'cpumap_soft'
+ * field (of libxl_bitmap type) is present in libxl_vcpuinfo,
+ * containing the soft affinity of a vcpu.
+ */
+#define LIBXL_HAVE_VCPUINFO_SOFT_AFFINITY 1
+
+/*
+ * LIBXL_HAVE_SET_VCPUAFFINITY_FORCE indicates that the
+ * libxl_set_vcpuaffinity_force() library call is available.
+ */
+#define LIBXL_HAVE_SET_VCPUAFFINITY_FORCE 1
+
+/*
+ * LIBXL_HAVE_DEVICE_DISK_DIRECT_IO_SAFE indicates that a
+ * 'direct_io_safe' field (of boolean type) is present in
+ * libxl_device_disk.
+ */
+#define LIBXL_HAVE_DEVICE_DISK_DIRECT_IO_SAFE 1
+
+/*
+ * The libxl_device_disk has the discard_enable field.
+ */
+#define LIBXL_HAVE_LIBXL_DEVICE_DISK_DISCARD_ENABLE 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_IOMEM_START_GFN indicates that it is possible
+ * to specify the start guest frame number used to map a range of I/O
+ * memory machine frame numbers via the 'gfn' field (of type uint64)
+ * of the 'iomem' structure. An array of iomem structures is embedded
+ * in libxl_domain_build_info and used to map the indicated memory
+ * ranges during domain build.
+ */
+#define LIBXL_HAVE_BUILDINFO_IOMEM_START_GFN 1
+
+/*
+ * LIBXL_HAVE_SCHED_RTDS indicates that the RTDS real time scheduler
+ * is available. A 'budget' field added in libxl_domain_sched_params.
+ */
+#define LIBXL_HAVE_SCHED_RTDS 1
+
+/*
+ * LIBXL_HAVE_SCHED_NULL indicates that the 'null' static scheduler
+ * is available.
+ */
+#define LIBXL_HAVE_SCHED_NULL 1
+
+/*
+ * libxl_domain_build_info has u.hvm.viridian_enable and _disable bitmaps
+ * of the specified width.
+ */
+#define LIBXL_HAVE_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE 1
+#define LIBXL_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE_WIDTH 64
+
+/*
+ * libxl_domain_build_info has the u.hvm.mmio_hole_memkb field.
+ */
+#define LIBXL_HAVE_BUILDINFO_HVM_MMIO_HOLE_MEMKB 1
+
+/*
+ * libxl_domain_info returns ERROR_DOMAIN_NOTFOUND if the domain
+ * is not present, instead of ERROR_INVAL.
+ */
+#define LIBXL_HAVE_ERROR_DOMAIN_NOTFOUND 1
+
+/*
+ * libxl_domain_build_info has device_tree and libxl_device_dtdev
+ * exists. This mean Device Tree passthrough is supported for ARM
+ */
+#define LIBXL_HAVE_DEVICETREE_PASSTHROUGH 1
+
+/*
+ * libxl_domain_build_info has device_model_user to specify the user to
+ * run the device model with. See docs/misc/qemu-deprivilege.txt.
+ */
+#define LIBXL_HAVE_DEVICE_MODEL_USER 1
+
+/*
+ * libxl_vcpu_sched_params is used to store per-vcpu params.
+ */
+#define LIBXL_HAVE_VCPU_SCHED_PARAMS 1
+
+/*
+ * LIBXL_HAVE_SCHED_RTDS_VCPU_PARAMS indicates RTDS scheduler
+ * now supports per-vcpu settings.
+ */
+#define LIBXL_HAVE_SCHED_RTDS_VCPU_PARAMS 1
+
+/*
+ * LIBXL_HAVE_SCHED_RTDS_VCPU_EXTRA indicates RTDS scheduler
+ * now supports per-vcpu extratime settings.
+ */
+#define LIBXL_HAVE_SCHED_RTDS_VCPU_EXTRA 1
+
+/*
+ * libxl_domain_build_info has the arm.gic_version field.
+ */
+#define LIBXL_HAVE_BUILDINFO_ARM_GIC_VERSION 1
+
+/*
+ * libxl_domain_build_info has the arch_arm.tee field.
+ */
+#define LIBXL_HAVE_BUILDINFO_ARCH_ARM_TEE 1
+
+/*
+ * LIBXL_HAVE_SOFT_RESET indicates that libxl supports performing
+ * 'soft reset' for domains and there is 'soft_reset' shutdown reason
+ * in enum libxl_shutdown_reason.
+ */
+#define LIBXL_HAVE_SOFT_RESET 1
+
+/*
+ * LIBXL_HAVE_APIC_ASSIST indicates that the 'apic_assist' value
+ * is present in the viridian enlightenment enumeration.
+ */
+#define LIBXL_HAVE_APIC_ASSIST 1
+
+/*
+ * LIBXL_HAVE_BUILD_ID means that libxl_version_info has the extra
+ * field for the hypervisor build_id.
+ */
+#define LIBXL_HAVE_BUILD_ID 1
+
+/*
+ * LIBXL_HAVE_QEMU_MONITOR_COMMAND indiactes the availability of the
+ * libxl_qemu_monitor_command() function.
+ */
+#define LIBXL_HAVE_QEMU_MONITOR_COMMAND 1
+
+/*
+ * LIBXL_HAVE_SCHED_CREDIT2_PARAMS indicates the existance of a
+ * libxl_sched_credit2_params structure, containing Credit2 scheduler
+ * wide parameters (i.e., the ratelimiting value).
+ */
+#define LIBXL_HAVE_SCHED_CREDIT2_PARAMS 1
+
+/*
+ * LIBXL_HAVE_SCHED_CREDIT_MIGR_DELAY indicates that there is a field
+ * in libxl_sched_credit_params called vcpu_migr_delay_us which controls
+ * the resistance of the vCPUs of the cpupool to migrations among pCPUs.
+ */
+#define LIBXL_HAVE_SCHED_CREDIT_MIGR_DELAY
+
+/*
+ * LIBXL_HAVE_VIRIDIAN_CRASH_CTL indicates that the 'crash_ctl' value
+ * is present in the viridian enlightenment enumeration.
+ */
+#define LIBXL_HAVE_VIRIDIAN_CRASH_CTL 1
+
+/*
+ * LIBXL_HAVE_VIRIDIAN_SYNIC indicates that the 'synic' value
+ * is present in the viridian enlightenment enumeration.
+ */
+#define LIBXL_HAVE_VIRIDIAN_SYNIC 1
+
+/*
+ * LIBXL_HAVE_VIRIDIAN_STIMER indicates that the 'stimer' value
+ * is present in the viridian enlightenment enumeration.
+ */
+#define LIBXL_HAVE_VIRIDIAN_STIMER 1
+
+/*
+ * LIBXL_HAVE_VIRIDIAN_HCALL_IPI indicates that the 'hcall_ipi' value
+ * is present in the viridian enlightenment enumeration.
+ */
+#define LIBXL_HAVE_VIRIDIAN_HCALL_IPI 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE indicates that
+ * libxl_domain_build_info has the u.hvm.acpi_laptop_slate field.
+ */
+#define LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE 1
+
+/*
+ * LIBXL_HAVE_P9S indicates that the p9 field in IDL has been changed to p9s
+ */
+#define LIBXL_HAVE_P9S 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_ARM_VUART indicates that the toolstack supports virtual UART
+ * for ARM.
+ */
+#define LIBXL_HAVE_BUILDINFO_ARM_VUART 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_GRANT_LIMITS indicates that libxl_domain_build_info
+ * has the max_grant_frames and max_maptrack_frames fields.
+ */
+#define LIBXL_HAVE_BUILDINFO_GRANT_LIMITS 1
+
+#define LIBXL_MAX_GRANT_DEFAULT (~(uint32_t)0)
+#define LIBXL_MAX_GRANT_FRAMES_DEFAULT 32 /* deprecated */
+#define LIBXL_MAX_MAPTRACK_FRAMES_DEFAULT 1024 /* deprecated */
+/*
+ * LIBXL_HAVE_BUILDINFO_GRANT_DEFAULT indicates that the default
+ * values of max_grant_frames and max_maptrack_frames fields in
+ * libxl_domain_build_info are the special sentinel value
+ * LIBXL_MAX_GRANT_DEFAULT rather than the fixed values above.
+ * This means to use the hypervisor's default.
+ */
+#define LIBXL_HAVE_BUILDINFO_GRANT_DEFAULT 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_* indicates that libxl_domain_build_info has
+ * the field represented by the '*'. The original position of those
+ * fields is:
+ * - u.hvm.timer_mode
+ * - u.hvm.apic
+ * - u.hvm.nested_hvm
+ * - u.pv.bootloader
+ * - u.pv.bootloader_args
+ */
+#define LIBXL_HAVE_BUILDINFO_TIMER_MODE 1
+#define LIBXL_HAVE_BUILDINFO_APIC 1
+#define LIBXL_HAVE_BUILDINFO_NESTED_HVM 1
+#define LIBXL_HAVE_BUILDINFO_BOOTLOADER 1
+#define LIBXL_HAVE_BUILDINFO_BOOTLOADER_ARGS 1
+
+/*
+ * LIBXL_HAVE_EXTENDED_VKB indicates that libxl_device_vkb has extended fields:
+ * - unique_id;
+ * - feature_disable_keyboard;
+ * - feature_disable_pointer;
+ * - feature_abs_pointer;
+ * - feature_raw_pointer;
+ * - feature_multi_touch;
+ * - width;
+ * - height;
+ * - multi_touch_width;
+ * - multi_touch_height;
+ * - multi_touch_num_contacts.
+ */
+#define LIBXL_HAVE_EXTENDED_VKB 1
+
+/*
+ * LIBXL_HAVE_PHYSINFO_CAP_HAP_SHADOW indicates that libxl_physinfo has
+ * cap_hap and cap_shadow fields reflecting the hardware and Xen availability
+ * of Hardware Assisted, and Shadow paging support.
+ */
+#define LIBXL_HAVE_PHYSINFO_CAP_HAP_SHADOW 1
+
+/*
+ * LIBXL_HAVE_PHYSINFO_CAP_IOMMU_HAP_PT_SHARE indicates that libxl_physinfo
+ * has a cap_iommu_hap_pt_share field that indicates whether the hardware
+ * supports sharing the IOMMU and HAP page tables.
+ */
+#define LIBXL_HAVE_PHYSINFO_CAP_IOMMU_HAP_PT_SHARE 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_IOMMU_MEMKB indicates thate libxl_domain_build_info
+ * has an iommu_memkb field which should be set with the amount of memory
+ * overhead needed by the domain for populating IOMMU page tables.
+ */
+#define LIBXL_HAVE_BUILDINFO_IOMMU_MEMKB 1
+
+/*
+ * LIBXL_HAVE_CREATEINFO_PASSTHROUGH indicates that
+ * libxl_domain_create_info has a passthrough field (which is a
+ * libxl_passthrough enumeration) that indicates whether device pass-
+ * through is enabled for the domain and, if so, whether the IOMMU and
+ * HAP page tables may be shared or not.
+ */
+#define LIBXL_HAVE_CREATEINFO_PASSTHROUGH 1
+
+/*
+ * LIBXL_HAVE_DISK_SAFE_REMOVE indicates that the
+ * libxl_device_disk_safe_remove() function is defined.
+ */
+#define LIBXL_HAVE_DISK_SAFE_REMOVE 1
+
+/*
+ * libxl ABI compatibility
+ *
+ * The only guarantee which libxl makes regarding ABI compatibility
+ * across releases is that the SONAME will always be bumped whenever
+ * the ABI is changed in an incompatible way.
+ *
+ * This applies within stable branches as well as
+ * development branches. It is possible that a new stable release of
+ * Xen may require a rebuild of applications using the
+ * library. However per the API compatibility gaurantees such a
+ * rebuild should not normally require any source level changes.
+ *
+ * As with the API compatiblity the SONAME will only be bumped for the
+ * first ABI incompatible change in a development branch.
+ */
+
+/*
+ * libxl memory management
+ *
+ * From the point of view of the application (ie, libxl's caller),
+ * struct libxl_ctx* is threadsafe, and all returned allocated
+ * structures are obtained from malloc(), and must be freed by the
+ * caller either directly or by calling an appropriate free function
+ * provided by libxl. Ie the application does not get automatic
+ * assistance from libxl in managing these allocations.
+ *
+ * Specific details are in the header comments which should be found
+ * in libxl.h or libxlutil.h, next to the relevant function
+ * declarations.
+ *
+ * Internally, libxl has a garbage collection scheme which allows much libxl
+ * code to allocate strings etc. for internal use without needing to
+ * free them. These are called "temporary allocations".
+ *
+ * The pool for these temporary allocations, along with any other
+ * thread-specific data which is private to libxl but shared between
+ * libxl functions (such as the current xenstore transaction), is
+ * stored in the "gc context" which is a special enhanced context
+ * structure allocated automatically by convenience macros at every
+ * entry to libxl.
+ *
+ * Every libxl function falls into one of these categories:
+ *
+ * 1. Public functions (declared in libxl.h, libxlutil.h), which may
+ * be called by libxl applications. If a public function returns
+ * any allocated object to its caller, that object must have come
+ * from malloc.
+ *
+ * The definitions of public functions MUST use the gc context
+ * initialisation macros (or do the equivalent work themselves).
+ * These macros will ensure that all temporary allocations will be
+ * automatically freed before the function returns to its caller.
+ *
+ * A public function may be called from within libxl; the call
+ * context initialisation macros will make sure that the internal
+ * caller's context is reused (eg, so that the same xenstore
+ * transaction is used). But in-libxl callers of libxl public
+ * functions should note that any libxl public function may cause
+ * recursively reentry into libxl via the application's event
+ * callback hook.
+ *
+ * Public functions have names like libxl_foobar.
+ *
+ * 2. Private functions, which may not be called by libxl
+ * applications; they are not declared in libxl.h or libxlutil.h
+ * and they may not be called other than by other libxl functions.
+ *
+ * Private functions should not use the gc context initialisation
+ * macros.
+ *
+ * Private functions have names like libxl__foobar (NB, two underscores).
+ * Also the declaration of such functions must be preceeded by the _hidden
+ * macro.
+ *
+ * Allocations made by a libxl function fall into one of the following
+ * categories (where "object" includes any memory allocation):
+ *
+ * (a) Objects which are not returned to the function's caller.
+ * These should be allocated from the temporary pool.
+ *
+ * (b) Objects which are intended for return to the calling
+ * application. This includes all allocated objects returned by
+ * any public function.
+ *
+ * It may also include objects allocated by an internal function
+ * specifically for eventual return by the function's external
+ * callers, but this situation should be clearly documented in
+ * comments.
+ *
+ * These should be allocated from malloc() et al. and comments
+ * near the function declaration should explain the memory
+ * ownership. If a simple free() by the application is not
+ * sufficient, a suitable public freeing function should be
+ * provided.
+ *
+ * (c) Internal objects whose size and/or lifetime dictate explicit
+ * memory management within libxl. This includes objects which
+ * will be embedded in opaque structures which will be returned to
+ * the libxl caller (more generally, any internal object whose
+ * lifetime exceeds the libxl entrypoint which creates it) and
+ * objects which are so large or numerous that explicit memory
+ * management is required.
+ *
+ * These should be allocated from malloc() et al., and freed
+ * explicitly at the appropriate point. The situation should be
+ * documented in comments.
+ *
+ * (d) Objects which are allocated by internal-only functions and
+ * returned to the function's (therefore, internal) caller but are
+ * strictly for internal use by other parts of libxl. These
+ * should be allocated from the temporary pool.
+ *
+ * Where a function's primary purpose is to return such an object,
+ * it should have a libxl__gc * as it's first argument.
+ *
+ * Note that there are two ways to change an allocation from this
+ * category to the "public" category. Either the implementation
+ * is kept internal and a wrapper function duplicates all memory
+ * allocations so that they are suitable for return to external
+ * callers or the implementation uses plain malloc() et al calls
+ * and an internal wrapper adds the relevant pointers to the gc.
+ * The latter method is preferred for obvious performance reasons.
+ *
+ * No temporary objects allocated from the pool may be explicitly freed.
+ * Therefore public functions which initialize a libxl__gc MUST call
+ * libxl__free_all() before returning.
+ *
+ * Memory allocation failures are not handled gracefully. If malloc
+ * (or realloc) fails, libxl will cause the entire process to print
+ * a message to stderr and exit with status 255.
+ */
+/*
+ * libxl types
+ *
+ * Most libxl types are defined by the libxl IDL (see
+ * libxl_types.idl). The library provides a common set of methods for
+ * initialising and freeing these types.
+ *
+ * IDL-generated libxl types should be used as follows: the user must
+ * always call the "init" function before using a type, even if the
+ * variable is simply being passed by reference as an out parameter
+ * to a libxl function. The user must always calls "dispose" exactly
+ * once afterwards, to clean up, regardless of whether operations on
+ * this object succeeded or failed. See the xl code for examples.
+ *
+ * "init" and "dispose" are idempotent.
+ *
+ * void libxl_<type>_init(<type> *p):
+ *
+ * Initialises the members of "p" to all defaults. These may either
+ * be special value which indicates to the library that it should
+ * select an appropriate default when using this field or actual
+ * default values.
+ *
+ * Some fields within a data type (e.g. unions) cannot be sensibly
+ * initialised without further information. In these cases a
+ * separate subfield initialisation function is provided (see
+ * below).
+ *
+ * An instance which has been initialised using this method can
+ * always be safely passed to the dispose function (see
+ * below). This is true even if the data type contains fields which
+ * require a separate call to a subfield initialisation function.
+ *
+ * This method is provided for any aggregate type which is used as
+ * an input parameter.
+ *
+ * void libxl_<type>_init_<subfield>(<type> *p, subfield):
+ *
+ * Initialise those parts of "p" which are not initialised by the
+ * main init function due to the unknown value of "subfield". Sets
+ * p->subfield as well as initialising any fields to their default
+ * values.
+ *
+ * p->subfield must not have been previously initialised.
+ *
+ * This method is provided for any aggregate type.
+ *
+ * void libxl_<type>_dispose(instance *p):
+ *
+ * Frees any dynamically allocated memory used by the members of
+ * "p" but not the storage used by "p" itself (this allows for the
+ * allocation of arrays of types and for the composition of types).
+ *
+ * char *libxl_<type>_to_json(instance *p)
+ *
+ * Generates a JSON object from "p" in the form of a NULL terminated
+ * string.
+ *
+ * <type *> libxl_<type>_from_json(const char *json)
+ * int libxl_<type>_from_json(const char *json)
+ *
+ * Parses "json" and returns:
+ *
+ * an int value, if <type> is enumeration type. The value is the enum value
+ * representing the respective string in "json".
+ *
+ * an instance of <type>, if <type> is aggregate type. The returned
+ * instance has its fields filled in by the parser according to "json".
+ *
+ * If the parsing fails, caller cannot rely on the value / instance
+ * returned.
+ */
+#ifndef LIBXL_H
+#define LIBXL_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/wait.h> /* for pid_t */
+
+#include <xentoollog.h>
+
+typedef struct libxl__ctx libxl_ctx;
+
+#include <libxl_uuid.h>
+#include <_libxl_list.h>
+
+/* API compatibility. */
+#ifdef LIBXL_API_VERSION
+#if LIBXL_API_VERSION != 0x040200 && LIBXL_API_VERSION != 0x040300 && \
+ LIBXL_API_VERSION != 0x040400 && LIBXL_API_VERSION != 0x040500 && \
+ LIBXL_API_VERSION != 0x040700 && LIBXL_API_VERSION != 0x040800 && \
+ LIBXL_API_VERSION != 0x041300 && LIBXL_API_VERSION != 0x041400
+#error Unknown LIBXL_API_VERSION
+#endif
+#endif
+
+/* LIBXL_HAVE_RETRIEVE_DOMAIN_CONFIGURATION
+ *
+ * If this is defined we have libxl_retrieve_domain_configuration which
+ * returns the current configuration of a domain, which can be used to
+ * rebuild a domain.
+ */
+#define LIBXL_HAVE_RETRIEVE_DOMAIN_CONFIGURATION 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_VCPU_AFFINITY_ARRAYS
+ *
+ * If this is defined, then the libxl_domain_build_info structure will
+ * contain two arrays of libxl_bitmap-s, with all the necessary information
+ * to set the hard affinity (vcpu_hard_affinity) and the soft affinity
+ * (vcpu_soft_affinity) of the VCPUs.
+ *
+ * Note that, if the vcpu_hard_affinity array is used, libxl will ignore
+ * the content of the cpumap field of libxl_domain_build_info. That is to
+ * say, if the array is allocated and used by the caller, it is it and
+ * only it that determines the hard affinity of the domain's VCPUs.
+ *
+ * The number of libxl_bitmap-s in the arrays should be equal to the
+ * maximum number of VCPUs of the domain. If there only are N elements in
+ * an array, with N smaller the the maximum number of VCPUs, the hard or
+ * soft affinity (depending on which array we are talking about) will be
+ * set only for the first N VCPUs. The other VCPUs will just have affinity,
+ * both hard and soft, with all the host PCPUs.
+ * Each bitmap should be big enough to accommodate the maximum number of
+ * PCPUs of the host.
+ */
+#define LIBXL_HAVE_BUILDINFO_VCPU_AFFINITY_ARRAYS 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_VKB_DEVICE
+ *
+ * If this is defined, then the libxl_domain_build_info structure will
+ * contain a boolean hvm.vkb_device which instructs libxl whether to include
+ * a vkbd at build time or not.
+ */
+#define LIBXL_HAVE_BUILDINFO_VKB_DEVICE 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST
+ *
+ * If this is defined, then the libxl_domain_build_info structure will
+ * contain hvm.usbdevice_list, a libxl_string_list type that contains
+ * a list of USB devices to specify on the qemu command-line.
+ *
+ * If it is set, callers may use either hvm.usbdevice or
+ * hvm.usbdevice_list, but not both; if both are set, libxl will
+ * throw an error.
+ *
+ * If this is not defined, callers can only use hvm.usbdevice. Note
+ * that this means only one device can be added at domain build time.
+ */
+#define LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_USBVERSION
+ *
+ * If this is defined, then the libxl_domain_build_info structure will
+ * contain hvm.usbversion, a integer type that contains a USB
+ * controller version to specify on the qemu upstream command-line.
+ *
+ * If it is set, callers may use hvm.usbversion to specify if the usb
+ * controller is usb1, usb2 or usb3.
+ *
+ * If this is not defined, the hvm.usbversion field does not exist.
+ */
+#define LIBXL_HAVE_BUILDINFO_USBVERSION 1
+
+/*
+ * LIBXL_HAVE_DEVICE_BACKEND_DOMNAME
+ *
+ * If this is defined, libxl_device_* structures containing a backend_domid
+ * field also contain a backend_domname field. If backend_domname is set, it is
+ * resolved to a domain ID when the device is used and takes precedence over the
+ * backend_domid field.
+ *
+ * If this is not defined, the backend_domname field does not exist.
+ */
+#define LIBXL_HAVE_DEVICE_BACKEND_DOMNAME 1
+
+/*
+ * LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG
+ *
+ * This argument was erroneously "const" in the 4.2 release despite
+ * the requirement for the callback to free the event.
+ */
+#if LIBXL_API_VERSION != 0x040200
+#define LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG 1
+#endif
+
+/*
+ * LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
+ *
+ * The return value of libxl_basename is malloc'ed but the erroneously
+ * marked as "const" in releases before 4.5.
+ */
+#if !defined(LIBXL_API_VERSION) || LIBXL_API_VERSION >= 0x040500
+#define LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE 1
+#endif
+
+/*
+ * LIBXL_HAVE_PHYSINFO_OUTSTANDING_PAGES
+ *
+ * If this is defined, libxl_physinfo structure will contain an uint64 field
+ * called outstanding_pages, containing the number of pages claimed but not
+ * yet allocated for all domains.
+ */
+#define LIBXL_HAVE_PHYSINFO_OUTSTANDING_PAGES 1
+
+/*
+ * LIBXL_HAVE_PHYSINFO_MAX_POSSIBLE_MFN
+ *
+ * If this is defined, libxl_physinfo structure will contain an uint64 field
+ * called max_possible_mfn, containing the highest possible mfn on this host,
+ * possibly taking memory hotplug into account.
+ */
+#define LIBXL_HAVE_PHYSINFO_MAX_POSSIBLE_MFN 1
+
+/*
+ * LIBXL_HAVE_DOMINFO_OUTSTANDING_MEMKB 1
+ *
+ * If this is defined, libxl_dominfo will contain a MemKB type field called
+ * outstanding_memkb, containing the amount of claimed but not yet allocated
+ * memory for a specific domain.
+ */
+#define LIBXL_HAVE_DOMINFO_OUTSTANDING_MEMKB 1
+
+/*
+ * LIBXL_HAVE_DOMINFO_NEVER_STOP
+ *
+ * If this is defined, libxl_dominfo will contain a flag called never_stop
+ * indicating that the specific domain should never be stopped by the
+ * toolstack.
+ */
+#define LIBXL_HAVE_DOMINFO_NEVER_STOP 1
+
+/*
+ * LIBXL_HAVE_QXL
+ *
+ * If defined, then the libxl_vga_interface_type will contain another value:
+ * "QXL". This value define if qxl vga is supported.
+ *
+ * If this is not defined, the qxl vga support is missed.
+ */
+#define LIBXL_HAVE_QXL 1
+
+/*
+ * LIBXL_HAVE_SPICE_VDAGENT
+ *
+ * If defined, then the libxl_spice_info structure will contain a boolean type:
+ * vdagent and clipboard_sharing. These values define if Spice vdagent and
+ * clipboard sharing are enabled.
+ *
+ * If this is not defined, the Spice vdagent support is ignored.
+ */
+#define LIBXL_HAVE_SPICE_VDAGENT 1
+
+/*
+ * LIBXL_HAVE_SPICE_USBREDIRECTION
+ *
+ * If defined, then the libxl_spice_info structure will contain an integer type
+ * field: usbredirection. This value defines if Spice usbredirection is enabled
+ * and with how much channels.
+ *
+ * If this is not defined, the Spice usbredirection support is ignored.
+ */
+#define LIBXL_HAVE_SPICE_USBREDIREDIRECTION 1
+
+/*
+ * LIBXL_HAVE_SPICE_IMAGECOMPRESSION
+ *
+ * If defined, then the libxl_spice_info structure will contain a string type
+ * field: image_compression. This value defines what Spice image compression
+ * is used.
+ *
+ * If this is not defined, the Spice image compression setting support is ignored.
+ */
+#define LIBXL_HAVE_SPICE_IMAGECOMPRESSION 1
+
+/*
+ * LIBXL_HAVE_SPICE_STREAMINGVIDEO
+ *
+ * If defined, then the libxl_spice_info structure will contain a string type
+ * field: streaming_video. This value defines what Spice streaming video setting
+ * is used.
+ *
+ * If this is not defined, the Spice streaming video setting support is ignored.
+ */
+#define LIBXL_HAVE_SPICE_STREAMINGVIDEO 1
+
+/*
+ * LIBXL_HAVE_HVM_HDTYPE
+ *
+ * If defined, then the u.hvm structure will contain a enum type
+ * hdtype.
+ */
+#define LIBXL_HAVE_HVM_HDTYPE 1
+
+/*
+ * LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS 1
+ *
+ * If this is defined, libxl_domain_create_restore()'s API has changed to
+ * include a params structure.
+ */
+#define LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS 1
+
+/*
+ * LIBXL_HAVE_DOMAIN_CREATE_RESTORE_SEND_BACK_FD 1
+ *
+ * If this is defined, libxl_domain_create_restore()'s API includes the
+ * send_back_fd param. This is used only with COLO, for the libxl migration
+ * back channel; other callers should pass -1.
+ */
+#define LIBXL_HAVE_DOMAIN_CREATE_RESTORE_SEND_BACK_FD 1
+
+/*
+ * LIBXL_HAVE_DRIVER_DOMAIN_CREATION 1
+ *
+ * If this is defined, libxl_domain_create_info contains a driver_domain
+ * field that can be used to tell libxl that the domain that is going
+ * to be created is a driver domain, so the necessary actions are taken.
+ */
+#define LIBXL_HAVE_DRIVER_DOMAIN_CREATION 1
+
+/*
+ * LIBXL_HAVE_SIGCHLD_SELECTIVE_REAP
+ *
+ * If this is defined:
+ *
+ * Firstly, the enum libxl_sigchld_owner (in libxl_event.h) has the
+ * value libxl_sigchld_owner_libxl_always_selective_reap which may be
+ * passed to libxl_childproc_setmode in hooks->chldmode.
+ *
+ * Secondly, the function libxl_childproc_sigchld_occurred exists.
+ */
+#define LIBXL_HAVE_SIGCHLD_OWNER_SELECTIVE_REAP 1
+
+/*
+ * LIBXL_HAVE_SIGCHLD_SHARING
+ *
+ * If this is defined, it is permissible for multiple libxl ctxs
+ * to simultaneously "own" SIGCHLD. See "Subprocess handling"
+ * in libxl_event.h.
+ */
+#define LIBXL_HAVE_SIGCHLD_SHARING 1
+
+/*
+ * LIBXL_HAVE_NO_SUSPEND_RESUME
+ *
+ * Is this is defined then the platform has no support for saving,
+ * restoring or migrating a domain. In this case the related functions
+ * should be expected to return failure. That is:
+ * - libxl_domain_suspend
+ * - libxl_domain_resume
+ * - libxl_domain_remus_start
+ */
+#if defined(__arm__) || defined(__aarch64__)
+#define LIBXL_HAVE_NO_SUSPEND_RESUME 1
+#endif
+
+/*
+ * LIBXL_HAVE_DOMAIN_SUSPEND_ONLY
+ *
+ * If this is defined, function libxl_domains_suspend_only() is available.
+ */
+
+#define LIBXL_HAVE_DOMAIN_SUSPEND_ONLY 1
+
+/*
+ * LIBXL_HAVE_DEVICE_PCI_SEIZE
+ *
+ * If this is defined, then the libxl_device_pci struct will contain
+ * the "seize" boolean field. If this field is set, libxl_pci_add will
+ * check to see if the device is currently assigned to pciback, and if not,
+ * it will attempt to do so (unbinding the device from the existing driver).
+ */
+#define LIBXL_HAVE_DEVICE_PCI_SEIZE 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_KERNEL
+ *
+ * If this is defined, then the libxl_domain_build_info structure will
+ * contain 'kernel', 'ramdisk', 'cmdline' fields. 'kernel' is a string
+ * to indicate kernel image location, 'ramdisk' is a string to indicate
+ * ramdisk location, 'cmdline' is a string to indicate the paramters which
+ * would be appended to kernel image.
+ *
+ * Both PV guest and HVM guest can use these fields for direct kernel boot.
+ * But for compatibility reason, u.pv.kernel, u.pv.ramdisk and u.pv.cmdline
+ * still exist.
+ */
+#define LIBXL_HAVE_BUILDINFO_KERNEL 1
+
+/*
+ * LIBXL_HAVE_DEVICE_CHANNEL
+ *
+ * If this is defined, then the libxl_device_channel struct exists
+ * and channels can be attached to a domain. Channels manifest as consoles
+ * with names, see docs/misc/console.txt.
+ */
+#define LIBXL_HAVE_DEVICE_CHANNEL 1
+
+/*
+ * LIBXL_HAVE_AO_ABORT indicates the availability of libxl_ao_abort
+ */
+#define LIBXL_HAVE_AO_ABORT 1
+
+/* Functions annotated with LIBXL_EXTERNAL_CALLERS_ONLY may not be
+ * called from within libxl itself. Callers outside libxl, who
+ * do not #include libxl_internal.h, are fine. */
+#ifndef LIBXL_EXTERNAL_CALLERS_ONLY
+#define LIBXL_EXTERNAL_CALLERS_ONLY /* disappears for callers outside libxl */
+#endif
+
+/*
+ * LIBXL_HAVE_UUID_COPY_CTX_PARAM
+ *
+ * If this is defined, libxl_uuid_copy has changed to take a libxl_ctx
+ * structure.
+ */
+#define LIBXL_HAVE_UUID_COPY_CTX_PARAM 1
+
+/*
+ * LIBXL_HAVE_SSID_LABEL
+ *
+ * If this is defined, then libxl IDL contains string of XSM security
+ * label in all XSM related structures.
+ *
+ * If set this string takes precedence over the numeric field.
+ */
+#define LIBXL_HAVE_SSID_LABEL 1
+
+/*
+ * LIBXL_HAVE_CPUPOOL_NAME
+ *
+ * If this is defined, then libxl IDL contains string of CPU pool
+ * name in all CPU pool related structures.
+ *
+ * If set this string takes precedence over the numeric field.
+ */
+#define LIBXL_HAVE_CPUPOOL_NAME 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_SERIAL_LIST
+ *
+ * If this is defined, then the libxl_domain_build_info structure will
+ * contain hvm.serial_list, a libxl_string_list type that contains
+ * a list of serial ports to specify on the qemu command-line.
+ *
+ * If it is set, callers may use either hvm.serial or
+ * hvm.serial_list, but not both; if both are set, libxl will
+ * throw an error.
+ *
+ * If this is not defined, callers can only use hvm.serial. Note
+ * that this means only one serial port can be added at domain build time.
+ */
+#define LIBXL_HAVE_BUILDINFO_SERIAL_LIST 1
+
+/*
+ * LIBXL_HAVE_ALTP2M
+ * If this is defined, then libxl supports alternate p2m functionality.
+ */
+#define LIBXL_HAVE_ALTP2M 1
+
+/*
+ * LIBXL_HAVE_REMUS
+ * If this is defined, then libxl supports remus.
+ */
+#define LIBXL_HAVE_REMUS 1
+
+/*
+ * LIBXL_HAVE_COLO_USERSPACE_PROXY
+ * If this is defined, then libxl supports COLO userspace proxy.
+ */
+#define LIBXL_HAVE_COLO_USERSPACE_PROXY 1
+
+typedef uint8_t libxl_mac[6];
+#define LIBXL_MAC_FMT "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
+#define LIBXL_MAC_FMTLEN ((2*6)+5) /* 6 hex bytes plus 5 colons */
+#define LIBXL_MAC_BYTES(mac) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
+void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src);
+
+#if defined(__i386__) || defined(__x86_64__)
+/*
+ * LIBXL_HAVE_PSR_CMT
+ *
+ * If this is defined, the Cache Monitoring Technology feature is supported.
+ */
+#define LIBXL_HAVE_PSR_CMT 1
+
+/*
+ * LIBXL_HAVE_PSR_MBM
+ *
+ * If this is defined, the Memory Bandwidth Monitoring feature is supported.
+ */
+#define LIBXL_HAVE_PSR_MBM 1
+
+/*
+ * LIBXL_HAVE_PSR_CAT
+ *
+ * If this is defined, the Cache Allocation Technology feature is supported.
+ */
+#define LIBXL_HAVE_PSR_CAT 1
+
+/*
+ * LIBXL_HAVE_PSR_CDP
+ *
+ * If this is defined, the Code and Data Prioritization feature is supported.
+ */
+#define LIBXL_HAVE_PSR_CDP 1
+
+/*
+ * LIBXL_HAVE_PSR_L2_CAT
+ *
+ * If this is defined, the L2 Cache Allocation Technology feature is supported.
+ */
+#define LIBXL_HAVE_PSR_L2_CAT 1
+
+/*
+ * LIBXL_HAVE_PSR_GENERIC
+ *
+ * If this is defined, the Memory Bandwidth Allocation feature is supported.
+ * The following public functions are available:
+ * libxl_psr_{set/get}_val
+ * libxl_psr_get_hw_info
+ * libxl_psr_hw_info_list_free
+ */
+#define LIBXL_HAVE_PSR_GENERIC 1
+
+/*
+ * LIBXL_HAVE_MCA_CAPS
+ *
+ * If this is defined, setting MCA capabilities for HVM domain is supported.
+ */
+#define LIBXL_HAVE_MCA_CAPS 1
+#endif
+
+/*
+ * LIBXL_HAVE_PCITOPOLOGY
+ *
+ * If this is defined, then interface to query hypervisor about PCI device
+ * topology is available.
+ */
+#define LIBXL_HAVE_PCITOPOLOGY 1
+
+/*
+ * LIBXL_HAVE_SOCKET_BITMAP
+ *
+ * If this is defined, then libxl_socket_bitmap_alloc and
+ * libxl_get_online_socketmap exist.
+ */
+#define LIBXL_HAVE_SOCKET_BITMAP 1
+
+/*
+ * LIBXL_HAVE_SRM_V2
+ *
+ * If this is defined, then the libxl_domain_create_restore() interface takes
+ * a "stream_version" parameter and supports a value of 2.
+ *
+ * libxl_domain_suspend() will produce a v2 stream.
+ */
+#define LIBXL_HAVE_SRM_V2 1
+
+/*
+ * LIBXL_HAVE_SRM_V1
+ *
+ * In the case that LIBXL_HAVE_SRM_V2 is set, LIBXL_HAVE_SRM_V1
+ * indicates that libxl_domain_create_restore() can handle a "stream_version"
+ * parameter of 1, and convert the stream format automatically.
+ */
+#define LIBXL_HAVE_SRM_V1 1
+
+/*
+ * libxl_domain_build_info has the u.hvm.gfx_passthru_kind field and
+ * the libxl_gfx_passthru_kind enumeration is defined.
+*/
+#define LIBXL_HAVE_GFX_PASSTHRU_KIND
+
+/*
+ * LIBXL_HAVE_CHECKPOINTED_STREAM
+ *
+ * If this is defined, then libxl_checkpointed_stream exists.
+ */
+#define LIBXL_HAVE_CHECKPOINTED_STREAM 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_HVM_SYSTEM_FIRMWARE
+ *
+ * libxl_domain_build_info has u.hvm.system_firmware field which can be use
+ * to provide a different firmware blob (like SeaBIOS or OVMF).
+ */
+#define LIBXL_HAVE_BUILDINFO_HVM_SYSTEM_FIRMWARE
+
+/*
+ * ERROR_REMUS_XXX error code only exists from Xen 4.5, Xen 4.6 and it
+ * is changed to ERROR_CHECKPOINT_XXX in Xen 4.7
+ */
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION >= 0x040500 \
+ && LIBXL_API_VERSION < 0x040700
+#define ERROR_REMUS_DEVOPS_DOES_NOT_MATCH \
+ ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH
+#define ERROR_REMUS_DEVICE_NOT_SUPPORTED \
+ ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED
+#endif
+
+/*
+ * LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN
+ *
+ * In the case that LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN is set the
+ * libxl_vga_interface_type enumeration type contains a
+ * LIBXL_VGA_INTERFACE_TYPE_UNKNOWN identifier. This is used to signal
+ * that a libxl_vga_interface_type type has not been initialized yet.
+ */
+#define LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN 1
+
+/*
+ * LIBXL_HAVE_BYTEARRAY_UUID
+ *
+ * If this is defined, the internal member of libxl_uuid is defined
+ * as a 16 byte array that contains the UUID in big endian format.
+ * Also, the same structure layout is used across all OSes.
+ */
+#define LIBXL_HAVE_BYTEARRAY_UUID 1
+
+/*
+ * LIBXL_HAVE_MEMKB_64BITS
+ *
+ * If this is defined libxl_set_memory_target(), libxl_domain_setmaxmem()
+ * and libxl_wait_for_free_memory() will take a 64 bit value for the memory
+ * size parameter.
+ * From Xen 4.8 on libxl_get_memory_target(), libxl_domain_need_memory() and
+ * libxl_get_free_memory() return the memory size in a 64 bit value, too.
+ */
+#define LIBXL_HAVE_MEMKB_64BITS 1
+
+/*
+ * LIBXL_HAVE_QED
+ *
+ * If this is defined QED disk formats can be used for both HVM and PV guests.
+ */
+#define LIBXL_HAVE_QED 1
+
+/*
+ * LIBXL_HAVE_SET_PARAMETERS
+ *
+ * If this is defined setting hypervisor parameters is supported.
+ */
+#define LIBXL_HAVE_SET_PARAMETERS 1
+
+/*
+ * LIBXL_HAVE_PV_SHIM
+ *
+ * If this is defined, libxl_domain_build_info's pvh type information
+ * contains members pvshim, pvshim_path, pvshim_cmdline, pvshim_extra.
+ */
+#define LIBXL_HAVE_PV_SHIM 1
+
+/*
+ * LIBXL_HAVE_PVCALLS
+ *
+ * If this is defined, libxl supports creating pvcalls interfaces.
+ */
+#define LIBXL_HAVE_PVCALLS 1
+
+/*
+ * LIBXL_HAVE_FN_USING_QMP_ASYNC
+ *
+ * This define indicates that some function's API has changed and have an
+ * extra parameter "ao_how" which means that the function can be executed
+ * asynchronously. Those functions are:
+ * libxl_domain_pause()
+ * libxl_domain_unpause()
+ * libxl_send_trigger()
+ * libxl_set_vcpuonline()
+ * libxl_retrieve_domain_configuration()
+ * libxl_qemu_monitor_command()
+ * libxl_domain_shutdown()
+ * libxl_domain_reboot()
+ */
+#define LIBXL_HAVE_FN_USING_QMP_ASYNC 1
+
+/*
+ * LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONFIG
+ *
+ * If this is set, libxl_domain_need_memory takes a
+ * libxl_domain_config* (non-const) and uint32_t domid_for_logging
+ * (instead of a const libxl_domain_build_info*).
+ *
+ * If this is set, there is no need to call
+ * libxl_get_required_shadow_memory and instead the caller should
+ * simply leave shadow_memkb set to LIBXL_MEMKB_DEFAULT and allow
+ * libxl to fill in a suitable default in the usual way.
+ */
+#define LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONFIG
+
+/*
+ * LIBXL_HAVE_CREATEINFO_DOMID
+ *
+ * libxl_domain_create_new() and libxl_domain_create_restore() will use
+ * a domid specified in libxl_domain_create_info.
+ */
+#define LIBXL_HAVE_CREATEINFO_DOMID
+
+/*
+ * LIBXL_HAVE_CREATEINFO_XEND_SUSPEND_EVTCHN_COMPAT
+ *
+ * libxl_domain_create_info contains a boolean 'xend_suspend_evtchn_compat'
+ * value to control creation of the xenstore path for a domain's suspend
+ * event channel.
+ */
+#define LIBXL_HAVE_CREATEINFO_XEND_SUSPEND_EVTCHN_COMPAT
+
+typedef char **libxl_string_list;
+void libxl_string_list_dispose(libxl_string_list *sl);
+int libxl_string_list_length(const libxl_string_list *sl);
+void libxl_string_list_copy(libxl_ctx *ctx, libxl_string_list *dst,
+ const libxl_string_list *src);
+
+typedef char **libxl_key_value_list;
+void libxl_key_value_list_dispose(libxl_key_value_list *kvl);
+int libxl_key_value_list_length(const libxl_key_value_list *kvl);
+void libxl_key_value_list_copy(libxl_ctx *ctx,
+ libxl_key_value_list *dst,
+ const libxl_key_value_list *src);
+
+typedef uint32_t libxl_hwcap[8];
+void libxl_hwcap_copy(libxl_ctx *ctx, libxl_hwcap *dst, const libxl_hwcap *src);
+
+typedef uint64_t libxl_ev_user;
+
+typedef struct {
+ uint32_t size; /* number of bytes in map */
+ uint8_t *map;
+} libxl_bitmap;
+void libxl_bitmap_init(libxl_bitmap *map);
+void libxl_bitmap_dispose(libxl_bitmap *map);
+
+/*
+ * libxl_cpuid_policy is opaque in the libxl ABI. Users of both libxl and
+ * libxc may not make assumptions about xc_xend_cpuid.
+ */
+typedef struct xc_xend_cpuid libxl_cpuid_policy;
+typedef libxl_cpuid_policy * libxl_cpuid_policy_list;
+void libxl_cpuid_dispose(libxl_cpuid_policy_list *cpuid_list);
+int libxl_cpuid_policy_list_length(const libxl_cpuid_policy_list *l);
+void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
+ libxl_cpuid_policy_list *dst,
+ const libxl_cpuid_policy_list *src);
+
+#define LIBXL_PCI_FUNC_ALL (~0U)
+
+typedef uint32_t libxl_domid;
+typedef int libxl_devid;
+
+/*
+ * Formatting Enumerations.
+ *
+ * Each enumeration type libxl_E declares an associated lookup table
+ * libxl_E_string_table and a lookup function libxl_E_from_string.
+ */
+typedef struct {
+ const char *s;
+ int v;
+} libxl_enum_string_table;
+
+struct libxl_event;
+typedef LIBXL_TAILQ_ENTRY(struct libxl_event) libxl_ev_link;
+
+/*
+ * A boolean variable with an explicit default state.
+ *
+ * Users should treat this struct as opaque and use the following
+ * defined macros and accessor functions.
+ *
+ * To allow users of the library to naively select all defaults this
+ * state is represented as 0. False is < 0 and True is > 0.
+ */
+typedef struct {
+ int val;
+} libxl_defbool;
+
+void libxl_defbool_set(libxl_defbool *db, bool b);
+/* Resets to default */
+void libxl_defbool_unset(libxl_defbool *db);
+/* Sets db only if it is currently == default */
+void libxl_defbool_setdefault(libxl_defbool *db, bool b);
+bool libxl_defbool_is_default(libxl_defbool db);
+/* db must not be == default */
+bool libxl_defbool_val(libxl_defbool db);
+
+const char *libxl_defbool_to_string(libxl_defbool b);
+
+#define LIBXL_TIMER_MODE_DEFAULT -1
+#define LIBXL_MEMKB_DEFAULT ~0ULL
+
+/*
+ * We'd like to set a memory boundary to determine if we need to check
+ * any overlap with reserved device memory.
+ */
+#define LIBXL_RDM_MEM_BOUNDARY_MEMKB_DEFAULT (2048 * 1024)
+
+#define LIBXL_MS_VM_GENID_LEN 16
+typedef struct {
+ uint8_t bytes[LIBXL_MS_VM_GENID_LEN];
+} libxl_ms_vm_genid;
+
+#include "_libxl_types.h"
+
+const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx);
+
+/*
+ * Some libxl operations can take a long time. These functions take a
+ * parameter to control their concurrency:
+ * libxl_asyncop_how *ao_how
+ *
+ * If ao_how==NULL, the function will be synchronous.
+ *
+ * If ao_how!=NULL, the function will set the operation going, and if
+ * this is successful will return 0. In this case the zero error
+ * response does NOT mean that the operation was successful; it just
+ * means that it has been successfully started. It will finish later,
+ * perhaps with an error.
+ *
+ * If ao_how->callback!=NULL, the callback will be called when the
+ * operation completes. The same rules as for libxl_event_hooks
+ * apply, including the reentrancy rules and the possibility of
+ * "disaster", except that libxl calls ao_how->callback instead of
+ * libxl_event_hooks.event_occurs. (See libxl_event.h.)
+ *
+ * If ao_how->callback==NULL, a libxl_event will be generated which
+ * can be obtained from libxl_event_wait or libxl_event_check. The
+ * event will have type OPERATION_COMPLETE (which is not used
+ * elsewhere).
+ *
+ * Note that it is possible for an asynchronous operation which is to
+ * result in a callback to complete during its initiating function
+ * call. In this case the initiating function will return 0
+ * indicating the at the operation is "in progress", even though by
+ * the time it returns the operation is complete and the callback has
+ * already happened.
+ *
+ * The application must set and use ao_how->for_event (which will be
+ * copied into libxl_event.for_user) or ao_how->for_callback (passed
+ * to the callback) to determine which operation finished, and it must
+ * of course check the rc value for errors.
+ *
+ * *ao_how does not need to remain valid after the initiating function
+ * returns. All other parameters must remain valid for the lifetime of
+ * the asynchronous operation, unless otherwise specified.
+ *
+ * Callbacks may occur on any thread in which the application calls
+ * libxl.
+ */
+
+typedef struct {
+ void (*callback)(libxl_ctx *ctx, int rc, void *for_callback);
+ union {
+ libxl_ev_user for_event; /* used if callback==NULL */
+ void *for_callback; /* passed to callback */
+ } u;
+} libxl_asyncop_how;
+
+/*
+ * Some more complex asynchronous operations can report intermediate
+ * progress. How this is to be reported is controlled, for each
+ * function, by a parameter
+ * libxl_asyncprogress_how *aop_FOO_how;
+ * for each kind of progress FOO supported by that function. Each
+ * such kind of progress is associated with an event type.
+ *
+ * The function description will document whether, when, and how
+ * many times, the intermediate progress will be reported, and
+ * what the corresponding event type(s) are.
+ *
+ * If aop_FOO_how==NULL, intermediate progress reports are discarded.
+ *
+ * If aop_FOO_how->callback==NULL, intermediate progress reports
+ * generate libxl events which can be obtained from libxl_event_wait
+ * or libxl_event_check.
+ *
+ * If aop_FOO_how->callback!=NULL, libxl will report intermediate
+ * progress by calling callback(ctx, &event, for_callback).
+ *
+ * The rules for these events are otherwise the same as those for
+ * ordinary events. The reentrancy and threading rules for the
+ * callback are the same as those for ao completion callbacks.
+ *
+ * Note that the callback, if provided, is responsible for freeing
+ * the event.
+ *
+ * If callbacks are requested, they will be made, and returned, before
+ * the long-running libxl operation is considered finished (so if the
+ * long-running libxl operation was invoked with ao_how==NULL then any
+ * callbacks will occur strictly before the long-running operation
+ * returns). However, the callbacks may occur on any thread.
+ *
+ * In general, otherwise, no promises are made about the relative
+ * order of callbacks in a multithreaded program. In particular
+ * different callbacks relating to the same long-running operation may
+ * be delivered out of order.
+ */
+
+typedef struct {
+ void (*callback)(libxl_ctx *ctx, libxl_event*, void *for_callback);
+ libxl_ev_user for_event; /* always used */
+ void *for_callback; /* passed to callback */
+} libxl_asyncprogress_how;
+
+/*
+ * It is sometimes possible to abort an asynchronous operation.
+ *
+ * libxl_ao_abort searches for an ongoing asynchronous operation whose
+ * ao_how is identical to *how, and tries to abort it. The return
+ * values from libxl_ao_abort are as follows:
+ *
+ * 0
+ *
+ * The operation was found, and attempts are being made to cut it
+ * short. However, it may still take some time to stop. It is
+ * also possible that the operation will nevertheless complete
+ * successfully.
+ *
+ * ERROR_NOTFOUND
+ *
+ * No matching ongoing operation was found. This might happen
+ * for an actual operation if the operation has already completed
+ * (perhaps on another thread). The call to libxl_ao_abort has
+ * had no effect.
+ *
+ * ERROR_ABORTED
+ *
+ * The operation has already been the subject of at least one
+ * call to libxl_ao_abort.
+ *
+ * If the operation was indeed cut short due to the abort request, it
+ * will complete, at some point in the future, with ERROR_ABORTED. In
+ * that case, depending on the operation it have performed some of the
+ * work in question and left the operation half-done. Consult the
+ * documentation for individual operations.
+ *
+ * Note that an aborted operation might still fail for other reasons
+ * even after the abort was requested.
+ *
+ * If your application is multithreaded you must not reuse an
+ * ao_how->for_event or ao_how->for_callback value (with a particular
+ * ao_how->callback) unless you are sure that none of your other
+ * threads are going to abort the previous operation using that
+ * value; otherwise you risk aborting the wrong operation if the
+ * intended target of the abort request completes in the meantime.
+ *
+ * It is possible to abort even an operation which is being performed
+ * synchronously, but since in that case how==NULL you had better only
+ * have one such operation, because it is not possible to tell them
+ * apart (and libxl_ao_abort will abort only the first one it finds).
+ * (And, if you want to do this, obviously the abort would have to be
+ * requested on a different thread.)
+ */
+int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+
+#define LIBXL_VERSION 0
+
+/* context functions */
+int libxl_ctx_alloc(libxl_ctx **pctx, int version,
+ unsigned flags /* none currently defined */,
+ xentoollog_logger *lg);
+int libxl_ctx_free(libxl_ctx *ctx /* 0 is OK */);
+
+/* domain related functions */
+
+#define INVALID_DOMID ~0
+#define RANDOM_DOMID (INVALID_DOMID - 1)
+
+/* If the result is ERROR_ABORTED, the domain may or may not exist
+ * (in a half-created state). *domid will be valid and will be the
+ * domain id, or INVALID_DOMID, as appropriate */
+
+int libxl_domain_create_new(libxl_ctx *ctx, libxl_domain_config *d_config,
+ uint32_t *domid,
+ const libxl_asyncop_how *ao_how,
+ const libxl_asyncprogress_how *aop_console_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config,
+ uint32_t *domid, int restore_fd,
+ int send_back_fd,
+ const libxl_domain_restore_params *params,
+ const libxl_asyncop_how *ao_how,
+ const libxl_asyncprogress_how *aop_console_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040400
+
+static inline int libxl_domain_create_restore_0x040200(
+ libxl_ctx *ctx, libxl_domain_config *d_config,
+ uint32_t *domid, int restore_fd,
+ const libxl_asyncop_how *ao_how,
+ const libxl_asyncprogress_how *aop_console_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY
+{
+ libxl_domain_restore_params params;
+ int ret;
+
+ libxl_domain_restore_params_init(¶ms);
+
+ ret = libxl_domain_create_restore(
+ ctx, d_config, domid, restore_fd, -1, ¶ms, ao_how, aop_console_how);
+
+ libxl_domain_restore_params_dispose(¶ms);
+ return ret;
+}
+
+#define libxl_domain_create_restore libxl_domain_create_restore_0x040200
+
+#elif defined(LIBXL_API_VERSION) && LIBXL_API_VERSION >= 0x040400 \
+ && LIBXL_API_VERSION < 0x040700
+
+static inline int libxl_domain_create_restore_0x040400(
+ libxl_ctx *ctx, libxl_domain_config *d_config,
+ uint32_t *domid, int restore_fd,
+ const libxl_domain_restore_params *params,
+ const libxl_asyncop_how *ao_how,
+ const libxl_asyncprogress_how *aop_console_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY
+{
+ return libxl_domain_create_restore(ctx, d_config, domid, restore_fd,
+ -1, params, ao_how, aop_console_how);
+}
+
+#define libxl_domain_create_restore libxl_domain_create_restore_0x040400
+
+#endif
+
+int libxl_domain_soft_reset(libxl_ctx *ctx,
+ libxl_domain_config *d_config,
+ uint32_t domid,
+ const libxl_asyncop_how *ao_how,
+ const libxl_asyncprogress_how
+ *aop_console_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+ /* A progress report will be made via ao_console_how, of type
+ * domain_create_console_available, when the domain's primary
+ * console is available and can be connected to.
+ */
+
+void libxl_domain_config_init(libxl_domain_config *d_config);
+void libxl_domain_config_dispose(libxl_domain_config *d_config);
+
+/*
+ * Retrieve domain configuration and filled it in d_config. The
+ * returned configuration can be used to rebuild a domain. It only
+ * works with DomU.
+ */
+int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
+ libxl_domain_config *d_config,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+static inline int libxl_retrieve_domain_configuration_0x041200(
+ libxl_ctx *ctx, uint32_t domid, libxl_domain_config *d_config)
+{
+ return libxl_retrieve_domain_configuration(ctx, domid, d_config, NULL);
+}
+#define libxl_retrieve_domain_configuration \
+ libxl_retrieve_domain_configuration_0x041200
+#endif
+
+int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd,
+ int flags, /* LIBXL_SUSPEND_* */
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+#define LIBXL_SUSPEND_DEBUG 1
+#define LIBXL_SUSPEND_LIVE 2
+
+/*
+ * Only suspend domain, do not save its state to file, do not destroy it.
+ * Suspended domain can be resumed with libxl_domain_resume()
+ */
+int libxl_domain_suspend_only(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* @param suspend_cancel [from xenctrl.h:xc_domain_resume( @param fast )]
+ * If this parameter is true, use co-operative resume. The guest
+ * must support this.
+ */
+int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * This function doesn't return unless something has gone wrong with
+ * the replication to the secondary. If this function returns then the
+ * caller should resume the (primary) domain.
+ */
+int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
+ uint32_t domid, int send_fd, int recv_fd,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+static inline int libxl_domain_shutdown_0x041200(libxl_ctx *ctx,
+ uint32_t domid)
+{
+ return libxl_domain_shutdown(ctx, domid, NULL);
+}
+#define libxl_domain_shutdown libxl_domain_shutdown_0x041200
+static inline int libxl_domain_reboot_0x041200(libxl_ctx *ctx,
+ uint32_t domid)
+{
+ return libxl_domain_reboot(ctx, domid, NULL);
+}
+#define libxl_domain_reboot libxl_domain_reboot_0x041200
+#endif
+
+int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid, libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid);
+
+/* get max. number of cpus supported by hypervisor */
+int libxl_get_max_cpus(libxl_ctx *ctx);
+
+/* get the actual number of currently online cpus on the host */
+int libxl_get_online_cpus(libxl_ctx *ctx);
+ /* Beware that no locking or serialization is provided by libxl,
+ * so the information can be outdated as far as the function
+ * returns. If there are other entities in the system capable
+ * of onlining/offlining CPUs, it is up to the application
+ * to guarantee consistency, if that is important. */
+
+/* get max. number of NUMA nodes supported by hypervisor */
+int libxl_get_max_nodes(libxl_ctx *ctx);
+
+int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid,
+ const char *old_name, const char *new_name);
+
+ /* if old_name is NULL, any old name is OK; otherwise we check
+ * transactionally that the domain has the old old name; if
+ * trans is not 0 we use caller's transaction and caller must do retries */
+
+int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+static inline int libxl_domain_pause_0x041200(
+ libxl_ctx *ctx, uint32_t domid)
+{
+ return libxl_domain_pause(ctx, domid, NULL);
+}
+static inline int libxl_domain_unpause_0x041200(
+ libxl_ctx *ctx, uint32_t domid)
+{
+ return libxl_domain_unpause(ctx, domid, NULL);
+}
+#define libxl_domain_pause libxl_domain_pause_0x041200
+#define libxl_domain_unpause libxl_domain_unpause_0x041200
+#endif
+
+
+int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
+ const char *filename,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t target_memkb);
+int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid, int64_t target_memkb, int relative, int enforce);
+int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid, uint64_t *out_target);
+int libxl_get_memory_target_0x040700(libxl_ctx *ctx, uint32_t domid,
+ uint32_t *out_target)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * WARNING
+ * This memory management API is unstable even in Xen 4.2.
+ * It has a numer of deficiencies and we intend to replace it.
+ *
+ * The semantics of these functions should not be relied on to be very
+ * coherent or stable. We will however endeavour to keep working
+ * existing programs which use them in roughly the same way as libxl.
+ */
+/* how much free memory in the system a domain needs to be built */
+int libxl_domain_need_memory(libxl_ctx *ctx,
+ libxl_domain_config *config
+ /* ^ will be partially defaulted */,
+ uint32_t domid_for_logging /* INVALID_DOMID ok */,
+ uint64_t *need_memkb);
+int libxl_domain_need_memory_0x041200(libxl_ctx *ctx,
+ const libxl_domain_build_info *b_info_in,
+ uint64_t *need_memkb);
+int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
+ const libxl_domain_build_info *b_info_in,
+ uint32_t *need_memkb)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+/* how much free memory is available in the system */
+int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb);
+int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+/* wait for a given amount of memory to be free in the system */
+int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid, uint64_t memory_kb, int wait_secs);
+/*
+ * Wait for the memory target of a domain to be reached. Does not
+ * decrement wait_secs if the domain is making progress toward reaching
+ * the target. If the domain is not making progress, wait_secs is
+ * decremented. If the timeout expires before the target is reached, the
+ * function returns ERROR_FAIL.
+ *
+ * Older versions of this function (Xen 4.5 and older), decremented
+ * wait_secs even if the domain was making progress, resulting in far
+ * lower overall wait times. To make sure that your calling routine
+ * works with new and old implementations of the function, pass enough
+ * time for the guest to reach its target as an argument.
+ */
+int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs);
+
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040800
+#define libxl_get_memory_target libxl_get_memory_target_0x040700
+#define libxl_domain_need_memory libxl_domain_need_memory_0x040700
+#define libxl_get_free_memory libxl_get_free_memory_0x040700
+#elif defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+#define libxl_domain_need_memory libxl_domain_need_memory_0x041200
+#endif
+
+int libxl_vncviewer_exec(libxl_ctx *ctx, uint32_t domid, int autopass);
+
+/*
+ * If notify_fd is not -1, xenconsole will write 0x00 to it to nofity
+ * the caller that it has connected to the guest console.
+ */
+int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num,
+ libxl_console_type type, int notify_fd);
+/* libxl_primary_console_exec finds the domid and console number
+ * corresponding to the primary console of the given vm, then calls
+ * libxl_console_exec with the right arguments (domid might be different
+ * if the guest is using stubdoms).
+ * This function can be called after creating the device model, in
+ * case of HVM guests, and before libxl_run_bootloader in case of PV
+ * guests using pygrub.
+ * If notify_fd is not -1, xenconsole will write 0x00 to it to nofity
+ * the caller that it has connected to the guest console.
+ */
+int libxl_primary_console_exec(libxl_ctx *ctx, uint32_t domid_vm,
+ int notify_fd);
+
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040800
+
+static inline int libxl_console_exec_0x040700(libxl_ctx *ctx,
+ uint32_t domid, int cons_num,
+ libxl_console_type type)
+{
+ return libxl_console_exec(ctx, domid, cons_num, type, -1);
+}
+#define libxl_console_exec libxl_console_exec_0x040700
+
+static inline int libxl_primary_console_exec_0x040700(libxl_ctx *ctx,
+ uint32_t domid_vm)
+{
+ return libxl_primary_console_exec(ctx, domid_vm, -1);
+}
+#define libxl_primary_console_exec libxl_primary_console_exec_0x040700
+
+#endif
+
+/* libxl_console_get_tty retrieves the specified domain's console tty path
+ * and stores it in path. Caller is responsible for freeing the memory.
+ */
+int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num,
+ libxl_console_type type, char **path);
+
+/* libxl_primary_console_get_tty retrieves the specified domain's primary
+ * console tty path and stores it in path. Caller is responsible for freeing
+ * the memory.
+ */
+int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm, char **path);
+
+/* May be called with info_r == NULL to check for domain's existence.
+ * Returns ERROR_DOMAIN_NOTFOUND if domain does not exist (used to return
+ * ERROR_INVAL for this scenario). */
+int libxl_domain_info(libxl_ctx*, libxl_dominfo *info_r,
+ uint32_t domid);
+
+/* These functions each return (on success) an array of elements,
+ * and the length via the int* out parameter. These arrays and
+ * their contents come from malloc, and must be freed with the
+ * corresponding libxl_THING_list_free function.
+ */
+libxl_dominfo * libxl_list_domain(libxl_ctx*, int *nb_domain_out);
+void libxl_dominfo_list_free(libxl_dominfo *list, int nb_domain);
+
+libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx*, int *nb_pool_out);
+void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nb_pool);
+
+libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out);
+void libxl_vminfo_list_free(libxl_vminfo *list, int nb_vm);
+
+#define LIBXL_CPUTOPOLOGY_INVALID_ENTRY (~(uint32_t)0)
+libxl_cputopology *libxl_get_cpu_topology(libxl_ctx *ctx, int *nb_cpu_out);
+void libxl_cputopology_list_free(libxl_cputopology *, int nb_cpu);
+
+#define LIBXL_PCITOPOLOGY_INVALID_ENTRY (~(uint32_t)0)
+libxl_pcitopology *libxl_get_pci_topology(libxl_ctx *ctx, int *num_devs);
+void libxl_pcitopology_list_free(libxl_pcitopology *, int num_devs);
+
+#define LIBXL_NUMAINFO_INVALID_ENTRY (~(uint32_t)0)
+libxl_numainfo *libxl_get_numainfo(libxl_ctx *ctx, int *nr);
+void libxl_numainfo_list_free(libxl_numainfo *, int nr);
+
+libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
+ int *nb_vcpu, int *nr_cpus_out);
+void libxl_vcpuinfo_list_free(libxl_vcpuinfo *, int nr_vcpus);
+
+/*
+ * Devices
+ * =======
+ *
+ * Each device is represented by a libxl_device_<TYPE> data structure
+ * which is defined via the IDL. In addition some devices have an
+ * additional data type libxl_device_<TYPE>_getinfo which contains
+ * further runtime information about the device.
+ *
+ * In addition to the general methods available for libxl types (see
+ * "libxl types" above) a common set of methods are available for each
+ * device type. These are described below.
+ *
+ * Querying
+ * --------
+ *
+ * libxl_device_<type>_list(ctx, domid, nr):
+ *
+ * Returns an array of libxl_device_<type> length nr representing
+ * the devices attached to the specified domain.
+ *
+ * libxl_device_<type>_getinfo(ctx, domid, device, info):
+ *
+ * Initialises info with details of the given device which must be
+ * attached to the specified domain.
+ *
+ * Creation / Control
+ * ------------------
+ *
+ * libxl_device_<type>_add(ctx, domid, device):
+ *
+ * Adds the given device to the specified domain. This can be called
+ * while the guest is running (hotplug) or before boot (coldplug).
+ *
+ * This function only sets up the device but does not wait for the
+ * domain to connect to the device and therefore cannot block on the
+ * guest.
+ *
+ * device is an in/out parameter: fields left unspecified when the
+ * structure is passed in are filled in with appropriate values for
+ * the device created.
+ *
+ * libxl_device_<type>_destroy(ctx, domid, device):
+ *
+ * Removes the given device from the specified domain without guest
+ * co-operation. It is guest specific what affect this will have on
+ * a running guest.
+ *
+ * This function does not interact with the guest and therefore
+ * cannot block on the guest.
+ *
+ * libxl_device_<type>_remove(ctx, domid, device):
+ *
+ * Removes the given device from the specified domain by performing
+ * an orderly unplug with guest co-operation. This requires that the
+ * guest is running.
+ *
+ * This method is currently synchronous and therefore can block
+ * while interacting with the guest. There is a time-out of 10s on
+ * this interaction after which libxl_device_<type>_destroy()
+ * semantics apply.
+ *
+ * libxl_device_<type>_safe_remove(ctx, domid, device):
+ *
+ * This has the same semantics as libxl_device_<type>_remove() but,
+ * in the event of hitting the 10s time-out, this function will fail.
+ *
+ * Controllers
+ * -----------
+ *
+ * Most devices are treated individually. Some classes of device,
+ * however, like USB or SCSI, inherently have the need to have a
+ * hierarchy of different levels, with lower-level devices "attached"
+ * to higher-level ones. USB for instance has "controllers" at the
+ * top, which have buses, on which are devices, which consist of
+ * multiple interfaces. SCSI has "hosts" at the top, then buses,
+ * targets, and LUNs.
+ *
+ * In that case, for each <class>, there will be a set of functions
+ * and types for each <level>. For example, for <class>=usb, there
+ * may be <levels> ctrl (controller) and dev (device), with ctrl being
+ * level 0.
+ *
+ * libxl_device_<class><level0>_<function> will act more or
+ * less like top-level non-bus devices: they will either create or
+ * accept a libxl_devid which will be unique within the
+ * <class><level0> libxl_devid namespace.
+ *
+ * Lower-level devices must have a unique way to be identified. One
+ * way to do this would be to name it via the name of the next level
+ * up plus an index; for instance, <ctrl devid, port number>. Another
+ * way would be to have another devid namespace for that level. This
+ * identifier will be used for queries and removals.
+ *
+ * Lower-level devices will include in their
+ * libxl_device_<class><level> struct a field referring to the unique
+ * index of the level above. For instance, libxl_device_usbdev might
+ * contain the controller devid.
+ *
+ * In the case where there are multiple different ways to implement a
+ * given device -- for instance, one which is fully PV and one which
+ * uses an emulator -- the controller will contain a field which
+ * specifies what type of implementation is used. The implementations
+ * of individual devices will be known by the controller to which they
+ * are attached.
+ *
+ * If libxl_device_<class><level>_add receives an empty reference to
+ * the level above, it may return an error. Or it may (but is not
+ * required to) automatically choose a suitable device in the level
+ * above to which to attach the new device at this level. It may also
+ * (but is not required to) automatically create a new device at the
+ * level above if no suitable devices exist. Each class should
+ * document its behavior.
+ *
+ * libxl_device_<class><level>_list will list all devices of <class>
+ * at <level> in the domain. For example, libxl_device_usbctrl_list
+ * will list all usb controllers; libxl_class_usbdev_list will list
+ * all usb devices across all controllers.
+ *
+ * For each class, the domain config file will contain a single list
+ * for each level. libxl will first iterate through the list of
+ * top-level devices, then iterate through each level down in turn,
+ * adding devices to devices in the level above. For instance, there
+ * will be one list for all usb controllers, and one list for all usb
+ * devices.
+ *
+ * If libxl_device_<class><level>_add automatically creates
+ * higher-level devices as necessary, then it is permissible for the
+ * higher-level lists to be empty and the device list to have devices
+ * with the field containing a reference to the higher level device
+ * uninitialized.
+ */
+
+/* Disks */
+int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_disk *disk,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_disk_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_disk *disk,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_disk_destroy(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_disk *disk,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_disk_safe_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_disk *disk,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_disk *libxl_device_disk_list(libxl_ctx *ctx,
+ uint32_t domid, int *num)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+void libxl_device_disk_list_free(libxl_device_disk* list, int num)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_disk *disk, libxl_diskinfo *diskinfo)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * Insert a CD-ROM device. A device corresponding to disk must already
+ * be attached to the guest.
+ */
+int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * USB
+ *
+ * For each device removed or added, one of these protocols is available:
+ * - PV (i.e., PVUSB)
+ * - DEVICEMODEL (i.e, qemu)
+ *
+ * PV is available for either PV or HVM domains. DEVICEMODEL is only
+ * available for HVM domains. The caller can additionally specify
+ * "AUTO", in which case the library will try to determine the best
+ * protocol automatically.
+ *
+ * At the moment, the only protocol implemented is PV.
+ *
+ * One can add/remove USB controllers to/from guest, and attach/detach USB
+ * devices to/from USB controllers.
+ *
+ * To add USB controllers and USB devices, one can adding USB controllers
+ * first and then attaching USB devices to some USB controller, or adding
+ * USB devices to guest directly, it will automatically create a USB
+ * controller for USB devices to attach.
+ *
+ * To remove USB controllers or USB devices, one can remove USB devices
+ * under USB controller one by one and then remove USB controller, or
+ * remove USB controller directly, it will remove all USB devices under
+ * it automatically.
+ *
+ */
+/* USB Controllers*/
+int libxl_device_usbctrl_add(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_usbctrl *usbctrl,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_device_usbctrl_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_usbctrl *usbctrl,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_device_usbctrl_destroy(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_usbctrl *usbctrl,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_usbctrl *libxl_device_usbctrl_list(libxl_ctx *ctx,
+ uint32_t domid, int *num);
+
+void libxl_device_usbctrl_list_free(libxl_device_usbctrl *list, int nr);
+
+
+int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_usbctrl *usbctrl,
+ libxl_usbctrlinfo *usbctrlinfo);
+
+/* USB Devices */
+
+int libxl_device_usbdev_add(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_usbdev *usbdev,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_usbdev *usbdev,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_usbdev *
+libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num);
+
+void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr);
+
+/* Network Interfaces */
+int libxl_device_nic_add(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_nic_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_nic *nic,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_nic_destroy(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_nic *nic,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx,
+ uint32_t domid, int *num)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+void libxl_device_nic_list_free(libxl_device_nic* list, int num)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_nic *nic, libxl_nicinfo *nicinfo)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * Virtual Channels
+ * Channels manifest as consoles with names, see docs/misc/channels.txt
+ */
+libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx,
+ uint32_t domid,
+ int *num);
+int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_channel *channel,
+ libxl_channelinfo *channelinfo);
+
+/* Virtual TPMs */
+int libxl_device_vtpm_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vtpm_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_vtpm *vtpm,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vtpm_destroy(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_vtpm *vtpm,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_vtpm *libxl_device_vtpm_list(libxl_ctx *ctx,
+ uint32_t domid, int *num)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+void libxl_device_vtpm_list_free(libxl_device_vtpm*, int num)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vtpm_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_vtpm *vtpm, libxl_vtpminfo *vtpminfo)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* Virtual displays */
+int libxl_device_vdispl_add(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_vdispl *displ,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vdispl_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_vdispl *vdispl,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vdispl_destroy(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_vdispl *vdispl,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_vdispl *libxl_device_vdispl_list(libxl_ctx *ctx,
+ uint32_t domid, int *num)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+void libxl_device_vdispl_list_free(libxl_device_vdispl* list, int num)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vdispl_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_vdispl *vdispl,
+ libxl_vdisplinfo *vdisplinfo)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* Virtual sounds */
+int libxl_device_vsnd_add(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_vsnd *vsnd,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vsnd_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_vsnd *vsnd,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vsnd_destroy(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_vsnd *vsnd,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_vsnd *libxl_device_vsnd_list(libxl_ctx *ctx,
+ uint32_t domid, int *num)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+void libxl_device_vsnd_list_free(libxl_device_vsnd* list, int num)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vsnd_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_vsnd *vsnd,
+ libxl_vsndinfo *vsndlinfo)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* Keyboard */
+int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vkb_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_vkb *vkb,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vkb_destroy(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_vkb *vkb,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_vkb *libxl_device_vkb_list(libxl_ctx *ctx,
+ uint32_t domid, int *num)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+void libxl_device_vkb_list_free(libxl_device_vkb* list, int num)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vkb_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_vkb *vkb,
+ libxl_vkbinfo *vkbinfo)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* Framebuffer */
+int libxl_device_vfb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vfb_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_vfb *vfb,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vfb_destroy(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_vfb *vfb,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* 9pfs */
+int libxl_device_p9_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_p9 *p9,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_p9_destroy(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_p9 *p9,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* pvcalls interface */
+int libxl_device_pvcallsif_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_pvcallsif *pvcallsif,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_pvcallsif_destroy(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_pvcallsif *pvcallsif,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* PCI Passthrough */
+int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_pci *pcidev,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_pci *pcidev,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_pci_destroy(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_pci *pcidev,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_pci *libxl_device_pci_list(libxl_ctx *ctx, uint32_t domid,
+ int *num);
+
+/*
+ * Turns the current process into a backend device service daemon
+ * for a driver domain.
+ *
+ * From a libxl API point of view, this starts a long-running
+ * operation. That operation consists of "being a driver domain"
+ * and never completes.
+ *
+ * Attempting to abort this operation is not advisable; proper
+ * shutdown of the driver domain task is not supported.
+ */
+int libxl_device_events_handler(libxl_ctx *ctx,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * Functions related to making devices assignable -- that is, bound to
+ * the pciback driver, ready to be given to a guest via
+ * libxl_pci_device_add.
+ *
+ * - ..._add() will unbind the device from its current driver (if
+ * already bound) and re-bind it to pciback; at that point it will be
+ * ready to be assigned to a VM. If rebind is set, it will store the
+ * path to the old driver in xenstore so that it can be handed back to
+ * dom0 on restore.
+ *
+ * - ..._remove() will unbind the device from pciback, and if
+ * rebind is non-zero, attempt to assign it back to the driver
+ * from whence it came.
+ *
+ * - ..._list() will return a list of the PCI devices available to be
+ * assigned.
+ *
+ * add and remove are idempotent: if the device in question is already
+ * added or is not bound, the functions will emit a warning but return
+ * SUCCESS.
+ */
+int libxl_device_pci_assignable_add(libxl_ctx *ctx, libxl_device_pci *pcidev, int rebind);
+int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci *pcidev, int rebind);
+libxl_device_pci *libxl_device_pci_assignable_list(libxl_ctx *ctx, int *num);
+
+/* CPUID handling */
+int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str);
+int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid,
+ const char* str);
+#if LIBXL_API_VERSION < 0x041400
+/*
+ * Dropped from the API in Xen 4.14. At the time of writing, these functions
+ * don't appear to ever have had external callers.
+ *
+ * These have always been used internally during domain construction, and
+ * can't easily be used externally because of their implicit parameters in
+ * other pieces of global state.
+ *
+ * Furthermore, an API user can't usefully determine whether they get
+ * libxl_cpuid (the real implementation) or libxl_nocpuid (no-op stubs).
+ *
+ * The internal behaviour of these functions also needs to change. Therefore
+ * for simplicitly, provide the no-op stubs. Yes technically this is an API
+ * change in some cases for existing software, but there is 0 of that in
+ * practice.
+ */
+static inline void libxl_cpuid_apply_policy(libxl_ctx *ctx __attribute__((unused)),
+ uint32_t domid __attribute__((unused)))
+{}
+static inline void libxl_cpuid_set(libxl_ctx *ctx __attribute__((unused)),
+ uint32_t domid __attribute__((unused)),
+ libxl_cpuid_policy_list cpuid __attribute__((unused)))
+{}
+#endif
+
+/*
+ * Functions for allowing users of libxl to store private data
+ * relating to a domain. The data is an opaque sequence of bytes and
+ * is not interpreted or used by libxl.
+ *
+ * Data is indexed by the userdata userid, which is a short printable
+ * ASCII string. The following list is a registry of userdata userids
+ * (the registry may be updated by posting a patch to xen-devel):
+ *
+ * userid Data contents
+ * "xl" domain config file in xl format, Unix line endings
+ * "libvirt-xml" domain config file in libvirt XML format. See
+ * http://libvirt.org/formatdomain.html
+ * "domain-userdata-lock" lock file to protect domain userdata in libxl.
+ * It's a per-domain lock. Applications should
+ * not touch this file.
+ * "libxl-json" libxl_domain_config object in JSON format, generated
+ * by libxl. Applications should not access this file
+ * directly. This file is protected by domain-userdata-lock
+ * for against Read-Modify-Write operation and domain
+ * destruction.
+ *
+ * libxl does not enforce the registration of userdata userids or the
+ * semantics of the data. For specifications of the data formats
+ * see the code or documentation for the libxl caller in question.
+ */
+int libxl_userdata_store(libxl_ctx *ctx, uint32_t domid,
+ const char *userdata_userid,
+ const uint8_t *data, int datalen)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+ /* If datalen==0, data is not used and the user data for
+ * that domain and userdata_userid is deleted. */
+int libxl_userdata_retrieve(libxl_ctx *ctx, uint32_t domid,
+ const char *userdata_userid,
+ uint8_t **data_r, int *datalen_r)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+ /* On successful return, *data_r is from malloc.
+ * If there is no data for that domain and userdata_userid,
+ * *data_r and *datalen_r will be set to 0.
+ * data_r and datalen_r may be 0.
+ * On error return, *data_r and *datalen_r are undefined.
+ */
+int libxl_userdata_unlink(libxl_ctx *ctx, uint32_t domid,
+ const char *userdata_userid);
+
+
+int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo);
+int libxl_set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid,
+ const libxl_bitmap *cpumap_hard,
+ const libxl_bitmap *cpumap_soft);
+int libxl_set_vcpuaffinity_force(libxl_ctx *ctx, uint32_t domid,
+ uint32_t vcpuid,
+ const libxl_bitmap *cpumap_hard,
+ const libxl_bitmap *cpumap_soft);
+int libxl_set_vcpuaffinity_all(libxl_ctx *ctx, uint32_t domid,
+ unsigned int max_vcpus,
+ const libxl_bitmap *cpumap_hard,
+ const libxl_bitmap *cpumap_soft);
+
+#if defined (LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040500
+
+#define libxl_set_vcpuaffinity(ctx, domid, vcpuid, map) \
+ libxl_set_vcpuaffinity((ctx), (domid), (vcpuid), (map), NULL)
+#define libxl_set_vcpuaffinity_all(ctx, domid, max_vcpus, map) \
+ libxl_set_vcpuaffinity_all((ctx), (domid), (max_vcpus), (map), NULL)
+
+#endif
+
+int libxl_domain_set_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
+ libxl_bitmap *nodemap);
+int libxl_domain_get_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
+ libxl_bitmap *nodemap);
+int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid,
+ libxl_bitmap *cpumap,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+static inline int libxl_set_vcpuonline_0x041200(libxl_ctx *ctx,
+ uint32_t domid,
+ libxl_bitmap *cpumap)
+{
+ return libxl_set_vcpuonline(ctx, domid, cpumap, NULL);
+}
+#define libxl_set_vcpuonline libxl_set_vcpuonline_0x041200
+#endif
+
+/* A return value less than 0 should be interpreted as a libxl_error, while a
+ * return value greater than or equal to 0 should be interpreted as a
+ * libxl_scheduler. */
+int libxl_get_scheduler(libxl_ctx *ctx);
+
+/* Per-scheduler parameters */
+int libxl_sched_credit_params_get(libxl_ctx *ctx, uint32_t poolid,
+ libxl_sched_credit_params *scinfo);
+int libxl_sched_credit_params_set(libxl_ctx *ctx, uint32_t poolid,
+ libxl_sched_credit_params *scinfo);
+int libxl_sched_credit2_params_get(libxl_ctx *ctx, uint32_t poolid,
+ libxl_sched_credit2_params *scinfo);
+int libxl_sched_credit2_params_set(libxl_ctx *ctx, uint32_t poolid,
+ libxl_sched_credit2_params *scinfo);
+
+/* Scheduler Per-domain parameters */
+
+#define LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT -1
+#define LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT -1
+#define LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT -1
+#define LIBXL_DOMAIN_SCHED_PARAM_SLICE_DEFAULT -1
+#define LIBXL_DOMAIN_SCHED_PARAM_LATENCY_DEFAULT -1
+#define LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT -1
+#define LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT -1
+
+/* Per-VCPU parameters */
+#define LIBXL_SCHED_PARAM_VCPU_INDEX_DEFAULT -1
+
+/* Get the per-domain scheduling parameters.
+ * For schedulers that support per-vcpu settings (e.g., RTDS),
+ * calling *_domain_get functions will get default scheduling
+ * parameters.
+ */
+int libxl_domain_sched_params_get(libxl_ctx *ctx, uint32_t domid,
+ libxl_domain_sched_params *params);
+
+/* Set the per-domain scheduling parameters.
+ * For schedulers that support per-vcpu settings (e.g., RTDS),
+ * calling *_domain_set functions will set all vcpus with the same
+ * scheduling parameters.
+ */
+int libxl_domain_sched_params_set(libxl_ctx *ctx, uint32_t domid,
+ const libxl_domain_sched_params *params);
+
+/* Get the per-vcpu scheduling parameters */
+int libxl_vcpu_sched_params_get(libxl_ctx *ctx, uint32_t domid,
+ libxl_vcpu_sched_params *params);
+
+/* Get the per-vcpu scheduling parameters of all vcpus of a domain */
+int libxl_vcpu_sched_params_get_all(libxl_ctx *ctx, uint32_t domid,
+ libxl_vcpu_sched_params *params);
+
+/* Set the per-vcpu scheduling parameters */
+int libxl_vcpu_sched_params_set(libxl_ctx *ctx, uint32_t domid,
+ const libxl_vcpu_sched_params *params);
+
+/* Set the per-vcpu scheduling parameters of all vcpus of a domain */
+int libxl_vcpu_sched_params_set_all(libxl_ctx *ctx, uint32_t domid,
+ const libxl_vcpu_sched_params *params);
+
+int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid,
+ libxl_trigger trigger, uint32_t vcpuid,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+static inline int libxl_send_trigger_0x041200(
+ libxl_ctx *ctx, uint32_t domid, libxl_trigger trigger, uint32_t vcpuid)
+{
+ return libxl_send_trigger(ctx, domid, trigger, vcpuid, NULL);
+}
+#define libxl_send_trigger libxl_send_trigger_0x041200
+#endif
+int libxl_send_sysrq(libxl_ctx *ctx, uint32_t domid, char sysrq);
+int libxl_send_debug_keys(libxl_ctx *ctx, char *keys);
+int libxl_set_parameters(libxl_ctx *ctx, char *params);
+
+typedef struct libxl__xen_console_reader libxl_xen_console_reader;
+
+libxl_xen_console_reader *
+ libxl_xen_console_read_start(libxl_ctx *ctx, int clear);
+int libxl_xen_console_read_line(libxl_ctx *ctx,
+ libxl_xen_console_reader *cr,
+ char **line_r);
+void libxl_xen_console_read_finish(libxl_ctx *ctx,
+ libxl_xen_console_reader *cr);
+
+uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid);
+
+char *libxl_tmem_list(libxl_ctx *ctx, uint32_t domid, int use_long);
+int libxl_tmem_freeze(libxl_ctx *ctx, uint32_t domid);
+int libxl_tmem_thaw(libxl_ctx *ctx, uint32_t domid);
+int libxl_tmem_set(libxl_ctx *ctx, uint32_t domid, char* name,
+ uint32_t set);
+int libxl_tmem_shared_auth(libxl_ctx *ctx, uint32_t domid, char* uuid,
+ int auth);
+int libxl_tmem_freeable(libxl_ctx *ctx);
+
+int libxl_get_freecpus(libxl_ctx *ctx, libxl_bitmap *cpumap);
+
+/*
+ * Set poolid to LIBXL_CPUOOL_POOLID_ANY to have Xen choose a
+ * free poolid for you.
+ */
+#define LIBXL_CPUPOOL_POOLID_ANY 0xFFFFFFFF
+int libxl_cpupool_create(libxl_ctx *ctx, const char *name,
+ libxl_scheduler sched,
+ libxl_bitmap cpumap, libxl_uuid *uuid,
+ uint32_t *poolid);
+int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid);
+int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid);
+int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu);
+int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus);
+int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid,
+ const libxl_bitmap *cpumap);
+int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu);
+int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus);
+int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid,
+ const libxl_bitmap *cpumap);
+int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid);
+int libxl_cpupool_info(libxl_ctx *ctx, libxl_cpupoolinfo *info, uint32_t poolid);
+
+int libxl_domid_valid_guest(uint32_t domid);
+
+int libxl_flask_context_to_sid(libxl_ctx *ctx, char *buf, size_t len,
+ uint32_t *ssidref);
+int libxl_flask_sid_to_context(libxl_ctx *ctx, uint32_t ssidref, char **buf,
+ size_t *len);
+int libxl_flask_getenforce(libxl_ctx *ctx);
+int libxl_flask_setenforce(libxl_ctx *ctx, int mode);
+int libxl_flask_loadpolicy(libxl_ctx *ctx, void *policy, uint32_t size);
+
+int libxl_ms_vm_genid_generate(libxl_ctx *ctx, libxl_ms_vm_genid *id);
+bool libxl_ms_vm_genid_is_zero(const libxl_ms_vm_genid *id);
+void libxl_ms_vm_genid_copy(libxl_ctx *ctx, libxl_ms_vm_genid *dst,
+ const libxl_ms_vm_genid *src);
+
+#if defined(__i386__) || defined(__x86_64__)
+int libxl_psr_cmt_attach(libxl_ctx *ctx, uint32_t domid);
+int libxl_psr_cmt_detach(libxl_ctx *ctx, uint32_t domid);
+int libxl_psr_cmt_domain_attached(libxl_ctx *ctx, uint32_t domid);
+int libxl_psr_cmt_enabled(libxl_ctx *ctx);
+int libxl_psr_cmt_get_total_rmid(libxl_ctx *ctx, uint32_t *total_rmid);
+int libxl_psr_cmt_get_l3_cache_size(libxl_ctx *ctx,
+ uint32_t socketid,
+ uint32_t *l3_cache_size);
+int libxl_psr_cmt_get_cache_occupancy(libxl_ctx *ctx,
+ uint32_t domid,
+ uint32_t socketid,
+ uint32_t *l3_cache_occupancy);
+
+int libxl_psr_cmt_type_supported(libxl_ctx *ctx, libxl_psr_cmt_type type);
+int libxl_psr_cmt_get_sample(libxl_ctx *ctx,
+ uint32_t domid,
+ libxl_psr_cmt_type type,
+ uint64_t scope,
+ uint64_t *sample_r,
+ uint64_t *tsc_r);
+
+/*
+ * Function to set a domain's cbm. It operates on a single or multiple
+ * target(s) defined in 'target_map'. The definition of 'target_map' is
+ * related to 'type':
+ * 'L3_CBM': 'target_map' specifies all the sockets to be operated on.
+ */
+int libxl_psr_cat_set_cbm(libxl_ctx *ctx, uint32_t domid,
+ libxl_psr_cbm_type type, libxl_bitmap *target_map,
+ uint64_t cbm);
+/*
+ * Function to get a domain's cbm. It operates on a single 'target'.
+ * The definition of 'target' is related to 'type':
+ * 'L3_CBM': 'target' specifies which socket to be operated on.
+ */
+int libxl_psr_cat_get_cbm(libxl_ctx *ctx, uint32_t domid,
+ libxl_psr_cbm_type type, uint32_t target,
+ uint64_t *cbm_r);
+
+/*
+ * On success, the function returns an array of elements in 'info',
+ * and the length in 'nr'.
+ */
+int libxl_psr_cat_get_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
+ unsigned int *nr, unsigned int lvl);
+int libxl_psr_cat_get_l3_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
+ int *nr);
+void libxl_psr_cat_info_list_free(libxl_psr_cat_info *list, int nr);
+
+typedef enum libxl_psr_cbm_type libxl_psr_type;
+
+/*
+ * Function to set a domain's value. It operates on a single or multiple
+ * target(s) defined in 'target_map'. 'target_map' specifies all the sockets
+ * to be operated on.
+ */
+int libxl_psr_set_val(libxl_ctx *ctx, uint32_t domid,
+ libxl_psr_type type, libxl_bitmap *target_map,
+ uint64_t val);
+/*
+ * Function to get a domain's cbm. It operates on a single 'target'.
+ * 'target' specifies which socket to be operated on.
+ */
+int libxl_psr_get_val(libxl_ctx *ctx, uint32_t domid,
+ libxl_psr_type type, unsigned int target,
+ uint64_t *val);
+/*
+ * On success, the function returns an array of elements in 'info',
+ * and the length in 'nr'.
+ */
+int libxl_psr_get_hw_info(libxl_ctx *ctx, libxl_psr_feat_type type,
+ unsigned int lvl, unsigned int *nr,
+ libxl_psr_hw_info **info);
+void libxl_psr_hw_info_list_free(libxl_psr_hw_info *list, unsigned int nr);
+#endif
+
+/* misc */
+
+/* Each of these sets or clears the flag according to whether the
+ * 2nd parameter is nonzero. On failure, they log, and
+ * return ERROR_FAIL, but also leave errno valid. */
+int libxl_fd_set_cloexec(libxl_ctx *ctx, int fd, int cloexec);
+int libxl_fd_set_nonblock(libxl_ctx *ctx, int fd, int nonblock);
+
+/*
+ * Issue a qmp monitor command to the device model of the specified domain.
+ * The function returns the output of the command in a new allocated buffer
+ * via output.
+ */
+int libxl_qemu_monitor_command(libxl_ctx *ctx, uint32_t domid,
+ const char *command_line, char **output,
+ const libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+static inline int libxl_qemu_monitor_command_0x041200(libxl_ctx *ctx,
+ uint32_t domid, const char *command_line, char **output)
+{
+ return libxl_qemu_monitor_command(ctx, domid, command_line, output,
+ NULL);
+}
+#define libxl_qemu_monitor_command libxl_qemu_monitor_command_0x041200
+#endif
+
+#include <libxl_event.h>
+
+/*
+ * This function is for use only during host initialisation. If it is
+ * invoked on a host with running domains, or concurrent libxl
+ * processes then the system may malfuntion.
+ */
+int libxl_clear_domid_history(libxl_ctx *ctx);
+
+#endif /* LIBXL_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2011 Citrix Ltd.
+ * Author Ian Jackson <ian.jackson@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#ifndef LIBXL_EVENT_H
+#define LIBXL_EVENT_H
+
+#include <libxl.h>
+#include <poll.h>
+#include <sys/time.h>
+
+/*======================================================================*/
+
+/*
+ * Domain event handling - getting Xen events from libxl
+ *
+ * (Callers inside libxl may not call libxl_event_check or _wait.)
+ */
+
+#define LIBXL_EVENTMASK_ALL (~(unsigned long)0)
+
+typedef int libxl_event_predicate(const libxl_event*, void *user);
+ /* Return value is 0 if the event is unwanted or non-0 if it is.
+ * Predicates are not allowed to fail.
+ */
+
+int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r,
+ uint64_t typemask,
+ libxl_event_predicate *predicate, void *predicate_user)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+ /* Searches for an event, already-happened, which matches typemask
+ * and predicate. predicate==0 matches any event.
+ * libxl_event_check returns the event, which must then later be
+ * freed by the caller using libxl_event_free.
+ *
+ * Returns ERROR_NOT_READY if no such event has happened.
+ */
+
+int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r,
+ uint64_t typemask,
+ libxl_event_predicate *predicate, void *predicate_user)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+ /* Like libxl_event_check but blocks if no suitable events are
+ * available, until some are. Uses libxl_osevent_beforepoll/
+ * _afterpoll so may be inefficient if very many domains are being
+ * handled by a single program.
+ */
+
+void libxl_event_free(libxl_ctx *ctx, libxl_event *event);
+
+
+/* Alternatively or additionally, the application may also use this: */
+
+typedef struct libxl_event_hooks {
+ uint64_t event_occurs_mask;
+ void (*event_occurs)(void *user,
+#ifndef LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG
+ const
+#endif
+ libxl_event *event);
+ void (*disaster)(void *user, libxl_event_type type,
+ const char *msg, int errnoval);
+} libxl_event_hooks;
+
+void libxl_event_register_callbacks(libxl_ctx *ctx,
+ const libxl_event_hooks *hooks, void *user);
+ /*
+ * Arranges that libxl will henceforth call event_occurs for any
+ * events whose type is set in event_occurs_mask, rather than
+ * queueing the event for retrieval by libxl_event_check/wait.
+ * Events whose bit is clear in mask are not affected.
+ *
+ * event becomes owned by the application and must be freed, either
+ * by event_occurs or later.
+ *
+ * event_occurs may be NULL if mask is 0.
+ *
+ * libxl_event_register_callback also provides a way for libxl to
+ * report to the application that there was a problem reporting
+ * events; this can occur due to lack of host memory during event
+ * handling, or other wholly unrecoverable errors from system calls
+ * made by libxl. This will not happen for frivolous reasons - only
+ * if the system, or the Xen components of it, are badly broken.
+ *
+ * msg and errnoval will describe the action that libxl was trying
+ * to do, and type specifies the type of libxl events which may be
+ * missing. type may be 0 in which case events of all types may be
+ * missing.
+ *
+ * disaster may be NULL. If it is, or if _register_callbacks has
+ * not been called, errors of this kind are fatal to the entire
+ * application: libxl will print messages to its logs and to stderr
+ * and call exit(-1).
+ *
+ * If disaster returns, it may be the case that some or all future
+ * libxl calls will return errors; likewise it may be the case that
+ * no more events (of the specified type, if applicable) can be
+ * produced. An application which supplies a disaster function
+ * should normally react either by exiting, or by (when it has
+ * returned to its main event loop) shutting down libxl with
+ * libxl_ctx_free and perhaps trying to restart it with
+ * libxl_ctx_init.
+ *
+ * In any case before calling disaster, libxl will have logged a
+ * message with level XTL_CRITICAL.
+ *
+ * Reentrancy: it IS permitted to call libxl from within
+ * event_occurs. It is NOT permitted to call libxl from within
+ * disaster. The event_occurs and disaster callbacks may occur on
+ * any thread in which the application calls libxl.
+ *
+ * libxl_event_register_callbacks may be called as many times, with
+ * different parameters, as the application likes; the most recent
+ * call determines the libxl behaviour. However it is NOT safe to
+ * call _register_callbacks concurrently with, or reentrantly from,
+ * any other libxl function, nor while any event-generation
+ * facilities are enabled.
+ */
+
+
+/*
+ * Events are only generated if they have been requested.
+ * The following functions request the generation of specific events.
+ *
+ * Each set of functions for controlling event generation has this form:
+ *
+ * typedef struct libxl__evgen_FOO libxl__evgen_FOO;
+ * int libxl_evenable_FOO(libxl_ctx *ctx, FURTHER PARAMETERS,
+ * libxl_ev_user user, libxl__evgen_FOO **evgen_out);
+ * void libxl_evdisable_FOO(libxl_ctx *ctx, libxl__evgen_FOO *evgen);
+ *
+ * The evenable function arranges that the events (as described in the
+ * doc comment for the individual function) will start to be generated
+ * by libxl. On success, *evgen_out is set to a non-null pointer to
+ * an opaque struct.
+ *
+ * The user value is returned in the generated events and may be
+ * used by the caller for whatever it likes. The type ev_user is
+ * guaranteed to be an unsigned integer type which is at least
+ * as big as uint64_t and is also guaranteed to be big enough to
+ * contain any intptr_t value.
+ *
+ * If it becomes desirable to stop generation of the relevant events,
+ * or to reclaim the resources in libxl associated with the evgen
+ * structure, the same evgen value should be passed to the evdisable
+ * function. However, note that events which occurred prior to the
+ * evdisable call may still be returned.
+ *
+ * The caller may enable identical events more than once. If they do
+ * so, each actual occurrence will generate several events to be
+ * returned by libxl_event_check, with the appropriate user value(s).
+ * Aside from this, each occurrence of each event is returned by
+ * libxl_event_check exactly once.
+ *
+ * An evgen is associated with the libxl_ctx used for its creation.
+ * After libxl_ctx_free, all corresponding evgen handles become
+ * invalid and must no longer be passed to evdisable.
+ *
+ * Applications should ensure that they eventually retrieve every
+ * event using libxl_event_check or libxl_event_wait, since events
+ * which occur but are not retrieved by the application will be queued
+ * inside libxl indefinitely. libxl_event_check/_wait may be O(n)
+ * where n is the number of queued events which do not match the
+ * criteria specified in the arguments to check/wait.
+ */
+
+typedef struct libxl__evgen_domain_death libxl_evgen_domain_death;
+int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
+ libxl_ev_user, libxl_evgen_domain_death **evgen_out);
+void libxl_evdisable_domain_death(libxl_ctx *ctx, libxl_evgen_domain_death*);
+ /* Arranges for the generation of DOMAIN_SHUTDOWN and DOMAIN_DEATH
+ * events. A domain which is destroyed before it shuts down
+ * may generate only a DEATH event.
+ */
+
+typedef struct libxl__evgen_disk_eject libxl_evgen_disk_eject;
+int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t domid, const char *vdev,
+ libxl_ev_user, libxl_evgen_disk_eject **evgen_out);
+void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject*);
+ /* Arranges for the generation of DISK_EJECT events. A copy of the
+ * string *vdev will be made for libxl's internal use, and a pointer
+ * to this (or some other) copy will be returned as the vdev
+ * member of event.u.
+ */
+
+
+/*======================================================================*/
+
+/*
+ * OS event handling - passing low-level OS events to libxl
+ *
+ * Event-driven programs must use these facilities to allow libxl
+ * to become aware of readability/writeability of file descriptors
+ * and the occurrence of timeouts.
+ *
+ * There are two approaches available. The first is appropriate for
+ * simple programs handling reasonably small numbers of domains:
+ *
+ * for (;;) {
+ * libxl_osevent_beforepoll(...)
+ * poll();
+ * libxl_osevent_afterpoll(...);
+ * for (;;) {
+ * r = libxl_event_check(...);
+ * if (r==ERROR_NOT_READY) break;
+ * if (r) goto error_out;
+ * do something with the event;
+ * }
+ * }
+ *
+ * The second approach uses libxl_osevent_register_hooks and is
+ * suitable for programs which are already using a callback-based
+ * event library.
+ *
+ * An application may freely mix the two styles of interaction.
+ *
+ * (Callers inside libxl may not call libxl_osevent_... functions.)
+ */
+
+struct pollfd;
+
+/* The caller should provide beforepoll with some space for libxl's
+ * fds, and tell libxl how much space is available by setting *nfds_io.
+ * fds points to the start of this space (and fds may be a pointer into
+ * a larger array, for example, if the application has some fds of
+ * its own that it is interested in).
+ *
+ * On return *nfds_io will in any case have been updated by libxl
+ * according to how many fds libxl wants to poll on.
+ *
+ * If the space was sufficient, libxl fills in fds[0..<new
+ * *nfds_io>] suitably for poll(2), updates *timeout_upd if needed,
+ * and returns ok.
+ *
+ * If space was insufficient, fds[0..<old *nfds_io>] is undefined on
+ * return; *nfds_io on return will be greater than the value on
+ * entry; *timeout_upd may or may not have been updated; and
+ * libxl_osevent_beforepoll returns ERROR_BUFERFULL. In this case
+ * the application needs to make more space (enough space for
+ * *nfds_io struct pollfd) and then call beforepoll again, before
+ * entering poll(2). Typically this will involve calling realloc.
+ *
+ * The application may call beforepoll with fds==NULL and
+ * *nfds_io==0 in order to find out how much space is needed.
+ *
+ * *timeout_upd is as for poll(2): it's in milliseconds, and
+ * negative values mean no timeout (infinity).
+ * libxl_osevent_beforepoll will only reduce the timeout, naturally.
+ */
+int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
+ struct pollfd *fds, int *timeout_upd,
+ struct timeval now)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* nfds and fds[0..nfds] must be from the most recent call to
+ * _beforepoll, as modified by poll. (It is therefore not possible
+ * to have multiple threads simultaneously polling using this
+ * interface.)
+ *
+ * This function actually performs all of the IO and other actions,
+ * and generates events (libxl_event), which are implied by either
+ * (a) the time of day or (b) both (i) the returned information from
+ * _beforepoll, and (ii) the results from poll specified in
+ * fds[0..nfds-1]. Generated events can then be retrieved by
+ * libxl_event_check.
+ */
+void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds,
+ struct timeval now)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+
+typedef struct libxl_osevent_hooks {
+ int (*fd_register)(void *user, int fd, void **for_app_registration_out,
+ short events, void *for_libxl);
+ int (*fd_modify)(void *user, int fd, void **for_app_registration_update,
+ short events);
+ void (*fd_deregister)(void *user, int fd, void *for_app_registration);
+ int (*timeout_register)(void *user, void **for_app_registration_out,
+ struct timeval abs, void *for_libxl);
+ int (*timeout_modify)(void *user, void **for_app_registration_update,
+ struct timeval abs)
+ /* only ever called with abs={0,0}, meaning ASAP */;
+ void (*timeout_deregister)(void *user, void *for_app_registration)
+ /* will never be called */;
+} libxl_osevent_hooks;
+
+/* The application which calls register_fd_hooks promises to
+ * maintain a register of fds and timeouts that libxl is interested
+ * in, and make calls into libxl (libxl_osevent_occurred_*)
+ * when those fd events and timeouts occur. This is more efficient
+ * than _beforepoll/_afterpoll if there are many fds (which can
+ * happen if the same libxl application is managing many domains).
+ *
+ * For an fd event, events is as for poll(). register or modify may
+ * be called with events==0, in which case it must still work
+ * normally, just not generate any events.
+ *
+ * For a timeout event, milliseconds is as for poll().
+ * Specifically, negative values of milliseconds mean NO TIMEOUT.
+ * This is used by libxl to temporarily disable a timeout.
+ *
+ * If the register or modify hook succeeds it may update
+ * *for_app_registration_out/_update and must then return 0.
+ * On entry to register, *for_app_registration_out is always NULL.
+ *
+ * A registration or modification hook may fail, in which case it
+ * must leave the registration state of the fd or timeout unchanged.
+ * It may then either return ERROR_OSEVENT_REG_FAIL or any positive
+ * int. The value returned will be passed up through libxl and
+ * eventually returned back to the application. When register
+ * fails, any value stored into *for_registration_out is ignored by
+ * libxl; when modify fails, any changed value stored into
+ * *for_registration_update is honoured by libxl and will be passed
+ * to future modify or deregister calls.
+ *
+ * libxl may want to register more than one callback for any one fd;
+ * in that case: (i) each such registration will have at least one bit
+ * set in revents which is unique to that registration; (ii) if an
+ * event occurs which is relevant for multiple registrations the
+ * application's event system may call libxl_osevent_occurred_fd
+ * for one, some, or all of those registrations.
+ *
+ * If fd_modify is used, it is permitted for the application's event
+ * system to still make calls to libxl_osevent_occurred_fd for the
+ * "old" set of requested events; these will be safely ignored by
+ * libxl.
+ *
+ * libxl will remember the value stored in *for_app_registration_out
+ * (or *for_app_registration_update) by a successful call to
+ * register (or modify), and pass it to subsequent calls to modify
+ * or deregister.
+ *
+ * Note that the application must cope with a call from libxl to
+ * timeout_modify racing with its own call to
+ * libxl__osevent_occurred_timeout. libxl guarantees that
+ * timeout_modify will only be called with abs={0,0} but the
+ * application must still ensure that libxl's attempt to cause the
+ * timeout to occur immediately is safely ignored even the timeout is
+ * actually already in the process of occurring.
+ *
+ * timeout_deregister is not used because it forms part of a
+ * deprecated unsafe mode of use of the API.
+ *
+ * osevent_register_hooks may be called only once for each libxl_ctx.
+ * libxl may make calls to register/modify/deregister from within
+ * any libxl function (indeed, it will usually call register from
+ * register_event_hooks). Conversely, the application MUST NOT make
+ * the event occurrence calls (libxl_osevent_occurred_*) into libxl
+ * reentrantly from within libxl (for example, from within the
+ * register/modify functions).
+ *
+ * Lock hierarchy: the register/modify/deregister functions may be
+ * called with locks held. These locks (the "libxl internal locks")
+ * are inside the libxl_ctx. Therefore, if those register functions
+ * acquire any locks of their own ("caller register locks") outside
+ * libxl, to avoid deadlock one of the following must hold for each
+ * such caller register lock:
+ * (a) "acquire libxl internal locks before caller register lock":
+ * No libxl function may be called with the caller register
+ * lock held.
+ * (b) "acquire caller register lock before libxl internal locks":
+ * No libxl function may be called _without_ the caller
+ * register lock held.
+ * Of these we would normally recommend (a).
+ *
+ * The value *hooks is not copied and must outlast the libxl_ctx.
+ */
+void libxl_osevent_register_hooks(libxl_ctx *ctx,
+ const libxl_osevent_hooks *hooks,
+ void *user);
+
+/* It is NOT legal to call _occurred_ reentrantly within any libxl
+ * function. Specifically it is NOT legal to call it from within
+ * a register callback. Conversely, libxl MAY call register/deregister
+ * from within libxl_event_occurred_call_*.
+ */
+
+void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl,
+ int fd, short events, short revents)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* Implicitly, on entry to this function the timeout has been
+ * deregistered. If _occurred_timeout is called, libxl will not
+ * call timeout_deregister; if it wants to requeue the timeout it
+ * will call timeout_register again.
+ */
+void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+
+/*======================================================================*/
+
+/*
+ * Subprocess handling.
+ *
+ * Unfortunately the POSIX interface makes this very awkward.
+ *
+ * There are two possible arrangements for collecting statuses from
+ * wait/waitpid.
+ *
+ * For naive programs:
+ *
+ * libxl will keep a SIGCHLD handler installed whenever it has an
+ * active (unreaped) child. It will reap all children with
+ * wait(); any children it does not recognise will be passed to
+ * the application via an optional callback (and will result in
+ * logged warnings if no callback is provided or the callback
+ * denies responsibility for the child).
+ *
+ * libxl may have children whenever:
+ *
+ * - libxl is performing an operation which can be made
+ * asynchronous; ie one taking a libxl_asyncop_how, even
+ * if NULL is passed indicating that the operation is
+ * synchronous; or
+ *
+ * - events of any kind are being generated, as requested
+ * by libxl_evenable_....
+ *
+ * A multithreaded application which is naive in this sense may
+ * block SIGCHLD on some of its threads, but there must be at
+ * least one thread that has SIGCHLD unblocked. libxl will not
+ * modify the blocking flag for SIGCHLD (except that it may create
+ * internal service threads with all signals blocked).
+ *
+ * A naive program must only have at any one time only
+ * one libxl context which might have children.
+ *
+ * For programs which run their own children alongside libxl's:
+ *
+ * A program which does this must call libxl_childproc_setmode.
+ * There are three options:
+ *
+ * libxl_sigchld_owner_libxl:
+ *
+ * While any libxl operation which might use child processes
+ * is running, works like libxl_sigchld_owner_libxl_always;
+ * but, deinstalls the handler the rest of the time.
+ *
+ * In this mode, the application, while it uses any libxl
+ * operation which might create or use child processes (see
+ * above):
+ * - Must not have any child processes running.
+ * - Must not install a SIGCHLD handler.
+ * - Must not reap any children.
+ *
+ * This is the default (i.e. if setmode is not called, or 0 is
+ * passed for hooks).
+ *
+ * libxl_sigchld_owner_mainloop:
+ *
+ * The application must install a SIGCHLD handler and reap (at
+ * least) all of libxl's children and pass their exit status to
+ * libxl by calling libxl_childproc_exited. (If the application
+ * has multiple libxl ctx's, it must call libxl_childproc_exited
+ * on each ctx.)
+ *
+ * libxl_sigchld_owner_libxl_always:
+ *
+ * The application expects this libxl ctx to reap all of the
+ * process's children, and provides a callback to be notified of
+ * their exit statuses. The application must have only one
+ * libxl_ctx configured this way.
+ *
+ * libxl_sigchld_owner_libxl_always_selective_reap:
+ *
+ * The application expects to reap all of its own children
+ * synchronously, and does not use SIGCHLD. libxl is to install
+ * a SIGCHLD handler. The application may have multiple
+ * libxl_ctxs configured this way; in which case all of its ctxs
+ * must be so configured.
+ */
+
+
+typedef enum {
+ /* libxl owns SIGCHLD whenever it has a child, and reaps
+ * all children, including those not spawned by libxl. */
+ libxl_sigchld_owner_libxl,
+
+ /* Application promises to discover when SIGCHLD occurs and call
+ * libxl_childproc_exited or libxl_childproc_sigchld_occurred (but
+ * NOT from within a signal handler). libxl will not itself
+ * arrange to (un)block or catch SIGCHLD. */
+ libxl_sigchld_owner_mainloop,
+
+ /* libxl owns SIGCHLD all the time, and the application is
+ * relying on libxl's event loop for reaping its children too. */
+ libxl_sigchld_owner_libxl_always,
+
+ /* libxl owns SIGCHLD all the time, but it must only reap its own
+ * children. The application will reap its own children
+ * synchronously with waitpid, without the assistance of SIGCHLD. */
+ libxl_sigchld_owner_libxl_always_selective_reap,
+} libxl_sigchld_owner;
+
+typedef struct {
+ libxl_sigchld_owner chldowner;
+
+ /* All of these are optional: */
+
+ /* Called by libxl instead of fork. Should behave exactly like
+ * fork, including setting errno etc. May NOT reenter into libxl.
+ * Application may use this to discover pids of libxl's children,
+ * for example.
+ */
+ pid_t (*fork_replacement)(void *user);
+
+ /* With libxl_sigchld_owner_libxl, called by libxl when it has
+ * reaped a pid. (Not permitted with _owner_mainloop.)
+ *
+ * Should return 0 if the child was recognised by the application
+ * (or if the application does not keep those kind of records),
+ * ERROR_UNKNOWN_CHILD if the application knows that the child is not
+ * the application's; if it returns another error code it is a
+ * disaster as described for libxl_event_register_callbacks.
+ * (libxl will report unexpected children to its error log.)
+ *
+ * If not supplied, the application is assumed not to start
+ * any children of its own.
+ *
+ * This function is NOT called from within the signal handler.
+ * Rather it will be called from inside a libxl's event handling
+ * code and thus only when libxl is running, for example from
+ * within libxl_event_wait. (libxl uses the self-pipe trick
+ * to implement this.)
+ *
+ * childproc_exited_callback may call back into libxl, but it
+ * is best to avoid making long-running libxl calls as that might
+ * stall the calling event loop while the nested operation
+ * completes.
+ */
+ int (*reaped_callback)(pid_t, int status, void *user);
+} libxl_childproc_hooks;
+
+/* hooks may be 0 in which is equivalent to &{ libxl_sigchld_owner_libxl, 0, 0 }
+ *
+ * May not be called when libxl might have any child processes, or the
+ * behaviour is undefined. So it is best to call this at
+ * initialisation.
+ *
+ * The value *hooks is not copied and must outlast the libxl_ctx.
+ */
+void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks *hooks,
+ void *user);
+
+/*
+ * This function is for an application which owns SIGCHLD and which
+ * reaps all of the process's children, and dispatches the exit status
+ * to the correct place inside the application.
+ *
+ * May be called only by an application which has called setmode with
+ * chldowner == libxl_sigchld_owner_mainloop. If pid was a process started
+ * by this instance of libxl, returns 0 after doing whatever
+ * processing is appropriate. Otherwise silently returns
+ * ERROR_UNKNOWN_CHILD. No other error returns are possible.
+ *
+ * May NOT be called from within a signal handler which might
+ * interrupt any libxl operation. The application will almost
+ * certainly need to use the self-pipe trick (or a working pselect or
+ * ppoll) to implement this.
+ */
+int libxl_childproc_reaped(libxl_ctx *ctx, pid_t, int status)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * This function is for an application which owns SIGCHLD but which
+ * doesn't keep track of all of its own children in a manner suitable
+ * for reaping all of them and then dispatching them.
+ *
+ * Such an the application must notify libxl, by calling this
+ * function, that a SIGCHLD occurred. libxl will then check all its
+ * children, reap any that are ready, and take any action necessary -
+ * but it will not reap anything else.
+ *
+ * May be called only by an application which has called setmode with
+ * chldowner == libxl_sigchld_owner_mainloop.
+ *
+ * May NOT be called from within a signal handler which might
+ * interrupt any libxl operation (just like libxl_childproc_reaped).
+ */
+void libxl_childproc_sigchld_occurred(libxl_ctx *ctx)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+
+/*
+ * An application which initialises a libxl_ctx in a parent process
+ * and then forks a child which does not quickly exec, must
+ * instead libxl_postfork_child_noexec in the child. One call
+ * on any existing (or specially made) ctx is sufficient; after
+ * this all previously existing libxl_ctx's are invalidated and
+ * must not be used - or even freed. It is harmless to call this
+ * postfork function and then exec anyway.
+ *
+ * Until libxl_postfork_child_noexec has returned:
+ * - No other libxl calls may be made.
+ * - If any libxl ctx was configured handle the process's SIGCHLD,
+ * the child may not create further (grand)child processes, nor
+ * manipulate SIGCHLD.
+ *
+ * libxl_postfork_child_noexec may not reclaim all the resources
+ * associated with the libxl ctx. This includes but is not limited
+ * to: ordinary memory; files on disk and in /var/run; file
+ * descriptors; memory mapped into the process from domains being
+ * managed (grant maps); Xen event channels. Use of libxl in
+ * processes which fork long-lived children is not recommended for
+ * this reason. libxl_postfork_child_noexec is provided so that
+ * an application can make further libxl calls in a child which
+ * is going to exec or exit soon.
+ */
+void libxl_postfork_child_noexec(libxl_ctx *ctx);
+
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2011 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#ifndef LIBXL_JSON_H
+#define LIBXL_JSON_H
+
+#include <yajl/yajl_gen.h>
+#include <yajl/yajl_parse.h>
+
+#ifdef HAVE_YAJL_YAJL_VERSION_H
+# include <yajl/yajl_version.h>
+#endif
+
+yajl_gen_status libxl__uint64_gen_json(yajl_gen hand, uint64_t val);
+yajl_gen_status libxl_defbool_gen_json(yajl_gen hand, libxl_defbool *p);
+yajl_gen_status libxl_uuid_gen_json(yajl_gen hand, libxl_uuid *p);
+yajl_gen_status libxl_mac_gen_json(yajl_gen hand, libxl_mac *p);
+yajl_gen_status libxl_bitmap_gen_json(yajl_gen hand, libxl_bitmap *p);
+yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
+ libxl_cpuid_policy_list *p);
+yajl_gen_status libxl_string_list_gen_json(yajl_gen hand, libxl_string_list *p);
+yajl_gen_status libxl_key_value_list_gen_json(yajl_gen hand,
+ libxl_key_value_list *p);
+yajl_gen_status libxl_hwcap_gen_json(yajl_gen hand, libxl_hwcap *p);
+yajl_gen_status libxl_ms_vm_genid_gen_json(yajl_gen hand, libxl_ms_vm_genid *p);
+
+#include <_libxl_types_json.h>
+
+/* YAJL version check */
+#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
+# define HAVE_YAJL_V2 1
+#endif
+
+#ifdef HAVE_YAJL_V2
+
+typedef size_t libxl_yajl_length;
+
+static inline yajl_handle libxl__yajl_alloc(const yajl_callbacks *callbacks,
+ yajl_alloc_funcs *allocFuncs,
+ void *ctx)
+{
+ yajl_handle hand = yajl_alloc(callbacks, allocFuncs, ctx);
+ if (hand)
+ yajl_config(hand, yajl_allow_trailing_garbage, 1);
+ return hand;
+}
+
+static inline yajl_gen libxl_yajl_gen_alloc(const yajl_alloc_funcs *allocFuncs)
+{
+ yajl_gen g;
+ g = yajl_gen_alloc(allocFuncs);
+ if (g)
+ yajl_gen_config(g, yajl_gen_beautify, 1);
+ return g;
+}
+
+#else /* !HAVE_YAJL_V2 */
+
+#define yajl_complete_parse yajl_parse_complete
+
+typedef unsigned int libxl_yajl_length;
+
+static inline yajl_handle libxl__yajl_alloc(const yajl_callbacks *callbacks,
+ const yajl_alloc_funcs *allocFuncs,
+ void *ctx)
+{
+ yajl_parser_config cfg = {
+ .allowComments = 1,
+ .checkUTF8 = 1,
+ };
+ return yajl_alloc(callbacks, &cfg, allocFuncs, ctx);
+}
+
+static inline yajl_gen libxl_yajl_gen_alloc(const yajl_alloc_funcs *allocFuncs)
+{
+ yajl_gen_config conf = { 1, " " };
+ return yajl_gen_alloc(&conf, allocFuncs);
+}
+
+#endif /* !HAVE_YAJL_V2 */
+
+yajl_gen_status libxl_domain_config_gen_json(yajl_gen hand,
+ libxl_domain_config *p);
+
+#endif /* LIBXL_JSON_H */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#ifndef LIBXL_UTILS_H
+#define LIBXL_UTILS_H
+
+#include "libxl.h"
+
+#ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
+const
+#endif
+char *libxl_basename(const char *name); /* returns string from strdup */
+
+unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus);
+ /* deprecated; see LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONFIG in libxl.h */
+int libxl_name_to_domid(libxl_ctx *ctx, const char *name, uint32_t *domid);
+int libxl_domain_qualifier_to_domid(libxl_ctx *ctx, const char *name, uint32_t *domid);
+char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid);
+int libxl_cpupool_qualifier_to_cpupoolid(libxl_ctx *ctx, const char *p,
+ uint32_t *poolid_r,
+ int *was_name_r);
+int libxl_name_to_cpupoolid(libxl_ctx *ctx, const char *name, uint32_t *poolid);
+char *libxl_cpupoolid_to_name(libxl_ctx *ctx, uint32_t poolid);
+int libxl_cpupoolid_is_valid(libxl_ctx *ctx, uint32_t poolid);
+int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid);
+int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid);
+int libxl_create_logfile(libxl_ctx *ctx, const char *name, char **full_name);
+int libxl_string_to_backend(libxl_ctx *ctx, char *s, libxl_disk_backend *backend);
+
+int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
+ void **data_r, int *datalen_r);
+ /* Reads the contents of the plain file filename into a mallocd
+ * buffer. Returns 0 or errno. Any errors other than ENOENT are logged.
+ * If the file is empty, *data_r and *datalen_r are set to 0.
+ * On error, *data_r and *datalen_r are unchanged.
+ * data_r and/or datalen_r may be 0.
+ */
+
+int libxl_read_exactly(libxl_ctx *ctx, int fd, void *data, ssize_t sz,
+ const char *filename, const char *what);
+int libxl_write_exactly(libxl_ctx *ctx, int fd, const void *data,
+ ssize_t sz, const char *filename, const char *what);
+ /* Returns 0 or errno. If file is truncated on reading, returns
+ * EPROTO and you have no way to tell how much was read. Errors are
+ * logged using filename (which is only used for logging) and what
+ * (which may be 0). */
+
+int libxl_pipe(libxl_ctx *ctx, int pipes[2]);
+ /* Just like pipe(2), but log errors. */
+
+void libxl_report_child_exitstatus(libxl_ctx *ctx, xentoollog_level,
+ const char *what, pid_t pid, int status);
+ /* treats all exit statuses as errors; if that's not what you want,
+ * check status yourself first */
+
+int libxl_mac_to_device_nic(libxl_ctx *ctx, uint32_t domid,
+ const char *mac, libxl_device_nic *nic);
+int libxl_devid_to_device_nic(libxl_ctx *ctx, uint32_t domid, int devid,
+ libxl_device_nic *nic);
+
+int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid, const char *vdev,
+ libxl_device_disk *disk);
+
+int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
+ libxl_uuid *uuid, libxl_device_vtpm *vtpm);
+int libxl_devid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
+ int devid, libxl_device_vtpm *vtpm);
+int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, uint32_t domid,
+ int devid, libxl_device_usbctrl *usbctrl);
+
+int libxl_devid_to_device_vkb(libxl_ctx *ctx, uint32_t domid,
+ int devid, libxl_device_vkb *vkb);
+
+int libxl_devid_to_device_vdispl(libxl_ctx *ctx, uint32_t domid,
+ int devid, libxl_device_vdispl *vdispl);
+
+int libxl_devid_to_device_vsnd(libxl_ctx *ctx, uint32_t domid,
+ int devid, libxl_device_vsnd *vsnd);
+
+int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx, uint32_t domid,
+ int ctrl, int port,
+ libxl_device_usbdev *usbdev);
+
+int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits);
+ /* Allocated bimap is from malloc, libxl_bitmap_dispose() to be
+ * called by the application when done. */
+void libxl_bitmap_copy_alloc(libxl_ctx *ctx, libxl_bitmap *dptr,
+ const libxl_bitmap *sptr);
+void libxl_bitmap_copy(libxl_ctx *ctx, libxl_bitmap *dptr,
+ const libxl_bitmap *sptr);
+int libxl_bitmap_is_full(const libxl_bitmap *bitmap);
+int libxl_bitmap_is_empty(const libxl_bitmap *bitmap);
+int libxl_bitmap_test(const libxl_bitmap *bitmap, int bit);
+void libxl_bitmap_set(libxl_bitmap *bitmap, int bit);
+void libxl_bitmap_reset(libxl_bitmap *bitmap, int bit);
+int libxl_bitmap_count_set(const libxl_bitmap *bitmap);
+int libxl_bitmap_or(libxl_ctx *ctx, libxl_bitmap *or_map,
+ const libxl_bitmap *map1,
+ const libxl_bitmap *map2);
+int libxl_bitmap_and(libxl_ctx *ctx, libxl_bitmap *and_map,
+ const libxl_bitmap *map1,
+ const libxl_bitmap *map2);
+char *libxl_bitmap_to_hex_string(libxl_ctx *ctx, const libxl_bitmap *bitmap);
+static inline void libxl_bitmap_set_any(libxl_bitmap *bitmap)
+{
+ memset(bitmap->map, -1, bitmap->size);
+}
+static inline void libxl_bitmap_set_none(libxl_bitmap *bitmap)
+{
+ memset(bitmap->map, 0, bitmap->size);
+}
+static inline int libxl_bitmap_cpu_valid(libxl_bitmap *bitmap, int bit)
+{
+ return bit >= 0 && bit < (bitmap->size * 8);
+}
+#define libxl_for_each_bit(var, map) for (var = 0; var < (map).size * 8; var++)
+#define libxl_for_each_set_bit(v, m) for (v = 0; v < (m).size * 8; v++) \
+ if (libxl_bitmap_test(&(m), v))
+
+/*
+ * Compares two bitmaps bit by bit, up to nr_bits or, if nr_bits is 0, up
+ * to the size of the largest bitmap. If sizes does not match, bits past the
+ * of a bitmap are considered as being 0, which matches with the semantic and
+ * implementation of libxl_bitmap_test I think().
+ *
+ * So, basically, [0,1,0] and [0,1] are considered equal, while [0,1,1] and
+ * [0,1] are different.
+ */
+static inline int libxl_bitmap_equal(const libxl_bitmap *ba,
+ const libxl_bitmap *bb,
+ int nr_bits)
+{
+ int i;
+
+ if (nr_bits == 0)
+ nr_bits = ba->size > bb->size ? ba->size * 8 : bb->size * 8;
+
+ for (i = 0; i < nr_bits; i++) {
+ if (libxl_bitmap_test(ba, i) != libxl_bitmap_test(bb, i))
+ return 0;
+ }
+ return 1;
+}
+
+int libxl_cpu_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *cpumap, int max_cpus);
+int libxl_node_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *nodemap,
+ int max_nodes);
+int libxl_socket_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *socketmap,
+ int max_sockets);
+/* Fill socketmap with the CPU topology information on the system. */
+int libxl_get_online_socketmap(libxl_ctx *ctx, libxl_bitmap *socketmap);
+
+/* Populate cpumap with the cpus spanned by the nodes in nodemap */
+int libxl_nodemap_to_cpumap(libxl_ctx *ctx,
+ const libxl_bitmap *nodemap,
+ libxl_bitmap *cpumap);
+/* Populate cpumap with the cpus spanned by node */
+int libxl_node_to_cpumap(libxl_ctx *ctx, int node,
+ libxl_bitmap *cpumap);
+/* Populate nodemap with the nodes of the cpus in cpumap */
+int libxl_cpumap_to_nodemap(libxl_ctx *ctx,
+ const libxl_bitmap *cpumap,
+ libxl_bitmap *nodemap);
+
+ static inline uint32_t libxl__sizekb_to_mb(uint32_t s) {
+ return (s + 1023) / 1024;
+}
+
+void libxl_string_copy(libxl_ctx *ctx, char **dst, char * const*src);
+
+
+#define LIBXL_FILLZERO(object) (memset(&(object), 0, sizeof((object))))
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2008,2010 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#ifndef __LIBXL_UUID_H__
+#define __LIBXL_UUID_H__
+
+#define LIBXL_UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
+#define LIBXL_UUID_FMTLEN ((2*16)+4) /* 16 hex bytes plus 4 hypens */
+#define LIBXL__UUID_BYTES(uuid) uuid[0], uuid[1], uuid[2], uuid[3], \
+ uuid[4], uuid[5], uuid[6], uuid[7], \
+ uuid[8], uuid[9], uuid[10], uuid[11], \
+ uuid[12], uuid[13], uuid[14], uuid[15]
+#define LIBXL_UUID_BYTES(arg) LIBXL__UUID_BYTES((arg).uuid)
+
+typedef struct {
+ /* UUID as an octet stream in big-endian byte-order. */
+ unsigned char uuid[16];
+} libxl_uuid;
+
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040700
+#if defined(__linux__)
+
+#include <uuid/uuid.h>
+#include <stdint.h>
+
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+
+#include <uuid.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#else
+
+#error "Please update libxl_uuid.h for your OS"
+
+#endif
+#endif
+
+int libxl_uuid_is_nil(const libxl_uuid *uuid);
+void libxl_uuid_generate(libxl_uuid *uuid);
+int libxl_uuid_from_string(libxl_uuid *uuid, const char *in);
+void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst,
+ const libxl_uuid *src);
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040500
+static inline void libxl_uuid_copy_0x040400(libxl_uuid *dst,
+ const libxl_uuid *src)
+{
+ libxl_uuid_copy(NULL, dst, src);
+}
+#define libxl_uuid_copy libxl_uuid_copy_0x040400
+#endif
+
+void libxl_uuid_clear(libxl_uuid *uuid);
+int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2);
+const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid);
+uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid);
+
+#endif /* __LIBXL_UUID_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+int libxl_ctx_alloc(libxl_ctx **pctx, int version,
+ unsigned flags, xentoollog_logger * lg)
+{
+ libxl_ctx *ctx = NULL;
+ libxl__gc gc_buf, *gc = NULL;
+ int rc;
+
+ if (version != LIBXL_VERSION) { rc = ERROR_VERSION; goto out; }
+
+ ctx = malloc(sizeof(*ctx));
+ if (!ctx) {
+ xtl_log(lg, XTL_ERROR, errno, "libxl",
+ "%s:%d:%s: Failed to allocate context\n",
+ __FILE__, __LINE__, __func__);
+ rc = ERROR_NOMEM; goto out;
+ }
+
+ memset(ctx, 0, sizeof(libxl_ctx));
+ ctx->lg = lg;
+
+ /* First initialise pointers etc. (cannot fail) */
+
+ ctx->nogc_gc.alloc_maxsize = -1;
+ ctx->nogc_gc.owner = ctx;
+
+ LIBXL_TAILQ_INIT(&ctx->occurred);
+
+ ctx->osevent_hooks = 0;
+
+ ctx->poller_app = 0;
+ LIBXL_LIST_INIT(&ctx->pollers_event);
+ LIBXL_LIST_INIT(&ctx->pollers_idle);
+ LIBXL_LIST_INIT(&ctx->pollers_active);
+
+ LIBXL_LIST_INIT(&ctx->efds);
+ LIBXL_TAILQ_INIT(&ctx->etimes);
+
+ ctx->watch_slots = 0;
+ LIBXL_SLIST_INIT(&ctx->watch_freeslots);
+ libxl__ev_fd_init(&ctx->watch_efd);
+
+ ctx->xce = 0;
+ LIBXL_LIST_INIT(&ctx->evtchns_waiting);
+ libxl__ev_fd_init(&ctx->evtchn_efd);
+
+ LIBXL_LIST_INIT(&ctx->aos_inprogress);
+
+ LIBXL_TAILQ_INIT(&ctx->death_list);
+ libxl__ev_xswatch_init(&ctx->death_watch);
+
+ ctx->childproc_hooks = &libxl__childproc_default_hooks;
+ ctx->childproc_user = 0;
+
+ ctx->sigchld_selfpipe[0] = -1;
+ ctx->sigchld_selfpipe[1] = -1;
+ libxl__ev_fd_init(&ctx->sigchld_selfpipe_efd);
+
+ /* The mutex is special because we can't idempotently destroy it */
+
+ if (libxl__init_recursive_mutex(ctx, &ctx->lock) < 0) {
+ LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Failed to initialize mutex");
+ free(ctx);
+ ctx = 0;
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /* Now ctx is safe for ctx_free; failures simply set rc and "goto out" */
+ LIBXL_INIT_GC(gc_buf,ctx);
+ gc = &gc_buf;
+ /* Now gc is useable */
+
+ rc = libxl__atfork_init(ctx);
+ if (rc) goto out;
+
+ ctx->poller_app = libxl__poller_get(gc);
+ if (!ctx->poller_app) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ ctx->xch = xc_interface_open(lg,lg,0);
+ if (!ctx->xch) {
+ LOGEV(ERROR, errno, "cannot open libxc handle");
+ rc = ERROR_FAIL; goto out;
+ }
+
+ ctx->xsh = xs_daemon_open();
+ if (!ctx->xsh)
+ ctx->xsh = xs_domain_open();
+ if (!ctx->xsh) {
+ LOGEV(ERROR, errno, "cannot connect to xenstore");
+ rc = ERROR_FAIL; goto out;
+ }
+
+ *pctx = ctx;
+ return 0;
+
+ out:
+ if (gc) libxl__free_all(gc);
+ libxl_ctx_free(ctx);
+ *pctx = NULL;
+ return rc;
+}
+
+static void free_disable_deaths(libxl__gc *gc,
+ struct libxl__evgen_domain_death_list *l) {
+ libxl_evgen_domain_death *death;
+ while ((death = LIBXL_TAILQ_FIRST(l)))
+ libxl__evdisable_domain_death(gc, death);
+}
+
+static void discard_events(struct libxl__event_list *l) {
+ /* doesn't bother unlinking from the list, so l is corrupt on return */
+ libxl_event *ev, *next;
+ LIBXL_TAILQ_FOREACH_SAFE(ev, l, link, next)
+ libxl_event_free(0, ev);
+}
+
+int libxl_ctx_free(libxl_ctx *ctx)
+{
+ if (!ctx) return 0;
+
+ int i;
+ GC_INIT(ctx);
+
+ CTX_LOCK;
+ assert(!ctx->osevent_in_hook);
+ CTX->osevent_in_hook += 1000; /* make violations easier to debug */
+
+ /* Deregister all libxl__ev_KINDs: */
+
+ free_disable_deaths(gc, &CTX->death_list);
+ free_disable_deaths(gc, &CTX->death_reported);
+
+ libxl_evgen_disk_eject *eject;
+ while ((eject = LIBXL_LIST_FIRST(&CTX->disk_eject_evgens)))
+ libxl__evdisable_disk_eject(gc, eject);
+
+ libxl_childproc_setmode(CTX,0,0);
+ for (i = 0; i < ctx->watch_nslots; i++)
+ assert(!libxl__watch_slot_contents(gc, i));
+ assert(!libxl__ev_fd_isregistered(&ctx->watch_efd));
+ assert(!libxl__ev_fd_isregistered(&ctx->evtchn_efd));
+ assert(!libxl__ev_fd_isregistered(&ctx->sigchld_selfpipe_efd));
+
+ /* Now there should be no more events requested from the application: */
+
+ assert(LIBXL_LIST_EMPTY(&ctx->efds));
+ assert(LIBXL_TAILQ_EMPTY(&ctx->etimes));
+ assert(LIBXL_LIST_EMPTY(&ctx->evtchns_waiting));
+ assert(LIBXL_LIST_EMPTY(&ctx->aos_inprogress));
+
+ if (ctx->xch) xc_interface_close(ctx->xch);
+ libxl_version_info_dispose(&ctx->version_info);
+ if (ctx->xsh) xs_daemon_close(ctx->xsh);
+ if (ctx->xce) xenevtchn_close(ctx->xce);
+
+ libxl__poller_put(ctx, ctx->poller_app);
+ ctx->poller_app = NULL;
+ assert(LIBXL_LIST_EMPTY(&ctx->pollers_event));
+ assert(LIBXL_LIST_EMPTY(&ctx->pollers_active));
+ libxl__poller *poller, *poller_tmp;
+ LIBXL_LIST_FOREACH_SAFE(poller, &ctx->pollers_idle, entry, poller_tmp) {
+ libxl__poller_dispose(poller);
+ free(poller);
+ }
+
+ free(ctx->watch_slots);
+
+ discard_events(&ctx->occurred);
+
+ /* If we have outstanding children, then the application inherits
+ * them; we wish the application good luck with understanding
+ * this if and when it reaps them. */
+ libxl__sigchld_notneeded(gc);
+ libxl__pipe_close(ctx->sigchld_selfpipe);
+
+ CTX_UNLOCK;
+ pthread_mutex_destroy(&ctx->lock);
+
+ GC_FREE;
+ free(ctx);
+ return 0;
+}
+
+void libxl_string_list_dispose(libxl_string_list *psl)
+{
+ int i;
+ libxl_string_list sl = *psl;
+
+ if (!sl)
+ return;
+
+ for (i = 0; sl[i] != NULL; i++) {
+ free(sl[i]);
+ sl[i] = NULL;
+ }
+ free(sl);
+ *psl = NULL;
+}
+
+void libxl_string_list_copy(libxl_ctx *ctx,
+ libxl_string_list *dst,
+ const libxl_string_list *src)
+{
+ GC_INIT(ctx);
+ int i, len;
+
+ if (!*src) {
+ *dst = NULL;
+ goto out;
+ }
+
+ len = libxl_string_list_length(src);
+ /* one extra slot for sentinel */
+ *dst = libxl__calloc(NOGC, len + 1, sizeof(char *));
+
+ for (i = 0; i < len; i++)
+ (*dst)[i] = libxl__strdup(NOGC, (*src)[i]);
+
+out:
+ GC_FREE;
+}
+
+int libxl_string_list_length(const libxl_string_list *psl)
+{
+ int i = 0;
+
+ if (*psl)
+ while ((*psl)[i])
+ i++;
+
+ return i;
+}
+
+int libxl_key_value_list_length(const libxl_key_value_list *pkvl)
+{
+ int i = 0;
+ libxl_key_value_list kvl = *pkvl;
+
+ if (kvl) {
+ while (kvl[2 * i]) /* Only checks keys */
+ i++;
+ }
+
+ return i;
+}
+
+void libxl_key_value_list_dispose(libxl_key_value_list *pkvl)
+{
+ int i;
+ libxl_key_value_list kvl = *pkvl;
+
+ if (!kvl)
+ return;
+
+ for (i = 0; kvl[i] != NULL; i += 2) {
+ free(kvl[i]);
+ kvl[i] = NULL;
+ if (kvl[i + 1]) {
+ free(kvl[i + 1]);
+ kvl[i+1] = NULL;
+ }
+ }
+ free(kvl);
+ *pkvl = NULL;
+}
+
+void libxl_key_value_list_copy(libxl_ctx *ctx,
+ libxl_key_value_list *dst,
+ const libxl_key_value_list *src)
+{
+ GC_INIT(ctx);
+ int i, len;
+
+ if (*src == NULL) {
+ *dst = NULL;
+ goto out;
+ }
+
+ len = libxl_key_value_list_length(src);
+ /* one extra slot for sentinel */
+ *dst = libxl__calloc(NOGC, len * 2 + 1, sizeof(char *));
+
+ for (i = 0; i < len * 2; i += 2) {
+ (*dst)[i] = libxl__strdup(NOGC, (*src)[i]);
+ if ((*src)[i+1])
+ (*dst)[i+1] = libxl__strdup(NOGC, (*src)[i+1]);
+ else
+ (*dst)[i+1] = NULL;
+ }
+
+out:
+ GC_FREE;
+}
+
+void libxl_defbool_set(libxl_defbool *db, bool b)
+{
+ db->val = b ? LIBXL__DEFBOOL_TRUE : LIBXL__DEFBOOL_FALSE;
+}
+
+void libxl_defbool_unset(libxl_defbool *db)
+{
+ db->val = LIBXL__DEFBOOL_DEFAULT;
+}
+
+bool libxl_defbool_is_default(libxl_defbool db)
+{
+ return !db.val;
+}
+
+void libxl_defbool_setdefault(libxl_defbool *db, bool b)
+{
+ if (libxl_defbool_is_default(*db))
+ libxl_defbool_set(db, b);
+}
+
+bool libxl_defbool_val(libxl_defbool db)
+{
+ assert(!libxl_defbool_is_default(db));
+ return db.val > 0;
+}
+
+const char *libxl_defbool_to_string(libxl_defbool b)
+{
+ if (b.val < 0)
+ return LIBXL__DEFBOOL_STR_FALSE;
+ else if (b.val > 0)
+ return LIBXL__DEFBOOL_STR_TRUE;
+ else
+ return LIBXL__DEFBOOL_STR_DEFAULT;
+}
+
+/******************************************************************************/
+int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo)
+{
+ xc_physinfo_t xcphysinfo = { 0 };
+ int rc;
+ long l;
+ GC_INIT(ctx);
+
+ rc = xc_physinfo(ctx->xch, &xcphysinfo);
+ if (rc != 0) {
+ LOGE(ERROR, "getting physinfo");
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ physinfo->threads_per_core = xcphysinfo.threads_per_core;
+ physinfo->cores_per_socket = xcphysinfo.cores_per_socket;
+ physinfo->max_cpu_id = xcphysinfo.max_cpu_id;
+ physinfo->nr_cpus = xcphysinfo.nr_cpus;
+ physinfo->cpu_khz = xcphysinfo.cpu_khz;
+ physinfo->total_pages = xcphysinfo.total_pages;
+ physinfo->free_pages = xcphysinfo.free_pages;
+ physinfo->scrub_pages = xcphysinfo.scrub_pages;
+ physinfo->outstanding_pages = xcphysinfo.outstanding_pages;
+ physinfo->max_possible_mfn = xcphysinfo.max_mfn;
+ l = xc_sharing_freed_pages(ctx->xch);
+ if (l < 0 && errno == ENOSYS) {
+ l = 0;
+ } else if (l < 0) {
+ LOGEV(ERROR, l, "getting sharing freed pages");
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ physinfo->sharing_freed_pages = l;
+ l = xc_sharing_used_frames(ctx->xch);
+ if (l < 0 && errno == ENOSYS) {
+ l = 0;
+ } else if (l < 0) {
+ LOGEV(ERROR, l, "getting sharing used frames");
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ physinfo->sharing_used_frames = l;
+ physinfo->nr_nodes = xcphysinfo.nr_nodes;
+ memcpy(physinfo->hw_cap,xcphysinfo.hw_cap, sizeof(physinfo->hw_cap));
+
+ physinfo->cap_hvm = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_hvm);
+ physinfo->cap_pv = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_pv);
+ physinfo->cap_hvm_directio =
+ !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_directio);
+ physinfo->cap_hap = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_hap);
+ physinfo->cap_shadow =
+ !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_shadow);
+ physinfo->cap_iommu_hap_pt_share =
+ !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_iommu_hap_pt_share);
+
+ GC_FREE;
+ return 0;
+}
+
+libxl_cputopology *libxl_get_cpu_topology(libxl_ctx *ctx, int *nb_cpu_out)
+{
+ GC_INIT(ctx);
+ xc_cputopo_t *cputopo;
+ libxl_cputopology *ret = NULL;
+ int i;
+ unsigned num_cpus = 0;
+
+ /* Setting buffer to NULL makes the call return number of CPUs */
+ if (xc_cputopoinfo(ctx->xch, &num_cpus, NULL))
+ {
+ LOGE(ERROR, "Unable to determine number of CPUS");
+ goto out;
+ }
+
+ cputopo = libxl__zalloc(gc, sizeof(*cputopo) * num_cpus);
+
+ if (xc_cputopoinfo(ctx->xch, &num_cpus, cputopo)) {
+ LOGE(ERROR, "CPU topology info hypercall failed");
+ goto out;
+ }
+
+ ret = libxl__zalloc(NOGC, sizeof(libxl_cputopology) * num_cpus);
+
+ for (i = 0; i < num_cpus; i++) {
+#define V(map, i, invalid) ( cputopo[i].map == invalid) ? \
+ LIBXL_CPUTOPOLOGY_INVALID_ENTRY : cputopo[i].map
+ ret[i].core = V(core, i, XEN_INVALID_CORE_ID);
+ ret[i].socket = V(socket, i, XEN_INVALID_SOCKET_ID);
+ ret[i].node = V(node, i, XEN_INVALID_NODE_ID);
+#undef V
+ }
+
+ *nb_cpu_out = num_cpus;
+
+ out:
+ GC_FREE;
+ return ret;
+}
+
+libxl_pcitopology *libxl_get_pci_topology(libxl_ctx *ctx, int *num_devs)
+{
+ GC_INIT(ctx);
+ physdev_pci_device_t *devs;
+ uint32_t *nodes;
+ libxl_pcitopology *ret = NULL;
+ int i, rc;
+
+ *num_devs = libxl__pci_numdevs(gc);
+ if (*num_devs < 0) {
+ LOG(ERROR, "Unable to determine number of PCI devices, rc %d",
+ *num_devs);
+ goto out;
+ }
+
+ devs = libxl__zalloc(gc, sizeof(*devs) * *num_devs);
+ nodes = libxl__zalloc(gc, sizeof(*nodes) * *num_devs);
+
+ rc = libxl__pci_topology_init(gc, devs, *num_devs);
+ if (rc) {
+ LOG(ERROR, "Cannot initialize PCI hypercall structure, rc %d", rc);
+ goto out;
+ }
+
+ if (xc_pcitopoinfo(ctx->xch, *num_devs, devs, nodes) != 0) {
+ LOGE(ERROR, "PCI topology info hypercall failed");
+ goto out;
+ }
+
+ ret = libxl__zalloc(NOGC, sizeof(libxl_pcitopology) * *num_devs);
+
+ for (i = 0; i < *num_devs; i++) {
+ ret[i].seg = devs[i].seg;
+ ret[i].bus = devs[i].bus;
+ ret[i].devfn = devs[i].devfn;
+ ret[i].node = ((nodes[i] == XEN_INVALID_NODE_ID) ||
+ (nodes[i] == XEN_INVALID_DEV)) ?
+ LIBXL_PCITOPOLOGY_INVALID_ENTRY : nodes[i];
+ }
+
+ out:
+ GC_FREE;
+ return ret;
+}
+
+libxl_numainfo *libxl_get_numainfo(libxl_ctx *ctx, int *nr)
+{
+ GC_INIT(ctx);
+ xc_meminfo_t *meminfo;
+ uint32_t *distance;
+ libxl_numainfo *ret = NULL;
+ int i, j;
+ unsigned num_nodes = 0;
+
+ if (xc_numainfo(ctx->xch, &num_nodes, NULL, NULL)) {
+ LOGE(ERROR, "Unable to determine number of nodes");
+ goto out;
+ }
+
+ meminfo = libxl__zalloc(gc, sizeof(*meminfo) * num_nodes);
+ distance = libxl__zalloc(gc, sizeof(*distance) * num_nodes * num_nodes);
+
+ if (xc_numainfo(ctx->xch, &num_nodes, meminfo, distance)) {
+ LOGE(ERROR, "getting numainfo");
+ goto out;
+ }
+
+ *nr = num_nodes;
+
+ ret = libxl__zalloc(NOGC, sizeof(libxl_numainfo) * num_nodes);
+ for (i = 0; i < num_nodes; i++)
+ ret[i].dists = libxl__calloc(NOGC, num_nodes, sizeof(*distance));
+
+ for (i = 0; i < num_nodes; i++) {
+#define V(val, invalid) (val == invalid) ? \
+ LIBXL_NUMAINFO_INVALID_ENTRY : val
+ ret[i].size = V(meminfo[i].memsize, XEN_INVALID_MEM_SZ);
+ ret[i].free = V(meminfo[i].memfree, XEN_INVALID_MEM_SZ);
+ ret[i].num_dists = num_nodes;
+ for (j = 0; j < ret[i].num_dists; j++) {
+ unsigned idx = i * num_nodes + j;
+ ret[i].dists[j] = V(distance[idx], XEN_INVALID_NODE_DIST);
+ }
+#undef V
+ }
+
+ out:
+ GC_FREE;
+ return ret;
+}
+
+static int libxl__xc_version_wrap(libxl__gc *gc, libxl_version_info *info,
+ xen_build_id_t *build)
+{
+ int r;
+
+ r = xc_version(CTX->xch, XENVER_build_id, build);
+ switch (r) {
+ case -EPERM:
+ case -ENODATA:
+ case 0:
+ info->build_id = libxl__strdup(NOGC, "");
+ break;
+
+ case -ENOBUFS:
+ break;
+
+ default:
+ if (r > 0) {
+ unsigned int i;
+
+ info->build_id = libxl__zalloc(NOGC, (r * 2) + 1);
+
+ for (i = 0; i < r ; i++)
+ snprintf(&info->build_id[i * 2], 3, "%02hhx", build->buf[i]);
+
+ r = 0;
+ }
+ break;
+ }
+ return r;
+}
+
+const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx)
+{
+ GC_INIT(ctx);
+ union {
+ xen_extraversion_t xen_extra;
+ xen_compile_info_t xen_cc;
+ xen_changeset_info_t xen_chgset;
+ xen_capabilities_info_t xen_caps;
+ xen_platform_parameters_t p_parms;
+ xen_commandline_t xen_commandline;
+ xen_build_id_t build_id;
+ } u;
+ long xen_version;
+ int r;
+ libxl_version_info *info = &ctx->version_info;
+
+ if (info->xen_version_extra != NULL)
+ goto out;
+
+ xen_version = xc_version(ctx->xch, XENVER_version, NULL);
+ info->xen_version_major = xen_version >> 16;
+ info->xen_version_minor = xen_version & 0xFF;
+
+ xc_version(ctx->xch, XENVER_extraversion, &u.xen_extra);
+ info->xen_version_extra = libxl__strdup(NOGC, u.xen_extra);
+
+ xc_version(ctx->xch, XENVER_compile_info, &u.xen_cc);
+ info->compiler = libxl__strdup(NOGC, u.xen_cc.compiler);
+ info->compile_by = libxl__strdup(NOGC, u.xen_cc.compile_by);
+ info->compile_domain = libxl__strdup(NOGC, u.xen_cc.compile_domain);
+ info->compile_date = libxl__strdup(NOGC, u.xen_cc.compile_date);
+
+ xc_version(ctx->xch, XENVER_capabilities, &u.xen_caps);
+ info->capabilities = libxl__strdup(NOGC, u.xen_caps);
+
+ xc_version(ctx->xch, XENVER_changeset, &u.xen_chgset);
+ info->changeset = libxl__strdup(NOGC, u.xen_chgset);
+
+ xc_version(ctx->xch, XENVER_platform_parameters, &u.p_parms);
+ info->virt_start = u.p_parms.virt_start;
+
+ info->pagesize = xc_version(ctx->xch, XENVER_pagesize, NULL);
+
+ xc_version(ctx->xch, XENVER_commandline, &u.xen_commandline);
+ info->commandline = libxl__strdup(NOGC, u.xen_commandline);
+
+ u.build_id.len = sizeof(u) - sizeof(u.build_id);
+ r = libxl__xc_version_wrap(gc, info, &u.build_id);
+ if (r == -ENOBUFS) {
+ xen_build_id_t *build_id;
+
+ build_id = libxl__zalloc(gc, info->pagesize);
+ build_id->len = info->pagesize - sizeof(*build_id);
+ r = libxl__xc_version_wrap(gc, info, build_id);
+ if (r) LOGEV(ERROR, r, "getting build_id");
+ }
+ out:
+ GC_FREE;
+ return info;
+}
+
+int libxl_send_sysrq(libxl_ctx *ctx, uint32_t domid, char sysrq)
+{
+ GC_INIT(ctx);
+ char *dompath = libxl__xs_get_dompath(gc, domid);
+
+ libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/control/sysrq", dompath),
+ "%c", sysrq);
+
+ GC_FREE;
+ return 0;
+}
+
+int libxl_send_debug_keys(libxl_ctx *ctx, char *keys)
+{
+ int ret;
+ GC_INIT(ctx);
+ ret = xc_send_debug_keys(ctx->xch, keys);
+ if ( ret < 0 ) {
+ LOGE(ERROR, "sending debug keys");
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ GC_FREE;
+ return 0;
+}
+
+int libxl_set_parameters(libxl_ctx *ctx, char *params)
+{
+ int ret;
+ GC_INIT(ctx);
+ char *par, *val, *end, *path;
+ xenhypfs_handle *hypfs;
+
+ hypfs = xenhypfs_open(ctx->lg, 0);
+ if (!hypfs) {
+ LOGE(ERROR, "opening Xen hypfs");
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ while (isblank(*params))
+ params++;
+
+ for (par = params; *par; par = end) {
+ end = strchr(par, ' ');
+ if (!end)
+ end = par + strlen(par);
+
+ val = strchr(par, '=');
+ if (val > end)
+ val = NULL;
+ if (!val && !strncmp(par, "no", 2)) {
+ path = libxl__sprintf(gc, "/params/%s", par + 2);
+ path[end - par - 2 + 8] = 0;
+ val = "no";
+ par += 2;
+ } else {
+ path = libxl__sprintf(gc, "/params/%s", par);
+ path[val - par + 8] = 0;
+ val = libxl__strndup(gc, val + 1, end - val - 1);
+ }
+
+ LOG(DEBUG, "setting node \"%s\" to value \"%s\"", path, val);
+ ret = xenhypfs_write(hypfs, path, val);
+ if (ret < 0) {
+ LOGE(ERROR, "setting parameters");
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ while (isblank(*end))
+ end++;
+ }
+
+ ret = 0;
+
+out:
+ xenhypfs_close(hypfs);
+ GC_FREE;
+ return ret;
+}
+
+static int fd_set_flags(libxl_ctx *ctx, int fd,
+ int fcntlgetop, int fcntlsetop, const char *fl,
+ int flagmask, int set_p)
+{
+ int flags, r;
+ GC_INIT(ctx);
+
+ flags = fcntl(fd, fcntlgetop);
+ if (flags == -1) {
+ LOGE(ERROR, "fcntl(,F_GET%s) failed", fl);
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+
+ if (set_p)
+ flags |= flagmask;
+ else
+ flags &= ~flagmask;
+
+ r = fcntl(fd, fcntlsetop, flags);
+ if (r == -1) {
+ LOGE(ERROR, "fcntl(,F_SET%s) failed", fl);
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+
+ GC_FREE;
+ return 0;
+}
+
+int libxl_fd_set_cloexec(libxl_ctx *ctx, int fd, int cloexec)
+ { return fd_set_flags(ctx,fd, F_GETFD,F_SETFD,"FD", FD_CLOEXEC, cloexec); }
+
+int libxl_fd_set_nonblock(libxl_ctx *ctx, int fd, int nonblock)
+ { return fd_set_flags(ctx,fd, F_GETFL,F_SETFL,"FL", O_NONBLOCK, nonblock); }
+
+int libxl__fd_flags_modify_save(libxl__gc *gc, int fd,
+ int mask, int val, int *r_oldflags)
+{
+ int rc, ret, fdfl;
+
+ fdfl = fcntl(fd, F_GETFL);
+ if (fdfl < 0) {
+ LOGE(ERROR, "failed to fcntl.F_GETFL for fd %d", fd);
+ rc = ERROR_FAIL;
+ goto out_err;
+ }
+
+ LOG(DEBUG, "fnctl F_GETFL flags for fd %d are 0x%x", fd, fdfl);
+
+ if (r_oldflags)
+ *r_oldflags = fdfl;
+
+ fdfl &= mask;
+ fdfl |= val;
+
+ LOG(DEBUG, "fnctl F_SETFL of fd %d to 0x%x", fd, fdfl);
+
+ ret = fcntl(fd, F_SETFL, fdfl);
+ if (ret < 0) {
+ LOGE(ERROR, "failed to fcntl.F_SETFL for fd %d", fd);
+ rc = ERROR_FAIL;
+ goto out_err;
+ }
+
+ rc = 0;
+
+out_err:
+ return rc;
+}
+
+int libxl__fd_flags_restore(libxl__gc *gc, int fd, int fdfl)
+{
+ int ret, rc;
+
+ LOG(DEBUG, "fnctl F_SETFL of fd %d to 0x%x", fd, fdfl);
+
+ ret = fcntl(fd, F_SETFL, fdfl);
+ if (ret < 0) {
+ LOGE(ERROR, "failed to fcntl.F_SETFL for fd %d", fd);
+ rc = ERROR_FAIL;
+ goto out_err;
+ }
+
+ rc = 0;
+
+out_err:
+ return rc;
+
+}
+
+void libxl_hwcap_copy(libxl_ctx *ctx,libxl_hwcap *dst, const libxl_hwcap *src)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ (*dst)[i] = (*src)[i];
+}
+
+void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src)
+{
+ int i;
+
+ for (i = 0; i < 6; i++)
+ (*dst)[i] = (*src)[i];
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2017 Aporeto
+ * Author Stefano Stabellini <stefano@aporeto.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+static int libxl__device_p9_setdefault(libxl__gc *gc, uint32_t domid,
+ libxl_device_p9 *p9, bool hotplug)
+{
+ return libxl__resolve_domid(gc, p9->backend_domname, &p9->backend_domid);
+}
+
+static int libxl__set_xenstore_p9(libxl__gc *gc, uint32_t domid,
+ libxl_device_p9 *p9,
+ flexarray_t *back, flexarray_t *front,
+ flexarray_t *ro_front)
+{
+ flexarray_append_pair(back, "path", p9->path);
+ flexarray_append_pair(back, "security_model", p9->security_model);
+
+ flexarray_append_pair(front, "tag", p9->tag);
+
+ return 0;
+}
+
+#define libxl__add_p9s NULL
+#define libxl_device_p9_list NULL
+#define libxl_device_p9_compare NULL
+
+static LIBXL_DEFINE_UPDATE_DEVID(p9)
+static LIBXL_DEFINE_DEVICE_FROM_TYPE(p9)
+
+LIBXL_DEFINE_DEVICE_REMOVE(p9)
+
+DEFINE_DEVICE_TYPE_STRUCT(p9, 9PFS,
+ .skip_attach = 1,
+ .set_xenstore_config = (device_set_xenstore_config_fn_t)
+ libxl__set_xenstore_p9,
+);
--- /dev/null
+/*
+ * Copyright (C) 2010 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*----- xswait -----*/
+
+static libxl__ev_xswatch_callback xswait_xswatch_callback;
+static libxl__ev_time_callback xswait_timeout_callback;
+static void xswait_report_error(libxl__egc*, libxl__xswait_state*, int rc);
+
+void libxl__xswait_init(libxl__xswait_state *xswa)
+{
+ libxl__ev_time_init(&xswa->time_ev);
+ libxl__ev_xswatch_init(&xswa->watch_ev);
+}
+
+void libxl__xswait_stop(libxl__gc *gc, libxl__xswait_state *xswa)
+{
+ libxl__ev_time_deregister(gc, &xswa->time_ev);
+ libxl__ev_xswatch_deregister(gc, &xswa->watch_ev);
+}
+
+bool libxl__xswait_inuse(const libxl__xswait_state *xswa)
+{
+ bool time_inuse = libxl__ev_time_isregistered(&xswa->time_ev);
+ bool watch_inuse = libxl__ev_xswatch_isregistered(&xswa->watch_ev);
+ assert(time_inuse == watch_inuse);
+ return time_inuse;
+}
+
+int libxl__xswait_start(libxl__gc *gc, libxl__xswait_state *xswa)
+{
+ int rc;
+
+ rc = libxl__ev_time_register_rel(xswa->ao, &xswa->time_ev,
+ xswait_timeout_callback, xswa->timeout_ms);
+ if (rc) goto err;
+
+ rc = libxl__ev_xswatch_register(gc, &xswa->watch_ev,
+ xswait_xswatch_callback, xswa->path);
+ if (rc) goto err;
+
+ return 0;
+
+ err:
+ libxl__xswait_stop(gc, xswa);
+ return rc;
+}
+
+void xswait_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *xsw,
+ const char *watch_path, const char *event_path)
+{
+ EGC_GC;
+ libxl__xswait_state *xswa = CONTAINER_OF(xsw, *xswa, watch_ev);
+ int rc;
+ const char *data;
+
+ if (xswa->path[0] == '@') {
+ data = 0;
+ } else {
+ rc = libxl__xs_read_checked(gc, XBT_NULL, xswa->path, &data);
+ if (rc) { xswait_report_error(egc, xswa, rc); return; }
+ }
+
+ xswa->callback(egc, xswa, 0, data);
+}
+
+void xswait_timeout_callback(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ EGC_GC;
+ libxl__xswait_state *xswa = CONTAINER_OF(ev, *xswa, time_ev);
+ LOG(DEBUG, "%s: xswait timeout (path=%s)", xswa->what, xswa->path);
+ xswait_report_error(egc, xswa, rc);
+}
+
+static void xswait_report_error(libxl__egc *egc, libxl__xswait_state *xswa,
+ int rc)
+{
+ EGC_GC;
+ libxl__xswait_stop(gc, xswa);
+ xswa->callback(egc, xswa, rc, 0);
+}
+
+
+/*----- data copier -----*/
+
+void libxl__datacopier_init(libxl__datacopier_state *dc)
+{
+ assert(dc->ao);
+ libxl__ao_abortable_init(&dc->abrt);
+ libxl__ev_fd_init(&dc->toread);
+ libxl__ev_fd_init(&dc->towrite);
+ LIBXL_TAILQ_INIT(&dc->bufs);
+}
+
+void libxl__datacopier_kill(libxl__datacopier_state *dc)
+{
+ STATE_AO_GC(dc->ao);
+ libxl__datacopier_buf *buf, *tbuf;
+
+ libxl__ao_abortable_deregister(&dc->abrt);
+ libxl__ev_fd_deregister(gc, &dc->toread);
+ libxl__ev_fd_deregister(gc, &dc->towrite);
+ LIBXL_TAILQ_FOREACH_SAFE(buf, &dc->bufs, entry, tbuf)
+ free(buf);
+ LIBXL_TAILQ_INIT(&dc->bufs);
+}
+
+static void datacopier_callback(libxl__egc *egc, libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval)
+{
+ libxl__datacopier_kill(dc);
+ dc->callback(egc, dc, rc, onwrite, errnoval);
+}
+
+static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev,
+ int fd, short events, short revents);
+
+static void datacopier_check_state(libxl__egc *egc, libxl__datacopier_state *dc)
+{
+ STATE_AO_GC(dc->ao);
+ int rc;
+
+ if (dc->used && !dc->readbuf) {
+ if (!libxl__ev_fd_isregistered(&dc->towrite)) {
+ rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable,
+ dc->writefd, POLLOUT);
+ if (rc) {
+ LOG(ERROR, "unable to establish write event on %s"
+ " during copy of %s", dc->writewhat, dc->copywhat);
+ datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
+ return;
+ }
+ }
+ } else if (!libxl__ev_fd_isregistered(&dc->toread) ||
+ dc->bytes_to_read == 0) {
+ /* we have had eof */
+ datacopier_callback(egc, dc, 0, 0, 0);
+ return;
+ } else {
+ /* nothing buffered, but still reading */
+ libxl__ev_fd_deregister(gc, &dc->towrite);
+ }
+}
+
+void libxl__datacopier_prefixdata(libxl__egc *egc, libxl__datacopier_state *dc,
+ const void *data, size_t len)
+{
+ EGC_GC;
+ libxl__datacopier_buf *buf;
+ const uint8_t *ptr;
+
+ /*
+ * It is safe for this to be called immediately after _start, as
+ * is documented in the public comment. _start's caller must have
+ * the ctx locked, so other threads don't get to mess with the
+ * contents, and the fd events cannot happen reentrantly. So we
+ * are guaranteed to beat the first data from the read fd.
+ */
+
+ assert(len < dc->maxsz - dc->used);
+
+ for (ptr = data; len; len -= buf->used, ptr += buf->used) {
+ buf = libxl__malloc(NOGC, sizeof(*buf));
+ buf->used = min(len, sizeof(buf->buf));
+ memcpy(buf->buf, ptr, buf->used);
+
+ dc->used += buf->used;
+ LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry);
+ }
+}
+
+static int datacopier_pollhup_handled(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int fd, short revents, int onwrite)
+{
+ STATE_AO_GC(dc->ao);
+
+ if (dc->callback_pollhup && (revents & POLLHUP)) {
+ LOG(DEBUG, "received POLLHUP on fd %d: %s during copy of %s",
+ fd, onwrite ? dc->writewhat : dc->readwhat, dc->copywhat);
+ libxl__datacopier_kill(dc);
+ dc->callback_pollhup(egc, dc, ERROR_FAIL, onwrite, -1);
+ return 1;
+ }
+ return 0;
+}
+
+static void datacopier_abort(libxl__egc *egc, libxl__ao_abortable *abrt,
+ int rc)
+{
+ libxl__datacopier_state *dc = CONTAINER_OF(abrt, *dc, abrt);
+ STATE_AO_GC(dc->ao);
+
+ datacopier_callback(egc, dc, rc, -1, 0);
+}
+
+static void datacopier_readable(libxl__egc *egc, libxl__ev_fd *ev,
+ int fd, short events, short revents) {
+ libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, toread);
+ STATE_AO_GC(dc->ao);
+
+ if (datacopier_pollhup_handled(egc, dc, fd, revents, 0))
+ return;
+
+ if (revents & ~(POLLIN|POLLHUP)) {
+ LOG(ERROR, "unexpected poll event 0x%x on fd %d (expected POLLIN "
+ "and/or POLLHUP) reading %s during copy of %s",
+ revents, fd, dc->readwhat, dc->copywhat);
+ datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
+ return;
+ }
+ assert(revents & (POLLIN|POLLHUP));
+ for (;;) {
+ libxl__datacopier_buf *buf = NULL;
+ int r;
+
+ if (dc->readbuf) {
+ r = read(ev->fd, dc->readbuf + dc->used, dc->bytes_to_read);
+ } else {
+ while (dc->used >= dc->maxsz) {
+ libxl__datacopier_buf *rm = LIBXL_TAILQ_FIRST(&dc->bufs);
+ dc->used -= rm->used;
+ assert(dc->used >= 0);
+ LIBXL_TAILQ_REMOVE(&dc->bufs, rm, entry);
+ free(rm);
+ }
+
+ buf = LIBXL_TAILQ_LAST(&dc->bufs, libxl__datacopier_bufs);
+ if (!buf || buf->used >= sizeof(buf->buf)) {
+ buf = libxl__malloc(NOGC, sizeof(*buf));
+ buf->used = 0;
+ LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry);
+ }
+ r = read(ev->fd, buf->buf + buf->used,
+ min_t(size_t, sizeof(buf->buf) - buf->used,
+ (dc->bytes_to_read == -1) ? SIZE_MAX : dc->bytes_to_read));
+ }
+ if (r < 0) {
+ if (errno == EINTR) continue;
+ assert(errno);
+ if (errno == EWOULDBLOCK) {
+ if (revents & POLLHUP) {
+ LOG(ERROR,
+ "poll reported HUP but fd read gave EWOULDBLOCK"
+ " on %s during copy of %s",
+ dc->readwhat, dc->copywhat);
+ datacopier_callback(egc, dc, ERROR_FAIL, -1, 0);
+ return;
+ }
+ break;
+ }
+ LOGE(ERROR, "error reading %s during copy of %s",
+ dc->readwhat, dc->copywhat);
+ datacopier_callback(egc, dc, ERROR_FAIL, 0, errno);
+ return;
+ }
+ if (r == 0) {
+ if (dc->callback_pollhup) {
+ /* It might be that this "eof" is actually a HUP. If
+ * the caller cares about the difference,
+ * double-check using poll(2). */
+ struct pollfd hupchk;
+ hupchk.fd = ev->fd;
+ hupchk.events = POLLIN;
+ hupchk.revents = 0;
+ r = poll(&hupchk, 1, 0);
+ if (r < 0)
+ LIBXL__EVENT_DISASTER(gc,
+ "unexpected failure polling fd for datacopier eof hup check",
+ errno, 0);
+ if (datacopier_pollhup_handled(egc, dc, fd, hupchk.revents, 0))
+ return;
+ }
+ libxl__ev_fd_deregister(gc, &dc->toread);
+ break;
+ }
+ if (dc->log) {
+ int wrote = fwrite(buf->buf + buf->used, 1, r, dc->log);
+ if (wrote != r) {
+ assert(ferror(dc->log));
+ assert(errno);
+ LOGE(ERROR, "error logging %s", dc->copywhat);
+ datacopier_callback(egc, dc, ERROR_FAIL, 0, errno);
+ return;
+ }
+ }
+ if (!dc->readbuf) {
+ buf->used += r;
+ assert(buf->used <= sizeof(buf->buf));
+ }
+ dc->used += r;
+ if (dc->bytes_to_read > 0)
+ dc->bytes_to_read -= r;
+ if (dc->bytes_to_read == 0)
+ break;
+ }
+ datacopier_check_state(egc, dc);
+}
+
+static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev,
+ int fd, short events, short revents) {
+ libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, towrite);
+ STATE_AO_GC(dc->ao);
+
+ if (datacopier_pollhup_handled(egc, dc, fd, revents, 1))
+ return;
+
+ if (revents & ~POLLOUT) {
+ LOG(ERROR, "unexpected poll event 0x%x on fd %d (should be POLLOUT)"
+ " writing %s during copy of %s",
+ revents, fd, dc->writewhat, dc->copywhat);
+ datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
+ return;
+ }
+ assert(revents & POLLOUT);
+ for (;;) {
+ libxl__datacopier_buf *buf = LIBXL_TAILQ_FIRST(&dc->bufs);
+ if (!buf)
+ break;
+ if (!buf->used) {
+ LIBXL_TAILQ_REMOVE(&dc->bufs, buf, entry);
+ free(buf);
+ continue;
+ }
+ int r = write(ev->fd, buf->buf, buf->used);
+ if (r < 0) {
+ if (errno == EINTR) continue;
+ if (errno == EWOULDBLOCK) break;
+ assert(errno);
+ LOGE(ERROR, "error writing to %s during copy of %s",
+ dc->writewhat, dc->copywhat);
+ datacopier_callback(egc, dc, ERROR_FAIL, 1, errno);
+ return;
+ }
+ assert(r > 0);
+ assert(r <= buf->used);
+ buf->used -= r;
+ dc->used -= r;
+ assert(dc->used >= 0);
+ memmove(buf->buf, buf->buf+r, buf->used);
+ }
+ datacopier_check_state(egc, dc);
+}
+
+int libxl__datacopier_start(libxl__datacopier_state *dc)
+{
+ int rc;
+ STATE_AO_GC(dc->ao);
+
+ libxl__datacopier_init(dc);
+
+ assert(dc->readfd >= 0 || dc->writefd >= 0);
+ assert(!(dc->readbuf && dc->bytes_to_read == -1));
+
+ dc->abrt.ao = ao;
+ dc->abrt.callback = datacopier_abort;
+ rc = libxl__ao_abortable_register(&dc->abrt);
+ if (rc) goto out;
+
+ if (dc->readfd >= 0) {
+ rc = libxl__ev_fd_register(gc, &dc->toread, datacopier_readable,
+ dc->readfd, POLLIN);
+ if (rc) goto out;
+ }
+
+ if (dc->writefd >= 0) {
+ rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable,
+ dc->writefd, POLLOUT);
+ if (rc) goto out;
+ }
+
+ return 0;
+
+ out:
+ libxl__datacopier_kill(dc);
+ return rc;
+}
+
+/*----- openpty -----*/
+
+/* implementation */
+
+static void openpty_cleanup(libxl__openpty_state *op)
+{
+ int i;
+
+ for (i=0; i<op->count; i++) {
+ libxl__openpty_result *res = &op->results[i];
+ libxl__carefd_close(res->master); res->master = 0;
+ libxl__carefd_close(res->slave); res->slave = 0;
+ }
+}
+
+static void openpty_exited(libxl__egc *egc, libxl__ev_child *child,
+ pid_t pid, int status) {
+ libxl__openpty_state *op = CONTAINER_OF(child, *op, child);
+ STATE_AO_GC(op->ao);
+
+ if (status) {
+ /* Perhaps the child gave us the fds and then exited nonzero.
+ * Well that would be odd but we don't really care. */
+ libxl_report_child_exitstatus(CTX, op->rc ? LIBXL__LOG_ERROR
+ : LIBXL__LOG_WARNING,
+ "openpty child", pid, status);
+ }
+ if (op->rc)
+ openpty_cleanup(op);
+ op->callback(egc, op);
+}
+
+int libxl__openptys(libxl__openpty_state *op,
+ struct termios *termp,
+ struct winsize *winp) {
+ /*
+ * This is completely crazy. openpty calls grantpt which the spec
+ * says may fork, and may not be called with a SIGCHLD handler.
+ * Now our application may have a SIGCHLD handler so that's bad.
+ * We could perhaps block it but we'd need to block it on all
+ * threads. This is just Too Hard.
+ *
+ * So instead, we run openpty in a child process. That child
+ * process then of course has only our own thread and our own
+ * signal handlers. We pass the fds back.
+ *
+ * Since our only current caller actually wants two ptys, we
+ * support calling openpty multiple times for a single fork.
+ */
+ STATE_AO_GC(op->ao);
+ int count = op->count;
+ int r, i, rc, sockets[2], ptyfds[count][2];
+ libxl__carefd *for_child = 0;
+ pid_t pid = -1;
+
+ for (i=0; i<count; i++) {
+ ptyfds[i][0] = ptyfds[i][1] = -1;
+ libxl__openpty_result *res = &op->results[i];
+ assert(!res->master);
+ assert(!res->slave);
+ }
+ sockets[0] = sockets[1] = -1; /* 0 is for us, 1 for our child */
+
+ libxl__carefd_begin();
+ r = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+ if (r) { sockets[0] = sockets[1] = -1; }
+ for_child = libxl__carefd_opened(CTX, sockets[1]);
+ if (r) { LOGE(ERROR,"socketpair failed"); rc = ERROR_FAIL; goto out; }
+
+ pid = libxl__ev_child_fork(gc, &op->child, openpty_exited);
+ if (pid == -1) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (!pid) {
+ /* child */
+ close(sockets[0]);
+ signal(SIGCHLD, SIG_DFL);
+
+ for (i=0; i<count; i++) {
+ r = openpty(&ptyfds[i][0], &ptyfds[i][1], NULL, termp, winp);
+ if (r) { LOGE(ERROR,"openpty failed"); _exit(-1); }
+ }
+ rc = libxl__sendmsg_fds(gc, sockets[1], '\0',
+ 2*count, &ptyfds[0][0], "ptys");
+ if (rc) { LOGE(ERROR,"sendmsg to parent failed"); _exit(-1); }
+ _exit(0);
+ }
+
+ libxl__carefd_close(for_child);
+ for_child = 0;
+
+ /* this should be fast so do it synchronously */
+
+ libxl__carefd_begin();
+ char buf[1];
+ rc = libxl__recvmsg_fds(gc, sockets[0], buf,1,
+ 2*count, &ptyfds[0][0], "ptys");
+ if (!rc) {
+ for (i=0; i<count; i++) {
+ libxl__openpty_result *res = &op->results[i];
+ res->master = libxl__carefd_record(CTX, ptyfds[i][0]);
+ res->slave = libxl__carefd_record(CTX, ptyfds[i][1]);
+ }
+ }
+ /* now the pty fds are in the carefds, if they were ever open */
+ libxl__carefd_unlock();
+ if (rc)
+ goto out;
+
+ rc = 0;
+
+ out:
+ if (sockets[0] >= 0) close(sockets[0]);
+ libxl__carefd_close(for_child);
+ if (libxl__ev_child_inuse(&op->child)) {
+ op->rc = rc;
+ /* we will get a callback when the child dies */
+ return 0;
+ }
+
+ assert(rc);
+ openpty_cleanup(op);
+ return rc;
+}
+
+/*----- async exec -----*/
+
+static void async_exec_timeout(libxl__egc *egc,
+ libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ libxl__async_exec_state *aes = CONTAINER_OF(ev, *aes, time);
+ STATE_AO_GC(aes->ao);
+
+ if (!aes->rc)
+ aes->rc = rc;
+
+ libxl__ev_time_deregister(gc, &aes->time);
+
+ assert(libxl__ev_child_inuse(&aes->child));
+ LOG(ERROR, "killing execution of %s because of timeout", aes->what);
+
+ if (kill(aes->child.pid, SIGKILL)) {
+ LOGEV(ERROR, errno, "unable to kill %s [%ld]",
+ aes->what, (unsigned long)aes->child.pid);
+ }
+
+ return;
+}
+
+static void async_exec_done(libxl__egc *egc,
+ libxl__ev_child *child,
+ pid_t pid, int status)
+{
+ libxl__async_exec_state *aes = CONTAINER_OF(child, *aes, child);
+ STATE_AO_GC(aes->ao);
+
+ libxl__ev_time_deregister(gc, &aes->time);
+
+ if (status) {
+ if (!aes->rc)
+ libxl_report_child_exitstatus(CTX, LIBXL__LOG_ERROR,
+ aes->what, pid, status);
+ }
+
+ aes->callback(egc, aes, aes->rc, status);
+}
+
+void libxl__async_exec_init(libxl__async_exec_state *aes)
+{
+ libxl__ev_time_init(&aes->time);
+ libxl__ev_child_init(&aes->child);
+}
+
+int libxl__async_exec_start(libxl__async_exec_state *aes)
+{
+ pid_t pid;
+
+ /* Convenience aliases */
+ libxl__ao *ao = aes->ao;
+ AO_GC;
+ libxl__ev_child *const child = &aes->child;
+ char ** const args = aes->args;
+
+ aes->rc = 0;
+
+ /* Set execution timeout */
+ if (libxl__ev_time_register_rel(ao, &aes->time,
+ async_exec_timeout,
+ aes->timeout_ms)) {
+ LOG(ERROR, "unable to register timeout for executing: %s", aes->what);
+ goto out;
+ }
+
+ LOG(DEBUG, "forking to execute: %s ", aes->what);
+
+ /* Fork and exec */
+ pid = libxl__ev_child_fork(gc, child, async_exec_done);
+ if (pid == -1) {
+ LOG(ERROR, "unable to fork");
+ goto out;
+ }
+
+ if (!pid) {
+ /* child */
+ libxl__exec(gc, aes->stdfds[0], aes->stdfds[1],
+ aes->stdfds[2], args[0], args, aes->env);
+ }
+
+ return 0;
+
+out:
+ return ERROR_FAIL;
+}
+
+bool libxl__async_exec_inuse(const libxl__async_exec_state *aes)
+{
+ bool time_inuse = libxl__ev_time_isregistered(&aes->time);
+ bool child_inuse = libxl__ev_child_inuse(&aes->child);
+ assert(time_inuse == child_inuse);
+ return child_inuse;
+}
+
+void libxl__kill(libxl__gc *gc, pid_t pid, int sig, const char *what)
+{
+ int r = kill(pid, sig);
+ if (r) LOGE(WARN, "failed to kill() %s [%lu] (signal %d)",
+ what, (unsigned long)pid, sig);
+}
+
+/* Generic function to signal (HUP) a pid stored in xenstore */
+int libxl__kill_xs_path(libxl__gc *gc, const char *xs_path_pid,
+ const char *what)
+{
+ const char *xs_pid;
+ int ret, pid;
+
+ ret = libxl__xs_read_checked(gc, XBT_NULL, xs_path_pid, &xs_pid);
+ if (ret || !xs_pid) {
+ LOG(ERROR, "unable to find %s pid in %s", what, xs_path_pid);
+ ret = ret ? : ERROR_FAIL;
+ goto out;
+ }
+ pid = atoi(xs_pid);
+
+ ret = kill(pid, SIGHUP);
+ if (ret < 0 && errno == ESRCH) {
+ LOG(ERROR, "%s already exited", what);
+ ret = 0;
+ } else if (ret == 0) {
+ LOG(DEBUG, "%s signaled", what);
+ ret = 0;
+ } else {
+ LOGE(ERROR, "failed to kill %s [%d]", what, pid);
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2012 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#ifndef LIBXL_ARCH_H
+#define LIBXL_ARCH_H
+
+/* fill the arch specific configuration for the domain */
+_hidden
+int libxl__arch_domain_prepare_config(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ struct xen_domctl_createdomain *config);
+
+/* save the arch specific configuration for the domain */
+_hidden
+int libxl__arch_domain_save_config(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ libxl__domain_build_state *state,
+ const struct xen_domctl_createdomain *config);
+
+/* arch specific internal domain creation function */
+_hidden
+int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
+ uint32_t domid);
+
+/* setup arch specific hardware description, i.e. DTB on ARM */
+_hidden
+int libxl__arch_domain_init_hw_description(libxl__gc *gc,
+ libxl_domain_build_info *info,
+ libxl__domain_build_state *state,
+ struct xc_dom_image *dom);
+/* finalize arch specific hardware description. */
+_hidden
+int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_config *d_config,
+ struct xc_dom_image *dom);
+
+/* perform any pending hardware initialization */
+_hidden
+int libxl__arch_build_dom_finish(libxl__gc *gc,
+ libxl_domain_build_info *info,
+ struct xc_dom_image *dom,
+ libxl__domain_build_state *state);
+
+/* build vNUMA vmemrange with arch specific information */
+_hidden
+int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_build_info *b_info,
+ libxl__domain_build_state *state);
+
+/* arch specific irq map function */
+_hidden
+int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq);
+
+_hidden
+void libxl__arch_domain_create_info_setdefault(libxl__gc *gc,
+ libxl_domain_create_info *c_info);
+
+_hidden
+void libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
+ libxl_domain_build_info *b_info);
+
+_hidden
+int libxl__arch_passthrough_mode_setdefault(libxl__gc *gc,
+ uint32_t domid /* for logging, only */,
+ libxl_domain_config *d_config,
+ const libxl_physinfo *physinfo);
+
+_hidden
+int libxl__arch_extra_memory(libxl__gc *gc,
+ const libxl_domain_build_info *info,
+ uint64_t *out);
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#define LAPIC_BASE_ADDRESS 0xfee00000
+#define ACPI_INFO_PHYSICAL_ADDRESS 0xfc000000
+
+int libxl__dom_load_acpi(libxl__gc *gc,
+ const libxl_domain_build_info *b_info,
+ struct xc_dom_image *dom);
+#endif
+
+#endif
--- /dev/null
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+#include "libxl_libfdt_compat.h"
+#include "libxl_arm.h"
+
+#include <xenctrl_dom.h>
+#include <stdbool.h>
+#include <libfdt.h>
+#include <assert.h>
+#include <xen/device_tree_defs.h>
+
+static const char *gicv_to_string(libxl_gic_version gic_version)
+{
+ switch (gic_version) {
+ case LIBXL_GIC_VERSION_V2:
+ return "V2";
+ case LIBXL_GIC_VERSION_V3:
+ return "V3";
+ default:
+ return "unknown";
+ }
+}
+
+int libxl__arch_domain_prepare_config(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ struct xen_domctl_createdomain *config)
+{
+ uint32_t nr_spis = 0;
+ unsigned int i;
+ uint32_t vuart_irq;
+ bool vuart_enabled = false;
+
+ /*
+ * If pl011 vuart is enabled then increment the nr_spis to allow allocation
+ * of SPI VIRQ for pl011.
+ */
+ if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
+ nr_spis += (GUEST_VPL011_SPI - 32) + 1;
+ vuart_irq = GUEST_VPL011_SPI;
+ vuart_enabled = true;
+ }
+
+ for (i = 0; i < d_config->b_info.num_irqs; i++) {
+ uint32_t irq = d_config->b_info.irqs[i];
+ uint32_t spi;
+
+ /*
+ * This check ensures the if user has requested pass-through of a certain irq
+ * which conflicts with vpl011 irq then it flags an error to indicate to the
+ * user that the specific HW irq cannot be used as it is dedicated for vpl011.
+ *
+ * TODO:
+ * The vpl011 irq should be assigned such that it never conflicts with user
+ * specified irqs thereby preventing its pass-through. This TODO is for
+ * implementing that logic in future.
+ */
+ if (vuart_enabled && irq == vuart_irq) {
+ LOG(ERROR, "Physical IRQ %u conflicting with pl011 SPI\n", irq);
+ return ERROR_FAIL;
+ }
+
+ if (irq < 32)
+ continue;
+
+ spi = irq - 32;
+
+ if (nr_spis <= spi)
+ nr_spis = spi + 1;
+ }
+
+ LOG(DEBUG, "Configure the domain");
+
+ config->arch.nr_spis = nr_spis;
+ LOG(DEBUG, " - Allocate %u SPIs", nr_spis);
+
+ switch (d_config->b_info.arch_arm.gic_version) {
+ case LIBXL_GIC_VERSION_DEFAULT:
+ config->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_NATIVE;
+ break;
+ case LIBXL_GIC_VERSION_V2:
+ config->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_V2;
+ break;
+ case LIBXL_GIC_VERSION_V3:
+ config->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_V3;
+ break;
+ default:
+ LOG(ERROR, "Unknown GIC version %d",
+ d_config->b_info.arch_arm.gic_version);
+ return ERROR_FAIL;
+ }
+
+ switch (d_config->b_info.tee) {
+ case LIBXL_TEE_TYPE_NONE:
+ config->arch.tee_type = XEN_DOMCTL_CONFIG_TEE_NONE;
+ break;
+ case LIBXL_TEE_TYPE_OPTEE:
+ config->arch.tee_type = XEN_DOMCTL_CONFIG_TEE_OPTEE;
+ break;
+ default:
+ LOG(ERROR, "Unknown TEE type %d",
+ d_config->b_info.tee);
+ return ERROR_FAIL;
+ }
+
+ return 0;
+}
+
+int libxl__arch_domain_save_config(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ libxl__domain_build_state *state,
+ const struct xen_domctl_createdomain *config)
+{
+ switch (config->arch.gic_version) {
+ case XEN_DOMCTL_CONFIG_GIC_V2:
+ d_config->b_info.arch_arm.gic_version = LIBXL_GIC_VERSION_V2;
+ break;
+ case XEN_DOMCTL_CONFIG_GIC_V3:
+ d_config->b_info.arch_arm.gic_version = LIBXL_GIC_VERSION_V3;
+ break;
+ default:
+ LOG(ERROR, "Unexpected gic version %u", config->arch.gic_version);
+ return ERROR_FAIL;
+ }
+
+ state->clock_frequency = config->arch.clock_frequency;
+
+ return 0;
+}
+
+int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
+ uint32_t domid)
+{
+ return 0;
+}
+
+int libxl__arch_extra_memory(libxl__gc *gc,
+ const libxl_domain_build_info *info,
+ uint64_t *out)
+{
+ int rc = 0;
+ uint64_t size = 0;
+
+ if (libxl_defbool_val(info->acpi)) {
+ rc = libxl__get_acpi_size(gc, info, &size);
+ if (rc < 0) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ *out = LIBXL_MAXMEM_CONSTANT + DIV_ROUNDUP(size, 1024);
+out:
+ return rc;
+}
+
+static struct arch_info {
+ const char *guest_type;
+ const char *timer_compat;
+ const char *cpu_compat;
+} arch_info[] = {
+ {"xen-3.0-armv7l", "arm,armv7-timer", "arm,cortex-a15" },
+ {"xen-3.0-aarch64", "arm,armv8-timer", "arm,armv8" },
+};
+
+typedef uint32_t be32;
+typedef be32 gic_interrupt[3];
+
+#define PROP_INITRD_START "linux,initrd-start"
+#define PROP_INITRD_END "linux,initrd-end"
+
+static void set_cell(be32 **cellp, int size, uint64_t val)
+{
+ int cells = size;
+
+ while (size--) {
+ (*cellp)[size] = cpu_to_fdt32(val);
+ val >>= 32;
+ }
+
+ (*cellp) += cells;
+}
+
+static void set_interrupt(gic_interrupt interrupt, unsigned int irq,
+ unsigned int cpumask, unsigned int level)
+{
+ be32 *cells = interrupt;
+ int is_ppi = (irq < 32);
+
+ /* SGIs are not describe in the device tree */
+ assert(irq >= 16);
+
+ irq -= (is_ppi) ? 16: 32; /* PPIs start at 16, SPIs at 32 */
+
+ /* See linux Documentation/devictree/bindings/arm/gic.txt */
+ set_cell(&cells, 1, is_ppi); /* is a PPI? */
+ set_cell(&cells, 1, irq);
+ set_cell(&cells, 1, (cpumask << 8) | level);
+}
+
+static void set_range(be32 **cellp,
+ int address_cells, int size_cells,
+ uint64_t address, uint64_t size)
+{
+ set_cell(cellp, address_cells, address);
+ set_cell(cellp, size_cells, size);
+}
+
+static int fdt_property_compat(libxl__gc *gc, void *fdt, unsigned nr_compat, ...)
+{
+ const char *compats[nr_compat];
+ int i;
+ size_t sz;
+ va_list ap;
+ char *compat, *p;
+
+ va_start(ap, nr_compat);
+ sz = 0;
+ for (i = 0; i < nr_compat; i++) {
+ const char *c = va_arg(ap, const char *);
+ compats[i] = c;
+ sz += strlen(compats[i]) + 1;
+ }
+ va_end(ap);
+
+ p = compat = libxl__zalloc(gc, sz);
+ for (i = 0; i < nr_compat; i++) {
+ strcpy(p, compats[i]);
+ p += strlen(compats[i]) + 1;
+ }
+
+ return fdt_property(fdt, "compatible", compat, sz);
+}
+
+static int fdt_property_interrupts(libxl__gc *gc, void *fdt,
+ gic_interrupt *intr,
+ unsigned num_irq)
+{
+ int res;
+
+ res = fdt_property(fdt, "interrupts", intr, sizeof (intr[0]) * num_irq);
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "interrupt-parent", GUEST_PHANDLE_GIC);
+ if (res) return res;
+
+ return 0;
+}
+
+static int fdt_property_regs(libxl__gc *gc, void *fdt,
+ unsigned addr_cells,
+ unsigned size_cells,
+ unsigned num_regs, ...)
+{
+ uint32_t regs[num_regs*(addr_cells+size_cells)];
+ be32 *cells = ®s[0];
+ int i;
+ va_list ap;
+ uint64_t base, size;
+
+ va_start(ap, num_regs);
+ for (i = 0 ; i < num_regs; i++) {
+ base = addr_cells ? va_arg(ap, uint64_t) : 0;
+ size = size_cells ? va_arg(ap, uint64_t) : 0;
+ set_range(&cells, addr_cells, size_cells, base, size);
+ }
+ va_end(ap);
+
+ return fdt_property(fdt, "reg", regs, sizeof(regs));
+}
+
+static int make_root_properties(libxl__gc *gc,
+ const libxl_version_info *vers,
+ void *fdt)
+{
+ int res;
+
+ res = fdt_property_string(fdt, "model", GCSPRINTF("XENVM-%d.%d",
+ vers->xen_version_major,
+ vers->xen_version_minor));
+ if (res) return res;
+
+ res = fdt_property_compat(gc, fdt, 2,
+ GCSPRINTF("xen,xenvm-%d.%d",
+ vers->xen_version_major,
+ vers->xen_version_minor),
+ "xen,xenvm");
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "interrupt-parent", GUEST_PHANDLE_GIC);
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "#address-cells", GUEST_ROOT_ADDRESS_CELLS);
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "#size-cells", GUEST_ROOT_SIZE_CELLS);
+ if (res) return res;
+
+ return 0;
+}
+
+static int make_chosen_node(libxl__gc *gc, void *fdt, bool ramdisk,
+ libxl__domain_build_state *state,
+ const libxl_domain_build_info *info)
+{
+ int res;
+
+ /* See linux Documentation/devicetree/... */
+ res = fdt_begin_node(fdt, "chosen");
+ if (res) return res;
+
+ if (state->pv_cmdline) {
+ LOG(DEBUG, "/chosen/bootargs = %s", state->pv_cmdline);
+ res = fdt_property_string(fdt, "bootargs", state->pv_cmdline);
+ if (res) return res;
+ }
+
+ if (ramdisk) {
+ uint64_t dummy = 0;
+ LOG(DEBUG, "/chosen adding placeholder linux,initrd properties");
+ res = fdt_property(fdt, PROP_INITRD_START, &dummy, sizeof(dummy));
+ if (res) return res;
+ res = fdt_property(fdt, PROP_INITRD_END, &dummy, sizeof(dummy));
+ if (res) return res;
+ }
+
+ if (libxl_defbool_val(info->acpi)) {
+ const uint64_t acpi_base = GUEST_ACPI_BASE;
+ const char *name = GCSPRINTF("module@%"PRIx64, acpi_base);
+
+ res = fdt_begin_node(fdt, name);
+ if (res) return res;
+
+ res = fdt_property_compat(gc, fdt, 2, "xen,guest-acpi",
+ "multiboot,module");
+ if (res) return res;
+
+ res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+ 1, 0, 0);
+ if (res) return res;
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+ }
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+
+ return 0;
+}
+
+static int make_cpus_node(libxl__gc *gc, void *fdt, int nr_cpus,
+ const struct arch_info *ainfo)
+{
+ int res, i;
+ uint64_t mpidr_aff;
+
+ res = fdt_begin_node(fdt, "cpus");
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "#address-cells", 1);
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "#size-cells", 0);
+ if (res) return res;
+
+ for (i = 0; i < nr_cpus; i++) {
+ const char *name;
+
+ mpidr_aff = libxl__compute_mpdir(i);
+ name = GCSPRINTF("cpu@%"PRIx64, mpidr_aff);
+
+ res = fdt_begin_node(fdt, name);
+ if (res) return res;
+
+ res = fdt_property_string(fdt, "device_type", "cpu");
+ if (res) return res;
+
+ res = fdt_property_compat(gc, fdt, 1, ainfo->cpu_compat);
+ if (res) return res;
+
+ res = fdt_property_string(fdt, "enable-method", "psci");
+ if (res) return res;
+
+ res = fdt_property_regs(gc, fdt, 1, 0, 1, mpidr_aff);
+ if (res) return res;
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+ }
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+
+ return 0;
+}
+
+static int make_psci_node(libxl__gc *gc, void *fdt)
+{
+ int res;
+
+ res = fdt_begin_node(fdt, "psci");
+ if (res) return res;
+
+ res = fdt_property_compat(gc, fdt, 3, "arm,psci-1.0",
+ "arm,psci-0.2", "arm,psci");
+ if (res) return res;
+
+ res = fdt_property_string(fdt, "method", "hvc");
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "cpu_off", PSCI_cpu_off);
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "cpu_on", PSCI_cpu_on);
+ if (res) return res;
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+
+ return 0;
+}
+
+static int make_optee_node(libxl__gc *gc, void *fdt)
+{
+ int res;
+ LOG(DEBUG, "Creating OP-TEE node in dtb");
+
+ res = fdt_begin_node(fdt, "firmware");
+ if (res) return res;
+
+ res = fdt_begin_node(fdt, "optee");
+ if (res) return res;
+
+ res = fdt_property_compat(gc, fdt, 1, "linaro,optee-tz");
+ if (res) return res;
+
+ res = fdt_property_string(fdt, "method", "hvc");
+ if (res) return res;
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+
+ return 0;
+}
+
+static int make_memory_nodes(libxl__gc *gc, void *fdt,
+ const struct xc_dom_image *dom)
+{
+ int res, i;
+ const char *name;
+ const uint64_t bankbase[] = GUEST_RAM_BANK_BASES;
+
+ for (i = 0; i < GUEST_RAM_BANKS; i++) {
+ name = GCSPRINTF("memory@%"PRIx64, bankbase[i]);
+
+ LOG(DEBUG, "Creating placeholder node /%s", name);
+
+ res = fdt_begin_node(fdt, name);
+ if (res) return res;
+
+ res = fdt_property_string(fdt, "device_type", "memory");
+ if (res) return res;
+
+ res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+ 1, 0, 0);
+ if (res) return res;
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+ }
+
+ return 0;
+}
+
+static int make_gicv2_node(libxl__gc *gc, void *fdt,
+ uint64_t gicd_base, uint64_t gicd_size,
+ uint64_t gicc_base, uint64_t gicc_size)
+{
+ int res;
+ const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base);
+
+ res = fdt_begin_node(fdt, name);
+ if (res) return res;
+
+ res = fdt_property_compat(gc, fdt, 2,
+ "arm,cortex-a15-gic",
+ "arm,cortex-a9-gic");
+ if (res) return res;
+
+
+ res = fdt_property_cell(fdt, "#interrupt-cells", 3);
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "#address-cells", 0);
+ if (res) return res;
+
+ res = fdt_property(fdt, "interrupt-controller", NULL, 0);
+ if (res) return res;
+
+ res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+ 2,
+ gicd_base, gicd_size,
+ gicc_base, gicc_size);
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "linux,phandle", GUEST_PHANDLE_GIC);
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "phandle", GUEST_PHANDLE_GIC);
+ if (res) return res;
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+
+ return 0;
+}
+
+static int make_gicv3_node(libxl__gc *gc, void *fdt)
+{
+ int res;
+ const uint64_t gicd_base = GUEST_GICV3_GICD_BASE;
+ const uint64_t gicd_size = GUEST_GICV3_GICD_SIZE;
+ const uint64_t gicr0_base = GUEST_GICV3_GICR0_BASE;
+ const uint64_t gicr0_size = GUEST_GICV3_GICR0_SIZE;
+ const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base);
+
+ res = fdt_begin_node(fdt, name);
+ if (res) return res;
+
+ res = fdt_property_compat(gc, fdt, 1, "arm,gic-v3");
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "#interrupt-cells", 3);
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "#address-cells", 0);
+ if (res) return res;
+
+ res = fdt_property(fdt, "interrupt-controller", NULL, 0);
+ if (res) return res;
+
+ res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+ 2,
+ gicd_base, gicd_size,
+ gicr0_base, gicr0_size);
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "linux,phandle", GUEST_PHANDLE_GIC);
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "phandle", GUEST_PHANDLE_GIC);
+ if (res) return res;
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+
+ return 0;
+}
+
+static int make_timer_node(libxl__gc *gc, void *fdt,
+ const struct arch_info *ainfo,
+ uint32_t frequency)
+{
+ int res;
+ gic_interrupt ints[3];
+
+ res = fdt_begin_node(fdt, "timer");
+ if (res) return res;
+
+ res = fdt_property_compat(gc, fdt, 1, ainfo->timer_compat);
+ if (res) return res;
+
+ set_interrupt(ints[0], GUEST_TIMER_PHYS_S_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
+ set_interrupt(ints[1], GUEST_TIMER_PHYS_NS_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
+ set_interrupt(ints[2], GUEST_TIMER_VIRT_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
+
+ res = fdt_property_interrupts(gc, fdt, ints, 3);
+ if (res) return res;
+
+ if ( frequency )
+ fdt_property_u32(fdt, "clock-frequency", frequency);
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+
+ return 0;
+}
+
+static int make_hypervisor_node(libxl__gc *gc, void *fdt,
+ const libxl_version_info *vers)
+{
+ int res;
+ gic_interrupt intr;
+
+ /* See linux Documentation/devicetree/bindings/arm/xen.txt */
+ res = fdt_begin_node(fdt, "hypervisor");
+ if (res) return res;
+
+ res = fdt_property_compat(gc, fdt, 2,
+ GCSPRINTF("xen,xen-%d.%d",
+ vers->xen_version_major,
+ vers->xen_version_minor),
+ "xen,xen");
+ if (res) return res;
+
+ /* reg 0 is grant table space */
+ res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+ 1,GUEST_GNTTAB_BASE, GUEST_GNTTAB_SIZE);
+ if (res) return res;
+
+ /*
+ * interrupts is evtchn upcall:
+ * - Active-low level-sensitive
+ * - All cpus
+ */
+ set_interrupt(intr, GUEST_EVTCHN_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
+
+ res = fdt_property_interrupts(gc, fdt, &intr, 1);
+ if (res) return res;
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+
+ return 0;
+}
+
+static int make_vpl011_uart_node(libxl__gc *gc, void *fdt,
+ const struct arch_info *ainfo,
+ struct xc_dom_image *dom)
+{
+ int res;
+ gic_interrupt intr;
+
+ res = fdt_begin_node(fdt, "sbsa-pl011");
+ if (res) return res;
+
+ res = fdt_property_compat(gc, fdt, 1, "arm,sbsa-uart");
+ if (res) return res;
+
+ res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+ 1,
+ GUEST_PL011_BASE, GUEST_PL011_SIZE);
+ if (res) return res;
+
+ set_interrupt(intr, GUEST_VPL011_SPI, 0xf, DT_IRQ_TYPE_LEVEL_HIGH);
+
+ res = fdt_property_interrupts(gc, fdt, &intr, 1);
+ if (res) return res;
+
+ /* Use a default baud rate of 115200. */
+ fdt_property_u32(fdt, "current-speed", 115200);
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+
+ return 0;
+}
+
+static const struct arch_info *get_arch_info(libxl__gc *gc,
+ const struct xc_dom_image *dom)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(arch_info); i++) {
+ const struct arch_info *info = &arch_info[i];
+ if (!strcmp(dom->guest_type, info->guest_type))
+ return info;
+ }
+ LOG(ERROR, "Unable to find arch FDT info for %s", dom->guest_type);
+ return NULL;
+}
+
+static void debug_dump_fdt(libxl__gc *gc, void *fdt)
+{
+ int fd = -1, rc, r;
+
+ const char *dtb = getenv("LIBXL_DEBUG_DUMP_DTB");
+
+ if (!dtb) goto out;
+
+ fd = open(dtb, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+ if (fd < 0) {
+ LOGE(DEBUG, "cannot open %s for LIBXL_DEBUG_DUMP_DTB", dtb);
+ goto out;
+ }
+
+ rc = libxl_write_exactly(CTX, fd, fdt, fdt_totalsize(fdt), dtb, "dtb");
+ if (rc < 0) goto out;
+
+out:
+ if (fd >= 0) {
+ r = close(fd);
+ if (r < 0) LOGE(DEBUG, "failed to close DTB debug dump output");
+ }
+}
+
+#ifdef ENABLE_PARTIAL_DEVICE_TREE
+
+static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size)
+{
+ int r;
+
+ if (fdt_magic(fdt) != FDT_MAGIC) {
+ LOG(ERROR, "Partial FDT is not a valid Flat Device Tree");
+ return ERROR_FAIL;
+ }
+
+ r = fdt_check_header(fdt);
+ if (r) {
+ LOG(ERROR, "Failed to check the partial FDT (%d)", r);
+ return ERROR_FAIL;
+ }
+
+ if (fdt_totalsize(fdt) > size) {
+ LOG(ERROR, "Partial FDT totalsize is too big");
+ return ERROR_FAIL;
+ }
+
+ return 0;
+}
+
+static int copy_properties(libxl__gc *gc, void *fdt, void *pfdt,
+ int nodeoff)
+{
+ int propoff, nameoff, r;
+ const struct fdt_property *prop;
+
+ for (propoff = fdt_first_property_offset(pfdt, nodeoff);
+ propoff >= 0;
+ propoff = fdt_next_property_offset(pfdt, propoff)) {
+
+ if (!(prop = fdt_get_property_by_offset(pfdt, propoff, NULL))) {
+ return -FDT_ERR_INTERNAL;
+ }
+
+ nameoff = fdt32_to_cpu(prop->nameoff);
+ r = fdt_property(fdt, fdt_string(pfdt, nameoff),
+ prop->data, fdt32_to_cpu(prop->len));
+ if (r) return r;
+ }
+
+ /* FDT_ERR_NOTFOUND => There is no more properties for this node */
+ return (propoff != -FDT_ERR_NOTFOUND)? propoff : 0;
+}
+
+/* Copy a node from the partial device tree to the guest device tree */
+static int copy_node(libxl__gc *gc, void *fdt, void *pfdt,
+ int nodeoff, int depth)
+{
+ int r;
+
+ r = fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL));
+ if (r) return r;
+
+ r = copy_properties(gc, fdt, pfdt, nodeoff);
+ if (r) return r;
+
+ for (nodeoff = fdt_first_subnode(pfdt, nodeoff);
+ nodeoff >= 0;
+ nodeoff = fdt_next_subnode(pfdt, nodeoff)) {
+ r = copy_node(gc, fdt, pfdt, nodeoff, depth + 1);
+ if (r) return r;
+ }
+
+ if (nodeoff != -FDT_ERR_NOTFOUND)
+ return nodeoff;
+
+ r = fdt_end_node(fdt);
+ if (r) return r;
+
+ return 0;
+}
+
+static int copy_node_by_path(libxl__gc *gc, const char *path,
+ void *fdt, void *pfdt)
+{
+ int nodeoff, r;
+ const char *name = strrchr(path, '/');
+
+ if (!name)
+ return -FDT_ERR_INTERNAL;
+
+ name++;
+
+ /*
+ * The FDT function to look at a node doesn't take into account the
+ * unit (i.e anything after @) when search by name. Check if the
+ * name exactly matches.
+ */
+ nodeoff = fdt_path_offset(pfdt, path);
+ if (nodeoff < 0)
+ return nodeoff;
+
+ if (strcmp(fdt_get_name(pfdt, nodeoff, NULL), name))
+ return -FDT_ERR_NOTFOUND;
+
+ r = copy_node(gc, fdt, pfdt, nodeoff, 0);
+ if (r) return r;
+
+ return 0;
+}
+
+/*
+ * The partial device tree is not copied entirely. Only the relevant bits are
+ * copied to the guest device tree:
+ * - /passthrough node
+ * - /aliases node
+ */
+static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt)
+{
+ int r;
+
+ r = copy_node_by_path(gc, "/passthrough", fdt, pfdt);
+ if (r < 0) {
+ LOG(ERROR, "Can't copy the node \"/passthrough\" from the partial FDT");
+ return r;
+ }
+
+ r = copy_node_by_path(gc, "/aliases", fdt, pfdt);
+ if (r < 0 && r != -FDT_ERR_NOTFOUND) {
+ LOG(ERROR, "Can't copy the node \"/aliases\" from the partial FDT");
+ return r;
+ }
+
+ return 0;
+}
+
+#else
+
+static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size)
+{
+ LOG(ERROR, "partial device tree not supported");
+
+ return ERROR_FAIL;
+}
+
+static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt)
+{
+ /*
+ * We should never be here when the partial device tree is not
+ * supported.
+ * */
+ return -FDT_ERR_INTERNAL;
+}
+
+#endif /* ENABLE_PARTIAL_DEVICE_TREE */
+
+#define FDT_MAX_SIZE (1<<20)
+
+static int libxl__prepare_dtb(libxl__gc *gc, libxl_domain_build_info *info,
+ libxl__domain_build_state *state,
+ struct xc_dom_image *dom)
+{
+ void *fdt = NULL;
+ void *pfdt = NULL;
+ int rc, res;
+ size_t fdt_size = 0;
+ int pfdt_size = 0;
+
+ const libxl_version_info *vers;
+ const struct arch_info *ainfo;
+
+ vers = libxl_get_version_info(CTX);
+ if (vers == NULL) return ERROR_FAIL;
+
+ ainfo = get_arch_info(gc, dom);
+ if (ainfo == NULL) return ERROR_FAIL;
+
+ LOG(DEBUG, "constructing DTB for Xen version %d.%d guest",
+ vers->xen_version_major, vers->xen_version_minor);
+ LOG(DEBUG, " - vGIC version: %s",
+ gicv_to_string(info->arch_arm.gic_version));
+
+ if (info->device_tree) {
+ LOG(DEBUG, " - Partial device tree provided: %s", info->device_tree);
+
+ rc = libxl_read_file_contents(CTX, info->device_tree,
+ &pfdt, &pfdt_size);
+ if (rc) {
+ LOGEV(ERROR, rc, "failed to read the partial device file %s",
+ info->device_tree);
+ return ERROR_FAIL;
+ }
+ libxl__ptr_add(gc, pfdt);
+
+ if (check_partial_fdt(gc, pfdt, pfdt_size))
+ return ERROR_FAIL;
+ }
+
+/*
+ * Call "call" handling FDT_ERR_*. Will either:
+ * - loop back to retry_resize
+ * - set rc and goto out
+ * - fall through successfully
+ *
+ * On FDT_ERR_NOSPACE we start again from scratch rather than
+ * realloc+libfdt_open_into because "call" may have failed half way
+ * through a series of steps leaving the partial tree in an
+ * inconsistent state, e.g. leaving a node open.
+ */
+#define FDT( call ) do { \
+ int fdt_res = (call); \
+ if (fdt_res == -FDT_ERR_NOSPACE && fdt_size < FDT_MAX_SIZE) \
+ goto next_resize; \
+ else if (fdt_res < 0) { \
+ LOG(ERROR, "FDT: %s failed: %d = %s", \
+ #call, fdt_res, fdt_strerror(fdt_res)); \
+ rc = ERROR_FAIL; \
+ goto out; \
+ } \
+} while(0)
+
+ for (;;) {
+next_resize:
+ if (fdt_size) {
+ fdt_size <<= 1;
+ LOG(DEBUG, "Increasing FDT size to %zd and retrying", fdt_size);
+ } else {
+ fdt_size = 4096;
+ }
+
+ fdt = libxl__realloc(gc, fdt, fdt_size);
+
+ FDT( fdt_create(fdt, fdt_size) );
+
+ FDT( fdt_finish_reservemap(fdt) );
+
+ FDT( fdt_begin_node(fdt, "") );
+
+ FDT( make_root_properties(gc, vers, fdt) );
+ FDT( make_chosen_node(gc, fdt, !!dom->modules[0].blob, state, info) );
+ FDT( make_cpus_node(gc, fdt, info->max_vcpus, ainfo) );
+ FDT( make_psci_node(gc, fdt) );
+
+ FDT( make_memory_nodes(gc, fdt, dom) );
+
+ switch (info->arch_arm.gic_version) {
+ case LIBXL_GIC_VERSION_V2:
+ FDT( make_gicv2_node(gc, fdt,
+ GUEST_GICD_BASE, GUEST_GICD_SIZE,
+ GUEST_GICC_BASE, GUEST_GICC_SIZE) );
+ break;
+ case LIBXL_GIC_VERSION_V3:
+ FDT( make_gicv3_node(gc, fdt) );
+ break;
+ default:
+ LOG(ERROR, "Unknown GIC version %s",
+ gicv_to_string(info->arch_arm.gic_version));
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ FDT( make_timer_node(gc, fdt, ainfo, state->clock_frequency) );
+ FDT( make_hypervisor_node(gc, fdt, vers) );
+
+ if (info->arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART)
+ FDT( make_vpl011_uart_node(gc, fdt, ainfo, dom) );
+
+ if (info->tee == LIBXL_TEE_TYPE_OPTEE)
+ FDT( make_optee_node(gc, fdt) );
+
+ if (pfdt)
+ FDT( copy_partial_fdt(gc, fdt, pfdt) );
+
+ FDT( fdt_end_node(fdt) );
+
+ FDT( fdt_finish(fdt) );
+ break;
+ }
+#undef FDT
+
+ LOG(DEBUG, "fdt total size %d", fdt_totalsize(fdt));
+
+ res = xc_dom_devicetree_mem(dom, fdt, fdt_totalsize(fdt));
+ if (res) {
+ LOGE(ERROR, "xc_dom_devicetree_mem failed");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+int libxl__arch_domain_init_hw_description(libxl__gc *gc,
+ libxl_domain_build_info *info,
+ libxl__domain_build_state *state,
+ struct xc_dom_image *dom)
+{
+ int rc;
+ uint64_t val;
+
+ if (info->type != LIBXL_DOMAIN_TYPE_PVH) {
+ LOG(ERROR, "Unsupported Arm guest type %s",
+ libxl_domain_type_to_string(info->type));
+ return ERROR_INVAL;
+ }
+
+ /* Set the value of domain param HVM_PARAM_CALLBACK_IRQ. */
+ val = MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI,
+ HVM_PARAM_CALLBACK_IRQ_TYPE_MASK);
+ /* Active-low level-sensitive */
+ val |= MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_LOW_LEVEL,
+ HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_MASK);
+ val |= GUEST_EVTCHN_PPI;
+ rc = xc_hvm_param_set(dom->xch, dom->guest_domid, HVM_PARAM_CALLBACK_IRQ,
+ val);
+ if (rc)
+ return rc;
+
+ rc = libxl__prepare_dtb(gc, info, state, dom);
+ if (rc) goto out;
+
+ if (!libxl_defbool_val(info->acpi)) {
+ LOG(DEBUG, "Generating ACPI tables is disabled by user.");
+ rc = 0;
+ goto out;
+ }
+
+ if (strcmp(dom->guest_type, "xen-3.0-aarch64")) {
+ /* ACPI is only supported for 64-bit guest currently. */
+ LOG(ERROR, "Can not enable libxl option 'acpi' for %s", dom->guest_type);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__prepare_acpi(gc, info, dom);
+
+out:
+ return rc;
+}
+
+static void finalise_one_node(libxl__gc *gc, void *fdt, const char *uname,
+ uint64_t base, uint64_t size)
+{
+ int node, res;
+ const char *name = GCSPRINTF("%s@%"PRIx64, uname, base);
+
+ node = fdt_path_offset(fdt, name);
+ assert(node > 0);
+
+ if (size == 0) {
+ LOG(DEBUG, "Nopping out placeholder node %s", name);
+ fdt_nop_node(fdt, node);
+ } else {
+ uint32_t regs[GUEST_ROOT_ADDRESS_CELLS+GUEST_ROOT_SIZE_CELLS];
+ be32 *cells = ®s[0];
+
+ LOG(DEBUG, "Populating placeholder node %s", name);
+
+ set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS, base, size);
+
+ res = fdt_setprop_inplace(fdt, node, "reg", regs, sizeof(regs));
+ assert(!res);
+ }
+}
+
+int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_config *d_config,
+ struct xc_dom_image *dom)
+{
+ void *fdt = dom->devicetree_blob;
+ int i;
+ const uint64_t bankbase[] = GUEST_RAM_BANK_BASES;
+
+ const struct xc_dom_seg *ramdisk = dom->modules[0].blob ?
+ &dom->modules[0].seg : NULL;
+
+ if (ramdisk) {
+ int chosen, res;
+ uint64_t val;
+
+ /* Neither the fdt_path_offset() nor either of the
+ * fdt_setprop_inplace() calls can fail. If they do then
+ * make_chosen_node() (see above) has got something very
+ * wrong.
+ */
+ chosen = fdt_path_offset(fdt, "/chosen");
+ assert(chosen > 0);
+
+ LOG(DEBUG, "/chosen updating initrd properties to cover "
+ "%"PRIx64"-%"PRIx64,
+ ramdisk->vstart, ramdisk->vend);
+
+ val = cpu_to_fdt64(ramdisk->vstart);
+ res = fdt_setprop_inplace(fdt, chosen, PROP_INITRD_START,
+ &val, sizeof(val));
+ assert(!res);
+
+ val = cpu_to_fdt64(ramdisk->vend);
+ res = fdt_setprop_inplace(fdt, chosen, PROP_INITRD_END,
+ &val, sizeof(val));
+ assert(!res);
+
+ }
+
+ for (i = 0; i < GUEST_RAM_BANKS; i++) {
+ const uint64_t size = (uint64_t)dom->rambank_size[i] << XC_PAGE_SHIFT;
+
+ finalise_one_node(gc, fdt, "/memory", bankbase[i], size);
+ }
+
+ if (dom->acpi_modules[0].data) {
+ finalise_one_node(gc, fdt, "/chosen/module", GUEST_ACPI_BASE,
+ dom->acpi_modules[0].length);
+ }
+
+ debug_dump_fdt(gc, fdt);
+
+ return 0;
+}
+
+int libxl__arch_build_dom_finish(libxl__gc *gc,
+ libxl_domain_build_info *info,
+ struct xc_dom_image *dom,
+ libxl__domain_build_state *state)
+{
+ int rc = 0, ret;
+
+ if (info->arch_arm.vuart != LIBXL_VUART_TYPE_SBSA_UART) {
+ rc = 0;
+ goto out;
+ }
+
+ ret = xc_dom_vuart_init(CTX->xch,
+ XEN_DOMCTL_VUART_TYPE_VPL011,
+ dom->guest_domid,
+ dom->console_domid,
+ dom->vuart_gfn,
+ &state->vuart_port);
+ if (ret < 0) {
+ rc = ERROR_FAIL;
+ LOG(ERROR, "xc_dom_vuart_init failed\n");
+ }
+
+out:
+ return rc;
+}
+
+int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_build_info *info,
+ libxl__domain_build_state *state)
+{
+ return libxl__vnuma_build_vmemrange_pv_generic(gc, domid, info, state);
+}
+
+int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq)
+{
+ return xc_domain_bind_pt_spi_irq(CTX->xch, domid, irq, irq);
+}
+
+void libxl__arch_domain_create_info_setdefault(libxl__gc *gc,
+ libxl_domain_create_info *c_info)
+{
+ /*
+ * Arm guest are now considered as PVH by the toolstack. To allow
+ * compatibility with previous toolstack, PV guest are automatically
+ * converted to PVH.
+ */
+ if (c_info->type == LIBXL_DOMAIN_TYPE_PV) {
+ LOG(WARN, "Converting PV guest to PVH.");
+ LOG(WARN, "Arm guest are now PVH.");
+ LOG(WARN, "Please fix your configuration file/toolstack.");
+
+ c_info->type = LIBXL_DOMAIN_TYPE_PVH;
+ /* All other fields can remain untouched */
+ }
+}
+
+void libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
+ libxl_domain_build_info *b_info)
+{
+ /* ACPI is disabled by default */
+ libxl_defbool_setdefault(&b_info->acpi, false);
+
+ if (b_info->type != LIBXL_DOMAIN_TYPE_PV)
+ return;
+
+ LOG(DEBUG, "Converting build_info to PVH");
+
+ /* Re-initialize type to PVH and all associated fields to defaults. */
+ memset(&b_info->u, '\0', sizeof(b_info->u));
+ b_info->type = LIBXL_DOMAIN_TYPE_INVALID;
+ libxl_domain_build_info_init_type(b_info, LIBXL_DOMAIN_TYPE_PVH);
+}
+
+int libxl__arch_passthrough_mode_setdefault(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_config *d_config,
+ const libxl_physinfo *physinfo)
+{
+ int rc;
+ libxl_domain_create_info *const c_info = &d_config->c_info;
+
+ if (c_info->passthrough == LIBXL_PASSTHROUGH_ENABLED) {
+ c_info->passthrough = LIBXL_PASSTHROUGH_SHARE_PT;
+ }
+
+ switch (c_info->passthrough) {
+ case LIBXL_PASSTHROUGH_DISABLED:
+ case LIBXL_PASSTHROUGH_SHARE_PT:
+ break;
+
+ default:
+ LOGD(ERROR, domid,
+ "passthrough=\"%s\" not supported on ARM\n",
+ libxl_passthrough_to_string(c_info->passthrough));
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ rc = 0;
+ out:
+ return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2016 Linaro Ltd.
+ *
+ * Author: Shannon Zhao <shannon.zhao@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+
+#include <xenctrl_dom.h>
+
+_hidden
+int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
+ struct xc_dom_image *dom);
+
+_hidden
+int libxl__get_acpi_size(libxl__gc *gc,
+ const libxl_domain_build_info *info,
+ uint64_t *out);
+
+static inline uint64_t libxl__compute_mpdir(unsigned int cpuid)
+{
+ /*
+ * According to ARM CPUs bindings, the reg field should match
+ * the MPIDR's affinity bits. We will use AFF0 and AFF1 when
+ * constructing the reg value of the guest at the moment, for it
+ * is enough for the current max vcpu number.
+ */
+ return (cpuid & 0x0f) | (((cpuid >> 4) & 0xff) << 8);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * ARM DomU ACPI generation
+ *
+ * Copyright (C) 2016 Linaro Ltd.
+ *
+ * Author: Shannon Zhao <shannon.zhao@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_arm.h"
+
+#include <stdint.h>
+
+/* Below typedefs are useful for the headers under acpi/ */
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int64_t s64;
+
+#include <acpi/acconfig.h>
+#include <acpi/actbl.h>
+
+#ifndef BITS_PER_LONG
+#ifdef _LP64
+#define BITS_PER_LONG 64
+#else
+#define BITS_PER_LONG 32
+#endif
+#endif
+#define ACPI_MACHINE_WIDTH BITS_PER_LONG
+#define COMPILER_DEPENDENT_INT64 int64_t
+#define COMPILER_DEPENDENT_UINT64 uint64_t
+
+#include <acpi/actypes.h>
+
+_hidden
+extern const unsigned char dsdt_anycpu_arm[];
+_hidden
+extern const int dsdt_anycpu_arm_len;
+
+#define ACPI_OEM_ID "Xen\0\0"
+#define ACPI_OEM_TABLE_ID "ARM\0\0\0\0"
+#define ACPI_ASL_COMPILER_ID "XL\0"
+
+enum {
+ RSDP,
+ XSDT,
+ GTDT,
+ MADT,
+ FADT,
+ DSDT,
+ MAX_TABLE_NUMS,
+};
+
+struct acpitable {
+ uint64_t addr;
+ size_t size;
+};
+
+static int libxl__estimate_madt_size(libxl__gc *gc,
+ const libxl_domain_build_info *info,
+ size_t *size)
+{
+ int rc = 0;
+
+ switch (info->arch_arm.gic_version) {
+ case LIBXL_GIC_VERSION_V2:
+ *size = sizeof(struct acpi_table_madt) +
+ ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus +
+ sizeof(struct acpi_madt_generic_distributor);
+ break;
+ case LIBXL_GIC_VERSION_V3:
+ *size = sizeof(struct acpi_table_madt) +
+ ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus +
+ sizeof(struct acpi_madt_generic_distributor) +
+ sizeof(struct acpi_madt_generic_redistributor);
+ break;
+ default:
+ LOG(ERROR, "Unknown GIC version");
+ *size = 0;
+ rc = ERROR_FAIL;
+ break;
+ }
+
+ return rc;
+}
+
+int libxl__get_acpi_size(libxl__gc *gc,
+ const libxl_domain_build_info *info,
+ uint64_t *out)
+{
+ uint64_t size;
+ int rc = 0;
+
+
+ rc = libxl__estimate_madt_size(gc, info, &size);
+ if (rc < 0)
+ goto out;
+
+ *out = ROUNDUP(size, 3) +
+ ROUNDUP(sizeof(struct acpi_table_rsdp), 3) +
+ ROUNDUP(sizeof(struct acpi_table_xsdt), 3) +
+ ROUNDUP(sizeof(struct acpi_table_gtdt), 3) +
+ ROUNDUP(sizeof(struct acpi_table_fadt), 3) +
+ ROUNDUP(sizeof(dsdt_anycpu_arm_len), 3);
+
+out:
+ return rc;
+}
+
+static int libxl__allocate_acpi_tables(libxl__gc *gc,
+ libxl_domain_build_info *info,
+ struct xc_dom_image *dom,
+ struct acpitable acpitables[])
+{
+ int rc;
+ size_t size;
+
+ acpitables[RSDP].addr = GUEST_ACPI_BASE;
+ acpitables[RSDP].size = sizeof(struct acpi_table_rsdp);
+ dom->acpi_modules[0].length += ROUNDUP(acpitables[RSDP].size, 3);
+
+ acpitables[XSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
+ /*
+ * Currently only 3 tables(GTDT, FADT, MADT) are pointed by XSDT. Alloc
+ * entries for them.
+ */
+ acpitables[XSDT].size = sizeof(struct acpi_table_xsdt) +
+ sizeof(uint64_t) * 2;
+ dom->acpi_modules[0].length += ROUNDUP(acpitables[XSDT].size, 3);
+
+ acpitables[GTDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
+ acpitables[GTDT].size = sizeof(struct acpi_table_gtdt);
+ dom->acpi_modules[0].length += ROUNDUP(acpitables[GTDT].size, 3);
+
+ acpitables[MADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
+
+ rc = libxl__estimate_madt_size(gc, info, &size);
+ if (rc < 0)
+ goto out;
+
+ acpitables[MADT].size = size;
+ dom->acpi_modules[0].length += ROUNDUP(acpitables[MADT].size, 3);
+
+ acpitables[FADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
+ acpitables[FADT].size = sizeof(struct acpi_table_fadt);
+ dom->acpi_modules[0].length += ROUNDUP(acpitables[FADT].size, 3);
+
+ acpitables[DSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
+ acpitables[DSDT].size = dsdt_anycpu_arm_len;
+ dom->acpi_modules[0].length += ROUNDUP(acpitables[DSDT].size, 3);
+
+ assert(dom->acpi_modules[0].length <= GUEST_ACPI_SIZE);
+ dom->acpi_modules[0].data = libxl__zalloc(gc, dom->acpi_modules[0].length);
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static void calculate_checksum(void *table, uint32_t checksum_offset,
+ uint32_t length)
+{
+ uint8_t *p, sum = 0;
+
+ p = table;
+ p[checksum_offset] = 0;
+
+ while (length--)
+ sum = sum + *p++;
+
+ p = table;
+ p[checksum_offset] = -sum;
+}
+
+static void make_acpi_rsdp(libxl__gc *gc, struct xc_dom_image *dom,
+ struct acpitable acpitables[])
+{
+ uint64_t offset = acpitables[RSDP].addr - GUEST_ACPI_BASE;
+ struct acpi_table_rsdp *rsdp = (void *)dom->acpi_modules[0].data + offset;
+
+ memcpy(rsdp->signature, "RSD PTR ", sizeof(rsdp->signature));
+ BUILD_BUG_ON(sizeof(ACPI_OEM_ID) != sizeof(rsdp->oem_id));
+ memcpy(rsdp->oem_id, ACPI_OEM_ID, sizeof(rsdp->oem_id));
+ rsdp->length = acpitables[RSDP].size;
+ rsdp->revision = 0x02;
+ rsdp->xsdt_physical_address = acpitables[XSDT].addr;
+ calculate_checksum(rsdp,
+ offsetof(struct acpi_table_rsdp, extended_checksum),
+ acpitables[RSDP].size);
+}
+
+static void make_acpi_header(struct acpi_table_header *h, const char *sig,
+ size_t len, uint8_t rev)
+{
+ memcpy(h->signature, sig, 4);
+ h->length = len;
+ h->revision = rev;
+ BUILD_BUG_ON(sizeof(ACPI_OEM_ID) != sizeof(h->oem_id));
+ memcpy(h->oem_id, ACPI_OEM_ID, sizeof(h->oem_id));
+ BUILD_BUG_ON(sizeof(ACPI_OEM_TABLE_ID) != sizeof(h->oem_table_id));
+ memcpy(h->oem_table_id, ACPI_OEM_TABLE_ID, sizeof(h->oem_table_id));
+ h->oem_revision = 0;
+ BUILD_BUG_ON(sizeof(ACPI_ASL_COMPILER_ID) != sizeof(h->asl_compiler_id));
+ memcpy(h->asl_compiler_id, ACPI_ASL_COMPILER_ID,
+ sizeof(h->asl_compiler_id));
+ h->asl_compiler_revision = 0;
+ h->checksum = 0;
+}
+
+static void make_acpi_xsdt(libxl__gc *gc, struct xc_dom_image *dom,
+ struct acpitable acpitables[])
+{
+ uint64_t offset = acpitables[XSDT].addr - GUEST_ACPI_BASE;
+ struct acpi_table_xsdt *xsdt = (void *)dom->acpi_modules[0].data + offset;
+
+ xsdt->table_offset_entry[0] = acpitables[MADT].addr;
+ xsdt->table_offset_entry[1] = acpitables[GTDT].addr;
+ xsdt->table_offset_entry[2] = acpitables[FADT].addr;
+ make_acpi_header(&xsdt->header, "XSDT", acpitables[XSDT].size, 1);
+ calculate_checksum(xsdt, offsetof(struct acpi_table_header, checksum),
+ acpitables[XSDT].size);
+}
+
+static void make_acpi_gtdt(libxl__gc *gc, struct xc_dom_image *dom,
+ struct acpitable acpitables[])
+{
+ uint64_t offset = acpitables[GTDT].addr - GUEST_ACPI_BASE;
+ struct acpi_table_gtdt *gtdt = (void *)dom->acpi_modules[0].data + offset;
+
+ gtdt->non_secure_el1_interrupt = GUEST_TIMER_PHYS_NS_PPI;
+ gtdt->non_secure_el1_flags =
+ (ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE)
+ |(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY);
+ gtdt->virtual_timer_interrupt = GUEST_TIMER_VIRT_PPI;
+ gtdt->virtual_timer_flags =
+ (ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE)
+ |(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY);
+
+ gtdt->counter_block_addresss = ~((uint64_t)0);
+ gtdt->counter_read_block_address = ~((uint64_t)0);
+
+ make_acpi_header(>dt->header, "GTDT", acpitables[GTDT].size, 2);
+ calculate_checksum(gtdt, offsetof(struct acpi_table_header, checksum),
+ acpitables[GTDT].size);
+}
+
+static void make_acpi_madt_gicc(void *table, int nr_cpus, uint64_t gicc_base)
+{
+ int i;
+ struct acpi_madt_generic_interrupt *gicc = table;
+
+ for (i = 0; i < nr_cpus; i++) {
+ gicc->header.type = ACPI_MADT_TYPE_GENERIC_INTERRUPT;
+ gicc->header.length = ACPI_MADT_GICC_SIZE_v5;
+ gicc->base_address = gicc_base;
+ gicc->cpu_interface_number = i;
+ gicc->arm_mpidr = libxl__compute_mpdir(i);
+ gicc->uid = i;
+ gicc->flags = ACPI_MADT_ENABLED;
+ gicc = table + ACPI_MADT_GICC_SIZE_v5;
+ }
+}
+
+static void make_acpi_madt_gicd(void *table, uint64_t gicd_base,
+ uint8_t gic_version)
+{
+ struct acpi_madt_generic_distributor *gicd = table;
+
+ gicd->header.type = ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR;
+ gicd->header.length = sizeof(*gicd);
+ gicd->base_address = gicd_base;
+ /* This version field has no meaning before ACPI 5.1 errata. */
+ gicd->version = gic_version;
+}
+
+static void make_acpi_madt_gicr(void *table, uint64_t gicr_base,
+ uint64_t gicr_size)
+{
+ struct acpi_madt_generic_redistributor *gicr = table;
+
+ gicr->header.type = ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR;
+ gicr->header.length = sizeof(*gicr);
+ gicr->base_address = gicr_base;
+ gicr->length = gicr_size;
+}
+
+static int make_acpi_madt(libxl__gc *gc, struct xc_dom_image *dom,
+ libxl_domain_build_info *info,
+ struct acpitable acpitables[])
+{
+ uint64_t offset = acpitables[MADT].addr - GUEST_ACPI_BASE;
+ void *table = dom->acpi_modules[0].data + offset;
+ struct acpi_table_madt *madt = table;
+ int rc = 0;
+
+ switch (info->arch_arm.gic_version) {
+ case LIBXL_GIC_VERSION_V2:
+ table += sizeof(struct acpi_table_madt);
+ make_acpi_madt_gicc(table, info->max_vcpus, GUEST_GICC_BASE);
+
+ table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus;
+ make_acpi_madt_gicd(table, GUEST_GICD_BASE, ACPI_MADT_GIC_VERSION_V2);
+ break;
+ case LIBXL_GIC_VERSION_V3:
+ table += sizeof(struct acpi_table_madt);
+ make_acpi_madt_gicc(table, info->max_vcpus, 0);
+
+ table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus;
+ make_acpi_madt_gicd(table, GUEST_GICV3_GICD_BASE,
+ ACPI_MADT_GIC_VERSION_V3);
+
+ table += sizeof(struct acpi_madt_generic_distributor);
+ make_acpi_madt_gicr(table, GUEST_GICV3_GICR0_BASE,
+ GUEST_GICV3_GICR0_SIZE);
+ break;
+ default:
+ LOG(ERROR, "Unknown GIC version");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ make_acpi_header(&madt->header, "APIC", acpitables[MADT].size, 3);
+ calculate_checksum(madt, offsetof(struct acpi_table_header, checksum),
+ acpitables[MADT].size);
+
+out:
+ return rc;
+}
+
+static void make_acpi_fadt(libxl__gc *gc, struct xc_dom_image *dom,
+ struct acpitable acpitables[])
+{
+ uint64_t offset = acpitables[FADT].addr - GUEST_ACPI_BASE;
+ struct acpi_table_fadt *fadt = (void *)dom->acpi_modules[0].data + offset;
+
+ /* Hardware Reduced = 1 and use PSCI 0.2+ and with HVC */
+ fadt->flags = ACPI_FADT_HW_REDUCED;
+ fadt->arm_boot_flags = ACPI_FADT_PSCI_COMPLIANT | ACPI_FADT_PSCI_USE_HVC;
+
+ /* ACPI v5.1 (fadt->revision.fadt->minor_revision) */
+ fadt->minor_revision = 0x1;
+ fadt->dsdt = acpitables[DSDT].addr;
+
+ make_acpi_header(&fadt->header, "FACP", acpitables[FADT].size, 5);
+ calculate_checksum(fadt, offsetof(struct acpi_table_header, checksum),
+ acpitables[FADT].size);
+}
+
+static void make_acpi_dsdt(libxl__gc *gc, struct xc_dom_image *dom,
+ struct acpitable acpitables[])
+{
+ uint64_t offset = acpitables[DSDT].addr - GUEST_ACPI_BASE;
+ void *dsdt = dom->acpi_modules[0].data + offset;
+
+ memcpy(dsdt, dsdt_anycpu_arm, dsdt_anycpu_arm_len);
+}
+
+int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
+ struct xc_dom_image *dom)
+{
+ const libxl_version_info *vers;
+ int rc = 0;
+ struct acpitable acpitables[MAX_TABLE_NUMS];
+
+ vers = libxl_get_version_info(CTX);
+ if (vers == NULL) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ LOG(DEBUG, "constructing ACPI tables for Xen version %d.%d guest",
+ vers->xen_version_major, vers->xen_version_minor);
+
+ dom->acpi_modules[0].data = NULL;
+ dom->acpi_modules[0].length = 0;
+ dom->acpi_modules[0].guest_addr_out = GUEST_ACPI_BASE;
+
+ rc = libxl__allocate_acpi_tables(gc, info, dom, acpitables);
+ if (rc)
+ goto out;
+
+ make_acpi_rsdp(gc, dom, acpitables);
+ make_acpi_xsdt(gc, dom, acpitables);
+ make_acpi_gtdt(gc, dom, acpitables);
+ rc = make_acpi_madt(gc, dom, info, acpitables);
+ if (rc)
+ goto out;
+
+ make_acpi_fadt(gc, dom, acpitables);
+ make_acpi_dsdt(gc, dom, acpitables);
+
+out:
+ return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * ARM DomU ACPI generation
+ *
+ * Copyright (C) 2016 Linaro Ltd.
+ *
+ * Author: Shannon Zhao <shannon.zhao@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_arm.h"
+
+int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
+ struct xc_dom_image *dom)
+{
+ return ERROR_FAIL;
+}
+
+int libxl__get_acpi_size(libxl__gc *gc,
+ const libxl_domain_build_info *info,
+ uint64_t *out)
+{
+ return ERROR_FAIL;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2010 Citrix Ltd.
+ * Author Ian Campbell <ian.campbell@citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <termios.h>
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif
+
+#ifdef INCLUDE_LIBUTIL_H
+#include INCLUDE_LIBUTIL_H
+#endif
+
+#include "libxl_internal.h"
+
+#define BOOTLOADER_BUF_OUT 65536
+#define BOOTLOADER_BUF_IN 4096
+
+static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op);
+static void bootloader_keystrokes_copyfail(libxl__egc *egc,
+ libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
+static void bootloader_display_copyfail(libxl__egc *egc,
+ libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
+static void bootloader_domaindeath(libxl__egc*, libxl__domaindeathcheck *dc,
+ int rc);
+static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child,
+ pid_t pid, int status);
+
+/*----- bootloader arguments -----*/
+
+static void bootloader_arg(libxl__bootloader_state *bl, const char *arg)
+{
+ assert(bl->nargs < bl->argsspace);
+ bl->args[bl->nargs++] = arg;
+}
+
+static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl,
+ const char *bootloader_path)
+{
+ const libxl_domain_build_info *info = bl->info;
+
+ bl->argsspace = 9 + libxl_string_list_length(&info->bootloader_args);
+
+ GCNEW_ARRAY(bl->args, bl->argsspace);
+
+#define ARG(arg) bootloader_arg(bl, (arg))
+
+ ARG(bootloader_path);
+
+ if (info->kernel)
+ ARG(GCSPRINTF("--kernel=%s", info->kernel));
+ if (info->ramdisk)
+ ARG(GCSPRINTF("--ramdisk=%s", info->ramdisk));
+ if (info->cmdline && *info->cmdline != '\0')
+ ARG(GCSPRINTF("--args=%s", info->cmdline));
+
+ ARG(GCSPRINTF("--output=%s", bl->outputpath));
+ ARG("--output-format=simple0");
+ ARG(GCSPRINTF("--output-directory=%s", bl->outputdir));
+
+ if (info->bootloader_args) {
+ char **p = info->bootloader_args;
+ while (*p) {
+ ARG(*p);
+ p++;
+ }
+ }
+
+ ARG(bl->dls.diskpath);
+
+ /* Sentinel for execv */
+ ARG(NULL);
+
+#undef ARG
+}
+
+/*----- synchronous subroutines -----*/
+
+static int setup_xenconsoled_pty(libxl__egc *egc, libxl__bootloader_state *bl,
+ char *slave_path, size_t slave_path_len)
+{
+ STATE_AO_GC(bl->ao);
+ struct termios termattr;
+ int r, rc;
+ int slave = libxl__carefd_fd(bl->ptys[1].slave);
+ int master = libxl__carefd_fd(bl->ptys[1].master);
+
+ r = ttyname_r(slave, slave_path, slave_path_len);
+ if (r == -1) {
+ LOGED(ERROR, bl->domid, "ttyname_r failed");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /*
+ * On Solaris, the pty master side will get cranky if we try
+ * to write to it while there is no slave. To work around this,
+ * keep the slave descriptor open until we're done. Set it
+ * to raw terminal parameters, otherwise it will echo back
+ * characters, which will confuse the I/O loop below.
+ * Furthermore, a raw master pty device has no terminal
+ * semantics on Solaris, so don't try to set any attributes
+ * for it.
+ */
+ tcgetattr(master, &termattr);
+ cfmakeraw(&termattr);
+ tcsetattr(master, TCSANOW, &termattr);
+
+ return 0;
+
+ out:
+ return rc;
+}
+
+static const char *bootloader_result_command(libxl__gc *gc, const char *buf,
+ const char *prefix, size_t prefixlen, uint32_t domid) {
+ if (strncmp(buf, prefix, prefixlen))
+ return 0;
+
+ const char *rhs = buf + prefixlen;
+ if (!CTYPE(isspace,*rhs))
+ return 0;
+
+ while (CTYPE(isspace,*rhs))
+ rhs++;
+
+ LOGD(DEBUG, domid, "bootloader output contained %s %s", prefix, rhs);
+
+ return rhs;
+}
+
+static int parse_bootloader_result(libxl__egc *egc,
+ libxl__bootloader_state *bl)
+{
+ STATE_AO_GC(bl->ao);
+ char buf[PATH_MAX*2];
+ FILE *f = 0;
+ int rc = ERROR_FAIL;
+
+ f = fopen(bl->outputpath, "r");
+ if (!f) {
+ LOGED(ERROR, bl->domid, "open bootloader output file %s",
+ bl->outputpath);
+ goto out;
+ }
+
+ for (;;) {
+ /* Read a nul-terminated "line" and put the result in
+ * buf, and its length (not including the nul) in l */
+ int l = 0, c;
+ while ((c = getc(f)) != EOF && c != '\0') {
+ if (l < sizeof(buf)-1)
+ buf[l] = c;
+ l++;
+ }
+ if (c == EOF) {
+ if (ferror(f)) {
+ LOGED(ERROR, bl->domid, "read bootloader output file %s",
+ bl->outputpath);
+ goto out;
+ }
+ if (!l)
+ break;
+ }
+ if (l >= sizeof(buf)) {
+ LOGD(WARN, bl->domid, "bootloader output contained"
+ " overly long item `%.150s...'", buf);
+ continue;
+ }
+ buf[l] = 0;
+
+ const char *rhs;
+#define COMMAND(s) ((rhs = bootloader_result_command(gc, buf, s, sizeof(s)-1, bl->domid)))
+
+ if (COMMAND("kernel")) {
+ bl->kernel->path = libxl__strdup(gc, rhs);
+ libxl__file_reference_map(bl->kernel);
+ unlink(bl->kernel->path);
+ } else if (COMMAND("ramdisk")) {
+ bl->ramdisk->path = libxl__strdup(gc, rhs);
+ libxl__file_reference_map(bl->ramdisk);
+ unlink(bl->ramdisk->path);
+ } else if (COMMAND("args")) {
+ bl->cmdline = libxl__strdup(gc, rhs);
+ } else if (l) {
+ LOGD(WARN, bl->domid,
+ "unexpected output from bootloader: `%s'", buf);
+ }
+ }
+ rc = 0;
+
+ out:
+ if (f) fclose(f);
+ return rc;
+}
+
+
+/*----- init and cleanup -----*/
+
+void libxl__bootloader_init(libxl__bootloader_state *bl)
+{
+ assert(bl->ao);
+ bl->rc = 0;
+ bl->dls.diskpath = NULL;
+ bl->openpty.ao = bl->ao;
+ bl->dls.ao = bl->ao;
+ bl->ptys[0].master = bl->ptys[0].slave = 0;
+ bl->ptys[1].master = bl->ptys[1].slave = 0;
+ libxl__ev_child_init(&bl->child);
+ libxl__domaindeathcheck_init(&bl->deathcheck);
+ bl->keystrokes.ao = bl->ao; libxl__datacopier_init(&bl->keystrokes);
+ bl->display.ao = bl->ao; libxl__datacopier_init(&bl->display);
+ bl->got_pollhup = 0;
+}
+
+static void bootloader_cleanup(libxl__egc *egc, libxl__bootloader_state *bl)
+{
+ STATE_AO_GC(bl->ao);
+ int i;
+
+ if (bl->outputpath) libxl__remove_file(gc, bl->outputpath);
+ if (bl->outputdir) libxl__remove_directory(gc, bl->outputdir);
+
+ libxl__domaindeathcheck_stop(gc,&bl->deathcheck);
+ libxl__datacopier_kill(&bl->keystrokes);
+ libxl__datacopier_kill(&bl->display);
+ for (i=0; i<2; i++) {
+ libxl__carefd_close(bl->ptys[i].master);
+ libxl__carefd_close(bl->ptys[i].slave);
+ }
+ if (bl->display.log) {
+ fclose(bl->display.log);
+ bl->display.log = NULL;
+ }
+}
+
+static void bootloader_setpaths(libxl__gc *gc, libxl__bootloader_state *bl)
+{
+ uint32_t domid = bl->domid;
+ bl->outputdir = GCSPRINTF(XEN_RUN_DIR "/bootloader.%"PRIu32".d", domid);
+ bl->outputpath = GCSPRINTF(XEN_RUN_DIR "/bootloader.%"PRIu32".out", domid);
+}
+
+/* Callbacks */
+
+static void bootloader_local_detached_cb(libxl__egc *egc,
+ libxl__disk_local_state *dls,
+ int rc);
+
+static void bootloader_callback(libxl__egc *egc, libxl__bootloader_state *bl,
+ int rc)
+{
+ if (!bl->rc)
+ bl->rc = rc;
+
+ bootloader_cleanup(egc, bl);
+
+ bl->dls.callback = bootloader_local_detached_cb;
+ libxl__device_disk_local_initiate_detach(egc, &bl->dls);
+}
+
+static void bootloader_local_detached_cb(libxl__egc *egc,
+ libxl__disk_local_state *dls,
+ int rc)
+{
+ STATE_AO_GC(dls->ao);
+ libxl__bootloader_state *bl = CONTAINER_OF(dls, *bl, dls);
+
+ if (rc) {
+ LOGD(ERROR, bl->domid,
+ "unable to detach locally attached disk");
+ if (!bl->rc)
+ bl->rc = rc;
+ }
+
+ bl->callback(egc, bl, bl->rc);
+}
+
+/* might be called at any time, provided it's init'd */
+static void bootloader_stop(libxl__egc *egc,
+ libxl__bootloader_state *bl, int rc)
+{
+ STATE_AO_GC(bl->ao);
+ int r;
+
+ libxl__datacopier_kill(&bl->keystrokes);
+ libxl__datacopier_kill(&bl->display);
+ if (libxl__ev_child_inuse(&bl->child)) {
+ r = kill(bl->child.pid, SIGTERM);
+ if (r) LOGED(WARN, bl->domid, "%sfailed to kill bootloader [%lu]",
+ rc ? "after failure, " : "", (unsigned long)bl->child.pid);
+ }
+ if (!bl->rc)
+ bl->rc = rc;
+}
+
+/*----- main flow of control -----*/
+
+/* Callbacks */
+
+static void bootloader_disk_attached_cb(libxl__egc *egc,
+ libxl__disk_local_state *dls,
+ int rc);
+
+void libxl__bootloader_run(libxl__egc *egc, libxl__bootloader_state *bl)
+{
+ STATE_AO_GC(bl->ao);
+ const libxl_domain_build_info *info = bl->info;
+ uint32_t domid = bl->domid;
+ char *logfile_tmp = NULL;
+ int rc, r;
+
+ libxl__bootloader_init(bl);
+
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
+ LOGD(DEBUG, domid, "not a PV/PVH domain, skipping bootloader");
+ rc = 0;
+ goto out_ok;
+ }
+
+ if (!info->bootloader) {
+ LOGD(DEBUG, domid,
+ "no bootloader configured, using user supplied kernel");
+ bl->kernel->path = bl->info->kernel;
+ bl->ramdisk->path = bl->info->ramdisk;
+ bl->cmdline = bl->info->cmdline;
+ rc = 0;
+ goto out_ok;
+ }
+
+ if (!bl->disk) {
+ LOGD(ERROR, domid, "cannot run bootloader with no boot disk");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ bootloader_setpaths(gc, bl);
+
+ const char *logfile_leaf = GCSPRINTF("bootloader.%"PRIu32, domid);
+ rc = libxl_create_logfile(CTX, logfile_leaf, &logfile_tmp);
+ if (rc) goto out;
+
+ /* Transfer ownership of log filename to bl and the gc */
+ bl->logfile = logfile_tmp;
+ libxl__ptr_add(gc, logfile_tmp);
+ logfile_tmp = NULL;
+
+ bl->display.log = fopen(bl->logfile, "a");
+ if (!bl->display.log) {
+ LOGED(ERROR, domid,
+ "failed to create bootloader logfile %s", bl->logfile);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ for (;;) {
+ r = mkdir(bl->outputdir, 0600);
+ if (!r) break;
+ if (errno == EINTR) continue;
+ if (errno == EEXIST) break;
+ LOGED(ERROR, domid,
+ "failed to create bootloader dir %s", bl->outputdir);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ for (;;) {
+ r = open(bl->outputpath, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+ if (r>=0) { close(r); break; }
+ if (errno == EINTR) continue;
+ LOGED(ERROR, domid,
+ "failed to precreate bootloader output %s", bl->outputpath);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+
+ /* This sets the state of the dls struct from Undefined to Idle */
+ libxl__device_disk_local_init(&bl->dls);
+ bl->dls.ao = ao;
+ bl->dls.in_disk = bl->disk;
+ bl->dls.blkdev_start = info->blkdev_start;
+ bl->dls.callback = bootloader_disk_attached_cb;
+ libxl__device_disk_local_initiate_attach(egc, &bl->dls);
+ return;
+
+ out:
+ assert(rc);
+ out_ok:
+ free(logfile_tmp);
+ bootloader_callback(egc, bl, rc);
+}
+
+static void bootloader_disk_attached_cb(libxl__egc *egc,
+ libxl__disk_local_state *dls,
+ int rc)
+{
+ STATE_AO_GC(dls->ao);
+ libxl__bootloader_state *bl = CONTAINER_OF(dls, *bl, dls);
+ const libxl_domain_build_info *info = bl->info;
+ const char *bootloader;
+
+ if (rc) {
+ LOGD(ERROR, bl->domid,
+ "failed to attach local disk for bootloader execution");
+ goto out;
+ }
+
+ LOGD(DEBUG, bl->domid,
+ "Config bootloader value: %s", info->bootloader);
+
+ if ( !strcmp(info->bootloader, "/usr/bin/pygrub") )
+ LOGD(WARN, bl->domid,
+ "bootloader='/usr/bin/pygrub' is deprecated; use " \
+ "bootloader='pygrub' instead");
+
+ bootloader = info->bootloader;
+
+ /* If the full path is not specified, check in the libexec path */
+ if ( bootloader[0] != '/' ) {
+ const char *bltmp;
+ struct stat st;
+
+ bltmp = libxl__abs_path(gc, bootloader, libxl__private_bindir_path());
+ /* Check to see if the file exists in this location; if not,
+ * fall back to checking the path */
+ LOGD(DEBUG, bl->domid,
+ "Checking for bootloader in libexec path: %s", bltmp);
+
+ if ( lstat(bltmp, &st) )
+ LOGD(DEBUG, bl->domid,
+ "%s doesn't exist, falling back to config path",
+ bltmp);
+ else
+ bootloader = bltmp;
+ }
+
+ make_bootloader_args(gc, bl, bootloader);
+
+ bl->openpty.ao = ao;
+ bl->openpty.callback = bootloader_gotptys;
+ bl->openpty.count = 2;
+ bl->openpty.results = bl->ptys;
+ rc = libxl__openptys(&bl->openpty, 0,0);
+ if (rc) goto out;
+
+ return;
+
+ out:
+ assert(rc);
+ bootloader_callback(egc, bl, rc);
+}
+
+static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op)
+{
+ libxl__bootloader_state *bl = CONTAINER_OF(op, *bl, openpty);
+ STATE_AO_GC(bl->ao);
+ int rc, r;
+ char *const env[] = { "TERM", "vt100", NULL };
+
+ if (bl->openpty.rc) {
+ rc = bl->openpty.rc;
+ goto out;
+ }
+
+ /*
+ * We need to present the bootloader's tty as a pty slave that xenconsole
+ * can access. Since the bootloader itself needs a pty slave,
+ * we end up with a connection like this:
+ *
+ * xenconsole -- (slave pty1 master) <-> (master pty2 slave) -- bootloader
+ *
+ * where we copy characters between the two master fds, as well as
+ * listening on the bootloader's fifo for the results.
+ */
+
+ char *dom_console_xs_path;
+ char dom_console_slave_tty_path[PATH_MAX];
+ rc = setup_xenconsoled_pty(egc, bl,
+ &dom_console_slave_tty_path[0],
+ sizeof(dom_console_slave_tty_path));
+ if (rc) goto out;
+
+ char *dompath = libxl__xs_get_dompath(gc, bl->domid);
+ if (!dompath) { rc = ERROR_FAIL; goto out; }
+
+ dom_console_xs_path = GCSPRINTF("%s/console/tty", dompath);
+
+ rc = libxl__xs_printf(gc, XBT_NULL, dom_console_xs_path, "%s",
+ dom_console_slave_tty_path);
+ if (rc) {
+ LOGED(ERROR, bl->domid, "xs write console path %s := %s failed",
+ dom_console_xs_path, dom_console_slave_tty_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ bl->deathcheck.what = "stopping bootloader";
+ bl->deathcheck.domid = bl->domid;
+ bl->deathcheck.callback = bootloader_domaindeath;
+ rc = libxl__domaindeathcheck_start(ao, &bl->deathcheck);
+ if (rc) goto out;
+
+ if (bl->console_available)
+ bl->console_available(egc, bl);
+
+ int bootloader_master = libxl__carefd_fd(bl->ptys[0].master);
+ int xenconsole_master = libxl__carefd_fd(bl->ptys[1].master);
+
+ libxl_fd_set_nonblock(CTX, bootloader_master, 1);
+ libxl_fd_set_nonblock(CTX, xenconsole_master, 1);
+
+ bl->keystrokes.writefd = bl->display.readfd = bootloader_master;
+ bl->keystrokes.writewhat = bl->display.readwhat = "bootloader pty";
+
+ bl->keystrokes.readfd = bl->display.writefd = xenconsole_master;
+ bl->keystrokes.readwhat = bl->display.writewhat = "xenconsole client pty";
+
+ bl->keystrokes.ao = ao;
+ bl->keystrokes.maxsz = BOOTLOADER_BUF_OUT;
+ bl->keystrokes.bytes_to_read = -1;
+ bl->keystrokes.copywhat =
+ GCSPRINTF("bootloader input for domain %"PRIu32, bl->domid);
+ bl->keystrokes.callback = bootloader_keystrokes_copyfail;
+ bl->keystrokes.callback_pollhup = bootloader_keystrokes_copyfail;
+ /* pollhup gets called with errnoval==-1 which is not otherwise
+ * possible since errnos are nonnegative, so it's unambiguous */
+ rc = libxl__datacopier_start(&bl->keystrokes);
+ if (rc) goto out;
+
+ bl->display.ao = ao;
+ bl->display.maxsz = BOOTLOADER_BUF_IN;
+ bl->display.bytes_to_read = -1;
+ bl->display.copywhat =
+ GCSPRINTF("bootloader output for domain %"PRIu32, bl->domid);
+ bl->display.callback = bootloader_display_copyfail;
+ bl->display.callback_pollhup = bootloader_display_copyfail;
+ rc = libxl__datacopier_start(&bl->display);
+ if (rc) goto out;
+
+ LOGD(DEBUG, bl->domid, "executing bootloader: %s", bl->args[0]);
+ for (const char **blarg = bl->args;
+ *blarg;
+ blarg++)
+ LOGD(DEBUG, bl->domid, " bootloader arg: %s", *blarg);
+
+ struct termios termattr;
+
+ pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished);
+ if (pid == -1) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (!pid) {
+ /* child */
+ r = login_tty(libxl__carefd_fd(bl->ptys[0].slave));
+ if (r) { LOGED(ERROR, bl->domid, "login_tty failed"); exit(-1); }
+ libxl__exec(gc, -1, -1, -1, bl->args[0], (char **) bl->args, env);
+ }
+
+ /* parent */
+
+ /*
+ * On Solaris, the master pty side does not have terminal semantics,
+ * so don't try to set any attributes, as it will fail.
+ */
+#if !defined(__sun__)
+ tcgetattr(bootloader_master, &termattr);
+ cfmakeraw(&termattr);
+ tcsetattr(bootloader_master, TCSANOW, &termattr);
+#endif
+
+ return;
+
+ out:
+ bootloader_callback(egc, bl, rc);
+}
+
+/* perhaps one of these will be called, but perhaps not */
+static void bootloader_copyfail(libxl__egc *egc, const char *which,
+ libxl__bootloader_state *bl, int ondisplay,
+ int rc, int onwrite, int errnoval)
+{
+ STATE_AO_GC(bl->ao);
+
+ if (errnoval==-1) {
+ /* POLLHUP */
+ if (!!ondisplay != !!onwrite) {
+ rc = 0;
+ bl->got_pollhup = 1;
+ } else {
+ LOGD(ERROR, bl->domid, "unexpected POLLHUP on %s", which);
+ }
+ } else if (!rc) {
+ LOGD(ERROR, bl->domid, "unexpected eof copying %s", which);
+ rc = ERROR_FAIL;
+ }
+
+ bootloader_stop(egc, bl, rc);
+}
+static void bootloader_keystrokes_copyfail(libxl__egc *egc,
+ libxl__datacopier_state *dc, int rc, int onwrite, int errnoval)
+{
+ libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, keystrokes);
+ bootloader_copyfail(egc, "bootloader input", bl, 0, rc,onwrite,errnoval);
+}
+static void bootloader_display_copyfail(libxl__egc *egc,
+ libxl__datacopier_state *dc, int rc, int onwrite, int errnoval)
+{
+ libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, display);
+ bootloader_copyfail(egc, "bootloader output", bl, 1, rc,onwrite,errnoval);
+}
+
+static void bootloader_domaindeath(libxl__egc *egc,
+ libxl__domaindeathcheck *dc,
+ int rc)
+{
+ libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, deathcheck);
+ bootloader_stop(egc, bl, rc);
+}
+
+static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child,
+ pid_t pid, int status)
+{
+ libxl__bootloader_state *bl = CONTAINER_OF(child, *bl, child);
+ STATE_AO_GC(bl->ao);
+ int rc;
+
+ libxl__datacopier_kill(&bl->keystrokes);
+ libxl__datacopier_kill(&bl->display);
+
+ if (status) {
+ if (bl->got_pollhup && WIFSIGNALED(status) && WTERMSIG(status)==SIGTERM)
+ LOGD(ERROR, bl->domid, "got POLLHUP, sent SIGTERM");
+ LOGD(ERROR, bl->domid,
+ "bootloader failed - consult logfile %s", bl->logfile);
+ libxl_report_child_exitstatus(CTX, XTL_ERROR, "bootloader",
+ pid, status);
+ rc = ERROR_FAIL;
+ goto out;
+ } else {
+ LOGD(DEBUG, bl->domid, "bootloader completed");
+ }
+
+ if (bl->rc) {
+ /* datacopier went wrong */
+ rc = bl->rc;
+ goto out;
+ }
+
+ rc = parse_bootloader_result(egc, bl);
+ if (rc) goto out;
+
+ rc = 0;
+ LOGD(DEBUG, bl->domid, "bootloader execution successful");
+
+ out:
+ bootloader_callback(egc, bl, rc);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2014 FUJITSU LIMITED
+ * Author: Yang Hongyang <yanghy@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*----- setup() and teardown() -----*/
+
+/* callbacks */
+
+static void all_devices_setup_cb(libxl__egc *egc,
+ libxl__multidev *multidev,
+ int rc);
+static void device_setup_iterate(libxl__egc *egc,
+ libxl__ao_device *aodev);
+static void devices_teardown_cb(libxl__egc *egc,
+ libxl__multidev *multidev,
+ int rc);
+
+/* checkpoint device setup and teardown */
+
+static libxl__checkpoint_device* checkpoint_device_init(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ libxl__device_kind kind,
+ void *libxl_dev)
+{
+ libxl__checkpoint_device *dev = NULL;
+
+ STATE_AO_GC(cds->ao);
+ GCNEW(dev);
+ dev->backend_dev = libxl_dev;
+ dev->kind = kind;
+ dev->cds = cds;
+
+ return dev;
+}
+
+static void checkpoint_devices_setup(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds);
+
+void libxl__checkpoint_devices_setup(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds)
+{
+ int i;
+
+ STATE_AO_GC(cds->ao);
+
+ cds->num_devices = 0;
+ cds->num_nics = 0;
+ cds->num_disks = 0;
+
+ if (cds->device_kind_flags & (1 << LIBXL__DEVICE_KIND_VIF))
+ cds->nics = libxl__device_list(gc, &libxl__nic_devtype, cds->domid,
+ &cds->num_nics);
+
+ if (cds->device_kind_flags & (1 << LIBXL__DEVICE_KIND_VBD))
+ cds->disks = libxl__device_list(gc, &libxl__disk_devtype, cds->domid,
+ &cds->num_disks);
+
+ if (cds->num_nics == 0 && cds->num_disks == 0)
+ goto out;
+
+ GCNEW_ARRAY(cds->devs, cds->num_nics + cds->num_disks);
+
+ for (i = 0; i < cds->num_nics; i++) {
+ cds->devs[cds->num_devices++] = checkpoint_device_init(egc, cds,
+ LIBXL__DEVICE_KIND_VIF,
+ &cds->nics[i]);
+ }
+
+ for (i = 0; i < cds->num_disks; i++) {
+ cds->devs[cds->num_devices++] = checkpoint_device_init(egc, cds,
+ LIBXL__DEVICE_KIND_VBD,
+ &cds->disks[i]);
+ }
+
+ checkpoint_devices_setup(egc, cds);
+
+ return;
+
+out:
+ cds->callback(egc, cds, 0);
+}
+
+static void checkpoint_devices_setup(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds)
+{
+ int i, rc;
+
+ STATE_AO_GC(cds->ao);
+
+ libxl__multidev_begin(ao, &cds->multidev);
+ cds->multidev.callback = all_devices_setup_cb;
+ for (i = 0; i < cds->num_devices; i++) {
+ libxl__checkpoint_device *dev = cds->devs[i];
+ dev->ops_index = -1;
+ libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);
+
+ dev->aodev.rc = ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED;
+ dev->aodev.callback = device_setup_iterate;
+ device_setup_iterate(egc,&dev->aodev);
+ }
+
+ rc = 0;
+ libxl__multidev_prepared(egc, &cds->multidev, rc);
+}
+
+
+static void device_setup_iterate(libxl__egc *egc, libxl__ao_device *aodev)
+{
+ libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
+ EGC_GC;
+
+ if (aodev->rc != ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED &&
+ aodev->rc != ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH)
+ /* might be success or disaster */
+ goto out;
+
+ do {
+ dev->ops = dev->cds->ops[++dev->ops_index];
+ if (!dev->ops) {
+ libxl_device_nic * nic = NULL;
+ libxl_device_disk * disk = NULL;
+ uint32_t domid = INVALID_DOMID;
+ int devid;
+ if (dev->kind == LIBXL__DEVICE_KIND_VIF) {
+ nic = (libxl_device_nic *)dev->backend_dev;
+ domid = nic->backend_domid;
+ devid = nic->devid;
+ } else if (dev->kind == LIBXL__DEVICE_KIND_VBD) {
+ disk = (libxl_device_disk *)dev->backend_dev;
+ domid = disk->backend_domid;
+ devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
+ } else {
+ LOGD(ERROR, domid, "device kind not handled by checkpoint: %s",
+ libxl__device_kind_to_string(dev->kind));
+ aodev->rc = ERROR_FAIL;
+ goto out;
+ }
+ LOGD(ERROR, domid, "device not handled by checkpoint"
+ " (device=%s:%"PRId32"/%"PRId32")",
+ libxl__device_kind_to_string(dev->kind),
+ domid, devid);
+ aodev->rc = ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED;
+ goto out;
+ }
+ } while (dev->ops->kind != dev->kind);
+
+ /* found the next ops_index to try */
+ assert(dev->aodev.callback == device_setup_iterate);
+ dev->ops->setup(egc,dev);
+ return;
+
+ out:
+ libxl__multidev_one_callback(egc,aodev);
+}
+
+static void all_devices_setup_cb(libxl__egc *egc,
+ libxl__multidev *multidev,
+ int rc)
+{
+ STATE_AO_GC(multidev->ao);
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *const cds =
+ CONTAINER_OF(multidev, *cds, multidev);
+
+ cds->callback(egc, cds, rc);
+}
+
+void libxl__checkpoint_devices_teardown(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds)
+{
+ int i;
+ libxl__checkpoint_device *dev;
+
+ STATE_AO_GC(cds->ao);
+
+ libxl__multidev_begin(ao, &cds->multidev);
+ cds->multidev.callback = devices_teardown_cb;
+ for (i = 0; i < cds->num_devices; i++) {
+ dev = cds->devs[i];
+ if (!dev->ops || !dev->matched)
+ continue;
+
+ libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);
+ dev->ops->teardown(egc,dev);
+ }
+
+ libxl__multidev_prepared(egc, &cds->multidev, 0);
+}
+
+static void devices_teardown_cb(libxl__egc *egc,
+ libxl__multidev *multidev,
+ int rc)
+{
+ STATE_AO_GC(multidev->ao);
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *const cds =
+ CONTAINER_OF(multidev, *cds, multidev);
+
+ /* clean nic */
+ libxl__device_list_free(&libxl__nic_devtype, cds->nics, cds->num_nics);
+ cds->nics = NULL;
+ cds->num_nics = 0;
+
+ /* clean disk */
+ libxl__device_list_free(&libxl__disk_devtype, cds->disks, cds->num_disks);
+ cds->disks = NULL;
+ cds->num_disks = 0;
+
+ cds->callback(egc, cds, rc);
+}
+
+/*----- checkpointing APIs -----*/
+
+/* callbacks */
+
+static void devices_checkpoint_cb(libxl__egc *egc,
+ libxl__multidev *multidev,
+ int rc);
+
+/* API implementations */
+
+#define define_checkpoint_api(api) \
+void libxl__checkpoint_devices_##api(libxl__egc *egc, \
+ libxl__checkpoint_devices_state *cds) \
+{ \
+ int i; \
+ libxl__checkpoint_device *dev; \
+ \
+ STATE_AO_GC(cds->ao); \
+ \
+ libxl__multidev_begin(ao, &cds->multidev); \
+ cds->multidev.callback = devices_checkpoint_cb; \
+ for (i = 0; i < cds->num_devices; i++) { \
+ dev = cds->devs[i]; \
+ if (!dev->matched || !dev->ops->api) \
+ continue; \
+ libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);\
+ dev->ops->api(egc,dev); \
+ } \
+ \
+ libxl__multidev_prepared(egc, &cds->multidev, 0); \
+}
+
+define_checkpoint_api(postsuspend);
+
+define_checkpoint_api(preresume);
+
+define_checkpoint_api(commit);
+
+static void devices_checkpoint_cb(libxl__egc *egc,
+ libxl__multidev *multidev,
+ int rc)
+{
+ STATE_AO_GC(multidev->ao);
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *const cds =
+ CONTAINER_OF(multidev, *cds, multidev);
+
+ cds->callback(egc, cds, rc);
+}
--- /dev/null
+/*
+ * Copyright (C) 2016 FUJITSU LIMITED
+ * Author: Wen Congyang <wency@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#ifndef LIBXL_COLO_H
+#define LIBXL_COLO_H
+
+#include "libxl_internal.h"
+
+/* Maximum time(5s) to wait for colo proxy checkpoit */
+#define COLO_PROXY_CHECKPOINT_TIMEOUT 5000000
+
+#define ASYNC_CALL(egc, ao, child, param, func, callback) do { \
+ int pid = -1; \
+ STATE_AO_GC(ao); \
+ \
+ pid = libxl__ev_child_fork(gc, child, callback); \
+ if (pid == -1) { \
+ LOGD(ERROR, ao->domid, "unable to fork"); \
+ goto out; \
+ } \
+ \
+ if (!pid) { \
+ /* child */ \
+ func(param); \
+ /* notreached */ \
+ abort(); \
+ } \
+ \
+ return; \
+out: \
+ callback(egc, child, -1, 1); \
+} while (0)
+
+enum {
+ LIBXL_COLO_SETUPED,
+ LIBXL_COLO_SUSPENDED,
+ LIBXL_COLO_RESUMED,
+};
+
+struct libxl__colo_device_nic {
+ int devid;
+ const char *vif;
+};
+
+struct libxl__colo_qdisk {
+ bool setuped;
+};
+
+struct libxl__colo_proxy_state {
+ /* set by caller of colo_proxy_setup */
+ struct libxl__ao *ao;
+
+ int sock_fd;
+ int index;
+ /*
+ * Private, True means use userspace colo proxy
+ * False means use kernel colo proxy.
+ */
+ bool is_userspace_proxy;
+ const char *checkpoint_host;
+ const char *checkpoint_port;
+};
+
+struct libxl__colo_save_state {
+ int send_fd;
+ int recv_fd;
+ char *colo_proxy_script;
+
+ /* private */
+ libxl__stream_read_state srs;
+ void (*callback)(libxl__egc *, libxl__colo_save_state *, int);
+ bool svm_running;
+ bool paused;
+
+ /* private, used by qdisk block replication */
+ bool qdisk_used;
+ bool qdisk_setuped;
+
+ /* private, used by colo-proxy */
+ libxl__colo_proxy_state cps;
+ libxl__ev_child child;
+};
+
+
+typedef void libxl__colo_callback(struct libxl__egc *egc,
+ libxl__colo_restore_state *crs, int rc);
+
+struct libxl__colo_restore_state {
+ /* must set by caller of libxl__colo_(setup|teardown) */
+ struct libxl__ao *ao;
+ uint32_t domid;
+ int send_back_fd;
+ int recv_fd;
+ int hvm;
+ libxl__colo_callback *callback;
+ char *colo_proxy_script;
+
+ /* private, colo restore checkpoint state */
+ libxl__domain_create_cb *saved_cb;
+ void *crcs;
+
+ /* private, used by qdisk block replication */
+ bool qdisk_used;
+ bool qdisk_setuped;
+ const char *host;
+ const char *port;
+
+ /* private, used by colo-proxy */
+ libxl__colo_proxy_state cps;
+};
+
+int init_subkind_qdisk(struct libxl__checkpoint_devices_state *cds);
+
+void cleanup_subkind_qdisk(struct libxl__checkpoint_devices_state *cds);
+
+int init_subkind_colo_nic(struct libxl__checkpoint_devices_state *cds);
+
+void cleanup_subkind_colo_nic(struct libxl__checkpoint_devices_state *cds);
+
+extern void libxl__colo_restore_setup(struct libxl__egc *egc,
+ libxl__colo_restore_state *crs);
+extern void libxl__colo_restore_teardown(struct libxl__egc *egc, void *dcs_void,
+ int ret, int retval, int errnoval);
+extern void libxl__colo_save_setup(struct libxl__egc *egc,
+ struct libxl__colo_save_state *css);
+extern void libxl__colo_save_teardown(struct libxl__egc *egc,
+ struct libxl__colo_save_state *css,
+ int rc);
+extern int colo_proxy_setup(libxl__colo_proxy_state *cps);
+extern void colo_proxy_teardown(libxl__colo_proxy_state *cps);
+extern void colo_proxy_preresume(libxl__colo_proxy_state *cps);
+extern void colo_proxy_postresume(libxl__colo_proxy_state *cps);
+extern int colo_proxy_checkpoint(libxl__colo_proxy_state *cps,
+ unsigned int timeout_us);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2016 FUJITSU LIMITED
+ * Author: Wen Congyang <wency@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+enum {
+ primary,
+ secondary,
+};
+
+/* ========== init() and cleanup() ========== */
+
+int init_subkind_colo_nic(libxl__checkpoint_devices_state *cds)
+{
+ return 0;
+}
+
+void cleanup_subkind_colo_nic(libxl__checkpoint_devices_state *cds)
+{
+}
+
+/* ========== helper functions ========== */
+
+static void colo_save_setup_script_cb(libxl__egc *egc,
+ libxl__async_exec_state *aes,
+ int rc, int status);
+static void colo_save_teardown_script_cb(libxl__egc *egc,
+ libxl__async_exec_state *aes,
+ int rc, int status);
+
+/*
+ * If the device has a vifname, then use that instead of
+ * the vifX.Y format.
+ * it must ONLY be used for remus because if driver domains
+ * were in use it would constitute a security vulnerability.
+ */
+static const char *get_vifname(libxl__checkpoint_device *dev,
+ const libxl_device_nic *nic)
+{
+ const char *vifname = NULL;
+ const char *path;
+ int rc;
+
+ STATE_AO_GC(dev->cds->ao);
+
+ /* Convenience aliases */
+ const uint32_t domid = dev->cds->domid;
+
+ path = GCSPRINTF("%s/vifname",
+ libxl__domain_device_backend_path(gc, 0,
+ domid, nic->devid, LIBXL__DEVICE_KIND_VIF));
+ rc = libxl__xs_read_checked(gc, XBT_NULL, path, &vifname);
+ if (!rc && !vifname) {
+ vifname = libxl__device_nic_devname(gc, domid,
+ nic->devid,
+ nic->nictype);
+ }
+
+ return vifname;
+}
+
+/*
+ * the script needs the following env & args
+ * $vifname
+ * $forwarddev
+ * $mode(primary/secondary)
+ * $index
+ * $bridge
+ * setup/teardown as command line arg.
+ */
+static void setup_async_exec(libxl__checkpoint_device *dev, char *op,
+ libxl__colo_proxy_state *cps, int side,
+ char *colo_proxy_script)
+{
+ int arraysize, nr = 0;
+ char **env = NULL, **args = NULL;
+ libxl__colo_device_nic *colo_nic = dev->concrete_data;
+ libxl__checkpoint_devices_state *cds = dev->cds;
+ libxl__async_exec_state *aes = &dev->aodev.aes;
+ const libxl_device_nic *nic = dev->backend_dev;
+
+ STATE_AO_GC(cds->ao);
+
+ /* Convenience aliases */
+ const char *const vif = colo_nic->vif;
+
+ arraysize = 11;
+ GCNEW_ARRAY(env, arraysize);
+ env[nr++] = "vifname";
+ env[nr++] = libxl__strdup(gc, vif);
+ env[nr++] = "forwarddev";
+ env[nr++] = libxl__strdup(gc, nic->coloft_forwarddev);
+ env[nr++] = "mode";
+ if (side == primary)
+ env[nr++] = "primary";
+ else
+ env[nr++] = "secondary";
+ env[nr++] = "index";
+ env[nr++] = GCSPRINTF("%d", cps->index);
+ env[nr++] = "bridge";
+ env[nr++] = libxl__strdup(gc, nic->bridge);
+ env[nr++] = NULL;
+ assert(nr == arraysize);
+
+ arraysize = 3; nr = 0;
+ GCNEW_ARRAY(args, arraysize);
+ args[nr++] = colo_proxy_script;
+ args[nr++] = op;
+ args[nr++] = NULL;
+ assert(nr == arraysize);
+
+ aes->ao = dev->cds->ao;
+ aes->what = GCSPRINTF("%s %s", args[0], args[1]);
+ aes->env = env;
+ aes->args = args;
+ aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
+ aes->stdfds[0] = -1;
+ aes->stdfds[1] = -1;
+ aes->stdfds[2] = -1;
+
+ if (!strcmp(op, "teardown"))
+ aes->callback = colo_save_teardown_script_cb;
+ else
+ aes->callback = colo_save_setup_script_cb;
+}
+
+/* ========== setup() and teardown() ========== */
+
+static void colo_nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev,
+ libxl__colo_proxy_state *cps, int side,
+ char *colo_proxy_script)
+{
+ int rc;
+ libxl__colo_device_nic *colo_nic;
+ const libxl_device_nic *nic = dev->backend_dev;
+
+ STATE_AO_GC(dev->cds->ao);
+
+ /*
+ * thers's no subkind of nic devices, so nic ops is always matched
+ * with nic devices, we begin to setup the nic device
+ */
+ dev->matched = 1;
+
+ if (!nic->coloft_forwarddev) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ GCNEW(colo_nic);
+ dev->concrete_data = colo_nic;
+ colo_nic->devid = nic->devid;
+ colo_nic->vif = get_vifname(dev, nic);
+ if (!colo_nic->vif) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ setup_async_exec(dev, "setup", cps, side, colo_proxy_script);
+ rc = libxl__async_exec_start(&dev->aodev.aes);
+ if (rc)
+ goto out;
+
+ return;
+
+out:
+ dev->aodev.rc = rc;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+static void colo_save_setup_script_cb(libxl__egc *egc,
+ libxl__async_exec_state *aes,
+ int rc, int status)
+{
+ libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
+ libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
+ libxl__colo_device_nic *colo_nic = dev->concrete_data;
+ libxl__checkpoint_devices_state *cds = dev->cds;
+ const char *out_path_base, *hotplug_error = NULL;
+
+ EGC_GC;
+
+ /* Convenience aliases */
+ const uint32_t domid = cds->domid;
+ const int devid = colo_nic->devid;
+ const char *const vif = colo_nic->vif;
+
+ if (status && !rc)
+ rc = ERROR_FAIL;
+ if (rc)
+ goto out;
+
+ out_path_base = GCSPRINTF("%s/colo_proxy/%d",
+ libxl__xs_libxl_path(gc, domid), devid);
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/hotplug-error", out_path_base),
+ &hotplug_error);
+ if (rc)
+ goto out;
+
+ if (hotplug_error) {
+ LOGD(ERROR, domid, "colo_proxy script %s setup failed for vif %s: %s",
+ aes->args[0], vif, hotplug_error);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+static void colo_nic_teardown(libxl__egc *egc, libxl__checkpoint_device *dev,
+ libxl__colo_proxy_state *cps, int side,
+ char *colo_proxy_script)
+{
+ int rc;
+ libxl__colo_device_nic *colo_nic = dev->concrete_data;
+
+ if (!colo_nic || !colo_nic->vif) {
+ /* colo nic has not yet been set up, just return */
+ rc = 0;
+ goto out;
+ }
+
+ setup_async_exec(dev, "teardown", cps, side, colo_proxy_script);
+
+ rc = libxl__async_exec_start(&dev->aodev.aes);
+ if (rc)
+ goto out;
+
+ return;
+
+out:
+ dev->aodev.rc = rc;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+static void colo_save_teardown_script_cb(libxl__egc *egc,
+ libxl__async_exec_state *aes,
+ int rc, int status)
+{
+ libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
+
+ if (status && !rc)
+ rc = ERROR_FAIL;
+ else
+ rc = 0;
+
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+/* ======== primary ======== */
+
+static void colo_nic_save_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+ libxl__colo_save_state *css = dev->cds->concrete_data;
+
+ colo_nic_setup(egc, dev, &css->cps, primary, css->colo_proxy_script);
+}
+
+static void colo_nic_save_teardown(libxl__egc *egc,
+ libxl__checkpoint_device *dev)
+{
+ libxl__colo_save_state *css = dev->cds->concrete_data;
+
+ colo_nic_teardown(egc, dev, &css->cps, primary, css->colo_proxy_script);
+}
+
+const libxl__checkpoint_device_instance_ops colo_save_device_nic = {
+ .kind = LIBXL__DEVICE_KIND_VIF,
+ .setup = colo_nic_save_setup,
+ .teardown = colo_nic_save_teardown,
+};
+
+/* ======== secondary ======== */
+
+static void colo_nic_restore_setup(libxl__egc *egc,
+ libxl__checkpoint_device *dev)
+{
+ libxl__colo_restore_state *crs = dev->cds->concrete_data;
+
+ colo_nic_setup(egc, dev, &crs->cps, secondary, crs->colo_proxy_script);
+}
+
+static void colo_nic_restore_teardown(libxl__egc *egc,
+ libxl__checkpoint_device *dev)
+{
+ libxl__colo_restore_state *crs = dev->cds->concrete_data;
+
+ colo_nic_teardown(egc, dev, &crs->cps, secondary, crs->colo_proxy_script);
+}
+
+const libxl__checkpoint_device_instance_ops colo_restore_device_nic = {
+ .kind = LIBXL__DEVICE_KIND_VIF,
+ .setup = colo_nic_restore_setup,
+ .teardown = colo_nic_restore_teardown,
+};
--- /dev/null
+/*
+ * Copyright (C) 2016 FUJITSU LIMITED
+ * Author: Yang Hongyang <hongyang.yang@easystack.cn>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#include <netlink/netlink.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/* Consistent with the new COLO netlink channel in kernel side */
+#define NETLINK_COLO 28
+#define COLO_DEFAULT_WAIT_TIME 500000
+
+enum colo_netlink_op {
+ COLO_QUERY_CHECKPOINT = (NLMSG_MIN_TYPE + 1),
+ COLO_CHECKPOINT,
+ COLO_FAILOVER,
+ COLO_PROXY_INIT,
+ COLO_PROXY_RESET, /* UNUSED, will be used for continuous FT */
+};
+
+/* ========= colo-proxy: helper functions ========== */
+
+static int colo_proxy_send(libxl__colo_proxy_state *cps, uint8_t *buff,
+ uint64_t size, int type)
+{
+ struct sockaddr_nl sa;
+ struct nlmsghdr msg;
+ struct iovec iov;
+ struct msghdr mh;
+ int ret;
+
+ STATE_AO_GC(cps->ao);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.nl_family = AF_NETLINK;
+ sa.nl_pid = 0;
+ sa.nl_groups = 0;
+
+ msg.nlmsg_len = NLMSG_SPACE(0);
+ msg.nlmsg_flags = NLM_F_REQUEST;
+ if (type == COLO_PROXY_INIT)
+ msg.nlmsg_flags |= NLM_F_ACK;
+ msg.nlmsg_seq = 0;
+ msg.nlmsg_pid = cps->index;
+ msg.nlmsg_type = type;
+
+ iov.iov_base = &msg;
+ iov.iov_len = msg.nlmsg_len;
+
+ mh.msg_name = &sa;
+ mh.msg_namelen = sizeof(sa);
+ mh.msg_iov = &iov;
+ mh.msg_iovlen = 1;
+ mh.msg_control = NULL;
+ mh.msg_controllen = 0;
+ mh.msg_flags = 0;
+
+ ret = sendmsg(cps->sock_fd, &mh, 0);
+ if (ret <= 0) {
+ LOGD(ERROR, ao->domid, "can't send msg to kernel by netlink: %s",
+ strerror(errno));
+ }
+
+ return ret;
+}
+
+static int colo_userspace_proxy_send(libxl__colo_proxy_state *cps,
+ uint8_t *buff,
+ uint32_t size)
+{
+ int ret = 0;
+ uint32_t len = 0;
+
+ len = htonl(size);
+ ret = send(cps->sock_fd, (uint8_t *)&len, sizeof(len), 0);
+ if (ret != sizeof(len)) {
+ goto err;
+ }
+
+ ret = send(cps->sock_fd, (uint8_t *)buff, size, 0);
+ if (ret != size) {
+ goto err;
+ }
+
+err:
+ return ret;
+}
+
+static int colo_userspace_proxy_recv(libxl__colo_proxy_state *cps,
+ char *buff,
+ unsigned int timeout_us)
+{
+ struct timeval tv;
+ int ret;
+ uint32_t len = 0;
+ uint32_t size = 0;
+
+ STATE_AO_GC(cps->ao);
+
+ if (timeout_us) {
+ tv.tv_sec = timeout_us / 1000000;
+ tv.tv_usec = timeout_us % 1000000;
+ ret = setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv,
+ sizeof(tv));
+ if (ret < 0) {
+ LOGD(ERROR, ao->domid,
+ "colo_userspace_proxy_recv setsockopt error: %s",
+ strerror(errno));
+ }
+ }
+
+ ret = recv(cps->sock_fd, (uint8_t *)&len, sizeof(len), 0);
+ if (ret < 0) {
+ goto err;
+ }
+
+ size = ntohl(len);
+ ret = recv(cps->sock_fd, buff, size, 0);
+
+err:
+ return ret;
+}
+
+/* error: return -1, otherwise return 0 */
+static int64_t colo_proxy_recv(libxl__colo_proxy_state *cps, uint8_t **buff,
+ unsigned int timeout_us)
+{
+ struct sockaddr_nl sa;
+ struct iovec iov;
+ struct msghdr mh = {
+ .msg_name = &sa,
+ .msg_namelen = sizeof(sa),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ struct timeval tv;
+ uint32_t size = 16384;
+ int64_t len = 0;
+ int ret;
+
+ STATE_AO_GC(cps->ao);
+ uint8_t *tmp = libxl__malloc(NOGC, size);
+
+ if (timeout_us) {
+ tv.tv_sec = timeout_us / 1000000;
+ tv.tv_usec = timeout_us % 1000000;
+ setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ }
+
+ iov.iov_base = tmp;
+ iov.iov_len = size;
+next:
+ ret = recvmsg(cps->sock_fd, &mh, 0);
+ if (ret <= 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
+ LOGED(ERROR, ao->domid, "can't recv msg from kernel by netlink");
+ goto err;
+ }
+
+ len += ret;
+ if (mh.msg_flags & MSG_TRUNC) {
+ size += 16384;
+ tmp = libxl__realloc(NOGC, tmp, size);
+ iov.iov_base = tmp + len;
+ iov.iov_len = size - len;
+ goto next;
+ }
+
+ *buff = tmp;
+ ret = len;
+ goto out;
+
+err:
+ free(tmp);
+ *buff = NULL;
+
+out:
+ if (timeout_us) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ }
+ return ret;
+}
+
+/* ========= colo-proxy: setup and teardown ========== */
+
+int colo_proxy_setup(libxl__colo_proxy_state *cps)
+{
+ int skfd = 0;
+ struct sockaddr_nl sa;
+ struct nlmsghdr *h;
+ int i = 1;
+ int ret = ERROR_FAIL;
+ uint8_t *buff = NULL;
+ int64_t size;
+
+ STATE_AO_GC(cps->ao);
+
+ /* If enable userspace proxy mode, we don't need setup kernel proxy */
+ if (cps->is_userspace_proxy) {
+ struct sockaddr_in addr;
+ int port;
+ char recvbuff[1024];
+ const char sendbuf[] = "COLO_USERSPACE_PROXY_INIT";
+
+ memset(&addr, 0, sizeof(addr));
+ port = atoi(cps->checkpoint_port);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = inet_addr(cps->checkpoint_host);
+
+ skfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (skfd < 0) {
+ LOGD(ERROR, ao->domid, "can not create a TCP socket: %s",
+ strerror(errno));
+ goto out;
+ }
+
+ cps->sock_fd = skfd;
+
+ if (connect(skfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ LOGD(ERROR, ao->domid, "connect error");
+ goto out;
+ }
+
+ ret = colo_userspace_proxy_send(cps, (uint8_t *)sendbuf, strlen(sendbuf));
+ if (ret < 0)
+ goto out;
+
+ ret = colo_userspace_proxy_recv(cps, recvbuff, COLO_DEFAULT_WAIT_TIME);
+ if (ret < 0) {
+ LOGD(ERROR, ao->domid, "Can't recv msg from qemu colo-compare: %s",
+ strerror(errno));
+ goto out;
+ }
+
+ return 0;
+ }
+
+ skfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_COLO);
+ if (skfd < 0) {
+ LOGD(ERROR, ao->domid, "can not create a netlink socket: %s", strerror(errno));
+ goto out;
+ }
+ cps->sock_fd = skfd;
+ memset(&sa, 0, sizeof(sa));
+ sa.nl_family = AF_NETLINK;
+ sa.nl_groups = 0;
+retry:
+ sa.nl_pid = i++;
+
+ if (i > 10) {
+ LOGD(ERROR, ao->domid, "netlink bind error");
+ goto out;
+ }
+
+ ret = bind(skfd, (struct sockaddr *)&sa, sizeof(sa));
+ if (ret < 0 && errno == EADDRINUSE) {
+ LOGD(ERROR, ao->domid, "colo index %d has already in used", sa.nl_pid);
+ goto retry;
+ } else if (ret < 0) {
+ LOGD(ERROR, ao->domid, "netlink bind error");
+ goto out;
+ }
+
+ cps->index = sa.nl_pid;
+ ret = colo_proxy_send(cps, NULL, 0, COLO_PROXY_INIT);
+ if (ret < 0)
+ goto out;
+
+ /* receive ack */
+ size = colo_proxy_recv(cps, &buff, 500000);
+ if (size < 0) {
+ LOGD(ERROR, ao->domid, "Can't recv msg from kernel by netlink: %s",
+ strerror(errno));
+ goto out;
+ }
+
+ if (size) {
+ h = (struct nlmsghdr *)buff;
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ /* ack's type is NLMSG_ERROR */
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
+
+ if (size - sizeof(*h) < sizeof(*err)) {
+ LOGD(ERROR, ao->domid, "NLMSG_LENGTH is too short");
+ goto out;
+ }
+
+ if (err->error) {
+ LOGD(ERROR, ao->domid, "NLMSG_ERROR contains error %d", err->error);
+ goto out;
+ }
+ }
+ }
+
+ ret = 0;
+
+out:
+ free(buff);
+ if (ret) {
+ close(cps->sock_fd);
+ cps->sock_fd = -1;
+ }
+ return ret;
+}
+
+void colo_proxy_teardown(libxl__colo_proxy_state *cps)
+{
+ /*
+ * If enable userspace proxy mode,
+ * we don't need teardown kernel proxy
+ */
+ if (cps->is_userspace_proxy)
+ return;
+
+ if (cps->sock_fd >= 0) {
+ close(cps->sock_fd);
+ cps->sock_fd = -1;
+ }
+}
+
+/* ========= colo-proxy: preresume, postresume and checkpoint ========== */
+
+void colo_proxy_preresume(libxl__colo_proxy_state *cps)
+{
+ /*
+ * If enable userspace proxy mode,
+ * we don't need preresume kernel proxy
+ */
+ if (cps->is_userspace_proxy) {
+ const char sendbuf[] = "COLO_CHECKPOINT";
+ colo_userspace_proxy_send(cps,
+ (uint8_t *)sendbuf,
+ strlen(sendbuf));
+ return;
+ }
+
+ colo_proxy_send(cps, NULL, 0, COLO_CHECKPOINT);
+ /* TODO: need to handle if the call fails... */
+}
+
+void colo_proxy_postresume(libxl__colo_proxy_state *cps)
+{
+ /* nothing to do... */
+}
+
+typedef struct colo_msg {
+ bool is_checkpoint;
+} colo_msg;
+
+/*
+ * Return value:
+ * -1: error
+ * 0: no checkpoint event is received before timeout
+ * 1: do checkpoint
+ */
+int colo_proxy_checkpoint(libxl__colo_proxy_state *cps,
+ unsigned int timeout_us)
+{
+ uint8_t *buff = NULL;
+ int64_t size;
+ struct nlmsghdr *h;
+ struct colo_msg *m;
+ int ret = -1;
+ char recvbuff[1024];
+
+ STATE_AO_GC(cps->ao);
+
+ /*
+ * Enable userspace proxy to periodical checkpoint mode,
+ * sleeping temporarily for colo userspace proxy mode.
+ * then we will use socket recv instead of this usleep.
+ * In other words, we use socket communicate with Qemu
+ * Proxy part(colo-compare), for example, notify checkpoint
+ * event.
+ */
+ if (cps->is_userspace_proxy) {
+ ret = colo_userspace_proxy_recv(cps, recvbuff, timeout_us);
+ if (ret <= 0) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!strcmp(recvbuff, "DO_CHECKPOINT")) {
+ ret = 1;
+ } else {
+ LOGD(ERROR, ao->domid, "receive qemu colo-compare checkpoint error");
+ ret = 0;
+ }
+ goto out;
+ }
+
+ size = colo_proxy_recv(cps, &buff, timeout_us);
+
+ /* timeout, return no checkpoint message. */
+ if (size <= 0) {
+ ret = 0;
+ goto out;
+ }
+
+ h = (struct nlmsghdr *) buff;
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ LOGD(ERROR, ao->domid, "receive NLMSG_ERROR");
+ goto out;
+ }
+
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*m))) {
+ LOGD(ERROR, ao->domid, "NLMSG_LENGTH is too short");
+ goto out;
+ }
+
+ m = NLMSG_DATA(h);
+
+ ret = m->is_checkpoint ? 1 : 0;
+
+out:
+ free(buff);
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2016 FUJITSU LIMITED
+ * Author: Wen Congyang <wency@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/* ========== init() and cleanup() ========== */
+
+int init_subkind_qdisk(libxl__checkpoint_devices_state *cds)
+{
+ /*
+ * We don't know if we use qemu block replication, so
+ * we cannot start block replication here.
+ */
+ return 0;
+}
+
+void cleanup_subkind_qdisk(libxl__checkpoint_devices_state *cds)
+{
+}
+
+/* ========== setup() and teardown() ========== */
+
+static void colo_qdisk_setup(libxl__egc *egc, libxl__checkpoint_device *dev,
+ bool primary)
+{
+ const libxl_device_disk *disk = dev->backend_dev;
+ int ret, rc = 0;
+ libxl__colo_qdisk *colo_qdisk = NULL;
+ char port[32];
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *const cds = dev->cds;
+ const char *host = disk->colo_host;
+ const char *export_name = disk->colo_export;
+ const int domid = cds->domid;
+
+ STATE_AO_GC(dev->cds->ao);
+
+ if (disk->backend != LIBXL_DISK_BACKEND_QDISK ||
+ !libxl_defbool_val(disk->colo_enable) ||
+ !host || !export_name || (disk->colo_port <= 0) ||
+ !disk->active_disk || !disk->hidden_disk) {
+ rc = ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH;
+ goto out;
+ }
+
+ dev->matched = true;
+
+ GCNEW(colo_qdisk);
+ dev->concrete_data = colo_qdisk;
+
+ if (primary) {
+ libxl__colo_save_state *css = cds->concrete_data;
+
+ css->qdisk_used = true;
+ /* NBD server is not ready, so we cannot start block replication now */
+ goto out;
+ } else {
+ libxl__colo_restore_state *crs = cds->concrete_data;
+ sprintf(port, "%d", disk->colo_port);
+
+ if (!crs->qdisk_used) {
+ /* start nbd server */
+ ret = libxl__qmp_nbd_server_start(gc, domid, host, port);
+ if (ret) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ crs->host = host;
+ crs->port = port;
+ } else {
+ if (strcmp(crs->host, host) || strcmp(crs->port, port)) {
+ LOGD(ERROR, domid, "The host and port of all disks must be the same");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ crs->qdisk_used = true;
+
+ ret = libxl__qmp_nbd_server_add(gc, domid, export_name);
+ if (ret)
+ rc = ERROR_FAIL;
+
+ colo_qdisk->setuped = true;
+ }
+
+out:
+ dev->aodev.rc = rc;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+static void colo_qdisk_teardown(libxl__egc *egc, libxl__checkpoint_device *dev,
+ bool primary)
+{
+ int ret, rc = 0;
+ const libxl__colo_qdisk *colo_qdisk = dev->concrete_data;
+ const libxl_device_disk *disk = dev->backend_dev;
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *const cds = dev->cds;
+ const int domid = cds->domid;
+ const char *export_name = disk->colo_export;
+
+ EGC_GC;
+
+ if (primary) {
+ if (!colo_qdisk->setuped)
+ goto out;
+
+ /*
+ * There is no way to get the child name, but we know it is children.1
+ */
+ ret = libxl__qmp_x_blockdev_change(gc, domid, export_name,
+ "children.1", NULL);
+ if (ret)
+ rc = ERROR_FAIL;
+ } else {
+ libxl__colo_restore_state *crs = cds->concrete_data;
+
+ if (crs->qdisk_used) {
+ ret = libxl__qmp_nbd_server_stop(gc, domid);
+ if (ret)
+ rc = ERROR_FAIL;
+ }
+ }
+
+out:
+ dev->aodev.rc = rc;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+/* ========== checkpointing APIs ========== */
+
+static void colo_qdisk_save_preresume(libxl__egc *egc,
+ libxl__checkpoint_device *dev)
+{
+ libxl__colo_qdisk *colo_qdisk = dev->concrete_data;
+ const libxl_device_disk *disk = dev->backend_dev;
+ int ret, rc = 0;
+ char *node = NULL;
+ char *cmd = NULL;
+
+ /* Convenience aliases */
+ const int domid = dev->cds->domid;
+ const char *host = disk->colo_host;
+ int port = disk->colo_port;
+ const char *export_name = disk->colo_export;
+
+ EGC_GC;
+
+ if (colo_qdisk->setuped)
+ goto out;
+
+ /* qmp command doesn't support the driver "nbd" */
+ node = GCSPRINTF("colo_node%d",
+ libxl__device_disk_dev_number(disk->vdev, NULL, NULL));
+ cmd = GCSPRINTF("drive_add -n buddy driver=replication,mode=primary,"
+ "file.driver=nbd,file.host=%s,file.port=%d,"
+ "file.export=%s,node-name=%s",
+ host, port, export_name, node);
+ ret = libxl__qmp_hmp(gc, domid, cmd, NULL);
+ if (ret)
+ rc = ERROR_FAIL;
+
+ ret = libxl__qmp_x_blockdev_change(gc, domid, export_name, NULL, node);
+ if (ret)
+ rc = ERROR_FAIL;
+
+ colo_qdisk->setuped = true;
+
+out:
+ dev->aodev.rc = rc;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+/* ======== primary ======== */
+
+static void colo_qdisk_save_setup(libxl__egc *egc,
+ libxl__checkpoint_device *dev)
+{
+ colo_qdisk_setup(egc, dev, true);
+}
+
+static void colo_qdisk_save_teardown(libxl__egc *egc,
+ libxl__checkpoint_device *dev)
+{
+ colo_qdisk_teardown(egc, dev, true);
+}
+
+const libxl__checkpoint_device_instance_ops colo_save_device_qdisk = {
+ .kind = LIBXL__DEVICE_KIND_VBD,
+ .setup = colo_qdisk_save_setup,
+ .teardown = colo_qdisk_save_teardown,
+ .preresume = colo_qdisk_save_preresume,
+};
+
+/* ======== secondary ======== */
+
+static void colo_qdisk_restore_setup(libxl__egc *egc,
+ libxl__checkpoint_device *dev)
+{
+ colo_qdisk_setup(egc, dev, false);
+}
+
+static void colo_qdisk_restore_teardown(libxl__egc *egc,
+ libxl__checkpoint_device *dev)
+{
+ colo_qdisk_teardown(egc, dev, false);
+}
+
+const libxl__checkpoint_device_instance_ops colo_restore_device_qdisk = {
+ .kind = LIBXL__DEVICE_KIND_VBD,
+ .setup = colo_qdisk_restore_setup,
+ .teardown = colo_qdisk_restore_teardown,
+};
--- /dev/null
+/*
+ * Copyright (C) 2016 FUJITSU LIMITED
+ * Author: Wen Congyang <wency@cn.fujitsu.com>
+ * Yang Hongyang <hongyang.yang@easystack.cn>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+#include "libxl_sr_stream_format.h"
+
+typedef struct libxl__colo_restore_checkpoint_state libxl__colo_restore_checkpoint_state;
+struct libxl__colo_restore_checkpoint_state {
+ libxl__domain_suspend_state dsps;
+ libxl__logdirty_switch lds;
+ libxl__colo_restore_state *crs;
+ libxl__stream_write_state sws;
+ int status;
+ bool preresume;
+ /* used for teardown */
+ int teardown_devices;
+ int saved_rc;
+ char *state_file;
+
+ void (*callback)(libxl__egc *,
+ libxl__colo_restore_checkpoint_state *,
+ int);
+};
+
+extern const libxl__checkpoint_device_instance_ops colo_restore_device_nic;
+extern const libxl__checkpoint_device_instance_ops colo_restore_device_qdisk;
+
+static const libxl__checkpoint_device_instance_ops *colo_restore_ops[] = {
+ &colo_restore_device_nic,
+ &colo_restore_device_qdisk,
+ NULL,
+};
+
+/* ===================== colo: common functions ===================== */
+
+static void colo_enable_logdirty(libxl__colo_restore_state *crs, libxl__egc *egc)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+ libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+ /* Convenience aliases */
+ const uint32_t domid = crs->domid;
+ libxl__logdirty_switch *const lds = &crcs->lds;
+
+ EGC_GC;
+
+ /* we need to know which pages are dirty to restore the guest */
+ if (xc_shadow_control(CTX->xch, domid,
+ XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY,
+ NULL, 0, NULL, 0, NULL) < 0) {
+ LOGD(ERROR, domid, "cannot enable secondary vm's logdirty");
+ lds->callback(egc, lds, ERROR_FAIL);
+ return;
+ }
+
+ if (crs->hvm) {
+ libxl__domain_common_switch_qemu_logdirty(egc, domid, 1, lds);
+ return;
+ }
+
+ lds->callback(egc, lds, 0);
+}
+
+static void colo_disable_logdirty(libxl__colo_restore_state *crs,
+ libxl__egc *egc)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+ libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+ /* Convenience aliases */
+ const uint32_t domid = crs->domid;
+ libxl__logdirty_switch *const lds = &crcs->lds;
+
+ EGC_GC;
+
+ /* we need to know which pages are dirty to restore the guest */
+ if (xc_shadow_control(CTX->xch, domid, XEN_DOMCTL_SHADOW_OP_OFF,
+ NULL, 0, NULL, 0, NULL) < 0)
+ LOGD(WARN, domid, "cannot disable secondary vm's logdirty");
+
+ if (crs->hvm) {
+ libxl__domain_common_switch_qemu_logdirty(egc, domid, 0, lds);
+ return;
+ }
+
+ lds->callback(egc, lds, 0);
+}
+
+static void colo_resume_vm(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs,
+ int restore_device_model)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+ int rc;
+
+ /* Convenience aliases */
+ libxl__colo_restore_state *const crs = crcs->crs;
+
+ EGC_GC;
+
+ if (!crs->saved_cb) {
+ /* TODO: sync mmu for hvm? */
+ if (restore_device_model) {
+ rc = libxl__qmp_restore(gc, crs->domid, crcs->state_file);
+ if (rc) {
+ LOGD(ERROR, crs->domid,
+ "cannot restore device model for secondary vm");
+ crcs->callback(egc, crcs, rc);
+ return;
+ }
+ }
+ rc = libxl__domain_resume_deprecated(gc, crs->domid, 0);
+ if (rc)
+ LOGD(ERROR, crs->domid, "cannot resume secondary vm");
+
+ crcs->callback(egc, crcs, rc);
+ return;
+ }
+
+ libxl__xc_domain_restore_done(egc, dcs, 0, 0, 0);
+
+ return;
+}
+
+static int init_device_subkind(libxl__checkpoint_devices_state *cds)
+{
+ /* init device subkind-specific state in the libxl ctx */
+ int rc;
+ STATE_AO_GC(cds->ao);
+
+ rc = init_subkind_colo_nic(cds);
+ if (rc) goto out;
+
+ rc = init_subkind_qdisk(cds);
+ if (rc) {
+ cleanup_subkind_colo_nic(cds);
+ goto out;
+ }
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds)
+{
+ /* cleanup device subkind-specific state in the libxl ctx */
+ STATE_AO_GC(cds->ao);
+
+ cleanup_subkind_colo_nic(cds);
+ cleanup_subkind_qdisk(cds);
+}
+
+/* ================ colo: setup restore environment ================ */
+
+static void libxl__colo_domain_create_cb(libxl__egc *egc,
+ libxl__domain_create_state *dcs,
+ int rc, uint32_t domid);
+
+static int init_dsps(libxl__domain_suspend_state *dsps)
+{
+ int rc = ERROR_FAIL;
+ libxl_domain_type type;
+
+ STATE_AO_GC(dsps->ao);
+
+ libxl__xswait_init(&dsps->pvcontrol);
+ libxl__ev_evtchn_init(&dsps->guest_evtchn);
+ libxl__ev_xswatch_init(&dsps->guest_watch);
+ libxl__ev_time_init(&dsps->guest_timeout);
+
+ type = libxl__domain_type(gc, dsps->domid);
+ if (type == LIBXL_DOMAIN_TYPE_INVALID)
+ goto out;
+
+ dsps->type = type;
+
+ dsps->guest_evtchn.port = -1;
+ dsps->guest_evtchn_lockfd = -1;
+ dsps->guest_responded = 0;
+ dsps->dm_savefile = libxl__device_model_savefile(gc, dsps->domid);
+
+ /* Secondary vm is not created, so we cannot get evtchn port */
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+/*
+ * checkpoint callbacks are called in the following order:
+ * 1. resume
+ * 2. wait checkpoint
+ * 3. suspend
+ * 4. checkpoint
+ */
+static void libxl__colo_restore_domain_resume_callback(void *data);
+static void libxl__colo_restore_domain_wait_checkpoint_callback(void *data);
+static void libxl__colo_restore_domain_suspend_callback(void *data);
+static void libxl__colo_restore_domain_checkpoint_callback(void *data);
+
+void libxl__colo_restore_setup(libxl__egc *egc,
+ libxl__colo_restore_state *crs)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+ libxl__colo_restore_checkpoint_state *crcs;
+ int rc = ERROR_FAIL;
+
+ /* Convenience aliases */
+ libxl__srm_restore_autogen_callbacks *const callbacks =
+ &dcs->srs.shs.callbacks.restore.a;
+ const int domid = crs->domid;
+
+ STATE_AO_GC(crs->ao);
+
+ GCNEW(crcs);
+ crs->crcs = crcs;
+ crcs->crs = crs;
+ crs->qdisk_setuped = false;
+ crs->qdisk_used = false;
+ if (dcs->colo_proxy_script)
+ crs->colo_proxy_script = libxl__strdup(gc, dcs->colo_proxy_script);
+ else
+ crs->colo_proxy_script = GCSPRINTF("%s/colo-proxy-setup",
+ libxl__xen_script_dir_path());
+
+ /* setup dsps */
+ crcs->dsps.ao = ao;
+ crcs->dsps.domid = domid;
+ if (init_dsps(&crcs->dsps))
+ goto out;
+
+ callbacks->postcopy = libxl__colo_restore_domain_resume_callback;
+ callbacks->wait_checkpoint = libxl__colo_restore_domain_wait_checkpoint_callback;
+ callbacks->suspend = libxl__colo_restore_domain_suspend_callback;
+ callbacks->checkpoint = libxl__colo_restore_domain_checkpoint_callback;
+
+ /*
+ * Secondary vm is running in colo mode, so we need to call
+ * libxl__xc_domain_restore_done() to create secondary vm.
+ * But we will exit in domain_create_cb(). So replace the
+ * callback here.
+ */
+ crs->saved_cb = dcs->callback;
+ dcs->callback = libxl__colo_domain_create_cb;
+ crcs->state_file = GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", domid);
+ crcs->status = LIBXL_COLO_SETUPED;
+
+ libxl__logdirty_init(&crcs->lds);
+ crcs->lds.ao = ao;
+
+ crcs->sws.fd = crs->send_back_fd;
+ crcs->sws.ao = ao;
+ crcs->sws.back_channel = true;
+
+ dcs->cds.concrete_data = crs;
+
+ libxl__stream_write_start(egc, &crcs->sws);
+
+ rc = 0;
+
+out:
+ crs->callback(egc, crs, rc);
+ return;
+}
+
+static void libxl__colo_domain_create_cb(libxl__egc *egc,
+ libxl__domain_create_state *dcs,
+ int rc, uint32_t domid)
+{
+ libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
+
+ crcs->callback(egc, crcs, rc);
+}
+
+/* ================ colo: teardown restore environment ================ */
+
+static void colo_restore_teardown_devices_done(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds, int rc);
+static void do_failover(libxl__egc *egc, libxl__colo_restore_state *crs);
+static void do_failover_done(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state* crcs,
+ int rc);
+static void colo_disable_logdirty_done(libxl__egc *egc,
+ libxl__logdirty_switch *lds,
+ int rc);
+static void libxl__colo_restore_teardown_done(libxl__egc *egc,
+ libxl__colo_restore_state *crs,
+ int rc);
+
+void libxl__colo_restore_teardown(libxl__egc *egc, void *dcs_void,
+ int ret, int retval, int errnoval)
+{
+ libxl__domain_create_state *dcs = dcs_void;
+ libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
+ int rc = 1;
+
+ /* convenience aliases */
+ libxl__colo_restore_state *const crs = &dcs->crs;
+ EGC_GC;
+
+ if (ret == 0 && retval == 0)
+ rc = 0;
+
+ LOGD(INFO, crs->domid, "%s", rc ? "colo fails" : "failover");
+
+ libxl__stream_write_abort(egc, &crcs->sws, 1);
+ if (crs->saved_cb) {
+ /* crcs->status is LIBXL_COLO_SETUPED */
+ dcs->srs.completion_callback = NULL;
+ }
+ libxl__xc_domain_restore_done(egc, dcs, ret, retval, errnoval);
+
+ if (crs->qdisk_setuped) {
+ libxl__qmp_stop_replication(gc, crs->domid, false);
+ crs->qdisk_setuped = false;
+ }
+
+ crcs->saved_rc = rc;
+ if (!crcs->teardown_devices) {
+ colo_restore_teardown_devices_done(egc, &dcs->cds, 0);
+ return;
+ }
+
+ dcs->cds.callback = colo_restore_teardown_devices_done;
+ libxl__checkpoint_devices_teardown(egc, &dcs->cds);
+}
+
+static void colo_restore_teardown_devices_done(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds, int rc)
+{
+ libxl__colo_restore_state *crs = cds->concrete_data;
+ libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+ libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+
+ EGC_GC;
+
+ if (rc)
+ LOGD(ERROR, cds->domid, "COLO: failed to teardown device for guest,"
+ " rc %d", rc);
+
+ if (crcs->teardown_devices)
+ cleanup_device_subkind(cds);
+
+ colo_proxy_teardown(&crs->cps);
+
+ rc = crcs->saved_rc;
+ if (!rc) {
+ crcs->callback = do_failover_done;
+ do_failover(egc, crs);
+ return;
+ }
+
+ libxl__colo_restore_teardown_done(egc, crs, rc);
+}
+
+static void do_failover(libxl__egc *egc, libxl__colo_restore_state *crs)
+{
+ libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+ /* Convenience aliases */
+ const int status = crcs->status;
+ libxl__logdirty_switch *const lds = &crcs->lds;
+
+ EGC_GC;
+
+ switch(status) {
+ case LIBXL_COLO_SETUPED:
+ /*
+ * We will come here only when reading emulator xenstore data or
+ * emulator context fails, and libxl__xc_domain_restore_done()
+ * is not called. In this case, the migration is not finished,
+ * so we cannot do failover.
+ */
+ LOGD(ERROR, crs->domid, "migration fails");
+ crcs->callback(egc, crcs, ERROR_FAIL);
+ return;
+ case LIBXL_COLO_SUSPENDED:
+ case LIBXL_COLO_RESUMED:
+ /* disable logdirty first */
+ lds->callback = colo_disable_logdirty_done;
+ colo_disable_logdirty(crs, egc);
+ return;
+ default:
+ LOGD(ERROR, crs->domid, "invalid status: %d", status);
+ crcs->callback(egc, crcs, ERROR_FAIL);
+ }
+}
+
+static void do_failover_done(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state* crcs,
+ int rc)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+
+ /* Convenience aliases */
+ libxl__colo_restore_state *const crs = crcs->crs;
+
+ EGC_GC;
+
+ if (rc)
+ LOGD(ERROR, crs->domid, "cannot do failover");
+
+ libxl__colo_restore_teardown_done(egc, crs, rc);
+}
+
+static void colo_disable_logdirty_done(libxl__egc *egc,
+ libxl__logdirty_switch *lds,
+ int rc)
+{
+ libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
+
+ EGC_GC;
+
+ if (rc)
+ LOGD(WARN, crcs->crs->domid, "cannot disable logdirty");
+
+ if (crcs->status == LIBXL_COLO_SUSPENDED) {
+ /*
+ * failover when reading state from master, so no need to
+ * call libxl__qmp_restore().
+ */
+ colo_resume_vm(egc, crcs, 0);
+ return;
+ }
+
+ /* If we cannot disable logdirty, we still can do failover */
+ crcs->callback(egc, crcs, 0);
+}
+
+static void libxl__colo_restore_teardown_done(libxl__egc *egc,
+ libxl__colo_restore_state *crs,
+ int rc)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+ EGC_GC;
+
+ /* convenience aliases */
+ const int domid = crs->domid;
+ const libxl_ctx *const ctx = libxl__gc_owner(gc);
+ xc_interface *const xch = ctx->xch;
+
+ if (!rc)
+ /* failover, no need to destroy the secondary vm */
+ goto out;
+
+ xc_domain_destroy(xch, domid);
+
+out:
+ if (crs->saved_cb) {
+ dcs->callback = crs->saved_cb;
+ crs->saved_cb = NULL;
+ }
+
+ dcs->callback(egc, dcs, rc, crs->domid);
+}
+
+static void colo_common_write_stream_done(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ int rc);
+static void colo_common_read_stream_done(libxl__egc *egc,
+ libxl__stream_read_state *stream,
+ int rc);
+
+/* ======================== colo: checkpoint ======================= */
+
+/*
+ * Do the following things when resuming secondary vm:
+ * 1. read emulator xenstore data
+ * 2. read emulator context
+ * 3. REC_TYPE_CHECKPOINT_END
+ */
+static void libxl__colo_restore_domain_checkpoint_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
+ libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
+ libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
+
+ crcs->callback = NULL;
+ dcs->srs.checkpoint_callback = colo_common_read_stream_done;
+ libxl__stream_read_start_checkpoint(shs->egc, &dcs->srs);
+}
+
+/* ===================== colo: resume secondary vm ===================== */
+
+/*
+ * Do the following things when resuming secondary vm the first time:
+ * 1. resume secondary vm
+ * 2. enable log dirty
+ * 3. setup checkpoint devices
+ * 4. write CHECKPOINT_SVM_READY
+ * 5. unpause secondary vm
+ * 6. write CHECKPOINT_SVM_RESUMED
+ *
+ * Do the following things when resuming secondary vm:
+ * 1. write CHECKPOINT_SVM_READY
+ * 2. resume secondary vm
+ * 3. write CHECKPOINT_SVM_RESUMED
+ */
+static void colo_send_svm_ready(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs);
+static void colo_send_svm_ready_done(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs,
+ int rc);
+static void colo_restore_preresume_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+static void colo_restore_resume_vm(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs);
+static void colo_resume_vm_done(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs,
+ int rc);
+static void colo_write_svm_resumed(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs);
+static void colo_enable_logdirty_done(libxl__egc *egc,
+ libxl__logdirty_switch *lds,
+ int retval);
+static void colo_reenable_logdirty(libxl__egc *egc,
+ libxl__logdirty_switch *lds,
+ int rc);
+static void colo_reenable_logdirty_done(libxl__egc *egc,
+ libxl__logdirty_switch *lds,
+ int rc);
+static void colo_setup_checkpoint_devices(libxl__egc *egc,
+ libxl__colo_restore_state *crs);
+static void colo_restore_setup_cds_done(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+static void colo_unpause_svm(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs);
+
+static void libxl__colo_restore_domain_resume_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
+ libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
+ libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
+
+ if (crcs->teardown_devices)
+ colo_send_svm_ready(shs->egc, crcs);
+ else
+ colo_restore_resume_vm(shs->egc, crcs);
+}
+
+static void colo_send_svm_ready(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs)
+{
+ libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_READY };
+
+ crcs->callback = colo_send_svm_ready_done;
+ crcs->sws.checkpoint_callback = colo_common_write_stream_done;
+ libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs);
+}
+
+static void colo_send_svm_ready_done(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs,
+ int rc)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *cds = &dcs->cds;
+
+ if (!crcs->preresume) {
+ crcs->preresume = true;
+ colo_unpause_svm(egc, crcs);
+ return;
+ }
+
+ cds->callback = colo_restore_preresume_cb;
+ libxl__checkpoint_devices_preresume(egc, cds);
+}
+
+static void colo_restore_preresume_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__colo_restore_state *crs = cds->concrete_data;
+ libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+ libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+ /* Convenience aliases */
+ libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, crs->domid, "preresume fails");
+ goto out;
+ }
+
+ if (crs->qdisk_setuped) {
+ if (libxl__qmp_colo_do_checkpoint(gc, crs->domid)) {
+ LOGD(ERROR, crs->domid, "doing checkpoint fails");
+ goto out;
+ }
+ }
+
+ if (!crs->cps.is_userspace_proxy)
+ colo_proxy_preresume(&crs->cps);
+
+ colo_restore_resume_vm(egc, crcs);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+static void colo_restore_resume_vm(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs)
+{
+
+ crcs->callback = colo_resume_vm_done;
+ colo_resume_vm(egc, crcs, 1);
+}
+
+static void colo_resume_vm_done(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs,
+ int rc)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+
+ /* Convenience aliases */
+ libxl__colo_restore_state *const crs = crcs->crs;
+ libxl__logdirty_switch *const lds = &crcs->lds;
+ libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, crs->domid, "cannot resume secondary vm");
+ goto out;
+ }
+
+ crcs->status = LIBXL_COLO_RESUMED;
+
+ colo_proxy_postresume(&crs->cps);
+
+ /* avoid calling stream->completion_callback() more than once */
+ if (crs->saved_cb) {
+ dcs->callback = crs->saved_cb;
+ crs->saved_cb = NULL;
+
+ dcs->srs.completion_callback = NULL;
+
+ lds->callback = colo_enable_logdirty_done;
+ colo_enable_logdirty(crs, egc);
+ return;
+ }
+
+ colo_write_svm_resumed(egc, crcs);
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+static void colo_write_svm_resumed(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs)
+{
+ libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_RESUMED };
+
+ crcs->callback = NULL;
+ crcs->sws.checkpoint_callback = colo_common_write_stream_done;
+ libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs);
+}
+
+static void colo_enable_logdirty_done(libxl__egc *egc,
+ libxl__logdirty_switch *lds,
+ int rc)
+{
+ libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
+
+ /* Convenience aliases */
+ libxl__colo_restore_state *const crs = crcs->crs;
+
+ EGC_GC;
+
+ if (rc) {
+ /*
+ * log-dirty already enabled? There's no test op,
+ * so attempt to disable then reenable it
+ */
+ lds->callback = colo_reenable_logdirty;
+ colo_disable_logdirty(crs, egc);
+ return;
+ }
+
+ colo_setup_checkpoint_devices(egc, crs);
+}
+
+static void colo_reenable_logdirty(libxl__egc *egc,
+ libxl__logdirty_switch *lds,
+ int rc)
+{
+ libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
+ libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+
+ /* Convenience aliases */
+ libxl__colo_restore_state *const crs = crcs->crs;
+ libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, crs->domid, "cannot enable logdirty");
+ goto out;
+ }
+
+ lds->callback = colo_reenable_logdirty_done;
+ colo_enable_logdirty(crs, egc);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+static void colo_reenable_logdirty_done(libxl__egc *egc,
+ libxl__logdirty_switch *lds,
+ int rc)
+{
+ libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
+ libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+
+ /* Convenience aliases */
+ libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, crcs->crs->domid, "cannot enable logdirty");
+ goto out;
+ }
+
+ colo_setup_checkpoint_devices(egc, crcs->crs);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+/*
+ * We cannot setup checkpoint devices in libxl__colo_restore_setup(),
+ * because the guest is not ready.
+ */
+static void colo_setup_checkpoint_devices(libxl__egc *egc,
+ libxl__colo_restore_state *crs)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+ libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *cds = &dcs->cds;
+ libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+ STATE_AO_GC(crs->ao);
+
+ if (crs->cps.is_userspace_proxy)
+ cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VBD);
+ else
+ cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VIF) |
+ (1 << LIBXL__DEVICE_KIND_VBD);
+
+ cds->callback = colo_restore_setup_cds_done;
+ cds->ao = ao;
+ cds->domid = crs->domid;
+ cds->ops = colo_restore_ops;
+
+ crs->cps.ao = ao;
+ if (!crs->cps.is_userspace_proxy) {
+ if (colo_proxy_setup(&crs->cps)) {
+ LOGD(ERROR, cds->domid, "COLO: failed to setup colo proxy for guest");
+ goto out;
+ }
+ }
+
+ if (init_device_subkind(cds))
+ goto out;
+
+ crcs->teardown_devices = 1;
+
+ libxl__checkpoint_devices_setup(egc, cds);
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+static void colo_restore_setup_cds_done(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__colo_restore_state *crs = cds->concrete_data;
+ libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+ libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+ /* Convenience aliases */
+ libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, cds->domid, "COLO: failed to setup device for guest");
+ goto out;
+ }
+
+ if (crs->qdisk_used && !crs->qdisk_setuped) {
+ if (libxl__qmp_start_replication(gc, crs->domid, false)) {
+ LOGD(ERROR, cds->domid, "starting replication fails");
+ goto out;
+ }
+ crs->qdisk_setuped = true;
+ }
+
+ colo_send_svm_ready(egc, crcs);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+static void colo_unpause_svm(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+ int rc;
+
+ /* Convenience aliases */
+ const uint32_t domid = crcs->crs->domid;
+ libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+ EGC_GC;
+
+ /* We have enabled secondary vm's logdirty, so we can unpause it now */
+ rc = libxl__domain_unpause_deprecated(gc, domid);
+ if (rc) {
+ LOGD(ERROR, domid, "cannot unpause secondary vm");
+ goto out;
+ }
+
+ colo_write_svm_resumed(egc, crcs);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+/* ===================== colo: wait new checkpoint ===================== */
+
+static void colo_restore_commit_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+static void colo_stream_read_done(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs,
+ int real_size);
+
+static void libxl__colo_restore_domain_wait_checkpoint_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
+ libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *cds = &dcs->cds;
+
+ cds->callback = colo_restore_commit_cb;
+ libxl__checkpoint_devices_commit(shs->egc, cds);
+}
+
+static void colo_restore_commit_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__colo_restore_state *crs = cds->concrete_data;
+ libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+ libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, crs->domid, "commit fails");
+ goto out;
+ }
+
+ crcs->callback = colo_stream_read_done;
+ dcs->srs.checkpoint_callback = colo_common_read_stream_done;
+ libxl__stream_read_checkpoint_state(egc, &dcs->srs);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, 0);
+}
+
+static void colo_stream_read_done(libxl__egc *egc,
+ libxl__colo_restore_checkpoint_state *crcs,
+ int id)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+ int ok = 0;
+
+ EGC_GC;
+
+ if (id != CHECKPOINT_NEW) {
+ LOGD(ERROR, crcs->crs->domid, "invalid section: %d", id);
+ goto out;
+ }
+
+ ok = 1;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok);
+}
+
+/* ===================== colo: suspend secondary vm ===================== */
+
+/*
+ * Do the following things when resuming secondary vm:
+ * 1. suspend secondary vm
+ * 2. send CHECKPOINT_SVM_SUSPENDED
+ */
+static void colo_suspend_vm_done(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps,
+ int ok);
+static void colo_restore_postsuspend_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+
+static void libxl__colo_restore_domain_suspend_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
+ libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
+ libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
+
+ STATE_AO_GC(dcs->ao);
+
+ /* Convenience aliases */
+ libxl__domain_suspend_state *const dsps = &crcs->dsps;
+
+ /* suspend secondary vm */
+ dsps->callback_common_done = colo_suspend_vm_done;
+
+ libxl__domain_suspend(shs->egc, dsps);
+}
+
+static void colo_suspend_vm_done(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps,
+ int rc)
+{
+ libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(dsps, *crcs, dsps);
+ libxl__colo_restore_state *crs = crcs->crs;
+ libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *cds = &dcs->cds;
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, crs->domid, "cannot suspend secondary vm");
+ goto out;
+ }
+
+ crcs->status = LIBXL_COLO_SUSPENDED;
+
+ if (libxl__qmp_query_xen_replication_status(gc, crs->domid)) {
+ LOGD(ERROR, crs->domid, "replication error occurs when secondary vm is running");
+ goto out;
+ }
+
+ cds->callback = colo_restore_postsuspend_cb;
+ libxl__checkpoint_devices_postsuspend(egc, cds);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, 0);
+}
+
+static void colo_restore_postsuspend_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__colo_restore_state *crs = cds->concrete_data;
+ libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+ libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+ libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_SUSPENDED };
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, crs->domid, "postsuspend fails");
+ goto out;
+ }
+
+ crcs->callback = NULL;
+ crcs->sws.checkpoint_callback = colo_common_write_stream_done;
+ libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, !rc);
+}
+
+/* ===================== colo: common callback ===================== */
+
+static void colo_common_write_stream_done(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ int rc)
+{
+ libxl__colo_restore_checkpoint_state *crcs =
+ CONTAINER_OF(stream, *crcs, sws);
+ libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+ int ok;
+
+ EGC_GC;
+
+ if (rc < 0) {
+ /* TODO: it may be a internal error, but we don't know */
+ LOGD(ERROR, crcs->crs->domid, "sending data fails");
+ ok = 2;
+ goto out;
+ }
+
+ if (!crcs->callback) {
+ /* Everythins is OK */
+ ok = 1;
+ goto out;
+ }
+
+ crcs->callback(egc, crcs, 0);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok);
+}
+
+static void colo_common_read_stream_done(libxl__egc *egc,
+ libxl__stream_read_state *stream,
+ int rc)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(stream, *dcs, srs);
+ libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
+ int ok;
+
+ EGC_GC;
+
+ if (rc < 0) {
+ /* TODO: it may be a internal error, but we don't know */
+ LOGD(ERROR, crcs->crs->domid, "reading data fails");
+ ok = 2;
+ goto out;
+ }
+
+ if (!crcs->callback) {
+ /* Everythins is OK */
+ ok = 1;
+ goto out;
+ }
+
+ /* rc contains the id */
+ crcs->callback(egc, crcs, rc);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok);
+}
--- /dev/null
+/*
+ * Copyright (C) 2016 FUJITSU LIMITED
+ * Author: Wen Congyang <wency@cn.fujitsu.com>
+ * Yang Hongyang <hongyang.yang@easystack.cn>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+extern const libxl__checkpoint_device_instance_ops colo_save_device_nic;
+extern const libxl__checkpoint_device_instance_ops colo_save_device_qdisk;
+
+static const libxl__checkpoint_device_instance_ops *colo_ops[] = {
+ &colo_save_device_nic,
+ &colo_save_device_qdisk,
+ NULL,
+};
+
+/* ================= helper functions ================= */
+
+static int init_device_subkind(libxl__checkpoint_devices_state *cds)
+{
+ /* init device subkind-specific state in the libxl ctx */
+ int rc;
+ STATE_AO_GC(cds->ao);
+
+ rc = init_subkind_colo_nic(cds);
+ if (rc) goto out;
+
+ rc = init_subkind_qdisk(cds);
+ if (rc) {
+ cleanup_subkind_colo_nic(cds);
+ goto out;
+ }
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds)
+{
+ /* cleanup device subkind-specific state in the libxl ctx */
+ STATE_AO_GC(cds->ao);
+
+ cleanup_subkind_colo_nic(cds);
+ cleanup_subkind_qdisk(cds);
+}
+
+/* ================= colo: setup save environment ================= */
+
+static void colo_save_setup_done(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+static void colo_save_setup_failed(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+/*
+ * checkpoint callbacks are called in the following order:
+ * 1. suspend
+ * 2. checkpoint
+ * 3. resume
+ * 4. wait checkpoint
+ */
+static void libxl__colo_save_domain_suspend_callback(void *data);
+static void libxl__colo_save_domain_checkpoint_callback(void *data);
+static void libxl__colo_save_domain_resume_callback(void *data);
+static void libxl__colo_save_domain_wait_checkpoint_callback(void *data);
+
+void libxl__colo_save_setup(libxl__egc *egc, libxl__colo_save_state *css)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *const cds = &dss->cds;
+ libxl__srm_save_autogen_callbacks *const callbacks =
+ &dss->sws.shs.callbacks.save.a;
+
+ STATE_AO_GC(dss->ao);
+
+ if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
+ LOGD(ERROR, dss->domid, "COLO only supports hvm now");
+ goto out;
+ }
+
+ css->send_fd = dss->fd;
+ css->recv_fd = dss->recv_fd;
+ css->svm_running = false;
+ css->paused = true;
+ css->qdisk_setuped = false;
+ css->qdisk_used = false;
+ libxl__ev_child_init(&css->child);
+ css->cps.is_userspace_proxy =
+ libxl_defbool_val(dss->remus->userspace_colo_proxy);
+
+ if (dss->remus->netbufscript)
+ css->colo_proxy_script = libxl__strdup(gc, dss->remus->netbufscript);
+ else
+ css->colo_proxy_script = GCSPRINTF("%s/colo-proxy-setup",
+ libxl__xen_script_dir_path());
+
+ cds->ops = colo_ops;
+ cds->callback = colo_save_setup_done;
+ cds->ao = ao;
+ cds->domid = dss->domid;
+ cds->concrete_data = css;
+
+ /* If enable userspace proxy mode, we don't need VIF */
+ if (css->cps.is_userspace_proxy) {
+ cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VBD);
+
+ /* Use this args we can connect to qemu colo-compare */
+ cds->nics = libxl__device_list(gc, &libxl__nic_devtype,
+ cds->domid, &cds->num_nics);
+ if (cds->num_nics > 0) {
+ css->cps.checkpoint_host = cds->nics[0].colo_checkpoint_host;
+ css->cps.checkpoint_port = cds->nics[0].colo_checkpoint_port;
+ }
+ } else {
+ cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VIF) |
+ (1 << LIBXL__DEVICE_KIND_VBD);
+ }
+
+ css->srs.ao = ao;
+ css->srs.fd = css->recv_fd;
+ css->srs.back_channel = true;
+ libxl__stream_read_start(egc, &css->srs);
+ css->cps.ao = ao;
+ if (colo_proxy_setup(&css->cps)) {
+ LOGD(ERROR, cds->domid, "COLO: failed to setup colo proxy for guest");
+ goto out;
+ }
+
+ if (init_device_subkind(cds))
+ goto out;
+
+ callbacks->suspend = libxl__colo_save_domain_suspend_callback;
+ callbacks->checkpoint = libxl__colo_save_domain_checkpoint_callback;
+ callbacks->postcopy = libxl__colo_save_domain_resume_callback;
+ callbacks->wait_checkpoint = libxl__colo_save_domain_wait_checkpoint_callback;
+
+ libxl__checkpoint_devices_setup(egc, &dss->cds);
+
+ return;
+
+out:
+ dss->callback(egc, dss, ERROR_FAIL);
+}
+
+static void colo_save_setup_done(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__colo_save_state *css = cds->concrete_data;
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+ EGC_GC;
+
+ if (!rc) {
+ libxl__domain_save(egc, dss);
+ return;
+ }
+
+ LOGD(ERROR, dss->domid, "COLO: failed to setup device for guest");
+ cds->callback = colo_save_setup_failed;
+ libxl__checkpoint_devices_teardown(egc, cds);
+}
+
+static void colo_save_setup_failed(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__colo_save_state *css = cds->concrete_data;
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+ STATE_AO_GC(cds->ao);
+
+ if (rc)
+ LOGD(ERROR, cds->domid,
+ "COLO: failed to teardown device after setup failed"
+ " for guest, rc %d", rc);
+
+ cleanup_device_subkind(cds);
+ dss->callback(egc, dss, rc);
+}
+
+/* ================= colo: teardown save environment ================= */
+
+static void colo_teardown_done(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+
+void libxl__colo_save_teardown(libxl__egc *egc,
+ libxl__colo_save_state *css,
+ int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+ EGC_GC;
+
+ LOGD(WARN, dss->domid,
+ "COLO: Domain suspend terminated with rc %d,"
+ " teardown COLO devices...", rc);
+
+ libxl__stream_read_abort(egc, &css->srs, 1);
+
+ if (css->qdisk_setuped) {
+ libxl__qmp_stop_replication(gc, dss->domid, true);
+ css->qdisk_setuped = false;
+ }
+
+ dss->cds.callback = colo_teardown_done;
+ libxl__checkpoint_devices_teardown(egc, &dss->cds);
+ return;
+}
+
+static void colo_teardown_done(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__colo_save_state *css = cds->concrete_data;
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+ cleanup_device_subkind(cds);
+ colo_proxy_teardown(&css->cps);
+ dss->callback(egc, dss, rc);
+}
+
+static void colo_common_write_stream_done(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ int rc);
+static void colo_common_read_stream_done(libxl__egc *egc,
+ libxl__stream_read_state *stream,
+ int rc);
+
+/* ===================== colo: suspend primary vm ===================== */
+
+static void colo_read_svm_suspended_done(libxl__egc *egc,
+ libxl__colo_save_state *css,
+ int id);
+/*
+ * Do the following things when suspending primary vm:
+ * 1. suspend primary vm
+ * 2. do postsuspend
+ * 3. read CHECKPOINT_SVM_SUSPENDED
+ * 4. read secondary vm's dirty pages
+ */
+static void colo_suspend_primary_vm_done(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps,
+ int ok);
+static void colo_postsuspend_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+
+static void libxl__colo_save_domain_suspend_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__egc *egc = shs->egc;
+ libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
+ libxl__domain_save_state *dss = sws->dss;
+
+ /* Convenience aliases */
+ libxl__domain_suspend_state *dsps = &dss->dsps;
+
+ dsps->callback_common_done = colo_suspend_primary_vm_done;
+ libxl__domain_suspend(egc, dsps);
+}
+
+static void colo_suspend_primary_vm_done(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps,
+ int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, dss->domid, "cannot suspend primary vm");
+ goto out;
+ }
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *const cds = &dss->cds;
+
+ cds->callback = colo_postsuspend_cb;
+ libxl__checkpoint_devices_postsuspend(egc, cds);
+ return;
+
+out:
+ dss->rc = rc;
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+static void colo_postsuspend_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__colo_save_state *css = cds->concrete_data;
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, dss->domid, "postsuspend fails");
+ goto out;
+ }
+
+ if (!css->svm_running) {
+ rc = 0;
+ goto out;
+ }
+
+ /*
+ * read CHECKPOINT_SVM_SUSPENDED
+ */
+ css->callback = colo_read_svm_suspended_done;
+ css->srs.checkpoint_callback = colo_common_read_stream_done;
+ libxl__stream_read_checkpoint_state(egc, &css->srs);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+static void colo_read_svm_suspended_done(libxl__egc *egc,
+ libxl__colo_save_state *css,
+ int id)
+{
+ int ok = 0;
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+ EGC_GC;
+
+ if (id != CHECKPOINT_SVM_SUSPENDED) {
+ LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id,
+ CHECKPOINT_SVM_SUSPENDED);
+ goto out;
+ }
+
+ if (!css->paused &&
+ libxl__qmp_query_xen_replication_status(gc, dss->domid)) {
+ LOGD(ERROR, dss->domid,
+ "replication error occurs when primary vm is running");
+ goto out;
+ }
+
+ ok = 1;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
+}
+
+/* ===================== colo: send tailbuf ========================== */
+
+static void libxl__colo_save_domain_checkpoint_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
+ libxl__domain_save_state *dss = sws->dss;
+
+ /* Convenience aliases */
+ libxl__colo_save_state *const css = &dss->css;
+
+ /* write emulator xenstore data, emulator context, and checkpoint end */
+ css->callback = NULL;
+ dss->sws.checkpoint_callback = colo_common_write_stream_done;
+ libxl__stream_write_start_checkpoint(shs->egc, &dss->sws);
+}
+
+/* ===================== colo: resume primary vm ===================== */
+
+/*
+ * Do the following things when resuming primary vm:
+ * 1. read CHECKPOINT_SVM_READY
+ * 2. do preresume
+ * 3. resume primary vm
+ * 4. read CHECKPOINT_SVM_RESUMED
+ */
+static void colo_read_svm_ready_done(libxl__egc *egc,
+ libxl__colo_save_state *css,
+ int id);
+static void colo_preresume_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+static void colo_read_svm_resumed_done(libxl__egc *egc,
+ libxl__colo_save_state *css,
+ int id);
+
+static void libxl__colo_save_domain_resume_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__egc *egc = shs->egc;
+ libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
+ libxl__domain_save_state *dss = sws->dss;
+
+ /* Convenience aliases */
+ libxl__colo_save_state *const css = &dss->css;
+
+ EGC_GC;
+
+ /* read CHECKPOINT_SVM_READY */
+ css->callback = colo_read_svm_ready_done;
+ css->srs.checkpoint_callback = colo_common_read_stream_done;
+ libxl__stream_read_checkpoint_state(egc, &css->srs);
+}
+
+static void colo_read_svm_ready_done(libxl__egc *egc,
+ libxl__colo_save_state *css,
+ int id)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+ EGC_GC;
+
+ if (id != CHECKPOINT_SVM_READY) {
+ LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id,
+ CHECKPOINT_SVM_READY);
+ goto out;
+ }
+
+ colo_proxy_preresume(&css->cps);
+
+ css->svm_running = true;
+ dss->cds.callback = colo_preresume_cb;
+ libxl__checkpoint_devices_preresume(egc, &dss->cds);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+static void colo_preresume_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__colo_save_state *css = cds->concrete_data;
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, dss->domid, "preresume fails");
+ goto out;
+ }
+
+ if (css->qdisk_used && !css->qdisk_setuped) {
+ if (libxl__qmp_start_replication(gc, dss->domid, true)) {
+ LOGD(ERROR, dss->domid, "starting replication fails");
+ goto out;
+ }
+ css->qdisk_setuped = true;
+ }
+
+ if (!css->paused) {
+ if (libxl__qmp_colo_do_checkpoint(gc, dss->domid)) {
+ LOGD(ERROR, dss->domid, "doing checkpoint fails");
+ goto out;
+ }
+ }
+
+ /* Resumes the domain and the device model */
+ if (libxl__domain_resume_deprecated(gc, dss->domid, /* Fast Suspend */1)) {
+ LOGD(ERROR, dss->domid, "cannot resume primary vm");
+ goto out;
+ }
+
+ /*
+ * The guest should be paused before doing colo because there is
+ * no disk migration.
+ */
+ if (css->paused) {
+ rc = libxl__domain_unpause_deprecated(gc, dss->domid);
+ if (rc) {
+ LOGD(ERROR, dss->domid, "cannot unpause primary vm");
+ goto out;
+ }
+ css->paused = false;
+ }
+
+ /* read CHECKPOINT_SVM_RESUMED */
+ css->callback = colo_read_svm_resumed_done;
+ css->srs.checkpoint_callback = colo_common_read_stream_done;
+ libxl__stream_read_checkpoint_state(egc, &css->srs);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+static void colo_read_svm_resumed_done(libxl__egc *egc,
+ libxl__colo_save_state *css,
+ int id)
+{
+ int ok = 0;
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+ EGC_GC;
+
+ if (id != CHECKPOINT_SVM_RESUMED) {
+ LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id,
+ CHECKPOINT_SVM_RESUMED);
+ goto out;
+ }
+
+ colo_proxy_postresume(&css->cps);
+
+ ok = 1;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
+}
+
+/* ===================== colo: wait new checkpoint ===================== */
+
+static void colo_start_new_checkpoint(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+static void colo_proxy_async_wait_for_checkpoint(libxl__colo_save_state *css);
+static void colo_proxy_async_call_done(libxl__egc *egc,
+ libxl__ev_child *child,
+ int pid,
+ int status);
+
+static void colo_proxy_wait_for_checkpoint(libxl__egc *egc,
+ libxl__colo_save_state *css)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+ ASYNC_CALL(egc, dss->cds.ao, &css->child, css,
+ colo_proxy_async_wait_for_checkpoint,
+ colo_proxy_async_call_done);
+}
+
+static void colo_proxy_async_wait_for_checkpoint(libxl__colo_save_state *css)
+{
+ int req;
+
+ req = colo_proxy_checkpoint(&css->cps, COLO_PROXY_CHECKPOINT_TIMEOUT);
+ if (req < 0) {
+ /* some error happens */
+ _exit(1);
+ } else {
+ /* req == 0: no checkpoint is needed, do a checkpoint every 5s */
+ /* req > 0: net packets is not consistent, we need to start a
+ * checkpoint
+ */
+ _exit(0);
+ }
+}
+
+static void colo_proxy_async_call_done(libxl__egc *egc,
+ libxl__ev_child *child,
+ int pid,
+ int status)
+{
+ libxl__colo_save_state *css = CONTAINER_OF(child, *css, child);
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+ EGC_GC;
+
+ if (status) {
+ LOGD(ERROR, dss->domid, "failed to wait for new checkpoint");
+ colo_start_new_checkpoint(egc, &dss->cds, ERROR_FAIL);
+ return;
+ }
+
+ colo_start_new_checkpoint(egc, &dss->cds, 0);
+}
+
+/*
+ * Do the following things:
+ * 1. do commit
+ * 2. wait for a new checkpoint
+ * 3. write CHECKPOINT_NEW
+ */
+static void colo_device_commit_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+
+static void libxl__colo_save_domain_wait_checkpoint_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
+ libxl__domain_save_state *dss = sws->dss;
+ libxl__egc *egc = dss->sws.shs.egc;
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *const cds = &dss->cds;
+
+ cds->callback = colo_device_commit_cb;
+ libxl__checkpoint_devices_commit(egc, cds);
+}
+
+static void colo_device_commit_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__colo_save_state *css = cds->concrete_data;
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, dss->domid, "commit fails");
+ goto out;
+ }
+
+ colo_proxy_wait_for_checkpoint(egc, css);
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+static void colo_start_new_checkpoint(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__colo_save_state *css = cds->concrete_data;
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+ libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_NEW };
+
+ if (rc)
+ goto out;
+
+ /* write CHECKPOINT_NEW */
+ css->callback = NULL;
+ dss->sws.checkpoint_callback = colo_common_write_stream_done;
+ libxl__stream_write_checkpoint_state(egc, &dss->sws, &srcs);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+/* ===================== colo: common callback ===================== */
+
+static void colo_common_write_stream_done(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(stream, *dss, sws);
+ int ok;
+
+ /* Convenience aliases */
+ libxl__colo_save_state *const css = &dss->css;
+
+ EGC_GC;
+
+ if (rc < 0) {
+ /* TODO: it may be a internal error, but we don't know */
+ LOGD(ERROR, dss->domid, "sending data fails");
+ ok = 0;
+ goto out;
+ }
+
+ if (!css->callback) {
+ /* Everythins is OK */
+ ok = 1;
+ goto out;
+ }
+
+ css->callback(egc, css, 0);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
+}
+
+static void colo_common_read_stream_done(libxl__egc *egc,
+ libxl__stream_read_state *stream,
+ int rc)
+{
+ libxl__colo_save_state *css = CONTAINER_OF(stream, *css, srs);
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+ int ok;
+
+ EGC_GC;
+
+ if (rc < 0) {
+ /* TODO: it may be a internal error, but we don't know */
+ LOGD(ERROR, dss->domid, "reading data fails");
+ ok = 0;
+ goto out;
+ }
+
+ if (!css->callback) {
+ /* Everythins is OK */
+ ok = 1;
+ goto out;
+ }
+
+ /* rc contains the id */
+ css->callback(egc, css, rc);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
+}
--- /dev/null
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+int libxl__console_tty_path(libxl__gc *gc, uint32_t domid, int cons_num,
+ libxl_console_type type, char **tty_path)
+{
+ int rc;
+ char *dom_path;
+
+ dom_path = libxl__xs_get_dompath(gc, domid);
+ if (!dom_path) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ switch (type) {
+ case LIBXL_CONSOLE_TYPE_SERIAL:
+ *tty_path = GCSPRINTF("%s/serial/%d/tty", dom_path, cons_num);
+ rc = 0;
+ break;
+ case LIBXL_CONSOLE_TYPE_PV:
+ if (cons_num == 0)
+ *tty_path = GCSPRINTF("%s/console/tty", dom_path);
+ else
+ *tty_path = GCSPRINTF("%s/tty",
+ libxl__domain_device_frontend_path(gc, domid,
+ cons_num, LIBXL__DEVICE_KIND_CONSOLE));
+ rc = 0;
+ break;
+ default:
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+out:
+ return rc;
+}
+
+int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num,
+ libxl_console_type type, int notify_fd)
+{
+ GC_INIT(ctx);
+ char *p = GCSPRINTF("%s/xenconsole", libxl__private_bindir_path());
+ char *domid_s = GCSPRINTF("%d", domid);
+ char *cons_num_s = GCSPRINTF("%d", cons_num);
+ char *notify_fd_s;
+ char *cons_type_s;
+
+ switch (type) {
+ case LIBXL_CONSOLE_TYPE_PV:
+ cons_type_s = "pv";
+ break;
+ case LIBXL_CONSOLE_TYPE_SERIAL:
+ cons_type_s = "serial";
+ break;
+ case LIBXL_CONSOLE_TYPE_VUART:
+ cons_type_s = "vuart";
+ break;
+ default:
+ goto out;
+ }
+
+ if (notify_fd != -1) {
+ notify_fd_s = GCSPRINTF("%d", notify_fd);
+ execl(p, p, domid_s, "--num", cons_num_s, "--type", cons_type_s,
+ "--start-notify-fd", notify_fd_s, (void *)NULL);
+ } else {
+ execl(p, p, domid_s, "--num", cons_num_s, "--type", cons_type_s,
+ (void *)NULL);
+ }
+
+out:
+ GC_FREE;
+ return ERROR_FAIL;
+}
+
+int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num,
+ libxl_console_type type, char **path)
+{
+ GC_INIT(ctx);
+ char *tty_path;
+ char *tty;
+ int rc;
+
+ rc = libxl__console_tty_path(gc, domid, cons_num, type, &tty_path);
+ if (rc) {
+ LOGD(ERROR, domid, "Failed to get tty path\n");
+ goto out;
+ }
+
+ tty = libxl__xs_read(gc, XBT_NULL, tty_path);
+ if (!tty || tty[0] == '\0') {
+ LOGED(ERROR, domid, "Unable to read console tty path `%s'",
+ tty_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ *path = libxl__strdup(NOGC, tty);
+ rc = 0;
+out:
+ GC_FREE;
+ return rc;
+}
+
+static int libxl__primary_console_find(libxl_ctx *ctx, uint32_t domid_vm,
+ uint32_t *domid, int *cons_num,
+ libxl_console_type *type)
+{
+ GC_INIT(ctx);
+ uint32_t stubdomid = libxl_get_stubdom_id(ctx, domid_vm);
+ int rc;
+
+ if (stubdomid) {
+ *domid = stubdomid;
+ *cons_num = STUBDOM_CONSOLE_SERIAL;
+ *type = LIBXL_CONSOLE_TYPE_PV;
+ } else {
+ switch (libxl__domain_type(gc, domid_vm)) {
+ case LIBXL_DOMAIN_TYPE_HVM:
+ *domid = domid_vm;
+ *cons_num = 0;
+ *type = LIBXL_CONSOLE_TYPE_SERIAL;
+ break;
+ case LIBXL_DOMAIN_TYPE_PVH:
+ case LIBXL_DOMAIN_TYPE_PV:
+ *domid = domid_vm;
+ *cons_num = 0;
+ *type = LIBXL_CONSOLE_TYPE_PV;
+ break;
+ case LIBXL_DOMAIN_TYPE_INVALID:
+ rc = ERROR_INVAL;
+ goto out;
+ default: abort();
+ }
+ }
+
+ rc = 0;
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_primary_console_exec(libxl_ctx *ctx, uint32_t domid_vm, int notify_fd)
+{
+ uint32_t domid;
+ int cons_num;
+ libxl_console_type type;
+ int rc;
+
+ rc = libxl__primary_console_find(ctx, domid_vm, &domid, &cons_num, &type);
+ if ( rc ) return rc;
+ return libxl_console_exec(ctx, domid, cons_num, type, notify_fd);
+}
+
+int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm,
+ char **path)
+{
+ uint32_t domid;
+ int cons_num;
+ libxl_console_type type;
+ int rc;
+
+ rc = libxl__primary_console_find(ctx, domid_vm, &domid, &cons_num, &type);
+ if ( rc ) return rc;
+ return libxl_console_get_tty(ctx, domid, cons_num, type, path);
+}
+
+int libxl_vncviewer_exec(libxl_ctx *ctx, uint32_t domid, int autopass)
+{
+ GC_INIT(ctx);
+ const char *vnc_port;
+ const char *vnc_listen = NULL, *vnc_pass = NULL;
+ int port = 0, autopass_fd = -1;
+ char *vnc_bin, *args[] = {
+ "vncviewer",
+ NULL, /* hostname:display */
+ NULL, /* -autopass */
+ NULL,
+ };
+
+ vnc_port = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF(
+ "/local/domain/%d/console/vnc-port", domid));
+ if (!vnc_port) {
+ LOGD(ERROR, domid, "Cannot get vnc-port");
+ goto x_fail;
+ }
+
+ port = atoi(vnc_port) - 5900;
+
+ vnc_listen = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("/local/domain/%d/console/vnc-listen",
+ domid));
+
+ if ( autopass )
+ vnc_pass = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("/local/domain/%d/console/vnc-pass",
+ domid));
+
+ if ( NULL == vnc_listen )
+ vnc_listen = "localhost";
+
+ if ( (vnc_bin = getenv("VNCVIEWER")) )
+ args[0] = vnc_bin;
+
+ args[1] = GCSPRINTF("%s:%d", vnc_listen, port);
+
+ if ( vnc_pass ) {
+ char tmpname[] = "/tmp/vncautopass.XXXXXX";
+ autopass_fd = mkstemp(tmpname);
+ if ( autopass_fd < 0 ) {
+ LOGED(ERROR, domid, "mkstemp %s failed", tmpname);
+ goto x_fail;
+ }
+
+ if ( unlink(tmpname) ) {
+ /* should never happen */
+ LOGED(ERROR, domid, "unlink %s failed", tmpname);
+ goto x_fail;
+ }
+
+ if ( libxl_write_exactly(ctx, autopass_fd, vnc_pass, strlen(vnc_pass),
+ tmpname, "vnc password") )
+ goto x_fail;
+
+ if ( lseek(autopass_fd, SEEK_SET, 0) ) {
+ LOGED(ERROR, domid, "rewind %s (autopass) failed", tmpname);
+ goto x_fail;
+ }
+
+ args[2] = "-autopass";
+ }
+
+ libxl__exec(gc, autopass_fd, -1, -1, args[0], args, NULL);
+
+ x_fail:
+ GC_FREE;
+ return ERROR_FAIL;
+}
+
+int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
+ libxl__device_console *console,
+ libxl__domain_build_state *state,
+ libxl__device *device)
+{
+ flexarray_t *front, *ro_front;
+ flexarray_t *back;
+ int rc;
+
+ if (console->devid && state) {
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ if (!console->devid && (console->name || console->path)) {
+ LOGD(ERROR, domid, "Primary console has invalid configuration");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ front = flexarray_make(gc, 16, 1);
+ ro_front = flexarray_make(gc, 16, 1);
+ back = flexarray_make(gc, 16, 1);
+
+ device->backend_devid = console->devid;
+ device->backend_domid = console->backend_domid;
+ device->backend_kind = LIBXL__DEVICE_KIND_CONSOLE;
+ device->devid = console->devid;
+ device->domid = domid;
+ device->kind = LIBXL__DEVICE_KIND_CONSOLE;
+
+ flexarray_append(back, "frontend-id");
+ flexarray_append(back, GCSPRINTF("%d", domid));
+ flexarray_append(back, "online");
+ flexarray_append(back, "1");
+ flexarray_append(back, "state");
+ flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
+ flexarray_append(back, "protocol");
+ flexarray_append(back, LIBXL_XENCONSOLE_PROTOCOL);
+
+ if (console->name) {
+ flexarray_append(ro_front, "name");
+ flexarray_append(ro_front, console->name);
+ flexarray_append(back, "name");
+ flexarray_append(back, console->name);
+ }
+ if (console->connection) {
+ flexarray_append(back, "connection");
+ flexarray_append(back, console->connection);
+ }
+ if (console->path) {
+ flexarray_append(back, "path");
+ flexarray_append(back, console->path);
+ }
+
+ flexarray_append(front, "backend-id");
+ flexarray_append(front, GCSPRINTF("%d", console->backend_domid));
+
+ flexarray_append(ro_front, "limit");
+ flexarray_append(ro_front, GCSPRINTF("%d", LIBXL_XENCONSOLE_LIMIT));
+ flexarray_append(ro_front, "type");
+ if (console->consback == LIBXL__CONSOLE_BACKEND_XENCONSOLED)
+ flexarray_append(ro_front, "xenconsoled");
+ else
+ flexarray_append(ro_front, "ioemu");
+ flexarray_append(ro_front, "output");
+ flexarray_append(ro_front, console->output);
+ flexarray_append(ro_front, "tty");
+ if (state && state->console_tty)
+ flexarray_append(ro_front, state->console_tty);
+ else
+ flexarray_append(ro_front, "");
+
+ if (state) {
+ flexarray_append(ro_front, "port");
+ flexarray_append(ro_front, GCSPRINTF("%"PRIu32, state->console_port));
+ flexarray_append(ro_front, "ring-ref");
+ flexarray_append(ro_front, GCSPRINTF("%lu", state->console_mfn));
+ } else {
+ flexarray_append(front, "state");
+ flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
+ flexarray_append(front, "protocol");
+ flexarray_append(front, LIBXL_XENCONSOLE_PROTOCOL);
+ }
+ libxl__device_generic_add(gc, XBT_NULL, device,
+ libxl__xs_kvs_of_flexarray(gc, back),
+ libxl__xs_kvs_of_flexarray(gc, front),
+ libxl__xs_kvs_of_flexarray(gc, ro_front));
+ rc = 0;
+out:
+ return rc;
+}
+
+int libxl__device_vuart_add(libxl__gc *gc, uint32_t domid,
+ libxl__device_console *console,
+ libxl__domain_build_state *state)
+{
+ libxl__device device;
+ flexarray_t *ro_front;
+ flexarray_t *back;
+ int rc;
+
+ ro_front = flexarray_make(gc, 16, 1);
+ back = flexarray_make(gc, 16, 1);
+
+ device.backend_devid = console->devid;
+ device.backend_domid = console->backend_domid;
+ device.backend_kind = LIBXL__DEVICE_KIND_VUART;
+ device.devid = console->devid;
+ device.domid = domid;
+ device.kind = LIBXL__DEVICE_KIND_VUART;
+
+ flexarray_append(back, "frontend-id");
+ flexarray_append(back, GCSPRINTF("%d", domid));
+ flexarray_append(back, "online");
+ flexarray_append(back, "1");
+ flexarray_append(back, "state");
+ flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
+ flexarray_append(back, "protocol");
+ flexarray_append(back, LIBXL_XENCONSOLE_PROTOCOL);
+
+ flexarray_append(ro_front, "port");
+ flexarray_append(ro_front, GCSPRINTF("%"PRIu32, state->vuart_port));
+ flexarray_append(ro_front, "ring-ref");
+ flexarray_append(ro_front, GCSPRINTF("%"PRIu_xen_pfn, state->vuart_gfn));
+ flexarray_append(ro_front, "limit");
+ flexarray_append(ro_front, GCSPRINTF("%d", LIBXL_XENCONSOLE_LIMIT));
+ flexarray_append(ro_front, "type");
+ flexarray_append(ro_front, "xenconsoled");
+
+ rc = libxl__device_generic_add(gc, XBT_NULL, &device,
+ libxl__xs_kvs_of_flexarray(gc, back),
+ NULL,
+ libxl__xs_kvs_of_flexarray(gc, ro_front));
+ return rc;
+}
+
+int libxl__init_console_from_channel(libxl__gc *gc,
+ libxl__device_console *console,
+ int dev_num,
+ libxl_device_channel *channel)
+{
+ int rc;
+
+ libxl__device_console_init(console);
+
+ /* Perform validation first, allocate second. */
+
+ if (channel->devid == -1)
+ channel->devid = dev_num;
+
+ if (!channel->name) {
+ LOG(ERROR, "channel %d has no name", channel->devid);
+ return ERROR_INVAL;
+ }
+
+ if (channel->backend_domname) {
+ rc = libxl_domain_qualifier_to_domid(CTX, channel->backend_domname,
+ &channel->backend_domid);
+ if (rc < 0) return rc;
+ }
+
+ /* The xenstore 'output' node tells the backend what to connect the console
+ to. If the channel has "connection = pty" then the "output" node will be
+ set to "pty". If the channel has "connection = socket" then the "output"
+ node will be set to "chardev:libxl-channel%d". This tells the qemu
+ backend to proxy data between the console ring and the character device
+ with id "libxl-channel%d". These character devices are currently defined
+ on the qemu command-line via "-chardev" options in libxl_dm.c */
+
+ switch (channel->connection) {
+ case LIBXL_CHANNEL_CONNECTION_UNKNOWN:
+ LOG(ERROR, "channel %d has no defined connection; "
+ "to where should it be connected?", channel->devid);
+ return ERROR_INVAL;
+ case LIBXL_CHANNEL_CONNECTION_PTY:
+ console->connection = libxl__strdup(NOGC, "pty");
+ console->output = libxl__sprintf(NOGC, "pty");
+ break;
+ case LIBXL_CHANNEL_CONNECTION_SOCKET:
+ if (!channel->u.socket.path) {
+ LOG(ERROR, "channel %d has no path", channel->devid);
+ return ERROR_INVAL;
+ }
+ console->connection = libxl__strdup(NOGC, "socket");
+ console->path = libxl__strdup(NOGC, channel->u.socket.path);
+ console->output = libxl__sprintf(NOGC, "chardev:libxl-channel%d",
+ channel->devid);
+ break;
+ default:
+ /* We've forgotten to add the clause */
+ LOG(ERROR, "%s: missing implementation for channel connection %d",
+ __func__, channel->connection);
+ abort();
+ }
+
+ console->devid = channel->devid;
+ console->consback = LIBXL__CONSOLE_BACKEND_IOEMU;
+ console->backend_domid = channel->backend_domid;
+ console->name = libxl__strdup(NOGC, channel->name);
+
+ return 0;
+}
+
+static int libxl__device_channel_from_xenstore(libxl__gc *gc,
+ const char *libxl_path,
+ libxl_device_channel *channel)
+{
+ const char *tmp;
+ int rc;
+
+ libxl_device_channel_init(channel);
+
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/name", libxl_path),
+ (const char **)(&channel->name));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/connection", libxl_path), &tmp);
+ if (rc) goto out;
+ if (!strcmp(tmp, "pty")) {
+ channel->connection = LIBXL_CHANNEL_CONNECTION_PTY;
+ } else if (!strcmp(tmp, "socket")) {
+ channel->connection = LIBXL_CHANNEL_CONNECTION_SOCKET;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/path", libxl_path),
+ (const char **)(&channel->u.socket.path));
+ if (rc) goto out;
+ } else {
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ rc = 0;
+ out:
+ return rc;
+}
+
+static int libxl__append_channel_list(libxl__gc *gc,
+ uint32_t domid,
+ libxl_device_channel **channels,
+ int *nchannels)
+{
+ char *libxl_dir_path = NULL;
+ char **dir = NULL;
+ unsigned int n = 0, devid = 0;
+ libxl_device_channel *next = NULL;
+ int rc = 0, i;
+
+ libxl_dir_path = GCSPRINTF("%s/device/%s",
+ libxl__xs_libxl_path(gc, domid),
+ libxl__device_kind_to_string(
+ LIBXL__DEVICE_KIND_CONSOLE));
+ dir = libxl__xs_directory(gc, XBT_NULL, libxl_dir_path, &n);
+ if (!dir || !n)
+ goto out;
+
+ for (i = 0; i < n; i++) {
+ const char *libxl_path, *name;
+ libxl_device_channel *tmp;
+
+ libxl_path = GCSPRINTF("%s/%s", libxl_dir_path, dir[i]);
+ name = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/name", libxl_path));
+ /* 'channels' are consoles with names, so ignore all consoles
+ without names */
+ if (!name) continue;
+ tmp = realloc(*channels,
+ sizeof(libxl_device_channel) * (*nchannels + devid + 1));
+ if (!tmp) {
+ rc = ERROR_NOMEM;
+ goto out;
+ }
+ *channels = tmp;
+ next = *channels + *nchannels + devid;
+ rc = libxl__device_channel_from_xenstore(gc, libxl_path, next);
+ if (rc) goto out;
+ next->devid = devid;
+ devid++;
+ }
+ *nchannels += devid;
+ return 0;
+
+ out:
+ return rc;
+}
+
+libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx,
+ uint32_t domid,
+ int *num)
+{
+ GC_INIT(ctx);
+ libxl_device_channel *channels = NULL;
+ int rc;
+
+ *num = 0;
+
+ rc = libxl__append_channel_list(gc, domid, &channels, num);
+ if (rc) goto out_err;
+
+ GC_FREE;
+ return channels;
+
+out_err:
+ LOGD(ERROR, domid, "Unable to list channels");
+ while (*num) {
+ (*num)--;
+ libxl_device_channel_dispose(&channels[*num]);
+ }
+ free(channels);
+ return NULL;
+}
+
+int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_channel *channel,
+ libxl_channelinfo *channelinfo)
+{
+ GC_INIT(ctx);
+ char *fe_path, *libxl_path;
+ char *val;
+ int rc;
+
+ channelinfo->devid = channel->devid;
+
+ fe_path = libxl__domain_device_frontend_path(gc, domid,
+ channelinfo->devid + 1,
+ LIBXL__DEVICE_KIND_CONSOLE);
+ libxl_path = libxl__domain_device_libxl_path(gc, domid,
+ channelinfo->devid + 1,
+ LIBXL__DEVICE_KIND_CONSOLE);
+
+ channelinfo->backend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path), NULL);
+ if (!channelinfo->backend) {
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ rc = libxl__backendpath_parse_domid(gc, channelinfo->backend,
+ &channelinfo->backend_id);
+ if (rc) goto out;
+
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
+ channelinfo->state = val ? strtoul(val, NULL, 10) : -1;
+ channelinfo->frontend = libxl__strdup(NOGC, fe_path);
+ channelinfo->frontend_id = domid;
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
+ channelinfo->rref = val ? strtoul(val, NULL, 10) : -1;
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/port", fe_path));
+ channelinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
+
+ channelinfo->connection = channel->connection;
+ switch (channel->connection) {
+ case LIBXL_CHANNEL_CONNECTION_PTY:
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tty", fe_path));
+ /*
+ * It is obviously very wrong for this value to be in the
+ * frontend. But in XSA-175 we don't want to re-engineer
+ * this because other xenconsole code elsewhere (some
+ * even out of tree, perhaps) expects this node to be
+ * here.
+ *
+ * FE/pty is readonly for the guest. It always exists if
+ * FE does because libxl__device_console_add
+ * unconditionally creates it and nothing deletes it.
+ *
+ * The guest can delete the whole FE (which it has write
+ * privilege on) but the containing directories
+ * /local/GUEST[/device[/console]] are also RO for the
+ * guest. So if the guest deletes FE it cannot recreate
+ * it.
+ *
+ * Therefore the guest cannot cause FE/pty to contain bad
+ * data, although it can cause it to not exist.
+ */
+ if (!val) val = "/NO-SUCH-PATH";
+ channelinfo->u.pty.path = strdup(val);
+ break;
+ default:
+ break;
+ }
+ rc = 0;
+ out:
+ GC_FREE;
+ return rc;
+}
+
+static int libxl__device_vfb_setdefault(libxl__gc *gc, uint32_t domid,
+ libxl_device_vfb *vfb, bool hotplug)
+{
+ int rc;
+
+ libxl_defbool_setdefault(&vfb->vnc.enable, true);
+ if (libxl_defbool_val(vfb->vnc.enable)) {
+ if (!vfb->vnc.listen) {
+ vfb->vnc.listen = strdup("127.0.0.1");
+ if (!vfb->vnc.listen) return ERROR_NOMEM;
+ }
+
+ libxl_defbool_setdefault(&vfb->vnc.findunused, true);
+ } else {
+ libxl_defbool_setdefault(&vfb->vnc.findunused, false);
+ }
+
+ libxl_defbool_setdefault(&vfb->sdl.enable, false);
+ libxl_defbool_setdefault(&vfb->sdl.opengl, false);
+
+ rc = libxl__resolve_domid(gc, vfb->backend_domname, &vfb->backend_domid);
+ return rc;
+}
+
+int libxl_device_vfb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ int rc;
+
+ rc = libxl__device_add(gc, domid, &libxl__vfb_devtype, vfb);
+ if (rc) {
+ LOGD(ERROR, domid, "Unable to add vfb device");
+ goto out;
+ }
+
+out:
+ libxl__ao_complete(egc, ao, rc);
+ return AO_INPROGRESS;
+}
+
+static int libxl__set_xenstore_vfb(libxl__gc *gc, uint32_t domid,
+ libxl_device_vfb *vfb,
+ flexarray_t *back, flexarray_t *front,
+ flexarray_t *ro_front)
+{
+ flexarray_append_pair(back, "vnc",
+ libxl_defbool_val(vfb->vnc.enable) ? "1" : "0");
+ flexarray_append_pair(back, "vnclisten", vfb->vnc.listen);
+ flexarray_append_pair(back, "vncpasswd", vfb->vnc.passwd);
+ flexarray_append_pair(back, "vncdisplay",
+ GCSPRINTF("%d", vfb->vnc.display));
+ flexarray_append_pair(back, "vncunused",
+ libxl_defbool_val(vfb->vnc.findunused) ? "1" : "0");
+ flexarray_append_pair(back, "sdl",
+ libxl_defbool_val(vfb->sdl.enable) ? "1" : "0");
+ flexarray_append_pair(back, "opengl",
+ libxl_defbool_val(vfb->sdl.opengl) ? "1" : "0");
+ if (vfb->sdl.xauthority) {
+ flexarray_append_pair(back, "xauthority", vfb->sdl.xauthority);
+ }
+ if (vfb->sdl.display) {
+ flexarray_append_pair(back, "display", vfb->sdl.display);
+ }
+
+ return 0;
+}
+
+/* The following functions are defined:
+ * libxl_device_vfb_remove
+ * libxl_device_vfb_destroy
+ */
+
+/* channel/console hotunplug is not implemented. There are 2 possibilities:
+ * 1. add support for secondary consoles to xenconsoled
+ * 2. dynamically add/remove qemu chardevs via qmp messages. */
+
+#define libxl__add_vfbs NULL
+#define libxl_device_vfb_list NULL
+#define libxl_device_vfb_compare NULL
+
+static LIBXL_DEFINE_UPDATE_DEVID(vfb)
+static LIBXL_DEFINE_DEVICE_FROM_TYPE(vfb)
+
+/* vfb */
+LIBXL_DEFINE_DEVICE_REMOVE(vfb)
+
+DEFINE_DEVICE_TYPE_STRUCT(vfb, VFB,
+ .skip_attach = 1,
+ .set_xenstore_config = (device_set_xenstore_config_fn_t)
+ libxl__set_xenstore_vfb,
+);
+
+libxl_xen_console_reader *
+ libxl_xen_console_read_start(libxl_ctx *ctx, int clear)
+{
+ GC_INIT(ctx);
+ libxl_xen_console_reader *cr;
+ unsigned int size = 16384;
+
+ cr = libxl__zalloc(NOGC, sizeof(libxl_xen_console_reader));
+ cr->buffer = libxl__zalloc(NOGC, size);
+ cr->size = size;
+ cr->count = size;
+ cr->clear = clear;
+ cr->incremental = 1;
+
+ GC_FREE;
+ return cr;
+}
+
+/* return values: *line_r
+ * 1 success, whole line obtained from buffer non-0
+ * 0 no more lines available right now 0
+ * negative error code ERROR_* 0
+ * On success *line_r is updated to point to a nul-terminated
+ * string which is valid until the next call on the same console
+ * reader. The libxl caller may overwrite parts of the string
+ * if it wishes. */
+int libxl_xen_console_read_line(libxl_ctx *ctx,
+ libxl_xen_console_reader *cr,
+ char **line_r)
+{
+ int ret;
+ GC_INIT(ctx);
+
+ memset(cr->buffer, 0, cr->size);
+ ret = xc_readconsolering(ctx->xch, cr->buffer, &cr->count,
+ cr->clear, cr->incremental, &cr->index);
+ if (ret < 0) {
+ LOGE(ERROR, "reading console ring buffer");
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ if (!ret) {
+ if (cr->count) {
+ *line_r = cr->buffer;
+ ret = 1;
+ } else {
+ *line_r = NULL;
+ ret = 0;
+ }
+ }
+
+ GC_FREE;
+ return ret;
+}
+
+void libxl_xen_console_read_finish(libxl_ctx *ctx,
+ libxl_xen_console_reader *cr)
+{
+ free(cr->buffer);
+ free(cr);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2014 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+/*
+ * Infrastructure for converting a legacy migration stream into a
+ * libxl v2 stream.
+ *
+ * This is done by fork()ing the python conversion script, which takes
+ * in a legacy stream, and puts out a suitably-formatted v2 stream.
+ */
+
+static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
+ pid_t pid, int status);
+static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc);
+static void helper_done(libxl__egc *egc,
+ libxl__conversion_helper_state *chs);
+
+/*----- Entrypoints -----*/
+
+void libxl__conversion_helper_init(libxl__conversion_helper_state *chs)
+{
+ assert(chs->ao);
+
+ chs->v2_carefd = NULL;
+ chs->rc = 0;
+ libxl__ao_abortable_init(&chs->abrt);
+ libxl__ev_child_init(&chs->child);
+}
+
+int libxl__convert_legacy_stream(libxl__egc *egc,
+ libxl__conversion_helper_state *chs)
+{
+ STATE_AO_GC(chs->ao);
+ libxl__carefd *child_in = NULL, *child_out = NULL;
+ int rc = 0;
+
+ chs->abrt.ao = chs->ao;
+ chs->abrt.callback = helper_stop;
+ rc = libxl__ao_abortable_register(&chs->abrt);
+ if (rc) goto err;
+
+ libxl__carefd_begin();
+ int fds[2];
+ if (libxl_pipe(CTX, fds)) {
+ rc = ERROR_FAIL;
+ libxl__carefd_unlock();
+ goto err;
+ }
+ child_out = libxl__carefd_record(CTX, fds[0]);
+ child_in = libxl__carefd_record(CTX, fds[1]);
+ libxl__carefd_unlock();
+
+ pid_t pid = libxl__ev_child_fork(gc, &chs->child, helper_exited);
+ if (!pid) {
+ char * const args[] =
+ {
+ getenv("LIBXL_CONVERT_HELPER") ?:
+ LIBEXEC_BIN "/convert-legacy-stream",
+ "--in", GCSPRINTF("%d", chs->legacy_fd),
+ "--out", GCSPRINTF("%d", fds[1]),
+ /*
+ * The width calculation is an assumption for the common
+ * case. The conversion script needs to know the width of
+ * the toolstack which saved the legacy stream.
+ *
+ * In the overwhelming majority of cases, the width of the
+ * saving toolstack will be the same as our current
+ * width. To avoid extending the libxl API with a
+ * parameter intended to disappear shortly, this option
+ * has not been exposed to the caller.
+ *
+ * If more complicated conversion is required, the
+ * conversion script can be instantiated manually, which
+ * will bypass all of this conversion logic.
+ */
+ "--width", sizeof(unsigned long) == 8 ? "64" : "32",
+
+ "--guest", chs->hvm ? "hvm" : "pv",
+ "--format", "libxl",
+ /* "--verbose", */
+ NULL,
+ };
+
+ libxl_fd_set_cloexec(CTX, chs->legacy_fd, 0);
+ libxl_fd_set_cloexec(CTX, libxl__carefd_fd(child_in), 0);
+
+ libxl__exec(gc,
+ -1, -1, -1,
+ args[0], args, NULL);
+ }
+
+ libxl__carefd_close(child_in);
+ chs->v2_carefd = child_out;
+
+ assert(!rc);
+ return rc;
+
+ err:
+ libxl__ao_abortable_deregister(&chs->abrt);
+ assert(rc);
+ return rc;
+}
+
+void libxl__conversion_helper_abort(libxl__egc *egc,
+ libxl__conversion_helper_state *chs,
+ int rc)
+{
+ STATE_AO_GC(chs->ao);
+ assert(rc);
+
+ if (libxl__conversion_helper_inuse(chs)) {
+
+ if (!chs->rc)
+ chs->rc = rc;
+
+ libxl__kill(gc, chs->child.pid, SIGTERM, "conversion helper");
+ }
+}
+
+/*----- State handling -----*/
+
+static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
+{
+ libxl__conversion_helper_state *chs = CONTAINER_OF(abrt, *chs, abrt);
+ STATE_AO_GC(chs->ao);
+
+ libxl__conversion_helper_abort(egc, chs, rc);
+}
+
+static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
+ pid_t pid, int status)
+{
+ libxl__conversion_helper_state *chs = CONTAINER_OF(ch, *chs, child);
+ STATE_AO_GC(chs->ao);
+
+ if (status) {
+ libxl_report_child_exitstatus(
+ CTX, chs->rc ? XTL_DEBUG : XTL_ERROR,
+ "conversion helper", pid, status);
+
+ if (!chs->rc)
+ chs->rc = ERROR_FAIL;
+ }
+
+ helper_done(egc, chs);
+}
+
+static void helper_done(libxl__egc *egc,
+ libxl__conversion_helper_state *chs)
+{
+ STATE_AO_GC(chs->ao);
+
+ assert(!libxl__conversion_helper_inuse(chs));
+
+ libxl__ao_abortable_deregister(&chs->abrt);
+
+ chs->completion_callback(egc, chs, chs->rc);
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl)
+{
+ return !libxl_cpuid_policy_list_length(pl);
+}
+
+void libxl_cpuid_dispose(libxl_cpuid_policy_list *p_cpuid_list)
+{
+ int i, j;
+ libxl_cpuid_policy_list cpuid_list = *p_cpuid_list;
+
+ if (cpuid_list == NULL)
+ return;
+ for (i = 0; cpuid_list[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
+ for (j = 0; j < 4; j++)
+ if (cpuid_list[i].policy[j] != NULL) {
+ free(cpuid_list[i].policy[j]);
+ cpuid_list[i].policy[j] = NULL;
+ }
+ }
+ free(cpuid_list);
+ *p_cpuid_list = NULL;
+ return;
+}
+
+#define CPUID_REG_INV 0
+#define CPUID_REG_EAX 1
+#define CPUID_REG_EBX 2
+#define CPUID_REG_ECX 3
+#define CPUID_REG_EDX 4
+
+/* mapping CPUID features to names
+ * holds a "name" for each feature, specified by the "leaf" number (and an
+ * optional "subleaf" in ECX), the "reg"ister (EAX-EDX) used and a number of
+ * bits starting with "bit" and being "length" bits long.
+ * Used for the static structure describing all features.
+ */
+struct cpuid_flags {
+ char* name;
+ uint32_t leaf;
+ uint32_t subleaf;
+ int reg;
+ int bit;
+ int length;
+};
+
+/* go through the dynamic array finding the entry for a specified leaf.
+ * if no entry exists, allocate one and return that.
+ */
+static libxl_cpuid_policy_list cpuid_find_match(libxl_cpuid_policy_list *list,
+ uint32_t leaf, uint32_t subleaf)
+{
+ int i = 0;
+
+ if (*list != NULL) {
+ for (i = 0; (*list)[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
+ if ((*list)[i].input[0] == leaf && (*list)[i].input[1] == subleaf)
+ return *list + i;
+ }
+ }
+ *list = realloc(*list, sizeof((*list)[0]) * (i + 2));
+ (*list)[i].input[0] = leaf;
+ (*list)[i].input[1] = subleaf;
+ memset((*list)[i].policy, 0, 4 * sizeof(char*));
+ (*list)[i + 1].input[0] = XEN_CPUID_INPUT_UNUSED;
+ return *list + i;
+}
+
+/* parse a single key=value pair and translate it into the libxc
+ * used interface using 32-characters strings for each register.
+ * Will overwrite earlier entries and thus can be called multiple
+ * times.
+ */
+int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str)
+{
+#define NA XEN_CPUID_INPUT_UNUSED
+ static const struct cpuid_flags cpuid_flags[] = {
+ {"maxleaf", 0x00000000, NA, CPUID_REG_EAX, 0, 32},
+ /* the following two entries are subject to tweaking later in the code */
+ {"stepping", 0x00000001, NA, CPUID_REG_EAX, 0, 4},
+ {"model", 0x00000001, NA, CPUID_REG_EAX, 4, 8},
+ {"family", 0x00000001, NA, CPUID_REG_EAX, 8, 8},
+
+ {"brandid", 0x00000001, NA, CPUID_REG_EBX, 0, 8},
+ {"clflush", 0x00000001, NA, CPUID_REG_EBX, 8, 8},
+ {"proccount", 0x00000001, NA, CPUID_REG_EBX, 16, 8},
+ {"localapicid", 0x00000001, NA, CPUID_REG_EBX, 24, 8},
+
+ {"sse3", 0x00000001, NA, CPUID_REG_ECX, 0, 1},
+ {"pclmulqdq", 0x00000001, NA, CPUID_REG_ECX, 1, 1},
+ {"dtes64", 0x00000001, NA, CPUID_REG_ECX, 2, 1},
+ {"monitor", 0x00000001, NA, CPUID_REG_ECX, 3, 1},
+ {"dscpl", 0x00000001, NA, CPUID_REG_ECX, 4, 1},
+ {"vmx", 0x00000001, NA, CPUID_REG_ECX, 5, 1},
+ {"smx", 0x00000001, NA, CPUID_REG_ECX, 6, 1},
+ {"est", 0x00000001, NA, CPUID_REG_ECX, 7, 1},
+ {"tm2", 0x00000001, NA, CPUID_REG_ECX, 8, 1},
+ {"ssse3", 0x00000001, NA, CPUID_REG_ECX, 9, 1},
+ {"cntxid", 0x00000001, NA, CPUID_REG_ECX, 10, 1},
+ {"fma", 0x00000001, NA, CPUID_REG_ECX, 12, 1},
+ {"cmpxchg16", 0x00000001, NA, CPUID_REG_ECX, 13, 1},
+ {"xtpr", 0x00000001, NA, CPUID_REG_ECX, 14, 1},
+ {"pdcm", 0x00000001, NA, CPUID_REG_ECX, 15, 1},
+ {"pcid", 0x00000001, NA, CPUID_REG_ECX, 17, 1},
+ {"dca", 0x00000001, NA, CPUID_REG_ECX, 18, 1},
+ /* Linux uses sse4_{1,2}. Keep sse4.{1,2} for compatibility */
+ {"sse4_1", 0x00000001, NA, CPUID_REG_ECX, 19, 1},
+ {"sse4.1", 0x00000001, NA, CPUID_REG_ECX, 19, 1},
+ {"sse4_2", 0x00000001, NA, CPUID_REG_ECX, 20, 1},
+ {"sse4.2", 0x00000001, NA, CPUID_REG_ECX, 20, 1},
+ {"x2apic", 0x00000001, NA, CPUID_REG_ECX, 21, 1},
+ {"movbe", 0x00000001, NA, CPUID_REG_ECX, 22, 1},
+ {"popcnt", 0x00000001, NA, CPUID_REG_ECX, 23, 1},
+ {"tsc-deadline", 0x00000001, NA, CPUID_REG_ECX, 24, 1},
+ {"aes", 0x00000001, NA, CPUID_REG_ECX, 25, 1},
+ {"xsave", 0x00000001, NA, CPUID_REG_ECX, 26, 1},
+ {"osxsave", 0x00000001, NA, CPUID_REG_ECX, 27, 1},
+ {"avx", 0x00000001, NA, CPUID_REG_ECX, 28, 1},
+ {"f16c", 0x00000001, NA, CPUID_REG_ECX, 29, 1},
+ {"rdrand", 0x00000001, NA, CPUID_REG_ECX, 30, 1},
+ {"hypervisor", 0x00000001, NA, CPUID_REG_ECX, 31, 1},
+
+ {"fpu", 0x00000001, NA, CPUID_REG_EDX, 0, 1},
+ {"vme", 0x00000001, NA, CPUID_REG_EDX, 1, 1},
+ {"de", 0x00000001, NA, CPUID_REG_EDX, 2, 1},
+ {"pse", 0x00000001, NA, CPUID_REG_EDX, 3, 1},
+ {"tsc", 0x00000001, NA, CPUID_REG_EDX, 4, 1},
+ {"msr", 0x00000001, NA, CPUID_REG_EDX, 5, 1},
+ {"pae", 0x00000001, NA, CPUID_REG_EDX, 6, 1},
+ {"mce", 0x00000001, NA, CPUID_REG_EDX, 7, 1},
+ {"cmpxchg8", 0x00000001, NA, CPUID_REG_EDX, 8, 1},
+ {"apic", 0x00000001, NA, CPUID_REG_EDX, 9, 1},
+ {"sysenter", 0x00000001, NA, CPUID_REG_EDX, 11, 1},
+ {"mtrr", 0x00000001, NA, CPUID_REG_EDX, 12, 1},
+ {"pge", 0x00000001, NA, CPUID_REG_EDX, 13, 1},
+ {"mca", 0x00000001, NA, CPUID_REG_EDX, 14, 1},
+ {"cmov", 0x00000001, NA, CPUID_REG_EDX, 15, 1},
+ {"pat", 0x00000001, NA, CPUID_REG_EDX, 16, 1},
+ {"pse36", 0x00000001, NA, CPUID_REG_EDX, 17, 1},
+ {"psn", 0x00000001, NA, CPUID_REG_EDX, 18, 1},
+ {"clfsh", 0x00000001, NA, CPUID_REG_EDX, 19, 1},
+ {"ds", 0x00000001, NA, CPUID_REG_EDX, 21, 1},
+ {"acpi", 0x00000001, NA, CPUID_REG_EDX, 22, 1},
+ {"mmx", 0x00000001, NA, CPUID_REG_EDX, 23, 1},
+ {"fxsr", 0x00000001, NA, CPUID_REG_EDX, 24, 1},
+ {"sse", 0x00000001, NA, CPUID_REG_EDX, 25, 1},
+ {"sse2", 0x00000001, NA, CPUID_REG_EDX, 26, 1},
+ {"ss", 0x00000001, NA, CPUID_REG_EDX, 27, 1},
+ {"htt", 0x00000001, NA, CPUID_REG_EDX, 28, 1},
+ {"tm", 0x00000001, NA, CPUID_REG_EDX, 29, 1},
+ {"ia64", 0x00000001, NA, CPUID_REG_EDX, 30, 1},
+ {"pbe", 0x00000001, NA, CPUID_REG_EDX, 31, 1},
+
+ {"arat", 0x00000006, NA, CPUID_REG_EAX, 2, 1},
+
+ {"fsgsbase", 0x00000007, 0, CPUID_REG_EBX, 0, 1},
+ {"tsc_adjust", 0x00000007, 0, CPUID_REG_EBX, 1, 1},
+ {"bmi1", 0x00000007, 0, CPUID_REG_EBX, 3, 1},
+ {"hle", 0x00000007, 0, CPUID_REG_EBX, 4, 1},
+ {"avx2", 0x00000007, 0, CPUID_REG_EBX, 5, 1},
+ {"smep", 0x00000007, 0, CPUID_REG_EBX, 7, 1},
+ {"bmi2", 0x00000007, 0, CPUID_REG_EBX, 8, 1},
+ {"erms", 0x00000007, 0, CPUID_REG_EBX, 9, 1},
+ {"invpcid", 0x00000007, 0, CPUID_REG_EBX, 10, 1},
+ {"rtm", 0x00000007, 0, CPUID_REG_EBX, 11, 1},
+ {"cmt", 0x00000007, 0, CPUID_REG_EBX, 12, 1},
+ {"mpx", 0x00000007, 0, CPUID_REG_EBX, 14, 1},
+ {"avx512f", 0x00000007, 0, CPUID_REG_EBX, 16, 1},
+ {"avx512dq", 0x00000007, 0, CPUID_REG_EBX, 17, 1},
+ {"rdseed", 0x00000007, 0, CPUID_REG_EBX, 18, 1},
+ {"adx", 0x00000007, 0, CPUID_REG_EBX, 19, 1},
+ {"smap", 0x00000007, 0, CPUID_REG_EBX, 20, 1},
+ {"avx512-ifma", 0x00000007, 0, CPUID_REG_EBX, 21, 1},
+ {"clflushopt", 0x00000007, 0, CPUID_REG_EBX, 23, 1},
+ {"clwb", 0x00000007, 0, CPUID_REG_EBX, 24, 1},
+ {"avx512pf", 0x00000007, 0, CPUID_REG_EBX, 26, 1},
+ {"avx512er", 0x00000007, 0, CPUID_REG_EBX, 27, 1},
+ {"avx512cd", 0x00000007, 0, CPUID_REG_EBX, 28, 1},
+ {"sha", 0x00000007, 0, CPUID_REG_EBX, 29, 1},
+ {"avx512bw", 0x00000007, 0, CPUID_REG_EBX, 30, 1},
+ {"avx512vl", 0x00000007, 0, CPUID_REG_EBX, 31, 1},
+
+ {"prefetchwt1", 0x00000007, 0, CPUID_REG_ECX, 0, 1},
+ {"avx512-vbmi", 0x00000007, 0, CPUID_REG_ECX, 1, 1},
+ {"umip", 0x00000007, 0, CPUID_REG_ECX, 2, 1},
+ {"pku", 0x00000007, 0, CPUID_REG_ECX, 3, 1},
+ {"ospke", 0x00000007, 0, CPUID_REG_ECX, 4, 1},
+ {"avx512-vbmi2", 0x00000007, 0, CPUID_REG_ECX, 6, 1},
+ {"cet-ss", 0x00000007, 0, CPUID_REG_ECX, 7, 1},
+ {"gfni", 0x00000007, 0, CPUID_REG_ECX, 8, 1},
+ {"vaes", 0x00000007, 0, CPUID_REG_ECX, 9, 1},
+ {"vpclmulqdq", 0x00000007, 0, CPUID_REG_ECX, 10, 1},
+ {"avx512-vnni", 0x00000007, 0, CPUID_REG_ECX, 11, 1},
+ {"avx512-bitalg",0x00000007, 0, CPUID_REG_ECX, 12, 1},
+ {"avx512-vpopcntdq",0x00000007,0,CPUID_REG_ECX, 14, 1},
+ {"tsxldtrk", 0x00000007, 0, CPUID_REG_ECX, 16, 1},
+ {"rdpid", 0x00000007, 0, CPUID_REG_ECX, 22, 1},
+ {"cldemote", 0x00000007, 0, CPUID_REG_ECX, 25, 1},
+
+ {"avx512-4vnniw",0x00000007, 0, CPUID_REG_EDX, 2, 1},
+ {"avx512-4fmaps",0x00000007, 0, CPUID_REG_EDX, 3, 1},
+ {"avx512-vp2intersect",0x00000007,0,CPUID_REG_EDX,8, 1},
+ {"srbds-ctrl", 0x00000007, 0, CPUID_REG_EDX, 9, 1},
+ {"md-clear", 0x00000007, 0, CPUID_REG_EDX, 10, 1},
+ {"serialize", 0x00000007, 0, CPUID_REG_EDX, 14, 1},
+ {"cet-ibt", 0x00000007, 0, CPUID_REG_EDX, 20, 1},
+ {"ibrsb", 0x00000007, 0, CPUID_REG_EDX, 26, 1},
+ {"stibp", 0x00000007, 0, CPUID_REG_EDX, 27, 1},
+ {"l1d-flush", 0x00000007, 0, CPUID_REG_EDX, 28, 1},
+ {"arch-caps", 0x00000007, 0, CPUID_REG_EDX, 29, 1},
+ {"core-caps", 0x00000007, 0, CPUID_REG_EDX, 30, 1},
+ {"ssbd", 0x00000007, 0, CPUID_REG_EDX, 31, 1},
+
+ {"avx512-bf16", 0x00000007, 1, CPUID_REG_EAX, 5, 1},
+
+ {"lahfsahf", 0x80000001, NA, CPUID_REG_ECX, 0, 1},
+ {"cmplegacy", 0x80000001, NA, CPUID_REG_ECX, 1, 1},
+ {"svm", 0x80000001, NA, CPUID_REG_ECX, 2, 1},
+ {"extapic", 0x80000001, NA, CPUID_REG_ECX, 3, 1},
+ {"altmovcr8", 0x80000001, NA, CPUID_REG_ECX, 4, 1},
+ {"abm", 0x80000001, NA, CPUID_REG_ECX, 5, 1},
+ {"sse4a", 0x80000001, NA, CPUID_REG_ECX, 6, 1},
+ {"misalignsse", 0x80000001, NA, CPUID_REG_ECX, 7, 1},
+ {"3dnowprefetch",0x80000001, NA, CPUID_REG_ECX, 8, 1},
+ {"osvw", 0x80000001, NA, CPUID_REG_ECX, 9, 1},
+ {"ibs", 0x80000001, NA, CPUID_REG_ECX, 10, 1},
+ {"xop", 0x80000001, NA, CPUID_REG_ECX, 11, 1},
+ {"skinit", 0x80000001, NA, CPUID_REG_ECX, 12, 1},
+ {"wdt", 0x80000001, NA, CPUID_REG_ECX, 13, 1},
+ {"lwp", 0x80000001, NA, CPUID_REG_ECX, 15, 1},
+ {"fma4", 0x80000001, NA, CPUID_REG_ECX, 16, 1},
+ {"nodeid", 0x80000001, NA, CPUID_REG_ECX, 19, 1},
+ {"tbm", 0x80000001, NA, CPUID_REG_ECX, 21, 1},
+ {"topoext", 0x80000001, NA, CPUID_REG_ECX, 22, 1},
+ {"perfctr_core", 0x80000001, NA, CPUID_REG_ECX, 23, 1},
+ {"perfctr_nb", 0x80000001, NA, CPUID_REG_ECX, 24, 1},
+
+ {"syscall", 0x80000001, NA, CPUID_REG_EDX, 11, 1},
+ {"nx", 0x80000001, NA, CPUID_REG_EDX, 20, 1},
+ {"mmxext", 0x80000001, NA, CPUID_REG_EDX, 22, 1},
+ {"ffxsr", 0x80000001, NA, CPUID_REG_EDX, 25, 1},
+ {"page1gb", 0x80000001, NA, CPUID_REG_EDX, 26, 1},
+ {"rdtscp", 0x80000001, NA, CPUID_REG_EDX, 27, 1},
+ {"lm", 0x80000001, NA, CPUID_REG_EDX, 29, 1},
+ {"3dnowext", 0x80000001, NA, CPUID_REG_EDX, 30, 1},
+ {"3dnow", 0x80000001, NA, CPUID_REG_EDX, 31, 1},
+
+ {"procpkg", 0x00000004, 0, CPUID_REG_EAX, 26, 6},
+
+ {"invtsc", 0x80000007, NA, CPUID_REG_EDX, 8, 1},
+
+ {"clzero", 0x80000008, NA, CPUID_REG_EBX, 0, 1},
+ {"rstr-fp-err-ptrs", 0x80000008, NA, CPUID_REG_EBX, 2, 1},
+ {"wbnoinvd", 0x80000008, NA, CPUID_REG_EBX, 9, 1},
+ {"ibpb", 0x80000008, NA, CPUID_REG_EBX, 12, 1},
+ {"ppin", 0x80000008, NA, CPUID_REG_EBX, 23, 1},
+
+ {"nc", 0x80000008, NA, CPUID_REG_ECX, 0, 8},
+ {"apicidsize", 0x80000008, NA, CPUID_REG_ECX, 12, 4},
+
+ {"svm_npt", 0x8000000a, NA, CPUID_REG_EDX, 0, 1},
+ {"svm_lbrv", 0x8000000a, NA, CPUID_REG_EDX, 1, 1},
+ {"svm_nrips", 0x8000000a, NA, CPUID_REG_EDX, 3, 1},
+ {"svm_tscrate", 0x8000000a, NA, CPUID_REG_EDX, 4, 1},
+ {"svm_vmcbclean",0x8000000a, NA, CPUID_REG_EDX, 5, 1},
+ {"svm_decode", 0x8000000a, NA, CPUID_REG_EDX, 7, 1},
+ {"svm_pausefilt",0x8000000a, NA, CPUID_REG_EDX, 10, 1},
+
+ {"maxhvleaf", 0x40000000, NA, CPUID_REG_EAX, 0, 8},
+
+ {NULL, 0, NA, CPUID_REG_INV, 0, 0}
+ };
+#undef NA
+ char *sep, *val, *endptr;
+ int i;
+ const struct cpuid_flags *flag;
+ struct xc_xend_cpuid *entry;
+ unsigned long num;
+ char flags[33], *resstr;
+
+ sep = strchr(str, '=');
+ if (sep == NULL) {
+ return 1;
+ } else {
+ val = sep + 1;
+ }
+ for (flag = cpuid_flags; flag->name != NULL; flag++) {
+ if(!strncmp(str, flag->name, sep - str) && flag->name[sep - str] == 0)
+ break;
+ }
+ if (flag->name == NULL) {
+ return 2;
+ }
+ entry = cpuid_find_match(cpuid, flag->leaf, flag->subleaf);
+ resstr = entry->policy[flag->reg - 1];
+ num = strtoull(val, &endptr, 0);
+ flags[flag->length] = 0;
+ if (endptr != val) {
+ /* if this was a valid number, write the binary form into the string */
+ for (i = 0; i < flag->length; i++) {
+ flags[flag->length - 1 - i] = "01"[!!(num & (1 << i))];
+ }
+ } else {
+ switch(val[0]) {
+ case 'x': case 'k': case 's':
+ memset(flags, val[0], flag->length);
+ break;
+ default:
+ return 3;
+ }
+ }
+
+ if (resstr == NULL) {
+ resstr = strdup("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+ }
+
+ /* the family and model entry is potentially split up across
+ * two fields in Fn0000_0001_EAX, so handle them here separately.
+ */
+ if (!strncmp(str, "family", sep - str)) {
+ if (num < 16) {
+ memcpy(resstr + (32 - 4) - flag->bit, flags + 4, 4);
+ memcpy(resstr + (32 - 8) - 20, "00000000", 8);
+ } else {
+ num -= 15;
+ memcpy(resstr + (32 - 4) - flag->bit, "1111", 4);
+ for (i = 0; i < 7; i++) {
+ flags[7 - i] = "01"[num & 1];
+ num >>= 1;
+ }
+ memcpy(resstr + (32 - 8) - 20, flags, 8);
+ }
+ } else if (!strncmp(str, "model", sep - str)) {
+ memcpy(resstr + (32 - 4) - 16, flags, 4);
+ memcpy(resstr + (32 - 4) - flag->bit, flags + 4, 4);
+ } else {
+ memcpy(resstr + (32 - flag->length) - flag->bit, flags,
+ flag->length);
+ }
+ entry->policy[flag->reg - 1] = resstr;
+
+ return 0;
+}
+
+/* parse a single list item from the legacy Python xend syntax, where
+ * the strings for each register were directly exposed to the user.
+ * Used for maintaining compatibility with older config files
+ */
+int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid,
+ const char* str)
+{
+ char *endptr;
+ unsigned long value;
+ uint32_t leaf, subleaf = XEN_CPUID_INPUT_UNUSED;
+ struct xc_xend_cpuid *entry;
+
+ /* parse the leaf number */
+ value = strtoul(str, &endptr, 0);
+ if (str == endptr) {
+ return 1;
+ }
+ leaf = value;
+ /* check for an optional subleaf number */
+ if (*endptr == ',') {
+ str = endptr + 1;
+ value = strtoul(str, &endptr, 0);
+ if (str == endptr) {
+ return 2;
+ }
+ subleaf = value;
+ }
+ if (*endptr != ':') {
+ return 3;
+ }
+ str = endptr + 1;
+ entry = cpuid_find_match(cpuid, leaf, subleaf);
+ for (str = endptr + 1; *str != 0;) {
+ if (str[0] != 'e' || str[2] != 'x') {
+ return 4;
+ }
+ value = str[1] - 'a';
+ endptr = strchr(str, '=');
+ if (value > 3 || endptr == NULL) {
+ return 4;
+ }
+ str = endptr + 1;
+ endptr = strchr(str, ',');
+ if (endptr == NULL) {
+ endptr = strchr(str, 0);
+ }
+ if (endptr - str != 32) {
+ return 5;
+ }
+ entry->policy[value] = calloc(32 + 1, 1);
+ strncpy(entry->policy[value], str, 32);
+ entry->policy[value][32] = 0;
+ if (*endptr == 0) {
+ break;
+ }
+ for (str = endptr + 1; *str == ' ' || *str == '\n'; str++);
+ }
+ return 0;
+}
+
+void libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool restore,
+ libxl_domain_build_info *info)
+{
+ bool pae = true;
+ bool itsc;
+ bool nested_virt = libxl_defbool_val(info->nested_hvm);
+
+ /*
+ * For PV guests, PAE is Xen-controlled (it is the 'p' that differentiates
+ * the xen-3.0-x86_32 and xen-3.0-x86_32p ABIs). It is mandatory as Xen
+ * is 64bit only these days.
+ *
+ * For PVH guests, there is no top-level PAE control in the domain config,
+ * so is treated as always available.
+ *
+ * HVM guests get a top-level choice of whether PAE is available.
+ */
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM)
+ pae = libxl_defbool_val(info->u.hvm.pae);
+
+ /*
+ * Advertising Invariant TSC to a guest means that the TSC frequency won't
+ * change at any point in the future.
+ *
+ * We do not have enough information about potential migration
+ * destinations to know whether advertising ITSC is safe, but if the guest
+ * isn't going to migrate, then the current hardware is all that matters.
+ *
+ * Alternatively, an internal property of vTSC is that the values read are
+ * invariant. Advertise ITSC when we know the domain will have emualted
+ * TSC everywhere it goes.
+ */
+ itsc = (libxl_defbool_val(info->disable_migrate) ||
+ info->tsc_mode == LIBXL_TSC_MODE_ALWAYS_EMULATE);
+
+ xc_cpuid_apply_policy(ctx->xch, domid, restore, NULL, 0,
+ pae, itsc, nested_virt, info->cpuid);
+}
+
+static const char *input_names[2] = { "leaf", "subleaf" };
+static const char *policy_names[4] = { "eax", "ebx", "ecx", "edx" };
+/*
+ * Aiming for:
+ * [
+ * { 'leaf': 'val-eax',
+ * 'subleaf': 'val-ecx',
+ * 'eax': 'filter',
+ * 'ebx': 'filter',
+ * 'ecx': 'filter',
+ * 'edx': 'filter' },
+ * { 'leaf': 'val-eax', ..., 'eax': 'filter', ... },
+ * ... etc ...
+ * ]
+ */
+
+yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
+ libxl_cpuid_policy_list *pcpuid)
+{
+ libxl_cpuid_policy_list cpuid = *pcpuid;
+ yajl_gen_status s;
+ int i, j;
+
+ s = yajl_gen_array_open(hand);
+ if (s != yajl_gen_status_ok) goto out;
+
+ if (cpuid == NULL) goto empty;
+
+ for (i = 0; cpuid[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
+ s = yajl_gen_map_open(hand);
+ if (s != yajl_gen_status_ok) goto out;
+
+ for (j = 0; j < 2; j++) {
+ if (cpuid[i].input[j] != XEN_CPUID_INPUT_UNUSED) {
+ s = libxl__yajl_gen_asciiz(hand, input_names[j]);
+ if (s != yajl_gen_status_ok) goto out;
+ s = yajl_gen_integer(hand, cpuid[i].input[j]);
+ if (s != yajl_gen_status_ok) goto out;
+ }
+ }
+
+ for (j = 0; j < 4; j++) {
+ if (cpuid[i].policy[j] != NULL) {
+ s = libxl__yajl_gen_asciiz(hand, policy_names[j]);
+ if (s != yajl_gen_status_ok) goto out;
+ s = yajl_gen_string(hand,
+ (const unsigned char *)cpuid[i].policy[j], 32);
+ if (s != yajl_gen_status_ok) goto out;
+ }
+ }
+ s = yajl_gen_map_close(hand);
+ if (s != yajl_gen_status_ok) goto out;
+ }
+
+empty:
+ s = yajl_gen_array_close(hand);
+out:
+ return s;
+}
+
+int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
+ const libxl__json_object *o,
+ libxl_cpuid_policy_list *p)
+{
+ int i, size;
+ libxl_cpuid_policy_list l;
+ flexarray_t *array;
+
+ if (!libxl__json_object_is_array(o))
+ return ERROR_FAIL;
+
+ array = libxl__json_object_get_array(o);
+ if (!array->count)
+ return 0;
+
+ size = array->count;
+ /* need one extra slot as sentinel */
+ l = *p = libxl__calloc(NOGC, size + 1, sizeof(libxl_cpuid_policy));
+
+ l[size].input[0] = XEN_CPUID_INPUT_UNUSED;
+ l[size].input[1] = XEN_CPUID_INPUT_UNUSED;
+
+ for (i = 0; i < size; i++) {
+ const libxl__json_object *t;
+ int j;
+
+ if (flexarray_get(array, i, (void**)&t) != 0)
+ return ERROR_FAIL;
+
+ if (!libxl__json_object_is_map(t))
+ return ERROR_FAIL;
+
+ for (j = 0; j < ARRAY_SIZE(l[0].input); j++) {
+ const libxl__json_object *r;
+
+ r = libxl__json_map_get(input_names[j], t, JSON_INTEGER);
+ if (!r)
+ l[i].input[j] = XEN_CPUID_INPUT_UNUSED;
+ else
+ l[i].input[j] = libxl__json_object_get_integer(r);
+ }
+
+ for (j = 0; j < ARRAY_SIZE(l[0].policy); j++) {
+ const libxl__json_object *r;
+
+ r = libxl__json_map_get(policy_names[j], t, JSON_STRING);
+ if (!r)
+ l[i].policy[j] = NULL;
+ else
+ l[i].policy[j] =
+ libxl__strdup(NOGC, libxl__json_object_get_string(r));
+ }
+ }
+
+ return 0;
+}
+
+int libxl_cpuid_policy_list_length(const libxl_cpuid_policy_list *pl)
+{
+ int i = 0;
+ libxl_cpuid_policy_list l = *pl;
+
+ if (l) {
+ while (l[i].input[0] != XEN_CPUID_INPUT_UNUSED)
+ i++;
+ }
+
+ return i;
+}
+
+void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
+ libxl_cpuid_policy_list *dst,
+ const libxl_cpuid_policy_list *src)
+{
+ GC_INIT(ctx);
+ int i, j, len;
+
+ if (*src == NULL) {
+ *dst = NULL;
+ goto out;
+ }
+
+ len = libxl_cpuid_policy_list_length(src);
+ /* one extra slot for sentinel */
+ *dst = libxl__calloc(NOGC, len + 1, sizeof(libxl_cpuid_policy));
+ (*dst)[len].input[0] = XEN_CPUID_INPUT_UNUSED;
+ (*dst)[len].input[1] = XEN_CPUID_INPUT_UNUSED;
+
+ for (i = 0; i < len; i++) {
+ for (j = 0; j < 2; j++)
+ (*dst)[i].input[j] = (*src)[i].input[j];
+ for (j = 0; j < 4; j++)
+ if ((*src)[i].policy[j])
+ (*dst)[i].policy[j] =
+ libxl__strdup(NOGC, (*src)[i].policy[j]);
+ else
+ (*dst)[i].policy[j] = NULL;
+ }
+
+out:
+ GC_FREE;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+/* Returns:
+ * 0 - success
+ * ERROR_FAIL + errno == ENOENT - no entry found
+ * ERROR_$FOO + errno != ENOENT - other failure
+ */
+static int cpupool_info(libxl__gc *gc,
+ libxl_cpupoolinfo *info,
+ uint32_t poolid,
+ bool exact /* exactly poolid or >= poolid */)
+{
+ xc_cpupoolinfo_t *xcinfo;
+ int rc = ERROR_FAIL;
+
+ xcinfo = xc_cpupool_getinfo(CTX->xch, poolid);
+ if (xcinfo == NULL)
+ {
+ if (exact || errno != ENOENT)
+ LOGE(ERROR, "failed to get info for cpupool%d", poolid);
+ return ERROR_FAIL;
+ }
+
+ if (exact && xcinfo->cpupool_id != poolid)
+ {
+ LOG(ERROR, "got info for cpupool%d, wanted cpupool%d\n",
+ xcinfo->cpupool_id, poolid);
+ goto out;
+ }
+
+ info->poolid = xcinfo->cpupool_id;
+ info->pool_name = libxl_cpupoolid_to_name(CTX, info->poolid);
+ if (!info->pool_name) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ info->sched = xcinfo->sched_id;
+ info->n_dom = xcinfo->n_dom;
+ rc = libxl_cpu_bitmap_alloc(CTX, &info->cpumap, 0);
+ if (rc)
+ goto out;
+
+ memcpy(info->cpumap.map, xcinfo->cpumap, info->cpumap.size);
+
+ rc = 0;
+out:
+ xc_cpupool_infofree(CTX->xch, xcinfo);
+ return rc;
+}
+
+int libxl_cpupool_info(libxl_ctx *ctx,
+ libxl_cpupoolinfo *info, uint32_t poolid)
+{
+ GC_INIT(ctx);
+ int rc = cpupool_info(gc, info, poolid, true);
+ GC_FREE;
+ return rc;
+}
+
+libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx *ctx, int *nb_pool_out)
+{
+ GC_INIT(ctx);
+ libxl_cpupoolinfo info, *ptr;
+
+ int i;
+ uint32_t poolid;
+
+ ptr = NULL;
+
+ poolid = 0;
+ for (i = 0;; i++) {
+ libxl_cpupoolinfo_init(&info);
+ if (cpupool_info(gc, &info, poolid, false)) {
+ libxl_cpupoolinfo_dispose(&info);
+ if (errno != ENOENT) goto out;
+ break;
+ }
+
+ ptr = libxl__realloc(NOGC, ptr, (i+1) * sizeof(libxl_cpupoolinfo));
+ ptr[i] = info;
+ poolid = info.poolid + 1;
+ /* Don't dispose of info because it will be returned to caller */
+ }
+
+ *nb_pool_out = i;
+
+ GC_FREE;
+ return ptr;
+
+out:
+ libxl_cpupoolinfo_list_free(ptr, i);
+ *nb_pool_out = 0;
+ GC_FREE;
+ return NULL;
+}
+
+int libxl_get_freecpus(libxl_ctx *ctx, libxl_bitmap *cpumap)
+{
+ int ncpus;
+
+ ncpus = libxl_get_max_cpus(ctx);
+ if (ncpus < 0)
+ return ncpus;
+
+ cpumap->map = xc_cpupool_freeinfo(ctx->xch);
+ if (cpumap->map == NULL)
+ return ERROR_FAIL;
+
+ cpumap->size = (ncpus + 7) / 8;
+
+ return 0;
+}
+
+int libxl_cpupool_create(libxl_ctx *ctx, const char *name,
+ libxl_scheduler sched,
+ libxl_bitmap cpumap, libxl_uuid *uuid,
+ uint32_t *poolid)
+{
+ GC_INIT(ctx);
+ int rc;
+ int i;
+ xs_transaction_t t;
+ char *uuid_string;
+ uint32_t xcpoolid;
+
+ /* Accept '0' as 'any poolid' for backwards compatibility */
+ if ( *poolid == LIBXL_CPUPOOL_POOLID_ANY
+ || *poolid == 0 )
+ xcpoolid = XC_CPUPOOL_POOLID_ANY;
+ else
+ xcpoolid = *poolid;
+
+ uuid_string = libxl__uuid2string(gc, *uuid);
+ if (!uuid_string) {
+ GC_FREE;
+ return ERROR_NOMEM;
+ }
+
+ rc = xc_cpupool_create(ctx->xch, &xcpoolid, sched);
+ if (rc) {
+ LOGEV(ERROR, rc, "Could not create cpupool");
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ *poolid = xcpoolid;
+
+ libxl_for_each_bit(i, cpumap)
+ if (libxl_bitmap_test(&cpumap, i)) {
+ rc = xc_cpupool_addcpu(ctx->xch, *poolid, i);
+ if (rc) {
+ LOGEV(ERROR, rc, "Error moving cpu to cpupool");
+ libxl_cpupool_destroy(ctx, *poolid);
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ }
+
+ for (;;) {
+ t = xs_transaction_start(ctx->xsh);
+
+ xs_mkdir(ctx->xsh, t, GCSPRINTF("/local/pool/%d", *poolid));
+ libxl__xs_printf(gc, t,
+ GCSPRINTF("/local/pool/%d/uuid", *poolid),
+ "%s", uuid_string);
+ libxl__xs_printf(gc, t,
+ GCSPRINTF("/local/pool/%d/name", *poolid),
+ "%s", name);
+
+ if (xs_transaction_end(ctx->xsh, t, 0) || (errno != EAGAIN)) {
+ GC_FREE;
+ return 0;
+ }
+ }
+}
+
+int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid)
+{
+ GC_INIT(ctx);
+ int rc, i;
+ xc_cpupoolinfo_t *info;
+ xs_transaction_t t;
+ libxl_bitmap cpumap;
+
+ info = xc_cpupool_getinfo(ctx->xch, poolid);
+ if (info == NULL) {
+ GC_FREE;
+ return ERROR_NOMEM;
+ }
+
+ rc = ERROR_INVAL;
+ if ((info->cpupool_id != poolid) || (info->n_dom))
+ goto out;
+
+ rc = libxl_cpu_bitmap_alloc(ctx, &cpumap, 0);
+ if (rc)
+ goto out;
+
+ memcpy(cpumap.map, info->cpumap, cpumap.size);
+ libxl_for_each_bit(i, cpumap)
+ if (libxl_bitmap_test(&cpumap, i)) {
+ rc = xc_cpupool_removecpu(ctx->xch, poolid, i);
+ if (rc) {
+ LOGEV(ERROR, rc, "Error removing cpu from cpupool");
+ rc = ERROR_FAIL;
+ goto out1;
+ }
+ }
+
+ rc = xc_cpupool_destroy(ctx->xch, poolid);
+ if (rc) {
+ LOGEV(ERROR, rc, "Could not destroy cpupool");
+ rc = ERROR_FAIL;
+ goto out1;
+ }
+
+ for (;;) {
+ t = xs_transaction_start(ctx->xsh);
+
+ xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF("/local/pool/%d", poolid));
+
+ if (xs_transaction_end(ctx->xsh, t, 0) || (errno != EAGAIN))
+ break;
+ }
+
+ rc = 0;
+
+out1:
+ libxl_bitmap_dispose(&cpumap);
+out:
+ xc_cpupool_infofree(ctx->xch, info);
+ GC_FREE;
+
+ return rc;
+}
+
+int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid)
+{
+ GC_INIT(ctx);
+ xs_transaction_t t;
+ xc_cpupoolinfo_t *info;
+ int rc;
+
+ info = xc_cpupool_getinfo(ctx->xch, poolid);
+ if (info == NULL) {
+ GC_FREE;
+ return ERROR_NOMEM;
+ }
+
+ rc = ERROR_INVAL;
+ if (info->cpupool_id != poolid)
+ goto out;
+
+ rc = 0;
+
+ for (;;) {
+ t = xs_transaction_start(ctx->xsh);
+
+ libxl__xs_printf(gc, t,
+ GCSPRINTF("/local/pool/%d/name", poolid),
+ "%s", name);
+
+ if (xs_transaction_end(ctx->xsh, t, 0))
+ break;
+
+ if (errno == EAGAIN)
+ continue;
+
+ rc = ERROR_FAIL;
+ break;
+ }
+
+out:
+ xc_cpupool_infofree(ctx->xch, info);
+ GC_FREE;
+
+ return rc;
+}
+
+int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu)
+{
+ GC_INIT(ctx);
+ int rc = 0;
+
+ rc = xc_cpupool_addcpu(ctx->xch, poolid, cpu);
+ if (rc) {
+ LOGE(ERROR, "Error moving cpu %d to cpupool", cpu);
+ rc = ERROR_FAIL;
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid,
+ const libxl_bitmap *cpumap)
+{
+ int c, ncpus = 0, rc = 0;
+
+ libxl_for_each_set_bit(c, *cpumap) {
+ if (!libxl_cpupool_cpuadd(ctx, poolid, c))
+ ncpus++;
+ }
+
+ if (ncpus != libxl_bitmap_count_set(cpumap))
+ rc = ERROR_FAIL;
+
+ return rc;
+}
+
+int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus)
+{
+ int rc = 0;
+ int cpu, nr;
+ libxl_bitmap freemap;
+ libxl_cputopology *topology;
+
+ if (libxl_get_freecpus(ctx, &freemap)) {
+ return ERROR_FAIL;
+ }
+
+ topology = libxl_get_cpu_topology(ctx, &nr);
+ if (!topology) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ *cpus = 0;
+ for (cpu = 0; cpu < nr; cpu++) {
+ if (libxl_bitmap_test(&freemap, cpu) && (topology[cpu].node == node) &&
+ !libxl_cpupool_cpuadd(ctx, poolid, cpu)) {
+ (*cpus)++;
+ }
+ libxl_cputopology_dispose(&topology[cpu]);
+ }
+
+ free(topology);
+out:
+ libxl_bitmap_dispose(&freemap);
+ return rc;
+}
+
+int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu)
+{
+ GC_INIT(ctx);
+ int rc = 0;
+
+ rc = xc_cpupool_removecpu(ctx->xch, poolid, cpu);
+ if (rc) {
+ LOGE(ERROR, "Error removing cpu %d from cpupool", cpu);
+ rc = ERROR_FAIL;
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid,
+ const libxl_bitmap *cpumap)
+{
+ int c, ncpus = 0, rc = 0;
+
+ libxl_for_each_set_bit(c, *cpumap) {
+ if (!libxl_cpupool_cpuremove(ctx, poolid, c))
+ ncpus++;
+ }
+
+ if (ncpus != libxl_bitmap_count_set(cpumap))
+ rc = ERROR_FAIL;
+
+ return rc;
+}
+
+int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus)
+{
+ int ret = 0;
+ int n_pools;
+ int p;
+ int cpu, nr_cpus;
+ libxl_cputopology *topology;
+ libxl_cpupoolinfo *poolinfo;
+
+ poolinfo = libxl_list_cpupool(ctx, &n_pools);
+ if (!poolinfo) {
+ return ERROR_NOMEM;
+ }
+
+ topology = libxl_get_cpu_topology(ctx, &nr_cpus);
+ if (!topology) {
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ *cpus = 0;
+ for (p = 0; p < n_pools; p++) {
+ if (poolinfo[p].poolid == poolid) {
+ for (cpu = 0; cpu < nr_cpus; cpu++) {
+ if ((topology[cpu].node == node) &&
+ libxl_bitmap_test(&poolinfo[p].cpumap, cpu) &&
+ !libxl_cpupool_cpuremove(ctx, poolid, cpu)) {
+ (*cpus)++;
+ }
+ }
+ }
+ }
+
+ libxl_cputopology_list_free(topology, nr_cpus);
+
+out:
+ libxl_cpupoolinfo_list_free(poolinfo, n_pools);
+
+ return ret;
+}
+
+int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid)
+{
+ GC_INIT(ctx);
+ int rc;
+
+ rc = xc_cpupool_movedomain(ctx->xch, poolid, domid);
+ if (rc) {
+ LOGEVD(ERROR, rc, domid, "Error moving domain to cpupool");
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+
+ GC_FREE;
+ return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2010 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ * Author Gianni Tedesco <gianni.tedesco@citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+
+#include <xenctrl_dom.h>
+#include <xenguest.h>
+#include <xen/hvm/hvm_info_table.h>
+#include <xen/hvm/e820.h>
+
+#include <xen-xsm/flask/flask.h>
+
+int libxl__domain_create_info_setdefault(libxl__gc *gc,
+ libxl_domain_create_info *c_info,
+ const libxl_physinfo *info)
+{
+ if (!c_info->type) {
+ LOG(ERROR, "domain type unspecified");
+ return ERROR_INVAL;
+ }
+
+ libxl__arch_domain_create_info_setdefault(gc, c_info);
+
+ if (c_info->type != LIBXL_DOMAIN_TYPE_PV) {
+ if (info->cap_hap) {
+ libxl_defbool_setdefault(&c_info->hap, true);
+ } else if (info->cap_shadow) {
+ libxl_defbool_setdefault(&c_info->hap, false);
+ } else {
+ LOG(ERROR, "neither hap nor shadow paging available");
+ return ERROR_INVAL;
+ }
+
+ libxl_defbool_setdefault(&c_info->oos, true);
+ }
+
+ libxl_defbool_setdefault(&c_info->run_hotplug_scripts, true);
+ libxl_defbool_setdefault(&c_info->driver_domain, false);
+
+ if (!c_info->ssidref)
+ c_info->ssidref = SECINITSID_DOMU;
+
+ libxl_defbool_setdefault(&c_info->xend_suspend_evtchn_compat, false);
+
+ return 0;
+}
+
+void libxl__rdm_setdefault(libxl__gc *gc, libxl_domain_build_info *b_info)
+{
+ if (b_info->u.hvm.rdm.policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
+ b_info->u.hvm.rdm.policy = LIBXL_RDM_RESERVE_POLICY_RELAXED;
+
+ if (b_info->u.hvm.rdm_mem_boundary_memkb == LIBXL_MEMKB_DEFAULT)
+ b_info->u.hvm.rdm_mem_boundary_memkb =
+ LIBXL_RDM_MEM_BOUNDARY_MEMKB_DEFAULT;
+}
+
+int libxl__domain_build_info_setdefault(libxl__gc *gc,
+ libxl_domain_build_info *b_info)
+{
+ int i, rc;
+
+ if (b_info->type != LIBXL_DOMAIN_TYPE_HVM &&
+ b_info->type != LIBXL_DOMAIN_TYPE_PV &&
+ b_info->type != LIBXL_DOMAIN_TYPE_PVH) {
+ LOG(ERROR, "invalid domain type");
+ return ERROR_INVAL;
+ }
+
+ /* Copy deprecated options to it's new position. */
+ rc = libxl__domain_build_info_copy_deprecated(CTX, b_info);
+ if (rc) {
+ LOG(ERROR, "Unable to copy deprecated fields");
+ return rc;
+ }
+
+ libxl_defbool_setdefault(&b_info->device_model_stubdomain, false);
+
+ if (libxl_defbool_val(b_info->device_model_stubdomain) &&
+ !b_info->device_model_ssidref)
+ b_info->device_model_ssidref = SECINITSID_DOMDM;
+
+ if (!b_info->device_model_version) {
+ if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
+ if (libxl_defbool_val(b_info->device_model_stubdomain)) {
+ b_info->device_model_version =
+ LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
+ } else {
+ b_info->device_model_version = libxl__default_device_model(gc);
+ }
+ } else {
+ b_info->device_model_version =
+ LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
+ }
+ if (b_info->device_model_version
+ == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+ const char *dm;
+
+ dm = libxl__domain_device_model(gc, b_info);
+ rc = access(dm, X_OK);
+ if (rc < 0) {
+ /* qemu-xen unavailable, use qemu-xen-traditional */
+ if (errno == ENOENT) {
+ LOGE(INFO, "qemu-xen is unavailable"
+ ", using qemu-xen-traditional instead");
+ b_info->device_model_version =
+ LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
+ } else {
+ LOGE(ERROR, "qemu-xen access error");
+ return ERROR_FAIL;
+ }
+ }
+ }
+ }
+
+ if (b_info->blkdev_start == NULL)
+ b_info->blkdev_start = libxl__strdup(NOGC, "xvda");
+
+ if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
+ if (!b_info->u.hvm.bios)
+ switch (b_info->device_model_version) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ b_info->u.hvm.bios = LIBXL_BIOS_TYPE_ROMBIOS; break;
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ b_info->u.hvm.bios = LIBXL_BIOS_TYPE_SEABIOS; break;
+ default:
+ LOG(ERROR, "unknown device model version");
+ return ERROR_INVAL;
+ }
+
+ /* Enforce BIOS<->Device Model version relationship */
+ switch (b_info->device_model_version) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ if (b_info->u.hvm.bios != LIBXL_BIOS_TYPE_ROMBIOS) {
+ LOG(ERROR, "qemu-xen-traditional requires bios=rombios.");
+ return ERROR_INVAL;
+ }
+ break;
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ if (b_info->u.hvm.bios == LIBXL_BIOS_TYPE_ROMBIOS) {
+ LOG(ERROR, "qemu-xen does not support bios=rombios.");
+ return ERROR_INVAL;
+ }
+ break;
+ default:abort();
+ }
+
+ /* Check HVM direct boot parameters, we should honour ->ramdisk and
+ * ->cmdline iff ->kernel is set.
+ */
+ if (!b_info->kernel && (b_info->ramdisk || b_info->cmdline)) {
+ LOG(ERROR, "direct boot parameters specified but kernel missing");
+ return ERROR_INVAL;
+ }
+ }
+
+ if (b_info->type == LIBXL_DOMAIN_TYPE_HVM &&
+ libxl_defbool_val(b_info->device_model_stubdomain)) {
+ if (!b_info->stubdomain_kernel) {
+ switch (b_info->device_model_version) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ b_info->stubdomain_kernel =
+ libxl__abs_path(NOGC, "ioemu-stubdom.gz", libxl__xenfirmwaredir_path());
+ break;
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ b_info->stubdomain_kernel =
+ libxl__abs_path(NOGC,
+ "qemu-stubdom-linux-kernel",
+ libxl__xenfirmwaredir_path());
+ break;
+ default:
+ abort();
+ }
+ }
+ if (!b_info->stubdomain_ramdisk) {
+ switch (b_info->device_model_version) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ break;
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ b_info->stubdomain_ramdisk =
+ libxl__abs_path(NOGC,
+ "qemu-stubdom-linux-rootfs",
+ libxl__xenfirmwaredir_path());
+ break;
+ default:
+ abort();
+ }
+ }
+ }
+
+ if (!b_info->max_vcpus)
+ b_info->max_vcpus = 1;
+ if (!b_info->avail_vcpus.size) {
+ if (libxl_cpu_bitmap_alloc(CTX, &b_info->avail_vcpus, 1)) {
+ LOG(ERROR, "unable to allocate avail_vcpus bitmap");
+ return ERROR_FAIL;
+ }
+ libxl_bitmap_set(&b_info->avail_vcpus, 0);
+ } else if (b_info->avail_vcpus.size > HVM_MAX_VCPUS) {
+ LOG(ERROR, "avail_vcpus bitmap contains too many VCPUS");
+ return ERROR_FAIL;
+ }
+
+ /* In libxl internals, we want to deal with vcpu_hard_affinity only! */
+ if (b_info->cpumap.size && !b_info->num_vcpu_hard_affinity) {
+ b_info->vcpu_hard_affinity = libxl__calloc(gc, b_info->max_vcpus,
+ sizeof(libxl_bitmap));
+ for (i = 0; i < b_info->max_vcpus; i++) {
+ if (libxl_cpu_bitmap_alloc(CTX, &b_info->vcpu_hard_affinity[i], 0)) {
+ LOG(ERROR, "failed to allocate vcpu hard affinity bitmap");
+ return ERROR_FAIL;
+ }
+ libxl_bitmap_copy(CTX, &b_info->vcpu_hard_affinity[i],
+ &b_info->cpumap);
+ }
+ b_info->num_vcpu_hard_affinity = b_info->max_vcpus;
+ }
+
+ libxl_defbool_setdefault(&b_info->numa_placement, true);
+
+ if (b_info->max_memkb == LIBXL_MEMKB_DEFAULT)
+ b_info->max_memkb = 32 * 1024;
+ if (b_info->target_memkb == LIBXL_MEMKB_DEFAULT)
+ b_info->target_memkb = b_info->max_memkb;
+
+ if (b_info->stubdomain_memkb == LIBXL_MEMKB_DEFAULT) {
+ if (libxl_defbool_val(b_info->device_model_stubdomain)) {
+ if (libxl__stubdomain_is_linux(b_info))
+ b_info->stubdomain_memkb = LIBXL_LINUX_STUBDOM_MEM * 1024;
+ else
+ b_info->stubdomain_memkb = 28 * 1024; // MiniOS
+ } else {
+ b_info->stubdomain_memkb = 0; // no stubdomain
+ }
+ }
+
+ libxl_defbool_setdefault(&b_info->claim_mode, false);
+
+ libxl_defbool_setdefault(&b_info->localtime, false);
+
+ libxl_defbool_setdefault(&b_info->disable_migrate, false);
+
+ for (i = 0 ; i < b_info->num_iomem; i++)
+ if (b_info->iomem[i].gfn == LIBXL_INVALID_GFN)
+ b_info->iomem[i].gfn = b_info->iomem[i].start;
+
+ if (!b_info->event_channels)
+ b_info->event_channels = 1023;
+
+ libxl__arch_domain_build_info_setdefault(gc, b_info);
+ libxl_defbool_setdefault(&b_info->dm_restrict, false);
+
+ if (b_info->iommu_memkb == LIBXL_MEMKB_DEFAULT)
+ /* Normally defaulted in libxl__domain_create_info_setdefault */
+ b_info->iommu_memkb = 0;
+
+ switch (b_info->type) {
+ case LIBXL_DOMAIN_TYPE_HVM:
+ if (b_info->shadow_memkb == LIBXL_MEMKB_DEFAULT)
+ /* Normally defaulted in libxl__domain_create_info_setdefault */
+ b_info->shadow_memkb = 0;
+ if (b_info->u.hvm.mmio_hole_memkb == LIBXL_MEMKB_DEFAULT)
+ b_info->u.hvm.mmio_hole_memkb = 0;
+
+ if (b_info->u.hvm.vga.kind == LIBXL_VGA_INTERFACE_TYPE_UNKNOWN) {
+ b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_CIRRUS;
+ }
+
+ if (!b_info->u.hvm.hdtype)
+ b_info->u.hvm.hdtype = LIBXL_HDTYPE_IDE;
+
+ switch (b_info->device_model_version) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ switch (b_info->u.hvm.vga.kind) {
+ case LIBXL_VGA_INTERFACE_TYPE_NONE:
+ if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+ b_info->video_memkb = 0;
+ break;
+ case LIBXL_VGA_INTERFACE_TYPE_QXL:
+ LOG(ERROR,"qemu upstream required for qxl vga");
+ return ERROR_INVAL;
+ break;
+ case LIBXL_VGA_INTERFACE_TYPE_STD:
+ if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+ b_info->video_memkb = 8 * 1024;
+ if (b_info->video_memkb < 8 * 1024) {
+ LOG(ERROR, "videoram must be at least 8 MB for STDVGA on QEMU_XEN_TRADITIONAL");
+ return ERROR_INVAL;
+ }
+ break;
+ case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
+ default:
+ if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+ b_info->video_memkb = 4 * 1024;
+ if (b_info->video_memkb != 4 * 1024)
+ LOG(WARN, "ignoring videoram other than 4 MB for CIRRUS on QEMU_XEN_TRADITIONAL");
+ break;
+ }
+ break;
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ default:
+ switch (b_info->u.hvm.vga.kind) {
+ case LIBXL_VGA_INTERFACE_TYPE_NONE:
+ if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+ b_info->video_memkb = 0;
+ break;
+ case LIBXL_VGA_INTERFACE_TYPE_QXL:
+ if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) {
+ b_info->video_memkb = (128 * 1024);
+ } else if (b_info->video_memkb < (128 * 1024)) {
+ LOG(ERROR,
+ "128 Mib videoram is the minimum for qxl default");
+ return ERROR_INVAL;
+ }
+ break;
+ case LIBXL_VGA_INTERFACE_TYPE_STD:
+ if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+ b_info->video_memkb = 16 * 1024;
+ if (b_info->video_memkb < 16 * 1024) {
+ LOG(ERROR, "videoram must be at least 16 MB for STDVGA on QEMU_XEN");
+ return ERROR_INVAL;
+ }
+ break;
+ case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
+ default:
+ if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+ b_info->video_memkb = 8 * 1024;
+ if (b_info->video_memkb < 8 * 1024) {
+ LOG(ERROR, "videoram must be at least 8 MB for CIRRUS on QEMU_XEN");
+ return ERROR_INVAL;
+ }
+ break;
+ }
+ break;
+ }
+
+ libxl_defbool_setdefault(&b_info->u.hvm.pae, true);
+ libxl_defbool_setdefault(&b_info->u.hvm.acpi, true);
+ libxl_defbool_setdefault(&b_info->u.hvm.acpi_s3, true);
+ libxl_defbool_setdefault(&b_info->u.hvm.acpi_s4, true);
+ libxl_defbool_setdefault(&b_info->u.hvm.acpi_laptop_slate, false);
+ libxl_defbool_setdefault(&b_info->u.hvm.nx, true);
+ libxl_defbool_setdefault(&b_info->u.hvm.viridian, false);
+ libxl_defbool_setdefault(&b_info->u.hvm.hpet, true);
+ libxl_defbool_setdefault(&b_info->u.hvm.vpt_align, true);
+ libxl_defbool_setdefault(&b_info->u.hvm.altp2m, false);
+ libxl_defbool_setdefault(&b_info->u.hvm.usb, false);
+ libxl_defbool_setdefault(&b_info->u.hvm.vkb_device, true);
+ libxl_defbool_setdefault(&b_info->u.hvm.xen_platform_pci, true);
+
+ libxl_defbool_setdefault(&b_info->u.hvm.spice.enable, false);
+ if (!libxl_defbool_val(b_info->u.hvm.spice.enable) &&
+ (b_info->u.hvm.spice.usbredirection > 0) ){
+ b_info->u.hvm.spice.usbredirection = 0;
+ LOG(WARN, "spice disabled, disabling usbredirection");
+ }
+
+ if (!b_info->u.hvm.usbversion &&
+ (b_info->u.hvm.spice.usbredirection > 0) )
+ b_info->u.hvm.usbversion = 2;
+
+ if ((b_info->u.hvm.usbversion || b_info->u.hvm.spice.usbredirection) &&
+ ( libxl_defbool_val(b_info->u.hvm.usb)
+ || b_info->u.hvm.usbdevice_list
+ || b_info->u.hvm.usbdevice) ){
+ LOG(ERROR,"usbversion and/or usbredirection cannot be "
+ "enabled with usb and/or usbdevice parameters.");
+ return ERROR_INVAL;
+ }
+
+ if (!b_info->u.hvm.boot)
+ b_info->u.hvm.boot = libxl__strdup(NOGC, "cda");
+
+ libxl_defbool_setdefault(&b_info->u.hvm.vnc.enable, true);
+ if (libxl_defbool_val(b_info->u.hvm.vnc.enable)) {
+ libxl_defbool_setdefault(&b_info->u.hvm.vnc.findunused, true);
+ if (!b_info->u.hvm.vnc.listen)
+ b_info->u.hvm.vnc.listen = libxl__strdup(NOGC, "127.0.0.1");
+ }
+
+ libxl_defbool_setdefault(&b_info->u.hvm.sdl.enable, false);
+ if (libxl_defbool_val(b_info->u.hvm.sdl.enable)) {
+ libxl_defbool_setdefault(&b_info->u.hvm.sdl.opengl, false);
+ }
+
+ if (libxl_defbool_val(b_info->u.hvm.spice.enable)) {
+ libxl_defbool_setdefault(&b_info->u.hvm.spice.disable_ticketing,
+ false);
+ libxl_defbool_setdefault(&b_info->u.hvm.spice.agent_mouse, true);
+ libxl_defbool_setdefault(&b_info->u.hvm.spice.vdagent, false);
+ libxl_defbool_setdefault(&b_info->u.hvm.spice.clipboard_sharing,
+ false);
+ }
+
+ libxl_defbool_setdefault(&b_info->u.hvm.nographic, false);
+
+ libxl_defbool_setdefault(&b_info->u.hvm.gfx_passthru, false);
+
+ libxl__rdm_setdefault(gc, b_info);
+ break;
+ case LIBXL_DOMAIN_TYPE_PV:
+ libxl_defbool_setdefault(&b_info->u.pv.e820_host, false);
+ if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+ b_info->video_memkb = 0;
+ if (b_info->shadow_memkb == LIBXL_MEMKB_DEFAULT)
+ /* Normally defaulted in libxl__domain_create_info_setdefault */
+ b_info->shadow_memkb = 0;
+ if (b_info->u.pv.slack_memkb == LIBXL_MEMKB_DEFAULT)
+ b_info->u.pv.slack_memkb = 0;
+ break;
+ case LIBXL_DOMAIN_TYPE_PVH:
+ libxl_defbool_setdefault(&b_info->u.pvh.pvshim, false);
+ if (libxl_defbool_val(b_info->u.pvh.pvshim)) {
+ if (!b_info->u.pvh.pvshim_path)
+ b_info->u.pvh.pvshim_path =
+ libxl__sprintf(NOGC, "%s/%s",
+ libxl__xenfirmwaredir_path(),
+ PVSHIM_BASENAME);
+ if (!b_info->u.pvh.pvshim_cmdline)
+ b_info->u.pvh.pvshim_cmdline =
+ libxl__strdup(NOGC, PVSHIM_CMDLINE);
+ }
+
+ break;
+ default:
+ LOG(ERROR, "invalid domain type %s in create info",
+ libxl_domain_type_to_string(b_info->type));
+ return ERROR_INVAL;
+ }
+
+ /* Configuration fields shared between PVH and HVM. */
+ if (b_info->type != LIBXL_DOMAIN_TYPE_PV) {
+ if (libxl__timer_mode_is_default(&b_info->timer_mode))
+ b_info->timer_mode = LIBXL_TIMER_MODE_NO_DELAY_FOR_MISSED_TICKS;
+
+ libxl_defbool_setdefault(&b_info->apic, true);
+ libxl_defbool_setdefault(&b_info->nested_hvm, false);
+ }
+
+ return 0;
+}
+
+static void init_console_info(libxl__gc *gc,
+ libxl__device_console *console,
+ int dev_num)
+{
+ libxl__device_console_init(console);
+ console->devid = dev_num;
+ console->consback = LIBXL__CONSOLE_BACKEND_XENCONSOLED;
+ console->output = libxl__strdup(NOGC, "pty");
+ /* console->{name,connection,path} are NULL on normal consoles.
+ Only 'channels' when mapped to consoles have a string name. */
+}
+
+int libxl__domain_build(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ uint32_t domid,
+ libxl__domain_build_state *state)
+{
+ libxl_domain_build_info *const info = &d_config->b_info;
+ char **vments = NULL, **localents = NULL;
+ struct timeval start_time;
+ int i, ret;
+
+ ret = libxl__build_pre(gc, domid, d_config, state);
+ if (ret)
+ goto out;
+
+ gettimeofday(&start_time, NULL);
+
+ switch (info->type) {
+ case LIBXL_DOMAIN_TYPE_HVM:
+ ret = libxl__build_hvm(gc, domid, d_config, state);
+ if (ret)
+ goto out;
+
+ vments = libxl__calloc(gc, 7, sizeof(char *));
+ vments[0] = "rtc/timeoffset";
+ vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : "";
+ vments[2] = "image/ostype";
+ vments[3] = "hvm";
+ vments[4] = "start_time";
+ vments[5] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
+
+ localents = libxl__calloc(gc, 13, sizeof(char *));
+ i = 0;
+ localents[i++] = "platform/acpi";
+ localents[i++] = libxl__acpi_defbool_val(info) ? "1" : "0";
+ localents[i++] = "platform/acpi_s3";
+ localents[i++] = libxl_defbool_val(info->u.hvm.acpi_s3) ? "1" : "0";
+ localents[i++] = "platform/acpi_s4";
+ localents[i++] = libxl_defbool_val(info->u.hvm.acpi_s4) ? "1" : "0";
+ localents[i++] = "platform/acpi_laptop_slate";
+ localents[i++] = libxl_defbool_val(info->u.hvm.acpi_laptop_slate) ? "1" : "0";
+ if (info->u.hvm.mmio_hole_memkb) {
+ uint64_t max_ram_below_4g =
+ (1ULL << 32) - (info->u.hvm.mmio_hole_memkb << 10);
+
+ if (max_ram_below_4g <= HVM_BELOW_4G_MMIO_START) {
+ localents[i++] = "platform/mmio_hole_size";
+ localents[i++] =
+ GCSPRINTF("%"PRIu64,
+ info->u.hvm.mmio_hole_memkb << 10);
+ }
+ }
+ localents[i++] = "platform/device-model";
+ localents[i++] = (char *)libxl_device_model_version_to_string(info->device_model_version);
+
+ break;
+ case LIBXL_DOMAIN_TYPE_PV:
+ ret = libxl__build_pv(gc, domid, d_config, state);
+ if (ret)
+ goto out;
+
+ vments = libxl__calloc(gc, 11, sizeof(char *));
+ i = 0;
+ vments[i++] = "image/ostype";
+ vments[i++] = "linux";
+ vments[i++] = "image/kernel";
+ vments[i++] = (char *) state->pv_kernel.path;
+ vments[i++] = "start_time";
+ vments[i++] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
+ if (state->pv_ramdisk.path) {
+ vments[i++] = "image/ramdisk";
+ vments[i++] = (char *) state->pv_ramdisk.path;
+ }
+ if (state->pv_cmdline) {
+ vments[i++] = "image/cmdline";
+ vments[i++] = (char *) state->pv_cmdline;
+ }
+
+ break;
+ case LIBXL_DOMAIN_TYPE_PVH:
+ state->shim_path = info->u.pvh.pvshim_path;
+ state->shim_cmdline = GCSPRINTF("%s%s%s",
+ info->u.pvh.pvshim_cmdline,
+ info->u.pvh.pvshim_extra ? " " : "",
+ info->u.pvh.pvshim_extra ? info->u.pvh.pvshim_extra : "");
+
+ ret = libxl__build_hvm(gc, domid, d_config, state);
+ if (ret)
+ goto out;
+
+ vments = libxl__calloc(gc, 3, sizeof(char *));
+ vments[0] = "start_time";
+ vments[1] = GCSPRINTF("%"PRIu64".%02ld",
+ (uint64_t)start_time.tv_sec,
+ (long)start_time.tv_usec/10000);
+
+ break;
+ default:
+ ret = ERROR_INVAL;
+ goto out;
+ }
+ ret = libxl__build_post(gc, domid, info, state, vments, localents);
+out:
+ return ret;
+}
+
+int libxl__domain_make(libxl__gc *gc, libxl_domain_config *d_config,
+ libxl__domain_build_state *state,
+ uint32_t *domid, bool soft_reset)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ int ret, rc, nb_vm;
+ const char *dom_type;
+ char *uuid_string;
+ char *dom_path, *vm_path, *libxl_path;
+ struct xs_permissions roperm[2];
+ struct xs_permissions rwperm[1];
+ struct xs_permissions noperm[1];
+ xs_transaction_t t = 0;
+ libxl_vminfo *vm_list;
+
+ /* convenience aliases */
+ libxl_domain_create_info *info = &d_config->c_info;
+ libxl_domain_build_info *b_info = &d_config->b_info;
+
+ assert(soft_reset || *domid == INVALID_DOMID);
+
+ uuid_string = libxl__uuid2string(gc, info->uuid);
+ if (!uuid_string) {
+ rc = ERROR_NOMEM;
+ goto out;
+ }
+
+ if (!soft_reset) {
+ struct xen_domctl_createdomain create = {
+ .ssidref = info->ssidref,
+ .max_vcpus = b_info->max_vcpus,
+ .max_evtchn_port = b_info->event_channels,
+ .max_grant_frames = b_info->max_grant_frames,
+ .max_maptrack_frames = b_info->max_maptrack_frames,
+ };
+
+ if (info->type != LIBXL_DOMAIN_TYPE_PV) {
+ create.flags |= XEN_DOMCTL_CDF_hvm;
+ create.flags |=
+ libxl_defbool_val(info->hap) ? XEN_DOMCTL_CDF_hap : 0;
+ create.flags |=
+ libxl_defbool_val(info->oos) ? 0 : XEN_DOMCTL_CDF_oos_off;
+ }
+
+ assert(info->passthrough != LIBXL_PASSTHROUGH_DEFAULT);
+ LOG(DETAIL, "passthrough: %s",
+ libxl_passthrough_to_string(info->passthrough));
+
+ if (info->passthrough != LIBXL_PASSTHROUGH_DISABLED)
+ create.flags |= XEN_DOMCTL_CDF_iommu;
+
+ if (info->passthrough == LIBXL_PASSTHROUGH_SYNC_PT)
+ create.iommu_opts |= XEN_DOMCTL_IOMMU_no_sharept;
+
+ /* Ultimately, handle is an array of 16 uint8_t, same as uuid */
+ libxl_uuid_copy(ctx, (libxl_uuid *)&create.handle, &info->uuid);
+
+ ret = libxl__arch_domain_prepare_config(gc, d_config, &create);
+ if (ret < 0) {
+ LOGED(ERROR, *domid, "fail to get domain config");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ for (;;) {
+ uint32_t local_domid;
+ bool recent;
+
+ if (info->domid == RANDOM_DOMID) {
+ uint16_t v;
+
+ ret = libxl__random_bytes(gc, (void *)&v, sizeof(v));
+ if (ret < 0)
+ break;
+
+ v &= DOMID_MASK;
+ if (!libxl_domid_valid_guest(v))
+ continue;
+
+ local_domid = v;
+ } else {
+ local_domid = info->domid; /* May not be valid */
+ }
+
+ ret = xc_domain_create(ctx->xch, &local_domid, &create);
+ if (ret < 0) {
+ /*
+ * If we generated a random domid and creation failed
+ * because that domid already exists then simply try
+ * again.
+ */
+ if (errno == EEXIST && info->domid == RANDOM_DOMID)
+ continue;
+
+ LOGED(ERROR, local_domid, "domain creation fail");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /* A new domain now exists */
+ *domid = local_domid;
+
+ rc = libxl__is_domid_recent(gc, local_domid, &recent);
+ if (rc)
+ goto out;
+
+ /* The domid is not recent, so we're done */
+ if (!recent)
+ break;
+
+ /*
+ * If the domid was specified then there's no point in
+ * trying again.
+ */
+ if (libxl_domid_valid_guest(info->domid)) {
+ LOGED(ERROR, local_domid, "domain id recently used");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /*
+ * The domain is recent and so cannot be used. Clear domid
+ * here since, if xc_domain_destroy() fails below there is
+ * little point calling it again in the error path.
+ */
+ *domid = INVALID_DOMID;
+
+ ret = xc_domain_destroy(ctx->xch, local_domid);
+ if (ret < 0) {
+ LOGED(ERROR, local_domid, "domain destroy fail");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /* The domain was successfully destroyed, so we can try again */
+ }
+
+ rc = libxl__arch_domain_save_config(gc, d_config, state, &create);
+ if (rc < 0)
+ goto out;
+ }
+
+ /*
+ * If soft_reset is set the the domid will have been valid on entry.
+ * If it was not set then xc_domain_create() should have assigned a
+ * valid value. Either way, if we reach this point, domid should be
+ * valid.
+ */
+ assert(libxl_domid_valid_guest(*domid));
+
+ ret = xc_cpupool_movedomain(ctx->xch, info->poolid, *domid);
+ if (ret < 0) {
+ LOGED(ERROR, *domid, "domain move fail");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ dom_path = libxl__xs_get_dompath(gc, *domid);
+ if (!dom_path) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ vm_path = GCSPRINTF("/vm/%s", uuid_string);
+ if (!vm_path) {
+ LOGD(ERROR, *domid, "cannot allocate create paths");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ libxl_path = libxl__xs_libxl_path(gc, *domid);
+ if (!libxl_path) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ noperm[0].id = 0;
+ noperm[0].perms = XS_PERM_NONE;
+
+ roperm[0].id = 0;
+ roperm[0].perms = XS_PERM_NONE;
+ roperm[1].id = *domid;
+ roperm[1].perms = XS_PERM_READ;
+
+ rwperm[0].id = *domid;
+ rwperm[0].perms = XS_PERM_NONE;
+
+retry_transaction:
+ t = xs_transaction_start(ctx->xsh);
+
+ xs_rm(ctx->xsh, t, dom_path);
+ libxl__xs_mknod(gc, t, dom_path, roperm, ARRAY_SIZE(roperm));
+
+ xs_rm(ctx->xsh, t, vm_path);
+ libxl__xs_mknod(gc, t, vm_path, roperm, ARRAY_SIZE(roperm));
+
+ xs_rm(ctx->xsh, t, libxl_path);
+ libxl__xs_mknod(gc, t, libxl_path, noperm, ARRAY_SIZE(noperm));
+ libxl__xs_mknod(gc, t, GCSPRINTF("%s/device", libxl_path),
+ noperm, ARRAY_SIZE(noperm));
+
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
+ rc = libxl__domain_rename(gc, *domid, 0, info->name, t);
+ if (rc)
+ goto out;
+
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/cpu", dom_path),
+ roperm, ARRAY_SIZE(roperm));
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/memory", dom_path),
+ roperm, ARRAY_SIZE(roperm));
+
+ if (!libxl_defbool_val(info->xend_suspend_evtchn_compat)) {
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/device", dom_path),
+ roperm, ARRAY_SIZE(roperm));
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/device/suspend/event-channel",
+ dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+ } else {
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/device", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+ }
+
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/control", dom_path),
+ roperm, ARRAY_SIZE(roperm));
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM)
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/hvmloader", dom_path),
+ roperm, ARRAY_SIZE(roperm));
+
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/control/shutdown", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/control/feature-poweroff", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/control/feature-reboot", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/control/feature-suspend", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/control/feature-s3", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/control/feature-s4", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+ }
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/control/sysrq", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/data", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/drivers", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/feature", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/attr", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+ libxl__xs_mknod(gc, t,
+ GCSPRINTF("%s/error", dom_path),
+ rwperm, ARRAY_SIZE(rwperm));
+
+ if (libxl_defbool_val(info->driver_domain)) {
+ /*
+ * Create a local "libxl" directory for each guest, since we might want
+ * to use libxl from inside the guest
+ */
+ libxl__xs_mknod(gc, t, GCSPRINTF("%s/libxl", dom_path), rwperm,
+ ARRAY_SIZE(rwperm));
+ /*
+ * Create a local "device-model" directory for each guest, since we
+ * might want to use Qemu from inside the guest
+ */
+ libxl__xs_mknod(gc, t, GCSPRINTF("%s/device-model", dom_path), rwperm,
+ ARRAY_SIZE(rwperm));
+ }
+
+ vm_list = libxl_list_vm(ctx, &nb_vm);
+ if (!vm_list) {
+ LOGD(ERROR, *domid, "cannot get number of running guests");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ libxl_vminfo_list_free(vm_list, nb_vm);
+
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/name", vm_path), info->name, strlen(info->name));
+
+ libxl__xs_writev(gc, t, dom_path, info->xsdata);
+ libxl__xs_writev(gc, t, GCSPRINTF("%s/platform", dom_path), info->platformdata);
+
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/control/platform-feature-multiprocessor-suspend", dom_path), "1", 1);
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/control/platform-feature-xs_reset_watches", dom_path), "1", 1);
+
+ dom_type = libxl_domain_type_to_string(info->type);
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/type", libxl_path), dom_type,
+ strlen(dom_type));
+
+ if (!xs_transaction_end(ctx->xsh, t, 0)) {
+ if (errno == EAGAIN) {
+ t = 0;
+ goto retry_transaction;
+ }
+ LOGED(ERROR, *domid, "domain creation ""xenstore transaction commit failed");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ t = 0;
+
+ rc = 0;
+ out:
+ if (t) xs_transaction_end(ctx->xsh, t, 1);
+ return rc;
+}
+
+static int store_libxl_entry(libxl__gc *gc, uint32_t domid,
+ libxl_domain_build_info *b_info)
+{
+ char *path = NULL;
+
+ path = libxl__xs_libxl_path(gc, domid);
+ path = GCSPRINTF("%s/dm-version", path);
+ return libxl__xs_printf(gc, XBT_NULL, path, "%s",
+ libxl_device_model_version_to_string(b_info->device_model_version));
+}
+
+void libxl__domain_build_state_init(libxl__domain_build_state *state)
+{
+ state->dm_monitor_fd = -1;
+}
+
+void libxl__domain_build_state_dispose(libxl__domain_build_state *state)
+{
+ libxl__file_reference_unmap(&state->pv_kernel);
+ libxl__file_reference_unmap(&state->pv_ramdisk);
+ if (state->dm_monitor_fd >= 0) {
+ close(state->dm_monitor_fd);
+ state->dm_monitor_fd = -1;
+ }
+}
+
+/*----- main domain creation -----*/
+
+/* We have a linear control flow; only one event callback is
+ * outstanding at any time. Each initiation and callback function
+ * arranges for the next to be called, as the very last thing it
+ * does. (If that particular sub-operation is not needed, a
+ * function will call the next event callback directly.)
+ */
+
+/* Event callbacks, in this order: */
+static void domcreate_bootloader_console_available(libxl__egc *egc,
+ libxl__bootloader_state *bl);
+static void domcreate_console_available(libxl__egc *egc,
+ libxl__domain_create_state *dcs);
+
+static void domcreate_bootloader_done(libxl__egc *egc,
+ libxl__bootloader_state *bl,
+ int rc);
+static void libxl__colo_restore_setup_done(libxl__egc *egc,
+ libxl__colo_restore_state *crs,
+ int rc);
+static void domcreate_stream_done(libxl__egc *egc,
+ libxl__stream_read_state *srs,
+ int ret);
+static void domcreate_rebuild_done(libxl__egc *egc,
+ libxl__domain_create_state *dcs,
+ int ret);
+static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *aodevs,
+ int ret);
+static void domcreate_devmodel_started(libxl__egc *egc,
+ libxl__dm_spawn_state *dmss,
+ int rc);
+static void domcreate_attach_devices(libxl__egc *egc,
+ libxl__multidev *multidev,
+ int ret);
+static void console_xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa,
+ int rc, const char *p);
+
+/* Our own function to clean up and call the user's callback.
+ * The final call in the sequence. */
+static void domcreate_complete(libxl__egc *egc,
+ libxl__domain_create_state *dcs,
+ int rc);
+
+/* If creation is not successful, this callback will be executed
+ * when domain destruction is finished */
+static void domcreate_destruction_cb(libxl__egc *egc,
+ libxl__domain_destroy_state *dds,
+ int rc);
+
+static bool ok_to_default_memkb_in_create(libxl__gc *gc)
+{
+ /*
+ * This is a fudge. We are trying to find whether the caller
+ * calls the old version of libxl_domain_need_memory. If they do
+ * then, because it only gets the b_info, and because it can't
+ * update the b_info (because it's const), it will base its
+ * calculations on defaulting shadow_memkb and iommu_memkb to 0
+ * In that case we probably shouldn't default them differently
+ * during libxl_domain_create.
+ *
+ * The result is that the behaviour with old callers is the same
+ * as in 4.13: no additional memory is allocated for shadow and
+ * iommu (unless the caller set shadow_memkb, eg from a call to
+ * libxl_get_required_shadow_memory).
+ */
+ return !CTX->libxl_domain_need_memory_0x041200_called ||
+ CTX->libxl_domain_need_memory_called;
+ /*
+ * Treat mixed callers as new callers. Presumably they know what
+ * they are doing.
+ */
+}
+
+static unsigned long libxl__get_required_iommu_memory(unsigned long maxmem_kb)
+{
+ unsigned long iommu_pages = 0, mem_pages = maxmem_kb / 4;
+ unsigned int level;
+
+ /* Assume a 4 level page table with 512 entries per level */
+ for (level = 0; level < 4; level++)
+ {
+ mem_pages = DIV_ROUNDUP(mem_pages, 512);
+ iommu_pages += mem_pages;
+ }
+
+ return iommu_pages * 4;
+}
+
+int libxl__domain_config_setdefault(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ uint32_t domid /* for logging, only */)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ int ret;
+ bool pod_enabled = false;
+ libxl_domain_create_info *c_info = &d_config->c_info;
+
+ libxl_physinfo physinfo;
+ ret = libxl_get_physinfo(CTX, &physinfo);
+ if (ret) goto error_out;
+
+ if (d_config->c_info.ssid_label) {
+ char *s = d_config->c_info.ssid_label;
+ ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
+ &d_config->c_info.ssidref);
+ if (ret) {
+ if (errno == ENOSYS) {
+ LOGD(WARN, domid, "XSM Disabled: init_seclabel not supported");
+ ret = 0;
+ } else {
+ LOGD(ERROR, domid, "Invalid init_seclabel: %s", s);
+ goto error_out;
+ }
+ }
+ }
+
+ if (d_config->b_info.exec_ssid_label) {
+ char *s = d_config->b_info.exec_ssid_label;
+ ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
+ &d_config->b_info.exec_ssidref);
+ if (ret) {
+ if (errno == ENOSYS) {
+ LOGD(WARN, domid, "XSM Disabled: seclabel not supported");
+ ret = 0;
+ } else {
+ LOGD(ERROR, domid, "Invalid seclabel: %s", s);
+ goto error_out;
+ }
+ }
+ }
+
+ if (d_config->b_info.device_model_ssid_label) {
+ char *s = d_config->b_info.device_model_ssid_label;
+ ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
+ &d_config->b_info.device_model_ssidref);
+ if (ret) {
+ if (errno == ENOSYS) {
+ LOGD(WARN, domid,
+ "XSM Disabled: device_model_stubdomain_seclabel not supported");
+ ret = 0;
+ } else {
+ LOGD(ERROR, domid, "Invalid device_model_stubdomain_seclabel: %s", s);
+ goto error_out;
+ }
+ }
+ }
+
+ if (d_config->c_info.pool_name) {
+ d_config->c_info.poolid = -1;
+ libxl_cpupool_qualifier_to_cpupoolid(ctx, d_config->c_info.pool_name,
+ &d_config->c_info.poolid,
+ NULL);
+ }
+ if (!libxl_cpupoolid_is_valid(ctx, d_config->c_info.poolid)) {
+ LOGD(ERROR, domid, "Illegal pool specified: %s",
+ d_config->c_info.pool_name);
+ ret = ERROR_INVAL;
+ goto error_out;
+ }
+
+ ret = libxl__domain_create_info_setdefault(gc, &d_config->c_info,
+ &physinfo);
+ if (ret) {
+ LOGD(ERROR, domid, "Unable to set domain create info defaults");
+ goto error_out;
+ }
+
+ bool need_pt = d_config->num_pcidevs || d_config->num_dtdevs;
+ if (c_info->passthrough == LIBXL_PASSTHROUGH_DEFAULT) {
+ c_info->passthrough = need_pt
+ ? LIBXL_PASSTHROUGH_ENABLED : LIBXL_PASSTHROUGH_DISABLED;
+ }
+
+ bool iommu_enabled = physinfo.cap_hvm_directio;
+ if (c_info->passthrough != LIBXL_PASSTHROUGH_DISABLED && !iommu_enabled) {
+ LOGD(ERROR, domid,
+ "passthrough not supported on this platform\n");
+ ret = ERROR_INVAL;
+ goto error_out;
+ }
+
+ if (c_info->passthrough == LIBXL_PASSTHROUGH_DISABLED && need_pt) {
+ LOGD(ERROR, domid,
+ "passthrough disabled but devices are specified");
+ ret = ERROR_INVAL;
+ goto error_out;
+ }
+
+ ret = libxl__arch_passthrough_mode_setdefault(gc,domid,d_config,&physinfo);
+ if (ret) goto error_out;
+
+ /* An explicit setting should now have been chosen */
+ assert(c_info->passthrough != LIBXL_PASSTHROUGH_DEFAULT);
+ assert(c_info->passthrough != LIBXL_PASSTHROUGH_ENABLED);
+
+ /* If target_memkb is smaller than max_memkb, the subsequent call
+ * to libxc when building HVM domain will enable PoD mode.
+ */
+ pod_enabled = (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV) &&
+ (d_config->b_info.target_memkb < d_config->b_info.max_memkb);
+
+ /* We cannot have PoD and PCI device assignment at the same time
+ * for HVM guest. It was reported that IOMMU cannot work with PoD
+ * enabled because it needs to populated entire page table for
+ * guest. To stay on the safe side, we disable PCI device
+ * assignment when PoD is enabled.
+ */
+ if (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
+ d_config->num_pcidevs && pod_enabled) {
+ ret = ERROR_INVAL;
+ LOGD(ERROR, domid,
+ "PCI device assignment for HVM guest failed due to PoD enabled");
+ goto error_out;
+ }
+
+ /* Disallow PoD and vNUMA to be enabled at the same time because PoD
+ * pool is not vNUMA-aware yet.
+ */
+ if (pod_enabled && d_config->b_info.num_vnuma_nodes) {
+ ret = ERROR_INVAL;
+ LOGD(ERROR, domid, "Cannot enable PoD and vNUMA at the same time");
+ goto error_out;
+ }
+
+ /* PV vNUMA is not yet supported because there is an issue with
+ * cpuid handling.
+ */
+ if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_PV &&
+ d_config->b_info.num_vnuma_nodes) {
+ ret = ERROR_INVAL;
+ LOGD(ERROR, domid, "PV vNUMA is not yet supported");
+ goto error_out;
+ }
+
+ if (d_config->b_info.shadow_memkb == LIBXL_MEMKB_DEFAULT
+ && ok_to_default_memkb_in_create(gc))
+ d_config->b_info.shadow_memkb =
+ libxl_get_required_shadow_memory(d_config->b_info.max_memkb,
+ d_config->b_info.max_vcpus);
+
+ /* No IOMMU reservation is needed if passthrough mode is not 'sync_pt' */
+ if (d_config->b_info.iommu_memkb == LIBXL_MEMKB_DEFAULT
+ && ok_to_default_memkb_in_create(gc))
+ d_config->b_info.iommu_memkb =
+ (d_config->c_info.passthrough == LIBXL_PASSTHROUGH_SYNC_PT)
+ ? libxl__get_required_iommu_memory(d_config->b_info.max_memkb)
+ : 0;
+
+ ret = libxl__domain_build_info_setdefault(gc, &d_config->b_info);
+ if (ret) {
+ LOGD(ERROR, domid, "Unable to set domain build info defaults");
+ goto error_out;
+ }
+
+ if (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
+ (libxl_defbool_val(d_config->b_info.nested_hvm) &&
+ ((d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM &&
+ libxl_defbool_val(d_config->b_info.u.hvm.altp2m)) ||
+ (d_config->b_info.altp2m != LIBXL_ALTP2M_MODE_DISABLED)))) {
+ ret = ERROR_INVAL;
+ LOGD(ERROR, domid, "nestedhvm and altp2mhvm cannot be used together");
+ goto error_out;
+ }
+
+ if (((d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM &&
+ libxl_defbool_val(d_config->b_info.u.hvm.altp2m)) ||
+ (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
+ d_config->b_info.altp2m != LIBXL_ALTP2M_MODE_DISABLED)) &&
+ pod_enabled) {
+ ret = ERROR_INVAL;
+ LOGD(ERROR, domid, "Cannot enable PoD and ALTP2M at the same time");
+ goto error_out;
+ }
+
+ ret = 0;
+ error_out:
+ return ret;
+}
+
+static void initiate_domain_create(libxl__egc *egc,
+ libxl__domain_create_state *dcs)
+{
+ STATE_AO_GC(dcs->ao);
+ uint32_t domid;
+ int i, ret;
+
+ /* convenience aliases */
+ libxl_domain_config *const d_config = dcs->guest_config;
+ libxl__domain_build_state *dbs = &dcs->build_state;
+
+ libxl__xswait_init(&dcs->console_xswait);
+
+ domid = dcs->domid;
+ libxl__domain_build_state_init(dbs);
+ dbs->restore = dcs->restore_fd >= 0;
+
+ ret = libxl__domain_config_setdefault(gc,d_config,domid);
+ if (ret) goto error_out;
+
+ ret = libxl__domain_make(gc, d_config, dbs, &domid, dcs->soft_reset);
+ if (ret) {
+ LOGD(ERROR, domid, "cannot make domain: %d", ret);
+ dcs->guest_domid = domid;
+ ret = ERROR_FAIL;
+ goto error_out;
+ }
+
+ dcs->guest_domid = domid;
+ dcs->sdss.dm.guest_domid = 0; /* means we haven't spawned */
+
+ /* post-4.13 todo: move these next bits of defaulting to
+ * libxl__domain_config_setdefault */
+
+ /*
+ * Set the dm version quite early so that libxl doesn't have to pass the
+ * build info around just to know if the domain has a device model or not.
+ */
+ store_libxl_entry(gc, domid, &d_config->b_info);
+
+ for (i = 0; i < d_config->num_disks; i++) {
+ ret = libxl__disk_devtype.set_default(gc, domid, &d_config->disks[i],
+ false);
+ if (ret) {
+ LOGD(ERROR, domid, "Unable to set disk defaults for disk %d", i);
+ goto error_out;
+ }
+ }
+
+ dcs->bl.ao = ao;
+ libxl_device_disk *bootdisk =
+ d_config->num_disks > 0 ? &d_config->disks[0] : NULL;
+
+ /*
+ * The devid has to be set before launching the device model. For the
+ * hotplug case this is done in libxl_device_nic_add but on domain
+ * creation this is called too late.
+ * Make two runs over configured NICs in order to avoid duplicate IDs
+ * in case the caller partially assigned IDs.
+ */
+ ret = libxl__device_nic_set_devids(gc, d_config, domid);
+ if (ret)
+ goto error_out;
+
+ if (dbs->restore || dcs->soft_reset) {
+ LOGD(DEBUG, domid, "restoring, not running bootloader");
+ domcreate_bootloader_done(egc, &dcs->bl, 0);
+ } else {
+ LOGD(DEBUG, domid, "running bootloader");
+ dcs->bl.callback = domcreate_bootloader_done;
+ dcs->bl.console_available = domcreate_bootloader_console_available;
+ dcs->bl.info = &d_config->b_info;
+ dcs->bl.disk = bootdisk;
+ dcs->bl.domid = dcs->guest_domid;
+
+ dcs->bl.kernel = &dbs->pv_kernel;
+ dcs->bl.ramdisk = &dbs->pv_ramdisk;
+
+ libxl__bootloader_run(egc, &dcs->bl);
+ }
+ return;
+
+error_out:
+ assert(ret);
+ domcreate_complete(egc, dcs, ret);
+}
+
+static void domcreate_bootloader_console_available(libxl__egc *egc,
+ libxl__bootloader_state *bl)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(bl, *dcs, bl);
+ STATE_AO_GC(bl->ao);
+ domcreate_console_available(egc, dcs);
+}
+
+static void domcreate_console_available(libxl__egc *egc,
+ libxl__domain_create_state *dcs) {
+ libxl__ao_progress_report(egc, dcs->ao, &dcs->aop_console_how,
+ NEW_EVENT(egc, DOMAIN_CREATE_CONSOLE_AVAILABLE,
+ dcs->guest_domid,
+ dcs->aop_console_how.for_event));
+}
+
+static void domcreate_bootloader_done(libxl__egc *egc,
+ libxl__bootloader_state *bl,
+ int rc)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(bl, *dcs, bl);
+ STATE_AO_GC(bl->ao);
+
+ /* convenience aliases */
+ const uint32_t domid = dcs->guest_domid;
+ libxl_domain_config *const d_config = dcs->guest_config;
+ const int restore_fd = dcs->restore_fd;
+ libxl__domain_build_state *const state = &dcs->build_state;
+ const int checkpointed_stream = dcs->restore_params.checkpointed_stream;
+ libxl__colo_restore_state *const crs = &dcs->crs;
+ libxl_domain_build_info *const info = &d_config->b_info;
+ libxl__srm_restore_autogen_callbacks *const callbacks =
+ &dcs->srs.shs.callbacks.restore.a;
+
+ if (rc) {
+ domcreate_rebuild_done(egc, dcs, rc);
+ return;
+ }
+
+ /* consume bootloader outputs. state->pv_{kernel,ramdisk} have
+ * been initialised by the bootloader already.
+ */
+ state->pv_cmdline = bl->cmdline;
+
+ /* We might be going to call libxl__spawn_local_dm, or _spawn_stub_dm.
+ * Fill in any field required by either, including both relevant
+ * callbacks (_spawn_stub_dm will overwrite our trespass if needed). */
+ dcs->sdss.dm.spawn.ao = ao;
+ dcs->sdss.dm.guest_config = dcs->guest_config;
+ dcs->sdss.dm.build_state = &dcs->build_state;
+ dcs->sdss.dm.callback = domcreate_devmodel_started;
+ dcs->sdss.callback = domcreate_devmodel_started;
+
+ if (restore_fd < 0 && !dcs->soft_reset) {
+ rc = libxl__domain_build(gc, d_config, domid, state);
+ domcreate_rebuild_done(egc, dcs, rc);
+ return;
+ }
+
+ /* Prepare environment for domcreate_stream_done */
+ dcs->srs.dcs = dcs;
+
+ /* Restore */
+ callbacks->static_data_done = libxl__srm_callout_callback_static_data_done;
+ callbacks->restore_results = libxl__srm_callout_callback_restore_results;
+
+ /* COLO only supports HVM now because it does not work very
+ * well with pv drivers:
+ * 1. We need to resume vm in the slow path. In this case we
+ * need to disconnect/reconnect backend and frontend. It
+ * will take too much time and the performance is very slow.
+ * 2. PV disk cannot reuse block replication that is implemented
+ * in QEMU.
+ */
+ if (info->type != LIBXL_DOMAIN_TYPE_HVM &&
+ checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) {
+ LOGD(ERROR, domid, "COLO only supports HVM, unable to restore domain");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__build_pre(gc, domid, d_config, state);
+ if (rc)
+ goto out;
+
+ dcs->srs.ao = ao;
+ dcs->srs.fd = restore_fd;
+ dcs->srs.legacy = (dcs->restore_params.stream_version == 1);
+ dcs->srs.back_channel = false;
+ dcs->srs.completion_callback = domcreate_stream_done;
+
+ if (restore_fd >= 0) {
+ switch (checkpointed_stream) {
+ case LIBXL_CHECKPOINTED_STREAM_COLO:
+ /* colo restore setup */
+ crs->ao = ao;
+ crs->domid = domid;
+ crs->send_back_fd = dcs->send_back_fd;
+ crs->recv_fd = restore_fd;
+ crs->hvm = (info->type != LIBXL_DOMAIN_TYPE_PV);
+ crs->callback = libxl__colo_restore_setup_done;
+ libxl__colo_restore_setup(egc, crs);
+ break;
+ case LIBXL_CHECKPOINTED_STREAM_REMUS:
+ libxl__remus_restore_setup(egc, dcs);
+ /* fall through */
+ case LIBXL_CHECKPOINTED_STREAM_NONE:
+ libxl__stream_read_start(egc, &dcs->srs);
+ }
+ return;
+ }
+
+ out:
+ domcreate_stream_done(egc, &dcs->srs, rc);
+}
+
+static void libxl__colo_restore_setup_done(libxl__egc *egc,
+ libxl__colo_restore_state *crs,
+ int rc)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, dcs->guest_domid, "colo restore setup fails: %d", rc);
+ domcreate_stream_done(egc, &dcs->srs, rc);
+ return;
+ }
+
+ libxl__stream_read_start(egc, &dcs->srs);
+}
+
+int libxl__srm_callout_callback_static_data_done(unsigned int missing,
+ void *user)
+{
+ libxl__save_helper_state *shs = user;
+ libxl__domain_create_state *dcs = shs->caller_state;
+ STATE_AO_GC(dcs->ao);
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+
+ libxl_domain_config *d_config = dcs->guest_config;
+ libxl_domain_build_info *info = &d_config->b_info;
+
+ /*
+ * CPUID/MSR information is not present in pre Xen-4.14 streams.
+ *
+ * Libxl used to always regenerate the CPUID policy from first principles
+ * on migrate. Continue to do so for backwards compatibility when the
+ * stream doesn't contain any CPUID data.
+ */
+ if (missing & XGR_SDD_MISSING_CPUID)
+ libxl__cpuid_legacy(ctx, dcs->guest_domid, true, info);
+
+ return 0;
+}
+
+void libxl__srm_callout_callback_restore_results(xen_pfn_t store_mfn,
+ xen_pfn_t console_mfn, void *user)
+{
+ libxl__save_helper_state *shs = user;
+ libxl__domain_create_state *dcs = shs->caller_state;
+ STATE_AO_GC(dcs->ao);
+ libxl__domain_build_state *const state = &dcs->build_state;
+
+ state->store_mfn = store_mfn;
+ state->console_mfn = console_mfn;
+ shs->need_results = 0;
+}
+
+static void domcreate_stream_done(libxl__egc *egc,
+ libxl__stream_read_state *srs,
+ int ret)
+{
+ /* NB perhaps only srs->dcs is valid; eg in the case of an
+ * early branch to domcreate_bootloader_done's `out' block */
+ libxl__domain_create_state *dcs = srs->dcs;
+ STATE_AO_GC(dcs->ao);
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char **vments = NULL, **localents = NULL;
+ struct timeval start_time;
+ int i, esave;
+
+ /* convenience aliases */
+ const uint32_t domid = dcs->guest_domid;
+ libxl_domain_config *const d_config = dcs->guest_config;
+ libxl_domain_build_info *const info = &d_config->b_info;
+ libxl__domain_build_state *const state = &dcs->build_state;
+ const int fd = dcs->restore_fd;
+
+ if (ret)
+ goto out;
+
+ gettimeofday(&start_time, NULL);
+
+ switch (info->type) {
+ case LIBXL_DOMAIN_TYPE_HVM:
+ vments = libxl__calloc(gc, 7, sizeof(char *));
+ vments[0] = "rtc/timeoffset";
+ vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : "";
+ vments[2] = "image/ostype";
+ vments[3] = "hvm";
+ vments[4] = "start_time";
+ vments[5] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
+ break;
+ case LIBXL_DOMAIN_TYPE_PV:
+ vments = libxl__calloc(gc, 11, sizeof(char *));
+ i = 0;
+ vments[i++] = "image/ostype";
+ vments[i++] = "linux";
+ vments[i++] = "image/kernel";
+ vments[i++] = (char *) state->pv_kernel.path;
+ vments[i++] = "start_time";
+ vments[i++] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
+ if (state->pv_ramdisk.path) {
+ vments[i++] = "image/ramdisk";
+ vments[i++] = (char *) state->pv_ramdisk.path;
+ }
+ if (state->pv_cmdline) {
+ vments[i++] = "image/cmdline";
+ vments[i++] = (char *) state->pv_cmdline;
+ }
+ break;
+ case LIBXL_DOMAIN_TYPE_PVH:
+ vments = libxl__calloc(gc, 3, sizeof(char *));
+ vments[0] = "start_time";
+ vments[1] = GCSPRINTF("%"PRIu64".%02ld",
+ (uint64_t)start_time.tv_sec,
+ (long)start_time.tv_usec/10000);
+ break;
+ default:
+ ret = ERROR_INVAL;
+ goto out;
+ }
+
+ /*
+ * The scheduler on the sending domain may be different than the
+ * scheduler running here. Setting the scheduler to UNKNOWN will
+ * cause the code to take to take whatever parameters are
+ * available in that scheduler, while discarding the rest.
+ */
+ info->sched_params.sched = LIBXL_SCHEDULER_UNKNOWN;
+
+ ret = libxl__build_post(gc, domid, info, state, vments, localents);
+ if (ret)
+ goto out;
+
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
+ state->saved_state = GCSPRINTF(
+ LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", domid);
+ }
+
+out:
+ if (info->type == LIBXL_DOMAIN_TYPE_PV) {
+ libxl__file_reference_unmap(&state->pv_kernel);
+ libxl__file_reference_unmap(&state->pv_ramdisk);
+ }
+
+ /* fd == -1 here means we're doing soft reset. */
+ if (fd != -1) {
+ esave = errno;
+ libxl_fd_set_nonblock(ctx, fd, 0);
+ errno = esave;
+ }
+ domcreate_rebuild_done(egc, dcs, ret);
+}
+
+static void domcreate_rebuild_done(libxl__egc *egc,
+ libxl__domain_create_state *dcs,
+ int ret)
+{
+ STATE_AO_GC(dcs->ao);
+
+ /* convenience aliases */
+ const uint32_t domid = dcs->guest_domid;
+ libxl_domain_config *const d_config = dcs->guest_config;
+
+ if (ret) {
+ LOGD(ERROR, domid, "cannot (re-)build domain: %d", ret);
+ ret = ERROR_FAIL;
+ goto error_out;
+ }
+
+ store_libxl_entry(gc, domid, &d_config->b_info);
+
+ libxl__multidev_begin(ao, &dcs->multidev);
+ dcs->multidev.callback = domcreate_launch_dm;
+ libxl__add_disks(egc, ao, domid, d_config, &dcs->multidev);
+ libxl__multidev_prepared(egc, &dcs->multidev, 0);
+
+ return;
+
+ error_out:
+ assert(ret);
+ domcreate_complete(egc, dcs, ret);
+}
+
+static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *multidev,
+ int ret)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev);
+ STATE_AO_GC(dcs->ao);
+ int i;
+
+ /* convenience aliases */
+ const uint32_t domid = dcs->guest_domid;
+ libxl_domain_config *const d_config = dcs->guest_config;
+ libxl__domain_build_state *const state = &dcs->build_state;
+
+ if (ret) {
+ LOGD(ERROR, domid, "unable to add disk devices");
+ goto error_out;
+ }
+
+ for (i = 0; i < d_config->b_info.num_ioports; i++) {
+ libxl_ioport_range *io = &d_config->b_info.ioports[i];
+
+ LOGD(DEBUG, domid, "ioports %"PRIx32"-%"PRIx32,
+ io->first, io->first + io->number - 1);
+
+ ret = xc_domain_ioport_permission(CTX->xch, domid,
+ io->first, io->number, 1);
+ if (ret < 0) {
+ LOGED(ERROR, domid,
+ "failed give domain access to ioports %"PRIx32"-%"PRIx32,
+ io->first, io->first + io->number - 1);
+ ret = ERROR_FAIL;
+ goto error_out;
+ }
+ }
+
+ for (i = 0; i < d_config->b_info.num_irqs; i++) {
+ int irq = d_config->b_info.irqs[i];
+
+ LOGD(DEBUG, domid, "irq %d", irq);
+
+ ret = irq >= 0 ? libxl__arch_domain_map_irq(gc, domid, irq)
+ : -EOVERFLOW;
+ if (ret) {
+ LOGED(ERROR, domid, "failed give domain access to irq %d", irq);
+ ret = ERROR_FAIL;
+ goto error_out;
+ }
+ }
+
+ for (i = 0; i < d_config->b_info.num_iomem; i++) {
+ libxl_iomem_range *io = &d_config->b_info.iomem[i];
+
+ LOGD(DEBUG, domid, "iomem %"PRIx64"-%"PRIx64,
+ io->start, io->start + io->number - 1);
+
+ ret = xc_domain_iomem_permission(CTX->xch, domid,
+ io->start, io->number, 1);
+ if (ret < 0) {
+ LOGED(ERROR, domid,
+ "failed give domain access to iomem range %"PRIx64"-%"PRIx64,
+ io->start, io->start + io->number - 1);
+ ret = ERROR_FAIL;
+ goto error_out;
+ }
+ ret = xc_domain_memory_mapping(CTX->xch, domid,
+ io->gfn, io->start,
+ io->number, 1);
+ if (ret < 0) {
+ LOGED(ERROR, domid,
+ "failed to map to domain iomem range %"PRIx64"-%"PRIx64
+ " to guest address %"PRIx64,
+ io->start, io->start + io->number - 1, io->gfn);
+ ret = ERROR_FAIL;
+ goto error_out;
+ }
+ }
+
+ /* For both HVM and PV the 0th console is a regular console. We
+ map channels to IOEMU consoles starting at 1 */
+ for (i = 0; i < d_config->num_channels; i++) {
+ libxl__device_console console;
+ libxl__device device;
+ ret = libxl__init_console_from_channel(gc, &console, i + 1,
+ &d_config->channels[i]);
+ if ( ret ) {
+ libxl__device_console_dispose(&console);
+ goto error_out;
+ }
+ libxl__device_console_add(gc, domid, &console, NULL, &device);
+ libxl__device_console_dispose(&console);
+ }
+
+ for (i = 0; i < d_config->num_p9s; i++)
+ libxl__device_add(gc, domid, &libxl__p9_devtype, &d_config->p9s[i]);
+
+ for (i = 0; i < d_config->num_pvcallsifs; i++)
+ libxl__device_add(gc, domid, &libxl__pvcallsif_devtype,
+ &d_config->pvcallsifs[i]);
+
+ switch (d_config->c_info.type) {
+ case LIBXL_DOMAIN_TYPE_HVM:
+ {
+ libxl__device_console console;
+ libxl__device device;
+ libxl_device_vkb vkb;
+
+ init_console_info(gc, &console, 0);
+ console.backend_domid = state->console_domid;
+ libxl__device_console_add(gc, domid, &console, state, &device);
+ libxl__device_console_dispose(&console);
+
+ if (libxl_defbool_val(d_config->b_info.u.hvm.vkb_device)) {
+ libxl_device_vkb_init(&vkb);
+ libxl__device_add(gc, domid, &libxl__vkb_devtype, &vkb);
+ libxl_device_vkb_dispose(&vkb);
+ }
+
+ dcs->sdss.dm.guest_domid = domid;
+ if (libxl_defbool_val(d_config->b_info.device_model_stubdomain))
+ libxl__spawn_stub_dm(egc, &dcs->sdss);
+ else
+ libxl__spawn_local_dm(egc, &dcs->sdss.dm);
+
+ /*
+ * Handle the domain's (and the related stubdomain's) access to
+ * the VGA framebuffer.
+ */
+ ret = libxl__grant_vga_iomem_permission(gc, domid, d_config);
+ if ( ret )
+ goto error_out;
+
+ return;
+ }
+ case LIBXL_DOMAIN_TYPE_PV:
+ case LIBXL_DOMAIN_TYPE_PVH:
+ {
+ libxl__device_console console, vuart;
+ libxl__device device;
+
+ for (i = 0; i < d_config->num_vfbs; i++) {
+ libxl__device_add(gc, domid, &libxl__vfb_devtype,
+ &d_config->vfbs[i]);
+ }
+
+ for (i = 0; i < d_config->num_vkbs; i++) {
+ libxl__device_add(gc, domid, &libxl__vkb_devtype,
+ &d_config->vkbs[i]);
+ }
+
+ if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
+ init_console_info(gc, &vuart, 0);
+ vuart.backend_domid = state->console_domid;
+ libxl__device_vuart_add(gc, domid, &vuart, state);
+ libxl__device_console_dispose(&vuart);
+ }
+
+ init_console_info(gc, &console, 0);
+ console.backend_domid = state->console_domid;
+ libxl__device_console_add(gc, domid, &console, state, &device);
+ libxl__device_console_dispose(&console);
+
+ ret = libxl__need_xenpv_qemu(gc, d_config);
+ if (ret < 0)
+ goto error_out;
+ if (ret) {
+ dcs->sdss.dm.guest_domid = domid;
+ libxl__spawn_local_dm(egc, &dcs->sdss.dm);
+ return;
+ } else {
+ assert(!dcs->sdss.dm.guest_domid);
+ domcreate_devmodel_started(egc, &dcs->sdss.dm, 0);
+ return;
+ }
+ }
+ default:
+ ret = ERROR_INVAL;
+ goto error_out;
+ }
+ abort(); /* not reached */
+
+ error_out:
+ assert(ret);
+ domcreate_complete(egc, dcs, ret);
+}
+
+static void libxl__add_dtdevs(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
+ libxl_domain_config *d_config,
+ libxl__multidev *multidev)
+{
+ AO_GC;
+ libxl__ao_device *aodev = libxl__multidev_prepare(multidev);
+ int i, rc = 0;
+
+ for (i = 0; i < d_config->num_dtdevs; i++) {
+ const libxl_device_dtdev *dtdev = &d_config->dtdevs[i];
+
+ LOGD(DEBUG, domid, "Assign device \"%s\" to domain", dtdev->path);
+ rc = xc_assign_dt_device(CTX->xch, domid, dtdev->path);
+ if (rc < 0) {
+ LOGD(ERROR, domid, "xc_assign_dtdevice failed: %d", rc);
+ goto out;
+ }
+ }
+
+out:
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+#define libxl_device_dtdev_list NULL
+#define libxl_device_dtdev_compare NULL
+#define libxl__device_from_dtdev NULL
+#define libxl__device_dtdev_setdefault NULL
+#define libxl__device_dtdev_update_devid NULL
+static DEFINE_DEVICE_TYPE_STRUCT(dtdev, NONE);
+
+const libxl__device_type *device_type_tbl[] = {
+ &libxl__disk_devtype,
+ &libxl__nic_devtype,
+ &libxl__vtpm_devtype,
+ &libxl__usbctrl_devtype,
+ &libxl__usbdev_devtype,
+ &libxl__pcidev_devtype,
+ &libxl__dtdev_devtype,
+ &libxl__vdispl_devtype,
+ &libxl__vsnd_devtype,
+ NULL
+};
+
+static void domcreate_devmodel_started(libxl__egc *egc,
+ libxl__dm_spawn_state *dmss,
+ int ret)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(dmss, *dcs, sdss.dm);
+ STATE_AO_GC(dmss->spawn.ao);
+ int domid = dcs->guest_domid;
+
+ if (ret) {
+ LOGD(ERROR, domid, "device model did not start: %d", ret);
+ goto error_out;
+ }
+
+ dcs->device_type_idx = -1;
+ domcreate_attach_devices(egc, &dcs->multidev, 0);
+ return;
+
+error_out:
+ assert(ret);
+ domcreate_complete(egc, dcs, ret);
+}
+
+static void domcreate_attach_devices(libxl__egc *egc,
+ libxl__multidev *multidev,
+ int ret)
+{
+ libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev);
+ STATE_AO_GC(dcs->ao);
+ int domid = dcs->guest_domid;
+ libxl_domain_config *const d_config = dcs->guest_config;
+ const libxl__device_type *dt;
+ char *tty_path;
+
+ if (ret) {
+ LOGD(ERROR, domid, "unable to add %s devices",
+ libxl__device_kind_to_string(device_type_tbl[dcs->device_type_idx]->type));
+ goto error_out;
+ }
+
+ dcs->device_type_idx++;
+ dt = device_type_tbl[dcs->device_type_idx];
+ if (dt) {
+ if (*libxl__device_type_get_num(dt, d_config) > 0 && !dt->skip_attach) {
+ /* Attach devices */
+ libxl__multidev_begin(ao, &dcs->multidev);
+ dcs->multidev.callback = domcreate_attach_devices;
+ dt->add(egc, ao, domid, d_config, &dcs->multidev);
+ libxl__multidev_prepared(egc, &dcs->multidev, 0);
+ return;
+ }
+
+ domcreate_attach_devices(egc, &dcs->multidev, 0);
+ return;
+ }
+
+ ret = libxl__console_tty_path(gc, domid, 0, LIBXL_CONSOLE_TYPE_PV, &tty_path);
+ if (ret) {
+ LOG(ERROR, "failed to get domain %d console tty path",
+ domid);
+ goto error_out;
+ }
+
+ dcs->console_xswait.ao = ao;
+ dcs->console_xswait.what = GCSPRINTF("domain %d console tty", domid);
+ dcs->console_xswait.path = tty_path;
+ dcs->console_xswait.timeout_ms = LIBXL_INIT_TIMEOUT * 1000;
+ dcs->console_xswait.callback = console_xswait_callback;
+ ret = libxl__xswait_start(gc, &dcs->console_xswait);
+ if (ret) {
+ LOG(ERROR, "unable to set up watch for domain %d console tty path",
+ domid);
+ goto error_out;
+ }
+
+ return;
+
+error_out:
+ assert(ret);
+ domcreate_complete(egc, dcs, ret);
+}
+
+static void console_xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa,
+ int rc, const char *p)
+{
+ EGC_GC;
+ libxl__domain_create_state *dcs = CONTAINER_OF(xswa, *dcs, console_xswait);
+
+ if (rc) {
+ if (rc == ERROR_TIMEDOUT)
+ LOG(ERROR, "%s: timed out", xswa->what);
+ goto out;
+ }
+
+ if (p && p[0] != '\0') {
+ domcreate_console_available(egc, dcs);
+ goto out;
+ }
+
+ return;
+
+out:
+ libxl__xswait_stop(gc, xswa);
+ domcreate_complete(egc, dcs, rc);
+}
+
+static void domcreate_complete(libxl__egc *egc,
+ libxl__domain_create_state *dcs,
+ int rc)
+{
+ STATE_AO_GC(dcs->ao);
+ libxl_domain_config *const d_config = dcs->guest_config;
+ libxl_domain_config *d_config_saved = &dcs->guest_config_saved;
+
+ libxl__xswait_stop(gc, &dcs->console_xswait);
+
+ libxl__domain_build_state_dispose(&dcs->build_state);
+
+ if (!rc && d_config->b_info.exec_ssidref)
+ rc = xc_flask_relabel_domain(CTX->xch, dcs->guest_domid, d_config->b_info.exec_ssidref);
+
+ bool retain_domain = !rc || rc == ERROR_ABORTED;
+
+ if (retain_domain) {
+ libxl__flock *lock;
+
+ /* Note that we hold CTX lock at this point so only need to
+ * take data store lock
+ */
+ lock = libxl__lock_domain_userdata(gc, dcs->guest_domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ } else {
+ libxl__update_domain_configuration(gc, d_config_saved, d_config);
+ int cfg_rc = libxl__set_domain_configuration
+ (gc, dcs->guest_domid, d_config_saved);
+ if (!rc)
+ rc = cfg_rc;
+ libxl__unlock_file(lock);
+ }
+ }
+
+ libxl_domain_config_dispose(d_config_saved);
+
+ if (!retain_domain) {
+ if (dcs->guest_domid > 0) {
+ dcs->dds.ao = ao;
+ dcs->dds.domid = dcs->guest_domid;
+ dcs->dds.callback = domcreate_destruction_cb;
+ libxl__domain_destroy(egc, &dcs->dds);
+ return;
+ }
+ dcs->guest_domid = INVALID_DOMID;
+ }
+ dcs->callback(egc, dcs, rc, dcs->guest_domid);
+}
+
+static void domcreate_destruction_cb(libxl__egc *egc,
+ libxl__domain_destroy_state *dds,
+ int rc)
+{
+ STATE_AO_GC(dds->ao);
+ libxl__domain_create_state *dcs = CONTAINER_OF(dds, *dcs, dds);
+
+ if (rc)
+ LOGD(ERROR, dds->domid, "unable to destroy domain following failed creation");
+
+ dcs->callback(egc, dcs, ERROR_FAIL, dcs->guest_domid);
+}
+
+/*----- application-facing domain creation interface -----*/
+
+typedef struct {
+ libxl__domain_create_state dcs;
+ uint32_t *domid_out;
+} libxl__app_domain_create_state;
+
+typedef struct {
+ libxl__app_domain_create_state cdcs;
+ libxl__domain_destroy_state dds;
+ libxl__domain_save_state dss;
+ char *toolstack_buf;
+ uint32_t toolstack_len;
+} libxl__domain_soft_reset_state;
+
+static void domain_create_cb(libxl__egc *egc,
+ libxl__domain_create_state *dcs,
+ int rc, uint32_t domid);
+
+static int do_domain_create(libxl_ctx *ctx, libxl_domain_config *d_config,
+ uint32_t *domid, int restore_fd, int send_back_fd,
+ const libxl_domain_restore_params *params,
+ const libxl_asyncop_how *ao_how,
+ const libxl_asyncprogress_how *aop_console_how)
+{
+ AO_CREATE(ctx, 0, ao_how);
+ libxl__app_domain_create_state *cdcs;
+ int rc;
+
+ GCNEW(cdcs);
+ cdcs->dcs.ao = ao;
+ cdcs->dcs.guest_config = d_config;
+ libxl_domain_config_init(&cdcs->dcs.guest_config_saved);
+ libxl_domain_config_copy(ctx, &cdcs->dcs.guest_config_saved, d_config);
+ cdcs->dcs.restore_fd = cdcs->dcs.libxc_fd = restore_fd;
+ cdcs->dcs.send_back_fd = send_back_fd;
+ if (restore_fd >= 0) {
+ cdcs->dcs.restore_params = *params;
+ rc = libxl__fd_flags_modify_save(gc, cdcs->dcs.restore_fd,
+ ~(O_NONBLOCK|O_NDELAY), 0,
+ &cdcs->dcs.restore_fdfl);
+ if (rc < 0) goto out_err;
+ }
+ cdcs->dcs.callback = domain_create_cb;
+ cdcs->dcs.domid = INVALID_DOMID;
+ cdcs->dcs.soft_reset = false;
+
+ if (cdcs->dcs.restore_params.checkpointed_stream ==
+ LIBXL_CHECKPOINTED_STREAM_COLO) {
+ cdcs->dcs.colo_proxy_script =
+ cdcs->dcs.restore_params.colo_proxy_script;
+ cdcs->dcs.crs.cps.is_userspace_proxy =
+ libxl_defbool_val(cdcs->dcs.restore_params.userspace_colo_proxy);
+ } else {
+ cdcs->dcs.colo_proxy_script = NULL;
+ cdcs->dcs.crs.cps.is_userspace_proxy = false;
+ }
+
+ libxl__ao_progress_gethow(&cdcs->dcs.aop_console_how, aop_console_how);
+ cdcs->domid_out = domid;
+
+ initiate_domain_create(egc, &cdcs->dcs);
+
+ return AO_INPROGRESS;
+
+ out_err:
+ return AO_CREATE_FAIL(rc);
+
+}
+
+static void domain_soft_reset_cb(libxl__egc *egc,
+ libxl__domain_destroy_state *dds,
+ int rc)
+{
+ STATE_AO_GC(dds->ao);
+ libxl__domain_soft_reset_state *srs = CONTAINER_OF(dds, *srs, dds);
+ libxl__app_domain_create_state *cdcs = &srs->cdcs;
+ char *savefile, *restorefile;
+
+ if (rc) {
+ LOGD(ERROR, dds->domid, "destruction of domain failed.");
+ goto error;
+ }
+
+ cdcs->dcs.guest_domid = dds->domid;
+ rc = libxl__restore_emulator_xenstore_data(&cdcs->dcs, srs->toolstack_buf,
+ srs->toolstack_len);
+ if (rc) {
+ LOGD(ERROR, dds->domid, "failed to restore toolstack record.");
+ goto error;
+ }
+
+ if (cdcs->dcs.guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
+ savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", dds->domid);
+ restorefile = GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%d",
+ dds->domid);
+ rc = rename(savefile, restorefile);
+ if (rc) {
+ LOGD(ERROR, dds->domid, "failed to rename dm save file.");
+ goto error;
+ }
+ }
+
+ initiate_domain_create(egc, &cdcs->dcs);
+ return;
+
+error:
+ domcreate_complete(egc, &cdcs->dcs, rc);
+}
+
+static void soft_reset_dm_suspended(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps,
+ int rc);
+static int do_domain_soft_reset(libxl_ctx *ctx,
+ libxl_domain_config *d_config,
+ uint32_t domid,
+ const libxl_asyncop_how *ao_how,
+ const libxl_asyncprogress_how
+ *aop_console_how)
+{
+ AO_CREATE(ctx, 0, ao_how);
+ libxl__domain_soft_reset_state *srs;
+ libxl__app_domain_create_state *cdcs;
+ libxl__domain_create_state *dcs;
+ libxl__domain_build_state *state;
+ libxl__domain_save_state *dss;
+ const char *console_tty, *xs_store_mfn, *xs_console_mfn;
+ char *dom_path;
+ uint32_t domid_out;
+ int rc;
+
+ GCNEW(srs);
+ cdcs = &srs->cdcs;
+ dcs = &cdcs->dcs;
+ state = &dcs->build_state;
+ dss = &srs->dss;
+
+ srs->cdcs.dcs.ao = ao;
+ srs->cdcs.dcs.guest_config = d_config;
+ libxl_domain_config_init(&srs->cdcs.dcs.guest_config_saved);
+ libxl_domain_config_copy(ctx, &srs->cdcs.dcs.guest_config_saved,
+ d_config);
+ cdcs->dcs.restore_fd = -1;
+ cdcs->dcs.domid = domid;
+ cdcs->dcs.soft_reset = true;
+ cdcs->dcs.callback = domain_create_cb;
+ libxl__ao_progress_gethow(&srs->cdcs.dcs.aop_console_how,
+ aop_console_how);
+ cdcs->domid_out = &domid_out;
+
+ dom_path = libxl__xs_get_dompath(gc, domid);
+ if (!dom_path) {
+ LOGD(ERROR, domid, "failed to read domain path");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/store/ring-ref", dom_path),
+ &xs_store_mfn);
+ if (rc) {
+ LOGD(ERROR, domid, "failed to read store/ring-ref.");
+ goto out;
+ }
+ state->store_mfn = xs_store_mfn ? atol(xs_store_mfn): 0;
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/console/ring-ref", dom_path),
+ &xs_console_mfn);
+ if (rc) {
+ LOGD(ERROR, domid, "failed to read console/ring-ref.");
+ goto out;
+ }
+ state->console_mfn = xs_console_mfn ? atol(xs_console_mfn): 0;
+
+ rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+ GCSPRINTF("%s/console/tty", dom_path),
+ &console_tty);
+ if (rc) {
+ LOGD(ERROR, domid, "failed to read console/tty.");
+ goto out;
+ }
+ state->console_tty = libxl__strdup(gc, console_tty);
+
+ dss->ao = ao;
+ dss->domid = dss->dsps.domid = domid;
+ dss->dsps.dm_savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d",
+ domid);
+
+ rc = libxl__save_emulator_xenstore_data(dss, &srs->toolstack_buf,
+ &srs->toolstack_len);
+ if (rc) {
+ LOGD(ERROR, domid, "failed to save toolstack record.");
+ goto out;
+ }
+
+ dss->dsps.ao = ao;
+ dss->dsps.callback_device_model_done = soft_reset_dm_suspended;
+ libxl__domain_suspend_device_model(egc, &dss->dsps); /* must be last */
+
+ return AO_INPROGRESS;
+
+ out:
+ return AO_CREATE_FAIL(rc);
+}
+
+static void soft_reset_dm_suspended(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps,
+ int rc)
+{
+ STATE_AO_GC(dsps->ao);
+ libxl__domain_soft_reset_state *srs =
+ CONTAINER_OF(dsps, *srs, dss.dsps);
+ libxl__app_domain_create_state *cdcs = &srs->cdcs;
+
+ /*
+ * Ask all backends to disconnect by removing the domain from
+ * xenstore. On the creation path the domain will be introduced to
+ * xenstore again with probably different store/console/...
+ * channels.
+ */
+ xs_release_domain(CTX->xsh, cdcs->dcs.domid);
+
+ srs->dds.ao = ao;
+ srs->dds.domid = cdcs->dcs.domid;
+ srs->dds.callback = domain_soft_reset_cb;
+ srs->dds.soft_reset = true;
+ libxl__domain_destroy(egc, &srs->dds);
+}
+
+static void domain_create_cb(libxl__egc *egc,
+ libxl__domain_create_state *dcs,
+ int rc, uint32_t domid)
+{
+ libxl__app_domain_create_state *cdcs = CONTAINER_OF(dcs, *cdcs, dcs);
+ int flrc;
+ STATE_AO_GC(cdcs->dcs.ao);
+
+ *cdcs->domid_out = domid;
+
+ if (dcs->restore_fd >= 0) {
+ flrc = libxl__fd_flags_restore(gc,
+ dcs->restore_fd, dcs->restore_fdfl);
+ /*
+ * If restore has failed already then report that error not
+ * this one.
+ */
+ if (flrc && !rc) rc = flrc;
+ }
+
+ libxl__ao_complete(egc, ao, rc);
+}
+
+
+static void set_disk_colo_restore(libxl_domain_config *d_config)
+{
+ int i;
+
+ for (i = 0; i < d_config->num_disks; i++)
+ libxl_defbool_set(&d_config->disks[i].colo_restore_enable, true);
+}
+
+static void unset_disk_colo_restore(libxl_domain_config *d_config)
+{
+ int i;
+
+ for (i = 0; i < d_config->num_disks; i++)
+ libxl_defbool_set(&d_config->disks[i].colo_restore_enable, false);
+}
+
+int libxl_domain_create_new(libxl_ctx *ctx, libxl_domain_config *d_config,
+ uint32_t *domid,
+ const libxl_asyncop_how *ao_how,
+ const libxl_asyncprogress_how *aop_console_how)
+{
+ unset_disk_colo_restore(d_config);
+ return do_domain_create(ctx, d_config, domid, -1, -1, NULL,
+ ao_how, aop_console_how);
+}
+
+int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config,
+ uint32_t *domid, int restore_fd,
+ int send_back_fd,
+ const libxl_domain_restore_params *params,
+ const libxl_asyncop_how *ao_how,
+ const libxl_asyncprogress_how *aop_console_how)
+{
+ if (params->checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) {
+ set_disk_colo_restore(d_config);
+ } else {
+ unset_disk_colo_restore(d_config);
+ }
+
+ return do_domain_create(ctx, d_config, domid, restore_fd, send_back_fd,
+ params, ao_how, aop_console_how);
+}
+
+int libxl_domain_soft_reset(libxl_ctx *ctx,
+ libxl_domain_config *d_config,
+ uint32_t domid,
+ const libxl_asyncop_how *ao_how,
+ const libxl_asyncprogress_how
+ *aop_console_how)
+{
+ libxl_domain_build_info *const info = &d_config->b_info;
+
+ if (info->type != LIBXL_DOMAIN_TYPE_HVM) return ERROR_INVAL;
+
+ return do_domain_soft_reset(ctx, d_config, domid, ao_how,
+ aop_console_how);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+static char *libxl__device_frontend_path(libxl__gc *gc, libxl__device *device)
+{
+ char *dom_path = libxl__xs_get_dompath(gc, device->domid);
+
+ /* Console 0 is a special case */
+ if (device->kind == LIBXL__DEVICE_KIND_CONSOLE && device->devid == 0)
+ return GCSPRINTF("%s/%s", dom_path,
+ libxl__device_kind_to_string(device->kind));
+
+ if (device->kind == LIBXL__DEVICE_KIND_VUART)
+ return GCSPRINTF("%s/%s/%d", dom_path,
+ libxl__device_kind_to_string(device->kind),
+ device->devid);
+
+ return GCSPRINTF("%s/device/%s/%d", dom_path,
+ libxl__device_kind_to_string(device->kind),
+ device->devid);
+}
+
+char *libxl__domain_device_frontend_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
+ libxl__device_kind device_kind)
+{
+ char *dom_path = libxl__xs_get_dompath(gc, domid);
+
+ return GCSPRINTF("%s/device/%s/%d", dom_path,
+ libxl__device_kind_to_string(device_kind), devid);
+}
+
+char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device)
+{
+ char *dom_path = libxl__xs_get_dompath(gc, device->backend_domid);
+
+ return GCSPRINTF("%s/backend/%s/%u/%d", dom_path,
+ libxl__device_kind_to_string(device->backend_kind),
+ device->domid, device->devid);
+}
+
+char *libxl__domain_device_backend_path(libxl__gc *gc, uint32_t backend_domid,
+ uint32_t domid, uint32_t devid,
+ libxl__device_kind backend_kind)
+{
+ char *dom_path = libxl__xs_get_dompath(gc, backend_domid);
+
+ return GCSPRINTF("%s/backend/%s/%u/%d", dom_path,
+ libxl__device_kind_to_string(backend_kind),
+ domid, devid);
+}
+
+char *libxl__device_libxl_path(libxl__gc *gc, libxl__device *device)
+{
+ char *libxl_dom_path = libxl__xs_libxl_path(gc, device->domid);
+
+ return GCSPRINTF("%s/device/%s/%d", libxl_dom_path,
+ libxl__device_kind_to_string(device->kind),
+ device->devid);
+}
+
+char *libxl__domain_device_libxl_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
+ libxl__device_kind device_kind)
+{
+ char *libxl_dom_path = libxl__xs_libxl_path(gc, domid);
+
+ return GCSPRINTF("%s/device/%s/%d", libxl_dom_path,
+ libxl__device_kind_to_string(device_kind), devid);
+}
+
+/* Returns 1 if device exists, 0 if not, ERROR_* (<0) on error. */
+int libxl__device_exists(libxl__gc *gc, xs_transaction_t t,
+ libxl__device *device)
+{
+ int rc;
+ char *be_path = libxl__device_libxl_path(gc, device);
+ const char *dir;
+
+ rc = libxl__xs_read_checked(gc, t, be_path, &dir);
+
+ if (rc)
+ return rc;
+
+ if (dir)
+ return 1;
+ return 0;
+}
+
+int libxl__parse_backend_path(libxl__gc *gc,
+ const char *path,
+ libxl__device *dev)
+{
+ /* /local/domain/<domid>/backend/<kind>/<domid>/<devid> */
+ char strkind[16]; /* Longest is actually "console" */
+ int rc = sscanf(path, "/local/domain/%d/backend/%15[^/]/%u/%d",
+ &dev->backend_domid,
+ strkind,
+ &dev->domid,
+ &dev->devid);
+
+ if (rc != 4)
+ return ERROR_FAIL;
+
+ return libxl__device_kind_from_string(strkind, &dev->backend_kind);
+}
+
+int libxl__nic_type(libxl__gc *gc, libxl__device *dev, libxl_nic_type *nictype)
+{
+ char *snictype, *be_path;
+ int rc = 0;
+
+ be_path = libxl__device_backend_path(gc, dev);
+ snictype = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/%s", be_path, "type"));
+ if (!snictype) {
+ LOGED(ERROR, dev->domid, "unable to read nictype from %s", be_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = libxl_nic_type_from_string(snictype, nictype);
+ if (rc) {
+ LOGED(ERROR, dev->domid, "unable to parse nictype from %s", be_path);
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t,
+ libxl__device *device, char **bents, char **fents, char **ro_fents)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char *frontend_path = NULL, *backend_path = NULL, *libxl_path;
+ struct xs_permissions frontend_perms[2];
+ struct xs_permissions ro_frontend_perms[2];
+ struct xs_permissions backend_perms[2];
+ int create_transaction = t == XBT_NULL;
+ int libxl_only = device->backend_kind == LIBXL__DEVICE_KIND_NONE;
+ int rc;
+
+ if (libxl_only) {
+ /* bents should be set as this is used to setup libxl_path content. */
+ assert(!fents && !ro_fents);
+ } else {
+ frontend_path = libxl__device_frontend_path(gc, device);
+ backend_path = libxl__device_backend_path(gc, device);
+ }
+ libxl_path = libxl__device_libxl_path(gc, device);
+
+ frontend_perms[0].id = device->domid;
+ frontend_perms[0].perms = XS_PERM_NONE;
+ frontend_perms[1].id = device->backend_domid;
+ frontend_perms[1].perms = XS_PERM_READ;
+
+ ro_frontend_perms[0].id = backend_perms[0].id = device->backend_domid;
+ ro_frontend_perms[0].perms = backend_perms[0].perms = XS_PERM_NONE;
+ ro_frontend_perms[1].id = backend_perms[1].id = device->domid;
+ ro_frontend_perms[1].perms = backend_perms[1].perms = XS_PERM_READ;
+
+retry_transaction:
+ if (create_transaction)
+ t = xs_transaction_start(ctx->xsh);
+
+ /* FIXME: read frontend_path and check state before removing stuff */
+
+ rc = libxl__xs_rm_checked(gc, t, libxl_path);
+ if (rc) goto out;
+
+ if (!libxl_only) {
+ rc = libxl__xs_write_checked(gc, t, GCSPRINTF("%s/frontend",libxl_path),
+ frontend_path);
+ if (rc) goto out;
+
+ rc = libxl__xs_write_checked(gc, t, GCSPRINTF("%s/backend",libxl_path),
+ backend_path);
+ if (rc) goto out;
+ }
+
+ /* xxx much of this function lacks error checks! */
+
+ if (fents || ro_fents) {
+ xs_rm(ctx->xsh, t, frontend_path);
+ xs_mkdir(ctx->xsh, t, frontend_path);
+ /* Console 0 is a special case. It doesn't use the regular PV
+ * state machine but also the frontend directory has
+ * historically contained other information, such as the
+ * vnc-port, which we don't want the guest fiddling with.
+ */
+ if ((device->kind == LIBXL__DEVICE_KIND_CONSOLE && device->devid == 0) ||
+ (device->kind == LIBXL__DEVICE_KIND_VUART))
+ xs_set_permissions(ctx->xsh, t, frontend_path,
+ ro_frontend_perms, ARRAY_SIZE(ro_frontend_perms));
+ else
+ xs_set_permissions(ctx->xsh, t, frontend_path,
+ frontend_perms, ARRAY_SIZE(frontend_perms));
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/backend", frontend_path),
+ backend_path, strlen(backend_path));
+ if (fents)
+ libxl__xs_writev_perms(gc, t, frontend_path, fents,
+ frontend_perms, ARRAY_SIZE(frontend_perms));
+ if (ro_fents)
+ libxl__xs_writev_perms(gc, t, frontend_path, ro_fents,
+ ro_frontend_perms, ARRAY_SIZE(ro_frontend_perms));
+ }
+
+ if (bents) {
+ if (!libxl_only) {
+ xs_rm(ctx->xsh, t, backend_path);
+ xs_mkdir(ctx->xsh, t, backend_path);
+ xs_set_permissions(ctx->xsh, t, backend_path, backend_perms,
+ ARRAY_SIZE(backend_perms));
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/frontend", backend_path),
+ frontend_path, strlen(frontend_path));
+ libxl__xs_writev(gc, t, backend_path, bents);
+ }
+
+ /*
+ * We make a copy of everything for the backend in the libxl
+ * path as well. This means we don't need to trust the
+ * backend. Ideally this information would not be used and we
+ * would use the information from the json configuration
+ * instead. But there are still places in libxl that try to
+ * reconstruct a config from xenstore.
+ *
+ * For devices without PV backend (e.g. USB devices emulated via qemu)
+ * only the libxl path is written.
+ *
+ * This duplication will typically produces duplicate keys
+ * which will go out of date, but that's OK because nothing
+ * reads those. For example, there is usually
+ * /libxl/$guest/device/$kind/$devid/state
+ * which starts out containing XenbusStateInitialising ("1")
+ * just like the copy in
+ * /local/domain/$driverdom/backend/$guest/$kind/$devid/state
+ * but which won't ever be updated.
+ *
+ * This duplication is superfluous and messy but as discussed
+ * the proper fix is more intrusive than we want to do now.
+ */
+ rc = libxl__xs_writev(gc, t, libxl_path, bents);
+ if (rc) goto out;
+ }
+
+ if (!create_transaction)
+ return 0;
+
+ if (!xs_transaction_end(ctx->xsh, t, 0)) {
+ if (errno == EAGAIN)
+ goto retry_transaction;
+ else {
+ LOGED(ERROR, device->domid, "xs transaction failed");
+ return ERROR_FAIL;
+ }
+ }
+ return 0;
+
+ out:
+ if (create_transaction && t)
+ libxl__xs_transaction_abort(gc, &t);
+ return rc;
+}
+
+typedef struct {
+ libxl__gc *gc;
+ libxl_device_disk *disk;
+ struct stat stab;
+} disk_try_backend_args;
+
+static int disk_try_backend(disk_try_backend_args *a,
+ libxl_disk_backend backend)
+ {
+ libxl__gc *gc = a->gc;
+ /* returns 0 (ie, DISK_BACKEND_UNKNOWN) on failure, or
+ * backend on success */
+
+ switch (backend) {
+ case LIBXL_DISK_BACKEND_PHY:
+ if (a->disk->format != LIBXL_DISK_FORMAT_RAW) {
+ goto bad_format;
+ }
+
+ if (libxl_defbool_val(a->disk->colo_enable))
+ goto bad_colo;
+
+ if (a->disk->backend_domid != LIBXL_TOOLSTACK_DOMID) {
+ LOG(DEBUG, "Disk vdev=%s, is using a storage driver domain, "
+ "skipping physical device check", a->disk->vdev);
+ return backend;
+ }
+
+ if (a->disk->script) {
+ LOG(DEBUG, "Disk vdev=%s, uses script=... assuming phy backend",
+ a->disk->vdev);
+ return backend;
+ }
+
+ if (libxl__try_phy_backend(a->stab.st_mode))
+ return backend;
+
+ LOG(DEBUG, "Disk vdev=%s, backend phy unsuitable as phys path not a "
+ "block device", a->disk->vdev);
+ return 0;
+
+ case LIBXL_DISK_BACKEND_TAP:
+ LOG(DEBUG, "Disk vdev=%s, backend tap unsuitable because blktap "
+ "not available", a->disk->vdev);
+ return 0;
+
+ case LIBXL_DISK_BACKEND_QDISK:
+ if (a->disk->script) goto bad_script;
+ return backend;
+
+ default:
+ LOG(DEBUG, "Disk vdev=%s, backend %d unknown", a->disk->vdev, backend);
+ return 0;
+
+ }
+ abort(); /* notreached */
+
+ bad_format:
+ LOG(DEBUG, "Disk vdev=%s, backend %s unsuitable due to format %s",
+ a->disk->vdev,
+ libxl_disk_backend_to_string(backend),
+ libxl_disk_format_to_string(a->disk->format));
+ return 0;
+
+ bad_script:
+ LOG(DEBUG, "Disk vdev=%s, backend %s not compatible with script=...",
+ a->disk->vdev, libxl_disk_backend_to_string(backend));
+ return 0;
+
+ bad_colo:
+ LOG(DEBUG, "Disk vdev=%s, backend %s not compatible with colo",
+ a->disk->vdev, libxl_disk_backend_to_string(backend));
+ return 0;
+}
+
+int libxl__backendpath_parse_domid(libxl__gc *gc, const char *be_path,
+ libxl_domid *domid_out) {
+ int r;
+ unsigned int domid_sc;
+ char delim_sc;
+
+ r = sscanf(be_path, "/local/domain/%u%c", &domid_sc, &delim_sc);
+ if (!(r==2 && delim_sc=='/')) {
+ LOG(ERROR, "internal error: backend path %s unparseable!", be_path);
+ return ERROR_FAIL;
+ }
+ *domid_out = domid_sc;
+ return 0;
+}
+
+int libxl__device_disk_set_backend(libxl__gc *gc, libxl_device_disk *disk) {
+ libxl_disk_backend ok;
+ disk_try_backend_args a;
+
+ a.gc = gc;
+ a.disk = disk;
+
+ LOG(DEBUG, "Disk vdev=%s spec.backend=%s", disk->vdev,
+ libxl_disk_backend_to_string(disk->backend));
+
+ if (disk->format == LIBXL_DISK_FORMAT_EMPTY) {
+ if (!disk->is_cdrom) {
+ LOG(ERROR, "Disk vdev=%s is empty but not cdrom", disk->vdev);
+ return ERROR_INVAL;
+ }
+ if (disk->pdev_path != NULL && strcmp(disk->pdev_path, "")) {
+ LOG(ERROR,
+ "Disk vdev=%s is empty but an image has been provided: %s",
+ disk->vdev, disk->pdev_path);
+ return ERROR_INVAL;
+ }
+ memset(&a.stab, 0, sizeof(a.stab));
+ } else if ((disk->backend == LIBXL_DISK_BACKEND_UNKNOWN ||
+ disk->backend == LIBXL_DISK_BACKEND_PHY) &&
+ disk->backend_domid == LIBXL_TOOLSTACK_DOMID &&
+ !disk->script) {
+ if (stat(disk->pdev_path, &a.stab)) {
+ LOGE(ERROR, "Disk vdev=%s failed to stat: %s",
+ disk->vdev, disk->pdev_path);
+ return ERROR_INVAL;
+ }
+ }
+
+ if (disk->backend != LIBXL_DISK_BACKEND_UNKNOWN) {
+ ok= disk_try_backend(&a, disk->backend);
+ } else {
+ ok=
+ disk_try_backend(&a, LIBXL_DISK_BACKEND_PHY) ?:
+ disk_try_backend(&a, LIBXL_DISK_BACKEND_QDISK) ?:
+ disk_try_backend(&a, LIBXL_DISK_BACKEND_TAP);
+ if (ok)
+ LOG(DEBUG, "Disk vdev=%s, using backend %s",
+ disk->vdev,
+ libxl_disk_backend_to_string(ok));
+ }
+ if (!ok) {
+ LOG(ERROR, "no suitable backend for disk %s", disk->vdev);
+ return ERROR_INVAL;
+ }
+ disk->backend = ok;
+ return 0;
+}
+
+char *libxl__device_disk_string_of_format(libxl_disk_format format)
+{
+ switch (format) {
+ case LIBXL_DISK_FORMAT_QCOW: return "qcow";
+ case LIBXL_DISK_FORMAT_QCOW2: return "qcow2";
+ case LIBXL_DISK_FORMAT_VHD: return "vhd";
+ case LIBXL_DISK_FORMAT_RAW:
+ case LIBXL_DISK_FORMAT_EMPTY: return "aio";
+ case LIBXL_DISK_FORMAT_QED: return "qed";
+ default: return NULL;
+ }
+}
+
+char *libxl__device_disk_string_of_backend(libxl_disk_backend backend)
+{
+ switch (backend) {
+ case LIBXL_DISK_BACKEND_QDISK: return "qdisk";
+ case LIBXL_DISK_BACKEND_TAP: return "phy";
+ case LIBXL_DISK_BACKEND_PHY: return "phy";
+ default: return NULL;
+ }
+}
+
+const char *libxl__qemu_disk_format_string(libxl_disk_format format)
+{
+ switch (format) {
+ case LIBXL_DISK_FORMAT_QCOW: return "qcow";
+ case LIBXL_DISK_FORMAT_QCOW2: return "qcow2";
+ case LIBXL_DISK_FORMAT_VHD: return "vpc";
+ case LIBXL_DISK_FORMAT_RAW: return "raw";
+ case LIBXL_DISK_FORMAT_EMPTY: return NULL;
+ case LIBXL_DISK_FORMAT_QED: return "qed";
+ default: return NULL;
+ }
+}
+
+int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor)
+{
+ struct stat buf;
+ if (stat(physpath, &buf) < 0)
+ return -1;
+ if (!S_ISBLK(buf.st_mode))
+ return -1;
+ *major = major(buf.st_rdev);
+ *minor = minor(buf.st_rdev);
+ return 0;
+}
+
+static int device_virtdisk_matches(const char *virtpath, const char *devtype,
+ int *index_r, int max_index,
+ int *partition_r, int max_partition) {
+ const char *p;
+ char *ep;
+ int tl, c;
+ long pl;
+
+ tl = strlen(devtype);
+ if (memcmp(virtpath, devtype, tl))
+ return 0;
+
+ /* We decode the drive letter as if it were in base 52
+ * with digits a-zA-Z, more or less */
+ *index_r = -1;
+ p = virtpath + tl;
+ for (;;) {
+ c = *p++;
+ if (c >= 'a' && c <= 'z') {
+ c -= 'a';
+ } else {
+ --p;
+ break;
+ }
+ (*index_r)++;
+ (*index_r) *= 26;
+ (*index_r) += c;
+
+ if (*index_r > max_index)
+ return 0;
+ }
+
+ if (!*p) {
+ *partition_r = 0;
+ return 1;
+ }
+
+ if (*p=='0')
+ return 0; /* leading zeroes not permitted in partition number */
+
+ pl = strtoul(p, &ep, 10);
+ if (pl > max_partition || *ep)
+ return 0;
+
+ *partition_r = pl;
+ return 1;
+}
+
+int libxl__device_disk_dev_number(const char *virtpath, int *pdisk,
+ int *ppartition)
+{
+ int disk, partition;
+ char *ep;
+ unsigned long ul;
+ int chrused;
+
+ chrused = -1;
+ if ((sscanf(virtpath, "d%ip%i%n", &disk, &partition, &chrused) >= 2
+ && chrused == strlen(virtpath) && disk < (1<<20) && partition < 256)
+ ||
+ device_virtdisk_matches(virtpath, "xvd",
+ &disk, (1<<20)-1,
+ &partition, 255)) {
+ if (pdisk) *pdisk = disk;
+ if (ppartition) *ppartition = partition;
+ if (disk <= 15 && partition <= 15)
+ return (202 << 8) | (disk << 4) | partition;
+ else
+ return (1 << 28) | (disk << 8) | partition;
+ }
+
+ errno = 0;
+ ul = strtoul(virtpath, &ep, 0);
+ if (!errno && !*ep && ul <= INT_MAX) {
+ /* FIXME: should parse ul to determine these. */
+ if (pdisk || ppartition)
+ return -1;
+ return ul;
+ }
+
+ if (device_virtdisk_matches(virtpath, "hd",
+ &disk, 3,
+ &partition, 63)) {
+ if (pdisk) *pdisk = disk;
+ if (ppartition) *ppartition = partition;
+ return ((disk<2 ? 3 : 22) << 8) | ((disk & 1) << 6) | partition;
+ }
+ if (device_virtdisk_matches(virtpath, "sd",
+ &disk, 15,
+ &partition, 15)) {
+ if (pdisk) *pdisk = disk;
+ if (ppartition) *ppartition = partition;
+ return (8 << 8) | (disk << 4) | partition;
+ }
+ return -1;
+}
+
+static char *encode_disk_name(char *ptr, unsigned int n)
+{
+ if (n >= 26)
+ ptr = encode_disk_name(ptr, n / 26 - 1);
+ *ptr = 'a' + n % 26;
+ return ptr + 1;
+}
+
+char *libxl__devid_to_vdev(libxl__gc *gc, int devid)
+{
+ unsigned int minor;
+ int offset;
+ int nr_parts;
+ char *ptr = NULL;
+/* Same as in Linux.
+ * encode_disk_name might end up using up to 29 bytes (BUFFER_SIZE - 3)
+ * including the trailing \0.
+ *
+ * The code is safe because 26 raised to the power of 28 (that is the
+ * maximum offset that can be stored in the allocated buffer as a
+ * string) is far greater than UINT_MAX on 64 bits so offset cannot be
+ * big enough to exhaust the available bytes in ret. */
+#define BUFFER_SIZE 32
+ char *ret = libxl__zalloc(gc, BUFFER_SIZE);
+
+#define EXT_SHIFT 28
+#define EXTENDED (1<<EXT_SHIFT)
+#define VDEV_IS_EXTENDED(dev) ((dev)&(EXTENDED))
+#define BLKIF_MINOR_EXT(dev) ((dev)&(~EXTENDED))
+/* the size of the buffer to store the device name is 32 bytes to match the
+ * equivalent buffer in the Linux kernel code */
+
+ if (!VDEV_IS_EXTENDED(devid)) {
+ minor = devid & 0xff;
+ nr_parts = 16;
+ } else {
+ minor = BLKIF_MINOR_EXT(devid);
+ nr_parts = 256;
+ }
+ offset = minor / nr_parts;
+
+ strcpy(ret, "xvd");
+ ptr = encode_disk_name(ret + 3, offset);
+ if (minor % nr_parts == 0)
+ *ptr = 0;
+ else
+ /* overflow cannot happen, thanks to the upper bound */
+ snprintf(ptr, ret + 32 - ptr,
+ "%d", minor & (nr_parts - 1));
+ return ret;
+#undef BUFFER_SIZE
+#undef EXT_SHIFT
+#undef EXTENDED
+#undef VDEV_IS_EXTENDED
+#undef BLKIF_MINOR_EXT
+}
+
+/* Device AO operations */
+
+void libxl__prepare_ao_device(libxl__ao *ao, libxl__ao_device *aodev)
+{
+ aodev->ao = ao;
+ aodev->rc = 0;
+ aodev->dev = NULL;
+ aodev->num_exec = 0;
+ /* Initialize timer for QEMU Bodge */
+ libxl__ev_time_init(&aodev->timeout);
+ /*
+ * Initialize xs_watch, because it's not used on all possible
+ * execution paths, but it's unconditionally destroyed when finished.
+ */
+ libxl__xswait_init(&aodev->xswait);
+ aodev->active = 1;
+ /* We init this here because we might call device_hotplug_done
+ * without actually calling any hotplug script */
+ libxl__async_exec_init(&aodev->aes);
+ libxl__ev_child_init(&aodev->child);
+
+ libxl__ev_qmp_init(&aodev->qmp);
+}
+
+/* multidev */
+
+void libxl__multidev_begin(libxl__ao *ao, libxl__multidev *multidev)
+{
+ AO_GC;
+
+ multidev->ao = ao;
+ multidev->array = 0;
+ multidev->used = multidev->allocd = 0;
+
+ /* We allocate an aodev to represent the operation of preparing
+ * all of the other operations. This operation is completed when
+ * we have started all the others (ie, when the user calls
+ * _prepared). That arranges automatically that
+ * (i) we do not think we have finished even if one of the
+ * operations completes while we are still preparing
+ * (ii) if we are starting zero operations, we do still
+ * make the callback as soon as we know this fact
+ * (iii) we have a nice consistent way to deal with any
+ * error that might occur while deciding what to initiate
+ */
+ multidev->preparation = libxl__multidev_prepare(multidev);
+}
+
+void libxl__multidev_prepare_with_aodev(libxl__multidev *multidev,
+ libxl__ao_device *aodev) {
+ STATE_AO_GC(multidev->ao);
+
+ aodev->multidev = multidev;
+ aodev->callback = libxl__multidev_one_callback;
+ libxl__prepare_ao_device(ao, aodev);
+
+ if (multidev->used >= multidev->allocd) {
+ multidev->allocd = multidev->used * 2 + 5;
+ GCREALLOC_ARRAY(multidev->array, multidev->allocd);
+ }
+ multidev->array[multidev->used++] = aodev;
+}
+
+libxl__ao_device *libxl__multidev_prepare(libxl__multidev *multidev) {
+ STATE_AO_GC(multidev->ao);
+ libxl__ao_device *aodev;
+
+ GCNEW(aodev);
+ libxl__multidev_prepare_with_aodev(multidev, aodev);
+
+ return aodev;
+}
+
+void libxl__multidev_one_callback(libxl__egc *egc, libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ libxl__multidev *multidev = aodev->multidev;
+ int i, error = 0;
+
+ aodev->active = 0;
+
+ for (i = 0; i < multidev->used; i++) {
+ if (multidev->array[i]->active)
+ return;
+
+ if (multidev->array[i]->rc)
+ error = multidev->array[i]->rc;
+ }
+
+ multidev->callback(egc, multidev, error);
+ return;
+}
+
+void libxl__multidev_prepared(libxl__egc *egc,
+ libxl__multidev *multidev, int rc)
+{
+ multidev->preparation->rc = rc;
+ libxl__multidev_one_callback(egc, multidev->preparation);
+}
+
+/******************************************************************************/
+
+int libxl__device_destroy(libxl__gc *gc, libxl__device *dev)
+{
+ const char *be_path = NULL;
+ const char *fe_path = NULL;
+ const char *libxl_path = libxl__device_libxl_path(gc, dev);
+ xs_transaction_t t = 0;
+ int rc;
+ uint32_t domid;
+ int libxl_only = dev->backend_kind == LIBXL__DEVICE_KIND_NONE;
+
+ if (!libxl_only) {
+ be_path = libxl__device_backend_path(gc, dev);
+ fe_path = libxl__device_frontend_path(gc, dev);
+ }
+
+ rc = libxl__get_domid(gc, &domid);
+ if (rc) goto out;
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+
+ if (domid == LIBXL_TOOLSTACK_DOMID) {
+ /*
+ * The toolstack domain is in charge of removing the
+ * frontend and libxl paths.
+ */
+ if (!libxl_only)
+ libxl__xs_path_cleanup(gc, t, fe_path);
+ libxl__xs_path_cleanup(gc, t, libxl_path);
+ }
+ if (dev->backend_domid == domid && !libxl_only) {
+ /*
+ * The driver domain is in charge of removing what it can
+ * from the backend path.
+ */
+ libxl__xs_path_cleanup(gc, t, be_path);
+ }
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc < 0) goto out;
+ }
+
+out:
+ libxl__xs_transaction_abort(gc, &t);
+ return rc;
+}
+
+/* Callback for device destruction */
+
+static void devices_remove_callback(libxl__egc *egc,
+ libxl__multidev *multidev, int rc);
+
+void libxl__devices_destroy(libxl__egc *egc, libxl__devices_remove_state *drs)
+{
+ STATE_AO_GC(drs->ao);
+ uint32_t domid = drs->domid;
+ char *path;
+ unsigned int num_kinds, num_dev_xsentries;
+ char **kinds = NULL, **devs = NULL;
+ int i, j, rc = 0;
+ libxl__device *dev;
+ libxl__multidev *multidev = &drs->multidev;
+ libxl__ao_device *aodev;
+ libxl__device_kind kind;
+
+ libxl__multidev_begin(ao, multidev);
+ multidev->callback = devices_remove_callback;
+
+ path = GCSPRINTF("/libxl/%d/device", domid);
+ kinds = libxl__xs_directory(gc, XBT_NULL, path, &num_kinds);
+ if (!kinds) {
+ if (errno != ENOENT) {
+ LOGE(ERROR, "unable to get xenstore device listing %s", path);
+ goto out;
+ }
+ num_kinds = 0;
+ }
+ for (i = 0; i < num_kinds; i++) {
+ if (libxl__device_kind_from_string(kinds[i], &kind))
+ continue;
+
+ path = GCSPRINTF("/libxl/%d/device/%s", domid, kinds[i]);
+ devs = libxl__xs_directory(gc, XBT_NULL, path, &num_dev_xsentries);
+ if (!devs)
+ continue;
+ for (j = 0; j < num_dev_xsentries; j++) {
+ path = GCSPRINTF("/libxl/%d/device/%s/%s/backend",
+ domid, kinds[i], devs[j]);
+ path = libxl__xs_read(gc, XBT_NULL, path);
+ GCNEW(dev);
+ if (path && libxl__parse_backend_path(gc, path, dev) == 0) {
+ dev->domid = domid;
+ dev->kind = kind;
+ dev->devid = atoi(devs[j]);
+ if (dev->backend_kind == LIBXL__DEVICE_KIND_CONSOLE ||
+ dev->backend_kind == LIBXL__DEVICE_KIND_VUART) {
+ /* Currently console devices can be destroyed
+ * synchronously by just removing xenstore entries,
+ * this is what libxl__device_destroy does.
+ */
+ libxl__device_destroy(gc, dev);
+ continue;
+ }
+ aodev = libxl__multidev_prepare(multidev);
+ aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+ aodev->dev = dev;
+ aodev->force = drs->force;
+ if (dev->kind == LIBXL__DEVICE_KIND_VUSB)
+ libxl__initiate_device_usbctrl_remove(egc, aodev);
+ else
+ libxl__initiate_device_generic_remove(egc, aodev);
+ }
+ }
+ }
+
+out:
+ libxl__multidev_prepared(egc, multidev, rc);
+}
+
+/* Callbacks for device related operations */
+
+/*
+ * device_backend_callback is the main callback entry point, for both device
+ * addition and removal. It gets called if we reach the desired state
+ * (XenbusStateClosed or XenbusStateInitWait). After that, all this
+ * functions get called in the order displayed below.
+ *
+ * If new device types are added, they should only need to modify the
+ * specific hotplug scripts call, which can be found in each OS specific
+ * file. If this new devices don't need a hotplug script, no modification
+ * should be needed.
+ */
+
+/* This callback is part of the Qemu devices Badge */
+static void device_qemu_timeout(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs, int rc);
+
+static void device_backend_callback(libxl__egc *egc, libxl__ev_devstate *ds,
+ int rc);
+
+static void device_backend_cleanup(libxl__gc *gc,
+ libxl__ao_device *aodev);
+
+static void device_hotplug(libxl__egc *egc, libxl__ao_device *aodev);
+
+static void device_hotplug_child_death_cb(libxl__egc *egc,
+ libxl__async_exec_state *aes,
+ int rc, int status);
+
+static void device_destroy_be_watch_cb(libxl__egc *egc,
+ libxl__xswait_state *xswait,
+ int rc, const char *data);
+
+static void device_hotplug_done(libxl__egc *egc, libxl__ao_device *aodev);
+
+static void device_hotplug_clean(libxl__gc *gc, libxl__ao_device *aodev);
+
+void libxl__wait_device_connection(libxl__egc *egc, libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ char *be_path = libxl__device_backend_path(gc, aodev->dev);
+ char *state_path = GCSPRINTF("%s/state", be_path);
+ int rc = 0;
+
+ if (QEMU_BACKEND(aodev->dev)) {
+ /*
+ * If Qemu is not running, there's no point in waiting for
+ * it to change the state of the device.
+ *
+ * If Qemu is running, it will set the state of the device to
+ * 4 directly, without waiting in state 2 for any hotplug execution.
+ */
+ device_hotplug(egc, aodev);
+ return;
+ }
+
+ rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds,
+ device_backend_callback,
+ state_path, XenbusStateInitWait,
+ LIBXL_INIT_TIMEOUT * 1000);
+ if (rc) {
+ LOGD(ERROR, aodev->dev->domid, "unable to initialize device %s", be_path);
+ goto out;
+ }
+
+ return;
+
+out:
+ aodev->rc = rc;
+ device_hotplug_done(egc, aodev);
+ return;
+}
+
+void libxl__initiate_device_generic_remove(libxl__egc *egc,
+ libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ xs_transaction_t t = 0;
+ char *be_path = libxl__device_backend_path(gc, aodev->dev);
+ char *state_path = GCSPRINTF("%s/state", be_path);
+ char *online_path = GCSPRINTF("%s/online", be_path);
+ const char *state;
+ libxl_dominfo info;
+ uint32_t my_domid, domid = aodev->dev->domid;
+ int rc = 0;
+
+ libxl_dominfo_init(&info);
+
+ rc = libxl__get_domid(gc, &my_domid);
+ if (rc) {
+ LOGD(ERROR, domid, "unable to get my domid");
+ goto out;
+ }
+
+ if (my_domid == LIBXL_TOOLSTACK_DOMID) {
+ rc = libxl_domain_info(CTX, &info, domid);
+ if (rc) {
+ LOGD(ERROR, domid, "unable to get info for domain %d", domid);
+ goto out;
+ }
+ if (QEMU_BACKEND(aodev->dev) &&
+ (info.paused || info.dying || info.shutdown)) {
+ /*
+ * TODO: 4.2 Bodge due to QEMU, see comment on top of
+ * libxl__initiate_device_generic_remove in libxl_internal.h
+ */
+ rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
+ device_qemu_timeout,
+ LIBXL_QEMU_BODGE_TIMEOUT * 1000);
+ if (rc) {
+ LOGD(ERROR, domid, "unable to register timeout for Qemu device %s",
+ be_path);
+ goto out;
+ }
+ goto out_success;
+ }
+ }
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) {
+ LOGD(ERROR, domid, "unable to start transaction");
+ goto out;
+ }
+
+ if (aodev->force.flag == LIBXL__FORCE_ON)
+ libxl__xs_path_cleanup(gc, t,
+ libxl__device_frontend_path(gc, aodev->dev));
+
+ rc = libxl__xs_read_checked(gc, t, state_path, &state);
+ if (rc) {
+ LOGD(ERROR, domid, "unable to read device state from path %s", state_path);
+ goto out;
+ }
+
+ /* if state_path is empty, assume backend is gone (backend domain
+ * shutdown?), cleanup frontend only; rc=0 */
+ if (!state) {
+ LOG(INFO, "backend %s already removed, cleanup frontend only", be_path);
+ goto out;
+ }
+
+ rc = libxl__xs_write_checked(gc, t, online_path, "0");
+ if (rc)
+ goto out;
+
+ /*
+ * Check if device is already in "closed" state, in which case
+ * it should not be changed.
+ */
+ if (state && atoi(state) != XenbusStateClosed) {
+ rc = libxl__xs_write_checked(gc, t, state_path, GCSPRINTF("%d", XenbusStateClosing));
+ if (rc) {
+ LOGD(ERROR, domid, "unable to write to xenstore path %s", state_path);
+ goto out;
+ }
+ }
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc < 0) goto out;
+ }
+
+ rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds,
+ device_backend_callback,
+ state_path, XenbusStateClosed,
+ LIBXL_DESTROY_TIMEOUT * 1000);
+ if (rc) {
+ LOGD(ERROR, domid, "unable to remove device %s", be_path);
+ goto out;
+ }
+
+out_success:
+ libxl_dominfo_dispose(&info);
+ return;
+
+out:
+ aodev->rc = rc;
+ libxl_dominfo_dispose(&info);
+ libxl__xs_transaction_abort(gc, &t);
+ device_hotplug_done(egc, aodev);
+ return;
+}
+
+static void device_qemu_timeout(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs, int rc)
+{
+ libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
+ STATE_AO_GC(aodev->ao);
+ char *be_path = libxl__device_backend_path(gc, aodev->dev);
+ char *state_path = GCSPRINTF("%s/state", be_path);
+ const char *xs_state;
+ xs_transaction_t t = 0;
+
+ if (rc != ERROR_TIMEDOUT)
+ goto out;
+
+ libxl__ev_time_deregister(gc, &aodev->timeout);
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) {
+ LOGD(ERROR, aodev->dev->domid, "unable to start transaction");
+ goto out;
+ }
+
+ /*
+ * Check that the state path exists and is actually different than
+ * 6 before unconditionally setting it. If Qemu runs on a driver
+ * domain it is possible that the driver domain has already cleaned
+ * the backend path if the device has reached state 6.
+ */
+ rc = libxl__xs_read_checked(gc, XBT_NULL, state_path, &xs_state);
+ if (rc) goto out;
+
+ if (xs_state && atoi(xs_state) != XenbusStateClosed) {
+ rc = libxl__xs_write_checked(gc, XBT_NULL, state_path, GCSPRINTF("%d", XenbusStateClosed));
+ if (rc) goto out;
+ }
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc < 0) goto out;
+ }
+
+ device_hotplug(egc, aodev);
+ return;
+
+out:
+ libxl__xs_transaction_abort(gc, &t);
+ aodev->rc = rc;
+ device_hotplug_done(egc, aodev);
+}
+
+static void device_backend_callback(libxl__egc *egc, libxl__ev_devstate *ds,
+ int rc) {
+ libxl__ao_device *aodev = CONTAINER_OF(ds, *aodev, backend_ds);
+ STATE_AO_GC(aodev->ao);
+
+ LOGD(DEBUG, aodev->dev->domid, "calling device_backend_cleanup");
+ device_backend_cleanup(gc, aodev);
+
+ if (rc == ERROR_TIMEDOUT &&
+ aodev->action == LIBXL__DEVICE_ACTION_REMOVE &&
+ aodev->force.flag == LIBXL__FORCE_AUTO) {
+ LOGD(DEBUG, aodev->dev->domid, "Timeout reached, initiating forced remove");
+ aodev->force.flag = LIBXL__FORCE_ON;
+ libxl__initiate_device_generic_remove(egc, aodev);
+ return;
+ }
+
+ if (rc) {
+ LOGD(ERROR, aodev->dev->domid, "unable to %s device with path %s",
+ libxl__device_action_to_string(aodev->action),
+ libxl__device_backend_path(gc, aodev->dev));
+ goto out;
+ }
+
+ device_hotplug(egc, aodev);
+ return;
+
+out:
+ aodev->rc = rc;
+ device_hotplug_done(egc, aodev);
+ return;
+}
+
+static void device_backend_cleanup(libxl__gc *gc, libxl__ao_device *aodev)
+{
+ if (!aodev) return;
+ libxl__ev_devstate_cancel(gc, &aodev->backend_ds);
+}
+
+static void device_hotplug(libxl__egc *egc, libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ libxl__async_exec_state *aes = &aodev->aes;
+ char *be_path = libxl__device_backend_path(gc, aodev->dev);
+ char **args = NULL, **env = NULL;
+ int rc = 0;
+ int hotplug, nullfd = -1;
+ uint32_t domid;
+
+ /*
+ * If device is attached from a driver domain don't try to execute
+ * hotplug scripts
+ */
+ rc = libxl__get_domid(gc, &domid);
+ if (rc) {
+ LOGD(ERROR, aodev->dev->domid, "Failed to get domid");
+ goto out;
+ }
+ if (aodev->dev->backend_domid != domid) {
+ LOGD(DEBUG, aodev->dev->domid,
+ "Backend domid %d, domid %d, assuming driver domains",
+ aodev->dev->backend_domid, domid);
+
+ if (aodev->action != LIBXL__DEVICE_ACTION_REMOVE) {
+ LOG(DEBUG, "Not a remove, not executing hotplug scripts");
+ goto out;
+ }
+
+ aodev->xswait.ao = ao;
+ aodev->xswait.what = "removal of backend path";
+ aodev->xswait.path = be_path;
+ aodev->xswait.timeout_ms = LIBXL_DESTROY_TIMEOUT * 1000;
+ aodev->xswait.callback = device_destroy_be_watch_cb;
+ rc = libxl__xswait_start(gc, &aodev->xswait);
+ if (rc) {
+ LOGD(ERROR, aodev->dev->domid,
+ "Setup of backend removal watch failed (path %s)", be_path);
+ goto out;
+ }
+
+ return;
+ }
+
+ /* Check if we have to execute hotplug scripts for this device
+ * and return the necessary args/env vars for execution */
+ hotplug = libxl__get_hotplug_script_info(gc, aodev->dev, &args, &env,
+ aodev->action,
+ aodev->num_exec);
+ switch (hotplug) {
+ case 0:
+ /* no hotplug script to execute */
+ LOGD(DEBUG, aodev->dev->domid, "No hotplug script to execute");
+ goto out;
+ case 1:
+ /* execute hotplug script */
+ break;
+ default:
+ /* everything else is an error */
+ LOGD(ERROR, aodev->dev->domid,
+ "unable to get args/env to execute hotplug script for "
+ "device %s", libxl__device_backend_path(gc, aodev->dev));
+ rc = hotplug;
+ goto out;
+ }
+
+ assert(args != NULL);
+ LOGD(DEBUG, aodev->dev->domid, "calling hotplug script: %s %s", args[0], args[1]);
+ LOGD(DEBUG, aodev->dev->domid, "extra args:");
+ {
+ const char *arg;
+ unsigned int x;
+
+ for (x = 2; (arg = args[x]); x++)
+ LOGD(DEBUG, aodev->dev->domid, "\t%s", arg);
+ }
+ LOGD(DEBUG, aodev->dev->domid, "env:");
+ if (env != NULL) {
+ const char *k, *v;
+ unsigned int x;
+
+ for (x = 0; (k = env[x]); x += 2) {
+ v = env[x+1];
+ LOGD(DEBUG, aodev->dev->domid, "\t%s: %s", k, v);
+ }
+ }
+
+ nullfd = open("/dev/null", O_RDONLY);
+ if (nullfd < 0) {
+ LOGD(ERROR, aodev->dev->domid, "unable to open /dev/null for hotplug script");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ aes->ao = ao;
+ aes->what = GCSPRINTF("%s %s", args[0], args[1]);
+ aes->env = env;
+ aes->args = args;
+ aes->callback = device_hotplug_child_death_cb;
+ aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
+ aes->stdfds[0] = nullfd;
+ aes->stdfds[1] = 2;
+ aes->stdfds[2] = -1;
+
+ rc = libxl__async_exec_start(aes);
+ if (rc)
+ goto out;
+
+ close(nullfd);
+ assert(libxl__async_exec_inuse(&aodev->aes));
+
+ return;
+
+out:
+ if (nullfd >= 0) close(nullfd);
+ aodev->rc = rc;
+ device_hotplug_done(egc, aodev);
+ return;
+}
+
+static void device_hotplug_child_death_cb(libxl__egc *egc,
+ libxl__async_exec_state *aes,
+ int rc, int status)
+{
+ libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
+ STATE_AO_GC(aodev->ao);
+ char *be_path = libxl__device_backend_path(gc, aodev->dev);
+ char *hotplug_error;
+
+ device_hotplug_clean(gc, aodev);
+
+ if (status && !rc) {
+ hotplug_error = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/hotplug-error", be_path));
+ if (hotplug_error)
+ LOG(ERROR, "script: %s", hotplug_error);
+ rc = ERROR_FAIL;
+ }
+
+ if (rc) {
+ if (!aodev->rc)
+ aodev->rc = rc;
+ if (aodev->action == LIBXL__DEVICE_ACTION_ADD)
+ /*
+ * Only fail on device connection, on disconnection
+ * ignore error, and continue with the remove process
+ */
+ goto error;
+ }
+
+ /* Increase num_exec and call hotplug scripts again if necessary
+ * If no more executions are needed, device_hotplug will call
+ * device_hotplug_done breaking the loop.
+ */
+ aodev->num_exec++;
+ device_hotplug(egc, aodev);
+
+ return;
+
+error:
+ assert(aodev->rc);
+ device_hotplug_done(egc, aodev);
+}
+
+static void device_destroy_be_watch_cb(libxl__egc *egc,
+ libxl__xswait_state *xswait,
+ int rc, const char *dir)
+{
+ libxl__ao_device *aodev = CONTAINER_OF(xswait, *aodev, xswait);
+ STATE_AO_GC(aodev->ao);
+
+ if (rc) {
+ if (rc == ERROR_TIMEDOUT)
+ LOGD(ERROR, aodev->dev->domid,
+ "timed out while waiting for %s to be removed",
+ xswait->path);
+ aodev->rc = rc;
+ goto out;
+ }
+
+ if (dir) {
+ /* backend path still exists, wait a little longer... */
+ return;
+ }
+
+out:
+ /* We are done, backend path no longer exists */
+ device_hotplug_done(egc, aodev);
+}
+
+static void device_hotplug_done(libxl__egc *egc, libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ int rc;
+
+ device_hotplug_clean(gc, aodev);
+
+ /* Clean xenstore if it's a disconnection */
+ if (aodev->action == LIBXL__DEVICE_ACTION_REMOVE &&
+ (aodev->force.flag == LIBXL__FORCE_ON || !aodev->rc)) {
+ rc = libxl__device_destroy(gc, aodev->dev);
+ if (!aodev->rc)
+ aodev->rc = rc;
+ }
+
+ aodev->callback(egc, aodev);
+ return;
+}
+
+static void device_hotplug_clean(libxl__gc *gc, libxl__ao_device *aodev)
+{
+ /* Clean events and check reentrancy */
+ libxl__ev_time_deregister(gc, &aodev->timeout);
+ libxl__xswait_stop(gc, &aodev->xswait);
+ assert(!libxl__async_exec_inuse(&aodev->aes));
+}
+
+static void devices_remove_callback(libxl__egc *egc,
+ libxl__multidev *multidev, int rc)
+{
+ libxl__devices_remove_state *drs = CONTAINER_OF(multidev, *drs, multidev);
+ STATE_AO_GC(drs->ao);
+
+ drs->callback(egc, drs, rc);
+ return;
+}
+
+int libxl__wait_for_device_model_deprecated(libxl__gc *gc,
+ uint32_t domid, char *state,
+ libxl__spawn_starting *spawning,
+ int (*check_callback)(libxl__gc *gc,
+ uint32_t domid,
+ const char *state,
+ void *userdata),
+ void *check_callback_userdata)
+{
+ char *path;
+ uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+
+ path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+ return libxl__xenstore_child_wait_deprecated(gc, domid,
+ LIBXL_DEVICE_MODEL_START_TIMEOUT,
+ "Device Model", path, state, spawning,
+ check_callback, check_callback_userdata);
+}
+
+int libxl__wait_for_backend(libxl__gc *gc, const char *be_path,
+ const char *state)
+{
+ int watchdog = 100;
+ const char *p, *path = GCSPRINTF("%s/state", be_path);
+ int rc;
+
+ while (watchdog-- > 0) {
+ rc = libxl__xs_read_checked(gc, XBT_NULL, path, &p);
+ if (rc) return rc;
+
+ if (p == NULL) {
+ LOG(ERROR, "Backend %s does not exist", be_path);
+ return ERROR_FAIL;
+ }
+
+ if (!strcmp(p, state))
+ return 0;
+
+ usleep(100000);
+ }
+
+ LOG(ERROR, "Backend %s not ready", be_path);
+ return ERROR_FAIL;
+}
+
+/* generic callback for devices that only need to set ao_complete */
+void device_addrm_aocomplete(libxl__egc *egc, libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+
+ if (aodev->rc) {
+ if (aodev->dev) {
+ LOGD(ERROR, aodev->dev->domid, "Unable to %s %s with id %u",
+ libxl__device_action_to_string(aodev->action),
+ libxl__device_kind_to_string(aodev->dev->kind),
+ aodev->dev->devid);
+ } else {
+ LOG(ERROR, "unable to %s device",
+ libxl__device_action_to_string(aodev->action));
+ }
+ goto out;
+ }
+
+out:
+ libxl__ao_complete(egc, ao, aodev->rc);
+ return;
+}
+
+/* common function to get next device id */
+int libxl__device_nextid(libxl__gc *gc, uint32_t domid,
+ libxl__device_kind device)
+{
+ char *libxl_dom_path, **l;
+ unsigned int nb;
+ int nextid = -1;
+
+ if (!(libxl_dom_path = libxl__xs_libxl_path(gc, domid)))
+ return nextid;
+
+ l = libxl__xs_directory(gc, XBT_NULL,
+ GCSPRINTF("%s/device/%s", libxl_dom_path,
+ libxl__device_kind_to_string(device)), &nb);
+ if (l == NULL || nb == 0)
+ nextid = 0;
+ else
+ nextid = strtoul(l[nb - 1], NULL, 10) + 1;
+
+ return nextid;
+}
+
+static void device_complete(libxl__egc *egc, libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+
+ LOG(DEBUG, "device %s %s %s",
+ libxl__device_backend_path(gc, aodev->dev),
+ libxl__device_action_to_string(aodev->action),
+ aodev->rc ? "failed" : "succeed");
+
+ libxl__nested_ao_free(aodev->ao);
+}
+
+static void qdisk_spawn_outcome(libxl__egc *egc, libxl__dm_spawn_state *dmss,
+ int rc)
+{
+ STATE_AO_GC(dmss->spawn.ao);
+
+ LOGD(DEBUG, dmss->guest_domid, "qdisk backend spawn %s",
+ rc ? "failed" : "succeed");
+
+ libxl__nested_ao_free(dmss->spawn.ao);
+}
+
+/*
+ * Data structures used to track devices handled by driver domains
+ */
+
+/*
+ * Structure that describes a device handled by a driver domain
+ */
+typedef struct libxl__ddomain_device {
+ libxl__device *dev;
+ LIBXL_SLIST_ENTRY(struct libxl__ddomain_device) next;
+} libxl__ddomain_device;
+
+/*
+ * Structure that describes a domain and it's associated devices
+ */
+typedef struct libxl__ddomain_guest {
+ uint32_t domid;
+ int num_qdisks;
+ LIBXL_SLIST_HEAD(, struct libxl__ddomain_device) devices;
+ LIBXL_SLIST_ENTRY(struct libxl__ddomain_guest) next;
+} libxl__ddomain_guest;
+
+/*
+ * Main structure used by a driver domain to keep track of devices
+ * currently in use
+ */
+typedef struct {
+ libxl__ao *ao;
+ libxl__ev_xswatch watch;
+ LIBXL_SLIST_HEAD(, struct libxl__ddomain_guest) guests;
+} libxl__ddomain;
+
+static libxl__ddomain_guest *search_for_guest(libxl__ddomain *ddomain,
+ uint32_t domid)
+{
+ libxl__ddomain_guest *dguest;
+
+ LIBXL_SLIST_FOREACH(dguest, &ddomain->guests, next) {
+ if (dguest->domid == domid)
+ return dguest;
+ }
+ return NULL;
+}
+
+static libxl__ddomain_device *search_for_device(libxl__ddomain_guest *dguest,
+ libxl__device *dev)
+{
+ libxl__ddomain_device *ddev;
+
+ LIBXL_SLIST_FOREACH(ddev, &dguest->devices, next) {
+#define LIBXL_DEVICE_CMP(dev1, dev2, entry) (dev1->entry == dev2->entry)
+ if (LIBXL_DEVICE_CMP(ddev->dev, dev, backend_devid) &&
+ LIBXL_DEVICE_CMP(ddev->dev, dev, backend_domid) &&
+ LIBXL_DEVICE_CMP(ddev->dev, dev, devid) &&
+ LIBXL_DEVICE_CMP(ddev->dev, dev, domid) &&
+ LIBXL_DEVICE_CMP(ddev->dev, dev, backend_kind) &&
+ LIBXL_DEVICE_CMP(ddev->dev, dev, kind))
+ return ddev;
+#undef LIBXL_DEVICE_CMP
+ }
+
+ return NULL;
+}
+
+static void check_and_maybe_remove_guest(libxl__gc *gc,
+ libxl__ddomain *ddomain,
+ libxl__ddomain_guest *dguest)
+{
+ assert(ddomain);
+
+ if (dguest != NULL && LIBXL_SLIST_FIRST(&dguest->devices) == NULL) {
+ LIBXL_SLIST_REMOVE(&ddomain->guests, dguest, libxl__ddomain_guest,
+ next);
+ LOGD(DEBUG, dguest->domid, "Removed domain from the list of active guests");
+ /* Clear any leftovers in libxl/<domid> */
+ libxl__xs_rm_checked(gc, XBT_NULL,
+ GCSPRINTF("libxl/%u", dguest->domid));
+ free(dguest);
+ }
+}
+
+/*
+ * The following comment applies to both add_device and remove_device.
+ *
+ * If the return value is greater than 0, it means there's no ao dispatched,
+ * so the free of the nested ao should be done by the parent when it has
+ * finished.
+ */
+static int add_device(libxl__egc *egc, libxl__ao *ao,
+ libxl__ddomain_guest *dguest,
+ libxl__device *dev)
+{
+ AO_GC;
+ libxl__ao_device *aodev;
+ libxl__ddomain_device *ddev;
+ libxl__dm_spawn_state *dmss;
+ int rc = 0;
+
+ /*
+ * New device addition, allocate a struct to hold it and add it
+ * to the list of active devices for a given guest.
+ */
+ ddev = libxl__zalloc(NOGC, sizeof(*ddev));
+ ddev->dev = libxl__zalloc(NOGC, sizeof(*ddev->dev));
+ *ddev->dev = *dev;
+ LIBXL_SLIST_INSERT_HEAD(&dguest->devices, ddev, next);
+ LOGD(DEBUG, dev->domid, "Added device %s to the list of active devices",
+ libxl__device_backend_path(gc, dev));
+
+ switch(dev->backend_kind) {
+ case LIBXL__DEVICE_KIND_QDISK:
+ if (dguest->num_qdisks == 0) {
+ GCNEW(dmss);
+ dmss->guest_domid = dev->domid;
+ dmss->spawn.ao = ao;
+ dmss->callback = qdisk_spawn_outcome;
+
+ libxl__spawn_qdisk_backend(egc, dmss);
+ }
+ dguest->num_qdisks++;
+ break;
+ default:
+ GCNEW(aodev);
+ libxl__prepare_ao_device(ao, aodev);
+ /*
+ * Clone the libxl__device to avoid races if remove_device is called
+ * before the device addition has finished.
+ */
+ GCNEW(aodev->dev);
+ *aodev->dev = *dev;
+ aodev->action = LIBXL__DEVICE_ACTION_ADD;
+ aodev->callback = device_complete;
+ libxl__wait_device_connection(egc, aodev);
+ break;
+ }
+
+ return rc;
+}
+
+static int remove_device(libxl__egc *egc, libxl__ao *ao,
+ libxl__ddomain_guest *dguest,
+ libxl__ddomain_device *ddev)
+{
+ AO_GC;
+ libxl__device *dev = ddev->dev;
+ libxl__ao_device *aodev;
+ int rc = 0;
+
+ switch(ddev->dev->backend_kind) {
+ case LIBXL__DEVICE_KIND_QDISK:
+ if (--dguest->num_qdisks == 0) {
+ rc = libxl__destroy_qdisk_backend(gc, dev->domid);
+ if (rc)
+ goto out;
+ }
+ libxl__device_destroy(gc, dev);
+ /* Return > 0, no ao has been dispatched */
+ rc = 1;
+ break;
+ default:
+ GCNEW(aodev);
+ libxl__prepare_ao_device(ao, aodev);
+ /*
+ * Clone the libxl__device to avoid races if there's a add_device
+ * running in parallel.
+ */
+ GCNEW(aodev->dev);
+ *aodev->dev = *dev;
+ aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+ aodev->callback = device_complete;
+ libxl__initiate_device_generic_remove(egc, aodev);
+ break;
+ }
+
+ /*
+ * Removal of an active device, remove it from the list and
+ * free it's data structures if they are no longer needed.
+ *
+ * NB: the freeing is safe because all the async ops launched
+ * above or from add_device make a copy of the data they use, so
+ * there's no risk of dereferencing.
+ */
+ LIBXL_SLIST_REMOVE(&dguest->devices, ddev, libxl__ddomain_device,
+ next);
+ LOGD(DEBUG, dev->domid, "Removed device %s from the list of active devices",
+ libxl__device_backend_path(gc, dev));
+
+ free(ddev->dev);
+ free(ddev);
+
+out:
+ return rc;
+}
+
+static void backend_watch_callback(libxl__egc *egc, libxl__ev_xswatch *watch,
+ const char *watch_path,
+ const char *event_path)
+{
+ libxl__ddomain *ddomain = CONTAINER_OF(watch, *ddomain, watch);
+ libxl__ao *nested_ao = libxl__nested_ao_create(ddomain->ao);
+ STATE_AO_GC(nested_ao);
+ char *p, *path;
+ const char *sstate, *sonline;
+ int state, online, rc;
+ libxl__device *dev;
+ libxl__ddomain_device *ddev = NULL;
+ libxl__ddomain_guest *dguest = NULL;
+ bool free_ao = false;
+
+ /* Check if event_path ends with "state" or "online" and truncate it. */
+ path = libxl__strdup(gc, event_path);
+ p = strrchr(path, '/');
+ if (p == NULL)
+ goto skip;
+ if (strcmp(p, "/state") != 0 && strcmp(p, "/online") != 0)
+ goto skip;
+ /* Truncate the string so it points to the backend directory. */
+ *p = '\0';
+
+ /* Fetch the value of the state and online nodes. */
+ rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/state", path),
+ &sstate);
+ if (rc || !sstate)
+ goto skip;
+ state = atoi(sstate);
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/online", path),
+ &sonline);
+ if (rc || !sonline)
+ goto skip;
+ online = atoi(sonline);
+
+ GCNEW(dev);
+ rc = libxl__parse_backend_path(gc, path, dev);
+ if (rc)
+ goto skip;
+
+ dguest = search_for_guest(ddomain, dev->domid);
+ if (dguest == NULL && state == XenbusStateClosed) {
+ /*
+ * Spurious state change, device has already been disconnected
+ * or never attached.
+ */
+ goto skip;
+ }
+ if (dguest == NULL) {
+ /* Create a new guest struct and initialize it */
+ dguest = libxl__zalloc(NOGC, sizeof(*dguest));
+ dguest->domid = dev->domid;
+ LIBXL_SLIST_INIT(&dguest->devices);
+ LIBXL_SLIST_INSERT_HEAD(&ddomain->guests, dguest, next);
+ LOGD(DEBUG, dguest->domid, "Added domain to the list of active guests");
+ }
+ ddev = search_for_device(dguest, dev);
+ if (ddev == NULL && state == XenbusStateClosed) {
+ /*
+ * Spurious state change, device has already been disconnected
+ * or never attached.
+ */
+ goto skip;
+ } else if (ddev == NULL) {
+ rc = add_device(egc, nested_ao, dguest, dev);
+ if (rc > 0)
+ free_ao = true;
+ } else if (state == XenbusStateClosed && online == 0) {
+ rc = remove_device(egc, nested_ao, dguest, ddev);
+ if (rc > 0)
+ free_ao = true;
+ check_and_maybe_remove_guest(gc, ddomain, dguest);
+ }
+
+ if (free_ao)
+ libxl__nested_ao_free(nested_ao);
+
+ return;
+
+skip:
+ libxl__nested_ao_free(nested_ao);
+ check_and_maybe_remove_guest(gc, ddomain, dguest);
+ return;
+}
+
+/* Handler of events for device driver domains */
+int libxl_device_events_handler(libxl_ctx *ctx,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, 0, ao_how);
+ int rc;
+ uint32_t domid;
+ libxl__ddomain ddomain;
+ char *be_path;
+ char **kinds = NULL, **domains = NULL, **devs = NULL;
+ const char *sstate;
+ char *state_path;
+ int state;
+ unsigned int nkinds, ndomains, ndevs;
+ int i, j, k;
+
+ ddomain.ao = ao;
+ LIBXL_SLIST_INIT(&ddomain.guests);
+
+ rc = libxl__get_domid(gc, &domid);
+ if (rc) {
+ LOG(ERROR, "unable to get domain id");
+ goto out;
+ }
+
+ /*
+ * We use absolute paths because we want xswatch to also return
+ * absolute paths that can be parsed by libxl__parse_backend_path.
+ */
+ be_path = GCSPRINTF("/local/domain/%u/backend", domid);
+ rc = libxl__ev_xswatch_register(gc, &ddomain.watch, backend_watch_callback,
+ be_path);
+ if (rc) goto out;
+
+ kinds = libxl__xs_directory(gc, XBT_NULL, be_path, &nkinds);
+ if (kinds) {
+ for (i = 0; i < nkinds; i++) {
+ domains = libxl__xs_directory(gc, XBT_NULL,
+ GCSPRINTF("%s/%s", be_path, kinds[i]), &ndomains);
+ if (!domains)
+ continue;
+ for (j = 0; j < ndomains; j++) {
+ devs = libxl__xs_directory(gc, XBT_NULL,
+ GCSPRINTF("%s/%s/%s", be_path, kinds[i], domains[j]), &ndevs);
+ if (!devs)
+ continue;
+ for (k = 0; k < ndevs; k++) {
+ state_path = GCSPRINTF("%s/%s/%s/%s/state",
+ be_path, kinds[i], domains[j], devs[k]);
+ rc = libxl__xs_read_checked(gc, XBT_NULL, state_path, &sstate);
+ if (rc || !sstate)
+ continue;
+ state = atoi(sstate);
+ if (state == XenbusStateInitWait)
+ backend_watch_callback(egc, &ddomain.watch,
+ be_path, state_path);
+ }
+ }
+ }
+ }
+
+ return AO_INPROGRESS;
+
+out:
+ return AO_CREATE_FAIL(rc);
+}
+
+void device_add_domain_config(libxl__gc *gc, libxl_domain_config *d_config,
+ const libxl__device_type *dt, const void *dev)
+{
+ int *num_dev;
+ unsigned int i;
+ void *item = NULL;
+
+ num_dev = libxl__device_type_get_num(dt, d_config);
+
+ /* Check for existing device */
+ for (i = 0; i < *num_dev; i++) {
+ if (dt->compare(libxl__device_type_get_elem(dt, d_config, i), dev)) {
+ item = libxl__device_type_get_elem(dt, d_config, i);
+ }
+ }
+
+ if (!item) {
+ void **devs = libxl__device_type_get_ptr(dt, d_config);
+ *devs = libxl__realloc(NOGC, *devs,
+ dt->dev_elem_size * (*num_dev + 1));
+ item = libxl__device_type_get_elem(dt, d_config, *num_dev);
+ (*num_dev)++;
+ } else {
+ dt->dispose(item);
+ }
+
+ dt->init(item);
+ dt->copy(CTX, item, dev);
+}
+
+void libxl__device_add_async(libxl__egc *egc, uint32_t domid,
+ const libxl__device_type *dt, void *type,
+ libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ flexarray_t *back;
+ flexarray_t *front, *ro_front;
+ libxl__device *device;
+ xs_transaction_t t = XBT_NULL;
+ libxl_domain_config d_config;
+ void *type_saved;
+ libxl__flock *lock = NULL;
+ int rc;
+
+ libxl_domain_config_init(&d_config);
+
+ type_saved = libxl__malloc(gc, dt->dev_elem_size);
+
+ dt->init(type_saved);
+ dt->copy(CTX, type_saved, type);
+
+ if (dt->set_default) {
+ rc = dt->set_default(gc, domid, type, aodev->update_json);
+ if (rc) goto out;
+ }
+
+ if (dt->update_devid) {
+ rc = dt->update_devid(gc, domid, type);
+ if (rc) goto out;
+ }
+
+ if (dt->update_config)
+ dt->update_config(gc, type_saved, type);
+
+ GCNEW(device);
+ rc = dt->to_device(gc, domid, type, device);
+ if (rc) goto out;
+
+ if (aodev->update_json) {
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
+ device_add_domain_config(gc, &d_config, dt, type_saved);
+
+ rc = libxl__dm_check_start(gc, &d_config, domid);
+ if (rc) goto out;
+ }
+
+ back = flexarray_make(gc, 16, 1);
+ front = flexarray_make(gc, 16, 1);
+ ro_front = flexarray_make(gc, 16, 1);
+
+ flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
+ flexarray_append_pair(back, "online", "1");
+ flexarray_append_pair(back, "state",
+ GCSPRINTF("%d", XenbusStateInitialising));
+
+ flexarray_append_pair(front, "backend-id",
+ GCSPRINTF("%d", device->backend_domid));
+ flexarray_append_pair(front, "state",
+ GCSPRINTF("%d", XenbusStateInitialising));
+
+ if (dt->set_xenstore_config)
+ dt->set_xenstore_config(gc, domid, type, back, front, ro_front);
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+
+ rc = libxl__device_exists(gc, t, device);
+ if (rc < 0) goto out;
+ if (rc == 1) { /* already exists in xenstore */
+ LOGD(ERROR, domid, "device already exists in xenstore");
+ aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
+ rc = ERROR_DEVICE_EXISTS;
+ goto out;
+ }
+
+ if (aodev->update_json) {
+ rc = libxl__set_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+ }
+
+ libxl__device_generic_add(gc, t, device,
+ libxl__xs_kvs_of_flexarray(gc, back),
+ libxl__xs_kvs_of_flexarray(gc, front),
+ libxl__xs_kvs_of_flexarray(gc, ro_front));
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc < 0) goto out;
+ }
+
+ aodev->dev = device;
+ aodev->action = LIBXL__DEVICE_ACTION_ADD;
+ libxl__wait_device_connection(egc, aodev);
+
+ rc = 0;
+
+out:
+ libxl__xs_transaction_abort(gc, &t);
+ if (lock) libxl__unlock_file(lock);
+ dt->dispose(type_saved);
+ libxl_domain_config_dispose(&d_config);
+ aodev->rc = rc;
+ if (rc) aodev->callback(egc, aodev);
+ return;
+}
+
+int libxl__device_add(libxl__gc *gc, uint32_t domid,
+ const libxl__device_type *dt, void *type)
+{
+ flexarray_t *back;
+ flexarray_t *front, *ro_front;
+ libxl__device *device;
+ int rc;
+
+ if (dt->set_default) {
+ rc = dt->set_default(gc, domid, type, false);
+ if (rc) goto out;
+ }
+
+ if (dt->update_devid) {
+ rc = dt->update_devid(gc, domid, type);
+ if (rc) goto out;
+ }
+
+ GCNEW(device);
+ rc = dt->to_device(gc, domid, type, device);
+ if (rc) goto out;
+
+ back = flexarray_make(gc, 16, 1);
+ front = flexarray_make(gc, 16, 1);
+ ro_front = flexarray_make(gc, 16, 1);
+
+ flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
+ flexarray_append_pair(back, "online", "1");
+ flexarray_append_pair(back, "state",
+ GCSPRINTF("%d", XenbusStateInitialising));
+ flexarray_append_pair(front, "backend-id",
+ libxl__sprintf(gc, "%d", device->backend_domid));
+ flexarray_append_pair(front, "state",
+ GCSPRINTF("%d", XenbusStateInitialising));
+
+ if (dt->set_xenstore_config)
+ dt->set_xenstore_config(gc, domid, type, back, front, ro_front);
+
+ rc = libxl__device_generic_add(gc, XBT_NULL, device,
+ libxl__xs_kvs_of_flexarray(gc, back),
+ libxl__xs_kvs_of_flexarray(gc, front),
+ libxl__xs_kvs_of_flexarray(gc, ro_front));
+ if (rc) goto out;
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+void *libxl__device_list(libxl__gc *gc, const libxl__device_type *dt,
+ uint32_t domid, int *num)
+{
+ void *r = NULL;
+ void *list = NULL;
+ void *item = NULL;
+ char *libxl_path;
+ char **dir = NULL;
+ unsigned int ndirs = 0;
+ unsigned int ndevs = 0;
+ int rc;
+
+ *num = 0;
+
+ libxl_path = GCSPRINTF("%s/device/%s",
+ libxl__xs_libxl_path(gc, domid),
+ libxl__device_kind_to_string(dt->type));
+
+ dir = libxl__xs_directory(gc, XBT_NULL, libxl_path, &ndirs);
+
+ if (dir && ndirs) {
+ if (dt->get_num) {
+ if (ndirs != 1) {
+ LOGD(ERROR, domid, "multiple entries in %s\n", libxl_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = dt->get_num(gc, GCSPRINTF("%s/%s", libxl_path, *dir), &ndevs);
+ if (rc) goto out;
+ } else {
+ ndevs = ndirs;
+ }
+ list = libxl__malloc(NOGC, dt->dev_elem_size * ndevs);
+ item = list;
+
+ while (*num < ndevs) {
+ dt->init(item);
+
+ if (dt->from_xenstore) {
+ int nr = dt->get_num ? *num : atoi(*dir);
+ char *device_libxl_path = GCSPRINTF("%s/%s", libxl_path, *dir);
+ rc = dt->from_xenstore(gc, device_libxl_path, nr, item);
+ if (rc) goto out;
+ }
+
+ item = (uint8_t *)item + dt->dev_elem_size;
+ ++(*num);
+ if (!dt->get_num)
+ ++dir;
+ }
+ }
+
+ r = list;
+ list = NULL;
+
+out:
+
+ if (list) {
+ libxl__device_list_free(dt, list, *num);
+ *num = 0;
+ }
+
+ return r;
+}
+
+void libxl__device_list_free(const libxl__device_type *dt,
+ void *list, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ dt->dispose((uint8_t*)list + i * dt->dev_elem_size);
+
+ free(list);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+#define BACKEND_STRING_SIZE 5
+
+static void disk_eject_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
+ const char *wpath, const char *epath) {
+ EGC_GC;
+ libxl_evgen_disk_eject *evg = (void*)w;
+ const char *backend;
+ char *value;
+ char backend_type[BACKEND_STRING_SIZE+1];
+ int rc;
+
+ value = libxl__xs_read(gc, XBT_NULL, wpath);
+
+ if (!value || strcmp(value, "eject"))
+ return;
+
+ if (libxl__xs_printf(gc, XBT_NULL, wpath, "")) {
+ LIBXL__EVENT_DISASTER(gc, "xs_write failed acknowledging eject",
+ errno, LIBXL_EVENT_TYPE_DISK_EJECT);
+ return;
+ }
+
+ libxl_event *ev = NEW_EVENT(egc, DISK_EJECT, evg->domid, evg->user);
+ libxl_device_disk *disk = &ev->u.disk_eject.disk;
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL, evg->be_ptr_path, &backend);
+ if (rc) {
+ LIBXL__EVENT_DISASTER(gc, "xs_read failed reading be_ptr_path",
+ errno, LIBXL_EVENT_TYPE_DISK_EJECT);
+ return;
+ }
+ if (!backend) {
+ /* device has been removed, not simply ejected */
+ return;
+ }
+
+ sscanf(backend,
+ "/local/domain/%d/backend/%" TOSTRING(BACKEND_STRING_SIZE)
+ "[a-z]/%*d/%*d",
+ &disk->backend_domid, backend_type);
+ if (!strcmp(backend_type, "tap") || !strcmp(backend_type, "vbd")) {
+ disk->backend = LIBXL_DISK_BACKEND_TAP;
+ } else if (!strcmp(backend_type, "qdisk")) {
+ disk->backend = LIBXL_DISK_BACKEND_QDISK;
+ } else {
+ disk->backend = LIBXL_DISK_BACKEND_UNKNOWN;
+ }
+
+ disk->pdev_path = strdup(""); /* xxx fixme malloc failure */
+ disk->format = LIBXL_DISK_FORMAT_EMPTY;
+ /* this value is returned to the user: do not free right away */
+ disk->vdev = libxl__strdup(NOGC, evg->vdev);
+ disk->removable = 1;
+ disk->readwrite = 0;
+ disk->is_cdrom = 1;
+
+ libxl__event_occurred(egc, ev);
+}
+
+int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t guest_domid,
+ const char *vdev, libxl_ev_user user,
+ libxl_evgen_disk_eject **evgen_out) {
+ GC_INIT(ctx);
+ CTX_LOCK;
+ int rc;
+ char *path;
+ libxl_evgen_disk_eject *evg = NULL;
+
+ evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; }
+ memset(evg, 0, sizeof(*evg));
+ evg->user = user;
+ evg->domid = guest_domid;
+ LIBXL_LIST_INSERT_HEAD(&CTX->disk_eject_evgens, evg, entry);
+
+ uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid);
+
+ if (!domid)
+ domid = guest_domid;
+
+ int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
+
+ path = GCSPRINTF("%s/eject",
+ libxl__domain_device_frontend_path(gc, domid, devid,
+ LIBXL__DEVICE_KIND_VBD));
+ if (!path) { rc = ERROR_NOMEM; goto out; }
+
+ const char *libxl_path = libxl__domain_device_frontend_path(gc, domid, devid,
+ LIBXL__DEVICE_KIND_VBD);
+ evg->be_ptr_path = libxl__sprintf(NOGC, "%s/backend", libxl_path);
+
+ const char *configured_vdev;
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/dev", libxl_path), &configured_vdev);
+ if (rc) goto out;
+
+ evg->vdev = libxl__strdup(NOGC, configured_vdev);
+
+ rc = libxl__ev_xswatch_register(gc, &evg->watch,
+ disk_eject_xswatch_callback, path);
+ if (rc) goto out;
+
+ *evgen_out = evg;
+ CTX_UNLOCK;
+ GC_FREE;
+ return 0;
+
+ out:
+ if (evg)
+ libxl__evdisable_disk_eject(gc, evg);
+ CTX_UNLOCK;
+ GC_FREE;
+ return rc;
+}
+
+void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) {
+ CTX_LOCK;
+
+ LIBXL_LIST_REMOVE(evg, entry);
+
+ if (libxl__ev_xswatch_isregistered(&evg->watch))
+ libxl__ev_xswatch_deregister(gc, &evg->watch);
+
+ free(evg->vdev);
+ free(evg->be_ptr_path);
+ free(evg);
+
+ CTX_UNLOCK;
+}
+
+void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) {
+ GC_INIT(ctx);
+ libxl__evdisable_disk_eject(gc, evg);
+ GC_FREE;
+}
+
+static int libxl__device_disk_setdefault(libxl__gc *gc, uint32_t domid,
+ libxl_device_disk *disk, bool hotplug)
+{
+ int rc;
+
+ libxl_defbool_setdefault(&disk->discard_enable, !!disk->readwrite);
+ libxl_defbool_setdefault(&disk->colo_enable, false);
+ libxl_defbool_setdefault(&disk->colo_restore_enable, false);
+
+ rc = libxl__resolve_domid(gc, disk->backend_domname, &disk->backend_domid);
+ if (rc < 0) return rc;
+
+ /* Force Qdisk backend for CDROM devices of guests with a device model. */
+ if (disk->is_cdrom != 0 &&
+ libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) {
+ if (!(disk->backend == LIBXL_DISK_BACKEND_QDISK ||
+ disk->backend == LIBXL_DISK_BACKEND_UNKNOWN)) {
+ LOGD(ERROR, domid, "Backend for CD devices on HVM guests must be Qdisk");
+ return ERROR_FAIL;
+ }
+ disk->backend = LIBXL_DISK_BACKEND_QDISK;
+ }
+
+ rc = libxl__device_disk_set_backend(gc, disk);
+ return rc;
+}
+
+static int libxl__device_from_disk(libxl__gc *gc, uint32_t domid,
+ const libxl_device_disk *disk,
+ libxl__device *device)
+{
+ int devid;
+
+ devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
+ if (devid==-1) {
+ LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
+ disk->vdev);
+ return ERROR_INVAL;
+ }
+
+ device->backend_domid = disk->backend_domid;
+ device->backend_devid = devid;
+
+ switch (disk->backend) {
+ case LIBXL_DISK_BACKEND_PHY:
+ device->backend_kind = LIBXL__DEVICE_KIND_VBD;
+ break;
+ case LIBXL_DISK_BACKEND_TAP:
+ device->backend_kind = LIBXL__DEVICE_KIND_VBD;
+ break;
+ case LIBXL_DISK_BACKEND_QDISK:
+ device->backend_kind = LIBXL__DEVICE_KIND_QDISK;
+ break;
+ default:
+ LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
+ disk->backend);
+ return ERROR_INVAL;
+ }
+
+ device->domid = domid;
+ device->devid = devid;
+ device->kind = LIBXL__DEVICE_KIND_VBD;
+
+ return 0;
+}
+
+/* Specific function called directly only by local disk attach,
+ * all other users should instead use the regular
+ * libxl__device_disk_add wrapper
+ *
+ * The (optionally) passed function get_vdev will be used to
+ * set the vdev the disk should be attached to. When it is set the caller
+ * must also pass get_vdev_user, which will be passed to get_vdev.
+ *
+ * The passed get_vdev function is also in charge of printing
+ * the corresponding error message when appropiate.
+ */
+static void device_disk_add(libxl__egc *egc, uint32_t domid,
+ libxl_device_disk *disk,
+ libxl__ao_device *aodev,
+ char *get_vdev(libxl__gc *, void *,
+ xs_transaction_t),
+ void *get_vdev_user)
+{
+ STATE_AO_GC(aodev->ao);
+ flexarray_t *front = NULL;
+ flexarray_t *back = NULL;
+ char *dev = NULL, *script;
+ libxl__device *device;
+ int rc;
+ libxl_ctx *ctx = gc->owner;
+ xs_transaction_t t = XBT_NULL;
+ libxl_domain_config d_config;
+ libxl_device_disk disk_saved;
+ libxl__flock *lock = NULL;
+
+ libxl_domain_config_init(&d_config);
+ libxl_device_disk_init(&disk_saved);
+ libxl_device_disk_copy(ctx, &disk_saved, disk);
+
+ libxl_domain_type type = libxl__domain_type(gc, domid);
+ if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /*
+ * get_vdev != NULL -> local attach
+ * get_vdev == NULL -> block attach
+ *
+ * We don't care about local attach state because it's only
+ * intermediate state.
+ */
+ if (!get_vdev && aodev->update_json) {
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
+ device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
+ &disk_saved);
+
+ rc = libxl__dm_check_start(gc, &d_config, domid);
+ if (rc) goto out;
+ }
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+
+ if (get_vdev) {
+ assert(get_vdev_user);
+ disk->vdev = get_vdev(gc, get_vdev_user, t);
+ if (disk->vdev == NULL) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ rc = libxl__device_disk_setdefault(gc, domid, disk, aodev->update_json);
+ if (rc) goto out;
+
+ front = flexarray_make(gc, 16, 1);
+ back = flexarray_make(gc, 16, 1);
+
+ GCNEW(device);
+ rc = libxl__device_from_disk(gc, domid, disk, device);
+ if (rc != 0) {
+ LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
+ disk->vdev);
+ goto out;
+ }
+
+ rc = libxl__device_exists(gc, t, device);
+ if (rc < 0) goto out;
+ if (rc == 1) { /* already exists in xenstore */
+ LOGD(ERROR, domid, "device already exists in xenstore");
+ aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
+ rc = ERROR_DEVICE_EXISTS;
+ goto out;
+ }
+
+ switch (disk->backend) {
+ case LIBXL_DISK_BACKEND_PHY:
+ dev = disk->pdev_path;
+
+ flexarray_append(back, "params");
+ flexarray_append(back, dev);
+
+ script = libxl__abs_path(gc, disk->script?: "block",
+ libxl__xen_script_dir_path());
+ flexarray_append_pair(back, "script", script);
+
+ assert(device->backend_kind == LIBXL__DEVICE_KIND_VBD);
+ break;
+
+ case LIBXL_DISK_BACKEND_TAP:
+ LOG(ERROR, "blktap is not supported");
+ rc = ERROR_FAIL;
+ goto out;
+ case LIBXL_DISK_BACKEND_QDISK:
+ flexarray_append(back, "params");
+ flexarray_append(back, GCSPRINTF("%s:%s",
+ libxl__device_disk_string_of_format(disk->format),
+ disk->pdev_path ? : ""));
+ if (libxl_defbool_val(disk->colo_enable)) {
+ flexarray_append(back, "colo-host");
+ flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_host));
+ flexarray_append(back, "colo-port");
+ flexarray_append(back, libxl__sprintf(gc, "%d", disk->colo_port));
+ flexarray_append(back, "colo-export");
+ flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_export));
+ flexarray_append(back, "active-disk");
+ flexarray_append(back, libxl__sprintf(gc, "%s", disk->active_disk));
+ flexarray_append(back, "hidden-disk");
+ flexarray_append(back, libxl__sprintf(gc, "%s", disk->hidden_disk));
+ }
+ assert(device->backend_kind == LIBXL__DEVICE_KIND_QDISK);
+ break;
+ default:
+ LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
+ disk->backend);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ flexarray_append(back, "frontend-id");
+ flexarray_append(back, GCSPRINTF("%d", domid));
+ flexarray_append(back, "online");
+ flexarray_append(back, "1");
+ flexarray_append(back, "removable");
+ flexarray_append(back, GCSPRINTF("%d", (disk->removable) ? 1 : 0));
+ flexarray_append(back, "bootable");
+ flexarray_append(back, GCSPRINTF("%d", 1));
+ flexarray_append(back, "state");
+ flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
+ flexarray_append(back, "dev");
+ flexarray_append(back, disk->vdev);
+ flexarray_append(back, "type");
+ flexarray_append(back, libxl__device_disk_string_of_backend(disk->backend));
+ flexarray_append(back, "mode");
+ flexarray_append(back, disk->readwrite ? "w" : "r");
+ flexarray_append(back, "device-type");
+ flexarray_append(back, disk->is_cdrom ? "cdrom" : "disk");
+ if (disk->direct_io_safe) {
+ flexarray_append(back, "direct-io-safe");
+ flexarray_append(back, "1");
+ }
+ flexarray_append_pair(back, "discard-enable",
+ libxl_defbool_val(disk->discard_enable) ?
+ "1" : "0");
+
+ flexarray_append(front, "backend-id");
+ flexarray_append(front, GCSPRINTF("%d", disk->backend_domid));
+ flexarray_append(front, "state");
+ flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
+ flexarray_append(front, "virtual-device");
+ flexarray_append(front, GCSPRINTF("%d", device->devid));
+ flexarray_append(front, "device-type");
+ flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk");
+
+ /*
+ * Old PV kernel disk frontends before 2.6.26 rely on tool stack to
+ * write disk native protocol to frontend node. Xend does this, port
+ * this behaviour to xl.
+ *
+ * New kernels write this node themselves. In that case it just
+ * overwrites an existing node which is OK.
+ */
+ if (type == LIBXL_DOMAIN_TYPE_PV) {
+ const char *protocol =
+ xc_domain_get_native_protocol(ctx->xch, domid);
+ if (protocol) {
+ flexarray_append(front, "protocol");
+ flexarray_append(front, libxl__strdup(gc, protocol));
+ }
+ }
+
+ if (!get_vdev && aodev->update_json) {
+ rc = libxl__set_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+ }
+
+ libxl__device_generic_add(gc, t, device,
+ libxl__xs_kvs_of_flexarray(gc, back),
+ libxl__xs_kvs_of_flexarray(gc, front),
+ NULL);
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc < 0) goto out;
+ }
+
+ aodev->dev = device;
+ aodev->action = LIBXL__DEVICE_ACTION_ADD;
+ libxl__wait_device_connection(egc, aodev);
+
+ rc = 0;
+
+out:
+ libxl__xs_transaction_abort(gc, &t);
+ if (lock) libxl__unlock_file(lock);
+ libxl_device_disk_dispose(&disk_saved);
+ libxl_domain_config_dispose(&d_config);
+ aodev->rc = rc;
+ if (rc) aodev->callback(egc, aodev);
+ return;
+}
+
+static void libxl__device_disk_add(libxl__egc *egc, uint32_t domid,
+ libxl_device_disk *disk,
+ libxl__ao_device *aodev)
+{
+ device_disk_add(egc, domid, disk, aodev, NULL, NULL);
+}
+
+static int libxl__disk_from_xenstore(libxl__gc *gc, const char *libxl_path,
+ libxl_devid devid,
+ libxl_device_disk *disk)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ unsigned int len;
+ char *tmp;
+ int rc;
+
+ const char *backend_path;
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path),
+ &backend_path);
+ if (rc) goto out;
+
+ if (!backend_path) {
+ LOG(ERROR, "disk %s does not exist (no backend path", libxl_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__backendpath_parse_domid(gc, backend_path, &disk->backend_domid);
+ if (rc) {
+ LOG(ERROR, "Unable to fetch device backend domid from %s", backend_path);
+ goto out;
+ }
+
+ /*
+ * "params" may not be present; but everything else must be.
+ * colo releated entries(colo-host, colo-port, colo-export,
+ * active-disk and hidden-disk) are present only if colo is
+ * enabled.
+ */
+ tmp = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/params", libxl_path), &len);
+ if (tmp && strchr(tmp, ':')) {
+ disk->pdev_path = strdup(strchr(tmp, ':') + 1);
+ free(tmp);
+ } else {
+ disk->pdev_path = tmp;
+ }
+
+ tmp = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/colo-host", libxl_path), &len);
+ if (tmp) {
+ libxl_defbool_set(&disk->colo_enable, true);
+ disk->colo_host = tmp;
+
+ tmp = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/colo-port", libxl_path), &len);
+ if (!tmp) {
+ LOG(ERROR, "Missing xenstore node %s/colo-port", libxl_path);
+ goto cleanup;
+ }
+ disk->colo_port = atoi(tmp);
+
+#define XS_READ_COLO(param, item) do { \
+ tmp = xs_read(ctx->xsh, XBT_NULL, \
+ GCSPRINTF("%s/"#param"", libxl_path), &len); \
+ if (!tmp) { \
+ LOG(ERROR, "Missing xenstore node %s/"#param"", libxl_path); \
+ goto cleanup; \
+ } \
+ disk->item = tmp; \
+} while (0)
+ XS_READ_COLO(colo-export, colo_export);
+ XS_READ_COLO(active-disk, active_disk);
+ XS_READ_COLO(hidden-disk, hidden_disk);
+#undef XS_READ_COLO
+ } else {
+ libxl_defbool_set(&disk->colo_enable, false);
+ }
+
+ tmp = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/type", libxl_path));
+ if (!tmp) {
+ LOG(ERROR, "Missing xenstore node %s/type", libxl_path);
+ goto cleanup;
+ }
+ libxl_string_to_backend(ctx, tmp, &(disk->backend));
+
+ disk->vdev = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/dev", libxl_path), &len);
+ if (!disk->vdev) {
+ LOG(ERROR, "Missing xenstore node %s/dev", libxl_path);
+ goto cleanup;
+ }
+
+ tmp = libxl__xs_read(gc, XBT_NULL, libxl__sprintf
+ (gc, "%s/removable", libxl_path));
+ if (!tmp) {
+ LOG(ERROR, "Missing xenstore node %s/removable", libxl_path);
+ goto cleanup;
+ }
+ disk->removable = atoi(tmp);
+
+ tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/mode", libxl_path));
+ if (!tmp) {
+ LOG(ERROR, "Missing xenstore node %s/mode", libxl_path);
+ goto cleanup;
+ }
+ if (!strcmp(tmp, "w"))
+ disk->readwrite = 1;
+ else
+ disk->readwrite = 0;
+
+ tmp = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/device-type", libxl_path));
+ if (!tmp) {
+ LOG(ERROR, "Missing xenstore node %s/device-type", libxl_path);
+ goto cleanup;
+ }
+ disk->is_cdrom = !strcmp(tmp, "cdrom");
+
+ disk->format = LIBXL_DISK_FORMAT_UNKNOWN;
+
+ return 0;
+cleanup:
+ rc = ERROR_FAIL;
+ out:
+ libxl_device_disk_dispose(disk);
+ return rc;
+}
+
+int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid,
+ const char *vdev, libxl_device_disk *disk)
+{
+ GC_INIT(ctx);
+ char *libxl_path;
+ int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
+ int rc = ERROR_FAIL;
+
+ if (devid < 0)
+ return ERROR_INVAL;
+
+ libxl_device_disk_init(disk);
+
+ libxl_path = libxl__domain_device_libxl_path(gc, domid, devid,
+ LIBXL__DEVICE_KIND_VBD);
+
+ rc = libxl__disk_from_xenstore(gc, libxl_path, devid, disk);
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_disk *disk,
+ libxl_diskinfo *diskinfo)
+{
+ GC_INIT(ctx);
+ char *fe_path, *libxl_path;
+ char *val;
+ int rc;
+
+ diskinfo->backend = NULL;
+
+ diskinfo->devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
+
+ /* tap devices entries in xenstore are written as vbd devices. */
+ fe_path = libxl__domain_device_frontend_path(gc, domid, diskinfo->devid,
+ LIBXL__DEVICE_KIND_VBD);
+ libxl_path = libxl__domain_device_libxl_path(gc, domid, diskinfo->devid,
+ LIBXL__DEVICE_KIND_VBD);
+ diskinfo->backend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path), NULL);
+ if (!diskinfo->backend) {
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ rc = libxl__backendpath_parse_domid(gc, diskinfo->backend,
+ &diskinfo->backend_id);
+ if (rc) goto out;
+
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
+ diskinfo->state = val ? strtoul(val, NULL, 10) : -1;
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", fe_path));
+ diskinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
+ diskinfo->rref = val ? strtoul(val, NULL, 10) : -1;
+ diskinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/frontend", libxl_path), NULL);
+ diskinfo->frontend_id = domid;
+
+ GC_FREE;
+ return 0;
+
+ out:
+ free(diskinfo->backend);
+ return rc;
+}
+
+typedef struct {
+ libxl__ao *ao;
+ libxl_domid domid;
+ libxl_device_disk *disk;
+ libxl_device_disk disk_saved;
+ libxl__ev_slowlock qmp_lock;
+ int dm_ver;
+ libxl__ev_time time;
+ libxl__ev_qmp qmp;
+} libxl__cdrom_insert_state;
+
+static void cdrom_insert_lock_acquired(libxl__egc *, libxl__ev_slowlock *,
+ int rc);
+static void cdrom_insert_ejected(libxl__egc *egc, libxl__ev_qmp *,
+ const libxl__json_object *, int rc);
+static void cdrom_insert_addfd_cb(libxl__egc *egc, libxl__ev_qmp *,
+ const libxl__json_object *, int rc);
+static void cdrom_insert_inserted(libxl__egc *egc, libxl__ev_qmp *,
+ const libxl__json_object *, int rc);
+static void cdrom_insert_timout(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc);
+static void cdrom_insert_done(libxl__egc *egc,
+ libxl__cdrom_insert_state *cis,
+ int rc);
+
+int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ int num = 0, i;
+ libxl_device_disk *disks = NULL;
+ int rc;
+ libxl__cdrom_insert_state *cis;
+
+ GCNEW(cis);
+ cis->ao = ao;
+ cis->domid = domid;
+ cis->disk = disk;
+ libxl_device_disk_init(&cis->disk_saved);
+ libxl_device_disk_copy(ctx, &cis->disk_saved, disk);
+ libxl__ev_devlock_init(&cis->qmp_lock);
+ cis->qmp_lock.ao = ao;
+ cis->qmp_lock.domid = domid;
+ libxl__ev_time_init(&cis->time);
+ libxl__ev_qmp_init(&cis->qmp);
+ cis->qmp.ao = ao;
+ cis->qmp.domid = domid;
+ cis->qmp.payload_fd = -1;
+
+ libxl_domain_type type = libxl__domain_type(gc, domid);
+ if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ if (type != LIBXL_DOMAIN_TYPE_HVM) {
+ LOGD(ERROR, domid, "cdrom-insert requires an HVM domain");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ if (libxl_get_stubdom_id(ctx, domid) != 0) {
+ LOGD(ERROR, domid, "cdrom-insert doesn't work for stub domains");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ cis->dm_ver = libxl__device_model_version_running(gc, domid);
+ if (cis->dm_ver == -1) {
+ LOGD(ERROR, domid, "Cannot determine device model version");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ disks = libxl__device_list(gc, &libxl__disk_devtype, domid, &num);
+ for (i = 0; i < num; i++) {
+ if (disks[i].is_cdrom && !strcmp(disk->vdev, disks[i].vdev))
+ {
+ /* Found. Set backend type appropriately. */
+ disk->backend=disks[i].backend;
+ break;
+ }
+ }
+ if (i == num) {
+ LOGD(ERROR, domid, "Virtual device not found");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__device_disk_setdefault(gc, domid, disk, false);
+ if (rc) goto out;
+
+ if (!disk->pdev_path) {
+ disk->pdev_path = libxl__strdup(NOGC, "");
+ disk->format = LIBXL_DISK_FORMAT_EMPTY;
+ }
+
+out:
+ libxl__device_list_free(&libxl__disk_devtype, disks, num);
+ if (rc) {
+ cdrom_insert_done(egc, cis, rc); /* must be last */
+ } else {
+ cis->qmp_lock.callback = cdrom_insert_lock_acquired;
+ libxl__ev_slowlock_lock(egc, &cis->qmp_lock); /* must be last */
+ }
+ return AO_INPROGRESS;
+}
+
+static void cdrom_insert_lock_acquired(libxl__egc *egc,
+ libxl__ev_slowlock *lock,
+ int rc)
+{
+ libxl__cdrom_insert_state *cis = CONTAINER_OF(lock, *cis, qmp_lock);
+ STATE_AO_GC(cis->ao);
+
+ if (rc) goto out;
+
+ rc = libxl__ev_time_register_rel(ao, &cis->time,
+ cdrom_insert_timout,
+ LIBXL_HOTPLUG_TIMEOUT * 1000);
+ if (rc) goto out;
+
+ /* We need to eject the original image first.
+ * JSON is not updated.
+ */
+
+ if (cis->dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+ libxl__json_object *args = NULL;
+ int devid = libxl__device_disk_dev_number(cis->disk->vdev,
+ NULL, NULL);
+
+ QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", devid);
+ cis->qmp.callback = cdrom_insert_ejected;
+ rc = libxl__ev_qmp_send(egc, &cis->qmp, "eject", args);
+ if (rc) goto out;
+ } else {
+ cdrom_insert_ejected(egc, &cis->qmp, NULL, 0); /* must be last */
+ }
+ return;
+
+out:
+ cdrom_insert_done(egc, cis, rc); /* must be last */
+}
+
+static void cdrom_insert_ejected(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *response,
+ int rc)
+{
+ EGC_GC;
+ libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
+ libxl__flock *data_lock = NULL;
+ libxl__device device;
+ const char *be_path, *libxl_path;
+ flexarray_t *empty = NULL;
+ xs_transaction_t t = XBT_NULL;
+ char *tmp;
+ libxl_domain_config d_config;
+ bool has_callback = false;
+
+ /* convenience aliases */
+ libxl_domid domid = cis->domid;
+ libxl_device_disk *disk = cis->disk;
+
+ libxl_domain_config_init(&d_config);
+
+ if (rc) goto out;
+
+ rc = libxl__device_from_disk(gc, domid, disk, &device);
+ if (rc) goto out;
+ be_path = libxl__device_backend_path(gc, &device);
+ libxl_path = libxl__device_libxl_path(gc, &device);
+
+ data_lock = libxl__lock_domain_userdata(gc, domid);
+ if (!data_lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ empty = flexarray_make(gc, 4, 1);
+ flexarray_append_pair(empty, "type",
+ libxl__device_disk_string_of_backend(disk->backend));
+ flexarray_append_pair(empty, "params", "");
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+ /* Sanity check: make sure the device exists before writing here */
+ tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
+ if (!tmp)
+ {
+ LOGD(ERROR, domid, "Internal error: %s does not exist",
+ GCSPRINTF("%s/frontend", libxl_path));
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ char **kvs = libxl__xs_kvs_of_flexarray(gc, empty);
+
+ rc = libxl__xs_writev(gc, t, be_path, kvs);
+ if (rc) goto out;
+
+ rc = libxl__xs_writev(gc, t, libxl_path, kvs);
+ if (rc) goto out;
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc < 0) goto out;
+ }
+
+ /*
+ * Now that the drive is empty, we can insert the new media.
+ */
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
+ device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
+ &cis->disk_saved);
+
+ rc = libxl__dm_check_start(gc, &d_config, domid);
+ if (rc) goto out;
+
+ if (cis->dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN &&
+ disk->format != LIBXL_DISK_FORMAT_EMPTY) {
+ libxl__json_object *args = NULL;
+
+ assert(qmp->payload_fd == -1);
+ qmp->payload_fd = open(disk->pdev_path, O_RDONLY);
+ if (qmp->payload_fd < 0) {
+ LOGED(ERROR, domid, "Failed to open cdrom file %s",
+ disk->pdev_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /* This free form parameter is not use by QEMU or libxl. */
+ QMP_PARAMETERS_SPRINTF(&args, "opaque", "%s:%s",
+ libxl_disk_format_to_string(disk->format),
+ disk->pdev_path);
+ qmp->callback = cdrom_insert_addfd_cb;
+ rc = libxl__ev_qmp_send(egc, qmp, "add-fd", args);
+ if (rc) goto out;
+ has_callback = true;
+ } else {
+ has_callback = false;
+ }
+
+ rc = 0;
+
+out:
+ libxl__xs_transaction_abort(gc, &t);
+ libxl_domain_config_dispose(&d_config);
+ if (data_lock) libxl__unlock_file(data_lock);
+ if (rc) {
+ cdrom_insert_done(egc, cis, rc); /* must be last */
+ } else if (!has_callback) {
+ /* Only called if no asynchronous callback are set. */
+ cdrom_insert_inserted(egc, qmp, NULL, 0); /* must be last */
+ }
+}
+
+static void cdrom_insert_addfd_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *response,
+ int rc)
+{
+ EGC_GC;
+ libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
+ libxl__json_object *args = NULL;
+ const libxl__json_object *o;
+ int devid;
+ int fdset;
+
+ /* convenience aliases */
+ libxl_device_disk *disk = cis->disk;
+
+ close(qmp->payload_fd);
+ qmp->payload_fd = -1;
+
+ if (rc) goto out;
+
+ o = libxl__json_map_get("fdset-id", response, JSON_INTEGER);
+ if (!o) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ fdset = libxl__json_object_get_integer(o);
+
+ devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
+ QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", devid);
+ QMP_PARAMETERS_SPRINTF(&args, "target", "/dev/fdset/%d", fdset);
+ libxl__qmp_param_add_string(gc, &args, "arg",
+ libxl__qemu_disk_format_string(disk->format));
+ qmp->callback = cdrom_insert_inserted;
+ rc = libxl__ev_qmp_send(egc, qmp, "change", args);
+out:
+ if (rc)
+ cdrom_insert_done(egc, cis, rc); /* must be last */
+}
+
+static void cdrom_insert_inserted(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *response,
+ int rc)
+{
+ EGC_GC;
+ libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
+ libxl__flock *data_lock = NULL;
+ libxl_domain_config d_config;
+ flexarray_t *insert = NULL;
+ xs_transaction_t t = XBT_NULL;
+ libxl__device device;
+ const char *be_path, *libxl_path;
+ char *tmp;
+
+ /* convenience aliases */
+ libxl_domid domid = cis->domid;
+ libxl_device_disk *disk = cis->disk;
+
+ libxl_domain_config_init(&d_config);
+
+ if (rc) goto out;
+
+ rc = libxl__device_from_disk(gc, domid, disk, &device);
+ if (rc) goto out;
+ be_path = libxl__device_backend_path(gc, &device);
+ libxl_path = libxl__device_libxl_path(gc, &device);
+
+ data_lock = libxl__lock_domain_userdata(gc, domid);
+ if (!data_lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
+ device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
+ &cis->disk_saved);
+
+ insert = flexarray_make(gc, 4, 1);
+ flexarray_append_pair(insert, "type",
+ libxl__device_disk_string_of_backend(disk->backend));
+ if (disk->format != LIBXL_DISK_FORMAT_EMPTY)
+ flexarray_append_pair(insert, "params",
+ GCSPRINTF("%s:%s",
+ libxl__device_disk_string_of_format(disk->format),
+ disk->pdev_path));
+ else
+ flexarray_append_pair(insert, "params", "");
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+ /* Sanity check: make sure the device exists before writing here */
+ tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
+ if (!tmp)
+ {
+ LOGD(ERROR, domid, "Internal error: %s does not exist",
+ GCSPRINTF("%s/frontend", libxl_path));
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__set_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
+ char **kvs = libxl__xs_kvs_of_flexarray(gc, insert);
+
+ rc = libxl__xs_writev(gc, t, be_path, kvs);
+ if (rc) goto out;
+
+ rc = libxl__xs_writev(gc, t, libxl_path, kvs);
+ if (rc) goto out;
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc < 0) goto out;
+ }
+
+ rc = 0;
+
+out:
+ libxl__xs_transaction_abort(gc, &t);
+ libxl_domain_config_dispose(&d_config);
+ if (data_lock) libxl__unlock_file(data_lock);
+ cdrom_insert_done(egc, cis, rc); /* must be last */
+}
+
+static void cdrom_insert_timout(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ EGC_GC;
+ libxl__cdrom_insert_state *cis = CONTAINER_OF(ev, *cis, time);
+ LOGD(ERROR, cis->domid, "cdrom insertion timed out");
+ cdrom_insert_done(egc, cis, rc);
+}
+
+static void cdrom_insert_done(libxl__egc *egc,
+ libxl__cdrom_insert_state *cis,
+ int rc)
+{
+ EGC_GC;
+
+ libxl__ev_time_deregister(gc, &cis->time);
+ libxl__ev_qmp_dispose(gc, &cis->qmp);
+ if (cis->qmp.payload_fd >= 0) close(cis->qmp.payload_fd);
+ libxl__ev_slowlock_unlock(gc, &cis->qmp_lock);
+ libxl_device_disk_dispose(&cis->disk_saved);
+ libxl__ao_complete(egc, cis->ao, rc);
+}
+
+/* libxl__alloc_vdev only works on the local domain, that is the domain
+ * where the toolstack is running */
+static char * libxl__alloc_vdev(libxl__gc *gc, void *get_vdev_user,
+ xs_transaction_t t)
+{
+ const char *blkdev_start = (const char *) get_vdev_user;
+ int devid = 0, disk = 0, part = 0;
+
+ libxl__device_disk_dev_number(blkdev_start, &disk, &part);
+ if (part != 0) {
+ LOG(ERROR, "blkdev_start is invalid");
+ return NULL;
+ }
+
+ do {
+ devid = libxl__device_disk_dev_number(GCSPRINTF("d%dp0", disk),
+ NULL, NULL);
+ if (devid < 0)
+ return NULL;
+ if (libxl__xs_read(gc, t, GCSPRINTF("%s/backend",
+ libxl__domain_device_libxl_path(gc,
+ LIBXL_TOOLSTACK_DOMID, devid,
+ LIBXL__DEVICE_KIND_VBD))) == NULL) {
+ if (errno == ENOENT)
+ return libxl__devid_to_vdev(gc, devid);
+ else
+ return NULL;
+ }
+ disk++;
+ } while (1);
+ return NULL;
+}
+
+/* Callbacks */
+
+char *libxl__device_disk_find_local_path(libxl__gc *gc,
+ libxl_domid guest_domid,
+ const libxl_device_disk *disk,
+ bool qdisk_direct)
+{
+ char *path = NULL;
+
+ /* No local paths for driver domains */
+ if (disk->backend_domname != NULL) {
+ LOG(DEBUG, "Non-local backend, can't access locally.\n");
+ goto out;
+ }
+
+ /*
+ * If this is in raw format, and we're not using a script or a
+ * driver domain, we can access the target path directly.
+ */
+ if (disk->format == LIBXL_DISK_FORMAT_RAW
+ && disk->script == NULL) {
+ path = libxl__strdup(gc, disk->pdev_path);
+ LOG(DEBUG, "Directly accessing local RAW disk %s", path);
+ goto out;
+ }
+
+ /*
+ * If we're being called for a qemu path, we can pass the target
+ * string directly as well
+ */
+ if (qdisk_direct && disk->backend == LIBXL_DISK_BACKEND_QDISK) {
+ path = libxl__strdup(gc, disk->pdev_path);
+ LOG(DEBUG, "Directly accessing local QDISK target %s", path);
+ goto out;
+ }
+
+ /*
+ * If the format isn't raw and / or we're using a script, then see
+ * if the script has written a path to the "cooked" node
+ */
+ if (disk->script && guest_domid != INVALID_DOMID) {
+ libxl__device device;
+ char *be_path, *pdpath;
+ int rc;
+
+ LOGD(DEBUG, guest_domid,
+ "Run from a script; checking for physical-device-path (vdev %s)",
+ disk->vdev);
+
+ rc = libxl__device_from_disk(gc, guest_domid, disk, &device);
+ if (rc < 0)
+ goto out;
+
+ be_path = libxl__device_backend_path(gc, &device);
+
+ pdpath = libxl__sprintf(gc, "%s/physical-device-path", be_path);
+
+ LOGD(DEBUG, guest_domid, "Attempting to read node %s", pdpath);
+ path = libxl__xs_read(gc, XBT_NULL, pdpath);
+
+ if (path)
+ LOGD(DEBUG, guest_domid, "Accessing cooked block device %s", path);
+ else
+ LOGD(DEBUG, guest_domid, "No physical-device-path, can't access locally.");
+
+ goto out;
+ }
+
+ out:
+ return path;
+}
+
+static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev);
+
+void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
+ libxl__disk_local_state *dls)
+{
+ STATE_AO_GC(dls->ao);
+ int rc;
+ const libxl_device_disk *in_disk = dls->in_disk;
+ libxl_device_disk *disk = &dls->disk;
+ const char *blkdev_start = dls->blkdev_start;
+
+ assert(in_disk->pdev_path);
+
+ disk->vdev = NULL;
+
+ if (dls->diskpath)
+ LOG(DEBUG, "Strange, dls->diskpath already set: %s", dls->diskpath);
+
+ LOG(DEBUG, "Trying to find local path");
+
+ dls->diskpath = libxl__device_disk_find_local_path(gc, INVALID_DOMID,
+ in_disk, false);
+ if (dls->diskpath) {
+ LOG(DEBUG, "Local path found, executing callback.");
+ dls->callback(egc, dls, 0);
+ } else {
+ LOG(DEBUG, "Local path not found, initiating attach.");
+
+ memcpy(disk, in_disk, sizeof(libxl_device_disk));
+ disk->pdev_path = libxl__strdup(gc, in_disk->pdev_path);
+ if (in_disk->script != NULL)
+ disk->script = libxl__strdup(gc, in_disk->script);
+ disk->vdev = NULL;
+
+ rc = libxl__device_disk_setdefault(gc, LIBXL_TOOLSTACK_DOMID, disk,
+ false);
+ if (rc) goto out;
+
+ libxl__prepare_ao_device(ao, &dls->aodev);
+ dls->aodev.callback = local_device_attach_cb;
+ device_disk_add(egc, LIBXL_TOOLSTACK_DOMID, disk, &dls->aodev,
+ libxl__alloc_vdev, (void *) blkdev_start);
+ }
+
+ return;
+
+ out:
+ assert(rc);
+ dls->rc = rc;
+ libxl__device_disk_local_initiate_detach(egc, dls);
+ dls->callback(egc, dls, rc);
+}
+
+static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
+ char *be_path = NULL;
+ int rc;
+ libxl__device device;
+ libxl_device_disk *disk = &dls->disk;
+
+ rc = aodev->rc;
+ if (rc) {
+ LOGE(ERROR, "unable locally attach device: %s", disk->pdev_path);
+ goto out;
+ }
+
+ rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, &device);
+ if (rc < 0)
+ goto out;
+ be_path = libxl__device_backend_path(gc, &device);
+ rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected));
+ if (rc < 0)
+ goto out;
+
+ dls->diskpath = GCSPRINTF("/dev/%s",
+ libxl__devid_to_localdev(gc, device.devid));
+ LOG(DEBUG, "locally attached disk %s", dls->diskpath);
+
+ dls->callback(egc, dls, 0);
+ return;
+
+ out:
+ assert(rc);
+ dls->rc = rc;
+ libxl__device_disk_local_initiate_detach(egc, dls);
+ return;
+}
+
+/* Callbacks for local detach */
+
+static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev);
+
+void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
+ libxl__disk_local_state *dls)
+{
+ STATE_AO_GC(dls->ao);
+ int rc = 0;
+ libxl_device_disk *disk = &dls->disk;
+ libxl__device *device;
+ libxl__ao_device *aodev = &dls->aodev;
+ libxl__prepare_ao_device(ao, aodev);
+
+ if (!dls->diskpath) goto out;
+
+ if (disk->vdev != NULL) {
+ GCNEW(device);
+ rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID,
+ disk, device);
+ if (rc != 0) goto out;
+
+ aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+ aodev->dev = device;
+ aodev->callback = local_device_detach_cb;
+ aodev->force.flag = LIBXL__FORCE_AUTO;
+ libxl__initiate_device_generic_remove(egc, aodev);
+ return;
+ }
+
+out:
+ aodev->rc = rc;
+ local_device_detach_cb(egc, aodev);
+ return;
+}
+
+static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
+ int rc;
+
+ if (aodev->rc) {
+ LOGED(ERROR, aodev->dev->domid, "Unable to %s %s with id %u",
+ libxl__device_action_to_string(aodev->action),
+ libxl__device_kind_to_string(aodev->dev->kind),
+ aodev->dev->devid);
+ goto out;
+ }
+
+out:
+ /*
+ * If there was an error in dls->rc, it means we have been called from
+ * a failed execution of libxl__device_disk_local_initiate_attach,
+ * so return the original error.
+ */
+ rc = dls->rc ? dls->rc : aodev->rc;
+ dls->callback(egc, dls, rc);
+ return;
+}
+
+/* The following functions are defined:
+ * libxl_device_disk_add
+ * libxl__add_disks
+ * libxl_device_disk_remove
+ * libxl_device_disk_destroy
+ * libxl_device_disk_safe_remove
+ */
+LIBXL_DEFINE_DEVICE_ADD(disk)
+LIBXL_DEFINE_DEVICES_ADD(disk)
+LIBXL_DEFINE_DEVICE_REMOVE(disk)
+LIBXL_DEFINE_DEVICE_SAFE_REMOVE(disk)
+
+static int libxl_device_disk_compare(const libxl_device_disk *d1,
+ const libxl_device_disk *d2)
+{
+ return COMPARE_DISK(d1, d2);
+}
+
+/* Take care of removable device. We maintain invariant in the
+ * insert / remove operation so that:
+ * 1. if xenstore is "empty" while JSON is not, the result
+ * is "empty"
+ * 2. if xenstore has a different media than JSON, use the
+ * one in JSON
+ * 3. if xenstore and JSON have the same media, well, you
+ * know the answer :-)
+ *
+ * Currently there is only one removable device -- CDROM.
+ * Look for libxl_cdrom_insert for reference.
+ */
+static void libxl_device_disk_merge(libxl_ctx *ctx, void *d1, void *d2)
+{
+ GC_INIT(ctx);
+ libxl_device_disk *src = d1;
+ libxl_device_disk *dst = d2;
+
+ if (src->removable) {
+ if (!src->pdev_path || *src->pdev_path == '\0') {
+ /* 1, no media in drive */
+ free(dst->pdev_path);
+ dst->pdev_path = libxl__strdup(NOGC, "");
+ dst->format = LIBXL_DISK_FORMAT_EMPTY;
+ } else {
+ /* 2 and 3, use JSON, no need to touch anything */
+ ;
+ }
+ }
+}
+
+static int libxl_device_disk_dm_needed(void *e, unsigned domid)
+{
+ libxl_device_disk *elem = e;
+
+ return elem->backend == LIBXL_DISK_BACKEND_QDISK &&
+ elem->backend_domid == domid;
+}
+
+LIBXL_DEFINE_DEVICE_LIST(disk)
+
+#define libxl__device_disk_update_devid NULL
+
+DEFINE_DEVICE_TYPE_STRUCT(disk, VBD,
+ .merge = libxl_device_disk_merge,
+ .dm_needed = libxl_device_disk_dm_needed,
+ .from_xenstore = (device_from_xenstore_fn_t)libxl__disk_from_xenstore,
+ .skip_attach = 1,
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2010 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ * Author Gianni Tedesco <gianni.tedesco@citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#include <xenctrl_dom.h>
+#include <xen/hvm/e820.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+static const char *libxl_tapif_script(libxl__gc *gc)
+{
+#if defined(__linux__) || defined(__FreeBSD__)
+ return libxl__strdup(gc, "no");
+#else
+ return GCSPRINTF("%s/qemu-ifup", libxl__xen_script_dir_path());
+#endif
+}
+
+const char *libxl__device_model_savefile(libxl__gc *gc, uint32_t domid)
+{
+ return GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", domid);
+}
+
+static const char *qemu_xen_path(libxl__gc *gc)
+{
+ return QEMU_XEN_PATH;
+}
+
+static int libxl__create_qemu_logfile(libxl__gc *gc, char *name)
+{
+ char *logfile;
+ int rc, logfile_w;
+
+ rc = libxl_create_logfile(CTX, name, &logfile);
+ if (rc) return rc;
+
+ logfile_w = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644);
+
+ if (logfile_w < 0) {
+ LOGE(ERROR, "unable to open Qemu logfile: %s", logfile);
+ free(logfile);
+ return ERROR_FAIL;
+ }
+
+ free(logfile);
+
+ return logfile_w;
+}
+
+/*
+ * userlookup_helper_getpwnam(libxl__gc*, const char *user,
+ * struct passwd **pwd_r);
+ *
+ * userlookup_helper_getpwuid(libxl__gc*, uid_t uid,
+ * struct passwd **pwd_r);
+ *
+ * If the user is found, return 0 and set *pwd_r to the appropriat
+ * value.
+ *
+ * If the user is not found but there are no errors, return 0
+ * and set *pwd_r to NULL.
+ *
+ * On error, return a libxl-style error code.
+ */
+#define DEFINE_USERLOOKUP_HELPER(NAME,SPEC_TYPE,STRUCTNAME,SYSCONF) \
+ static int userlookup_helper_##NAME(libxl__gc *gc, \
+ SPEC_TYPE spec, \
+ struct STRUCTNAME *resultbuf, \
+ struct STRUCTNAME **out) \
+ { \
+ struct STRUCTNAME *resultp = NULL; \
+ char *buf = NULL; \
+ long buf_size; \
+ int r; \
+ \
+ buf_size = sysconf(SYSCONF); \
+ if (buf_size < 0) { \
+ buf_size = 2048; \
+ LOG(DEBUG, \
+ "sysconf failed, setting the initial buffer size to %ld", \
+ buf_size); \
+ } \
+ \
+ while (1) { \
+ buf = libxl__realloc(gc, buf, buf_size); \
+ r = NAME##_r(spec, resultbuf, buf, buf_size, &resultp); \
+ if (r == ERANGE) { \
+ buf_size += 128; \
+ continue; \
+ } \
+ if (r != 0) { \
+ LOGEV(ERROR, r, "Looking up username/uid with " #NAME); \
+ return ERROR_FAIL; \
+ } \
+ *out = resultp; \
+ return 0; \
+ } \
+ }
+
+DEFINE_USERLOOKUP_HELPER(getpwnam, const char*, passwd, _SC_GETPW_R_SIZE_MAX);
+DEFINE_USERLOOKUP_HELPER(getpwuid, uid_t, passwd, _SC_GETPW_R_SIZE_MAX);
+
+static int libxl__domain_get_device_model_uid(libxl__gc *gc,
+ libxl__dm_spawn_state *dmss)
+{
+ int guest_domid = dmss->guest_domid;
+ libxl__domain_build_state *const state = dmss->build_state;
+ const libxl_domain_build_info *b_info = &dmss->guest_config->b_info;
+
+ struct passwd *user_base, user_pwbuf;
+ int rc;
+ char *user;
+ uid_t intended_uid = -1;
+ bool kill_by_uid;
+
+ /* Only qemu-upstream can run as a different uid */
+ if (b_info->device_model_version != LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN)
+ return 0;
+
+ /*
+ * From this point onward, all paths should go through the `out`
+ * label. The invariants should be:
+ * - rc may be 0, or an error code.
+ * - if rc is an error code, user and intended_uid are ignored.
+ * - if rc is 0, user may be set or not set.
+ * - if user is set, then intended_uid must be set to a UID matching
+ * the username `user`, and kill_by_uid must be set to the appropriate
+ * value. intended_uid will be checked for root (0).
+ */
+
+ /*
+ * If device_model_user is present, set `-runas` even if
+ * dm_restrict isn't in use
+ */
+ user = b_info->device_model_user;
+ if (user) {
+ rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base);
+ if (rc)
+ goto out;
+
+ if (!user_base) {
+ LOGD(ERROR, guest_domid, "Couldn't find device_model_user %s",
+ user);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ intended_uid = user_base->pw_uid;
+ kill_by_uid = true;
+ rc = 0;
+ goto out;
+ }
+
+ /*
+ * If dm_restrict isn't set, and we don't have a specified user, don't
+ * bother setting a `-runas` parameter.
+ */
+ if (!libxl_defbool_val(b_info->dm_restrict)) {
+ LOGD(DEBUG, guest_domid,
+ "dm_restrict disabled, starting QEMU as root");
+ user = NULL; /* Should already be null, but just in case */
+ kill_by_uid = false; /* Keep older versions of gcc happy */
+ rc = 0;
+ goto out;
+ }
+
+ /*
+ * dm_restrict is set, but device_model_user isn't set; look for
+ * QEMU_USER_BASE_RANGE
+ */
+ rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE,
+ &user_pwbuf, &user_base);
+ if (rc)
+ goto out;
+ if (user_base) {
+ struct passwd *user_clash, user_clash_pwbuf;
+
+ intended_uid = user_base->pw_uid + guest_domid;
+ rc = userlookup_helper_getpwuid(gc, intended_uid,
+ &user_clash_pwbuf, &user_clash);
+ if (rc)
+ goto out;
+ if (user_clash) {
+ LOGD(ERROR, guest_domid,
+ "wanted to use uid %ld (%s + %d) but that is user %s !",
+ (long)intended_uid, LIBXL_QEMU_USER_RANGE_BASE,
+ guest_domid, user_clash->pw_name);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ LOGD(DEBUG, guest_domid, "using uid %ld", (long)intended_uid);
+ user = GCSPRINTF("%ld:%ld", (long)intended_uid,
+ (long)user_base->pw_gid);
+ kill_by_uid = true;
+ rc = 0;
+ goto out;
+ }
+
+ /*
+ * We couldn't find QEMU_USER_BASE_RANGE; look for
+ * QEMU_USER_SHARED. NB for QEMU_USER_SHARED, all QEMU will run
+ * as the same UID, we can't kill by uid; therefore don't set uid.
+ */
+ user = LIBXL_QEMU_USER_SHARED;
+ rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base);
+ if (rc)
+ goto out;
+ if (user_base) {
+ LOGD(WARN, guest_domid, "Could not find user %s, falling back to %s",
+ LIBXL_QEMU_USER_RANGE_BASE, LIBXL_QEMU_USER_SHARED);
+ intended_uid = user_base->pw_uid;
+ kill_by_uid = false;
+ rc = 0;
+ goto out;
+ }
+
+ /*
+ * dm_depriv is set, but we can't find a non-root uid to run as;
+ * fail domain creation
+ */
+ LOGD(ERROR, guest_domid,
+ "Could not find user %s or range base pseudo-user %s, cannot restrict",
+ LIBXL_QEMU_USER_SHARED, LIBXL_QEMU_USER_RANGE_BASE);
+ rc = ERROR_INVAL;
+
+out:
+ /* First, do a root check if appropriate */
+ if (!rc) {
+ if (user && intended_uid == 0) {
+ LOGD(ERROR, guest_domid, "intended_uid is 0 (root)!");
+ rc = ERROR_INVAL;
+ }
+ }
+
+ /* Then do the final set, if still appropriate */
+ if (!rc && user) {
+ state->dm_runas = user;
+ if (kill_by_uid)
+ state->dm_kill_uid = GCSPRINTF("%ld", (long)intended_uid);
+ }
+
+ return rc;
+}
+
+/*
+ * Look up "reaper UID". If present and non-root, returns 0 and sets
+ * reaper_uid. Otherwise returns libxl-style error.
+ */
+static int libxl__get_reaper_uid(libxl__gc *gc, uid_t *reaper_uid)
+{
+ struct passwd *user_base, user_pwbuf;
+ int rc;
+
+ rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_REAPER,
+ &user_pwbuf, &user_base);
+ /*
+ * Either there was an error, or we found a suitable user; stop
+ * looking
+ */
+ if (rc || user_base)
+ goto out;
+
+ rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE,
+ &user_pwbuf, &user_base);
+ if (rc || user_base)
+ goto out;
+
+ LOG(WARN, "Couldn't find uid for reaper process");
+ rc = ERROR_INVAL;
+
+ out:
+ /* First check to see if the discovered user maps to root */
+ if (!rc) {
+ if (user_base->pw_uid == 0) {
+ LOG(ERROR, "UID for reaper process maps to root!");
+ rc = ERROR_INVAL;
+ }
+ }
+
+ /* If everything is OK, set reaper_uid as appropriate */
+ if (!rc)
+ *reaper_uid = user_base->pw_uid;
+
+ return rc;
+}
+
+const char *libxl__domain_device_model(libxl__gc *gc,
+ const libxl_domain_build_info *info)
+{
+ const char *dm;
+
+ if (libxl_defbool_val(info->device_model_stubdomain))
+ return NULL;
+
+ if (info->device_model) {
+ dm = libxl__strdup(gc, info->device_model);
+ } else {
+ switch (info->device_model_version) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ dm = libxl__abs_path(gc, "qemu-dm", libxl__private_bindir_path());
+ break;
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ dm = qemu_xen_path(gc);
+ break;
+ default:
+ LOG(ERROR, "invalid device model version %d",
+ info->device_model_version);
+ dm = NULL;
+ break;
+ }
+ }
+ return dm;
+}
+
+static int
+libxl__xc_device_get_rdm(libxl__gc *gc,
+ uint32_t flags,
+ uint16_t seg,
+ uint8_t bus,
+ uint8_t devfn,
+ unsigned int *nr_entries,
+ struct xen_reserved_device_memory **xrdm)
+{
+ int rc = 0, r;
+
+ /*
+ * We really can't presume how many entries we can get in advance.
+ */
+ *nr_entries = 0;
+ r = xc_reserved_device_memory_map(CTX->xch, flags, seg, bus, devfn,
+ NULL, nr_entries);
+ assert(r <= 0);
+ /* "0" means we have no any rdm entry. */
+ if (!r) goto out;
+
+ if (errno != ENOBUFS) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ GCNEW_ARRAY(*xrdm, *nr_entries);
+ r = xc_reserved_device_memory_map(CTX->xch, flags, seg, bus, devfn,
+ *xrdm, nr_entries);
+ if (r)
+ rc = ERROR_FAIL;
+
+ out:
+ if (rc) {
+ *nr_entries = 0;
+ *xrdm = NULL;
+ LOG(ERROR, "Could not get reserved device memory maps.");
+ }
+ return rc;
+}
+
+/*
+ * Check whether there exists rdm hole in the specified memory range.
+ * Returns true if exists, else returns false.
+ */
+static bool overlaps_rdm(uint64_t start, uint64_t memsize,
+ uint64_t rdm_start, uint64_t rdm_size)
+{
+ return (start + memsize > rdm_start) && (start < rdm_start + rdm_size);
+}
+
+static void
+add_rdm_entry(libxl__gc *gc, libxl_domain_config *d_config,
+ uint64_t rdm_start, uint64_t rdm_size, int rdm_policy)
+{
+ d_config->rdms = libxl__realloc(NOGC, d_config->rdms,
+ (d_config->num_rdms+1) * sizeof(libxl_device_rdm));
+
+ d_config->rdms[d_config->num_rdms].start = rdm_start;
+ d_config->rdms[d_config->num_rdms].size = rdm_size;
+ d_config->rdms[d_config->num_rdms].policy = rdm_policy;
+ d_config->num_rdms++;
+}
+
+/*
+ * Check reported RDM regions and handle potential gfn conflicts according
+ * to user preferred policy.
+ *
+ * RDM can reside in address space beyond 4G theoretically, but we never
+ * see this in real world. So in order to avoid breaking highmem layout
+ * we don't solve highmem conflict. Note this means highmem rmrr could
+ * still be supported if no conflict.
+ *
+ * But in the case of lowmem, RDM probably scatter the whole RAM space.
+ * Especially multiple RDM entries would worsen this to lead a complicated
+ * memory layout. And then its hard to extend hvm_info_table{} to work
+ * hvmloader out. So here we're trying to figure out a simple solution to
+ * avoid breaking existing layout. So when a conflict occurs,
+ *
+ * #1. Above a predefined boundary (default 2G)
+ * - Move lowmem_end below reserved region to solve conflict;
+ *
+ * #2. Below a predefined boundary (default 2G)
+ * - Check strict/relaxed policy.
+ * "strict" policy leads to fail libxl.
+ * "relaxed" policy issue a warning message and also mask this entry
+ * INVALID to indicate we shouldn't expose this entry to hvmloader.
+ * Note when both policies are specified on a given region, the per-device
+ * policy should override the global policy.
+ */
+int libxl__domain_device_construct_rdm(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ uint64_t rdm_mem_boundary,
+ struct xc_dom_image *dom)
+{
+ int i, j, conflict, rc;
+ struct xen_reserved_device_memory *xrdm = NULL;
+ uint32_t strategy = d_config->b_info.u.hvm.rdm.strategy;
+ uint16_t seg;
+ uint8_t bus, devfn;
+ uint64_t rdm_start, rdm_size;
+ uint64_t highmem_end = dom->highmem_end;
+
+ /*
+ * We just want to construct RDM once since RDM is specific to the
+ * given platform, so this shouldn't change again.
+ */
+ if (d_config->num_rdms)
+ return 0;
+
+ /* Might not expose rdm. */
+ if (strategy == LIBXL_RDM_RESERVE_STRATEGY_IGNORE &&
+ !d_config->num_pcidevs)
+ return 0;
+
+ /* Query all RDM entries in this platform */
+ if (strategy == LIBXL_RDM_RESERVE_STRATEGY_HOST) {
+ unsigned int nr_entries;
+
+ /* Collect all rdm info if exist. */
+ rc = libxl__xc_device_get_rdm(gc, XENMEM_RDM_ALL,
+ 0, 0, 0, &nr_entries, &xrdm);
+ if (rc)
+ goto out;
+ if (!nr_entries)
+ return 0;
+
+ assert(xrdm);
+
+ for (i = 0; i < nr_entries; i++)
+ {
+ add_rdm_entry(gc, d_config,
+ pfn_to_paddr(xrdm[i].start_pfn),
+ pfn_to_paddr(xrdm[i].nr_pages),
+ d_config->b_info.u.hvm.rdm.policy);
+ }
+ }
+
+ /* Query RDM entries per-device */
+ for (i = 0; i < d_config->num_pcidevs; i++) {
+ unsigned int n, nr_entries;
+
+ seg = d_config->pcidevs[i].domain;
+ bus = d_config->pcidevs[i].bus;
+ devfn = PCI_DEVFN(d_config->pcidevs[i].dev,
+ d_config->pcidevs[i].func);
+ nr_entries = 0;
+ rc = libxl__xc_device_get_rdm(gc, 0,
+ seg, bus, devfn, &nr_entries, &xrdm);
+ if (rc)
+ goto out;
+ /* No RDM to associated with this device. */
+ if (!nr_entries)
+ continue;
+
+ assert(xrdm);
+
+ rc = libxl__device_pci_setdefault(gc, DOMID_INVALID,
+ &d_config->pcidevs[i], false);
+ if (rc)
+ goto out;
+
+ for (n = 0; n < nr_entries; ++n) {
+ bool new = true;
+
+ /*
+ * Need to check whether this entry is already saved in the
+ * array. This could come from two cases:
+ *
+ * - user may configure to get all RDMs in this platform,
+ * which is already queried before this point
+ * - or two assigned devices may share one RDM entry
+ *
+ * Different policies may be configured on the same RDM due to
+ * above two cases. But we don't allow to assign such a group
+ * of devices right now so it doesn't come true in our case.
+ */
+ for (j = 0; j < d_config->num_rdms; j++) {
+ if (d_config->rdms[j].start
+ == pfn_to_paddr(xrdm[n].start_pfn))
+ {
+ /*
+ * So the per-device policy always override the
+ * global policy in this case.
+ */
+ d_config->rdms[j].policy
+ = d_config->pcidevs[i].rdm_policy;
+ new = false;
+ break;
+ }
+ }
+
+ if (new)
+ add_rdm_entry(gc, d_config,
+ pfn_to_paddr(xrdm[n].start_pfn),
+ pfn_to_paddr(xrdm[n].nr_pages),
+ d_config->pcidevs[i].rdm_policy);
+ }
+ }
+
+ /*
+ * Next step is to check and avoid potential conflict between RDM
+ * entries and guest RAM. To avoid intrusive impact to existing
+ * memory layout {lowmem, mmio, highmem} which is passed around
+ * various function blocks, below conflicts are not handled which
+ * are rare and handling them would lead to a more scattered
+ * layout:
+ * - RDM in highmem area (>4G)
+ * - RDM lower than a defined memory boundary (e.g. 2G)
+ * Otherwise for conflicts between boundary and 4G, we'll simply
+ * move lowmem end below reserved region to solve conflict.
+ *
+ * If a conflict is detected on a given RDM entry, an error will
+ * be returned if 'strict' policy is specified. Instead, if
+ * 'relaxed' policy specified, this conflict is treated just as a
+ * warning, but we mark this RDM entry as INVALID to indicate that
+ * this entry shouldn't be exposed to hvmloader.
+ *
+ * Firstly we should check the case of rdm < 4G because we may
+ * need to expand highmem_end.
+ */
+ for (i = 0; i < d_config->num_rdms; i++) {
+ rdm_start = d_config->rdms[i].start;
+ rdm_size = d_config->rdms[i].size;
+ conflict = overlaps_rdm(0, dom->lowmem_end, rdm_start, rdm_size);
+
+ if (!conflict)
+ continue;
+
+ /* Just check if RDM > our memory boundary. */
+ if (rdm_start > rdm_mem_boundary) {
+ /*
+ * For HAP guests round down to a 2Mb boundary to allow use
+ * of large page mappings.
+ */
+ if (libxl_defbool_val(d_config->c_info.hap)
+ && rdm_start > MB(2))
+ rdm_start &= ~(MB(2) - 1);
+ /*
+ * We will move downwards lowmem_end so we have to expand
+ * highmem_end.
+ */
+ if (!highmem_end)
+ highmem_end = 1ull << 32;
+ highmem_end += (dom->lowmem_end - rdm_start);
+ /* Now move downwards lowmem_end. */
+ dom->lowmem_end = rdm_start;
+ }
+ }
+
+ /* Sync highmem_end. */
+ dom->highmem_end = highmem_end;
+
+ /*
+ * Finally we can take same policy to check lowmem(< 2G) and
+ * highmem adjusted above.
+ */
+ for (i = 0; i < d_config->num_rdms; i++) {
+ rdm_start = d_config->rdms[i].start;
+ rdm_size = d_config->rdms[i].size;
+ /* Does this entry conflict with lowmem? */
+ conflict = overlaps_rdm(0, dom->lowmem_end,
+ rdm_start, rdm_size);
+ /* Does this entry conflict with highmem? */
+ if (highmem_end)
+ conflict |= overlaps_rdm((1ULL << 32),
+ highmem_end - (1ULL << 32),
+ rdm_start, rdm_size);
+
+ if (!conflict)
+ continue;
+
+ if (d_config->rdms[i].policy == LIBXL_RDM_RESERVE_POLICY_STRICT) {
+ LOG(ERROR, "RDM conflict at 0x%"PRIx64".\n",
+ d_config->rdms[i].start);
+ goto out;
+ } else {
+ LOG(WARN, "Ignoring RDM conflict at 0x%"PRIx64".\n",
+ d_config->rdms[i].start);
+
+ /*
+ * Then mask this INVALID to indicate we shouldn't expose this
+ * to hvmloader.
+ */
+ d_config->rdms[i].policy = LIBXL_RDM_RESERVE_POLICY_INVALID;
+ }
+ }
+
+ return 0;
+
+ out:
+ return ERROR_FAIL;
+}
+
+/* XSA-180 / CVE-2014-3672
+ *
+ * The QEMU shipped with Xen has a bodge. It checks for
+ * XEN_QEMU_CONSOLE_LIMIT to see how much data QEMU is allowed
+ * to write to stderr. We set that to 1MB if it is not set by
+ * system administrator.
+ */
+static void libxl__set_qemu_env_for_xsa_180(libxl__gc *gc,
+ flexarray_t *dm_envs)
+{
+ if (getenv("XEN_QEMU_CONSOLE_LIMIT")) return;
+ flexarray_append_pair(dm_envs, "XEN_QEMU_CONSOLE_LIMIT", "1048576");
+}
+
+const libxl_vnc_info *libxl__dm_vnc(const libxl_domain_config *guest_config)
+{
+ const libxl_vnc_info *vnc = NULL;
+ if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
+ vnc = &guest_config->b_info.u.hvm.vnc;
+ } else if (guest_config->num_vfbs > 0) {
+ vnc = &guest_config->vfbs[0].vnc;
+ }
+ return vnc && libxl_defbool_val(vnc->enable) ? vnc : NULL;
+}
+
+static const libxl_sdl_info *dm_sdl(const libxl_domain_config *guest_config)
+{
+ const libxl_sdl_info *sdl = NULL;
+ if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
+ sdl = &guest_config->b_info.u.hvm.sdl;
+ } else if (guest_config->num_vfbs > 0) {
+ sdl = &guest_config->vfbs[0].sdl;
+ }
+ return sdl && libxl_defbool_val(sdl->enable) ? sdl : NULL;
+}
+
+static const char *dm_keymap(const libxl_domain_config *guest_config)
+{
+ if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
+ return guest_config->b_info.u.hvm.keymap;
+ } else if (guest_config->num_vfbs > 0) {
+ return guest_config->vfbs[0].keymap;
+ } else
+ return NULL;
+}
+
+static int libxl__build_device_model_args_old(libxl__gc *gc,
+ const char *dm, int domid,
+ const libxl_domain_config *guest_config,
+ char ***args, char ***envs,
+ const libxl__domain_build_state *state)
+{
+ const libxl_domain_create_info *c_info = &guest_config->c_info;
+ const libxl_domain_build_info *b_info = &guest_config->b_info;
+ const libxl_device_nic *nics = guest_config->nics;
+ const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
+ const libxl_sdl_info *sdl = dm_sdl(guest_config);
+ const int num_nics = guest_config->num_nics;
+ const char *keymap = dm_keymap(guest_config);
+ int i;
+ flexarray_t *dm_args, *dm_envs;
+ dm_args = flexarray_make(gc, 16, 1);
+ dm_envs = flexarray_make(gc, 16, 1);
+
+ assert(state->dm_monitor_fd == -1);
+
+ libxl__set_qemu_env_for_xsa_180(gc, dm_envs);
+
+ flexarray_vappend(dm_args, dm,
+ "-d", GCSPRINTF("%d", domid), NULL);
+
+ if (c_info->name)
+ flexarray_vappend(dm_args, "-domain-name", c_info->name, NULL);
+
+ if (vnc) {
+ char *vncarg = NULL;
+
+ flexarray_append(dm_args, "-vnc");
+
+ /*
+ * If vnc->listen is present and contains a :, and
+ * - vnc->display is 0, use vnc->listen
+ * - vnc->display is non-zero, be confused
+ * If vnc->listen is present but doesn't, use vnc->listen:vnc->display.
+ * If vnc->listen is not present, use 127.0.0.1:vnc->display
+ * (Remembering that vnc->display already defaults to 0.)
+ */
+ if (vnc->listen) {
+ if (strchr(vnc->listen, ':') != NULL) {
+ if (vnc->display) {
+ LOGD(ERROR, domid, "vncdisplay set, vnclisten contains display");
+ return ERROR_INVAL;
+ }
+ vncarg = vnc->listen;
+ } else {
+ vncarg = GCSPRINTF("%s:%d", vnc->listen, vnc->display);
+ }
+ } else
+ vncarg = GCSPRINTF("127.0.0.1:%d", vnc->display);
+
+ if (vnc->passwd && vnc->passwd[0]) {
+ vncarg = GCSPRINTF("%s,password", vncarg);
+ }
+
+ flexarray_append(dm_args, vncarg);
+
+ if (libxl_defbool_val(vnc->findunused)) {
+ flexarray_append(dm_args, "-vncunused");
+ }
+ } else if (!sdl) {
+ /*
+ * VNC is not enabled by default by qemu-xen-traditional,
+ * however skipping -vnc causes SDL to be
+ * (unexpectedly) enabled by default. If undesired, disable graphics at
+ * all.
+ */
+ flexarray_append(dm_args, "-nographic");
+ }
+
+ if (sdl) {
+ flexarray_append(dm_args, "-sdl");
+ if (!libxl_defbool_val(sdl->opengl)) {
+ flexarray_append(dm_args, "-disable-opengl");
+ }
+ if (sdl->display)
+ flexarray_append_pair(dm_envs, "DISPLAY", sdl->display);
+ if (sdl->xauthority)
+ flexarray_append_pair(dm_envs, "XAUTHORITY", sdl->xauthority);
+ }
+ if (keymap) {
+ flexarray_vappend(dm_args, "-k", keymap, NULL);
+ }
+ if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
+ int ioemu_nics = 0;
+ int nr_set_cpus = 0;
+ char *s;
+
+ if (b_info->kernel) {
+ LOGD(ERROR, domid, "HVM direct kernel boot is not supported by "
+ "qemu-xen-traditional");
+ return ERROR_INVAL;
+ }
+
+ if (b_info->u.hvm.serial || b_info->u.hvm.serial_list) {
+ if ( b_info->u.hvm.serial && b_info->u.hvm.serial_list )
+ {
+ LOGD(ERROR, domid, "Both serial and serial_list set");
+ return ERROR_INVAL;
+ }
+ if (b_info->u.hvm.serial) {
+ flexarray_vappend(dm_args,
+ "-serial", b_info->u.hvm.serial, NULL);
+ } else if (b_info->u.hvm.serial_list) {
+ char **p;
+ for (p = b_info->u.hvm.serial_list;
+ *p;
+ p++) {
+ flexarray_vappend(dm_args,
+ "-serial",
+ *p, NULL);
+ }
+ }
+ }
+
+ if (libxl_defbool_val(b_info->u.hvm.nographic) && (!sdl && !vnc)) {
+ flexarray_append(dm_args, "-nographic");
+ }
+
+ if (b_info->video_memkb) {
+ flexarray_vappend(dm_args, "-videoram",
+ GCSPRINTF("%d", libxl__sizekb_to_mb(b_info->video_memkb)),
+ NULL);
+ }
+
+ switch (b_info->u.hvm.vga.kind) {
+ case LIBXL_VGA_INTERFACE_TYPE_STD:
+ flexarray_append(dm_args, "-std-vga");
+ break;
+ case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
+ break;
+ case LIBXL_VGA_INTERFACE_TYPE_NONE:
+ flexarray_append_pair(dm_args, "-vga", "none");
+ break;
+ case LIBXL_VGA_INTERFACE_TYPE_QXL:
+ break;
+ default:
+ LOGD(ERROR, domid, "Invalid emulated video card specified");
+ return ERROR_INVAL;
+ }
+
+ if (b_info->u.hvm.boot) {
+ flexarray_vappend(dm_args, "-boot", b_info->u.hvm.boot, NULL);
+ }
+ if (libxl_defbool_val(b_info->u.hvm.usb)
+ || b_info->u.hvm.usbdevice
+ || libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
+ if (b_info->u.hvm.usbdevice
+ && libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
+ LOGD(ERROR, domid, "Both usbdevice and usbdevice_list set");
+ return ERROR_INVAL;
+ }
+ flexarray_append(dm_args, "-usb");
+ if (b_info->u.hvm.usbdevice) {
+ flexarray_vappend(dm_args,
+ "-usbdevice", b_info->u.hvm.usbdevice, NULL);
+ } else if (b_info->u.hvm.usbdevice_list) {
+ char **p;
+ for (p = b_info->u.hvm.usbdevice_list;
+ *p;
+ p++) {
+ flexarray_vappend(dm_args,
+ "-usbdevice",
+ *p, NULL);
+ }
+ }
+ }
+ if (b_info->u.hvm.soundhw) {
+ flexarray_vappend(dm_args, "-soundhw", b_info->u.hvm.soundhw, NULL);
+ }
+ if (libxl__acpi_defbool_val(b_info)) {
+ flexarray_append(dm_args, "-acpi");
+ }
+ if (b_info->max_vcpus > 1) {
+ flexarray_vappend(dm_args, "-vcpus",
+ GCSPRINTF("%d", b_info->max_vcpus),
+ NULL);
+ }
+
+ nr_set_cpus = libxl_bitmap_count_set(&b_info->avail_vcpus);
+ s = libxl_bitmap_to_hex_string(CTX, &b_info->avail_vcpus);
+ flexarray_vappend(dm_args, "-vcpu_avail",
+ GCSPRINTF("%s", s), NULL);
+ free(s);
+
+ for (i = 0; i < num_nics; i++) {
+ if (nics[i].nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
+ char *smac = GCSPRINTF(
+ LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nics[i].mac));
+ const char *ifname = libxl__device_nic_devname(gc,
+ domid, nics[i].devid,
+ LIBXL_NIC_TYPE_VIF_IOEMU);
+ flexarray_vappend(dm_args,
+ "-net",
+ GCSPRINTF(
+ "nic,vlan=%d,macaddr=%s,model=%s",
+ nics[i].devid, smac, nics[i].model),
+ "-net",
+ GCSPRINTF(
+ "tap,vlan=%d,ifname=%s,bridge=%s,"
+ "script=%s,downscript=%s",
+ nics[i].devid, ifname, nics[i].bridge,
+ libxl_tapif_script(gc),
+ libxl_tapif_script(gc)),
+ NULL);
+ ioemu_nics++;
+ }
+ }
+ /* If we have no emulated nics, tell qemu not to create any */
+ if ( ioemu_nics == 0 ) {
+ flexarray_vappend(dm_args, "-net", "none", NULL);
+ }
+ if (libxl_defbool_val(b_info->u.hvm.gfx_passthru)) {
+ switch (b_info->u.hvm.gfx_passthru_kind) {
+ case LIBXL_GFX_PASSTHRU_KIND_DEFAULT:
+ case LIBXL_GFX_PASSTHRU_KIND_IGD:
+ flexarray_append(dm_args, "-gfx_passthru");
+ break;
+ default:
+ LOGD(ERROR, domid, "unsupported gfx_passthru_kind.");
+ return ERROR_INVAL;
+ }
+ }
+ } else {
+ if (!sdl && !vnc)
+ flexarray_append(dm_args, "-nographic");
+ }
+
+ if (libxl_defbool_val(b_info->dm_restrict)) {
+ LOGD(ERROR, domid,
+ "dm_restrict not supported by qemu-xen-traditional");
+ return ERROR_INVAL;
+ }
+
+ if (state->saved_state) {
+ flexarray_vappend(dm_args, "-loadvm", state->saved_state, NULL);
+ }
+ for (i = 0; b_info->extra && b_info->extra[i] != NULL; i++)
+ flexarray_append(dm_args, b_info->extra[i]);
+ flexarray_append(dm_args, "-M");
+ switch (b_info->type) {
+ case LIBXL_DOMAIN_TYPE_PVH:
+ case LIBXL_DOMAIN_TYPE_PV:
+ flexarray_append(dm_args, "xenpv");
+ for (i = 0; b_info->extra_pv && b_info->extra_pv[i] != NULL; i++)
+ flexarray_append(dm_args, b_info->extra_pv[i]);
+ break;
+ case LIBXL_DOMAIN_TYPE_HVM:
+ flexarray_append(dm_args, "xenfv");
+ for (i = 0; b_info->extra_hvm && b_info->extra_hvm[i] != NULL; i++)
+ flexarray_append(dm_args, b_info->extra_hvm[i]);
+ break;
+ default:
+ abort();
+ }
+ flexarray_append(dm_args, NULL);
+ *args = (char **) flexarray_contents(dm_args);
+ flexarray_append(dm_envs, NULL);
+ if (envs)
+ *envs = (char **) flexarray_contents(dm_envs);
+ return 0;
+}
+
+static char *dm_spice_options(libxl__gc *gc,
+ const libxl_spice_info *spice)
+{
+ char *opt;
+
+ if (!spice->port && !spice->tls_port) {
+ LOG(ERROR,
+ "at least one of the spiceport or tls_port must be provided");
+ return NULL;
+ }
+
+ if (!libxl_defbool_val(spice->disable_ticketing)) {
+ if (!spice->passwd) {
+ LOG(ERROR, "spice ticketing is enabled but missing password");
+ return NULL;
+ }
+ else if (!spice->passwd[0]) {
+ LOG(ERROR, "spice password can't be empty");
+ return NULL;
+ }
+ }
+ opt = GCSPRINTF("port=%d,tls-port=%d", spice->port, spice->tls_port);
+ if (spice->host)
+ opt = GCSPRINTF("%s,addr=%s", opt, spice->host);
+ if (libxl_defbool_val(spice->disable_ticketing))
+ opt = GCSPRINTF("%s,disable-ticketing", opt);
+ else
+ opt = GCSPRINTF("%s,password=%s", opt, spice->passwd);
+ opt = GCSPRINTF("%s,agent-mouse=%s", opt,
+ libxl_defbool_val(spice->agent_mouse) ? "on" : "off");
+
+ if (!libxl_defbool_val(spice->clipboard_sharing))
+ opt = GCSPRINTF("%s,disable-copy-paste", opt);
+
+ if (spice->image_compression)
+ opt = GCSPRINTF("%s,image-compression=%s", opt,
+ spice->image_compression);
+
+ if (spice->streaming_video)
+ opt = GCSPRINTF("%s,streaming-video=%s", opt, spice->streaming_video);
+
+ return opt;
+}
+
+static enum libxl_gfx_passthru_kind
+libxl__detect_gfx_passthru_kind(libxl__gc *gc,
+ const libxl_domain_config *guest_config)
+{
+ const libxl_domain_build_info *b_info = &guest_config->b_info;
+
+ if (b_info->u.hvm.gfx_passthru_kind != LIBXL_GFX_PASSTHRU_KIND_DEFAULT)
+ return b_info->u.hvm.gfx_passthru_kind;
+
+ if (libxl__is_igd_vga_passthru(gc, guest_config)) {
+ return LIBXL_GFX_PASSTHRU_KIND_IGD;
+ }
+
+ return LIBXL_GFX_PASSTHRU_KIND_DEFAULT;
+}
+
+/* colo mode */
+enum {
+ LIBXL__COLO_NONE = 0,
+ LIBXL__COLO_PRIMARY,
+ LIBXL__COLO_SECONDARY,
+};
+
+static char *qemu_disk_scsi_drive_string(libxl__gc *gc, const char *target_path,
+ int unit, const char *format,
+ const libxl_device_disk *disk,
+ int colo_mode, const char **id_ptr)
+{
+ char *drive = NULL;
+ char *common = GCSPRINTF("if=none,readonly=%s,cache=writeback",
+ disk->readwrite ? "off" : "on");
+ const char *exportname = disk->colo_export;
+ const char *active_disk = disk->active_disk;
+ const char *hidden_disk = disk->hidden_disk;
+ const char *id;
+
+ switch (colo_mode) {
+ case LIBXL__COLO_NONE:
+ id = GCSPRINTF("scsi0-hd%d", unit);
+ drive = GCSPRINTF("file=%s,id=%s,format=%s,%s",
+ target_path, id, format, common);
+ break;
+ case LIBXL__COLO_PRIMARY:
+ id = exportname;
+ drive = GCSPRINTF(
+ "%s,id=%s,driver=quorum,"
+ "children.0.file.filename=%s,"
+ "children.0.driver=%s,"
+ "read-pattern=fifo,"
+ "vote-threshold=1",
+ common, id, target_path, format);
+ break;
+ case LIBXL__COLO_SECONDARY:
+ id = "top-colo";
+ drive = GCSPRINTF(
+ "%s,id=%s,driver=replication,"
+ "mode=secondary,"
+ "top-id=top-colo,"
+ "file.driver=qcow2,"
+ "file.file.filename=%s,"
+ "file.backing.driver=qcow2,"
+ "file.backing.file.filename=%s,"
+ "file.backing.backing=%s",
+ common, id, active_disk, hidden_disk, exportname);
+ break;
+ default:
+ abort();
+ }
+
+ *id_ptr = id;
+
+ return drive;
+}
+
+static char *qemu_disk_ide_drive_string(libxl__gc *gc, const char *target_path,
+ int unit, const char *format,
+ const libxl_device_disk *disk,
+ int colo_mode)
+{
+ char *drive = NULL;
+ const char *exportname = disk->colo_export;
+ const char *active_disk = disk->active_disk;
+ const char *hidden_disk = disk->hidden_disk;
+
+ assert(disk->readwrite); /* should have been checked earlier */
+
+ switch (colo_mode) {
+ case LIBXL__COLO_NONE:
+ drive = GCSPRINTF
+ ("file=%s,if=ide,index=%d,media=disk,format=%s,cache=writeback",
+ target_path, unit, format);
+ break;
+ case LIBXL__COLO_PRIMARY:
+ /*
+ * primary:
+ * -dirve if=ide,index=x,media=disk,cache=writeback,driver=quorum,\
+ * id=exportname,\
+ * children.0.file.filename=target_path,\
+ * children.0.driver=format,\
+ * read-pattern=fifo,\
+ * vote-threshold=1
+ */
+ drive = GCSPRINTF(
+ "if=ide,index=%d,media=disk,cache=writeback,driver=quorum,"
+ "id=%s,"
+ "children.0.file.filename=%s,"
+ "children.0.driver=%s,"
+ "read-pattern=fifo,"
+ "vote-threshold=1",
+ unit, exportname, target_path, format);
+ break;
+ case LIBXL__COLO_SECONDARY:
+ /*
+ * secondary:
+ * -drive if=ide,index=x,media=disk,cache=writeback,driver=replication,\
+ * mode=secondary,\
+ * file.driver=qcow2,\
+ * file.file.filename=active_disk,\
+ * file.backing.driver=qcow2,\
+ * file.backing.file.filename=hidden_disk,\
+ * file.backing.backing=exportname,
+ */
+ drive = GCSPRINTF(
+ "if=ide,index=%d,id=top-colo,media=disk,cache=writeback,"
+ "driver=replication,"
+ "mode=secondary,"
+ "top-id=top-colo,"
+ "file.driver=qcow2,"
+ "file.file.filename=%s,"
+ "file.backing.driver=qcow2,"
+ "file.backing.file.filename=%s,"
+ "file.backing.backing=%s",
+ unit, active_disk, hidden_disk, exportname);
+ break;
+ default:
+ abort();
+ }
+
+ return drive;
+}
+
+static int libxl__pre_open_qmp_socket(libxl__gc *gc, libxl_domid domid,
+ int *fd_r)
+{
+ int rc, r;
+ int fd;
+ struct sockaddr_un un;
+ const char *path = libxl__qemu_qmp_path(gc, domid);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ LOGED(ERROR, domid, "socket() failed");
+ return ERROR_FAIL;
+ }
+
+ rc = libxl__prepare_sockaddr_un(gc, &un, path, "QEMU's QMP socket");
+ if (rc)
+ goto out;
+
+ rc = libxl__remove_file(gc, path);
+ if (rc)
+ goto out;
+
+ r = bind(fd, (struct sockaddr *) &un, sizeof(un));
+ if (r < 0) {
+ LOGED(ERROR, domid, "bind('%s') failed", path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ r = listen(fd, 1);
+ if (r < 0) {
+ LOGED(ERROR, domid, "listen() failed");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ *fd_r = fd;
+ rc = 0;
+
+out:
+ if (rc && fd >= 0)
+ close(fd);
+ return rc;
+}
+
+static int libxl__build_device_model_args_new(libxl__gc *gc,
+ const char *dm, int guest_domid,
+ const libxl_domain_config *guest_config,
+ char ***args, char ***envs,
+ const libxl__domain_build_state *state,
+ int *dm_state_fd)
+{
+ const libxl_domain_create_info *c_info = &guest_config->c_info;
+ const libxl_domain_build_info *b_info = &guest_config->b_info;
+ const libxl_device_disk *disks = guest_config->disks;
+ const libxl_device_nic *nics = guest_config->nics;
+ const int num_disks = guest_config->num_disks;
+ const int num_nics = guest_config->num_nics;
+ const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
+ const libxl_sdl_info *sdl = dm_sdl(guest_config);
+ const char *keymap = dm_keymap(guest_config);
+ char *machinearg;
+ flexarray_t *dm_args, *dm_envs;
+ int i, connection, devid;
+ uint64_t ram_size;
+ const char *path, *chardev;
+ bool is_stubdom = libxl_defbool_val(b_info->device_model_stubdomain);
+
+ dm_args = flexarray_make(gc, 16, 1);
+ dm_envs = flexarray_make(gc, 16, 1);
+
+ libxl__set_qemu_env_for_xsa_180(gc, dm_envs);
+
+ flexarray_vappend(dm_args, dm,
+ "-xen-domid",
+ GCSPRINTF("%d", guest_domid), NULL);
+ flexarray_append(dm_args, "-no-shutdown");
+
+ /*
+ * QMP access to qemu running in stubdomain is done over vchan. The
+ * stubdomain init script adds the appropriate monitor options for
+ * vchan-socket-proxy.
+ */
+ if (!is_stubdom) {
+ flexarray_append(dm_args, "-chardev");
+ if (state->dm_monitor_fd >= 0) {
+ flexarray_append(dm_args,
+ GCSPRINTF("socket,id=libxl-cmd,fd=%d,server,nowait",
+ state->dm_monitor_fd));
+
+ /*
+ * Start QEMU with its "CPU" paused, it will not start any emulation
+ * until the QMP command "cont" is used. This also prevent QEMU from
+ * writing "running" to the "state" xenstore node so we only use this
+ * flag when we have the QMP based startup notification.
+ * */
+ flexarray_append(dm_args, "-S");
+ } else {
+ flexarray_append(dm_args,
+ GCSPRINTF("socket,id=libxl-cmd,"
+ "path=%s,server,nowait",
+ libxl__qemu_qmp_path(gc, guest_domid)));
+ }
+
+ flexarray_append(dm_args, "-mon");
+ flexarray_append(dm_args, "chardev=libxl-cmd,mode=control");
+
+ flexarray_append(dm_args, "-chardev");
+ flexarray_append(dm_args,
+ GCSPRINTF("socket,id=libxenstat-cmd,"
+ "path=%s/qmp-libxenstat-%d,server,nowait",
+ libxl__run_dir_path(), guest_domid));
+
+ flexarray_append(dm_args, "-mon");
+ flexarray_append(dm_args, "chardev=libxenstat-cmd,mode=control");
+ }
+
+ for (i = 0; i < guest_config->num_channels; i++) {
+ connection = guest_config->channels[i].connection;
+ devid = guest_config->channels[i].devid;
+ switch (connection) {
+ case LIBXL_CHANNEL_CONNECTION_PTY:
+ chardev = GCSPRINTF("pty,id=libxl-channel%d", devid);
+ break;
+ case LIBXL_CHANNEL_CONNECTION_SOCKET:
+ path = guest_config->channels[i].u.socket.path;
+ chardev = GCSPRINTF("socket,id=libxl-channel%d,path=%s,"
+ "server,nowait", devid, path);
+ break;
+ default:
+ /* We've forgotten to add the clause */
+ LOGD(ERROR, guest_domid, "%s: unknown channel connection %d",
+ __func__, connection);
+ return ERROR_INVAL;
+ }
+ flexarray_append(dm_args, "-chardev");
+ flexarray_append(dm_args, (void*)chardev);
+ }
+
+ /*
+ * Remove default devices created by qemu. Qemu will create only devices
+ * defined by xen, since the devices not defined by xen are not usable.
+ */
+ flexarray_append(dm_args, "-nodefaults");
+
+ /*
+ * Do not use any of the user-provided config files in sysconfdir,
+ * avoiding unknown and uncontrolled configuration.
+ */
+ flexarray_append(dm_args, "-no-user-config");
+
+ if (b_info->type != LIBXL_DOMAIN_TYPE_HVM) {
+ flexarray_append(dm_args, "-xen-attach");
+ }
+
+ if (c_info->name) {
+ flexarray_vappend(dm_args, "-name", c_info->name, NULL);
+ }
+
+ if (vnc && !is_stubdom) {
+ char *vncarg = NULL;
+
+ flexarray_append(dm_args, "-vnc");
+
+ /*
+ * If vnc->listen is present and contains a :, and
+ * - vnc->display is 0, use vnc->listen
+ * - vnc->display is non-zero, be confused
+ * If vnc->listen is present but doesn't, use vnc->listen:vnc->display.
+ * If vnc->listen is not present, use 127.0.0.1:vnc->display
+ * (Remembering that vnc->display already defaults to 0.)
+ */
+ if (vnc->listen) {
+ if (strchr(vnc->listen, ':') != NULL) {
+ if (vnc->display) {
+ LOGD(ERROR, guest_domid,
+ "vncdisplay set, vnclisten contains display");
+ return ERROR_INVAL;
+ }
+ vncarg = vnc->listen;
+ } else {
+ vncarg = GCSPRINTF("%s:%d", vnc->listen, vnc->display);
+ }
+ } else
+ vncarg = GCSPRINTF("127.0.0.1:%d", vnc->display);
+
+ if (vnc->passwd && vnc->passwd[0]) {
+ vncarg = GCSPRINTF("%s,password", vncarg);
+ }
+
+ if (libxl_defbool_val(vnc->findunused)) {
+ /* This option asks to QEMU to try this number of port before to
+ * give up. So QEMU will try ports between $display and $display +
+ * 99. This option needs to be the last one of the vnc options. */
+ vncarg = GCSPRINTF("%s,to=99", vncarg);
+ }
+
+ flexarray_append(dm_args, vncarg);
+ } else if (!is_stubdom) {
+ /*
+ * Ensure that by default no vnc server is created.
+ */
+ flexarray_append_pair(dm_args, "-vnc", "none");
+ }
+
+ /*
+ * Ensure that by default no display backend is created. Further
+ * options given below might then enable more.
+ */
+ flexarray_append_pair(dm_args, "-display", "none");
+
+ if (sdl && !is_stubdom) {
+ flexarray_append(dm_args, "-sdl");
+ if (sdl->display)
+ flexarray_append_pair(dm_envs, "DISPLAY", sdl->display);
+ if (sdl->xauthority)
+ flexarray_append_pair(dm_envs, "XAUTHORITY", sdl->xauthority);
+ }
+
+ if (keymap) {
+ flexarray_vappend(dm_args, "-k", keymap, NULL);
+ }
+
+ if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
+ int ioemu_nics = 0;
+
+ if (b_info->kernel)
+ flexarray_vappend(dm_args, "-kernel", b_info->kernel, NULL);
+
+ if (b_info->ramdisk)
+ flexarray_vappend(dm_args, "-initrd", b_info->ramdisk, NULL);
+
+ if (b_info->cmdline)
+ flexarray_vappend(dm_args, "-append", b_info->cmdline, NULL);
+
+ /* Find out early if one of the disk is on the scsi bus and add a scsi
+ * controller. This is done ahead to keep the same behavior as previous
+ * version of QEMU (have the controller on the same PCI slot). */
+ for (i = 0; i < num_disks; i++) {
+ if (disks[i].is_cdrom) {
+ continue;
+ }
+ if (strncmp(disks[i].vdev, "sd", 2) == 0) {
+ flexarray_vappend(dm_args, "-device", "lsi53c895a", NULL);
+ break;
+ }
+ }
+
+ if (b_info->u.hvm.serial || b_info->u.hvm.serial_list) {
+ if ( b_info->u.hvm.serial && b_info->u.hvm.serial_list )
+ {
+ LOGD(ERROR, guest_domid, "Both serial and serial_list set");
+ return ERROR_INVAL;
+ } else {
+ if (b_info->u.hvm.serial) {
+ if (is_stubdom) {
+ /* see spawn_stub_launch_dm() for connecting STUBDOM_CONSOLE_SERIAL */
+ flexarray_vappend(dm_args,
+ "-serial",
+ GCSPRINTF("/dev/hvc%d", STUBDOM_CONSOLE_SERIAL),
+ NULL);
+ } else {
+ flexarray_vappend(dm_args,
+ "-serial", b_info->u.hvm.serial, NULL);
+ }
+ } else if (b_info->u.hvm.serial_list) {
+ char **p;
+ /* see spawn_stub_launch_dm() for connecting STUBDOM_CONSOLE_SERIAL */
+ for (p = b_info->u.hvm.serial_list, i = 0;
+ *p;
+ p++, i++) {
+ if (is_stubdom)
+ flexarray_vappend(dm_args,
+ "-serial",
+ GCSPRINTF("/dev/hvc%d", STUBDOM_CONSOLE_SERIAL + i),
+ NULL);
+ else
+ flexarray_vappend(dm_args,
+ "-serial",
+ *p, NULL);
+ }
+ }
+ }
+ }
+
+ if (libxl_defbool_val(b_info->u.hvm.nographic) && (!sdl && !vnc)) {
+ flexarray_append(dm_args, "-nographic");
+ }
+
+ if (libxl_defbool_val(b_info->u.hvm.spice.enable) && !is_stubdom) {
+ const libxl_spice_info *spice = &b_info->u.hvm.spice;
+ char *spiceoptions = dm_spice_options(gc, spice);
+ if (!spiceoptions)
+ return ERROR_INVAL;
+
+ flexarray_append(dm_args, "-spice");
+ flexarray_append(dm_args, spiceoptions);
+ if (libxl_defbool_val(b_info->u.hvm.spice.vdagent)) {
+ flexarray_vappend(dm_args, "-device", "virtio-serial",
+ "-chardev", "spicevmc,id=vdagent,name=vdagent", "-device",
+ "virtserialport,chardev=vdagent,name=com.redhat.spice.0",
+ NULL);
+ }
+ }
+
+ switch (b_info->u.hvm.vga.kind) {
+ case LIBXL_VGA_INTERFACE_TYPE_STD:
+ flexarray_append_pair(dm_args, "-device",
+ GCSPRINTF("VGA,vgamem_mb=%d",
+ libxl__sizekb_to_mb(b_info->video_memkb)));
+ break;
+ case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
+ flexarray_append_pair(dm_args, "-device",
+ GCSPRINTF("cirrus-vga,vgamem_mb=%d",
+ libxl__sizekb_to_mb(b_info->video_memkb)));
+ break;
+ case LIBXL_VGA_INTERFACE_TYPE_NONE:
+ break;
+ case LIBXL_VGA_INTERFACE_TYPE_QXL:
+ /* QXL have 2 ram regions, ram and vram */
+ flexarray_append_pair(dm_args, "-device",
+ GCSPRINTF("qxl-vga,vram_size_mb=%"PRIu64",ram_size_mb=%"PRIu64,
+ (b_info->video_memkb/2/1024), (b_info->video_memkb/2/1024) ) );
+ break;
+ default:
+ LOGD(ERROR, guest_domid, "Invalid emulated video card specified");
+ return ERROR_INVAL;
+ }
+
+ if (b_info->u.hvm.boot) {
+ flexarray_vappend(dm_args, "-boot",
+ GCSPRINTF("order=%s", b_info->u.hvm.boot), NULL);
+ }
+ if (libxl_defbool_val(b_info->u.hvm.usb)
+ || b_info->u.hvm.usbdevice
+ || libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
+ if (b_info->u.hvm.usbdevice
+ && libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
+ LOGD(ERROR, guest_domid, "Both usbdevice and usbdevice_list set");
+ return ERROR_INVAL;
+ }
+ flexarray_append(dm_args, "-usb");
+ if (b_info->u.hvm.usbdevice) {
+ flexarray_vappend(dm_args,
+ "-usbdevice", b_info->u.hvm.usbdevice, NULL);
+ } else if (b_info->u.hvm.usbdevice_list) {
+ char **p;
+ for (p = b_info->u.hvm.usbdevice_list;
+ *p;
+ p++) {
+ flexarray_vappend(dm_args,
+ "-usbdevice",
+ *p, NULL);
+ }
+ }
+ } else if (b_info->u.hvm.usbversion) {
+ switch (b_info->u.hvm.usbversion) {
+ case 1:
+ flexarray_vappend(dm_args,
+ "-device", "piix3-usb-uhci,id=usb", NULL);
+ break;
+ case 2:
+ flexarray_append_pair(dm_args, "-device",
+ "ich9-usb-ehci1,id=usb,addr=0x1d.0x7,multifunction=on");
+ for (i = 1; i < 4; i++)
+ flexarray_append_pair(dm_args, "-device",
+ GCSPRINTF("ich9-usb-uhci%d,masterbus=usb.0,"
+ "firstport=%d,addr=0x1d.%#x,multifunction=on",
+ i, 2*(i-1), i-1));
+ break;
+ case 3:
+ flexarray_vappend(dm_args,
+ "-device", "nec-usb-xhci,id=usb", NULL);
+ break;
+ default:
+ LOGD(ERROR, guest_domid, "usbversion parameter is invalid, "
+ "must be between 1 and 3");
+ return ERROR_INVAL;
+ }
+ if (b_info->u.hvm.spice.usbredirection >= 0 &&
+ b_info->u.hvm.spice.usbredirection < 5) {
+ for (i = 1; i <= b_info->u.hvm.spice.usbredirection; i++)
+ flexarray_vappend(dm_args, "-chardev",
+ GCSPRINTF("spicevmc,name=usbredir,id=usbrc%d", i),
+ "-device",
+ GCSPRINTF("usb-redir,chardev=usbrc%d,"
+ "id=usbrc%d", i, i), NULL);
+ } else {
+ LOGD(ERROR, guest_domid, "usbredirection parameter is invalid, "
+ "it must be between 1 and 4");
+ return ERROR_INVAL;
+ }
+ }
+ if (b_info->u.hvm.soundhw) {
+ flexarray_vappend(dm_args, "-soundhw", b_info->u.hvm.soundhw, NULL);
+ }
+ if (!libxl__acpi_defbool_val(b_info)) {
+ flexarray_append(dm_args, "-no-acpi");
+ }
+ if (b_info->max_vcpus > 1) {
+ flexarray_append(dm_args, "-smp");
+ if (b_info->avail_vcpus.size) {
+ int nr_set_cpus = 0;
+ nr_set_cpus = libxl_bitmap_count_set(&b_info->avail_vcpus);
+
+ flexarray_append(dm_args, GCSPRINTF("%d,maxcpus=%d",
+ nr_set_cpus,
+ b_info->max_vcpus));
+ } else
+ flexarray_append(dm_args, GCSPRINTF("%d", b_info->max_vcpus));
+ }
+ for (i = 0; i < num_nics; i++) {
+ if (nics[i].nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
+ char *smac = GCSPRINTF(LIBXL_MAC_FMT,
+ LIBXL_MAC_BYTES(nics[i].mac));
+ const char *ifname = libxl__device_nic_devname(gc,
+ guest_domid, nics[i].devid,
+ LIBXL_NIC_TYPE_VIF_IOEMU);
+ flexarray_append(dm_args, "-device");
+ flexarray_append(dm_args,
+ GCSPRINTF("%s,id=nic%d,netdev=net%d,mac=%s",
+ nics[i].model, nics[i].devid,
+ nics[i].devid, smac));
+ flexarray_append(dm_args, "-netdev");
+ flexarray_append(dm_args,
+ GCSPRINTF("type=tap,id=net%d,ifname=%s,"
+ "script=%s,downscript=%s",
+ nics[i].devid, ifname,
+ libxl_tapif_script(gc),
+ libxl_tapif_script(gc)));
+
+ /* Userspace COLO Proxy need this */
+#define APPEND_COLO_SOCK_SERVER(sock_id, sock_ip, sock_port) ({ \
+ if (nics[i].colo_##sock_id && \
+ nics[i].colo_##sock_ip && \
+ nics[i].colo_##sock_port) { \
+ flexarray_append(dm_args, "-chardev"); \
+ flexarray_append(dm_args, \
+ GCSPRINTF("socket,id=%s,host=%s,port=%s,server,nowait", \
+ nics[i].colo_##sock_id, \
+ nics[i].colo_##sock_ip, \
+ nics[i].colo_##sock_port)); \
+ } \
+})
+
+#define APPEND_COLO_SOCK_CLIENT(sock_id, sock_ip, sock_port) ({ \
+ if (nics[i].colo_##sock_id && \
+ nics[i].colo_##sock_ip && \
+ nics[i].colo_##sock_port) { \
+ flexarray_append(dm_args, "-chardev"); \
+ flexarray_append(dm_args, \
+ GCSPRINTF("socket,id=%s,host=%s,port=%s", \
+ nics[i].colo_##sock_id, \
+ nics[i].colo_##sock_ip, \
+ nics[i].colo_##sock_port)); \
+ } \
+})
+
+ if (state->saved_state) {
+ /* secondary colo run */
+
+ APPEND_COLO_SOCK_CLIENT(sock_sec_redirector0_id,
+ sock_sec_redirector0_ip,
+ sock_sec_redirector0_port);
+
+ APPEND_COLO_SOCK_CLIENT(sock_sec_redirector1_id,
+ sock_sec_redirector1_ip,
+ sock_sec_redirector1_port);
+
+ if (nics[i].colo_filter_sec_redirector0_queue &&
+ nics[i].colo_filter_sec_redirector0_indev) {
+ flexarray_append(dm_args, "-object");
+ flexarray_append(dm_args,
+ GCSPRINTF("filter-redirector,id=rs1,netdev=net%d,queue=%s,indev=%s",
+ nics[i].devid,
+ nics[i].colo_filter_sec_redirector0_queue,
+ nics[i].colo_filter_sec_redirector0_indev));
+ }
+ if (nics[i].colo_filter_sec_redirector1_queue &&
+ nics[i].colo_filter_sec_redirector1_outdev) {
+ flexarray_append(dm_args, "-object");
+ flexarray_append(dm_args,
+ GCSPRINTF("filter-redirector,id=rs2,netdev=net%d,queue=%s,outdev=%s",
+ nics[i].devid,
+ nics[i].colo_filter_sec_redirector1_queue,
+ nics[i].colo_filter_sec_redirector1_outdev));
+ }
+ if (nics[i].colo_filter_sec_rewriter0_queue) {
+ flexarray_append(dm_args, "-object");
+ flexarray_append(dm_args,
+ GCSPRINTF("filter-rewriter,id=rs3,netdev=net%d,queue=%s",
+ nics[i].devid,
+ nics[i].colo_filter_sec_rewriter0_queue));
+ }
+ } else {
+ /* primary colo run */
+
+ APPEND_COLO_SOCK_SERVER(sock_mirror_id,
+ sock_mirror_ip,
+ sock_mirror_port);
+
+ APPEND_COLO_SOCK_SERVER(sock_compare_pri_in_id,
+ sock_compare_pri_in_ip,
+ sock_compare_pri_in_port);
+
+ APPEND_COLO_SOCK_SERVER(sock_compare_sec_in_id,
+ sock_compare_sec_in_ip,
+ sock_compare_sec_in_port);
+
+ APPEND_COLO_SOCK_SERVER(sock_compare_notify_id,
+ sock_compare_notify_ip,
+ sock_compare_notify_port);
+
+ APPEND_COLO_SOCK_SERVER(sock_redirector0_id,
+ sock_redirector0_ip,
+ sock_redirector0_port);
+
+ APPEND_COLO_SOCK_CLIENT(sock_redirector1_id,
+ sock_redirector1_ip,
+ sock_redirector1_port);
+
+ APPEND_COLO_SOCK_CLIENT(sock_redirector2_id,
+ sock_redirector2_ip,
+ sock_redirector2_port);
+
+ if (nics[i].colo_filter_mirror_queue &&
+ nics[i].colo_filter_mirror_outdev) {
+ flexarray_append(dm_args, "-object");
+ flexarray_append(dm_args,
+ GCSPRINTF("filter-mirror,id=m1,netdev=net%d,queue=%s,outdev=%s",
+ nics[i].devid,
+ nics[i].colo_filter_mirror_queue,
+ nics[i].colo_filter_mirror_outdev));
+ }
+ if (nics[i].colo_filter_redirector0_queue &&
+ nics[i].colo_filter_redirector0_indev) {
+ flexarray_append(dm_args, "-object");
+ flexarray_append(dm_args,
+ GCSPRINTF("filter-redirector,id=r1,netdev=net%d,queue=%s,indev=%s",
+ nics[i].devid,
+ nics[i].colo_filter_redirector0_queue,
+ nics[i].colo_filter_redirector0_indev));
+ }
+ if (nics[i].colo_filter_redirector1_queue &&
+ nics[i].colo_filter_redirector1_outdev) {
+ flexarray_append(dm_args, "-object");
+ flexarray_append(dm_args,
+ GCSPRINTF("filter-redirector,id=r2,netdev=net%d,queue=%s,outdev=%s",
+ nics[i].devid,
+ nics[i].colo_filter_redirector1_queue,
+ nics[i].colo_filter_redirector1_outdev));
+ }
+ if (nics[i].colo_compare_pri_in &&
+ nics[i].colo_compare_sec_in &&
+ nics[i].colo_compare_out &&
+ nics[i].colo_compare_notify_dev) {
+ flexarray_append(dm_args, "-object");
+ flexarray_append(dm_args, "iothread,id=colo-compare-iothread-1");
+ flexarray_append(dm_args, "-object");
+ flexarray_append(dm_args,
+ GCSPRINTF("colo-compare,id=c1,primary_in=%s,secondary_in=%s,outdev=%s,notify_dev=%s,iothread=colo-compare-iothread-1",
+ nics[i].colo_compare_pri_in,
+ nics[i].colo_compare_sec_in,
+ nics[i].colo_compare_out,
+ nics[i].colo_compare_notify_dev));
+ }
+ }
+ ioemu_nics++;
+
+#undef APPEND_COLO_SOCK_SERVER
+#undef APPEND_COLO_SOCK_CLIENT
+ }
+ }
+ /* If we have no emulated nics, tell qemu not to create any */
+ if ( ioemu_nics == 0 ) {
+ flexarray_append(dm_args, "-net");
+ flexarray_append(dm_args, "none");
+ }
+ } else {
+ if (!sdl && !vnc) {
+ flexarray_append(dm_args, "-nographic");
+ }
+ }
+
+ if (libxl_defbool_val(b_info->dm_restrict)) {
+ char *chroot_dir = GCSPRINTF("%s/qemu-root-%d",
+ libxl__run_dir_path(), guest_domid);
+ int r;
+
+ flexarray_append(dm_args, "-xen-domid-restrict");
+
+ /*
+ * Run QEMU in a chroot at XEN_RUN_DIR/qemu-root-<domid>
+ *
+ * There is no library function to do the equivalent of `rm
+ * -rf`. However deprivileged QEMU in theory shouldn't be
+ * able to write any files, as the chroot would be owned by
+ * root, but it would be running as an unprivileged process.
+ * So in theory, old chroots should always be empty.
+ *
+ * rmdir the directory before attempting to create
+ * it; if it returns anything other than ENOENT, fail domain
+ * creation.
+ */
+ r = rmdir(chroot_dir);
+ if (r != 0 && errno != ENOENT) {
+ LOGED(ERROR, guest_domid,
+ "failed to remove existing chroot dir %s", chroot_dir);
+ return ERROR_FAIL;
+ }
+
+ for (;;) {
+ r = mkdir(chroot_dir, 0000);
+ if (!r)
+ break;
+ if (errno == EINTR) continue;
+ LOGED(ERROR, guest_domid,
+ "failed to create chroot dir %s", chroot_dir);
+ return ERROR_FAIL;
+ }
+
+ /* Add "-chroot [dir]" to command-line */
+ flexarray_append(dm_args, "-chroot");
+ flexarray_append(dm_args, chroot_dir);
+ }
+
+ if (state->saved_state) {
+ if (is_stubdom) {
+ /* Linux stubdomain must replace $STUBDOM_RESTORE_INCOMING_ARG
+ * with the approriate fd:$num argument for the
+ * STUBDOM_CONSOLE_RESTORE console 2.
+ */
+ flexarray_append(dm_args, "-incoming");
+ flexarray_append(dm_args, "$STUBDOM_RESTORE_INCOMING_ARG");
+ } else {
+ /* This file descriptor is meant to be used by QEMU */
+ *dm_state_fd = open(state->saved_state, O_RDONLY);
+ flexarray_append(dm_args, "-incoming");
+ flexarray_append(dm_args, GCSPRINTF("fd:%d",*dm_state_fd));
+ }
+ }
+ for (i = 0; b_info->extra && b_info->extra[i] != NULL; i++)
+ flexarray_append(dm_args, b_info->extra[i]);
+
+ flexarray_append(dm_args, "-machine");
+ switch (b_info->type) {
+ case LIBXL_DOMAIN_TYPE_PVH:
+ case LIBXL_DOMAIN_TYPE_PV:
+ flexarray_append(dm_args, "xenpv");
+ for (i = 0; b_info->extra_pv && b_info->extra_pv[i] != NULL; i++)
+ flexarray_append(dm_args, b_info->extra_pv[i]);
+ break;
+ case LIBXL_DOMAIN_TYPE_HVM:
+ if (!libxl_defbool_val(b_info->u.hvm.xen_platform_pci)) {
+ /* Switching here to the machine "pc" which does not add
+ * the xen-platform device instead of the default "xenfv" machine.
+ */
+ machinearg = libxl__strdup(gc, "pc,accel=xen");
+ } else {
+ machinearg = libxl__strdup(gc, "xenfv");
+ }
+ if (b_info->u.hvm.mmio_hole_memkb) {
+ uint64_t max_ram_below_4g = (1ULL << 32) -
+ (b_info->u.hvm.mmio_hole_memkb << 10);
+
+ if (max_ram_below_4g > HVM_BELOW_4G_MMIO_START) {
+ LOGD(WARN, guest_domid, "mmio_hole_memkb=%"PRIu64
+ " invalid ignored.\n",
+ b_info->u.hvm.mmio_hole_memkb);
+ } else {
+ machinearg = GCSPRINTF("%s,max-ram-below-4g=%"PRIu64,
+ machinearg, max_ram_below_4g);
+ }
+ }
+
+ if (libxl_defbool_val(b_info->u.hvm.gfx_passthru)) {
+ enum libxl_gfx_passthru_kind gfx_passthru_kind =
+ libxl__detect_gfx_passthru_kind(gc, guest_config);
+ switch (gfx_passthru_kind) {
+ case LIBXL_GFX_PASSTHRU_KIND_IGD:
+ machinearg = GCSPRINTF("%s,igd-passthru=on", machinearg);
+ break;
+ case LIBXL_GFX_PASSTHRU_KIND_DEFAULT:
+ LOGD(ERROR, guest_domid, "unable to detect required gfx_passthru_kind");
+ return ERROR_FAIL;
+ default:
+ LOGD(ERROR, guest_domid, "invalid value for gfx_passthru_kind");
+ return ERROR_INVAL;
+ }
+ }
+
+ flexarray_append(dm_args, machinearg);
+ for (i = 0; b_info->extra_hvm && b_info->extra_hvm[i] != NULL; i++)
+ flexarray_append(dm_args, b_info->extra_hvm[i]);
+ break;
+ default:
+ abort();
+ }
+
+ ram_size = libxl__sizekb_to_mb(b_info->max_memkb - b_info->video_memkb);
+ flexarray_append(dm_args, "-m");
+ flexarray_append(dm_args, GCSPRINTF("%"PRId64, ram_size));
+
+ if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
+ if (b_info->u.hvm.hdtype == LIBXL_HDTYPE_AHCI)
+ flexarray_append_pair(dm_args, "-device", "ahci,id=ahci0");
+ for (i = 0; i < num_disks; i++) {
+ int disk, part;
+ int dev_number =
+ libxl__device_disk_dev_number(disks[i].vdev, &disk, &part);
+ const char *format;
+ char *drive;
+ const char *target_path = NULL;
+ int colo_mode;
+
+ if (dev_number == -1) {
+ LOGD(WARN, guest_domid, "unable to determine"" disk number for %s",
+ disks[i].vdev);
+ continue;
+ }
+
+ /*
+ * If qemu isn't doing the interpreting, the parameter is
+ * always raw
+ */
+ if (libxl_defbool_val(b_info->device_model_stubdomain))
+ format = "host_device";
+ else if (disks[i].backend == LIBXL_DISK_BACKEND_QDISK)
+ format = libxl__qemu_disk_format_string(disks[i].format);
+ else
+ format = libxl__qemu_disk_format_string(LIBXL_DISK_FORMAT_RAW);
+
+ if (disks[i].format == LIBXL_DISK_FORMAT_EMPTY) {
+ if (!disks[i].is_cdrom) {
+ LOGD(WARN, guest_domid, "Cannot support empty disk format for %s",
+ disks[i].vdev);
+ continue;
+ }
+ } else if (libxl_defbool_val(b_info->device_model_stubdomain)) {
+ if (disk > 'z' - 'a') {
+ LOGD(WARN, guest_domid,
+ "Emulation of only first %d disks is supported with qemu-xen in stubdomain.\n"
+ "Disk %d will be available via PV drivers but not as an emulated disk.",
+ 'z' - 'a',
+ disk);
+ continue;
+ }
+ target_path = GCSPRINTF("/dev/xvd%c", 'a' + disk);
+ } else {
+ if (format == NULL) {
+ LOGD(WARN, guest_domid,
+ "Unable to determine disk image format: %s\n"
+ "Disk will be available via PV drivers but not as an"
+ "emulated disk.",
+ disks[i].vdev);
+ continue;
+ }
+
+ assert(disks[i].backend != LIBXL_DISK_BACKEND_TAP);
+ target_path = libxl__device_disk_find_local_path(gc,
+ guest_domid, &disks[i], true);
+
+ if (!target_path) {
+ LOGD(WARN, guest_domid, "No way to get local access disk to image: %s\n"
+ "Disk will be available via PV drivers but not as an"
+ "emulated disk.",
+ disks[i].vdev);
+ continue;
+ }
+ }
+
+ if (disks[i].is_cdrom) {
+ if (disk > 4) {
+ LOGD(WARN, guest_domid, "Emulated CDROM can be only one of the first 4 disks.\n"
+ "Disk %s will be available via PV drivers but not as an "
+ "emulated disk.",
+ disks[i].vdev);
+ continue;
+ }
+ drive = libxl__sprintf(gc,
+ "if=ide,index=%d,readonly=on,media=cdrom,id=ide-%i",
+ disk, dev_number);
+
+ if (target_path)
+ drive = libxl__sprintf(gc, "%s,file=%s,format=%s",
+ drive, target_path, format);
+ } else {
+ /*
+ * Explicit sd disks are passed through as is.
+ *
+ * For other disks we translate devices 0..3 into
+ * hd[a-d] and ignore the rest.
+ */
+
+ if (libxl_defbool_val(disks[i].colo_enable)) {
+ if (libxl_defbool_val(disks[i].colo_restore_enable))
+ colo_mode = LIBXL__COLO_SECONDARY;
+ else
+ colo_mode = LIBXL__COLO_PRIMARY;
+ } else {
+ colo_mode = LIBXL__COLO_NONE;
+ }
+
+ if (strncmp(disks[i].vdev, "sd", 2) == 0) {
+ const char *drive_id;
+ if (colo_mode == LIBXL__COLO_SECONDARY) {
+ drive = libxl__sprintf
+ (gc, "if=none,driver=%s,file=%s,id=%s,readonly=%s",
+ format, target_path, disks[i].colo_export,
+ disks[i].readwrite ? "off" : "on");
+
+ flexarray_append(dm_args, "-drive");
+ flexarray_append(dm_args, drive);
+ }
+ drive = qemu_disk_scsi_drive_string(gc, target_path, disk,
+ format,
+ &disks[i],
+ colo_mode,
+ &drive_id),
+ flexarray_vappend(dm_args,
+ "-drive", drive,
+ "-device", GCSPRINTF("scsi-disk,drive=%s,scsi-id=%d",
+ drive_id, disk),
+ NULL);
+ continue;
+ } else if (disk < 6 && b_info->u.hvm.hdtype == LIBXL_HDTYPE_AHCI) {
+ if (!disks[i].readwrite) {
+ LOGD(ERROR, guest_domid,
+ "qemu-xen doesn't support read-only AHCI disk drivers");
+ return ERROR_INVAL;
+ }
+ flexarray_vappend(dm_args, "-drive",
+ GCSPRINTF("file=%s,if=none,id=ahcidisk-%d,format=%s,cache=writeback",
+ target_path, disk, format),
+ "-device", GCSPRINTF("ide-hd,bus=ahci0.%d,unit=0,drive=ahcidisk-%d",
+ disk, disk), NULL);
+ continue;
+ } else if (disk < 4) {
+ if (!disks[i].readwrite) {
+ LOGD(ERROR, guest_domid,
+ "qemu-xen doesn't support read-only IDE disk drivers");
+ return ERROR_INVAL;
+ }
+ if (colo_mode == LIBXL__COLO_SECONDARY) {
+ drive = libxl__sprintf
+ (gc, "if=none,driver=%s,file=%s,id=%s",
+ format, target_path, disks[i].colo_export);
+
+ flexarray_append(dm_args, "-drive");
+ flexarray_append(dm_args, drive);
+ }
+ drive = qemu_disk_ide_drive_string(gc, target_path, disk,
+ format,
+ &disks[i],
+ colo_mode);
+ } else {
+ LOGD(WARN, guest_domid, "Only 4 emulated IDE disks are supported.\n"
+ "Disk %s will be available via PV drivers but not as an "
+ "emulated disk.",
+ disks[i].vdev);
+ continue; /* Do not emulate this disk */
+ }
+
+ if (!drive)
+ continue;
+ }
+
+ flexarray_append(dm_args, "-drive");
+ flexarray_append(dm_args, drive);
+ }
+
+ switch (b_info->u.hvm.vendor_device) {
+ case LIBXL_VENDOR_DEVICE_XENSERVER:
+ flexarray_append(dm_args, "-device");
+ flexarray_append(dm_args, "xen-pvdevice,device-id=0xc000");
+ break;
+ default:
+ break;
+ }
+
+ if (state->dm_runas) {
+ flexarray_append(dm_args, "-runas");
+ flexarray_append(dm_args, state->dm_runas);
+ }
+ }
+ flexarray_append(dm_args, NULL);
+ *args = (char **) flexarray_contents(dm_args);
+ flexarray_append(dm_envs, NULL);
+ if (envs)
+ *envs = (char **) flexarray_contents(dm_envs);
+ return 0;
+}
+
+static int libxl__build_device_model_args(libxl__gc *gc,
+ const char *dm, int guest_domid,
+ const libxl_domain_config *guest_config,
+ char ***args, char ***envs,
+ const libxl__domain_build_state *state,
+ int *dm_state_fd)
+/* dm_state_fd may be NULL iff caller knows we are using stubdom
+ * and therefore will be passing a filename rather than a fd. */
+{
+ switch (guest_config->b_info.device_model_version) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ return libxl__build_device_model_args_old(gc, dm,
+ guest_domid, guest_config,
+ args, envs,
+ state);
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ if (!libxl_defbool_val(guest_config->b_info.device_model_stubdomain)) {
+ assert(dm_state_fd != NULL);
+ assert(*dm_state_fd < 0);
+ }
+ return libxl__build_device_model_args_new(gc, dm,
+ guest_domid, guest_config,
+ args, envs,
+ state, dm_state_fd);
+ default:
+ LOGED(ERROR, guest_domid, "unknown device model version %d",
+ guest_config->b_info.device_model_version);
+ return ERROR_INVAL;
+ }
+}
+
+static void libxl__dm_vifs_from_hvm_guest_config(libxl__gc *gc,
+ libxl_domain_config * const guest_config,
+ libxl_domain_config *dm_config)
+{
+ int i, nr = guest_config->num_nics;
+
+ GCNEW_ARRAY(dm_config->nics, nr);
+
+ for (i=0; i<nr; i++) {
+ dm_config->nics[i] = guest_config->nics[i];
+ dm_config->nics[i].nictype = LIBXL_NIC_TYPE_VIF;
+ if (dm_config->nics[i].ifname)
+ dm_config->nics[i].ifname = GCSPRINTF("%s" TAP_DEVICE_SUFFIX,
+ dm_config->nics[i].ifname);
+ }
+
+ dm_config->num_nics = nr;
+}
+
+static int libxl__vfb_and_vkb_from_hvm_guest_config(libxl__gc *gc,
+ const libxl_domain_config *guest_config,
+ libxl_device_vfb *vfb,
+ libxl_device_vkb *vkb)
+{
+ const libxl_domain_build_info *b_info = &guest_config->b_info;
+
+ if (b_info->type != LIBXL_DOMAIN_TYPE_HVM)
+ return ERROR_INVAL;
+
+ libxl_device_vfb_init(vfb);
+ libxl_device_vkb_init(vkb);
+
+ vfb->backend_domid = 0;
+ vfb->devid = 0;
+ vfb->vnc = b_info->u.hvm.vnc;
+ vfb->keymap = b_info->u.hvm.keymap;
+ vfb->sdl = b_info->u.hvm.sdl;
+
+ vkb->backend_domid = 0;
+ vkb->devid = 0;
+
+ return 0;
+}
+
+static int libxl__write_stub_dmargs(libxl__gc *gc,
+ int dm_domid, int guest_domid,
+ char **args, bool is_linux_stubdom)
+{
+ struct xs_permissions roperm[2];
+ xs_transaction_t t = XBT_NULL;
+ char *dmargs;
+ int rc;
+
+ roperm[0].id = 0;
+ roperm[0].perms = XS_PERM_NONE;
+ roperm[1].id = dm_domid;
+ roperm[1].perms = XS_PERM_READ;
+
+ if (!is_linux_stubdom) {
+ int dmargs_size = 0;
+ int i = 0;
+
+ while (args[i] != NULL) {
+ dmargs_size = dmargs_size + strlen(args[i]) + 1;
+ i++;
+ }
+
+ dmargs_size++;
+ dmargs = (char *) libxl__malloc(gc, dmargs_size);
+
+ i = 1;
+ dmargs[0] = '\0';
+ while (args[i] != NULL) {
+ if (strcmp(args[i], "-sdl") && strcmp(args[i], "-M") && strcmp(args[i], "xenfv")) {
+ strcat(dmargs, " ");
+ strcat(dmargs, args[i]);
+ }
+ i++;
+ }
+ }
+
+ for (;;) {
+ const char *vm_path;
+ char *path;
+
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+
+ rc = libxl__xs_read_mandatory(gc, t,
+ GCSPRINTF("/local/domain/%d/vm",
+ guest_domid),
+ &vm_path);
+ if (rc) goto out;
+
+ if (is_linux_stubdom) {
+ int i;
+
+ path = GCSPRINTF("%s/image/dm-argv", vm_path);
+
+ rc = libxl__xs_mknod(gc, t, path, roperm, ARRAY_SIZE(roperm));
+ if (rc) goto out;
+
+ for (i=1; args[i] != NULL; i++) {
+ rc = libxl__xs_write_checked(gc, t,
+ GCSPRINTF("%s/%03d", path, i),
+ args[i]);
+ if (rc) goto out;
+ }
+ } else {
+ path = GCSPRINTF("%s/image/dmargs", vm_path);
+
+ rc = libxl__xs_mknod(gc, t, path, roperm, ARRAY_SIZE(roperm));
+ if (rc) goto out;
+
+ rc = libxl__xs_write_checked(gc, t, path, dmargs);
+ if (rc) goto out;
+
+ rc = libxl__xs_mknod(gc, t, GCSPRINTF("%s/rtc/timeoffset", vm_path),
+ roperm, ARRAY_SIZE(roperm));
+ if (rc) goto out;
+ }
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc<0) goto out;
+ }
+
+ return 0;
+
+ out:
+ libxl__xs_transaction_abort(gc, &t);
+
+ return rc;
+}
+
+static int libxl__store_libxl_entry(libxl__gc *gc, uint32_t domid,
+ const char *name, const char *value)
+{
+ char *path = NULL;
+
+ path = libxl__xs_libxl_path(gc, domid);
+ path = libxl__sprintf(gc, "%s/%s", path, name);
+ return libxl__xs_printf(gc, XBT_NULL, path, "%s", value);
+}
+
+static void dmss_init(libxl__dm_spawn_state *dmss)
+{
+ libxl__ev_qmp_init(&dmss->qmp);
+ libxl__ev_time_init(&dmss->timeout);
+}
+
+static void dmss_dispose(libxl__gc *gc, libxl__dm_spawn_state *dmss)
+{
+ libxl__ev_qmp_dispose(gc, &dmss->qmp);
+ libxl__ev_time_deregister(gc, &dmss->timeout);
+}
+
+static void spawn_stubdom_pvqemu_cb(libxl__egc *egc,
+ libxl__dm_spawn_state *stubdom_dmss,
+ int rc);
+
+static void spawn_stub_launch_dm(libxl__egc *egc,
+ libxl__multidev *aodevs, int ret);
+
+static void stubdom_pvqemu_cb(libxl__egc *egc,
+ libxl__multidev *aodevs,
+ int rc);
+static void stubdom_pvqemu_unpaused(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs,
+ int rc);
+
+static void stubdom_xswait_cb(libxl__egc *egc, libxl__xswait_state *xswait,
+ int rc, const char *p);
+
+static void spawn_qmp_proxy(libxl__egc *egc,
+ libxl__stub_dm_spawn_state *sdss);
+
+static void qmp_proxy_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
+ const char *xsdata);
+
+static void qmp_proxy_startup_failed(libxl__egc *egc,
+ libxl__spawn_state *spawn,
+ int rc);
+
+static void qmp_proxy_detached(libxl__egc *egc,
+ libxl__spawn_state *spawn);
+
+static void qmp_proxy_spawn_outcome(libxl__egc *egc,
+ libxl__stub_dm_spawn_state *sdss,
+ int rc);
+
+char *libxl__stub_dm_name(libxl__gc *gc, const char *guest_name)
+{
+ return GCSPRINTF("%s-dm", guest_name);
+}
+
+void libxl__spawn_stub_dm(libxl__egc *egc, libxl__stub_dm_spawn_state *sdss)
+{
+ STATE_AO_GC(sdss->dm.spawn.ao);
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ int ret;
+ libxl_device_vfb *vfb;
+ libxl_device_vkb *vkb;
+ char **args;
+ struct xs_permissions perm[2];
+ xs_transaction_t t;
+
+ /* convenience aliases */
+ libxl_domain_config *const dm_config = &sdss->dm_config;
+ libxl_domain_config *const guest_config = sdss->dm.guest_config;
+ const int guest_domid = sdss->dm.guest_domid;
+ libxl__domain_build_state *const d_state = sdss->dm.build_state;
+ libxl__domain_build_state *const stubdom_state = &sdss->dm_state;
+
+ /* Initialise private part of sdss */
+ libxl__domain_build_state_init(stubdom_state);
+ dmss_init(&sdss->dm);
+ dmss_init(&sdss->pvqemu);
+ libxl__xswait_init(&sdss->xswait);
+
+ assert(libxl_defbool_val(guest_config->b_info.device_model_stubdomain));
+
+ sdss->pvqemu.guest_domid = INVALID_DOMID;
+
+ libxl_domain_create_info_init(&dm_config->c_info);
+ dm_config->c_info.type = LIBXL_DOMAIN_TYPE_PV;
+ dm_config->c_info.name = libxl__stub_dm_name(gc,
+ libxl__domid_to_name(gc, guest_domid));
+ /* When we are here to launch stubdom, ssidref is a valid value
+ * already, no need to parse it again.
+ */
+ dm_config->c_info.ssidref = guest_config->b_info.device_model_ssidref;
+ dm_config->c_info.ssid_label = NULL;
+
+ libxl_uuid_generate(&dm_config->c_info.uuid);
+
+ libxl_domain_build_info_init(&dm_config->b_info);
+ libxl_domain_build_info_init_type(&dm_config->b_info, LIBXL_DOMAIN_TYPE_PV);
+
+ dm_config->b_info.shadow_memkb = 0;
+ dm_config->b_info.max_vcpus = 1;
+ dm_config->b_info.max_memkb = guest_config->b_info.stubdomain_memkb;
+ dm_config->b_info.max_memkb += guest_config->b_info.video_memkb;
+ dm_config->b_info.target_memkb = dm_config->b_info.max_memkb;
+
+ dm_config->b_info.max_grant_frames = guest_config->b_info.max_grant_frames;
+ dm_config->b_info.max_maptrack_frames = 0;
+
+ dm_config->b_info.u.pv.features = "";
+
+ dm_config->b_info.device_model_version =
+ guest_config->b_info.device_model_version;
+ dm_config->b_info.device_model =
+ guest_config->b_info.device_model;
+ dm_config->b_info.extra = guest_config->b_info.extra;
+ dm_config->b_info.extra_pv = guest_config->b_info.extra_pv;
+ dm_config->b_info.extra_hvm = guest_config->b_info.extra_hvm;
+
+ dm_config->disks = guest_config->disks;
+ dm_config->num_disks = guest_config->num_disks;
+
+ libxl__dm_vifs_from_hvm_guest_config(gc, guest_config, dm_config);
+
+ dm_config->c_info.run_hotplug_scripts =
+ guest_config->c_info.run_hotplug_scripts;
+
+ ret = libxl__domain_config_setdefault(gc, dm_config, guest_domid);
+ if (ret) goto out;
+
+ if (libxl_defbool_val(guest_config->b_info.u.hvm.vnc.enable)
+ || libxl_defbool_val(guest_config->b_info.u.hvm.spice.enable)
+ || libxl_defbool_val(guest_config->b_info.u.hvm.sdl.enable)) {
+ GCNEW(vfb);
+ GCNEW(vkb);
+ libxl__vfb_and_vkb_from_hvm_guest_config(gc, guest_config, vfb, vkb);
+ dm_config->vfbs = vfb;
+ dm_config->num_vfbs = 1;
+ dm_config->vkbs = vkb;
+ dm_config->num_vkbs = 1;
+ }
+
+ if (guest_config->b_info.stubdomain_kernel &&
+ access(guest_config->b_info.stubdomain_kernel, R_OK) != 0) {
+ LOGED(ERROR, guest_domid, "could not access stubdomain kernel %s",
+ guest_config->b_info.stubdomain_kernel);
+ ret = ERROR_INVAL;
+ goto out;
+ }
+
+ if (guest_config->b_info.stubdomain_ramdisk &&
+ access(guest_config->b_info.stubdomain_ramdisk, R_OK) != 0) {
+ LOGED(ERROR, guest_domid, "could not access stubdomain ramdisk %s",
+ guest_config->b_info.stubdomain_ramdisk);
+ ret = ERROR_INVAL;
+ goto out;
+ }
+
+ stubdom_state->pv_kernel.path = guest_config->b_info.stubdomain_kernel;
+ stubdom_state->pv_ramdisk.path = guest_config->b_info.stubdomain_ramdisk;
+
+ /* fixme: this function can leak the stubdom if it fails */
+ ret = libxl__domain_make(gc, dm_config, stubdom_state,
+ &sdss->pvqemu.guest_domid, false);
+ if (ret)
+ goto out;
+ uint32_t dm_domid = sdss->pvqemu.guest_domid;
+ ret = libxl__domain_build(gc, dm_config, dm_domid, stubdom_state);
+ if (ret)
+ goto out;
+
+ ret = libxl__build_device_model_args(gc, "stubdom-dm", guest_domid,
+ guest_config, &args, NULL,
+ d_state, NULL);
+ if (ret) {
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ libxl__store_libxl_entry(gc, guest_domid, "dm-version",
+ libxl_device_model_version_to_string(dm_config->b_info.device_model_version));
+
+ libxl__write_stub_dmargs(gc, dm_domid, guest_domid, args,
+ libxl__stubdomain_is_linux(&guest_config->b_info));
+ libxl__xs_printf(gc, XBT_NULL,
+ GCSPRINTF("%s/image/device-model-domid",
+ libxl__xs_get_dompath(gc, guest_domid)),
+ "%d", dm_domid);
+ libxl__xs_printf(gc, XBT_NULL,
+ GCSPRINTF("%s/target",
+ libxl__xs_get_dompath(gc, dm_domid)),
+ "%d", guest_domid);
+ if (guest_config->b_info.device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+ /* qemu-xen is used as a dm in the stubdomain, so we set the bios
+ * accroding to this */
+ libxl__xs_printf(gc, XBT_NULL,
+ libxl__sprintf(gc, "%s/hvmloader/bios",
+ libxl__xs_get_dompath(gc, guest_domid)),
+ "%s",
+ libxl_bios_type_to_string(guest_config->b_info.u.hvm.bios));
+ }
+ ret = xc_domain_set_target(ctx->xch, dm_domid, guest_domid);
+ if (ret<0) {
+ LOGED(ERROR, guest_domid, "setting target domain %d -> %d",
+ dm_domid, guest_domid);
+ ret = ERROR_FAIL;
+ goto out;
+ }
+ xs_set_target(ctx->xsh, dm_domid, guest_domid);
+
+ perm[0].id = dm_domid;
+ perm[0].perms = XS_PERM_NONE;
+ perm[1].id = guest_domid;
+ perm[1].perms = XS_PERM_READ;
+retry_transaction:
+ t = xs_transaction_start(ctx->xsh);
+ xs_mkdir(ctx->xsh, t, DEVICE_MODEL_XS_PATH(gc, dm_domid, guest_domid, ""));
+ xs_set_permissions(ctx->xsh, t,
+ DEVICE_MODEL_XS_PATH(gc, dm_domid, guest_domid, ""),
+ perm, ARRAY_SIZE(perm));
+ if (!xs_transaction_end(ctx->xsh, t, 0))
+ if (errno == EAGAIN)
+ goto retry_transaction;
+
+ libxl__multidev_begin(ao, &sdss->multidev);
+ sdss->multidev.callback = spawn_stub_launch_dm;
+ libxl__add_disks(egc, ao, dm_domid, dm_config, &sdss->multidev);
+ libxl__multidev_prepared(egc, &sdss->multidev, 0);
+
+ return;
+
+out:
+ assert(ret);
+ spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, ret);
+}
+
+static void spawn_stub_launch_dm(libxl__egc *egc,
+ libxl__multidev *multidev, int ret)
+{
+ libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(multidev, *sdss, multidev);
+ STATE_AO_GC(sdss->dm.spawn.ao);
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ int i, num_console = STUBDOM_SPECIAL_CONSOLES;
+ libxl__device_console *console;
+
+ /* convenience aliases */
+ libxl_domain_config *const dm_config = &sdss->dm_config;
+ libxl_domain_config *const guest_config = sdss->dm.guest_config;
+ const int guest_domid = sdss->dm.guest_domid;
+ libxl__domain_build_state *const d_state = sdss->dm.build_state;
+ libxl__domain_build_state *const stubdom_state = &sdss->dm_state;
+ uint32_t dm_domid = sdss->pvqemu.guest_domid;
+ int need_qemu;
+
+ if (ret) {
+ LOGD(ERROR, guest_domid, "error connecting disk devices");
+ goto out;
+ }
+
+ for (i = 0; i < dm_config->num_nics; i++) {
+ /* We have to init the nic here, because we still haven't
+ * called libxl_device_nic_add at this point, but qemu needs
+ * the nic information to be complete.
+ */
+ ret = libxl__nic_devtype.set_default(gc, dm_domid, &dm_config->nics[i],
+ false);
+ if (ret)
+ goto out;
+ }
+ if (dm_config->num_vfbs) {
+ ret = libxl__device_add(gc, dm_domid, &libxl__vfb_devtype,
+ &dm_config->vfbs[0]);
+ if (ret) goto out;
+ }
+ if (dm_config->num_vkbs) {
+ ret = libxl__device_add(gc, dm_domid, &libxl__vkb_devtype,
+ &dm_config->vkbs[0]);
+ if (ret) goto out;
+ }
+
+ if (guest_config->b_info.u.hvm.serial) {
+ num_console++;
+ } else if (guest_config->b_info.u.hvm.serial_list) {
+ char **serial = guest_config->b_info.u.hvm.serial_list;
+ while (*(serial++))
+ num_console++;
+ }
+
+ console = libxl__calloc(gc, num_console, sizeof(libxl__device_console));
+
+ for (i = 0; i < num_console; i++) {
+ console[i].devid = i;
+ console[i].consback = LIBXL__CONSOLE_BACKEND_IOEMU;
+ /* STUBDOM_CONSOLE_LOGGING (console 0) is for minios logging
+ * STUBDOM_CONSOLE_SAVE (console 1) is for writing the save file
+ * STUBDOM_CONSOLE_RESTORE (console 2) is for reading the save file
+ */
+ switch (i) {
+ char *filename;
+ char *name;
+ case STUBDOM_CONSOLE_LOGGING:
+ name = GCSPRINTF("qemu-dm-%s",
+ libxl_domid_to_name(ctx, guest_domid));
+ ret = libxl_create_logfile(ctx, name, &filename);
+ if (ret) goto out;
+ console[i].output = GCSPRINTF("file:%s", filename);
+ free(filename);
+ /* will be changed back to LIBXL__CONSOLE_BACKEND_IOEMU if qemu
+ * will be in use */
+ console[i].consback = LIBXL__CONSOLE_BACKEND_XENCONSOLED;
+ break;
+ case STUBDOM_CONSOLE_SAVE:
+ console[i].output = GCSPRINTF("file:%s",
+ libxl__device_model_savefile(gc, guest_domid));
+ break;
+ case STUBDOM_CONSOLE_RESTORE:
+ if (d_state->saved_state)
+ console[i].output =
+ GCSPRINTF("pipe:%s", d_state->saved_state);
+ break;
+ case STUBDOM_CONSOLE_SERIAL:
+ if (guest_config->b_info.u.hvm.serial) {
+ console[i].output = guest_config->b_info.u.hvm.serial;
+ break;
+ }
+ /* fall-through */
+ default:
+ /* Serial_list is set, as otherwise num_consoles would be
+ * smaller and consoles 0-2 are handled above. */
+ assert(guest_config->b_info.u.hvm.serial_list);
+ console[i].output = guest_config->b_info.u.hvm.serial_list[
+ i-STUBDOM_CONSOLE_SERIAL];
+ break;
+ }
+ }
+
+ /*
+ * Until xenconsoled learns how to handle multiple consoles, require qemu
+ * in dom0 to serve consoles for a stubdomain - it require at least 3 of them.
+ */
+ need_qemu = 1 || libxl__need_xenpv_qemu(gc, &sdss->dm_config);
+
+ for (i = 0; i < num_console; i++) {
+ libxl__device device;
+ if (need_qemu)
+ console[i].consback = LIBXL__CONSOLE_BACKEND_IOEMU;
+ ret = libxl__device_console_add(gc, dm_domid, &console[i],
+ i == STUBDOM_CONSOLE_LOGGING ? stubdom_state : NULL,
+ &device);
+ if (ret)
+ goto out;
+ }
+
+ sdss->qmp_proxy_spawn.ao = ao;
+ if (libxl__stubdomain_is_linux(&guest_config->b_info)) {
+ spawn_qmp_proxy(egc, sdss);
+ } else {
+ qmp_proxy_spawn_outcome(egc, sdss, 0);
+ }
+
+ return;
+
+out:
+ assert(ret);
+ qmp_proxy_spawn_outcome(egc, sdss, ret);
+}
+
+static void spawn_qmp_proxy(libxl__egc *egc,
+ libxl__stub_dm_spawn_state *sdss)
+{
+ STATE_AO_GC(sdss->qmp_proxy_spawn.ao);
+ const uint32_t guest_domid = sdss->dm.guest_domid;
+ const uint32_t dm_domid = sdss->pvqemu.guest_domid;
+ const char *dom_path = libxl__xs_get_dompath(gc, dm_domid);
+ char **args;
+ int nr = 0;
+ int rc, logfile_w, null;
+
+ if (access(STUBDOM_QMP_PROXY_PATH, X_OK) < 0) {
+ LOGED(ERROR, guest_domid, "qmp proxy %s is not executable", STUBDOM_QMP_PROXY_PATH);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ sdss->qmp_proxy_spawn.what = GCSPRINTF("domain %d device model qmp proxy", guest_domid);
+ sdss->qmp_proxy_spawn.pidpath = GCSPRINTF("%s/image/qmp-proxy-pid", dom_path);
+ sdss->qmp_proxy_spawn.xspath = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID,
+ dm_domid, "/qmp-proxy-state");
+ sdss->qmp_proxy_spawn.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
+ sdss->qmp_proxy_spawn.midproc_cb = libxl__spawn_record_pid;
+ sdss->qmp_proxy_spawn.confirm_cb = qmp_proxy_confirm;
+ sdss->qmp_proxy_spawn.failure_cb = qmp_proxy_startup_failed;
+ sdss->qmp_proxy_spawn.detached_cb = qmp_proxy_detached;
+
+ const int arraysize = 6;
+ GCNEW_ARRAY(args, arraysize);
+ args[nr++] = STUBDOM_QMP_PROXY_PATH;
+ args[nr++] = GCSPRINTF("--state-path=%s", sdss->qmp_proxy_spawn.xspath);
+ args[nr++] = GCSPRINTF("%u", dm_domid);
+ args[nr++] = GCSPRINTF("%s/device-model/%u/qmp-vchan", dom_path, guest_domid);
+ args[nr++] = (char*)libxl__qemu_qmp_path(gc, guest_domid);
+ args[nr++] = NULL;
+ assert(nr == arraysize);
+
+ logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qmp-proxy-%s",
+ sdss->dm_config.c_info.name));
+ if (logfile_w < 0) {
+ rc = logfile_w;
+ goto out;
+ }
+ null = open("/dev/null", O_RDWR);
+ if (null < 0) {
+ LOGED(ERROR, guest_domid, "unable to open /dev/null");
+ rc = ERROR_FAIL;
+ goto out_close;
+ }
+
+ rc = libxl__spawn_spawn(egc, &sdss->qmp_proxy_spawn);
+ if (rc < 0)
+ goto out_close;
+ if (!rc) { /* inner child */
+ setsid();
+ libxl__exec(gc, null, null, logfile_w, STUBDOM_QMP_PROXY_PATH, args, NULL);
+ /* unreachable */
+ }
+
+ rc = 0;
+
+out_close:
+ if (logfile_w >= 0)
+ close(logfile_w);
+ if (null >= 0)
+ close(null);
+out:
+ if (rc)
+ qmp_proxy_spawn_outcome(egc, sdss, rc);
+}
+
+static void qmp_proxy_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
+ const char *xsdata)
+{
+ STATE_AO_GC(spawn->ao);
+
+ if (!xsdata)
+ return;
+
+ if (strcmp(xsdata, "running"))
+ return;
+
+ libxl__spawn_initiate_detach(gc, spawn);
+}
+
+static void qmp_proxy_startup_failed(libxl__egc *egc,
+ libxl__spawn_state *spawn,
+ int rc)
+{
+ libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(spawn, *sdss, qmp_proxy_spawn);
+ qmp_proxy_spawn_outcome(egc, sdss, rc);
+}
+
+static void qmp_proxy_detached(libxl__egc *egc,
+ libxl__spawn_state *spawn)
+{
+ libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(spawn, *sdss, qmp_proxy_spawn);
+ qmp_proxy_spawn_outcome(egc, sdss, 0);
+}
+
+static void qmp_proxy_spawn_outcome(libxl__egc *egc,
+ libxl__stub_dm_spawn_state *sdss,
+ int rc)
+{
+ STATE_AO_GC(sdss->qmp_proxy_spawn.ao);
+ /*
+ * Until xenconsoled learns how to handle multiple consoles, require qemu
+ * in dom0 to serve consoles for a stubdomain - it require at least 3 of them.
+ */
+ int need_pvqemu = 1 || libxl__need_xenpv_qemu(gc, &sdss->dm_config);
+
+ if (rc) goto out;
+
+ if (need_pvqemu < 0) {
+ rc = need_pvqemu;
+ goto out;
+ }
+
+ sdss->pvqemu.spawn.ao = ao;
+ sdss->pvqemu.guest_config = &sdss->dm_config;
+ sdss->pvqemu.build_state = &sdss->dm_state;
+ sdss->pvqemu.callback = spawn_stubdom_pvqemu_cb;
+ if (need_pvqemu) {
+ libxl__spawn_local_dm(egc, &sdss->pvqemu);
+ } else {
+ /* If dom0 qemu not needed, do not launch it */
+ spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, 0);
+ }
+
+ return;
+
+out:
+ assert(rc);
+ spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, rc);
+}
+
+static void spawn_stubdom_pvqemu_cb(libxl__egc *egc,
+ libxl__dm_spawn_state *stubdom_dmss,
+ int rc)
+{
+ libxl__stub_dm_spawn_state *sdss =
+ CONTAINER_OF(stubdom_dmss, *sdss, pvqemu);
+ STATE_AO_GC(sdss->dm.spawn.ao);
+ uint32_t dm_domid = sdss->pvqemu.guest_domid;
+ libxl_domain_config *d_config = stubdom_dmss->guest_config;
+
+ if (rc) goto out;
+
+ if (d_config->num_nics > 0) {
+ libxl__multidev_begin(ao, &sdss->multidev);
+ sdss->multidev.callback = stubdom_pvqemu_cb;
+ libxl__add_nics(egc, ao, dm_domid, d_config, &sdss->multidev);
+ libxl__multidev_prepared(egc, &sdss->multidev, 0);
+ return;
+ }
+
+out:
+ stubdom_pvqemu_cb(egc, &sdss->multidev, rc);
+}
+
+static void stubdom_pvqemu_cb(libxl__egc *egc,
+ libxl__multidev *multidev,
+ int rc)
+{
+ libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(multidev, *sdss, multidev);
+ STATE_AO_GC(sdss->dm.spawn.ao);
+ uint32_t dm_domid = sdss->pvqemu.guest_domid;
+
+ if (rc) {
+ LOGED(ERROR, sdss->dm.guest_domid,
+ "error connecting nics devices");
+ goto out;
+ }
+
+ sdss->pvqemu.dmrs.ao = ao;
+ sdss->pvqemu.dmrs.domid = dm_domid;
+ sdss->pvqemu.dmrs.callback = stubdom_pvqemu_unpaused;
+ libxl__domain_unpause(egc, &sdss->pvqemu.dmrs); /* must be last */
+ return;
+out:
+ stubdom_pvqemu_unpaused(egc, &sdss->pvqemu.dmrs, rc);
+}
+
+static void stubdom_pvqemu_unpaused(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs,
+ int rc)
+{
+ libxl__stub_dm_spawn_state *sdss =
+ CONTAINER_OF(dmrs, *sdss, pvqemu.dmrs);
+ STATE_AO_GC(sdss->dm.spawn.ao);
+ uint32_t dm_domid = sdss->pvqemu.guest_domid;
+
+ if (rc) goto out;
+
+ sdss->xswait.ao = ao;
+ sdss->xswait.what = GCSPRINTF("Stubdom %u for %u startup",
+ dm_domid, sdss->dm.guest_domid);
+ sdss->xswait.path = DEVICE_MODEL_XS_PATH(gc, dm_domid, sdss->dm.guest_domid,
+ "/state");
+ sdss->xswait.timeout_ms = LIBXL_STUBDOM_START_TIMEOUT * 1000;
+ sdss->xswait.callback = stubdom_xswait_cb;
+ rc = libxl__xswait_start(gc, &sdss->xswait);
+ if (rc) goto out;
+
+ return;
+
+ out:
+ stubdom_xswait_cb(egc, &sdss->xswait, rc, NULL);
+}
+
+static void stubdom_xswait_cb(libxl__egc *egc, libxl__xswait_state *xswait,
+ int rc, const char *p)
+{
+ EGC_GC;
+ libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(xswait, *sdss, xswait);
+
+ if (rc) {
+ if (rc == ERROR_TIMEDOUT)
+ LOGD(ERROR, sdss->dm.guest_domid,
+ "%s: startup timed out", xswait->what);
+ goto out;
+ }
+
+ if (!p) return;
+
+ if (strcmp(p, "running"))
+ return;
+ out:
+ libxl__domain_build_state_dispose(&sdss->dm_state);
+ libxl__xswait_stop(gc, xswait);
+ dmss_dispose(gc, &sdss->dm);
+ dmss_dispose(gc, &sdss->pvqemu);
+ sdss->callback(egc, &sdss->dm, rc);
+}
+
+/* callbacks passed to libxl__spawn_spawn */
+static void device_model_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
+ const char *xsdata);
+static void device_model_startup_failed(libxl__egc *egc,
+ libxl__spawn_state *spawn,
+ int rc);
+static void device_model_detached(libxl__egc *egc,
+ libxl__spawn_state *spawn);
+static void device_model_qmp_cb(libxl__egc *egc, libxl__ev_qmp *ev,
+ const libxl__json_object *response,
+ int rc);
+
+/* our "next step" function, called from those callbacks and elsewhere */
+static void device_model_spawn_outcome(libxl__egc *egc,
+ libxl__dm_spawn_state *dmss,
+ int rc);
+static void device_model_postconfig_chardev(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
+static void device_model_postconfig_vnc(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
+static void device_model_postconfig_vnc_passwd(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
+static void devise_model_postconfig_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void device_model_postconfig_done(libxl__egc *egc,
+ libxl__dm_spawn_state *dmss, int rc);
+
+void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state *dmss)
+{
+ /* convenience aliases */
+ const int domid = dmss->guest_domid;
+ libxl__domain_build_state *const state = dmss->build_state;
+ libxl__spawn_state *const spawn = &dmss->spawn;
+
+ STATE_AO_GC(dmss->spawn.ao);
+
+ libxl_ctx *ctx = CTX;
+ libxl_domain_config *guest_config = dmss->guest_config;
+ const libxl_domain_create_info *c_info = &guest_config->c_info;
+ const libxl_domain_build_info *b_info = &guest_config->b_info;
+ const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
+ char *path;
+ int logfile_w, null;
+ int rc;
+ char **args, **arg, **envs;
+ xs_transaction_t t;
+ char *vm_path;
+ char **pass_stuff;
+ const char *dm;
+ int dm_state_fd = -1;
+
+ dmss_init(dmss);
+
+ if (libxl_defbool_val(b_info->device_model_stubdomain)) {
+ abort();
+ }
+
+ dm = libxl__domain_device_model(gc, b_info);
+ if (!dm) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ if (access(dm, X_OK) < 0) {
+ LOGED(ERROR, domid, "device model %s is not executable", dm);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__domain_get_device_model_uid(gc, dmss);
+ if (rc)
+ goto out;
+
+ if (b_info->device_model_version
+ == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN &&
+ libxl_defbool_val(b_info->dm_restrict)) {
+ /* If we have to use dm_restrict, QEMU needs to be new enough
+ * and will have the new interface where we can pre-open the
+ * QMP socket. */
+ rc = libxl__pre_open_qmp_socket(gc, domid, &state->dm_monitor_fd);
+ if (rc) goto out;
+ }
+
+ rc = libxl__build_device_model_args(gc, dm, domid, guest_config,
+ &args, &envs, state,
+ &dm_state_fd);
+ if (rc)
+ goto out;
+
+ if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
+ path = xs_get_domain_path(ctx->xsh, domid);
+ libxl__xs_printf(gc, XBT_NULL,
+ GCSPRINTF("%s/hvmloader/bios", path),
+ "%s", libxl_bios_type_to_string(b_info->u.hvm.bios));
+ /* Disable relocating memory to make the MMIO hole larger
+ * unless we're running qemu-traditional and vNUMA is not
+ * configured. */
+ libxl__xs_printf(gc, XBT_NULL,
+ GCSPRINTF("%s/hvmloader/allow-memory-relocate", path),
+ "%d",
+ b_info->device_model_version==LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL &&
+ !libxl__vnuma_configured(b_info));
+ free(path);
+ }
+
+ path = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid, "");
+ xs_mkdir(ctx->xsh, XBT_NULL, path);
+
+ if (b_info->type == LIBXL_DOMAIN_TYPE_HVM &&
+ b_info->device_model_version
+ == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL)
+ libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/disable_pf", path),
+ "%d", !libxl_defbool_val(b_info->u.hvm.xen_platform_pci));
+
+ logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qemu-dm-%s",
+ c_info->name));
+ if (logfile_w < 0) {
+ rc = logfile_w;
+ goto out;
+ }
+ null = open("/dev/null", O_RDONLY);
+ if (null < 0) {
+ LOGED(ERROR, domid, "unable to open /dev/null");
+ rc = ERROR_FAIL;
+ goto out_close;
+ }
+
+ const char *dom_path = libxl__xs_get_dompath(gc, domid);
+
+ /*
+ * If we're starting the dm with a non-root UID, save the UID so
+ * that we can reliably kill it and any subprocesses
+ */
+ if (state->dm_kill_uid)
+ libxl__xs_printf(gc, XBT_NULL,
+ GCSPRINTF("%s/image/device-model-kill-uid", dom_path),
+ "%s", state->dm_kill_uid);
+
+ if (vnc && vnc->passwd) {
+ /* This xenstore key will only be used by qemu-xen-traditionnal.
+ * The code to supply vncpasswd to qemu-xen is later. */
+retry_transaction:
+ /* Find uuid and the write the vnc password to xenstore for qemu. */
+ t = xs_transaction_start(ctx->xsh);
+ vm_path = libxl__xs_read(gc,t,GCSPRINTF("%s/vm", dom_path));
+ if (vm_path) {
+ /* Now write the vncpassword into it. */
+ pass_stuff = libxl__calloc(gc, 3, sizeof(char *));
+ pass_stuff[0] = "vncpasswd";
+ pass_stuff[1] = vnc->passwd;
+ libxl__xs_writev(gc,t,vm_path,pass_stuff);
+ if (!xs_transaction_end(ctx->xsh, t, 0))
+ if (errno == EAGAIN)
+ goto retry_transaction;
+ }
+ }
+
+ LOGD(DEBUG, domid, "Spawning device-model %s with arguments:", dm);
+ for (arg = args; *arg; arg++)
+ LOGD(DEBUG, domid, " %s", *arg);
+ if (*envs) {
+ LOGD(DEBUG, domid, "Spawning device-model %s with additional environment:", dm);
+ for (arg = envs; *arg; arg += 2)
+ LOGD(DEBUG, domid, " %s=%s", arg[0], arg[1]);
+ }
+
+ spawn->what = GCSPRINTF("domain %d device model", domid);
+ spawn->xspath = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid,
+ "/state");
+ spawn->timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
+ spawn->pidpath = GCSPRINTF("%s/image/device-model-pid", dom_path);
+ spawn->midproc_cb = libxl__spawn_record_pid;
+ spawn->confirm_cb = device_model_confirm;
+ spawn->failure_cb = device_model_startup_failed;
+ spawn->detached_cb = device_model_detached;
+
+ if (state->dm_monitor_fd >= 0) {
+ /* There is a valid QMP socket available now,
+ * use it to find out when QEMU is ready */
+ dmss->qmp.ao = ao;
+ dmss->qmp.callback = device_model_qmp_cb;
+ dmss->qmp.domid = domid;
+ dmss->qmp.payload_fd = -1;
+ rc = libxl__ev_qmp_send(egc, &dmss->qmp, "query-status", NULL);
+ if (rc) goto out_close;
+ }
+
+ rc = libxl__spawn_spawn(egc, spawn);
+ if (rc < 0)
+ goto out_close;
+ if (!rc) { /* inner child */
+ setsid();
+ if (libxl_defbool_val(b_info->dm_restrict)) {
+ rc = libxl__local_dm_preexec_restrict(gc);
+ if (rc)
+ _exit(-1);
+ }
+ libxl__exec(gc, null, logfile_w, logfile_w, dm, args, envs);
+ }
+
+ rc = 0;
+
+out_close:
+ if (null >= 0) close(null);
+ if (logfile_w >= 0) close(logfile_w);
+out:
+ if (dm_state_fd >= 0) close(dm_state_fd);
+ if (rc)
+ device_model_spawn_outcome(egc, dmss, rc);
+}
+
+bool libxl__query_qemu_backend(libxl__gc *gc, uint32_t domid,
+ uint32_t backend_id, const char *type, bool def)
+{
+ char *path;
+ char **dir;
+ unsigned int n;
+
+ path = GCSPRINTF("%s/device-model/%u/backends",
+ libxl__xs_get_dompath(gc, backend_id), domid);
+ dir = libxl__xs_directory(gc, XBT_NULL, path, &n);
+ if (!dir)
+ return def;
+
+ path = GCSPRINTF("%s/device-model/%u/backends/%s",
+ libxl__xs_get_dompath(gc, backend_id), domid, type);
+ dir = libxl__xs_directory(gc, XBT_NULL, path, &n);
+
+ return !!dir;
+}
+
+static void device_model_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
+ const char *xsdata)
+{
+ STATE_AO_GC(spawn->ao);
+
+ if (!xsdata)
+ return;
+
+ if (strcmp(xsdata, "running"))
+ return;
+
+ libxl__spawn_initiate_detach(gc, spawn);
+}
+
+static void device_model_startup_failed(libxl__egc *egc,
+ libxl__spawn_state *spawn,
+ int rc)
+{
+ libxl__dm_spawn_state *dmss = CONTAINER_OF(spawn, *dmss, spawn);
+ device_model_spawn_outcome(egc, dmss, rc);
+}
+
+static void device_model_detached(libxl__egc *egc,
+ libxl__spawn_state *spawn)
+{
+ libxl__dm_spawn_state *dmss = CONTAINER_OF(spawn, *dmss, spawn);
+ device_model_spawn_outcome(egc, dmss, 0);
+}
+
+static void device_model_qmp_cb(libxl__egc *egc, libxl__ev_qmp *ev,
+ const libxl__json_object *response,
+ int rc)
+{
+ EGC_GC;
+ libxl__dm_spawn_state *dmss = CONTAINER_OF(ev, *dmss, qmp);
+ const libxl__json_object *o;
+ const char *status;
+ const char *expected_state;
+
+ libxl__ev_qmp_dispose(gc, ev);
+
+ if (rc)
+ goto failed;
+
+ o = libxl__json_map_get("status", response, JSON_STRING);
+ if (!o) {
+ LOGD(ERROR, ev->domid,
+ "Missing 'status' in response to 'query-status'");
+ LOGD(DEBUG, ev->domid, ".. instead, got: %s", JSON(response));
+ rc = ERROR_QEMU_API;
+ goto failed;
+ }
+ status = libxl__json_object_get_string(o);
+ if (!dmss->build_state->saved_state)
+ expected_state = "prelaunch";
+ else
+ expected_state = "paused";
+ if (strcmp(status, expected_state)) {
+ LOGD(ERROR, ev->domid, "Unexpected QEMU status: %s", status);
+ rc = ERROR_NOT_READY;
+ goto failed;
+ }
+
+ libxl__spawn_initiate_detach(gc, &dmss->spawn);
+ return;
+
+failed:
+ LOGD(ERROR, ev->domid, "QEMU did not start properly, rc=%d", rc);
+ libxl__spawn_initiate_failure(egc, &dmss->spawn, rc);
+}
+
+static void device_model_spawn_outcome(libxl__egc *egc,
+ libxl__dm_spawn_state *dmss,
+ int rc)
+{
+ STATE_AO_GC(dmss->spawn.ao);
+ int ret2;
+
+ /* Convenience aliases */
+ libxl_domain_config *const d_config = dmss->guest_config;
+
+ if (rc)
+ LOGD(ERROR, dmss->guest_domid,
+ "%s: spawn failed (rc=%d)", dmss->spawn.what, rc);
+
+ libxl__domain_build_state *state = dmss->build_state;
+
+ if (state->saved_state) {
+ ret2 = unlink(state->saved_state);
+ if (ret2) {
+ LOGED(ERROR, dmss->guest_domid, "%s: failed to remove device-model state %s",
+ dmss->spawn.what, state->saved_state);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ /* Check if spawn failed */
+ if (rc) goto out;
+
+ if (d_config->b_info.device_model_version
+ == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+ rc = libxl__ev_time_register_rel(ao, &dmss->timeout,
+ devise_model_postconfig_timeout,
+ LIBXL_QMP_CMD_TIMEOUT * 1000);
+ if (rc) goto out;
+ dmss->qmp.ao = ao;
+ dmss->qmp.domid = dmss->guest_domid;
+ dmss->qmp.payload_fd = -1;
+ dmss->qmp.callback = device_model_postconfig_chardev;
+ rc = libxl__ev_qmp_send(egc, &dmss->qmp, "query-chardev", NULL);
+ if (rc) goto out;
+ return;
+ }
+
+ out:
+ device_model_postconfig_done(egc, dmss, rc); /* must be last */
+}
+
+static void device_model_postconfig_chardev(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
+{
+ EGC_GC;
+ libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp);
+ const libxl__json_object *item = NULL;
+ const libxl__json_object *o = NULL;
+ int i = 0;
+ const char *label;
+ const char *filename;
+ int port;
+ char *endptr;
+ const char *dompath;
+ const char serial[] = "serial";
+ const size_t seriall = sizeof(serial) - 1;
+ const char pty[] = "pty:";
+ const size_t ptyl = sizeof(pty) - 1;
+
+ if (rc) goto out;
+
+ /*
+ * query-chardev response:
+ * [{ 'label': 'str',
+ * 'filename': 'str',
+ * 'frontend-open': 'bool' }, ...]
+ */
+
+ for (i = 0; (item = libxl__json_array_get(response, i)); i++) {
+ o = libxl__json_map_get("label", item, JSON_STRING);
+ if (!o) goto protocol_error;
+ label = libxl__json_object_get_string(o);
+
+ /* Check if the "label" start with "serial". */
+ if (!label || strncmp(label, serial, seriall))
+ continue;
+ port = strtol(label + seriall, &endptr, 10);
+ if (*(label + seriall) == '\0' || *endptr != '\0') {
+ LOGD(ERROR, qmp->domid,
+ "Invalid serial port number: %s", label);
+ rc = ERROR_QEMU_API;
+ goto out;
+ }
+
+ o = libxl__json_map_get("filename", item, JSON_STRING);
+ if (!o) goto protocol_error;
+ filename = libxl__json_object_get_string(o);
+
+ /* Check if filename start with "pty:" */
+ if (!filename || strncmp(filename, pty, ptyl))
+ continue;
+
+ dompath = libxl__xs_get_dompath(gc, qmp->domid);
+ if (!dompath) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = libxl__xs_printf(gc, XBT_NULL,
+ GCSPRINTF("%s/serial/%d/tty", dompath, port),
+ "%s", filename + ptyl);
+ if (rc) goto out;
+ }
+
+ qmp->callback = device_model_postconfig_vnc;
+ rc = libxl__ev_qmp_send(egc, qmp, "query-vnc", NULL);
+ if (rc) goto out;
+ return;
+
+protocol_error:
+ rc = ERROR_QEMU_API;
+ LOGD(ERROR, qmp->domid,
+ "unexpected response to QMP cmd 'query-chardev', received:\n%s",
+ JSON(response));
+out:
+ device_model_postconfig_done(egc, dmss, rc); /* must be last */
+}
+
+static void device_model_postconfig_vnc(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
+{
+ EGC_GC;
+ libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp);
+ const libxl_vnc_info *vnc = libxl__dm_vnc(dmss->guest_config);
+ const libxl__json_object *o;
+ libxl__json_object *args = NULL;
+
+ if (rc) goto out;
+
+ /*
+ * query-vnc response:
+ * { 'enabled': 'bool', '*host': 'str', '*service': 'str' }
+ */
+
+ o = libxl__json_map_get("enabled", response, JSON_BOOL);
+ if (!o) goto protocol_error;
+ if (libxl__json_object_get_bool(o)) {
+ const char *addr, *port;
+ const char *dompath;
+
+ o = libxl__json_map_get("host", response, JSON_STRING);
+ if (!o) goto protocol_error;
+ addr = libxl__json_object_get_string(o);
+ o = libxl__json_map_get("service", response, JSON_STRING);
+ if (!o) goto protocol_error;
+ port = libxl__json_object_get_string(o);
+
+ dompath = libxl__xs_get_dompath(gc, qmp->domid);
+ if (!dompath) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = libxl__xs_printf(gc, XBT_NULL,
+ GCSPRINTF("%s/console/vnc-listen", dompath),
+ "%s", addr);
+ if (rc) goto out;
+ rc = libxl__xs_printf(gc, XBT_NULL,
+ GCSPRINTF("%s/console/vnc-port", dompath),
+ "%s", port);
+ if (rc) goto out;
+ }
+
+ if (vnc && vnc->passwd && vnc->passwd[0]) {
+ qmp->callback = device_model_postconfig_vnc_passwd;
+ libxl__qmp_param_add_string(gc, &args, "password", vnc->passwd);
+ rc = libxl__ev_qmp_send(egc, qmp, "change-vnc-password", args);
+ if (rc) goto out;
+ return;
+ }
+
+ rc = 0;
+ goto out;
+
+protocol_error:
+ rc = ERROR_QEMU_API;
+ LOGD(ERROR, qmp->domid,
+ "unexpected response to QMP cmd 'query-vnc', received:\n%s",
+ JSON(response));
+out:
+ device_model_postconfig_done(egc, dmss, rc); /* must be last */
+}
+
+static void device_model_postconfig_vnc_passwd(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
+{
+ EGC_GC;
+ libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp);
+ const libxl_vnc_info *vnc = libxl__dm_vnc(dmss->guest_config);
+ const char *dompath;
+
+ if (rc) goto out;
+
+ dompath = libxl__xs_get_dompath(gc, qmp->domid);
+ if (!dompath) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = libxl__xs_printf(gc, XBT_NULL,
+ GCSPRINTF("%s/console/vnc-pass", dompath),
+ "%s", vnc->passwd);
+
+out:
+ device_model_postconfig_done(egc, dmss, rc); /* must be last */
+}
+
+void devise_model_postconfig_timeout(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ libxl__dm_spawn_state *dmss = CONTAINER_OF(ev, *dmss, timeout);
+ device_model_postconfig_done(egc, dmss, rc); /* must be last */
+}
+
+
+static void device_model_postconfig_done(libxl__egc *egc,
+ libxl__dm_spawn_state *dmss,
+ int rc)
+{
+ EGC_GC;
+
+ if (rc)
+ LOGD(ERROR, dmss->guest_domid,
+ "Post DM startup configs failed, rc=%d", rc);
+ dmss_dispose(gc, dmss);
+ dmss->callback(egc, dmss, rc);
+}
+
+void libxl__spawn_qdisk_backend(libxl__egc *egc, libxl__dm_spawn_state *dmss)
+{
+ STATE_AO_GC(dmss->spawn.ao);
+ flexarray_t *dm_args, *dm_envs;
+ char **args, **envs;
+ const char *dm;
+ int logfile_w, null = -1, rc;
+ uint32_t domid = dmss->guest_domid;
+
+ dmss_init(dmss);
+
+ /* Always use qemu-xen as device model */
+ dm = qemu_xen_path(gc);
+
+ dm_args = flexarray_make(gc, 15, 1);
+ dm_envs = flexarray_make(gc, 1, 1);
+
+ flexarray_vappend(dm_args, dm, "-xen-domid",
+ GCSPRINTF("%d", domid), NULL);
+ flexarray_append(dm_args, "-xen-attach");
+ flexarray_vappend(dm_args, "-name",
+ GCSPRINTF("domain-%u", domid), NULL);
+ flexarray_append(dm_args, "-nographic");
+ flexarray_vappend(dm_args, "-M", "xenpv", NULL);
+ flexarray_vappend(dm_args, "-monitor", "/dev/null", NULL);
+ flexarray_vappend(dm_args, "-serial", "/dev/null", NULL);
+ flexarray_vappend(dm_args, "-parallel", "/dev/null", NULL);
+ flexarray_append(dm_args, NULL);
+ args = (char **) flexarray_contents(dm_args);
+
+ libxl__set_qemu_env_for_xsa_180(gc, dm_envs);
+ envs = (char **) flexarray_contents(dm_envs);
+
+ logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qdisk-%u", domid));
+ if (logfile_w < 0) {
+ rc = logfile_w;
+ goto out;
+ }
+ null = open("/dev/null", O_RDONLY);
+ if (null < 0) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ dmss->guest_config = NULL;
+ /*
+ * Clearly specify Qemu not using a saved state, so
+ * device_model_spawn_outcome doesn't try to unlink it.
+ */
+ dmss->build_state = libxl__zalloc(gc, sizeof(*dmss->build_state));
+ dmss->build_state->saved_state = 0;
+
+ dmss->spawn.what = GCSPRINTF("domain %u Qdisk backend", domid);
+ dmss->spawn.xspath = GCSPRINTF("device-model/%u/state", domid);
+ dmss->spawn.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
+ /*
+ * We cannot save Qemu pid anywhere in the xenstore guest dir,
+ * because we will call this from unprivileged driver domains,
+ * so save it in the current domain libxl private dir.
+ */
+ dmss->spawn.pidpath = GCSPRINTF("libxl/%u/qdisk-backend-pid", domid);
+ dmss->spawn.midproc_cb = libxl__spawn_record_pid;
+ dmss->spawn.confirm_cb = device_model_confirm;
+ dmss->spawn.failure_cb = device_model_startup_failed;
+ dmss->spawn.detached_cb = device_model_detached;
+ rc = libxl__spawn_spawn(egc, &dmss->spawn);
+ if (rc < 0)
+ goto out;
+ if (!rc) { /* inner child */
+ setsid();
+ libxl__exec(gc, null, logfile_w, logfile_w, dm, args, envs);
+ }
+
+ rc = 0;
+out:
+ dmss_dispose(gc, dmss);
+ if (logfile_w >= 0) close(logfile_w);
+ if (null >= 0) close(null);
+ /* callback on error only, success goes via dmss->spawn.*_cb */
+ if (rc) dmss->callback(egc, dmss, rc);
+ return;
+}
+
+/* Generic function to signal a Qemu instance to exit */
+static int kill_device_model(libxl__gc *gc, const char *xs_path_pid)
+{
+ return libxl__kill_xs_path(gc, xs_path_pid, "Device Model");
+}
+
+/* Helper to destroy a Qdisk backend */
+int libxl__destroy_qdisk_backend(libxl__gc *gc, uint32_t domid)
+{
+ char *pid_path;
+ int rc;
+
+ pid_path = GCSPRINTF("libxl/%u/qdisk-backend-pid", domid);
+
+ rc = kill_device_model(gc, pid_path);
+ if (rc)
+ goto out;
+
+ libxl__xs_rm_checked(gc, XBT_NULL, pid_path);
+ libxl__xs_rm_checked(gc, XBT_NULL,
+ GCSPRINTF("device-model/%u", domid));
+
+out:
+ return rc;
+}
+
+/* Asynchronous device model destroy functions */
+
+static int kill_device_model_uid_child(libxl__destroy_devicemodel_state *ddms,
+ const char *dm_kill_uid_str);
+
+static void kill_device_model_uid_cb(libxl__egc *egc,
+ libxl__ev_child *destroyer,
+ pid_t pid, int status);
+
+/*
+ * If we have a uid, we shouldn't kill by pid. This is because a
+ * hostile QEMU might have exited, in which case the pid we have may
+ * be that of another process.
+ *
+ * The running devicemodel has permission over a specific domain id;
+ * this means that ideally we wouldn't the domain in question (freeing
+ * up the domain id for reuse) until we're confident that we've killed
+ * the domain.
+ *
+ * In general, destroy as much as we can; but return an error if there
+ * are any errors, so that the domain destroy will be aborted, and the
+ * domain itself will remain, giving the admin an opportunity to fix
+ * any issues and re-try the domain destroy.
+ */
+void libxl__destroy_device_model(libxl__egc *egc,
+ libxl__destroy_devicemodel_state *ddms)
+{
+ STATE_AO_GC(ddms->ao);
+ int rc;
+ int domid = ddms->domid;
+ char *path;
+ const char *dm_kill_uid_str = NULL;
+ int reaper_pid;
+
+ ddms->rc = 0;
+
+ path = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid, "");
+ rc = libxl__xs_rm_checked(gc, XBT_NULL, path);
+ if (rc) {
+ ACCUMULATE_RC(ddms->rc);
+ LOGD(ERROR, domid, "xs_rm failed for %s", path);
+ }
+
+ /*
+ * See if we should try to kill by uid
+ */
+ path = GCSPRINTF("/local/domain/%d/image/device-model-kill-uid", domid);
+ rc = libxl__xs_read_checked(gc, XBT_NULL, path, &dm_kill_uid_str);
+
+ /*
+ * If there was an error here, accumulate the error and fall back
+ * to killing by pid.
+ */
+ if (rc) {
+ /*
+ * Technically the state of the string passed to libxl__xs_read_checked() is
+ * "undefined" in the case rc == 0 (according to libxl_internal.h). Set it to
+ * NULL to prevent undefined behavior.
+ */
+ dm_kill_uid_str = NULL;
+ ACCUMULATE_RC(ddms->rc);
+ LOGD(ERROR, domid, "Reading dm UID path failed for %s", path);
+ }
+
+ /* The DM has its own uid; Attempt to kill all processes with that UID */
+ if (dm_kill_uid_str) {
+ LOGD(DEBUG, domid, "Found DM uid %s, destroying by uid",
+ dm_kill_uid_str);
+
+ reaper_pid = libxl__ev_child_fork(gc, &ddms->destroyer,
+ kill_device_model_uid_cb);
+ if (reaper_pid < 0) {
+ rc = ERROR_FAIL;
+ ACCUMULATE_RC(ddms->rc);
+ /*
+ * Note that if this fails, we still don't kill by pid, to
+ * make sure that an untrusted DM has not "maliciously"
+ * exited (potentially causing us to kill an unrelated
+ * process which happened to get the same pid).
+ */
+ goto out;
+ }
+
+ if (!reaper_pid) { /* child */
+ rc = kill_device_model_uid_child(ddms, dm_kill_uid_str);
+ assert(rc <= 0 && rc >= -125);
+ _exit(-rc);
+ }
+
+ /*
+ * Parent of successful fork; execution will pick up in
+ * kill_device_model_uid_cb when child exits
+ */
+ return;
+ }
+
+ /*
+ * No uid to kill; attept to kill by pid.
+ */
+ LOGD(DEBUG, domid, "Didn't find dm UID; destroying by pid");
+
+ path = GCSPRINTF("/local/domain/%d/image/device-model-pid", domid);
+ rc = kill_device_model(gc, path);
+
+ if (rc) {
+ ACCUMULATE_RC(ddms->rc);
+ LOGD(ERROR, domid, "Killing device model pid from path %s", path);
+ }
+
+out:
+ /*
+ * NB that we always pass '0' here for the "status of exited
+ * process"; since there is no process, it always "succeeds".
+ * Errors are accumulated in ddms->rc and will be handled
+ * correctly.
+ */
+ kill_device_model_uid_cb(egc, &ddms->destroyer, -1, 0);
+ return;
+}
+
+/*
+ * Note that this attempts to grab a file lock, so must be called from
+ * a sub-process.
+ */
+static int get_reaper_lock_and_uid(libxl__destroy_devicemodel_state *ddms,
+ uid_t *reaper_uid)
+{
+ STATE_AO_GC(ddms->ao);
+ int domid = ddms->domid;
+ int r;
+ const char * lockfile;
+ int fd; /* "leaked" into the general process context (even on failure) */
+
+ /* Try to lock the "reaper uid" */
+ lockfile = GCSPRINTF("%s/dm-reaper-lock", libxl__run_dir_path());
+
+ /*
+ * NB that since we've just forked, we can't have any
+ * threads; so we don't need the libxl__carefd
+ * infrastructure here.
+ */
+ fd = open(lockfile, O_RDWR|O_CREAT, 0644);
+ if (fd < 0) {
+ LOGED(ERROR, domid,
+ "unexpected error while trying to open lockfile %s, errno=%d",
+ lockfile, errno);
+ return ERROR_FAIL;
+ }
+
+ /* Try to lock the file, retrying on EINTR */
+ for (;;) {
+ r = flock(fd, LOCK_EX);
+ if (!r)
+ break;
+ if (errno != EINTR) {
+ /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
+ LOGED(ERROR, domid,
+ "unexpected error while trying to lock %s, fd=%d, errno=%d",
+ lockfile, fd, errno);
+ return ERROR_FAIL;
+ }
+ }
+
+ /*
+ * Get reaper_uid. If we can't find such a uid, return an error.
+ */
+ return libxl__get_reaper_uid(gc, reaper_uid);
+}
+
+
+/*
+ * Destroy all processes of the given uid by setresuid to the
+ * specified uid and kill(-1). NB this MUST BE CALLED FROM A SEPARATE
+ * PROCESS from the normal libxl process, and should exit immediately
+ * after return. Returns a libxl-style error code that is guaranteed
+ * to be >= -125.
+ */
+static int kill_device_model_uid_child(libxl__destroy_devicemodel_state *ddms,
+ const char *dm_kill_uid_str) {
+ STATE_AO_GC(ddms->ao);
+ int domid = ddms->domid;
+ int r, rc = 0;
+ uid_t dm_kill_uid = atoi(dm_kill_uid_str);
+ uid_t reaper_uid;
+
+ /*
+ * Try to kill the devicemodel by uid. The safest way to do this
+ * is to set euid == dm_uid, but the ruid to something else. If
+ * we can't get a separate ruid, carry on trying to kill the
+ * process anyway using dm_uid for the ruid. This is racy (the dm
+ * may be able to kill(-1) us before we kill them), but worth
+ * trying.
+ *
+ * NB: Even if we don't have a separate reaper_uid, the parent can
+ * know whether we won the race by looking at the status variable;
+ * so we don't strictly need to return failure in this case. But
+ * if there's a misconfiguration, it's better to alert the
+ * administator sooner rather than later; so if we fail to get a
+ * reaper uid, report an error even if the kill succeeds.
+ */
+ rc = get_reaper_lock_and_uid(ddms, &reaper_uid);
+ if (rc) {
+ reaper_uid = dm_kill_uid;
+ LOGD(WARN, domid, "Couldn't get separate reaper uid;"
+ "carrying on with unsafe kill");
+ }
+
+ /*
+ * Should never happen; but if it does, better to have the
+ * toolstack crash with an error than nuking dom0.
+ */
+ assert(reaper_uid);
+ assert(dm_kill_uid);
+
+ LOGD(DEBUG, domid, "DM reaper: calling setresuid(%d, %d, 0)",
+ reaper_uid, dm_kill_uid);
+ r = setresuid(reaper_uid, dm_kill_uid, 0);
+ if (r) {
+ LOGED(ERROR, domid, "setresuid to (%d, %d, 0)",
+ reaper_uid, dm_kill_uid);
+ rc = rc ?: ERROR_FAIL;
+ goto out;
+ }
+
+ /*
+ * And kill everyone but me.
+ *
+ * NB that it's not clear from either POSIX or the Linux man page
+ * that ESRCH would be returned with a pid value of -1, but it
+ * doesn't hurt to check.
+ */
+ r = kill(-1, 9);
+ if (r && errno != ESRCH) {
+ LOGED(ERROR, domid, "kill(-1,9)");
+ rc = rc ?: ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static void kill_device_model_uid_cb(libxl__egc *egc,
+ libxl__ev_child *destroyer,
+ pid_t pid, int status)
+{
+ libxl__destroy_devicemodel_state *ddms = CONTAINER_OF(destroyer, *ddms, destroyer);
+ STATE_AO_GC(ddms->ao);
+
+ if (status) {
+ int rc = ERROR_FAIL;
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) <= 125)
+ rc = -WEXITSTATUS(status);
+
+ ACCUMULATE_RC(ddms->rc);
+ libxl_report_child_exitstatus(CTX, XTL_ERROR,
+ "async domain destroy", pid, status);
+ }
+
+ /* Always try to clean up qmp, even if something went wrong */
+ libxl__qmp_cleanup(gc, ddms->domid);
+
+ ddms->callback(egc, ddms, ddms->rc);
+}
+
+/* Return 0 if no dm needed, 1 if needed and <0 if error. */
+int libxl__need_xenpv_qemu(libxl__gc *gc, libxl_domain_config *d_config)
+{
+ int idx, i, ret, num;
+ uint32_t domid;
+ const libxl__device_type *dt;
+
+ ret = libxl__get_domid(gc, &domid);
+ if (ret) {
+ LOG(ERROR, "unable to get domain id");
+ goto out;
+ }
+
+ if (d_config->num_vfbs > 0 || d_config->num_p9s > 0) {
+ ret = 1;
+ goto out;
+ }
+
+ for (idx = 0;; idx++) {
+ dt = device_type_tbl[idx];
+ if (!dt)
+ break;
+
+ num = *libxl__device_type_get_num(dt, d_config);
+ if (!dt->dm_needed || !num)
+ continue;
+
+ for (i = 0; i < num; i++) {
+ if (dt->dm_needed(libxl__device_type_get_elem(dt, d_config, i),
+ domid)) {
+ ret = 1;
+ goto out;
+ }
+ }
+ }
+
+ for (i = 0; i < d_config->num_channels; i++) {
+ if (d_config->channels[i].backend_domid == domid) {
+ /* xenconsoled is limited to the first console only.
+ Until this restriction is removed we must use qemu for
+ secondary consoles which includes all channels. */
+ ret = 1;
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+int libxl__dm_active(libxl__gc *gc, uint32_t domid)
+{
+ char *pid, *dm_domid, *path;
+
+ path = GCSPRINTF("/local/domain/%d/image/device-model-pid", domid);
+ pid = libxl__xs_read(gc, XBT_NULL, path);
+
+ if (pid)
+ return true;
+
+ path = GCSPRINTF("/local/domain/%d/image/device-model-domid", domid);
+ dm_domid = libxl__xs_read(gc, XBT_NULL, path);
+
+ return dm_domid != NULL;
+}
+
+int libxl__dm_check_start(libxl__gc *gc, libxl_domain_config *d_config,
+ uint32_t domid)
+{
+ int rc;
+
+ if (libxl__dm_active(gc, domid))
+ return 0;
+
+ rc = libxl__need_xenpv_qemu(gc, d_config);
+ if (rc < 0)
+ goto out;
+
+ if (!rc)
+ return 0;
+
+ LOGD(ERROR, domid, "device model required but not running");
+ rc = ERROR_FAIL;
+
+out:
+ return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <glob.h>
+
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+
+#include <xenctrl_dom.h>
+#include <xen/hvm/hvm_info_table.h>
+#include <xen/hvm/hvm_xs_strings.h>
+#include <xen/hvm/e820.h>
+
+#include "_paths.h"
+
+//#define DEBUG 1
+
+libxl_domain_type libxl__domain_type(libxl__gc *gc, uint32_t domid)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ xc_domaininfo_t info;
+ int ret;
+
+ ret = xc_domain_getinfolist(ctx->xch, domid, 1, &info);
+ if (ret != 1 || info.domain != domid) {
+ LOG(ERROR, "unable to get domain type for domid=%"PRIu32, domid);
+ return LIBXL_DOMAIN_TYPE_INVALID;
+ }
+ if (info.flags & XEN_DOMINF_hvm_guest) {
+ const char *type_path = GCSPRINTF("%s/type",
+ libxl__xs_libxl_path(gc, domid));
+ const char *type;
+ libxl_domain_type t;
+ int rc;
+
+ rc = libxl__xs_read_mandatory(gc, XBT_NULL, type_path, &type);
+ if (rc) {
+ LOG(WARN,
+ "unable to get domain type for domid=%"PRIu32", assuming HVM",
+ domid);
+ return LIBXL_DOMAIN_TYPE_HVM;
+ }
+
+ rc = libxl_domain_type_from_string(type, &t);
+ if (rc) {
+ LOG(WARN,
+ "unable to get domain type for domid=%"PRIu32", assuming HVM",
+ domid);
+ return LIBXL_DOMAIN_TYPE_HVM;
+ }
+
+ return t;
+ } else
+ return LIBXL_DOMAIN_TYPE_PV;
+}
+
+int libxl__domain_cpupool(libxl__gc *gc, uint32_t domid)
+{
+ xc_domaininfo_t info;
+ int ret;
+
+ ret = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
+ if (ret != 1)
+ {
+ LOGE(ERROR, "getinfolist failed %d", ret);
+ return ERROR_FAIL;
+ }
+ if (info.domain != domid)
+ {
+ LOGE(ERROR, "got info for dom%d, wanted dom%d\n", info.domain, domid);
+ return ERROR_FAIL;
+ }
+ return info.cpupool;
+}
+
+libxl_scheduler libxl__domain_scheduler(libxl__gc *gc, uint32_t domid)
+{
+ int cpupool = libxl__domain_cpupool(gc, domid);
+ libxl_cpupoolinfo poolinfo;
+ libxl_scheduler sched = LIBXL_SCHEDULER_UNKNOWN;
+ int rc;
+
+ if (cpupool < 0)
+ return sched;
+
+ libxl_cpupoolinfo_init(&poolinfo);
+ rc = libxl_cpupool_info(CTX, &poolinfo, cpupool);
+ if (rc < 0)
+ goto out;
+
+ sched = poolinfo.sched;
+
+out:
+ libxl_cpupoolinfo_dispose(&poolinfo);
+ return sched;
+}
+
+/*
+ * Two NUMA placement candidates are compared by means of the following
+ * heuristics:
+
+ * - the number of vcpus runnable on the candidates is considered, and
+ * candidates with fewer of them are preferred. If two candidate have
+ * the same number of runnable vcpus,
+ * - the amount of free memory in the candidates is considered, and the
+ * candidate with greater amount of it is preferred.
+ *
+ * In fact, leaving larger memory holes, maximizes the probability of being
+ * able to put other domains on the node. That hopefully means many domains
+ * will benefit from local memory accesses, but also introduces the risk of
+ * overloading large (from a memory POV) nodes. That's right the effect
+ * that counting the vcpus able to run on the nodes tries to prevent.
+ *
+ * Note that this completely ignore the number of nodes each candidate span,
+ * as the fact that fewer nodes is better is already accounted for in the
+ * algorithm.
+ */
+static int numa_cmpf(const libxl__numa_candidate *c1,
+ const libxl__numa_candidate *c2)
+{
+ if (c1->nr_vcpus != c2->nr_vcpus)
+ return c1->nr_vcpus - c2->nr_vcpus;
+
+ return c2->free_memkb - c1->free_memkb;
+}
+
+/* The actual automatic NUMA placement routine */
+static int numa_place_domain(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config *d_config)
+{
+ libxl_domain_build_info *info = &d_config->b_info;
+ int found;
+ libxl__numa_candidate candidate;
+ libxl_bitmap cpumap, cpupool_nodemap, *map;
+ libxl_cpupoolinfo cpupool_info;
+ int i, cpupool, rc = 0;
+ uint64_t memkb;
+
+ libxl__numa_candidate_init(&candidate);
+ libxl_bitmap_init(&cpumap);
+ libxl_bitmap_init(&cpupool_nodemap);
+ libxl_cpupoolinfo_init(&cpupool_info);
+
+ /*
+ * Extract the cpumap from the cpupool the domain belong to. In fact,
+ * it only makes sense to consider the cpus/nodes that are in there
+ * for placement.
+ */
+ rc = cpupool = libxl__domain_cpupool(gc, domid);
+ if (rc < 0)
+ goto out;
+ rc = libxl_cpupool_info(CTX, &cpupool_info, cpupool);
+ if (rc)
+ goto out;
+ map = &cpupool_info.cpumap;
+
+ /*
+ * If there's a well defined hard affinity mask (i.e., the same one for all
+ * the vcpus), we can try to run the placement considering only the pcpus
+ * within such mask.
+ */
+ if (info->num_vcpu_hard_affinity)
+ {
+#ifdef DEBUG
+ int j;
+
+ for (j = 0; j < info->num_vcpu_hard_affinity; j++)
+ assert(libxl_bitmap_equal(&info->vcpu_hard_affinity[0],
+ &info->vcpu_hard_affinity[j], 0));
+#endif /* DEBUG */
+
+ rc = libxl_bitmap_and(CTX, &cpumap, &info->vcpu_hard_affinity[0],
+ &cpupool_info.cpumap);
+ if (rc)
+ goto out;
+
+ /* Hard affinity must contain at least one cpu of our cpupool */
+ if (libxl_bitmap_is_empty(&cpumap)) {
+ LOG(ERROR, "Hard affinity completely outside of domain's cpupool!");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ }
+
+ rc = libxl__domain_need_memory_calculate(gc, info, &memkb);
+ if (rc)
+ goto out;
+ if (libxl_node_bitmap_alloc(CTX, &cpupool_nodemap, 0)) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /* Find the best candidate with enough free memory and at least
+ * as much pcpus as the domain has vcpus. */
+ rc = libxl__get_numa_candidate(gc, memkb, info->max_vcpus,
+ 0, 0, map, numa_cmpf, &candidate, &found);
+ if (rc)
+ goto out;
+
+ /* Not even a suitable placement candidate! Let's just don't touch the
+ * domain's info->cpumap. It will have affinity with all nodes/cpus. */
+ if (found == 0)
+ goto out;
+
+ /* Map the candidate's node map to the domain's info->nodemap */
+ libxl__numa_candidate_get_nodemap(gc, &candidate, &info->nodemap);
+
+ /* Avoid trying to set the affinity to nodes that might be in the
+ * candidate's nodemap but out of our cpupool. */
+ rc = libxl_cpumap_to_nodemap(CTX, &cpupool_info.cpumap,
+ &cpupool_nodemap);
+ if (rc)
+ goto out;
+
+ libxl_for_each_set_bit(i, info->nodemap) {
+ if (!libxl_bitmap_test(&cpupool_nodemap, i))
+ libxl_bitmap_reset(&info->nodemap, i);
+ }
+
+ LOG(DETAIL, "NUMA placement candidate with %d nodes, %d cpus and "
+ "%"PRIu64" KB free selected", candidate.nr_nodes,
+ candidate.nr_cpus, candidate.free_memkb / 1024);
+
+ out:
+ libxl__numa_candidate_dispose(&candidate);
+ libxl_bitmap_dispose(&cpupool_nodemap);
+ libxl_bitmap_dispose(&cpumap);
+ libxl_cpupoolinfo_dispose(&cpupool_info);
+ return rc;
+}
+
+int libxl__build_pre(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config *d_config, libxl__domain_build_state *state)
+{
+ libxl_domain_build_info *const info = &d_config->b_info;
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char *xs_domid, *con_domid;
+ int rc;
+ uint64_t size;
+
+ if (xc_domain_max_vcpus(ctx->xch, domid, info->max_vcpus) != 0) {
+ LOG(ERROR, "Couldn't set max vcpu count");
+ return ERROR_FAIL;
+ }
+
+ /*
+ * Check if the domain has any CPU or node affinity already. If not, try
+ * to build up the latter via automatic NUMA placement. In fact, in case
+ * numa_place_domain() manage to find a placement, in info->nodemap is
+ * updated accordingly; if it does not manage, info->nodemap is just left
+ * alone. It is then the the subsequent call to
+ * libxl_domain_set_nodeaffinity() that enacts the actual placement.
+ *
+ * As far as scheduling is concerned, we achieve NUMA-aware scheduling
+ * by having the results of placement affect the soft affinity of all
+ * the vcpus of the domain. Of course, we want that iff placement is
+ * enabled and actually happens, so we only change info->cpumap_soft to
+ * reflect the placement result if that is the case
+ */
+ if (libxl_defbool_val(info->numa_placement)) {
+ if (info->cpumap.size || info->num_vcpu_soft_affinity)
+ LOG(WARN, "Can't run NUMA placement, as a soft "
+ "affinity has been specified explicitly");
+ else if (info->nodemap.size)
+ LOG(WARN, "Can't run NUMA placement, as the domain has "
+ "NUMA node affinity set already");
+ else {
+ libxl_bitmap cpumap_soft;
+
+ rc = libxl_node_bitmap_alloc(ctx, &info->nodemap, 0);
+ if (rc)
+ return rc;
+ libxl_bitmap_set_any(&info->nodemap);
+
+ rc = libxl_cpu_bitmap_alloc(ctx, &cpumap_soft, 0);
+ if (rc)
+ return rc;
+
+ rc = numa_place_domain(gc, domid, d_config);
+ if (rc) {
+ libxl_bitmap_dispose(&cpumap_soft);
+ return rc;
+ }
+
+ /*
+ * All we need to do now is converting the result of automatic
+ * placement from nodemap to cpumap, and then use such cpumap
+ * as the soft affinity for all the vcpus of the domain.
+ *
+ * When calling libxl_set_vcpuaffinity_all(), it is ok to use
+ * NULL as hard affinity, as we know we don't have one, or we
+ * won't be here.
+ */
+ libxl_nodemap_to_cpumap(ctx, &info->nodemap, &cpumap_soft);
+ libxl_set_vcpuaffinity_all(ctx, domid, info->max_vcpus,
+ NULL, &cpumap_soft);
+
+ libxl_bitmap_dispose(&cpumap_soft);
+
+ /*
+ * Placement has run, so avoid for it to be re-run, if this
+ * same config we are using and building here is ever re-used.
+ * This means that people re-using configs will get the same
+ * results, consistently, across every re-use, which is what
+ * we expect most people to want.
+ */
+ libxl_defbool_set(&info->numa_placement, false);
+ }
+ }
+
+ if (info->nodemap.size)
+ libxl_domain_set_nodeaffinity(ctx, domid, &info->nodemap);
+
+ if (info->num_vcpu_hard_affinity || info->num_vcpu_soft_affinity) {
+ libxl_bitmap *hard_affinity, *soft_affinity;
+ int i, n_vcpus;
+
+ n_vcpus = info->num_vcpu_hard_affinity > info->num_vcpu_soft_affinity ?
+ info->num_vcpu_hard_affinity : info->num_vcpu_soft_affinity;
+
+ for (i = 0; i < n_vcpus; i++) {
+ /*
+ * Prepare hard and soft affinity pointers in a way that allows
+ * us to issue only one call to libxl_set_vcpuaffinity(), setting,
+ * for each vcpu, both hard and soft affinity "atomically".
+ */
+ hard_affinity = NULL;
+ if (info->num_vcpu_hard_affinity &&
+ i < info->num_vcpu_hard_affinity)
+ hard_affinity = &info->vcpu_hard_affinity[i];
+
+ soft_affinity = NULL;
+ if (info->num_vcpu_soft_affinity &&
+ i < info->num_vcpu_soft_affinity)
+ soft_affinity = &info->vcpu_soft_affinity[i];
+
+ if (libxl_set_vcpuaffinity(ctx, domid, i,
+ hard_affinity, soft_affinity)) {
+ LOG(ERROR, "setting affinity failed on vcpu `%d'", i);
+ return ERROR_FAIL;
+ }
+ }
+ }
+
+
+ rc = libxl__arch_extra_memory(gc, info, &size);
+ if (rc < 0) {
+ LOGE(ERROR, "Couldn't get arch extra constant memory size");
+ return ERROR_FAIL;
+ }
+
+ if (xc_domain_setmaxmem(ctx->xch, domid, info->target_memkb + size) < 0) {
+ LOGE(ERROR, "Couldn't set max memory");
+ return ERROR_FAIL;
+ }
+
+ xs_domid = xs_read(ctx->xsh, XBT_NULL, "/tool/xenstored/domid", NULL);
+ state->store_domid = xs_domid ? atoi(xs_domid) : 0;
+ free(xs_domid);
+
+ con_domid = xs_read(ctx->xsh, XBT_NULL, "/tool/xenconsoled/domid", NULL);
+ state->console_domid = con_domid ? atoi(con_domid) : 0;
+ free(con_domid);
+
+ state->store_port = xc_evtchn_alloc_unbound(ctx->xch, domid, state->store_domid);
+ state->console_port = xc_evtchn_alloc_unbound(ctx->xch, domid, state->console_domid);
+
+ rc = libxl__arch_domain_create(gc, d_config, domid);
+
+ /* Construct a CPUID policy, but only for brand new domains. Domains
+ * being migrated-in/restored have CPUID handled during the
+ * static_data_done() callback. */
+ if (!state->restore)
+ libxl__cpuid_legacy(ctx, domid, false, info);
+
+ return rc;
+}
+
+static int set_vnuma_affinity(libxl__gc *gc, uint32_t domid,
+ libxl_domain_build_info *info)
+{
+ libxl_bitmap cpumap;
+ libxl_vnode_info *v;
+ unsigned int i, j;
+ int rc = 0;
+
+ libxl_bitmap_init(&cpumap);
+
+ rc = libxl_cpu_bitmap_alloc(CTX, &cpumap, 0);
+ if (rc) {
+ LOG(ERROR, "Can't allocate nodemap");
+ goto out;
+ }
+
+ /*
+ * For each vcpu in each vnode, set its soft affinity to
+ * the pcpus belonging to the pnode the vnode is on
+ */
+ for (i = 0; i < info->num_vnuma_nodes; i++) {
+ v = &info->vnuma_nodes[i];
+
+ rc = libxl_node_to_cpumap(CTX, v->pnode, &cpumap);
+ if (rc) {
+ LOG(ERROR, "Can't get cpumap for vnode %d", i);
+ goto out;
+ }
+
+ libxl_for_each_set_bit(j, v->vcpus) {
+ rc = libxl_set_vcpuaffinity(CTX, domid, j, NULL, &cpumap);
+ if (rc) {
+ LOG(ERROR, "Can't set cpu affinity for %d", j);
+ goto out;
+ }
+ }
+ }
+
+out:
+ libxl_bitmap_dispose(&cpumap);
+ return rc;
+}
+
+int libxl__build_post(libxl__gc *gc, uint32_t domid,
+ libxl_domain_build_info *info,
+ libxl__domain_build_state *state,
+ char **vms_ents, char **local_ents)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char *dom_path, *vm_path;
+ xs_transaction_t t;
+ char **ents;
+ int i, rc;
+
+ if (info->num_vnuma_nodes && !info->num_vcpu_soft_affinity) {
+ rc = set_vnuma_affinity(gc, domid, info);
+ if (rc)
+ return rc;
+ }
+
+ rc = libxl_domain_sched_params_set(CTX, domid, &info->sched_params);
+ if (rc)
+ return rc;
+
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM
+ && !libxl_ms_vm_genid_is_zero(&info->u.hvm.ms_vm_genid)) {
+ rc = libxl__ms_vm_genid_set(gc, domid,
+ &info->u.hvm.ms_vm_genid);
+ if (rc) {
+ LOG(ERROR, "Failed to set VM Generation ID");
+ return rc;
+ }
+ }
+
+ ents = libxl__calloc(gc, 12 + (info->max_vcpus * 2) + 2, sizeof(char *));
+ ents[0] = "memory/static-max";
+ ents[1] = GCSPRINTF("%"PRId64, info->max_memkb);
+ ents[2] = "memory/target";
+ ents[3] = GCSPRINTF("%"PRId64, info->target_memkb -
+ libxl__get_targetmem_fudge(gc, info));
+ ents[4] = "memory/videoram";
+ ents[5] = GCSPRINTF("%"PRId64, info->video_memkb);
+ ents[6] = "domid";
+ ents[7] = GCSPRINTF("%d", domid);
+ ents[8] = "store/port";
+ ents[9] = GCSPRINTF("%"PRIu32, state->store_port);
+ ents[10] = "store/ring-ref";
+ ents[11] = GCSPRINTF("%lu", state->store_mfn);
+ for (i = 0; i < info->max_vcpus; i++) {
+ ents[12+(i*2)] = GCSPRINTF("cpu/%d/availability", i);
+ ents[12+(i*2)+1] = libxl_bitmap_test(&info->avail_vcpus, i)
+ ? "online" : "offline";
+ }
+
+ dom_path = libxl__xs_get_dompath(gc, domid);
+ if (!dom_path) {
+ return ERROR_FAIL;
+ }
+
+ vm_path = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/vm", dom_path), NULL);
+retry_transaction:
+ t = xs_transaction_start(ctx->xsh);
+
+ libxl__xs_writev(gc, t, dom_path, ents);
+ libxl__xs_writev(gc, t, dom_path, local_ents);
+ libxl__xs_writev(gc, t, vm_path, vms_ents);
+
+ if (!xs_transaction_end(ctx->xsh, t, 0))
+ if (errno == EAGAIN)
+ goto retry_transaction;
+ xs_introduce_domain(ctx->xsh, domid, state->store_mfn, state->store_port);
+ free(vm_path);
+ return 0;
+}
+
+static int set_vnuma_info(libxl__gc *gc, uint32_t domid,
+ const libxl_domain_build_info *info,
+ const libxl__domain_build_state *state)
+{
+ int rc = 0;
+ unsigned int i, nr_vdistance;
+ unsigned int *vcpu_to_vnode, *vnode_to_pnode, *vdistance = NULL;
+
+ vcpu_to_vnode = libxl__calloc(gc, info->max_vcpus,
+ sizeof(unsigned int));
+ vnode_to_pnode = libxl__calloc(gc, info->num_vnuma_nodes,
+ sizeof(unsigned int));
+
+ nr_vdistance = info->num_vnuma_nodes * info->num_vnuma_nodes;
+ vdistance = libxl__calloc(gc, nr_vdistance, sizeof(unsigned int));
+
+ for (i = 0; i < info->num_vnuma_nodes; i++) {
+ libxl_vnode_info *v = &info->vnuma_nodes[i];
+ int j;
+
+ /* vnode to pnode mapping */
+ vnode_to_pnode[i] = v->pnode;
+
+ /* vcpu to vnode mapping */
+ libxl_for_each_set_bit(j, v->vcpus)
+ vcpu_to_vnode[j] = i;
+
+ /* node distances */
+ assert(info->num_vnuma_nodes == v->num_distances);
+ memcpy(vdistance + (i * info->num_vnuma_nodes),
+ v->distances,
+ v->num_distances * sizeof(unsigned int));
+ }
+
+ if (xc_domain_setvnuma(CTX->xch, domid, info->num_vnuma_nodes,
+ state->num_vmemranges, info->max_vcpus,
+ state->vmemranges, vdistance,
+ vcpu_to_vnode, vnode_to_pnode) < 0) {
+ LOGE(ERROR, "xc_domain_setvnuma failed");
+ rc = ERROR_FAIL;
+ }
+
+ return rc;
+}
+
+static int libxl__build_dom(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config *d_config, libxl__domain_build_state *state,
+ struct xc_dom_image *dom)
+{
+ libxl_domain_build_info *const info = &d_config->b_info;
+ uint64_t mem_kb;
+ int ret;
+
+ if ( (ret = xc_dom_boot_xen_init(dom, CTX->xch, domid)) != 0 ) {
+ LOGE(ERROR, "xc_dom_boot_xen_init failed");
+ goto out;
+ }
+#ifdef GUEST_RAM_BASE
+ if ( (ret = xc_dom_rambase_init(dom, GUEST_RAM_BASE)) != 0 ) {
+ LOGE(ERROR, "xc_dom_rambase failed");
+ goto out;
+ }
+#endif
+ if ( (ret = xc_dom_parse_image(dom)) != 0 ) {
+ LOG(ERROR, "xc_dom_parse_image failed");
+ goto out;
+ }
+ if ( (ret = libxl__arch_domain_init_hw_description(gc, info, state, dom)) != 0 ) {
+ LOGE(ERROR, "libxl__arch_domain_init_hw_description failed");
+ goto out;
+ }
+
+ mem_kb = dom->container_type == XC_DOM_HVM_CONTAINER ?
+ (info->max_memkb - info->video_memkb) : info->target_memkb;
+ if ( (ret = xc_dom_mem_init(dom, mem_kb / 1024)) != 0 ) {
+ LOGE(ERROR, "xc_dom_mem_init failed");
+ goto out;
+ }
+ if ( (ret = xc_dom_boot_mem_init(dom)) != 0 ) {
+ LOGE(ERROR, "xc_dom_boot_mem_init failed");
+ goto out;
+ }
+ if ( (ret = libxl__arch_domain_finalise_hw_description(gc, domid, d_config, dom)) != 0 ) {
+ LOGE(ERROR, "libxl__arch_domain_finalise_hw_description failed");
+ goto out;
+ }
+ if ( (ret = xc_dom_build_image(dom)) != 0 ) {
+ LOGE(ERROR, "xc_dom_build_image failed");
+ goto out;
+ }
+ if ( (ret = xc_dom_boot_image(dom)) != 0 ) {
+ LOGE(ERROR, "xc_dom_boot_image failed");
+ goto out;
+ }
+ if ( (ret = xc_dom_gnttab_init(dom)) != 0 ) {
+ LOGE(ERROR, "xc_dom_gnttab_init failed");
+ goto out;
+ }
+ if ((ret = libxl__arch_build_dom_finish(gc, info, dom, state)) != 0) {
+ LOGE(ERROR, "libxl__arch_build_dom_finish failed");
+ goto out;
+ }
+
+out:
+ return ret != 0 ? ERROR_FAIL : 0;
+}
+
+int libxl__build_pv(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config *d_config, libxl__domain_build_state *state)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ libxl_domain_build_info *const info = &d_config->b_info;
+ struct xc_dom_image *dom;
+ int ret;
+ int flags = 0;
+
+ xc_dom_loginit(ctx->xch);
+
+ dom = xc_dom_allocate(ctx->xch, state->pv_cmdline, info->u.pv.features);
+ if (!dom) {
+ LOGE(ERROR, "xc_dom_allocate failed");
+ return ERROR_FAIL;
+ }
+
+ dom->container_type = XC_DOM_PV_CONTAINER;
+
+ LOG(DEBUG, "pv kernel mapped %d path %s", state->pv_kernel.mapped, state->pv_kernel.path);
+
+ if (state->pv_kernel.mapped) {
+ ret = xc_dom_kernel_mem(dom,
+ state->pv_kernel.data,
+ state->pv_kernel.size);
+ if ( ret != 0) {
+ LOGE(ERROR, "xc_dom_kernel_mem failed");
+ goto out;
+ }
+ } else {
+ ret = xc_dom_kernel_file(dom, state->pv_kernel.path);
+ if ( ret != 0) {
+ LOGE(ERROR, "xc_dom_kernel_file failed");
+ goto out;
+ }
+ }
+
+ if ( state->pv_ramdisk.path && strlen(state->pv_ramdisk.path) ) {
+ if (state->pv_ramdisk.mapped) {
+ if ( (ret = xc_dom_module_mem(dom, state->pv_ramdisk.data, state->pv_ramdisk.size, NULL)) != 0 ) {
+ LOGE(ERROR, "xc_dom_ramdisk_mem failed");
+ goto out;
+ }
+ } else {
+ if ( (ret = xc_dom_module_file(dom, state->pv_ramdisk.path, NULL)) != 0 ) {
+ LOGE(ERROR, "xc_dom_ramdisk_file failed");
+ goto out;
+ }
+ }
+ }
+
+ dom->flags = flags;
+ dom->console_evtchn = state->console_port;
+ dom->console_domid = state->console_domid;
+ dom->xenstore_evtchn = state->store_port;
+ dom->xenstore_domid = state->store_domid;
+ dom->claim_enabled = libxl_defbool_val(info->claim_mode);
+ dom->max_vcpus = info->max_vcpus;
+
+ if (info->num_vnuma_nodes != 0) {
+ unsigned int i;
+
+ ret = libxl__vnuma_build_vmemrange_pv(gc, domid, info, state);
+ if (ret) {
+ LOGE(ERROR, "cannot build vmemranges");
+ goto out;
+ }
+ ret = libxl__vnuma_config_check(gc, info, state);
+ if (ret) goto out;
+
+ ret = set_vnuma_info(gc, domid, info, state);
+ if (ret) goto out;
+
+ dom->nr_vmemranges = state->num_vmemranges;
+ dom->vmemranges = xc_dom_malloc(dom, sizeof(*dom->vmemranges) *
+ dom->nr_vmemranges);
+
+ for (i = 0; i < dom->nr_vmemranges; i++) {
+ dom->vmemranges[i].start = state->vmemranges[i].start;
+ dom->vmemranges[i].end = state->vmemranges[i].end;
+ dom->vmemranges[i].flags = state->vmemranges[i].flags;
+ dom->vmemranges[i].nid = state->vmemranges[i].nid;
+ }
+
+ dom->nr_vnodes = info->num_vnuma_nodes;
+ dom->vnode_to_pnode = xc_dom_malloc(dom, sizeof(*dom->vnode_to_pnode) *
+ dom->nr_vnodes);
+ for (i = 0; i < info->num_vnuma_nodes; i++)
+ dom->vnode_to_pnode[i] = info->vnuma_nodes[i].pnode;
+ }
+
+ ret = libxl__build_dom(gc, domid, d_config, state, dom);
+ if (ret != 0)
+ goto out;
+
+ if (xc_dom_translated(dom)) {
+ state->console_mfn = dom->console_pfn;
+ state->store_mfn = dom->xenstore_pfn;
+ state->vuart_gfn = dom->vuart_gfn;
+ } else {
+ state->console_mfn = xc_dom_p2m(dom, dom->console_pfn);
+ state->store_mfn = xc_dom_p2m(dom, dom->xenstore_pfn);
+ }
+
+ ret = 0;
+out:
+ xc_dom_release(dom);
+ return ret == 0 ? 0 : ERROR_FAIL;
+}
+
+static int hvm_build_set_params(xc_interface *handle, uint32_t domid,
+ libxl_domain_build_info *info,
+ int store_evtchn, unsigned long *store_mfn,
+ int console_evtchn, unsigned long *console_mfn,
+ domid_t store_domid, domid_t console_domid)
+{
+ struct hvm_info_table *va_hvm;
+ uint8_t *va_map, sum;
+ uint64_t str_mfn, cons_mfn;
+ int i;
+
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
+ va_map = xc_map_foreign_range(handle, domid,
+ XC_PAGE_SIZE, PROT_READ | PROT_WRITE,
+ HVM_INFO_PFN);
+ if (va_map == NULL)
+ return ERROR_FAIL;
+
+ va_hvm = (struct hvm_info_table *)(va_map + HVM_INFO_OFFSET);
+ va_hvm->apic_mode = libxl_defbool_val(info->apic);
+ va_hvm->nr_vcpus = info->max_vcpus;
+ memset(va_hvm->vcpu_online, 0, sizeof(va_hvm->vcpu_online));
+ memcpy(va_hvm->vcpu_online, info->avail_vcpus.map, info->avail_vcpus.size);
+ for (i = 0, sum = 0; i < va_hvm->length; i++)
+ sum += ((uint8_t *) va_hvm)[i];
+ va_hvm->checksum -= sum;
+ munmap(va_map, XC_PAGE_SIZE);
+ }
+
+ xc_hvm_param_get(handle, domid, HVM_PARAM_STORE_PFN, &str_mfn);
+ xc_hvm_param_get(handle, domid, HVM_PARAM_CONSOLE_PFN, &cons_mfn);
+ xc_hvm_param_set(handle, domid, HVM_PARAM_STORE_EVTCHN, store_evtchn);
+ xc_hvm_param_set(handle, domid, HVM_PARAM_CONSOLE_EVTCHN, console_evtchn);
+
+ *store_mfn = str_mfn;
+ *console_mfn = cons_mfn;
+
+ return 0;
+}
+
+static int hvm_build_set_xs_values(libxl__gc *gc,
+ uint32_t domid,
+ struct xc_dom_image *dom,
+ const libxl_domain_build_info *info)
+{
+ char *path = NULL;
+ int ret = 0;
+
+ if (dom->smbios_module.guest_addr_out) {
+ path = GCSPRINTF("/local/domain/%d/"HVM_XS_SMBIOS_PT_ADDRESS, domid);
+
+ ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%"PRIx64,
+ dom->smbios_module.guest_addr_out);
+ if (ret)
+ goto err;
+
+ path = GCSPRINTF("/local/domain/%d/"HVM_XS_SMBIOS_PT_LENGTH, domid);
+
+ ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%x",
+ dom->smbios_module.length);
+ if (ret)
+ goto err;
+ }
+
+ /* Only one module can be passed. PVHv2 guests do not support this. */
+ if (dom->acpi_modules[0].guest_addr_out &&
+ info->type == LIBXL_DOMAIN_TYPE_HVM) {
+ path = GCSPRINTF("/local/domain/%d/"HVM_XS_ACPI_PT_ADDRESS, domid);
+
+ ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%"PRIx64,
+ dom->acpi_modules[0].guest_addr_out);
+ if (ret)
+ goto err;
+
+ path = GCSPRINTF("/local/domain/%d/"HVM_XS_ACPI_PT_LENGTH, domid);
+
+ ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%x",
+ dom->acpi_modules[0].length);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ LOG(ERROR, "failed to write firmware xenstore value, err: %d", ret);
+ return ret;
+}
+
+static int libxl__load_hvm_firmware_module(libxl__gc *gc,
+ const char *filename,
+ const char *what,
+ struct xc_hvm_firmware_module *m)
+{
+ int datalen = 0;
+ void *data = NULL;
+ int r, rc;
+
+ LOG(DEBUG, "Loading %s: %s", what, filename);
+ r = libxl_read_file_contents(CTX, filename, &data, &datalen);
+ if (r) {
+ /*
+ * Print a message only on ENOENT, other errors are logged by the
+ * function libxl_read_file_contents().
+ */
+ if (r == ENOENT)
+ LOGEV(ERROR, r, "failed to read %s file", what);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ libxl__ptr_add(gc, data);
+ if (datalen) {
+ /* Only accept non-empty files */
+ m->data = data;
+ m->length = datalen;
+ } else {
+ LOG(ERROR, "file %s for %s is empty", filename, what);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ rc = 0;
+out:
+ return rc;
+}
+
+static int libxl__domain_firmware(libxl__gc *gc,
+ libxl_domain_build_info *info,
+ libxl__domain_build_state *state,
+ struct xc_dom_image *dom)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ const char *firmware = NULL;
+ int e, rc;
+ int datalen = 0;
+ void *data;
+ const char *bios_filename = NULL;
+
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
+ if (info->u.hvm.firmware) {
+ firmware = info->u.hvm.firmware;
+ } else {
+ switch (info->device_model_version)
+ {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ firmware = "hvmloader";
+ break;
+ default:
+ LOG(ERROR, "invalid device model version %d",
+ info->device_model_version);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+ }
+
+ if (state->pv_kernel.path != NULL &&
+ info->type == LIBXL_DOMAIN_TYPE_PVH) {
+
+ if (state->shim_path) {
+ rc = xc_dom_kernel_file(dom, state->shim_path);
+ if (rc) {
+ LOGE(ERROR, "xc_dom_kernel_file failed");
+ goto out;
+ }
+
+ /* We've loaded the shim, so load the kernel as a secondary module */
+ if (state->pv_kernel.mapped) {
+ LOG(DEBUG, "xc_dom_module_mem, cmdline %s",
+ state->pv_cmdline);
+ rc = xc_dom_module_mem(dom, state->pv_kernel.data,
+ state->pv_kernel.size, state->pv_cmdline);
+ if (rc) {
+ LOGE(ERROR, "xc_dom_kernel_mem failed");
+ goto out;
+ }
+ } else {
+ LOG(DEBUG, "xc_dom_module_file, path %s cmdline %s",
+ state->pv_kernel.path, state->pv_cmdline);
+ rc = xc_dom_module_file(dom, state->pv_kernel.path, state->pv_cmdline);
+ if (rc) {
+ LOGE(ERROR, "xc_dom_kernel_file failed");
+ goto out;
+ }
+ }
+ } else {
+ /* No shim, so load the kernel directly */
+ if (state->pv_kernel.mapped) {
+ rc = xc_dom_kernel_mem(dom, state->pv_kernel.data,
+ state->pv_kernel.size);
+ if (rc) {
+ LOGE(ERROR, "xc_dom_kernel_mem failed");
+ goto out;
+ }
+ } else {
+ rc = xc_dom_kernel_file(dom, state->pv_kernel.path);
+ if (rc) {
+ LOGE(ERROR, "xc_dom_kernel_file failed");
+ goto out;
+ }
+ }
+ }
+
+ if (state->pv_ramdisk.path && strlen(state->pv_ramdisk.path)) {
+ if (state->pv_ramdisk.mapped) {
+ rc = xc_dom_module_mem(dom, state->pv_ramdisk.data,
+ state->pv_ramdisk.size, NULL);
+ if (rc) {
+ LOGE(ERROR, "xc_dom_ramdisk_mem failed");
+ goto out;
+ }
+ } else {
+ rc = xc_dom_module_file(dom, state->pv_ramdisk.path, NULL);
+ if (rc) {
+ LOGE(ERROR, "xc_dom_ramdisk_file failed");
+ goto out;
+ }
+ }
+ }
+ } else {
+ /*
+ * Only HVM guests should get here, PVH should always have a set
+ * kernel at this point.
+ */
+ assert(info->type == LIBXL_DOMAIN_TYPE_HVM);
+ rc = xc_dom_kernel_file(dom, libxl__abs_path(gc, firmware,
+ libxl__xenfirmwaredir_path()));
+ }
+
+ if (rc != 0) {
+ LOGE(ERROR, "xc_dom_{kernel_file/ramdisk_file} failed");
+ goto out;
+ }
+
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+ info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+ if (info->u.hvm.system_firmware) {
+ bios_filename = info->u.hvm.system_firmware;
+ } else {
+ switch (info->u.hvm.bios) {
+ case LIBXL_BIOS_TYPE_SEABIOS:
+ bios_filename = libxl__seabios_path();
+ break;
+ case LIBXL_BIOS_TYPE_OVMF:
+ bios_filename = libxl__ovmf_path();
+ break;
+ case LIBXL_BIOS_TYPE_ROMBIOS:
+ default:
+ abort();
+ }
+ }
+ }
+
+ if (bios_filename) {
+ rc = libxl__load_hvm_firmware_module(gc, bios_filename, "BIOS",
+ &dom->system_firmware_module);
+ if (rc) goto out;
+ }
+
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+ info->u.hvm.bios == LIBXL_BIOS_TYPE_ROMBIOS &&
+ libxl__ipxe_path()) {
+ const char *fp = libxl__ipxe_path();
+ rc = xc_dom_module_file(dom, fp, "ipxe");
+
+ if (rc) {
+ LOGE(ERROR, "failed to load IPXE %s (%d)", fp, rc);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+ info->u.hvm.smbios_firmware) {
+ data = NULL;
+ e = libxl_read_file_contents(ctx, info->u.hvm.smbios_firmware,
+ &data, &datalen);
+ if (e) {
+ LOGEV(ERROR, e, "failed to read SMBIOS firmware file %s",
+ info->u.hvm.smbios_firmware);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ libxl__ptr_add(gc, data);
+ if (datalen) {
+ /* Only accept non-empty files */
+ dom->smbios_module.data = data;
+ dom->smbios_module.length = (uint32_t)datalen;
+ }
+ }
+
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+ info->u.hvm.acpi_firmware) {
+ data = NULL;
+ e = libxl_read_file_contents(ctx, info->u.hvm.acpi_firmware,
+ &data, &datalen);
+ if (e) {
+ LOGEV(ERROR, e, "failed to read ACPI firmware file %s",
+ info->u.hvm.acpi_firmware);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ libxl__ptr_add(gc, data);
+ if (datalen) {
+ /* Only accept a non-empty file */
+ dom->acpi_modules[0].data = data;
+ dom->acpi_modules[0].length = (uint32_t)datalen;
+ }
+ }
+
+ return 0;
+out:
+ assert(rc != 0);
+ return rc;
+}
+
+int libxl__build_hvm(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config *d_config,
+ libxl__domain_build_state *state)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ int rc;
+ uint64_t mmio_start, lowmem_end, highmem_end, mem_size;
+ libxl_domain_build_info *const info = &d_config->b_info;
+ struct xc_dom_image *dom = NULL;
+ bool device_model = info->type == LIBXL_DOMAIN_TYPE_HVM ? true : false;
+
+ xc_dom_loginit(ctx->xch);
+
+ /*
+ * If PVH and we have a shim override, use the shim cmdline.
+ * If PVH and no shim override, use the pv cmdline.
+ * If not PVH, use info->cmdline.
+ */
+ dom = xc_dom_allocate(ctx->xch, info->type == LIBXL_DOMAIN_TYPE_PVH ?
+ (state->shim_path ? state->shim_cmdline : state->pv_cmdline) :
+ info->cmdline, NULL);
+ if (!dom) {
+ LOGE(ERROR, "xc_dom_allocate failed");
+ rc = ERROR_NOMEM;
+ goto out;
+ }
+
+ dom->container_type = XC_DOM_HVM_CONTAINER;
+
+ /* The params from the configuration file are in Mb, which are then
+ * multiplied by 1 Kb. This was then divided off when calling
+ * the old xc_hvm_build_target_mem() which then turned them to bytes.
+ * Do all this in one step here...
+ */
+ mem_size = (uint64_t)(info->max_memkb - info->video_memkb) << 10;
+ dom->target_pages = (uint64_t)(info->target_memkb - info->video_memkb) >> 2;
+ dom->claim_enabled = libxl_defbool_val(info->claim_mode);
+ if (info->u.hvm.mmio_hole_memkb) {
+ uint64_t max_ram_below_4g = (1ULL << 32) -
+ (info->u.hvm.mmio_hole_memkb << 10);
+
+ if (max_ram_below_4g < HVM_BELOW_4G_MMIO_START)
+ dom->mmio_size = info->u.hvm.mmio_hole_memkb << 10;
+ }
+
+ rc = libxl__domain_firmware(gc, info, state, dom);
+ if (rc != 0) {
+ LOG(ERROR, "initializing domain firmware failed");
+ goto out;
+ }
+
+ if (dom->target_pages == 0)
+ dom->target_pages = mem_size >> XC_PAGE_SHIFT;
+ if (dom->mmio_size == 0 && device_model)
+ dom->mmio_size = HVM_BELOW_4G_MMIO_LENGTH;
+ else if (dom->mmio_size == 0 && !device_model) {
+#if defined(__i386__) || defined(__x86_64__)
+ /*
+ * Make sure the local APIC page, the ACPI tables and the special pages
+ * are inside the MMIO hole.
+ */
+ xen_paddr_t start =
+ (X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES) <<
+ XC_PAGE_SHIFT;
+
+ start = min_t(xen_paddr_t, start, LAPIC_BASE_ADDRESS);
+ start = min_t(xen_paddr_t, start, ACPI_INFO_PHYSICAL_ADDRESS);
+ dom->mmio_size = GB(4) - start;
+#else
+ assert(1);
+#endif
+ }
+ lowmem_end = mem_size;
+ highmem_end = 0;
+ mmio_start = (1ull << 32) - dom->mmio_size;
+ if (lowmem_end > mmio_start)
+ {
+ highmem_end = (1ull << 32) + (lowmem_end - mmio_start);
+ lowmem_end = mmio_start;
+ }
+ dom->lowmem_end = lowmem_end;
+ dom->highmem_end = highmem_end;
+ dom->mmio_start = mmio_start;
+ dom->vga_hole_size = device_model ? LIBXL_VGA_HOLE_SIZE : 0;
+ dom->device_model = device_model;
+ dom->max_vcpus = info->max_vcpus;
+ dom->console_domid = state->console_domid;
+ dom->xenstore_domid = state->store_domid;
+
+ rc = libxl__domain_device_construct_rdm(gc, d_config,
+ info->u.hvm.rdm_mem_boundary_memkb*1024,
+ dom);
+ if (rc) {
+ LOG(ERROR, "checking reserved device memory failed");
+ goto out;
+ }
+
+ if (info->num_vnuma_nodes != 0) {
+ int i;
+
+ rc = libxl__vnuma_build_vmemrange_hvm(gc, domid, info, state, dom);
+ if (rc != 0) {
+ LOG(ERROR, "hvm build vmemranges failed");
+ goto out;
+ }
+ rc = libxl__vnuma_config_check(gc, info, state);
+ if (rc != 0) goto out;
+ rc = set_vnuma_info(gc, domid, info, state);
+ if (rc != 0) goto out;
+
+ dom->nr_vmemranges = state->num_vmemranges;
+ dom->vmemranges = libxl__malloc(gc, sizeof(*dom->vmemranges) *
+ dom->nr_vmemranges);
+
+ for (i = 0; i < dom->nr_vmemranges; i++) {
+ dom->vmemranges[i].start = state->vmemranges[i].start;
+ dom->vmemranges[i].end = state->vmemranges[i].end;
+ dom->vmemranges[i].flags = state->vmemranges[i].flags;
+ dom->vmemranges[i].nid = state->vmemranges[i].nid;
+ }
+
+ dom->nr_vnodes = info->num_vnuma_nodes;
+ dom->vnode_to_pnode = libxl__malloc(gc, sizeof(*dom->vnode_to_pnode) *
+ dom->nr_vnodes);
+ for (i = 0; i < dom->nr_vnodes; i++)
+ dom->vnode_to_pnode[i] = info->vnuma_nodes[i].pnode;
+ }
+
+ rc = libxl__build_dom(gc, domid, d_config, state, dom);
+ if (rc != 0)
+ goto out;
+
+ rc = hvm_build_set_params(ctx->xch, domid, info, state->store_port,
+ &state->store_mfn, state->console_port,
+ &state->console_mfn, state->store_domid,
+ state->console_domid);
+ if (rc != 0) {
+ LOG(ERROR, "hvm build set params failed");
+ goto out;
+ }
+
+ rc = hvm_build_set_xs_values(gc, domid, dom, info);
+ if (rc != 0) {
+ LOG(ERROR, "hvm build set xenstore values failed");
+ goto out;
+ }
+
+ xc_dom_release(dom);
+ return 0;
+
+out:
+ assert(rc != 0);
+ if (dom != NULL) xc_dom_release(dom);
+ return rc;
+}
+
+int libxl__qemu_traditional_cmd(libxl__gc *gc, uint32_t domid,
+ const char *cmd)
+{
+ char *path = NULL;
+ uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+ path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/command");
+ return libxl__xs_printf(gc, XBT_NULL, path, "%s", cmd);
+}
+
+/*==================== Miscellaneous ====================*/
+
+char *libxl__uuid2string(libxl__gc *gc, const libxl_uuid uuid)
+{
+ return GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(uuid));
+}
+
+const char *libxl__userdata_path(libxl__gc *gc, uint32_t domid,
+ const char *userdata_userid,
+ const char *wh)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char *uuid_string, *path;
+ libxl_dominfo info;
+ int rc;
+
+ libxl_dominfo_init(&info);
+
+ rc = libxl_domain_info(ctx, &info, domid);
+ if (rc) {
+ LOGE(ERROR, "unable to find domain info for domain %"PRIu32, domid);
+ path = NULL;
+ goto out;
+ }
+ uuid_string = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid));
+ path = GCSPRINTF(XEN_LIB_DIR "/userdata-%s.%u.%s.%s",
+ wh, domid, uuid_string, userdata_userid);
+
+ out:
+ libxl_dominfo_dispose(&info);
+ return path;
+}
+
+static int userdata_delete(libxl__gc *gc, const char *path)
+{
+ int r;
+ r = unlink(path);
+ if (r) {
+ LOGE(ERROR, "remove failed for %s", path);
+ return errno;
+ }
+ return 0;
+}
+
+void libxl__userdata_destroyall(libxl__gc *gc, uint32_t domid)
+{
+ const char *pattern;
+ glob_t gl;
+ int r, i;
+
+ pattern = libxl__userdata_path(gc, domid, "*", "?");
+ if (!pattern)
+ goto out;
+
+ gl.gl_pathc = 0;
+ gl.gl_pathv = 0;
+ gl.gl_offs = 0;
+ r = glob(pattern, GLOB_ERR|GLOB_NOSORT|GLOB_MARK, 0, &gl);
+ if (r == GLOB_NOMATCH)
+ goto out;
+ if (r)
+ LOGE(ERROR, "glob failed for %s", pattern);
+
+ /* Note: don't delete domain-userdata-lock, it will be handled by
+ * unlock function.
+ */
+ for (i=0; i<gl.gl_pathc; i++) {
+ if (!strstr(gl.gl_pathv[i], "domain-userdata-lock"))
+ userdata_delete(gc, gl.gl_pathv[i]);
+ }
+ globfree(&gl);
+out:
+ return;
+}
+
+int libxl__userdata_store(libxl__gc *gc, uint32_t domid,
+ const char *userdata_userid,
+ const uint8_t *data, int datalen)
+{
+ const char *filename;
+ const char *newfilename;
+ int e, rc;
+ int fd = -1;
+
+ filename = libxl__userdata_path(gc, domid, userdata_userid, "d");
+ if (!filename) {
+ rc = ERROR_NOMEM;
+ goto out;
+ }
+
+ if (!datalen) {
+ rc = userdata_delete(gc, filename);
+ goto out;
+ }
+
+ newfilename = libxl__userdata_path(gc, domid, userdata_userid, "n");
+ if (!newfilename) {
+ rc = ERROR_NOMEM;
+ goto out;
+ }
+
+ rc = ERROR_FAIL;
+
+ fd = open(newfilename, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (fd < 0)
+ goto err;
+
+ if (libxl_write_exactly(CTX, fd, data, datalen, "userdata", newfilename))
+ goto err;
+
+ if (close(fd) < 0) {
+ fd = -1;
+ goto err;
+ }
+ fd = -1;
+
+ if (rename(newfilename, filename))
+ goto err;
+
+ rc = 0;
+
+err:
+ if (fd >= 0) {
+ e = errno;
+ close(fd);
+ errno = e;
+ }
+
+ if (rc)
+ LOGE(ERROR, "cannot write/rename %s for %s", newfilename, filename);
+out:
+ return rc;
+}
+
+int libxl_userdata_store(libxl_ctx *ctx, uint32_t domid,
+ const char *userdata_userid,
+ const uint8_t *data, int datalen)
+{
+ GC_INIT(ctx);
+ int rc;
+ libxl__flock *lock;
+
+ CTX_LOCK;
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__userdata_store(gc, domid, userdata_userid,
+ data, datalen);
+
+ libxl__unlock_file(lock);
+
+out:
+ CTX_UNLOCK;
+ GC_FREE;
+ return rc;
+}
+
+int libxl__userdata_retrieve(libxl__gc *gc, uint32_t domid,
+ const char *userdata_userid,
+ uint8_t **data_r, int *datalen_r)
+{
+ const char *filename;
+ int e, rc;
+ int datalen = 0;
+ void *data = 0;
+
+ filename = libxl__userdata_path(gc, domid, userdata_userid, "d");
+ if (!filename) {
+ rc = ERROR_NOMEM;
+ goto out;
+ }
+
+ e = libxl_read_file_contents(CTX, filename, data_r ? &data : 0, &datalen);
+ if (e && errno != ENOENT) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ if (!e && !datalen) {
+ LOG(ERROR, "userdata file %s is empty", filename);
+ if (data_r) assert(!*data_r);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (data_r) *data_r = data;
+ if (datalen_r) *datalen_r = datalen;
+ rc = 0;
+
+out:
+ return rc;
+}
+
+int libxl_userdata_retrieve(libxl_ctx *ctx, uint32_t domid,
+ const char *userdata_userid,
+ uint8_t **data_r, int *datalen_r)
+{
+ GC_INIT(ctx);
+ int rc;
+ libxl__flock *lock;
+
+ CTX_LOCK;
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__userdata_retrieve(gc, domid, userdata_userid,
+ data_r, datalen_r);
+
+
+ libxl__unlock_file(lock);
+out:
+ CTX_UNLOCK;
+ GC_FREE;
+ return rc;
+}
+
+int libxl_userdata_unlink(libxl_ctx *ctx, uint32_t domid,
+ const char *userdata_userid)
+{
+ GC_INIT(ctx);
+ CTX_LOCK;
+
+ int rc;
+ libxl__flock *lock = NULL;
+ const char *filename;
+
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ filename = libxl__userdata_path(gc, domid, userdata_userid, "d");
+ if (!filename) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ if (unlink(filename)) {
+ LOGE(ERROR, "error deleting userdata file: %s", filename);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+out:
+ if (lock)
+ libxl__unlock_file(lock);
+ CTX_UNLOCK;
+ GC_FREE;
+ return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#include <xen/errno.h>
+
+/*========================= Domain save ============================*/
+
+static void stream_done(libxl__egc *egc,
+ libxl__stream_write_state *sws, int rc);
+static void domain_save_done(libxl__egc *egc,
+ libxl__domain_save_state *dss, int rc);
+
+/*----- complicated callback, called by xc_domain_save -----*/
+
+/*
+ * We implement the other end of protocol for controlling qemu-dm's
+ * logdirty. There is no documentation for this protocol, but our
+ * counterparty's implementation is in
+ * qemu-xen-traditional.git:xenstore.c in the function
+ * xenstore_process_logdirty_event
+ */
+
+static void domain_suspend_switch_qemu_xen_traditional_logdirty
+ (libxl__egc *egc, int domid, unsigned enable,
+ libxl__logdirty_switch *lds);
+static void switch_logdirty_xswatch(libxl__egc *egc, libxl__ev_xswatch*,
+ const char *watch_path, const char *event_path);
+static void domain_suspend_switch_qemu_xen_logdirty
+ (libxl__egc *egc, int domid, unsigned enable,
+ libxl__logdirty_switch *lds);
+static void switch_qemu_xen_logdirty_done(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *,
+ int rc);
+static void switch_logdirty_timeout(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc);
+static void switch_logdirty_done(libxl__egc *egc,
+ libxl__logdirty_switch *lds, int rc);
+
+void libxl__logdirty_init(libxl__logdirty_switch *lds)
+{
+ lds->cmd_path = 0;
+ libxl__ev_xswatch_init(&lds->watch);
+ libxl__ev_time_init(&lds->timeout);
+ libxl__ev_qmp_init(&lds->qmp);
+}
+
+void libxl__domain_common_switch_qemu_logdirty(libxl__egc *egc,
+ int domid, unsigned enable,
+ libxl__logdirty_switch *lds)
+{
+ STATE_AO_GC(lds->ao);
+
+ switch (libxl__device_model_version_running(gc, domid)) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ domain_suspend_switch_qemu_xen_traditional_logdirty(egc, domid, enable,
+ lds);
+ break;
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ domain_suspend_switch_qemu_xen_logdirty(egc, domid, enable, lds);
+ break;
+ default:
+ LOGD(ERROR, domid, "logdirty switch failed"
+ ", no valid device model version found, abandoning suspend");
+ lds->callback(egc, lds, ERROR_FAIL);
+ }
+}
+
+static void domain_suspend_switch_qemu_xen_traditional_logdirty
+ (libxl__egc *egc, int domid, unsigned enable,
+ libxl__logdirty_switch *lds)
+{
+ STATE_AO_GC(lds->ao);
+ int rc;
+ xs_transaction_t t = 0;
+ const char *got;
+
+ if (!lds->cmd_path) {
+ uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+ lds->cmd_path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid,
+ "/logdirty/cmd");
+ lds->ret_path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid,
+ "/logdirty/ret");
+ }
+ lds->cmd = enable ? "enable" : "disable";
+
+ rc = libxl__ev_xswatch_register(gc, &lds->watch,
+ switch_logdirty_xswatch, lds->ret_path);
+ if (rc) goto out;
+
+ rc = libxl__ev_time_register_rel(ao, &lds->timeout,
+ switch_logdirty_timeout, 10*1000);
+ if (rc) goto out;
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+
+ rc = libxl__xs_read_checked(gc, t, lds->cmd_path, &got);
+ if (rc) goto out;
+
+ if (got) {
+ const char *got_ret;
+ rc = libxl__xs_read_checked(gc, t, lds->ret_path, &got_ret);
+ if (rc) goto out;
+
+ if (!got_ret || strcmp(got, got_ret)) {
+ LOGD(ERROR, domid, "controlling logdirty: qemu was already sent"
+ " command `%s' (xenstore path `%s') but result is `%s'",
+ got, lds->cmd_path, got_ret ? got_ret : "<none>");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = libxl__xs_rm_checked(gc, t, lds->cmd_path);
+ if (rc) goto out;
+ }
+
+ rc = libxl__xs_rm_checked(gc, t, lds->ret_path);
+ if (rc) goto out;
+
+ rc = libxl__xs_write_checked(gc, t, lds->cmd_path, lds->cmd);
+ if (rc) goto out;
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc<0) goto out;
+ }
+
+ /* OK, wait for some callback */
+ return;
+
+ out:
+ LOGD(ERROR, domid, "logdirty switch failed (rc=%d), abandoning suspend",rc);
+ libxl__xs_transaction_abort(gc, &t);
+ switch_logdirty_done(egc,lds,rc);
+}
+
+static void switch_logdirty_xswatch(libxl__egc *egc, libxl__ev_xswatch *watch,
+ const char *watch_path, const char *event_path)
+{
+ libxl__logdirty_switch *lds = CONTAINER_OF(watch, *lds, watch);
+ STATE_AO_GC(lds->ao);
+ const char *got;
+ xs_transaction_t t = 0;
+ int rc;
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+
+ rc = libxl__xs_read_checked(gc, t, lds->ret_path, &got);
+ if (rc) goto out;
+
+ if (!got) {
+ rc = +1;
+ goto out;
+ }
+
+ if (strcmp(got, lds->cmd)) {
+ LOG(ERROR,"logdirty switch: sent command `%s' but got reply `%s'"
+ " (xenstore paths `%s' / `%s')", lds->cmd, got,
+ lds->cmd_path, lds->ret_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__xs_rm_checked(gc, t, lds->cmd_path);
+ if (rc) goto out;
+
+ rc = libxl__xs_rm_checked(gc, t, lds->ret_path);
+ if (rc) goto out;
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc<0) goto out;
+ }
+
+ out:
+ /* rc < 0: error
+ * rc == 0: ok, we are done
+ * rc == +1: need to keep waiting
+ */
+ libxl__xs_transaction_abort(gc, &t);
+
+ if (rc <= 0) {
+ if (rc < 0)
+ LOG(ERROR,"logdirty switch: failed (rc=%d)",rc);
+ switch_logdirty_done(egc,lds,rc);
+ }
+}
+
+static void domain_suspend_switch_qemu_xen_logdirty
+ (libxl__egc *egc, int domid, unsigned enable,
+ libxl__logdirty_switch *lds)
+{
+ STATE_AO_GC(lds->ao);
+ int rc;
+ libxl__json_object *args = NULL;
+
+ /* Convenience aliases. */
+ libxl__ev_qmp *const qmp = &lds->qmp;
+
+ rc = libxl__ev_time_register_rel(ao, &lds->timeout,
+ switch_logdirty_timeout, 10 * 1000);
+ if (rc) goto out;
+
+ qmp->ao = ao;
+ qmp->domid = domid;
+ qmp->payload_fd = -1;
+ qmp->callback = switch_qemu_xen_logdirty_done;
+ libxl__qmp_param_add_bool(gc, &args, "enable", enable);
+ rc = libxl__ev_qmp_send(egc, qmp, "xen-set-global-dirty-log", args);
+ if (rc) goto out;
+
+ return;
+out:
+ switch_qemu_xen_logdirty_done(egc, qmp, NULL, rc);
+}
+
+static void switch_qemu_xen_logdirty_done(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *r,
+ int rc)
+{
+ EGC_GC;
+ libxl__logdirty_switch *lds = CONTAINER_OF(qmp, *lds, qmp);
+
+ if (rc)
+ LOGD(ERROR, qmp->domid,
+ "logdirty switch failed (rc=%d), abandoning suspend",rc);
+ switch_logdirty_done(egc, lds, rc);
+}
+
+static void switch_logdirty_timeout(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ libxl__logdirty_switch *lds = CONTAINER_OF(ev, *lds, timeout);
+ STATE_AO_GC(lds->ao);
+ LOG(ERROR,"logdirty switch: wait for device model timed out");
+ switch_logdirty_done(egc,lds,ERROR_FAIL);
+}
+
+static void switch_logdirty_done(libxl__egc *egc,
+ libxl__logdirty_switch *lds,
+ int rc)
+{
+ STATE_AO_GC(lds->ao);
+
+ libxl__ev_xswatch_deregister(gc, &lds->watch);
+ libxl__ev_time_deregister(gc, &lds->timeout);
+ libxl__ev_qmp_dispose(gc, &lds->qmp);
+
+ lds->callback(egc, lds, rc);
+}
+
+static void domain_suspend_switch_qemu_logdirty_done
+ (libxl__egc *egc, libxl__logdirty_switch *lds, int rc);
+
+void libxl__domain_suspend_common_switch_qemu_logdirty
+ (uint32_t domid, unsigned enable, void *user)
+{
+ libxl__save_helper_state *shs = user;
+ libxl__egc *egc = shs->egc;
+ libxl__domain_save_state *dss = shs->caller_state;
+
+ /* Convenience aliases. */
+ libxl__logdirty_switch *const lds = &dss->logdirty;
+
+ if (dss->type == LIBXL_DOMAIN_TYPE_PVH) {
+ domain_suspend_switch_qemu_logdirty_done(egc, lds, 0);
+ return;
+ }
+
+ lds->callback = domain_suspend_switch_qemu_logdirty_done;
+ libxl__domain_common_switch_qemu_logdirty(egc, domid, enable, lds);
+}
+
+static void domain_suspend_switch_qemu_logdirty_done
+ (libxl__egc *egc, libxl__logdirty_switch *lds, int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(lds, *dss, logdirty);
+
+ if (rc) {
+ dss->rc = rc;
+ libxl__xc_domain_saverestore_async_callback_done(egc,
+ &dss->sws.shs, -1);
+ } else
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+/*----- callbacks, called by xc_domain_save -----*/
+
+/*
+ * Expand the buffer 'buf' of length 'len', to append 'str' including its NUL
+ * terminator.
+ */
+static void append_string(libxl__gc *gc, char **buf, uint32_t *len,
+ const char *str)
+{
+ size_t extralen = strlen(str) + 1;
+ char *new = libxl__realloc(gc, *buf, *len + extralen);
+
+ *buf = new;
+ memcpy(new + *len, str, extralen);
+ *len += extralen;
+}
+
+int libxl__save_emulator_xenstore_data(libxl__domain_save_state *dss,
+ char **callee_buf,
+ uint32_t *callee_len)
+{
+ STATE_AO_GC(dss->ao);
+ const char *xs_root;
+ char **entries, *buf = NULL;
+ unsigned int nr_entries, i, j, len = 0;
+ int rc;
+
+ const uint32_t domid = dss->domid;
+ const uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+
+ xs_root = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "");
+
+ entries = libxl__xs_directory(gc, 0, GCSPRINTF("%s/physmap", xs_root),
+ &nr_entries);
+ if (!entries || nr_entries == 0) { rc = 0; goto out; }
+
+ for (i = 0; i < nr_entries; ++i) {
+ static const char *const physmap_subkeys[] = {
+ "start_addr", "size", "name"
+ };
+
+ for (j = 0; j < ARRAY_SIZE(physmap_subkeys); ++j) {
+ const char *key = GCSPRINTF("physmap/%s/%s",
+ entries[i], physmap_subkeys[j]);
+
+ const char *val =
+ libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/%s", xs_root, key));
+
+ if (!val) { rc = ERROR_FAIL; goto out; }
+
+ append_string(gc, &buf, &len, key);
+ append_string(gc, &buf, &len, val);
+ }
+ }
+
+ rc = 0;
+
+ out:
+ if (!rc) {
+ *callee_buf = buf;
+ *callee_len = len;
+ }
+
+ return rc;
+}
+
+/*----- main code for saving, in order of execution -----*/
+
+void libxl__domain_save(libxl__egc *egc, libxl__domain_save_state *dss)
+{
+ STATE_AO_GC(dss->ao);
+ int rc, ret;
+
+ /* Convenience aliases */
+ const uint32_t domid = dss->domid;
+ const libxl_domain_type type = dss->type;
+ const int live = dss->live;
+ const int debug = dss->debug;
+ const libxl_domain_remus_info *const r_info = dss->remus;
+ libxl__srm_save_autogen_callbacks *const callbacks =
+ &dss->sws.shs.callbacks.save.a;
+ unsigned int nr_vnodes = 0, nr_vmemranges = 0, nr_vcpus = 0;
+ libxl__domain_suspend_state *dsps = &dss->dsps;
+
+ if (dss->checkpointed_stream != LIBXL_CHECKPOINTED_STREAM_NONE && !r_info) {
+ LOGD(ERROR, domid, "Migration stream is checkpointed, but there's no "
+ "checkpoint info!");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ dss->rc = 0;
+ libxl__logdirty_init(&dss->logdirty);
+ dss->logdirty.ao = ao;
+
+ dsps->ao = ao;
+ dsps->domid = domid;
+ dsps->live = !!live;
+ rc = libxl__domain_suspend_init(egc, dsps, type);
+ if (rc) goto out;
+
+ dss->xcflags = (live ? XCFLAGS_LIVE : 0)
+ | (debug ? XCFLAGS_DEBUG : 0);
+
+ /* Disallow saving a guest with vNUMA configured because migration
+ * stream does not preserve node information.
+ *
+ * Reject any domain which has vnuma enabled, even if the
+ * configuration is empty. Only domains which have no vnuma
+ * configuration at all are supported.
+ */
+ ret = xc_domain_getvnuma(CTX->xch, domid, &nr_vnodes, &nr_vmemranges,
+ &nr_vcpus, NULL, NULL, NULL);
+ if (ret != -1 || errno != EOPNOTSUPP) {
+ LOGD(ERROR, domid, "Cannot save a guest with vNUMA configured");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (dss->checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_NONE)
+ callbacks->suspend = libxl__domain_suspend_callback;
+
+ callbacks->switch_qemu_logdirty = libxl__domain_suspend_common_switch_qemu_logdirty;
+
+ dss->sws.ao = dss->ao;
+ dss->sws.dss = dss;
+ dss->sws.fd = dss->fd;
+ dss->sws.back_channel = false;
+ dss->sws.completion_callback = stream_done;
+
+ libxl__stream_write_start(egc, &dss->sws);
+ return;
+
+ out:
+ domain_save_done(egc, dss, rc);
+}
+
+static void stream_done(libxl__egc *egc,
+ libxl__stream_write_state *sws, int rc)
+{
+ domain_save_done(egc, sws->dss, rc);
+}
+
+static void domain_save_done(libxl__egc *egc,
+ libxl__domain_save_state *dss, int rc)
+{
+ STATE_AO_GC(dss->ao);
+
+ /* Convenience aliases */
+ const uint32_t domid = dss->domid;
+ libxl__domain_suspend_state *dsps = &dss->dsps;
+
+ libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
+
+ if (dsps->guest_evtchn.port > 0)
+ xc_suspend_evtchn_release(CTX->xch, CTX->xce, domid,
+ dsps->guest_evtchn.port, &dsps->guest_evtchn_lockfd);
+
+ if (dss->remus) {
+ /*
+ * With Remus/COLO, if we reach this point, it means either
+ * backup died or some network error occurred preventing us
+ * from sending checkpoints. Teardown the network buffers and
+ * release netlink resources. This is an async op.
+ */
+ if (libxl_defbool_val(dss->remus->colo))
+ libxl__colo_save_teardown(egc, &dss->css, rc);
+ else
+ libxl__remus_teardown(egc, &dss->rs, rc);
+ return;
+ }
+
+ dss->callback(egc, dss, rc);
+}
+
+/*========================= Domain restore ============================*/
+
+/*
+ * Inspect the buffer between start and end, and return a pointer to the
+ * character following the NUL terminator of start, or NULL if start is not
+ * terminated before end.
+ */
+static const char *next_string(const char *start, const char *end)
+{
+ if (start >= end) return NULL;
+
+ size_t total_len = end - start;
+ size_t len = strnlen(start, total_len);
+
+ if (len == total_len)
+ return NULL;
+ else
+ return start + len + 1;
+}
+
+int libxl__restore_emulator_xenstore_data(libxl__domain_create_state *dcs,
+ const char *ptr, uint32_t size)
+{
+ STATE_AO_GC(dcs->ao);
+ const char *next = ptr, *end = ptr + size, *key, *val;
+ int rc;
+
+ const uint32_t domid = dcs->guest_domid;
+ const uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+ const char *xs_root = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "");
+
+ while (next < end) {
+ key = next;
+ next = next_string(next, end);
+
+ /* Sanitise 'key'. */
+ if (!next) {
+ rc = ERROR_FAIL;
+ LOGD(ERROR, domid, "Key in xenstore data not NUL terminated");
+ goto out;
+ }
+ if (key[0] == '\0') {
+ rc = ERROR_FAIL;
+ LOGD(ERROR, domid, "empty key found in xenstore data");
+ goto out;
+ }
+ if (key[0] == '/') {
+ rc = ERROR_FAIL;
+ LOGD(ERROR, domid, "Key in xenstore data not relative");
+ goto out;
+ }
+
+ val = next;
+ next = next_string(next, end);
+
+ /* Sanitise 'val'. */
+ if (!next) {
+ rc = ERROR_FAIL;
+ LOGD(ERROR, domid, "Val in xenstore data not NUL terminated");
+ goto out;
+ }
+
+ libxl__xs_printf(gc, XBT_NULL,
+ GCSPRINTF("%s/%s", xs_root, key),
+ "%s", val);
+ }
+
+ rc = 0;
+
+ out:
+ return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*====================== Domain suspend =======================*/
+
+int libxl__domain_suspend_init(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps,
+ libxl_domain_type type)
+{
+ STATE_AO_GC(dsps->ao);
+ int rc = ERROR_FAIL;
+ int port;
+
+ /* Convenience aliases */
+ const uint32_t domid = dsps->domid;
+
+ libxl__xswait_init(&dsps->pvcontrol);
+ libxl__ev_evtchn_init(&dsps->guest_evtchn);
+ libxl__ev_xswatch_init(&dsps->guest_watch);
+ libxl__ev_time_init(&dsps->guest_timeout);
+ libxl__ev_qmp_init(&dsps->qmp);
+
+ if (type == LIBXL_DOMAIN_TYPE_INVALID) goto out;
+ dsps->type = type;
+
+ dsps->guest_evtchn.port = -1;
+ dsps->guest_evtchn_lockfd = -1;
+ dsps->guest_responded = 0;
+ dsps->dm_savefile = libxl__device_model_savefile(gc, domid);
+
+ port = xs_suspend_evtchn_port(domid);
+
+ if (port >= 0) {
+ rc = libxl__ctx_evtchn_init(gc);
+ if (rc) goto out;
+
+ dsps->guest_evtchn.port =
+ xc_suspend_evtchn_init_exclusive(CTX->xch, CTX->xce,
+ domid, port, &dsps->guest_evtchn_lockfd);
+
+ if (dsps->guest_evtchn.port < 0) {
+ LOGD(WARN, domid, "Suspend event channel initialization failed");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+/*----- callbacks, called by xc_domain_save -----*/
+
+void libxl__domain_suspend_device_model(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps)
+{
+ STATE_AO_GC(dsps->ao);
+ int rc = 0;
+ uint32_t const domid = dsps->domid;
+ const char *const filename = dsps->dm_savefile;
+
+ switch (libxl__device_model_version_running(gc, domid)) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
+ LOGD(DEBUG, domid, "Saving device model state to %s", filename);
+ libxl__qemu_traditional_cmd(gc, domid, "save");
+ libxl__wait_for_device_model_deprecated(gc, domid, "paused", NULL, NULL, NULL);
+ break;
+ }
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ /* calls dsps->callback_device_model_done when done */
+ libxl__qmp_suspend_save(egc, dsps); /* must be last */
+ return;
+ default:
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+out:
+ if (rc)
+ LOGD(ERROR, dsps->domid,
+ "failed to suspend device model, rc=%d", rc);
+ dsps->callback_device_model_done(egc, dsps, rc); /* must be last */
+}
+
+static void domain_suspend_common_wait_guest(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps);
+static void domain_suspend_common_guest_suspended(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps);
+
+static void domain_suspend_common_pvcontrol_suspending(libxl__egc *egc,
+ libxl__xswait_state *xswa, int rc, const char *state);
+static void domain_suspend_common_wait_guest_evtchn(libxl__egc *egc,
+ libxl__ev_evtchn *evev);
+static void suspend_common_wait_guest_watch(libxl__egc *egc,
+ libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path);
+static void suspend_common_wait_guest_check(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps);
+static void suspend_common_wait_guest_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+
+static void domain_suspend_common_done(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps,
+ int rc);
+
+static void domain_suspend_callback_common(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps);
+static void domain_suspend_callback_common_done(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps, int rc);
+
+/* calls dsps->callback_common_done when done */
+void libxl__domain_suspend(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps)
+{
+ domain_suspend_callback_common(egc, dsps);
+}
+
+static bool domain_suspend_pvcontrol_acked(const char *state) {
+ /* any value other than "suspend", including ENOENT (i.e. !state), is OK */
+ if (!state) return 1;
+ return strcmp(state,"suspend");
+}
+
+/* calls dsps->callback_common_done when done */
+static void domain_suspend_callback_common(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps)
+{
+ STATE_AO_GC(dsps->ao);
+ uint64_t hvm_s_state = 0, hvm_pvdrv = 0;
+ int ret, rc;
+
+ /* Convenience aliases */
+ const uint32_t domid = dsps->domid;
+
+ if (dsps->type != LIBXL_DOMAIN_TYPE_PV) {
+ xc_hvm_param_get(CTX->xch, domid, HVM_PARAM_CALLBACK_IRQ, &hvm_pvdrv);
+ xc_hvm_param_get(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, &hvm_s_state);
+ }
+
+ if ((hvm_s_state == 0) && (dsps->guest_evtchn.port >= 0)) {
+ LOGD(DEBUG, domid, "issuing %s suspend request via event channel",
+ dsps->type != LIBXL_DOMAIN_TYPE_PV ? "PVH/HVM" : "PV");
+ ret = xenevtchn_notify(CTX->xce, dsps->guest_evtchn.port);
+ if (ret < 0) {
+ LOGD(ERROR, domid, "xenevtchn_notify failed ret=%d", ret);
+ rc = ERROR_FAIL;
+ goto err;
+ }
+
+ dsps->guest_evtchn.callback = domain_suspend_common_wait_guest_evtchn;
+ rc = libxl__ev_evtchn_wait(gc, &dsps->guest_evtchn);
+ if (rc) goto err;
+
+ rc = libxl__ev_time_register_rel(ao, &dsps->guest_timeout,
+ suspend_common_wait_guest_timeout,
+ 60*1000);
+ if (rc) goto err;
+
+ return;
+ }
+
+ if (dsps->type == LIBXL_DOMAIN_TYPE_HVM && (!hvm_pvdrv || hvm_s_state)) {
+ LOGD(DEBUG, domid, "Calling xc_domain_shutdown on HVM domain");
+ ret = xc_domain_shutdown(CTX->xch, domid, SHUTDOWN_suspend);
+ if (ret < 0) {
+ LOGED(ERROR, domid, "xc_domain_shutdown failed");
+ rc = ERROR_FAIL;
+ goto err;
+ }
+ /* The guest does not (need to) respond to this sort of request. */
+ dsps->guest_responded = 1;
+ domain_suspend_common_wait_guest(egc, dsps);
+ return;
+ }
+
+ LOGD(DEBUG, domid, "issuing %s suspend request via XenBus control node",
+ dsps->type != LIBXL_DOMAIN_TYPE_PV ? "PVH/HVM" : "PV");
+
+ dsps->pvcontrol.ao = ao;
+ dsps->pvcontrol.callback = domain_suspend_common_pvcontrol_suspending;
+ rc = libxl__domain_pvcontrol(egc, &dsps->pvcontrol, domid, "suspend");
+ if (rc) goto err;
+
+ return;
+
+ err:
+ domain_suspend_common_done(egc, dsps, rc);
+}
+
+static void domain_suspend_common_wait_guest_evtchn(libxl__egc *egc,
+ libxl__ev_evtchn *evev)
+{
+ libxl__domain_suspend_state *dsps = CONTAINER_OF(evev, *dsps, guest_evtchn);
+ STATE_AO_GC(dsps->ao);
+ /* If we should be done waiting, suspend_common_wait_guest_check
+ * will end up calling domain_suspend_common_guest_suspended or
+ * domain_suspend_common_done, both of which cancel the evtchn
+ * wait as needed. So re-enable it now. */
+ libxl__ev_evtchn_wait(gc, &dsps->guest_evtchn);
+ suspend_common_wait_guest_check(egc, dsps);
+}
+
+static void domain_suspend_common_pvcontrol_suspending(libxl__egc *egc,
+ libxl__xswait_state *xswa, int rc, const char *state)
+{
+ libxl__domain_suspend_state *dsps = CONTAINER_OF(xswa, *dsps, pvcontrol);
+ STATE_AO_GC(dsps->ao);
+ xs_transaction_t t = 0;
+
+ if (!rc && !domain_suspend_pvcontrol_acked(state))
+ /* keep waiting */
+ return;
+
+ libxl__xswait_stop(gc, &dsps->pvcontrol);
+
+ if (rc == ERROR_TIMEDOUT) {
+ /*
+ * Guest appears to not be responding. Cancel the suspend
+ * request.
+ *
+ * We re-read the suspend node and clear it within a
+ * transaction in order to handle the case where we race
+ * against the guest catching up and acknowledging the request
+ * at the last minute.
+ */
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto err;
+
+ rc = libxl__xs_read_checked(gc, t, xswa->path, &state);
+ if (rc) goto err;
+
+ if (domain_suspend_pvcontrol_acked(state))
+ /* last minute ack */
+ break;
+
+ rc = libxl__xs_write_checked(gc, t, xswa->path, "");
+ if (rc) goto err;
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) {
+ LOGD(ERROR, dsps->domid,
+ "guest didn't acknowledge suspend, cancelling request");
+ goto err;
+ }
+ if (rc<0) goto err;
+ }
+ } else if (rc) {
+ /* some error in xswait's read of xenstore, already logged */
+ goto err;
+ }
+
+ assert(domain_suspend_pvcontrol_acked(state));
+ LOGD(DEBUG, dsps->domid, "guest acknowledged suspend request");
+
+ libxl__xs_transaction_abort(gc, &t);
+ dsps->guest_responded = 1;
+ domain_suspend_common_wait_guest(egc,dsps);
+ return;
+
+ err:
+ libxl__xs_transaction_abort(gc, &t);
+ domain_suspend_common_done(egc, dsps, rc);
+ return;
+}
+
+static void domain_suspend_common_wait_guest(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps)
+{
+ STATE_AO_GC(dsps->ao);
+ int rc;
+
+ LOGD(DEBUG, dsps->domid, "wait for the guest to suspend");
+
+ rc = libxl__ev_xswatch_register(gc, &dsps->guest_watch,
+ suspend_common_wait_guest_watch,
+ "@releaseDomain");
+ if (rc) goto err;
+
+ rc = libxl__ev_time_register_rel(ao, &dsps->guest_timeout,
+ suspend_common_wait_guest_timeout,
+ 60*1000);
+ if (rc) goto err;
+ return;
+
+ err:
+ domain_suspend_common_done(egc, dsps, rc);
+}
+
+static void suspend_common_wait_guest_watch(libxl__egc *egc,
+ libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path)
+{
+ libxl__domain_suspend_state *dsps = CONTAINER_OF(xsw, *dsps, guest_watch);
+ suspend_common_wait_guest_check(egc, dsps);
+}
+
+static void suspend_common_wait_guest_check(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps)
+{
+ STATE_AO_GC(dsps->ao);
+ xc_domaininfo_t info;
+ int ret;
+ int shutdown_reason;
+
+ /* Convenience aliases */
+ const uint32_t domid = dsps->domid;
+
+ ret = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
+ if (ret < 0) {
+ LOGED(ERROR, domid, "unable to check for status of guest");
+ goto err;
+ }
+
+ if (!(ret == 1 && info.domain == domid)) {
+ LOGED(ERROR, domid, "guest we were suspending has been destroyed");
+ goto err;
+ }
+
+ if (!(info.flags & XEN_DOMINF_shutdown))
+ /* keep waiting */
+ return;
+
+ shutdown_reason = (info.flags >> XEN_DOMINF_shutdownshift)
+ & XEN_DOMINF_shutdownmask;
+ if (shutdown_reason != SHUTDOWN_suspend) {
+ LOGD(DEBUG, domid, "guest we were suspending has shut down"
+ " with unexpected reason code %d", shutdown_reason);
+ goto err;
+ }
+
+ LOGD(DEBUG, domid, "guest has suspended");
+ domain_suspend_common_guest_suspended(egc, dsps);
+ return;
+
+ err:
+ domain_suspend_common_done(egc, dsps, ERROR_FAIL);
+}
+
+static void suspend_common_wait_guest_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
+{
+ libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, guest_timeout);
+ STATE_AO_GC(dsps->ao);
+ if (rc == ERROR_TIMEDOUT) {
+ LOGD(ERROR, dsps->domid, "guest did not suspend, timed out");
+ rc = ERROR_GUEST_TIMEDOUT;
+ }
+ domain_suspend_common_done(egc, dsps, rc);
+}
+
+static void domain_suspend_common_guest_suspended(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps)
+{
+ STATE_AO_GC(dsps->ao);
+
+ libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
+ libxl__ev_xswatch_deregister(gc, &dsps->guest_watch);
+ libxl__ev_time_deregister(gc, &dsps->guest_timeout);
+
+ if (dsps->type == LIBXL_DOMAIN_TYPE_HVM) {
+ dsps->callback_device_model_done = domain_suspend_common_done;
+ libxl__domain_suspend_device_model(egc, dsps); /* must be last */
+ return;
+ }
+ domain_suspend_common_done(egc, dsps, 0);
+}
+
+static void domain_suspend_common_done(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps,
+ int rc)
+{
+ EGC_GC;
+ assert(!libxl__xswait_inuse(&dsps->pvcontrol));
+ libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
+ libxl__ev_xswatch_deregister(gc, &dsps->guest_watch);
+ libxl__ev_time_deregister(gc, &dsps->guest_timeout);
+ libxl__ev_qmp_dispose(gc, &dsps->qmp);
+ dsps->callback_common_done(egc, dsps, rc);
+}
+
+void libxl__domain_suspend_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__egc *egc = shs->egc;
+ libxl__domain_save_state *dss = shs->caller_state;
+ libxl__domain_suspend_state *dsps = &dss->dsps;
+
+ dsps->callback_common_done = domain_suspend_callback_common_done;
+ domain_suspend_callback_common(egc, dsps);
+}
+
+static void domain_suspend_callback_common_done(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps, int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
+ dss->rc = rc;
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+/*======================= Domain resume ========================*/
+
+int libxl__domain_resume_device_model_deprecated(libxl__gc *gc, uint32_t domid)
+{
+ const char *path, *state;
+
+ switch (libxl__device_model_version_running(gc, domid)) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
+ uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+
+ path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+ state = libxl__xs_read(gc, XBT_NULL, path);
+ if (state != NULL && !strcmp(state, "paused")) {
+ libxl__qemu_traditional_cmd(gc, domid, "continue");
+ libxl__wait_for_device_model_deprecated(gc, domid, "running",
+ NULL, NULL, NULL);
+ }
+ break;
+ }
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ if (libxl__qmp_resume(gc, domid))
+ return ERROR_FAIL;
+ break;
+ default:
+ return ERROR_INVAL;
+ }
+
+ return 0;
+}
+
+int libxl__domain_resume_deprecated(libxl__gc *gc, uint32_t domid, int suspend_cancel)
+{
+ int rc = 0;
+
+ libxl_domain_type type = libxl__domain_type(gc, domid);
+ if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (type == LIBXL_DOMAIN_TYPE_HVM) {
+ rc = libxl__domain_resume_device_model_deprecated(gc, domid);
+ if (rc) {
+ LOGD(ERROR, domid, "failed to resume device model:%d", rc);
+ goto out;
+ }
+ }
+
+ if (xc_domain_resume(CTX->xch, domid, suspend_cancel)) {
+ LOGED(ERROR, domid, "xc_domain_resume failed");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (!xs_resume_domain(CTX->xsh, domid)) {
+ LOGED(ERROR, domid, "xs_resume_domain failed");
+ rc = ERROR_FAIL;
+ }
+out:
+ return rc;
+}
+
+static void dm_resume_init(libxl__dm_resume_state *dmrs)
+{
+ libxl__ev_qmp_init(&dmrs->qmp);
+ libxl__ev_time_init(&dmrs->time);
+ libxl__ev_xswatch_init(&dmrs->watch);
+}
+
+static void dm_resume_dispose(libxl__gc *gc,
+ libxl__dm_resume_state *dmrs)
+{
+ libxl__ev_qmp_dispose(gc, &dmrs->qmp);
+ libxl__ev_time_deregister(gc, &dmrs->time);
+ libxl__ev_xswatch_deregister(gc, &dmrs->watch);
+}
+
+static void dm_resume_xswatch_cb(libxl__egc *egc,
+ libxl__ev_xswatch *, const char *watch_path, const char *);
+static void dm_resume_qmp_done(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *, int rc);
+static void dm_resume_timeout(libxl__egc *egc,
+ libxl__ev_time *, const struct timeval *, int rc);
+static void dm_resume_done(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs, int rc);
+
+void libxl__dm_resume(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs)
+{
+ STATE_AO_GC(dmrs->ao);
+ int rc = 0;
+
+ /* Convenience aliases */
+ libxl_domid domid = dmrs->domid;
+ libxl__ev_qmp *qmp = &dmrs->qmp;
+
+ dm_resume_init(dmrs);
+
+ rc = libxl__ev_time_register_rel(dmrs->ao,
+ &dmrs->time,
+ dm_resume_timeout,
+ LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000);
+ if (rc) goto out;
+
+ switch (libxl__device_model_version_running(gc, domid)) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
+ uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+ const char *path, *state;
+
+ path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+ rc = libxl__xs_read_checked(gc, XBT_NULL, path, &state);
+ if (rc) goto out;
+ if (!state || strcmp(state, "paused")) {
+ /* already running */
+ rc = 0;
+ goto out;
+ }
+
+ rc = libxl__qemu_traditional_cmd(gc, domid, "continue");
+ if (rc) goto out;
+ rc = libxl__ev_xswatch_register(gc, &dmrs->watch,
+ dm_resume_xswatch_cb,
+ path);
+ if (rc) goto out;
+ break;
+ }
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ qmp->ao = dmrs->ao;
+ qmp->domid = domid;
+ qmp->callback = dm_resume_qmp_done;
+ qmp->payload_fd = -1;
+ rc = libxl__ev_qmp_send(egc, qmp, "cont", NULL);
+ if (rc) goto out;
+ break;
+ default:
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ return;
+
+out:
+ dm_resume_done(egc, dmrs, rc);
+}
+
+static void dm_resume_xswatch_cb(libxl__egc *egc,
+ libxl__ev_xswatch *xsw,
+ const char *watch_path,
+ const char *event_path)
+{
+ EGC_GC;
+ libxl__dm_resume_state *dmrs = CONTAINER_OF(xsw, *dmrs, watch);
+ int rc;
+ const char *value;
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL, watch_path, &value);
+ if (rc) goto out;
+
+ if (!value || strcmp(value, "running"))
+ return;
+
+ rc = 0;
+out:
+ dm_resume_done(egc, dmrs, rc);
+}
+
+static void dm_resume_qmp_done(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *response,
+ int rc)
+{
+ libxl__dm_resume_state *dmrs = CONTAINER_OF(qmp, *dmrs, qmp);
+ dm_resume_done(egc, dmrs, rc);
+}
+
+static void dm_resume_timeout(libxl__egc *egc,
+ libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ libxl__dm_resume_state *dmrs = CONTAINER_OF(ev, *dmrs, time);
+ dm_resume_done(egc, dmrs, rc);
+}
+
+static void dm_resume_done(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs,
+ int rc)
+{
+ EGC_GC;
+
+ if (rc) {
+ LOGD(ERROR, dmrs->domid,
+ "Failed to resume device model: rc=%d", rc);
+ }
+
+ dm_resume_dispose(gc, dmrs);
+ dmrs->dm_resumed_callback(egc, dmrs, rc);
+}
+
+
+static void domain_resume_done(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs, int rc);
+
+void libxl__domain_resume(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs,
+ bool suspend_cancel)
+{
+ STATE_AO_GC(dmrs->ao);
+ int rc = 0;
+ libxl_domain_type type = libxl__domain_type(gc, dmrs->domid);
+
+ if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (type != LIBXL_DOMAIN_TYPE_HVM) {
+ rc = 0;
+ goto out;
+ }
+
+ dmrs->suspend_cancel = suspend_cancel;
+ dmrs->dm_resumed_callback = domain_resume_done;
+ libxl__dm_resume(egc, dmrs); /* must be last */
+ return;
+
+out:
+ domain_resume_done(egc, dmrs, rc);
+}
+
+static void domain_resume_done(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs, int rc)
+{
+ EGC_GC;
+
+ /* Convenience aliases */
+ libxl_domid domid = dmrs->domid;
+
+ if (rc) goto out;
+
+ if (xc_domain_resume(CTX->xch, domid, dmrs->suspend_cancel)) {
+ LOGED(ERROR, domid, "xc_domain_resume failed");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (!xs_resume_domain(CTX->xsh, domid)) {
+ LOGED(ERROR, domid, "xs_resume_domain failed");
+ rc = ERROR_FAIL;
+ }
+out:
+ dmrs->callback(egc, dmrs, rc);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+#define PAGE_TO_MEMKB(pages) ((pages) * 4)
+
+int libxl__domain_rename(libxl__gc *gc, uint32_t domid,
+ const char *old_name, const char *new_name,
+ xs_transaction_t trans)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char *dom_path = 0;
+ const char *name_path;
+ char *got_old_name;
+ unsigned int got_old_len;
+ xs_transaction_t our_trans = 0;
+ uint32_t stub_dm_domid;
+ const char *stub_dm_old_name = NULL, *stub_dm_new_name = NULL;
+ int rc;
+ libxl_dominfo info;
+ char *uuid;
+ const char *vm_name_path;
+
+ libxl_dominfo_init(&info);
+
+ dom_path = libxl__xs_get_dompath(gc, domid);
+ if (!dom_path) goto x_nomem;
+
+ name_path= GCSPRINTF("%s/name", dom_path);
+ if (!name_path) goto x_nomem;
+
+ stub_dm_domid = libxl_get_stubdom_id(CTX, domid);
+ if (stub_dm_domid) {
+ stub_dm_old_name = libxl__stub_dm_name(gc, old_name);
+ stub_dm_new_name = libxl__stub_dm_name(gc, new_name);
+ }
+
+ retry_transaction:
+ if (!trans) {
+ trans = our_trans = xs_transaction_start(ctx->xsh);
+ if (!our_trans) {
+ LOGEVD(ERROR, errno, domid, "Create xs transaction for domain (re)name");
+ goto x_fail;
+ }
+ }
+
+ if (!new_name) {
+ LOGD(ERROR, domid, "New domain name not specified");
+ rc = ERROR_INVAL;
+ goto x_rc;
+ }
+
+ if (new_name[0]) {
+ /* nonempty names must be unique */
+ uint32_t domid_e;
+ rc = libxl_name_to_domid(ctx, new_name, &domid_e);
+ if (rc == ERROR_INVAL) {
+ /* no such domain, good */
+ } else if (rc != 0) {
+ LOGD(ERROR, domid, "Unexpected error checking for existing domain");
+ goto x_rc;
+ } else if (domid_e == domid) {
+ /* domain already has this name, ok (but we do still
+ * need the rest of the code as we may need to check
+ * old_name, for example). */
+ } else {
+ LOGD(ERROR, domid, "Domain with name \"%s\" already exists.", new_name);
+ rc = ERROR_INVAL;
+ goto x_rc;
+ }
+ }
+
+ if (old_name) {
+ got_old_name = xs_read(ctx->xsh, trans, name_path, &got_old_len);
+ if (!got_old_name) {
+ LOGEVD(ERROR, errno, domid,
+ "Check old name for domain allegedly named `%s'",
+ old_name);
+ goto x_fail;
+ }
+ if (strcmp(old_name, got_old_name)) {
+ LOGD(ERROR, domid,
+ "Allegedly named `%s' is actually named `%s' - racing ?",
+ old_name,
+ got_old_name);
+ free(got_old_name);
+ goto x_fail;
+ }
+ free(got_old_name);
+ }
+ if (!xs_write(ctx->xsh, trans, name_path,
+ new_name, strlen(new_name))) {
+ LOGD(ERROR, domid,
+ "Failed to write new name `%s'"
+ " for domain previously named `%s'",
+ new_name,
+ old_name);
+ goto x_fail;
+ }
+
+ /* update /vm/<uuid>/name */
+ rc = libxl_domain_info(ctx, &info, domid);
+ if (rc)
+ goto x_rc;
+
+ uuid = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid));
+ vm_name_path = GCSPRINTF("/vm/%s/name", uuid);
+ if (libxl__xs_write_checked(gc, trans, vm_name_path, new_name))
+ goto x_fail;
+
+ if (stub_dm_domid) {
+ rc = libxl__domain_rename(gc, stub_dm_domid,
+ stub_dm_old_name,
+ stub_dm_new_name,
+ trans);
+ if (rc) {
+ LOGED(ERROR, domid, "Unable to rename stub-domain");
+ goto x_rc;
+ }
+ }
+
+ if (our_trans) {
+ if (!xs_transaction_end(ctx->xsh, our_trans, 0)) {
+ trans = our_trans = 0;
+ if (errno != EAGAIN) {
+ LOGD(ERROR, domid,
+ "Failed to commit new name `%s'"
+ " for domain previously named `%s'",
+ new_name,
+ old_name);
+ goto x_fail;
+ }
+ LOGD(DEBUG, domid,
+ "Need to retry rename transaction"
+ " for domain (name_path=\"%s\", new_name=\"%s\")",
+ name_path,
+ new_name);
+ goto retry_transaction;
+ }
+ our_trans = 0;
+ }
+
+ rc = 0;
+ x_rc:
+ if (our_trans) xs_transaction_end(ctx->xsh, our_trans, 1);
+ libxl_dominfo_dispose(&info);
+ return rc;
+
+ x_fail: rc = ERROR_FAIL; goto x_rc;
+ x_nomem: rc = ERROR_NOMEM; goto x_rc;
+}
+
+int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid,
+ const char *old_name, const char *new_name)
+{
+ GC_INIT(ctx);
+ int rc;
+ rc = libxl__domain_rename(gc, domid, old_name, new_name, XBT_NULL);
+ GC_FREE;
+ return rc;
+}
+
+static void domain_resume_done(libxl__egc *egc,
+ libxl__dm_resume_state *,
+ int rc);
+
+int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ libxl__dm_resume_state *dmrs;
+
+ GCNEW(dmrs);
+ dmrs->ao = ao;
+ dmrs->domid = domid;
+ dmrs->callback = domain_resume_done;
+ libxl__domain_resume(egc, dmrs, suspend_cancel);
+ return AO_INPROGRESS;
+}
+
+static void domain_resume_done(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs,
+ int rc)
+{
+ STATE_AO_GC(dmrs->ao);
+ libxl__ao_complete(egc, ao, rc);
+}
+
+/*
+ * Preserves a domain but rewrites xenstore etc to make it unique so
+ * that the domain can be restarted.
+ *
+ * Does not modify info so that it may be reused.
+ */
+int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid,
+ libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid)
+{
+ GC_INIT(ctx);
+ struct xs_permissions roperm[2];
+ xs_transaction_t t;
+ char *preserved_name;
+ char *uuid_string;
+ char *vm_path;
+ char *dom_path;
+
+ int rc;
+
+ preserved_name = GCSPRINTF("%s%s", info->name, name_suffix);
+ if (!preserved_name) {
+ GC_FREE;
+ return ERROR_NOMEM;
+ }
+
+ uuid_string = libxl__uuid2string(gc, new_uuid);
+ if (!uuid_string) {
+ GC_FREE;
+ return ERROR_NOMEM;
+ }
+
+ dom_path = libxl__xs_get_dompath(gc, domid);
+ if (!dom_path) {
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+
+ vm_path = GCSPRINTF("/vm/%s", uuid_string);
+ if (!vm_path) {
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+
+ roperm[0].id = 0;
+ roperm[0].perms = XS_PERM_NONE;
+ roperm[1].id = domid;
+ roperm[1].perms = XS_PERM_READ;
+
+ retry_transaction:
+ t = xs_transaction_start(ctx->xsh);
+
+ xs_rm(ctx->xsh, t, vm_path);
+ xs_mkdir(ctx->xsh, t, vm_path);
+ xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm));
+
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
+ rc = libxl__domain_rename(gc, domid, info->name, preserved_name, t);
+ if (rc) {
+ GC_FREE;
+ return rc;
+ }
+
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
+
+ if (!xs_transaction_end(ctx->xsh, t, 0))
+ if (errno == EAGAIN)
+ goto retry_transaction;
+
+ GC_FREE;
+ return 0;
+}
+
+void libxl__xcinfo2xlinfo(libxl_ctx *ctx,
+ const xc_domaininfo_t *xcinfo,
+ libxl_dominfo *xlinfo)
+{
+ size_t size;
+
+ memcpy(&(xlinfo->uuid), xcinfo->handle, sizeof(xen_domain_handle_t));
+ xlinfo->domid = xcinfo->domain;
+ xlinfo->ssidref = xcinfo->ssidref;
+ if (libxl_flask_sid_to_context(ctx, xlinfo->ssidref,
+ &xlinfo->ssid_label, &size) < 0)
+ xlinfo->ssid_label = NULL;
+
+ xlinfo->dying = !!(xcinfo->flags&XEN_DOMINF_dying);
+ xlinfo->shutdown = !!(xcinfo->flags&XEN_DOMINF_shutdown);
+ xlinfo->paused = !!(xcinfo->flags&XEN_DOMINF_paused);
+ xlinfo->blocked = !!(xcinfo->flags&XEN_DOMINF_blocked);
+ xlinfo->running = !!(xcinfo->flags&XEN_DOMINF_running);
+ xlinfo->never_stop = !!(xcinfo->flags&XEN_DOMINF_xs_domain);
+
+ if (xlinfo->shutdown)
+ xlinfo->shutdown_reason = (xcinfo->flags>>XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask;
+ else
+ xlinfo->shutdown_reason = LIBXL_SHUTDOWN_REASON_UNKNOWN;
+
+ xlinfo->outstanding_memkb = PAGE_TO_MEMKB(xcinfo->outstanding_pages);
+ xlinfo->current_memkb = PAGE_TO_MEMKB(xcinfo->tot_pages);
+ xlinfo->shared_memkb = PAGE_TO_MEMKB(xcinfo->shr_pages);
+ xlinfo->paged_memkb = PAGE_TO_MEMKB(xcinfo->paged_pages);
+ xlinfo->max_memkb = PAGE_TO_MEMKB(xcinfo->max_pages);
+ xlinfo->cpu_time = xcinfo->cpu_time;
+ xlinfo->vcpu_max_id = xcinfo->max_vcpu_id;
+ xlinfo->vcpu_online = xcinfo->nr_online_vcpus;
+ xlinfo->cpupool = xcinfo->cpupool;
+ xlinfo->domain_type = (xcinfo->flags & XEN_DOMINF_hvm_guest) ?
+ LIBXL_DOMAIN_TYPE_HVM : LIBXL_DOMAIN_TYPE_PV;
+}
+
+libxl_dominfo * libxl_list_domain(libxl_ctx *ctx, int *nb_domain_out)
+{
+ libxl_dominfo *ptr = NULL;
+ int i, ret;
+ xc_domaininfo_t *info;
+ int size = 0;
+ uint32_t domid = 0;
+ GC_INIT(ctx);
+
+ GCNEW_ARRAY(info, 1024);
+
+ while ((ret = xc_domain_getinfolist(ctx->xch, domid, 1024, info)) > 0) {
+ ptr = libxl__realloc(NOGC, ptr, (size + ret) * sizeof(libxl_dominfo));
+ for (i = 0; i < ret; i++) {
+ libxl__xcinfo2xlinfo(ctx, &info[i], &ptr[size + i]);
+ }
+ domid = info[ret - 1].domain + 1;
+ size += ret;
+ }
+
+ if (ret < 0) {
+ LOGE(ERROR, "getting domain info list");
+ free(ptr);
+ GC_FREE;
+ return NULL;
+ }
+
+ *nb_domain_out = size;
+ GC_FREE;
+ return ptr;
+}
+
+int libxl_domain_info(libxl_ctx *ctx, libxl_dominfo *info_r,
+ uint32_t domid) {
+ xc_domaininfo_t xcinfo;
+ int ret;
+ GC_INIT(ctx);
+
+ ret = xc_domain_getinfolist(ctx->xch, domid, 1, &xcinfo);
+ if (ret<0) {
+ LOGED(ERROR, domid, "Getting domain info list");
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ if (ret==0 || xcinfo.domain != domid) {
+ GC_FREE;
+ return ERROR_DOMAIN_NOTFOUND;
+ }
+
+ if (info_r)
+ libxl__xcinfo2xlinfo(ctx, &xcinfo, info_r);
+ GC_FREE;
+ return 0;
+}
+
+/* this API call only list VM running on this host. A VM can
+ * be an aggregate of multiple domains. */
+libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out)
+{
+ GC_INIT(ctx);
+ libxl_dominfo *info;
+ libxl_vminfo *ptr = NULL;
+ int idx, i, n_doms;
+
+ info = libxl_list_domain(ctx, &n_doms);
+ if (!info)
+ goto out;
+
+ /*
+ * Always make sure to allocate at least one element; if we don't and we
+ * request zero, libxl__calloc (might) think its internal call to calloc
+ * has failed (if it returns null), if so it would kill our process.
+ */
+ ptr = libxl__calloc(NOGC, n_doms ? n_doms : 1, sizeof(libxl_vminfo));
+
+ for (idx = i = 0; i < n_doms; i++) {
+ if (libxl_is_stubdom(ctx, info[i].domid, NULL))
+ continue;
+ ptr[idx].uuid = info[i].uuid;
+ ptr[idx].domid = info[i].domid;
+
+ idx++;
+ }
+ *nb_vm_out = idx;
+ libxl_dominfo_list_free(info, n_doms);
+
+out:
+ GC_FREE;
+ return ptr;
+}
+
+static void remus_failover_cb(libxl__egc *egc,
+ libxl__domain_save_state *dss, int rc);
+
+int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
+ uint32_t domid, int send_fd, int recv_fd,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ libxl__domain_save_state *dss;
+ int rc;
+
+ libxl_domain_type type = libxl__domain_type(gc, domid);
+ if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /* The caller must set this defbool */
+ if (libxl_defbool_is_default(info->colo)) {
+ LOGD(ERROR, domid, "Colo mode must be enabled/disabled");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ libxl_defbool_setdefault(&info->allow_unsafe, false);
+ libxl_defbool_setdefault(&info->blackhole, false);
+ libxl_defbool_setdefault(&info->compression,
+ !libxl_defbool_val(info->colo));
+ libxl_defbool_setdefault(&info->netbuf, true);
+ libxl_defbool_setdefault(&info->diskbuf, true);
+
+ if (libxl_defbool_val(info->colo) &&
+ libxl_defbool_val(info->compression)) {
+ LOGD(ERROR, domid, "Cannot use memory checkpoint "
+ "compression in COLO mode");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (!libxl_defbool_val(info->allow_unsafe) &&
+ (libxl_defbool_val(info->blackhole) ||
+ !libxl_defbool_val(info->netbuf) ||
+ !libxl_defbool_val(info->diskbuf))) {
+ LOGD(ERROR, domid, "Unsafe mode must be enabled to replicate to /dev/null,"
+ "disable network buffering and disk replication");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+
+ GCNEW(dss);
+ dss->ao = ao;
+ dss->callback = remus_failover_cb;
+ dss->domid = domid;
+ dss->fd = send_fd;
+ dss->recv_fd = recv_fd;
+ dss->type = type;
+ dss->live = 1;
+ dss->debug = 0;
+ dss->remus = info;
+ if (libxl_defbool_val(info->colo))
+ dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_COLO;
+ else
+ dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_REMUS;
+
+ assert(info);
+
+ /* Point of no return */
+ if (libxl_defbool_val(info->colo))
+ libxl__colo_save_setup(egc, &dss->css);
+ else
+ libxl__remus_setup(egc, &dss->rs);
+ return AO_INPROGRESS;
+
+ out:
+ return AO_CREATE_FAIL(rc);
+}
+
+static void remus_failover_cb(libxl__egc *egc,
+ libxl__domain_save_state *dss, int rc)
+{
+ STATE_AO_GC(dss->ao);
+ /*
+ * With Remus, if we reach this point, it means either
+ * backup died or some network error occurred preventing us
+ * from sending checkpoints.
+ */
+ libxl__ao_complete(egc, ao, rc);
+}
+
+static void domain_suspend_cb(libxl__egc *egc,
+ libxl__domain_save_state *dss, int rc)
+{
+ STATE_AO_GC(dss->ao);
+ int flrc;
+
+ flrc = libxl__fd_flags_restore(gc, dss->fd, dss->fdfl);
+ /* If suspend has failed already then report that error not this one. */
+ if (flrc && !rc) rc = flrc;
+
+ libxl__ao_complete(egc,ao,rc);
+
+}
+
+int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ int rc;
+
+ libxl_domain_type type = libxl__domain_type(gc, domid);
+ if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+ rc = ERROR_FAIL;
+ goto out_err;
+ }
+
+ libxl__domain_save_state *dss;
+ GCNEW(dss);
+
+ dss->ao = ao;
+ dss->callback = domain_suspend_cb;
+
+ dss->domid = domid;
+ dss->fd = fd;
+ dss->type = type;
+ dss->live = flags & LIBXL_SUSPEND_LIVE;
+ dss->debug = flags & LIBXL_SUSPEND_DEBUG;
+ dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE;
+
+ rc = libxl__fd_flags_modify_save(gc, dss->fd,
+ ~(O_NONBLOCK|O_NDELAY), 0,
+ &dss->fdfl);
+ if (rc < 0) goto out_err;
+
+ libxl__domain_save(egc, dss);
+ return AO_INPROGRESS;
+
+ out_err:
+ return AO_CREATE_FAIL(rc);
+}
+
+static void domain_suspend_empty_cb(libxl__egc *egc,
+ libxl__domain_suspend_state *dss, int rc)
+{
+ STATE_AO_GC(dss->ao);
+ libxl__ao_complete(egc,ao,rc);
+}
+
+int libxl_domain_suspend_only(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ libxl__domain_suspend_state *dsps;
+ int rc;
+
+ libxl_domain_type type = libxl__domain_type(gc, domid);
+ if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+ rc = ERROR_FAIL;
+ goto out_err;
+ }
+
+ GCNEW(dsps);
+ dsps->ao = ao;
+ dsps->domid = domid;
+ dsps->type = type;
+ rc = libxl__domain_suspend_init(egc, dsps, type);
+ if (rc < 0) goto out_err;
+ dsps->callback_common_done = domain_suspend_empty_cb;
+ libxl__domain_suspend(egc, dsps);
+ return AO_INPROGRESS;
+
+ out_err:
+ return AO_CREATE_FAIL(rc);
+}
+
+int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ int r;
+ r = xc_domain_pause(ctx->xch, domid);
+ if (r < 0) {
+ LOGED(ERROR, domid, "Pausing domain");
+ return AO_CREATE_FAIL(ERROR_FAIL);
+ }
+ libxl__ao_complete(egc, ao, 0);
+ return AO_INPROGRESS;
+}
+
+int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
+ const char *filename,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ int ret, rc;
+
+ ret = xc_domain_dumpcore(ctx->xch, domid, filename);
+ if (ret<0) {
+ LOGED(ERROR, domid, "Core dumping domain to %s", filename);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+out:
+
+ libxl__ao_complete(egc, ao, rc);
+
+ return AO_INPROGRESS;
+}
+
+int libxl__domain_unpause_deprecated(libxl__gc *gc, libxl_domid domid)
+{
+ int r, rc;
+
+ libxl_domain_type type = libxl__domain_type(gc, domid);
+ if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (type == LIBXL_DOMAIN_TYPE_HVM) {
+ rc = libxl__domain_resume_device_model_deprecated(gc, domid);
+ if (rc < 0) {
+ LOGD(ERROR, domid,
+ "Failed to unpause device model for domain: %d", rc);
+ goto out;
+ }
+ }
+ r = xc_domain_unpause(CTX->xch, domid);
+ if (r < 0) {
+ LOGED(ERROR, domid, "Unpausing domain");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = 0;
+out:
+ return rc;
+}
+
+static void domain_unpause_done(libxl__egc *egc,
+ libxl__dm_resume_state *,
+ int rc);
+
+void libxl__domain_unpause(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs)
+{
+ STATE_AO_GC(dmrs->ao);
+ int rc = 0;
+
+ /* Convenience aliases */
+ libxl_domid domid = dmrs->domid;
+
+ libxl_domain_type type = libxl__domain_type(gc, domid);
+ if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (type == LIBXL_DOMAIN_TYPE_HVM) {
+ dmrs->dm_resumed_callback = domain_unpause_done;
+ libxl__dm_resume(egc, dmrs); /* must be last */
+ return;
+ }
+ rc = 0;
+out:
+ domain_unpause_done(egc, dmrs, rc);
+}
+
+static void domain_unpause_done(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs,
+ int rc)
+{
+ EGC_GC;
+ int r;
+
+ /* Convenience aliases */
+ libxl_domid domid = dmrs->domid;
+
+ if (rc) goto out;
+
+ r = xc_domain_unpause(CTX->xch, domid);
+ if (r < 0) {
+ LOGED(ERROR, domid, "Unpausing domain");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = 0;
+out:
+ dmrs->callback(egc, dmrs, rc);
+}
+
+static void domain_unpause_ao_done(libxl__egc *egc,
+ libxl__dm_resume_state *,
+ int rc);
+
+int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ libxl__dm_resume_state *dmrs;
+
+ GCNEW(dmrs);
+ dmrs->ao = ao;
+ dmrs->domid = domid;
+ dmrs->callback = domain_unpause_ao_done;
+ libxl__domain_unpause(egc, dmrs); /* must be last */
+ return AO_INPROGRESS;
+}
+
+static void domain_unpause_ao_done(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs,
+ int rc)
+{
+ STATE_AO_GC(dmrs->ao);
+
+ libxl__ao_complete(egc, ao, rc);
+}
+
+int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+
+ uint64_t pvdriver = 0;
+ int ret;
+
+ libxl_domain_type domtype = libxl__domain_type(gc, domid);
+ if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
+ return ERROR_FAIL;
+
+ if (domtype != LIBXL_DOMAIN_TYPE_HVM)
+ return 1;
+
+ ret = xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver);
+ if (ret<0) {
+ LOGED(ERROR, domid, "Getting HVM callback IRQ");
+ return ERROR_FAIL;
+ }
+ return !!pvdriver;
+}
+
+const char *libxl__domain_pvcontrol_xspath(libxl__gc *gc, uint32_t domid)
+{
+ const char *dom_path;
+
+ dom_path = libxl__xs_get_dompath(gc, domid);
+ if (!dom_path)
+ return NULL;
+
+ return GCSPRINTF("%s/control/shutdown", dom_path);
+}
+
+char * libxl__domain_pvcontrol_read(libxl__gc *gc, xs_transaction_t t,
+ uint32_t domid)
+{
+ const char *shutdown_path;
+
+ shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
+ if (!shutdown_path)
+ return NULL;
+
+ return libxl__xs_read(gc, t, shutdown_path);
+}
+
+int libxl__domain_pvcontrol(libxl__egc *egc, libxl__xswait_state *pvcontrol,
+ domid_t domid, const char *cmd)
+{
+ STATE_AO_GC(pvcontrol->ao);
+ const char *shutdown_path;
+ int rc;
+
+ rc = libxl__domain_pvcontrol_available(gc, domid);
+ if (rc < 0)
+ return rc;
+
+ if (!rc)
+ return ERROR_NOPARAVIRT;
+
+ shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
+ if (!shutdown_path)
+ return ERROR_FAIL;
+
+ rc = libxl__xs_printf(gc, XBT_NULL, shutdown_path, "%s", cmd);
+ if (rc)
+ return rc;
+
+ pvcontrol->path = shutdown_path;
+ pvcontrol->what = GCSPRINTF("guest acknowledgement of %s request", cmd);
+ pvcontrol->timeout_ms = 60 * 1000;
+ rc = libxl__xswait_start(gc, pvcontrol);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static bool pvcontrol_acked(const char *state)
+{
+ if (!state || !strcmp(state,""))
+ return true;
+
+ return false;
+}
+
+/* Xenstore watch callback prototype for the reboot/poweroff operations. */
+static void pvcontrol_cb(libxl__egc *egc, libxl__xswait_state *xswa, int rc,
+ const char *state);
+
+int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ libxl__xswait_state *pvcontrol;
+ int rc;
+
+ GCNEW(pvcontrol);
+ pvcontrol->ao = ao;
+ pvcontrol->callback = pvcontrol_cb;
+ rc = libxl__domain_pvcontrol(egc, pvcontrol, domid, "poweroff");
+
+ return rc ? AO_CREATE_FAIL(rc) : AO_INPROGRESS;
+}
+
+int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ libxl__xswait_state *pvcontrol;
+ int rc;
+
+ GCNEW(pvcontrol);
+ pvcontrol->ao = ao;
+ pvcontrol->callback = pvcontrol_cb;
+ rc = libxl__domain_pvcontrol(egc, pvcontrol, domid, "reboot");
+
+ return rc ? AO_CREATE_FAIL(rc) : AO_INPROGRESS;
+}
+
+static void pvcontrol_cb(libxl__egc *egc, libxl__xswait_state *xswa, int rc,
+ const char *state)
+{
+ STATE_AO_GC(xswa->ao);
+
+ if (!rc && !pvcontrol_acked(state))
+ return;
+
+ libxl__xswait_stop(gc, xswa);
+
+ if (rc)
+ LOG(ERROR, "guest didn't acknowledge control request: %d", rc);
+
+ libxl__ao_complete(egc, ao, rc);
+}
+
+static void domain_death_occurred(libxl__egc *egc,
+ libxl_evgen_domain_death **evg_upd,
+ const char *why) {
+ /* Removes **evg_upd from death_list and puts it on death_reported
+ * and advances *evg_upd to the next entry.
+ * Call sites in domain_death_xswatch_callback must use "continue". */
+ EGC_GC;
+ libxl_evgen_domain_death *const evg = *evg_upd;
+
+ LOGD(DEBUG, evg->domid, "%s", why);
+
+ libxl_evgen_domain_death *evg_next = LIBXL_TAILQ_NEXT(evg, entry);
+ *evg_upd = evg_next;
+
+ libxl_event *ev = NEW_EVENT(egc, DOMAIN_DEATH, evg->domid, evg->user);
+
+ libxl__event_occurred(egc, ev);
+
+ evg->death_reported = 1;
+ LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
+ LIBXL_TAILQ_INSERT_HEAD(&CTX->death_reported, evg, entry);
+}
+
+static void domain_death_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
+ const char *wpath, const char *epath) {
+ EGC_GC;
+ libxl_evgen_domain_death *evg;
+ int rc;
+
+ CTX_LOCK;
+
+ evg = LIBXL_TAILQ_FIRST(&CTX->death_list);
+
+ for (;;) {
+ if (!evg) goto out;
+
+ int nentries = LIBXL_TAILQ_NEXT(evg, entry) ? 200 : 1;
+ xc_domaininfo_t domaininfos[nentries];
+ const xc_domaininfo_t *got = domaininfos, *gotend;
+
+ rc = xc_domain_getinfolist(CTX->xch, evg->domid, nentries, domaininfos);
+ if (rc == -1) {
+ LIBXL__EVENT_DISASTER(gc, "xc_domain_getinfolist failed while"
+ " processing @releaseDomain watch event",
+ errno, 0);
+ goto out;
+ }
+ gotend = &domaininfos[rc];
+
+ LOGD(DEBUG, evg->domid, "[evg=%p] nentries=%d rc=%d %ld..%ld",
+ evg, nentries, rc,
+ rc>0 ? (long)domaininfos[0].domain : 0,
+ rc>0 ? (long)domaininfos[rc-1].domain : 0);
+
+ for (;;) {
+ if (!evg) {
+ LOG(DEBUG, "[evg=0] all reported");
+ goto all_reported;
+ }
+
+ LOGD(DEBUG, evg->domid, "[evg=%p]"
+ " got=domaininfos[%d] got->domain=%ld",
+ evg, (int)(got - domaininfos),
+ got < gotend ? (long)got->domain : -1L);
+
+ if (!rc) {
+ domain_death_occurred(egc, &evg, "empty list");
+ continue;
+ }
+
+ if (got == gotend) {
+ LOG(DEBUG, " got==gotend");
+ break;
+ }
+
+ if (got->domain > evg->domid) {
+ /* ie, the list doesn't contain evg->domid any more so
+ * the domain has been destroyed */
+ domain_death_occurred(egc, &evg, "missing from list");
+ continue;
+ }
+
+ if (got->domain < evg->domid) {
+ got++;
+ continue;
+ }
+
+ assert(evg->domid == got->domain);
+ LOGD(DEBUG, evg->domid, "Exists shutdown_reported=%d"" dominf.flags=%x",
+ evg->shutdown_reported, got->flags);
+
+ if (got->flags & XEN_DOMINF_dying) {
+ domain_death_occurred(egc, &evg, "dying");
+ continue;
+ }
+
+ if (!evg->shutdown_reported &&
+ (got->flags & XEN_DOMINF_shutdown)) {
+ libxl_event *ev = NEW_EVENT(egc, DOMAIN_SHUTDOWN,
+ got->domain, evg->user);
+
+ LOG(DEBUG, " shutdown reporting");
+
+ ev->u.domain_shutdown.shutdown_reason =
+ (got->flags >> XEN_DOMINF_shutdownshift) &
+ XEN_DOMINF_shutdownmask;
+ libxl__event_occurred(egc, ev);
+
+ evg->shutdown_reported = 1;
+ }
+ evg = LIBXL_TAILQ_NEXT(evg, entry);
+ }
+
+ assert(rc); /* rc==0 results in us eating all evgs and quitting */
+ }
+ all_reported:
+ out:
+
+ LOG(DEBUG, "domain death search done");
+
+ CTX_UNLOCK;
+}
+
+int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
+ libxl_ev_user user, libxl_evgen_domain_death **evgen_out) {
+ GC_INIT(ctx);
+ libxl_evgen_domain_death *evg, *evg_search;
+ int rc;
+
+ CTX_LOCK;
+
+ evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; }
+ memset(evg, 0, sizeof(*evg));
+ evg->domid = domid;
+ evg->user = user;
+
+ LIBXL_TAILQ_INSERT_SORTED(&ctx->death_list, entry, evg, evg_search, ,
+ evg->domid > evg_search->domid);
+
+ if (!libxl__ev_xswatch_isregistered(&ctx->death_watch)) {
+ rc = libxl__ev_xswatch_register(gc, &ctx->death_watch,
+ domain_death_xswatch_callback, "@releaseDomain");
+ if (rc) { libxl__evdisable_domain_death(gc, evg); goto out; }
+ }
+
+ *evgen_out = evg;
+ rc = 0;
+
+ out:
+ CTX_UNLOCK;
+ GC_FREE;
+ return rc;
+};
+
+void libxl__evdisable_domain_death(libxl__gc *gc,
+ libxl_evgen_domain_death *evg) {
+ CTX_LOCK;
+
+ if (!evg->death_reported)
+ LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
+ else
+ LIBXL_TAILQ_REMOVE(&CTX->death_reported, evg, entry);
+
+ free(evg);
+
+ if (!LIBXL_TAILQ_FIRST(&CTX->death_list) &&
+ libxl__ev_xswatch_isregistered(&CTX->death_watch))
+ libxl__ev_xswatch_deregister(gc, &CTX->death_watch);
+
+ CTX_UNLOCK;
+}
+
+void libxl_evdisable_domain_death(libxl_ctx *ctx,
+ libxl_evgen_domain_death *evg) {
+ GC_INIT(ctx);
+ libxl__evdisable_domain_death(gc, evg);
+ GC_FREE;
+}
+
+/* Callbacks for libxl_domain_destroy */
+
+static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
+ int rc);
+
+int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ libxl__domain_destroy_state *dds;
+
+ GCNEW(dds);
+ dds->ao = ao;
+ dds->domid = domid;
+ dds->callback = domain_destroy_cb;
+ libxl__domain_destroy(egc, dds);
+
+ return AO_INPROGRESS;
+}
+
+static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
+ int rc)
+{
+ STATE_AO_GC(dds->ao);
+
+ if (rc)
+ LOGD(ERROR, dds->domid, "Destruction of domain failed");
+
+ libxl__ao_complete(egc, ao, rc);
+}
+
+/* Callbacks for libxl__domain_destroy */
+
+static void stubdom_destroy_callback(libxl__egc *egc,
+ libxl__destroy_domid_state *dis,
+ int rc);
+
+static void domain_destroy_callback(libxl__egc *egc,
+ libxl__destroy_domid_state *dis,
+ int rc);
+
+static void destroy_finish_check(libxl__egc *egc,
+ libxl__domain_destroy_state *dds);
+
+void libxl__domain_destroy(libxl__egc *egc, libxl__domain_destroy_state *dds)
+{
+ STATE_AO_GC(dds->ao);
+ uint32_t stubdomid = libxl_get_stubdom_id(CTX, dds->domid);
+
+ if (stubdomid) {
+ dds->stubdom.ao = ao;
+ dds->stubdom.domid = stubdomid;
+ dds->stubdom.callback = stubdom_destroy_callback;
+ dds->stubdom.soft_reset = false;
+ libxl__destroy_domid(egc, &dds->stubdom);
+ } else {
+ dds->stubdom_finished = 1;
+ }
+
+ dds->domain.ao = ao;
+ dds->domain.domid = dds->domid;
+ dds->domain.callback = domain_destroy_callback;
+ dds->domain.soft_reset = dds->soft_reset;
+ libxl__destroy_domid(egc, &dds->domain);
+}
+
+static void stubdom_destroy_callback(libxl__egc *egc,
+ libxl__destroy_domid_state *dis,
+ int rc)
+{
+ STATE_AO_GC(dis->ao);
+ libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, stubdom);
+ const char *savefile;
+
+ if (rc) {
+ LOGD(ERROR, dds->domain.domid, "Unable to destroy stubdom with domid %u",
+ dis->domid);
+ dds->rc = rc;
+ }
+
+ dds->stubdom_finished = 1;
+ savefile = libxl__device_model_savefile(gc, dis->domid);
+ rc = libxl__remove_file(gc, savefile);
+ if (rc) {
+ LOGD(ERROR, dds->domain.domid, "Failed to remove device-model savefile %s",
+ savefile);
+ }
+
+ destroy_finish_check(egc, dds);
+}
+
+static void domain_destroy_callback(libxl__egc *egc,
+ libxl__destroy_domid_state *dis,
+ int rc)
+{
+ STATE_AO_GC(dis->ao);
+ libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, domain);
+
+ if (rc) {
+ LOGD(ERROR, dis->domid, "Unable to destroy guest");
+ dds->rc = rc;
+ }
+
+ dds->domain_finished = 1;
+ destroy_finish_check(egc, dds);
+}
+
+static void destroy_finish_check(libxl__egc *egc,
+ libxl__domain_destroy_state *dds)
+{
+ if (!(dds->domain_finished && dds->stubdom_finished))
+ return;
+
+ dds->callback(egc, dds, dds->rc);
+}
+
+/* Callbacks for libxl__destroy_domid */
+static void destroy_domid_pci_done(libxl__egc *egc,
+ libxl__multidev *multidev,
+ int rc);
+static void dm_destroy_cb(libxl__egc *egc,
+ libxl__destroy_devicemodel_state *ddms,
+ int rc);
+
+static void devices_destroy_cb(libxl__egc *egc,
+ libxl__devices_remove_state *drs,
+ int rc);
+
+static void domain_destroy_domid_cb(libxl__egc *egc,
+ libxl__ev_child *destroyer,
+ pid_t pid, int status);
+
+void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis)
+{
+ STATE_AO_GC(dis->ao);
+ uint32_t domid = dis->domid;
+ int rc;
+
+ libxl__ev_child_init(&dis->destroyer);
+
+ rc = libxl_domain_info(CTX, NULL, domid);
+ switch(rc) {
+ case 0:
+ break;
+ case ERROR_DOMAIN_NOTFOUND:
+ LOGD(ERROR, domid, "Non-existant domain");
+ default:
+ goto out;
+ }
+
+ libxl__multidev_begin(ao, &dis->multidev);
+ dis->multidev.callback = destroy_domid_pci_done;
+ libxl__device_pci_destroy_all(egc, domid, &dis->multidev);
+ libxl__multidev_prepared(egc, &dis->multidev, 0);
+ return;
+
+out:
+ assert(rc);
+ dis->callback(egc, dis, rc);
+}
+
+static void destroy_domid_pci_done(libxl__egc *egc,
+ libxl__multidev *multidev,
+ int rc)
+{
+ STATE_AO_GC(multidev->ao);
+ libxl__destroy_domid_state *dis =
+ CONTAINER_OF(multidev, *dis, multidev);
+ int dm_present;
+ int r;
+
+ /* Convenience aliases */
+ libxl_domid domid = dis->domid;
+
+ if (rc) {
+ LOGD(ERROR, domid, "Pci shutdown failed");
+ goto out;
+ }
+
+ r = xc_domain_pause(CTX->xch, domid);
+ if (r < 0) {
+ LOGEVD(ERROR, r, domid, "xc_domain_pause failed");
+ rc = ERROR_FAIL;
+ }
+
+ switch (libxl__domain_type(gc, domid)) {
+ case LIBXL_DOMAIN_TYPE_HVM:
+ if (libxl_get_stubdom_id(CTX, domid)) {
+ dm_present = 0;
+ break;
+ }
+ /* fall through */
+ case LIBXL_DOMAIN_TYPE_PVH:
+ case LIBXL_DOMAIN_TYPE_PV:
+ dm_present = libxl__dm_active(gc, domid);
+ break;
+ case LIBXL_DOMAIN_TYPE_INVALID:
+ rc = ERROR_FAIL;
+ goto out;
+ default:
+ abort();
+ }
+
+ if (dm_present) {
+ dis->ddms.ao = ao;
+ dis->ddms.domid = domid;
+ dis->ddms.callback = dm_destroy_cb;
+
+ libxl__destroy_device_model(egc, &dis->ddms);
+ return;
+ } else {
+ dm_destroy_cb(egc, &dis->ddms, 0);
+ return;
+ }
+
+out:
+ assert(rc);
+ dis->callback(egc, dis, rc);
+ return;
+}
+
+static void dm_destroy_cb(libxl__egc *egc,
+ libxl__destroy_devicemodel_state *ddms,
+ int rc)
+{
+ libxl__destroy_domid_state *dis = CONTAINER_OF(ddms, *dis, ddms);
+ STATE_AO_GC(dis->ao);
+ uint32_t domid = dis->domid;
+ uint32_t target_domid;
+
+ if (rc < 0)
+ LOGD(ERROR, domid, "libxl__destroy_device_model failed");
+
+ if (libxl_is_stubdom(CTX, domid, &target_domid) &&
+ libxl__stubdomain_is_linux_running(gc, target_domid)) {
+ char *path = GCSPRINTF("/local/domain/%d/image/qmp-proxy-pid", domid);
+
+ libxl__kill_xs_path(gc, path, "QMP Proxy");
+ /* qmp-proxy for stubdom registers target_domid's QMP sockets. */
+ libxl__qmp_cleanup(gc, target_domid);
+ }
+
+ dis->drs.ao = ao;
+ dis->drs.domid = domid;
+ dis->drs.callback = devices_destroy_cb;
+ dis->drs.force.flag = LIBXL__FORCE_ON;
+ libxl__devices_destroy(egc, &dis->drs);
+}
+
+static unsigned int libxl__get_domid_reuse_timeout(void)
+{
+ const char *env_timeout = getenv("LIBXL_DOMID_REUSE_TIMEOUT");
+
+ return env_timeout ? strtol(env_timeout, NULL, 0) :
+ LIBXL_DOMID_REUSE_TIMEOUT;
+}
+
+char *libxl__domid_history_path(libxl__gc *gc, const char *suffix)
+{
+ return GCSPRINTF("%s/domid-history%s", libxl__run_dir_path(),
+ suffix ?: "");
+}
+
+int libxl_clear_domid_history(libxl_ctx *ctx)
+{
+ GC_INIT(ctx);
+ char *path;
+ int rc = ERROR_FAIL;
+
+ path = libxl__domid_history_path(gc, NULL);
+ if (!path)
+ goto out;
+
+ if (unlink(path) < 0 && errno != ENOENT) {
+ LOGE(ERROR, "failed to remove '%s'\n", path);
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+struct libxl__domid_history {
+ long timeout;
+ char *path;
+ FILE *f;
+ struct timespec ts;
+};
+
+static void libxl__domid_history_dispose(
+ struct libxl__domid_history *ctxt)
+{
+ if (ctxt->f) {
+ fclose(ctxt->f);
+ ctxt->f = NULL;
+ }
+}
+
+static int libxl__open_domid_history(libxl__gc *gc,
+ struct libxl__domid_history *ctxt)
+{
+ ctxt->timeout = libxl__get_domid_reuse_timeout();
+ ctxt->path = libxl__domid_history_path(gc, NULL);
+
+ ctxt->f = fopen(ctxt->path, "r");
+ if (!ctxt->f && errno != ENOENT) {
+ LOGE(ERROR, "failed to open '%s'", ctxt->path);
+ return ERROR_FAIL;
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ctxt->ts)) {
+ LOGE(ERROR, "failed to get time");
+ libxl__domid_history_dispose(ctxt);
+ return ERROR_FAIL;
+ }
+
+ return 0;
+}
+
+static int libxl__close_domid_history(libxl__gc *gc,
+ struct libxl__domid_history *ctxt)
+{
+ int r;
+
+ if (!ctxt->f) return 0;
+
+ r = fclose(ctxt->f);
+ ctxt->f = NULL;
+ if (r == EOF) {
+ LOGE(ERROR, "failed to close '%s'", ctxt->path);
+ return ERROR_FAIL;
+ }
+
+ return 0;
+}
+
+static int libxl__read_recent(libxl__gc *gc,
+ struct libxl__domid_history *ctxt,
+ unsigned long *sec, unsigned int *domid)
+{
+ if (!ctxt->f) {
+ *domid = INVALID_DOMID;
+ return 0;
+ }
+
+ for (;;) {
+ int r = fscanf(ctxt->f, "%lu %u", sec, domid);
+
+ if (r == EOF) {
+ if (ferror(ctxt->f)) {
+ LOGE(ERROR, "failed to read from '%s'", ctxt->path);
+ return ERROR_FAIL;
+ }
+
+ *domid = INVALID_DOMID;
+ break;
+ } else if (r == 2 && libxl_domid_valid_guest(*domid) &&
+ ctxt->ts.tv_sec - *sec <= ctxt->timeout) {
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int libxl__mark_domid_recent(libxl__gc *gc, uint32_t domid)
+{
+ libxl__flock *lock;
+ struct libxl__domid_history ctxt = {};
+ char *new;
+ FILE *nf = NULL;
+ int r, rc;
+
+ lock = libxl__lock_domid_history(gc);
+ if (!lock) {
+ LOGED(ERROR, domid, "failed to acquire lock");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__open_domid_history(gc, &ctxt);
+ if (rc) goto out;
+
+ new = libxl__domid_history_path(gc, ".new");
+ nf = fopen(new, "a");
+ if (!nf) {
+ LOGED(ERROR, domid, "failed to open '%s'", new);
+ goto out;
+ }
+
+ for (;;) {
+ unsigned long sec;
+ unsigned int val;
+
+ rc = libxl__read_recent(gc, &ctxt, &sec, &val);
+ if (rc) goto out;
+
+ if (val == INVALID_DOMID) /* EOF */
+ break;
+
+ r = fprintf(nf, "%lu %u\n", sec, val);
+ if (r < 0) {
+ LOGED(ERROR, domid, "failed to write to '%s'", new);
+ goto out;
+ }
+ }
+
+ r = fprintf(nf, "%lu %u\n", ctxt.ts.tv_sec, domid);
+ if (r < 0) {
+ LOGED(ERROR, domid, "failed to write to '%s'", new);
+ goto out;
+ }
+
+ r = fclose(nf);
+ nf = NULL;
+ if (r == EOF) {
+ LOGED(ERROR, domid, "failed to close '%s'", new);
+ goto out;
+ }
+
+ rc = libxl__close_domid_history(gc, &ctxt);
+ if (rc) goto out;
+
+ r = rename(new, ctxt.path);
+ if (r) {
+ LOGE(ERROR, "failed to rename '%s' -> '%s'", new, ctxt.path);
+ return ERROR_FAIL;
+ }
+
+out:
+ if (nf) fclose(nf);
+ libxl__domid_history_dispose(&ctxt);
+ if (lock) libxl__unlock_file(lock);
+
+ return rc;
+}
+
+int libxl__is_domid_recent(libxl__gc *gc, uint32_t domid, bool *recent)
+{
+ struct libxl__domid_history ctxt = {};
+ int rc;
+
+ rc = libxl__open_domid_history(gc, &ctxt);
+ if (rc) goto out;
+
+ *recent = false;
+ for (;;) {
+ unsigned long sec;
+ unsigned int val;
+
+ rc = libxl__read_recent(gc, &ctxt, &sec, &val);
+ if (rc) goto out;
+
+ if (val == INVALID_DOMID) /* EOF */
+ break;
+
+ if (val == domid && ctxt.ts.tv_sec - sec <= ctxt.timeout) {
+ *recent = true;
+ break;
+ }
+ }
+
+ rc = libxl__close_domid_history(gc, &ctxt);
+
+out:
+ libxl__domid_history_dispose(&ctxt);
+
+ return rc;
+}
+
+static void devices_destroy_cb(libxl__egc *egc,
+ libxl__devices_remove_state *drs,
+ int rc)
+{
+ STATE_AO_GC(drs->ao);
+ libxl__destroy_domid_state *dis = CONTAINER_OF(drs, *dis, drs);
+ libxl_ctx *ctx = CTX;
+ uint32_t domid = dis->domid;
+ char *dom_path;
+ char *vm_path;
+ libxl__flock *lock;
+
+ dom_path = libxl__xs_get_dompath(gc, domid);
+ if (!dom_path) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (rc < 0)
+ LOGD(ERROR, domid, "libxl__devices_destroy failed");
+
+ vm_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vm", dom_path));
+ if (vm_path)
+ if (!xs_rm(ctx->xsh, XBT_NULL, vm_path))
+ LOGED(ERROR, domid, "xs_rm failed for %s", vm_path);
+
+ if (!xs_rm(ctx->xsh, XBT_NULL, dom_path))
+ LOGED(ERROR, domid, "xs_rm failed for %s", dom_path);
+
+ xs_rm(ctx->xsh, XBT_NULL, libxl__xs_libxl_path(gc, domid));
+ xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF( "/local/domain/%d/hvmloader", domid));
+
+ /* This is async operation, we already hold CTX lock */
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+ libxl__userdata_destroyall(gc, domid);
+
+ libxl__unlock_file(lock);
+
+ /* Clean up qemu-save and qemu-resume files. They are
+ * intermediate files created by libxc. Unfortunately they
+ * don't fit in existing userdata scheme very well. In soft reset
+ * case we need to keep the file.
+ */
+ if (!dis->soft_reset) {
+ rc = libxl__remove_file(gc,
+ libxl__device_model_savefile(gc, domid));
+ if (rc < 0) goto out;
+ }
+ rc = libxl__remove_file(gc,
+ GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", domid));
+ if (rc < 0) goto out;
+
+ rc = libxl__ev_child_fork(gc, &dis->destroyer, domain_destroy_domid_cb);
+ if (rc < 0) goto out;
+ if (!rc) { /* child */
+ ctx->xch = xc_interface_open(ctx->lg,0,0);
+ if (!ctx->xch) goto badchild;
+
+ if (!dis->soft_reset) {
+ rc = libxl__mark_domid_recent(gc, domid);
+ if (rc) goto badchild;
+ rc = xc_domain_destroy(ctx->xch, domid);
+ } else {
+ rc = xc_domain_pause(ctx->xch, domid);
+ if (rc < 0) goto badchild;
+ rc = xc_domain_soft_reset(ctx->xch, domid);
+ if (rc < 0) goto badchild;
+ rc = xc_domain_unpause(ctx->xch, domid);
+ }
+ if (rc < 0) goto badchild;
+ _exit(0);
+
+ badchild:
+ if (errno > 0 && errno < 126) {
+ _exit(errno);
+ } else {
+ LOGED(ERROR, domid,
+ "xc_domain_destroy failed (with difficult errno value %d)",
+ errno);
+ _exit(-1);
+ }
+ }
+ LOGD(DEBUG, domid, "Forked pid %ld for destroy of domain", (long)rc);
+
+ return;
+
+out:
+ dis->callback(egc, dis, rc);
+ return;
+}
+
+static void domain_destroy_domid_cb(libxl__egc *egc,
+ libxl__ev_child *destroyer,
+ pid_t pid, int status)
+{
+ libxl__destroy_domid_state *dis = CONTAINER_OF(destroyer, *dis, destroyer);
+ STATE_AO_GC(dis->ao);
+ int rc;
+
+ if (status) {
+ if (WIFEXITED(status) && WEXITSTATUS(status)<126) {
+ LOGEVD(ERROR, WEXITSTATUS(status), dis->domid,
+ "xc_domain_destroy failed");
+ } else {
+ libxl_report_child_exitstatus(CTX, XTL_ERROR,
+ "async domain destroy", pid, status);
+ }
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = 0;
+
+ out:
+ dis->callback(egc, dis, rc);
+}
+
+int libxl__get_domid(libxl__gc *gc, uint32_t *domid)
+{
+ int rc;
+ const char *xs_domid;
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL, DOMID_XS_PATH, &xs_domid);
+ if (rc) goto out;
+ if (!xs_domid) {
+ LOG(ERROR, "failed to get own domid (%s)", DOMID_XS_PATH);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ *domid = atoi(xs_domid);
+
+out:
+ return rc;
+}
+
+int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid)
+{
+ if (!name)
+ return 0;
+ return libxl_domain_qualifier_to_domid(CTX, name, domid);
+}
+
+libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
+ int *nr_vcpus_out, int *nr_cpus_out)
+{
+ GC_INIT(ctx);
+ libxl_vcpuinfo *ptr, *ret;
+ xc_domaininfo_t domaininfo;
+ xc_vcpuinfo_t vcpuinfo;
+
+ if (xc_domain_getinfolist(ctx->xch, domid, 1, &domaininfo) != 1) {
+ LOGED(ERROR, domid, "Getting infolist");
+ GC_FREE;
+ return NULL;
+ }
+
+ if (domaininfo.max_vcpu_id == XEN_INVALID_MAX_VCPU_ID) {
+ GC_FREE;
+ return NULL;
+ }
+
+ *nr_cpus_out = libxl_get_max_cpus(ctx);
+ ret = ptr = libxl__calloc(NOGC, domaininfo.max_vcpu_id + 1,
+ sizeof(libxl_vcpuinfo));
+
+ for (*nr_vcpus_out = 0;
+ *nr_vcpus_out <= domaininfo.max_vcpu_id;
+ ++*nr_vcpus_out, ++ptr) {
+ libxl_bitmap_init(&ptr->cpumap);
+ if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap, 0))
+ goto err;
+ libxl_bitmap_init(&ptr->cpumap_soft);
+ if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap_soft, 0))
+ goto err;
+ if (xc_vcpu_getinfo(ctx->xch, domid, *nr_vcpus_out, &vcpuinfo) == -1) {
+ LOGED(ERROR, domid, "Getting vcpu info");
+ goto err;
+ }
+
+ if (xc_vcpu_getaffinity(ctx->xch, domid, *nr_vcpus_out,
+ ptr->cpumap.map, ptr->cpumap_soft.map,
+ XEN_VCPUAFFINITY_SOFT|XEN_VCPUAFFINITY_HARD) == -1) {
+ LOGED(ERROR, domid, "Getting vcpu affinity");
+ goto err;
+ }
+ ptr->vcpuid = *nr_vcpus_out;
+ ptr->cpu = vcpuinfo.cpu;
+ ptr->online = !!vcpuinfo.online;
+ ptr->blocked = !!vcpuinfo.blocked;
+ ptr->running = !!vcpuinfo.running;
+ ptr->vcpu_time = vcpuinfo.cpu_time;
+ }
+ GC_FREE;
+ return ret;
+
+err:
+ libxl_bitmap_dispose(&ptr->cpumap);
+ libxl_bitmap_dispose(&ptr->cpumap_soft);
+ free(ret);
+ GC_FREE;
+ return NULL;
+}
+
+static int libxl__set_vcpuonline_xenstore(libxl__gc *gc, uint32_t domid,
+ const libxl_bitmap *cpumap,
+ const libxl_dominfo *info)
+{
+ char *dompath;
+ xs_transaction_t t;
+ int i, rc = ERROR_FAIL;
+
+ if (!(dompath = libxl__xs_get_dompath(gc, domid)))
+ goto out;
+
+retry_transaction:
+ t = xs_transaction_start(CTX->xsh);
+ for (i = 0; i <= info->vcpu_max_id; i++)
+ libxl__xs_printf(gc, t,
+ GCSPRINTF("%s/cpu/%u/availability", dompath, i),
+ "%s", libxl_bitmap_test(cpumap, i) ? "online" : "offline");
+ if (!xs_transaction_end(CTX->xsh, t, 0)) {
+ if (errno == EAGAIN)
+ goto retry_transaction;
+ } else
+ rc = 0;
+out:
+ return rc;
+}
+
+static int qmp_parse_query_cpus(libxl__gc *gc,
+ libxl_domid domid,
+ const libxl__json_object *response,
+ libxl_bitmap *const map)
+{
+ int i;
+ const libxl__json_object *cpu;
+
+ libxl_bitmap_set_none(map);
+ /* Parse response to QMP command "query-cpus":
+ * [ { 'CPU': 'int',...} ]
+ */
+ for (i = 0; (cpu = libxl__json_array_get(response, i)); i++) {
+ unsigned int cpu_index;
+ const libxl__json_object *o;
+
+ o = libxl__json_map_get("CPU", cpu, JSON_INTEGER);
+ if (!o) {
+ LOGD(ERROR, domid, "Failed to retrieve CPU index.");
+ return ERROR_QEMU_API;
+ }
+
+ cpu_index = libxl__json_object_get_integer(o);
+ libxl_bitmap_set(map, cpu_index);
+ }
+
+ return 0;
+}
+
+typedef struct set_vcpuonline_state {
+ libxl__ev_qmp qmp;
+ libxl__ev_time timeout;
+ const libxl_bitmap *cpumap;
+ libxl_dominfo info;
+ libxl_bitmap final_map;
+ int index; /* for loop on final_map */
+} set_vcpuonline_state;
+
+static void set_vcpuonline_qmp_cpus_queried(libxl__egc *,
+ libxl__ev_qmp *, const libxl__json_object *, int rc);
+static void set_vcpuonline_qmp_add_cpu(libxl__egc *,
+ libxl__ev_qmp *, const libxl__json_object *response, int rc);
+static void set_vcpuonline_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void set_vcpuonline_done(libxl__egc *egc,
+ set_vcpuonline_state *svos, int rc);
+
+int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid,
+ libxl_bitmap *cpumap,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ int rc, maxcpus;
+ set_vcpuonline_state *svos;
+
+ GCNEW(svos);
+ libxl__ev_qmp_init(&svos->qmp);
+ svos->qmp.ao = ao;
+ svos->qmp.domid = domid;
+ svos->qmp.payload_fd = -1;
+ libxl__ev_time_init(&svos->timeout);
+ svos->cpumap = cpumap;
+ libxl_dominfo_init(&svos->info);
+ libxl_bitmap_init(&svos->final_map);
+
+ /* Convenience aliases */
+ libxl_dominfo *info = &svos->info;
+ libxl__ev_qmp *qmp = &svos->qmp;
+
+ rc = libxl_domain_info(CTX, info, domid);
+ if (rc < 0) {
+ LOGED(ERROR, domid, "Getting domain info list");
+ goto out;
+ }
+
+ maxcpus = libxl_bitmap_count_set(cpumap);
+ if (maxcpus == 0)
+ {
+ LOGED(ERROR, domid, "Requested 0 VCPUs!");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ if (maxcpus > info->vcpu_max_id + 1)
+ {
+ LOGED(ERROR, domid, "Requested %d VCPUs, however maxcpus is %d!",
+ maxcpus, info->vcpu_max_id + 1);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ switch (libxl__domain_type(gc, domid)) {
+ case LIBXL_DOMAIN_TYPE_HVM:
+ switch (libxl__device_model_version_running(gc, domid)) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ break;
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ rc = libxl__ev_time_register_rel(ao, &svos->timeout,
+ set_vcpuonline_timeout,
+ LIBXL_QMP_CMD_TIMEOUT * 1000);
+ if (rc) goto out;
+ qmp->callback = set_vcpuonline_qmp_cpus_queried;
+ rc = libxl__ev_qmp_send(egc, qmp, "query-cpus", NULL);
+ if (rc) goto out;
+ return AO_INPROGRESS;
+ default:
+ rc = ERROR_INVAL;
+ }
+ break;
+ case LIBXL_DOMAIN_TYPE_PVH:
+ case LIBXL_DOMAIN_TYPE_PV:
+ break;
+ default:
+ rc = ERROR_INVAL;
+ }
+
+out:
+ set_vcpuonline_done(egc, svos, rc); /* must be last */
+ return AO_INPROGRESS;
+}
+
+static void set_vcpuonline_qmp_cpus_queried(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
+{
+ EGC_GC;
+ set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
+ int i;
+ libxl_bitmap current_map;
+
+ /* Convenience aliases */
+ libxl_bitmap *final_map = &svos->final_map;
+
+ libxl_bitmap_init(¤t_map);
+
+ if (rc) goto out;
+
+ libxl_bitmap_alloc(CTX, ¤t_map, svos->info.vcpu_max_id + 1);
+ rc = qmp_parse_query_cpus(gc, qmp->domid, response, ¤t_map);
+ if (rc) goto out;
+
+ libxl_bitmap_copy_alloc(CTX, final_map, svos->cpumap);
+
+ libxl_for_each_set_bit(i, current_map) {
+ libxl_bitmap_reset(final_map, i);
+ }
+
+out:
+ libxl_bitmap_dispose(¤t_map);
+ svos->index = -1;
+ set_vcpuonline_qmp_add_cpu(egc, qmp, NULL, rc); /* must be last */
+}
+
+static void set_vcpuonline_qmp_add_cpu(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
+{
+ STATE_AO_GC(qmp->ao);
+ set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
+ libxl__json_object *args = NULL;
+
+ /* Convenience aliases */
+ libxl_bitmap *map = &svos->final_map;
+
+ if (rc) goto out;
+
+ while (libxl_bitmap_cpu_valid(map, ++svos->index)) {
+ if (libxl_bitmap_test(map, svos->index)) {
+ qmp->callback = set_vcpuonline_qmp_add_cpu;
+ libxl__qmp_param_add_integer(gc, &args, "id", svos->index);
+ rc = libxl__ev_qmp_send(egc, qmp, "cpu-add", args);
+ if (rc) goto out;
+ return;
+ }
+ }
+
+out:
+ set_vcpuonline_done(egc, svos, rc);
+}
+
+static void set_vcpuonline_timeout(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ EGC_GC;
+ set_vcpuonline_state *svos = CONTAINER_OF(ev, *svos, timeout);
+
+ if (rc == ERROR_TIMEDOUT)
+ LOGD(ERROR, svos->qmp.domid,
+ "Setting CPU online in QEMU timed out");
+
+ set_vcpuonline_done(egc, svos, rc);
+}
+
+static void set_vcpuonline_done(libxl__egc *egc,
+ set_vcpuonline_state *svos,
+ int rc)
+{
+ STATE_AO_GC(svos->qmp.ao);
+
+ /* Convenience aliases */
+ libxl_domid domid = svos->qmp.domid;
+
+ if (!rc)
+ rc = libxl__set_vcpuonline_xenstore(gc, domid, svos->cpumap,
+ &svos->info);
+
+ libxl_bitmap_dispose(&svos->final_map);
+ libxl_dominfo_dispose(&svos->info);
+ libxl__ev_time_deregister(gc, &svos->timeout);
+ libxl__ev_qmp_dispose(gc, &svos->qmp);
+ libxl__ao_complete(egc, ao, rc);
+}
+
+static void domain_s3_resume_done(libxl__egc *egc, libxl__ev_qmp *qmp,
+ const libxl__json_object *response,
+ int rc);
+
+static void domain_s3_resume(libxl__ao *ao, libxl__egc *egc, int domid)
+{
+ AO_GC;
+ libxl__ev_qmp *qmp;
+ int rc = 0;
+ int r;
+
+ GCNEW(qmp);
+ libxl__ev_qmp_init(qmp);
+ qmp->ao = ao;
+ qmp->domid = domid;
+ qmp->payload_fd = -1;
+ qmp->callback = domain_s3_resume_done;
+
+ switch (libxl__domain_type(gc, domid)) {
+ case LIBXL_DOMAIN_TYPE_HVM:
+ switch (libxl__device_model_version_running(gc, domid)) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ r = xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, 0);
+ if (r) {
+ LOGED(ERROR, domid, "Send trigger '%s' failed",
+ libxl_trigger_to_string(LIBXL_TRIGGER_S3RESUME));
+ rc = ERROR_FAIL;
+ }
+ break;
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ rc = libxl__ev_qmp_send(egc, qmp, "system_wakeup", NULL);
+ if (rc) goto out;
+ return;
+ default:
+ rc = ERROR_INVAL;
+ break;
+ }
+ break;
+ default:
+ rc = ERROR_INVAL;
+ break;
+ }
+
+out:
+ domain_s3_resume_done(egc, qmp, NULL, rc);
+}
+
+static void domain_s3_resume_done(libxl__egc *egc, libxl__ev_qmp *qmp,
+ const libxl__json_object *response,
+ int rc)
+{
+ EGC_GC;
+
+ if (rc)
+ LOGD(ERROR, qmp->domid, "Send trigger '%s' failed, rc=%d",
+ libxl_trigger_to_string(LIBXL_TRIGGER_S3RESUME), rc);
+
+ libxl__ev_qmp_dispose(gc, qmp);
+ libxl__ao_complete(egc, qmp->ao, rc);
+}
+
+int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid,
+ libxl_trigger trigger, uint32_t vcpuid,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ int rc;
+
+ switch (trigger) {
+ case LIBXL_TRIGGER_POWER:
+ rc = xc_domain_send_trigger(ctx->xch, domid,
+ XEN_DOMCTL_SENDTRIGGER_POWER, vcpuid);
+ break;
+ case LIBXL_TRIGGER_SLEEP:
+ rc = xc_domain_send_trigger(ctx->xch, domid,
+ XEN_DOMCTL_SENDTRIGGER_SLEEP, vcpuid);
+ break;
+ case LIBXL_TRIGGER_NMI:
+ rc = xc_domain_send_trigger(ctx->xch, domid,
+ XEN_DOMCTL_SENDTRIGGER_NMI, vcpuid);
+ break;
+ case LIBXL_TRIGGER_INIT:
+ rc = xc_domain_send_trigger(ctx->xch, domid,
+ XEN_DOMCTL_SENDTRIGGER_INIT, vcpuid);
+ break;
+ case LIBXL_TRIGGER_RESET:
+ rc = xc_domain_send_trigger(ctx->xch, domid,
+ XEN_DOMCTL_SENDTRIGGER_RESET, vcpuid);
+ break;
+ case LIBXL_TRIGGER_S3RESUME:
+ domain_s3_resume(ao, egc, domid); /* must be last */
+ return AO_INPROGRESS;
+ default:
+ rc = -1;
+ errno = EINVAL;
+ break;
+ }
+
+ if (rc != 0) {
+ LOGED(ERROR, domid, "Send trigger '%s' failed",
+ libxl_trigger_to_string(trigger));
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ libxl__ao_complete(egc, ao, rc);
+ return AO_INPROGRESS;
+out:
+ return AO_CREATE_FAIL(rc);
+}
+
+uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid)
+{
+ GC_INIT(ctx);
+ char *dompath = libxl__xs_get_dompath(gc, domid);
+ char *vm_path, *start_time;
+ uint32_t ret;
+
+ vm_path = libxl__xs_read(
+ gc, XBT_NULL, GCSPRINTF("%s/vm", dompath));
+ start_time = libxl__xs_read(
+ gc, XBT_NULL, GCSPRINTF("%s/start_time", vm_path));
+ if (start_time == NULL) {
+ LOGEVD(ERROR, -1, domid, "Can't get start time of domain");
+ ret = -1;
+ }else{
+ ret = strtoul(start_time, NULL, 10);
+ }
+ GC_FREE;
+ return ret;
+}
+
+static int libxl__update_avail_vcpus_xenstore(libxl__gc *gc, uint32_t domid,
+ unsigned int max_vcpus,
+ libxl_bitmap *map)
+{
+ int rc;
+ unsigned int i;
+ const char *dompath;
+
+ dompath = libxl__xs_get_dompath(gc, domid);
+ if (!dompath) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ for (i = 0; i < max_vcpus; i++) {
+ const char *path = GCSPRINTF("%s/cpu/%u/availability", dompath, i);
+ const char *content;
+ rc = libxl__xs_read_checked(gc, XBT_NULL, path, &content);
+ if (rc) goto out;
+ if (content && !strcmp(content, "online"))
+ libxl_bitmap_set(map, i);
+ }
+
+ rc = 0;
+out:
+ return rc;
+}
+
+typedef struct {
+ libxl__ev_qmp qmp;
+ libxl__ev_time timeout;
+ libxl_domain_config *d_config; /* user pointer */
+ libxl__ev_slowlock devlock;
+ libxl_bitmap qemuu_cpus;
+} retrieve_domain_configuration_state;
+
+static void retrieve_domain_configuration_lock_acquired(
+ libxl__egc *egc, libxl__ev_slowlock *, int rc);
+static void retrieve_domain_configuration_cpu_queried(
+ libxl__egc *egc, libxl__ev_qmp *qmp,
+ const libxl__json_object *response, int rc);
+static void retrieve_domain_configuration_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void retrieve_domain_configuration_end(libxl__egc *egc,
+ retrieve_domain_configuration_state *rdcs, int rc);
+
+int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
+ libxl_domain_config *d_config,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ retrieve_domain_configuration_state *rdcs;
+
+ GCNEW(rdcs);
+ libxl__ev_qmp_init(&rdcs->qmp);
+ rdcs->qmp.ao = ao;
+ rdcs->qmp.domid = domid;
+ rdcs->qmp.payload_fd = -1;
+ libxl__ev_time_init(&rdcs->timeout);
+ rdcs->d_config = d_config;
+ libxl_bitmap_init(&rdcs->qemuu_cpus);
+ libxl__ev_devlock_init(&rdcs->devlock);
+ rdcs->devlock.ao = ao;
+ rdcs->devlock.domid = domid;
+ rdcs->devlock.callback = retrieve_domain_configuration_lock_acquired;
+ libxl__ev_slowlock_lock(egc, &rdcs->devlock);
+ return AO_INPROGRESS;
+}
+
+static void retrieve_domain_configuration_lock_acquired(
+ libxl__egc *egc, libxl__ev_slowlock *devlock, int rc)
+{
+ retrieve_domain_configuration_state *rdcs =
+ CONTAINER_OF(devlock, *rdcs, devlock);
+ STATE_AO_GC(rdcs->qmp.ao);
+ libxl__flock *lock = NULL;
+ bool has_callback = false;
+
+ /* Convenience aliases */
+ libxl_domid domid = rdcs->qmp.domid;
+ libxl_domain_config *const d_config = rdcs->d_config;
+
+ if (rc) goto out;
+
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, d_config);
+ if (rc) {
+ LOGD(ERROR, domid, "Fail to get domain configuration");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ libxl__unlock_file(lock);
+ lock = NULL;
+
+ /* We start by querying QEMU, if it is running, for its cpumap as this
+ * is a long operation. */
+ if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM &&
+ libxl__device_model_version_running(gc, domid) ==
+ LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+ /* For QEMU upstream we always need to provide the number
+ * of cpus present to QEMU whether they are online or not;
+ * otherwise QEMU won't accept the saved state.
+ */
+ rc = libxl__ev_time_register_rel(ao, &rdcs->timeout,
+ retrieve_domain_configuration_timeout,
+ LIBXL_QMP_CMD_TIMEOUT * 1000);
+ if (rc) goto out;
+ libxl_bitmap_alloc(CTX, &rdcs->qemuu_cpus,
+ d_config->b_info.max_vcpus);
+ rdcs->qmp.callback = retrieve_domain_configuration_cpu_queried;
+ rc = libxl__ev_qmp_send(egc, &rdcs->qmp, "query-cpus", NULL);
+ if (rc) goto out;
+ has_callback = true;
+ }
+
+out:
+ if (lock) libxl__unlock_file(lock);
+ if (!has_callback)
+ retrieve_domain_configuration_end(egc, rdcs, rc);
+}
+
+static void retrieve_domain_configuration_cpu_queried(
+ libxl__egc *egc, libxl__ev_qmp *qmp,
+ const libxl__json_object *response, int rc)
+{
+ EGC_GC;
+ retrieve_domain_configuration_state *rdcs =
+ CONTAINER_OF(qmp, *rdcs, qmp);
+
+ if (rc) goto out;
+
+ rc = qmp_parse_query_cpus(gc, qmp->domid, response, &rdcs->qemuu_cpus);
+
+out:
+ retrieve_domain_configuration_end(egc, rdcs, rc);
+}
+
+static void retrieve_domain_configuration_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
+{
+ retrieve_domain_configuration_state *rdcs =
+ CONTAINER_OF(ev, *rdcs, timeout);
+
+ retrieve_domain_configuration_end(egc, rdcs, rc);
+}
+
+static void retrieve_domain_configuration_end(libxl__egc *egc,
+ retrieve_domain_configuration_state *rdcs, int rc)
+{
+ STATE_AO_GC(rdcs->qmp.ao);
+ libxl__flock *lock = NULL;
+
+ /* Convenience aliases */
+ libxl_domain_config *const d_config = rdcs->d_config;
+ libxl_domid domid = rdcs->qmp.domid;
+
+ if (rc) goto out;
+
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ /* Domain name */
+ {
+ char *domname;
+ domname = libxl_domid_to_name(CTX, domid);
+ if (!domname) {
+ LOGD(ERROR, domid, "Fail to get domain name");
+ goto out;
+ }
+ free(d_config->c_info.name);
+ d_config->c_info.name = domname; /* steals allocation */
+ }
+
+ /* Domain UUID */
+ {
+ libxl_dominfo info;
+ libxl_dominfo_init(&info);
+ rc = libxl_domain_info(CTX, &info, domid);
+ if (rc) {
+ LOGD(ERROR, domid, "Fail to get domain info");
+ libxl_dominfo_dispose(&info);
+ goto out;
+ }
+ libxl_uuid_copy(CTX, &d_config->c_info.uuid, &info.uuid);
+ libxl_dominfo_dispose(&info);
+ }
+
+ /* VCPUs */
+ {
+ libxl_bitmap *map = &d_config->b_info.avail_vcpus;
+ unsigned int max_vcpus = d_config->b_info.max_vcpus;
+ libxl_device_model_version version;
+
+ libxl_bitmap_dispose(map);
+ libxl_bitmap_init(map);
+ libxl_bitmap_alloc(CTX, map, max_vcpus);
+ libxl_bitmap_set_none(map);
+
+ switch (d_config->b_info.type) {
+ case LIBXL_DOMAIN_TYPE_HVM:
+ version = libxl__device_model_version_running(gc, domid);
+ assert(version != LIBXL_DEVICE_MODEL_VERSION_UNKNOWN);
+ switch (version) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ libxl_bitmap_copy(CTX, map, &rdcs->qemuu_cpus);
+ break;
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ rc = libxl__update_avail_vcpus_xenstore(gc, domid,
+ max_vcpus, map);
+ break;
+ default:
+ abort();
+ }
+ break;
+ case LIBXL_DOMAIN_TYPE_PVH:
+ case LIBXL_DOMAIN_TYPE_PV:
+ rc = libxl__update_avail_vcpus_xenstore(gc, domid,
+ max_vcpus, map);
+ break;
+ default:
+ abort();
+ }
+
+ if (rc) {
+ LOGD(ERROR, domid, "Fail to update available cpu map");
+ goto out;
+ }
+ }
+
+
+ /* Memory limits:
+ *
+ * Currently there are three memory limits:
+ * 1. "target" in xenstore (originally memory= in config file)
+ * 2. "static-max" in xenstore (originally maxmem= in config file)
+ * 3. "max_memkb" in hypervisor
+ *
+ * The third one is not visible and currently managed by
+ * toolstack. In order to rebuild a domain we only need to have
+ * "target" and "static-max".
+ */
+ {
+ uint64_t target_memkb = 0, max_memkb = 0;
+
+ /* "target" */
+ rc = libxl__get_memory_target(gc, domid, &target_memkb, &max_memkb);
+ if (rc) {
+ LOGD(ERROR, domid, "Fail to get memory target");
+ goto out;
+ }
+
+ /* libxl__get_targetmem_fudge() calculates the difference from
+ * what is in xenstore to what we have in the domain build info.
+ */
+ d_config->b_info.target_memkb = target_memkb +
+ libxl__get_targetmem_fudge(gc, &d_config->b_info);
+
+ d_config->b_info.max_memkb = max_memkb;
+ }
+
+ /* Scheduler params */
+ {
+ libxl_domain_sched_params_dispose(&d_config->b_info.sched_params);
+ rc = libxl_domain_sched_params_get(CTX, domid,
+ &d_config->b_info.sched_params);
+ if (rc) {
+ LOGD(ERROR, domid, "Fail to get scheduler parameters");
+ goto out;
+ }
+ }
+
+ /* Devices: disk, nic, vtpm, pcidev etc. */
+
+ /* The MERGE macro implements following logic:
+ * 0. retrieve JSON (done by now)
+ * 1. retrieve list of device from xenstore
+ * 2. use xenstore entries as primary reference and compare JSON
+ * entries with them.
+ * a. if a device is present in xenstore and in JSON, merge the
+ * two views.
+ * b. if a device is not present in xenstore but in JSON, delete
+ * it from the result.
+ * c. it's impossible to have an entry present in xenstore but
+ * not in JSON, because we maintain an invariant that every
+ * entry in xenstore must have a corresponding entry in JSON.
+ * 3. "merge" operates on "src" and "dst". "src" points to the
+ * entry retrieved from xenstore while "dst" points to the entry
+ * retrieve from JSON.
+ */
+ {
+ const libxl__device_type *dt;
+ int idx;
+
+ for (idx = 0;; idx++) {
+ void *p = NULL;
+ void **devs;
+ int i, j, num;
+ int *num_dev;
+
+ dt = device_type_tbl[idx];
+ if (!dt)
+ break;
+
+ if (!dt->compare)
+ continue;
+
+ num_dev = libxl__device_type_get_num(dt, d_config);
+ p = libxl__device_list(gc, dt, domid, &num);
+ if (p == NULL) {
+ LOGD(DEBUG, domid, "No %s from xenstore",
+ libxl__device_kind_to_string(dt->type));
+ }
+ devs = libxl__device_type_get_ptr(dt, d_config);
+
+ for (i = 0; i < *num_dev; i++) {
+ void *q;
+
+ q = libxl__device_type_get_elem(dt, d_config, i);
+ for (j = 0; j < num; j++) {
+ if (dt->compare(p + dt->dev_elem_size * j, q))
+ break;
+ }
+
+ if (j < num) { /* found in xenstore */
+ if (dt->merge)
+ dt->merge(CTX, p + dt->dev_elem_size * j, q);
+ } else { /* not found in xenstore */
+ LOGD(WARN, domid,
+ "Device present in JSON but not in xenstore, ignored");
+
+ dt->dispose(q);
+
+ for (j = i; j < *num_dev - 1; j++)
+ memcpy(libxl__device_type_get_elem(dt, d_config, j),
+ libxl__device_type_get_elem(dt, d_config, j+1),
+ dt->dev_elem_size);
+
+ /* rewind counters */
+ (*num_dev)--;
+ i--;
+
+ *devs = libxl__realloc(NOGC, *devs,
+ dt->dev_elem_size * *num_dev);
+ }
+ }
+
+ for (i = 0; i < num; i++)
+ dt->dispose(p + dt->dev_elem_size * i);
+ free(p);
+ }
+ }
+
+out:
+ libxl__ev_slowlock_unlock(gc, &rdcs->devlock);
+ if (lock) libxl__unlock_file(lock);
+ libxl_bitmap_dispose(&rdcs->qemuu_cpus);
+ libxl__ev_qmp_dispose(gc, &rdcs->qmp);
+ libxl__ev_time_deregister(gc, &rdcs->timeout);
+ libxl__ao_complete(egc, ao, rc);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2011 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+/*
+ * Internal event machinery for use by other parts of libxl
+ */
+
+#include <poll.h>
+
+#include "libxl_internal.h"
+
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+# define LIBXL__DBG_LOG(ctx, args, ...) \
+ LIBXL__LOG((ctx), XTL_DEBUG, args, __VA_ARGS__)
+#else
+# define LIBXL__DBG_LOG(ctx, args, ...) ((void)0)
+#endif
+#define DBG(args, ...) LIBXL__DBG_LOG(CTX, args, __VA_ARGS__)
+
+
+static libxl__ao *ao_nested_root(libxl__ao *ao);
+
+static void ao__check_destroy(libxl_ctx *ctx, libxl__ao *ao);
+
+
+/*
+ * osevent update baton handling
+ *
+ * We need the following property (the "unstale liveness property"):
+ *
+ * Whenever any thread is blocking as a result of being given an fd
+ * set or timeout by libxl, at least one thread must be using an up to
+ * date osevent set. It is OK for all but one threads to have stale
+ * event sets, because so long as one waiting thread has the right
+ * event set, any actually interesting event will, if nothing else,
+ * wake that "right" thread up. It will then make some progress
+ * and/or, if it exits, ensure that some other thread becomes the
+ * "right" thread.
+ *
+ * For threads blocking outside libxl and which are receiving libxl's
+ * fd and timeout information via the libxl_osevent_hooks callbacks,
+ * libxl calls this function as soon as it becomes interested. It is
+ * the responsiblity of a provider of these functions in a
+ * multithreaded environment to make arrangements to wake up event
+ * waiting thread(s) with stale event sets.
+ *
+ * Waiters outside libxl using _beforepoll are dealt with below.
+ *
+ * For the libxl event loop, the argument is as follows:
+ *
+ * The issue we are concerned about is libxl sleeping on an out of
+ * date fd set, or too long a timeout, so that it doesn't make
+ * progress. If the property above is satisfied, then if any thread
+ * is waiting in libxl at least one such thread will be waiting on a
+ * sufficient osevent set, so any relevant osevent will wake up a
+ * libxl thread which will either handle the event, or arrange that at
+ * least one other libxl thread has the right set.
+ *
+ * There are two calls to poll in libxl: one is the fd recheck, which
+ * is not blocking. There is only the one blocking call, in
+ * eventloop_iteration. poll runs with the ctx unlocked, so osevents
+ * might be added after it unlocks the ctx - that is what we are
+ * worried about.
+ *
+ * To demonstrate that the unstale liveness property is satisfied:
+ *
+ * We define a baton holder as follows: a libxl thread is a baton
+ * holder if
+ * (a) it has an egc or an ao and holds the ctx lock, or
+ * (b) it has an active non-app poller and no osevents have been
+ * added since it released the lock, or
+ * (c) it has an active non-app poller which has been woken
+ * (by writing to its pipe), so it will not sleep
+ * We will maintain the invariant (the "baton invariant") that
+ * whenever there is any active poller, there is at least
+ * one baton holder. ("non-app" means simply "not poller_app".)
+ *
+ * No thread outside libxl can have an active non-app poller: pollers
+ * are put on the active list by poller_get which is called in three
+ * places: libxl_event_wait, which puts it before returning;
+ * libxl__ao_create but only in the synchronous case, in which case
+ * the poller is put before returning; and the poller_app, during
+ * initialisation.
+ *
+ * So any time when all libxl threads are blocking (and therefore do
+ * not have the ctx lock), the non-app active pollers belong to those
+ * threads. If at least one is a baton holder (the invariant), that
+ * thread has a good enough event set.
+ *
+ * Now we will demonstrate that the "baton invariant" is maintained:
+ *
+ * The rule is that any thread which might be the baton holder is
+ * responsible for checking that there continues to be a baton holder
+ * as needed.
+ *
+ * Firstly, consider the case when the baton holders (b) cease to be
+ * baton holders because osevents are added.
+ *
+ * There are only two kinds of osevents: timeouts and fds. Every
+ * other internal event source reduces to one of these eventually.
+ * Both of these cases are handled (in the case of fd events, add and
+ * modify, separately), calling pollers_note_osevent_added.
+ *
+ * This walks the poller_active list, marking the active pollers
+ * osevents_added=1. Such a poller cannot be the baton holder. But
+ * pollers_note_osevent_added is called only from ev_* functions,
+ * which are only called from event-chain libxl code: ie, code with an
+ * ao or an egc. So at this point we are a baton holder, and there is
+ * still a baton holder.
+ *
+ * Secondly, consider the case where baton holders (a) cease to be
+ * batton holders because they dispose of their egc or ao. We call
+ * libxl__egc_ao_cleanup_1_baton on every exit path. We arrange that
+ * everything that disposes of an egc or an ao checks that there is a
+ * new baton holder by calling libxl__egc_ao_cleanup_1_baton.
+ *
+ * This function handles the invariant explicitly: if we have any
+ * non-app active pollers it looks for one which is up to date (baton
+ * holder category (b)), and failing that it picks a victim to turn
+ * into the baton holder category (c) by waking it up. (Correctness
+ * depends on this function not spotting its own thread as the
+ * baton-holder, since it is on its way to not being the baton-holder,
+ * so it must be called after the poller has been put back.)
+ *
+ * Thirdly, we must consider the case (c). A thread in category (c)
+ * will reenter libxl when it gains the lock and necessarily then
+ * becomes a baton holder in category (a).
+ *
+ * So the "baton invariant" is maintained.
+ * QED (for waiters in libxl).
+ *
+ *
+ * For waiters outside libxl which used libxl_osevent_beforepoll
+ * to get the fd set:
+ *
+ * As above, adding an osevent involves having an egc or an ao.
+ * It sets poller->osevents_added on all active pollers. Notably
+ * it sets it on poller_app, which is always active.
+ *
+ * The thread which does this will dispose of its egc or ao before
+ * exiting libxl so it will always wake up the poller_app if the last
+ * call to _beforepoll was before the osevents were added. So the
+ * application's fd set contains at least a wakeup in the form of the
+ * poller_app fd. The application cannot sleep on the libxl fd set
+ * until it has called _afterpoll which empties the pipe, and it
+ * is expected to then call _beforepoll again before sleeping.
+ *
+ * So all the application's event waiting thread(s) will always have
+ * an up to date osevent set, and will be woken up if necessary to
+ * achieve this. (This is in contrast libxl's own event loop where
+ * only one thread need be up to date, as discussed above.)
+ */
+static void pollers_note_osevent_added(libxl_ctx *ctx) {
+ libxl__poller *poller;
+ LIBXL_LIST_FOREACH(poller, &ctx->pollers_active, active_entry)
+ poller->osevents_added = 1;
+}
+
+static void baton_wake(libxl__gc *gc, libxl__poller *wake)
+{
+ libxl__poller_wakeup(gc, wake);
+
+ wake->osevents_added = 0;
+ /* This serves to make _1_baton idempotent. It is OK even though
+ * that poller may currently be sleeping on only old osevents,
+ * because it is going to wake up because we've just prodded it,
+ * and it pick up new osevents on its next iteration (or pass
+ * on the baton). */
+}
+
+void libxl__egc_ao_cleanup_1_baton(libxl__gc *gc)
+ /* Any poller we had must have been `put' already. */
+{
+ libxl__poller *search, *wake=0;
+
+ if (CTX->poller_app->osevents_added)
+ baton_wake(gc, CTX->poller_app);
+
+ LIBXL_LIST_FOREACH(search, &CTX->pollers_active, active_entry) {
+ if (search == CTX->poller_app)
+ /* This one is special. We can't give it the baton. */
+ continue;
+ if (!search->osevents_added)
+ /* This poller is up to date and will wake up as needed. */
+ return;
+ if (!wake)
+ wake = search;
+ }
+
+ if (!wake)
+ /* no-one in libxl waiting for any events */
+ return;
+
+ baton_wake(gc, wake);
+}
+
+/*
+ * The counter osevent_in_hook is used to ensure that the application
+ * honours the reentrancy restriction documented in libxl_event.h.
+ *
+ * The application's registration hooks should be called ONLY via
+ * these macros, with the ctx locked. Likewise all the "occurred"
+ * entrypoints from the application should assert(!in_hook);
+ *
+ * During the hook call - including while the arguments are being
+ * evaluated - ev->nexus is guaranteed to be valid and refer to the
+ * nexus which is being used for this event registration. The
+ * arguments should specify ev->nexus for the for_libxl argument and
+ * ev->nexus->for_app_reg (or a pointer to it) for for_app_reg.
+ */
+#define OSEVENT_HOOK_INTERN(retval, failedp, evkind, hookop, nexusop, ...) do { \
+ if (CTX->osevent_hooks) { \
+ CTX->osevent_in_hook++; \
+ libxl__osevent_hook_nexi *nexi = &CTX->hook_##evkind##_nexi_idle; \
+ osevent_hook_pre_##nexusop(gc, ev, nexi, &ev->nexus); \
+ retval CTX->osevent_hooks->evkind##_##hookop \
+ (CTX->osevent_user, __VA_ARGS__); \
+ if ((failedp)) \
+ osevent_hook_failed_##nexusop(gc, ev, nexi, &ev->nexus); \
+ CTX->osevent_in_hook--; \
+ } \
+} while (0)
+
+#define OSEVENT_HOOK(evkind, hookop, nexusop, ...) ({ \
+ int osevent_hook_rc = 0; \
+ OSEVENT_HOOK_INTERN(osevent_hook_rc =, !!osevent_hook_rc, \
+ evkind, hookop, nexusop, __VA_ARGS__); \
+ osevent_hook_rc; \
+})
+
+#define OSEVENT_HOOK_VOID(evkind, hookop, nexusop, ...) \
+ OSEVENT_HOOK_INTERN(/* void */, 0, evkind, hookop, nexusop, __VA_ARGS__)
+
+/*
+ * The application's calls to libxl_osevent_occurred_... may be
+ * indefinitely delayed with respect to the rest of the program (since
+ * they are not necessarily called with any lock held). So the
+ * for_libxl value we receive may be (almost) arbitrarily old. All we
+ * know is that it came from this ctx.
+ *
+ * Therefore we may not free the object referred to by any for_libxl
+ * value until we free the whole libxl_ctx. And if we reuse it we
+ * must be able to tell when an old use turns up, and discard the
+ * stale event.
+ *
+ * Thus we cannot use the ev directly as the for_libxl value - we need
+ * a layer of indirection.
+ *
+ * We do this by keeping a pool of libxl__osevent_hook_nexus structs,
+ * and use pointers to them as for_libxl values. In fact, there are
+ * two pools: one for fds and one for timeouts. This ensures that we
+ * don't risk a type error when we upcast nexus->ev. In each nexus
+ * the ev is either null or points to a valid libxl__ev_time or
+ * libxl__ev_fd, as applicable.
+ *
+ * We /do/ allow ourselves to reassociate an old nexus with a new ev
+ * as otherwise we would have to leak nexi. (This reassociation
+ * might, of course, be an old ev being reused for a new purpose so
+ * simply comparing the ev pointer is not sufficient.) Thus the
+ * libxl_osevent_occurred functions need to check that the condition
+ * allegedly signalled by this event actually exists.
+ *
+ * The nexi and the lists are all protected by the ctx lock.
+ */
+
+struct libxl__osevent_hook_nexus {
+ void *ev;
+ void *for_app_reg;
+ LIBXL_SLIST_ENTRY(libxl__osevent_hook_nexus) next;
+};
+
+static void *osevent_ev_from_hook_nexus(libxl_ctx *ctx,
+ libxl__osevent_hook_nexus *nexus /* pass void *for_libxl */)
+{
+ return nexus->ev;
+}
+
+static void osevent_release_nexus(libxl__gc *gc,
+ libxl__osevent_hook_nexi *nexi_idle,
+ libxl__osevent_hook_nexus *nexus)
+{
+ nexus->ev = 0;
+ LIBXL_SLIST_INSERT_HEAD(nexi_idle, nexus, next);
+}
+
+/*----- OSEVENT* hook functions for nexusop "alloc" -----*/
+static void osevent_hook_pre_alloc(libxl__gc *gc, void *ev,
+ libxl__osevent_hook_nexi *nexi_idle,
+ libxl__osevent_hook_nexus **nexus_r)
+{
+ libxl__osevent_hook_nexus *nexus = LIBXL_SLIST_FIRST(nexi_idle);
+ if (nexus) {
+ LIBXL_SLIST_REMOVE_HEAD(nexi_idle, next);
+ } else {
+ nexus = libxl__zalloc(NOGC, sizeof(*nexus));
+ }
+ nexus->ev = ev;
+ *nexus_r = nexus;
+}
+static void osevent_hook_failed_alloc(libxl__gc *gc, void *ev,
+ libxl__osevent_hook_nexi *nexi_idle,
+ libxl__osevent_hook_nexus **nexus)
+{
+ osevent_release_nexus(gc, nexi_idle, *nexus);
+}
+
+/*----- OSEVENT* hook functions for nexusop "release" -----*/
+static void osevent_hook_pre_release(libxl__gc *gc, void *ev,
+ libxl__osevent_hook_nexi *nexi_idle,
+ libxl__osevent_hook_nexus **nexus)
+{
+ osevent_release_nexus(gc, nexi_idle, *nexus);
+}
+static void osevent_hook_failed_release(libxl__gc *gc, void *ev,
+ libxl__osevent_hook_nexi *nexi_idle,
+ libxl__osevent_hook_nexus **nexus)
+{
+ abort();
+}
+
+/*----- OSEVENT* hook functions for nexusop "noop" -----*/
+static void osevent_hook_pre_noop(libxl__gc *gc, void *ev,
+ libxl__osevent_hook_nexi *nexi_idle,
+ libxl__osevent_hook_nexus **nexus) { }
+static void osevent_hook_failed_noop(libxl__gc *gc, void *ev,
+ libxl__osevent_hook_nexi *nexi_idle,
+ libxl__osevent_hook_nexus **nexus) { }
+
+
+/*
+ * fd events
+ */
+
+int libxl__ev_fd_register(libxl__gc *gc, libxl__ev_fd *ev,
+ libxl__ev_fd_callback *func,
+ int fd, short events)
+{
+ int rc;
+
+ assert(fd >= 0);
+
+ CTX_LOCK;
+
+ DBG("ev_fd=%p register fd=%d events=%x", ev, fd, events);
+
+ rc = OSEVENT_HOOK(fd,register, alloc, fd, &ev->nexus->for_app_reg,
+ events, ev->nexus);
+ if (rc) goto out;
+
+ ev->fd = fd;
+ ev->events = events;
+ ev->func = func;
+
+ LIBXL_LIST_INSERT_HEAD(&CTX->efds, ev, entry);
+ pollers_note_osevent_added(CTX);
+
+ rc = 0;
+
+ out:
+ CTX_UNLOCK;
+ return rc;
+}
+
+int libxl__ev_fd_modify(libxl__gc *gc, libxl__ev_fd *ev, short events)
+{
+ int rc;
+
+ CTX_LOCK;
+ assert(libxl__ev_fd_isregistered(ev));
+
+ DBG("ev_fd=%p modify fd=%d events=%x", ev, ev->fd, events);
+
+ rc = OSEVENT_HOOK(fd,modify, noop, ev->fd, &ev->nexus->for_app_reg, events);
+ if (rc) goto out;
+
+ if ((events & ~ev->events))
+ pollers_note_osevent_added(CTX);
+ ev->events = events;
+
+ rc = 0;
+ out:
+ CTX_UNLOCK;
+ return rc;
+}
+
+void libxl__ev_fd_deregister(libxl__gc *gc, libxl__ev_fd *ev)
+{
+ CTX_LOCK;
+ libxl__poller *poller;
+
+ if (!libxl__ev_fd_isregistered(ev)) {
+ DBG("ev_fd=%p deregister unregistered",ev);
+ goto out;
+ }
+
+ DBG("ev_fd=%p deregister fd=%d", ev, ev->fd);
+
+ OSEVENT_HOOK_VOID(fd,deregister, release, ev->fd, ev->nexus->for_app_reg);
+ LIBXL_LIST_REMOVE(ev, entry);
+ ev->fd = -1;
+
+ LIBXL_LIST_FOREACH(poller, &CTX->pollers_active, active_entry)
+ poller->fds_deregistered = 1;
+
+ out:
+ CTX_UNLOCK;
+}
+
+short libxl__fd_poll_recheck(libxl__egc *egc, int fd, short events) {
+ struct pollfd check;
+ int r;
+ EGC_GC;
+
+ for (;;) {
+ check.fd = fd;
+ check.events = events;
+ r = poll(&check, 1, 0);
+ DBG("poll recheck fd=%d r=%d revents=%#x", fd, r, check.revents);
+ if (!r)
+ break;
+ if (r==1)
+ break;
+ assert(r<0);
+ if (errno != EINTR) {
+ LIBXL__EVENT_DISASTER(gc, "failed poll to check for fd", errno, 0);
+ return 0;
+ }
+ }
+ assert(!!r == !!check.revents);
+ return check.revents;
+}
+
+/*
+ * timeouts
+ */
+
+
+int libxl__gettimeofday(libxl__gc *gc, struct timeval *now_r)
+{
+ int rc = gettimeofday(now_r, 0);
+ if (rc) {
+ LOGE(ERROR, "gettimeofday failed");
+ return ERROR_FAIL;
+ }
+ return 0;
+}
+
+static int time_rel_to_abs(libxl__gc *gc, int ms, struct timeval *abs_out)
+{
+ int rc;
+ struct timeval additional = {
+ .tv_sec = ms / 1000,
+ .tv_usec = (ms % 1000) * 1000
+ };
+ struct timeval now;
+
+ rc = libxl__gettimeofday(gc, &now);
+ if (rc) return rc;
+
+ timeradd(&now, &additional, abs_out);
+ return 0;
+}
+
+static int time_register_finite(libxl__gc *gc, libxl__ev_time *ev,
+ struct timeval absolute)
+{
+ int rc;
+ libxl__ev_time *evsearch;
+
+ rc = OSEVENT_HOOK(timeout,register, alloc, &ev->nexus->for_app_reg,
+ absolute, ev->nexus);
+ if (rc) return rc;
+
+ ev->infinite = 0;
+ ev->abs = absolute;
+ LIBXL_TAILQ_INSERT_SORTED(&CTX->etimes, entry, ev, evsearch, /*empty*/,
+ timercmp(&ev->abs, &evsearch->abs, >));
+
+ pollers_note_osevent_added(CTX);
+ return 0;
+}
+
+static void time_deregister(libxl__gc *gc, libxl__ev_time *ev)
+{
+ libxl__ao_abortable_deregister(&ev->abrt);
+
+ if (!ev->infinite) {
+ struct timeval right_away = { 0, 0 };
+ if (ev->nexus) /* only set if app provided hooks */
+ ev->nexus->ev = 0;
+ OSEVENT_HOOK_VOID(timeout,modify,
+ noop /* release nexus in _occurred_ */,
+ &ev->nexus->for_app_reg, right_away);
+ LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry);
+ }
+}
+
+static void time_done_debug(libxl__gc *gc, const char *func,
+ libxl__ev_time *ev, int rc)
+{
+#ifdef DEBUG
+ libxl__log(CTX, XTL_DEBUG, -1, __FILE__, 0, func, INVALID_DOMID,
+ "ev_time=%p done rc=%d .func=%p infinite=%d abs=%lu.%06lu",
+ ev, rc, ev->func, ev->infinite,
+ (unsigned long)ev->abs.tv_sec, (unsigned long)ev->abs.tv_usec);
+#endif
+}
+
+static void time_aborted(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
+{
+ libxl__ev_time *ev = CONTAINER_OF(abrt, *ev, abrt);
+ EGC_GC;
+
+ time_deregister(gc, ev);
+ DBG("ev_time=%p aborted", ev);
+ ev->func(egc, ev, &ev->abs, rc);
+}
+
+static int time_register_abortable(libxl__ao *ao, libxl__ev_time *ev)
+{
+ ev->abrt.ao = ao;
+ ev->abrt.callback = time_aborted;
+ return libxl__ao_abortable_register(&ev->abrt);
+}
+
+int libxl__ev_time_register_abs(libxl__ao *ao, libxl__ev_time *ev,
+ libxl__ev_time_callback *func,
+ struct timeval absolute)
+{
+ AO_GC;
+ int rc;
+
+ CTX_LOCK;
+
+ DBG("ev_time=%p register abs=%lu.%06lu",
+ ev, (unsigned long)absolute.tv_sec, (unsigned long)absolute.tv_usec);
+
+ rc = time_register_abortable(ao, ev);
+ if (rc) goto out;
+
+ rc = time_register_finite(gc, ev, absolute);
+ if (rc) goto out;
+
+ ev->func = func;
+
+ rc = 0;
+ out:
+ libxl__ao_abortable_deregister(&ev->abrt);
+ time_done_debug(gc,__func__,ev,rc);
+ CTX_UNLOCK;
+ return rc;
+}
+
+
+int libxl__ev_time_register_rel(libxl__ao *ao, libxl__ev_time *ev,
+ libxl__ev_time_callback *func,
+ int milliseconds /* as for poll(2) */)
+{
+ AO_GC;
+ struct timeval absolute;
+ int rc;
+
+ CTX_LOCK;
+
+ DBG("ev_time=%p register ms=%d", ev, milliseconds);
+
+ rc = time_register_abortable(ao, ev);
+ if (rc) goto out;
+
+ if (milliseconds < 0) {
+ ev->infinite = 1;
+ } else {
+ rc = time_rel_to_abs(gc, milliseconds, &absolute);
+ if (rc) goto out;
+
+ rc = time_register_finite(gc, ev, absolute);
+ if (rc) goto out;
+ }
+
+ ev->func = func;
+ rc = 0;
+
+ out:
+ if (!libxl__ev_time_isregistered(ev))
+ libxl__ao_abortable_deregister(&ev->abrt);
+ time_done_debug(gc,__func__,ev,rc);
+ CTX_UNLOCK;
+ return rc;
+}
+
+void libxl__ev_time_deregister(libxl__gc *gc, libxl__ev_time *ev)
+{
+ CTX_LOCK;
+
+ DBG("ev_time=%p deregister", ev);
+
+ if (!libxl__ev_time_isregistered(ev))
+ goto out;
+
+ time_deregister(gc, ev);
+ ev->func = 0;
+
+ out:
+ time_done_debug(gc,__func__,ev,0);
+ CTX_UNLOCK;
+ return;
+}
+
+static void time_occurs(libxl__egc *egc, libxl__ev_time *etime, int rc)
+{
+ EGC_GC;
+
+ DBG("ev_time=%p occurs abs=%lu.%06lu",
+ etime, (unsigned long)etime->abs.tv_sec,
+ (unsigned long)etime->abs.tv_usec);
+
+ libxl__ev_time_callback *func = etime->func;
+ etime->func = 0;
+ func(egc, etime, &etime->abs, rc);
+}
+
+
+/*
+ * xenstore watches
+ */
+
+libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, int slotnum)
+{
+ libxl__ev_watch_slot *slot = &CTX->watch_slots[slotnum];
+ libxl__ev_watch_slot *slotcontents = LIBXL_SLIST_NEXT(slot, empty);
+
+ if (slotcontents == NULL ||
+ ((uintptr_t)slotcontents >= (uintptr_t)CTX->watch_slots &&
+ (uintptr_t)slotcontents < (uintptr_t)(CTX->watch_slots +
+ CTX->watch_nslots)))
+ /* An empty slot has either a NULL pointer (end of the
+ * free list), or a pointer to another entry in the array.
+ * So we can do a bounds check to distinguish empty from
+ * full slots.
+ */
+ /* We need to do the comparisons as uintptr_t because
+ * comparing pointers which are not in the same object is
+ * undefined behaviour; if the compiler managed to figure
+ * out that watch_slots[0..watch_nslots-1] is all of the
+ * whole array object it could prove that the above bounds
+ * check was always true if it was legal, and remove it!
+ *
+ * uintptr_t because even on a machine with signed
+ * pointers, objects do not cross zero; whereas on
+ * machines with unsigned pointers, they may cross
+ * 0x8bazillion.
+ */
+ return NULL;
+
+ /* see comment near libxl__ev_watch_slot definition */
+ return (void*)slotcontents;
+}
+
+static void libxl__set_watch_slot_contents(libxl__ev_watch_slot *slot,
+ libxl__ev_xswatch *w)
+{
+ /* we look a bit behind the curtain of LIBXL_SLIST, to explicitly
+ * assign to the pointer that's the next link. See the comment
+ * by the definition of libxl__ev_watch_slot */
+ slot->empty.sle_next = (void*)w;
+}
+
+static void watchfd_callback(libxl__egc *egc, libxl__ev_fd *ev,
+ int fd, short events, short revents)
+{
+ EGC_GC;
+
+ if (revents & (POLLERR|POLLHUP))
+ LIBXL__EVENT_DISASTER(gc, "unexpected poll event on watch fd", 0, 0);
+
+ for (;;) {
+ char **event = xs_check_watch(CTX->xsh);
+ if (!event) {
+ if (errno == EAGAIN) break;
+ if (errno == EINTR) continue;
+ LIBXL__EVENT_DISASTER(gc, "cannot check/read watches", errno, 0);
+ return;
+ }
+
+ const char *epath = event[0];
+ const char *token = event[1];
+ int slotnum;
+ uint32_t counterval;
+ int rc = sscanf(token, "%d/%"SCNx32, &slotnum, &counterval);
+ if (rc != 2) {
+ LOG(ERROR, "watch epath=%s token=%s: failed to parse token",
+ epath, token);
+ /* oh well */
+ goto ignore;
+ }
+ if (slotnum < 0 || slotnum >= CTX->watch_nslots) {
+ /* perhaps in the future we will make the watchslots array shrink */
+ LIBXL__LOG(CTX, LIBXL__LOG_DEBUG, "watch epath=%s token=%s:"
+ " slotnum %d out of range [0,%d>",
+ epath, token, slotnum, CTX->watch_nslots);
+ goto ignore;
+ }
+
+ libxl__ev_xswatch *w = libxl__watch_slot_contents(gc, slotnum);
+
+ if (!w) {
+ LOG(DEBUG, "watch epath=%s token=%s: empty slot", epath, token);
+ goto ignore;
+ }
+
+ if (w->counterval != counterval) {
+ LOG(DEBUG, "watch w=%p epath=%s token=%s: counter != %"PRIx32,
+ w, epath, token, w->counterval);
+ goto ignore;
+ }
+
+ /* Now it's possible, though unlikely, that this was an event
+ * from a previous use of the same slot with the same counterval.
+ *
+ * In that case either:
+ * - the event path is a child of the watch path, in
+ * which case this watch would really have generated this
+ * event if it had been registered soon enough and we are
+ * OK to give this possibly-spurious event to the caller; or
+ * - it is not, in which case we must suppress it as the
+ * caller should not see events for unrelated paths.
+ *
+ * See also docs/misc/xenstore.txt.
+ */
+ if (!xs_path_is_subpath(w->path, epath)) {
+ LOG(DEBUG, "watch w=%p wpath=%s token=%s: unexpected epath=%s",
+ w, w->path, token, epath);
+ goto ignore;
+ }
+
+ /* At last, we have checked everything! */
+ LOG(DEBUG, "watch w=%p wpath=%s token=%s: event epath=%s",
+ w, w->path, token, epath);
+ w->callback(egc, w, w->path, epath);
+
+ ignore:
+ free(event);
+ }
+}
+
+static char *watch_token(libxl__gc *gc, int slotnum, uint32_t counterval)
+{
+ return GCSPRINTF("%d/%"PRIx32, slotnum, counterval);
+}
+
+static void watches_check_fd_deregister(libxl__gc *gc)
+{
+ assert(CTX->nwatches>=0);
+ if (!CTX->nwatches)
+ libxl__ev_fd_deregister(gc, &CTX->watch_efd);
+}
+
+int libxl__ev_xswatch_register(libxl__gc *gc, libxl__ev_xswatch *w,
+ libxl__ev_xswatch_callback *func,
+ const char *path /* copied */)
+{
+ libxl__ev_watch_slot *use = NULL;
+ char *path_copy = NULL;
+ int rc;
+
+ CTX_LOCK;
+
+ if (!libxl__ev_fd_isregistered(&CTX->watch_efd)) {
+ rc = libxl__ev_fd_register(gc, &CTX->watch_efd, watchfd_callback,
+ xs_fileno(CTX->xsh), POLLIN);
+ if (rc) goto out_rc;
+ }
+
+ if (LIBXL_SLIST_EMPTY(&CTX->watch_freeslots)) {
+ /* Free list is empty so there is not in fact a linked
+ * free list in the array and we can safely realloc it */
+ int newarraysize = (CTX->watch_nslots + 1) << 2;
+ int i;
+ libxl__ev_watch_slot *newarray =
+ libxl__realloc(NOGC,
+ CTX->watch_slots, sizeof(*newarray) * newarraysize);
+ if (!newarray) goto out_nomem;
+ for (i = CTX->watch_nslots; i < newarraysize; i++)
+ LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots,
+ &newarray[i], empty);
+ CTX->watch_slots = newarray;
+ CTX->watch_nslots = newarraysize;
+ }
+ use = LIBXL_SLIST_FIRST(&CTX->watch_freeslots);
+ assert(use);
+ LIBXL_SLIST_REMOVE_HEAD(&CTX->watch_freeslots, empty);
+
+ path_copy = strdup(path);
+ if (!path_copy) goto out_nomem;
+
+ int slotnum = use - CTX->watch_slots;
+ w->counterval = CTX->watch_counter++;
+
+ const char *token = watch_token(gc, slotnum, w->counterval);
+ LOG(DEBUG, "watch w=%p wpath=%s token=%s: register slotnum=%d",
+ w, path, token, slotnum);
+
+ if (!xs_watch(CTX->xsh, path, token)) {
+ LOGEV(ERROR, errno, "create watch for path %s", path);
+ rc = ERROR_FAIL;
+ goto out_rc;
+ }
+
+ w->slotnum = slotnum;
+ w->path = path_copy;
+ w->callback = func;
+ CTX->nwatches++;
+ libxl__set_watch_slot_contents(use, w);
+
+ CTX_UNLOCK;
+ return 0;
+
+ out_nomem:
+ rc = ERROR_NOMEM;
+ out_rc:
+ if (use)
+ LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, use, empty);
+ free(path_copy);
+ watches_check_fd_deregister(gc);
+ CTX_UNLOCK;
+ return rc;
+}
+
+void libxl__ev_xswatch_deregister(libxl__gc *gc, libxl__ev_xswatch *w)
+{
+ /* it is legal to deregister from within _callback */
+ CTX_LOCK;
+
+ if (w->slotnum >= 0) {
+ const char *token = watch_token(gc, w->slotnum, w->counterval);
+
+ LOG(DEBUG, "watch w=%p wpath=%s token=%s: deregister slotnum=%d",
+ w, w->path, token, w->slotnum);
+
+ if (!xs_unwatch(CTX->xsh, w->path, token))
+ /* Oh well, we will just get watch events forever more
+ * and ignore them. But we should complain to the log. */
+ LOGEV(ERROR, errno, "remove watch for path %s", w->path);
+
+ libxl__ev_watch_slot *slot = &CTX->watch_slots[w->slotnum];
+ LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, slot, empty);
+ w->slotnum = -1;
+ CTX->nwatches--;
+ watches_check_fd_deregister(gc);
+ } else {
+ LOG(DEBUG, "watch w=%p: deregister unregistered", w);
+ }
+
+ free(w->path);
+ w->path = NULL;
+
+ CTX_UNLOCK;
+}
+
+/*
+ * evtchn
+ */
+
+static int evtchn_revents_check(libxl__egc *egc, int revents)
+{
+ EGC_GC;
+
+ if (revents & ~POLLIN) {
+ LOG(ERROR, "unexpected poll event on event channel fd: %x", revents);
+ LIBXL__EVENT_DISASTER(gc,
+ "unexpected poll event on event channel fd", 0, 0);
+ libxl__ev_fd_deregister(gc, &CTX->evtchn_efd);
+ return ERROR_FAIL;
+ }
+
+ assert(revents & POLLIN);
+
+ return 0;
+}
+
+static void evtchn_fd_callback(libxl__egc *egc, libxl__ev_fd *ev,
+ int fd, short events, short revents)
+{
+ EGC_GC;
+ libxl__ev_evtchn *evev;
+ int rc;
+ xenevtchn_port_or_error_t port;
+
+ rc = evtchn_revents_check(egc, revents);
+ if (rc) return;
+
+ for (;;) {
+ /* Check the fd again. The incoming revent may no longer be
+ * true, because the libxl ctx lock has not necessarily been
+ * held continuously since someone noticed the fd. Normally
+ * this wouldn't be a problem but evtchn devices don't always
+ * honour O_NONBLOCK (see xenctrl.h). */
+ revents = libxl__fd_poll_recheck(egc,fd,POLLIN);
+ if (!revents)
+ break;
+ rc = evtchn_revents_check(egc, revents);
+ if (rc) return;
+
+ /* OK, that's that workaround done. We can actually check for
+ * work for us to do: */
+
+ port = xenevtchn_pending(CTX->xce);
+ if (port < 0) {
+ if (errno == EAGAIN)
+ break;
+ LIBXL__EVENT_DISASTER(gc,
+ "unexpected failure fetching occurring event port number from evtchn",
+ errno, 0);
+ return;
+ }
+
+ LIBXL_LIST_FOREACH(evev, &CTX->evtchns_waiting, entry)
+ if (port == evev->port)
+ goto found;
+ /* not found */
+ DBG("ev_evtchn port=%d no-one cared", port);
+ continue;
+
+ found:
+ DBG("ev_evtchn=%p port=%d signaled", evev, port);
+ evev->waiting = 0;
+ LIBXL_LIST_REMOVE(evev, entry);
+ evev->callback(egc, evev);
+ }
+}
+
+int libxl__ctx_evtchn_init(libxl__gc *gc) {
+ xenevtchn_handle *xce;
+ int rc, fd;
+
+ if (CTX->xce)
+ return 0;
+
+ xce = xenevtchn_open(CTX->lg, 0);
+ if (!xce) {
+ LOGE(ERROR,"cannot open libxc evtchn handle");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ fd = xenevtchn_fd(xce);
+ assert(fd >= 0);
+
+ rc = libxl_fd_set_nonblock(CTX, fd, 1);
+ if (rc) goto out;
+
+ CTX->xce = xce;
+ return 0;
+
+ out:
+ xenevtchn_close(xce);
+ return rc;
+}
+
+static void evtchn_check_fd_deregister(libxl__gc *gc)
+{
+ if (CTX->xce && LIBXL_LIST_EMPTY(&CTX->evtchns_waiting))
+ libxl__ev_fd_deregister(gc, &CTX->evtchn_efd);
+}
+
+int libxl__ev_evtchn_wait(libxl__gc *gc, libxl__ev_evtchn *evev)
+{
+ int r, rc;
+
+ DBG("ev_evtchn=%p port=%d wait (was waiting=%d)",
+ evev, evev->port, evev->waiting);
+
+ rc = libxl__ctx_evtchn_init(gc);
+ if (rc) goto out;
+
+ if (!libxl__ev_fd_isregistered(&CTX->evtchn_efd)) {
+ rc = libxl__ev_fd_register(gc, &CTX->evtchn_efd, evtchn_fd_callback,
+ xenevtchn_fd(CTX->xce), POLLIN);
+ if (rc) goto out;
+ }
+
+ if (evev->waiting)
+ return 0;
+
+ r = xenevtchn_unmask(CTX->xce, evev->port);
+ if (r) {
+ LOGE(ERROR,"cannot unmask event channel %d",evev->port);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ evev->waiting = 1;
+ LIBXL_LIST_INSERT_HEAD(&CTX->evtchns_waiting, evev, entry);
+ return 0;
+
+ out:
+ evtchn_check_fd_deregister(gc);
+ return rc;
+}
+
+void libxl__ev_evtchn_cancel(libxl__gc *gc, libxl__ev_evtchn *evev)
+{
+ DBG("ev_evtchn=%p port=%d cancel (was waiting=%d)",
+ evev, evev->port, evev->waiting);
+
+ if (!evev->waiting)
+ return;
+
+ evev->waiting = 0;
+ LIBXL_LIST_REMOVE(evev, entry);
+ evtchn_check_fd_deregister(gc);
+}
+
+/*
+ * waiting for device state
+ */
+
+static void devstate_callback(libxl__egc *egc, libxl__xswait_state *xsw,
+ int rc, const char *sstate)
+{
+ EGC_GC;
+ libxl__ev_devstate *ds = CONTAINER_OF(xsw, *ds, w);
+
+ if (rc) {
+ if (rc == ERROR_TIMEDOUT)
+ LOG(DEBUG, "backend %s wanted state %d "" timed out", ds->w.path,
+ ds->wanted);
+ goto out;
+ }
+ if (!sstate) {
+ LOG(DEBUG, "backend %s wanted state %d"" but it was removed",
+ ds->w.path, ds->wanted);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ int got = atoi(sstate);
+ if (got == ds->wanted) {
+ LOG(DEBUG, "backend %s wanted state %d ok", ds->w.path, ds->wanted);
+ rc = 0;
+ } else {
+ LOG(DEBUG, "backend %s wanted state %d"" still waiting state %d",
+ ds->w.path, ds->wanted, got);
+ return;
+ }
+
+ out:
+ libxl__ev_devstate_cancel(gc, ds);
+ ds->callback(egc, ds, rc);
+}
+
+int libxl__ev_devstate_wait(libxl__ao *ao, libxl__ev_devstate *ds,
+ libxl__ev_devstate_callback cb,
+ const char *state_path, int state, int milliseconds)
+{
+ AO_GC;
+ int rc;
+
+ libxl__xswait_init(&ds->w);
+ ds->wanted = state;
+ ds->callback = cb;
+
+ ds->w.ao = ao;
+ ds->w.what = GCSPRINTF("backend %s (hoping for state change to %d)",
+ state_path, state);
+ ds->w.path = state_path;
+ ds->w.timeout_ms = milliseconds;
+ ds->w.callback = devstate_callback;
+ rc = libxl__xswait_start(gc, &ds->w);
+ if (rc) goto out;
+
+ return 0;
+
+ out:
+ libxl__ev_devstate_cancel(gc, ds);
+ return rc;
+}
+
+/*
+ * immediate non-reentrant callback
+ */
+
+void libxl__ev_immediate_register(libxl__egc *egc, libxl__ev_immediate *ei)
+{
+ LIBXL_STAILQ_INSERT_TAIL(&egc->ev_immediates, ei, entry);
+}
+
+/*
+ * domain death/destruction
+ */
+
+/*
+ * We use a xenstore watch on the domain's path, rather than using an
+ * @releaseDomain watch and asking the hypervisor. This is simpler
+ * because turning @releaseDomain into domain-specific information is
+ * complicated.
+ *
+ * It is also sufficient for our callers, which are generally trying
+ * to do cleanup of their own execution state on domain death, for the
+ * following reason: if the domain is destroyed then either (a) the
+ * entries in xenstore have already been deleted, in which case the
+ * test here works or (b) they have not in which case something has
+ * gone very badly wrong and we are going to leak those xenstore
+ * entries, in which case trying to avoid leaking other stuff is
+ * futile.
+ */
+
+void libxl__domaindeathcheck_init(libxl__domaindeathcheck *dc)
+{
+ libxl__ao_abortable_init(&dc->abrt);
+ libxl__ev_xswatch_init(&dc->watch);
+}
+
+void libxl__domaindeathcheck_stop(libxl__gc *gc, libxl__domaindeathcheck *dc)
+{
+ libxl__ao_abortable_deregister(&dc->abrt);
+ libxl__ev_xswatch_deregister(gc,&dc->watch);
+}
+
+static void domaindeathcheck_callback(libxl__egc *egc, libxl__ev_xswatch *w,
+ const char *watch_path, const char *event_path)
+{
+ libxl__domaindeathcheck *dc = CONTAINER_OF(w, *dc, watch);
+ EGC_GC;
+ const char *p = libxl__xs_read(gc, XBT_NULL, watch_path);
+ if (p) return;
+
+ libxl__domaindeathcheck_stop(gc,dc);
+
+ if (errno!=ENOENT) {
+ LIBXL__EVENT_DISASTER(gc,"failed to read xenstore"
+ " for domain detach check", errno, 0);
+ return;
+ }
+
+ LOG(ERROR,"%s: domain %"PRIu32" removed (%s no longer in xenstore)",
+ dc->what, dc->domid, watch_path);
+ dc->callback(egc, dc, ERROR_DOMAIN_DESTROYED);
+}
+
+static void domaindeathcheck_abort(libxl__egc *egc,
+ libxl__ao_abortable *abrt,
+ int rc)
+{
+ libxl__domaindeathcheck *dc = CONTAINER_OF(abrt, *dc, abrt);
+ EGC_GC;
+
+ libxl__domaindeathcheck_stop(gc,dc);
+ dc->callback(egc, dc, rc);
+}
+
+int libxl__domaindeathcheck_start(libxl__ao *ao,
+ libxl__domaindeathcheck *dc)
+{
+ AO_GC;
+ int rc;
+ const char *path = GCSPRINTF("/local/domain/%"PRIu32, dc->domid);
+
+ libxl__domaindeathcheck_init(dc);
+
+ dc->abrt.ao = ao;
+ dc->abrt.callback = domaindeathcheck_abort;
+ rc = libxl__ao_abortable_register(&dc->abrt);
+ if (rc) goto out;
+
+ rc = libxl__ev_xswatch_register(gc, &dc->watch,
+ domaindeathcheck_callback, path);
+ if (rc) goto out;
+
+ return 0;
+
+ out:
+ libxl__domaindeathcheck_stop(gc,dc);
+ return rc;
+}
+
+/*
+ * osevent poll
+ */
+
+static int beforepoll_internal(libxl__gc *gc, libxl__poller *poller,
+ int *nfds_io, struct pollfd *fds,
+ int *timeout_upd, struct timeval now)
+{
+ libxl__ev_fd *efd;
+ int rc;
+
+ /*
+ * We need to look at the fds we want twice: firstly, to count
+ * them so we can make the rindex array big enough, and secondly
+ * to actually fill the arrays in.
+ *
+ * To ensure correctness and avoid repeating the logic for
+ * deciding which fds are relevant, we define a macro
+ * REQUIRE_FDS( BODY )
+ * which calls
+ * do{
+ * int req_fd;
+ * int req_events;
+ * BODY;
+ * }while(0)
+ * for each fd with a nonzero events. This is invoked twice.
+ *
+ * The definition of REQUIRE_FDS is simplified with the helper
+ * macro
+ * void REQUIRE_FD(int req_fd, int req_events, BODY);
+ */
+
+#define REQUIRE_FDS(BODY) do{ \
+ \
+ LIBXL_LIST_FOREACH(efd, &CTX->efds, entry) \
+ REQUIRE_FD(efd->fd, efd->events, BODY); \
+ \
+ REQUIRE_FD(poller->wakeup_pipe[0], POLLIN, BODY); \
+ \
+ }while(0)
+
+#define REQUIRE_FD(req_fd_, req_events_, BODY) do{ \
+ int req_events = (req_events_); \
+ int req_fd = (req_fd_); \
+ if (req_events) { \
+ BODY; \
+ } \
+ }while(0)
+
+
+ /*
+ * In order to be able to efficiently find the libxl__ev_fd for a
+ * struct poll during _afterpoll, we maintain a shadow data
+ * structure in CTX->fd_rindices: each fd corresponds to a slot in
+ * fd_rindices, and each element in the rindices is three indices
+ * into the fd array (for POLLIN, POLLPRI and POLLOUT).
+ */
+
+ if (*nfds_io) {
+ /*
+ * As an optimisation, we don't touch fd_rindex
+ * if *nfds_io is zero on entry, since in that case the
+ * caller just wanted to know how big an array to give us.
+ *
+ * If !*nfds_io, the unconditional parts below are guaranteed
+ * not to mess with fd_rindex.
+ */
+
+ int maxfd = 0;
+
+ REQUIRE_FDS({
+ if (req_fd >= maxfd)
+ maxfd = req_fd + 1;
+ });
+
+ /* make sure our array is as big as *nfds_io */
+ if (poller->fd_rindices_allocd < maxfd) {
+ assert(ARRAY_SIZE_OK(poller->fd_rindices, maxfd));
+ poller->fd_rindices =
+ libxl__realloc(NOGC, poller->fd_rindices,
+ maxfd * sizeof(*poller->fd_rindices));
+ memset(poller->fd_rindices + poller->fd_rindices_allocd,
+ 0,
+ (maxfd - poller->fd_rindices_allocd)
+ * sizeof(*poller->fd_rindices));
+ poller->fd_rindices_allocd = maxfd;
+ }
+ }
+
+ int used = 0;
+
+ REQUIRE_FDS({
+ if (used < *nfds_io) {
+ fds[used].fd = req_fd;
+ fds[used].events = req_events;
+ fds[used].revents = 0;
+ assert(req_fd < poller->fd_rindices_allocd);
+ if (req_events & POLLIN) poller->fd_rindices[req_fd][0] = used;
+ if (req_events & POLLPRI) poller->fd_rindices[req_fd][1] = used;
+ if (req_events & POLLOUT) poller->fd_rindices[req_fd][2] = used;
+ }
+ used++;
+ });
+
+ rc = used <= *nfds_io ? 0 : ERROR_BUFFERFULL;
+
+ *nfds_io = used;
+
+ poller->fds_deregistered = 0;
+ poller->osevents_added = 0;
+
+ libxl__ev_time *etime = LIBXL_TAILQ_FIRST(&CTX->etimes);
+ if (etime) {
+ int our_timeout;
+ struct timeval rel;
+ static struct timeval zero;
+
+ timersub(&etime->abs, &now, &rel);
+
+ if (timercmp(&rel, &zero, <)) {
+ our_timeout = 0;
+ } else if (rel.tv_sec >= 2000000) {
+ our_timeout = 2000000000;
+ } else {
+ our_timeout = rel.tv_sec * 1000 + (rel.tv_usec + 999) / 1000;
+ }
+ if (*timeout_upd < 0 || our_timeout < *timeout_upd)
+ *timeout_upd = our_timeout;
+ }
+
+ return rc;
+}
+
+int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
+ struct pollfd *fds, int *timeout_upd,
+ struct timeval now)
+{
+ EGC_INIT(ctx);
+ CTX_LOCK;
+ int rc = beforepoll_internal(gc, ctx->poller_app,
+ nfds_io, fds, timeout_upd, now);
+ CTX_UNLOCK_EGC_FREE;
+ return rc;
+}
+
+static int afterpoll_check_fd(libxl__poller *poller,
+ const struct pollfd *fds, int nfds,
+ int fd, int events)
+ /* Returns mask of events which were requested and occurred. Will
+ * return nonzero only once for each (poller,fd,events)
+ * combination, until the next beforepoll. If events from
+ * different combinations overlap, between one such combination
+ * and all distinct combinations will produce nonzero returns. */
+{
+ if (fd >= poller->fd_rindices_allocd)
+ /* added after we went into poll, have to try again */
+ return 0;
+
+ events |= POLLERR | POLLHUP;
+
+ int i, revents = 0;
+ for (i=0; i<3; i++) {
+ int *slotp = &poller->fd_rindices[fd][i];
+ int slot = *slotp;
+
+ if (slot >= nfds)
+ /* stale slot entry (again, added afterwards), */
+ /* or slot for which we have already returned nonzero */
+ continue;
+
+ if (fds[slot].fd != fd)
+ /* again, stale slot entry */
+ continue;
+
+ assert(poller->fds_deregistered || !(fds[slot].revents & POLLNVAL));
+
+ /* we mask in case requested events have changed */
+ int slot_revents = fds[slot].revents & events;
+ if (!slot_revents)
+ /* this slot is for a different set of events */
+ continue;
+
+ revents |= slot_revents;
+ *slotp = INT_MAX; /* so that next time we'll see slot >= nfds */
+ }
+
+ return revents;
+}
+
+static void fd_occurs(libxl__egc *egc, libxl__ev_fd *efd, short revents_ign)
+{
+ short revents_current = libxl__fd_poll_recheck(egc, efd->fd, efd->events);
+ EGC_GC;
+
+ DBG("ev_fd=%p occurs fd=%d events=%x revents_ign=%x revents_current=%x",
+ efd, efd->fd, efd->events, revents_ign, revents_current);
+
+ if (revents_current)
+ efd->func(egc, efd, efd->fd, efd->events, revents_current);
+}
+
+static void afterpoll_internal(libxl__egc *egc, libxl__poller *poller,
+ int nfds, const struct pollfd *fds,
+ struct timeval now)
+{
+ /* May make callbacks into the application for child processes.
+ * ctx must be locked exactly once */
+ EGC_GC;
+ libxl__ev_fd *efd;
+
+ /*
+ * Warning! Reentrancy hazards!
+ *
+ * Many parts of this function eventually call arbitrary callback
+ * functions which may modify the event handling data structures.
+ *
+ * Of the data structures used here:
+ *
+ * egc, poller, now
+ * are allocated by our caller and relate to the
+ * current thread and its call stack down into the
+ * event machinery; it is not freed until we return.
+ * So it is safe.
+ *
+ * fds is either what application passed into
+ * libxl_osevent_afterpoll (which, although this
+ * isn't explicitly stated, clearly must remain
+ * valid until libxl_osevent_afterpoll returns) or
+ * it's poller->fd_polls which is modified only by
+ * our (non-recursive) caller eventloop_iteration.
+ *
+ * CTX comes from our caller, and applications are
+ * forbidden from destroying it while we are running.
+ * So the ctx pointer itself is safe to use; now
+ * for its contents:
+ *
+ * CTX->etimes is used in a simple reentrancy-safe manner.
+ *
+ * CTX->efds is more complicated; see below.
+ */
+
+ for (;;) {
+ /* We restart our scan of fd events whenever we call a
+ * callback function. This is necessary because such
+ * a callback might make arbitrary changes to CTX->efds.
+ * We invalidate the fd_rindices[] entries which were used
+ * so that we don't call the same function again. */
+ int revents;
+
+ LIBXL_LIST_FOREACH(efd, &CTX->efds, entry) {
+
+ if (!efd->events)
+ continue;
+
+ revents = afterpoll_check_fd(poller,fds,nfds,
+ efd->fd,efd->events);
+ if (revents)
+ goto found_fd_event;
+ }
+ /* no ordinary fd events, then */
+ break;
+
+ found_fd_event:
+ fd_occurs(egc, efd, revents);
+ }
+
+ for (;;) {
+ libxl__ev_time *etime = LIBXL_TAILQ_FIRST(&CTX->etimes);
+ if (!etime)
+ break;
+
+ assert(!etime->infinite);
+
+ if (timercmp(&etime->abs, &now, >))
+ break;
+
+ time_deregister(gc, etime);
+
+ time_occurs(egc, etime, ERROR_TIMEDOUT);
+ }
+
+ if (afterpoll_check_fd(poller,fds,nfds, poller->wakeup_pipe[0],POLLIN)) {
+ poller->pipe_nonempty = 0;
+ int e = libxl__self_pipe_eatall(poller->wakeup_pipe[0]);
+ if (e) LIBXL__EVENT_DISASTER(gc, "read wakeup", e, 0);
+ }
+}
+
+void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds,
+ struct timeval now)
+{
+ EGC_INIT(ctx);
+ CTX_LOCK;
+ afterpoll_internal(egc, ctx->poller_app, nfds, fds, now);
+ CTX_UNLOCK_EGC_FREE;
+}
+
+/*
+ * osevent hook and callback machinery
+ */
+
+void libxl_osevent_register_hooks(libxl_ctx *ctx,
+ const libxl_osevent_hooks *hooks,
+ void *user)
+{
+ GC_INIT(ctx);
+ CTX_LOCK;
+ assert(LIBXL_LIST_EMPTY(&ctx->efds));
+ assert(LIBXL_TAILQ_EMPTY(&ctx->etimes));
+ ctx->osevent_hooks = hooks;
+ ctx->osevent_user = user;
+ CTX_UNLOCK;
+ GC_FREE;
+}
+
+
+void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl,
+ int fd, short events_ign, short revents_ign)
+{
+ EGC_INIT(ctx);
+ CTX_LOCK;
+ assert(!CTX->osevent_in_hook);
+
+ libxl__ev_fd *ev = osevent_ev_from_hook_nexus(ctx, for_libxl);
+ if (!ev) goto out;
+ if (ev->fd != fd) goto out;
+
+ fd_occurs(egc, ev, revents_ign);
+
+ out:
+ CTX_UNLOCK_EGC_FREE;
+}
+
+void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl)
+{
+ EGC_INIT(ctx);
+ CTX_LOCK;
+ assert(!CTX->osevent_in_hook);
+
+ libxl__osevent_hook_nexus *nexus = for_libxl;
+ libxl__ev_time *ev = osevent_ev_from_hook_nexus(ctx, nexus);
+
+ osevent_release_nexus(gc, &CTX->hook_timeout_nexi_idle, nexus);
+
+ if (!ev) goto out;
+ assert(!ev->infinite);
+
+ LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry);
+
+ time_occurs(egc, ev, ERROR_TIMEDOUT);
+
+ out:
+ CTX_UNLOCK_EGC_FREE;
+}
+
+void libxl__event_disaster(libxl__gc *gc, const char *msg, int errnoval,
+ libxl_event_type type /* may be 0 */,
+ const char *file, int line, const char *func)
+{
+ libxl__log(CTX, XTL_CRITICAL, errnoval, file, line, func, INVALID_DOMID,
+ "DISASTER in event loop: %s%s%s%s",
+ msg,
+ type ? " (relates to event type " : "",
+ type ? libxl_event_type_to_string(type) : "",
+ type ? ")" : "");
+
+ if (CTX->event_hooks && CTX->event_hooks->disaster) {
+ CTX->event_hooks->disaster(CTX->event_hooks_user, type, msg, errnoval);
+ return;
+ }
+
+ const char verybad[] =
+ "DISASTER in event loop not handled by libxl application";
+ LIBXL__LOG(CTX, XTL_CRITICAL, verybad);
+ fprintf(stderr, "libxl: fatal error, exiting program: %s\n", verybad);
+ exit(-1);
+}
+
+static void egc_run_callbacks(libxl__egc *egc)
+{
+ /*
+ * The callbacks must happen with the ctx unlocked. See the
+ * comment near #define EGC_GC in libxl_internal.h and those in
+ * the definitions of libxl__egc, libxl__ao and libxl__aop.
+ */
+ EGC_GC;
+ libxl_event *ev, *ev_tmp;
+ libxl__aop_occurred *aop, *aop_tmp;
+ libxl__ev_immediate *ei;
+
+ while (!LIBXL_STAILQ_EMPTY(&egc->ev_immediates)) {
+ ei = LIBXL_STAILQ_FIRST(&egc->ev_immediates);
+ LIBXL_STAILQ_REMOVE_HEAD(&egc->ev_immediates, entry);
+ CTX_LOCK;
+ /* This callback is internal to libxl and expects CTX to be
+ * locked. */
+ ei->callback(egc, ei);
+ CTX_UNLOCK;
+ }
+
+ LIBXL_TAILQ_FOREACH_SAFE(ev, &egc->occurred_for_callback, link, ev_tmp) {
+ LIBXL_TAILQ_REMOVE(&egc->occurred_for_callback, ev, link);
+ LOG(DEBUG,"event %p callback type=%s",
+ ev, libxl_event_type_to_string(ev->type));
+ CTX->event_hooks->event_occurs(CTX->event_hooks_user, ev);
+ }
+
+ LIBXL_TAILQ_FOREACH_SAFE(aop, &egc->aops_for_callback, entry, aop_tmp) {
+ LIBXL_TAILQ_REMOVE(&egc->aops_for_callback, aop, entry);
+ LOG(DEBUG,"ao %p: progress report: callback aop=%p", aop->ao, aop);
+ aop->how->callback(CTX, aop->ev, aop->how->for_callback);
+
+ CTX_LOCK;
+ assert(aop->ao->magic == LIBXL__AO_MAGIC);
+ aop->ao->progress_reports_outstanding--;
+ libxl__ao_complete_check_progress_reports(egc, aop->ao);
+ CTX_UNLOCK;
+ }
+
+ libxl__ao *ao, *ao_tmp;
+ LIBXL_TAILQ_FOREACH_SAFE(ao, &egc->aos_for_callback,
+ entry_for_callback, ao_tmp) {
+ LIBXL_TAILQ_REMOVE(&egc->aos_for_callback, ao, entry_for_callback);
+ LOG(DEBUG,"ao %p: completion callback", ao);
+ ao->how.callback(CTX, ao->rc, ao->how.u.for_callback);
+ CTX_LOCK;
+ ao->notified = 1;
+ ao__check_destroy(CTX, ao);
+ CTX_UNLOCK;
+ }
+}
+
+void libxl__egc_cleanup_2_ul_cb_gc(libxl__egc *egc)
+{
+ EGC_GC;
+ egc_run_callbacks(egc);
+
+ libxl__free_all(gc);
+}
+
+/*
+ * Event retrieval etc.
+ */
+
+void libxl_event_register_callbacks(libxl_ctx *ctx,
+ const libxl_event_hooks *hooks, void *user)
+{
+ ctx->event_hooks = hooks;
+ ctx->event_hooks_user = user;
+}
+
+void libxl__event_occurred(libxl__egc *egc, libxl_event *event)
+{
+ EGC_GC;
+
+ if (CTX->event_hooks &&
+ (CTX->event_hooks->event_occurs_mask & (1UL << event->type))) {
+ /* libxl__egc_cleanup will call the callback, just before exit
+ * from libxl. This helps avoid reentrancy bugs: parts of
+ * libxl that call libxl__event_occurred do not have to worry
+ * that libxl might be reentered at that point. */
+ LIBXL_TAILQ_INSERT_TAIL(&egc->occurred_for_callback, event, link);
+ return;
+ } else {
+ libxl__poller *poller;
+ LIBXL_TAILQ_INSERT_TAIL(&CTX->occurred, event, link);
+ LIBXL_LIST_FOREACH(poller, &CTX->pollers_event, entry)
+ libxl__poller_wakeup(gc, poller);
+ }
+}
+
+void libxl_event_free(libxl_ctx *ctx, libxl_event *event)
+{
+ /* Exceptionally, this function may be called from libxl, with ctx==0 */
+ libxl_event_dispose(event);
+ free(event);
+}
+
+libxl_event *libxl__event_new(libxl__egc *egc,
+ libxl_event_type type, uint32_t domid,
+ libxl_ev_user for_user)
+{
+ EGC_GC;
+ libxl_event *ev;
+
+ ev = libxl__zalloc(NOGC,sizeof(*ev));
+
+ libxl_event_init(ev);
+ libxl_event_init_type(ev, type);
+
+ ev->domid = domid;
+ ev->for_user = for_user;
+
+ return ev;
+}
+
+static int event_check_internal(libxl__egc *egc, libxl_event **event_r,
+ unsigned long typemask,
+ libxl_event_predicate *pred, void *pred_user)
+{
+ EGC_GC;
+ libxl_event *ev;
+ int rc;
+
+ LIBXL_TAILQ_FOREACH(ev, &CTX->occurred, link) {
+ if (!(typemask & ((uint64_t)1 << ev->type)))
+ continue;
+
+ if (pred && !pred(ev, pred_user))
+ continue;
+
+ /* got one! */
+ LIBXL_TAILQ_REMOVE(&CTX->occurred, ev, link);
+ *event_r = ev;
+ rc = 0;
+ goto out;
+ }
+ rc = ERROR_NOT_READY;
+
+ out:
+ return rc;
+}
+
+int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r,
+ uint64_t typemask,
+ libxl_event_predicate *pred, void *pred_user)
+{
+ EGC_INIT(ctx);
+ CTX_LOCK;
+ int rc = event_check_internal(egc, event_r, typemask, pred, pred_user);
+ CTX_UNLOCK_EGC_FREE;
+ return rc;
+}
+
+/*
+ * Utilities for pipes (specifically, useful for self-pipes)
+ */
+
+void libxl__pipe_close(int fds[2])
+{
+ if (fds[0] >= 0) close(fds[0]);
+ if (fds[1] >= 0) close(fds[1]);
+ fds[0] = fds[1] = -1;
+}
+
+int libxl__pipe_nonblock(libxl_ctx *ctx, int fds[2])
+{
+ int r, rc;
+
+ r = libxl_pipe(ctx, fds);
+ if (r) {
+ fds[0] = fds[1] = -1;
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl_fd_set_nonblock(ctx, fds[0], 1);
+ if (rc) goto out;
+
+ rc = libxl_fd_set_nonblock(ctx, fds[1], 1);
+ if (rc) goto out;
+
+ return 0;
+
+ out:
+ libxl__pipe_close(fds);
+ return rc;
+}
+
+int libxl__self_pipe_wakeup(int fd)
+{
+ /* Called from signal handlers, so needs to be async-signal-safe */
+ static const char buf[1] = "";
+
+ for (;;) {
+ int r = write(fd, buf, 1);
+ if (r==1) return 0;
+ assert(r==-1);
+ if (errno == EINTR) continue;
+ if (errno == EWOULDBLOCK) return 0;
+ if (!errno) abort();
+ return errno;
+ }
+}
+
+int libxl__self_pipe_eatall(int fd)
+{
+ char buf[256];
+ for (;;) {
+ int r = read(fd, buf, sizeof(buf));
+ if (r == sizeof(buf)) continue;
+ if (r >= 0) return 0;
+ assert(r == -1);
+ if (errno == EINTR) continue;
+ if (errno == EWOULDBLOCK) return 0;
+ assert(errno);
+ return errno;
+ }
+}
+
+/*
+ * Manipulation of pollers
+ */
+
+int libxl__poller_init(libxl__gc *gc, libxl__poller *p)
+{
+ int rc;
+ p->fd_polls = 0;
+ p->fd_rindices = 0;
+ p->fds_deregistered = 0;
+
+ rc = libxl__pipe_nonblock(CTX, p->wakeup_pipe);
+ if (rc) goto out;
+
+ return 0;
+
+ out:
+ libxl__poller_dispose(p);
+ return rc;
+}
+
+void libxl__poller_dispose(libxl__poller *p)
+{
+ libxl__pipe_close(p->wakeup_pipe);
+ free(p->fd_polls);
+ free(p->fd_rindices);
+}
+
+libxl__poller *libxl__poller_get(libxl__gc *gc)
+{
+ /* must be called with ctx locked */
+ int rc;
+
+ libxl__poller *p = LIBXL_LIST_FIRST(&CTX->pollers_idle);
+ if (p) {
+ LIBXL_LIST_REMOVE(p, entry);
+ } else {
+ p = libxl__zalloc(NOGC, sizeof(*p));
+
+ rc = libxl__poller_init(gc, p);
+ if (rc) {
+ free(p);
+ return NULL;
+ }
+ }
+
+ LIBXL_LIST_INSERT_HEAD(&CTX->pollers_active, p,
+ active_entry);
+ return p;
+}
+
+void libxl__poller_put(libxl_ctx *ctx, libxl__poller *p)
+{
+ if (!p) return;
+ LIBXL_LIST_REMOVE(p, active_entry);
+ LIBXL_LIST_INSERT_HEAD(&ctx->pollers_idle, p, entry);
+}
+
+void libxl__poller_wakeup(libxl__gc *gc, libxl__poller *p)
+{
+ if (p->pipe_nonempty) return;
+ p->pipe_nonempty = 1;
+ int e = libxl__self_pipe_wakeup(p->wakeup_pipe[1]);
+ if (e) LIBXL__EVENT_DISASTER(gc, "cannot poke watch pipe", e, 0);
+}
+
+/*
+ * Main event loop iteration
+ */
+
+static int eventloop_iteration(libxl__egc *egc, libxl__poller *poller) {
+ /* The CTX must be locked EXACTLY ONCE so that this function
+ * can unlock it when it polls.
+ */
+ EGC_GC;
+ int rc, nfds;
+ struct timeval now;
+
+ rc = libxl__gettimeofday(gc, &now);
+ if (rc) goto out;
+
+ int timeout;
+
+ for (;;) {
+ nfds = poller->fd_polls_allocd;
+ timeout = -1;
+ rc = beforepoll_internal(gc, poller, &nfds, poller->fd_polls,
+ &timeout, now);
+ if (!rc) break;
+ if (rc != ERROR_BUFFERFULL) goto out;
+
+ struct pollfd *newarray =
+ (nfds > INT_MAX / sizeof(struct pollfd) / 2) ? 0 :
+ libxl__realloc(NOGC, poller->fd_polls, sizeof(*newarray) * nfds);
+
+ if (!newarray) { rc = ERROR_NOMEM; goto out; }
+
+ poller->fd_polls = newarray;
+ poller->fd_polls_allocd = nfds;
+ }
+
+ CTX_UNLOCK;
+ rc = poll(poller->fd_polls, nfds, timeout);
+ CTX_LOCK;
+
+ if (rc < 0) {
+ if (errno == EINTR)
+ return 0; /* will go round again if caller requires */
+
+ LOGEV(ERROR, errno, "poll failed");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__gettimeofday(gc, &now);
+ if (rc) goto out;
+
+ afterpoll_internal(egc, poller, nfds, poller->fd_polls, now);
+
+ rc = 0;
+ out:
+ return rc;
+}
+
+int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r,
+ uint64_t typemask,
+ libxl_event_predicate *pred, void *pred_user)
+{
+ int rc;
+ libxl__poller *poller = NULL;
+
+ EGC_INIT(ctx);
+ CTX_LOCK;
+
+ poller = libxl__poller_get(gc);
+ if (!poller) { rc = ERROR_FAIL; goto out; }
+
+ for (;;) {
+ rc = event_check_internal(egc, event_r, typemask, pred, pred_user);
+ if (rc != ERROR_NOT_READY) goto out;
+
+ rc = eventloop_iteration(egc, poller);
+ if (rc) goto out;
+
+ /* we unlock and cleanup the egc each time we go through this
+ * loop, so that (a) we don't accumulate garbage and (b) any
+ * events which are to be dispatched by callback are actually
+ * delivered in a timely fashion. _1_baton will be
+ * called to pass the baton iff we actually leave; otherwise
+ * we are still carrying it.
+ */
+ CTX_UNLOCK;
+ libxl__egc_cleanup_2_ul_cb_gc(egc);
+ CTX_LOCK;
+ }
+
+ out:
+ libxl__poller_put(ctx, poller);
+
+ CTX_UNLOCK_EGC_FREE;
+ return rc;
+}
+
+
+
+/*
+ * The two possible state flow of an ao:
+ *
+ * Completion before initiator return:
+ *
+ * Initiator thread Possible other threads
+ *
+ * * ao_create allocates memory and
+ * initialises the struct
+ *
+ * * the initiator function does its
+ * work, setting up various internal
+ * asynchronous operations -----------> * asynchronous operations
+ * start to take place and
+ * might cause ao completion
+ * |
+ * * initiator calls ao_inprogress |
+ * - if synchronous, run event loop |
+ * until the ao completes |
+ * - ao completes on some thread
+ * - completing thread releases the lock
+ * <--------------'
+ * - ao_inprogress takes the lock
+ * - destroy the ao
+ *
+ *
+ * Completion after initiator return (asynch. only):
+ *
+ *
+ * Initiator thread Possible other threads
+ *
+ * * ao_create allocates memory and
+ * initialises the struct
+ *
+ * * the initiator function does its
+ * work, setting up various internal
+ * asynchronous operations -----------> * asynchronous operations
+ * start to take place and
+ * might cause ao completion
+ * |
+ * * initiator calls ao_inprogress |
+ * - observes event not yet done, |
+ * - returns to caller |
+ * |
+ * - ao completes on some thread
+ * - generate the event or call the callback
+ * - destroy the ao
+ */
+
+
+/*
+ * A "manip" is a libxl public function manipulating this ao, which
+ * has a pointer to it. We have to not destroy it while that's the
+ * case, obviously. Callers must have the ctx locked, obviously.
+ */
+static void ao__manip_enter(libxl__ao *ao)
+{
+ assert(ao->manip_refcnt < INT_MAX);
+ ao->manip_refcnt++;
+}
+
+static void ao__manip_leave(libxl_ctx *ctx, libxl__ao *ao)
+{
+ assert(ao->manip_refcnt > 0);
+ ao->manip_refcnt--;
+ ao__check_destroy(ctx, ao);
+}
+
+static void ao__check_destroy(libxl_ctx *ctx, libxl__ao *ao)
+{
+ if (!ao->manip_refcnt && ao->notified) {
+ assert(ao->complete);
+ libxl__ao__destroy(ctx, ao);
+ }
+}
+
+void libxl__ao__destroy(libxl_ctx *ctx, libxl__ao *ao)
+{
+ AO_GC;
+ if (!ao) return;
+ LOG(DEBUG,"ao %p: destroy",ao);
+ libxl__poller_put(ctx, ao->poller);
+ ao->magic = LIBXL__AO_MAGIC_DESTROYED;
+ libxl__free_all(&ao->gc);
+ free(ao);
+}
+
+void libxl__ao_create_fail(libxl__ao *ao)
+{
+ AO_GC;
+ LOG(DEBUG,"ao %p: create fail",ao);
+ assert(ao->magic == LIBXL__AO_MAGIC);
+ assert(ao->in_initiator);
+ assert(!ao->complete);
+ assert(!ao->progress_reports_outstanding);
+ assert(!ao->aborting);
+ LIBXL_LIST_REMOVE(ao, inprogress_entry);
+ libxl__ao__destroy(CTX, ao);
+}
+
+libxl__gc *libxl__ao_inprogress_gc(libxl__ao *ao)
+{
+ assert(ao);
+ assert(ao->magic == LIBXL__AO_MAGIC);
+ assert(!ao->complete);
+ return &ao->gc;
+}
+
+void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc)
+{
+ AO_GC;
+ LOG(DEBUG,"ao %p: complete, rc=%d",ao,rc);
+ assert(ao->magic == LIBXL__AO_MAGIC);
+ assert(!ao->complete);
+ assert(!ao->nested_root);
+ assert(!ao->nested_progeny);
+ ao->complete = 1;
+ ao->rc = rc;
+ LIBXL_LIST_REMOVE(ao, inprogress_entry);
+ if (ao->outstanding_killed_child)
+ LOG(DEBUG, "ao %p: .. but waiting for %d fork to exit",
+ ao, ao->outstanding_killed_child);
+ libxl__ao_complete_check_progress_reports(egc, ao);
+}
+
+static bool ao_work_outstanding(libxl__ao *ao)
+{
+ /*
+ * We don't consider an ao complete if it has any outstanding
+ * callbacks. These callbacks might be outstanding on other
+ * threads, queued up in the other threads' egc's. Those threads
+ * will, after making the callback, take out the lock again,
+ * decrement progress_reports_outstanding, and call
+ * libxl__ao_complete_check_progress_reports.
+ */
+ return !ao->complete || ao->progress_reports_outstanding
+ || ao->outstanding_killed_child;
+}
+
+void libxl__ao_complete_check_progress_reports(libxl__egc *egc, libxl__ao *ao)
+{
+ EGC_GC;
+ libxl_ctx *ctx = libxl__gc_owner(&egc->gc);
+ assert(ao->progress_reports_outstanding >= 0);
+
+ if (ao_work_outstanding(ao))
+ return;
+
+ if (ao->poller) {
+ assert(ao->in_initiator);
+ if (!ao->constructing)
+ /* don't bother with this if we're not in the event loop */
+ libxl__poller_wakeup(gc, ao->poller);
+ } else if (ao->how.callback) {
+ LOG(DEBUG, "ao %p: complete for callback", ao);
+ LIBXL_TAILQ_INSERT_TAIL(&egc->aos_for_callback, ao, entry_for_callback);
+ } else {
+ libxl_event *ev;
+ ev = NEW_EVENT(egc, OPERATION_COMPLETE, ao->domid, ao->how.u.for_event);
+ if (ev) {
+ ev->u.operation_complete.rc = ao->rc;
+ libxl__event_occurred(egc, ev);
+ }
+ ao->notified = 1;
+ }
+
+ ao__check_destroy(ctx, ao);
+}
+
+libxl__ao *libxl__ao_create(libxl_ctx *ctx, uint32_t domid,
+ const libxl_asyncop_how *how,
+ const char *file, int line, const char *func)
+{
+ libxl__ao *ao;
+
+ ao = calloc(1, sizeof(*ao));
+ if (!ao) goto out;
+
+ ao->magic = LIBXL__AO_MAGIC;
+ ao->constructing = 1;
+ ao->in_initiator = 1;
+ ao__manip_enter(ao);
+ ao->poller = 0;
+ ao->domid = domid;
+ LIBXL_INIT_GC(ao->gc, ctx);
+
+ if (how) {
+ ao->how = *how;
+ } else {
+ ao->poller = libxl__poller_get(&ao->gc);
+ if (!ao->poller) goto out;
+ }
+ libxl__log(ctx,XTL_DEBUG,-1,file,line,func,domid,
+ "ao %p: create: how=%p callback=%p poller=%p",
+ ao, how, ao->how.callback, ao->poller);
+
+ LIBXL_LIST_INSERT_HEAD(&ctx->aos_inprogress, ao, inprogress_entry);
+
+ return ao;
+
+ out:
+ if (ao) libxl__ao__destroy(ctx, ao);
+ return NULL;
+}
+
+
+int libxl__ao_inprogress(libxl__ao *ao,
+ const char *file, int line, const char *func)
+{
+ AO_GC;
+ int rc;
+ uint32_t domid = ao->domid;
+
+ assert(ao->magic == LIBXL__AO_MAGIC);
+ assert(ao->constructing);
+ assert(ao->in_initiator);
+ ao->constructing = 0;
+
+ if (ao->nested_root)
+ domid = ao->nested_root->domid;
+
+ libxl__log(CTX,XTL_DEBUG,-1,file,line,func,domid,
+ "ao %p: inprogress: poller=%p, flags=%s%s%s%s",
+ ao, ao->poller,
+ ao->constructing ? "o" : "",
+ ao->in_initiator ? "i" : "",
+ ao->complete ? "c" : "",
+ ao->notified ? "n" : "");
+
+ if (ao->poller) {
+ /* Caller wants it done synchronously. */
+ /* We use a fresh gc, so that we can free things
+ * each time round the loop. */
+ libxl__egc egc;
+ LIBXL_INIT_EGC(egc,CTX);
+
+ for (;;) {
+ assert(ao->magic == LIBXL__AO_MAGIC);
+
+ if (!ao_work_outstanding(ao)) {
+ rc = ao->rc;
+ ao->notified = 1;
+ break;
+ }
+
+ DBG("ao %p: not ready, waiting",ao);
+
+ rc = eventloop_iteration(&egc,ao->poller);
+ if (rc) {
+ /* Oh dear, this is quite unfortunate. */
+ LOG(ERROR,
+ "Error waiting for"" event during long-running operation (rc=%d)",
+ rc);
+ sleep(1);
+ /* It's either this or return ERROR_I_DONT_KNOW_WHETHER
+ * _THE_THING_YOU_ASKED_FOR_WILL_BE_DONE_LATER_WHEN
+ * _YOU_DIDNT_EXPECT_IT, since we don't have a
+ * synchronous cancellation ability. */
+ }
+
+ /* The call to egc..1_baton is below, only if we are leaving. */
+ CTX_UNLOCK;
+ libxl__egc_cleanup_2_ul_cb_gc(&egc);
+ CTX_LOCK;
+ }
+
+ /* Dispose of this early so libxl__egc_ao_cleanup_1_baton
+ * doesn't mistake us for a baton-holder. No-one much is
+ * going to look at this ao now so setting this to 0 is fine.
+ * We can't call _baton below _leave because _leave destroys
+ * our gc, which _baton needs. */
+ libxl__poller_put(CTX, ao->poller);
+ ao->poller = 0;
+ } else {
+ rc = 0;
+ }
+
+ libxl__egc_ao_cleanup_1_baton(gc);
+ ao->in_initiator = 0;
+ ao__manip_leave(CTX, ao);
+
+ return rc;
+}
+
+
+/* abort requests */
+
+static int ao__abort(libxl_ctx *ctx, libxl__ao *parent)
+/* Temporarily unlocks ctx, which must be locked exactly once on entry. */
+{
+ libxl__egc egc;
+ LIBXL_INIT_EGC(egc,ctx);
+
+ int rc;
+ ao__manip_enter(parent);
+
+ if (parent->aborting) {
+ rc = ERROR_ABORTED;
+ goto out;
+ }
+
+ parent->aborting = 1;
+
+ if (LIBXL_LIST_EMPTY(&parent->abortables)) {
+ LIBXL__LOG(ctx, LIBXL__LOG_DEBUG,
+ "ao %p: abort requested and noted, but no-one interested",
+ parent);
+ rc = 0;
+ goto out;
+ }
+
+ /* We keep calling abort hooks until there are none left */
+ while (!LIBXL_LIST_EMPTY(&parent->abortables)) {
+ assert(!parent->complete);
+
+ libxl__ao_abortable *abrt = LIBXL_LIST_FIRST(&parent->abortables);
+ assert(parent == ao_nested_root(abrt->ao));
+
+ LIBXL_LIST_REMOVE(abrt, entry);
+ abrt->registered = 0;
+
+ LIBXL__LOG(ctx, LIBXL__LOG_DEBUG,
+ "ao %p: abrt=%p: aborting", parent, abrt->ao);
+ abrt->callback(&egc, abrt, ERROR_ABORTED);
+
+ /* The call to egc..1_baton is in the out block below. */
+ libxl__ctx_unlock(ctx);
+ libxl__egc_cleanup_2_ul_cb_gc(&egc);
+ libxl__ctx_lock(ctx);
+ }
+
+ rc = 0;
+
+ out:
+ libxl__egc_ao_cleanup_1_baton(&egc.gc);
+ ao__manip_leave(ctx, parent);
+ /* The call to egc..2_ul_cb_gc is above. This is sufficient
+ * because only code inside the loop adds anything to the egc, and
+ * we ensures that the egc is clean when we leave the loop. */
+ return rc;
+}
+
+int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how)
+{
+ libxl__ao *search;
+ libxl__ctx_lock(ctx);
+ int rc;
+
+ LIBXL_LIST_FOREACH(search, &ctx->aos_inprogress, inprogress_entry) {
+ if (how) {
+ /* looking for ao to be reported by callback or event */
+ if (search->poller)
+ /* sync */
+ continue;
+ if (how->callback != search->how.callback)
+ continue;
+ if (how->callback
+ ? (how->u.for_callback != search->how.u.for_callback)
+ : (how->u.for_event != search->how.u.for_event))
+ continue;
+ } else {
+ /* looking for synchronous call */
+ if (!search->poller)
+ /* async */
+ continue;
+ }
+ goto found;
+ }
+ rc = ERROR_NOTFOUND;
+ goto out;
+
+ found:
+ rc = ao__abort(ctx, search);
+ out:
+ libxl__ctx_unlock(ctx);
+ return rc;
+}
+
+int libxl__ao_aborting(libxl__ao *ao)
+{
+ libxl__ao *root = ao_nested_root(ao);
+ AO_GC;
+
+ if (root->aborting) {
+ DBG("ao=%p: aborting at explicit check (root=%p)", ao, root);
+ return ERROR_ABORTED;
+ }
+
+ return 0;
+}
+
+int libxl__ao_abortable_register(libxl__ao_abortable *abrt)
+{
+ libxl__ao *ao = abrt->ao;
+ libxl__ao *root = ao_nested_root(ao);
+ AO_GC;
+
+ if (root->aborting) {
+ DBG("ao=%p: preemptively aborting ao_abortable registration %p (root=%p)",
+ ao, abrt, root);
+ return ERROR_ABORTED;
+ }
+
+ DBG("ao=%p, abrt=%p: registering (root=%p)", ao, abrt, root);
+ LIBXL_LIST_INSERT_HEAD(&root->abortables, abrt, entry);
+ abrt->registered = 1;
+
+ return 0;
+}
+
+_hidden void libxl__ao_abortable_deregister(libxl__ao_abortable *abrt)
+{
+ if (!abrt->registered)
+ return;
+
+ libxl__ao *ao = abrt->ao;
+ libxl__ao *root __attribute__((unused)) = ao_nested_root(ao);
+ AO_GC;
+
+ DBG("ao=%p, abrt=%p: deregistering (root=%p)", ao, abrt, root);
+ LIBXL_LIST_REMOVE(abrt, entry);
+ abrt->registered = 0;
+}
+
+
+/* progress reporting */
+
+/* The application indicates a desire to ignore events by passing NULL
+ * for how. But we want to copy *how. So we have this dummy function
+ * whose address is stored in callback if the app passed how==NULL. */
+static void dummy_asyncprogress_callback_ignore
+ (libxl_ctx *ctx, libxl_event *ev, void *for_callback) { }
+
+void libxl__ao_progress_gethow(libxl_asyncprogress_how *in_state,
+ const libxl_asyncprogress_how *from_app) {
+ if (from_app)
+ *in_state = *from_app;
+ else
+ in_state->callback = dummy_asyncprogress_callback_ignore;
+}
+
+void libxl__ao_progress_report(libxl__egc *egc, libxl__ao *ao,
+ const libxl_asyncprogress_how *how, libxl_event *ev)
+{
+ AO_GC;
+ assert(!ao->nested_root);
+ if (how->callback == dummy_asyncprogress_callback_ignore) {
+ LOG(DEBUG,"ao %p: progress report: ignored",ao);
+ libxl_event_free(CTX,ev);
+ /* ignore */
+ } else if (how->callback) {
+ libxl__aop_occurred *aop = libxl__zalloc(&egc->gc, sizeof(*aop));
+ ao->progress_reports_outstanding++;
+ aop->ao = ao;
+ aop->ev = ev;
+ aop->how = how;
+ LIBXL_TAILQ_INSERT_TAIL(&egc->aops_for_callback, aop, entry);
+ LOG(DEBUG,"ao %p: progress report: callback queued aop=%p",ao,aop);
+ } else {
+ LOG(DEBUG,"ao %p: progress report: event queued ev=%p type=%s",
+ ao, ev, libxl_event_type_to_string(ev->type));
+ libxl__event_occurred(egc, ev);
+ }
+}
+
+
+/* nested ao */
+
+static libxl__ao *ao_nested_root(libxl__ao *ao) {
+ libxl__ao *root = ao->nested_root ? : ao;
+ assert(!root->nested_root);
+ return root;
+}
+
+_hidden libxl__ao *libxl__nested_ao_create(libxl__ao *parent)
+{
+ libxl__ao *child = NULL, *root;
+ libxl_ctx *ctx = libxl__gc_owner(&parent->gc);
+
+ assert(parent->magic == LIBXL__AO_MAGIC);
+ root = ao_nested_root(parent);
+
+ child = libxl__zalloc(&ctx->nogc_gc, sizeof(*child));
+ child->magic = LIBXL__AO_MAGIC;
+ child->nested_root = root;
+ assert(root->nested_progeny < INT_MAX);
+ root->nested_progeny++;
+ LIBXL_INIT_GC(child->gc, ctx);
+ libxl__gc *gc = &child->gc;
+
+ LOG(DEBUG,"ao %p: nested ao, parent %p", child, parent);
+ return child;
+}
+
+_hidden void libxl__nested_ao_free(libxl__ao *child)
+{
+ assert(child->magic == LIBXL__AO_MAGIC);
+ libxl__ao *root = child->nested_root;
+ assert(root);
+ assert(root->nested_progeny > 0);
+ root->nested_progeny--;
+ libxl_ctx *ctx = libxl__gc_owner(&child->gc);
+ libxl__ao__destroy(ctx, child);
+}
+
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+static void check_open_fds(const char *what)
+{
+ const char *env_debug;
+ int debug;
+ int i, flags, badness = 0;
+
+ env_debug = getenv("_LIBXL_DEBUG_EXEC_FDS");
+ if (!env_debug) return;
+
+ debug = strtol(env_debug, (char **) NULL, 10);
+ if (debug <= 0) return;
+
+ for (i = 4; i < 256; i++) {
+#ifdef __linux__
+ ssize_t len;
+ char path[PATH_MAX];
+ char linkpath[PATH_MAX+1];
+#endif
+ flags = fcntl(i, F_GETFD);
+ if ( flags == -1 ) {
+ if ( errno != EBADF )
+ fprintf(stderr, "libxl: execing %s: fd %d flags returned %s (%d)\n",
+ what, i, strerror(errno), errno);
+ continue;
+ }
+
+ if ( flags & FD_CLOEXEC )
+ continue;
+
+ badness++;
+
+#ifdef __linux__
+ snprintf(path, PATH_MAX, "/proc/%d/fd/%d", getpid(), i);
+ len = readlink(path, linkpath, PATH_MAX);
+ if (len > 0) {
+ linkpath[len] = '\0';
+ fprintf(stderr, "libxl: execing %s: fd %d is open to %s with flags %#x\n",
+ what, i, linkpath, flags);
+ } else
+#endif
+ fprintf(stderr, "libxl: execing %s: fd %d is open with flags %#x\n",
+ what, i, flags);
+ }
+ if (debug < 2) return;
+ if (badness) abort();
+}
+
+void libxl__exec(libxl__gc *gc, int stdinfd, int stdoutfd, int stderrfd,
+ const char *arg0, char *const args[], char *const env[])
+ /* call this in the child */
+{
+ if (stdinfd != -1)
+ dup2(stdinfd, STDIN_FILENO);
+ if (stdoutfd != -1)
+ dup2(stdoutfd, STDOUT_FILENO);
+ if (stderrfd != -1)
+ dup2(stderrfd, STDERR_FILENO);
+
+ if (stdinfd > 2)
+ close(stdinfd);
+ if (stdoutfd > 2 && stdoutfd != stdinfd)
+ close(stdoutfd);
+ if (stderrfd > 2 && stderrfd != stdinfd && stderrfd != stdoutfd)
+ close(stderrfd);
+
+ check_open_fds(arg0);
+
+ signal(SIGPIPE, SIG_DFL);
+ /* in case our caller set it to IGN. subprocesses are entitled
+ * to assume they got DFL. */
+
+ if (env != NULL) {
+ for (int i = 0; env[i] != NULL && env[i+1] != NULL; i += 2) {
+ if (setenv(env[i], env[i+1], 1) < 0) {
+ LOGEV(ERROR, errno, "setting env vars (%s = %s)",
+ env[i], env[i+1]);
+ goto out;
+ }
+ }
+ }
+ execvp(arg0, args);
+
+out:
+ fprintf(stderr, "libxl: cannot execute %s: %s\n", arg0, strerror(errno));
+ _exit(-1);
+}
+
+void libxl_report_child_exitstatus(libxl_ctx *ctx,
+ xentoollog_level level,
+ const char *what, pid_t pid, int status)
+{
+
+ if (WIFEXITED(status)) {
+ int st = WEXITSTATUS(status);
+ if (st)
+ LIBXL__LOG(ctx, level, "%s [%ld] exited"
+ " with error status %d", what, (unsigned long)pid, st);
+ else
+ LIBXL__LOG(ctx, level, "%s [%ld] unexpectedly"
+ " exited status zero", what, (unsigned long)pid);
+ } else if (WIFSIGNALED(status)) {
+ int sig = WTERMSIG(status);
+ const char *str = strsignal(sig);
+ const char *coredump = WCOREDUMP(status) ? " (core dumped)" : "";
+ if (str)
+ LIBXL__LOG(ctx, level, "%s [%ld] died due to"
+ " fatal signal %s%s", what, (unsigned long)pid,
+ str, coredump);
+ else
+ LIBXL__LOG(ctx, level, "%s [%ld] died due to unknown"
+ " fatal signal number %d%s", what, (unsigned long)pid,
+ sig, coredump);
+ } else {
+ LIBXL__LOG(ctx, level, "%s [%ld] gave unknown"
+ " wait status 0x%x", what, (unsigned long)pid, status);
+ }
+}
+
+int libxl__spawn_record_pid(libxl__gc *gc, libxl__spawn_state *spawn, pid_t pid)
+{
+ int r, rc;
+
+ rc = libxl__ev_child_xenstore_reopen(gc, spawn->what);
+ if (rc) goto out;
+
+ r = libxl__xs_printf(gc, XBT_NULL, spawn->pidpath, "%d", pid);
+ if (r) {
+ LOGE(ERROR,
+ "write %s = %d: xenstore write failed", spawn->pidpath, pid);
+ rc = ERROR_FAIL; goto out;
+ }
+
+ rc = 0;
+
+out:
+ return rc ? SIGTERM : 0;
+}
+
+int libxl__xenstore_child_wait_deprecated(libxl__gc *gc,
+ uint32_t domid,
+ uint32_t timeout, char *what,
+ char *path, char *state,
+ libxl__spawn_starting *spawning,
+ int (*check_callback)(libxl__gc *gc,
+ uint32_t domid,
+ const char *state,
+ void *userdata),
+ void *check_callback_userdata)
+{
+ char *p;
+ unsigned int len;
+ int rc = 0;
+ struct xs_handle *xsh;
+ int nfds;
+ fd_set rfds;
+ struct timeval tv;
+ unsigned int num;
+ char **l = NULL;
+
+ xsh = xs_daemon_open();
+ if (xsh == NULL) {
+ LOG(ERROR, "Unable to open xenstore connection");
+ goto err;
+ }
+
+ xs_watch(xsh, path, path);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ nfds = xs_fileno(xsh) + 1;
+ assert(!spawning);
+
+ while (rc > 0 || (!rc && tv.tv_sec > 0)) {
+ p = xs_read(xsh, XBT_NULL, path, &len);
+ if ( NULL == p )
+ goto again;
+
+ if ( NULL != state && strcmp(p, state) )
+ goto again;
+
+ if ( NULL != check_callback ) {
+ rc = (*check_callback)(gc, domid, p, check_callback_userdata);
+ if ( rc > 0 )
+ goto again;
+ }
+
+ free(p);
+ xs_unwatch(xsh, path, path);
+ xs_daemon_close(xsh);
+ return rc;
+again:
+ free(p);
+ FD_ZERO(&rfds);
+ FD_SET(xs_fileno(xsh), &rfds);
+ rc = select(nfds, &rfds, NULL, NULL, &tv);
+ if (rc > 0) {
+ if (FD_ISSET(xs_fileno(xsh), &rfds)) {
+ l = xs_read_watch(xsh, &num);
+ if (l != NULL)
+ free(l);
+ else
+ goto again;
+ }
+ }
+ }
+ LOG(ERROR, "%s not ready", what);
+
+ xs_unwatch(xsh, path, path);
+ xs_daemon_close(xsh);
+err:
+ return -1;
+}
+
+
+/*----- spawn implementation -----*/
+
+/*
+ * Full set of possible states of a libxl__spawn_state and its _detachable:
+ *
+ * detaching rc mid timeout xswatch
+ * - Undefined undef undef - undef undef
+ * - Idle any any Idle Idle Idle
+ * - Attached OK 0 0 Active Active Active
+ * - Attached Failed 0 non-0 Active Idle Idle
+ * - Detaching 1 maybe Active Idle Idle
+ * - Partial any any Idle Active/Idle Active/Idle
+ *
+ * When in states Detaching or Attached Failed, the middle process has
+ * been sent a SIGKILL.
+ *
+ * The difference between Attached OK and Attached Failed is not
+ * directly visible to callers - callers see these two the same,
+ * although of course Attached OK will hopefully eventually result in
+ * a call to detached_cb, whereas Attached Failed will end up
+ * in a call to failure_cb.
+ */
+
+/* Event callbacks. */
+static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
+ int rc, const char *xsdata);
+static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
+ pid_t pid, int status);
+
+/* Precondition: Partial. Results: Idle. */
+static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss);
+
+/* Precondition: Attached or Detaching; caller has logged failure reason.
+ * Results: Detaching, or Attached Failed */
+static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc);
+
+void libxl__spawn_init(libxl__spawn_state *ss)
+{
+ libxl__ev_child_init(&ss->mid);
+ libxl__xswait_init(&ss->xswait);
+}
+
+int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *ss)
+{
+ STATE_AO_GC(ss->ao);
+ int r;
+ pid_t child;
+ int status, rc;
+
+ libxl__spawn_init(ss);
+ ss->rc = ss->detaching = 0;
+
+ ss->xswait.ao = ao;
+ ss->xswait.what = GCSPRINTF("%s startup", ss->what);
+ ss->xswait.path = ss->xspath;
+ ss->xswait.timeout_ms = ss->timeout_ms;
+ ss->xswait.callback = spawn_watch_event;
+ rc = libxl__xswait_start(gc, &ss->xswait);
+ if (rc) goto out_err;
+
+ pid_t middle = libxl__ev_child_fork(gc, &ss->mid, spawn_middle_death);
+ if (middle ==-1) { rc = ERROR_FAIL; goto out_err; }
+
+ if (middle) {
+ /* parent */
+ return 1;
+ }
+
+ /* we are now the middle process */
+
+ pid_t (*fork_replacement)(void*) =
+ CTX->childproc_hooks
+ ? CTX->childproc_hooks->fork_replacement
+ : 0;
+ child =
+ fork_replacement
+ ? fork_replacement(CTX->childproc_user)
+ : fork();
+
+ if (child == -1)
+ exit(255);
+ if (!child) {
+ return 0; /* caller runs child code */
+ }
+
+ int failsig = ss->midproc_cb(gc, ss, child);
+ if (failsig) {
+ kill(child, failsig);
+ _exit(127);
+ }
+
+ for (;;) {
+ pid_t got = waitpid(child, &status, 0);
+ if (got == -1) {
+ assert(errno == EINTR);
+ continue;
+ }
+ assert(got == child);
+ break;
+ }
+
+ r = (WIFEXITED(status) && WEXITSTATUS(status) <= 127 ? WEXITSTATUS(status) :
+ WIFSIGNALED(status) && WTERMSIG(status) < 127 ? WTERMSIG(status)+128 :
+ -1);
+ _exit(r);
+
+ out_err:
+ spawn_cleanup(gc, ss);
+ return rc;
+}
+
+static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss)
+{
+ assert(!libxl__ev_child_inuse(&ss->mid));
+ libxl__xswait_stop(gc, &ss->xswait);
+}
+
+static void spawn_detach(libxl__gc *gc, libxl__spawn_state *ss)
+/* Precondition: Attached or Detaching, but caller must have just set
+ * at least one of detaching or rc.
+ * Results: Detaching or Attached Failed */
+{
+ int r;
+
+ assert(libxl__ev_child_inuse(&ss->mid));
+ assert(ss->detaching || ss->rc);
+ libxl__xswait_stop(gc, &ss->xswait);
+
+ pid_t child = ss->mid.pid;
+ r = kill(child, SIGKILL);
+ if (r && errno != ESRCH)
+ LOGE(WARN, "%s: failed to kill intermediate child (pid=%lu)",
+ ss->what, (unsigned long)child);
+}
+
+void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state *ss)
+{
+ ss->detaching = 1;
+ spawn_detach(gc, ss);
+}
+
+void libxl__spawn_initiate_failure(libxl__egc *egc, libxl__spawn_state *ss,
+ int rc)
+/* The spawn state must be Attached on entry and will be Attached Failed
+ * on return. */
+{
+ spawn_fail(egc, ss, rc);
+}
+
+static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc)
+/* Caller must have logged. Must be last thing in calling function,
+ * as it may make the callback. Precondition: Attached or Detaching. */
+{
+ EGC_GC;
+ assert(rc);
+ if (!ss->rc)
+ ss->rc = rc;
+ spawn_detach(gc, ss);
+}
+
+static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
+ int rc, const char *p)
+{
+ /* On entry, is Attached. */
+ EGC_GC;
+ libxl__spawn_state *ss = CONTAINER_OF(xswa, *ss, xswait);
+ if (rc) {
+ if (rc == ERROR_TIMEDOUT)
+ LOG(ERROR, "%s: startup timed out", ss->what);
+ spawn_fail(egc, ss, rc); /* must be last */
+ return;
+ }
+ LOG(DEBUG, "%s: spawn watch p=%s", ss->what, p);
+ ss->confirm_cb(egc, ss, p); /* must be last */
+}
+
+static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
+ pid_t pid, int status)
+ /* On entry, is Attached or Detaching */
+{
+ EGC_GC;
+ libxl__spawn_state *ss = CONTAINER_OF(childw, *ss, mid);
+
+ if ((ss->rc || ss->detaching) &&
+ ((WIFEXITED(status) && WEXITSTATUS(status)==0) ||
+ (WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL))) {
+ /* as expected */
+ const char *what = GCSPRINTF("%s (dying as expected)", ss->what);
+ libxl_report_child_exitstatus(CTX, XTL_DEBUG, what, pid, status);
+ } else if (!WIFEXITED(status)) {
+ int loglevel = ss->detaching ? XTL_WARN : XTL_ERROR;
+ const char *what =
+ GCSPRINTF("%s intermediate process (startup monitor)", ss->what);
+ libxl_report_child_exitstatus(CTX, loglevel, what, pid, status);
+ ss->rc = ERROR_FAIL;
+ } else {
+ if (!status)
+ LOG(ERROR, "%s [%ld]: unexpectedly exited with exit status 0,"
+ " when we were waiting for it to confirm startup",
+ ss->what, (unsigned long)pid);
+ else if (status <= 127)
+ LOG(ERROR, "%s [%ld]: failed startup with non-zero exit status %d",
+ ss->what, (unsigned long)pid, status);
+ else if (status < 255) {
+ int sig = status - 128;
+ const char *str = strsignal(sig);
+ if (str)
+ LOG(ERROR, "%s [%ld]: died during startup due to fatal"
+ " signal %s", ss->what, (unsigned long)pid, str);
+ else
+ LOG(ERROR, "%s [%ld]: died during startup due to unknown fatal"
+ " signal number %d", ss->what, (unsigned long)pid, sig);
+ }
+ ss->rc = ERROR_FAIL;
+ }
+
+ spawn_cleanup(gc, ss);
+
+ if (ss->rc && !ss->detaching) {
+ ss->failure_cb(egc, ss, ss->rc); /* must be last */
+ return;
+ }
+
+ if (ss->rc && ss->detaching)
+ LOG(WARN,"%s underlying machinery seemed to fail (%d),"
+ " but its function seems to have been successful",
+ ss->what, ss->rc);
+
+ assert(ss->detaching);
+ ss->detached_cb(egc, ss);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Author: Machon Gregory, <mbgrego@tycho.ncsc.mil>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+int libxl_flask_context_to_sid(libxl_ctx *ctx, char *buf, size_t len,
+ uint32_t *ssidref)
+{
+ int rc;
+
+ rc = xc_flask_context_to_sid(ctx->xch, buf, len, ssidref);
+
+ return rc;
+}
+
+int libxl_flask_sid_to_context(libxl_ctx *ctx, uint32_t ssidref,
+ char **buf, size_t *len)
+{
+ int rc;
+ char tmp[XC_PAGE_SIZE];
+
+ rc = xc_flask_sid_to_context(ctx->xch, ssidref, tmp, sizeof(tmp));
+
+ if (!rc) {
+ *len = strlen(tmp);
+ *buf = strdup(tmp);
+ }
+
+ return rc;
+}
+
+int libxl_flask_getenforce(libxl_ctx *ctx)
+{
+ int rc;
+
+ rc = xc_flask_getenforce(ctx->xch);
+
+ return rc;
+}
+
+int libxl_flask_setenforce(libxl_ctx *ctx, int mode)
+{
+ int rc;
+
+ rc = xc_flask_setenforce(ctx->xch, mode);
+
+ return rc;
+}
+
+int libxl_flask_loadpolicy(libxl_ctx *ctx, void *policy, uint32_t size)
+{
+
+ int rc;
+
+ rc = xc_flask_load(ctx->xch, policy, size);
+
+ return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2012 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+/*
+ * Internal child process machinery for use by other parts of libxl
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*
+ * carefd arrangements
+ *
+ * carefd_begin and _unlock take out the no_forking lock, which we
+ * also take and release in our pthread_atfork handlers. So when this
+ * lock is held the whole process cannot fork. We therefore protect
+ * our fds from leaking into children made by other threads.
+ *
+ * We maintain a list of all the carefds, so that if the application
+ * wants to fork a long-running but non-execing child, we can close
+ * them all.
+ *
+ * So the record function sets CLOEXEC for the benefit of execing
+ * children, and makes a note of the fd for the benefit of non-execing
+ * ones.
+ */
+
+struct libxl__carefd {
+ LIBXL_LIST_ENTRY(libxl__carefd) entry;
+ int fd;
+};
+
+static pthread_mutex_t no_forking = PTHREAD_MUTEX_INITIALIZER;
+static int atfork_registered;
+static LIBXL_LIST_HEAD(, libxl__carefd) carefds =
+ LIBXL_LIST_HEAD_INITIALIZER(carefds);
+
+/* Protected against concurrency by no_forking. sigchld_users is
+ * protected against being interrupted by SIGCHLD (and thus read
+ * asynchronously by the signal handler) by sigchld_defer (see
+ * below). */
+static bool sigchld_installed; /* 0 means not */
+static pthread_mutex_t sigchld_defer_mutex = PTHREAD_MUTEX_INITIALIZER;
+static LIBXL_LIST_HEAD(, libxl_ctx) sigchld_users =
+ LIBXL_LIST_HEAD_INITIALIZER(sigchld_users);
+static struct sigaction sigchld_saved_action;
+
+static void sigchld_removehandler_core(void); /* idempotent */
+static void sigchld_user_remove(libxl_ctx *ctx); /* idempotent */
+static void sigchld_sethandler_raw(void (*handler)(int), struct sigaction *old);
+
+static void defer_sigchld(void);
+static void release_sigchld(void);
+
+static void atfork_lock(void)
+{
+ int r = pthread_mutex_lock(&no_forking);
+ assert(!r);
+}
+
+static void atfork_unlock(void)
+{
+ int r = pthread_mutex_unlock(&no_forking);
+ assert(!r);
+}
+
+int libxl__atfork_init(libxl_ctx *ctx)
+{
+ int r, rc;
+
+ atfork_lock();
+ if (atfork_registered) { rc = 0; goto out; }
+
+ r = pthread_atfork(atfork_lock, atfork_unlock, atfork_unlock);
+ if (r) {
+ assert(r == ENOMEM);
+ libxl__alloc_failed(ctx, __func__, 0,0);
+ }
+
+ atfork_registered = 1;
+ rc = 0;
+ out:
+ atfork_unlock();
+ return rc;
+}
+
+void libxl__carefd_begin(void) { atfork_lock(); }
+void libxl__carefd_unlock(void) { atfork_unlock(); }
+
+libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd)
+{
+ libxl__carefd *cf = 0;
+
+ libxl_fd_set_cloexec(ctx, fd, 1);
+ cf = libxl__zalloc(&ctx->nogc_gc, sizeof(*cf));
+ cf->fd = fd;
+ LIBXL_LIST_INSERT_HEAD(&carefds, cf, entry);
+ return cf;
+}
+
+libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd)
+{
+ libxl__carefd *cf = 0;
+ int saved_errno = errno;
+
+ if (fd >= 0)
+ cf = libxl__carefd_record(ctx, fd);
+ libxl__carefd_unlock();
+ errno = saved_errno;
+ return cf;
+}
+
+void libxl_postfork_child_noexec(libxl_ctx *ctx)
+{
+ /*
+ * Anything running without the no_forking lock (atfork_lock)
+ * might be interrupted by fork. But libxl functions other than
+ * this one are then forbidden to the child.
+ *
+ * Conversely, this function might interrupt any other libxl
+ * operation (even though that other operation has the libxl ctx
+ * lock). We don't take the lock ourselves, since we are running
+ * in the child and if the lock is held the thread that took it no
+ * longer exists. To prevent us being interrupted by another call
+ * to ourselves (whether in another thread or by virtue of another
+ * fork) we take the atfork lock ourselves.
+ */
+ libxl__carefd *cf, *cf_tmp;
+ int r;
+
+ atfork_lock();
+
+ LIBXL_LIST_FOREACH_SAFE(cf, &carefds, entry, cf_tmp) {
+ if (cf->fd >= 0) {
+ r = close(cf->fd);
+ if (r)
+ LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_WARNING,
+ "failed to close fd=%d"
+ " (perhaps of another libxl ctx)", cf->fd);
+ }
+ free(cf);
+ }
+ LIBXL_LIST_INIT(&carefds);
+
+ if (sigchld_installed) {
+ /* We are in theory not at risk of concurrent execution of the
+ * SIGCHLD handler, because the application should call
+ * libxl_postfork_child_noexec before the child forks again.
+ * (If the SIGCHLD was in flight in the parent at the time of
+ * the fork, the thread it was delivered on exists only in the
+ * parent so is not our concern.)
+ *
+ * But in case the application violated this rule (and did so
+ * while multithreaded in the child), we use our deferral
+ * machinery. The result is that the SIGCHLD may then be lost
+ * (i.e. signaled to the now-defunct libxl ctx(s)). But at
+ * least we won't execute undefined behaviour (by examining
+ * the list in the signal handler concurrently with clearing
+ * it here), and since we won't actually reap the new children
+ * things will in fact go OK if the application doesn't try to
+ * use SIGCHLD, but instead just waits for the child(ren). */
+ defer_sigchld();
+
+ LIBXL_LIST_INIT(&sigchld_users);
+ /* After this the ->sigchld_user_registered entries in the
+ * now-obsolete contexts may be lies. But that's OK because
+ * no-one will look at them. */
+
+ release_sigchld();
+ sigchld_removehandler_core();
+ }
+
+ atfork_unlock();
+}
+
+int libxl__carefd_close(libxl__carefd *cf)
+{
+ if (!cf) return 0;
+ atfork_lock();
+ int r = cf->fd < 0 ? 0 : close(cf->fd);
+ int esave = errno;
+ LIBXL_LIST_REMOVE(cf, entry);
+ atfork_unlock();
+ free(cf);
+ errno = esave;
+ return r;
+}
+
+int libxl__carefd_fd(const libxl__carefd *cf)
+{
+ if (!cf) return -1;
+ return cf->fd;
+}
+
+/*
+ * Low-level functions for child process handling, including
+ * the main SIGCHLD handler.
+ */
+
+/* Like waitpid(,,WNOHANG) but handles all errors except ECHILD. */
+static pid_t checked_waitpid(libxl__egc *egc, pid_t want, int *status)
+{
+ EGC_GC;
+ for (;;) {
+ pid_t got = waitpid(want, status, WNOHANG);
+ if (got != -1)
+ return got;
+ if (errno == ECHILD)
+ return got;
+ if (errno == EINTR)
+ continue;
+ LIBXL__EVENT_DISASTER(gc, "waitpid() failed", errno, 0);
+ return 0;
+ }
+}
+
+static void sigchld_selfpipe_handler(libxl__egc *egc, libxl__ev_fd *ev,
+ int fd, short events, short revents);
+
+static void sigchld_handler(int signo)
+{
+ /* This function has to be reentrant! Luckily it is. */
+
+ libxl_ctx *notify;
+ int esave = errno;
+
+ int r = pthread_mutex_lock(&sigchld_defer_mutex);
+ assert(!r);
+
+ LIBXL_LIST_FOREACH(notify, &sigchld_users, sigchld_users_entry) {
+ int e = libxl__self_pipe_wakeup(notify->sigchld_selfpipe[1]);
+ if (e) abort(); /* errors are probably EBADF, very bad */
+ }
+
+ r = pthread_mutex_unlock(&sigchld_defer_mutex);
+ assert(!r);
+
+ errno = esave;
+}
+
+static void sigchld_sethandler_raw(void (*handler)(int), struct sigaction *old)
+{
+ struct sigaction ours;
+ int r;
+
+ memset(&ours,0,sizeof(ours));
+ ours.sa_handler = handler;
+ sigemptyset(&ours.sa_mask);
+ ours.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+ r = sigaction(SIGCHLD, &ours, old);
+ assert(!r);
+}
+
+/*
+ * SIGCHLD deferral
+ *
+ * sigchld_defer and sigchld_release are a bit like using sigprocmask
+ * to block the signal only they work for the whole process. Sadly
+ * this has to be done by setting a special handler that records the
+ * "pendingness" of the signal here in the program. How tedious.
+ *
+ * A property of this approach is that the signal handler itself
+ * must be reentrant (see the comment in release_sigchld).
+ *
+ * Callers have the atfork_lock so there is no risk of concurrency
+ * within these functions, aside from the risk of being interrupted by
+ * the signal. We use sigchld_defer_mutex to guard against the
+ * possibility of the real signal handler being still running on
+ * another thread.
+ */
+
+static volatile sig_atomic_t sigchld_occurred_while_deferred;
+
+static void sigchld_handler_when_deferred(int signo)
+{
+ sigchld_occurred_while_deferred = 1;
+}
+
+static void defer_sigchld(void)
+{
+ assert(sigchld_installed);
+
+ sigchld_sethandler_raw(sigchld_handler_when_deferred, 0);
+
+ /* Now _this thread_ cannot any longer be interrupted by the
+ * signal, so we can take the mutex without risk of deadlock. If
+ * another thread is in the signal handler, either it or we will
+ * block and wait for the other. */
+
+ int r = pthread_mutex_lock(&sigchld_defer_mutex);
+ assert(!r);
+}
+
+static void release_sigchld(void)
+{
+ assert(sigchld_installed);
+
+ int r = pthread_mutex_unlock(&sigchld_defer_mutex);
+ assert(!r);
+
+ sigchld_sethandler_raw(sigchld_handler, 0);
+ if (sigchld_occurred_while_deferred) {
+ sigchld_occurred_while_deferred = 0;
+ /* We might get another SIGCHLD here, in which case
+ * sigchld_handler will be interrupted and re-entered.
+ * This is OK. */
+ sigchld_handler(SIGCHLD);
+ }
+}
+
+/*
+ * Meat of the child process handling.
+ */
+
+static void sigchld_removehandler_core(void) /* idempotent */
+{
+ struct sigaction was;
+ int r;
+
+ if (!sigchld_installed)
+ return;
+
+ r = sigaction(SIGCHLD, &sigchld_saved_action, &was);
+ assert(!r);
+ assert(!(was.sa_flags & SA_SIGINFO));
+ assert(was.sa_handler == sigchld_handler);
+
+ sigchld_installed = 0;
+}
+
+static void sigchld_installhandler_core(void) /* idempotent */
+{
+ if (sigchld_installed)
+ return;
+
+ sigchld_installed = 1;
+
+ sigchld_sethandler_raw(sigchld_handler, &sigchld_saved_action);
+
+ assert(((void)"application must negotiate with libxl about SIGCHLD",
+ !(sigchld_saved_action.sa_flags & SA_SIGINFO) &&
+ (sigchld_saved_action.sa_handler == SIG_DFL ||
+ sigchld_saved_action.sa_handler == SIG_IGN)));
+}
+
+static void sigchld_user_remove(libxl_ctx *ctx) /* idempotent */
+{
+ if (!ctx->sigchld_user_registered)
+ return;
+
+ atfork_lock();
+ defer_sigchld();
+
+ LIBXL_LIST_REMOVE(ctx, sigchld_users_entry);
+
+ release_sigchld();
+
+ if (LIBXL_LIST_EMPTY(&sigchld_users))
+ sigchld_removehandler_core();
+
+ atfork_unlock();
+
+ ctx->sigchld_user_registered = 0;
+}
+
+void libxl__sigchld_notneeded(libxl__gc *gc) /* non-reentrant, idempotent */
+{
+ sigchld_user_remove(CTX);
+ libxl__ev_fd_deregister(gc, &CTX->sigchld_selfpipe_efd);
+}
+
+int libxl__sigchld_needed(libxl__gc *gc) /* non-reentrant, idempotent */
+{
+ int rc;
+
+ if (CTX->sigchld_selfpipe[0] < 0) {
+ rc = libxl__pipe_nonblock(CTX, CTX->sigchld_selfpipe);
+ if (rc) goto out;
+ }
+ if (!libxl__ev_fd_isregistered(&CTX->sigchld_selfpipe_efd)) {
+ rc = libxl__ev_fd_register(gc, &CTX->sigchld_selfpipe_efd,
+ sigchld_selfpipe_handler,
+ CTX->sigchld_selfpipe[0], POLLIN);
+ if (rc) goto out;
+ } else {
+ rc = libxl__ev_fd_modify(gc, &CTX->sigchld_selfpipe_efd, POLLIN);
+ if (rc) goto out;
+ }
+ if (!CTX->sigchld_user_registered) {
+ atfork_lock();
+
+ sigchld_installhandler_core();
+
+ defer_sigchld();
+
+ LIBXL_LIST_INSERT_HEAD(&sigchld_users, CTX, sigchld_users_entry);
+
+ release_sigchld();
+ atfork_unlock();
+
+ CTX->sigchld_user_registered = 1;
+ }
+
+ rc = 0;
+ out:
+ return rc;
+}
+
+static bool chldmode_ours(libxl_ctx *ctx, bool creating)
+{
+ switch (ctx->childproc_hooks->chldowner) {
+ case libxl_sigchld_owner_libxl:
+ return creating || !LIBXL_LIST_EMPTY(&ctx->children);
+ case libxl_sigchld_owner_mainloop:
+ return 0;
+ case libxl_sigchld_owner_libxl_always:
+ case libxl_sigchld_owner_libxl_always_selective_reap:
+ return 1;
+ }
+ abort();
+}
+
+static void perhaps_sigchld_notneeded(libxl__gc *gc)
+{
+ if (!chldmode_ours(CTX, 0))
+ libxl__sigchld_notneeded(gc);
+}
+
+static int perhaps_sigchld_needed(libxl__gc *gc, bool creating)
+{
+ int rc;
+
+ if (chldmode_ours(CTX, creating)) {
+ rc = libxl__sigchld_needed(gc);
+ if (rc) return rc;
+ }
+ return 0;
+}
+
+static void childproc_reaped_ours(libxl__egc *egc, libxl__ev_child *ch,
+ int status)
+{
+ pid_t pid = ch->pid;
+ LIBXL_LIST_REMOVE(ch, entry);
+ ch->pid = -1;
+ ch->callback(egc, ch, pid, status);
+}
+
+static int childproc_reaped(libxl__egc *egc, pid_t pid, int status)
+{
+ EGC_GC;
+ libxl__ev_child *ch;
+
+ LIBXL_LIST_FOREACH(ch, &CTX->children, entry)
+ if (ch->pid == pid)
+ goto found;
+
+ /* not found */
+ return ERROR_UNKNOWN_CHILD;
+
+ found:
+ childproc_reaped_ours(egc, ch, status);
+
+ perhaps_sigchld_notneeded(gc);
+
+ return 0;
+}
+
+int libxl_childproc_reaped(libxl_ctx *ctx, pid_t pid, int status)
+{
+ EGC_INIT(ctx);
+ CTX_LOCK;
+ assert(CTX->childproc_hooks->chldowner
+ == libxl_sigchld_owner_mainloop);
+ int rc = childproc_reaped(egc, pid, status);
+ CTX_UNLOCK_EGC_FREE;
+ return rc;
+}
+
+static void childproc_checkall(libxl__egc *egc)
+{
+ EGC_GC;
+ libxl__ev_child *ch;
+
+ for (;;) {
+ int status;
+ pid_t got;
+
+ LIBXL_LIST_FOREACH(ch, &CTX->children, entry) {
+ got = checked_waitpid(egc, ch->pid, &status);
+ if (got)
+ goto found;
+ }
+ /* not found */
+ return;
+
+ found:
+ if (got == -1) {
+ LIBXL__EVENT_DISASTER
+ (gc, "waitpid() gave ECHILD but we have a child",
+ ECHILD, 0);
+ /* it must have finished but we don't know its status */
+ status = 255<<8; /* no wait.h macro for this! */
+ assert(WIFEXITED(status));
+ assert(WEXITSTATUS(status)==255);
+ assert(!WIFSIGNALED(status));
+ assert(!WIFSTOPPED(status));
+ }
+ childproc_reaped_ours(egc, ch, status);
+ /* we need to restart the loop, as children may have been edited */
+ }
+}
+
+void libxl_childproc_sigchld_occurred(libxl_ctx *ctx)
+{
+ EGC_INIT(ctx);
+ CTX_LOCK;
+ assert(CTX->childproc_hooks->chldowner
+ == libxl_sigchld_owner_mainloop);
+ childproc_checkall(egc);
+ CTX_UNLOCK_EGC_FREE;
+}
+
+static void sigchld_selfpipe_handler(libxl__egc *egc, libxl__ev_fd *ev,
+ int fd, short events, short revents)
+{
+ /* May make callbacks into the application for child processes.
+ * So, this function may unlock and relock the CTX. This is OK
+ * because event callback functions are always called with the CTX
+ * locked exactly once, and from code which copes with reentrancy.
+ * (See also the comment in afterpoll_internal.) */
+ EGC_GC;
+
+ int selfpipe = CTX->sigchld_selfpipe[0];
+
+ if (revents & ~POLLIN) {
+ LOG(ERROR, "unexpected poll event 0x%x on SIGCHLD self pipe", revents);
+ LIBXL__EVENT_DISASTER(gc,
+ "unexpected poll event on SIGCHLD self pipe",
+ 0, 0);
+ }
+ assert(revents & POLLIN);
+
+ int e = libxl__self_pipe_eatall(selfpipe);
+ if (e) LIBXL__EVENT_DISASTER(gc, "read sigchld pipe", e, 0);
+
+ if (CTX->childproc_hooks->chldowner
+ == libxl_sigchld_owner_libxl_always_selective_reap) {
+ childproc_checkall(egc);
+ return;
+ }
+
+ while (chldmode_ours(CTX, 0) /* in case the app changes the mode */) {
+ int status;
+ pid_t pid = checked_waitpid(egc, -1, &status);
+
+ if (pid == 0 || pid == -1 /* ECHILD */)
+ return;
+
+ int rc = childproc_reaped(egc, pid, status);
+
+ if (rc) {
+ if (CTX->childproc_hooks->reaped_callback) {
+ CTX_UNLOCK;
+ rc = CTX->childproc_hooks->reaped_callback
+ (pid, status, CTX->childproc_user);
+ CTX_LOCK;
+ if (rc != 0 && rc != ERROR_UNKNOWN_CHILD) {
+ char disasterbuf[200];
+ snprintf(disasterbuf, sizeof(disasterbuf), " reported by"
+ " libxl_childproc_hooks->reaped_callback"
+ " (for pid=%lu, status=%d; error code %d)",
+ (unsigned long)pid, status, rc);
+ LIBXL__EVENT_DISASTER(gc, disasterbuf, 0, 0);
+ return;
+ }
+ } else {
+ rc = ERROR_UNKNOWN_CHILD;
+ }
+ if (rc)
+ libxl_report_child_exitstatus(CTX, XTL_WARN,
+ "unknown child", (long)pid, status);
+ }
+ }
+}
+
+pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *ch,
+ libxl__ev_child_callback *death)
+{
+ CTX_LOCK;
+ int rc;
+
+ perhaps_sigchld_needed(gc, 1);
+
+ pid_t pid =
+ CTX->childproc_hooks->fork_replacement
+ ? CTX->childproc_hooks->fork_replacement(CTX->childproc_user)
+ : fork();
+ if (pid == -1) {
+ LOGE(ERROR, "fork failed");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (!pid) {
+ /* woohoo! */
+ if (CTX->xsh) {
+ xs_daemon_destroy_postfork(CTX->xsh);
+ CTX->xsh = NULL; /* turns mistakes into crashes */
+ }
+ /* Yes, CTX is left locked in the child. */
+ return 0;
+ }
+
+ ch->pid = pid;
+ ch->callback = death;
+ LIBXL_LIST_INSERT_HEAD(&CTX->children, ch, entry);
+ rc = pid;
+
+ out:
+ perhaps_sigchld_notneeded(gc);
+ CTX_UNLOCK;
+ return rc;
+}
+
+void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks *hooks,
+ void *user)
+{
+ GC_INIT(ctx);
+ CTX_LOCK;
+
+ assert(LIBXL_LIST_EMPTY(&CTX->children));
+
+ if (!hooks)
+ hooks = &libxl__childproc_default_hooks;
+
+ ctx->childproc_hooks = hooks;
+ ctx->childproc_user = user;
+
+ perhaps_sigchld_notneeded(gc);
+ perhaps_sigchld_needed(gc, 0); /* idempotent, ok to ignore errors for now */
+
+ CTX_UNLOCK;
+ GC_FREE;
+}
+
+const libxl_childproc_hooks libxl__childproc_default_hooks = {
+ libxl_sigchld_owner_libxl, 0, 0
+};
+
+int libxl__ev_child_xenstore_reopen(libxl__gc *gc, const char *what) {
+ int rc;
+
+ assert(!CTX->xsh);
+ CTX->xsh = xs_daemon_open();
+ if (!CTX->xsh) {
+ LOGE(ERROR, "%s: xenstore reopen failed", what);
+ rc = ERROR_FAIL; goto out;
+ }
+
+ libxl_fd_set_cloexec(CTX, xs_fileno(CTX->xsh), 1);
+
+ return 0;
+
+ out:
+ return rc;
+}
+
+typedef struct ev_child_killed {
+ libxl__ao *ao;
+ libxl__ev_child ch;
+} ev_child_killed;
+static void deregistered_child_callback(libxl__egc *, libxl__ev_child *,
+ pid_t, int status);
+
+void libxl__ev_child_kill_deregister(libxl__ao *ao, libxl__ev_child *ch,
+ int sig)
+{
+ AO_GC;
+
+ if (!libxl__ev_child_inuse(ch))
+ return;
+
+ pid_t pid = ch->pid;
+
+ ev_child_killed *new_ch = GCNEW(new_ch);
+ new_ch->ao = ao;
+ new_ch->ch.pid = pid;
+ new_ch->ch.callback = deregistered_child_callback;
+ LIBXL_LIST_INSERT_HEAD(&CTX->children, &new_ch->ch, entry);
+ ao->outstanding_killed_child++;
+
+ LIBXL_LIST_REMOVE(ch, entry);
+ ch->pid = -1;
+ int r = kill(pid, sig);
+ if (r)
+ LOGED(ERROR, ao->domid,
+ "failed to kill child [%ld] with signal %d",
+ (unsigned long)pid, sig);
+}
+
+static void deregistered_child_callback(libxl__egc *egc,
+ libxl__ev_child *ch,
+ pid_t pid,
+ int status)
+{
+ ev_child_killed *ck = CONTAINER_OF(ch, *ck, ch);
+ EGC_GC;
+
+ libxl_report_child_exitstatus(CTX, XTL_ERROR,
+ "killed fork (dying as expected)",
+ pid, status);
+ ck->ao->outstanding_killed_child--;
+ libxl__ao_complete_check_progress_reports(egc, ck->ao);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2014
+ * Author Roger Pau Monne <roger.pau@entel.upc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+int libxl__try_phy_backend(mode_t st_mode)
+{
+ if (S_ISREG(st_mode) || S_ISBLK(st_mode) || S_ISCHR(st_mode))
+ return 1;
+
+ return 0;
+}
+
+char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
+{
+ /* This translation table has been copied from the FreeBSD blkfront code. */
+ const static struct vdev_info {
+ int major;
+ int shift;
+ int base;
+ const char *name;
+ } info[] = {
+ {3, 6, 0, "ada"}, /* ide0 */
+ {22, 6, 2, "ada"}, /* ide1 */
+ {33, 6, 4, "ada"}, /* ide2 */
+ {34, 6, 6, "ada"}, /* ide3 */
+ {56, 6, 8, "ada"}, /* ide4 */
+ {57, 6, 10, "ada"}, /* ide5 */
+ {88, 6, 12, "ada"}, /* ide6 */
+ {89, 6, 14, "ada"}, /* ide7 */
+ {90, 6, 16, "ada"}, /* ide8 */
+ {91, 6, 18, "ada"}, /* ide9 */
+
+ {8, 4, 0, "da"}, /* scsi disk0 */
+ {65, 4, 16, "da"}, /* scsi disk1 */
+ {66, 4, 32, "da"}, /* scsi disk2 */
+ {67, 4, 48, "da"}, /* scsi disk3 */
+ {68, 4, 64, "da"}, /* scsi disk4 */
+ {69, 4, 80, "da"}, /* scsi disk5 */
+ {70, 4, 96, "da"}, /* scsi disk6 */
+ {71, 4, 112, "da"}, /* scsi disk7 */
+ {128, 4, 128, "da"}, /* scsi disk8 */
+ {129, 4, 144, "da"}, /* scsi disk9 */
+ {130, 4, 160, "da"}, /* scsi disk10 */
+ {131, 4, 176, "da"}, /* scsi disk11 */
+ {132, 4, 192, "da"}, /* scsi disk12 */
+ {133, 4, 208, "da"}, /* scsi disk13 */
+ {134, 4, 224, "da"}, /* scsi disk14 */
+ {135, 4, 240, "da"}, /* scsi disk15 */
+
+ {202, 4, 0, "xbd"}, /* xbd */
+
+ {0, 0, 0, NULL},
+ };
+ int major = devid >> 8;
+ int minor = devid & 0xff;
+ int i;
+
+ if (devid & (1 << 28))
+ return GCSPRINTF("%s%d", "xbd", (devid & ((1 << 28) - 1)) >> 8);
+
+ for (i = 0; info[i].major; i++)
+ if (info[i].major == major)
+ return GCSPRINTF("%s%d", info[i].name,
+ info[i].base + (minor >> info[i].shift));
+
+ return GCSPRINTF("%s%d", "xbd", minor >> 4);
+}
+
+/* Hotplug scripts caller functions */
+static int libxl__hotplug_env_nic(libxl__gc *gc, libxl__device *dev, char ***env,
+ int num_exec)
+{
+ int nr = 0;
+ const int arraysize = 5;
+ libxl_nic_type type;
+
+ assert(dev->backend_kind == LIBXL__DEVICE_KIND_VIF);
+
+ /*
+ * On the first pass the PV interface is added to the bridge,
+ * on the second pass the tap interface will also be added.
+ */
+ type = num_exec == 0 ? LIBXL_NIC_TYPE_VIF : LIBXL_NIC_TYPE_VIF_IOEMU;
+
+ GCNEW_ARRAY(*env, arraysize);
+ (*env)[nr++] = "iface_dev";
+ (*env)[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
+ dev->devid, type);
+ (*env)[nr++] = "emulated";
+ (*env)[nr++] = type == LIBXL_NIC_TYPE_VIF_IOEMU ? "1" : "0";
+ (*env)[nr++] = NULL;
+ assert(nr == arraysize);
+
+ return 0;
+}
+
+static int libxl__hotplug_nic(libxl__gc *gc, libxl__device *dev,
+ char ***args, char ***env,
+ libxl__device_action action,
+ int num_exec)
+{
+ libxl_nic_type nictype;
+ char *be_path = libxl__device_backend_path(gc, dev);
+ char *script;
+ int nr = 0, rc;
+
+ rc = libxl__nic_type(gc, dev, &nictype);
+ if (rc) {
+ LOGD(ERROR, dev->domid, "error when fetching nic type");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /*
+ * For PV domains only one pass is needed (because there's no emulated
+ * interface). For HVM domains two passes are needed in order to add
+ * both the PV and the tap interfaces to the bridge.
+ */
+ if (nictype == LIBXL_NIC_TYPE_VIF && num_exec != 0) {
+ rc = 0;
+ goto out;
+ }
+
+ rc = libxl__hotplug_env_nic(gc, dev, env, num_exec);
+ if (rc)
+ goto out;
+
+ script = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/%s", be_path, "script"));
+ if (!script) {
+ LOGEVD(ERROR, errno, dev->domid,
+ "unable to read script from %s", be_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ const int arraysize = 4;
+ GCNEW_ARRAY(*args, arraysize);
+ (*args)[nr++] = script;
+ (*args)[nr++] = be_path;
+ (*args)[nr++] = (char *) libxl__device_action_to_string(action);
+ (*args)[nr++] = NULL;
+ assert(nr == arraysize);
+ rc = 1;
+
+out:
+ return rc;
+}
+
+static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev,
+ char ***args, char ***env,
+ libxl__device_action action)
+{
+ char *be_path = libxl__device_backend_path(gc, dev);
+ char *script;
+ int nr = 0, rc;
+
+ script = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/%s", be_path, "script"));
+ if (!script) {
+ LOGEVD(ERROR, errno, dev->domid,
+ "unable to read script from %s", be_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ const int arraysize = 4;
+ GCNEW_ARRAY(*args, arraysize);
+ (*args)[nr++] = script;
+ (*args)[nr++] = be_path;
+ (*args)[nr++] = (char *) libxl__device_action_to_string(action);
+ (*args)[nr++] = NULL;
+ assert(nr == arraysize);
+ rc = 1;
+
+out:
+ return rc;
+}
+
+int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
+ char ***args, char ***env,
+ libxl__device_action action,
+ int num_exec)
+{
+ int rc;
+
+ switch (dev->backend_kind) {
+ case LIBXL__DEVICE_KIND_VIF:
+ /*
+ * If domain has a stubdom we don't have to execute hotplug scripts
+ * for emulated interfaces
+ */
+ if ((num_exec > 1) ||
+ (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
+ rc = 0;
+ goto out;
+ }
+ rc = libxl__hotplug_nic(gc, dev, args, env, action, num_exec);
+ break;
+ case LIBXL__DEVICE_KIND_VBD:
+ if (num_exec != 0) {
+ rc = 0;
+ goto out;
+ }
+ rc = libxl__hotplug_disk(gc, dev, args, env, action);
+ break;
+ default:
+ /* No need to execute any hotplug scripts */
+ rc = 0;
+ break;
+ }
+
+out:
+ return rc;
+}
+
+libxl_device_model_version libxl__default_device_model(libxl__gc *gc)
+{
+ return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
+}
+
+int libxl__pci_numdevs(libxl__gc *gc)
+{
+ return ERROR_NI;
+}
+
+int libxl__pci_topology_init(libxl__gc *gc,
+ physdev_pci_device_t *devs,
+ int num_devs)
+{
+ return ERROR_NI;
+}
+
+int libxl__local_dm_preexec_restrict(libxl__gc *gc)
+{
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 Citrix Systems R&D Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#include <xenctrl.h>
+#include <xen/hvm/params.h>
+
+/*
+ * Generate a random VM generation ID.
+ *
+ * Returns ERROR_FAIL if a suitable source of random numbers is not
+ * available.
+ *
+ * See Microsoft's "Virtual Machine Generation ID" specification for
+ * further details, including when a new generation ID is required.
+ *
+ * http://www.microsoft.com/en-us/download/details.aspx?id=30707
+ */
+int libxl_ms_vm_genid_generate(libxl_ctx *ctx, libxl_ms_vm_genid *id)
+{
+ GC_INIT(ctx);
+ int ret;
+
+ ret = libxl__random_bytes(gc, id->bytes, LIBXL_MS_VM_GENID_LEN);
+
+ GC_FREE;
+ return ret;
+}
+
+/*
+ * Is this VM generation ID all zeros?
+ */
+bool libxl_ms_vm_genid_is_zero(const libxl_ms_vm_genid *id)
+{
+ static const libxl_ms_vm_genid zero;
+
+ return memcmp(id->bytes, zero.bytes, LIBXL_MS_VM_GENID_LEN) == 0;
+}
+
+void libxl_ms_vm_genid_copy(libxl_ctx *ctx, libxl_ms_vm_genid *dst,
+ const libxl_ms_vm_genid *src)
+{
+ memcpy(dst, src, LIBXL_MS_VM_GENID_LEN);
+}
+
+int libxl__ms_vm_genid_set(libxl__gc *gc, uint32_t domid,
+ const libxl_ms_vm_genid *id)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ const char *dom_path;
+ uint64_t genid[2];
+ uint64_t paddr = 0;
+ int rc;
+
+ memcpy(genid, id->bytes, LIBXL_MS_VM_GENID_LEN);
+
+ /*
+ * Set the "platform/generation-id" XenStore key to pass the ID to
+ * hvmloader.
+ */
+ dom_path = libxl__xs_get_dompath(gc, domid);
+ if (!dom_path) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = libxl__xs_printf(gc, XBT_NULL,
+ GCSPRINTF("%s/platform/generation-id", dom_path),
+ "%"PRIu64 ":%" PRIu64, genid[0], genid[1]);
+ if (rc < 0)
+ goto out;
+
+ /*
+ * Update the ID in guest memory (if available).
+ */
+ xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_VM_GENERATION_ID_ADDR, &paddr);
+ if (paddr) {
+ void *vaddr;
+
+ vaddr = xc_map_foreign_range(ctx->xch, domid, XC_PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ paddr >> XC_PAGE_SHIFT);
+ if (vaddr == NULL) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ memcpy(vaddr + (paddr & ~XC_PAGE_MASK), genid, 2 * sizeof(*genid));
+ munmap(vaddr, XC_PAGE_SIZE);
+
+ /*
+ * The spec requires an ACPI Notify event is injected into the
+ * guest when the generation ID is changed.
+ *
+ * This is only called for domains that are suspended or newly
+ * created and they won't be in a state to receive such an
+ * event.
+ */
+ }
+
+ rc = 0;
+
+ out:
+ return rc;
+}
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+void libxl__alloc_failed(libxl_ctx *ctx, const char *func,
+ size_t nmemb, size_t size) {
+#define M "libxl: FATAL ERROR: memory allocation failure"
+#define M_SIZE M " (%s, %lu x %lu)\n"
+#define M_NSIZE M " (%s)\n"
+ if (size) {
+ libxl__log(ctx, XTL_CRITICAL, ENOMEM, 0, 0, func, INVALID_DOMID,
+ M_SIZE, func, (unsigned long)nmemb, (unsigned long)size);
+ fprintf(stderr, M_SIZE, func, (unsigned long)nmemb,
+ (unsigned long)size);
+ } else {
+ libxl__log(ctx, XTL_CRITICAL, ENOMEM, 0, 0, func, INVALID_DOMID,
+ M_NSIZE, func);
+ fprintf(stderr, M_NSIZE, func);
+
+ }
+
+ fflush(stderr);
+ _exit(-1);
+#undef M_NSIZE
+#undef M_SIZE
+#undef M
+}
+
+void libxl__ptr_add(libxl__gc *gc, void *ptr)
+{
+ int i;
+
+ if (!libxl__gc_is_real(gc))
+ return;
+
+ if (!ptr)
+ return;
+
+ /* fast case: we have space in the array for storing the pointer */
+ for (i = 0; i < gc->alloc_maxsize; i++) {
+ if (!gc->alloc_ptrs[i]) {
+ gc->alloc_ptrs[i] = ptr;
+ return;
+ }
+ }
+ int new_maxsize = gc->alloc_maxsize * 2 + 25;
+ assert(new_maxsize < INT_MAX / sizeof(void*) / 2);
+ gc->alloc_ptrs = realloc(gc->alloc_ptrs, new_maxsize * sizeof(void *));
+ if (!gc->alloc_ptrs)
+ libxl__alloc_failed(CTX, __func__, new_maxsize, sizeof(void*));
+
+ gc->alloc_ptrs[gc->alloc_maxsize++] = ptr;
+
+ while (gc->alloc_maxsize < new_maxsize)
+ gc->alloc_ptrs[gc->alloc_maxsize++] = 0;
+
+ return;
+}
+
+void libxl__free_all(libxl__gc *gc)
+{
+ void *ptr;
+ int i;
+
+ assert(libxl__gc_is_real(gc));
+
+ for (i = 0; i < gc->alloc_maxsize; i++) {
+ ptr = gc->alloc_ptrs[i];
+ gc->alloc_ptrs[i] = NULL;
+ free(ptr);
+ }
+ free(gc->alloc_ptrs);
+ gc->alloc_ptrs = 0;
+ gc->alloc_maxsize = 0;
+}
+
+void *libxl__malloc(libxl__gc *gc, size_t size)
+{
+ void *ptr = malloc(size);
+ if (!ptr) libxl__alloc_failed(CTX, __func__, size, 1);
+
+ libxl__ptr_add(gc, ptr);
+ return ptr;
+}
+
+void *libxl__zalloc(libxl__gc *gc, size_t size)
+{
+ void *ptr = calloc(size, 1);
+ if (!ptr) libxl__alloc_failed(CTX, __func__, size, 1);
+
+ libxl__ptr_add(gc, ptr);
+ return ptr;
+}
+
+void *libxl__calloc(libxl__gc *gc, size_t nmemb, size_t size)
+{
+ void *ptr = calloc(nmemb, size);
+ if (!ptr) libxl__alloc_failed(CTX, __func__, nmemb, size);
+
+ libxl__ptr_add(gc, ptr);
+ return ptr;
+}
+
+void *libxl__realloc(libxl__gc *gc, void *ptr, size_t new_size)
+{
+ void *new_ptr = realloc(ptr, new_size);
+ int i = 0;
+
+ if (new_ptr == NULL && new_size != 0)
+ libxl__alloc_failed(CTX, __func__, new_size, 1);
+
+ if (ptr == NULL) {
+ libxl__ptr_add(gc, new_ptr);
+ } else if (new_ptr != ptr && libxl__gc_is_real(gc)) {
+ for (i = 0; ; i++) {
+ assert(i < gc->alloc_maxsize);
+ if (gc->alloc_ptrs[i] == ptr) {
+ gc->alloc_ptrs[i] = new_ptr;
+ break;
+ }
+ }
+ }
+
+ return new_ptr;
+}
+
+char *libxl__vsprintf(libxl__gc *gc, const char *fmt, va_list ap)
+{
+ char *s;
+ va_list aq;
+ int ret;
+
+ va_copy(aq, ap);
+ ret = vsnprintf(NULL, 0, fmt, aq);
+ va_end(aq);
+
+ assert(ret >= 0);
+
+ s = libxl__zalloc(gc, ret + 1);
+ va_copy(aq, ap);
+ ret = vsnprintf(s, ret + 1, fmt, aq);
+ va_end(aq);
+
+ return s;
+}
+
+char *libxl__sprintf(libxl__gc *gc, const char *fmt, ...)
+{
+ char *s;
+ va_list ap;
+
+ va_start(ap, fmt);
+ s = libxl__vsprintf(gc, fmt, ap);
+ va_end(ap);
+
+ return s;
+}
+
+char *libxl__strdup(libxl__gc *gc, const char *c)
+{
+ char *s;
+
+ if (!c) return NULL;
+
+ s = strdup(c);
+
+ if (!s) libxl__alloc_failed(CTX, __func__, strlen(c), 1);
+
+ libxl__ptr_add(gc, s);
+
+ return s;
+}
+
+char *libxl__strndup(libxl__gc *gc, const char *c, size_t n)
+{
+ char *s;
+
+ if (!c) return NULL;
+
+ s = strndup(c, n);
+
+ if (!s) libxl__alloc_failed(CTX, __func__, n, 1);
+
+ libxl__ptr_add(gc, s);
+
+ return s;
+}
+
+char *libxl__dirname(libxl__gc *gc, const char *s)
+{
+ char *c = strrchr(s, '/');
+
+ if (!c)
+ return NULL;
+
+ return libxl__strndup(gc, s, c - s);
+}
+
+void libxl__logv(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
+ const char *file, int line, const char *func,
+ uint32_t domid, const char *fmt, va_list ap)
+{
+ /* WARNING this function may not call any libxl-provided
+ * memory allocation function, as those may
+ * call libxl__alloc_failed which calls libxl__logv. */
+ char *enomem = "[out of memory formatting log message]";
+ char *base = NULL;
+ int rc, esave;
+ char fileline[256];
+ char domain[256];
+
+ esave = errno;
+
+ rc = vasprintf(&base, fmt, ap);
+ if (rc<0) { base = enomem; goto x; }
+
+ fileline[0] = 0;
+ if (file) snprintf(fileline, sizeof(fileline), "%s:%d",file,line);
+ fileline[sizeof(fileline)-1] = 0;
+
+ domain[0] = 0;
+ if (libxl_domid_valid_guest(domid))
+ snprintf(domain, sizeof(domain), "Domain %"PRIu32":", domid);
+ x:
+ xtl_log(ctx->lg, msglevel, errnoval, "libxl",
+ "%s%s%s%s%s" "%s",
+ fileline, func&&file?":":"", func?func:"", func||file?": ":"",
+ domain, base);
+ if (base != enomem) free(base);
+ errno = esave;
+}
+
+void libxl__log(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
+ const char *file, int line, const char *func,
+ uint32_t domid, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ libxl__logv(ctx, msglevel, errnoval, file, line, func, domid, fmt, ap);
+ va_end(ap);
+}
+
+char *libxl__abs_path(libxl__gc *gc, const char *s, const char *path)
+{
+ if (s[0] == '/') return libxl__strdup(gc, s);
+ return GCSPRINTF("%s/%s", path, s);
+}
+
+
+int libxl__file_reference_map(libxl__file_reference *f)
+{
+ struct stat st_buf;
+ int ret, fd;
+ void *data;
+
+ if (f->mapped)
+ return 0;
+
+ fd = open(f->path, O_RDONLY);
+ if (fd < 0)
+ return ERROR_FAIL;
+
+ ret = fstat(fd, &st_buf);
+ if (ret < 0)
+ goto out;
+
+ ret = -1;
+ data = mmap(NULL, st_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (data == MAP_FAILED)
+ goto out;
+
+ f->mapped = 1;
+ f->data = data;
+ f->size = st_buf.st_size;
+
+ ret = 0;
+out:
+ close(fd);
+
+ return ret == 0 ? 0 : ERROR_FAIL;
+}
+
+int libxl__file_reference_unmap(libxl__file_reference *f)
+{
+ int ret;
+
+ if (!f->mapped)
+ return 0;
+
+ ret = munmap(f->data, f->size);
+ if (ret == 0) {
+ f->mapped = 0;
+ f->data = NULL;
+ f->size = 0;
+ return 0;
+ }
+
+ return ERROR_FAIL;
+}
+
+_hidden int libxl__parse_mac(const char *s, libxl_mac mac)
+{
+ const char *tok;
+ char *endptr;
+ int i;
+
+ for (i = 0, tok = s; *tok && (i < 6); ++i, tok = endptr) {
+ mac[i] = strtol(tok, &endptr, 16);
+ if (endptr != (tok + 2) || (*endptr != '\0' && *endptr != ':') )
+ return ERROR_INVAL;
+ if (*endptr == ':')
+ endptr++;
+ }
+ if ( i != 6 )
+ return ERROR_INVAL;
+
+ return 0;
+}
+
+_hidden int libxl__compare_macs(libxl_mac *a, libxl_mac *b)
+{
+ int i;
+
+ for (i = 0; i<6; i++) {
+ if ((*a)[i] != (*b)[i])
+ return (*a)[i] - (*b)[i];
+ }
+
+ return 0;
+}
+
+_hidden int libxl__mac_is_default(libxl_mac *mac)
+{
+ return (!(*mac)[0] && !(*mac)[1] && !(*mac)[2] &&
+ !(*mac)[3] && !(*mac)[4] && !(*mac)[5]);
+}
+
+_hidden int libxl__init_recursive_mutex(libxl_ctx *ctx, pthread_mutex_t *lock)
+{
+ GC_INIT(ctx);
+ pthread_mutexattr_t attr;
+ int rc = 0;
+
+ if (pthread_mutexattr_init(&attr) != 0) {
+ LOGE(ERROR, "Failed to init mutex attributes");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
+ LOGE(ERROR, "Failed to set mutex attributes");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ if (pthread_mutex_init(lock, &attr) != 0) {
+ LOGE(ERROR, "Failed to init mutex");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+out:
+ pthread_mutexattr_destroy(&attr);
+ GC_FREE;
+ return rc;
+}
+
+int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid)
+{
+ char *path = NULL;
+ char *dm_version = NULL;
+ libxl_device_model_version value;
+
+ path = libxl__xs_libxl_path(gc, domid);
+ path = GCSPRINTF("%s/dm-version", path);
+ dm_version = libxl__xs_read(gc, XBT_NULL, path);
+ if (!dm_version) {
+ return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
+ }
+
+ if (libxl_device_model_version_from_string(dm_version, &value) < 0) {
+ LOGD(ERROR, domid, "fatal: %s contain a wrong value (%s)", path, dm_version);
+ return -1;
+ }
+ return value;
+}
+
+/* Portability note: this lock utilises flock(2) so a proper implementation of
+ * flock(2) is required.
+ */
+libxl__flock *libxl__lock_file(libxl__gc *gc, const char *lockfile)
+{
+ libxl__flock *lock;
+ int fd;
+ struct stat stab, fstab;
+
+ lock = libxl__zalloc(NOGC, sizeof(libxl__flock));
+ lock->path = libxl__strdup(NOGC, lockfile);
+
+ while (true) {
+ libxl__carefd_begin();
+ fd = open(lockfile, O_RDWR|O_CREAT, 0666);
+ if (fd < 0)
+ LOGE(ERROR,
+ "cannot open lockfile %s, errno=%d",
+ lockfile, errno);
+ lock->carefd = libxl__carefd_opened(CTX, fd);
+ if (fd < 0) goto out;
+
+ /* Lock the file in exclusive mode, wait indefinitely to
+ * acquire the lock
+ */
+ while (flock(fd, LOCK_EX)) {
+ switch (errno) {
+ case EINTR:
+ /* Signal received, retry */
+ continue;
+ default:
+ /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
+ LOGE(ERROR,
+ "unexpected error while trying to lock %s, fd=%d, errno=%d",
+ lockfile, fd, errno);
+ goto out;
+ }
+ }
+
+ if (fstat(fd, &fstab)) {
+ LOGE(ERROR, "cannot fstat %s, fd=%d, errno=%d",
+ lockfile, fd, errno);
+ goto out;
+ }
+ if (stat(lockfile, &stab)) {
+ if (errno != ENOENT) {
+ LOGE(ERROR, "cannot stat %s, errno=%d", lockfile, errno);
+ goto out;
+ }
+ } else {
+ if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino)
+ break;
+ }
+
+ libxl__carefd_close(lock->carefd);
+ }
+
+ return lock;
+
+out:
+ if (lock) libxl__unlock_file(lock);
+ return NULL;
+}
+
+void libxl__unlock_file(libxl__flock *lock)
+{
+ /* It's important to unlink the file before closing fd to avoid
+ * the following race (if close before unlink):
+ *
+ * P1 LOCK P2 UNLOCK
+ * fd1 = open(lockfile)
+ * close(fd2)
+ * flock(fd1)
+ * fstat and stat check success
+ * unlink(lockfile)
+ * return lock
+ *
+ * In above case P1 thinks it has got hold of the lock but
+ * actually lock is released by P2 (lockfile unlinked).
+ */
+ if (lock->path) unlink(lock->path);
+ if (lock->carefd) libxl__carefd_close(lock->carefd);
+ free(lock->path);
+ free(lock);
+}
+
+libxl__flock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid)
+{
+ const char *lockfile;
+ libxl__flock *lock;
+
+ lockfile = libxl__userdata_path(gc, domid, "domain-userdata-lock", "l");
+ if (!lockfile) return NULL;
+
+ lock = libxl__lock_file(gc, lockfile);
+
+ /* Check the domain is still there, if not we should release the
+ * lock and clean up.
+ */
+ if (libxl_domain_info(CTX, NULL, domid)) {
+ libxl__unlock_file(lock);
+ return NULL;
+ }
+
+ return lock;
+}
+
+libxl__flock *libxl__lock_domid_history(libxl__gc *gc)
+{
+ const char *lockfile;
+
+ lockfile = libxl__domid_history_path(gc, ".lock");
+ if (!lockfile) return NULL;
+
+ return libxl__lock_file(gc, lockfile);
+}
+
+int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config *d_config)
+{
+ uint8_t *data = NULL;
+ int rc, len;
+
+ rc = libxl__userdata_retrieve(gc, domid, "libxl-json", &data, &len);
+ if (rc) {
+ LOGEVD(ERROR, rc, domid,
+ "failed to retrieve domain configuration");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (len == 0) {
+ /* No logging, not necessary an error from caller's PoV. */
+ rc = ERROR_JSON_CONFIG_EMPTY;
+ goto out;
+ }
+ rc = libxl_domain_config_from_json(CTX, d_config, (const char *)data);
+
+out:
+ free(data);
+ return rc;
+}
+
+int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config *d_config)
+{
+ char *d_config_json;
+ int rc;
+
+ d_config_json = libxl_domain_config_to_json(CTX, d_config);
+ if (!d_config_json) {
+ LOGED(ERROR, domid,
+ "failed to convert domain configuration to JSON");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__userdata_store(gc, domid, "libxl-json",
+ (const uint8_t *)d_config_json,
+ strlen(d_config_json) + 1 /* include '\0' */);
+ if (rc) {
+ LOGEVD(ERROR, rc, domid, "failed to store domain configuration");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+out:
+ free(d_config_json);
+ return rc;
+}
+
+void libxl__update_domain_configuration(libxl__gc *gc,
+ libxl_domain_config *dst,
+ const libxl_domain_config *src)
+{
+ int i, idx, num;
+ const libxl__device_type *dt;
+
+ for (idx = 0;; idx++) {
+ dt = device_type_tbl[idx];
+ if (!dt)
+ break;
+
+ num = *libxl__device_type_get_num(dt, src);
+ if (!dt->update_config || !num)
+ continue;
+
+ for (i = 0; i < num; i++)
+ dt->update_config(gc, libxl__device_type_get_elem(dt, dst, i),
+ libxl__device_type_get_elem(dt, src, i));
+ }
+
+ /* update guest UUID */
+ libxl_uuid_copy(CTX, &dst->c_info.uuid, &src->c_info.uuid);
+
+ /* video ram */
+ dst->b_info.video_memkb = src->b_info.video_memkb;
+}
+
+static void ev_slowlock_init_internal(libxl__ev_slowlock *lock,
+ const char *userdata_userid)
+{
+ libxl__ev_child_init(&lock->child);
+ lock->userdata_userid = userdata_userid;
+ lock->path = NULL;
+ lock->fd = -1;
+ lock->held = false;
+}
+
+void libxl__ev_devlock_init(libxl__ev_slowlock *lock)
+{
+ ev_slowlock_init_internal(lock, "libxl-device-changes-lock");
+}
+
+void libxl__ev_qmplock_init(libxl__ev_slowlock *lock)
+{
+ ev_slowlock_init_internal(lock, "qmp-socket-lock");
+}
+
+static void ev_lock_prepare_fork(libxl__egc *egc, libxl__ev_slowlock *lock);
+static void ev_lock_child_callback(libxl__egc *egc, libxl__ev_child *child,
+ pid_t pid, int status);
+
+void libxl__ev_slowlock_lock(libxl__egc *egc, libxl__ev_slowlock *lock)
+{
+ STATE_AO_GC(lock->ao);
+ const char *lockfile;
+
+ lockfile = libxl__userdata_path(gc, lock->domid,
+ lock->userdata_userid, "l");
+ if (!lockfile) goto out;
+ lock->path = libxl__strdup(NOGC, lockfile);
+
+ ev_lock_prepare_fork(egc, lock);
+ return;
+out:
+ lock->callback(egc, lock, ERROR_LOCK_FAIL);
+}
+
+static void ev_lock_prepare_fork(libxl__egc *egc, libxl__ev_slowlock *lock)
+{
+ STATE_AO_GC(lock->ao);
+ pid_t pid;
+ int fd;
+
+ /* Convenience aliases */
+ libxl_domid domid = lock->domid;
+ const char *lockfile = lock->path;
+
+ lock->fd = open(lockfile, O_RDWR|O_CREAT, 0666);
+ if (lock->fd < 0) {
+ LOGED(ERROR, domid, "cannot open lockfile %s", lockfile);
+ goto out;
+ }
+ fd = lock->fd;
+
+ /* Enable this optimisation only in releases, so the fork code is
+ * exercised while libxl is built with debug=y. */
+#ifndef CONFIG_DEBUG
+ /*
+ * We try to grab the lock before forking as it is likely to be free.
+ * Even though we are supposed to CTX_UNLOCK before attempting to grab
+ * the ev_lock, it is fine to do a non-blocking request now with the
+ * CTX_LOCK held as if that fails we'll try again in a fork (CTX_UNLOCK
+ * will be called in libxl), that will avoid deadlocks.
+ */
+ int r = flock(fd, LOCK_EX | LOCK_NB);
+ if (!r) {
+ libxl_fd_set_cloexec(CTX, fd, 1);
+ /* We held a lock, no need to fork but we need to check it. */
+ ev_lock_child_callback(egc, &lock->child, 0, 0);
+ return;
+ }
+#endif
+
+ pid = libxl__ev_child_fork(gc, &lock->child, ev_lock_child_callback);
+ if (pid < 0)
+ goto out;
+ if (!pid) {
+ /* child */
+ int exit_val = 0;
+
+ /* Lock the file in exclusive mode, wait indefinitely to
+ * acquire the lock */
+ while (flock(fd, LOCK_EX)) {
+ switch (errno) {
+ case EINTR:
+ /* Signal received, retry */
+ continue;
+ default:
+ /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
+ LOGED(ERROR, domid,
+ "unexpected error while trying to lock %s, fd=%d",
+ lockfile, fd);
+ exit_val = 1;
+ break;
+ }
+ }
+ _exit(exit_val);
+ }
+
+ /* Now that the child has the fd, set cloexec in the parent to prevent
+ * more leakage than necessary */
+ libxl_fd_set_cloexec(CTX, fd, 1);
+ return;
+out:
+ libxl__ev_slowlock_unlock(gc, lock);
+ lock->callback(egc, lock, ERROR_LOCK_FAIL);
+}
+
+static void ev_lock_child_callback(libxl__egc *egc, libxl__ev_child *child,
+ pid_t pid, int status)
+{
+ EGC_GC;
+ libxl__ev_slowlock *lock = CONTAINER_OF(child, *lock, child);
+ struct stat stab, fstab;
+ int rc = ERROR_LOCK_FAIL;
+
+ /* Convenience aliases */
+ int fd = lock->fd;
+ const char *lockfile = lock->path;
+ libxl_domid domid = lock->domid;
+
+ if (status) {
+ libxl_report_child_exitstatus(CTX, XTL_ERROR, "flock child",
+ pid, status);
+ goto out;
+ }
+
+ if (fstat(fd, &fstab)) {
+ LOGED(ERROR, domid, "cannot fstat %s, fd=%d", lockfile, fd);
+ goto out;
+ }
+ if (stat(lockfile, &stab)) {
+ if (errno != ENOENT) {
+ LOGED(ERROR, domid, "cannot stat %s", lockfile);
+ goto out;
+ }
+ } else {
+ if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino) {
+ /* We held the lock */
+ lock->held = true;
+ rc = 0;
+ goto out;
+ }
+ }
+
+ /* We didn't grab the lock, let's try again */
+ flock(lock->fd, LOCK_UN);
+ close(lock->fd);
+ lock->fd = -1;
+ ev_lock_prepare_fork(egc, lock);
+ return;
+
+out:
+ if (lock->held) {
+ /* Check the domain is still there, if not we should release the
+ * lock and clean up. */
+ if (libxl_domain_info(CTX, NULL, domid))
+ rc = ERROR_LOCK_FAIL;
+ }
+ if (rc) {
+ LOGD(ERROR, domid, "Failed to grab lock for %s",
+ lock->userdata_userid);
+ libxl__ev_slowlock_unlock(gc, lock);
+ }
+ lock->callback(egc, lock, rc);
+}
+
+void libxl__ev_slowlock_unlock(libxl__gc *gc, libxl__ev_slowlock *lock)
+{
+ int r;
+
+ assert(!libxl__ev_child_inuse(&lock->child));
+
+ /* See the rationale in libxl__unlock_domain_userdata()
+ * about why we do unlink() before unlock(). */
+
+ if (lock->path && lock->held)
+ unlink(lock->path);
+
+ if (lock->fd >= 0) {
+ /* We need to call unlock as the fd may have leaked into other
+ * processes */
+ r = flock(lock->fd, LOCK_UN);
+ if (r)
+ LOGED(ERROR, lock->domid, "failed to unlock fd=%d, path=%s",
+ lock->fd, lock->path);
+ close(lock->fd);
+ }
+ free(lock->path);
+ ev_slowlock_init_internal(lock, lock->userdata_userid);
+}
+
+void libxl__ev_slowlock_dispose(libxl__gc *gc, libxl__ev_slowlock *lock)
+{
+ libxl__ev_child_kill_deregister(lock->ao, &lock->child, SIGKILL);
+ libxl__ev_slowlock_unlock(gc, lock);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#ifndef LIBXL_INTERNAL_H
+#define LIBXL_INTERNAL_H
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "xentoolcore_internal.h"
+
+#include "libxl_sr_stream_format.h"
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include <sys/mman.h>
+#include <poll.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+
+#include <xenevtchn.h>
+#include <xenstore.h>
+#define XC_WANT_COMPAT_MAP_FOREIGN_API
+#include <xenctrl.h>
+#include <xenguest.h>
+#include <xenhypfs.h>
+#include <xenctrl_dom.h>
+
+#include <xen-tools/libs.h>
+
+#include "xentoollog.h"
+
+#include <xen/io/xenbus.h>
+
+#ifdef LIBXL_H
+# error libxl.h should be included via libxl_internal.h, not separately
+#endif
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)
+# define LIBXL_EXTERNAL_CALLERS_ONLY \
+ __attribute__((warning("may not be called from within libxl")))
+#endif
+
+#include "libxl.h"
+#include "_paths.h"
+#include "_libxl_save_msgs_callout.h"
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+#define _hidden __attribute__((visibility("hidden")))
+#define _protected __attribute__((visibility("protected")))
+#else
+#define _hidden
+#define _protected
+#endif
+
+#include "flexarray.h"
+#include "libxl_utils.h"
+
+#include "libxl_json.h"
+
+#include "_libxl_types_internal.h"
+#include "_libxl_types_internal_json.h"
+
+#define LIBXL_INIT_TIMEOUT 10
+#define LIBXL_DESTROY_TIMEOUT 10
+#define LIBXL_HOTPLUG_TIMEOUT 40
+/* QEMU may be slow to load and start due to a bug in Linux where the I/O
+ * subsystem sometime produce high latency under load. */
+#define LIBXL_DEVICE_MODEL_START_TIMEOUT 60
+#define LIBXL_DEVICE_MODEL_SAVE_FILE XEN_LIB_DIR "/qemu-save" /* .$domid */
+#define LIBXL_DEVICE_MODEL_RESTORE_FILE XEN_LIB_DIR "/qemu-resume" /* .$domid */
+#define LIBXL_QMP_CMD_TIMEOUT 10
+#define LIBXL_STUBDOM_START_TIMEOUT 30
+#define LIBXL_QEMU_BODGE_TIMEOUT 2
+#define LIBXL_XENCONSOLE_LIMIT 1048576
+#define LIBXL_XENCONSOLE_PROTOCOL "vt100"
+#define LIBXL_MAXMEM_CONSTANT 1024
+#define LIBXL_PV_EXTRA_MEMORY 1024
+#define LIBXL_HVM_EXTRA_MEMORY 2048
+#define LIBXL_MIN_DOM0_MEM (128*1024)
+#define LIBXL_INVALID_GFN (~(uint64_t)0)
+#define LIBXL_VGA_HOLE_SIZE 0x20
+/* use 0 as the domid of the toolstack domain for now */
+#define LIBXL_TOOLSTACK_DOMID 0
+#define QEMU_SIGNATURE "DeviceModelRecord0002"
+#define STUBDOM_CONSOLE_LOGGING 0
+#define STUBDOM_CONSOLE_SAVE 1
+#define STUBDOM_CONSOLE_RESTORE 2
+#define STUBDOM_CONSOLE_SERIAL 3
+#define STUBDOM_SPECIAL_CONSOLES 3
+#define LIBXL_LINUX_STUBDOM_MEM 128
+#define TAP_DEVICE_SUFFIX "-emu"
+#define DOMID_XS_PATH "domid"
+#define PVSHIM_BASENAME "xen-shim"
+#define PVSHIM_CMDLINE "pv-shim console=xen,pv"
+
+/* Size macros. */
+#define __AC(X,Y) (X##Y)
+#define _AC(X,Y) __AC(X,Y)
+#define MB(_mb) (_AC(_mb, ULL) << 20)
+#define GB(_gb) (_AC(_gb, ULL) << 30)
+
+#define DIV_ROUNDUP(n, d) (((n) + (d) - 1) / (d))
+
+#define MASK_EXTR(v, m) (((v) & (m)) / ((m) & -(m)))
+#define MASK_INSR(v, m) (((v) * ((m) & -(m))) & (m))
+
+#define LIBXL__LOGGING_ENABLED
+
+#ifdef LIBXL__LOGGING_ENABLED
+#define LIBXL__LOG(ctx, loglevel, _f, _a...) libxl__log(ctx, loglevel, -1, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a)
+#define LIBXL__LOG_ERRNO(ctx, loglevel, _f, _a...) libxl__log(ctx, loglevel, errno, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a)
+#define LIBXL__LOG_ERRNOVAL(ctx, loglevel, errnoval, _f, _a...) libxl__log(ctx, loglevel, errnoval, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a)
+
+/* Same log functions as above, but with _d being a domain id. */
+#define LIBXL__LOGD(ctx, loglevel, _d, _f, _a...) libxl__log(ctx, loglevel, -1, __FILE__, __LINE__, __func__, _d, _f, ##_a)
+#define LIBXL__LOGD_ERRNO(ctx, loglevel, _d, _f, _a...) libxl__log(ctx, loglevel, errno, __FILE__, __LINE__, __func__, _d, _f, ##_a)
+#define LIBXL__LOGD_ERRNOVAL(ctx, loglevel, errnoval, _d, _f, _a...) libxl__log(ctx, loglevel, errnoval, __FILE__, __LINE__, __func__, _d, _f, ##_a)
+#else
+#define LIBXL__LOG(ctx, loglevel, _f, _a...)
+#define LIBXL__LOG_ERRNO(ctx, loglevel, _f, _a...)
+#define LIBXL__LOG_ERRNOVAL(ctx, loglevel, errnoval, _f, _a...)
+
+#define LIBXLD__LOG(ctx, loglevel, _d, _f, _a...)
+#define LIBXLD__LOG_ERRNO(ctx, loglevel, _d, _f, _a...)
+#define LIBXLD__LOG_ERRNOVAL(ctx, loglevel, errnoval, _d, _f, _a...)
+#endif
+ /* all of these macros preserve errno (saving and restoring) */
+
+/*
+ * A macro to help retain the first failure in "do as much as you can"
+ * situations. Note the hard-coded use of the variable name `rc`.
+ */
+#define ACCUMULATE_RC(rc_acc) ((rc_acc) = (rc_acc) ?: rc)
+
+/* Convert pfn to physical address space. */
+#define pfn_to_paddr(x) ((uint64_t)(x) << XC_PAGE_SHIFT)
+
+/* logging */
+_hidden void libxl__logv(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
+ const char *file /* may be 0 */, int line /* ignored if !file */,
+ const char *func /* may be 0 */,
+ uint32_t domid /* may be INVALID_DOMID */,
+ const char *fmt, va_list al)
+ __attribute__((format(printf,8,0)));
+
+_hidden void libxl__log(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
+ const char *file /* may be 0 */, int line /* ignored if !file */,
+ const char *func /* may be 0 */,
+ uint32_t domid /* may be INVALID_DOMID */,
+ const char *fmt, ...)
+ __attribute__((format(printf,8,9)));
+
+ /* these functions preserve errno (saving and restoring) */
+
+typedef struct libxl__gc libxl__gc;
+typedef struct libxl__egc libxl__egc;
+typedef struct libxl__ao libxl__ao;
+typedef struct libxl__aop_occurred libxl__aop_occurred;
+typedef struct libxl__osevent_hook_nexus libxl__osevent_hook_nexus;
+typedef struct libxl__osevent_hook_nexi libxl__osevent_hook_nexi;
+typedef struct libxl__device_type libxl__device_type;
+typedef struct libxl__json_object libxl__json_object;
+typedef struct libxl__carefd libxl__carefd;
+typedef struct libxl__ev_slowlock libxl__ev_slowlock;
+typedef struct libxl__dm_resume_state libxl__dm_resume_state;
+typedef struct libxl__ao_device libxl__ao_device;
+typedef struct libxl__multidev libxl__multidev;
+typedef struct libxl__ev_immediate libxl__ev_immediate;
+
+typedef struct libxl__domain_create_state libxl__domain_create_state;
+typedef void libxl__domain_create_cb(struct libxl__egc *egc,
+ libxl__domain_create_state *dcs,
+ int rc, uint32_t domid);
+
+typedef struct libxl__colo_device_nic libxl__colo_device_nic;
+typedef struct libxl__colo_qdisk libxl__colo_qdisk;
+typedef struct libxl__colo_proxy_state libxl__colo_proxy_state;
+typedef struct libxl__colo_save_state libxl__colo_save_state;
+typedef struct libxl__colo_restore_state libxl__colo_restore_state;
+
+_hidden void libxl__alloc_failed(libxl_ctx *, const char *func,
+ size_t nmemb, size_t size) __attribute__((noreturn));
+ /* func, size and nmemb are used only in the log message.
+ * You may pass size==0 if size and nmemb are not meaningful
+ * and should not be printed. */
+
+typedef struct libxl__ev_fd libxl__ev_fd;
+typedef void libxl__ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev,
+ int fd, short events, short revents);
+ /* Note that revents may contain POLLERR or POLLHUP regardless of
+ * events; otherwise revents contains only bits in events. Contrary
+ * to the documentation for poll(2), POLLERR and POLLHUP can occur
+ * even if only POLLIN was set in events. (POLLNVAL is a fatal
+ * error and will cause libxl event machinery to fail an assertion.)
+ *
+ * It is not permitted to listen for the same or overlapping events
+ * on the same fd using multiple different libxl__ev_fd's.
+ *
+ * Note that (depending on the underlying event loop implementation)
+ * it is possible that a the fd callback system is `level triggered'
+ * or `event triggered'. That is, the callback may be called only
+ * once for each transition from not ready to ready. So the
+ * callback must generally contain a loop which exhausts the fd,
+ * rather than relying on being called again if the fd is still
+ * ready.
+ *
+ * (Spurious wakeups, and spurious bits set in revents, are
+ * suppressed by the libxl event core.)
+ */
+struct libxl__ev_fd {
+ /* caller should include this in their own struct */
+ /* read-only for caller, who may read only when registered: */
+ int fd;
+ short events;
+ libxl__ev_fd_callback *func;
+ /* remainder is private for libxl__ev_fd... */
+ LIBXL_LIST_ENTRY(libxl__ev_fd) entry;
+ libxl__osevent_hook_nexus *nexus;
+};
+
+
+typedef struct libxl__ao_abortable libxl__ao_abortable;
+typedef void libxl__ao_abortable_callback(libxl__egc *egc,
+ libxl__ao_abortable *ao_abortable, int rc /* ABORTED */);
+
+struct libxl__ao_abortable {
+ /* caller must fill this in and it must remain valid */
+ libxl__ao *ao;
+ libxl__ao_abortable_callback *callback;
+ /* remainder is private for abort machinery */
+ bool registered;
+ LIBXL_LIST_ENTRY(libxl__ao_abortable) entry;
+ /*
+ * For nested aos:
+ * Semantically, abort affects the whole tree of aos,
+ * not just the parent.
+ * libxl__ao_abortable.ao refers to the child, so
+ * that the child callback sees the right ao. (After all,
+ * it was code dealing with the child that set .ao.)
+ * But, the abortable is recorded on the "abortables" list
+ * for the ultimate root ao, so that every possible child
+ * abort occurs as a result of the abort of the parent.
+ * We set ao->aborting only in the root.
+ */
+};
+
+_hidden int libxl__ao_abortable_register(libxl__ao_abortable*);
+_hidden void libxl__ao_abortable_deregister(libxl__ao_abortable*);
+
+static inline void libxl__ao_abortable_init
+ (libxl__ao_abortable *c) { c->registered = 0; }
+static inline bool libxl__ao_abortable_isregistered
+ (const libxl__ao_abortable *c) { return c->registered; }
+
+int libxl__ao_aborting(libxl__ao *ao); /* -> 0 or ERROR_ABORTED */
+
+
+typedef struct libxl__ev_time libxl__ev_time;
+typedef void libxl__ev_time_callback(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc); /* TIMEDOUT or ABORTED */
+struct libxl__ev_time {
+ /* caller should include this in their own struct */
+ /* read-only for caller, who may read only when registered: */
+ libxl__ev_time_callback *func;
+ /* remainder is private for libxl__ev_time... */
+ int infinite; /* not registered in list or with app if infinite */
+ LIBXL_TAILQ_ENTRY(libxl__ev_time) entry;
+ struct timeval abs;
+ libxl__osevent_hook_nexus *nexus;
+ libxl__ao_abortable abrt;
+};
+
+typedef struct libxl__ev_xswatch libxl__ev_xswatch;
+typedef void libxl__ev_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch*,
+ const char *watch_path, const char *event_path);
+struct libxl__ev_xswatch {
+ /* caller should include this in their own struct */
+ /* read-only for caller, who may read only when registered: */
+ char *path;
+ libxl__ev_xswatch_callback *callback;
+ /* remainder is private for libxl__ev_xswatch... */
+ int slotnum; /* registered iff slotnum >= 0 */
+ uint32_t counterval;
+};
+
+typedef struct libxl__ev_evtchn libxl__ev_evtchn;
+typedef void libxl__ev_evtchn_callback(libxl__egc *egc, libxl__ev_evtchn*);
+struct libxl__ev_evtchn {
+ /* caller must fill these in, and they must all remain valid */
+ libxl__ev_evtchn_callback *callback;
+ int port;
+ /* remainder is private for libxl__ev_evtchn_... */
+ bool waiting;
+ LIBXL_LIST_ENTRY(libxl__ev_evtchn) entry;
+};
+
+/*
+ * An entry in the watch_slots table is either:
+ * 1. an entry in the free list, ie NULL or pointer to next free list entry
+ * 2. an pointer to a libxl__ev_xswatch
+ *
+ * But we don't want to use unions or type-punning because the
+ * compiler might "prove" that our code is wrong and misoptimise it.
+ *
+ * The rules say that all struct pointers have identical
+ * representation and alignment requirements (C99+TC1+TC2 6.2.5p26) so
+ * what we do is simply declare our array as containing only the free
+ * list pointers, and explicitly convert from and to our actual
+ * xswatch pointers when we store and retrieve them.
+ */
+typedef struct libxl__ev_watch_slot {
+ LIBXL_SLIST_ENTRY(struct libxl__ev_watch_slot) empty;
+} libxl__ev_watch_slot;
+
+_hidden libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc,
+ int slotnum);
+
+
+typedef struct libxl__ev_child libxl__ev_child;
+typedef void libxl__ev_child_callback(libxl__egc *egc, libxl__ev_child*,
+ pid_t pid, int status);
+struct libxl__ev_child {
+ /* caller should include this in their own struct */
+ /* read-only for caller: */
+ pid_t pid; /* -1 means unused ("unregistered", ie Idle) */
+ libxl__ev_child_callback *callback;
+ /* remainder is private for libxl__ev_... */
+ LIBXL_LIST_ENTRY(struct libxl__ev_child) entry;
+};
+
+/* libxl__ev_immediate
+ *
+ * Allow to call a non-reentrant callback.
+ *
+ * `callback' will be called immediately as a new event.
+ */
+struct libxl__ev_immediate {
+ /* filled by user */
+ void (*callback)(libxl__egc *, libxl__ev_immediate *);
+ /* private to libxl__ev_immediate */
+ LIBXL_STAILQ_ENTRY(libxl__ev_immediate) entry;
+};
+void libxl__ev_immediate_register(libxl__egc *, libxl__ev_immediate *);
+
+/*
+ * Lock for device hotplug, qmp_lock.
+ *
+ * libxl__ev_slowlock implement a lock that is outside of CTX_LOCK in the
+ * lock hierarchy. It can be used when one want to make QMP calls to QEMU,
+ * which may take a significant amount time.
+ * It is to be acquired by an ao event callback.
+ *
+ * If libxl__ev_devlock is needed, it should be acquired while every
+ * libxl__ev_qmp are Idle for the current domain.
+ *
+ * It is to be acquired when adding/removing devices or making changes
+ * to them when this is a slow operation and json_lock isn't appropriate.
+ *
+ * Possible states of libxl__ev_slowlock:
+ * Undefined
+ * Might contain anything.
+ * Idle
+ * Struct contents are defined enough to pass to any
+ * libxl__ev_slowlock_* function.
+ * The struct does not contain references to any allocated private
+ * resources so can be thrown away.
+ * Active
+ * Waiting to get a lock.
+ * Needs to wait until the callback is called.
+ * LockAcquired
+ * libxl__ev_slowlock_unlock will need to be called to release the lock
+ * and the resources of libxl__ev_slowlock.
+ *
+ * libxl__ev_*lock_init: Undefined/Idle -> Idle
+ * libxl__ev_slowlock_lock: Idle -> Active
+ * May call callback synchronously.
+ * libxl__ev_slowlock_unlock: LockAcquired/Idle -> Idle
+ * libxl__ev_slowlock_dispose: Idle/Active/LockAcquired -> Idle
+ * The callback will not be called anymore.
+ * callback: When called: Active -> LockAcquired (on error: Idle)
+ * The callback is only called once.
+ */
+struct libxl__ev_slowlock {
+ /* filled by user */
+ libxl__ao *ao;
+ libxl_domid domid;
+ void (*callback)(libxl__egc *, libxl__ev_slowlock *, int rc);
+ /* private to libxl__ev_slowlock* */
+ libxl__ev_child child;
+ const char *userdata_userid;
+ char *path; /* path of the lock file itself */
+ int fd;
+ bool held;
+};
+_hidden void libxl__ev_devlock_init(libxl__ev_slowlock *);
+_hidden void libxl__ev_qmplock_init(libxl__ev_slowlock *);
+_hidden void libxl__ev_slowlock_lock(libxl__egc *, libxl__ev_slowlock *);
+_hidden void libxl__ev_slowlock_unlock(libxl__gc *, libxl__ev_slowlock *);
+_hidden void libxl__ev_slowlock_dispose(libxl__gc *, libxl__ev_slowlock *);
+
+/*
+ * QMP asynchronous calls
+ *
+ * This facility allows a command to be sent to QEMU, and the response
+ * to be handed to a callback function.
+ *
+ * Commands can be submited one after an other with the same
+ * connection (e.g. the result from the "add-fd" command need to be
+ * use in a follow-up command before disconnecting from QMP). A
+ * libxl__ev_qmp can be reused when the callback is been called in
+ * order to use the same connection.
+ *
+ * Only one connection at a time can be made to one QEMU, so avoid
+ * keeping a libxl__ev_qmp Connected for to long and call
+ * libxl__ev_qmp_dispose as soon as it is not needed anymore.
+ *
+ * Possible states of a libxl__ev_qmp:
+ * Undefined
+ * Might contain anything.
+ * Idle
+ * Struct contents are defined enough to pass to any
+ * libxl__ev_qmp_* function.
+ * The struct does not contain references to any allocated private
+ * resources so can be thrown away.
+ * Active
+ * Currently waiting for the callback to be called.
+ * _dispose must be called to reclaim resources.
+ * Connected
+ * Struct contain allocated ressources.
+ * Calling _send() with this same ev will use the same QMP connection.
+ * _dispose() must be called to reclaim resources.
+ *
+ * libxl__ev_qmp_init: Undefined/Idle -> Idle
+ *
+ * libxl__ev_qmp_send: Idle/Connected -> Active (on error: Idle)
+ * Sends a command to QEMU.
+ * callback will be called when a response is received or when an
+ * error as occured.
+ * callback isn't called synchronously.
+ *
+ * libxl__ev_qmp_dispose: Connected/Active/Idle -> Idle
+ *
+ * callback: When called: Active -> Connected (on error: Idle/Connected)
+ * When called, ev is Connected and can be reused or disposed of.
+ * On error, the callback is called with response == NULL and the
+ * error code in rc. The new state of ev depending on the value of rc:
+ * - rc == ERROR_QMP_*: This is an error associated with the cmd to
+ * run, ev is Connected.
+ * - otherwise: An other error happend, ev is now Idle.
+ * The callback is only called once.
+ */
+typedef struct libxl__ev_qmp libxl__ev_qmp;
+typedef void libxl__ev_qmp_callback(libxl__egc *egc, libxl__ev_qmp *ev,
+ const libxl__json_object *response,
+ int rc);
+
+_hidden void libxl__ev_qmp_init(libxl__ev_qmp *ev);
+_hidden int libxl__ev_qmp_send(libxl__egc *egc, libxl__ev_qmp *ev,
+ const char *cmd, libxl__json_object *args);
+_hidden void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev);
+
+typedef enum {
+ /* initial state */
+ qmp_state_disconnected = 1,
+ /* waiting for lock */
+ qmp_state_waiting_lock,
+ /* connected to QMP socket, waiting for greeting message */
+ qmp_state_connecting,
+ /* qmp_capabilities command sent, waiting for reply */
+ qmp_state_capability_negotiation,
+ /* sending user's cmd and waiting for reply */
+ qmp_state_waiting_reply,
+ /* ready to send commands */
+ qmp_state_connected,
+} libxl__qmp_state;
+
+struct libxl__ev_qmp {
+ /* caller should include this in their own struct */
+ /* caller must fill these in, and they must all remain valid */
+ libxl__ao *ao;
+ libxl_domid domid;
+ libxl__ev_qmp_callback *callback;
+ int payload_fd; /* set to send a fd with the command, -1 otherwise */
+
+ /* read-only when Connected
+ * and not to be accessed by the caller otherwise */
+ struct {
+ int major;
+ int minor;
+ int micro;
+ } qemu_version;
+
+ /*
+ * remaining fields are private to libxl_ev_qmp_*
+ */
+
+ libxl__carefd *cfd;
+ libxl__ev_fd efd;
+ libxl__qmp_state state;
+ libxl__ev_slowlock lock;
+ libxl__ev_immediate ei;
+ int rc;
+ int id;
+ int next_id; /* next id to use */
+ /* receive buffer */
+ char *rx_buf;
+ size_t rx_buf_size; /* current allocated size */
+ size_t rx_buf_used; /* actual data in the buffer */
+ /* sending buffer */
+ char *tx_buf;
+ size_t tx_buf_len; /* tx_buf size */
+ size_t tx_buf_off; /* already sent */
+ /* The message to send when ready */
+ char *msg;
+ int msg_id;
+};
+
+/* QMP parameters helpers */
+
+_hidden void libxl__qmp_param_add_string(libxl__gc *gc,
+ libxl__json_object **param,
+ const char *name, const char *s);
+_hidden void libxl__qmp_param_add_bool(libxl__gc *gc,
+ libxl__json_object **param,
+ const char *name, bool b);
+_hidden void libxl__qmp_param_add_integer(libxl__gc *gc,
+ libxl__json_object **param,
+ const char *name, const int i);
+#define QMP_PARAMETERS_SPRINTF(args, name, format, ...) \
+ libxl__qmp_param_add_string(gc, args, name, \
+ GCSPRINTF(format, __VA_ARGS__))
+
+
+/*
+ * evgen structures, which are the state we use for generating
+ * events for the caller.
+ *
+ * In general in each case there's an internal and an external
+ * version of the _evdisable_FOO function; the internal one is
+ * used during cleanup.
+ */
+struct libxl__evgen_domain_death {
+ uint32_t domid;
+ unsigned shutdown_reported:1, death_reported:1;
+ LIBXL_TAILQ_ENTRY(libxl_evgen_domain_death) entry;
+ /* on list .death_reported ? CTX->death_list : CTX->death_reported */
+ libxl_ev_user user;
+};
+_hidden void
+libxl__evdisable_domain_death(libxl__gc*, libxl_evgen_domain_death*);
+
+struct libxl__evgen_disk_eject {
+ libxl__ev_xswatch watch;
+ uint32_t domid;
+ LIBXL_LIST_ENTRY(libxl_evgen_disk_eject) entry;
+ libxl_ev_user user;
+ char *vdev, *be_ptr_path;
+};
+_hidden void
+libxl__evdisable_disk_eject(libxl__gc*, libxl_evgen_disk_eject*);
+
+typedef struct libxl__poller libxl__poller;
+struct libxl__poller {
+ /*
+ * These are used to allow other threads to wake up a thread which
+ * may be stuck in poll, because whatever it was waiting for
+ * hadn't happened yet. Threads which generate events will write
+ * a byte to each pipe. A thread which is waiting will empty its
+ * own pipe, and put its poller on the pollers_event list, before
+ * releasing the ctx lock and going into poll; when it comes out
+ * of poll it will take the poller off the pollers_event list.
+ *
+ * A thread which is waiting for completion of a synchronous ao
+ * will allocate a poller and record it in the ao, so that other
+ * threads can wake it up.
+ *
+ * When a thread is done with a poller it should put it onto
+ * pollers_idle, where it can be reused later.
+ *
+ * The "poller_app" is never idle, but is sometimes on
+ * pollers_event.
+ */
+ LIBXL_LIST_ENTRY(libxl__poller) entry;
+
+ struct pollfd *fd_polls;
+ int fd_polls_allocd;
+
+ int fd_rindices_allocd;
+ int (*fd_rindices)[3]; /* see libxl_event.c:beforepoll_internal */
+
+ int wakeup_pipe[2]; /* 0 means no fd allocated */
+ bool pipe_nonempty;
+
+ /*
+ * We also use the poller to record whether any fds have been
+ * deregistered since we entered poll. Each poller which is not
+ * idle is on the list pollers_active. fds_deregistered is
+ * cleared by beforepoll, and tested by afterpoll. Whenever an fd
+ * event is deregistered, we set the fds_deregistered of all non-idle
+ * pollers. So afterpoll can tell whether any POLLNVAL is
+ * plausibly due to an fd being closed and reopened.
+ *
+ * Additionally, we record whether any fd or time event sources
+ * have been registered. This is necessary because sometimes we
+ * need to wake up the only libxl thread stuck in
+ * eventloop_iteration so that it will pick up new fds or earlier
+ * timeouts. osevents_added is cleared by beforepoll, and set by
+ * fd or timeout event registration. When we are about to leave
+ * libxl (strictly, when we are about to give up an egc), we check
+ * whether there are any pollers. If there are, then at least one
+ * of them must have osevents_added clear. If not, we wake up the
+ * first one on the list. Any entry on pollers_active constitutes
+ * a promise to also make this check, so the baton will never be
+ * dropped.
+ */
+ LIBXL_LIST_ENTRY(libxl__poller) active_entry;
+ bool fds_deregistered;
+ bool osevents_added;
+};
+
+struct libxl__gc {
+ /* mini-GC */
+ int alloc_maxsize; /* -1 means this is the dummy non-gc gc */
+ void **alloc_ptrs;
+ libxl_ctx *owner;
+};
+
+struct libxl__ctx {
+ xentoollog_logger *lg;
+ xc_interface *xch;
+ struct xs_handle *xsh;
+ libxl__gc nogc_gc;
+
+ const libxl_event_hooks *event_hooks;
+ void *event_hooks_user;
+
+ pthread_mutex_t lock; /* protects data structures hanging off the ctx */
+ /* Always use libxl__ctx_lock and _unlock (or the convenience
+ * macors CTX_LOCK and CTX_UNLOCK) to manipulate this.
+ *
+ * You may acquire this mutex recursively if it is convenient to
+ * do so. You may not acquire this lock at the same time as any
+ * other lock. If you need to call application code outside
+ * libxl (ie, a callback) with this lock held then it is
+ * necessaray to impose restrictions on the caller to maintain a
+ * proper lock hierarchy, and these restrictions must then be
+ * documented in the libxl public interface.
+ */
+
+ LIBXL_TAILQ_HEAD(libxl__event_list, libxl_event) occurred;
+
+ int osevent_in_hook;
+ const libxl_osevent_hooks *osevent_hooks;
+ void *osevent_user;
+ /* See the comment for OSEVENT_HOOK_INTERN in libxl_event.c
+ * for restrictions on the use of the osevent fields. */
+
+ libxl__poller *poller_app; /* libxl_osevent_beforepoll and _afterpoll */
+ LIBXL_LIST_HEAD(, libxl__poller) pollers_event, pollers_idle;
+ LIBXL_LIST_HEAD(, libxl__poller) pollers_active;
+
+ LIBXL_SLIST_HEAD(libxl__osevent_hook_nexi, libxl__osevent_hook_nexus)
+ hook_fd_nexi_idle, hook_timeout_nexi_idle;
+ LIBXL_LIST_HEAD(, libxl__ev_fd) efds;
+ LIBXL_TAILQ_HEAD(, libxl__ev_time) etimes;
+
+ libxl__ev_watch_slot *watch_slots;
+ int watch_nslots, nwatches;
+ LIBXL_SLIST_HEAD(, libxl__ev_watch_slot) watch_freeslots;
+ uint32_t watch_counter; /* helps disambiguate slot reuse */
+ libxl__ev_fd watch_efd;
+
+ xenevtchn_handle *xce; /* waiting must be done only with libxl__ev_evtchn* */
+ LIBXL_LIST_HEAD(, libxl__ev_evtchn) evtchns_waiting;
+ libxl__ev_fd evtchn_efd;
+
+ LIBXL_LIST_HEAD(, libxl__ao) aos_inprogress;
+
+ LIBXL_TAILQ_HEAD(libxl__evgen_domain_death_list, libxl_evgen_domain_death)
+ death_list /* sorted by domid */,
+ death_reported;
+ libxl__ev_xswatch death_watch;
+
+ LIBXL_LIST_HEAD(, libxl_evgen_disk_eject) disk_eject_evgens;
+
+ const libxl_childproc_hooks *childproc_hooks;
+ void *childproc_user;
+ int sigchld_selfpipe[2]; /* [0]==-1 means handler not installed */
+ libxl__ev_fd sigchld_selfpipe_efd;
+ LIBXL_LIST_HEAD(, libxl__ev_child) children;
+ bool sigchld_user_registered;
+ LIBXL_LIST_ENTRY(libxl_ctx) sigchld_users_entry;
+
+ libxl_version_info version_info;
+
+ bool libxl_domain_need_memory_0x041200_called,
+ libxl_domain_need_memory_called;
+};
+
+/*
+ * libxl__device is a transparent structure that doesn't contain private fields
+ * or external memory references, and as such can be copied by assignment.
+ */
+typedef struct {
+ uint32_t backend_devid;
+ uint32_t backend_domid;
+ uint32_t devid;
+ uint32_t domid;
+ libxl__device_kind backend_kind;
+ libxl__device_kind kind;
+} libxl__device;
+
+/* Used to know if backend of given device is QEMU */
+#define QEMU_BACKEND(dev) (\
+ (dev)->backend_kind == LIBXL__DEVICE_KIND_QDISK || \
+ (dev)->backend_kind == LIBXL__DEVICE_KIND_VFB || \
+ (dev)->backend_kind == LIBXL__DEVICE_KIND_QUSB || \
+ (dev)->backend_kind == LIBXL__DEVICE_KIND_9PFS || \
+ (dev)->backend_kind == LIBXL__DEVICE_KIND_VKBD)
+
+#define XC_PCI_BDF "0x%x, 0x%x, 0x%x, 0x%x"
+#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
+#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
+#define PCI_FUNC(devfn) ((devfn) & 0x07)
+#define AUTO_PHP_SLOT 0x100
+
+#define PROC_PCI_NUM_RESOURCES 7
+#define PCI_BAR_IO 0x01
+
+#define PRINTF_ATTRIBUTE(x, y) __attribute__((format(printf, x, y)))
+
+struct libxl__egc {
+ /* For event-generating functions only.
+ * The egc and its gc may be accessed only on the creating thread. */
+ struct libxl__gc gc;
+ struct libxl__event_list occurred_for_callback;
+ LIBXL_TAILQ_HEAD(, libxl__ao) aos_for_callback;
+ LIBXL_TAILQ_HEAD(, libxl__aop_occurred) aops_for_callback;
+ LIBXL_STAILQ_HEAD(, libxl__ev_immediate) ev_immediates;
+};
+
+struct libxl__aop_occurred {
+ /*
+ * An aop belongs to, and may be accessed only on, the thread
+ * which created it. It normally lives in that thread's egc.
+ *
+ * While an aop exists, it corresponds to one refcount in
+ * ao->progress_reports_outstanding, preventing ao destruction.
+ */
+ LIBXL_TAILQ_ENTRY(libxl__aop_occurred) entry;
+ libxl__ao *ao;
+ libxl_event *ev;
+ const libxl_asyncprogress_how *how;
+};
+
+#define LIBXL__AO_MAGIC 0xA0FACE00ul
+#define LIBXL__AO_MAGIC_DESTROYED 0xA0DEAD00ul
+
+struct libxl__ao {
+ /*
+ * An ao and its gc may be accessed only with the ctx lock held.
+ *
+ * Special exception: If an ao has been added to
+ * egc->aos_for_callback, the thread owning the egc may remove the
+ * ao from that list and make the callback without holding the
+ * lock.
+ *
+ * Corresponding restriction: An ao may be added only to one
+ * egc->aos_for_callback, once; rc and how must already have been
+ * set and may not be subsequently modified. (This restriction is
+ * easily and obviously met since the ao is queued for callback
+ * only in libxl__ao_complete.)
+ */
+ uint32_t magic;
+ unsigned constructing:1, in_initiator:1, complete:1, notified:1,
+ aborting:1;
+ int manip_refcnt;
+ libxl__ao *nested_root;
+ int nested_progeny;
+ int progress_reports_outstanding;
+ int rc;
+ LIBXL_LIST_HEAD(, libxl__ao_abortable) abortables;
+ LIBXL_LIST_ENTRY(libxl__ao) inprogress_entry;
+ libxl__gc gc;
+ libxl_asyncop_how how;
+ libxl__poller *poller;
+ uint32_t domid;
+ LIBXL_TAILQ_ENTRY(libxl__ao) entry_for_callback;
+ int outstanding_killed_child;
+};
+
+#define LIBXL_INIT_GC(gc,ctx) do{ \
+ (gc).alloc_maxsize = 0; \
+ (gc).alloc_ptrs = 0; \
+ (gc).owner = (ctx); \
+ } while(0)
+ /* NB, also, a gc struct ctx->nogc_gc is initialised in libxl_ctx_alloc */
+
+static inline libxl_ctx *libxl__gc_owner(libxl__gc *gc)
+{
+ return gc->owner;
+}
+
+static inline int libxl__gc_is_real(const libxl__gc *gc)
+{
+ return gc->alloc_maxsize >= 0;
+}
+
+/*
+ * Memory allocation tracking/helpers
+ *
+ * See comment "libxl memory management" in libxl.h for a description
+ * of the framework which these calls belong to.
+ *
+ * These functions deal with memory allocations of type (a) and (d) in
+ * that description.
+ *
+ * All pointers returned by these functions are registered for garbage
+ * collection on exit from the outermost libxl callframe.
+ *
+ * However, where the argument is stated to be "gc_opt", &ctx->nogc_gc
+ * may be passed instead, in which case no garbage collection will
+ * occur; the pointer must later be freed with free(). (Passing NULL
+ * for gc_opt is not permitted.) This is for memory allocations of
+ * types (b) and (c). The convenience macro NOGC should be used where
+ * possible.
+ *
+ * NOGC (and ctx->nogc_gc) may ONLY be used with functions which
+ * explicitly declare that it's OK. Use with nonconsenting functions
+ * may result in leaks of those functions' internal allocations on the
+ * psuedo-gc.
+ */
+/* register ptr in gc for free on exit from outermost libxl callframe. */
+
+#define NN(...) __attribute__((nonnull(__VA_ARGS__)))
+#define NN1 __attribute__((nonnull(1)))
+ /* It used to be legal to pass NULL for gc_opt. Get the compiler to
+ * warn about this if any slip through. */
+
+_hidden void libxl__ptr_add(libxl__gc *gc_opt, void *ptr /* may be NULL */) NN1;
+/* if this is the outermost libxl callframe then free all pointers in @gc */
+_hidden void libxl__free_all(libxl__gc *gc);
+/* allocate @size bytes. (a gc'd malloc(3)) */
+_hidden void *libxl__malloc(libxl__gc *gc_opt, size_t size) NN1;
+/* allocate and zero @size. (similar to a gc'd malloc(3)+memzero()) */
+_hidden void *libxl__zalloc(libxl__gc *gc_opt, size_t size) NN1;
+/* allocate and zero memory for an array of @nmemb members of @size each.
+ * (similar to a gc'd calloc(3)). */
+_hidden void *libxl__calloc(libxl__gc *gc_opt, size_t nmemb, size_t size) NN1;
+/* change the size of the memory block pointed to by @ptr to @new_size bytes.
+ * unlike other allocation functions here any additional space between the
+ * oldsize and @new_size is not initialised (similar to a gc'd realloc(3)).
+ * if @ptr is non-NULL and @gc_opt is not nogc_gc then @ptr must have been
+ * registered with @gc_opt previously. */
+_hidden void *libxl__realloc(libxl__gc *gc_opt, void *ptr, size_t new_size) NN1;
+/* print @fmt into an allocated string large enoughto contain the result.
+ * (similar to gc'd asprintf(3)). */
+_hidden char *libxl__sprintf(libxl__gc *gc_opt, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3) NN1;
+_hidden char *libxl__vsprintf(libxl__gc *gc, const char *format, va_list ap) PRINTF_ATTRIBUTE(2, 0);
+/* duplicate the string @c (similar to a gc'd strdup(3)). */
+_hidden char *libxl__strdup(libxl__gc *gc_opt,
+ const char *c /* may be NULL */) NN1;
+/* duplicate at most @n bytes of string @c (similar to a gc'd strndup(3)). */
+_hidden char *libxl__strndup(libxl__gc *gc_opt,
+ const char *c /* may be NULL */,
+ size_t n) NN1;
+/* strip the last path component from @s and return as a newly allocated
+ * string. (similar to a gc'd dirname(3)). */
+_hidden char *libxl__dirname(libxl__gc *gc_opt, const char *s) NN1;
+
+/* Make a pipe and set both ends nonblocking. On error, nothing
+ * is left open and both fds[]==-1, and a message is logged.
+ * Useful for self-pipes. */
+_hidden int libxl__pipe_nonblock(libxl_ctx *ctx, int fds[2]);
+/* Closes the pipe fd(s). Either or both of fds[] may be -1 meaning
+ * `not open'. Ignores any errors. Sets fds[] to -1. */
+_hidden void libxl__pipe_close(int fds[2]);
+
+/* Change the flags for the file description associated with fd to
+ * (flags & mask) | val.
+ * If r_oldflags != NULL then sets *r_oldflags to the original set of
+ * flags.
+ */
+_hidden int libxl__fd_flags_modify_save(libxl__gc *gc, int fd,
+ int mask, int val, int *r_oldflags);
+/* Restores the flags for the file description associated with fd to
+ * to the previous value (returned by libxl__fd_flags_modify_save)
+ */
+_hidden int libxl__fd_flags_restore(libxl__gc *gc, int fd, int old_flags);
+
+/* Each of these logs errors and returns a libxl error code.
+ * They do not mind if path is already removed.
+ * For _file, path must not be a directory; for _directory it must be. */
+_hidden int libxl__remove_file(libxl__gc *gc, const char *path);
+_hidden int libxl__remove_directory(libxl__gc *gc, const char *path);
+_hidden int libxl__remove_file_or_directory(libxl__gc *gc, const char *path);
+
+
+_hidden char **libxl__xs_kvs_of_flexarray(libxl__gc *gc, flexarray_t *array);
+
+/* treats kvs as pairs of keys and values and writes each to dir. */
+_hidden int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t,
+ const char *dir, char **kvs);
+/* as writev but also sets the permissions on each path */
+_hidden int libxl__xs_writev_perms(libxl__gc *gc, xs_transaction_t t,
+ const char *dir, char *kvs[],
+ struct xs_permissions *perms,
+ unsigned int num_perms);
+/* _atonce creates a transaction and writes all keys at once */
+_hidden int libxl__xs_writev_atonce(libxl__gc *gc,
+ const char *dir, char **kvs);
+ /* Each fn returns 0 on success.
+ * On error: returns -1, sets errno (no logging) */
+
+_hidden char *libxl__xs_get_dompath(libxl__gc *gc, uint32_t domid);
+ /* On error: logs, returns NULL, sets errno. */
+
+_hidden char *libxl__xs_read(libxl__gc *gc, xs_transaction_t t,
+ const char *path);
+_hidden char **libxl__xs_directory(libxl__gc *gc, xs_transaction_t t,
+ const char *path, unsigned int *nb);
+ /* On error: returns NULL, sets errno (no logging) */
+_hidden char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t domid);
+
+_hidden int libxl__backendpath_parse_domid(libxl__gc *gc, const char *be_path,
+ libxl_domid *domid_out);
+
+/*----- "checked" xenstore access functions -----*/
+/* Each of these functions will check that it succeeded; if it
+ * fails it logs and returns ERROR_FAIL.
+ */
+
+int libxl__xs_vprintf(libxl__gc *gc, xs_transaction_t t,
+ const char *path, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(4, 0);
+int libxl__xs_printf(libxl__gc *gc, xs_transaction_t t,
+ const char *path, const char *fmt, ...) PRINTF_ATTRIBUTE(4, 5);
+
+/* On success, path will exist and will have an empty value */
+int libxl__xs_mknod(libxl__gc *gc, xs_transaction_t t,
+ const char *path, struct xs_permissions *perms,
+ unsigned int num_perms);
+
+/* On success, *result_out came from the gc.
+ * On error, *result_out is undefined.
+ * ENOENT is regarded as error.
+ */
+int libxl__xs_read_mandatory(libxl__gc *gc, xs_transaction_t t,
+ const char *path, const char **result_out);
+
+/* On success, *result_out came from the gc.
+ * On error, *result_out is undefined.
+ * ENOENT counts as success but sets *result_out=0
+ */
+int libxl__xs_read_checked(libxl__gc *gc, xs_transaction_t t,
+ const char *path, const char **result_out);
+
+/* Does not include a trailing null.
+ * May usefully be combined with GCSPRINTF if the format string
+ * behaviour of libxl__xs_printf is desirable. */
+int libxl__xs_write_checked(libxl__gc *gc, xs_transaction_t t,
+ const char *path, const char *string);
+
+/* ENOENT is not an error (even if the parent directories don't exist) */
+int libxl__xs_rm_checked(libxl__gc *gc, xs_transaction_t t, const char *path);
+
+/* Transaction functions, best used together.
+ * The caller should initialise *t to 0 (XBT_NULL) before calling start.
+ * Each function leaves *t!=0 iff the transaction needs cleaning up.
+ *
+ * libxl__xs_transaction_commit returns:
+ * <0 failure - a libxl error code
+ * +1 commit conflict; transaction has been destroyed and caller
+ * must go round again (call _start again and retry)
+ * 0 committed successfully
+ *
+ * The intended usage pattern looks like this:
+ * int some_function()
+ * {
+ * int rc;
+ * xs_transaction_t t = 0;
+ * // other initialisations
+ *
+ * // do whatever you need to do before the xenstore stuff
+ * // errors? set rc and goto out.
+ *
+ * for (;;) {
+ * rc = libxl__xs_transaction_start(gc, &t);
+ * if (rc) goto out;
+ *
+ * // do your work here, including all xenstore reads and writes
+ * // libxl__xs_*_checked are useful; pass them t.
+ * // errors? set rc and goto out.
+ *
+ * rc = libxl__xs_transaction_commit(gc, &t);
+ * if (!rc) break;
+ * if (rc<0) goto out;
+ * }
+ *
+ * // now the xenstore transaction succeeded
+ * // do whatever else you need to do
+ * // errors? set rc and goto out.
+ *
+ * return something;
+ *
+ * out:
+ * // other cleanups
+ * libxl__xs_transaction_abort(gc, &t);
+ * // other cleanups
+ * return rc;
+ * }
+ *
+ * Formally the states of *t are:
+ *
+ * name value of *t description
+ * Idle 0 no transaction exists
+ * Ready non-0 ready for work, nothing done yet
+ * Busy non-0 writes have been made but we are not finished
+ * Uncommitted non-0 writes have been made and should be committed
+ *
+ * libxl__xs_transaction_start: Idle -> Ready (on error: Idle)
+ *
+ * The transaction goes from Ready to Busy, and from Busy to
+ * Uncommitted, by the use of xenstore read and write operations
+ * (libxl__xs_..., xs_...) made by libxl__xs_transaction's caller.
+ *
+ * libxl__xs_transaction_commit: Ready/Uncommitted -> Idle
+ * on success (returns 0): xenstore has been updated
+ * on error (<0) or conflict (+1): updates discarded
+ *
+ * libxl__xs_transaction_abort: Any -> Idle (any updates discarded)
+ */
+int libxl__xs_transaction_start(libxl__gc *gc, xs_transaction_t *t);
+int libxl__xs_transaction_commit(libxl__gc *gc, xs_transaction_t *t);
+void libxl__xs_transaction_abort(libxl__gc *gc, xs_transaction_t *t);
+
+
+
+/*
+ * This is a recursive delete, from top to bottom. What this function does
+ * is remove empty folders that contained the deleted entry.
+ *
+ * It mimics xenstore-rm -t behaviour.
+ */
+_hidden int libxl__xs_path_cleanup(libxl__gc *gc, xs_transaction_t t,
+ const char *user_path);
+
+/*
+ * Event generation functions provided by the libxl event core to the
+ * rest of libxl. Implemented in terms of _beforepoll/_afterpoll
+ * and/or the fd registration machinery, as provided by the
+ * application.
+ *
+ * Semantics are similar to those of the fd and timeout registration
+ * functions provided to libxl_osevent_register_hooks.
+ *
+ * Non-0 returns from libxl__ev_{modify,deregister} have already been
+ * logged by the core and should be returned unmodified to libxl's
+ * caller; NB that they may be valid libxl error codes but they may
+ * also be positive numbers supplied by the caller.
+ *
+ * In each case, there is a libxl__ev_FOO structure which can be in
+ * one of three states:
+ *
+ * Undefined - Might contain anything. All-bits-zero is
+ * an undefined state.
+ *
+ * Idle - Struct contents are defined enough to pass to any
+ * libxl__ev_FOO function but not registered and
+ * callback will not be called. The struct does not
+ * contain references to any allocated resources so
+ * can be thrown away.
+ *
+ * Active - Request for events has been registered and events
+ * may be generated. _deregister must be called to
+ * reclaim resources.
+ *
+ * These functions are provided for each kind of event KIND:
+ *
+ * int libxl__ev_KIND_register(libxl__gc *gc, libxl__ev_KIND *GEN,
+ * libxl__ev_KIND_callback *FUNC,
+ * DETAILS);
+ * On entry *GEN must be in state Undefined or Idle.
+ * Returns a libxl error code; on error return *GEN is Idle.
+ * On successful return *GEN is Active and FUNC wil be
+ * called by the event machinery in future. FUNC will
+ * not be called from within the call to _register.
+ * FUNC will be called with the context locked (with CTX_LOCK).
+ *
+ * void libxl__ev_KIND_deregister(libxl__gc *gc, libxl__ev_KIND *GEN_upd);
+ * On entry *GEN must be in state Active or Idle.
+ * On return it is Idle. (Idempotent.)
+ *
+ * void libxl__ev_KIND_init(libxl__ev_KIND *GEN);
+ * Provided for initialising an Undefined KIND.
+ * On entry *GEN must be in state Idle or Undefined.
+ * On return it is Idle. (Idempotent.)
+ *
+ * int libxl__ev_KIND_isregistered(const libxl__ev_KIND *GEN);
+ * On entry *GEN must be Idle or Active.
+ * Returns nonzero if it is Active, zero otherwise.
+ * Cannot fail.
+ *
+ * int libxl__ev_KIND_modify(libxl__gc*, libxl__ev_KIND *GEN,
+ * DETAILS);
+ * Only provided for some kinds of generator.
+ * On entry *GEN must be Active and on return, whether successful
+ * or not, it will be Active.
+ * Returns a libxl error code; on error the modification
+ * is not effective.
+ *
+ * All of these functions are fully threadsafe and may be called by
+ * general code in libxl even from within event callback FUNCs.
+ * The ctx will be locked on entry to each FUNC and FUNC should not
+ * unlock it.
+ *
+ * Callers of libxl__ev_KIND_register must ensure that the
+ * registration is undone, with _deregister, in libxl_ctx_free.
+ * This means that normally each kind of libxl__evgen (ie each
+ * application-requested event source) needs to be on a list so that
+ * it can be automatically deregistered as promised in libxl_event.h.
+ */
+
+
+_hidden int libxl__ev_fd_register(libxl__gc*, libxl__ev_fd *ev_out,
+ libxl__ev_fd_callback*,
+ int fd, short events /* as for poll(2) */);
+_hidden int libxl__ev_fd_modify(libxl__gc*, libxl__ev_fd *ev,
+ short events);
+_hidden void libxl__ev_fd_deregister(libxl__gc*, libxl__ev_fd *ev);
+static inline void libxl__ev_fd_init(libxl__ev_fd *efd)
+ { efd->fd = -1; }
+static inline int libxl__ev_fd_isregistered(const libxl__ev_fd *efd)
+ { return efd->fd >= 0; }
+
+_hidden int libxl__ev_time_register_rel(libxl__ao*, libxl__ev_time *ev_out,
+ libxl__ev_time_callback*,
+ int milliseconds /* as for poll(2) */);
+_hidden int libxl__ev_time_register_abs(libxl__ao*, libxl__ev_time *ev_out,
+ libxl__ev_time_callback*,
+ struct timeval);
+_hidden int libxl__ev_time_modify_rel(libxl__gc*, libxl__ev_time *ev,
+ int milliseconds /* as for poll(2) */);
+_hidden int libxl__ev_time_modify_abs(libxl__gc*, libxl__ev_time *ev,
+ struct timeval);
+_hidden void libxl__ev_time_deregister(libxl__gc*, libxl__ev_time *ev);
+static inline void libxl__ev_time_init(libxl__ev_time *ev)
+ { ev->func = 0; libxl__ao_abortable_init(&ev->abrt); }
+static inline int libxl__ev_time_isregistered(const libxl__ev_time *ev)
+ { return !!ev->func; }
+
+
+_hidden int libxl__ev_xswatch_register(libxl__gc*, libxl__ev_xswatch *xsw_out,
+ libxl__ev_xswatch_callback*,
+ const char *path /* copied */);
+_hidden void libxl__ev_xswatch_deregister(libxl__gc *gc, libxl__ev_xswatch*);
+
+static inline void libxl__ev_xswatch_init(libxl__ev_xswatch *xswatch_out)
+ { xswatch_out->slotnum = -1; }
+static inline int libxl__ev_xswatch_isregistered(const libxl__ev_xswatch *xw)
+ { return xw->slotnum >= 0; }
+
+
+/*
+ * The evtchn facility is one-shot per call to libxl__ev_evtchn_wait.
+ * You should:
+ * Use libxl__ctx_evtchn_init to make sure CTX->xce is valid;
+ * Call some suitable xc bind function on (or to obtain) the port;
+ * Then call libxl__ev_evtchn_wait.
+ *
+ * When the event is signaled then the callback will be made, once.
+ * Then you must call libxl__ev_evtchn_wait again, if desired.
+ *
+ * You must NOT call xenevtchn_unmask. wait will do that for you.
+ *
+ * Calling libxl__ev_evtchn_cancel will arrange for libxl to disregard
+ * future occurrences of event. Both libxl__ev_evtchn_wait and
+ * libxl__ev_evtchn_cancel are idempotent.
+ *
+ * (Note of course that an event channel becomes signaled when it is
+ * first bound, so you will get one call to libxl__ev_evtchn_wait
+ * "right away"; unless you have won a very fast race, the condition
+ * you were waiting for won't exist yet so when you check for it
+ * you'll find you need to call wait again.)
+ *
+ * You must not wait on the same port twice at once (that is, with
+ * two separate libxl__ev_evtchn's).
+ */
+_hidden int libxl__ev_evtchn_wait(libxl__gc*, libxl__ev_evtchn *evev);
+_hidden void libxl__ev_evtchn_cancel(libxl__gc *gc, libxl__ev_evtchn *evev);
+
+static inline void libxl__ev_evtchn_init(libxl__ev_evtchn *evev)
+ { evev->waiting = 0; }
+static inline bool libxl__ev_evtchn_iswaiting(const libxl__ev_evtchn *evev)
+ { return evev->waiting; }
+
+_hidden int libxl__ctx_evtchn_init(libxl__gc *gc); /* for libxl_ctx_alloc */
+
+/*
+ * For making subprocesses. This is the only permitted mechanism for
+ * code in libxl to do so.
+ *
+ * In the parent, returns the pid, filling in childw_out.
+ * In the child, returns 0.
+ * If it fails, returns a libxl error (all of which are -ve).
+ *
+ * The child should go on to exec (or exit) soon. The child may not
+ * make any further calls to libxl infrastructure, except for memory
+ * allocation and logging. If the child needs to use xenstore it
+ * must open its own xs handle and use it directly, rather than via
+ * the libxl event machinery.
+ *
+ * The parent may signal the child but it must not reap it. That will
+ * be done by the event machinery.
+ *
+ * The child death event will generate exactly one event callback; until
+ * then the childw is Active and may not be reused.
+ *
+ * libxl__ev_child_kill_deregister: Active -> Idle
+ * This will transfer ownership of the child process death event from
+ * `ch' to `ao', thus deregister the callback.
+ * The `ao' completion will wait until the child have been reaped by the
+ * event machinery.
+ */
+_hidden pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *childw_out,
+ libxl__ev_child_callback *death);
+static inline void libxl__ev_child_init(libxl__ev_child *childw_out)
+ { childw_out->pid = -1; }
+static inline int libxl__ev_child_inuse(const libxl__ev_child *childw_out)
+ { return childw_out->pid >= 0; }
+_hidden void libxl__ev_child_kill_deregister(libxl__ao *ao,
+ libxl__ev_child *ch,
+ int sig);
+
+/* Useable (only) in the child to once more make the ctx useable for
+ * xenstore operations. logs failure in the form "what: <error
+ * message>". */
+_hidden int libxl__ev_child_xenstore_reopen(libxl__gc *gc, const char *what);
+
+
+/*
+ * Other event-handling support provided by the libxl event core to
+ * the rest of libxl.
+ */
+
+_hidden void libxl__event_occurred(libxl__egc*, libxl_event *event);
+ /* Arranges to notify the application that the event has occurred.
+ * event should be suitable for passing to libxl_event_free. */
+
+_hidden libxl_event *libxl__event_new(libxl__egc*, libxl_event_type,
+ uint32_t domid,
+ libxl_ev_user for_user);
+ /* Convenience function.
+ * Allocates a new libxl_event, fills in domid and type.
+ * Cannot fail. */
+
+#define NEW_EVENT(egc, type, domid, user) \
+ libxl__event_new((egc), LIBXL_EVENT_TYPE_##type, (domid), (user))
+ /* Convenience macro. */
+
+/*
+ * In general, call this via the macro LIBXL__EVENT_DISASTER.
+ *
+ * Event-generating functions, or ao machinery, may call this if they
+ * might have wanted to generate an event (either an internal one ie a
+ * libxl__ev_FOO_callback or an application event), but are prevented
+ * from doing so due to eg lack of memory.
+ *
+ * NB that this function may return and the caller isn't supposed to
+ * then crash, although it may fail (and henceforth leave things in a
+ * state where many or all calls fail).
+ */
+_hidden void libxl__event_disaster(libxl__gc*, const char *msg, int errnoval,
+ libxl_event_type type /* may be 0 */,
+ const char *file, int line,
+ const char *func);
+#define LIBXL__EVENT_DISASTER(gc, msg, errnoval, type) \
+ libxl__event_disaster(gc, msg, errnoval, type, __FILE__,__LINE__,__func__)
+
+
+/* Fills in, or disposes of, the resources held by, a poller whose
+ * space the caller has allocated. ctx must be locked. */
+_hidden int libxl__poller_init(libxl__gc *gc, libxl__poller *p);
+_hidden void libxl__poller_dispose(libxl__poller *p);
+
+/* Obtain a fresh poller from malloc or the idle list, and put it
+ * away again afterwards. _get can fail, returning NULL.
+ * ctx must be locked. */
+_hidden libxl__poller *libxl__poller_get(libxl__gc *gc);
+_hidden void libxl__poller_put(libxl_ctx*, libxl__poller *p /* may be NULL */);
+
+/* Notifies whoever is polling using p that they should wake up.
+ * ctx must be locked. */
+_hidden void libxl__poller_wakeup(libxl__gc *egc, libxl__poller *p);
+
+/* Internal to fork and child reaping machinery */
+extern const libxl_childproc_hooks libxl__childproc_default_hooks;
+int libxl__sigchld_needed(libxl__gc*); /* non-reentrant idempotent, logs errs */
+void libxl__sigchld_notneeded(libxl__gc*); /* non-reentrant idempotent */
+void libxl__sigchld_check_stale_handler(void);
+int libxl__self_pipe_wakeup(int fd); /* returns 0 or -1 setting errno */
+int libxl__self_pipe_eatall(int fd); /* returns 0 or -1 setting errno */
+
+
+_hidden int libxl__atfork_init(libxl_ctx *ctx);
+
+
+/* File references */
+typedef struct {
+ /*
+ * Path is always set if the file reference is valid. However if
+ * mapped is true then the actual file may already be unlinked.
+ */
+ const char * path;
+ int mapped;
+ void * data;
+ size_t size;
+} libxl__file_reference;
+_hidden int libxl__file_reference_map(libxl__file_reference *f);
+_hidden int libxl__file_reference_unmap(libxl__file_reference *f);
+
+/* from xl_dom */
+_hidden libxl_domain_type libxl__domain_type(libxl__gc *gc, uint32_t domid);
+_hidden int libxl__domain_cpupool(libxl__gc *gc, uint32_t domid);
+_hidden libxl_scheduler libxl__domain_scheduler(libxl__gc *gc, uint32_t domid);
+_hidden int libxl__sched_set_params(libxl__gc *gc, uint32_t domid,
+ libxl_domain_sched_params *scparams);
+_hidden int libxl__grant_vga_iomem_permission(libxl__gc *gc, const uint32_t domid,
+ libxl_domain_config *const d_config);
+
+typedef struct {
+ uint32_t store_port;
+ uint32_t store_domid;
+ unsigned long store_mfn;
+
+ uint32_t console_port;
+ uint32_t console_domid;
+ unsigned long console_mfn;
+ char *console_tty;
+
+ char *saved_state;
+ int dm_monitor_fd;
+
+ libxl__file_reference pv_kernel;
+ libxl__file_reference pv_ramdisk;
+ const char *shim_path;
+ const char *shim_cmdline;
+ const char *pv_cmdline;
+
+ /*
+ * dm_runas: If set, pass qemu the `-runas` parameter with this
+ * string as an argument
+ * dm_kill_uid: If set, the devicemodel should be killed by
+ * destroying all processes with this uid.
+ */
+ char *dm_runas, *dm_kill_uid;
+
+ xen_vmemrange_t *vmemranges;
+ uint32_t num_vmemranges;
+
+ xen_pfn_t vuart_gfn;
+ evtchn_port_t vuart_port;
+
+ /* ARM only to deal with broken firmware */
+ uint32_t clock_frequency;
+
+ /* Whether this domain is being migrated/restored, or booting fresh. Only
+ * applicable to the primary domain, not support domains (e.g. stub QEMU). */
+ bool restore;
+} libxl__domain_build_state;
+
+_hidden void libxl__domain_build_state_init(libxl__domain_build_state *s);
+_hidden void libxl__domain_build_state_dispose(libxl__domain_build_state *s);
+
+_hidden int libxl__build_pre(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config * const d_config,
+ libxl__domain_build_state *state);
+_hidden int libxl__build_post(libxl__gc *gc, uint32_t domid,
+ libxl_domain_build_info *info, libxl__domain_build_state *state,
+ char **vms_ents, char **local_ents);
+
+_hidden int libxl__build_pv(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config *const d_config, libxl__domain_build_state *state);
+_hidden int libxl__build_hvm(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config *d_config,
+ libxl__domain_build_state *state);
+
+_hidden int libxl__qemu_traditional_cmd(libxl__gc *gc, uint32_t domid,
+ const char *cmd);
+_hidden int libxl__domain_rename(libxl__gc *gc, uint32_t domid,
+ const char *old_name, const char *new_name,
+ xs_transaction_t trans);
+
+/* Deprecated, use libxl__dm_resume instead. */
+_hidden int libxl__domain_resume_device_model_deprecated(libxl__gc *gc, uint32_t domid);
+
+_hidden const char *libxl__userdata_path(libxl__gc *gc, uint32_t domid,
+ const char *userdata_userid,
+ const char *wh);
+_hidden void libxl__userdata_destroyall(libxl__gc *gc, uint32_t domid);
+/* Caller must hold userdata store lock before calling
+ * libxl__userdata_{retrieve,store}
+ * See libxl__{un,}lock_domain_userdata.
+ */
+_hidden int libxl__userdata_retrieve(libxl__gc *gc, uint32_t domid,
+ const char *userdata_userid,
+ uint8_t **data_r, int *datalen_r);
+_hidden int libxl__userdata_store(libxl__gc *gc, uint32_t domid,
+ const char *userdata_userid,
+ const uint8_t *data, int datalen);
+
+/* Deprecated, use libxl__domain_resume instead */
+_hidden int libxl__domain_resume_deprecated(libxl__gc *gc, uint32_t domid,
+ int suspend_cancel);
+/* Deprecated, use libxl__domain_unpause instead */
+_hidden int libxl__domain_unpause_deprecated(libxl__gc *,
+ libxl_domid domid);
+
+/* Call libxl__dm_resume_init() and fill the first few fields,
+ * then call one of libxl__domain_resume / libxl__domain_unpause
+ * or directly libxl__dm_resume if only the device model needs to be
+ * "resumed". */
+struct libxl__dm_resume_state {
+ /* caller must fill these in, and they must all remain valid */
+ libxl__ao *ao;
+ libxl_domid domid;
+ void (*callback)(libxl__egc *, libxl__dm_resume_state *, int rc);
+
+ /* private to libxl__domain_resume and libxl__domain_unpause */
+ void (*dm_resumed_callback)(libxl__egc *,
+ libxl__dm_resume_state *, int rc);
+ /* private to libxl__domain_resume */
+ bool suspend_cancel;
+
+ /* private to libxl__dm_resume */
+ libxl__ev_qmp qmp;
+ libxl__ev_time time;
+ libxl__ev_xswatch watch;
+};
+_hidden void libxl__dm_resume(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs);
+_hidden void libxl__domain_resume(libxl__egc *egc,
+ libxl__dm_resume_state *dmrs,
+ bool suspend_cancel);
+_hidden void libxl__domain_unpause(libxl__egc *,
+ libxl__dm_resume_state *dmrs);
+
+/* returns 0 or 1, or a libxl error code */
+_hidden int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid);
+
+_hidden const char *libxl__domain_pvcontrol_xspath(libxl__gc*, uint32_t domid);
+_hidden char * libxl__domain_pvcontrol_read(libxl__gc *gc,
+ xs_transaction_t t, uint32_t domid);
+
+/* from xl_device */
+_hidden char *libxl__device_disk_string_of_backend(libxl_disk_backend backend);
+_hidden char *libxl__device_disk_string_of_format(libxl_disk_format format);
+_hidden const char *libxl__qemu_disk_format_string(libxl_disk_format format);
+_hidden int libxl__device_disk_set_backend(libxl__gc*, libxl_device_disk*);
+
+_hidden int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor);
+_hidden int libxl__device_disk_dev_number(const char *virtpath,
+ int *pdisk, int *ppartition);
+_hidden char *libxl__devid_to_vdev(libxl__gc *gc, int devid);
+
+_hidden int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
+ libxl__device_console *console,
+ libxl__domain_build_state *state,
+ libxl__device *device);
+_hidden int libxl__device_vuart_add(libxl__gc *gc, uint32_t domid,
+ libxl__device_console *console,
+ libxl__domain_build_state *state);
+
+/* Returns 1 if device exists, 0 if not, ERROR_* (<0) on error. */
+_hidden int libxl__device_exists(libxl__gc *gc, xs_transaction_t t,
+ libxl__device *device);
+_hidden int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t,
+ libxl__device *device, char **bents, char **fents, char **ro_fents);
+_hidden char *libxl__domain_device_frontend_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
+ libxl__device_kind device_kind);
+_hidden char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device);
+_hidden char *libxl__domain_device_backend_path(libxl__gc *gc, uint32_t backend_domid,
+ uint32_t domid, uint32_t devid,
+ libxl__device_kind device_kind);
+_hidden char *libxl__device_libxl_path(libxl__gc *gc, libxl__device *device);
+_hidden char *libxl__domain_device_libxl_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
+ libxl__device_kind device_kind);
+_hidden int libxl__parse_backend_path(libxl__gc *gc, const char *path,
+ libxl__device *dev);
+_hidden int libxl__console_tty_path(libxl__gc *gc, uint32_t domid, int cons_num,
+ libxl_console_type type, char **tty_path);
+_hidden int libxl__device_destroy(libxl__gc *gc, libxl__device *dev);
+_hidden int libxl__wait_for_backend(libxl__gc *gc, const char *be_path,
+ const char *state);
+_hidden int libxl__nic_type(libxl__gc *gc, libxl__device *dev,
+ libxl_nic_type *nictype);
+_hidden int libxl__init_console_from_channel(libxl__gc *gc,
+ libxl__device_console *console,
+ int dev_num,
+ libxl_device_channel *channel);
+_hidden int libxl__device_nextid(libxl__gc *gc, uint32_t domid,
+ libxl__device_kind device);
+_hidden int libxl__resolve_domid(libxl__gc *gc, const char *name,
+ uint32_t *domid);
+
+/*
+ * For each aggregate type which can be used as an input we provide:
+ *
+ * int libxl__<type>_setdefault(gc, <type> *p):
+ *
+ * Idempotently sets any members of "p" which is currently set to
+ * a special value indicating that the defaults should be used
+ * (per libxl_<type>_init) to a specific value.
+ *
+ * All libxl API functions are expected to have arranged for this
+ * to be called before using any values within these structures.
+ */
+_hidden int libxl__domain_config_setdefault(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ uint32_t domid /* logging only */);
+_hidden int libxl__domain_create_info_setdefault(libxl__gc *gc,
+ libxl_domain_create_info *c_info,
+ const libxl_physinfo *info);
+_hidden int libxl__domain_build_info_setdefault(libxl__gc *gc,
+ libxl_domain_build_info *b_info);
+_hidden void libxl__rdm_setdefault(libxl__gc *gc,
+ libxl_domain_build_info *b_info);
+
+_hidden int libxl__domain_need_memory_calculate(libxl__gc *gc,
+ libxl_domain_build_info *b_info,
+ uint64_t *need_memkb);
+
+_hidden const char *libxl__device_nic_devname(libxl__gc *gc,
+ uint32_t domid,
+ uint32_t devid,
+ libxl_nic_type type);
+
+_hidden int libxl__get_domid(libxl__gc *gc, uint32_t *domid);
+
+/*----- xswait: wait for a xenstore node to be suitable -----*/
+
+typedef struct libxl__xswait_state libxl__xswait_state;
+
+/*
+ * rc describes the circumstances of this callback:
+ *
+ * rc==0
+ *
+ * The xenstore path (may have) changed. It has been read for
+ * you. The result is in data (allocated from the ao gc).
+ * data may be NULL, which means that the xenstore read gave
+ * ENOENT.
+ *
+ * If you are satisfied, you MUST call libxl__xswait_stop.
+ * Otherwise, xswait will continue waiting and watching and
+ * will call you back later.
+ *
+ * rc==ERROR_TIMEDOUT, rc==ERROR_ABORTED
+ *
+ * The specified timeout was reached.
+ * This has NOT been logged (except to the debug log).
+ * xswait will not continue (but calling libxl__xswait_stop is OK).
+ *
+ * rc!=0, !=ERROR_TIMEDOUT, !=ERROR_ABORTED
+ *
+ * Some other error occurred.
+ * This HAS been logged.
+ * xswait will not continue (but calling libxl__xswait_stop is OK).
+ *
+ * xswait.path may start with with '@', in which case no read is done
+ * and the callback will always get data==0.
+ */
+typedef void libxl__xswait_callback(libxl__egc *egc,
+ libxl__xswait_state *xswa, int rc, const char *data);
+
+struct libxl__xswait_state {
+ /* caller must fill these in, and they must all remain valid */
+ libxl__ao *ao;
+ const char *what; /* for error msgs: noun phrase, what we're waiting for */
+ const char *path;
+ int timeout_ms; /* as for poll(2) */
+ libxl__xswait_callback *callback;
+ /* remaining fields are private to xswait */
+ libxl__ev_time time_ev;
+ libxl__ev_xswatch watch_ev;
+};
+
+void libxl__xswait_init(libxl__xswait_state*);
+void libxl__xswait_stop(libxl__gc*, libxl__xswait_state*); /*idempotent*/
+bool libxl__xswait_inuse(const libxl__xswait_state *ss);
+
+int libxl__xswait_start(libxl__gc*, libxl__xswait_state*);
+
+/*
+ * libxl__ev_devstate - waits a given time for a device to
+ * reach a given state. Follows the libxl_ev_* conventions.
+ * Will generate only one event, and after that is automatically
+ * cancelled.
+ */
+typedef struct libxl__ev_devstate libxl__ev_devstate;
+typedef void libxl__ev_devstate_callback(libxl__egc *egc, libxl__ev_devstate*,
+ int rc);
+ /* rc will be 0, ERROR_TIMEDOUT, ERROR_ABORTED, ERROR_INVAL
+ * (meaning path was removed), or ERROR_FAIL if other stuff went
+ * wrong (in which latter case, logged) */
+
+struct libxl__ev_devstate {
+ /* read-only for caller, who may read only when waiting: */
+ int wanted;
+ libxl__ev_devstate_callback *callback;
+ /* as for the remainder, read-only public parts may also be
+ * read by the caller (notably, watch.path), but only when waiting: */
+ libxl__xswait_state w;
+};
+
+static inline void libxl__ev_devstate_init(libxl__ev_devstate *ds)
+{
+ libxl__xswait_init(&ds->w);
+}
+
+static inline void libxl__ev_devstate_cancel(libxl__gc *gc,
+ libxl__ev_devstate *ds)
+{
+ libxl__xswait_stop(gc,&ds->w);
+}
+
+_hidden int libxl__ev_devstate_wait(libxl__ao *ao, libxl__ev_devstate *ds,
+ libxl__ev_devstate_callback cb,
+ const char *state_path,
+ int state, int milliseconds);
+
+/*
+ * libxl__ev_domaindeathcheck_register - arranges to call back (once)
+ * if the domain is destroyed. If the domain dies, we log a message
+ * of the form "<what>: <explanation of the situation, including the domid>".
+ */
+
+typedef struct libxl__domaindeathcheck libxl__domaindeathcheck;
+typedef void libxl___domaindeathcheck_callback(libxl__egc *egc,
+ libxl__domaindeathcheck*,
+ int rc /* DESTROYED or ABORTED */);
+
+struct libxl__domaindeathcheck {
+ /* must be filled in by caller, and remain valid: */
+ const char *what;
+ uint32_t domid;
+ libxl___domaindeathcheck_callback *callback;
+ /* private */
+ libxl__ao_abortable abrt;
+ libxl__ev_xswatch watch;
+};
+
+_hidden int libxl__domaindeathcheck_start(libxl__ao *ao,
+ libxl__domaindeathcheck *dc);
+
+void libxl__domaindeathcheck_init(libxl__domaindeathcheck *dc);
+void libxl__domaindeathcheck_stop(libxl__gc *gc, libxl__domaindeathcheck *dc);
+
+
+/*
+ * libxl__try_phy_backend - Check if there's support for the passed
+ * type of file using the PHY backend
+ * st_mode: mode_t of the file, as returned by stat function
+ *
+ * Returns 1 on success, and 0 if not suitable for phy backend.
+ */
+_hidden int libxl__try_phy_backend(mode_t st_mode);
+
+
+_hidden char *libxl__devid_to_localdev(libxl__gc *gc, int devid);
+
+_hidden int libxl__pci_numdevs(libxl__gc *gc);
+_hidden int libxl__pci_topology_init(libxl__gc *gc,
+ physdev_pci_device_t *devs,
+ int num_devs);
+
+/* from libxl_pci */
+
+_hidden void libxl__device_pci_add(libxl__egc *egc, uint32_t domid,
+ libxl_device_pci *pcidev, bool starting,
+ libxl__ao_device *aodev);
+_hidden void libxl__device_pci_destroy_all(libxl__egc *egc, uint32_t domid,
+ libxl__multidev *);
+_hidden int libxl__device_pci_setdefault(libxl__gc *gc, uint32_t domid,
+ libxl_device_pci *pci, bool hotplug);
+_hidden bool libxl__is_igd_vga_passthru(libxl__gc *gc,
+ const libxl_domain_config *d_config);
+
+/* from libxl_dtdev */
+
+_hidden int libxl__device_dt_add(libxl__gc *gc, uint32_t domid,
+ const libxl_device_dtdev *dtdev);
+
+/*
+ *----- spawn -----
+ *
+ * Higher-level double-fork and separate detach eg as for device models
+ *
+ * Each libxl__spawn_state is in one of these states
+ * Undefined, Idle, Attached, Detaching
+ */
+
+typedef struct libxl__obsolete_spawn_starting libxl__spawn_starting;
+/* this type is never defined, so no objects of this type exist
+ * fixme-ao This should go away completely. */
+
+typedef struct libxl__spawn_state libxl__spawn_state;
+
+/* Clears out a spawn state; idempotent. */
+_hidden void libxl__spawn_init(libxl__spawn_state*);
+
+/*
+ * libxl__spawn_spawn - Create a new process which will become daemonic
+ * Forks twice, to allow the child to detach entirely from the parent.
+ *
+ * We call the two generated processes the "middle child" (result of
+ * the first fork) and the "inner child" (result of the second fork
+ * which takes place in the middle child).
+ *
+ * The inner child must soon exit or exec. It must also soon exit or
+ * notify the parent of its successful startup by writing to the
+ * xenstore path xspath OR via other means that the parent will have
+ * to set up.
+ *
+ * The user (in the parent) will be called back (confirm_cb) every
+ * time that xenstore path is modified.
+ *
+ * In both children, the ctx is not fully usable: gc and logging
+ * operations are OK, but operations on Xen and xenstore are not.
+ * (The restrictions are the same as those which apply to children
+ * made with libxl__ev_child_fork.)
+ *
+ * midproc_cb will be called in the middle child, with the pid of the
+ * inner child; this could for example record the pid. midproc_cb
+ * should be fast, and should return. It will be called (reentrantly)
+ * within libxl__spawn_init.
+ *
+ * failure_cb will be called in the parent on failure of the
+ * intermediate or final child; an error message will have been
+ * logged.
+ *
+ * confirm_cb, failure_cb and detached_cb will not be called
+ * reentrantly from within libxl__spawn_spawn.
+ *
+ * what: string describing the spawned process, used for logging
+ *
+ * Logs errors. A copy of "what" is taken.
+ * Return values:
+ * < 0 error, *spawn is now Idle and need not be detached
+ * +1 caller is the parent, *spawn is Attached and must be detached
+ * 0 caller is now the inner child, should probably call libxl__exec
+ *
+ * The spawn state must be Undefined or Idle on entry.
+ */
+_hidden int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *spawn);
+
+/*
+ * libxl__spawn_request_detach - Detaches the daemonic child.
+ *
+ * Works by killing the intermediate process from spawn_spawn.
+ * After this function returns, failures of either child are no
+ * longer reported via failure_cb.
+ *
+ * This is not synchronous: there will be a further callback when
+ * the detach is complete.
+ *
+ * If called before the inner child has been created, this may prevent
+ * it from running at all. Thus this should be called only when the
+ * inner child has notified that it is ready. Normally it will be
+ * called from within confirm_cb.
+ *
+ * Logs errors.
+ *
+ * The spawn state must be Attached entry and will be Detaching
+ * on return.
+ */
+_hidden void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state*);
+
+/*
+ * libxl__spawn_initiate_failure - Propagate failure from the caller to the
+ * callee.
+ *
+ * Works by killing the intermediate process from spawn_spawn.
+ * After this function returns, a failure will be reported.
+ *
+ * This is not synchronous: there will be a further callback when
+ * the detach is complete.
+ *
+ * Caller must have logged a failure reason.
+ *
+ * The spawn state must be Attached on entry and will remain Attached. It
+ * is possible for a spawn to fail for multiple reasons, for example
+ * call(s) to libxl__spawn_initiate_failure and also for some other reason.
+ * In that case the first rc value from any source will take precedence.
+ */
+_hidden void libxl__spawn_initiate_failure(libxl__egc *egc,
+ libxl__spawn_state *ss, int rc);
+
+/*
+ * If successful, this should return 0.
+ *
+ * Otherwise it should return a signal number, which will be
+ * sent to the inner child; the overall spawn will then fail.
+ */
+typedef int /* signal number */
+libxl__spawn_midproc_cb(libxl__gc*, libxl__spawn_state*, pid_t inner);
+
+/*
+ * Called if the spawn failed. The reason will have been logged.
+ * The spawn state will be Idle on entry to the callback (and
+ * it may be reused immediately if desired).
+ */
+typedef void libxl__spawn_failure_cb(libxl__egc*, libxl__spawn_state*,
+ int rc);
+
+/*
+ * Called when the xspath watch triggers. xspath will have been read
+ * and the result placed in xsdata; if that failed because the key
+ * didn't exist, xspath==0. (If it failed for some other reason,
+ * the spawn machinery calls failure_cb instead.)
+ *
+ * If the child has indicated its successful startup, or a failure
+ * has occurred, this should call libxl__spawn_detach.
+ *
+ * If the child is still starting up, should simply return, doing
+ * nothing.
+ *
+ * The spawn state will be Active on entry to the callback; there
+ * are no restrictions on the state on return; it may even have
+ * been detached and reused.
+ */
+typedef void libxl__spawn_confirm_cb(libxl__egc*, libxl__spawn_state*,
+ const char *xsdata);
+
+/*
+ * Called when the detach (requested by libxl__spawn_initiate_detach) has
+ * completed. On entry to the callback the spawn state is Idle.
+ */
+typedef void libxl__spawn_detached_cb(libxl__egc*, libxl__spawn_state*);
+
+struct libxl__spawn_state {
+ /* must be filled in by user and remain valid */
+ libxl__ao *ao;
+ const char *what;
+ const char *xspath;
+ const char *pidpath; /* only used by libxl__spawn_midproc_record_pid */
+ int timeout_ms; /* -1 means forever */
+ libxl__spawn_midproc_cb *midproc_cb;
+ libxl__spawn_failure_cb *failure_cb;
+ libxl__spawn_confirm_cb *confirm_cb;
+ libxl__spawn_detached_cb *detached_cb;
+
+ /* remaining fields are private to libxl_spawn_... */
+ int detaching; /* we are in Detaching */
+ int rc; /* might be non-0 whenever we are not Idle */
+ libxl__ev_child mid; /* always in use whenever we are not Idle */
+ libxl__xswait_state xswait;
+};
+
+static inline int libxl__spawn_inuse(const libxl__spawn_state *ss)
+ { return libxl__ev_child_inuse(&ss->mid); }
+
+/*
+ * libxl_spawner_record_pid - Record given pid in xenstore
+ *
+ * This function can be passed directly as an intermediate_hook to
+ * libxl__spawn_spawn. On failure, returns the value SIGTERM.
+ */
+_hidden int libxl__spawn_record_pid(libxl__gc*, libxl__spawn_state*,
+ pid_t innerchild);
+
+/*
+ * libxl__xenstore_child_wait_deprecated - Wait for daemonic child IPC
+ *
+ * This is a NOT function for waiting for ordinary child processes.
+ * If you want to run (fork/exec/wait) subprocesses from libxl:
+ * - Make your libxl entrypoint use the ao machinery
+ * - Use libxl__ev_child_fork, and use the callback programming style
+ *
+ * This function is intended for interprocess communication with a
+ * service process. If the service process does not respond quickly,
+ * the whole caller may be blocked. Therefore this function is
+ * deprecated. This function is currently used only by
+ * libxl__wait_for_device_model_deprecated.
+ *
+ * gc: allocation pool
+ * domid: guest to work with
+ * timeout: how many seconds to wait for the state to appear
+ * what: string describing the spawned process
+ * path: path to the state file in xenstore
+ * state: expected string to wait for in path (optional)
+ * spawning: malloc'd pointer to libxl__spawn_starting (optional)
+ * check_callback: (optional)
+ * check_callback_userdata: data to pass to the callback function
+ *
+ * Returns 0 on success, and < 0 on error.
+ *
+ * This function waits the given timeout for the given path to appear
+ * in xenstore, and optionally for state in path.
+ * If path appears and state matches, check_callback is called.
+ * If check_callback returns > 0, waiting for path or state continues.
+ * Otherwise libxl__xenstore_child_wait_deprecated returns.
+ */
+_hidden int libxl__xenstore_child_wait_deprecated(libxl__gc *gc,
+ uint32_t domid,
+ uint32_t timeout, char *what,
+ char *path, char *state,
+ libxl__spawn_starting *spawning,
+ int (*check_callback)(libxl__gc *gc,
+ uint32_t domid,
+ const char *state,
+ void *userdata),
+ void *check_callback_userdata);
+
+
+ /* low-level stuff, for synchronous subprocesses etc. */
+
+/*
+ * env should be passed using the following format,
+ *
+ * env[0]: name of env variable
+ * env[1]: value of env variable
+ * env[n]: ...
+ *
+ * So it efectively becomes something like:
+ * export env[n]=env[n+1]
+ * (where n%2 = 0)
+ *
+ * The last entry of the array always has to be NULL.
+ *
+ * stdinfd, stdoutfd, stderrfd will be dup2'd onto the corresponding
+ * fd in the child, if they are not -1. The original copy of the
+ * descriptor will be closed in the child (unless it's 0, 1 or 2
+ * ie the source descriptor is itself stdin, stdout or stderr).
+ *
+ * Logs errors, never returns.
+ */
+_hidden void libxl__exec(libxl__gc *gc, int stdinfd, int stdoutfd,
+ int stderrfd, const char *arg0, char *const args[],
+ char *const env[]) __attribute__((noreturn));
+
+/* from xl_create */
+
+ /* on entry, libxl_domid_valid_guest(domid) must be false;
+ * on exit (even error exit), domid may be valid and refer to a domain */
+_hidden int libxl__domain_make(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ libxl__domain_build_state *state,
+ uint32_t *domid, bool soft_reset);
+
+_hidden int libxl__domain_build(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ uint32_t domid,
+ libxl__domain_build_state *state);
+
+/* for device model creation */
+_hidden const char *libxl__domain_device_model(libxl__gc *gc,
+ const libxl_domain_build_info *info);
+_hidden int libxl__need_xenpv_qemu(libxl__gc *gc,
+ libxl_domain_config *d_config);
+_hidden bool libxl__query_qemu_backend(libxl__gc *gc,
+ uint32_t domid,
+ uint32_t backend_id,
+ const char *type,
+ bool def);
+_hidden int libxl__dm_active(libxl__gc *gc, uint32_t domid);
+_hidden int libxl__dm_check_start(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ uint32_t domid);
+
+/*
+ * This function will fix reserved device memory conflict
+ * according to user's configuration.
+ */
+_hidden int libxl__domain_device_construct_rdm(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ uint64_t rdm_mem_guard,
+ struct xc_dom_image *dom);
+
+/*
+ * This function will cause the whole libxl process to hang
+ * if the device model does not respond. It is deprecated.
+ *
+ * Instead of calling this function:
+ * - Make your libxl entrypoint use the ao machinery
+ * - Use libxl__ev_xswatch_register, and use the callback programming
+ * style
+ */
+_hidden int libxl__wait_for_device_model_deprecated(libxl__gc *gc,
+ uint32_t domid, char *state,
+ libxl__spawn_starting *spawning
+ /* NULL allowed */,
+ int (*check_callback)(libxl__gc *gc,
+ uint32_t domid,
+ const char *state,
+ void *userdata),
+ void *check_callback_userdata);
+
+_hidden const libxl_vnc_info *libxl__dm_vnc(const libxl_domain_config *g_cfg);
+
+_hidden char *libxl__abs_path(libxl__gc *gc, const char *s, const char *path);
+
+#define LIBXL__LOG_DEBUG XTL_DEBUG
+#define LIBXL__LOG_VERBOSE XTL_VERBOSE
+#define LIBXL__LOG_INFO XTL_INFO
+#define LIBXL__LOG_WARNING XTL_WARN
+#define LIBXL__LOG_ERROR XTL_ERROR
+
+_hidden char *libxl__domid_to_name(libxl__gc *gc, uint32_t domid);
+_hidden char *libxl__cpupoolid_to_name(libxl__gc *gc, uint32_t poolid);
+
+_hidden int libxl__enum_from_string(const libxl_enum_string_table *t,
+ const char *s, int *e) NN(2);
+
+_hidden yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str);
+
+_hidden yajl_gen_status libxl__string_gen_json(yajl_gen hand, const char *p);
+
+typedef yajl_gen_status (*libxl__gen_json_callback)(yajl_gen hand, void *);
+_hidden char *libxl__object_to_json(libxl_ctx *ctx, const char *type,
+ libxl__gen_json_callback gen, void *p);
+
+_hidden void libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool retore,
+ libxl_domain_build_info *info);
+
+/* Calls poll() again - useful to check whether a signaled condition
+ * is still true. Cannot fail. Returns currently-true revents. */
+_hidden short libxl__fd_poll_recheck(libxl__egc *egc, int fd, short events);
+
+_hidden char *libxl__uuid2string(libxl__gc *gc, const libxl_uuid uuid);
+
+struct libxl__xen_console_reader {
+ char *buffer;
+ unsigned int size;
+ unsigned int count;
+ unsigned int clear;
+ unsigned int incremental;
+ unsigned int index;
+};
+
+/* parse the string @s as a sequence of 6 colon separated bytes in to @mac */
+_hidden int libxl__parse_mac(const char *s, libxl_mac mac);
+/* compare mac address @a and @b. 0 if the same, -ve if a<b and +ve if a>b */
+_hidden int libxl__compare_macs(libxl_mac *a, libxl_mac *b);
+/* return true if mac address is all zero (the default value) */
+_hidden int libxl__mac_is_default(libxl_mac *mac);
+/* init a recursive mutex */
+_hidden int libxl__init_recursive_mutex(libxl_ctx *ctx, pthread_mutex_t *lock);
+
+_hidden int libxl__gettimeofday(libxl__gc *gc, struct timeval *now_r);
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+/* from libxl_qmp */
+typedef struct libxl__qmp_handler libxl__qmp_handler;
+
+/* Initialise and connect to the QMP socket.
+ * Return an handler or NULL if there is an error
+ */
+_hidden libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc,
+ uint32_t domid);
+/* Resume QEMU. */
+_hidden int libxl__qmp_resume(libxl__gc *gc, int domid);
+/* Load current QEMU state from file. */
+_hidden int libxl__qmp_restore(libxl__gc *gc, int domid, const char *filename);
+/* Start NBD server */
+_hidden int libxl__qmp_nbd_server_start(libxl__gc *gc, int domid,
+ const char *host, const char *port);
+/* Add a disk to NBD server */
+_hidden int libxl__qmp_nbd_server_add(libxl__gc *gc, int domid,
+ const char *disk);
+/* Start replication */
+_hidden int libxl__qmp_start_replication(libxl__gc *gc, int domid,
+ bool primary);
+/* Get replication error that occurs when the vm is running */
+_hidden int libxl__qmp_query_xen_replication_status(libxl__gc *gc, int domid);
+/* Do checkpoint */
+_hidden int libxl__qmp_colo_do_checkpoint(libxl__gc *gc, int domid);
+/* Stop replication */
+_hidden int libxl__qmp_stop_replication(libxl__gc *gc, int domid,
+ bool primary);
+/* Stop NBD server */
+_hidden int libxl__qmp_nbd_server_stop(libxl__gc *gc, int domid);
+/* Add or remove a child to/from quorum */
+_hidden int libxl__qmp_x_blockdev_change(libxl__gc *gc, int domid,
+ const char *parant,
+ const char *child, const char *node);
+/* run a hmp command in qmp mode */
+_hidden int libxl__qmp_hmp(libxl__gc *gc, int domid, const char *command_line,
+ char **out);
+/* close and free the QMP handler */
+_hidden void libxl__qmp_close(libxl__qmp_handler *qmp);
+/* remove the socket file, if the file has already been removed,
+ * nothing happen */
+_hidden void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid);
+
+/* `data' should contain a byte to send.
+ * When dealing with a non-blocking fd, it returns
+ * ERROR_NOT_READY on EWOULDBLOCK
+ * logs on other failures. */
+int libxl__sendmsg_fds(libxl__gc *gc, int carrier,
+ const char data,
+ int nfds, const int fds[], const char *what);
+
+/* Insists on receiving exactly nfds and datalen. On failure, logs
+ * and leaves *fds untouched. */
+int libxl__recvmsg_fds(libxl__gc *gc, int carrier,
+ void *databuf, size_t datalen,
+ int nfds, int fds[], const char *what);
+
+/* from libxl_json */
+#include <yajl/yajl_gen.h>
+
+_hidden yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str);
+_hidden yajl_gen_status libxl__yajl_gen_enum(yajl_gen hand, const char *str);
+
+typedef enum {
+ JSON_NULL = (1 << 0),
+ JSON_BOOL = (1 << 1),
+ JSON_INTEGER = (1 << 2),
+ JSON_DOUBLE = (1 << 3),
+ /* number is store in string, it's too big to be a long long or a double */
+ JSON_NUMBER = (1 << 4),
+ JSON_STRING = (1 << 5),
+ JSON_MAP = (1 << 6),
+ JSON_ARRAY = (1 << 7),
+ JSON_ANY = 255 /* this is a mask of all values above, adjust as needed */
+} libxl__json_node_type;
+
+struct libxl__json_object {
+ libxl__json_node_type type;
+ union {
+ bool b;
+ long long i;
+ double d;
+ char *string;
+ /* List of libxl__json_object */
+ flexarray_t *array;
+ /* List of libxl__json_map_node */
+ flexarray_t *map;
+ } u;
+ struct libxl__json_object *parent;
+};
+
+typedef int (*libxl__json_parse_callback)(libxl__gc *gc,
+ libxl__json_object *o,
+ void *p);
+_hidden int libxl__object_from_json(libxl_ctx *ctx, const char *type,
+ libxl__json_parse_callback parse,
+ void *p,
+ const char *s);
+
+typedef struct {
+ char *map_key;
+ libxl__json_object *obj;
+} libxl__json_map_node;
+
+static inline bool libxl__json_object_is_null(const libxl__json_object *o)
+{
+ return o != NULL && o->type == JSON_NULL;
+}
+static inline bool libxl__json_object_is_bool(const libxl__json_object *o)
+{
+ return o != NULL && o->type == JSON_BOOL;
+}
+static inline bool libxl__json_object_is_string(const libxl__json_object *o)
+{
+ return o != NULL && o->type == JSON_STRING;
+}
+static inline bool libxl__json_object_is_integer(const libxl__json_object *o)
+{
+ return o != NULL && o->type == JSON_INTEGER;
+}
+static inline bool libxl__json_object_is_double(const libxl__json_object *o)
+{
+ return o != NULL && o->type == JSON_DOUBLE;
+}
+static inline bool libxl__json_object_is_number(const libxl__json_object *o)
+{
+ return o != NULL && o->type == JSON_NUMBER;
+}
+static inline bool libxl__json_object_is_map(const libxl__json_object *o)
+{
+ return o != NULL && o->type == JSON_MAP;
+}
+static inline bool libxl__json_object_is_array(const libxl__json_object *o)
+{
+ return o != NULL && o->type == JSON_ARRAY;
+}
+
+/*
+ * `o` may be NULL for all libxl__json_object_get_* functions.
+ */
+
+static inline bool libxl__json_object_get_bool(const libxl__json_object *o)
+{
+ if (libxl__json_object_is_bool(o))
+ return o->u.b;
+ else
+ return false;
+}
+static inline
+const char *libxl__json_object_get_string(const libxl__json_object *o)
+{
+ if (libxl__json_object_is_string(o))
+ return o->u.string;
+ else
+ return NULL;
+}
+static inline
+const char *libxl__json_object_get_number(const libxl__json_object *o)
+{
+ if (libxl__json_object_is_number(o))
+ return o->u.string;
+ else
+ return NULL;
+}
+static inline
+flexarray_t *libxl__json_object_get_map(const libxl__json_object *o)
+{
+ if (libxl__json_object_is_map(o))
+ return o->u.map;
+ else
+ return NULL;
+}
+static inline
+flexarray_t *libxl__json_object_get_array(const libxl__json_object *o)
+{
+ if (libxl__json_object_is_array(o))
+ return o->u.array;
+ else
+ return NULL;
+}
+static inline long long libxl__json_object_get_integer(const libxl__json_object *o)
+{
+ if (libxl__json_object_is_integer(o))
+ return o->u.i;
+ else
+ return -1;
+}
+
+/*
+ * `o` may be NULL for the following libxl__json_*_get functions.
+ */
+
+_hidden libxl__json_object *libxl__json_array_get(const libxl__json_object *o,
+ int i);
+_hidden
+libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o,
+ int i);
+_hidden const libxl__json_object *libxl__json_map_get(const char *key,
+ const libxl__json_object *o,
+ libxl__json_node_type expected_type);
+
+/*
+ * NOGC can be used with those json_object functions, but the
+ * libxl__json_object* will need to be freed with libxl__json_object_free.
+ */
+_hidden libxl__json_object *libxl__json_object_alloc(libxl__gc *gc_opt,
+ libxl__json_node_type type);
+_hidden yajl_status libxl__json_object_to_yajl_gen(libxl__gc *gc_opt,
+ yajl_gen hand,
+ const libxl__json_object *param);
+_hidden void libxl__json_object_free(libxl__gc *gc_opt,
+ libxl__json_object *obj);
+
+_hidden libxl__json_object *libxl__json_parse(libxl__gc *gc_opt, const char *s);
+
+/* `args` may be NULL */
+_hidden char *libxl__json_object_to_json(libxl__gc *gc,
+ const libxl__json_object *args);
+/* Always return a valid string, but invalid json on error. */
+#define JSON(o) \
+ (libxl__json_object_to_json(gc, (o)) ? : "<invalid-json-object>")
+
+ /* Based on /local/domain/$domid/dm-version xenstore key
+ * default is qemu xen traditional */
+_hidden int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid);
+ /* Return the system-wide default device model */
+_hidden libxl_device_model_version libxl__default_device_model(libxl__gc *gc);
+
+static inline
+bool libxl__stubdomain_is_linux_running(libxl__gc *gc, uint32_t domid)
+{
+ /* same logic as in libxl__stubdomain_is_linux */
+ return libxl__device_model_version_running(gc, domid)
+ == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
+}
+
+static inline
+bool libxl__stubdomain_is_linux(libxl_domain_build_info *b_info)
+{
+ /* right now qemu-tranditional implies MiniOS stubdomain and qemu-xen
+ * implies Linux stubdomain */
+ return libxl_defbool_val(b_info->device_model_stubdomain) &&
+ b_info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
+}
+
+#define DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, fmt, _a...) \
+ libxl__sprintf(gc, "/local/domain/%u/device-model/%u" fmt, dm_domid, \
+ domid, ##_a)
+
+/*
+ * Calling context and GC for event-generating functions:
+ *
+ * These are for use by parts of libxl which directly or indirectly
+ * call libxl__event_occurred. These contain a gc but also a list of
+ * deferred events.
+ *
+ * You should never need to initialise an egc unless you are part of
+ * the event machinery itself. Otherwise you will always be given an
+ * egc if you need one. Even functions which generate specific kinds
+ * of events don't need to - rather, they will be passed an egc into
+ * their own callback function and should just use the one they're
+ * given.
+ *
+ * Functions using LIBXL_INIT_EGC may *not* generally be called from
+ * within libxl, because libxl__egc_cleanup may call back into the
+ * application. This should be enforced by declaring all such
+ * functions in libxl.h or libxl_event.h with
+ * LIBXL_EXTERNAL_CALLERS_ONLY. You should in any case not find it
+ * necessary to call egc-creators from within libxl.
+ *
+ * The callbacks must all take place with the ctx unlocked because
+ * the application is entitled to reenter libxl from them. This
+ * would be bad not because the lock is not recursive (it is) but
+ * because the application might make blocking libxl calls which
+ * would hold the lock unreasonably long.
+ *
+ * For the same reason libxl__egc_cleanup (or EGC_FREE) must be called
+ * with the ctx *unlocked*. So the right pattern has the EGC_...
+ * macro calls on the outside of the CTX_... ones.
+ */
+
+/* useful for all functions which take an egc: */
+
+#define EGC_GC \
+ libxl__gc *const gc __attribute__((unused)) = &egc->gc
+
+/* egc initialisation and destruction: */
+
+#define LIBXL_INIT_EGC(egc,ctx) do{ \
+ LIBXL_INIT_GC((egc).gc,ctx); \
+ LIBXL_TAILQ_INIT(&(egc).occurred_for_callback); \
+ LIBXL_TAILQ_INIT(&(egc).aos_for_callback); \
+ LIBXL_TAILQ_INIT(&(egc).aops_for_callback); \
+ LIBXL_STAILQ_INIT(&(egc).ev_immediates); \
+ } while(0)
+
+_hidden void libxl__egc_ao_cleanup_1_baton(libxl__gc *gc);
+ /* Passes the baton for added osevents. See comment for
+ * osevents_added in struct libxl__poller. */
+_hidden void libxl__egc_cleanup_2_ul_cb_gc(libxl__egc *egc);
+ /* Frees memory allocated within this egc's gc, and and report all
+ * occurred events via callback, if applicable. May reenter the
+ * application; see restrictions above. The ctx must be UNLOCKED. */
+
+/* convenience macros: */
+
+#define EGC_INIT(ctx) \
+ libxl__egc egc[1]; LIBXL_INIT_EGC(egc[0],ctx); \
+ EGC_GC
+
+#define CTX_UNLOCK_EGC_FREE do{ \
+ libxl__egc_ao_cleanup_1_baton(&egc->gc); \
+ CTX_UNLOCK; \
+ libxl__egc_cleanup_2_ul_cb_gc(egc); \
+ }while(0)
+
+
+/*
+ * Machinery for asynchronous operations ("ao")
+ *
+ * All "slow" functions (see below for the exact definition) need to
+ * use the asynchronous operation ("ao") machinery. The function
+ * should take a parameter const libxl_asyncop_how *ao_how and must
+ * start with a call to AO_CREATE or equivalent. These functions MAY
+ * NOT be called from inside libxl (regardless of what is passed for
+ * ao_how), because they can cause reentrancy hazards due to
+ * callbacks.
+ *
+ * For the same reason functions taking an ao_how may make themselves
+ * an egc with EGC_INIT (and they will generally want to, to be able
+ * to immediately complete an ao during its setup).
+ *
+ *
+ * "Slow" functions includes any that might block on a guest or an
+ * external script. More broadly, it includes any operations which
+ * are sufficiently slow that an application might reasonably want to
+ * initiate them, and then carry on doing something else, while the
+ * operation completes. That is, a "fast" function must be fast
+ * enough that we do not mind blocking all other management operations
+ * on the same host while it completes.
+ *
+ * There are certain primitive functions which make a libxl operation
+ * necessarily "slow" for API reasons. These are:
+ * - awaiting xenstore watches (although read-modify-write xenstore
+ * transactions are OK for fast functions)
+ * - spawning subprocesses
+ * - anything with a timeout
+ *
+ *
+ * Lifecycle of an ao:
+ *
+ * - Created by libxl__ao_create (or the AO_CREATE convenience macro).
+ *
+ * - After creation, can be used by code which implements
+ * the operation as follows:
+ * - the ao's gc, for allocating memory for the lifetime
+ * of the operation (possibly with the help of the AO_GC
+ * macro to introduce the gc into scope)
+ * - the ao itself may be passed about to sub-functions
+ * so that they can stash it away etc.
+ * - in particular, the ao pointer must be stashed in some
+ * per-operation structure which is also passed as a user
+ * pointer to the internal event generation request routines
+ * libxl__evgen_FOO, so that at some point a CALLBACK will be
+ * made when the operation is complete.
+ * - if the operation provides progress reports, the aop_how(s)
+ * must be copied into the per-operation structure using
+ * libxl__ao_progress_gethow.
+ *
+ * - If the initiation is unsuccessful, the initiating function must
+ * call libxl__ao_create_fail before unlocking and returning whatever
+ * error code is appropriate (AO_CREATE_FAIL macro).
+ *
+ * If initiation is successful:
+ *
+ * - The initiating function must run libxl__ao_inprogress right
+ * before unlocking and returning, and return whatever it returns.
+ * This is best achieved with the AO_INPROGRESS macro.
+ *
+ * - If the operation supports progress reports, it may generate
+ * suitable events with NEW_EVENT and report them with
+ * libxl__ao_progress_report (with the ctx locked).
+ *
+ * - Eventually, some callback function, whose callback has been
+ * requested directly or indirectly, should call libxl__ao_complete
+ * (with the ctx locked, as it will generally already be in any
+ * event callback function). This must happen exactly once for each
+ * ao, as the last that happens with that ao.
+ *
+ * - However, it is permissible for the initiating function to call
+ * libxl__ao_inprogress and/or libxl__ao_complete (directly or
+ * indirectly), before it uses AO_INPROGRESS to return. (The ao
+ * infrastructure will arrange to defer destruction of the ao, etc.,
+ * until the proper time.) An initiating function should do this
+ * if it takes a codepath which completes synchronously.
+ *
+ * - Conversely it is forbidden to call libxl__ao_complete in the
+ * initiating function _after_ AO_INPROGRESS, because
+ * libxl__ao_complete requires the ctx to be locked.
+ *
+ * - Note that during callback functions, two gcs are available:
+ * - The one in egc, whose lifetime is only this callback
+ * - The one in ao, whose lifetime is the asynchronous operation
+ * Usually a callback function should use CONTAINER_OF to obtain its
+ * own state structure, containing a pointer to the ao. It should
+ * then obtain the ao and use the ao's gc; this is most easily done
+ * using the convenience macro STATE_AO_GC.
+ */
+
+#define AO_CREATE(ctx, domid, ao_how) \
+ libxl__ctx_lock(ctx); \
+ libxl__ao *ao = libxl__ao_create(ctx, domid, ao_how, \
+ __FILE__, __LINE__, __func__); \
+ if (!ao) { libxl__ctx_unlock(ctx); return ERROR_NOMEM; } \
+ libxl__egc egc[1]; LIBXL_INIT_EGC(egc[0],ctx); \
+ AO_GC;
+
+#define AO_INPROGRESS ({ \
+ libxl_ctx *ao__ctx = libxl__gc_owner(&ao->gc); \
+ /* __ao_inprogress will do egc..1_baton if needed */ \
+ CTX_UNLOCK; \
+ libxl__egc_cleanup_2_ul_cb_gc(egc); \
+ CTX_LOCK; \
+ int ao__rc = libxl__ao_inprogress(ao, \
+ __FILE__, __LINE__, __func__); \
+ libxl__ctx_unlock(ao__ctx); /* gc is now invalid */ \
+ (ao__rc); \
+ })
+
+#define AO_CREATE_FAIL(rc) ({ \
+ libxl_ctx *ao__ctx = libxl__gc_owner(&ao->gc); \
+ assert(rc); \
+ libxl__ao_create_fail(ao); \
+ libxl__egc_ao_cleanup_1_baton(&egc->gc); \
+ libxl__ctx_unlock(ao__ctx); /* gc is now invalid */ \
+ libxl__egc_cleanup_2_ul_cb_gc(egc); \
+ (rc); \
+ })
+
+
+/*
+ * Given, in scope,
+ * libxl__ao *ao;
+ * produces, in scope,
+ * libxl__gc *gc;
+ */
+#define AO_GC \
+ libxl__gc *const gc __attribute__((unused)) = &ao->gc
+
+/*
+ * void STATE_AO_GC(libxl__ao *ao_spec);
+ * // Produces, in scope:
+ * libxl__ao *ao; // set from ao_spec
+ * libxl__gc *gc;
+ */
+#define STATE_AO_GC(op_ao) \
+ libxl__ao *const ao = (op_ao); \
+ libxl__gc *const gc __attribute__((unused)) = libxl__ao_inprogress_gc(ao)
+
+
+/* All of these MUST be called with the ctx locked.
+ * libxl__ao_inprogress MUST be called with the ctx locked exactly once. */
+_hidden libxl__ao *libxl__ao_create(libxl_ctx*, uint32_t domid,
+ const libxl_asyncop_how*,
+ const char *file, int line, const char *func);
+_hidden int libxl__ao_inprogress(libxl__ao *ao,
+ const char *file, int line, const char *func); /* temporarily unlocks */
+_hidden void libxl__ao_create_fail(libxl__ao *ao);
+_hidden void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc);
+_hidden libxl__gc *libxl__ao_inprogress_gc(libxl__ao *ao);
+
+/* Can be called at any time. Use is essential for any aop user. */
+_hidden void libxl__ao_progress_gethow(libxl_asyncprogress_how *in_state,
+ const libxl_asyncprogress_how *from_app);
+
+/* Must be called with the ctx locked. Will fill in ev->for_user,
+ * so caller need not do that. */
+_hidden void libxl__ao_progress_report(libxl__egc *egc, libxl__ao *ao,
+ const libxl_asyncprogress_how *how, libxl_event *ev /* consumed */);
+
+/* For use by ao machinery ONLY */
+_hidden void libxl__ao__destroy(libxl_ctx*, libxl__ao *ao);
+_hidden void libxl__ao_complete_check_progress_reports(libxl__egc*, libxl__ao*);
+
+
+/*
+ * Short-lived sub-ao, aka "nested ao".
+ *
+ * Some asynchronous operations are very long-running. Generally,
+ * since an ao has a gc, any allocations made in that ao will live
+ * until the ao is completed. When this is not desirable, these
+ * functions may be used to manage a "sub-ao".
+ *
+ * The returned sub-ao is suitable for passing to gc-related functions
+ * and macros such as libxl__ao_inprogress_gc, AO_GC, and STATE_AO_GC.
+ *
+ * It MUST NOT be used with AO_INPROGRESS, AO_CREATE_FAIL,
+ * libxl__ao_complete, libxl__ao_progress_report, and so on.
+ *
+ * The caller must ensure that all of the sub-ao's are freed before
+ * the parent is. Multiple levels of nesting are OK (although
+ * hopefully they won't be necessary).
+ */
+
+_hidden libxl__ao *libxl__nested_ao_create(libxl__ao *parent); /* cannot fail */
+_hidden void libxl__nested_ao_free(libxl__ao *child);
+
+
+/*
+ * File descriptors and CLOEXEC
+ */
+
+/*
+ * For libxl functions which create file descriptors, at least one
+ * of the following must be true:
+ * (a) libxl does not care if copies of this open-file are inherited
+ * by random children and might remain open indefinitely
+ * (b) libxl must take extra care for the fd (the actual descriptor,
+ * not the open-file) as below. We call this a "carefd".
+ *
+ * The rules for opening a carefd are:
+ * (i) Before bringing any carefds into existence,
+ * libxl code must call libxl__carefd_begin.
+ * (ii) Then for each carefd brought into existence,
+ * libxl code must call libxl__carefd_record
+ * and remember the libxl__carefd_record*.
+ * (iii) Then it must call libxl__carefd_unlock.
+ * (iv) When in a child process the fd is to be passed across
+ * exec by libxl, the libxl code must unset FD_CLOEXEC
+ * on the fd eg by using libxl_fd_set_cloexec.
+ * (v) Later, when the fd is to be closed in the same process,
+ * libxl code must not call close. Instead, it must call
+ * libxl__carefd_close.
+ * Steps (ii) and (iii) can be combined by calling the convenience
+ * function libxl__carefd_opened.
+ */
+/* libxl__carefd_begin and _unlock (or _opened) must be called always
+ * in pairs. They may be called with the CTX lock held. In between
+ * _begin and _unlock, the following are prohibited:
+ * - anything which might block
+ * - any callbacks to the application
+ * - nested calls to libxl__carefd_begin
+ * - fork (libxl__fork)
+ * In general nothing should be done before _unlock that could be done
+ * afterwards.
+ */
+
+_hidden void libxl__carefd_begin(void);
+_hidden void libxl__carefd_unlock(void);
+
+/* fd may be -1, in which case this returns a dummy libxl__fd_record
+ * on which it _carefd_close is a no-op. Cannot fail. */
+_hidden libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd);
+
+/* Combines _record and _unlock in a single call. If fd==-1,
+ * still does the unlock, but returns 0. */
+_hidden libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd);
+
+/* Works just like close(2). You may pass NULL, in which case it's
+ * a successful no-op. */
+_hidden int libxl__carefd_close(libxl__carefd*);
+
+/* You may pass NULL in which case the answer is -1. */
+_hidden int libxl__carefd_fd(const libxl__carefd*);
+
+/* common paths */
+_hidden const char *libxl__private_bindir_path(void);
+_hidden const char *libxl__xenfirmwaredir_path(void);
+_hidden const char *libxl__xen_config_dir_path(void);
+_hidden const char *libxl__xen_script_dir_path(void);
+_hidden const char *libxl__lock_dir_path(void);
+_hidden const char *libxl__run_dir_path(void);
+_hidden const char *libxl__seabios_path(void);
+_hidden const char *libxl__ovmf_path(void);
+_hidden const char *libxl__ipxe_path(void);
+
+/*----- subprocess execution with timeout -----*/
+
+typedef struct libxl__async_exec_state libxl__async_exec_state;
+
+typedef void libxl__async_exec_callback(libxl__egc *egc,
+ libxl__async_exec_state *aes, int rc, int status);
+/*
+ * Meaning of status and rc:
+ * rc==0, status==0 all went well
+ * rc==0, status!=0 everything OK except child exited nonzero (logged)
+ * rc!=0 something else went wrong (status is real
+ * exit status; maybe reflecting SIGKILL, and
+ * therefore not very interesting, if aes code
+ * killed the child). Logged unless ABORTED.
+ */
+
+struct libxl__async_exec_state {
+ /* caller must fill these in */
+ libxl__ao *ao;
+ const char *what; /* for error msgs, what we're executing */
+ int timeout_ms;
+ libxl__async_exec_callback *callback;
+ /* caller must fill in; as for libxl__exec */
+ int stdfds[3];
+ char **args; /* execution arguments */
+ char **env; /* execution environment */
+
+ /* private */
+ libxl__ev_time time;
+ libxl__ev_child child;
+ int rc;
+};
+
+void libxl__async_exec_init(libxl__async_exec_state *aes);
+int libxl__async_exec_start(libxl__async_exec_state *aes);
+bool libxl__async_exec_inuse(const libxl__async_exec_state *aes);
+
+_hidden void libxl__kill(libxl__gc *gc, pid_t pid, int sig, const char *what);
+/* kill SIGHUP a pid stored in xenstore */
+_hidden int libxl__kill_xs_path(libxl__gc *gc, const char *xs_path_pid,
+ const char *what);
+
+/*----- device addition/removal -----*/
+
+typedef void libxl__device_callback(libxl__egc*, libxl__ao_device*);
+
+/* This functions sets the necessary libxl__ao_device struct values to use
+ * safely inside functions. It marks the operation as "active"
+ * since we need to be sure that all device status structs are set
+ * to active before start queueing events, or we might call
+ * ao_complete before all devices had finished
+ *
+ * libxl__initiate_device_{remove/addition} should not be called without
+ * calling libxl__prepare_ao_device first, since it initializes the private
+ * fields of the struct libxl__ao_device to what this functions expect.
+ *
+ * Once _prepare has been called on a libxl__ao_device, it is safe to just
+ * discard this struct, there's no need to call any destroy function.
+ * _prepare can also be called multiple times with the same libxl__ao_device.
+ *
+ * But if any of the fields `backend_ds', `timeout', `xswait', `qmp' is
+ * used by a caller of _prepare, the caller will have to arrange to clean
+ * or dispose of them.
+ */
+_hidden void libxl__prepare_ao_device(libxl__ao *ao, libxl__ao_device *aodev);
+
+/* generic callback for devices that only need to set ao_complete */
+_hidden void device_addrm_aocomplete(libxl__egc *egc, libxl__ao_device *aodev);
+
+typedef struct {
+ enum {
+ LIBXL__FORCE_AUTO, /* Re-execute with FORCE_ON if op times out */
+ LIBXL__FORCE_ON,
+ LIBXL__FORCE_OFF,
+ } flag;
+} libxl__force;
+
+struct libxl__ao_device {
+ /* filled in by user */
+ libxl__ao *ao;
+ libxl__device_action action;
+ libxl__device *dev;
+ libxl__force force;
+ libxl__device_callback *callback;
+ /* return value, zeroed by user on entry, is valid on callback */
+ int rc;
+ /* private for multidev */
+ int active;
+ libxl__multidev *multidev; /* reference to the containing multidev */
+ /* private for add/remove implementation */
+ libxl__ev_devstate backend_ds;
+ /* Bodge for Qemu devices */
+ libxl__ev_time timeout;
+ /* xenstore watch for backend path of driver domains */
+ libxl__xswait_state xswait;
+ int num_exec;
+ /* for calling hotplug scripts */
+ libxl__async_exec_state aes;
+ /* If we need to update JSON config */
+ bool update_json;
+ /* for asynchronous execution of synchronous-only syscalls etc. */
+ libxl__ev_child child;
+ libxl__ev_qmp qmp;
+ /* 'device_config' can be used to to pass to callbacks a pointer of one
+ * of the type 'libxl_device_$type' corresponding to the device been
+ * hotplug. 'device_type' should have the corresponding
+ * 'libxl__$type_devtype'. */
+ void *device_config;
+ const libxl__device_type *device_type;
+};
+
+/*
+ * Multiple devices "multidev" handling.
+ *
+ * Firstly, you should
+ * libxl__multidev_begin
+ * multidev->callback = ...
+ * Then zero or more times
+ * libxl__multidev_prepare
+ * libxl__initiate_device_{remove/addition}
+ * (or some other thing which will eventually call
+ * aodev->callback or libxl__multidev_one_callback)
+ * Finally, once
+ * libxl__multidev_prepared
+ * which will result (perhaps reentrantly) in one call to
+ * multidev->callback().
+ */
+
+/* Starts preparing to add/remove a bunch of devices. */
+_hidden void libxl__multidev_begin(libxl__ao *ao, libxl__multidev*);
+
+/* Prepares to add/remove one of many devices.
+ * Calls libxl__prepare_ao_device on libxl__ao_device argument provided and
+ * also sets the aodev->callback (to libxl__multidev_one_callback)
+ * The user should not mess with aodev->callback.
+ */
+_hidden void libxl__multidev_prepare_with_aodev(libxl__multidev*,
+ libxl__ao_device*);
+
+/* A wrapper function around libxl__multidev_prepare_with_aodev.
+ * Allocates a libxl__ao_device and prepares it for addition/removal.
+ * Returns the newly allocated libxl__ao_dev.
+ */
+_hidden libxl__ao_device *libxl__multidev_prepare(libxl__multidev*);
+
+/* Indicates to multidev that this one device has been processed.
+ * Normally the multidev user does not need to touch this function, as
+ * multidev_prepare will name it in aodev->callback. However, if you
+ * want to do something more complicated you can set aodev->callback
+ * yourself to something else, so long as you eventually call
+ * libxl__multidev_one_callback.
+ */
+_hidden void libxl__multidev_one_callback(libxl__egc *egc,
+ libxl__ao_device *aodev);
+
+/* Notifies the multidev machinery that we have now finished preparing
+ * and initiating devices. multidev->callback may then be called as
+ * soon as there are no prepared but not completed operations
+ * outstanding, perhaps reentrantly. If rc!=0 (error should have been
+ * logged) multidev->callback will get a non-zero rc.
+ * callback may be set by the user at any point before prepared. */
+_hidden void libxl__multidev_prepared(libxl__egc*, libxl__multidev*, int rc);
+
+typedef void libxl__devices_callback(libxl__egc*, libxl__multidev*, int rc);
+struct libxl__multidev {
+ /* set by user: */
+ libxl__devices_callback *callback;
+ /* for private use by libxl__...ao_devices... machinery: */
+ libxl__ao *ao;
+ libxl__ao_device **array;
+ int used, allocd;
+ libxl__ao_device *preparation;
+};
+
+/*
+ * Algorithm for handling device removal (including domain
+ * destruction). This is somewhat subtle because we may already have
+ * killed the domain and caused the death of qemu.
+ *
+ * In current versions of qemu there is no mechanism for ensuring that
+ * the resources used by its devices (both emulated and any PV devices
+ * provided by qemu) are freed (eg, fds closed) before it shuts down,
+ * and no confirmation from a terminating qemu back to the toolstack.
+ *
+ * This will need to be fixed in future Xen versions. In the meantime
+ * (Xen 4.2) we implement a bodge.
+ *
+ * WE WANT TO UNPLUG WE WANT TO SHUT DOWN OR DESTROY
+ * | |
+ * | LIBXL SENDS SIGHUP TO QEMU
+ * | .....................|........................
+ * | : XEN 4.3+ PLANNED | :
+ * | : QEMU TEARS DOWN ALL DEVICES :
+ * | : FREES RESOURCES (closing fds) :
+ * | : SETS PV BACKENDS TO STATE 5, :
+ * | : waits for PV frontends to shut down :
+ * | : SETS PV BACKENDS TO STATE 6 :
+ * | : | :
+ * | : QEMU NOTIFIES TOOLSTACK (via :
+ * | : xenstore) that it is exiting :
+ * | : QEMU EXITS (parent may be init) :
+ * | : | :
+ * | : TOOLSTACK WAITS FOR QEMU :
+ * | : notices qemu has finished :
+ * | :....................|.......................:
+ * | .--------------------'
+ * V V
+ * for each device
+ * we want to unplug/remove
+ * ..................|...........................................
+ * : V XEN 4.2 RACY BODGE :
+ * : device is provided by qemu :
+ * : | `-----------. :
+ * : something| V :
+ * : else, eg| domain (that is domain for which :
+ * : blkback| this PV device is the backend, :
+ * : | which might be the stub dm) :
+ * : | is still alive? :
+ * : | | | :
+ * : | |alive |dead :
+ * : |<-----------------' | :
+ * : | hopefully qemu is | :
+ * : | still running | :
+ * :............|................. | :
+ * ,----->| : we may be racing :
+ * | backend state? : with qemu's death :
+ * ^ | | : | :
+ * xenstore| |other |6 : WAIT 2.0s :
+ * conflict| | | : TIMEOUT :
+ * | WRITE B.E. | : | :
+ * | STATE:=5 | : hopefully qemu has :
+ * `---' | | : gone by now and :
+ * |ok | : freed its resources :
+ * | | : | :
+ * WAIT FOR | : SET B.E. :
+ * STATE==6 | : STATE:=6 :
+ * / | | :..........|...................:
+ * timeout/ ok| | |
+ * / | | |
+ * | RUN HOTPLUG <-'<----------------'
+ * | SCRIPT
+ * | |
+ * `---> NUKE
+ * BACKEND
+ * |
+ * DONE.
+ */
+
+/*
+ * As of Xen 4.5 we maintain various information, including hotplug
+ * device information, in JSON files, so that we can use this JSON
+ * file as a template to reconstruct domain configuration.
+ *
+ * In essense there are now two views of device state, one is the
+ * primary config (xenstore or QEMU), the other is JSON file.
+ *
+ * Here we maintain one invariant: every device in the primary config
+ * must have an entry in JSON file.
+ *
+ * All device hotplug routines should comply to following pattern:
+ * lock json config (json_lock)
+ * read json config
+ * update in-memory json config with new entry, replacing
+ * any stale entry
+ * for loop -- xs transaction
+ * open xs transaction
+ * check device existence, bail if it exists
+ * write in-memory json config to disk
+ * commit xs transaction
+ * end for loop
+ * unlock json config
+ *
+ * Or in case QEMU is the primary config, this pattern can be use:
+ * qmp_lock (libxl__ev_devlock_init)
+ * lock json config (json_lock)
+ * read json config
+ * update in-memory json config with new entry, replacing
+ * any stale entry
+ * unlock json config
+ * apply new config to primary config
+ * lock json config (json_lock)
+ * read json config
+ * update in-memory json config with new entry, replacing
+ * any stale entry
+ * write in-memory json config to disk
+ * unlock json config
+ * unlock qmp_lock
+ * (CTX_LOCK can be acquired and released several time while holding the
+ * qmp_lock)
+ *
+ * Device removal routines are not touched.
+ *
+ * Here is the proof that we always maintain that invariant and we
+ * don't leak files during interaction of hotplug thread and other
+ * threads / processes.
+ *
+ * # Safe against parallel add
+ *
+ * When another thread / process tries to add same device, it's
+ * blocked by json_lock. The loser of two threads will bail at
+ * existence check, so that we don't overwrite anything.
+ *
+ * # Safe against domain destruction
+ *
+ * If the thread / process trying to destroy domain loses the race, it's
+ * blocked by json_lock. If the hotplug thread is loser, it bails at
+ * acquiring lock because lock acquisition function checks existence of
+ * the domain.
+ *
+ * # Safe against parallel removal
+ *
+ * When another thread / process tries to remove a device, it's _NOT_
+ * blocked by json_lock, but xenstore transaction can help maintain
+ * invariant. The removal threads either a) sees that device in
+ * xenstore, b) doesn't see that device in xenstore.
+ *
+ * In a), it sees that device in xenstore. At that point hotplug is
+ * already finished (both JSON and xenstore changes committed). So that
+ * device can be safely removed. JSON entry is left untouched and
+ * becomes stale, but this is a valid state -- next time when a
+ * device with same identifier gets added, the stale entry gets
+ * overwritten.
+ *
+ * In b), it doesn't see that device in xenstore, but it will commence
+ * anyway. Eventually a forcibly removal is initiated, which will forcely
+ * remove xenstore entry.
+ *
+ * If hotplug threads creates xenstore entry (therefore JSON entry as
+ * well) before force removal, that xenstore entry is removed. We're
+ * left with JSON stale entry but not xenstore entry, which is a valid
+ * state.
+ *
+ * If hotplug thread has not created xenstore entry when the removal
+ * is committed, we're obviously safe. Hotplug thread will add in
+ * xenstore entry afterwards. We have both JSON and xenstore entry,
+ * it's a valid state.
+ */
+
+/* Waits for the passed device to reach state XenbusStateInitWait.
+ * This is not really useful by itself, but is important when executing
+ * hotplug scripts, since we need to be sure the device is in the correct
+ * state before executing them.
+ *
+ * Once finished, aodev->callback will be executed.
+ */
+_hidden void libxl__wait_device_connection(libxl__egc*,
+ libxl__ao_device *aodev);
+
+/* Arranges that dev will be removed to the guest, and the
+ * hotplug scripts will be executed (if necessary). When
+ * this is done (or an error happens), the callback in
+ * aodev->callback will be called.
+ *
+ * The libxl__ao_device passed to this function should be
+ * prepared using libxl__prepare_ao_device prior to calling
+ * this function.
+ *
+ * Once finished, aodev->callback will be executed.
+ */
+_hidden void libxl__initiate_device_generic_remove(libxl__egc *egc,
+ libxl__ao_device *aodev);
+
+_hidden void libxl__initiate_device_usbctrl_remove(libxl__egc *egc,
+ libxl__ao_device *aodev);
+
+/*
+ * libxl__get_hotplug_script_info returns the args and env that should
+ * be passed to the hotplug script for the requested device.
+ *
+ * Since a device might not need to execute any hotplug script, this function
+ * can return the following values:
+ * < 0: Error
+ * 0: No need to execute hotplug script
+ * 1: Execute hotplug script
+ *
+ * The last parameter, "num_exec" refeers to the number of times hotplug
+ * scripts have been called for this device.
+ *
+ * The main body of libxl will, for each device, keep calling
+ * libxl__get_hotplug_script_info, with incrementing values of
+ * num_exec, and executing the resulting script accordingly,
+ * until libxl__get_hotplug_script_info returns<=0.
+ */
+_hidden int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
+ char ***args, char ***env,
+ libxl__device_action action,
+ int num_exec);
+
+/*----- local disk attach: attach a disk locally to run the bootloader -----*/
+
+typedef struct libxl__disk_local_state libxl__disk_local_state;
+typedef void libxl__disk_local_state_callback(libxl__egc*,
+ libxl__disk_local_state*,
+ int rc);
+
+/* A libxl__disk_local_state may be in the following states:
+ * Undefined, Idle, Attaching, Attached, Detaching.
+ */
+struct libxl__disk_local_state {
+ /* filled by the user */
+ libxl__ao *ao;
+ const libxl_device_disk *in_disk;
+ libxl_device_disk disk;
+ const char *blkdev_start;
+ libxl__disk_local_state_callback *callback;
+ /* filled by libxl__device_disk_local_initiate_attach */
+ char *diskpath;
+ /* private for implementation of local detach */
+ libxl__ao_device aodev;
+ int rc;
+};
+
+/*
+ * Prepares a dls for use.
+ * State Undefined -> Idle
+ */
+static inline void libxl__device_disk_local_init(libxl__disk_local_state *dls)
+{
+ dls->rc = 0;
+}
+
+/*
+ * See if we can find a way to access a disk locally
+ */
+_hidden char * libxl__device_disk_find_local_path(libxl__gc *gc,
+ libxl_domid guest_domid,
+ const libxl_device_disk *disk,
+ bool qdisk_direct);
+
+
+/* Make a disk available in this (the control) domain. Always calls
+ * dls->callback when finished.
+ * State Idle -> Attaching
+ *
+ * The state of dls on entry to the callback depends on the value
+ * of rc passed to the callback:
+ * rc == 0: Attached if rc == 0
+ * rc != 0: Idle
+ */
+_hidden void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
+ libxl__disk_local_state *dls);
+
+/* Disconnects a disk device form the control domain. If the passed
+ * dls is not attached (or has already been detached),
+ * libxl__device_disk_local_initiate_detach will just call the callback
+ * directly.
+ * State Idle/Attached -> Detaching
+ *
+ * The state of dls on entry to the callback is Idle.
+ */
+_hidden void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
+ libxl__disk_local_state *dls);
+
+/*----- datacopier: copies data from one fd to another -----*/
+
+typedef struct libxl__datacopier_state libxl__datacopier_state;
+typedef struct libxl__datacopier_buf libxl__datacopier_buf;
+
+/* onwrite==1 means problem happened when writing
+ * rc==FAIL errnoval >0 we had a write error, logged
+ * onwrite==0 means problem happened when reading
+ * rc==0 errnoval==0 we got eof and all data was written
+ * rc==FAIL errnoval >0 we had a read error, logged
+ * onwrite==-1 means some other internal problem
+ * rc==FAIL errnoval==EIO some other internal failure, logged
+ * rc==ABORTED errnoval==0 abort requested, not logged
+ * If we get POLLHUP, we call callback_pollhup with
+ * rc==FAIL errnoval==-1 POLLHUP signalled
+ * or if callback_pollhup==0 this is treated as eof (if POLLIN|POLLHUP
+ * on the reading fd) or an internal failure (otherwise), as above.
+ * In all cases copier is killed before calling this callback */
+typedef void libxl__datacopier_callback(libxl__egc *egc,
+ libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
+
+struct libxl__datacopier_buf {
+ /* private to datacopier */
+ LIBXL_TAILQ_ENTRY(libxl__datacopier_buf) entry;
+ int used;
+ char buf[1000];
+};
+
+struct libxl__datacopier_state {
+ /* caller must fill these in, and they must all remain valid */
+ libxl__ao *ao;
+ int readfd, writefd;
+ ssize_t maxsz;
+ ssize_t bytes_to_read; /* set to -1 to read until EOF */
+ const char *copywhat, *readwhat, *writewhat; /* for error msgs */
+ FILE *log; /* gets a copy of everything */
+ libxl__datacopier_callback *callback;
+ libxl__datacopier_callback *callback_pollhup;
+ void *readbuf; /* Set this to read data into it without writing to an
+ fd. The buffer should be at least as large as the
+ bytes_to_read parameter, which should not be -1. */
+ /* remaining fields are private to datacopier */
+ libxl__ao_abortable abrt;
+ libxl__ev_fd toread, towrite;
+ ssize_t used;
+ LIBXL_TAILQ_HEAD(libxl__datacopier_bufs, libxl__datacopier_buf) bufs;
+};
+
+_hidden void libxl__datacopier_init(libxl__datacopier_state *dc);
+_hidden void libxl__datacopier_kill(libxl__datacopier_state *dc);
+_hidden int libxl__datacopier_start(libxl__datacopier_state *dc);
+
+/* Inserts literal data into the output stream. The data is copied.
+ * May safely be used only immediately after libxl__datacopier_start
+ * (before the ctx is unlocked). But may be called multiple times.
+ * NB exceeding maxsz will fail an assertion! */
+_hidden void libxl__datacopier_prefixdata(libxl__egc*, libxl__datacopier_state*,
+ const void *data, size_t len);
+
+/*----- Save/restore helper (used by creation and suspend) -----*/
+
+typedef struct libxl__srm_save_callbacks {
+ libxl__srm_save_autogen_callbacks a;
+} libxl__srm_save_callbacks;
+
+typedef struct libxl__srm_restore_callbacks {
+ libxl__srm_restore_autogen_callbacks a;
+} libxl__srm_restore_callbacks;
+
+/* a pointer to this struct is also passed as "user" to the
+ * save callout helper callback functions */
+typedef struct libxl__save_helper_state {
+ /* public, caller of run_helper initialises */
+ libxl__ao *ao;
+ uint32_t domid;
+ union {
+ libxl__srm_save_callbacks save;
+ libxl__srm_restore_callbacks restore;
+ } callbacks;
+ int (*recv_callback)(const unsigned char *msg, uint32_t len, void *user);
+ void (*completion_callback)(libxl__egc *egc, void *caller_state,
+ int rc, int retval, int errnoval);
+ void *caller_state;
+ int need_results; /* set to 0 or 1 by caller of run_helper;
+ * if set to 1 then the ultimate caller's
+ * results function must set it to 0 */
+ /* private */
+ int rc;
+ int completed; /* retval/errnoval valid iff completed */
+ int retval, errnoval; /* from xc_domain_save / xc_domain_restore */
+ libxl__ao_abortable abrt;
+ libxl__carefd *pipes[2]; /* 0 = helper's stdin, 1 = helper's stdout */
+ libxl__ev_fd readable;
+ libxl__ev_child child;
+ const char *stdin_what, *stdout_what;
+
+ libxl__egc *egc; /* valid only for duration of each event callback;
+ * is here in this struct for the benefit of the
+ * marshalling and xc callback functions */
+} libxl__save_helper_state;
+
+/*----- checkpoint device related state structure -----*/
+/*
+ * The abstract checkpoint device layer exposes a common
+ * set of API to [external] libxl for manipulating devices attached to
+ * a guest protected by Remus/COLO. The device layer also exposes a set of
+ * [internal] interfaces that every device type must implement.
+ *
+ * The following API are exposed to libxl:
+ *
+ * One-time configuration operations:
+ * +libxl__checkpoint_devices_setup
+ * > Enable output buffering for NICs, setup disk replication, etc.
+ * +libxl__checkpoint_devices_teardown
+ * > Disable output buffering and disk replication; teardown any
+ * associated external setups like qdiscs for NICs.
+ *
+ * Operations executed every checkpoint (in order of invocation):
+ * +libxl__checkpoint_devices_postsuspend
+ * +libxl__checkpoint_devices_preresume
+ * +libxl__checkpoint_devices_commit
+ *
+ * Each device type needs to implement the interfaces specified in
+ * the libxl__checkpoint_device_instance_ops if it wishes to support Remus/COLO.
+ *
+ * The high-level control flow through the checkpoint device layer is shown
+ * below:
+ *
+ * xl remus
+ * |-> libxl_domain_remus_start
+ * |-> libxl__checkpoint_devices_setup
+ * |-> Per-checkpoint libxl__checkpoint_devices_[postsuspend,preresume,commit]
+ * ...
+ * |-> On backup failure, network error or other internal errors:
+ * libxl__checkpoint_devices_teardown
+ */
+
+typedef struct libxl__checkpoint_device libxl__checkpoint_device;
+typedef struct libxl__checkpoint_devices_state libxl__checkpoint_devices_state;
+typedef struct libxl__checkpoint_device_instance_ops libxl__checkpoint_device_instance_ops;
+
+/*
+ * Interfaces to be implemented by every device subkind that wishes to
+ * support Remus/COLO. Functions must be implemented unless otherwise
+ * stated. Many of these functions are asynchronous. They call
+ * dev->aodev.callback when done. The actual implementations may be
+ * synchronous and call dev->aodev.callback directly (as the last
+ * thing they do).
+ */
+struct libxl__checkpoint_device_instance_ops {
+ /* the device kind this ops belongs to... */
+ libxl__device_kind kind;
+
+ /*
+ * Checkpoint operations. May be NULL, meaning the op is not
+ * implemented and the caller should treat them as a no-op (and do
+ * nothing when checkpointing).
+ * Asynchronous.
+ */
+
+ void (*postsuspend)(libxl__egc *egc, libxl__checkpoint_device *dev);
+ void (*preresume)(libxl__egc *egc, libxl__checkpoint_device *dev);
+ void (*commit)(libxl__egc *egc, libxl__checkpoint_device *dev);
+
+ /*
+ * setup() and teardown() are refer to the actual checkpoint device.
+ * Asynchronous.
+ * teardown is called even if setup fails.
+ */
+ /*
+ * setup() should first determines whether the subkind matches the specific
+ * device. If matched, the device will then be managed with this set of
+ * subkind operations.
+ * Yields 0 if the device successfully set up.
+ * CHECKPOINT_DEVOPS_DOES_NOT_MATCH if the ops does not match the device.
+ * any other rc indicates failure.
+ */
+ void (*setup)(libxl__egc *egc, libxl__checkpoint_device *dev);
+ void (*teardown)(libxl__egc *egc, libxl__checkpoint_device *dev);
+};
+
+int init_subkind_nic(libxl__checkpoint_devices_state *cds);
+void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds);
+int init_subkind_drbd_disk(libxl__checkpoint_devices_state *cds);
+void cleanup_subkind_drbd_disk(libxl__checkpoint_devices_state *cds);
+
+typedef void libxl__checkpoint_callback(libxl__egc *,
+ libxl__checkpoint_devices_state *,
+ int rc);
+
+/*
+ * State associated with a checkpoint invocation, including parameters
+ * passed to the checkpoint abstract device layer by the remus
+ * save/restore machinery.
+ */
+struct libxl__checkpoint_devices_state {
+ /*-- must be set by caller of libxl__checkpoint_device_(setup|teardown) --*/
+
+ libxl__ao *ao;
+ uint32_t domid;
+ libxl__checkpoint_callback *callback;
+ void *concrete_data;
+ int device_kind_flags;
+ /* The ops must be pointer array, and the last ops must be NULL. */
+ const libxl__checkpoint_device_instance_ops **ops;
+
+ /*----- private for abstract layer only -----*/
+
+ int num_devices;
+ /*
+ * this array is allocated before setup the checkpoint devices by the
+ * checkpoint abstract layer.
+ * devs may be NULL, means there's no checkpoint devices that has been
+ * set up.
+ * the size of this array is 'num_devices', which is the total number
+ * of libxl nic devices and disk devices(num_nics + num_disks).
+ */
+ libxl__checkpoint_device **devs;
+
+ libxl_device_nic *nics;
+ int num_nics;
+ libxl_device_disk *disks;
+ int num_disks;
+
+ libxl__multidev multidev;
+};
+
+/*
+ * Information about a single device being handled by remus.
+ * Allocated by the checkpoint abstract layer.
+ */
+struct libxl__checkpoint_device {
+ /*----- shared between abstract and concrete layers -----*/
+ /*
+ * if this is true, that means the subkind ops match the device
+ */
+ bool matched;
+
+ /*----- set by checkpoint device abstruct layer -----*/
+ /* libxl__device_* which this checkpoint device related to */
+ const void *backend_dev;
+ libxl__device_kind kind;
+ libxl__checkpoint_devices_state *cds;
+ libxl__ao_device aodev;
+
+ /*----- private for abstract layer only -----*/
+
+ /*
+ * Control and state variables for the asynchronous callback
+ * based loops which iterate over device subkinds, and over
+ * individual devices.
+ */
+ int ops_index;
+ const libxl__checkpoint_device_instance_ops *ops;
+
+ /*----- private for concrete (device-specific) layer -----*/
+
+ /* concrete device's private data */
+ void *concrete_data;
+};
+
+/* the following 5 APIs are async ops, call cds->callback when done */
+_hidden void libxl__checkpoint_devices_setup(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds);
+_hidden void libxl__checkpoint_devices_teardown(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds);
+_hidden void libxl__checkpoint_devices_postsuspend(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds);
+_hidden void libxl__checkpoint_devices_preresume(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds);
+_hidden void libxl__checkpoint_devices_commit(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds);
+
+/*----- Remus related state structure -----*/
+typedef struct libxl__remus_state libxl__remus_state;
+struct libxl__remus_state {
+ /* private */
+ libxl__ev_time checkpoint_timeout; /* used for Remus checkpoint */
+ int interval; /* checkpoint interval */
+
+ /*----- private for concrete (device-specific) layer only -----*/
+ /* private for nic device subkind ops */
+ char *netbufscript;
+ struct nl_sock *nlsock;
+ struct nl_cache *qdisc_cache;
+
+ /* private for drbd disk subkind ops */
+ char *drbd_probe_script;
+};
+_hidden int libxl__netbuffer_enabled(libxl__gc *gc);
+
+/*----- Legacy conversion helper -----*/
+typedef struct libxl__conversion_helper_state libxl__conversion_helper_state;
+
+struct libxl__conversion_helper_state {
+ /* Public - Must be filled by caller unless noted. */
+ libxl__ao *ao;
+ int legacy_fd; /* fd to read the legacy stream from. */
+ bool hvm; /* pv or hvm domain? */
+ libxl__carefd *v2_carefd; /* Filled by successful call to
+ * libxl__convert_legacy_stream(). Caller
+ * assumes ownership of the fd. */
+ void (*completion_callback)(
+ libxl__egc *egc, libxl__conversion_helper_state *chs, int rc);
+ /* private */
+ int rc;
+ libxl__ao_abortable abrt;
+ libxl__ev_child child;
+};
+
+_hidden void libxl__conversion_helper_init
+ (libxl__conversion_helper_state *chs);
+_hidden int libxl__convert_legacy_stream(libxl__egc *egc,
+ libxl__conversion_helper_state *chs);
+_hidden void libxl__conversion_helper_abort(libxl__egc *egc,
+ libxl__conversion_helper_state *chs, int rc);
+static inline bool libxl__conversion_helper_inuse
+ (const libxl__conversion_helper_state *chs)
+{ return libxl__ev_child_inuse(&chs->child); }
+
+/* State for reading a libxl migration v2 stream */
+typedef struct libxl__stream_read_state libxl__stream_read_state;
+
+typedef struct libxl__sr_record_buf {
+ /* private to stream read helper */
+ LIBXL_STAILQ_ENTRY(struct libxl__sr_record_buf) entry;
+ libxl__sr_rec_hdr hdr;
+ void *body; /* iff hdr.length != 0 */
+} libxl__sr_record_buf;
+
+struct libxl__stream_read_state {
+ /* filled by the user */
+ libxl__ao *ao;
+ libxl__domain_create_state *dcs;
+ int fd;
+ bool legacy;
+ bool back_channel;
+ void (*completion_callback)(libxl__egc *egc,
+ libxl__stream_read_state *srs,
+ int rc);
+ void (*checkpoint_callback)(libxl__egc *egc,
+ libxl__stream_read_state *srs,
+ int rc);
+ /* Private */
+ int rc;
+ bool running;
+ bool in_checkpoint;
+ bool sync_teardown; /* Only used to coordinate shutdown on error path. */
+ bool in_checkpoint_state;
+ libxl__save_helper_state shs;
+ libxl__conversion_helper_state chs;
+
+ /* Main stream-reading data. */
+ libxl__datacopier_state dc; /* Only used when reading a record */
+ libxl__sr_hdr hdr;
+ LIBXL_STAILQ_HEAD(, libxl__sr_record_buf) record_queue; /* NOGC */
+ enum {
+ SRS_PHASE_NORMAL,
+ SRS_PHASE_BUFFERING,
+ SRS_PHASE_UNBUFFERING,
+ } phase;
+ bool recursion_guard;
+
+ /* Only used while actively reading a record from the stream. */
+ libxl__sr_record_buf *incoming_record; /* NOGC */
+
+ /* Both only used when processing an EMULATOR record. */
+ libxl__datacopier_state emu_dc;
+ libxl__carefd *emu_carefd;
+};
+
+_hidden void libxl__stream_read_init(libxl__stream_read_state *stream);
+_hidden void libxl__stream_read_start(libxl__egc *egc,
+ libxl__stream_read_state *stream);
+_hidden void libxl__stream_read_start_checkpoint(libxl__egc *egc,
+ libxl__stream_read_state *stream);
+_hidden void libxl__stream_read_checkpoint_state(libxl__egc *egc,
+ libxl__stream_read_state *stream);
+_hidden void libxl__stream_read_abort(libxl__egc *egc,
+ libxl__stream_read_state *stream, int rc);
+static inline bool
+libxl__stream_read_inuse(const libxl__stream_read_state *stream)
+{
+ return stream->running;
+}
+
+#include "libxl_colo.h"
+
+/*----- Domain suspend (save) state structure -----*/
+/*
+ * "suspend" refers to quiescing the VM, so pausing qemu, making a
+ * remote_shutdown(SHUTDOWN_suspend) hypercall etc.
+ *
+ * "save" refers to the actions involved in actually shuffling the
+ * state of the VM, so xc_domain_save() etc.
+ */
+
+typedef struct libxl__domain_suspend_state libxl__domain_suspend_state;
+typedef struct libxl__domain_save_state libxl__domain_save_state;
+
+typedef void libxl__domain_save_cb(libxl__egc*,
+ libxl__domain_save_state*, int rc);
+typedef void libxl__save_device_model_cb(libxl__egc*,
+ libxl__domain_save_state*, int rc);
+
+/* State for writing a libxl migration v2 stream */
+typedef struct libxl__stream_write_state libxl__stream_write_state;
+typedef void (*sws_record_done_cb)(libxl__egc *egc,
+ libxl__stream_write_state *sws);
+struct libxl__stream_write_state {
+ /* filled by the user */
+ libxl__ao *ao;
+ libxl__domain_save_state *dss;
+ int fd;
+ bool back_channel;
+ void (*completion_callback)(libxl__egc *egc,
+ libxl__stream_write_state *sws,
+ int rc);
+ void (*checkpoint_callback)(libxl__egc *egc,
+ libxl__stream_write_state *sws,
+ int rc);
+ /* Private */
+ int rc;
+ bool running;
+ bool in_checkpoint;
+ bool sync_teardown; /* Only used to coordinate shutdown on error path. */
+ bool in_checkpoint_state;
+ libxl__save_helper_state shs;
+
+ /* Main stream-writing data. */
+ libxl__datacopier_state dc;
+ sws_record_done_cb record_done_callback;
+
+ /* Cache device model version. */
+ libxl_device_model_version device_model_version;
+
+ /* Only used when constructing EMULATOR records. */
+ libxl__datacopier_state emu_dc;
+ libxl__carefd *emu_carefd;
+ libxl__sr_rec_hdr emu_rec_hdr;
+ libxl__sr_emulator_hdr emu_sub_hdr;
+ void *emu_body;
+};
+
+_hidden void libxl__stream_write_init(libxl__stream_write_state *stream);
+_hidden void libxl__stream_write_start(libxl__egc *egc,
+ libxl__stream_write_state *stream);
+_hidden void
+libxl__stream_write_start_checkpoint(libxl__egc *egc,
+ libxl__stream_write_state *stream);
+_hidden void
+libxl__stream_write_checkpoint_state(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ libxl_sr_checkpoint_state *srcs);
+_hidden void libxl__stream_write_abort(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ int rc);
+static inline bool
+libxl__stream_write_inuse(const libxl__stream_write_state *stream)
+{
+ return stream->running;
+}
+
+typedef struct libxl__logdirty_switch {
+ /* Set by caller of libxl__domain_common_switch_qemu_logdirty */
+ libxl__ao *ao;
+ void (*callback)(libxl__egc *egc, struct libxl__logdirty_switch *lds,
+ int rc);
+
+ const char *cmd;
+ const char *cmd_path;
+ const char *ret_path;
+ libxl__ev_xswatch watch;
+ libxl__ev_time timeout;
+ libxl__ev_qmp qmp;
+} libxl__logdirty_switch;
+
+_hidden void libxl__logdirty_init(libxl__logdirty_switch *lds);
+
+struct libxl__domain_suspend_state {
+ /* set by caller of libxl__domain_suspend_init */
+ libxl__ao *ao;
+ uint32_t domid;
+ bool live;
+
+ /* private */
+ libxl_domain_type type;
+
+ libxl__ev_evtchn guest_evtchn;
+ int guest_evtchn_lockfd;
+ int guest_responded;
+
+ libxl__xswait_state pvcontrol;
+ libxl__ev_xswatch guest_watch;
+ libxl__ev_time guest_timeout;
+ libxl__ev_qmp qmp;
+
+ const char *dm_savefile;
+ void (*callback_device_model_done)(libxl__egc*,
+ struct libxl__domain_suspend_state*, int rc);
+ void (*callback_common_done)(libxl__egc*,
+ struct libxl__domain_suspend_state*, int ok);
+};
+int libxl__domain_suspend_init(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps,
+ libxl_domain_type type);
+
+/* calls dsps->callback_device_model_done when done
+ * may synchronously calls this callback */
+_hidden void libxl__qmp_suspend_save(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps);
+
+struct libxl__domain_save_state {
+ /* set by caller of libxl__domain_save */
+ libxl__ao *ao;
+ libxl__domain_save_cb *callback;
+
+ uint32_t domid;
+ int fd;
+ int fdfl; /* original flags on fd */
+ int recv_fd;
+ libxl_domain_type type;
+ int live;
+ int debug;
+ int checkpointed_stream;
+ const libxl_domain_remus_info *remus;
+ /* private */
+ int rc;
+ int xcflags;
+ libxl__domain_suspend_state dsps;
+ union {
+ /* for Remus */
+ libxl__remus_state rs;
+ /* for COLO */
+ libxl__colo_save_state css;
+ };
+ libxl__checkpoint_devices_state cds;
+ libxl__stream_write_state sws;
+ libxl__logdirty_switch logdirty;
+};
+
+
+/*----- openpty -----*/
+
+/*
+ * opens count (>0) ptys like count calls to openpty, and then
+ * calls back. On entry, all op[].master and op[].slave must be
+ * 0. On callback, either rc==0 and master and slave are non-0,
+ * or rc is a libxl error and they are both 0. If libxl__openpty
+ * returns non-0 no callback will happen and everything is left
+ * cleaned up.
+ */
+
+typedef struct libxl__openpty_state libxl__openpty_state;
+typedef struct libxl__openpty_result libxl__openpty_result;
+typedef void libxl__openpty_callback(libxl__egc *egc, libxl__openpty_state *op);
+
+struct libxl__openpty_state {
+ /* caller must fill these in, and they must all remain valid */
+ libxl__ao *ao;
+ libxl__openpty_callback *callback;
+ int count;
+ libxl__openpty_result *results; /* actual size is count, out parameter */
+ /* public, result, caller may only read in callback */
+ int rc;
+ /* private for implementation */
+ libxl__ev_child child;
+};
+
+struct libxl__openpty_result {
+ libxl__carefd *master, *slave;
+};
+
+int libxl__openptys(libxl__openpty_state *op,
+ struct termios *termp,
+ struct winsize *winp);
+
+
+/*----- bootloader -----*/
+
+typedef struct libxl__bootloader_state libxl__bootloader_state;
+typedef void libxl__run_bootloader_callback(libxl__egc*,
+ libxl__bootloader_state*, int rc);
+typedef void libxl__bootloader_console_callback(libxl__egc*,
+ libxl__bootloader_state*);
+
+struct libxl__bootloader_state {
+ /* caller must fill these in, and they must all remain valid */
+ libxl__ao *ao;
+ libxl__run_bootloader_callback *callback;
+ libxl__bootloader_console_callback *console_available;
+ const libxl_domain_build_info *info;
+ libxl_device_disk *disk;
+ /* Should be zeroed by caller on entry. Will be filled in by
+ * bootloader machinery; represents the local attachment of the
+ * disk for the benefit of the bootloader. Must be detached by
+ * the caller using libxl__device_disk_local_initiate_detach.
+ * (This is safe to do after ->callback() has happened since
+ * the domain's kernel and initramfs will have been copied
+ * out of the guest's disk into a temporary directory, mapped
+ * as file references, and deleted. */
+ libxl__disk_local_state dls;
+ uint32_t domid;
+ /* outputs:
+ * - caller must initialise kernel and ramdisk to point to file
+ * references, these will be updated and mapped;
+ * - caller must initialise cmdline to NULL, it will be updated with a
+ * string allocated from the gc;
+ */
+ libxl__file_reference *kernel, *ramdisk;
+ const char *cmdline;
+ /* private to libxl__run_bootloader */
+ char *outputpath, *outputdir, *logfile;
+ libxl__openpty_state openpty;
+ libxl__openpty_result ptys[2]; /* [0] is for bootloader */
+ libxl__ev_child child;
+ libxl__domaindeathcheck deathcheck;
+ int nargs, argsspace;
+ const char **args;
+ libxl__datacopier_state keystrokes, display;
+ int rc, got_pollhup;
+};
+
+_hidden void libxl__bootloader_init(libxl__bootloader_state *bl);
+
+/* Will definitely call st->callback, perhaps reentrantly.
+ * If callback is passed rc==0, will have updated st->info appropriately */
+_hidden void libxl__bootloader_run(libxl__egc*, libxl__bootloader_state *st);
+
+/*----- Generic Device Handling -----*/
+#define LIBXL_DEFINE_DEVICE_ADD(type) \
+ int libxl_device_##type##_add(libxl_ctx *ctx, \
+ uint32_t domid, libxl_device_##type *type, \
+ const libxl_asyncop_how *ao_how) \
+ { \
+ AO_CREATE(ctx, domid, ao_how); \
+ libxl__ao_device *aodev; \
+ \
+ GCNEW(aodev); \
+ libxl__prepare_ao_device(ao, aodev); \
+ aodev->action = LIBXL__DEVICE_ACTION_ADD; \
+ aodev->callback = device_addrm_aocomplete; \
+ aodev->update_json = true; \
+ libxl__device_##type##_add(egc, domid, type, aodev); \
+ \
+ return AO_INPROGRESS; \
+ }
+
+#define LIBXL_DEFINE_DEVICES_ADD(type) \
+ void libxl__add_##type##s(libxl__egc *egc, libxl__ao *ao, uint32_t domid, \
+ libxl_domain_config *d_config, \
+ libxl__multidev *multidev) \
+ { \
+ AO_GC; \
+ int i; \
+ for (i = 0; i < d_config->num_##type##s; i++) { \
+ libxl__ao_device *aodev = libxl__multidev_prepare(multidev); \
+ libxl__device_##type##_add(egc, domid, &d_config->type##s[i], \
+ aodev); \
+ } \
+ }
+
+#define LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, remtype, removedestroy, f) \
+ int libxl_device_##type##_##removedestroy(libxl_ctx *ctx, \
+ uint32_t domid, libxl_device_##type *type, \
+ const libxl_asyncop_how *ao_how) \
+ { \
+ AO_CREATE(ctx, domid, ao_how); \
+ libxl__device *device; \
+ libxl__ao_device *aodev; \
+ int rc; \
+ \
+ GCNEW(device); \
+ rc = libxl__device_from_##type(gc, domid, type, device); \
+ if (rc != 0) goto out; \
+ \
+ GCNEW(aodev); \
+ libxl__prepare_ao_device(ao, aodev); \
+ aodev->action = LIBXL__DEVICE_ACTION_REMOVE; \
+ aodev->dev = device; \
+ aodev->callback = device_addrm_aocomplete; \
+ aodev->force.flag = f; \
+ libxl__initiate_device_##remtype##_remove(egc, aodev); \
+ \
+ out: \
+ if (rc) return AO_CREATE_FAIL(rc); \
+ return AO_INPROGRESS; \
+ }
+
+#define LIBXL_DEFINE_UPDATE_DEVID(name) \
+ int libxl__device_##name##_update_devid(libxl__gc *gc, \
+ uint32_t domid, \
+ libxl_device_##name *type) \
+ { \
+ if (type->devid == -1) \
+ type->devid = libxl__device_nextid(gc, domid, \
+ libxl__##name##_devtype.type); \
+ if (type->devid < 0) \
+ return ERROR_FAIL; \
+ return 0; \
+ }
+
+#define LIBXL_DEFINE_DEVICE_FROM_TYPE(name) \
+ int libxl__device_from_##name(libxl__gc *gc, uint32_t domid, \
+ libxl_device_##name *type, \
+ libxl__device *device) \
+ { \
+ device->backend_devid = type->devid; \
+ device->backend_domid = type->backend_domid; \
+ device->backend_kind = libxl__##name##_devtype.type; \
+ device->devid = type->devid; \
+ device->domid = domid; \
+ device->kind = libxl__##name##_devtype.type; \
+ \
+ return 0; \
+ }
+
+#define LIBXL_DEFINE_DEVID_TO_DEVICE(name) \
+ int libxl_devid_to_device_##name(libxl_ctx *ctx, uint32_t domid, \
+ int devid, \
+ libxl_device_##name *type) \
+ { \
+ GC_INIT(ctx); \
+ \
+ char *device_path; \
+ const char *tmp; \
+ int rc; \
+ \
+ libxl_device_##name##_init(type); \
+ \
+ device_path = GCSPRINTF("%s/device/%s/%d", \
+ libxl__xs_libxl_path(gc, domid), \
+ libxl__device_kind_to_string( \
+ libxl__##name##_devtype.type), \
+ devid); \
+ \
+ if (libxl__xs_read_mandatory(gc, XBT_NULL, device_path, &tmp)) {\
+ rc = ERROR_NOTFOUND; goto out; \
+ } \
+ \
+ if (libxl__##name##_devtype.from_xenstore) { \
+ rc = libxl__##name##_devtype.from_xenstore(gc, device_path, \
+ devid, type); \
+ if (rc) goto out; \
+ } \
+ \
+ rc = 0; \
+ \
+ out: \
+ \
+ GC_FREE; \
+ return rc; \
+ }
+
+
+#define LIBXL_DEFINE_DEVICE_REMOVE(type) \
+ LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, remove, \
+ LIBXL__FORCE_AUTO) \
+ LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, destroy, \
+ LIBXL__FORCE_ON)
+
+#define LIBXL_DEFINE_DEVICE_REMOVE_CUSTOM(type) \
+ LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, type, remove, \
+ LIBXL__FORCE_AUTO) \
+ LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, type, destroy, \
+ LIBXL__FORCE_ON)
+
+#define LIBXL_DEFINE_DEVICE_SAFE_REMOVE(type) \
+ LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, safe_remove, \
+ LIBXL__FORCE_OFF)
+
+#define LIBXL_DEFINE_DEVICE_LIST(type) \
+ libxl_device_##type *libxl_device_##type##_list(libxl_ctx *ctx, \
+ uint32_t domid, \
+ int *num) \
+ { \
+ libxl_device_##type *r; \
+ GC_INIT(ctx); \
+ r = libxl__device_list(gc, &libxl__##type##_devtype, \
+ domid, num); \
+ GC_FREE; \
+ return r; \
+ } \
+ \
+ void libxl_device_##type##_list_free(libxl_device_##type *list, \
+ int num) \
+ { \
+ libxl__device_list_free(&libxl__##type##_devtype, list, num); \
+ }
+
+typedef void (*device_add_fn_t)(libxl__egc *, libxl__ao *, uint32_t,
+ libxl_domain_config *, libxl__multidev *);
+typedef int (*device_set_default_fn_t)(libxl__gc *, uint32_t, void *, bool);
+typedef int (*device_to_device_fn_t)(libxl__gc *, uint32_t, void *,
+ libxl__device *);
+typedef void (*device_init_fn_t)(void *);
+typedef void (*device_copy_fn_t)(libxl_ctx *, void *dst, const void *src);
+typedef void (*device_dispose_fn_t)(void *);
+typedef int (*device_compare_fn_t)(const void *, const void *);
+typedef void (*device_merge_fn_t)(libxl_ctx *, void *, void *);
+typedef int (*device_dm_needed_fn_t)(void *, unsigned);
+typedef void (*device_update_config_fn_t)(libxl__gc *, void *, void *);
+typedef int (*device_update_devid_fn_t)(libxl__gc *, uint32_t, void *);
+typedef int (*device_get_num_fn_t)(libxl__gc *, const char *, unsigned int *);
+typedef int (*device_from_xenstore_fn_t)(libxl__gc *, const char *,
+ libxl_devid, void *);
+typedef int (*device_set_xenstore_config_fn_t)(libxl__gc *, uint32_t, void *,
+ flexarray_t *, flexarray_t *,
+ flexarray_t *);
+
+struct libxl__device_type {
+ libxl__device_kind type;
+ int skip_attach; /* Skip entry in domcreate_attach_devices() if 1 */
+ int ptr_offset; /* Offset of device array ptr in libxl_domain_config */
+ int num_offset; /* Offset of # of devices in libxl_domain_config */
+ int dev_elem_size; /* Size of one device element in array */
+ device_add_fn_t add;
+ device_set_default_fn_t set_default;
+ device_to_device_fn_t to_device;
+ device_init_fn_t init;
+ device_copy_fn_t copy;
+ device_dispose_fn_t dispose;
+ device_compare_fn_t compare;
+ device_merge_fn_t merge;
+ device_dm_needed_fn_t dm_needed;
+ device_update_config_fn_t update_config;
+ device_update_devid_fn_t update_devid;
+ device_get_num_fn_t get_num;
+ device_from_xenstore_fn_t from_xenstore;
+ device_set_xenstore_config_fn_t set_xenstore_config;
+};
+
+#define DEFINE_DEVICE_TYPE_STRUCT_X(name, sname, kind, ...) \
+ const libxl__device_type libxl__ ## name ## _devtype = { \
+ .type = LIBXL__DEVICE_KIND_ ## kind, \
+ .ptr_offset = offsetof(libxl_domain_config, name ## s), \
+ .num_offset = offsetof(libxl_domain_config, num_ ## name ## s), \
+ .dev_elem_size = sizeof(libxl_device_ ## sname), \
+ .add = libxl__add_ ## name ## s, \
+ .set_default = (device_set_default_fn_t) \
+ libxl__device_ ## sname ## _setdefault, \
+ .to_device = (device_to_device_fn_t)libxl__device_from_ ## name, \
+ .init = (device_init_fn_t)libxl_device_ ## sname ## _init, \
+ .copy = (device_copy_fn_t)libxl_device_ ## sname ## _copy, \
+ .dispose = (device_dispose_fn_t) \
+ libxl_device_ ## sname ## _dispose, \
+ .compare = (device_compare_fn_t) \
+ libxl_device_ ## sname ## _compare, \
+ .update_devid = (device_update_devid_fn_t) \
+ libxl__device_ ## sname ## _update_devid, \
+ __VA_ARGS__ \
+ }
+
+#define DEFINE_DEVICE_TYPE_STRUCT(name, kind, ...) \
+ DEFINE_DEVICE_TYPE_STRUCT_X(name, name, kind, __VA_ARGS__)
+
+static inline void **libxl__device_type_get_ptr(
+ const libxl__device_type *dt, const libxl_domain_config *d_config)
+{
+ return (void **)((void *)d_config + dt->ptr_offset);
+}
+
+static inline void *libxl__device_type_get_elem(
+ const libxl__device_type *dt, const libxl_domain_config *d_config,
+ int e)
+{
+ return *libxl__device_type_get_ptr(dt, d_config) + dt->dev_elem_size * e;
+}
+
+static inline int *libxl__device_type_get_num(
+ const libxl__device_type *dt, const libxl_domain_config *d_config)
+{
+ return (int *)((void *)d_config + dt->num_offset);
+}
+
+extern const libxl__device_type libxl__vfb_devtype;
+extern const libxl__device_type libxl__vkb_devtype;
+extern const libxl__device_type libxl__disk_devtype;
+extern const libxl__device_type libxl__nic_devtype;
+extern const libxl__device_type libxl__vtpm_devtype;
+extern const libxl__device_type libxl__usbctrl_devtype;
+extern const libxl__device_type libxl__usbdev_devtype;
+extern const libxl__device_type libxl__pcidev_devtype;
+extern const libxl__device_type libxl__vdispl_devtype;
+extern const libxl__device_type libxl__p9_devtype;
+extern const libxl__device_type libxl__pvcallsif_devtype;
+extern const libxl__device_type libxl__vsnd_devtype;
+
+extern const libxl__device_type *device_type_tbl[];
+
+/*----- Domain destruction -----*/
+
+/* Domain destruction has been split into two functions:
+ *
+ * libxl__domain_destroy is the main destroy function, which detects
+ * stubdoms and calls libxl__destroy_domid on the domain and its
+ * stubdom if present, creating a different libxl__destroy_domid_state
+ * for each one of them.
+ *
+ * libxl__destroy_domid actually destroys the domain, but it
+ * doesn't check for stubdomains, since that would involve
+ * recursion, which we want to avoid.
+ */
+
+typedef struct libxl__domain_destroy_state libxl__domain_destroy_state;
+typedef struct libxl__destroy_domid_state libxl__destroy_domid_state;
+typedef struct libxl__destroy_devicemodel_state libxl__destroy_devicemodel_state;
+typedef struct libxl__devices_remove_state libxl__devices_remove_state;
+
+typedef void libxl__domain_destroy_cb(libxl__egc *egc,
+ libxl__domain_destroy_state *dds,
+ int rc);
+
+typedef void libxl__domid_destroy_cb(libxl__egc *egc,
+ libxl__destroy_domid_state *dis,
+ int rc);
+
+typedef void libxl__devicemodel_destroy_cb(libxl__egc *egc,
+ libxl__destroy_devicemodel_state *ddms,
+ int rc);
+
+typedef void libxl__devices_remove_callback(libxl__egc *egc,
+ libxl__devices_remove_state *drs,
+ int rc);
+
+struct libxl__devices_remove_state {
+ /* filled in by user */
+ libxl__ao *ao;
+ uint32_t domid;
+ libxl__devices_remove_callback *callback;
+ libxl__force force; /* libxl_device_TYPE_destroy rather than _remove */
+ /* private */
+ libxl__multidev multidev;
+ int num_devices;
+};
+
+struct libxl__destroy_devicemodel_state {
+ /* filled in by user */
+ libxl__ao *ao;
+ uint32_t domid;
+ libxl__devicemodel_destroy_cb *callback; /* May be called re-entrantly */
+ /* private to implementation */
+ libxl__ev_child destroyer;
+ int rc; /* Accumulated return value for the destroy operation */
+};
+
+struct libxl__destroy_domid_state {
+ /* filled in by user */
+ libxl__ao *ao;
+ uint32_t domid;
+ libxl__domid_destroy_cb *callback;
+ /* private to implementation */
+ libxl__devices_remove_state drs;
+ libxl__destroy_devicemodel_state ddms;
+ libxl__ev_child destroyer;
+ bool soft_reset;
+ libxl__multidev multidev;
+};
+
+struct libxl__domain_destroy_state {
+ /* filled by the user */
+ libxl__ao *ao;
+ uint32_t domid;
+ libxl__domain_destroy_cb *callback;
+ /* Private */
+ int rc;
+ uint32_t stubdomid;
+ libxl__destroy_domid_state stubdom;
+ int stubdom_finished;
+ libxl__destroy_domid_state domain;
+ int domain_finished;
+ bool soft_reset;
+};
+
+/*
+ * Entry point for domain destruction
+ * This function checks for stubdom presence and then calls
+ * libxl__destroy_domid on the passed domain and its stubdom if found.
+ */
+_hidden void libxl__domain_destroy(libxl__egc *egc,
+ libxl__domain_destroy_state *dds);
+
+/* Used to destroy a domain with the passed id (it doesn't check for stubs) */
+_hidden void libxl__destroy_domid(libxl__egc *egc,
+ libxl__destroy_domid_state *dis);
+
+/* Used to detroy the device model */
+_hidden void libxl__destroy_device_model(libxl__egc *egc,
+ libxl__destroy_devicemodel_state *ddms);
+
+/* Entry point for devices destruction */
+_hidden void libxl__devices_destroy(libxl__egc *egc,
+ libxl__devices_remove_state *drs);
+
+/* Helper function to add a bunch of disks. This should be used when
+ * the caller is inside an async op. "multidev" will NOT be prepared by
+ * this function, so the caller must make sure to call
+ * libxl__multidev_begin before calling this function.
+ *
+ * The "callback" will be called for each device, and the user is responsible
+ * for calling libxl__ao_device_check_last on the callback.
+ */
+_hidden void libxl__add_disks(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
+ libxl_domain_config *d_config,
+ libxl__multidev *multidev);
+
+_hidden void libxl__add_nics(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
+ libxl_domain_config *d_config,
+ libxl__multidev *multidev);
+
+/*----- device model creation -----*/
+
+/* First layer; wraps libxl__spawn_spawn. */
+
+typedef struct libxl__dm_spawn_state libxl__dm_spawn_state;
+
+typedef void libxl__dm_spawn_cb(libxl__egc *egc, libxl__dm_spawn_state*,
+ int rc /* if !0, error was logged */);
+
+/* Call dmss_init and dmss_dispose to initialise and dispose of
+ * libxl__dm_spawn_state */
+struct libxl__dm_spawn_state {
+ /* mixed - spawn.ao must be initialised by user; rest is private: */
+ libxl__spawn_state spawn;
+ libxl__ev_qmp qmp;
+ libxl__ev_time timeout;
+ libxl__dm_resume_state dmrs;
+ /* filled in by user, must remain valid: */
+ uint32_t guest_domid; /* domain being served */
+ libxl_domain_config *guest_config;
+ libxl__domain_build_state *build_state; /* relates to guest_domid */
+ libxl__dm_spawn_cb *callback;
+};
+
+_hidden void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state*);
+
+/*
+ * Called after forking but before executing the local devicemodel.
+ */
+_hidden int libxl__local_dm_preexec_restrict(libxl__gc *gc);
+
+/* Stubdom device models. */
+
+typedef struct {
+ /* Mixed - user must fill in public parts EXCEPT callback,
+ * which may be undefined on entry. (See above for details) */
+ libxl__dm_spawn_state dm; /* the stub domain device model */
+ /* filled in by user, must remain valid: */
+ libxl__dm_spawn_cb *callback; /* called as callback(,&sdss->dm,) */
+ /* private to libxl__spawn_stub_dm: */
+ libxl_domain_config dm_config;
+ libxl__domain_build_state dm_state;
+ libxl__dm_spawn_state pvqemu;
+ libxl__destroy_domid_state dis;
+ libxl__multidev multidev;
+ libxl__xswait_state xswait;
+ libxl__spawn_state qmp_proxy_spawn;
+} libxl__stub_dm_spawn_state;
+
+_hidden void libxl__spawn_stub_dm(libxl__egc *egc, libxl__stub_dm_spawn_state*);
+
+_hidden char *libxl__stub_dm_name(libxl__gc *gc, const char * guest_name);
+
+/* Qdisk backend launch helpers */
+
+_hidden void libxl__spawn_qdisk_backend(libxl__egc *egc,
+ libxl__dm_spawn_state *dmss);
+_hidden int libxl__destroy_qdisk_backend(libxl__gc *gc, uint32_t domid);
+
+/*----- Domain creation -----*/
+
+
+struct libxl__domain_create_state {
+ /* filled in by user */
+ libxl__ao *ao;
+ libxl_domain_config *guest_config;
+ libxl_domain_config guest_config_saved; /* vanilla config */
+ int restore_fd, libxc_fd;
+ int restore_fdfl; /* original flags of restore_fd */
+ int send_back_fd;
+ libxl_domain_restore_params restore_params;
+ uint32_t domid;
+ bool soft_reset;
+ libxl__domain_create_cb *callback;
+ libxl_asyncprogress_how aop_console_how;
+ /* private to domain_create */
+ int guest_domid;
+ int device_type_idx;
+ const char *colo_proxy_script;
+ libxl__domain_build_state build_state;
+ libxl__colo_restore_state crs;
+ libxl__checkpoint_devices_state cds;
+ libxl__bootloader_state bl;
+ libxl__stub_dm_spawn_state sdss;
+ /* If we're not doing stubdom, we use only sdss.dm,
+ * for the non-stubdom device model. */
+ libxl__stream_read_state srs;
+ /* necessary if the domain creation failed and we have to destroy it */
+ libxl__domain_destroy_state dds;
+ libxl__multidev multidev;
+ libxl__xswait_state console_xswait;
+};
+
+_hidden int libxl__device_nic_set_devids(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ uint32_t domid);
+
+/*----- Domain suspend (save) functions -----*/
+
+/* calls dss->callback when done */
+_hidden void libxl__domain_save(libxl__egc *egc,
+ libxl__domain_save_state *dss);
+
+
+/* calls libxl__xc_domain_suspend_done when done */
+_hidden void libxl__xc_domain_save(libxl__egc *egc,
+ libxl__domain_save_state *dss,
+ libxl__save_helper_state *shs);
+/* If rc==0 then retval is the return value from xc_domain_save
+ * and errnoval is the errno value it provided.
+ * If rc!=0, retval and errnoval are undefined. */
+_hidden void libxl__xc_domain_save_done(libxl__egc*, void *dss_void,
+ int rc, int retval, int errnoval);
+
+/* Used by asynchronous callbacks: ie ones which xc regards as
+ * returning a value, but which we want to handle asynchronously.
+ * Such functions' actual callback function return void in libxl
+ * When they are ready to indicate completion, they call this. */
+void libxl__xc_domain_saverestore_async_callback_done(libxl__egc *egc,
+ libxl__save_helper_state *shs, int return_value);
+
+
+_hidden void libxl__domain_suspend_common_switch_qemu_logdirty
+ (uint32_t domid, unsigned int enable, void *data);
+_hidden void libxl__domain_common_switch_qemu_logdirty(libxl__egc *egc,
+ int domid, unsigned enable,
+ libxl__logdirty_switch *lds);
+_hidden int libxl__save_emulator_xenstore_data(libxl__domain_save_state *dss,
+ char **buf, uint32_t *len);
+_hidden int libxl__restore_emulator_xenstore_data
+ (libxl__domain_create_state *dcs, const char *ptr, uint32_t size);
+
+
+/* calls libxl__xc_domain_restore_done when done */
+_hidden void libxl__xc_domain_restore(libxl__egc *egc,
+ libxl__domain_create_state *dcs,
+ libxl__save_helper_state *shs);
+/* If rc==0 then retval is the return value from xc_domain_save
+ * and errnoval is the errno value it provided.
+ * If rc!=0, retval and errnoval are undefined. */
+_hidden void libxl__xc_domain_restore_done(libxl__egc *egc, void *dcs_void,
+ int rc, int retval, int errnoval);
+
+_hidden void libxl__save_helper_init(libxl__save_helper_state *shs);
+_hidden void libxl__save_helper_abort(libxl__egc *egc,
+ libxl__save_helper_state *shs);
+
+static inline bool libxl__save_helper_inuse(const libxl__save_helper_state *shs)
+{
+ return libxl__ev_child_inuse(&shs->child);
+}
+
+/* Each time the dm needs to be saved, we must call suspend and then save
+ * calls dsps->callback_device_model_done when done */
+_hidden void libxl__domain_suspend_device_model(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps);
+
+_hidden const char *libxl__device_model_savefile(libxl__gc *gc, uint32_t domid);
+
+/* calls dsps->callback_common_done when done */
+_hidden void libxl__domain_suspend(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps);
+/* used by libxc to suspend the guest during migration */
+_hidden void libxl__domain_suspend_callback(void *data);
+
+/* Remus setup and teardown */
+_hidden void libxl__remus_setup(libxl__egc *egc,
+ libxl__remus_state *rs);
+_hidden void libxl__remus_teardown(libxl__egc *egc,
+ libxl__remus_state *rs,
+ int rc);
+_hidden void libxl__remus_restore_setup(libxl__egc *egc,
+ libxl__domain_create_state *dcs);
+
+_hidden char *libxl__domid_history_path(libxl__gc *gc,
+ const char *suffix);
+
+/*
+ * Convenience macros.
+ */
+
+#define FILLZERO LIBXL_FILLZERO
+
+
+/*
+ * All of these assume (or define)
+ * libxl__gc *gc;
+ * as a local variable.
+ */
+
+#define GC_INIT(ctx) libxl__gc gc[1]; LIBXL_INIT_GC(gc[0],ctx)
+#define GC_FREE libxl__free_all(gc)
+#define CTX libxl__gc_owner(gc)
+#define NOGC (&CTX->nogc_gc) /* pass only to consenting functions */
+
+/* Allocation macros all of which use the gc. */
+
+#define ARRAY_SIZE_OK(ptr, nmemb) ((nmemb) < INT_MAX / (sizeof(*(ptr)) * 2))
+
+/*
+ * Expression statement <type> *GCNEW(<type> *var);
+ * Uses libxl__gc *gc;
+ *
+ * Allocates a new object of type <type> from the gc and zeroes it
+ * with memset. Sets var to point to the new object or zero (setting
+ * errno). Returns the new value of var.
+ */
+#define GCNEW(var) \
+ (((var) = libxl__zalloc((gc),sizeof(*(var)))))
+
+/*
+ * Expression statement <type> *GCNEW_ARRAY(<type> *var, ssize_t nmemb);
+ * Uses libxl__gc *gc;
+ *
+ * Like GCNEW but allocates an array of nmemb elements, as if from
+ * calloc. Does check for integer overflow due to large nmemb. If
+ * nmemb is 0 may succeed by returning 0.
+ */
+#define GCNEW_ARRAY(var, nmemb) \
+ ((var) = libxl__calloc((gc), (nmemb), sizeof(*(var))))
+
+/*
+ * Expression statement <type> *GCREALLOC_ARRAY(<type> *var, size_t nmemb);
+ * Uses libxl__gc *gc;
+ *
+ * Reallocates the array var to be of size nmemb elements. Updates
+ * var and returns the new value of var. Does check for integer
+ * overflow due to large nmemb.
+ *
+ * Do not pass nmemb==0. old may be 0 on entry.
+ */
+#define GCREALLOC_ARRAY(var, nmemb) \
+ (assert(nmemb > 0), \
+ assert(ARRAY_SIZE_OK((var), (nmemb))), \
+ (var) = libxl__realloc((gc), (var), (nmemb)*sizeof(*(var))))
+
+
+/*
+ * Expression char *GCSPRINTF(const char *fmt, ...);
+ * Uses libxl__gc *gc;
+ *
+ * Trivial convenience wrapper for libxl__sprintf.
+ */
+#define GCSPRINTF(fmt, ...) (libxl__sprintf((gc), (fmt), __VA_ARGS__))
+
+
+/*
+ * Expression statements
+ * void LOG(<xtl_level_suffix>, const char *fmt, ...);
+ * void LOGE(<xtl_level_suffix>, const char *fmt, ...);
+ * void LOGEV(<xtl_level_suffix>, int errnoval, const char *fmt, ...);
+ *
+ * void LOGD(<xtl_level_suffix>, uint32_t domid, const char *fmt, ...);
+ * void LOGED(<xtl_level_suffix>, uint32_t domid, const char *fmt, ...);
+ * void LOGEVD(<xtl_level_suffix>, int errnoval, uint32_t domid, const char *fmt, ...);
+ * Use
+ * libxl__gc *gc;
+ *
+ * Trivial convenience wrappers for LIBXL__LOG, LIBXL__LOG_ERRNO,
+ * LIBXL__LOG_ERRNOVAL, LIBXL__LOGD, LIBXL__LOGD_ERRNO and
+ * LIBXL__LOGD_ERRNOVAL respectively (and thus for libxl__log).
+ *
+ * XTL_<xtl_level_suffix> should exist and be an xentoollog.h log level
+ * So <xtl_level_suffix> should be one of
+ * DEBUG VERBOSE DETAIL PROGRESS INFO NOTICE WARN ERROR ERROR CRITICAL
+ * Of these, most of libxl uses
+ * DEBUG INFO WARN ERROR
+ *
+ * The LOG*D family will preprend the log message with a string formatted
+ * as follows: 'Domain %PRIu32:'. This should help better automatic sorting
+ * of log messages per domain.
+ */
+#define LOG(l,f, ...) LIBXL__LOG(CTX,XTL_##l,(f),##__VA_ARGS__)
+#define LOGE(l,f, ...) LIBXL__LOG_ERRNO(CTX,XTL_##l,(f),##__VA_ARGS__)
+#define LOGEV(l,e,f, ...) LIBXL__LOG_ERRNOVAL(CTX,XTL_##l,(e),(f),##__VA_ARGS__)
+
+#define LOGD(l,d,f, ...) LIBXL__LOGD(CTX,XTL_##l,(d),(f),##__VA_ARGS__)
+#define LOGED(l,d,f, ...) LIBXL__LOGD_ERRNO(CTX,XTL_##l,(d),(f),##__VA_ARGS__)
+#define LOGEVD(l,e,d,f, ...) LIBXL__LOGD_ERRNOVAL(CTX,XTL_##l,(e),(d),(f),##__VA_ARGS__)
+
+
+/* Locking functions. See comment for "lock" member of libxl__ctx. */
+
+static inline void libxl__ctx_lock(libxl_ctx *ctx) {
+ int r = pthread_mutex_lock(&ctx->lock);
+ assert(!r);
+}
+
+static inline void libxl__ctx_unlock(libxl_ctx *ctx) {
+ int r = pthread_mutex_unlock(&ctx->lock);
+ assert(!r);
+}
+
+#define CTX_LOCK (libxl__ctx_lock(CTX))
+#define CTX_UNLOCK (libxl__ctx_unlock(CTX))
+
+/*
+ * Automatic NUMA placement
+ *
+ * These functions and data structures deal with the initial placement of a
+ * domain onto the host NUMA nodes.
+ *
+ * The key concept here is the one of "NUMA placement candidate", which is
+ * basically a set of nodes whose characteristics have been successfully
+ * checked against some specific requirements. More precisely, a candidate
+ * is the nodemap associated with one of the possible subset of the host
+ * NUMA nodes providing a certain amount of free memory, or a given number
+ * of cpus, or even both (depending in what the caller wants). For
+ * convenience of use, some of this information are stored within the
+ * candidate itself, instead of always being dynamically computed. A single
+ * node can be valid placement candidate, as well as it is possible for a
+ * candidate to contain all the nodes of the host. The fewer nodes there
+ * are in a candidate, the better performance a domain placed onto it
+ * should get (at least from a NUMA point of view). For instance, looking
+ * for a numa candidates with 2GB of free memory means we want the subsets
+ * of the host NUMA nodes with, cumulatively, at least 2GB of free memory.
+ * This condition can be satisfied by just one particular node, or it may
+ * require more nodes, depending on the characteristics of the host, on how
+ * many domains have been created already, on how big they are, etc.
+ *
+ * The intended usage is as follows:
+ * 1. first of all, call libxl__get_numa_candidates(), and specify the
+ * proper constraints to it (e.g., the amount of memory a domain need
+ * as the minimum amount of free memory for the candidates). If a
+ * candidate comparison function is provided, the candidate with fewer
+ * nodes that is found to be best according to what such fucntion says
+ * is returned. If no comparison function is passed, the very first
+ * candidate is.
+ * 2. The chosen candidate's nodemap should be utilized for computing the
+ * actual affinity of the domain which, given the current NUMA support
+ * in the hypervisor, is what determines the placement of the domain's
+ * vcpus and memory.
+ */
+
+typedef struct {
+ int nr_cpus, nr_nodes;
+ int nr_vcpus;
+ uint64_t free_memkb;
+ libxl_bitmap nodemap;
+} libxl__numa_candidate;
+
+/* Signature for the comparison function between two candidates */
+typedef int (*libxl__numa_candidate_cmpf)(const libxl__numa_candidate *c1,
+ const libxl__numa_candidate *c2);
+
+/*
+ * This looks for the best NUMA placement candidate satisfying some
+ * specific conditions. If min_nodes and/or max_nodes are not 0, their
+ * value is used to determine the minimum and maximum number of nodes the
+ * candidate can have. If they are 0, it means the candidate can contain
+ * from 1 node (min_nodes=0) to the total number of nodes of the host
+ * (max_ndoes=0). If min_free_memkb and/or min_cpus are not 0, the caller
+ * only wants candidates with at least the amount of free memory and the
+ * number of cpus they specify, respectively. If they are 0, the
+ * candidates' free memory and/or number of cpus won't be checked at all.
+ *
+ * Candidates are compared among each others by calling numa_cmpf(), which
+ * is where the heuristics for determining which candidate is the best
+ * one is actually implemented. The only bit of it that is hardcoded in
+ * this function is the fact that candidates with fewer nodes are always
+ * preferrable.
+ *
+ * If at least one suitable candidate is found, it is returned in cndt_out,
+ * cndt_found is set to one, and the function returns successfully. On the
+ * other hand, if not even one single candidate can be found, the function
+ * still returns successfully but cndt_found will be zero.
+ *
+ * Finally, suitable_cpumap is useful for telling that only the cpus in that
+ * mask should be considered when generating placement candidates (for
+ * example because of cpupools).
+ *
+ * It is up to the function to properly allocate cndt_out (by calling
+ * libxl__numa_candidate_alloc()), while it is the caller that should init
+ * (libxl__numa_candidate_init()) and free (libxl__numa_candidate_dispose())
+ * it.
+ */
+_hidden int libxl__get_numa_candidate(libxl__gc *gc,
+ uint64_t min_free_memkb, int min_cpus,
+ int min_nodes, int max_nodes,
+ const libxl_bitmap *suitable_cpumap,
+ libxl__numa_candidate_cmpf numa_cmpf,
+ libxl__numa_candidate *cndt_out,
+ int *cndt_found);
+
+/* Initialization, allocation and deallocation for placement candidates */
+static inline void libxl__numa_candidate_init(libxl__numa_candidate *cndt)
+{
+ cndt->free_memkb = 0;
+ cndt->nr_cpus = cndt->nr_nodes = cndt->nr_vcpus = 0;
+ libxl_bitmap_init(&cndt->nodemap);
+}
+
+static inline int libxl__numa_candidate_alloc(libxl__gc *gc,
+ libxl__numa_candidate *cndt)
+{
+ return libxl_node_bitmap_alloc(CTX, &cndt->nodemap, 0);
+}
+static inline void libxl__numa_candidate_dispose(libxl__numa_candidate *cndt)
+{
+ libxl_bitmap_dispose(&cndt->nodemap);
+}
+
+/* Retrieve (in nodemap) the node map associated to placement candidate cndt */
+static inline
+void libxl__numa_candidate_get_nodemap(libxl__gc *gc,
+ const libxl__numa_candidate *cndt,
+ libxl_bitmap *nodemap)
+{
+ libxl_bitmap_copy(CTX, nodemap, &cndt->nodemap);
+}
+/* Set the node map of placement candidate cndt to match nodemap */
+static inline
+void libxl__numa_candidate_put_nodemap(libxl__gc *gc,
+ libxl__numa_candidate *cndt,
+ const libxl_bitmap *nodemap)
+{
+ libxl_bitmap_copy(CTX, &cndt->nodemap, nodemap);
+}
+
+/* Check if vNUMA config is valid. Returns 0 if valid,
+ * ERROR_VNUMA_CONFIG_INVALID otherwise.
+ */
+int libxl__vnuma_config_check(libxl__gc *gc,
+ const libxl_domain_build_info *b_info,
+ const libxl__domain_build_state *state);
+int libxl__vnuma_build_vmemrange_pv_generic(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_build_info *b_info,
+ libxl__domain_build_state *state);
+int libxl__vnuma_build_vmemrange_pv(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_build_info *b_info,
+ libxl__domain_build_state *state);
+int libxl__vnuma_build_vmemrange_hvm(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_build_info *b_info,
+ libxl__domain_build_state *state,
+ struct xc_dom_image *dom);
+bool libxl__vnuma_configured(const libxl_domain_build_info *b_info);
+
+_hidden int libxl__ms_vm_genid_set(libxl__gc *gc, uint32_t domid,
+ const libxl_ms_vm_genid *id);
+
+
+/* Som handy macros for defbool type. */
+#define LIBXL__DEFBOOL_DEFAULT (0)
+#define LIBXL__DEFBOOL_FALSE (-1)
+#define LIBXL__DEFBOOL_TRUE (1)
+#define LIBXL__DEFBOOL_STR_DEFAULT "<default>"
+#define LIBXL__DEFBOOL_STR_FALSE "False"
+#define LIBXL__DEFBOOL_STR_TRUE "True"
+static inline int libxl__defbool_is_default(libxl_defbool *db)
+{
+ return !db->val;
+}
+
+/*
+ * Inserts "elm_new" into the sorted list "head".
+ *
+ * "elm_search" must be a loop search variable of the same type as
+ * "elm_new". "new_after_search_p" must be an expression which is
+ * true iff the element "elm_new" sorts after the element
+ * "elm_search".
+ *
+ * "search_body" can be empty, or some declaration(s) and statement(s)
+ * needed for "new_after_search_p".
+ */
+#define LIBXL_TAILQ_INSERT_SORTED(head, entry, elm_new, elm_search, \
+ search_body, new_after_search_p) \
+ do { \
+ for ((elm_search) = LIBXL_TAILQ_FIRST((head)); \
+ (elm_search); \
+ (elm_search) = LIBXL_TAILQ_NEXT((elm_search), entry)) { \
+ search_body; \
+ if (!(new_after_search_p)) \
+ break; \
+ } \
+ /* now elm_search is either the element before which we want \
+ * to place elm_new, or NULL meaning we want to put elm_new at \
+ * the end */ \
+ if ((elm_search)) \
+ LIBXL_TAILQ_INSERT_BEFORE((elm_search), (elm_new), entry); \
+ else \
+ LIBXL_TAILQ_INSERT_TAIL((head), (elm_new), entry); \
+ } while(0)
+
+
+/*
+ * int CTYPE(ISFOO, char c);
+ * int CTYPE(toupper, char c);
+ * int CTYPE(tolower, char c);
+ *
+ * This is necessary because passing a simple char to a ctype.h
+ * is forbidden. ctype.h macros take ints derived from _unsigned_ chars.
+ *
+ * If you have a char which might be EOF then you should already have
+ * it in an int representing an unsigned char, and you can use the
+ * <ctype.h> macros directly. This generally happens only with values
+ * from fgetc et al.
+ *
+ * For any value known to be a character (eg, anything that came from
+ * a char[]), use CTYPE.
+ */
+#define CTYPE(isfoo,c) (isfoo((unsigned char)(c)))
+
+int libxl__defbool_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_defbool *p);
+int libxl__bool_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ bool *p);
+int libxl__mac_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_mac *p);
+int libxl__bitmap_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_bitmap *p);
+int libxl__uuid_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_uuid *p);
+int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
+ const libxl__json_object *o,
+ libxl_cpuid_policy_list *p);
+int libxl__string_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_string_list *p);
+int libxl__key_value_list_parse_json(libxl__gc *gc,
+ const libxl__json_object *o,
+ libxl_key_value_list *p);
+int libxl__hwcap_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_hwcap *p);
+int libxl__ms_vm_genid_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_ms_vm_genid *p);
+int libxl__int_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ void *p);
+int libxl__uint8_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ void *p);
+int libxl__uint16_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ void *p);
+int libxl__uint32_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ void *p);
+int libxl__uint64_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ void *p);
+int libxl__string_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ char **p);
+
+int libxl__random_bytes(libxl__gc *gc, uint8_t *buf, size_t len);
+
+#include "_libxl_types_private.h"
+#include "_libxl_types_internal_private.h"
+
+/* This always return false, there's no "default value" for hw cap */
+static inline int libxl__hwcap_is_default(libxl_hwcap *hwcap)
+{
+ return 0;
+}
+
+static inline int libxl__string_list_is_empty(libxl_string_list *psl)
+{
+ return !libxl_string_list_length(psl);
+}
+
+static inline int libxl__key_value_list_is_empty(libxl_key_value_list *pkvl)
+{
+ return !libxl_key_value_list_length(pkvl);
+}
+
+int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl);
+
+/* Portability note: a proper flock(2) implementation is required */
+typedef struct {
+ libxl__carefd *carefd;
+ char *path; /* path of the lock file itself */
+} libxl__flock;
+/* The CTX_LOCK must be held around uses of this lock */
+
+libxl__flock *libxl__lock_file(libxl__gc *gc, const char *filename);
+void libxl__unlock_file(libxl__flock *lock);
+
+libxl__flock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid);
+libxl__flock *libxl__lock_domid_history(libxl__gc *gc);
+
+/*
+ * Retrieve / store domain configuration from / to libxl private
+ * data store. The registry entry in libxl private data store
+ * is "libxl-json".
+ * Caller must hold user data lock.
+ *
+ * Other names used for this lock throughout the libxl code are json_lock,
+ * libxl__domain_userdata_lock, "libxl-json", data store lock.
+ *
+ * See the comment for libxl__ao_device, and "Algorithm for handling device
+ * removal", for information about using the libxl-json lock / json_lock.
+ */
+int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config *d_config);
+int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config *d_config);
+
+/* ------ Things related to updating domain configurations ----- */
+void libxl__update_domain_configuration(libxl__gc *gc,
+ libxl_domain_config *dst,
+ const libxl_domain_config *src);
+
+/* Target memory in xenstore is different from what user has
+ * asked for. The difference is video_memkb + (possible) fudge.
+ * See libxl_set_memory_target.
+ */
+static inline
+uint64_t libxl__get_targetmem_fudge(libxl__gc *gc,
+ const libxl_domain_build_info *info)
+{
+ int64_t mem_target_fudge = (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+ info->max_memkb > info->target_memkb)
+ ? LIBXL_MAXMEM_CONSTANT : 0;
+
+ return info->video_memkb + mem_target_fudge;
+}
+
+int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
+ uint64_t *out_target_memkb,
+ uint64_t *out_max_memkb);
+void libxl__xcinfo2xlinfo(libxl_ctx *ctx,
+ const xc_domaininfo_t *xcinfo,
+ libxl_dominfo *xlinfo);
+
+/* Macros used to compare device identifier. Returns true if the two
+ * devices have same identifier. */
+#define COMPARE_DEVID(a, b) ((a)->devid == (b)->devid)
+#define COMPARE_DISK(a, b) (!strcmp((a)->vdev, (b)->vdev))
+#define COMPARE_PCI(a, b) ((a)->func == (b)->func && \
+ (a)->bus == (b)->bus && \
+ (a)->dev == (b)->dev)
+#define COMPARE_USB(a, b) ((a)->ctrl == (b)->ctrl && \
+ (a)->port == (b)->port)
+#define COMPARE_USBCTRL(a, b) ((a)->devid == (b)->devid)
+
+/* This function copies X bytes from source to destination bitmap,
+ * where X is the smaller of the two sizes.
+ *
+ * If destination's size is larger than source, the extra bytes are
+ * untouched.
+ *
+ * XXX This is introduced to fix a regression for 4.5. It shall
+ * be revisited in 4.6 time frame.
+ */
+void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr,
+ const libxl_bitmap *sptr);
+
+int libxl__count_physical_sockets(libxl__gc *gc, int *sockets);
+
+_hidden int libxl__read_sysfs_file_contents(libxl__gc *gc,
+ const char *filename,
+ void **data_r,
+ int *datalen_r);
+
+#define LIBXL_QEMU_USER_PREFIX "xen-qemuuser"
+#define LIBXL_QEMU_USER_SHARED LIBXL_QEMU_USER_PREFIX"-shared"
+#define LIBXL_QEMU_USER_RANGE_BASE LIBXL_QEMU_USER_PREFIX"-range-base"
+#define LIBXL_QEMU_USER_REAPER LIBXL_QEMU_USER_PREFIX"-reaper"
+
+static inline bool libxl__acpi_defbool_val(const libxl_domain_build_info *b_info)
+{
+ return libxl_defbool_val(b_info->acpi) &&
+ libxl_defbool_val(b_info->u.hvm.acpi);
+}
+
+/*
+ * Add a device in libxl_domain_config structure
+ *
+ * If there is already a device with the same identifier in d_config,
+ * that entry is updated.
+ *
+ * parameters:
+ * d_config: pointer to template domain config
+ * dt: type of `dev'
+ * dev: the device that is to be added / removed / updated
+ * (a copy of `dev' will be made)
+ */
+void device_add_domain_config(libxl__gc *gc, libxl_domain_config *d_config,
+ const libxl__device_type *dt,
+ const void *dev);
+
+void libxl__device_add_async(libxl__egc *egc, uint32_t domid,
+ const libxl__device_type *dt, void *type,
+ libxl__ao_device *aodev);
+int libxl__device_add(libxl__gc *gc, uint32_t domid,
+ const libxl__device_type *dt, void *type);
+
+/* Caller is responsible for freeing the memory by calling
+ * libxl__device_list_free
+ */
+void* libxl__device_list(libxl__gc *gc, const libxl__device_type *dt,
+ uint32_t domid, int *num);
+void libxl__device_list_free(const libxl__device_type *dt,
+ void *list, int num);
+
+static inline bool libxl__timer_mode_is_default(libxl_timer_mode *tm)
+{
+ return *tm == LIBXL_TIMER_MODE_DEFAULT;
+}
+
+static inline bool libxl__string_is_default(char **s)
+{
+ return *s == NULL;
+}
+
+_hidden int libxl__prepare_sockaddr_un(libxl__gc *gc, struct sockaddr_un *un,
+ const char *path, const char *what);
+static inline const char *libxl__qemu_qmp_path(libxl__gc *gc, int domid)
+{
+ return GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
+}
+
+/* Send control commands over xenstore and wait for an Ack. */
+_hidden int libxl__domain_pvcontrol(libxl__egc *egc,
+ libxl__xswait_state *pvcontrol,
+ domid_t domid, const char *cmd);
+
+/*
+ * Maximum number of seconds after desctruction then a domid remains
+ * 'recent'. Recent domids are not allowed to be re-used. This can be
+ * overidden, for debugging purposes, by the environment variable of the
+ * same name.
+ */
+#define LIBXL_DOMID_REUSE_TIMEOUT 60
+
+/* Check whether a domid is recent */
+int libxl__is_domid_recent(libxl__gc *gc, uint32_t domid, bool *recent);
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2011 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <math.h>
+
+#include <yajl/yajl_parse.h>
+#include <yajl/yajl_gen.h>
+
+#include "libxl_internal.h"
+
+/* #define DEBUG_ANSWER */
+
+typedef struct libxl__yajl_ctx {
+ libxl__gc *gc;
+ yajl_handle hand;
+ libxl__json_object *head;
+ libxl__json_object *current;
+#ifdef DEBUG_ANSWER
+ yajl_gen g;
+#endif
+} libxl__yajl_ctx;
+
+#ifdef DEBUG_ANSWER
+#if YAJL_VERSION < 20000
+# define DEBUG_GEN_ALLOC(ctx) \
+ if ((ctx)->g == NULL) { \
+ yajl_gen_config conf = { 1, " " }; \
+ (ctx)->g = yajl_gen_alloc(&conf, NULL); \
+ }
+#else /* YAJL2 */
+# define DEBUG_GEN_ALLOC(ctx) \
+ if ((ctx)->g == NULL) { \
+ (ctx)->g = yajl_gen_alloc(NULL); \
+ yajl_gen_config((ctx)->g, yajl_gen_beautify, 1); \
+ yajl_gen_config((ctx)->g, yajl_gen_indent_string, " "); \
+ }
+#endif
+# define DEBUG_GEN_FREE(ctx) \
+ if ((ctx)->g) yajl_gen_free((ctx)->g)
+# define DEBUG_GEN(ctx, type) yajl_gen_##type(ctx->g)
+# define DEBUG_GEN_VALUE(ctx, type, value) yajl_gen_##type(ctx->g, value)
+# define DEBUG_GEN_STRING(ctx, str, n) yajl_gen_string(ctx->g, str, n)
+# define DEBUG_GEN_NUMBER(ctx, str, n) yajl_gen_number(ctx->g, str, n)
+# define DEBUG_GEN_REPORT(yajl_ctx) \
+ do { \
+ const unsigned char *buf = NULL; \
+ size_t len = 0; \
+ yajl_gen_get_buf((yajl_ctx)->g, &buf, &len); \
+ LIBXL__LOG(libxl__gc_owner((yajl_ctx)->gc), XTL_DEBUG, \
+ "response: %s\n", buf); \
+ yajl_gen_free((yajl_ctx)->g); \
+ (yajl_ctx)->g = NULL; \
+ } while (0)
+#else
+# define DEBUG_GEN_ALLOC(ctx) ((void)0)
+# define DEBUG_GEN_FREE(ctx) ((void)0)
+# define DEBUG_GEN(ctx, type) ((void)0)
+# define DEBUG_GEN_VALUE(ctx, type, value) ((void)0)
+# define DEBUG_GEN_STRING(ctx, value, length) ((void)0)
+# define DEBUG_GEN_NUMBER(ctx, value, length) ((void)0)
+# define DEBUG_GEN_REPORT(ctx) ((void)0)
+#endif
+
+/*
+ * YAJL Helper
+ */
+
+yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str)
+{
+ return yajl_gen_string(hand, (const unsigned char *)str, strlen(str));
+}
+
+yajl_gen_status libxl__yajl_gen_enum(yajl_gen hand, const char *str)
+{
+ if (str)
+ return libxl__yajl_gen_asciiz(hand, str);
+ else
+ return yajl_gen_null(hand);
+}
+
+/*
+ * YAJL generators for builtin libxl types.
+ */
+yajl_gen_status libxl_defbool_gen_json(yajl_gen hand,
+ libxl_defbool *db)
+{
+ return libxl__yajl_gen_asciiz(hand, libxl_defbool_to_string(*db));
+}
+
+int libxl__defbool_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_defbool *p)
+{
+ const char *s;
+
+ if (!libxl__json_object_is_string(o))
+ return ERROR_FAIL;
+
+ s = libxl__json_object_get_string(o);
+
+ if (!strncmp(s, LIBXL__DEFBOOL_STR_DEFAULT,
+ strlen(LIBXL__DEFBOOL_STR_DEFAULT)))
+ p->val = LIBXL__DEFBOOL_DEFAULT;
+ else if (!strncmp(s, LIBXL__DEFBOOL_STR_TRUE,
+ strlen(LIBXL__DEFBOOL_STR_TRUE)))
+ p->val = LIBXL__DEFBOOL_TRUE;
+ else if (!strncmp(s, LIBXL__DEFBOOL_STR_FALSE,
+ strlen(LIBXL__DEFBOOL_STR_FALSE)))
+ p->val = LIBXL__DEFBOOL_FALSE;
+ else
+ return ERROR_FAIL;
+
+ return 0;
+}
+
+int libxl__bool_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ bool *p)
+{
+ if (!libxl__json_object_is_bool(o))
+ return ERROR_FAIL;
+
+ *p = libxl__json_object_get_bool(o);
+
+ return 0;
+}
+
+yajl_gen_status libxl_uuid_gen_json(yajl_gen hand,
+ libxl_uuid *uuid)
+{
+ char buf[LIBXL_UUID_FMTLEN+1];
+ snprintf(buf, sizeof(buf), LIBXL_UUID_FMT, LIBXL_UUID_BYTES((*uuid)));
+ return yajl_gen_string(hand, (const unsigned char *)buf, LIBXL_UUID_FMTLEN);
+}
+
+int libxl__uuid_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_uuid *p)
+{
+ if (!libxl__json_object_is_string(o))
+ return ERROR_FAIL;
+
+ return libxl_uuid_from_string(p, o->u.string);
+}
+
+yajl_gen_status libxl_bitmap_gen_json(yajl_gen hand,
+ libxl_bitmap *bitmap)
+{
+ yajl_gen_status s;
+ int i;
+
+ s = yajl_gen_array_open(hand);
+ if (s != yajl_gen_status_ok) goto out;
+
+ libxl_for_each_bit(i, *bitmap) {
+ if (libxl_bitmap_test(bitmap, i)) {
+ s = yajl_gen_integer(hand, i);
+ if (s != yajl_gen_status_ok) goto out;
+ }
+ }
+ s = yajl_gen_array_close(hand);
+out:
+ return s;
+}
+
+int libxl__bitmap_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_bitmap *p)
+{
+ int i;
+ int size;
+ const libxl__json_object *t;
+ flexarray_t *array;
+
+ if (!libxl__json_object_is_array(o))
+ return ERROR_FAIL;
+
+ array = libxl__json_object_get_array(o);
+ if (!array->count) {
+ libxl_bitmap_init(p);
+ return 0;
+ }
+
+ t = libxl__json_array_get(o, array->count - 1);
+ if (!libxl__json_object_is_integer(t))
+ return ERROR_FAIL;
+ size = libxl__json_object_get_integer(t) + 1;
+
+ libxl_bitmap_alloc(CTX, p, size);
+
+ for (i = 0; (t = libxl__json_array_get(o, i)); i++) {
+ if (!libxl__json_object_is_integer(t))
+ return ERROR_FAIL;
+
+ libxl_bitmap_set(p, libxl__json_object_get_integer(t));
+ }
+
+ return 0;
+}
+
+yajl_gen_status libxl_key_value_list_gen_json(yajl_gen hand,
+ libxl_key_value_list *pkvl)
+{
+ libxl_key_value_list kvl = *pkvl;
+ yajl_gen_status s;
+ int i;
+
+ s = yajl_gen_map_open(hand);
+ if (s != yajl_gen_status_ok) goto out;
+
+ if (!kvl) goto empty;
+
+ for (i = 0; kvl[i] != NULL; i += 2) {
+ s = libxl__yajl_gen_asciiz(hand, kvl[i]);
+ if (s != yajl_gen_status_ok) goto out;
+ if (kvl[i + 1])
+ s = libxl__yajl_gen_asciiz(hand, kvl[i+1]);
+ else
+ s = yajl_gen_null(hand);
+ if (s != yajl_gen_status_ok) goto out;
+ }
+empty:
+ s = yajl_gen_map_close(hand);
+out:
+ return s;
+}
+
+int libxl__key_value_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_key_value_list *p)
+{
+ libxl__json_map_node *node = NULL;
+ flexarray_t *maps = NULL;
+ int i, size;
+ libxl_key_value_list kvl;
+
+ if (!libxl__json_object_is_map(o))
+ return ERROR_FAIL;
+
+ maps = libxl__json_object_get_map(o);
+ size = maps->count * 2;
+ kvl = *p = libxl__calloc(NOGC, size+1, sizeof(char *));
+
+ for (i = 0; i < maps->count; i++) {
+ int idx = i * 2;
+ if (flexarray_get(maps, i, (void**)&node) != 0)
+ return ERROR_FAIL;
+
+ if (!libxl__json_object_is_string(node->obj) &&
+ !libxl__json_object_is_null(node->obj))
+ return ERROR_FAIL;
+
+ kvl[idx] = libxl__strdup(NOGC, node->map_key);
+ if (libxl__json_object_is_string(node->obj))
+ kvl[idx+1] =
+ libxl__strdup(NOGC, libxl__json_object_get_string(node->obj));
+ else
+ kvl[idx+1] = NULL;
+ }
+
+ return 0;
+}
+
+yajl_gen_status libxl_string_list_gen_json(yajl_gen hand, libxl_string_list *pl)
+{
+ libxl_string_list l = *pl;
+ yajl_gen_status s;
+ int i;
+
+ s = yajl_gen_array_open(hand);
+ if (s != yajl_gen_status_ok) goto out;
+
+ if (!l) goto empty;
+
+ for (i = 0; l[i] != NULL; i++) {
+ s = libxl__yajl_gen_asciiz(hand, l[i]);
+ if (s != yajl_gen_status_ok) goto out;
+ }
+empty:
+ s = yajl_gen_array_close(hand);
+out:
+ return s;
+}
+
+int libxl__string_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_string_list *p)
+{
+ const libxl__json_object *t;
+ libxl_string_list l;
+ flexarray_t *array = NULL;
+ int i, size;
+
+ if (!libxl__json_object_is_array(o))
+ return ERROR_FAIL;
+
+ array = libxl__json_object_get_array(o);
+ size = array->count;
+
+ if (size == 0) {
+ *p = NULL;
+ return 0;
+ }
+
+ /* need one extra slot as sentinel */
+ l = *p = libxl__calloc(NOGC, size + 1, sizeof(char *));
+
+ for (i = 0; (t = libxl__json_array_get(o, i)); i++) {
+ if (!libxl__json_object_is_string(t))
+ return ERROR_FAIL;
+
+ l[i] = libxl__strdup(NOGC, libxl__json_object_get_string(t));
+ }
+
+ return 0;
+}
+
+yajl_gen_status libxl_mac_gen_json(yajl_gen hand, libxl_mac *mac)
+{
+ char buf[LIBXL_MAC_FMTLEN+1];
+ snprintf(buf, sizeof(buf), LIBXL_MAC_FMT, LIBXL_MAC_BYTES((*mac)));
+ return yajl_gen_string(hand, (const unsigned char *)buf, LIBXL_MAC_FMTLEN);
+}
+
+int libxl__mac_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_mac *p)
+{
+ if (!libxl__json_object_is_string(o))
+ return ERROR_FAIL;
+
+ return libxl__parse_mac(libxl__json_object_get_string(o), *p);
+}
+
+yajl_gen_status libxl_hwcap_gen_json(yajl_gen hand,
+ libxl_hwcap *p)
+{
+ yajl_gen_status s;
+ int i;
+
+ s = yajl_gen_array_open(hand);
+ if (s != yajl_gen_status_ok) goto out;
+
+ for(i=0; i<4; i++) {
+ s = yajl_gen_integer(hand, (*p)[i]);
+ if (s != yajl_gen_status_ok) goto out;
+ }
+ s = yajl_gen_array_close(hand);
+out:
+ return s;
+}
+
+int libxl__hwcap_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_hwcap *p)
+{
+ int i;
+
+ if (!libxl__json_object_is_array(o))
+ return ERROR_FAIL;
+
+ for (i = 0; i<4; i++) {
+ const libxl__json_object *t;
+
+ t = libxl__json_array_get(o, i);
+ if (!t || !libxl__json_object_is_integer(t))
+ return ERROR_FAIL;
+
+ (*p)[i] = libxl__json_object_get_integer(t);
+ }
+
+ return 0;
+}
+
+yajl_gen_status libxl_ms_vm_genid_gen_json(yajl_gen hand, libxl_ms_vm_genid *p)
+{
+ yajl_gen_status s;
+ int i;
+
+ s = yajl_gen_array_open(hand);
+ if (s != yajl_gen_status_ok)
+ return s;
+
+ for (i = 0; i < LIBXL_MS_VM_GENID_LEN; i++) {
+ s = yajl_gen_integer(hand, p->bytes[i]);
+ if (s != yajl_gen_status_ok)
+ return s;
+ }
+
+ return yajl_gen_array_close(hand);
+}
+
+int libxl__ms_vm_genid_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ libxl_ms_vm_genid *p)
+{
+ unsigned int i;
+
+ if (!libxl__json_object_is_array(o))
+ return ERROR_FAIL;
+
+ for (i = 0; i < LIBXL_MS_VM_GENID_LEN; i++) {
+ const libxl__json_object *t;
+
+ t = libxl__json_array_get(o, i);
+ if (!t || !libxl__json_object_is_integer(t))
+ return ERROR_FAIL;
+
+ p->bytes[i] = libxl__json_object_get_integer(t);
+ }
+
+ return 0;
+}
+
+yajl_gen_status libxl__string_gen_json(yajl_gen hand,
+ const char *p)
+{
+ if (p)
+ return libxl__yajl_gen_asciiz(hand, p);
+ else
+ return yajl_gen_null(hand);
+}
+
+int libxl__string_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ char **p)
+{
+ if (!libxl__json_object_is_string(o) && !libxl__json_object_is_null(o))
+ return ERROR_FAIL;
+
+ if (libxl__json_object_is_null(o))
+ *p = NULL;
+ else
+ *p = libxl__strdup(NOGC, libxl__json_object_get_string(o));
+
+ return 0;
+}
+
+/*
+ * libxl__json_object helper functions
+ */
+
+libxl__json_object *libxl__json_object_alloc(libxl__gc *gc,
+ libxl__json_node_type type)
+{
+ libxl__json_object *obj;
+
+ obj = libxl__zalloc(gc, sizeof(*obj));
+
+ obj->type = type;
+
+ if (type == JSON_MAP || type == JSON_ARRAY) {
+ flexarray_t *array = flexarray_make(gc, 1, 1);
+ if (type == JSON_MAP)
+ obj->u.map = array;
+ else
+ obj->u.array = array;
+ }
+
+ return obj;
+}
+
+static int libxl__json_object_append_to(libxl__gc *gc,
+ libxl__json_object *obj,
+ libxl__yajl_ctx *ctx)
+{
+ libxl__json_object *dst = ctx->current;
+
+ if (dst) {
+ switch (dst->type) {
+ case JSON_MAP: {
+ libxl__json_map_node *last;
+
+ if (dst->u.map->count == 0) {
+ LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR,
+ "Try to add a value to an empty map (with no key)");
+ return ERROR_FAIL;
+ }
+ flexarray_get(dst->u.map, dst->u.map->count - 1, (void**)&last);
+ last->obj = obj;
+ break;
+ }
+ case JSON_ARRAY:
+ flexarray_append(dst->u.array, obj);
+ break;
+ default:
+ LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR,
+ "Try append an object is not a map/array (%i)",
+ dst->type);
+ return ERROR_FAIL;
+ }
+ }
+
+ obj->parent = dst;
+
+ if (libxl__json_object_is_map(obj) || libxl__json_object_is_array(obj))
+ ctx->current = obj;
+ if (ctx->head == NULL)
+ ctx->head = obj;
+
+ return 0;
+}
+
+void libxl__json_object_free(libxl__gc *gc, libxl__json_object *obj)
+{
+ int idx = 0;
+
+ if (obj == NULL)
+ return;
+ switch (obj->type) {
+ case JSON_STRING:
+ case JSON_NUMBER:
+ free(obj->u.string);
+ break;
+ case JSON_MAP: {
+ libxl__json_map_node *node = NULL;
+
+ for (idx = 0; idx < obj->u.map->count; idx++) {
+ if (flexarray_get(obj->u.map, idx, (void**)&node) != 0)
+ break;
+ libxl__json_object_free(gc, node->obj);
+ free(node->map_key);
+ free(node);
+ node = NULL;
+ }
+ flexarray_free(obj->u.map);
+ break;
+ }
+ case JSON_ARRAY: {
+ libxl__json_object *node = NULL;
+
+ for (idx = 0; idx < obj->u.array->count; idx++) {
+ if (flexarray_get(obj->u.array, idx, (void**)&node) != 0)
+ break;
+ libxl__json_object_free(gc, node);
+ node = NULL;
+ }
+ flexarray_free(obj->u.array);
+ break;
+ }
+ default:
+ break;
+ }
+ free(obj);
+}
+
+libxl__json_object *libxl__json_array_get(const libxl__json_object *o, int i)
+{
+ flexarray_t *array = NULL;
+ libxl__json_object *obj = NULL;
+
+ if ((array = libxl__json_object_get_array(o)) == NULL) {
+ return NULL;
+ }
+
+ if (i >= array->count)
+ return NULL;
+
+ if (flexarray_get(array, i, (void**)&obj) != 0)
+ return NULL;
+
+ return obj;
+}
+
+libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o,
+ int i)
+{
+ flexarray_t *array = NULL;
+ libxl__json_map_node *obj = NULL;
+
+ if ((array = libxl__json_object_get_map(o)) == NULL) {
+ return NULL;
+ }
+
+ if (i >= array->count)
+ return NULL;
+
+ if (flexarray_get(array, i, (void**)&obj) != 0)
+ return NULL;
+
+ return obj;
+}
+
+const libxl__json_object *libxl__json_map_get(const char *key,
+ const libxl__json_object *o,
+ libxl__json_node_type expected_type)
+{
+ flexarray_t *maps = NULL;
+ int idx = 0;
+
+ if (libxl__json_object_is_map(o)) {
+ libxl__json_map_node *node = NULL;
+
+ maps = o->u.map;
+ for (idx = 0; idx < maps->count; idx++) {
+ if (flexarray_get(maps, idx, (void**)&node) != 0)
+ return NULL;
+ if (strcmp(key, node->map_key) == 0) {
+ if (expected_type == JSON_ANY
+ || (node->obj && (node->obj->type & expected_type))) {
+ return node->obj;
+ } else {
+ return NULL;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+yajl_status libxl__json_object_to_yajl_gen(libxl__gc *gc,
+ yajl_gen hand,
+ const libxl__json_object *obj)
+{
+ int idx = 0;
+ yajl_status rc;
+
+#define CONVERT_YAJL_GEN_TO_STATUS(gen) \
+ ((gen) == yajl_gen_status_ok ? yajl_status_ok : yajl_status_error)
+
+ switch (obj->type) {
+ case JSON_NULL:
+ return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_null(hand));
+ case JSON_BOOL:
+ return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_bool(hand, obj->u.b));
+ case JSON_INTEGER:
+ return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_integer(hand, obj->u.i));
+ case JSON_DOUBLE:
+ return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_double(hand, obj->u.d));
+ case JSON_NUMBER:
+ return CONVERT_YAJL_GEN_TO_STATUS(
+ yajl_gen_number(hand, obj->u.string, strlen(obj->u.string)));
+ case JSON_STRING:
+ return CONVERT_YAJL_GEN_TO_STATUS(
+ libxl__yajl_gen_asciiz(hand, obj->u.string));
+ case JSON_MAP: {
+ libxl__json_map_node *node = NULL;
+
+ rc = CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_map_open(hand));
+ if (rc != yajl_status_ok)
+ return rc;
+ for (idx = 0; idx < obj->u.map->count; idx++) {
+ if (flexarray_get(obj->u.map, idx, (void**)&node) != 0)
+ break;
+
+ rc = CONVERT_YAJL_GEN_TO_STATUS(
+ libxl__yajl_gen_asciiz(hand, node->map_key));
+ if (rc != yajl_status_ok)
+ return rc;
+ rc = libxl__json_object_to_yajl_gen(gc, hand, node->obj);
+ if (rc != yajl_status_ok)
+ return rc;
+ }
+ return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_map_close(hand));
+ }
+ case JSON_ARRAY: {
+ libxl__json_object *node = NULL;
+
+ rc = CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_array_open(hand));
+ if (rc != yajl_status_ok)
+ return rc;
+ for (idx = 0; idx < obj->u.array->count; idx++) {
+ if (flexarray_get(obj->u.array, idx, (void**)&node) != 0)
+ break;
+ rc = libxl__json_object_to_yajl_gen(gc, hand, node);
+ if (rc != yajl_status_ok)
+ return rc;
+ }
+ return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_array_close(hand));
+ }
+ case JSON_ANY:
+ /* JSON_ANY is not a valid value for obj->type. */
+ ;
+ }
+ abort();
+#undef CONVERT_YAJL_GEN_TO_STATUS
+}
+
+
+/*
+ * JSON callbacks
+ */
+
+static int json_callback_null(void *opaque)
+{
+ libxl__yajl_ctx *ctx = opaque;
+ libxl__json_object *obj;
+
+ DEBUG_GEN(ctx, null);
+
+ obj = libxl__json_object_alloc(ctx->gc, JSON_NULL);
+
+ if (libxl__json_object_append_to(ctx->gc, obj, ctx))
+ return 0;
+
+ return 1;
+}
+
+static int json_callback_boolean(void *opaque, int boolean)
+{
+ libxl__yajl_ctx *ctx = opaque;
+ libxl__json_object *obj;
+
+ DEBUG_GEN_VALUE(ctx, bool, boolean);
+
+ obj = libxl__json_object_alloc(ctx->gc, JSON_BOOL);
+ obj->u.b = boolean;
+
+ if (libxl__json_object_append_to(ctx->gc, obj, ctx))
+ return 0;
+
+ return 1;
+}
+
+static bool is_decimal(const char *s, unsigned len)
+{
+ const char *end = s + len;
+ for (; s < end; s++) {
+ if (*s == '.')
+ return true;
+ }
+ return false;
+}
+
+static int json_callback_number(void *opaque, const char *s, libxl_yajl_length len)
+{
+ libxl__yajl_ctx *ctx = opaque;
+ libxl__json_object *obj = NULL;
+ char *t = NULL;
+
+ DEBUG_GEN_NUMBER(ctx, s, len);
+
+ if (is_decimal(s, len)) {
+ double d = strtod(s, NULL);
+
+ if ((d == HUGE_VALF || d == HUGE_VALL) && errno == ERANGE) {
+ goto error;
+ }
+
+ obj = libxl__json_object_alloc(ctx->gc, JSON_DOUBLE);
+ obj->u.d = d;
+ } else {
+ long long i = strtoll(s, NULL, 10);
+
+ if ((i == LLONG_MIN || i == LLONG_MAX) && errno == ERANGE) {
+ goto error;
+ }
+
+ obj = libxl__json_object_alloc(ctx->gc, JSON_INTEGER);
+ obj->u.i = i;
+ }
+ goto out;
+
+error:
+ /* If the conversion fail, we just store the original string. */
+ obj = libxl__json_object_alloc(ctx->gc, JSON_NUMBER);
+
+ t = libxl__zalloc(ctx->gc, len + 1);
+ strncpy(t, s, len);
+ t[len] = 0;
+
+ obj->u.string = t;
+
+out:
+ if (libxl__json_object_append_to(ctx->gc, obj, ctx))
+ return 0;
+
+ return 1;
+}
+
+static int json_callback_string(void *opaque, const unsigned char *str,
+ libxl_yajl_length len)
+{
+ libxl__yajl_ctx *ctx = opaque;
+ char *t = NULL;
+ libxl__json_object *obj = NULL;
+
+ t = libxl__zalloc(ctx->gc, len + 1);
+
+ DEBUG_GEN_STRING(ctx, str, len);
+
+ strncpy(t, (const char *) str, len);
+ t[len] = 0;
+
+ obj = libxl__json_object_alloc(ctx->gc, JSON_STRING);
+ obj->u.string = t;
+
+ if (libxl__json_object_append_to(ctx->gc, obj, ctx))
+ return 0;
+
+ return 1;
+}
+
+static int json_callback_map_key(void *opaque, const unsigned char *str,
+ libxl_yajl_length len)
+{
+ libxl__yajl_ctx *ctx = opaque;
+ char *t = NULL;
+ libxl__json_object *obj = ctx->current;
+ libxl__gc *gc = ctx->gc;
+
+ t = libxl__zalloc(gc, len + 1);
+
+ DEBUG_GEN_STRING(ctx, str, len);
+
+ strncpy(t, (const char *) str, len);
+ t[len] = 0;
+
+ if (libxl__json_object_is_map(obj)) {
+ libxl__json_map_node *node;
+
+ GCNEW(node);
+ node->map_key = t;
+ node->obj = NULL;
+
+ flexarray_append(obj->u.map, node);
+ } else {
+ LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
+ "Current json object is not a map");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int json_callback_start_map(void *opaque)
+{
+ libxl__yajl_ctx *ctx = opaque;
+ libxl__json_object *obj = NULL;
+
+ DEBUG_GEN(ctx, map_open);
+
+ obj = libxl__json_object_alloc(ctx->gc, JSON_MAP);
+
+ if (libxl__json_object_append_to(ctx->gc, obj, ctx))
+ return 0;
+
+ return 1;
+}
+
+static int json_callback_end_map(void *opaque)
+{
+ libxl__yajl_ctx *ctx = opaque;
+
+ DEBUG_GEN(ctx, map_close);
+
+ if (ctx->current) {
+ ctx->current = ctx->current->parent;
+ } else {
+ LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
+ "No current libxl__json_object, cannot use his parent.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int json_callback_start_array(void *opaque)
+{
+ libxl__yajl_ctx *ctx = opaque;
+ libxl__json_object *obj = NULL;
+
+ DEBUG_GEN(ctx, array_open);
+
+ obj = libxl__json_object_alloc(ctx->gc, JSON_ARRAY);
+
+ if (libxl__json_object_append_to(ctx->gc, obj, ctx))
+ return 0;
+
+ return 1;
+}
+
+static int json_callback_end_array(void *opaque)
+{
+ libxl__yajl_ctx *ctx = opaque;
+
+ DEBUG_GEN(ctx, array_close);
+
+ if (ctx->current) {
+ ctx->current = ctx->current->parent;
+ } else {
+ LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
+ "No current libxl__json_object, cannot use his parent.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static yajl_callbacks callbacks = {
+ json_callback_null,
+ json_callback_boolean,
+ NULL,
+ NULL,
+ json_callback_number,
+ json_callback_string,
+ json_callback_start_map,
+ json_callback_map_key,
+ json_callback_end_map,
+ json_callback_start_array,
+ json_callback_end_array
+};
+
+static void yajl_ctx_free(libxl__yajl_ctx *yajl_ctx)
+{
+ if (yajl_ctx->hand) {
+ yajl_free(yajl_ctx->hand);
+ yajl_ctx->hand = NULL;
+ }
+ DEBUG_GEN_FREE(yajl_ctx);
+}
+
+libxl__json_object *libxl__json_parse(libxl__gc *gc, const char *s)
+{
+ yajl_status status;
+ libxl__yajl_ctx yajl_ctx;
+ libxl__json_object *o = NULL;
+ unsigned char *str = NULL;
+
+ memset(&yajl_ctx, 0, sizeof (yajl_ctx));
+ yajl_ctx.gc = gc;
+
+ DEBUG_GEN_ALLOC(&yajl_ctx);
+
+ if (yajl_ctx.hand == NULL) {
+ yajl_ctx.hand = libxl__yajl_alloc(&callbacks, NULL, &yajl_ctx);
+ }
+ status = yajl_parse(yajl_ctx.hand, (const unsigned char *)s, strlen(s));
+ if (status != yajl_status_ok)
+ goto out;
+
+ status = yajl_complete_parse(yajl_ctx.hand);
+ if (status != yajl_status_ok)
+ goto out;
+
+ o = yajl_ctx.head;
+
+ DEBUG_GEN_REPORT(&yajl_ctx);
+
+ yajl_ctx.head = NULL;
+
+ yajl_ctx_free(&yajl_ctx);
+ return o;
+
+out:
+ str = yajl_get_error(yajl_ctx.hand, 1, (const unsigned char*)s, strlen(s));
+
+ LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR, "yajl error: %s", str);
+ yajl_free_error(yajl_ctx.hand, str);
+ yajl_ctx_free(&yajl_ctx);
+ return NULL;
+}
+
+static const char *yajl_gen_status_to_string(yajl_gen_status s)
+{
+ switch (s) {
+ case yajl_gen_status_ok: abort();
+ case yajl_gen_keys_must_be_strings:
+ return "keys must be strings";
+ case yajl_max_depth_exceeded:
+ return "max depth exceeded";
+ case yajl_gen_in_error_state:
+ return "in error state";
+ case yajl_gen_generation_complete:
+ return "generation complete";
+ case yajl_gen_invalid_number:
+ return "invalid number";
+#if 0 /* This is in the docs but not implemented in the version I am running. */
+ case yajl_gen_no_buf:
+ return "no buffer";
+ case yajl_gen_invalid_string:
+ return "invalid string";
+#endif
+ default:
+ return "unknown error";
+ }
+}
+
+char *libxl__object_to_json(libxl_ctx *ctx, const char *type,
+ libxl__gen_json_callback gen, void *p)
+{
+ const unsigned char *buf;
+ char *ret = NULL;
+ libxl_yajl_length len = 0;
+ yajl_gen_status s;
+ yajl_gen hand;
+
+ hand = libxl_yajl_gen_alloc(NULL);
+ if (!hand)
+ return NULL;
+
+ s = gen(hand, p);
+ if (s != yajl_gen_status_ok)
+ goto out;
+
+ s = yajl_gen_get_buf(hand, &buf, &len);
+ if (s != yajl_gen_status_ok)
+ goto out;
+ ret = strdup((const char *)buf);
+
+out:
+ yajl_gen_free(hand);
+
+ if (s != yajl_gen_status_ok) {
+ LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
+ "unable to convert %s to JSON representation. "
+ "YAJL error code %d: %s", type,
+ s, yajl_gen_status_to_string(s));
+ } else if (!ret) {
+ LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
+ "unable to allocate space for to JSON representation of %s",
+ type);
+ }
+
+ return ret;
+}
+
+char *libxl__json_object_to_json(libxl__gc *gc,
+ const libxl__json_object *args)
+{
+ const unsigned char *buf;
+ libxl_yajl_length len;
+ yajl_gen_status s;
+ yajl_gen hand;
+ char *ret = NULL;
+ int rc;
+
+ if (!args)
+ return NULL;
+
+ hand = libxl_yajl_gen_alloc(NULL);
+ if (!hand)
+ return NULL;
+
+ rc = libxl__json_object_to_yajl_gen(gc, hand, args);
+ if (rc)
+ goto out;
+
+ s = yajl_gen_get_buf(hand, &buf, &len);
+ if (s)
+ goto out;
+
+ ret = libxl__strndup(gc, (const char *)buf, len);
+
+out:
+ yajl_gen_free(hand);
+ return ret;
+}
+
+yajl_gen_status libxl__uint64_gen_json(yajl_gen hand, uint64_t val)
+{
+ char *num;
+ int len;
+ yajl_gen_status s;
+
+
+ len = asprintf(&num, "%"PRIu64, val);
+ if (len == -1) {
+ s = yajl_gen_in_error_state;
+ goto out;
+ }
+
+ s = yajl_gen_number(hand, num, len);
+
+ free(num);
+
+out:
+ return s;
+}
+
+int libxl__object_from_json(libxl_ctx *ctx, const char *type,
+ libxl__json_parse_callback parse,
+ void *p, const char *s)
+{
+ GC_INIT(ctx);
+ libxl__json_object *o;
+ int rc;
+
+ o = libxl__json_parse(gc, s);
+ if (!o) {
+ LOG(ERROR,
+ "unable to generate libxl__json_object from JSON representation of %s.",
+ type);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = parse(gc, o, p);
+ if (rc) {
+ LOG(ERROR, "unable to convert libxl__json_object to %s. (rc=%d)", type, rc);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl__int_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ void *p)
+{
+ long long i;
+
+ if (!libxl__json_object_is_integer(o))
+ return ERROR_FAIL;
+
+ i = libxl__json_object_get_integer(o);
+
+ if (i > INT_MAX || i < INT_MIN)
+ return ERROR_FAIL;
+
+ *((int *)p) = i;
+
+ return 0;
+}
+
+/* Macro to generate:
+ * libxl__uint8_parse_json
+ * libxl__uint16_parse_json
+ * libxl__uint32_parse_json
+ */
+#define PARSE_UINT(width) \
+ int libxl__uint ## width ## _parse_json(libxl__gc *gc, \
+ const libxl__json_object *o,\
+ void *p) \
+ { \
+ long long i; \
+ \
+ if (!libxl__json_object_is_integer(o)) \
+ return ERROR_FAIL; \
+ \
+ i = libxl__json_object_get_integer(o); \
+ \
+ if (i < 0 || i > UINT ## width ## _MAX) \
+ return ERROR_FAIL; \
+ \
+ *((uint ## width ## _t *)p) = i; \
+ \
+ return 0; \
+ }
+
+PARSE_UINT(8);
+PARSE_UINT(16);
+PARSE_UINT(32);
+
+int libxl__uint64_parse_json(libxl__gc *gc, const libxl__json_object *o,
+ void *p)
+{
+ if (!libxl__json_object_is_integer(o) &&
+ !libxl__json_object_is_number(o))
+ return ERROR_FAIL;
+
+ if (libxl__json_object_is_integer(o)) {
+ long long i = libxl__json_object_get_integer(o);
+
+ if (i < 0)
+ return ERROR_FAIL;
+
+ *((uint64_t *)p) = i;
+ } else {
+ const char *s;
+ unsigned long long i;
+ int saved_errno = errno;
+
+ s = libxl__json_object_get_number(o);
+
+ errno = 0;
+ i = strtoull(s, NULL, 10);
+
+ if (i == ULLONG_MAX && errno == ERANGE)
+ return ERROR_FAIL;
+
+ errno = saved_errno;
+ *((uint64_t *)p) = i;
+ }
+
+ return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * This file is part of libxl, and was originally taken from libfdt.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * Additionally, this particular file is dual licensed. That is,
+ * alternatively, at your option:
+ *
+ * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
+ *
+ * Note that this applies only to this file, and other files with a
+ * similar notice. Also, note that when the same code is distributed
+ * along with the rest of libxl, you must comply with the terms of the
+ * LGPLv2.1 for the whole of libxl including this file.
+ *
+ * The intent is to permit, in particular, upstream libfdt to
+ * incorporate improvements to this file within upstream libfdt. At
+ * the time of writing, upstream libfdt is dual licensed: 2-clause BSD
+ * (as above) and GPLv2-or-later. The 2-clause BSD licence is
+ * compatible with both GPLv2-or-later and LGPLv2.1-only; this permits
+ * copying in both directions, and the optional licence upgrade to a
+ * copyleft licence by libdft upstream or the Xen Project,
+ * respectively.
+ */
+
+#include <libfdt.h>
+
+#include "libxl_libfdt_compat.h"
+
+#ifndef HAVE_FDT_FIRST_SUBNODE
+_hidden int fdt_first_subnode(const void *fdt, int offset)
+{
+ int depth = 0;
+
+ offset = fdt_next_node(fdt, offset, &depth);
+ if (offset < 0 || depth != 1)
+ return -FDT_ERR_NOTFOUND;
+
+ return offset;
+}
+#endif
+
+#ifndef HAVE_FDT_NEXT_SUBNODE
+_hidden int fdt_next_subnode(const void *fdt, int offset)
+{
+ int depth = 1;
+
+ /*
+ * With respect to the parent, the depth of the next subnode will be
+ * the same as the last.
+ */
+ do {
+ offset = fdt_next_node(fdt, offset, &depth);
+ if (offset < 0 || depth < 1)
+ return -FDT_ERR_NOTFOUND;
+ } while (depth > 1);
+
+ return offset;
+}
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * This file is part of libxl, and was originally taken from libfdt.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * Additionally, this particular file is dual licensed. That is,
+ * alternatively, at your option:
+ *
+ * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
+ *
+ * Note that this applies only to this file, and other files with a
+ * similar notice. Also, note that when the same code is distributed
+ * along with the rest of libxl, you must comply with the terms of the
+ * LGPLv2.1 for the whole of libxl including this file.
+ *
+ * The intent is to permit, in particular, upstream libfdt to
+ * incorporate improvements to this file within upstream libfdt. At
+ * the time of writing, upstream libfdt is dual licensed: 2-clause BSD
+ * (as above) and GPLv2-or-later. The 2-clause BSD licence is
+ * compatible with both GPLv2-or-later and LGPLv2.1-only; this permits
+ * copying in both directions, and the optional licence upgrade to a
+ * copyleft licence by libdft upstream or the Xen Project,
+ * respectively.
+ */
+
+#ifndef LIBXL_LIBFDT_COMPAT_H
+#define LIBXL_LIBFDT_COMPAT_H
+
+#include "libxl_internal.h"
+#include <libfdt.h>
+
+#if !HAVE_DECL_FDT_FIRST_SUBNODE
+_hidden int fdt_first_subnode(const void *fdt, int offset);
+#endif
+
+#if !HAVE_DECL_FDT_NEXT_SUBNODE
+_hidden int fdt_next_subnode(const void *fdt, int offset);
+#endif
+
+#if !HAVE_DECL_FDT_PROPERTY_U32
+static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val)
+{
+ uint32_t tmp = cpu_to_fdt32(val);
+ return fdt_property(fdt, name, &tmp, sizeof(tmp));
+}
+#endif
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2011
+ * Author Roger Pau Monne <roger.pau@entel.upc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <sys/resource.h>
+#include "libxl_internal.h"
+
+
+/* Workarounds for Linux-specific lacks can go here: */
+
+#ifndef CLONE_NEWIPC /* Available as of Linux 2.6.19 / glibc 2.8 */
+# define CLONE_NEWIPC 0x08000000
+#endif
+
+
+int libxl__try_phy_backend(mode_t st_mode)
+{
+ if (S_ISBLK(st_mode) || S_ISREG(st_mode)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
+{
+ return libxl__devid_to_vdev(gc, devid);
+}
+
+/* Hotplug scripts helpers */
+
+static char **get_hotplug_env(libxl__gc *gc,
+ char *script, libxl__device *dev)
+{
+ const char *type = libxl__device_kind_to_string(dev->backend_kind);
+ char *be_path = libxl__device_backend_path(gc, dev);
+ char **env;
+ int nr = 0;
+
+ const int arraysize = 15;
+ GCNEW_ARRAY(env, arraysize);
+ env[nr++] = "script";
+ env[nr++] = script;
+ env[nr++] = "XENBUS_TYPE";
+ env[nr++] = (char *) type;
+ env[nr++] = "XENBUS_PATH";
+ env[nr++] = GCSPRINTF("backend/%s/%u/%d", type, dev->domid, dev->devid);
+ env[nr++] = "XENBUS_BASE_PATH";
+ env[nr++] = "backend";
+ if (dev->backend_kind == LIBXL__DEVICE_KIND_VIF) {
+ libxl_nic_type nictype;
+ char *gatewaydev;
+
+ gatewaydev = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/%s", be_path, "gatewaydev"));
+ env[nr++] = "netdev";
+ env[nr++] = gatewaydev ? : "";
+
+ if (libxl__nic_type(gc, dev, &nictype)) {
+ LOGD(ERROR, dev->domid, "unable to get nictype");
+ return NULL;
+ }
+ switch (nictype) {
+ case LIBXL_NIC_TYPE_VIF_IOEMU:
+ env[nr++] = "INTERFACE";
+ env[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
+ dev->devid,
+ LIBXL_NIC_TYPE_VIF_IOEMU);
+ /*
+ * We need to fall through because for PV_IOEMU nic types we need
+ * to execute both the vif and the tap hotplug script, and we
+ * don't know which one we are executing in this call, so provide
+ * both env variables.
+ */
+ case LIBXL_NIC_TYPE_VIF:
+ env[nr++] = "vif";
+ env[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
+ dev->devid,
+ LIBXL_NIC_TYPE_VIF);
+ break;
+ default:
+ return NULL;
+ }
+ }
+
+ env[nr++] = NULL;
+ assert(nr <= arraysize);
+
+ return env;
+}
+
+/* Hotplug scripts caller functions */
+
+static int libxl__hotplug_nic(libxl__gc *gc, libxl__device *dev,
+ char ***args, char ***env,
+ libxl__device_action action, int num_exec)
+{
+ char *be_path = libxl__device_backend_path(gc, dev);
+ char *script;
+ int nr = 0, rc = 0;
+ libxl_nic_type nictype;
+
+ script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path,
+ "script"));
+ if (!script) {
+ LOGED(ERROR, dev->domid,
+ "unable to read script from %s", be_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__nic_type(gc, dev, &nictype);
+ if (rc) {
+ LOGD(ERROR, dev->domid, "error when fetching nic type");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ if (nictype == LIBXL_NIC_TYPE_VIF && num_exec != 0) {
+ rc = 0;
+ goto out;
+ }
+
+ *env = get_hotplug_env(gc, script, dev);
+ if (!*env) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ const int arraysize = 4;
+ GCNEW_ARRAY(*args, arraysize);
+ (*args)[nr++] = script;
+
+ if (nictype == LIBXL_NIC_TYPE_VIF_IOEMU && num_exec) {
+ (*args)[nr++] = (char *) libxl__device_action_to_string(action);
+ (*args)[nr++] = "type_if=tap";
+ (*args)[nr++] = NULL;
+ } else {
+ (*args)[nr++] = action == LIBXL__DEVICE_ACTION_ADD ? "online" :
+ "offline";
+ (*args)[nr++] = "type_if=vif";
+ (*args)[nr++] = NULL;
+ }
+ assert(nr == arraysize);
+ rc = 1;
+
+out:
+ return rc;
+}
+
+static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev,
+ char ***args, char ***env,
+ libxl__device_action action)
+{
+ char *be_path = libxl__device_backend_path(gc, dev);
+ char *script;
+ int nr = 0, rc = 0;
+
+ script = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/%s", be_path, "script"));
+ if (!script) {
+ LOGEVD(ERROR, errno, dev->domid,
+ "unable to read script from %s", be_path);
+ rc = ERROR_FAIL;
+ goto error;
+ }
+
+ *env = get_hotplug_env(gc, script, dev);
+ if (!*env) {
+ LOGD(ERROR, dev->domid, "Failed to get hotplug environment");
+ rc = ERROR_FAIL;
+ goto error;
+ }
+
+ const int arraysize = 3;
+ GCNEW_ARRAY(*args, arraysize);
+ (*args)[nr++] = script;
+ (*args)[nr++] = (char *) libxl__device_action_to_string(action);
+ (*args)[nr++] = NULL;
+ assert(nr == arraysize);
+
+ LOGD(DEBUG, dev->domid, "Args and environment ready");
+ rc = 1;
+
+error:
+ return rc;
+}
+
+int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
+ char ***args, char ***env,
+ libxl__device_action action,
+ int num_exec)
+{
+ int rc;
+
+ switch (dev->backend_kind) {
+ case LIBXL__DEVICE_KIND_VBD:
+ if (num_exec != 0) {
+ LOGD(DEBUG, dev->domid,
+ "num_exec %d, not running hotplug scripts", num_exec);
+ rc = 0;
+ goto out;
+ }
+ rc = libxl__hotplug_disk(gc, dev, args, env, action);
+ break;
+ case LIBXL__DEVICE_KIND_VIF:
+ /*
+ * If domain has a stubdom we don't have to execute hotplug scripts
+ * for emulated interfaces
+ */
+ if ((num_exec > 1) ||
+ (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
+ LOGD(DEBUG, dev->domid,
+ "num_exec %d, not running hotplug scripts", num_exec);
+ rc = 0;
+ goto out;
+ }
+ rc = libxl__hotplug_nic(gc, dev, args, env, action, num_exec);
+ break;
+ default:
+ /* No need to execute any hotplug scripts */
+ LOGD(DEBUG, dev->domid,
+ "backend_kind %d, no need to execute scripts", dev->backend_kind);
+ rc = 0;
+ break;
+ }
+
+out:
+ return rc;
+}
+
+libxl_device_model_version libxl__default_device_model(libxl__gc *gc)
+{
+ return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
+}
+
+int libxl__pci_numdevs(libxl__gc *gc)
+{
+ DIR *dir;
+ struct dirent *entry;
+ int num_devs = 0;
+
+ dir = opendir("/sys/bus/pci/devices");
+ if (!dir) {
+ LOGE(ERROR, "Cannot open /sys/bus/pci/devices");
+ return ERROR_FAIL;
+ }
+
+ while ((entry = readdir(dir))) {
+ if (entry->d_name[0] == '.')
+ continue;
+ num_devs++;
+ }
+ closedir(dir);
+
+ return num_devs;
+}
+
+int libxl__pci_topology_init(libxl__gc *gc,
+ physdev_pci_device_t *devs,
+ int num_devs)
+{
+
+ DIR *dir;
+ struct dirent *entry;
+ int i, err = 0;
+
+ dir = opendir("/sys/bus/pci/devices");
+ if (!dir) {
+ LOGE(ERROR, "Cannot open /sys/bus/pci/devices");
+ return ERROR_FAIL;
+ }
+
+ i = 0;
+ while ((entry = readdir(dir))) {
+ unsigned int dom, bus, dev, func;
+
+ if (entry->d_name[0] == '.')
+ continue;
+
+ if (i == num_devs) {
+ LOG(ERROR, "Too many devices");
+ err = ERROR_FAIL;
+ errno = -ENOSPC;
+ goto out;
+ }
+
+ if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4) {
+ LOGE(ERROR, "Error processing /sys/bus/pci/devices");
+ err = ERROR_FAIL;
+ goto out;
+ }
+
+ devs[i].seg = dom;
+ devs[i].bus = bus;
+ devs[i].devfn = ((dev & 0x1f) << 3) | (func & 7);
+
+ i++;
+ }
+
+ out:
+ closedir(dir);
+
+ return err;
+}
+
+static struct {
+ int resource;
+ rlim_t limit;
+} rlimits[] = {
+#define RLIMIT_ENTRY(r, l) \
+ { .resource = r, .limit = l }
+ /* Big enough for log files, not big enough for a DoS */
+ RLIMIT_ENTRY(RLIMIT_FSIZE, 256*1024),
+
+ /* Shouldn't need any of these */
+ RLIMIT_ENTRY(RLIMIT_CORE, 0),
+ RLIMIT_ENTRY(RLIMIT_MSGQUEUE, 0),
+ RLIMIT_ENTRY(RLIMIT_LOCKS, 0),
+ RLIMIT_ENTRY(RLIMIT_MEMLOCK, 0),
+
+ /* End-of-list marker */
+ RLIMIT_ENTRY(RLIMIT_NLIMITS, 0),
+#undef RLIMIT_ENTRY
+};
+
+int libxl__local_dm_preexec_restrict(libxl__gc *gc)
+{
+ int r;
+ unsigned i;
+
+ /* Unshare mount and IPC namespaces. These are unused by QEMU. */
+ r = unshare(CLONE_NEWNS | CLONE_NEWIPC);
+ if (r) {
+ LOGE(ERROR, "libxl: unshare Mount and IPC namespace failed");
+ return ERROR_FAIL;
+ }
+
+ /* Set various "easy" rlimits */
+ for (i = 0; rlimits[i].resource != RLIMIT_NLIMITS; i++) {
+ struct rlimit rlim;
+
+ rlim.rlim_cur = rlim.rlim_max = rlimits[i].limit;
+
+ r = setrlimit(rlimits[i].resource, &rlim);
+ if (r < 0) {
+ LOGE(ERROR, "Setting rlimit %d to %llu failed\n",
+ rlimits[i].resource,
+ (unsigned long long)rlimits[i].limit);
+ return ERROR_FAIL;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+
+/*
+ * Set the maximum memory size of the domain in the hypervisor. There is no
+ * change of the current memory size involved. The specified memory size can
+ * even be above the configured maxmem size of the domain, but the related
+ * Xenstore entry memory/static-max isn't modified!
+ */
+int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t max_memkb)
+{
+ GC_INIT(ctx);
+ char *mem, *endptr;
+ uint64_t memorykb, size;
+ char *dompath = libxl__xs_get_dompath(gc, domid);
+ int rc = 1;
+ libxl__flock *lock = NULL;
+ libxl_domain_config d_config;
+
+ libxl_domain_config_init(&d_config);
+
+ CTX_LOCK;
+
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ mem = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target", dompath));
+ if (!mem) {
+ LOGED(ERROR, domid, "Cannot get memory info from %s/memory/target",
+ dompath);
+ goto out;
+ }
+ memorykb = strtoull(mem, &endptr, 10);
+ if (*endptr != '\0') {
+ LOGED(ERROR, domid, "Invalid memory %s from %s/memory/target\n",
+ mem, dompath);
+ goto out;
+ }
+
+ if (max_memkb < memorykb) {
+ LOGED(ERROR, domid,
+ "memory_static_max must be greater than or or equal to memory_dynamic_max");
+ goto out;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc < 0) {
+ LOGE(ERROR, "unable to retrieve domain configuration");
+ goto out;
+ }
+
+ rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size);
+ if (rc < 0) {
+ LOGE(ERROR, "Couldn't get arch extra constant memory size");
+ goto out;
+ }
+
+ rc = xc_domain_setmaxmem(ctx->xch, domid, max_memkb + size);
+ if (rc != 0) {
+ LOGED(ERROR, domid,
+ "xc_domain_setmaxmem domid=%d memkb=%"PRIu64" failed ""rc=%d\n",
+ domid, max_memkb + size, rc);
+ goto out;
+ }
+
+ rc = 0;
+out:
+ libxl_domain_config_dispose(&d_config);
+ if (lock) libxl__unlock_file(lock);
+ CTX_UNLOCK;
+ GC_FREE;
+ return rc;
+}
+
+static int libxl__fill_dom0_memory_info(libxl__gc *gc, uint64_t *target_memkb,
+ uint64_t *max_memkb)
+{
+ int rc;
+ libxl_dominfo info;
+ libxl_physinfo physinfo;
+ char *target = NULL, *staticmax = NULL, *endptr = NULL;
+ char *target_path = "/local/domain/0/memory/target";
+ char *max_path = "/local/domain/0/memory/static-max";
+ xs_transaction_t t;
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+
+ libxl_dominfo_init(&info);
+
+retry_transaction:
+ t = xs_transaction_start(ctx->xsh);
+
+ target = libxl__xs_read(gc, t, target_path);
+ staticmax = libxl__xs_read(gc, t, max_path);
+ if (target && staticmax) {
+ rc = 0;
+ goto out;
+ }
+
+ if (target) {
+ *target_memkb = strtoull(target, &endptr, 10);
+ if (*endptr != '\0') {
+ LOGED(ERROR, 0, "Invalid memory target %s from %s\n", target,
+ target_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ if (staticmax) {
+ *max_memkb = strtoull(staticmax, &endptr, 10);
+ if (*endptr != '\0') {
+ LOGED(ERROR, 0, "Invalid memory static-max %s from %s\n",
+ staticmax,
+ max_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ libxl_dominfo_dispose(&info);
+ libxl_dominfo_init(&info);
+ rc = libxl_domain_info(ctx, &info, 0);
+ if (rc < 0)
+ goto out;
+
+ rc = libxl_get_physinfo(ctx, &physinfo);
+ if (rc < 0)
+ goto out;
+
+ if (target == NULL) {
+ libxl__xs_printf(gc, t, target_path, "%"PRIu64, info.current_memkb);
+ *target_memkb = info.current_memkb;
+ }
+ if (staticmax == NULL) {
+ libxl__xs_printf(gc, t, max_path, "%"PRIu64, info.max_memkb);
+ *max_memkb = info.max_memkb;
+ }
+
+ rc = 0;
+
+out:
+ if (!xs_transaction_end(ctx->xsh, t, 0)) {
+ if (errno == EAGAIN)
+ goto retry_transaction;
+ else
+ rc = ERROR_FAIL;
+ }
+
+ libxl_dominfo_dispose(&info);
+ return rc;
+}
+
+int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid,
+ int64_t target_memkb, int relative, int enforce)
+{
+ GC_INIT(ctx);
+ int rc, r, lrc, abort_transaction = 0;
+ uint64_t memorykb, size;
+ uint64_t videoram = 0;
+ uint64_t current_target_memkb = 0, new_target_memkb = 0;
+ uint64_t current_max_memkb = 0;
+ char *memmax, *endptr, *videoram_s = NULL, *target = NULL;
+ char *dompath = libxl__xs_get_dompath(gc, domid);
+ xc_domaininfo_t info;
+ libxl_dominfo ptr;
+ char *uuid;
+ xs_transaction_t t;
+ libxl__flock *lock;
+ libxl_domain_config d_config;
+
+ libxl_domain_config_init(&d_config);
+
+ CTX_LOCK;
+
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out_no_transaction;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc < 0) {
+ LOGE(ERROR, "unable to retrieve domain configuration");
+ goto out_no_transaction;
+ }
+
+ rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size);
+ if (rc < 0) {
+ LOGE(ERROR, "Couldn't get arch extra constant memory size");
+ goto out_no_transaction;
+ }
+
+retry_transaction:
+ t = xs_transaction_start(ctx->xsh);
+
+ target = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/target", dompath));
+ if (!target && !domid) {
+ if (!xs_transaction_end(ctx->xsh, t, 1)) {
+ rc = ERROR_FAIL;
+ goto out_no_transaction;
+ }
+ lrc = libxl__fill_dom0_memory_info(gc, ¤t_target_memkb,
+ ¤t_max_memkb);
+ if (lrc < 0) { rc = ERROR_FAIL; goto out_no_transaction; }
+ goto retry_transaction;
+ } else if (!target) {
+ LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
+ dompath);
+ abort_transaction = 1;
+ rc = ERROR_FAIL;
+ goto out;
+ } else {
+ current_target_memkb = strtoull(target, &endptr, 10);
+ if (*endptr != '\0') {
+ LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
+ target, dompath);
+ abort_transaction = 1;
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+ memmax = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/static-max", dompath));
+ if (!memmax) {
+ LOGED(ERROR, domid, "Cannot get memory info from %s/memory/static-max",
+ dompath);
+ abort_transaction = 1;
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ memorykb = strtoull(memmax, &endptr, 10);
+ if (*endptr != '\0') {
+ LOGED(ERROR, domid, "Invalid max memory %s from %s/memory/static-max\n",
+ memmax, dompath);
+ abort_transaction = 1;
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ videoram_s = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/videoram",
+ dompath));
+ videoram = videoram_s ? atoi(videoram_s) : 0;
+
+ if (relative) {
+ if (target_memkb < 0 && llabs(target_memkb) > current_target_memkb)
+ new_target_memkb = 0;
+ else
+ new_target_memkb = current_target_memkb + target_memkb;
+ } else
+ new_target_memkb = target_memkb - videoram;
+ if (new_target_memkb > memorykb) {
+ LOGD(ERROR, domid,
+ "memory_dynamic_max must be less than or equal to"
+ " memory_static_max\n");
+ abort_transaction = 1;
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ if (!domid && new_target_memkb < LIBXL_MIN_DOM0_MEM) {
+ LOGD(ERROR, domid,
+ "New target %"PRIu64" for dom0 is below the minimum threshold",
+ new_target_memkb);
+ abort_transaction = 1;
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ if (enforce) {
+ memorykb = new_target_memkb + videoram;
+ r = xc_domain_setmaxmem(ctx->xch, domid, memorykb + size);
+ if (r != 0) {
+ LOGED(ERROR, domid,
+ "xc_domain_setmaxmem memkb=%"PRIu64" failed ""rc=%d\n",
+ memorykb + size,
+ r);
+ abort_transaction = 1;
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ if (d_config.c_info.type != LIBXL_DOMAIN_TYPE_PV) {
+ r = xc_domain_set_pod_target(ctx->xch, domid,
+ (new_target_memkb + size) / 4, NULL, NULL, NULL);
+ if (r != 0) {
+ LOGED(ERROR, domid,
+ "xc_domain_set_pod_target memkb=%"PRIu64" failed rc=%d\n",
+ (new_target_memkb + size) / 4,
+ r);
+ abort_transaction = 1;
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ libxl__xs_printf(gc, t, GCSPRINTF("%s/memory/target", dompath),
+ "%"PRIu64, new_target_memkb);
+
+ r = xc_domain_getinfolist(ctx->xch, domid, 1, &info);
+ if (r != 1 || info.domain != domid) {
+ abort_transaction = 1;
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ libxl_dominfo_init(&ptr);
+ libxl__xcinfo2xlinfo(ctx, &info, &ptr);
+ uuid = libxl__uuid2string(gc, ptr.uuid);
+ libxl__xs_printf(gc, t, GCSPRINTF("/vm/%s/memory", uuid),
+ "%"PRIu64, new_target_memkb / 1024);
+ libxl_dominfo_dispose(&ptr);
+
+ rc = 0;
+out:
+ if (!xs_transaction_end(ctx->xsh, t, abort_transaction)
+ && !abort_transaction)
+ if (errno == EAGAIN)
+ goto retry_transaction;
+
+out_no_transaction:
+ libxl_domain_config_dispose(&d_config);
+ if (lock) libxl__unlock_file(lock);
+ CTX_UNLOCK;
+ GC_FREE;
+ return rc;
+}
+
+/* out_target_memkb and out_max_memkb can be NULL */
+int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
+ uint64_t *out_target_memkb,
+ uint64_t *out_max_memkb)
+{
+ int rc;
+ char *target = NULL, *static_max = NULL, *endptr = NULL;
+ char *dompath = libxl__xs_get_dompath(gc, domid);
+ uint64_t target_memkb, max_memkb;
+
+ target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target",
+ dompath));
+ static_max = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/memory/static-max", dompath));
+
+ rc = ERROR_FAIL;
+ if ((!target || !static_max) && !domid) {
+ rc = libxl__fill_dom0_memory_info(gc, &target_memkb,
+ &max_memkb);
+ if (rc < 0)
+ goto out;
+ } else if (!target) {
+ LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
+ dompath);
+ goto out;
+ } else if (!static_max) {
+ LOGED(ERROR, domid,
+ "Cannot get target memory info from %s/memory/static-max",
+ dompath);
+ goto out;
+ } else {
+ target_memkb = strtoull(target, &endptr, 10);
+ if (*endptr != '\0') {
+ LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
+ target, dompath);
+ goto out;
+ }
+ max_memkb = strtoull(static_max, &endptr, 10);
+ if (*endptr != '\0') {
+ LOGED(ERROR, domid,
+ "Invalid memory target %s from %s/memory/static-max\n",
+ static_max,
+ dompath);
+ goto out;
+ }
+
+ }
+
+ if (out_target_memkb)
+ *out_target_memkb = target_memkb;
+
+ if (out_max_memkb)
+ *out_max_memkb = max_memkb;
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static int libxl__memkb_64to32(libxl_ctx *ctx, int rc,
+ uint64_t val64, uint32_t *ptr32)
+{
+ GC_INIT(ctx);
+
+ if (rc)
+ goto out;
+
+ *ptr32 = val64;
+ if (*ptr32 == val64)
+ goto out;
+
+ LOGE(ERROR, "memory size %"PRIu64" too large for 32 bit value\n", val64);
+ rc = ERROR_FAIL;
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid,
+ uint64_t *out_target)
+{
+ GC_INIT(ctx);
+ int rc;
+
+ rc = libxl__get_memory_target(gc, domid, out_target, NULL);
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_get_memory_target_0x040700(
+ libxl_ctx *ctx, uint32_t domid, uint32_t *out_target)
+{
+ uint64_t my_out_target;
+ int rc;
+
+ rc = libxl_get_memory_target(ctx, domid, &my_out_target);
+ return libxl__memkb_64to32(ctx, rc, my_out_target, out_target);
+}
+
+int libxl__domain_need_memory_calculate(libxl__gc *gc,
+ libxl_domain_build_info *b_info,
+ uint64_t *need_memkb)
+{
+ int rc;
+
+ *need_memkb = b_info->target_memkb;
+ *need_memkb += b_info->shadow_memkb + b_info->iommu_memkb;
+
+ switch (b_info->type) {
+ case LIBXL_DOMAIN_TYPE_PVH:
+ case LIBXL_DOMAIN_TYPE_HVM:
+ *need_memkb += LIBXL_HVM_EXTRA_MEMORY;
+ if (libxl_defbool_val(b_info->device_model_stubdomain)) {
+ *need_memkb += b_info->stubdomain_memkb;
+ *need_memkb += b_info->video_memkb;
+ }
+ break;
+ case LIBXL_DOMAIN_TYPE_PV:
+ *need_memkb += LIBXL_PV_EXTRA_MEMORY;
+ break;
+ default:
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ if (*need_memkb % (2 * 1024))
+ *need_memkb += (2 * 1024) - (*need_memkb % (2 * 1024));
+ rc = 0;
+out:
+ return rc;
+}
+
+int libxl_domain_need_memory(libxl_ctx *ctx,
+ libxl_domain_config *d_config,
+ uint32_t domid_for_logging,
+ uint64_t *need_memkb)
+{
+ GC_INIT(ctx);
+ int rc;
+
+ ctx->libxl_domain_need_memory_called = 1;
+
+ rc = libxl__domain_config_setdefault(gc,
+ d_config,
+ domid_for_logging);
+ if (rc) goto out;
+
+ rc = libxl__domain_need_memory_calculate(gc,
+ &d_config->b_info,
+ need_memkb);
+ if (rc) goto out;
+
+ rc = 0;
+ out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_domain_need_memory_0x041200(libxl_ctx *ctx,
+ const libxl_domain_build_info *b_info_in,
+ uint64_t *need_memkb)
+{
+ GC_INIT(ctx);
+ int rc;
+
+ ctx->libxl_domain_need_memory_0x041200_called = 1;
+
+ libxl_domain_build_info b_info[1];
+ libxl_domain_build_info_init(b_info);
+ libxl_domain_build_info_copy(ctx, b_info, b_info_in);
+
+ rc = libxl__domain_build_info_setdefault(gc, b_info);
+ if (rc) goto out;
+
+ rc = libxl__domain_need_memory_calculate(gc,
+ b_info,
+ need_memkb);
+ if (rc) goto out;
+
+ rc = 0;
+ out:
+ libxl_domain_build_info_dispose(b_info);
+ GC_FREE;
+ return rc;
+}
+
+int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
+ const libxl_domain_build_info *b_info_in,
+ uint32_t *need_memkb)
+{
+ uint64_t my_need_memkb;
+ int rc;
+
+ rc = libxl_domain_need_memory_0x041200(ctx, b_info_in, &my_need_memkb);
+ return libxl__memkb_64to32(ctx, rc, my_need_memkb, need_memkb);
+}
+
+int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb)
+{
+ int rc = 0;
+ libxl_physinfo info;
+ GC_INIT(ctx);
+
+ rc = libxl_get_physinfo(ctx, &info);
+ if (rc < 0)
+ goto out;
+
+ *memkb = (info.free_pages + info.scrub_pages) * 4;
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
+{
+ uint64_t my_memkb;
+ int rc;
+
+ rc = libxl_get_free_memory(ctx, &my_memkb);
+ return libxl__memkb_64to32(ctx, rc, my_memkb, memkb);
+}
+
+int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid,
+ uint64_t memory_kb, int wait_secs)
+{
+ int rc = 0;
+ libxl_physinfo info;
+ GC_INIT(ctx);
+
+ while (wait_secs > 0) {
+ rc = libxl_get_physinfo(ctx, &info);
+ if (rc < 0)
+ goto out;
+ if (info.free_pages * 4 >= memory_kb) {
+ rc = 0;
+ goto out;
+ }
+ wait_secs--;
+ sleep(1);
+ }
+ rc = ERROR_NOMEM;
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs)
+{
+ int rc = 0;
+ uint64_t target_memkb = 0;
+ uint64_t current_memkb, prev_memkb;
+ libxl_dominfo info;
+
+ rc = libxl_get_memory_target(ctx, domid, &target_memkb);
+ if (rc < 0)
+ return rc;
+
+ libxl_dominfo_init(&info);
+ prev_memkb = UINT64_MAX;
+
+ do {
+ sleep(2);
+
+ libxl_dominfo_dispose(&info);
+ libxl_dominfo_init(&info);
+ rc = libxl_domain_info(ctx, &info, domid);
+ if (rc < 0)
+ goto out;
+
+ current_memkb = info.current_memkb + info.outstanding_memkb;
+
+ if (current_memkb > prev_memkb)
+ {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ else if (current_memkb == prev_memkb)
+ wait_secs -= 2;
+ /* if current_memkb < prev_memkb loop for free as progress has
+ * been made */
+
+ prev_memkb = current_memkb;
+ } while (wait_secs > 0 && current_memkb > target_memkb);
+
+ if (current_memkb <= target_memkb)
+ rc = 0;
+ else
+ rc = ERROR_FAIL;
+
+out:
+ libxl_dominfo_dispose(&info);
+ return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2011
+ * Author Roger Pau Monne <roger.pau@entel.upc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+int libxl__try_phy_backend(mode_t st_mode)
+{
+ if (S_ISREG(st_mode) || S_ISBLK(st_mode))
+ return 1;
+
+ return 0;
+}
+
+char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
+{
+ /* TODO */
+ return NULL;
+}
+
+/* Hotplug scripts caller functions */
+static int libxl__hotplug(libxl__gc *gc, libxl__device *dev, char ***args,
+ libxl__device_action action)
+{
+ char *be_path = libxl__device_backend_path(gc, dev);
+ char *script;
+ int nr = 0, rc = 0, arraysize = 4;
+
+ script = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/%s", be_path, "script"));
+ if (!script) {
+ LOGEVD(ERROR, errno, dev->domid,
+ "unable to read script from %s", be_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ GCNEW_ARRAY(*args, arraysize);
+ (*args)[nr++] = script;
+ (*args)[nr++] = be_path;
+ (*args)[nr++] = GCSPRINTF("%d", action == LIBXL__DEVICE_ACTION_ADD ?
+ XenbusStateInitWait : XenbusStateClosed);
+ (*args)[nr++] = NULL;
+ assert(nr == arraysize);
+
+out:
+ return rc;
+}
+
+int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
+ char ***args, char ***env,
+ libxl__device_action action,
+ int num_exec)
+{
+ int rc;
+
+ switch (dev->backend_kind) {
+ case LIBXL__DEVICE_KIND_VBD:
+ if (num_exec != 0) {
+ LOGD(DEBUG, dev->domid,
+ "num_exec %d, not running hotplug scripts", num_exec);
+ rc = 0;
+ goto out;
+ }
+ rc = libxl__hotplug(gc, dev, args, action);
+ if (!rc) rc = 1;
+ break;
+ case LIBXL__DEVICE_KIND_VIF:
+ /*
+ * If domain has a stubdom we don't have to execute hotplug scripts
+ * for emulated interfaces
+ *
+ * NetBSD let QEMU call a script to plug emulated nic, so
+ * only test if num_exec == 0 in that case.
+ */
+ if ((num_exec != 0) ||
+ (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
+ LOGD(DEBUG, dev->domid,
+ "num_exec %d, not running hotplug scripts", num_exec);
+ rc = 0;
+ goto out;
+ }
+ rc = libxl__hotplug(gc, dev, args, action);
+ if (!rc) rc = 1;
+ break;
+ default:
+ /* If no need to execute any hotplug scripts,
+ * call the callback manually
+ */
+ rc = 0;
+ break;
+ }
+
+out:
+ return rc;
+}
+
+libxl_device_model_version libxl__default_device_model(libxl__gc *gc)
+{
+ return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
+}
+
+int libxl__pci_numdevs(libxl__gc *gc)
+{
+ return ERROR_NI;
+}
+
+int libxl__pci_topology_init(libxl__gc *gc,
+ physdev_pci_device_t *devs,
+ int num_devs)
+{
+ return ERROR_NI;
+}
+
+int libxl__local_dm_preexec_restrict(libxl__gc *gc)
+{
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2014
+ * Author Shriram Rajagopalan <rshriram@cs.ubc.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#include <netlink/cache.h>
+#include <netlink/socket.h>
+#include <netlink/attr.h>
+#include <netlink/route/link.h>
+#include <netlink/route/route.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/qdisc/plug.h>
+
+typedef struct libxl__remus_device_nic {
+ int devid;
+
+ const char *vif;
+ const char *ifb;
+ struct rtnl_qdisc *qdisc;
+} libxl__remus_device_nic;
+
+int libxl__netbuffer_enabled(libxl__gc *gc)
+{
+ return 1;
+}
+
+int init_subkind_nic(libxl__checkpoint_devices_state *cds)
+{
+ int rc, ret;
+ libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+ libxl__remus_state *rs = cds->concrete_data;
+
+ STATE_AO_GC(cds->ao);
+
+ rs->nlsock = nl_socket_alloc();
+ if (!rs->nlsock) {
+ LOGD(ERROR, dss->domid, "cannot allocate nl socket");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ ret = nl_connect(rs->nlsock, NETLINK_ROUTE);
+ if (ret) {
+ LOGD(ERROR, dss->domid, "failed to open netlink socket: %s",
+ nl_geterror(ret));
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /* get list of all qdiscs installed on network devs. */
+ ret = rtnl_qdisc_alloc_cache(rs->nlsock, &rs->qdisc_cache);
+ if (ret) {
+ LOGD(ERROR, dss->domid, "failed to allocate qdisc cache: %s",
+ nl_geterror(ret));
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (dss->remus->netbufscript) {
+ rs->netbufscript = libxl__strdup(gc, dss->remus->netbufscript);
+ } else {
+ rs->netbufscript = GCSPRINTF("%s/remus-netbuf-setup",
+ libxl__xen_script_dir_path());
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds)
+{
+ libxl__remus_state *rs = cds->concrete_data;
+
+ STATE_AO_GC(cds->ao);
+
+ /* free qdisc cache */
+ if (rs->qdisc_cache) {
+ nl_cache_clear(rs->qdisc_cache);
+ nl_cache_free(rs->qdisc_cache);
+ rs->qdisc_cache = NULL;
+ }
+
+ /* close & free nlsock */
+ if (rs->nlsock) {
+ nl_close(rs->nlsock);
+ nl_socket_free(rs->nlsock);
+ rs->nlsock = NULL;
+ }
+}
+
+/*----- setup() and teardown() -----*/
+
+/* helper functions */
+
+/*
+ * If the device has a vifname, then use that instead of
+ * the vifX.Y format.
+ * it must ONLY be used for remus because if driver domains
+ * were in use it would constitute a security vulnerability.
+ */
+static const char *get_vifname(libxl__checkpoint_device *dev,
+ const libxl_device_nic *nic)
+{
+ const char *vifname = NULL;
+ const char *path;
+ int rc;
+
+ STATE_AO_GC(dev->cds->ao);
+
+ /* Convenience aliases */
+ const uint32_t domid = dev->cds->domid;
+
+ path = GCSPRINTF("%s/vifname",
+ libxl__domain_device_backend_path(gc, 0, domid,
+ nic->devid, LIBXL__DEVICE_KIND_VIF));
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL, path, &vifname);
+ if (!rc && !vifname) {
+ vifname = libxl__device_nic_devname(gc, domid,
+ nic->devid,
+ nic->nictype);
+ }
+
+ return vifname;
+}
+
+static void free_qdisc(libxl__remus_device_nic *remus_nic)
+{
+ if (remus_nic->qdisc == NULL)
+ return;
+
+ nl_object_put((struct nl_object *)(remus_nic->qdisc));
+ remus_nic->qdisc = NULL;
+}
+
+static int init_qdisc(libxl__checkpoint_devices_state *cds,
+ libxl__remus_device_nic *remus_nic)
+{
+ int rc, ret, ifindex;
+ struct rtnl_link *ifb = NULL;
+ struct rtnl_qdisc *qdisc = NULL;
+ libxl__remus_state *rs = cds->concrete_data;
+
+ STATE_AO_GC(cds->ao);
+
+ /* Now that we have brought up REMUS_IFB device with plug qdisc for
+ * this vif, so we need to refill the qdisc cache.
+ */
+ ret = nl_cache_refill(rs->nlsock, rs->qdisc_cache);
+ if (ret) {
+ LOGD(ERROR, cds->domid,
+ "cannot refill qdisc cache: %s", nl_geterror(ret));
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /* get a handle to the REMUS_IFB interface */
+ ret = rtnl_link_get_kernel(rs->nlsock, 0, remus_nic->ifb, &ifb);
+ if (ret) {
+ LOGD(ERROR, cds->domid,
+ "cannot obtain handle for %s: %s", remus_nic->ifb,
+ nl_geterror(ret));
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ ifindex = rtnl_link_get_ifindex(ifb);
+ if (!ifindex) {
+ LOGD(ERROR, cds->domid,
+ "interface %s has no index", remus_nic->ifb);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /* Get a reference to the root qdisc installed on the REMUS_IFB, by
+ * querying the qdisc list we obtained earlier. The netbufscript
+ * sets up the plug qdisc as the root qdisc, so we don't have to
+ * search the entire qdisc tree on the REMUS_IFB dev.
+
+ * There is no need to explicitly free this qdisc as its just a
+ * reference from the qdisc cache we allocated earlier.
+ */
+ qdisc = rtnl_qdisc_get_by_parent(rs->qdisc_cache, ifindex, TC_H_ROOT);
+ if (qdisc) {
+ const char *tc_kind = rtnl_tc_get_kind(TC_CAST(qdisc));
+ /* Sanity check: Ensure that the root qdisc is a plug qdisc. */
+ if (!tc_kind || strcmp(tc_kind, "plug")) {
+ LOGD(ERROR, cds->domid,
+ "plug qdisc is not installed on %s", remus_nic->ifb);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ remus_nic->qdisc = qdisc;
+ } else {
+ LOGD(ERROR, cds->domid,
+ "Cannot get qdisc handle from ifb %s", remus_nic->ifb);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ if (ifb)
+ rtnl_link_put(ifb);
+
+ if (rc && qdisc)
+ nl_object_put((struct nl_object *)qdisc);
+
+ return rc;
+}
+
+/* callbacks */
+
+static void netbuf_setup_script_cb(libxl__egc *egc,
+ libxl__async_exec_state *aes,
+ int rc, int status);
+static void netbuf_teardown_script_cb(libxl__egc *egc,
+ libxl__async_exec_state *aes,
+ int rc, int status);
+
+/*
+ * the script needs the following env & args
+ * $vifname
+ * $XENBUS_PATH (/libxl/<domid>/remus/netbuf/<devid>/)
+ * $REMUS_IFB (for teardown)
+ * setup/teardown as command line arg.
+ */
+static void setup_async_exec(libxl__checkpoint_device *dev, char *op)
+{
+ int arraysize, nr = 0;
+ char **env = NULL, **args = NULL;
+ libxl__remus_device_nic *remus_nic = dev->concrete_data;
+ libxl__checkpoint_devices_state *cds = dev->cds;
+ libxl__async_exec_state *aes = &dev->aodev.aes;
+ libxl__remus_state *rs = cds->concrete_data;
+
+ STATE_AO_GC(cds->ao);
+
+ /* Convenience aliases */
+ char *const script = libxl__strdup(gc, rs->netbufscript);
+ const uint32_t domid = cds->domid;
+ const int dev_id = remus_nic->devid;
+ const char *const vif = remus_nic->vif;
+ const char *const ifb = remus_nic->ifb;
+
+ arraysize = 7;
+ GCNEW_ARRAY(env, arraysize);
+ env[nr++] = "vifname";
+ env[nr++] = libxl__strdup(gc, vif);
+ env[nr++] = "XENBUS_PATH";
+ env[nr++] = GCSPRINTF("%s/remus/netbuf/%d",
+ libxl__xs_libxl_path(gc, domid), dev_id);
+ if (!strcmp(op, "teardown") && ifb) {
+ env[nr++] = "REMUS_IFB";
+ env[nr++] = libxl__strdup(gc, ifb);
+ }
+ env[nr++] = NULL;
+ assert(nr <= arraysize);
+
+ arraysize = 3; nr = 0;
+ GCNEW_ARRAY(args, arraysize);
+ args[nr++] = script;
+ args[nr++] = op;
+ args[nr++] = NULL;
+ assert(nr == arraysize);
+
+ aes->ao = dev->cds->ao;
+ aes->what = GCSPRINTF("%s %s", args[0], args[1]);
+ aes->env = env;
+ aes->args = args;
+ aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
+ aes->stdfds[0] = -1;
+ aes->stdfds[1] = -1;
+ aes->stdfds[2] = -1;
+
+ if (!strcmp(op, "teardown"))
+ aes->callback = netbuf_teardown_script_cb;
+ else
+ aes->callback = netbuf_setup_script_cb;
+}
+
+/* setup() and teardown() */
+
+static void nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+ int rc;
+ libxl__remus_device_nic *remus_nic;
+ const libxl_device_nic *nic = dev->backend_dev;
+
+ STATE_AO_GC(dev->cds->ao);
+
+ /*
+ * thers's no subkind of nic devices, so nic ops is always matched
+ * with nic devices
+ */
+ dev->matched = true;
+
+ GCNEW(remus_nic);
+ dev->concrete_data = remus_nic;
+ remus_nic->devid = nic->devid;
+ remus_nic->vif = get_vifname(dev, nic);
+ if (!remus_nic->vif) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ setup_async_exec(dev, "setup");
+ rc = libxl__async_exec_start(&dev->aodev.aes);
+ if (rc)
+ goto out;
+
+ return;
+
+out:
+ dev->aodev.rc = rc;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+/*
+ * In return, the script writes the name of REMUS_IFB device (during setup)
+ * to be used for output buffering into XENBUS_PATH/ifb
+ */
+static void netbuf_setup_script_cb(libxl__egc *egc,
+ libxl__async_exec_state *aes,
+ int rc, int status)
+{
+ libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
+ libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
+ libxl__remus_device_nic *remus_nic = dev->concrete_data;
+ libxl__checkpoint_devices_state *cds = dev->cds;
+ libxl__remus_state *rs = cds->concrete_data;
+ const char *out_path_base, *hotplug_error = NULL;
+
+ STATE_AO_GC(cds->ao);
+
+ /* Convenience aliases */
+ const uint32_t domid = cds->domid;
+ const int devid = remus_nic->devid;
+ const char *const vif = remus_nic->vif;
+ const char **const ifb = &remus_nic->ifb;
+
+ if (status && !rc)
+ rc = ERROR_FAIL;
+ if (rc)
+ goto out;
+
+ /*
+ * we need to get ifb first because it's needed for teardown
+ */
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/remus/netbuf/%d/ifb",
+ libxl__xs_libxl_path(gc, domid),
+ devid),
+ ifb);
+ if (rc)
+ goto out;
+
+ if (!(*ifb)) {
+ LOGD(ERROR, domid, "Cannot get ifb dev name for domain %u dev %s",
+ domid, vif);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ out_path_base = GCSPRINTF("%s/remus/netbuf/%d",
+ libxl__xs_libxl_path(gc, domid), devid);
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/hotplug-error", out_path_base),
+ &hotplug_error);
+ if (rc)
+ goto out;
+
+ if (hotplug_error) {
+ LOGD(ERROR, domid, "netbuf script %s setup failed for vif %s: %s",
+ rs->netbufscript, vif, hotplug_error);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (status) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ LOGD(DEBUG, domid, "%s will buffer packets from vif %s", *ifb, vif);
+ rc = init_qdisc(cds, remus_nic);
+
+out:
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+static void nic_teardown(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+ int rc;
+ STATE_AO_GC(dev->cds->ao);
+
+ setup_async_exec(dev, "teardown");
+
+ rc = libxl__async_exec_start(&dev->aodev.aes);
+ if (rc)
+ goto out;
+
+ return;
+
+out:
+ dev->aodev.rc = rc;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+static void netbuf_teardown_script_cb(libxl__egc *egc,
+ libxl__async_exec_state *aes,
+ int rc, int status)
+{
+ libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
+ libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
+ libxl__remus_device_nic *remus_nic = dev->concrete_data;
+
+ if (status && !rc)
+ rc = ERROR_FAIL;
+
+ free_qdisc(remus_nic);
+
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+/*----- checkpointing APIs -----*/
+
+/* The value of buffer_op, not the value passed to kernel */
+enum {
+ tc_buffer_start,
+ tc_buffer_release
+};
+
+/* API implementations */
+
+static int remus_netbuf_op(libxl__remus_device_nic *remus_nic,
+ libxl__checkpoint_devices_state *cds,
+ int buffer_op)
+{
+ int rc, ret;
+ libxl__remus_state *rs = cds->concrete_data;
+
+ STATE_AO_GC(cds->ao);
+
+ if (buffer_op == tc_buffer_start)
+ ret = rtnl_qdisc_plug_buffer(remus_nic->qdisc);
+ else
+ ret = rtnl_qdisc_plug_release_one(remus_nic->qdisc);
+
+ if (ret) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ ret = rtnl_qdisc_add(rs->nlsock, remus_nic->qdisc, NLM_F_REQUEST);
+ if (ret) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ if (rc)
+ LOGD(ERROR, cds-> domid, "Remus: cannot do netbuf op %s on %s:%s",
+ ((buffer_op == tc_buffer_start) ?
+ "start_new_epoch" : "release_prev_epoch"),
+ remus_nic->ifb, nl_geterror(ret));
+ return rc;
+}
+
+static void nic_postsuspend(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+ int rc;
+ libxl__remus_device_nic *remus_nic = dev->concrete_data;
+
+ STATE_AO_GC(dev->cds->ao);
+
+ rc = remus_netbuf_op(remus_nic, dev->cds, tc_buffer_start);
+
+ dev->aodev.rc = rc;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+static void nic_commit(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+ int rc;
+ libxl__remus_device_nic *remus_nic = dev->concrete_data;
+
+ STATE_AO_GC(dev->cds->ao);
+
+ rc = remus_netbuf_op(remus_nic, dev->cds, tc_buffer_release);
+
+ dev->aodev.rc = rc;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+const libxl__checkpoint_device_instance_ops remus_device_nic = {
+ .kind = LIBXL__DEVICE_KIND_VIF,
+ .setup = nic_setup,
+ .teardown = nic_teardown,
+ .postsuspend = nic_postsuspend,
+ .commit = nic_commit,
+};
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2016 SUSE Linux GmbH
+ * Author Juergen Gross <jgross@suse.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+int libxl_mac_to_device_nic(libxl_ctx *ctx, uint32_t domid,
+ const char *mac, libxl_device_nic *nic)
+{
+ GC_INIT(ctx);
+ libxl_device_nic *nics;
+ int nb, rc, i;
+ libxl_mac mac_n;
+
+ rc = libxl__parse_mac(mac, mac_n);
+ if (rc)
+ return rc;
+
+ nics = libxl__device_list(gc, &libxl__nic_devtype, domid, &nb);
+ if (!nics)
+ return ERROR_FAIL;
+
+ memset(nic, 0, sizeof (libxl_device_nic));
+
+ rc = ERROR_INVAL;
+ for (i = 0; i < nb; ++i) {
+ if (!libxl__compare_macs(&mac_n, &nics[i].mac)) {
+ *nic = nics[i];
+ rc = 0;
+ i++; /* Do not dispose this NIC on exit path */
+ break;
+ }
+ libxl_device_nic_dispose(&nics[i]);
+ }
+
+ for (; i<nb; i++)
+ libxl_device_nic_dispose(&nics[i]);
+
+ free(nics);
+ return rc;
+}
+
+#define LIBXL_DEVICE_NIC_MTU_DEFAULT 1500
+
+static int libxl__device_nic_setdefault(libxl__gc *gc, uint32_t domid,
+ libxl_device_nic *nic, bool hotplug)
+{
+ int rc;
+
+ if (!nic->mtu)
+ nic->mtu = LIBXL_DEVICE_NIC_MTU_DEFAULT;
+ if (!nic->model) {
+ nic->model = strdup("rtl8139");
+ if (!nic->model) return ERROR_NOMEM;
+ }
+ if (libxl__mac_is_default(&nic->mac)) {
+ const uint8_t *r;
+ libxl_uuid uuid;
+
+ libxl_uuid_generate(&uuid);
+ r = libxl_uuid_bytearray(&uuid);
+
+ nic->mac[0] = 0x00;
+ nic->mac[1] = 0x16;
+ nic->mac[2] = 0x3e;
+ nic->mac[3] = r[0] & 0x7f;
+ nic->mac[4] = r[1];
+ nic->mac[5] = r[2];
+ }
+ if (!nic->bridge) {
+ nic->bridge = strdup("xenbr0");
+ if (!nic->bridge) return ERROR_NOMEM;
+ }
+ if ( !nic->script && asprintf(&nic->script, "%s/vif-bridge",
+ libxl__xen_script_dir_path()) < 0 )
+ return ERROR_FAIL;
+
+ rc = libxl__resolve_domid(gc, nic->backend_domname, &nic->backend_domid);
+ if (rc < 0) return rc;
+
+ switch (libxl__domain_type(gc, domid)) {
+ case LIBXL_DOMAIN_TYPE_HVM:
+ if (!nic->nictype) {
+ if (hotplug)
+ nic->nictype = LIBXL_NIC_TYPE_VIF;
+ else
+ nic->nictype = LIBXL_NIC_TYPE_VIF_IOEMU;
+ }
+ break;
+ case LIBXL_DOMAIN_TYPE_PVH:
+ case LIBXL_DOMAIN_TYPE_PV:
+ if (nic->nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
+ LOGD(ERROR, domid,
+ "trying to create PV or PVH guest with an emulated interface");
+ return ERROR_INVAL;
+ }
+ nic->nictype = LIBXL_NIC_TYPE_VIF;
+ break;
+ case LIBXL_DOMAIN_TYPE_INVALID:
+ return ERROR_FAIL;
+ default:
+ abort();
+ }
+
+ return rc;
+}
+
+static void libxl__update_config_nic(libxl__gc *gc, libxl_device_nic *dst,
+ const libxl_device_nic *src)
+{
+ dst->devid = src->devid;
+ dst->nictype = src->nictype;
+ libxl_mac_copy(CTX, &dst->mac, &src->mac);
+}
+
+static int libxl__set_xenstore_nic(libxl__gc *gc, uint32_t domid,
+ libxl_device_nic *nic,
+ flexarray_t *back, flexarray_t *front,
+ flexarray_t *ro_front)
+{
+ flexarray_grow(back, 2);
+
+ if (nic->script)
+ flexarray_append_pair(back, "script",
+ libxl__abs_path(gc, nic->script,
+ libxl__xen_script_dir_path()));
+
+ if (nic->ifname) {
+ flexarray_append(back, "vifname");
+ flexarray_append(back, nic->ifname);
+ }
+
+ if (nic->coloft_forwarddev) {
+ flexarray_append(back, "forwarddev");
+ flexarray_append(back, nic->coloft_forwarddev);
+ }
+
+#define MAYBE_ADD_COLO_ARGS(arg) ({ \
+ if (nic->colo_##arg) { \
+ flexarray_append(back, "colo_"#arg); \
+ flexarray_append(back, nic->colo_##arg); \
+ } \
+})
+
+ MAYBE_ADD_COLO_ARGS(sock_mirror_id);
+ MAYBE_ADD_COLO_ARGS(sock_mirror_ip);
+ MAYBE_ADD_COLO_ARGS(sock_mirror_port);
+ MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_id);
+ MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_ip);
+ MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_port);
+ MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_id);
+ MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_ip);
+ MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_port);
+ MAYBE_ADD_COLO_ARGS(sock_compare_notify_id);
+ MAYBE_ADD_COLO_ARGS(sock_compare_notify_ip);
+ MAYBE_ADD_COLO_ARGS(sock_compare_notify_port);
+ MAYBE_ADD_COLO_ARGS(sock_redirector0_id);
+ MAYBE_ADD_COLO_ARGS(sock_redirector0_ip);
+ MAYBE_ADD_COLO_ARGS(sock_redirector0_port);
+ MAYBE_ADD_COLO_ARGS(sock_redirector1_id);
+ MAYBE_ADD_COLO_ARGS(sock_redirector1_ip);
+ MAYBE_ADD_COLO_ARGS(sock_redirector1_port);
+ MAYBE_ADD_COLO_ARGS(sock_redirector2_id);
+ MAYBE_ADD_COLO_ARGS(sock_redirector2_ip);
+ MAYBE_ADD_COLO_ARGS(sock_redirector2_port);
+ MAYBE_ADD_COLO_ARGS(filter_mirror_queue);
+ MAYBE_ADD_COLO_ARGS(filter_mirror_outdev);
+ MAYBE_ADD_COLO_ARGS(filter_redirector0_queue);
+ MAYBE_ADD_COLO_ARGS(filter_redirector0_indev);
+ MAYBE_ADD_COLO_ARGS(filter_redirector0_outdev);
+ MAYBE_ADD_COLO_ARGS(filter_redirector1_queue);
+ MAYBE_ADD_COLO_ARGS(filter_redirector1_indev);
+ MAYBE_ADD_COLO_ARGS(filter_redirector1_outdev);
+ MAYBE_ADD_COLO_ARGS(compare_pri_in);
+ MAYBE_ADD_COLO_ARGS(compare_sec_in);
+ MAYBE_ADD_COLO_ARGS(compare_out);
+ MAYBE_ADD_COLO_ARGS(compare_notify_dev);
+
+ MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_id);
+ MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_ip);
+ MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_port);
+ MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_id);
+ MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_ip);
+ MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_port);
+ MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_queue);
+ MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_indev);
+ MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_outdev);
+ MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_queue);
+ MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_indev);
+ MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_outdev);
+ MAYBE_ADD_COLO_ARGS(filter_sec_rewriter0_queue);
+ MAYBE_ADD_COLO_ARGS(checkpoint_host);
+ MAYBE_ADD_COLO_ARGS(checkpoint_port);
+
+#undef MAYBE_ADD_COLO_ARGS
+
+ flexarray_append(back, "mac");
+ flexarray_append(back,GCSPRINTF(LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
+ if (nic->ip) {
+ flexarray_append(back, "ip");
+ flexarray_append(back, libxl__strdup(gc, nic->ip));
+ }
+ if (nic->gatewaydev) {
+ flexarray_append(back, "gatewaydev");
+ flexarray_append(back, libxl__strdup(gc, nic->gatewaydev));
+ }
+
+ if (nic->rate_interval_usecs > 0) {
+ flexarray_append(back, "rate");
+ flexarray_append(back, GCSPRINTF("%"PRIu64",%"PRIu32"",
+ nic->rate_bytes_per_interval,
+ nic->rate_interval_usecs));
+ }
+
+ if (nic->mtu != LIBXL_DEVICE_NIC_MTU_DEFAULT) {
+ flexarray_append(back, "mtu");
+ flexarray_append(back, GCSPRINTF("%u", nic->mtu));
+ }
+
+ flexarray_append(back, "bridge");
+ flexarray_append(back, libxl__strdup(gc, nic->bridge));
+ flexarray_append(back, "handle");
+ flexarray_append(back, GCSPRINTF("%d", nic->devid));
+ flexarray_append(back, "type");
+ flexarray_append(back, libxl__strdup(gc,
+ libxl_nic_type_to_string(nic->nictype)));
+
+ flexarray_append(front, "handle");
+ flexarray_append(front, GCSPRINTF("%d", nic->devid));
+ flexarray_append(front, "mac");
+ flexarray_append(front, GCSPRINTF(
+ LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
+
+ flexarray_append(ro_front, "mtu");
+ flexarray_append(ro_front, GCSPRINTF("%u", nic->mtu));
+
+ return 0;
+}
+
+static void libxl__device_nic_add(libxl__egc *egc, uint32_t domid,
+ libxl_device_nic *nic,
+ libxl__ao_device *aodev)
+{
+ libxl__device_add_async(egc, domid, &libxl__nic_devtype, nic, aodev);
+}
+
+static int libxl__nic_from_xenstore(libxl__gc *gc, const char *libxl_path,
+ libxl_devid devid, libxl_device_nic *nic)
+{
+ const char *tmp;
+ int rc;
+
+ libxl_device_nic_init(nic);
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/handle", libxl_path), &tmp);
+ if (rc) goto out;
+ if (tmp)
+ nic->devid = atoi(tmp);
+ else
+ nic->devid = 0;
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path), &tmp);
+ if (rc) goto out;
+
+ if (!tmp) {
+ LOG(ERROR, "nic %s does not exist (no backend path)", libxl_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = libxl__backendpath_parse_domid(gc, tmp, &nic->backend_domid);
+ if (rc) goto out;
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/mtu", libxl_path), &tmp);
+ if (rc) goto out;
+ if (tmp) {
+ char *endptr;
+
+ nic->mtu = strtol(tmp, &endptr, 10);
+ if (*endptr != '\0') {
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ } else {
+ nic->mtu = LIBXL_DEVICE_NIC_MTU_DEFAULT;
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/mac", libxl_path), &tmp);
+ if (rc) goto out;
+ if (tmp) {
+ rc = libxl__parse_mac(tmp, nic->mac);
+ if (rc) goto out;
+ } else {
+ memset(nic->mac, 0, sizeof(nic->mac));
+ }
+
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/ip", libxl_path),
+ (const char **)(&nic->ip));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/bridge", libxl_path),
+ (const char **)(&nic->bridge));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/script", libxl_path),
+ (const char **)(&nic->script));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/forwarddev", libxl_path),
+ (const char **)(&nic->coloft_forwarddev));
+ if (rc) goto out;
+
+#define CHECK_COLO_ARGS(arg) ({ \
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL, \
+ GCSPRINTF("%s/colo_"#arg, libxl_path), \
+ (const char **)(&nic->colo_##arg)); \
+ if (rc) goto out; \
+})
+
+ CHECK_COLO_ARGS(sock_mirror_id);
+ CHECK_COLO_ARGS(sock_mirror_ip);
+ CHECK_COLO_ARGS(sock_mirror_port);
+ CHECK_COLO_ARGS(sock_compare_pri_in_id);
+ CHECK_COLO_ARGS(sock_compare_pri_in_ip);
+ CHECK_COLO_ARGS(sock_compare_pri_in_port);
+ CHECK_COLO_ARGS(sock_compare_sec_in_id);
+ CHECK_COLO_ARGS(sock_compare_sec_in_ip);
+ CHECK_COLO_ARGS(sock_compare_sec_in_port);
+ CHECK_COLO_ARGS(sock_compare_notify_id);
+ CHECK_COLO_ARGS(sock_compare_notify_ip);
+ CHECK_COLO_ARGS(sock_compare_notify_port);
+ CHECK_COLO_ARGS(sock_redirector0_id);
+ CHECK_COLO_ARGS(sock_redirector0_ip);
+ CHECK_COLO_ARGS(sock_redirector0_port);
+ CHECK_COLO_ARGS(sock_redirector1_id);
+ CHECK_COLO_ARGS(sock_redirector1_ip);
+ CHECK_COLO_ARGS(sock_redirector1_port);
+ CHECK_COLO_ARGS(sock_redirector2_id);
+ CHECK_COLO_ARGS(sock_redirector2_ip);
+ CHECK_COLO_ARGS(sock_redirector2_port);
+ CHECK_COLO_ARGS(filter_mirror_queue);
+ CHECK_COLO_ARGS(filter_mirror_outdev);
+ CHECK_COLO_ARGS(filter_redirector0_queue);
+ CHECK_COLO_ARGS(filter_redirector0_indev);
+ CHECK_COLO_ARGS(filter_redirector0_outdev);
+ CHECK_COLO_ARGS(filter_redirector1_queue);
+ CHECK_COLO_ARGS(filter_redirector1_indev);
+ CHECK_COLO_ARGS(filter_redirector1_outdev);
+ CHECK_COLO_ARGS(compare_pri_in);
+ CHECK_COLO_ARGS(compare_sec_in);
+ CHECK_COLO_ARGS(compare_out);
+ CHECK_COLO_ARGS(compare_notify_dev);
+ CHECK_COLO_ARGS(sock_sec_redirector0_id);
+ CHECK_COLO_ARGS(sock_sec_redirector0_ip);
+ CHECK_COLO_ARGS(sock_sec_redirector0_port);
+ CHECK_COLO_ARGS(sock_sec_redirector1_id);
+ CHECK_COLO_ARGS(sock_sec_redirector1_ip);
+ CHECK_COLO_ARGS(sock_sec_redirector1_port);
+ CHECK_COLO_ARGS(filter_sec_redirector0_queue);
+ CHECK_COLO_ARGS(filter_sec_redirector0_indev);
+ CHECK_COLO_ARGS(filter_sec_redirector0_outdev);
+ CHECK_COLO_ARGS(filter_sec_redirector1_queue);
+ CHECK_COLO_ARGS(filter_sec_redirector1_indev);
+ CHECK_COLO_ARGS(filter_sec_redirector1_outdev);
+ CHECK_COLO_ARGS(filter_sec_rewriter0_queue);
+ CHECK_COLO_ARGS(checkpoint_host);
+ CHECK_COLO_ARGS(checkpoint_port);
+
+#undef CHECK_COLO_ARGS
+
+ /* vif_ioemu nics use the same xenstore entries as vif interfaces */
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/type", libxl_path), &tmp);
+ if (rc) goto out;
+ if (tmp) {
+ rc = libxl_nic_type_from_string(tmp, &nic->nictype);
+ if (rc) goto out;
+ } else {
+ nic->nictype = LIBXL_NIC_TYPE_VIF;
+ }
+ nic->model = NULL; /* XXX Only for TYPE_IOEMU */
+ nic->ifname = NULL; /* XXX Only for TYPE_IOEMU */
+
+ rc = 0;
+ out:
+ return rc;
+}
+
+libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx, uint32_t domid, int *num)
+{
+ libxl_device_nic *r;
+
+ GC_INIT(ctx);
+
+ r = libxl__device_list(gc, &libxl__nic_devtype, domid, num);
+
+ GC_FREE;
+
+ return r;
+}
+
+void libxl_device_nic_list_free(libxl_device_nic* list, int num)
+{
+ libxl__device_list_free(&libxl__nic_devtype, list, num);
+}
+
+int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_nic *nic,
+ libxl_nicinfo *nicinfo)
+{
+ GC_INIT(ctx);
+ char *nicpath, *libxl_path;
+ char *val;
+ int rc;
+
+ nicinfo->devid = nic->devid;
+
+ nicpath = libxl__domain_device_frontend_path(gc, domid, nicinfo->devid,
+ LIBXL__DEVICE_KIND_VIF);
+ libxl_path = libxl__domain_device_libxl_path(gc, domid, nicinfo->devid,
+ LIBXL__DEVICE_KIND_VIF);
+ nicinfo->backend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path), NULL);
+ if (!nicinfo->backend) {
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ rc = libxl__backendpath_parse_domid(gc, nicinfo->backend,
+ &nicinfo->backend_id);
+ if (rc) goto out;
+
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", nicpath));
+ nicinfo->state = val ? strtoul(val, NULL, 10) : -1;
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", nicpath));
+ nicinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tx-ring-ref", nicpath));
+ nicinfo->rref_tx = val ? strtoul(val, NULL, 10) : -1;
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/rx-ring-ref", nicpath));
+ nicinfo->rref_rx = val ? strtoul(val, NULL, 10) : -1;
+ nicinfo->frontend = libxl__strdup(NOGC, nicpath);
+ nicinfo->frontend_id = domid;
+
+ rc = 0;
+ out:
+ GC_FREE;
+ return rc;
+}
+
+const char *libxl__device_nic_devname(libxl__gc *gc,
+ uint32_t domid,
+ uint32_t devid,
+ libxl_nic_type type)
+{
+ switch (type) {
+ case LIBXL_NIC_TYPE_VIF:
+ return GCSPRINTF(NETBACK_NIC_NAME, domid, devid);
+ case LIBXL_NIC_TYPE_VIF_IOEMU:
+ return GCSPRINTF(NETBACK_NIC_NAME TAP_DEVICE_SUFFIX, domid, devid);
+ default:
+ abort();
+ }
+}
+
+static int libxl_device_nic_compare(const libxl_device_nic *d1,
+ const libxl_device_nic *d2)
+{
+ return COMPARE_DEVID(d1, d2);
+}
+
+static void libxl_device_nic_update_config(libxl__gc *gc, void *d, void *s)
+{
+ libxl__update_config_nic(gc, d, s);
+}
+
+int libxl__device_nic_set_devids(libxl__gc *gc, libxl_domain_config *d_config,
+ uint32_t domid)
+{
+ int ret = 0;
+ int i;
+ size_t last_devid = -1;
+
+ for (i = 0; i < d_config->num_nics; i++) {
+ /* We have to init the nic here, because we still haven't
+ * called libxl_device_nic_add when domcreate_launch_dm gets called,
+ * but qemu needs the nic information to be complete.
+ */
+ ret = libxl__device_nic_setdefault(gc, domid, &d_config->nics[i],
+ false);
+ if (ret) {
+ LOGD(ERROR, domid, "Unable to set nic defaults for nic %d", i);
+ goto out;
+ }
+
+ if (d_config->nics[i].devid > last_devid)
+ last_devid = d_config->nics[i].devid;
+ }
+ for (i = 0; i < d_config->num_nics; i++) {
+ if (d_config->nics[i].devid < 0)
+ d_config->nics[i].devid = ++last_devid;
+ }
+
+out:
+ return ret;
+}
+
+static LIBXL_DEFINE_UPDATE_DEVID(nic)
+static LIBXL_DEFINE_DEVICE_FROM_TYPE(nic)
+
+LIBXL_DEFINE_DEVID_TO_DEVICE(nic)
+LIBXL_DEFINE_DEVICE_ADD(nic)
+LIBXL_DEFINE_DEVICES_ADD(nic)
+LIBXL_DEFINE_DEVICE_REMOVE(nic)
+
+DEFINE_DEVICE_TYPE_STRUCT(nic, VIF,
+ .update_config = libxl_device_nic_update_config,
+ .from_xenstore = (device_from_xenstore_fn_t)libxl__nic_from_xenstore,
+ .set_xenstore_config = (device_set_xenstore_config_fn_t)
+ libxl__set_xenstore_nic,
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2016
+ * Author Wei Liu <wei.liu2@citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+void libxl__colo_restore_setup(libxl__egc *egc,
+ libxl__colo_restore_state *crs)
+{
+ STATE_AO_GC(crs->ao);
+
+ LOGD(ERROR, crs->domid, "COLO is not supported");
+
+ crs->callback(egc, crs, ERROR_FAIL);
+}
+
+void libxl__colo_restore_teardown(libxl__egc *egc, void *dcs_void,
+ int ret, int retval, int errnoval)
+{
+ /* Shouldn't be here because setup already failed */
+ abort();
+}
+
+void libxl__colo_save_setup(libxl__egc *egc, libxl__colo_save_state *css)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+ STATE_AO_GC(dss->ao);
+
+ LOGD(ERROR, dss->domid, "COLO is not supported");
+
+ dss->callback(egc, dss, ERROR_FAIL);
+}
+
+void libxl__colo_save_teardown(libxl__egc *egc,
+ libxl__colo_save_state *css,
+ int rc)
+{
+ /* Shouldn't be here because setup already failed */
+ abort();
+}
+
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2015 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+void libxl__conversion_helper_init(libxl__conversion_helper_state *chs)
+{
+ libxl__ev_child_init(&chs->child);
+}
+
+int libxl__convert_legacy_stream(libxl__egc *egc,
+ libxl__conversion_helper_state *chs)
+{
+ return ERROR_FAIL;
+}
+
+void libxl__conversion_helper_abort(libxl__egc *egc,
+ libxl__conversion_helper_state *chs,
+ int rc)
+{
+ /* no op */
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl)
+{
+ return 1;
+}
+
+void libxl_cpuid_dispose(libxl_cpuid_policy_list *p_cpuid_list)
+{
+}
+
+int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str)
+{
+ return 0;
+}
+
+int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid,
+ const char* str)
+{
+ return 0;
+}
+
+void libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool restore,
+ libxl_domain_build_info *info)
+{
+}
+
+yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
+ libxl_cpuid_policy_list *pcpuid)
+{
+ return 0;
+}
+
+int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
+ const libxl__json_object *o,
+ libxl_cpuid_policy_list *p)
+{
+ return 0;
+}
+
+void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
+ libxl_cpuid_policy_list *dst,
+ const libxl_cpuid_policy_list *src)
+{
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2014
+ * Author Shriram Rajagopalan <rshriram@cs.ubc.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+int libxl__netbuffer_enabled(libxl__gc *gc)
+{
+ return 0;
+}
+
+int init_subkind_nic(libxl__checkpoint_devices_state *cds)
+{
+ return 0;
+}
+
+void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds)
+{
+ return;
+}
+
+static void nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+ STATE_AO_GC(dev->cds->ao);
+
+ dev->aodev.rc = ERROR_FAIL;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+const libxl__checkpoint_device_instance_ops remus_device_nic = {
+ .kind = LIBXL__DEVICE_KIND_VIF,
+ .setup = nic_setup,
+};
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2012 Citrix Ltd.
+ * Author Dario Faggioli <dario.faggioli@citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <glob.h>
+
+#include "libxl_internal.h"
+
+/*
+ * What follows are helpers for generating all the k-combinations
+ * without repetitions of a set S with n elements in it. Formally
+ * speaking, they are subsets of k distinct elements of S and, if
+ * S is n elements big, the number of k-combinations is equal to the
+ * binomial coefficient C(n k)=n!/(k! * (n - k)!).
+ *
+ * The various subset are generated one after the other by calling
+ * comb_init() first, and, after that, comb_next()
+ * C(n k)-1 times. An iterator is used to store the current status
+ * of the whole generation operation (i.e., basically, the last
+ * combination that has been generated). As soon as all combinations
+ * have been generated, comb_next() will start returning 0 instead of
+ * 1. The same instance of the iterator and the same values for
+ * n and k _must_ be used for each call (if that doesn't happen, the
+ * result is unspecified).
+ *
+ * The algorithm is a well known one (see, for example, D. Knuth's "The
+ * Art of Computer Programming - Volume 4, Fascicle 3" and it produces
+ * the combinations in such a way that they (well, more precisely, their
+ * indexes it the array/map representing the set) come with lexicographic
+ * ordering.
+ *
+ * For example, with n = 5 and k = 3, calling comb_init()
+ * will generate { 0, 1, 2 }, while subsequent valid calls to
+ * comb_next() will produce the following:
+ * { { 0, 1, 3 }, { 0, 1, 4 },
+ * { 0, 2, 3 }, { 0, 2, 4 }, { 0, 3, 4 },
+ * { 1, 2, 3 }, { 1, 2, 4 }, { 1, 3, 4 },
+ * { 2, 3, 4 } }
+ *
+ * This is used by the automatic NUMA placement logic below.
+ */
+typedef int* comb_iter_t;
+
+static int comb_init(libxl__gc *gc, comb_iter_t *it, int n, int k)
+{
+ comb_iter_t new_iter;
+ int i;
+
+ if (n < k)
+ return 0;
+
+ /* First set is always { 0, 1, 2, ..., k-1 } */
+ GCNEW_ARRAY(new_iter, k);
+ for (i = 0; i < k; i++)
+ new_iter[i] = i;
+
+ *it = new_iter;
+ return 1;
+}
+
+static int comb_next(comb_iter_t it, int n, int k)
+{
+ int i;
+
+ /*
+ * The idea here is to find the leftmost element from where
+ * we should start incrementing the indexes of the iterator.
+ * This means looking for the highest index that can be increased
+ * while still producing value smaller than n-1. In the example
+ * above, when dealing with { 0, 1, 4 }, such an element is the
+ * second one, as the third is already equal to 4 (which actually
+ * is n-1).
+ * Once we found from where to start, we increment that element
+ * and override the right-hand rest of the iterator with its
+ * successors, thus achieving lexicographic ordering.
+ *
+ * Regarding the termination of the generation process, when we
+ * manage in bringing n-k at the very first position of the iterator,
+ * we know that is the last valid combination ( { 2, 3, 4 }, with
+ * n - k = 5 - 2 = 2, in the example above), and thus we start
+ * returning 0 as soon as we cross that border.
+ */
+ for (i = k - 1; it[i] == n - k + i; i--) {
+ if (i <= 0)
+ return 0;
+ }
+ for (it[i]++, i++; i < k; i++)
+ it[i] = it[i - 1] + 1;
+ return 1;
+}
+
+/* NUMA automatic placement (see libxl_internal.h for details) */
+
+/*
+ * This function turns a k-combination iterator into a node map,
+ * given another map, telling us which nodes should be considered.
+ *
+ * This means the bits that are set in suitable_nodemap and that
+ * corresponds to the indexes of the given combination are the ones
+ * that will be set in nodemap.
+ *
+ * For example, given a fully set suitable_nodemap, if the iterator
+ * represents the combination { 0, 2, 4}, nodmeap will have bits #0,
+ * #2 and #4 set.
+ * On the other hand, if, say, suitable_nodemap=01011011, the same
+ * iterator will cause bits #1, #4 and #7 of nodemap to be set.
+ */
+static void comb_get_nodemap(comb_iter_t it, libxl_bitmap *suitable_nodemap,
+ libxl_bitmap *nodemap, int k)
+{
+ int i, m = 0, n = 0;
+
+ libxl_bitmap_set_none(nodemap);
+ libxl_for_each_set_bit(i, *suitable_nodemap) {
+ /* Check wether the n-th set bit of suitable_nodemap
+ * matches with the m-th element of the iterator (and,
+ * only if it does, advance to the next one) */
+ if (m < k && n == it[m]) {
+ libxl_bitmap_set(nodemap, i);
+ m++;
+ }
+ n++;
+ }
+}
+
+/* Retrieve the number of cpus that the nodes that are part of the nodemap
+ * span and are also set in suitable_cpumap. */
+static int nodemap_to_nr_cpus(libxl_cputopology *tinfo, int nr_cpus,
+ const libxl_bitmap *suitable_cpumap,
+ const libxl_bitmap *nodemap)
+{
+ int i, nodes_cpus = 0;
+
+ for (i = 0; i < nr_cpus; i++) {
+ if (libxl_bitmap_test(suitable_cpumap, i) &&
+ libxl_bitmap_test(nodemap, tinfo[i].node))
+ nodes_cpus++;
+ }
+ return nodes_cpus;
+}
+
+/* Retrieve the amount of free memory within the nodemap */
+static uint32_t nodemap_to_free_memkb(libxl_numainfo *ninfo,
+ libxl_bitmap *nodemap)
+{
+ uint32_t free_memkb = 0;
+ int i;
+
+ libxl_for_each_set_bit(i, *nodemap)
+ free_memkb += ninfo[i].free / 1024;
+
+ return free_memkb;
+}
+
+/* Retrieve the number of vcpus able to run on the nodes in nodemap */
+static int nodemap_to_nr_vcpus(libxl__gc *gc, int vcpus_on_node[],
+ const libxl_bitmap *nodemap)
+{
+ int i, nr_vcpus = 0;
+
+ libxl_for_each_set_bit(i, *nodemap)
+ nr_vcpus += vcpus_on_node[i];
+
+ return nr_vcpus;
+}
+
+/* Number of vcpus able to run on the cpus of the various nodes
+ * (reported by filling the array vcpus_on_node[]). */
+static int nr_vcpus_on_nodes(libxl__gc *gc, libxl_cputopology *tinfo,
+ size_t tinfo_elements,
+ const libxl_bitmap *suitable_cpumap,
+ int vcpus_on_node[])
+{
+ libxl_dominfo *dinfo = NULL;
+ libxl_bitmap dom_nodemap, nodes_counted;
+ int nr_doms, nr_cpus;
+ int i, j, k;
+
+ dinfo = libxl_list_domain(CTX, &nr_doms);
+ if (dinfo == NULL)
+ return ERROR_FAIL;
+
+ if (libxl_node_bitmap_alloc(CTX, &nodes_counted, 0) < 0) {
+ libxl_dominfo_list_free(dinfo, nr_doms);
+ return ERROR_FAIL;
+ }
+
+ if (libxl_node_bitmap_alloc(CTX, &dom_nodemap, 0) < 0) {
+ libxl_bitmap_dispose(&nodes_counted);
+ libxl_dominfo_list_free(dinfo, nr_doms);
+ return ERROR_FAIL;
+ }
+
+ for (i = 0; i < nr_doms; i++) {
+ libxl_vcpuinfo *vinfo = NULL;
+ int nr_dom_vcpus = 0;
+ libxl_cpupoolinfo cpupool_info;
+ int cpupool;
+
+ libxl_cpupoolinfo_init(&cpupool_info);
+
+ cpupool = libxl__domain_cpupool(gc, dinfo[i].domid);
+ if (cpupool < 0)
+ goto next;
+ if (libxl_cpupool_info(CTX, &cpupool_info, cpupool))
+ goto next;
+
+ vinfo = libxl_list_vcpu(CTX, dinfo[i].domid, &nr_dom_vcpus, &nr_cpus);
+ if (vinfo == NULL)
+ goto next;
+
+ /* Retrieve the domain's node-affinity map */
+ libxl_domain_get_nodeaffinity(CTX, dinfo[i].domid, &dom_nodemap);
+
+ for (j = 0; j < nr_dom_vcpus; j++) {
+ /*
+ * For each vcpu of each domain, it must have both vcpu-affinity
+ * and node-affinity to (a pcpu belonging to) a certain node to
+ * cause an increment in the corresponding element of the array.
+ *
+ * Note that we also need to check whether the cpu actually
+ * belongs to the domain's cpupool (the cpupool of the domain
+ * being checked). In fact, it could be that the vcpu has affinity
+ * with cpus in suitable_cpumask, but that are not in its own
+ * cpupool, and we don't want to consider those!
+ */
+ libxl_bitmap_set_none(&nodes_counted);
+ libxl_for_each_set_bit(k, vinfo[j].cpumap) {
+ if (k >= tinfo_elements)
+ break;
+ int node = tinfo[k].node;
+
+ if (libxl_bitmap_test(suitable_cpumap, k) &&
+ libxl_bitmap_test(&cpupool_info.cpumap, k) &&
+ libxl_bitmap_test(&dom_nodemap, node) &&
+ !libxl_bitmap_test(&nodes_counted, node)) {
+ libxl_bitmap_set(&nodes_counted, node);
+ vcpus_on_node[node]++;
+ }
+ }
+ }
+
+ next:
+ libxl_cpupoolinfo_dispose(&cpupool_info);
+ libxl_vcpuinfo_list_free(vinfo, nr_dom_vcpus);
+ }
+
+ libxl_bitmap_dispose(&dom_nodemap);
+ libxl_bitmap_dispose(&nodes_counted);
+ libxl_dominfo_list_free(dinfo, nr_doms);
+ return 0;
+}
+
+/*
+ * This function tries to figure out if the host has a consistent number
+ * of cpus along all its NUMA nodes. In fact, if that is the case, we can
+ * calculate the minimum number of nodes needed for a domain by just
+ * dividing its total number of vcpus by this value computed here.
+ * However, we are not allowed to assume that all the nodes have the
+ * same number of cpus. Therefore, in case discrepancies among different
+ * nodes are found, this function just returns 0, for the caller to know
+ * it shouldn't rely on this 'optimization', and sort out things in some
+ * other way (by doing something basic, like starting trying with
+ * candidates with just one node).
+ */
+static int count_cpus_per_node(libxl_cputopology *tinfo, int nr_cpus,
+ int nr_nodes)
+{
+ int cpus_per_node = 0;
+ int j, i;
+
+ /* This makes sense iff # of PCPUs is the same for all nodes */
+ for (j = 0; j < nr_nodes; j++) {
+ int curr_cpus = 0;
+
+ for (i = 0; i < nr_cpus; i++) {
+ if (tinfo[i].node == j)
+ curr_cpus++;
+ }
+ /* So, if the above does not hold, turn the whole thing off! */
+ cpus_per_node = cpus_per_node == 0 ? curr_cpus : cpus_per_node;
+ if (cpus_per_node != curr_cpus)
+ return 0;
+ }
+ return cpus_per_node;
+}
+
+/*
+ * Looks for the placement candidates that satisfyies some specific
+ * conditions and return the best one according to the provided
+ * comparison function.
+ */
+int libxl__get_numa_candidate(libxl__gc *gc,
+ uint64_t min_free_memkb, int min_cpus,
+ int min_nodes, int max_nodes,
+ const libxl_bitmap *suitable_cpumap,
+ libxl__numa_candidate_cmpf numa_cmpf,
+ libxl__numa_candidate *cndt_out,
+ int *cndt_found)
+{
+ libxl__numa_candidate new_cndt;
+ libxl_cputopology *tinfo = NULL;
+ libxl_numainfo *ninfo = NULL;
+ int nr_nodes = 0, nr_suit_nodes, nr_cpus = 0;
+ libxl_bitmap suitable_nodemap, nodemap;
+ int *vcpus_on_node, rc = 0;
+
+ libxl_bitmap_init(&nodemap);
+ libxl_bitmap_init(&suitable_nodemap);
+ libxl__numa_candidate_init(&new_cndt);
+
+ /* Get platform info and prepare the map for testing the combinations */
+ ninfo = libxl_get_numainfo(CTX, &nr_nodes);
+ if (ninfo == NULL)
+ return ERROR_FAIL;
+
+ if (nr_nodes <= 1) {
+ *cndt_found = 0;
+ goto out;
+ }
+
+ GCNEW_ARRAY(vcpus_on_node, nr_nodes);
+
+ /*
+ * The good thing about this solution is that it is based on heuristics
+ * (implemented in numa_cmpf() ), but we at least can evaluate it on
+ * all the possible placement candidates. That can happen because the
+ * number of nodes present in current NUMA systems is quite small.
+ * In fact, even if a sum of binomials is involved, if the system has
+ * up to 16 nodes it "only" takes 65535 steps. This is fine, as the
+ * number of nodes the biggest NUMA systems provide at the time of this
+ * writing is 8 (and it will probably continue to be so for a while).
+ * However, computanional complexity would explode on systems bigger
+ * than that, and it's really important we avoid trying to run this
+ * on monsters with 32, 64 or more nodes (if they ever pop into being).
+ * Therefore, here it comes a safety catch that disables the algorithm
+ * for the cases when it wouldn't work well.
+ */
+ if (nr_nodes > 16) {
+ /* Log we did nothing and return 0, as no real error occurred */
+ LOG(WARN, "System has %d NUMA nodes, which is too big for the "
+ "placement algorithm to work effectively: skipping it. "
+ "Consider manually pinning the vCPUs and/or looking at "
+ "cpupools for manually partitioning the system.",
+ nr_nodes);
+ *cndt_found = 0;
+ goto out;
+ }
+
+ tinfo = libxl_get_cpu_topology(CTX, &nr_cpus);
+ if (tinfo == NULL) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl_node_bitmap_alloc(CTX, &nodemap, 0);
+ if (rc)
+ goto out;
+ rc = libxl__numa_candidate_alloc(gc, &new_cndt);
+ if (rc)
+ goto out;
+
+ /* Allocate and prepare the map of the node that can be utilized for
+ * placement, basing on the map of suitable cpus. */
+ rc = libxl_node_bitmap_alloc(CTX, &suitable_nodemap, 0);
+ if (rc)
+ goto out;
+ rc = libxl_cpumap_to_nodemap(CTX, suitable_cpumap, &suitable_nodemap);
+ if (rc)
+ goto out;
+
+ /*
+ * Later on, we will try to figure out how many vcpus are runnable on
+ * each candidate (as a part of choosing the best one of them). That
+ * requires going through all the vcpus of all the domains and check
+ * their affinities. So, instead of doing that for each candidate,
+ * let's count here the number of vcpus runnable on each node, so that
+ * all we have to do later is summing up the right elements of the
+ * vcpus_on_node array.
+ */
+ rc = nr_vcpus_on_nodes(gc, tinfo, nr_cpus, suitable_cpumap, vcpus_on_node);
+ if (rc)
+ goto out;
+
+ /*
+ * If the minimum number of NUMA nodes is not explicitly specified
+ * (i.e., min_nodes == 0), we try to figure out a sensible number of nodes
+ * from where to start generating candidates, if possible (or just start
+ * from 1 otherwise). The maximum number of nodes should not exceed the
+ * number of existent NUMA nodes on the host, or the candidate generation
+ * won't work properly.
+ */
+ if (!min_nodes) {
+ int cpus_per_node;
+
+ cpus_per_node = count_cpus_per_node(tinfo, nr_cpus, nr_nodes);
+ if (cpus_per_node == 0)
+ min_nodes = 1;
+ else
+ min_nodes = (min_cpus + cpus_per_node - 1) / cpus_per_node;
+ }
+ /* We also need to be sure we do not exceed the number of
+ * nodes we are allowed to use. */
+ nr_suit_nodes = libxl_bitmap_count_set(&suitable_nodemap);
+
+ if (min_nodes > nr_suit_nodes)
+ min_nodes = nr_suit_nodes;
+ if (!max_nodes || max_nodes > nr_suit_nodes)
+ max_nodes = nr_suit_nodes;
+ if (min_nodes > max_nodes) {
+ LOG(ERROR, "Inconsistent minimum or maximum number of guest nodes");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ /* This is up to the caller to be disposed */
+ rc = libxl__numa_candidate_alloc(gc, cndt_out);
+ if (rc)
+ goto out;
+
+ /*
+ * Consider all the combinations with sizes in [min_nodes, max_nodes]
+ * (see comb_init() and comb_next()). Note that, since the fewer the
+ * number of nodes the better, it is guaranteed that any candidate
+ * found during the i-eth step will be better than any other one we
+ * could find during the (i+1)-eth and all the subsequent steps (they
+ * all will have more nodes). It's thus pointless to keep going if
+ * we already found something.
+ */
+ *cndt_found = 0;
+ while (min_nodes <= max_nodes && *cndt_found == 0) {
+ comb_iter_t comb_iter;
+ int comb_ok;
+
+ /*
+ * And here it is. Each step of this cycle generates a combination of
+ * nodes as big as min_nodes mandates. Each of these combinations is
+ * checked against the constraints provided by the caller (namely,
+ * amount of free memory and number of cpus) and it can concur to
+ * become our best placement iff it passes the check.
+ */
+ for (comb_ok = comb_init(gc, &comb_iter, nr_suit_nodes, min_nodes);
+ comb_ok;
+ comb_ok = comb_next(comb_iter, nr_suit_nodes, min_nodes)) {
+ uint64_t nodes_free_memkb;
+ int nodes_cpus;
+
+ /* Get the nodemap for the combination, only considering
+ * suitable nodes. */
+ comb_get_nodemap(comb_iter, &suitable_nodemap,
+ &nodemap, min_nodes);
+
+ /* If there is not enough memory in this combination, skip it
+ * and go generating the next one... */
+ nodes_free_memkb = nodemap_to_free_memkb(ninfo, &nodemap);
+ if (min_free_memkb && nodes_free_memkb < min_free_memkb)
+ continue;
+
+ /* And the same applies if this combination is short in cpus */
+ nodes_cpus = nodemap_to_nr_cpus(tinfo, nr_cpus, suitable_cpumap,
+ &nodemap);
+ if (min_cpus && nodes_cpus < min_cpus)
+ continue;
+
+ /*
+ * Conditions are met, we can compare this candidate with the
+ * current best one (if any).
+ */
+ libxl__numa_candidate_put_nodemap(gc, &new_cndt, &nodemap);
+ new_cndt.nr_vcpus = nodemap_to_nr_vcpus(gc, vcpus_on_node,
+ &nodemap);
+ new_cndt.free_memkb = nodes_free_memkb;
+ new_cndt.nr_nodes = libxl_bitmap_count_set(&nodemap);
+ new_cndt.nr_cpus = nodes_cpus;
+
+ /*
+ * Check if the new candidate we is better the what we found up
+ * to now by means of the comparison function. If no comparison
+ * function is provided, just return as soon as we find our first
+ * candidate.
+ */
+ if (*cndt_found == 0 || numa_cmpf(&new_cndt, cndt_out) < 0) {
+ *cndt_found = 1;
+
+ LOG(DEBUG, "New best NUMA placement candidate found: "
+ "nr_nodes=%d, nr_cpus=%d, nr_vcpus=%d, "
+ "free_memkb=%"PRIu64"", new_cndt.nr_nodes,
+ new_cndt.nr_cpus, new_cndt.nr_vcpus,
+ new_cndt.free_memkb / 1024);
+
+ libxl__numa_candidate_put_nodemap(gc, cndt_out, &nodemap);
+ cndt_out->nr_vcpus = new_cndt.nr_vcpus;
+ cndt_out->free_memkb = new_cndt.free_memkb;
+ cndt_out->nr_nodes = new_cndt.nr_nodes;
+ cndt_out->nr_cpus = new_cndt.nr_cpus;
+
+ if (numa_cmpf == NULL)
+ break;
+ }
+ }
+ min_nodes++;
+ }
+
+ if (*cndt_found == 0)
+ LOG(NOTICE, "NUMA placement failed, performance might be affected");
+
+ out:
+ libxl_bitmap_dispose(&nodemap);
+ libxl_bitmap_dispose(&suitable_nodemap);
+ libxl__numa_candidate_dispose(&new_cndt);
+ libxl_numainfo_list_free(ninfo, nr_nodes);
+ libxl_cputopology_list_free(tinfo, nr_cpus);
+ return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+/*
+ * This header must be included first, before any system headers,
+ * so that _GNU_SOURCE takes effect properly.
+ */
+
+#ifndef LIBXL_OSDEP
+#define LIBXL_OSDEP
+
+#define _GNU_SOURCE
+
+#if defined(__NetBSD__)
+#define SYSFS_USB_DEV "/sys/bus/usb/devices"
+#define SYSFS_USBBACK_DRIVER "/kern/xen/usb"
+#define SYSFS_PCI_DEV "/sys/bus/pci/devices"
+#define SYSFS_PCIBACK_DRIVER "/kern/xen/pci"
+#define NETBACK_NIC_NAME "xvif%ui%d"
+#include <util.h>
+#include <uuid.h>
+#elif defined(__OpenBSD__)
+#include <util.h>
+#elif defined(__linux__)
+#define SYSFS_USB_DEV "/sys/bus/usb/devices"
+#define SYSFS_USBBACK_DRIVER "/sys/bus/usb/drivers/usbback"
+#define SYSFS_PCI_DEV "/sys/bus/pci/devices"
+#define SYSFS_PCIBACK_DRIVER "/sys/bus/pci/drivers/pciback"
+#define NETBACK_NIC_NAME "vif%u.%d"
+#include <sys/sysmacros.h>
+#include <pty.h>
+#include <uuid/uuid.h>
+#elif defined(__sun__)
+#include <stropts.h>
+#elif defined(__FreeBSD__)
+#define SYSFS_USB_DEV "/dev/null"
+#define SYSFS_USBBACK_DRIVER "/dev/null"
+#define SYSFS_PCI_DEV "/dev/null"
+#define SYSFS_PCIBACK_DRIVER "/dev/null"
+#define NETBACK_NIC_NAME "xnb%u.%d"
+#include <libutil.h>
+#include <sys/endian.h>
+#include <uuid.h>
+/*
+ * FreeBSD doesn't have ENODATA errno ATM, so privcmd always translates
+ * ENODATA into ENOENT.
+ */
+#ifndef ENODATA
+#define ENODATA ENOENT
+#endif
+#endif
+
+#ifndef SYSFS_USBBACK_DRIVER
+#error define SYSFS_USBBACK_DRIVER for your platform
+#endif
+#ifndef SYSFS_USB_DEV
+#error define SYSFS_USB_DEV for your platform
+#endif
+
+#ifndef SYSFS_PCIBACK_DRIVER
+#error define SYSFS_PCIBACK_DRIVER for your platform
+#endif
+#ifndef SYSFS_PCI_DEV
+#error define SYSFS_PCI_DEV for your platform
+#endif
+
+#ifdef NEED_OWN_ASPRINTF
+#include <stdarg.h>
+
+int asprintf(char **buffer, char *fmt, ...);
+int vasprintf(char **buffer, const char *fmt, va_list ap);
+#endif /*NEED_OWN_ASPRINTF*/
+
+#ifndef htobe32 /* glibc < 2.9 */
+# include <byteswap.h>
+
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+# define htobe16(x) __bswap_16(x)
+# define htole16(x) (x)
+# define be16toh(x) __bswap_16(x)
+# define le16toh(x) (x)
+
+# define htobe32(x) __bswap_32(x)
+# define htole32(x) (x)
+# define be32toh(x) __bswap_32(x)
+# define le32toh(x) (x)
+
+# define htobe64(x) __bswap_64(x)
+# define htole64(x) (x)
+# define be64toh(x) __bswap_64(x)
+# define le64toh(x) (x)
+# else
+# define htobe16(x) (x)
+# define htole16(x) __bswap_16(x)
+# define be16toh(x) (x)
+# define le16toh(x) __bswap_16(x)
+
+# define htobe32(x) (x)
+# define htole32(x) __bswap_32(x)
+# define be32toh(x) (x)
+# define le32toh(x) __bswap_32(x)
+
+# define htobe64(x) (x)
+# define htole64(x) __bswap_64(x)
+# define be64toh(x) (x)
+# define le64toh(x) __bswap_64(x)
+# endif
+#endif
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2010 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+#include "libxl_internal.h"
+
+const char *libxl__private_bindir_path(void)
+{
+ return LIBEXEC_BIN;
+}
+
+const char *libxl__xenfirmwaredir_path(void)
+{
+ return XENFIRMWAREDIR;
+}
+
+const char *libxl__xen_script_dir_path(void)
+{
+ return XEN_SCRIPT_DIR;
+}
+
+const char *libxl__run_dir_path(void)
+{
+ return XEN_RUN_DIR;
+}
+
+const char *libxl__seabios_path(void)
+{
+#ifdef SEABIOS_PATH
+ return SEABIOS_PATH;
+#else
+ return NULL;
+#endif
+}
+
+const char *libxl__ovmf_path(void)
+{
+#ifdef OVMF_PATH
+ return OVMF_PATH;
+#else
+ return NULL;
+#endif
+}
+
+const char *libxl__ipxe_path(void)
+{
+#ifdef IPXE_PATH
+ return IPXE_PATH;
+#else
+ return NULL;
+#endif
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#define PCI_BDF "%04x:%02x:%02x.%01x"
+#define PCI_BDF_SHORT "%02x:%02x.%01x"
+#define PCI_BDF_VDEVFN "%04x:%02x:%02x.%01x@%02x"
+#define PCI_OPTIONS "msitranslate=%d,power_mgmt=%d"
+#define PCI_BDF_XSPATH "%04x-%02x-%02x-%01x"
+#define PCI_PT_QDEV_ID "pci-pt-%02x_%02x.%01x"
+
+static unsigned int pcidev_encode_bdf(libxl_device_pci *pcidev)
+{
+ unsigned int value;
+
+ value = pcidev->domain << 16;
+ value |= (pcidev->bus & 0xff) << 8;
+ value |= (pcidev->dev & 0x1f) << 3;
+ value |= (pcidev->func & 0x7);
+
+ return value;
+}
+
+static void pcidev_struct_fill(libxl_device_pci *pcidev, unsigned int domain,
+ unsigned int bus, unsigned int dev,
+ unsigned int func, unsigned int vdevfn)
+{
+ pcidev->domain = domain;
+ pcidev->bus = bus;
+ pcidev->dev = dev;
+ pcidev->func = func;
+ pcidev->vdevfn = vdevfn;
+}
+
+static void libxl_create_pci_backend_device(libxl__gc *gc,
+ flexarray_t *back,
+ int num,
+ const libxl_device_pci *pcidev)
+{
+ flexarray_append(back, GCSPRINTF("key-%d", num));
+ flexarray_append(back, GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
+ flexarray_append(back, GCSPRINTF("dev-%d", num));
+ flexarray_append(back, GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
+ if (pcidev->vdevfn)
+ flexarray_append_pair(back, GCSPRINTF("vdevfn-%d", num), GCSPRINTF("%x", pcidev->vdevfn));
+ flexarray_append(back, GCSPRINTF("opts-%d", num));
+ flexarray_append(back,
+ GCSPRINTF("msitranslate=%d,power_mgmt=%d,permissive=%d",
+ pcidev->msitranslate, pcidev->power_mgmt,
+ pcidev->permissive));
+ flexarray_append_pair(back, GCSPRINTF("state-%d", num), GCSPRINTF("%d", XenbusStateInitialising));
+}
+
+static void libxl__device_from_pcidev(libxl__gc *gc, uint32_t domid,
+ const libxl_device_pci *pcidev,
+ libxl__device *device)
+{
+ device->backend_devid = 0;
+ device->backend_domid = 0;
+ device->backend_kind = LIBXL__DEVICE_KIND_PCI;
+ device->devid = 0;
+ device->domid = domid;
+ device->kind = LIBXL__DEVICE_KIND_PCI;
+}
+
+static int libxl__create_pci_backend(libxl__gc *gc, uint32_t domid,
+ const libxl_device_pci *pcidev,
+ int num)
+{
+ flexarray_t *front = NULL;
+ flexarray_t *back = NULL;
+ libxl__device device;
+ int i;
+
+ front = flexarray_make(gc, 16, 1);
+ back = flexarray_make(gc, 16, 1);
+
+ LOGD(DEBUG, domid, "Creating pci backend");
+
+ /* add pci device */
+ libxl__device_from_pcidev(gc, domid, pcidev, &device);
+
+ flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
+ flexarray_append_pair(back, "online", "1");
+ flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateInitialising));
+ flexarray_append_pair(back, "domain", libxl__domid_to_name(gc, domid));
+
+ for (i = 0; i < num; i++, pcidev++)
+ libxl_create_pci_backend_device(gc, back, i, pcidev);
+
+ flexarray_append_pair(back, "num_devs", GCSPRINTF("%d", num));
+ flexarray_append_pair(front, "backend-id", GCSPRINTF("%d", 0));
+ flexarray_append_pair(front, "state", GCSPRINTF("%d", XenbusStateInitialising));
+
+ return libxl__device_generic_add(gc, XBT_NULL, &device,
+ libxl__xs_kvs_of_flexarray(gc, back),
+ libxl__xs_kvs_of_flexarray(gc, front),
+ NULL);
+}
+
+static int libxl__device_pci_add_xenstore(libxl__gc *gc,
+ uint32_t domid,
+ const libxl_device_pci *pcidev,
+ bool starting)
+{
+ flexarray_t *back;
+ char *num_devs, *be_path;
+ int num = 0;
+ xs_transaction_t t = XBT_NULL;
+ int rc;
+ libxl_domain_config d_config;
+ libxl__flock *lock = NULL;
+ bool is_stubdomain = libxl_is_stubdom(CTX, domid, NULL);
+
+ /* Stubdomain doesn't have own config. */
+ if (!is_stubdomain)
+ libxl_domain_config_init(&d_config);
+
+ be_path = libxl__domain_device_backend_path(gc, 0, domid, 0,
+ LIBXL__DEVICE_KIND_PCI);
+ num_devs = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/num_devs", be_path));
+ if (!num_devs)
+ return libxl__create_pci_backend(gc, domid, pcidev, 1);
+
+ libxl_domain_type domtype = libxl__domain_type(gc, domid);
+ if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
+ return ERROR_FAIL;
+
+ if (!starting && domtype == LIBXL_DOMAIN_TYPE_PV) {
+ if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0)
+ return ERROR_FAIL;
+ }
+
+ back = flexarray_make(gc, 16, 1);
+
+ LOGD(DEBUG, domid, "Adding new pci device to xenstore");
+ num = atoi(num_devs);
+ libxl_create_pci_backend_device(gc, back, num, pcidev);
+ flexarray_append_pair(back, "num_devs", GCSPRINTF("%d", num + 1));
+ if (!starting)
+ flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateReconfiguring));
+
+ /*
+ * Stubdomin config is derived from its target domain, it doesn't have
+ * its own file.
+ */
+ if (!is_stubdomain) {
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
+ device_add_domain_config(gc, &d_config, &libxl__pcidev_devtype,
+ pcidev);
+
+ rc = libxl__dm_check_start(gc, &d_config, domid);
+ if (rc) goto out;
+ }
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+
+ if (lock) {
+ rc = libxl__set_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+ }
+
+ libxl__xs_writev(gc, t, be_path, libxl__xs_kvs_of_flexarray(gc, back));
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc < 0) goto out;
+ }
+
+out:
+ libxl__xs_transaction_abort(gc, &t);
+ if (lock) libxl__unlock_file(lock);
+ if (!is_stubdomain)
+ libxl_domain_config_dispose(&d_config);
+ return rc;
+}
+
+static int libxl__device_pci_remove_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char *be_path, *num_devs_path, *num_devs, *xsdev, *tmp, *tmppath;
+ int num, i, j;
+ xs_transaction_t t;
+
+ be_path = libxl__domain_device_backend_path(gc, 0, domid, 0,
+ LIBXL__DEVICE_KIND_PCI);
+ num_devs_path = GCSPRINTF("%s/num_devs", be_path);
+ num_devs = libxl__xs_read(gc, XBT_NULL, num_devs_path);
+ if (!num_devs)
+ return ERROR_INVAL;
+ num = atoi(num_devs);
+
+ libxl_domain_type domtype = libxl__domain_type(gc, domid);
+ if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
+ return ERROR_FAIL;
+
+ if (domtype == LIBXL_DOMAIN_TYPE_PV) {
+ if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0) {
+ LOGD(DEBUG, domid, "pci backend at %s is not ready", be_path);
+ return ERROR_FAIL;
+ }
+ }
+
+ for (i = 0; i < num; i++) {
+ unsigned int domain = 0, bus = 0, dev = 0, func = 0;
+ xsdev = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/dev-%d", be_path, i));
+ sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func);
+ if (domain == pcidev->domain && bus == pcidev->bus &&
+ pcidev->dev == dev && pcidev->func == func) {
+ break;
+ }
+ }
+ if (i == num) {
+ LOGD(ERROR, domid, "Couldn't find the device on xenstore");
+ return ERROR_INVAL;
+ }
+
+retry_transaction:
+ t = xs_transaction_start(ctx->xsh);
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, i), GCSPRINTF("%d", XenbusStateClosing), 1);
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/state", be_path), GCSPRINTF("%d", XenbusStateReconfiguring), 1);
+ if (!xs_transaction_end(ctx->xsh, t, 0))
+ if (errno == EAGAIN)
+ goto retry_transaction;
+
+ if (domtype == LIBXL_DOMAIN_TYPE_PV) {
+ if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0) {
+ LOGD(DEBUG, domid, "pci backend at %s is not ready", be_path);
+ return ERROR_FAIL;
+ }
+ }
+
+retry_transaction2:
+ t = xs_transaction_start(ctx->xsh);
+ xs_rm(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, i));
+ xs_rm(ctx->xsh, t, GCSPRINTF("%s/key-%d", be_path, i));
+ xs_rm(ctx->xsh, t, GCSPRINTF("%s/dev-%d", be_path, i));
+ xs_rm(ctx->xsh, t, GCSPRINTF("%s/vdev-%d", be_path, i));
+ xs_rm(ctx->xsh, t, GCSPRINTF("%s/opts-%d", be_path, i));
+ xs_rm(ctx->xsh, t, GCSPRINTF("%s/vdevfn-%d", be_path, i));
+ libxl__xs_printf(gc, t, num_devs_path, "%d", num - 1);
+ for (j = i + 1; j < num; j++) {
+ tmppath = GCSPRINTF("%s/state-%d", be_path, j);
+ tmp = libxl__xs_read(gc, t, tmppath);
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, j - 1), tmp, strlen(tmp));
+ xs_rm(ctx->xsh, t, tmppath);
+ tmppath = GCSPRINTF("%s/dev-%d", be_path, j);
+ tmp = libxl__xs_read(gc, t, tmppath);
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/dev-%d", be_path, j - 1), tmp, strlen(tmp));
+ xs_rm(ctx->xsh, t, tmppath);
+ tmppath = GCSPRINTF("%s/key-%d", be_path, j);
+ tmp = libxl__xs_read(gc, t, tmppath);
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/key-%d", be_path, j - 1), tmp, strlen(tmp));
+ xs_rm(ctx->xsh, t, tmppath);
+ tmppath = GCSPRINTF("%s/vdev-%d", be_path, j);
+ tmp = libxl__xs_read(gc, t, tmppath);
+ if (tmp) {
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/vdev-%d", be_path, j - 1), tmp, strlen(tmp));
+ xs_rm(ctx->xsh, t, tmppath);
+ }
+ tmppath = GCSPRINTF("%s/opts-%d", be_path, j);
+ tmp = libxl__xs_read(gc, t, tmppath);
+ if (tmp) {
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/opts-%d", be_path, j - 1), tmp, strlen(tmp));
+ xs_rm(ctx->xsh, t, tmppath);
+ }
+ tmppath = GCSPRINTF("%s/vdevfn-%d", be_path, j);
+ tmp = libxl__xs_read(gc, t, tmppath);
+ if (tmp) {
+ xs_write(ctx->xsh, t, GCSPRINTF("%s/vdevfn-%d", be_path, j - 1), tmp, strlen(tmp));
+ xs_rm(ctx->xsh, t, tmppath);
+ }
+ }
+ if (!xs_transaction_end(ctx->xsh, t, 0))
+ if (errno == EAGAIN)
+ goto retry_transaction2;
+
+ if (num == 1) {
+ libxl__device dev;
+ if (libxl__parse_backend_path(gc, be_path, &dev) != 0)
+ return ERROR_FAIL;
+
+ dev.domid = domid;
+ dev.kind = LIBXL__DEVICE_KIND_PCI;
+ dev.devid = 0;
+
+ libxl__device_destroy(gc, &dev);
+ return 0;
+ }
+
+ return 0;
+}
+
+static int get_all_assigned_devices(libxl__gc *gc, libxl_device_pci **list, int *num)
+{
+ char **domlist;
+ unsigned int nd = 0, i;
+
+ *list = NULL;
+ *num = 0;
+
+ domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &nd);
+ for(i = 0; i < nd; i++) {
+ char *path, *num_devs;
+
+ path = GCSPRINTF("/local/domain/0/backend/%s/%s/0/num_devs",
+ libxl__device_kind_to_string(LIBXL__DEVICE_KIND_PCI),
+ domlist[i]);
+ num_devs = libxl__xs_read(gc, XBT_NULL, path);
+ if ( num_devs ) {
+ int ndev = atoi(num_devs), j;
+ char *devpath, *bdf;
+
+ for(j = 0; j < ndev; j++) {
+ devpath = GCSPRINTF("/local/domain/0/backend/%s/%s/0/dev-%u",
+ libxl__device_kind_to_string(LIBXL__DEVICE_KIND_PCI),
+ domlist[i], j);
+ bdf = libxl__xs_read(gc, XBT_NULL, devpath);
+ if ( bdf ) {
+ unsigned dom, bus, dev, func;
+ if ( sscanf(bdf, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
+ continue;
+
+ *list = realloc(*list, sizeof(libxl_device_pci) * ((*num) + 1));
+ if (*list == NULL)
+ return ERROR_NOMEM;
+ pcidev_struct_fill(*list + *num, dom, bus, dev, func, 0);
+ (*num)++;
+ }
+ }
+ }
+ }
+ libxl__ptr_add(gc, *list);
+
+ return 0;
+}
+
+static int is_pcidev_in_array(libxl_device_pci *assigned, int num_assigned,
+ int dom, int bus, int dev, int func)
+{
+ int i;
+
+ for(i = 0; i < num_assigned; i++) {
+ if ( assigned[i].domain != dom )
+ continue;
+ if ( assigned[i].bus != bus )
+ continue;
+ if ( assigned[i].dev != dev )
+ continue;
+ if ( assigned[i].func != func )
+ continue;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Write the standard BDF into the sysfs path given by sysfs_path. */
+static int sysfs_write_bdf(libxl__gc *gc, const char * sysfs_path,
+ libxl_device_pci *pcidev)
+{
+ int rc, fd;
+ char *buf;
+
+ fd = open(sysfs_path, O_WRONLY);
+ if (fd < 0) {
+ LOGE(ERROR, "Couldn't open %s", sysfs_path);
+ return ERROR_FAIL;
+ }
+
+ buf = GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus,
+ pcidev->dev, pcidev->func);
+ rc = write(fd, buf, strlen(buf));
+ /* Annoying to have two if's, but we need the errno */
+ if (rc < 0)
+ LOGE(ERROR, "write to %s returned %d", sysfs_path, rc);
+ close(fd);
+
+ if (rc < 0)
+ return ERROR_FAIL;
+
+ return 0;
+}
+
+libxl_device_pci *libxl_device_pci_assignable_list(libxl_ctx *ctx, int *num)
+{
+ GC_INIT(ctx);
+ libxl_device_pci *pcidevs = NULL, *new, *assigned;
+ struct dirent *de;
+ DIR *dir;
+ int r, num_assigned;
+
+ *num = 0;
+
+ r = get_all_assigned_devices(gc, &assigned, &num_assigned);
+ if (r) goto out;
+
+ dir = opendir(SYSFS_PCIBACK_DRIVER);
+ if (NULL == dir) {
+ if (errno == ENOENT) {
+ LOG(ERROR, "Looks like pciback driver not loaded");
+ } else {
+ LOGE(ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER);
+ }
+ goto out;
+ }
+
+ while((de = readdir(dir))) {
+ unsigned dom, bus, dev, func;
+ if (sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4)
+ continue;
+
+ if (is_pcidev_in_array(assigned, num_assigned, dom, bus, dev, func))
+ continue;
+
+ new = realloc(pcidevs, ((*num) + 1) * sizeof(*new));
+ if (NULL == new)
+ continue;
+
+ pcidevs = new;
+ new = pcidevs + *num;
+
+ memset(new, 0, sizeof(*new));
+ pcidev_struct_fill(new, dom, bus, dev, func, 0);
+ (*num)++;
+ }
+
+ closedir(dir);
+out:
+ GC_FREE;
+ return pcidevs;
+}
+
+/* Unbind device from its current driver, if any. If driver_path is non-NULL,
+ * store the path to the original driver in it. */
+static int sysfs_dev_unbind(libxl__gc *gc, libxl_device_pci *pcidev,
+ char **driver_path)
+{
+ char * spath, *dp = NULL;
+ struct stat st;
+
+ spath = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/driver",
+ pcidev->domain,
+ pcidev->bus,
+ pcidev->dev,
+ pcidev->func);
+ if ( !lstat(spath, &st) ) {
+ /* Find the canonical path to the driver. */
+ dp = libxl__zalloc(gc, PATH_MAX);
+ dp = realpath(spath, dp);
+ if ( !dp ) {
+ LOGE(ERROR, "realpath() failed");
+ return -1;
+ }
+
+ LOG(DEBUG, "Driver re-plug path: %s", dp);
+
+ /* Unbind from the old driver */
+ spath = GCSPRINTF("%s/unbind", dp);
+ if ( sysfs_write_bdf(gc, spath, pcidev) < 0 ) {
+ LOGE(ERROR, "Couldn't unbind device");
+ return -1;
+ }
+ }
+
+ if ( driver_path )
+ *driver_path = dp;
+
+ return 0;
+}
+
+static uint16_t sysfs_dev_get_vendor(libxl__gc *gc, libxl_device_pci *pcidev)
+{
+ char *pci_device_vendor_path =
+ GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/vendor",
+ pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+ uint16_t read_items;
+ uint16_t pci_device_vendor;
+
+ FILE *f = fopen(pci_device_vendor_path, "r");
+ if (!f) {
+ LOGE(ERROR,
+ "pci device "PCI_BDF" does not have vendor attribute",
+ pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+ return 0xffff;
+ }
+ read_items = fscanf(f, "0x%hx\n", &pci_device_vendor);
+ fclose(f);
+ if (read_items != 1) {
+ LOGE(ERROR,
+ "cannot read vendor of pci device "PCI_BDF,
+ pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+ return 0xffff;
+ }
+
+ return pci_device_vendor;
+}
+
+static uint16_t sysfs_dev_get_device(libxl__gc *gc, libxl_device_pci *pcidev)
+{
+ char *pci_device_device_path =
+ GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/device",
+ pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+ uint16_t read_items;
+ uint16_t pci_device_device;
+
+ FILE *f = fopen(pci_device_device_path, "r");
+ if (!f) {
+ LOGE(ERROR,
+ "pci device "PCI_BDF" does not have device attribute",
+ pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+ return 0xffff;
+ }
+ read_items = fscanf(f, "0x%hx\n", &pci_device_device);
+ fclose(f);
+ if (read_items != 1) {
+ LOGE(ERROR,
+ "cannot read device of pci device "PCI_BDF,
+ pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+ return 0xffff;
+ }
+
+ return pci_device_device;
+}
+
+static int sysfs_dev_get_class(libxl__gc *gc, libxl_device_pci *pcidev,
+ unsigned long *class)
+{
+ char *pci_device_class_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/class",
+ pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+ int read_items, ret = 0;
+
+ FILE *f = fopen(pci_device_class_path, "r");
+ if (!f) {
+ LOGE(ERROR,
+ "pci device "PCI_BDF" does not have class attribute",
+ pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+ ret = ERROR_FAIL;
+ goto out;
+ }
+ read_items = fscanf(f, "0x%lx\n", class);
+ fclose(f);
+ if (read_items != 1) {
+ LOGE(ERROR,
+ "cannot read class of pci device "PCI_BDF,
+ pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+ ret = ERROR_FAIL;
+ }
+
+out:
+ return ret;
+}
+
+/*
+ * Some devices may need some ways to work well. Here like IGD,
+ * we have to pass a specific option to qemu.
+ */
+bool libxl__is_igd_vga_passthru(libxl__gc *gc,
+ const libxl_domain_config *d_config)
+{
+ unsigned int i;
+ uint16_t pt_vendor, pt_device;
+ unsigned long class;
+
+ for (i = 0 ; i < d_config->num_pcidevs ; i++) {
+ libxl_device_pci *pcidev = &d_config->pcidevs[i];
+ pt_vendor = sysfs_dev_get_vendor(gc, pcidev);
+ pt_device = sysfs_dev_get_device(gc, pcidev);
+
+ if (pt_vendor == 0xffff || pt_device == 0xffff ||
+ pt_vendor != 0x8086)
+ continue;
+
+ if (sysfs_dev_get_class(gc, pcidev, &class))
+ continue;
+ if (class == 0x030000)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * A brief comment about slots. I don't know what slots are for; however,
+ * I have by experimentation determined:
+ * - Before a device can be bound to pciback, its BDF must first be listed
+ * in pciback/slots
+ * - The way to get the BDF listed there is to write BDF to
+ * pciback/new_slot
+ * - Writing the same BDF to pciback/new_slot is not idempotent; it results
+ * in two entries of the BDF in pciback/slots
+ * It's not clear whether having two entries in pciback/slots is a problem
+ * or not. Just to be safe, this code does the conservative thing, and
+ * first checks to see if there is a slot, adding one only if one does not
+ * already exist.
+ */
+
+/* Scan through /sys/.../pciback/slots looking for pcidev's BDF */
+static int pciback_dev_has_slot(libxl__gc *gc, libxl_device_pci *pcidev)
+{
+ FILE *f;
+ int rc = 0;
+ unsigned dom, bus, dev, func;
+
+ f = fopen(SYSFS_PCIBACK_DRIVER"/slots", "r");
+
+ if (f == NULL) {
+ LOGE(ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER"/slots");
+ return ERROR_FAIL;
+ }
+
+ while(fscanf(f, "%x:%x:%x.%d\n", &dom, &bus, &dev, &func)==4) {
+ if(dom == pcidev->domain
+ && bus == pcidev->bus
+ && dev == pcidev->dev
+ && func == pcidev->func) {
+ rc = 1;
+ goto out;
+ }
+ }
+out:
+ fclose(f);
+ return rc;
+}
+
+static int pciback_dev_is_assigned(libxl__gc *gc, libxl_device_pci *pcidev)
+{
+ char * spath;
+ int rc;
+ struct stat st;
+
+ if ( access(SYSFS_PCIBACK_DRIVER, F_OK) < 0 ) {
+ if ( errno == ENOENT ) {
+ LOG(ERROR, "Looks like pciback driver is not loaded");
+ } else {
+ LOGE(ERROR, "Can't access "SYSFS_PCIBACK_DRIVER);
+ }
+ return -1;
+ }
+
+ spath = GCSPRINTF(SYSFS_PCIBACK_DRIVER"/"PCI_BDF,
+ pcidev->domain, pcidev->bus,
+ pcidev->dev, pcidev->func);
+ rc = lstat(spath, &st);
+
+ if( rc == 0 )
+ return 1;
+ if ( rc < 0 && errno == ENOENT )
+ return 0;
+ LOGE(ERROR, "Accessing %s", spath);
+ return -1;
+}
+
+static int pciback_dev_assign(libxl__gc *gc, libxl_device_pci *pcidev)
+{
+ int rc;
+
+ if ( (rc=pciback_dev_has_slot(gc, pcidev)) < 0 ) {
+ LOGE(ERROR, "Error checking for pciback slot");
+ return ERROR_FAIL;
+ } else if (rc == 0) {
+ if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/new_slot",
+ pcidev) < 0 ) {
+ LOGE(ERROR, "Couldn't bind device to pciback!");
+ return ERROR_FAIL;
+ }
+ }
+
+ if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/bind", pcidev) < 0 ) {
+ LOGE(ERROR, "Couldn't bind device to pciback!");
+ return ERROR_FAIL;
+ }
+ return 0;
+}
+
+static int pciback_dev_unassign(libxl__gc *gc, libxl_device_pci *pcidev)
+{
+ /* Remove from pciback */
+ if ( sysfs_dev_unbind(gc, pcidev, NULL) < 0 ) {
+ LOG(ERROR, "Couldn't unbind device!");
+ return ERROR_FAIL;
+ }
+
+ /* Remove slot if necessary */
+ if ( pciback_dev_has_slot(gc, pcidev) > 0 ) {
+ if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/remove_slot",
+ pcidev) < 0 ) {
+ LOGE(ERROR, "Couldn't remove pciback slot");
+ return ERROR_FAIL;
+ }
+ }
+ return 0;
+}
+
+#define PCIBACK_INFO_PATH "/libxl/pciback"
+
+static void pci_assignable_driver_path_write(libxl__gc *gc,
+ libxl_device_pci *pcidev,
+ char *driver_path)
+{
+ char *path;
+
+ path = GCSPRINTF(PCIBACK_INFO_PATH"/"PCI_BDF_XSPATH"/driver_path",
+ pcidev->domain,
+ pcidev->bus,
+ pcidev->dev,
+ pcidev->func);
+ if ( libxl__xs_printf(gc, XBT_NULL, path, "%s", driver_path) < 0 ) {
+ LOGE(WARN, "Write of %s to node %s failed.", driver_path, path);
+ }
+}
+
+static char * pci_assignable_driver_path_read(libxl__gc *gc,
+ libxl_device_pci *pcidev)
+{
+ return libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF(
+ PCIBACK_INFO_PATH "/" PCI_BDF_XSPATH "/driver_path",
+ pcidev->domain,
+ pcidev->bus,
+ pcidev->dev,
+ pcidev->func));
+}
+
+static void pci_assignable_driver_path_remove(libxl__gc *gc,
+ libxl_device_pci *pcidev)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+
+ /* Remove the xenstore entry */
+ xs_rm(ctx->xsh, XBT_NULL,
+ GCSPRINTF(PCIBACK_INFO_PATH "/" PCI_BDF_XSPATH,
+ pcidev->domain,
+ pcidev->bus,
+ pcidev->dev,
+ pcidev->func) );
+}
+
+static int libxl__device_pci_assignable_add(libxl__gc *gc,
+ libxl_device_pci *pcidev,
+ int rebind)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ unsigned dom, bus, dev, func;
+ char *spath, *driver_path = NULL;
+ int rc;
+ struct stat st;
+
+ /* Local copy for convenience */
+ dom = pcidev->domain;
+ bus = pcidev->bus;
+ dev = pcidev->dev;
+ func = pcidev->func;
+
+ /* See if the device exists */
+ spath = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF, dom, bus, dev, func);
+ if ( lstat(spath, &st) ) {
+ LOGE(ERROR, "Couldn't lstat %s", spath);
+ return ERROR_FAIL;
+ }
+
+ /* Check to see if it's already assigned to pciback */
+ rc = pciback_dev_is_assigned(gc, pcidev);
+ if ( rc < 0 ) {
+ return ERROR_FAIL;
+ }
+ if ( rc ) {
+ LOG(WARN, PCI_BDF" already assigned to pciback", dom, bus, dev, func);
+ goto quarantine;
+ }
+
+ /* Check to see if there's already a driver that we need to unbind from */
+ if ( sysfs_dev_unbind(gc, pcidev, &driver_path ) ) {
+ LOG(ERROR, "Couldn't unbind "PCI_BDF" from driver",
+ dom, bus, dev, func);
+ return ERROR_FAIL;
+ }
+
+ /* Store driver_path for rebinding to dom0 */
+ if ( rebind ) {
+ if ( driver_path ) {
+ pci_assignable_driver_path_write(gc, pcidev, driver_path);
+ } else if ( (driver_path =
+ pci_assignable_driver_path_read(gc, pcidev)) != NULL ) {
+ LOG(INFO, PCI_BDF" not bound to a driver, will be rebound to %s",
+ dom, bus, dev, func, driver_path);
+ } else {
+ LOG(WARN, PCI_BDF" not bound to a driver, will not be rebound.",
+ dom, bus, dev, func);
+ }
+ } else {
+ pci_assignable_driver_path_remove(gc, pcidev);
+ }
+
+ if ( pciback_dev_assign(gc, pcidev) ) {
+ LOG(ERROR, "Couldn't bind device to pciback!");
+ return ERROR_FAIL;
+ }
+
+quarantine:
+ /*
+ * DOMID_IO is just a sentinel domain, without any actual mappings,
+ * so always pass XEN_DOMCTL_DEV_RDM_RELAXED to avoid assignment being
+ * unnecessarily denied.
+ */
+ rc = xc_assign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev),
+ XEN_DOMCTL_DEV_RDM_RELAXED);
+ if ( rc < 0 ) {
+ LOG(ERROR, "failed to quarantine "PCI_BDF, dom, bus, dev, func);
+ return ERROR_FAIL;
+ }
+
+ return 0;
+}
+
+static int libxl__device_pci_assignable_remove(libxl__gc *gc,
+ libxl_device_pci *pcidev,
+ int rebind)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ int rc;
+ char *driver_path;
+
+ /* De-quarantine */
+ rc = xc_deassign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev));
+ if ( rc < 0 ) {
+ LOG(ERROR, "failed to de-quarantine "PCI_BDF, pcidev->domain, pcidev->bus,
+ pcidev->dev, pcidev->func);
+ return ERROR_FAIL;
+ }
+
+ /* Unbind from pciback */
+ if ( (rc=pciback_dev_is_assigned(gc, pcidev)) < 0 ) {
+ return ERROR_FAIL;
+ } else if ( rc ) {
+ pciback_dev_unassign(gc, pcidev);
+ } else {
+ LOG(WARN, "Not bound to pciback");
+ }
+
+ /* Rebind if necessary */
+ driver_path = pci_assignable_driver_path_read(gc, pcidev);
+
+ if ( driver_path ) {
+ if ( rebind ) {
+ LOG(INFO, "Rebinding to driver at %s", driver_path);
+
+ if ( sysfs_write_bdf(gc,
+ GCSPRINTF("%s/bind", driver_path),
+ pcidev) < 0 ) {
+ LOGE(ERROR, "Couldn't bind device to %s", driver_path);
+ return -1;
+ }
+
+ pci_assignable_driver_path_remove(gc, pcidev);
+ }
+ } else {
+ if ( rebind ) {
+ LOG(WARN,
+ "Couldn't find path for original driver; not rebinding");
+ }
+ }
+
+ return 0;
+}
+
+int libxl_device_pci_assignable_add(libxl_ctx *ctx, libxl_device_pci *pcidev,
+ int rebind)
+{
+ GC_INIT(ctx);
+ int rc;
+
+ rc = libxl__device_pci_assignable_add(gc, pcidev, rebind);
+
+ GC_FREE;
+ return rc;
+}
+
+
+int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci *pcidev,
+ int rebind)
+{
+ GC_INIT(ctx);
+ int rc;
+
+ rc = libxl__device_pci_assignable_remove(gc, pcidev, rebind);
+
+ GC_FREE;
+ return rc;
+}
+
+/*
+ * This function checks that all functions of a device are bound to pciback
+ * driver. It also initialises a bit-mask of which function numbers are present
+ * on that device.
+*/
+static int pci_multifunction_check(libxl__gc *gc, libxl_device_pci *pcidev, unsigned int *func_mask)
+{
+ struct dirent *de;
+ DIR *dir;
+
+ *func_mask = 0;
+
+ dir = opendir(SYSFS_PCI_DEV);
+ if ( NULL == dir ) {
+ LOGE(ERROR, "Couldn't open %s", SYSFS_PCI_DEV);
+ return -1;
+ }
+
+ while( (de = readdir(dir)) ) {
+ unsigned dom, bus, dev, func;
+ struct stat st;
+ char *path;
+
+ if ( sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
+ continue;
+ if ( pcidev->domain != dom )
+ continue;
+ if ( pcidev->bus != bus )
+ continue;
+ if ( pcidev->dev != dev )
+ continue;
+
+ path = GCSPRINTF("%s/" PCI_BDF, SYSFS_PCIBACK_DRIVER, dom, bus, dev, func);
+ if ( lstat(path, &st) ) {
+ if ( errno == ENOENT )
+ LOG(ERROR, PCI_BDF " is not assigned to pciback driver",
+ dom, bus, dev, func);
+ else
+ LOGE(ERROR, "Couldn't lstat %s", path);
+ closedir(dir);
+ return -1;
+ }
+ (*func_mask) |= (1 << func);
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+static int pci_ins_check(libxl__gc *gc, uint32_t domid, const char *state, void *priv)
+{
+ char *orig_state = priv;
+
+ if ( !strcmp(state, "pci-insert-failed") )
+ return -1;
+ if ( !strcmp(state, "pci-inserted") )
+ return 0;
+ if ( !strcmp(state, orig_state) )
+ return 1;
+
+ return 1;
+}
+
+static int qemu_pci_add_xenstore(libxl__gc *gc, uint32_t domid,
+ libxl_device_pci *pcidev)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ int rc = 0;
+ char *path;
+ char *state, *vdevfn;
+ uint32_t dm_domid;
+
+ dm_domid = libxl_get_stubdom_id(CTX, domid);
+ path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+ state = libxl__xs_read(gc, XBT_NULL, path);
+ path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter");
+ if (pcidev->vdevfn) {
+ libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF_VDEVFN","PCI_OPTIONS,
+ pcidev->domain, pcidev->bus, pcidev->dev,
+ pcidev->func, pcidev->vdevfn, pcidev->msitranslate,
+ pcidev->power_mgmt);
+ } else {
+ libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF","PCI_OPTIONS,
+ pcidev->domain, pcidev->bus, pcidev->dev,
+ pcidev->func, pcidev->msitranslate, pcidev->power_mgmt);
+ }
+
+ libxl__qemu_traditional_cmd(gc, domid, "pci-ins");
+ rc = libxl__wait_for_device_model_deprecated(gc, domid, NULL, NULL,
+ pci_ins_check, state);
+ path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter");
+ vdevfn = libxl__xs_read(gc, XBT_NULL, path);
+ path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+ if ( rc < 0 )
+ LOGD(ERROR, domid, "qemu refused to add device: %s", vdevfn);
+ else if ( sscanf(vdevfn, "0x%x", &pcidev->vdevfn) != 1 ) {
+ LOGD(ERROR, domid, "wrong format for the vdevfn: '%s'", vdevfn);
+ rc = -1;
+ }
+ xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
+
+ return rc;
+}
+
+static int check_qemu_running(libxl__gc *gc,
+ libxl_domid domid,
+ libxl__xswait_state *xswa,
+ int rc,
+ const char *state)
+{
+ if (rc) {
+ if (rc == ERROR_TIMEDOUT) {
+ LOGD(ERROR, domid, "%s not ready", xswa->what);
+ }
+ goto out;
+ }
+
+ if (!state || strcmp(state, "running"))
+ return ERROR_NOT_READY;
+
+out:
+ libxl__xswait_stop(gc, xswa);
+ return rc;
+}
+
+typedef struct pci_add_state {
+ /* filled by user of do_pci_add */
+ libxl__ao_device *aodev;
+ libxl_domid domid;
+ bool starting;
+ void (*callback)(libxl__egc *, struct pci_add_state *, int rc);
+
+ /* private to device_pci_add_stubdom_wait */
+ libxl__ev_devstate pciback_ds;
+
+ /* private to do_pci_add */
+ libxl__xswait_state xswait;
+ libxl__ev_qmp qmp;
+ libxl__ev_time timeout;
+ libxl_device_pci *pcidev;
+ int pci_domid;
+} pci_add_state;
+
+static void pci_add_qemu_trad_watch_state_cb(libxl__egc *egc,
+ libxl__xswait_state *xswa, int rc, const char *state);
+static void pci_add_qmp_device_add(libxl__egc *, pci_add_state *);
+static void pci_add_qmp_device_add_cb(libxl__egc *,
+ libxl__ev_qmp *, const libxl__json_object *, int rc);
+static void pci_add_qmp_query_pci_cb(libxl__egc *,
+ libxl__ev_qmp *, const libxl__json_object *, int rc);
+static void pci_add_timeout(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs, int rc);
+static void pci_add_dm_done(libxl__egc *,
+ pci_add_state *, int rc);
+
+static void do_pci_add(libxl__egc *egc,
+ libxl_domid domid,
+ libxl_device_pci *pcidev,
+ pci_add_state *pas)
+{
+ STATE_AO_GC(pas->aodev->ao);
+ libxl_domain_type type = libxl__domain_type(gc, domid);
+ int rc;
+
+ /* init pci_add_state */
+ libxl__xswait_init(&pas->xswait);
+ libxl__ev_qmp_init(&pas->qmp);
+ pas->pcidev = pcidev;
+ pas->pci_domid = domid;
+ libxl__ev_time_init(&pas->timeout);
+
+ if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (type == LIBXL_DOMAIN_TYPE_HVM) {
+ switch (libxl__device_model_version_running(gc, domid)) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ pas->xswait.ao = ao;
+ pas->xswait.what = "Device Model";
+ pas->xswait.path = DEVICE_MODEL_XS_PATH(gc,
+ libxl_get_stubdom_id(CTX, domid), domid, "/state");
+ pas->xswait.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
+ pas->xswait.callback = pci_add_qemu_trad_watch_state_cb;
+ rc = libxl__xswait_start(gc, &pas->xswait);
+ if (rc) goto out;
+ return;
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ pci_add_qmp_device_add(egc, pas); /* must be last */
+ return;
+ default:
+ rc = ERROR_INVAL;
+ break;
+ }
+ }
+
+ rc = 0;
+
+out:
+ pci_add_dm_done(egc, pas, rc); /* must be last */
+}
+
+static void pci_add_qemu_trad_watch_state_cb(libxl__egc *egc,
+ libxl__xswait_state *xswa,
+ int rc,
+ const char *state)
+{
+ pci_add_state *pas = CONTAINER_OF(xswa, *pas, xswait);
+ STATE_AO_GC(pas->aodev->ao);
+
+ /* Convenience aliases */
+ libxl_domid domid = pas->domid;
+ libxl_device_pci *pcidev = pas->pcidev;
+
+ rc = check_qemu_running(gc, domid, xswa, rc, state);
+ if (rc == ERROR_NOT_READY)
+ return;
+ if (rc)
+ goto out;
+
+ rc = qemu_pci_add_xenstore(gc, domid, pcidev);
+out:
+ pci_add_dm_done(egc, pas, rc); /* must be last */
+}
+
+static void pci_add_qmp_device_add(libxl__egc *egc, pci_add_state *pas)
+{
+ STATE_AO_GC(pas->aodev->ao);
+ libxl__json_object *args = NULL;
+ int rc;
+
+ /* Convenience aliases */
+ libxl_domid domid = pas->domid;
+ libxl_device_pci *pcidev = pas->pcidev;
+ libxl__ev_qmp *const qmp = &pas->qmp;
+
+ rc = libxl__ev_time_register_rel(ao, &pas->timeout,
+ pci_add_timeout,
+ LIBXL_QMP_CMD_TIMEOUT * 1000);
+ if (rc) goto out;
+
+ libxl__qmp_param_add_string(gc, &args, "driver",
+ "xen-pci-passthrough");
+ QMP_PARAMETERS_SPRINTF(&args, "id", PCI_PT_QDEV_ID,
+ pcidev->bus, pcidev->dev, pcidev->func);
+ QMP_PARAMETERS_SPRINTF(&args, "hostaddr",
+ "%04x:%02x:%02x.%01x", pcidev->domain,
+ pcidev->bus, pcidev->dev, pcidev->func);
+ if (pcidev->vdevfn) {
+ QMP_PARAMETERS_SPRINTF(&args, "addr", "%x.%x",
+ PCI_SLOT(pcidev->vdevfn),
+ PCI_FUNC(pcidev->vdevfn));
+ }
+ /*
+ * Version of QEMU prior to the XSA-131 fix did not support
+ * this property and were effectively always in permissive
+ * mode. The fix for XSA-131 switched the default to be
+ * restricted by default and added the permissive property.
+ *
+ * Therefore in order to support both old and new QEMU we only
+ * set the permissive flag if it is true. Users of older QEMU
+ * have no reason to set the flag so this is ok.
+ */
+ if (pcidev->permissive)
+ libxl__qmp_param_add_bool(gc, &args, "permissive", true);
+
+ qmp->ao = pas->aodev->ao;
+ qmp->domid = domid;
+ qmp->payload_fd = -1;
+ qmp->callback = pci_add_qmp_device_add_cb;
+ rc = libxl__ev_qmp_send(egc, qmp, "device_add", args);
+ if (rc) goto out;
+ return;
+
+out:
+ pci_add_dm_done(egc, pas, rc); /* must be last */
+}
+
+static void pci_add_qmp_device_add_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *response,
+ int rc)
+{
+ EGC_GC;
+ pci_add_state *pas = CONTAINER_OF(qmp, *pas, qmp);
+
+ if (rc) goto out;
+
+ qmp->callback = pci_add_qmp_query_pci_cb;
+ rc = libxl__ev_qmp_send(egc, qmp, "query-pci", NULL);
+ if (rc) goto out;
+ return;
+
+out:
+ pci_add_dm_done(egc, pas, rc); /* must be last */
+}
+
+static void pci_add_qmp_query_pci_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *response,
+ int rc)
+{
+ EGC_GC;
+ pci_add_state *pas = CONTAINER_OF(qmp, *pas, qmp);
+ const libxl__json_object *bus = NULL;
+ char *asked_id;
+ int i, j;
+ const libxl__json_object *devices = NULL;
+ const libxl__json_object *device = NULL;
+ const libxl__json_object *o = NULL;
+ const char *id = NULL;
+ int dev_slot, dev_func;
+
+ /* Convenience aliases */
+ libxl_device_pci *pcidev = pas->pcidev;
+
+ if (rc) goto out;
+
+ /* `query-pci' returns:
+ * [
+ * {'bus': 'int',
+ * 'devices': [
+ * {'bus': 'int', 'slot': 'int', 'function': 'int',
+ * 'class_info': 'PciDeviceClass', 'id': 'PciDeviceId',
+ * '*irq': 'int', 'qdev_id': 'str',
+ * '*pci_bridge': 'PciBridgeInfo',
+ * 'regions': ['PciMemoryRegion']
+ * }
+ * ]
+ * }
+ * ]
+ * (See qemu.git/qapi/ for the struct that aren't detailed here)
+ */
+
+ asked_id = GCSPRINTF(PCI_PT_QDEV_ID,
+ pcidev->bus, pcidev->dev, pcidev->func);
+
+ for (i = 0; (bus = libxl__json_array_get(response, i)); i++) {
+ devices = libxl__json_map_get("devices", bus, JSON_ARRAY);
+ if (!devices) {
+ rc = ERROR_QEMU_API;
+ goto out;
+ }
+
+ for (j = 0; (device = libxl__json_array_get(devices, j)); j++) {
+ o = libxl__json_map_get("qdev_id", device, JSON_STRING);
+ if (!o) {
+ rc = ERROR_QEMU_API;
+ goto out;
+ }
+ id = libxl__json_object_get_string(o);
+ if (!id || strcmp(asked_id, id))
+ continue;
+
+ o = libxl__json_map_get("slot", device, JSON_INTEGER);
+ if (!o) {
+ rc = ERROR_QEMU_API;
+ goto out;
+ }
+ dev_slot = libxl__json_object_get_integer(o);
+ o = libxl__json_map_get("function", device, JSON_INTEGER);
+ if (!o) {
+ rc = ERROR_QEMU_API;
+ goto out;
+ }
+ dev_func = libxl__json_object_get_integer(o);
+
+ pcidev->vdevfn = PCI_DEVFN(dev_slot, dev_func);
+
+ rc = 0;
+ goto out;
+ }
+ }
+
+ rc = ERROR_FAIL;
+ LOGD(ERROR, qmp->domid,
+ "PCI device id '%s' wasn't found in QEMU's 'query-pci' response.",
+ asked_id);
+
+out:
+ if (rc == ERROR_QEMU_API) {
+ LOGD(ERROR, qmp->domid,
+ "Unexpected response to QMP cmd 'query-pci', received:\n%s",
+ JSON(response));
+ }
+ pci_add_dm_done(egc, pas, rc); /* must be last */
+}
+
+static void pci_add_timeout(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ pci_add_state *pas = CONTAINER_OF(ev, *pas, timeout);
+
+ pci_add_dm_done(egc, pas, rc);
+}
+
+static void pci_add_dm_done(libxl__egc *egc,
+ pci_add_state *pas,
+ int rc)
+{
+ STATE_AO_GC(pas->aodev->ao);
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ libxl_domid domid = pas->pci_domid;
+ char *sysfs_path;
+ FILE *f;
+ unsigned long long start, end, flags, size;
+ int irq, i;
+ int r;
+ uint32_t flag = XEN_DOMCTL_DEV_RDM_RELAXED;
+ uint32_t domainid = domid;
+ bool isstubdom = libxl_is_stubdom(ctx, domid, &domainid);
+
+ /* Convenience aliases */
+ bool starting = pas->starting;
+ libxl_device_pci *pcidev = pas->pcidev;
+ bool hvm = libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM;
+
+ libxl__ev_qmp_dispose(gc, &pas->qmp);
+
+ if (rc) goto out;
+
+ /* stubdomain is always running by now, even at create time */
+ if (isstubdom)
+ starting = false;
+
+ sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
+ pcidev->bus, pcidev->dev, pcidev->func);
+ f = fopen(sysfs_path, "r");
+ start = end = flags = size = 0;
+ irq = 0;
+
+ if (f == NULL) {
+ LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
+ if (fscanf(f, "0x%llx 0x%llx 0x%llx\n", &start, &end, &flags) != 3)
+ continue;
+ size = end - start + 1;
+ if (start) {
+ if (flags & PCI_BAR_IO) {
+ r = xc_domain_ioport_permission(ctx->xch, domid, start, size, 1);
+ if (r < 0) {
+ LOGED(ERROR, domainid,
+ "xc_domain_ioport_permission 0x%llx/0x%llx (error %d)",
+ start, size, r);
+ fclose(f);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ } else {
+ r = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
+ (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 1);
+ if (r < 0) {
+ LOGED(ERROR, domainid,
+ "xc_domain_iomem_permission 0x%llx/0x%llx (error %d)",
+ start, size, r);
+ fclose(f);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+ }
+ }
+ fclose(f);
+ sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
+ pcidev->bus, pcidev->dev, pcidev->func);
+ f = fopen(sysfs_path, "r");
+ if (f == NULL) {
+ LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
+ goto out_no_irq;
+ }
+ if ((fscanf(f, "%u", &irq) == 1) && irq) {
+ r = xc_physdev_map_pirq(ctx->xch, domid, irq, &irq);
+ if (r < 0) {
+ LOGED(ERROR, domainid, "xc_physdev_map_pirq irq=%d (error=%d)",
+ irq, r);
+ fclose(f);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ r = xc_domain_irq_permission(ctx->xch, domid, irq, 1);
+ if (r < 0) {
+ LOGED(ERROR, domainid,
+ "xc_domain_irq_permission irq=%d (error=%d)", irq, r);
+ fclose(f);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+ fclose(f);
+
+ /* Don't restrict writes to the PCI config space from this VM */
+ if (pcidev->permissive) {
+ if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/permissive",
+ pcidev) < 0 ) {
+ LOGD(ERROR, domainid, "Setting permissive for device");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+out_no_irq:
+ if (!isstubdom) {
+ if (pcidev->rdm_policy == LIBXL_RDM_RESERVE_POLICY_STRICT) {
+ flag &= ~XEN_DOMCTL_DEV_RDM_RELAXED;
+ } else if (pcidev->rdm_policy != LIBXL_RDM_RESERVE_POLICY_RELAXED) {
+ LOGED(ERROR, domainid, "unknown rdm check flag.");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ r = xc_assign_device(ctx->xch, domid, pcidev_encode_bdf(pcidev), flag);
+ if (r < 0 && (hvm || errno != ENOSYS)) {
+ LOGED(ERROR, domainid, "xc_assign_device failed");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ if (!starting && !libxl_get_stubdom_id(CTX, domid))
+ rc = libxl__device_pci_add_xenstore(gc, domid, pcidev, starting);
+ else
+ rc = 0;
+out:
+ libxl__ev_time_deregister(gc, &pas->timeout);
+ pas->callback(egc, pas, rc);
+}
+
+static int libxl__device_pci_reset(libxl__gc *gc, unsigned int domain, unsigned int bus,
+ unsigned int dev, unsigned int func)
+{
+ char *reset;
+ int fd, rc;
+
+ reset = GCSPRINTF("%s/do_flr", SYSFS_PCIBACK_DRIVER);
+ fd = open(reset, O_WRONLY);
+ if (fd >= 0) {
+ char *buf = GCSPRINTF(PCI_BDF, domain, bus, dev, func);
+ rc = write(fd, buf, strlen(buf));
+ if (rc < 0)
+ LOGD(ERROR, domain, "write to %s returned %d", reset, rc);
+ close(fd);
+ return rc < 0 ? rc : 0;
+ }
+ if (errno != ENOENT)
+ LOGED(ERROR, domain, "Failed to access pciback path %s", reset);
+ reset = GCSPRINTF("%s/"PCI_BDF"/reset", SYSFS_PCI_DEV, domain, bus, dev, func);
+ fd = open(reset, O_WRONLY);
+ if (fd >= 0) {
+ rc = write(fd, "1", 1);
+ if (rc < 0)
+ LOGED(ERROR, domain, "write to %s returned %d", reset, rc);
+ close(fd);
+ return rc < 0 ? rc : 0;
+ }
+ if (errno == ENOENT) {
+ LOGD(ERROR, domain,
+ "The kernel doesn't support reset from sysfs for PCI device "PCI_BDF,
+ domain, bus, dev, func);
+ } else {
+ LOGED(ERROR, domain, "Failed to access reset path %s", reset);
+ }
+ return -1;
+}
+
+int libxl__device_pci_setdefault(libxl__gc *gc, uint32_t domid,
+ libxl_device_pci *pci, bool hotplug)
+{
+ /* We'd like to force reserve rdm specific to a device by default.*/
+ if (pci->rdm_policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
+ pci->rdm_policy = LIBXL_RDM_RESERVE_POLICY_STRICT;
+ return 0;
+}
+
+int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_pci *pcidev,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ libxl__ao_device *aodev;
+
+ GCNEW(aodev);
+ libxl__prepare_ao_device(ao, aodev);
+ aodev->action = LIBXL__DEVICE_ACTION_ADD;
+ aodev->callback = device_addrm_aocomplete;
+ aodev->update_json = true;
+ libxl__device_pci_add(egc, domid, pcidev, false, aodev);
+ return AO_INPROGRESS;
+}
+
+static int libxl_pcidev_assignable(libxl_ctx *ctx, libxl_device_pci *pcidev)
+{
+ libxl_device_pci *pcidevs;
+ int num, i;
+
+ pcidevs = libxl_device_pci_assignable_list(ctx, &num);
+ for (i = 0; i < num; i++) {
+ if (pcidevs[i].domain == pcidev->domain &&
+ pcidevs[i].bus == pcidev->bus &&
+ pcidevs[i].dev == pcidev->dev &&
+ pcidevs[i].func == pcidev->func)
+ break;
+ }
+ free(pcidevs);
+ return i != num;
+}
+
+static void device_pci_add_stubdom_wait(libxl__egc *egc,
+ pci_add_state *pas, int rc);
+static void device_pci_add_stubdom_ready(libxl__egc *egc,
+ libxl__ev_devstate *ds, int rc);
+static void device_pci_add_stubdom_done(libxl__egc *egc,
+ pci_add_state *, int rc);
+static void device_pci_add_done(libxl__egc *egc,
+ pci_add_state *, int rc);
+
+void libxl__device_pci_add(libxl__egc *egc, uint32_t domid,
+ libxl_device_pci *pcidev, bool starting,
+ libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ libxl_device_pci *assigned;
+ int num_assigned, rc;
+ int stubdomid = 0;
+ pci_add_state *pas;
+
+ /* Store *pcidev to be used by callbacks */
+ aodev->device_config = pcidev;
+ aodev->device_type = &libxl__pcidev_devtype;
+
+ GCNEW(pas);
+ pas->aodev = aodev;
+ pas->domid = domid;
+ pas->starting = starting;
+ pas->callback = device_pci_add_stubdom_done;
+
+ if (libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) {
+ rc = xc_test_assign_device(ctx->xch, domid, pcidev_encode_bdf(pcidev));
+ if (rc) {
+ LOGD(ERROR, domid,
+ "PCI device %04x:%02x:%02x.%u %s?",
+ pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func,
+ errno == EOPNOTSUPP ? "cannot be assigned - no IOMMU"
+ : "already assigned to a different guest");
+ goto out;
+ }
+ }
+
+ rc = libxl__device_pci_setdefault(gc, domid, pcidev, !starting);
+ if (rc) goto out;
+
+ if (pcidev->seize && !pciback_dev_is_assigned(gc, pcidev)) {
+ rc = libxl__device_pci_assignable_add(gc, pcidev, 1);
+ if ( rc )
+ goto out;
+ }
+
+ if (!libxl_pcidev_assignable(ctx, pcidev)) {
+ LOGD(ERROR, domid, "PCI device %x:%x:%x.%x is not assignable",
+ pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = get_all_assigned_devices(gc, &assigned, &num_assigned);
+ if ( rc ) {
+ LOGD(ERROR, domid,
+ "cannot determine if device is assigned, refusing to continue");
+ goto out;
+ }
+ if ( is_pcidev_in_array(assigned, num_assigned, pcidev->domain,
+ pcidev->bus, pcidev->dev, pcidev->func) ) {
+ LOGD(ERROR, domid, "PCI device already attached to a domain");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ libxl__device_pci_reset(gc, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+
+ stubdomid = libxl_get_stubdom_id(ctx, domid);
+ if (stubdomid != 0) {
+ libxl_device_pci *pcidev_s;
+
+ GCNEW(pcidev_s);
+ libxl_device_pci_init(pcidev_s);
+ libxl_device_pci_copy(CTX, pcidev_s, pcidev);
+ pas->callback = device_pci_add_stubdom_wait;
+
+ do_pci_add(egc, stubdomid, pcidev_s, pas); /* must be last */
+ return;
+ }
+
+ device_pci_add_stubdom_done(egc, pas, 0); /* must be last */
+ return;
+
+out:
+ device_pci_add_done(egc, pas, rc); /* must be last */
+}
+
+static void device_pci_add_stubdom_wait(libxl__egc *egc,
+ pci_add_state *pas,
+ int rc)
+{
+ libxl__ao_device *aodev = pas->aodev;
+ STATE_AO_GC(aodev->ao);
+ int stubdomid = libxl_get_stubdom_id(CTX, pas->domid);
+ char *state_path;
+
+ if (rc) goto out;
+
+ /* Wait for the device actually being connected, otherwise device model
+ * running there will fail to find the device. */
+ state_path = GCSPRINTF("%s/state",
+ libxl__domain_device_backend_path(gc, 0, stubdomid, 0,
+ LIBXL__DEVICE_KIND_PCI));
+ rc = libxl__ev_devstate_wait(ao, &pas->pciback_ds,
+ device_pci_add_stubdom_ready,
+ state_path, XenbusStateConnected,
+ LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000);
+ if (rc) goto out;
+ return;
+out:
+ device_pci_add_done(egc, pas, rc); /* must be last */
+}
+
+static void device_pci_add_stubdom_ready(libxl__egc *egc,
+ libxl__ev_devstate *ds,
+ int rc)
+{
+ pci_add_state *pas = CONTAINER_OF(ds, *pas, pciback_ds);
+
+ device_pci_add_stubdom_done(egc, pas, rc); /* must be last */
+}
+
+static void device_pci_add_stubdom_done(libxl__egc *egc,
+ pci_add_state *pas,
+ int rc)
+{
+ STATE_AO_GC(pas->aodev->ao);
+ unsigned int orig_vdev, pfunc_mask;
+ int i;
+
+ /* Convenience aliases */
+ libxl__ao_device *aodev = pas->aodev;
+ libxl_domid domid = pas->domid;
+ libxl_device_pci *pcidev = aodev->device_config;
+
+ if (rc) goto out;
+
+ orig_vdev = pcidev->vdevfn & ~7U;
+
+ if ( pcidev->vfunc_mask == LIBXL_PCI_FUNC_ALL ) {
+ if ( !(pcidev->vdevfn >> 3) ) {
+ LOGD(ERROR, domid, "Must specify a v-slot for multi-function devices");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ if ( pci_multifunction_check(gc, pcidev, &pfunc_mask) ) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ pcidev->vfunc_mask &= pfunc_mask;
+ /* so now vfunc_mask == pfunc_mask */
+ }else{
+ pfunc_mask = (1 << pcidev->func);
+ }
+
+ for(rc = 0, i = 7; i >= 0; --i) {
+ if ( (1 << i) & pfunc_mask ) {
+ if ( pcidev->vfunc_mask == pfunc_mask ) {
+ pcidev->func = i;
+ pcidev->vdevfn = orig_vdev | i;
+ }else{
+ /* if not passing through multiple devices in a block make
+ * sure that virtual function number 0 is always used otherwise
+ * guest won't see the device
+ */
+ pcidev->vdevfn = orig_vdev;
+ }
+ pas->callback = device_pci_add_done;
+ do_pci_add(egc, domid, pcidev, pas); /* must be last */
+ return;
+ }
+ }
+
+out:
+ device_pci_add_done(egc, pas, rc);
+}
+
+static void device_pci_add_done(libxl__egc *egc,
+ pci_add_state *pas,
+ int rc)
+{
+ EGC_GC;
+ libxl__ao_device *aodev = pas->aodev;
+ libxl_domid domid = pas->domid;
+ libxl_device_pci *pcidev = aodev->device_config;
+
+ if (rc) {
+ LOGD(ERROR, domid,
+ "libxl__device_pci_add failed for "
+ "PCI device %x:%x:%x.%x (rc %d)",
+ pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func,
+ rc);
+ }
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+typedef struct {
+ libxl__multidev multidev;
+ libxl__ao_device *outer_aodev;
+ libxl_domain_config *d_config;
+ libxl_domid domid;
+} add_pcidevs_state;
+
+static void add_pcidevs_done(libxl__egc *, libxl__multidev *, int rc);
+
+static void libxl__add_pcidevs(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
+ libxl_domain_config *d_config,
+ libxl__multidev *multidev)
+{
+ AO_GC;
+ add_pcidevs_state *apds;
+ int i;
+
+ /* We need to start a new multidev in order to be able to execute
+ * libxl__create_pci_backend only once. */
+
+ GCNEW(apds);
+ apds->outer_aodev = libxl__multidev_prepare(multidev);
+ apds->d_config = d_config;
+ apds->domid = domid;
+ apds->multidev.callback = add_pcidevs_done;
+ libxl__multidev_begin(ao, &apds->multidev);
+
+ for (i = 0; i < d_config->num_pcidevs; i++) {
+ libxl__ao_device *aodev = libxl__multidev_prepare(&apds->multidev);
+ libxl__device_pci_add(egc, domid, &d_config->pcidevs[i],
+ true, aodev);
+ }
+
+ libxl__multidev_prepared(egc, &apds->multidev, 0);
+}
+
+static void add_pcidevs_done(libxl__egc *egc, libxl__multidev *multidev,
+ int rc)
+{
+ EGC_GC;
+ add_pcidevs_state *apds = CONTAINER_OF(multidev, *apds, multidev);
+
+ /* Convenience aliases */
+ libxl_domain_config *d_config = apds->d_config;
+ libxl_domid domid = apds->domid;
+ libxl__ao_device *aodev = apds->outer_aodev;
+
+ if (rc) goto out;
+
+ if (d_config->num_pcidevs > 0 && !libxl_get_stubdom_id(CTX, domid)) {
+ rc = libxl__create_pci_backend(gc, domid, d_config->pcidevs,
+ d_config->num_pcidevs);
+ if (rc < 0) {
+ LOGD(ERROR, domid, "libxl_create_pci_backend failed: %d", rc);
+ goto out;
+ }
+ }
+
+out:
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+static int qemu_pci_remove_xenstore(libxl__gc *gc, uint32_t domid,
+ libxl_device_pci *pcidev, int force)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char *state;
+ char *path;
+ uint32_t dm_domid;
+
+ dm_domid = libxl_get_stubdom_id(CTX, domid);
+
+ path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+ state = libxl__xs_read(gc, XBT_NULL, path);
+ path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter");
+ libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF, pcidev->domain,
+ pcidev->bus, pcidev->dev, pcidev->func);
+
+ /* Remove all functions at once atomically by only signalling
+ * device-model for function 0 */
+ if ( !force && (pcidev->vdevfn & 0x7) == 0 ) {
+ libxl__qemu_traditional_cmd(gc, domid, "pci-rem");
+ if (libxl__wait_for_device_model_deprecated(gc, domid, "pci-removed",
+ NULL, NULL, NULL) < 0) {
+ LOGD(ERROR, domid, "Device Model didn't respond in time");
+ /* This depends on guest operating system acknowledging the
+ * SCI, if it doesn't respond in time then we may wish to
+ * force the removal.
+ */
+ return ERROR_FAIL;
+ }
+ }
+ path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+ xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
+
+ return 0;
+}
+
+typedef struct pci_remove_state {
+ libxl__ao_device *aodev;
+ libxl_domid domid;
+ libxl_device_pci *pcidev;
+ bool force;
+ bool hvm;
+ unsigned int orig_vdev;
+ unsigned int pfunc_mask;
+ int next_func;
+ libxl__ao_device stubdom_aodev;
+ libxl__xswait_state xswait;
+ libxl__ev_qmp qmp;
+ libxl__ev_time timeout;
+ libxl__ev_time retry_timer;
+} pci_remove_state;
+
+static void libxl__device_pci_remove_common(libxl__egc *egc,
+ uint32_t domid, libxl_device_pci *pcidev, bool force,
+ libxl__ao_device *aodev);
+static void device_pci_remove_common_next(libxl__egc *egc,
+ pci_remove_state *prs, int rc);
+
+static void pci_remove_qemu_trad_watch_state_cb(libxl__egc *egc,
+ libxl__xswait_state *xswa, int rc, const char *state);
+static void pci_remove_qmp_device_del(libxl__egc *egc,
+ pci_remove_state *prs);
+static void pci_remove_qmp_device_del_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
+static void pci_remove_qmp_retry_timer_cb(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void pci_remove_qmp_query_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
+static void pci_remove_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void pci_remove_detatched(libxl__egc *egc,
+ pci_remove_state *prs, int rc);
+static void pci_remove_stubdom_done(libxl__egc *egc,
+ libxl__ao_device *aodev);
+static void pci_remove_done(libxl__egc *egc,
+ pci_remove_state *prs, int rc);
+
+static void do_pci_remove(libxl__egc *egc, uint32_t domid,
+ libxl_device_pci *pcidev, int force,
+ pci_remove_state *prs)
+{
+ STATE_AO_GC(prs->aodev->ao);
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ libxl_device_pci *assigned;
+ libxl_domain_type type = libxl__domain_type(gc, domid);
+ int rc, num;
+ uint32_t domainid = domid;
+
+ assigned = libxl_device_pci_list(ctx, domid, &num);
+ if (assigned == NULL) {
+ rc = ERROR_FAIL;
+ goto out_fail;
+ }
+ libxl__ptr_add(gc, assigned);
+
+ rc = ERROR_INVAL;
+ if ( !is_pcidev_in_array(assigned, num, pcidev->domain,
+ pcidev->bus, pcidev->dev, pcidev->func) ) {
+ LOGD(ERROR, domainid, "PCI device not attached to this domain");
+ goto out_fail;
+ }
+
+ rc = ERROR_FAIL;
+ if (type == LIBXL_DOMAIN_TYPE_HVM) {
+ prs->hvm = true;
+ switch (libxl__device_model_version_running(gc, domid)) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ prs->xswait.ao = ao;
+ prs->xswait.what = "Device Model";
+ prs->xswait.path = DEVICE_MODEL_XS_PATH(gc,
+ libxl_get_stubdom_id(CTX, domid), domid, "/state");
+ prs->xswait.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
+ prs->xswait.callback = pci_remove_qemu_trad_watch_state_cb;
+ rc = libxl__xswait_start(gc, &prs->xswait);
+ if (rc) goto out_fail;
+ return;
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ pci_remove_qmp_device_del(egc, prs); /* must be last */
+ return;
+ default:
+ rc = ERROR_INVAL;
+ goto out_fail;
+ }
+ } else {
+ assert(type == LIBXL_DOMAIN_TYPE_PV);
+
+ char *sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
+ pcidev->bus, pcidev->dev, pcidev->func);
+ FILE *f = fopen(sysfs_path, "r");
+ unsigned int start = 0, end = 0, flags = 0, size = 0;
+ int irq = 0;
+ int i;
+
+ if (f == NULL) {
+ LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
+ goto skip1;
+ }
+ for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
+ if (fscanf(f, "0x%x 0x%x 0x%x\n", &start, &end, &flags) != 3)
+ continue;
+ size = end - start + 1;
+ if (start) {
+ if (flags & PCI_BAR_IO) {
+ rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 0);
+ if (rc < 0)
+ LOGED(ERROR, domainid,
+ "xc_domain_ioport_permission error 0x%x/0x%x",
+ start,
+ size);
+ } else {
+ rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
+ (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 0);
+ if (rc < 0)
+ LOGED(ERROR, domainid,
+ "xc_domain_iomem_permission error 0x%x/0x%x",
+ start,
+ size);
+ }
+ }
+ }
+ fclose(f);
+skip1:
+ sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
+ pcidev->bus, pcidev->dev, pcidev->func);
+ f = fopen(sysfs_path, "r");
+ if (f == NULL) {
+ LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
+ goto skip_irq;
+ }
+ if ((fscanf(f, "%u", &irq) == 1) && irq) {
+ rc = xc_physdev_unmap_pirq(ctx->xch, domid, irq);
+ if (rc < 0) {
+ LOGED(ERROR, domainid, "xc_physdev_unmap_pirq irq=%d", irq);
+ }
+ rc = xc_domain_irq_permission(ctx->xch, domid, irq, 0);
+ if (rc < 0) {
+ LOGED(ERROR, domainid, "xc_domain_irq_permission irq=%d", irq);
+ }
+ }
+ fclose(f);
+ }
+skip_irq:
+ rc = 0;
+out_fail:
+ pci_remove_detatched(egc, prs, rc); /* must be last */
+}
+
+static void pci_remove_qemu_trad_watch_state_cb(libxl__egc *egc,
+ libxl__xswait_state *xswa,
+ int rc,
+ const char *state)
+{
+ pci_remove_state *prs = CONTAINER_OF(xswa, *prs, xswait);
+ STATE_AO_GC(prs->aodev->ao);
+
+ /* Convenience aliases */
+ libxl_domid domid = prs->domid;
+ libxl_device_pci *const pcidev = prs->pcidev;
+
+ rc = check_qemu_running(gc, domid, xswa, rc, state);
+ if (rc == ERROR_NOT_READY)
+ return;
+ if (rc)
+ goto out;
+
+ rc = qemu_pci_remove_xenstore(gc, domid, pcidev, prs->force);
+
+out:
+ pci_remove_detatched(egc, prs, rc);
+}
+
+static void pci_remove_qmp_device_del(libxl__egc *egc,
+ pci_remove_state *prs)
+{
+ STATE_AO_GC(prs->aodev->ao);
+ libxl__json_object *args = NULL;
+ int rc;
+
+ /* Convenience aliases */
+ libxl_device_pci *const pcidev = prs->pcidev;
+
+ rc = libxl__ev_time_register_rel(ao, &prs->timeout,
+ pci_remove_timeout,
+ LIBXL_QMP_CMD_TIMEOUT * 1000);
+ if (rc) goto out;
+
+ QMP_PARAMETERS_SPRINTF(&args, "id", PCI_PT_QDEV_ID,
+ pcidev->bus, pcidev->dev, pcidev->func);
+ prs->qmp.callback = pci_remove_qmp_device_del_cb;
+ rc = libxl__ev_qmp_send(egc, &prs->qmp, "device_del", args);
+ if (rc) goto out;
+ return;
+
+out:
+ pci_remove_detatched(egc, prs, rc);
+}
+
+static void pci_remove_qmp_device_del_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *response,
+ int rc)
+{
+ EGC_GC;
+ pci_remove_state *prs = CONTAINER_OF(qmp, *prs, qmp);
+
+ if (rc) goto out;
+
+ /* Now that the command is sent, we want to wait until QEMU has
+ * confirmed that the device is removed. */
+ /* TODO: Instead of using a poll loop { ev_timer ; query-pci }, it
+ * could be possible to listen to events sent by QEMU via QMP in order
+ * to wait for the passthrough pci-device to be removed from QEMU. */
+ pci_remove_qmp_retry_timer_cb(egc, &prs->retry_timer, NULL,
+ ERROR_TIMEDOUT);
+ return;
+
+out:
+ pci_remove_detatched(egc, prs, rc);
+}
+
+static void pci_remove_qmp_retry_timer_cb(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ EGC_GC;
+ pci_remove_state *prs = CONTAINER_OF(ev, *prs, retry_timer);
+
+ prs->qmp.callback = pci_remove_qmp_query_cb;
+ rc = libxl__ev_qmp_send(egc, &prs->qmp, "query-pci", NULL);
+ if (rc) goto out;
+ return;
+
+out:
+ pci_remove_detatched(egc, prs, rc);
+}
+
+static void pci_remove_qmp_query_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *response,
+ int rc)
+{
+ EGC_GC;
+ pci_remove_state *prs = CONTAINER_OF(qmp, *prs, qmp);
+ const libxl__json_object *bus = NULL;
+ const char *asked_id;
+ int i, j;
+
+ /* Convenience aliases */
+ libxl__ao *const ao = prs->aodev->ao;
+ libxl_device_pci *const pcidev = prs->pcidev;
+
+ if (rc) goto out;
+
+ libxl__ev_qmp_dispose(gc, qmp);
+
+ asked_id = GCSPRINTF(PCI_PT_QDEV_ID,
+ pcidev->bus, pcidev->dev, pcidev->func);
+
+ /* query-pci response:
+ * [{ 'devices': [ 'qdev_id': 'str', ... ], ... }]
+ * */
+
+ for (i = 0; (bus = libxl__json_array_get(response, i)); i++) {
+ const libxl__json_object *devices = NULL;
+ const libxl__json_object *device = NULL;
+ const libxl__json_object *o = NULL;
+ const char *id = NULL;
+
+ devices = libxl__json_map_get("devices", bus, JSON_ARRAY);
+ if (!devices) {
+ rc = ERROR_QEMU_API;
+ goto out;
+ }
+
+ for (j = 0; (device = libxl__json_array_get(devices, j)); j++) {
+ o = libxl__json_map_get("qdev_id", device, JSON_STRING);
+ if (!o) {
+ rc = ERROR_QEMU_API;
+ goto out;
+ }
+ id = libxl__json_object_get_string(o);
+
+ if (id && !strcmp(asked_id, id)) {
+ /* Device still in QEMU, need to wait longuer. */
+ rc = libxl__ev_time_register_rel(ao, &prs->retry_timer,
+ pci_remove_qmp_retry_timer_cb, 1000);
+ if (rc) goto out;
+ return;
+ }
+ }
+ }
+
+out:
+ pci_remove_detatched(egc, prs, rc); /* must be last */
+}
+
+static void pci_remove_timeout(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ EGC_GC;
+ pci_remove_state *prs = CONTAINER_OF(ev, *prs, timeout);
+
+ /* Convenience aliases */
+ libxl_device_pci *const pcidev = prs->pcidev;
+
+ LOGD(WARN, prs->domid, "timed out waiting for DM to remove "
+ PCI_PT_QDEV_ID, pcidev->bus, pcidev->dev, pcidev->func);
+
+ /* If we timed out, we might still want to keep destroying the device
+ * (when force==true), so let the next function decide what to do on
+ * error */
+ pci_remove_detatched(egc, prs, rc);
+}
+
+static void pci_remove_detatched(libxl__egc *egc,
+ pci_remove_state *prs,
+ int rc)
+{
+ STATE_AO_GC(prs->aodev->ao);
+ int stubdomid = 0;
+ uint32_t domainid = prs->domid;
+ bool isstubdom;
+
+ /* Convenience aliases */
+ libxl_device_pci *const pcidev = prs->pcidev;
+ libxl_domid domid = prs->domid;
+
+ /* Cleaning QMP states ASAP */
+ libxl__ev_qmp_dispose(gc, &prs->qmp);
+ libxl__ev_time_deregister(gc, &prs->timeout);
+ libxl__ev_time_deregister(gc, &prs->retry_timer);
+
+ if (rc && !prs->force)
+ goto out;
+
+ isstubdom = libxl_is_stubdom(CTX, domid, &domainid);
+
+ /* don't do multiple resets while some functions are still passed through */
+ if ( (pcidev->vdevfn & 0x7) == 0 ) {
+ libxl__device_pci_reset(gc, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+ }
+
+ if (!isstubdom) {
+ rc = xc_deassign_device(CTX->xch, domid, pcidev_encode_bdf(pcidev));
+ if (rc < 0 && (prs->hvm || errno != ENOSYS))
+ LOGED(ERROR, domainid, "xc_deassign_device failed");
+ }
+
+ stubdomid = libxl_get_stubdom_id(CTX, domid);
+ if (stubdomid != 0) {
+ libxl_device_pci *pcidev_s;
+ libxl__ao_device *const stubdom_aodev = &prs->stubdom_aodev;
+
+ GCNEW(pcidev_s);
+ libxl_device_pci_init(pcidev_s);
+ libxl_device_pci_copy(CTX, pcidev_s, pcidev);
+
+ libxl__prepare_ao_device(ao, stubdom_aodev);
+ stubdom_aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+ stubdom_aodev->callback = pci_remove_stubdom_done;
+ stubdom_aodev->update_json = prs->aodev->update_json;
+ libxl__device_pci_remove_common(egc, stubdomid, pcidev_s,
+ prs->force, stubdom_aodev);
+ return;
+ }
+
+ rc = 0;
+out:
+ pci_remove_done(egc, prs, rc);
+}
+
+static void pci_remove_stubdom_done(libxl__egc *egc,
+ libxl__ao_device *aodev)
+{
+ pci_remove_state *prs = CONTAINER_OF(aodev, *prs, stubdom_aodev);
+
+ pci_remove_done(egc, prs, 0);
+}
+
+static void pci_remove_done(libxl__egc *egc,
+ pci_remove_state *prs,
+ int rc)
+{
+ EGC_GC;
+
+ if (rc) goto out;
+
+ libxl__device_pci_remove_xenstore(gc, prs->domid, prs->pcidev);
+out:
+ device_pci_remove_common_next(egc, prs, rc);
+}
+
+static void libxl__device_pci_remove_common(libxl__egc *egc,
+ uint32_t domid,
+ libxl_device_pci *pcidev,
+ bool force,
+ libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ int rc;
+ pci_remove_state *prs;
+
+ GCNEW(prs);
+ prs->aodev = aodev;
+ prs->domid = domid;
+ prs->pcidev = pcidev;
+ prs->force = force;
+ libxl__xswait_init(&prs->xswait);
+ libxl__ev_qmp_init(&prs->qmp);
+ prs->qmp.ao = prs->aodev->ao;
+ prs->qmp.domid = prs->domid;
+ prs->qmp.payload_fd = -1;
+ libxl__ev_time_init(&prs->timeout);
+ libxl__ev_time_init(&prs->retry_timer);
+
+ prs->orig_vdev = pcidev->vdevfn & ~7U;
+
+ if ( pcidev->vfunc_mask == LIBXL_PCI_FUNC_ALL ) {
+ if ( pci_multifunction_check(gc, pcidev, &prs->pfunc_mask) ) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ pcidev->vfunc_mask &= prs->pfunc_mask;
+ }else{
+ prs->pfunc_mask = (1 << pcidev->func);
+ }
+
+ rc = 0;
+ prs->next_func = 7;
+out:
+ device_pci_remove_common_next(egc, prs, rc);
+}
+
+static void device_pci_remove_common_next(libxl__egc *egc,
+ pci_remove_state *prs,
+ int rc)
+{
+ EGC_GC;
+
+ /* Convenience aliases */
+ libxl_domid domid = prs->domid;
+ libxl_device_pci *const pcidev = prs->pcidev;
+ libxl__ao_device *const aodev = prs->aodev;
+ const unsigned int pfunc_mask = prs->pfunc_mask;
+ const unsigned int orig_vdev = prs->orig_vdev;
+
+ if (rc) goto out;
+
+ while (prs->next_func >= 0) {
+ const int i = prs->next_func;
+ prs->next_func--;
+ if ( (1 << i) & pfunc_mask ) {
+ if ( pcidev->vfunc_mask == pfunc_mask ) {
+ pcidev->func = i;
+ pcidev->vdevfn = orig_vdev | i;
+ }else{
+ pcidev->vdevfn = orig_vdev;
+ }
+ do_pci_remove(egc, domid, pcidev, prs->force, prs);
+ return;
+ }
+ }
+
+ rc = 0;
+out:
+ libxl__ev_qmp_dispose(gc, &prs->qmp);
+ libxl__xswait_stop(gc, &prs->xswait);
+ libxl__ev_time_deregister(gc, &prs->timeout);
+ libxl__ev_time_deregister(gc, &prs->retry_timer);
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_pci *pcidev,
+ const libxl_asyncop_how *ao_how)
+
+{
+ AO_CREATE(ctx, domid, ao_how);
+ libxl__ao_device *aodev;
+
+ GCNEW(aodev);
+ libxl__prepare_ao_device(ao, aodev);
+ aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+ aodev->callback = device_addrm_aocomplete;
+ aodev->update_json = true;
+ libxl__device_pci_remove_common(egc, domid, pcidev, false, aodev);
+ return AO_INPROGRESS;
+}
+
+int libxl_device_pci_destroy(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_pci *pcidev,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ libxl__ao_device *aodev;
+
+ GCNEW(aodev);
+ libxl__prepare_ao_device(ao, aodev);
+ aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+ aodev->callback = device_addrm_aocomplete;
+ aodev->update_json = true;
+ libxl__device_pci_remove_common(egc, domid, pcidev, true, aodev);
+ return AO_INPROGRESS;
+}
+
+static int libxl__device_pci_from_xs_be(libxl__gc *gc,
+ const char *be_path,
+ libxl_devid nr, void *data)
+{
+ char *s;
+ unsigned int domain = 0, bus = 0, dev = 0, func = 0, vdevfn = 0;
+ libxl_device_pci *pci = data;
+
+ s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/dev-%d", be_path, nr));
+ sscanf(s, PCI_BDF, &domain, &bus, &dev, &func);
+
+ s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vdevfn-%d", be_path, nr));
+ if (s)
+ vdevfn = strtol(s, (char **) NULL, 16);
+
+ pcidev_struct_fill(pci, domain, bus, dev, func, vdevfn);
+
+ s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/opts-%d", be_path, nr));
+ if (s) {
+ char *saveptr;
+ char *p = strtok_r(s, ",=", &saveptr);
+ do {
+ while (*p == ' ')
+ p++;
+ if (!strcmp(p, "msitranslate")) {
+ p = strtok_r(NULL, ",=", &saveptr);
+ pci->msitranslate = atoi(p);
+ } else if (!strcmp(p, "power_mgmt")) {
+ p = strtok_r(NULL, ",=", &saveptr);
+ pci->power_mgmt = atoi(p);
+ } else if (!strcmp(p, "permissive")) {
+ p = strtok_r(NULL, ",=", &saveptr);
+ pci->permissive = atoi(p);
+ }
+ } while ((p = strtok_r(NULL, ",=", &saveptr)) != NULL);
+ }
+
+ return 0;
+}
+
+static int libxl__device_pci_get_num(libxl__gc *gc, const char *be_path,
+ unsigned int *num)
+{
+ char *num_devs;
+ int rc = 0;
+
+ num_devs = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/num_devs", be_path));
+ if (!num_devs)
+ rc = ERROR_FAIL;
+ else
+ *num = atoi(num_devs);
+
+ return rc;
+}
+
+libxl_device_pci *libxl_device_pci_list(libxl_ctx *ctx, uint32_t domid, int *num)
+{
+ GC_INIT(ctx);
+ char *be_path;
+ unsigned int n, i;
+ libxl_device_pci *pcidevs = NULL;
+
+ *num = 0;
+
+ be_path = libxl__domain_device_backend_path(gc, 0, domid, 0,
+ LIBXL__DEVICE_KIND_PCI);
+ if (libxl__device_pci_get_num(gc, be_path, &n))
+ goto out;
+
+ pcidevs = calloc(n, sizeof(libxl_device_pci));
+
+ for (i = 0; i < n; i++)
+ libxl__device_pci_from_xs_be(gc, be_path, i, pcidevs + i);
+
+ *num = n;
+out:
+ GC_FREE;
+ return pcidevs;
+}
+
+void libxl__device_pci_destroy_all(libxl__egc *egc, uint32_t domid,
+ libxl__multidev *multidev)
+{
+ STATE_AO_GC(multidev->ao);
+ libxl_device_pci *pcidevs;
+ int num, i;
+
+ pcidevs = libxl_device_pci_list(CTX, domid, &num);
+ if ( pcidevs == NULL )
+ return;
+ libxl__ptr_add(gc, pcidevs);
+
+ for (i = 0; i < num; i++) {
+ /* Force remove on shutdown since, on HVM, qemu will not always
+ * respond to SCI interrupt because the guest kernel has shut down the
+ * devices by the time we even get here!
+ */
+ libxl__ao_device *aodev = libxl__multidev_prepare(multidev);
+ libxl__device_pci_remove_common(egc, domid, pcidevs + i, true,
+ aodev);
+ }
+}
+
+int libxl__grant_vga_iomem_permission(libxl__gc *gc, const uint32_t domid,
+ libxl_domain_config *const d_config)
+{
+ int i, ret;
+
+ if (!libxl_defbool_val(d_config->b_info.u.hvm.gfx_passthru))
+ return 0;
+
+ for (i = 0 ; i < d_config->num_pcidevs ; i++) {
+ uint64_t vga_iomem_start = 0xa0000 >> XC_PAGE_SHIFT;
+ uint32_t stubdom_domid;
+ libxl_device_pci *pcidev = &d_config->pcidevs[i];
+ unsigned long pci_device_class;
+
+ if (sysfs_dev_get_class(gc, pcidev, &pci_device_class))
+ continue;
+ if (pci_device_class != 0x030000) /* VGA class */
+ continue;
+
+ stubdom_domid = libxl_get_stubdom_id(CTX, domid);
+ ret = xc_domain_iomem_permission(CTX->xch, stubdom_domid,
+ vga_iomem_start, 0x20, 1);
+ if (ret < 0) {
+ LOGED(ERROR, domid,
+ "failed to give stubdom%d access to iomem range "
+ "%"PRIx64"-%"PRIx64" for VGA passthru",
+ stubdom_domid,
+ vga_iomem_start, (vga_iomem_start + 0x20 - 1));
+ return ret;
+ }
+ ret = xc_domain_iomem_permission(CTX->xch, domid,
+ vga_iomem_start, 0x20, 1);
+ if (ret < 0) {
+ LOGED(ERROR, domid,
+ "failed to give dom%d access to iomem range "
+ "%"PRIx64"-%"PRIx64" for VGA passthru",
+ domid, vga_iomem_start, (vga_iomem_start + 0x20 - 1));
+ return ret;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int libxl_device_pci_compare(const libxl_device_pci *d1,
+ const libxl_device_pci *d2)
+{
+ return COMPARE_PCI(d1, d2);
+}
+
+#define libxl__device_pci_update_devid NULL
+
+DEFINE_DEVICE_TYPE_STRUCT_X(pcidev, pci, PCI,
+ .get_num = libxl__device_pci_get_num,
+ .from_xenstore = libxl__device_pci_from_xs_be,
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2014 Intel Corporation
+ * Author Dongxiao Xu <dongxiao.xu@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+#include "libxl_internal.h"
+
+#include <xen-tools/libs.h>
+
+#define IA32_QM_CTR_ERROR_MASK (0x3ul << 62)
+
+static void libxl__psr_log_err_msg(libxl__gc *gc, int err)
+{
+ char *msg;
+
+ switch (err) {
+ case ENOSYS:
+ case EOPNOTSUPP:
+ msg = "unsupported operation";
+ break;
+ case ESRCH:
+ msg = "invalid domain ID";
+ break;
+ case ENOTSOCK:
+ msg = "socket is not supported";
+ break;
+ case EFAULT:
+ msg = "failed to exchange data with Xen";
+ break;
+ default:
+ msg = "unknown error";
+ break;
+ }
+
+ LOGE(ERROR, "%s", msg);
+}
+
+static void libxl__psr_cmt_log_err_msg(libxl__gc *gc, int err)
+{
+ char *msg;
+
+ switch (err) {
+ case ENODEV:
+ msg = "CMT is not supported in this system";
+ break;
+ case EEXIST:
+ msg = "CMT is already attached to this domain";
+ break;
+ case ENOENT:
+ msg = "CMT is not attached to this domain";
+ break;
+ case EOVERFLOW:
+ msg = "no free RMID available";
+ break;
+ default:
+ libxl__psr_log_err_msg(gc, err);
+ return;
+ }
+
+ LOGE(ERROR, "%s", msg);
+}
+
+static void libxl__psr_alloc_log_err_msg(libxl__gc *gc,
+ int err,
+ libxl_psr_type type)
+{
+ /*
+ * Index is 'libxl_psr_type' so we set two 'CDP' to correspond to
+ * DATA and CODE.
+ */
+ const char * const feat_name[] = {
+ [LIBXL_PSR_CBM_TYPE_UNKNOWN] = "UNKNOWN",
+ [LIBXL_PSR_CBM_TYPE_L3_CBM] = "L3 CAT",
+ [LIBXL_PSR_CBM_TYPE_L3_CBM_CODE...LIBXL_PSR_CBM_TYPE_L3_CBM_DATA] =
+ "CDP",
+ [LIBXL_PSR_CBM_TYPE_L2_CBM] = "L2 CAT",
+ [LIBXL_PSR_CBM_TYPE_MBA_THRTL] = "MBA",
+ };
+ char *msg;
+
+ switch (err) {
+ case ENODEV:
+ msg = "is not supported in this system";
+ break;
+ case ENOENT:
+ msg = "is not enabled on the socket";
+ break;
+ case EOVERFLOW:
+ msg = "no free COS available";
+ break;
+ case EEXIST:
+ msg = "The same CBM is already set to this domain";
+ break;
+ case ENXIO:
+ msg = "Unable to set code or data CBM when CDP is disabled";
+ break;
+ case EINVAL:
+ msg = "Invalid input or some internal values are not expected";
+ break;
+ case ERANGE:
+ msg = "Socket number is wrong";
+ break;
+ case ENOSPC:
+ msg = "Value array exceeds the range";
+ break;
+
+ default:
+ libxl__psr_log_err_msg(gc, err);
+ return;
+ }
+
+ LOG(ERROR, "%s: %s", feat_name[type], msg);
+}
+
+static int libxl__pick_socket_cpu(libxl__gc *gc, uint32_t socketid)
+{
+ int i, nr_cpus;
+ libxl_cputopology *topology;
+ int cpu = ERROR_FAIL;
+
+ topology = libxl_get_cpu_topology(CTX, &nr_cpus);
+ if (!topology)
+ return ERROR_FAIL;
+
+ for (i = 0; i < nr_cpus; i++)
+ if (topology[i].socket == socketid) {
+ cpu = i;
+ break;
+ }
+
+ libxl_cputopology_list_free(topology, nr_cpus);
+ return cpu;
+}
+
+int libxl_psr_cmt_attach(libxl_ctx *ctx, uint32_t domid)
+{
+ GC_INIT(ctx);
+ int rc;
+
+ rc = xc_psr_cmt_attach(ctx->xch, domid);
+ if (rc < 0) {
+ libxl__psr_cmt_log_err_msg(gc, errno);
+ rc = ERROR_FAIL;
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_psr_cmt_detach(libxl_ctx *ctx, uint32_t domid)
+{
+ GC_INIT(ctx);
+ int rc;
+
+ rc = xc_psr_cmt_detach(ctx->xch, domid);
+ if (rc < 0) {
+ libxl__psr_cmt_log_err_msg(gc, errno);
+ rc = ERROR_FAIL;
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_psr_cmt_domain_attached(libxl_ctx *ctx, uint32_t domid)
+{
+ int rc;
+ uint32_t rmid;
+
+ rc = xc_psr_cmt_get_domain_rmid(ctx->xch, domid, &rmid);
+ if (rc < 0)
+ return 0;
+
+ return !!rmid;
+}
+
+int libxl_psr_cmt_enabled(libxl_ctx *ctx)
+{
+ return xc_psr_cmt_enabled(ctx->xch);
+}
+
+int libxl_psr_cmt_get_total_rmid(libxl_ctx *ctx, uint32_t *total_rmid)
+{
+ GC_INIT(ctx);
+ int rc;
+
+ rc = xc_psr_cmt_get_total_rmid(ctx->xch, total_rmid);
+ if (rc < 0) {
+ libxl__psr_cmt_log_err_msg(gc, errno);
+ rc = ERROR_FAIL;
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_psr_cmt_get_l3_cache_size(libxl_ctx *ctx,
+ uint32_t socketid,
+ uint32_t *l3_cache_size)
+{
+ GC_INIT(ctx);
+
+ int rc;
+ int cpu = libxl__pick_socket_cpu(gc, socketid);
+
+ if (cpu < 0) {
+ LOGE(ERROR, "failed to get socket cpu");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = xc_psr_cmt_get_l3_cache_size(ctx->xch, cpu, l3_cache_size);
+ if (rc < 0) {
+ libxl__psr_cmt_log_err_msg(gc, errno);
+ rc = ERROR_FAIL;
+ }
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_psr_cmt_type_supported(libxl_ctx *ctx, libxl_psr_cmt_type type)
+{
+ GC_INIT(ctx);
+ uint32_t event_mask;
+ int rc;
+
+ rc = xc_psr_cmt_get_l3_event_mask(ctx->xch, &event_mask);
+ if (rc < 0) {
+ libxl__psr_cmt_log_err_msg(gc, errno);
+ rc = 0;
+ } else {
+ rc = event_mask & (1 << (type - 1));
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_psr_cmt_get_sample(libxl_ctx *ctx,
+ uint32_t domid,
+ libxl_psr_cmt_type type,
+ uint64_t scope,
+ uint64_t *sample_r,
+ uint64_t *tsc_r)
+{
+ GC_INIT(ctx);
+ unsigned int rmid;
+ uint32_t upscaling_factor;
+ uint64_t monitor_data;
+ int cpu, rc;
+
+ rc = xc_psr_cmt_get_domain_rmid(ctx->xch, domid, &rmid);
+ if (rc < 0 || rmid == 0) {
+ LOGED(ERROR, domid, "fail to get the domain rmid, "
+ "or domain is not attached with platform QoS monitoring service");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ cpu = libxl__pick_socket_cpu(gc, scope);
+ if (cpu < 0) {
+ LOGED(ERROR, domid, "failed to get socket cpu");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = xc_psr_cmt_get_data(ctx->xch, rmid, cpu, type - 1,
+ &monitor_data, tsc_r);
+ if (rc < 0) {
+ LOGED(ERROR, domid, "failed to get monitoring data");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = xc_psr_cmt_get_l3_upscaling_factor(ctx->xch, &upscaling_factor);
+ if (rc < 0) {
+ LOGED(ERROR, domid, "failed to get L3 upscaling factor");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ *sample_r = monitor_data * upscaling_factor;
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_psr_cmt_get_cache_occupancy(libxl_ctx *ctx,
+ uint32_t domid,
+ uint32_t socketid,
+ uint32_t *l3_cache_occupancy)
+{
+ uint64_t data;
+ int rc;
+
+ rc = libxl_psr_cmt_get_sample(ctx, domid,
+ LIBXL_PSR_CMT_TYPE_CACHE_OCCUPANCY,
+ socketid, &data, NULL);
+ if (rc < 0)
+ goto out;
+
+ *l3_cache_occupancy = data / 1024;
+out:
+ return rc;
+}
+
+static inline xc_psr_type libxl__psr_type_to_libxc_psr_type(
+ libxl_psr_type type)
+{
+ BUILD_BUG_ON(sizeof(libxl_psr_type) != sizeof(xc_psr_type));
+ return (xc_psr_type)type;
+}
+
+int libxl_psr_cat_set_cbm(libxl_ctx *ctx, uint32_t domid,
+ libxl_psr_cbm_type type, libxl_bitmap *target_map,
+ uint64_t cbm)
+{
+ return libxl_psr_set_val(ctx, domid, type, target_map, cbm);
+}
+
+int libxl_psr_cat_get_cbm(libxl_ctx *ctx, uint32_t domid,
+ libxl_psr_cbm_type type, uint32_t target,
+ uint64_t *cbm_r)
+{
+ return libxl_psr_get_val(ctx, domid, type, target, cbm_r);
+}
+
+static xc_psr_feat_type libxl__feat_type_to_libxc_feat_type(
+ libxl_psr_feat_type type, unsigned int lvl)
+{
+ xc_psr_feat_type xc_type;
+
+ switch (type) {
+ case LIBXL_PSR_FEAT_TYPE_CAT:
+ assert(lvl == 3 || lvl == 2);
+
+ if (lvl == 3)
+ xc_type = XC_PSR_CAT_L3;
+ if (lvl == 2)
+ xc_type = XC_PSR_CAT_L2;
+ break;
+ case LIBXL_PSR_FEAT_TYPE_MBA:
+ xc_type = XC_PSR_MBA;
+ break;
+ default:
+ /* Could not happen */
+ assert(0);
+ }
+
+ return xc_type;
+}
+
+static void libxl__hw_info_to_libxl_cat_info(
+ libxl_psr_feat_type type, libxl_psr_hw_info *hw_info,
+ libxl_psr_cat_info *cat_info)
+{
+ assert(type == LIBXL_PSR_FEAT_TYPE_CAT);
+
+ cat_info->id = hw_info->id;
+ cat_info->cos_max = hw_info->u.cat.cos_max;
+ cat_info->cbm_len = hw_info->u.cat.cbm_len;
+ cat_info->cdp_enabled = hw_info->u.cat.cdp_enabled;
+}
+
+int libxl_psr_cat_get_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
+ unsigned int *nr, unsigned int lvl)
+{
+ GC_INIT(ctx);
+ int rc;
+ unsigned int i;
+ libxl_psr_hw_info *hw_info;
+ libxl_psr_cat_info *ptr;
+
+ rc = libxl_psr_get_hw_info(ctx, LIBXL_PSR_FEAT_TYPE_CAT, lvl, nr, &hw_info);
+ if (rc)
+ goto out;
+
+ ptr = libxl__malloc(NOGC, *nr * sizeof(libxl_psr_cat_info));
+
+ for (i = 0; i < *nr; i++)
+ libxl__hw_info_to_libxl_cat_info(LIBXL_PSR_FEAT_TYPE_CAT,
+ &hw_info[i],
+ &ptr[i]);
+
+ *info = ptr;
+ libxl_psr_hw_info_list_free(hw_info, *nr);
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_psr_cat_get_l3_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
+ int *nr)
+{
+ int rc;
+ unsigned int num;
+
+ rc = libxl_psr_cat_get_info(ctx, info, &num, 3);
+ if (!rc)
+ *nr = num;
+
+ return rc;
+}
+
+void libxl_psr_cat_info_list_free(libxl_psr_cat_info *list, int nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ libxl_psr_cat_info_dispose(&list[i]);
+ free(list);
+}
+
+int libxl_psr_set_val(libxl_ctx *ctx, uint32_t domid,
+ libxl_psr_type type, libxl_bitmap *target_map,
+ uint64_t val)
+{
+ GC_INIT(ctx);
+ int rc, socketid, nr_sockets;
+ xc_psr_type xc_type = libxl__psr_type_to_libxc_psr_type(type);
+
+ rc = libxl__count_physical_sockets(gc, &nr_sockets);
+ if (rc) {
+ LOG(ERROR, "failed to get system socket count");
+ goto out;
+ }
+
+ libxl_for_each_set_bit(socketid, *target_map) {
+ if (socketid >= nr_sockets)
+ break;
+
+ if (xc_psr_set_domain_data(ctx->xch, domid, xc_type,
+ socketid, val)) {
+ libxl__psr_alloc_log_err_msg(gc, errno, type);
+ rc = ERROR_FAIL;
+ }
+ }
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_psr_get_val(libxl_ctx *ctx, uint32_t domid,
+ libxl_psr_type type, unsigned int target,
+ uint64_t *val)
+{
+ GC_INIT(ctx);
+ int rc = 0;
+ xc_psr_type xc_type = libxl__psr_type_to_libxc_psr_type(type);
+
+ if (xc_psr_get_domain_data(ctx->xch, domid, xc_type,
+ target, val)) {
+ libxl__psr_alloc_log_err_msg(gc, errno, type);
+ rc = ERROR_FAIL;
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+static void libxl__xc_hw_info_to_libxl_hw_info(
+ libxl_psr_feat_type type, xc_psr_hw_info *xc_info,
+ libxl_psr_hw_info *xl_info)
+{
+ switch (type) {
+ case LIBXL_PSR_FEAT_TYPE_CAT:
+ xl_info->u.cat.cos_max = xc_info->cat.cos_max;
+ xl_info->u.cat.cbm_len = xc_info->cat.cbm_len;
+ xl_info->u.cat.cdp_enabled = xc_info->cat.cdp_enabled;
+ break;
+ case LIBXL_PSR_FEAT_TYPE_MBA:
+ xl_info->u.mba.cos_max = xc_info->mba.cos_max;
+ xl_info->u.mba.thrtl_max = xc_info->mba.thrtl_max;
+ xl_info->u.mba.linear = xc_info->mba.linear;
+ break;
+ default:
+ assert(0);
+ }
+}
+
+int libxl_psr_get_hw_info(libxl_ctx *ctx, libxl_psr_feat_type type,
+ unsigned int lvl, unsigned int *nr,
+ libxl_psr_hw_info **info)
+{
+ GC_INIT(ctx);
+ int rc, nr_sockets;
+ unsigned int i = 0, socketid;
+ libxl_bitmap socketmap;
+ libxl_psr_hw_info *ptr;
+ xc_psr_feat_type xc_type;
+ xc_psr_hw_info hw_info;
+
+ libxl_bitmap_init(&socketmap);
+
+ xc_type = libxl__feat_type_to_libxc_feat_type(type, lvl);
+
+ rc = libxl__count_physical_sockets(gc, &nr_sockets);
+ if (rc) {
+ LOG(ERROR, "failed to get system socket count");
+ goto out;
+ }
+
+ libxl_socket_bitmap_alloc(ctx, &socketmap, nr_sockets);
+ rc = libxl_get_online_socketmap(ctx, &socketmap);
+ if (rc) {
+ LOGE(ERROR, "failed to get available sockets");
+ goto out;
+ }
+
+ ptr = libxl__malloc(NOGC, nr_sockets * sizeof(libxl_psr_hw_info));
+
+ libxl_for_each_set_bit(socketid, socketmap) {
+ ptr[i].id = socketid;
+ if (xc_psr_get_hw_info(ctx->xch, socketid, xc_type, &hw_info)) {
+ rc = ERROR_FAIL;
+ free(ptr);
+ goto out;
+ }
+
+ libxl__xc_hw_info_to_libxl_hw_info(type, &hw_info, &ptr[i]);
+
+ i++;
+ }
+
+ *info = ptr;
+ *nr = i;
+out:
+ libxl_bitmap_dispose(&socketmap);
+ GC_FREE;
+ return rc;
+}
+
+void libxl_psr_hw_info_list_free(libxl_psr_hw_info *list, unsigned int nr)
+{
+ unsigned int i;
+
+ for (i = 0; i < nr; i++)
+ libxl_psr_hw_info_dispose(&list[i]);
+ free(list);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2018 Aporeto
+ * Author Stefano Stabellini <stefano@aporeto.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+static int libxl__device_pvcallsif_setdefault(libxl__gc *gc, uint32_t domid,
+ libxl_device_pvcallsif *pvcallsif,
+ bool hotplug)
+{
+ return libxl__resolve_domid(gc, pvcallsif->backend_domname,
+ &pvcallsif->backend_domid);
+}
+
+static LIBXL_DEFINE_UPDATE_DEVID(pvcallsif)
+static LIBXL_DEFINE_DEVICE_FROM_TYPE(pvcallsif)
+
+#define libxl__add_pvcallsifs NULL
+#define libxl_device_pvcallsif_list NULL
+#define libxl_device_pvcallsif_compare NULL
+
+LIBXL_DEFINE_DEVICE_REMOVE(pvcallsif)
+
+DEFINE_DEVICE_TYPE_STRUCT(pvcallsif, PVCALLS);
--- /dev/null
+/*
+ * Copyright (C) 2011 Citrix Ltd.
+ * Author Anthony PERARD <anthony.perard@citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+/*
+ * This file implement a client for QMP (QEMU Monitor Protocol). For the
+ * Specification, see in the QEMU repository.
+ *
+ * WARNING - Do not trust QEMU when writing codes for new commands or when
+ * improving the client code.
+ */
+
+/*
+ * Logic used to send command to QEMU
+ *
+ * qmp_open():
+ * Will open a socket and connect to QEMU.
+ *
+ * qmp_next():
+ * Will read data sent by QEMU and then call qmp_handle_response() once a
+ * complete QMP message is received.
+ * The function return on timeout/error or once every data received as been
+ * processed.
+ *
+ * qmp_handle_response()
+ * This process json messages received from QEMU and update different list and
+ * may call callback function.
+ * `libxl__qmp_handler.wait_for_id` is reset once a message with this ID is
+ * processed.
+ * `libxl__qmp_handler.callback_list`: list with ID of command sent and
+ * optional assotiated callback function. The return value of a callback is
+ * set in context.
+ *
+ * qmp_send():
+ * Simply prepare a QMP command and send it to QEMU.
+ * It also add a `struct callback_id_pair` on the
+ * `libxl__qmp_handler.callback_list` via qmp_send_prepare().
+ *
+ * qmp_synchronous_send():
+ * This function calls qmp_send(), then wait for QEMU to reply to the command.
+ * The wait is done by calling qmp_next() over and over again until either
+ * there is a response for the command or there is an error.
+ *
+ * An ID can be set for each QMP command, this is set into
+ * `libxl__qmp_handler.wait_for_id`. qmp_next will check every response's ID
+ * again this field and change the value of the field once the ID is found.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <sys/un.h>
+
+#include <yajl/yajl_gen.h>
+
+#include "_libxl_list.h"
+#include "libxl_internal.h"
+
+/* #define DEBUG_RECEIVED */
+
+#ifdef DEBUG_RECEIVED
+# define DEBUG_REPORT_RECEIVED(dom, buf, len) \
+ LOGD(DEBUG, dom, "received: '%.*s'", len, buf)
+#else
+# define DEBUG_REPORT_RECEIVED(dom, buf, len) ((void)0)
+#endif
+
+#ifdef DEBUG_QMP_CLIENT
+# define LOG_QMP(f, ...) LOGD(DEBUG, ev->domid, f, ##__VA_ARGS__)
+#else
+# define LOG_QMP(f, ...)
+#endif
+
+/*
+ * QMP types & constant
+ */
+
+#define QMP_RECEIVE_BUFFER_SIZE 4096
+#define QMP_MAX_SIZE_RX_BUF MB(1)
+
+/*
+ * qmp_callback_t is call whenever a message from QMP contain the "id"
+ * associated with the callback.
+ * "tree" contain the JSON tree that is in "return" of a QMP message. If QMP
+ * sent an error message, "tree" will be NULL.
+ */
+typedef int (*qmp_callback_t)(libxl__qmp_handler *qmp,
+ const libxl__json_object *tree,
+ void *opaque);
+
+typedef struct qmp_request_context {
+ int rc;
+} qmp_request_context;
+
+typedef struct callback_id_pair {
+ int id;
+ qmp_callback_t callback;
+ void *opaque;
+ qmp_request_context *context;
+ LIBXL_STAILQ_ENTRY(struct callback_id_pair) next;
+} callback_id_pair;
+
+struct libxl__qmp_handler {
+ int qmp_fd;
+ bool connected;
+ time_t timeout;
+ /* wait_for_id will be used by the synchronous send function */
+ int wait_for_id;
+
+ char buffer[QMP_RECEIVE_BUFFER_SIZE + 1];
+
+ libxl_ctx *ctx;
+ uint32_t domid;
+
+ int last_id_used;
+ LIBXL_STAILQ_HEAD(callback_list, callback_id_pair) callback_list;
+ struct {
+ int major;
+ int minor;
+ int micro;
+ } version;
+};
+
+static int qmp_send(libxl__qmp_handler *qmp,
+ const char *cmd, libxl__json_object *args,
+ qmp_callback_t callback, void *opaque,
+ qmp_request_context *context);
+
+static const int QMP_SOCKET_CONNECT_TIMEOUT = 5;
+
+/*
+ * QMP callbacks functions
+ */
+
+static int qmp_capabilities_callback(libxl__qmp_handler *qmp,
+ const libxl__json_object *o, void *unused)
+{
+ qmp->connected = true;
+
+ return 0;
+}
+
+/*
+ * QMP commands
+ */
+
+static int enable_qmp_capabilities(libxl__qmp_handler *qmp)
+{
+ return qmp_send(qmp, "qmp_capabilities", NULL,
+ qmp_capabilities_callback, NULL, NULL);
+}
+
+/*
+ * Helpers
+ */
+
+static libxl__qmp_message_type qmp_response_type(const libxl__json_object *o)
+{
+ libxl__qmp_message_type type;
+ libxl__json_map_node *node = NULL;
+ int i = 0;
+
+ for (i = 0; (node = libxl__json_map_node_get(o, i)); i++) {
+ if (libxl__qmp_message_type_from_string(node->map_key, &type) == 0)
+ return type;
+ }
+
+ return LIBXL__QMP_MESSAGE_TYPE_INVALID;
+}
+
+static callback_id_pair *qmp_get_callback_from_id(libxl__qmp_handler *qmp,
+ const libxl__json_object *o)
+{
+ const libxl__json_object *id_object = libxl__json_map_get("id", o,
+ JSON_INTEGER);
+ int id = -1;
+ callback_id_pair *pp = NULL;
+
+ if (id_object) {
+ id = libxl__json_object_get_integer(id_object);
+
+ LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) {
+ if (pp->id == id) {
+ return pp;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void qmp_handle_error_response(libxl__gc *gc, libxl__qmp_handler *qmp,
+ const libxl__json_object *resp)
+{
+ callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
+
+ resp = libxl__json_map_get("error", resp, JSON_MAP);
+ resp = libxl__json_map_get("desc", resp, JSON_STRING);
+
+ if (pp) {
+ if (pp->callback) {
+ int rc = pp->callback(qmp, NULL, pp->opaque);
+ if (pp->context) {
+ pp->context->rc = rc;
+ }
+ }
+ if (pp->id == qmp->wait_for_id) {
+ /* tell that the id have been processed */
+ qmp->wait_for_id = 0;
+ }
+ LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair, next);
+ free(pp);
+ }
+
+ LOGD(ERROR, qmp->domid, "received an error message from QMP server: %s",
+ libxl__json_object_get_string(resp));
+}
+
+static int qmp_handle_response(libxl__gc *gc, libxl__qmp_handler *qmp,
+ const libxl__json_object *resp)
+{
+ libxl__qmp_message_type type = LIBXL__QMP_MESSAGE_TYPE_INVALID;
+
+ type = qmp_response_type(resp);
+ LOGD(DEBUG, qmp->domid, "message type: %s", libxl__qmp_message_type_to_string(type));
+
+ switch (type) {
+ case LIBXL__QMP_MESSAGE_TYPE_QMP: {
+ const libxl__json_object *o;
+ o = libxl__json_map_get("QMP", resp, JSON_MAP);
+ o = libxl__json_map_get("version", o, JSON_MAP);
+ o = libxl__json_map_get("qemu", o, JSON_MAP);
+ qmp->version.major = libxl__json_object_get_integer(
+ libxl__json_map_get("major", o, JSON_INTEGER));
+ qmp->version.minor = libxl__json_object_get_integer(
+ libxl__json_map_get("minor", o, JSON_INTEGER));
+ qmp->version.micro = libxl__json_object_get_integer(
+ libxl__json_map_get("micro", o, JSON_INTEGER));
+ LOGD(DEBUG, qmp->domid, "QEMU version: %d.%d.%d",
+ qmp->version.major, qmp->version.minor, qmp->version.micro);
+ /* On the greeting message from the server, enable QMP capabilities */
+ return enable_qmp_capabilities(qmp);
+ }
+ case LIBXL__QMP_MESSAGE_TYPE_RETURN: {
+ callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
+
+ if (pp) {
+ if (pp->callback) {
+ int rc = pp->callback(qmp,
+ libxl__json_map_get("return", resp, JSON_ANY),
+ pp->opaque);
+ if (pp->context) {
+ pp->context->rc = rc;
+ }
+ }
+ if (pp->id == qmp->wait_for_id) {
+ /* tell that the id have been processed */
+ qmp->wait_for_id = 0;
+ }
+ LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair,
+ next);
+ free(pp);
+ }
+ return 0;
+ }
+ case LIBXL__QMP_MESSAGE_TYPE_ERROR:
+ qmp_handle_error_response(gc, qmp, resp);
+ return -1;
+ case LIBXL__QMP_MESSAGE_TYPE_EVENT:
+ return 0;
+ case LIBXL__QMP_MESSAGE_TYPE_INVALID:
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * return values:
+ * < 0 if qemu's version < asked version
+ * = 0 if qemu's version == asked version
+ * > 0 if qemu's version > asked version
+ */
+static int qmp_ev_qemu_compare_version(libxl__ev_qmp *ev, int major,
+ int minor, int micro)
+{
+#define CHECK_VERSION(level) do { \
+ if (ev->qemu_version.level > (level)) return +1; \
+ if (ev->qemu_version.level < (level)) return -1; \
+} while (0)
+
+ CHECK_VERSION(major);
+ CHECK_VERSION(minor);
+ CHECK_VERSION(micro);
+
+#undef CHECK_VERSION
+
+ return 0;
+}
+
+/*
+ * Handler functions
+ */
+
+static libxl__qmp_handler *qmp_init_handler(libxl__gc *gc, uint32_t domid)
+{
+ libxl__qmp_handler *qmp = NULL;
+
+ qmp = calloc(1, sizeof (libxl__qmp_handler));
+ if (qmp == NULL) {
+ LOGED(ERROR, domid, "Failed to allocate qmp_handler");
+ return NULL;
+ }
+ qmp->ctx = CTX;
+ qmp->domid = domid;
+ qmp->timeout = 5;
+
+ LIBXL_STAILQ_INIT(&qmp->callback_list);
+
+ return qmp;
+}
+
+static int qmp_open(libxl__qmp_handler *qmp, const char *qmp_socket_path,
+ int timeout)
+{
+ GC_INIT(qmp->ctx);
+ int ret = -1;
+ int i = 0;
+ struct sockaddr_un addr;
+
+ qmp->qmp_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (qmp->qmp_fd < 0) {
+ goto out;
+ }
+ ret = libxl_fd_set_nonblock(qmp->ctx, qmp->qmp_fd, 1);
+ if (ret) {
+ ret = -1;
+ goto out;
+ }
+ ret = libxl_fd_set_cloexec(qmp->ctx, qmp->qmp_fd, 1);
+ if (ret) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = libxl__prepare_sockaddr_un(gc, &addr, qmp_socket_path, "QMP socket");
+ if (ret)
+ goto out;
+
+ do {
+ ret = connect(qmp->qmp_fd, (struct sockaddr *) &addr, sizeof(addr));
+ if (ret == 0)
+ break;
+ if (errno == ENOENT || errno == ECONNREFUSED) {
+ /* ENOENT : Socket may not have shown up yet
+ * ECONNREFUSED : Leftover socket hasn't been removed yet */
+ continue;
+ }
+ ret = -1;
+ goto out;
+ } while ((++i / 5 <= timeout) && (usleep(200 * 1000) <= 0));
+
+out:
+ if (ret == -1 && qmp->qmp_fd > -1) close(qmp->qmp_fd);
+
+ GC_FREE;
+ return ret;
+}
+
+static void qmp_close(libxl__qmp_handler *qmp)
+{
+ callback_id_pair *pp = NULL;
+ callback_id_pair *tmp = NULL;
+
+ close(qmp->qmp_fd);
+ LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) {
+ free(tmp);
+ tmp = pp;
+ }
+ free(tmp);
+}
+
+static int qmp_next(libxl__gc *gc, libxl__qmp_handler *qmp)
+{
+ ssize_t rd;
+ char *s = NULL;
+ char *s_end = NULL;
+
+ char *incomplete = NULL;
+ size_t incomplete_size = 0;
+ int rc = 0;
+
+ do {
+ fd_set rfds;
+ int ret = 0;
+ struct timeval timeout = {
+ .tv_sec = qmp->timeout,
+ .tv_usec = 0,
+ };
+
+ FD_ZERO(&rfds);
+ FD_SET(qmp->qmp_fd, &rfds);
+
+ ret = select(qmp->qmp_fd + 1, &rfds, NULL, NULL, &timeout);
+ if (ret == 0) {
+ LOGD(ERROR, qmp->domid, "timeout");
+ return -1;
+ } else if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ LOGED(ERROR, qmp->domid, "Select error");
+ return -1;
+ }
+
+ rd = read(qmp->qmp_fd, qmp->buffer, QMP_RECEIVE_BUFFER_SIZE);
+ if (rd == 0) {
+ LOGD(ERROR, qmp->domid, "Unexpected end of socket");
+ return -1;
+ } else if (rd < 0) {
+ LOGED(ERROR, qmp->domid, "Socket read error");
+ return rd;
+ }
+ qmp->buffer[rd] = '\0';
+
+ DEBUG_REPORT_RECEIVED(qmp->domid, qmp->buffer, (int)rd);
+
+ if (incomplete) {
+ size_t current_pos = s - incomplete;
+ incomplete = libxl__realloc(gc, incomplete,
+ incomplete_size + rd + 1);
+ strncat(incomplete + incomplete_size, qmp->buffer, rd);
+ s = incomplete + current_pos;
+ incomplete_size += rd;
+ s_end = incomplete + incomplete_size;
+ } else {
+ incomplete = libxl__strndup(gc, qmp->buffer, rd);
+ incomplete_size = rd;
+ s = incomplete;
+ s_end = s + rd;
+ rd = 0;
+ }
+
+ do {
+ char *end = NULL;
+
+ end = strstr(s, "\r\n");
+ if (end) {
+ libxl__json_object *o = NULL;
+
+ *end = '\0';
+
+ o = libxl__json_parse(gc, s);
+
+ if (o) {
+ rc = qmp_handle_response(gc, qmp, o);
+ } else {
+ LOGD(ERROR, qmp->domid, "Parse error of : %s", s);
+ return -1;
+ }
+
+ s = end + 2;
+ } else {
+ break;
+ }
+ } while (s < s_end);
+ } while (s < s_end);
+
+ return rc;
+}
+
+static char *qmp_prepare_cmd(libxl__gc *gc, const char *cmd,
+ const libxl__json_object *args,
+ int id)
+{
+ yajl_gen hand = NULL;
+ /* memory for 'buf' is owned by 'hand' */
+ const unsigned char *buf;
+ libxl_yajl_length len;
+ yajl_gen_status s;
+ char *ret = NULL;
+
+ hand = libxl_yajl_gen_alloc(NULL);
+
+ if (!hand) {
+ return NULL;
+ }
+
+#if HAVE_YAJL_V2
+ /* Disable beautify for data sent to QEMU */
+ yajl_gen_config(hand, yajl_gen_beautify, 0);
+#endif
+
+ yajl_gen_map_open(hand);
+ libxl__yajl_gen_asciiz(hand, "execute");
+ libxl__yajl_gen_asciiz(hand, cmd);
+ libxl__yajl_gen_asciiz(hand, "id");
+ yajl_gen_integer(hand, id);
+ if (args) {
+ libxl__yajl_gen_asciiz(hand, "arguments");
+ libxl__json_object_to_yajl_gen(gc, hand, args);
+ }
+ yajl_gen_map_close(hand);
+
+ s = yajl_gen_get_buf(hand, &buf, &len);
+
+ if (s != yajl_gen_status_ok)
+ goto out;
+
+ ret = libxl__sprintf(gc, "%*.*s\r\n", (int)len, (int)len, buf);
+
+out:
+ yajl_gen_free(hand);
+ return ret;
+}
+
+static char *qmp_send_prepare(libxl__gc *gc, libxl__qmp_handler *qmp,
+ const char *cmd, libxl__json_object *args,
+ qmp_callback_t callback, void *opaque,
+ qmp_request_context *context)
+{
+ char *buf;
+ callback_id_pair *elm;
+
+ buf = qmp_prepare_cmd(gc, cmd, args, ++qmp->last_id_used);
+
+ if (!buf) {
+ LOGD(ERROR, qmp->domid, "Failed to generate a qmp command");
+ goto out;
+ }
+
+ elm = malloc(sizeof (callback_id_pair));
+ if (elm == NULL) {
+ LOGED(ERROR, qmp->domid, "Failed to allocate a QMP callback");
+ goto out;
+ }
+ elm->id = qmp->last_id_used;
+ elm->callback = callback;
+ elm->opaque = opaque;
+ elm->context = context;
+ LIBXL_STAILQ_INSERT_TAIL(&qmp->callback_list, elm, next);
+
+ LOGD(DEBUG, qmp->domid, "next qmp command: '%s'", buf);
+
+out:
+ return buf;
+}
+
+static int qmp_send(libxl__qmp_handler *qmp,
+ const char *cmd, libxl__json_object *args,
+ qmp_callback_t callback, void *opaque,
+ qmp_request_context *context)
+{
+ char *buf = NULL;
+ int rc = -1;
+ GC_INIT(qmp->ctx);
+
+ buf = qmp_send_prepare(gc, qmp, cmd, args, callback, opaque, context);
+
+ if (buf == NULL) {
+ goto out;
+ }
+
+ if (libxl_write_exactly(qmp->ctx, qmp->qmp_fd, buf, strlen(buf),
+ "QMP command", "QMP socket"))
+ goto out;
+
+ rc = qmp->last_id_used;
+out:
+ GC_FREE;
+ return rc;
+}
+
+static int qmp_synchronous_send(libxl__qmp_handler *qmp, const char *cmd,
+ libxl__json_object *args,
+ qmp_callback_t callback, void *opaque,
+ int ask_timeout)
+{
+ int id = 0;
+ int ret = 0;
+ GC_INIT(qmp->ctx);
+ qmp_request_context context = { .rc = 0 };
+
+ id = qmp_send(qmp, cmd, args, callback, opaque, &context);
+ if (id <= 0) {
+ return ERROR_FAIL;
+ }
+ qmp->wait_for_id = id;
+
+ while (qmp->wait_for_id == id) {
+ if ((ret = qmp_next(gc, qmp)) < 0) {
+ break;
+ }
+ }
+
+ if (qmp->wait_for_id != id && ret == 0) {
+ ret = context.rc;
+ }
+
+ GC_FREE;
+
+ return ret;
+}
+
+static void qmp_free_handler(libxl__qmp_handler *qmp)
+{
+ free(qmp);
+}
+
+/*
+ * QMP Parameters Helpers
+ */
+static void qmp_parameters_common_add(libxl__gc *gc,
+ libxl__json_object **param,
+ const char *name,
+ libxl__json_object *obj)
+{
+ libxl__json_map_node *arg = NULL;
+
+ if (!*param) {
+ *param = libxl__json_object_alloc(gc, JSON_MAP);
+ }
+
+ GCNEW(arg);
+
+ arg->map_key = libxl__strdup(gc, name);
+ arg->obj = obj;
+
+ flexarray_append((*param)->u.map, arg);
+}
+
+void libxl__qmp_param_add_string(libxl__gc *gc,
+ libxl__json_object **param,
+ const char *name, const char *argument)
+{
+ libxl__json_object *obj;
+
+ obj = libxl__json_object_alloc(gc, JSON_STRING);
+ obj->u.string = libxl__strdup(gc, argument);
+
+ qmp_parameters_common_add(gc, param, name, obj);
+}
+
+void libxl__qmp_param_add_bool(libxl__gc *gc,
+ libxl__json_object **param,
+ const char *name, bool b)
+{
+ libxl__json_object *obj;
+
+ obj = libxl__json_object_alloc(gc, JSON_BOOL);
+ obj->u.b = b;
+ qmp_parameters_common_add(gc, param, name, obj);
+}
+
+void libxl__qmp_param_add_integer(libxl__gc *gc,
+ libxl__json_object **param,
+ const char *name, const int i)
+{
+ libxl__json_object *obj;
+
+ obj = libxl__json_object_alloc(gc, JSON_INTEGER);
+ obj->u.i = i;
+
+ qmp_parameters_common_add(gc, param, name, obj);
+}
+
+/*
+ * API
+ */
+
+libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc, uint32_t domid)
+{
+ int ret = 0;
+ libxl__qmp_handler *qmp = NULL;
+ char *qmp_socket;
+
+ qmp = qmp_init_handler(gc, domid);
+ if (!qmp) return NULL;
+
+ qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
+ if ((ret = qmp_open(qmp, qmp_socket, QMP_SOCKET_CONNECT_TIMEOUT)) < 0) {
+ LOGED(ERROR, domid, "Connection error");
+ qmp_free_handler(qmp);
+ return NULL;
+ }
+
+ LOGD(DEBUG, domid, "connected to %s", qmp_socket);
+
+ /* Wait for the response to qmp_capabilities */
+ while (!qmp->connected) {
+ if ((ret = qmp_next(gc, qmp)) < 0) {
+ break;
+ }
+ }
+
+ if (!qmp->connected) {
+ LOGD(ERROR, domid, "Failed to connect to QMP");
+ libxl__qmp_close(qmp);
+ return NULL;
+ }
+ return qmp;
+}
+
+void libxl__qmp_close(libxl__qmp_handler *qmp)
+{
+ if (!qmp)
+ return;
+ qmp_close(qmp);
+ qmp_free_handler(qmp);
+}
+
+void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid)
+{
+ char *qmp_socket;
+
+ qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
+ if (unlink(qmp_socket) == -1) {
+ if (errno != ENOENT) {
+ LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket);
+ }
+ }
+
+ qmp_socket = GCSPRINTF("%s/qmp-libxenstat-%d", libxl__run_dir_path(), domid);
+ if (unlink(qmp_socket) == -1) {
+ if (errno != ENOENT) {
+ LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket);
+ }
+ }
+}
+
+static int qmp_run_command(libxl__gc *gc, int domid,
+ const char *cmd, libxl__json_object *args,
+ qmp_callback_t callback, void *opaque)
+{
+ libxl__qmp_handler *qmp = NULL;
+ int rc = 0;
+
+ qmp = libxl__qmp_initialize(gc, domid);
+ if (!qmp)
+ return ERROR_FAIL;
+
+ rc = qmp_synchronous_send(qmp, cmd, args, callback, opaque, qmp->timeout);
+
+ libxl__qmp_close(qmp);
+ return rc;
+}
+
+int libxl__qmp_restore(libxl__gc *gc, int domid, const char *state_file)
+{
+ libxl__json_object *args = NULL;
+
+ libxl__qmp_param_add_string(gc, &args, "filename", state_file);
+
+ return qmp_run_command(gc, domid, "xen-load-devices-state", args,
+ NULL, NULL);
+}
+
+int libxl__qmp_resume(libxl__gc *gc, int domid)
+{
+ return qmp_run_command(gc, domid, "cont", NULL, NULL, NULL);
+}
+
+int libxl__qmp_nbd_server_start(libxl__gc *gc, int domid,
+ const char *host, const char *port)
+{
+ libxl__json_object *args = NULL;
+ libxl__json_object *addr = NULL;
+ libxl__json_object *data = NULL;
+
+ /* 'addr': {
+ * 'type': 'inet',
+ * 'data': {
+ * 'host': '$nbd_host',
+ * 'port': '$nbd_port'
+ * }
+ * }
+ */
+ libxl__qmp_param_add_string(gc, &data, "host", host);
+ libxl__qmp_param_add_string(gc, &data, "port", port);
+
+ libxl__qmp_param_add_string(gc, &addr, "type", "inet");
+ qmp_parameters_common_add(gc, &addr, "data", data);
+
+ qmp_parameters_common_add(gc, &args, "addr", addr);
+
+ return qmp_run_command(gc, domid, "nbd-server-start", args, NULL, NULL);
+}
+
+int libxl__qmp_nbd_server_add(libxl__gc *gc, int domid, const char *disk)
+{
+ libxl__json_object *args = NULL;
+
+ libxl__qmp_param_add_string(gc, &args, "device", disk);
+ libxl__qmp_param_add_bool(gc, &args, "writable", true);
+
+ return qmp_run_command(gc, domid, "nbd-server-add", args, NULL, NULL);
+}
+
+int libxl__qmp_start_replication(libxl__gc *gc, int domid, bool primary)
+{
+ libxl__json_object *args = NULL;
+
+ libxl__qmp_param_add_bool(gc, &args, "enable", true);
+ libxl__qmp_param_add_bool(gc, &args, "primary", primary);
+
+ return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL);
+}
+
+int libxl__qmp_query_xen_replication_status(libxl__gc *gc, int domid)
+{
+ return qmp_run_command(gc, domid, "query-xen-replication-status", NULL,
+ NULL, NULL);
+}
+
+int libxl__qmp_colo_do_checkpoint(libxl__gc *gc, int domid)
+{
+ return qmp_run_command(gc, domid, "xen-colo-do-checkpoint",
+ NULL, NULL, NULL);
+}
+
+int libxl__qmp_stop_replication(libxl__gc *gc, int domid, bool primary)
+{
+ libxl__json_object *args = NULL;
+
+ libxl__qmp_param_add_bool(gc, &args, "enable", false);
+ libxl__qmp_param_add_bool(gc, &args, "primary", primary);
+
+ return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL);
+}
+
+int libxl__qmp_nbd_server_stop(libxl__gc *gc, int domid)
+{
+ return qmp_run_command(gc, domid, "nbd-server-stop", NULL, NULL, NULL);
+}
+
+int libxl__qmp_x_blockdev_change(libxl__gc *gc, int domid, const char *parent,
+ const char *child, const char *node)
+{
+ libxl__json_object *args = NULL;
+
+ libxl__qmp_param_add_string(gc, &args, "parent", parent);
+ if (child)
+ libxl__qmp_param_add_string(gc, &args, "child", child);
+ if (node)
+ libxl__qmp_param_add_string(gc, &args, "node", node);
+
+ return qmp_run_command(gc, domid, "x-blockdev-change", args, NULL, NULL);
+}
+
+static int hmp_callback(libxl__qmp_handler *qmp,
+ const libxl__json_object *response,
+ void *opaque)
+{
+ char **output = opaque;
+ GC_INIT(qmp->ctx);
+ int rc;
+
+ rc = 0;
+ if (!output)
+ goto out;
+
+ *output = NULL;
+
+ if (libxl__json_object_is_string(response)) {
+ *output = libxl__strdup(NOGC, libxl__json_object_get_string(response));
+ goto out;
+ }
+
+ LOG(ERROR, "Response has unexpected format");
+ rc = ERROR_FAIL;
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl__qmp_hmp(libxl__gc *gc, int domid, const char *command_line,
+ char **output)
+{
+ libxl__json_object *args = NULL;
+
+ libxl__qmp_param_add_string(gc, &args, "command-line", command_line);
+
+ return qmp_run_command(gc, domid, "human-monitor-command", args,
+ hmp_callback, output);
+}
+
+
+typedef struct {
+ libxl__ev_qmp qmp;
+ char **output; /* user pointer */
+} qemu_monitor_command_state;
+
+static void qemu_monitor_command_done(libxl__egc *, libxl__ev_qmp *,
+ const libxl__json_object *response,
+ int rc);
+
+int libxl_qemu_monitor_command(libxl_ctx *ctx, uint32_t domid,
+ const char *command_line, char **output,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ qemu_monitor_command_state *qmcs;
+ libxl__json_object *args = NULL;
+ int rc;
+
+ if (!output) {
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ GCNEW(qmcs);
+ libxl__ev_qmp_init(&qmcs->qmp);
+ qmcs->qmp.ao = ao;
+ qmcs->qmp.domid = domid;
+ qmcs->qmp.payload_fd = -1;
+ qmcs->qmp.callback = qemu_monitor_command_done;
+ qmcs->output = output;
+ libxl__qmp_param_add_string(gc, &args, "command-line", command_line);
+ rc = libxl__ev_qmp_send(egc, &qmcs->qmp, "human-monitor-command", args);
+out:
+ if (rc) return AO_CREATE_FAIL(rc);
+ return AO_INPROGRESS;
+}
+
+static void qemu_monitor_command_done(libxl__egc *egc, libxl__ev_qmp *qmp,
+ const libxl__json_object *response,
+ int rc)
+{
+ STATE_AO_GC(qmp->ao);
+ qemu_monitor_command_state *qmcs = CONTAINER_OF(qmp, *qmcs, qmp);
+
+ if (rc) goto out;
+
+ if (!libxl__json_object_is_string(response)) {
+ rc = ERROR_QEMU_API;
+ LOGD(ERROR, qmp->domid, "Response has unexpected format");
+ goto out;
+ }
+
+ *(qmcs->output) =
+ libxl__strdup(NOGC, libxl__json_object_get_string(response));
+ rc = 0;
+
+out:
+ libxl__ev_qmp_dispose(gc, qmp);
+ libxl__ao_complete(egc, ao, rc);
+}
+
+/*
+ * Functions using libxl__ev_qmp
+ */
+
+static void dm_stopped(libxl__egc *egc, libxl__ev_qmp *ev,
+ const libxl__json_object *response, int rc);
+static void dm_state_fd_ready(libxl__egc *egc, libxl__ev_qmp *ev,
+ const libxl__json_object *response, int rc);
+static void dm_state_save_to_fdset(libxl__egc *egc, libxl__ev_qmp *ev, int fdset);
+static void dm_state_saved(libxl__egc *egc, libxl__ev_qmp *ev,
+ const libxl__json_object *response, int rc);
+
+/* calls dsps->callback_device_model_done when done */
+void libxl__qmp_suspend_save(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps)
+{
+ EGC_GC;
+ int rc;
+ libxl__ev_qmp *ev = &dsps->qmp;
+
+ ev->ao = dsps->ao;
+ ev->domid = dsps->domid;
+ ev->callback = dm_stopped;
+ ev->payload_fd = -1;
+
+ rc = libxl__ev_qmp_send(egc, ev, "stop", NULL);
+ if (rc)
+ goto error;
+
+ return;
+
+error:
+ dsps->callback_device_model_done(egc, dsps, rc);
+}
+
+static void dm_stopped(libxl__egc *egc, libxl__ev_qmp *ev,
+ const libxl__json_object *response, int rc)
+{
+ EGC_GC;
+ libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
+ const char *const filename = dsps->dm_savefile;
+ uint32_t dm_domid = libxl_get_stubdom_id(CTX, dsps->domid);
+
+ if (rc)
+ goto error;
+
+ if (dm_domid) {
+ /* see Linux stubdom interface in docs/stubdom.txt */
+ dm_state_save_to_fdset(egc, ev, 1);
+ return;
+ }
+
+ ev->payload_fd = open(filename, O_WRONLY | O_CREAT, 0600);
+ if (ev->payload_fd < 0) {
+ LOGED(ERROR, ev->domid,
+ "Failed to open file %s for QEMU", filename);
+ rc = ERROR_FAIL;
+ goto error;
+ }
+
+ ev->callback = dm_state_fd_ready;
+ rc = libxl__ev_qmp_send(egc, ev, "add-fd", NULL);
+ if (rc)
+ goto error;
+
+ return;
+
+error:
+ if (ev->payload_fd >= 0) {
+ close(ev->payload_fd);
+ libxl__remove_file(gc, filename);
+ ev->payload_fd = -1;
+ }
+ dsps->callback_device_model_done(egc, dsps, rc);
+}
+
+static void dm_state_fd_ready(libxl__egc *egc, libxl__ev_qmp *ev,
+ const libxl__json_object *response, int rc)
+{
+ EGC_GC;
+ int fdset;
+ const libxl__json_object *o;
+ libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
+
+ close(ev->payload_fd);
+ ev->payload_fd = -1;
+
+ if (rc)
+ goto error;
+
+ o = libxl__json_map_get("fdset-id", response, JSON_INTEGER);
+ if (!o) {
+ rc = ERROR_QEMU_API;
+ goto error;
+ }
+ fdset = libxl__json_object_get_integer(o);
+ dm_state_save_to_fdset(egc, ev, fdset);
+ return;
+
+error:
+ assert(rc);
+ libxl__remove_file(gc, dsps->dm_savefile);
+ dsps->callback_device_model_done(egc, dsps, rc);
+}
+
+static void dm_state_save_to_fdset(libxl__egc *egc, libxl__ev_qmp *ev, int fdset)
+{
+ EGC_GC;
+ int rc;
+ libxl__json_object *args = NULL;
+ libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
+
+ ev->callback = dm_state_saved;
+
+ /* The `live` parameter was added to QEMU 2.11. It signals QEMU that
+ * the save operation is for a live migration rather than for taking a
+ * snapshot. */
+ if (qmp_ev_qemu_compare_version(ev, 2, 11, 0) >= 0)
+ libxl__qmp_param_add_bool(gc, &args, "live", dsps->live);
+ QMP_PARAMETERS_SPRINTF(&args, "filename", "/dev/fdset/%d", fdset);
+ rc = libxl__ev_qmp_send(egc, ev, "xen-save-devices-state", args);
+ if (rc)
+ goto error;
+
+ return;
+
+error:
+ assert(rc);
+ if (!libxl_get_stubdom_id(CTX, dsps->domid))
+ libxl__remove_file(gc, dsps->dm_savefile);
+ dsps->callback_device_model_done(egc, dsps, rc);
+}
+
+static void dm_state_saved(libxl__egc *egc, libxl__ev_qmp *ev,
+ const libxl__json_object *response, int rc)
+{
+ EGC_GC;
+ libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
+
+ if (rc)
+ libxl__remove_file(gc, dsps->dm_savefile);
+
+ dsps->callback_device_model_done(egc, dsps, rc);
+}
+
+
+/* ------------ Implementation of libxl__ev_qmp ---------------- */
+
+/*
+ * Possible internal state compared to qmp_state:
+ *
+ * qmp_state External cfd efd id rx_buf* tx_buf* msg* lock
+ * disconnected Idle NULL Idle reset free free free Idle
+ * waiting_lock Active open Idle reset used free set Active
+ * connecting Active open IN reset used free set Acquired
+ * cap.neg Active open IN|OUT sent used cap_neg set Acquired
+ * cap.neg Active open IN sent used free set Acquired
+ * connected Connected open IN any used free free Acquired
+ * waiting_reply Active open IN|OUT sent used free set Acquired
+ * waiting_reply Active open IN|OUT sent used user's free Acquired
+ * waiting_reply Active open IN sent used free free Acquired
+ * broken[1] none[2] any Active any any any any any
+ *
+ * [1] When an internal function return an error, it can leave ev_qmp in a
+ * `broken` state but only if the caller is another internal function.
+ * That `broken` needs to be cleaned up, e.i. transitionned to the
+ * `disconnected` state, before the control of ev_qmp is released outsides
+ * of ev_qmp implementation.
+ *
+ * [2] This internal state should not be visible externally, see [1].
+ *
+ * Possible buffers states:
+ * - receiving buffer:
+ * free used
+ * rx_buf NULL NULL or allocated
+ * rx_buf_size 0 allocation size of `rx_buf`
+ * rx_buf_used 0 <= rx_buf_size, actual data in the buffer
+ * - transmitting buffer:
+ * free used
+ * tx_buf NULL contains data
+ * tx_buf_len 0 size of data
+ * tx_buf_off 0 <= tx_buf_len, data already sent
+ * - queued user command:
+ * free set
+ * msg NULL contains data
+ * msg_id 0 id assoctiated with the command in `msg`
+ *
+ * - Allowed internal state transition:
+ * disconnected -> waiting_lock
+ * waiting_lock -> connecting
+ * connection -> capability_negotiation
+ * capability_negotiation/connected -> waiting_reply
+ * waiting_reply -> connected
+ * any -> broken
+ * broken -> disconnected
+ * any -> disconnected
+ *
+ * The QEMU Machine Protocol (QMP) specification can be found in the QEMU
+ * repository:
+ * https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/interop/qmp-spec.txt
+ */
+
+/* prototypes */
+
+static void qmp_ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd,
+ int fd, short events, short revents);
+static int qmp_ev_callback_writable(libxl__gc *gc,
+ libxl__ev_qmp *ev, int fd);
+static int qmp_ev_callback_readable(libxl__egc *egc,
+ libxl__ev_qmp *ev, int fd);
+static int qmp_ev_get_next_msg(libxl__egc *egc, libxl__ev_qmp *ev,
+ libxl__json_object **o_r);
+static int qmp_ev_handle_message(libxl__egc *egc,
+ libxl__ev_qmp *ev,
+ const libxl__json_object *resp);
+
+/* helpers */
+
+static void qmp_ev_ensure_reading_writing(libxl__gc *gc, libxl__ev_qmp *ev)
+ /* Update the state of `efd` to match the permited state
+ * on entry: !disconnected */
+{
+ short events = POLLIN;
+
+ if (ev->state == qmp_state_waiting_lock)
+ /* We can't modify the efd yet, as it isn't registered. */
+ return;
+
+ if (ev->tx_buf)
+ events |= POLLOUT;
+ else if ((ev->state == qmp_state_waiting_reply) && ev->msg)
+ events |= POLLOUT;
+
+ libxl__ev_fd_modify(gc, &ev->efd, events);
+}
+
+static void qmp_ev_set_state(libxl__gc *gc, libxl__ev_qmp *ev,
+ libxl__qmp_state new_state)
+ /* on entry: !broken and !disconnected */
+{
+ switch (new_state) {
+ case qmp_state_disconnected:
+ break;
+ case qmp_state_waiting_lock:
+ assert(ev->state == qmp_state_disconnected);
+ break;
+ case qmp_state_connecting:
+ assert(ev->state == qmp_state_waiting_lock);
+ break;
+ case qmp_state_capability_negotiation:
+ assert(ev->state == qmp_state_connecting);
+ break;
+ case qmp_state_waiting_reply:
+ assert(ev->state == qmp_state_capability_negotiation ||
+ ev->state == qmp_state_connected);
+ break;
+ case qmp_state_connected:
+ assert(ev->state == qmp_state_waiting_reply);
+ break;
+ }
+
+ ev->state = new_state;
+
+ qmp_ev_ensure_reading_writing(gc, ev);
+}
+
+static void qmp_ev_tx_buf_clear(libxl__ev_qmp *ev)
+{
+ ev->tx_buf = NULL;
+ ev->tx_buf_len = 0;
+ ev->tx_buf_off = 0;
+}
+
+static int qmp_error_class_to_libxl_error_code(libxl__gc *gc,
+ const char *eclass)
+{
+ const libxl_enum_string_table *t = libxl_error_string_table;
+ const char skip[] = "QMP_";
+ const size_t skipl = sizeof(skip) - 1;
+
+ /* compare "QMP_GENERIC_ERROR" from libxl_error to "GenericError"
+ * generated by the QMP server */
+
+ for (; t->s; t++) {
+ const char *s = eclass;
+ const char *se = t->s;
+ if (strncasecmp(t->s, skip, skipl))
+ continue;
+ se += skipl;
+ while (*s && *se) {
+ /* skip underscores */
+ if (*se == '_') {
+ se++;
+ continue;
+ }
+ if (tolower(*s) != tolower(*se))
+ break;
+ s++, se++;
+ }
+ if (!*s && !*se)
+ return t->v;
+ }
+
+ LOG(ERROR, "Unknown QMP error class '%s'", eclass);
+ return ERROR_UNKNOWN_QMP_ERROR;
+}
+
+/* Setup connection */
+
+static void qmp_ev_lock_aquired(libxl__egc *, libxl__ev_slowlock *,
+ int rc);
+static void lock_error_callback(libxl__egc *, libxl__ev_immediate *);
+
+static int qmp_ev_connect(libxl__egc *egc, libxl__ev_qmp *ev)
+ /* disconnected -> waiting_lock/connecting but with `msg` free
+ * on error: broken */
+{
+ EGC_GC;
+ int fd;
+ int rc;
+
+ /* Convenience aliases */
+ libxl__ev_slowlock *lock = &ev->lock;
+
+ assert(ev->state == qmp_state_disconnected);
+
+ libxl__carefd_begin();
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ ev->cfd = libxl__carefd_opened(CTX, fd);
+ if (!ev->cfd) {
+ LOGED(ERROR, ev->domid, "socket() failed");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = libxl_fd_set_nonblock(CTX, libxl__carefd_fd(ev->cfd), 1);
+ if (rc)
+ goto out;
+
+ qmp_ev_set_state(gc, ev, qmp_state_waiting_lock);
+
+ lock->ao = ev->ao;
+ lock->domid = ev->domid;
+ lock->callback = qmp_ev_lock_aquired;
+ libxl__ev_slowlock_lock(egc, &ev->lock);
+
+ return 0;
+
+out:
+ return rc;
+}
+
+static void qmp_ev_lock_aquired(libxl__egc *egc, libxl__ev_slowlock *lock,
+ int rc)
+ /* waiting_lock (with `lock' Acquired) -> connecting
+ * on error: broken */
+{
+ libxl__ev_qmp *ev = CONTAINER_OF(lock, *ev, lock);
+ EGC_GC;
+ const char *qmp_socket_path;
+ struct sockaddr_un un;
+ int r;
+
+ if (rc) goto out;
+
+ qmp_socket_path = libxl__qemu_qmp_path(gc, ev->domid);
+
+ LOGD(DEBUG, ev->domid, "Connecting to %s", qmp_socket_path);
+
+ rc = libxl__prepare_sockaddr_un(gc, &un, qmp_socket_path,
+ "QMP socket");
+ if (rc)
+ goto out;
+
+ r = connect(libxl__carefd_fd(ev->cfd),
+ (struct sockaddr *) &un, sizeof(un));
+ if (r && errno != EINPROGRESS) {
+ LOGED(ERROR, ev->domid, "Failed to connect to QMP socket %s",
+ qmp_socket_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__ev_fd_register(gc, &ev->efd, qmp_ev_fd_callback,
+ libxl__carefd_fd(ev->cfd), POLLIN);
+ if (rc)
+ goto out;
+
+ qmp_ev_set_state(gc, ev, qmp_state_connecting);
+
+ return;
+
+out:
+ /* An error occurred and we need to let the caller know. At this
+ * point, we can only do so via the callback. Unfortunately, the
+ * callback of libxl__ev_slowlock_lock() might be called synchronously,
+ * but libxl__ev_qmp_send() promise that it will not call the callback
+ * synchronously. So we have to arrange to call the callback
+ * asynchronously. */
+ ev->rc = rc;
+ ev->ei.callback = lock_error_callback;
+ libxl__ev_immediate_register(egc, &ev->ei);
+}
+
+static void lock_error_callback(libxl__egc *egc, libxl__ev_immediate *ei)
+ /* broken -> disconnected */
+{
+ EGC_GC;
+ libxl__ev_qmp *ev = CONTAINER_OF(ei, *ev, ei);
+
+ int rc = ev->rc;
+
+ /* On error, deallocate all private resources */
+ libxl__ev_qmp_dispose(gc, ev);
+
+ /* And tell libxl__ev_qmp user about the error */
+ ev->callback(egc, ev, NULL, rc); /* must be last */
+}
+
+/* QMP FD callbacks */
+
+static void qmp_ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd,
+ int fd, short events, short revents)
+ /* On entry, ev_fd is (of course) Active. The ev_qmp may be in any
+ * state where this is permitted. qmp_ev_fd_callback will do the work
+ * necessary to make progress, depending on the current state, and make
+ * the appropriate state transitions and callbacks. */
+{
+ libxl__ev_qmp *ev = CONTAINER_OF(ev_fd, *ev, efd);
+ STATE_AO_GC(ev->ao);
+ int rc;
+
+ if (revents & (POLLHUP|POLLERR)) {
+ int r;
+ int error_val = 0;
+ socklen_t opt_len = sizeof(error_val);
+
+ r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error_val, &opt_len);
+ if (r)
+ LOGED(ERROR, ev->domid, "getsockopt failed");
+ if (!r && error_val) {
+ errno = error_val;
+ LOGED(ERROR, ev->domid, "error on QMP socket");
+ } else {
+ LOGD(ERROR, ev->domid,
+ "received POLLHUP|POLLERR from QMP socket");
+ }
+ rc = ERROR_PROTOCOL_ERROR_QMP;
+ goto error;
+ }
+
+ if (revents & ~(POLLIN|POLLOUT)) {
+ LOGD(ERROR, ev->domid,
+ "unexpected poll event 0x%x on QMP socket (expected POLLIN "
+ "and/or POLLOUT)",
+ revents);
+ rc = ERROR_FAIL;
+ goto error;
+ }
+
+ if (revents & POLLOUT) {
+ rc = qmp_ev_callback_writable(gc, ev, fd);
+ if (rc)
+ goto error;
+ }
+
+ if (revents & POLLIN) {
+ rc = qmp_ev_callback_readable(egc, ev, fd);
+ if (rc < 0)
+ goto error;
+ if (rc == 1) {
+ /* user callback has been called */
+ return;
+ }
+ }
+
+ return;
+
+error:
+ assert(rc);
+
+ LOGD(ERROR, ev->domid,
+ "Error happened with the QMP connection to QEMU");
+
+ /* On error, deallocate all private ressources */
+ libxl__ev_qmp_dispose(gc, ev);
+
+ /* And tell libxl__ev_qmp user about the error */
+ ev->callback(egc, ev, NULL, rc); /* must be last */
+}
+
+static int qmp_ev_callback_writable(libxl__gc *gc,
+ libxl__ev_qmp *ev, int fd)
+ /* on entry: !disconnected
+ * on return, one of these state transition:
+ * waiting_reply (with msg set) -> waiting_reply (with msg free)
+ * tx_buf set -> same state or tx_buf free
+ * on error: broken */
+{
+ int rc;
+ ssize_t r;
+
+ if (ev->state == qmp_state_waiting_reply) {
+ if (ev->msg) {
+ assert(!ev->tx_buf);
+ ev->tx_buf = ev->msg;
+ ev->tx_buf_len = strlen(ev->msg);
+ ev->tx_buf_off = 0;
+ ev->id = ev->msg_id;
+ ev->msg = NULL;
+ ev->msg_id = 0;
+ }
+ }
+
+ assert(ev->tx_buf);
+
+ LOG_QMP("sending: '%.*s'", (int)ev->tx_buf_len, ev->tx_buf);
+
+ /*
+ * We will send a file descriptor associated with a command on the
+ * first byte of this command.
+ */
+ if (ev->state == qmp_state_waiting_reply &&
+ ev->payload_fd >= 0 &&
+ ev->tx_buf_off == 0) {
+
+ rc = libxl__sendmsg_fds(gc, fd, ev->tx_buf[ev->tx_buf_off],
+ 1, &ev->payload_fd, "QMP socket");
+ /* Check for EWOULDBLOCK, and return to try again later */
+ if (rc == ERROR_NOT_READY)
+ return 0;
+ if (rc)
+ return rc;
+ ev->tx_buf_off++;
+ }
+
+ while (ev->tx_buf_off < ev->tx_buf_len) {
+ ssize_t max_write = ev->tx_buf_len - ev->tx_buf_off;
+ r = write(fd, ev->tx_buf + ev->tx_buf_off, max_write);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EWOULDBLOCK)
+ break;
+ LOGED(ERROR, ev->domid, "failed to write to QMP socket");
+ return ERROR_FAIL;
+ }
+ assert(r > 0 && r <= max_write);
+ ev->tx_buf_off += r;
+ }
+
+ if (ev->tx_buf_off == ev->tx_buf_len)
+ qmp_ev_tx_buf_clear(ev);
+
+ qmp_ev_ensure_reading_writing(gc, ev);
+
+ return 0;
+}
+
+static int qmp_ev_callback_readable(libxl__egc *egc,
+ libxl__ev_qmp *ev, int fd)
+ /*
+ * Return values:
+ * < 0 libxl error code
+ * 0 success
+ * 1 success, but a user callback has been called,
+ * `ev` should not be used anymore.
+ *
+ * This function will update the rx buffer and possibly update
+ * ev->state:
+ * connecting -> capability_negotiation
+ * capability_negotiation -> waiting_reply
+ * waiting_reply -> connected
+ * on error: broken
+ */
+{
+ STATE_AO_GC(ev->ao);
+ int rc;
+ ssize_t r;
+
+ while (1) {
+ while (1) {
+ libxl__json_object *o = NULL;
+
+ /* parse rx buffer to find one json object */
+ rc = qmp_ev_get_next_msg(egc, ev, &o);
+ if (rc == ERROR_NOTFOUND)
+ break;
+ else if (rc)
+ return rc;
+
+ /* Must be last and return when the user callback is called */
+ rc = qmp_ev_handle_message(egc, ev, o);
+ if (rc)
+ /* returns both rc values -ERROR_* and 1 */
+ return rc;
+ }
+
+ /* Check if the buffer still have space, or increase size */
+ if (ev->rx_buf_size - ev->rx_buf_used < QMP_RECEIVE_BUFFER_SIZE) {
+ size_t newsize = ev->rx_buf_size * 2 + QMP_RECEIVE_BUFFER_SIZE;
+
+ if (newsize > QMP_MAX_SIZE_RX_BUF) {
+ LOGD(ERROR, ev->domid,
+ "QMP receive buffer is too big (%zu > %lld)",
+ newsize, QMP_MAX_SIZE_RX_BUF);
+ return ERROR_BUFFERFULL;
+ }
+ ev->rx_buf_size = newsize;
+ ev->rx_buf = libxl__realloc(gc, ev->rx_buf, ev->rx_buf_size);
+ }
+
+ r = read(fd, ev->rx_buf + ev->rx_buf_used,
+ ev->rx_buf_size - ev->rx_buf_used);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EWOULDBLOCK)
+ break;
+ LOGED(ERROR, ev->domid, "error reading QMP socket");
+ return ERROR_FAIL;
+ }
+
+ if (r == 0) {
+ LOGD(ERROR, ev->domid, "Unexpected EOF on QMP socket");
+ return ERROR_PROTOCOL_ERROR_QMP;
+ }
+
+ LOG_QMP("received %ldB: '%.*s'", r,
+ (int)r, ev->rx_buf + ev->rx_buf_used);
+
+ ev->rx_buf_used += r;
+ assert(ev->rx_buf_used <= ev->rx_buf_size);
+ }
+
+ return 0;
+}
+
+/* Handle messages received from QMP server */
+
+static int qmp_ev_get_next_msg(libxl__egc *egc, libxl__ev_qmp *ev,
+ libxl__json_object **o_r)
+ /* Find a JSON object and store it in o_r.
+ * return ERROR_NOTFOUND if no object is found.
+ *
+ * !disconnected -> same state (with rx buffer updated)
+ */
+{
+ STATE_AO_GC(ev->ao);
+ size_t len;
+ char *end = NULL;
+ const char eom[] = "\r\n";
+ const size_t eoml = sizeof(eom) - 1;
+ libxl__json_object *o = NULL;
+
+ if (!ev->rx_buf_used)
+ return ERROR_NOTFOUND;
+
+ /* Search for the end of a QMP message: "\r\n" */
+ end = memmem(ev->rx_buf, ev->rx_buf_used, eom, eoml);
+ if (!end)
+ return ERROR_NOTFOUND;
+ len = (end - ev->rx_buf) + eoml;
+
+ LOG_QMP("parsing %luB: '%.*s'", len, (int)len, ev->rx_buf);
+
+ /* Replace \r by \0 so that libxl__json_parse can use strlen */
+ ev->rx_buf[len - eoml] = '\0';
+ o = libxl__json_parse(gc, ev->rx_buf);
+
+ if (!o) {
+ LOGD(ERROR, ev->domid, "Parse error");
+ return ERROR_PROTOCOL_ERROR_QMP;
+ }
+
+ ev->rx_buf_used -= len;
+ memmove(ev->rx_buf, ev->rx_buf + len, ev->rx_buf_used);
+
+ LOG_QMP("JSON object received: %s", JSON(o));
+
+ *o_r = o;
+
+ return 0;
+}
+
+static int qmp_ev_parse_error_messages(libxl__egc *egc,
+ libxl__ev_qmp *ev,
+ const libxl__json_object *resp);
+
+static int qmp_ev_handle_message(libxl__egc *egc,
+ libxl__ev_qmp *ev,
+ const libxl__json_object *resp)
+ /*
+ * This function will handle every messages sent by the QMP server.
+ * Return values:
+ * < 0 libxl error code
+ * 0 success
+ * 1 success, but a user callback has been called,
+ * `ev` should not be used anymore.
+ *
+ * Possible state changes:
+ * connecting -> capability_negotiation
+ * capability_negotiation -> waiting_reply
+ * waiting_reply -> waiting_reply/connected
+ *
+ * on error: broken
+ */
+{
+ STATE_AO_GC(ev->ao);
+ int id;
+ char *buf;
+ int rc = 0;
+ const libxl__json_object *o;
+ const libxl__json_object *response;
+ libxl__qmp_message_type type = qmp_response_type(resp);
+
+ switch (type) {
+ case LIBXL__QMP_MESSAGE_TYPE_QMP:
+ /* greeting message */
+
+ if (ev->state != qmp_state_connecting) {
+ LOGD(ERROR, ev->domid,
+ "Unexpected greeting message received");
+ return ERROR_PROTOCOL_ERROR_QMP;
+ }
+
+ /*
+ * Store advertised QEMU version
+ * { "QMP": { "version": {
+ * "qemu": { "major": int, "minor": int, "micro": int } } } }
+ */
+ o = libxl__json_map_get("QMP", resp, JSON_MAP);
+ o = libxl__json_map_get("version", o, JSON_MAP);
+ o = libxl__json_map_get("qemu", o, JSON_MAP);
+#define GRAB_VERSION(level) do { \
+ ev->qemu_version.level = libxl__json_object_get_integer( \
+ libxl__json_map_get(#level, o, JSON_INTEGER)); \
+ } while (0)
+ GRAB_VERSION(major);
+ GRAB_VERSION(minor);
+ GRAB_VERSION(micro);
+#undef GRAB_VERSION
+ LOGD(DEBUG, ev->domid, "QEMU version: %d.%d.%d",
+ ev->qemu_version.major,
+ ev->qemu_version.minor,
+ ev->qemu_version.micro);
+
+ /* Prepare next message to send */
+ assert(!ev->tx_buf);
+ ev->id = ev->next_id++;
+ buf = qmp_prepare_cmd(gc, "qmp_capabilities", NULL, ev->id);
+ if (!buf) {
+ LOGD(ERROR, ev->domid,
+ "Failed to generate qmp_capabilities command");
+ return ERROR_FAIL;
+ }
+ ev->tx_buf = buf;
+ ev->tx_buf_len = strlen(buf);
+ ev->tx_buf_off = 0;
+ qmp_ev_set_state(gc, ev, qmp_state_capability_negotiation);
+
+ return 0;
+
+ case LIBXL__QMP_MESSAGE_TYPE_RETURN:
+ case LIBXL__QMP_MESSAGE_TYPE_ERROR:
+ /*
+ * Reply to a command (success/error) or server error
+ *
+ * In this cases, we are parsing two possibles responses:
+ * - success:
+ * { "return": json-value, "id": int }
+ * - error:
+ * { "error": { "class": string, "desc": string }, "id": int }
+ */
+
+ o = libxl__json_map_get("id", resp, JSON_INTEGER);
+ if (!o) {
+ /*
+ * If "id" isn't present, an error occur on the server before
+ * it has read the "id" provided by libxl.
+ *
+ * We deliberately squash all errors into
+ * ERROR_PROTOCOL_ERROR_QMP as qmp_ev_parse_error_messages may
+ * also return ERROR_QMP_* but those are reserved for errors
+ * return by the caller's command.
+ */
+ qmp_ev_parse_error_messages(egc, ev, resp);
+ return ERROR_PROTOCOL_ERROR_QMP;
+ }
+
+ id = libxl__json_object_get_integer(o);
+
+ if (id != ev->id) {
+ LOGD(ERROR, ev->domid,
+ "Message from QEMU with unexpected id %d: %s",
+ id, JSON(resp));
+ return ERROR_PROTOCOL_ERROR_QMP;
+ }
+
+ switch (ev->state) {
+ case qmp_state_capability_negotiation:
+ if (type != LIBXL__QMP_MESSAGE_TYPE_RETURN) {
+ LOGD(ERROR, ev->domid,
+ "Error during capability negotiation: %s",
+ JSON(resp));
+ return ERROR_PROTOCOL_ERROR_QMP;
+ }
+ qmp_ev_set_state(gc, ev, qmp_state_waiting_reply);
+ return 0;
+ case qmp_state_waiting_reply:
+ if (type == LIBXL__QMP_MESSAGE_TYPE_RETURN) {
+ response = libxl__json_map_get("return", resp, JSON_ANY);
+ rc = 0;
+ } else {
+ /* error message */
+ response = NULL;
+ rc = qmp_ev_parse_error_messages(egc, ev, resp);
+ }
+ qmp_ev_set_state(gc, ev, qmp_state_connected);
+ ev->callback(egc, ev, response, rc); /* must be last */
+ return 1;
+ default:
+ LOGD(ERROR, ev->domid, "Unexpected message: %s", JSON(resp));
+ return ERROR_PROTOCOL_ERROR_QMP;
+ }
+ return 0;
+
+ case LIBXL__QMP_MESSAGE_TYPE_EVENT:
+ /* Events are ignored */
+ return 0;
+
+ case LIBXL__QMP_MESSAGE_TYPE_INVALID:
+ LOGD(ERROR, ev->domid, "Unexpected message received: %s",
+ JSON(resp));
+ return ERROR_PROTOCOL_ERROR_QMP;
+
+ default:
+ abort();
+ }
+
+ return 0;
+}
+
+static int qmp_ev_parse_error_messages(libxl__egc *egc,
+ libxl__ev_qmp *ev,
+ const libxl__json_object *resp)
+ /* no state change */
+{
+ STATE_AO_GC(ev->ao);
+ int rc;
+ const char *s;
+ const libxl__json_object *o;
+ const libxl__json_object *err;
+
+ /*
+ * { "error": { "class": string, "desc": string } }
+ */
+
+ err = libxl__json_map_get("error", resp, JSON_MAP);
+
+ o = libxl__json_map_get("class", err, JSON_STRING);
+ if (!o) {
+ LOGD(ERROR, ev->domid,
+ "Protocol error: missing 'class' member in error message");
+ return ERROR_PROTOCOL_ERROR_QMP;
+ }
+ s = libxl__json_object_get_string(o);
+ if (s)
+ rc = qmp_error_class_to_libxl_error_code(gc, s);
+ else
+ rc = ERROR_PROTOCOL_ERROR_QMP;
+
+ o = libxl__json_map_get("desc", err, JSON_STRING);
+ if (!o) {
+ LOGD(ERROR, ev->domid,
+ "Protocol error: missing 'desc' member in error message");
+ return ERROR_PROTOCOL_ERROR_QMP;
+ }
+ s = libxl__json_object_get_string(o);
+ if (s)
+ LOGD(ERROR, ev->domid, "%s", s);
+ else
+ LOGD(ERROR, ev->domid, "Received unexpected error: %s",
+ JSON(resp));
+ return rc;
+}
+
+/*
+ * libxl__ev_qmp_*
+ */
+
+void libxl__ev_qmp_init(libxl__ev_qmp *ev)
+ /* disconnected -> disconnected */
+{
+ /* Start with an message ID that is obviously generated by libxl
+ * "xlq\0" */
+ ev->next_id = 0x786c7100;
+
+ ev->cfd = NULL;
+ libxl__ev_fd_init(&ev->efd);
+ ev->state = qmp_state_disconnected;
+ ev->id = 0;
+
+ ev->rx_buf = NULL;
+ ev->rx_buf_size = ev->rx_buf_used = 0;
+ qmp_ev_tx_buf_clear(ev);
+
+ ev->msg = NULL;
+ ev->msg_id = 0;
+
+ ev->qemu_version.major = -1;
+ ev->qemu_version.minor = -1;
+ ev->qemu_version.micro = -1;
+
+ libxl__ev_qmplock_init(&ev->lock);
+ ev->rc = 0;
+}
+
+int libxl__ev_qmp_send(libxl__egc *egc, libxl__ev_qmp *ev,
+ const char *cmd, libxl__json_object *args)
+ /* disconnected -> waiting_lock/connecting
+ * connected -> waiting_reply (with msg set)
+ * on error: disconnected */
+{
+ STATE_AO_GC(ev->ao);
+ int rc;
+
+ LOGD(DEBUG, ev->domid, " ev %p, cmd '%s'", ev, cmd);
+
+ assert(ev->state == qmp_state_disconnected ||
+ ev->state == qmp_state_connected);
+ assert(cmd);
+
+ /* Connect to QEMU if not already connected */
+ if (ev->state == qmp_state_disconnected) {
+ rc = qmp_ev_connect(egc, ev);
+ if (rc)
+ goto error;
+ }
+
+ /* Prepare user command */
+ ev->msg_id = ev->next_id++;
+ ev->msg = qmp_prepare_cmd(gc, cmd, args, ev->msg_id);
+ if (!ev->msg) {
+ LOGD(ERROR, ev->domid, "Failed to generate caller's command %s",
+ cmd);
+ rc = ERROR_FAIL;
+ goto error;
+ }
+ if (ev->state == qmp_state_connected) {
+ qmp_ev_set_state(gc, ev, qmp_state_waiting_reply);
+ }
+
+ return 0;
+
+error:
+ libxl__ev_qmp_dispose(gc, ev);
+ return rc;
+}
+
+void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev)
+ /* * -> disconnected */
+{
+ LOGD(DEBUG, ev->domid, " ev %p", ev);
+
+ libxl__ev_fd_deregister(gc, &ev->efd);
+ libxl__carefd_close(ev->cfd);
+ libxl__ev_slowlock_dispose(gc, &ev->lock);
+
+ libxl__ev_qmp_init(ev);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Yang Hongyang <hongyang.yang@easystack.cn>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+extern const libxl__checkpoint_device_instance_ops remus_device_nic;
+extern const libxl__checkpoint_device_instance_ops remus_device_drbd_disk;
+static const libxl__checkpoint_device_instance_ops *remus_ops[] = {
+ &remus_device_nic,
+ &remus_device_drbd_disk,
+ NULL,
+};
+
+/*----- helper functions -----*/
+
+static int init_device_subkind(libxl__checkpoint_devices_state *cds)
+{
+ /* init device subkind-specific state in the libxl ctx */
+ int rc;
+ STATE_AO_GC(cds->ao);
+
+ if (libxl__netbuffer_enabled(gc)) {
+ rc = init_subkind_nic(cds);
+ if (rc) goto out;
+ }
+
+ rc = init_subkind_drbd_disk(cds);
+ if (rc) goto out;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds)
+{
+ /* cleanup device subkind-specific state in the libxl ctx */
+ STATE_AO_GC(cds->ao);
+
+ if (libxl__netbuffer_enabled(gc))
+ cleanup_subkind_nic(cds);
+
+ cleanup_subkind_drbd_disk(cds);
+}
+
+/*-------------------- Remus setup and teardown ---------------------*/
+
+static void remus_setup_done(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds, int rc);
+static void remus_setup_failed(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds, int rc);
+static void remus_checkpoint_stream_written(
+ libxl__egc *egc, libxl__stream_write_state *sws, int rc);
+static void libxl__remus_domain_suspend_callback(void *data);
+static void libxl__remus_domain_resume_callback(void *data);
+static void libxl__remus_domain_save_checkpoint_callback(void *data);
+
+void libxl__remus_setup(libxl__egc *egc, libxl__remus_state *rs)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(rs, *dss, rs);
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *const cds = &dss->cds;
+ const libxl_domain_remus_info *const info = dss->remus;
+ libxl__srm_save_autogen_callbacks *const callbacks =
+ &dss->sws.shs.callbacks.save.a;
+
+ STATE_AO_GC(dss->ao);
+
+ if (libxl_defbool_val(info->netbuf)) {
+ if (!libxl__netbuffer_enabled(gc)) {
+ LOGD(ERROR, dss->domid,
+ "Remus: No support for network buffering");
+ goto out;
+ }
+ cds->device_kind_flags |= (1 << LIBXL__DEVICE_KIND_VIF);
+ }
+
+ if (libxl_defbool_val(info->diskbuf))
+ cds->device_kind_flags |= (1 << LIBXL__DEVICE_KIND_VBD);
+
+ cds->ao = ao;
+ cds->domid = dss->domid;
+ cds->callback = remus_setup_done;
+ cds->ops = remus_ops;
+ cds->concrete_data = rs;
+ rs->interval = info->interval;
+
+ if (init_device_subkind(cds)) {
+ LOGD(ERROR, dss->domid,
+ "Remus: failed to init device subkind");
+ goto out;
+ }
+
+ dss->sws.checkpoint_callback = remus_checkpoint_stream_written;
+
+ callbacks->suspend = libxl__remus_domain_suspend_callback;
+ callbacks->postcopy = libxl__remus_domain_resume_callback;
+ callbacks->checkpoint = libxl__remus_domain_save_checkpoint_callback;
+
+ libxl__checkpoint_devices_setup(egc, cds);
+ return;
+
+out:
+ dss->callback(egc, dss, ERROR_FAIL);
+}
+
+static void remus_setup_done(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds, int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+ STATE_AO_GC(dss->ao);
+
+ if (!rc) {
+ libxl__domain_save(egc, dss);
+ return;
+ }
+
+ LOGD(ERROR, dss->domid, "Remus: failed to setup device, rc %d", rc);
+ cds->callback = remus_setup_failed;
+ libxl__checkpoint_devices_teardown(egc, cds);
+}
+
+static void remus_setup_failed(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds, int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+ STATE_AO_GC(dss->ao);
+
+ if (rc)
+ LOGD(ERROR, dss->domid,
+ "Remus: failed to teardown device after setup failed, rc %d", rc);
+
+ cleanup_device_subkind(cds);
+
+ dss->callback(egc, dss, rc);
+}
+
+static void remus_teardown_done(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+void libxl__remus_teardown(libxl__egc *egc,
+ libxl__remus_state *rs,
+ int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(rs, *dss, rs);
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *const cds = &dss->cds;
+
+ EGC_GC;
+
+ LOGD(WARN, dss->domid, "Remus: Domain suspend terminated with rc %d,"
+ " teardown Remus devices...", rc);
+ cds->callback = remus_teardown_done;
+ libxl__checkpoint_devices_teardown(egc, cds);
+}
+
+static void remus_teardown_done(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+ STATE_AO_GC(dss->ao);
+
+ if (rc)
+ LOGD(ERROR, dss->domid, "Remus: failed to teardown device,"
+ " rc %d", rc);
+
+ cleanup_device_subkind(cds);
+
+ dss->callback(egc, dss, rc);
+}
+
+/*---------------------- remus callbacks (save) -----------------------*/
+
+static void remus_domain_suspend_callback_common_done(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps, int ok);
+static void remus_devices_postsuspend_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+static void remus_devices_preresume_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+
+static void libxl__remus_domain_suspend_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__egc *egc = shs->egc;
+ libxl__domain_save_state *dss = shs->caller_state;
+ libxl__domain_suspend_state *dsps = &dss->dsps;
+
+ dsps->callback_common_done = remus_domain_suspend_callback_common_done;
+ libxl__domain_suspend(egc, dsps);
+}
+
+static void remus_domain_suspend_callback_common_done(libxl__egc *egc,
+ libxl__domain_suspend_state *dsps, int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
+
+ if (rc)
+ goto out;
+
+ libxl__checkpoint_devices_state *const cds = &dss->cds;
+ cds->callback = remus_devices_postsuspend_cb;
+ libxl__checkpoint_devices_postsuspend(egc, cds);
+ return;
+
+out:
+ dss->rc = rc;
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+static void remus_devices_postsuspend_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+
+ if (rc)
+ goto out;
+
+ rc = 0;
+
+out:
+ if (rc)
+ dss->rc = rc;
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+static void libxl__remus_domain_resume_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__egc *egc = shs->egc;
+ libxl__domain_save_state *dss = shs->caller_state;
+ STATE_AO_GC(dss->ao);
+
+ libxl__checkpoint_devices_state *const cds = &dss->cds;
+ cds->callback = remus_devices_preresume_cb;
+ libxl__checkpoint_devices_preresume(egc, cds);
+}
+
+static void remus_devices_preresume_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+ STATE_AO_GC(dss->ao);
+
+ if (rc)
+ goto out;
+
+ /* Resumes the domain and the device model */
+ rc = libxl__domain_resume_deprecated(gc, dss->domid, /* Fast Suspend */1);
+ if (rc)
+ goto out;
+
+ rc = 0;
+
+out:
+ if (rc)
+ dss->rc = rc;
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+/*----- remus asynchronous checkpoint callback -----*/
+
+static void remus_devices_commit_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc);
+static void remus_next_checkpoint(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc);
+
+static void libxl__remus_domain_save_checkpoint_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__domain_save_state *dss = shs->caller_state;
+ libxl__egc *egc = shs->egc;
+ STATE_AO_GC(dss->ao);
+
+ libxl__stream_write_start_checkpoint(egc, &dss->sws);
+}
+
+static void remus_checkpoint_stream_written(
+ libxl__egc *egc, libxl__stream_write_state *sws, int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(sws, *dss, sws);
+
+ /* Convenience aliases */
+ libxl__checkpoint_devices_state *const cds = &dss->cds;
+
+ STATE_AO_GC(dss->ao);
+
+ if (rc) {
+ LOGD(ERROR, dss->domid, "Failed to save device model."
+ " Terminating Remus..");
+ goto out;
+ }
+
+ cds->callback = remus_devices_commit_cb;
+ libxl__checkpoint_devices_commit(egc, cds);
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+static void remus_devices_commit_cb(libxl__egc *egc,
+ libxl__checkpoint_devices_state *cds,
+ int rc)
+{
+ libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+
+ STATE_AO_GC(dss->ao);
+
+ if (rc) {
+ LOGD(ERROR, dss->domid, "Failed to do device commit op."
+ " Terminating Remus..");
+ goto out;
+ }
+
+ /*
+ * At this point, we have successfully checkpointed the guest and
+ * committed it at the backup. We'll come back after the checkpoint
+ * interval to checkpoint the guest again. Until then, let the guest
+ * continue execution.
+ */
+
+ /* Set checkpoint interval timeout */
+ rc = libxl__ev_time_register_rel(ao, &dss->rs.checkpoint_timeout,
+ remus_next_checkpoint,
+ dss->rs.interval);
+
+ if (rc)
+ goto out;
+
+ return;
+
+out:
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+static void remus_next_checkpoint(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ libxl__domain_save_state *dss =
+ CONTAINER_OF(ev, *dss, rs.checkpoint_timeout);
+
+ STATE_AO_GC(dss->ao);
+
+ if (rc == ERROR_TIMEDOUT) /* As intended */
+ rc = 0;
+
+ /*
+ * Time to checkpoint the guest again. We return 1 to libxc
+ * (xc_domain_save.c). in order to continue executing the infinite loop
+ * (suspend, checkpoint, resume) in xc_domain_save().
+ */
+
+ if (rc)
+ dss->rc = rc;
+
+ libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+/*---------------------- remus callbacks (restore) -----------------------*/
+
+/*----- remus asynchronous checkpoint callback -----*/
+
+static void remus_checkpoint_stream_done(
+ libxl__egc *egc, libxl__stream_read_state *srs, int rc);
+
+static void libxl__remus_domain_restore_checkpoint_callback(void *data)
+{
+ libxl__save_helper_state *shs = data;
+ libxl__domain_create_state *dcs = shs->caller_state;
+ libxl__egc *egc = shs->egc;
+ STATE_AO_GC(dcs->ao);
+
+ libxl__stream_read_start_checkpoint(egc, &dcs->srs);
+}
+
+static void remus_checkpoint_stream_done(
+ libxl__egc *egc, libxl__stream_read_state *stream, int rc)
+{
+ libxl__xc_domain_saverestore_async_callback_done(egc, &stream->shs, rc);
+}
+
+void libxl__remus_restore_setup(libxl__egc *egc,
+ libxl__domain_create_state *dcs)
+{
+ /* Convenience aliases */
+ libxl__srm_restore_autogen_callbacks *const callbacks =
+ &dcs->srs.shs.callbacks.restore.a;
+
+ callbacks->checkpoint = libxl__remus_domain_restore_checkpoint_callback;
+ dcs->srs.checkpoint_callback = remus_checkpoint_stream_done;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2014 FUJITSU LIMITED
+ * Author Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*** drbd implementation ***/
+const int DRBD_SEND_CHECKPOINT = 20;
+const int DRBD_WAIT_CHECKPOINT_ACK = 30;
+
+typedef struct libxl__remus_drbd_disk {
+ int ctl_fd;
+ int ackwait;
+} libxl__remus_drbd_disk;
+
+int init_subkind_drbd_disk(libxl__checkpoint_devices_state *cds)
+{
+ libxl__remus_state *rs = cds->concrete_data;
+ STATE_AO_GC(cds->ao);
+
+ rs->drbd_probe_script = GCSPRINTF("%s/block-drbd-probe",
+ libxl__xen_script_dir_path());
+
+ return 0;
+}
+
+void cleanup_subkind_drbd_disk(libxl__checkpoint_devices_state *cds)
+{
+ return;
+}
+
+/*----- match(), setup() and teardown() -----*/
+
+/* callbacks */
+static void match_async_exec_cb(libxl__egc *egc,
+ libxl__async_exec_state *aes,
+ int rc, int status);
+
+/* implementations */
+
+static void match_async_exec(libxl__egc *egc, libxl__checkpoint_device *dev);
+
+static void drbd_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+ STATE_AO_GC(dev->cds->ao);
+
+ match_async_exec(egc, dev);
+}
+
+static void match_async_exec(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+ int arraysize, nr = 0, rc;
+ const libxl_device_disk *disk = dev->backend_dev;
+ libxl__async_exec_state *aes = &dev->aodev.aes;
+ libxl__remus_state *rs = dev->cds->concrete_data;
+ STATE_AO_GC(dev->cds->ao);
+
+ /* setup env & args */
+ arraysize = 1;
+ GCNEW_ARRAY(aes->env, arraysize);
+ aes->env[nr++] = NULL;
+ assert(nr <= arraysize);
+
+ arraysize = 3;
+ nr = 0;
+ GCNEW_ARRAY(aes->args, arraysize);
+ aes->args[nr++] = rs->drbd_probe_script;
+ aes->args[nr++] = disk->pdev_path;
+ aes->args[nr++] = NULL;
+ assert(nr <= arraysize);
+
+ aes->ao = dev->cds->ao;
+ aes->what = GCSPRINTF("%s %s", aes->args[0], aes->args[1]);
+ aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
+ aes->callback = match_async_exec_cb;
+ aes->stdfds[0] = -1;
+ aes->stdfds[1] = -1;
+ aes->stdfds[2] = -1;
+
+ rc = libxl__async_exec_start(aes);
+ if (rc)
+ goto out;
+
+ return;
+
+out:
+ dev->aodev.rc = rc;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+static void match_async_exec_cb(libxl__egc *egc,
+ libxl__async_exec_state *aes,
+ int rc, int status)
+{
+ libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
+ libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
+ libxl__remus_drbd_disk *drbd_disk;
+ const libxl_device_disk *disk = dev->backend_dev;
+
+ STATE_AO_GC(aodev->ao);
+
+ if (rc)
+ goto out;
+
+ if (status) {
+ rc = ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH;
+ /* BUG: seems to assume that any exit status means `no match' */
+ /* BUG: exit status will have been logged as an error */
+ goto out;
+ }
+
+ /* ops matched */
+ dev->matched = true;
+
+ GCNEW(drbd_disk);
+ dev->concrete_data = drbd_disk;
+ drbd_disk->ackwait = 0;
+ drbd_disk->ctl_fd = open(disk->pdev_path, O_RDONLY);
+ if (drbd_disk->ctl_fd < 0) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+static void drbd_teardown(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+ libxl__remus_drbd_disk *drbd_disk = dev->concrete_data;
+ STATE_AO_GC(dev->cds->ao);
+
+ close(drbd_disk->ctl_fd);
+ dev->aodev.rc = 0;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+/*----- checkpointing APIs -----*/
+
+/* callbacks */
+static void checkpoint_async_call_done(libxl__egc *egc,
+ libxl__ev_child *child,
+ pid_t pid, int status);
+
+/* API implementations */
+
+/* this op will not wait and block, so implement as sync op */
+static void drbd_postsuspend(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+ STATE_AO_GC(dev->cds->ao);
+
+ libxl__remus_drbd_disk *rdd = dev->concrete_data;
+
+ if (!rdd->ackwait) {
+ if (ioctl(rdd->ctl_fd, DRBD_SEND_CHECKPOINT, 0) <= 0)
+ rdd->ackwait = 1;
+ }
+
+ dev->aodev.rc = 0;
+ dev->aodev.callback(egc, &dev->aodev);
+}
+
+
+static void drbd_preresume_async(libxl__checkpoint_device *dev);
+
+static void drbd_preresume(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+ ASYNC_CALL(egc, dev->cds->ao, &dev->aodev.child, dev,
+ drbd_preresume_async,
+ checkpoint_async_call_done);
+}
+
+static void drbd_preresume_async(libxl__checkpoint_device *dev)
+{
+ libxl__remus_drbd_disk *rdd = dev->concrete_data;
+ int ackwait = rdd->ackwait;
+
+ if (ackwait) {
+ ioctl(rdd->ctl_fd, DRBD_WAIT_CHECKPOINT_ACK, 0);
+ ackwait = 0;
+ }
+
+ _exit(ackwait);
+}
+
+static void checkpoint_async_call_done(libxl__egc *egc,
+ libxl__ev_child *child,
+ pid_t pid, int status)
+{
+ int rc;
+ libxl__ao_device *aodev = CONTAINER_OF(child, *aodev, child);
+ libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
+ libxl__remus_drbd_disk *rdd = dev->concrete_data;
+
+ STATE_AO_GC(aodev->ao);
+
+ if (!WIFEXITED(status)) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rdd->ackwait = WEXITSTATUS(status);
+ rc = 0;
+
+out:
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+const libxl__checkpoint_device_instance_ops remus_device_drbd_disk = {
+ .kind = LIBXL__DEVICE_KIND_VBD,
+ .setup = drbd_setup,
+ .teardown = drbd_teardown,
+ .postsuspend = drbd_postsuspend,
+ .preresume = drbd_preresume,
+};
--- /dev/null
+/*
+ * Copyright (C) 2012 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+/* stream_fd is as from the caller (eventually, the application).
+ * It may be 0, 1 or 2, in which case we need to dup it elsewhere.
+ * The actual fd value is not included in the supplied argnums; rather
+ * it will be automatically supplied by run_helper as the 2nd argument.
+ *
+ * preserve_fds are fds that the caller is intending to pass to the
+ * helper so which need cloexec clearing. They may not be 0, 1 or 2.
+ * An entry may be -1 in which case it will be ignored.
+ */
+static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs,
+ const char *mode_arg,
+ int stream_fd, int back_channel_fd,
+ const int *preserve_fds, int num_preserve_fds,
+ const unsigned long *argnums, int num_argnums);
+
+static void helper_failed(libxl__egc*, libxl__save_helper_state *shs, int rc);
+static void helper_stop(libxl__egc *egc, libxl__ao_abortable*, int rc);
+static void helper_stdout_readable(libxl__egc *egc, libxl__ev_fd *ev,
+ int fd, short events, short revents);
+static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
+ pid_t pid, int status);
+static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs);
+
+/*----- entrypoints -----*/
+
+void libxl__xc_domain_restore(libxl__egc *egc, libxl__domain_create_state *dcs,
+ libxl__save_helper_state *shs)
+{
+ STATE_AO_GC(dcs->ao);
+
+ /* Convenience aliases */
+ const uint32_t domid = dcs->guest_domid;
+ const int restore_fd = dcs->libxc_fd;
+ const int send_back_fd = dcs->send_back_fd;
+ libxl__domain_build_state *const state = &dcs->build_state;
+
+ unsigned cbflags =
+ libxl__srm_callout_enumcallbacks_restore(&shs->callbacks.restore.a);
+
+ const unsigned long argnums[] = {
+ domid,
+ state->store_port,
+ state->store_domid, state->console_port,
+ state->console_domid,
+ cbflags, dcs->restore_params.checkpointed_stream,
+ };
+
+ shs->ao = ao;
+ shs->domid = domid;
+ shs->recv_callback = libxl__srm_callout_received_restore;
+ if (dcs->restore_params.checkpointed_stream ==
+ LIBXL_CHECKPOINTED_STREAM_COLO)
+ shs->completion_callback = libxl__colo_restore_teardown;
+ else
+ shs->completion_callback = libxl__xc_domain_restore_done;
+ shs->caller_state = dcs;
+ shs->need_results = 1;
+
+ run_helper(egc, shs, "--restore-domain", restore_fd, send_back_fd, 0, 0,
+ argnums, ARRAY_SIZE(argnums));
+}
+
+void libxl__xc_domain_save(libxl__egc *egc, libxl__domain_save_state *dss,
+ libxl__save_helper_state *shs)
+{
+ STATE_AO_GC(dss->ao);
+
+ unsigned cbflags =
+ libxl__srm_callout_enumcallbacks_save(&shs->callbacks.save.a);
+
+ const unsigned long argnums[] = {
+ dss->domid, dss->xcflags, cbflags,
+ dss->checkpointed_stream,
+ };
+
+ shs->ao = ao;
+ shs->domid = dss->domid;
+ shs->recv_callback = libxl__srm_callout_received_save;
+ shs->completion_callback = libxl__xc_domain_save_done;
+ shs->caller_state = dss;
+ shs->need_results = 0;
+
+ run_helper(egc, shs, "--save-domain", dss->fd, dss->recv_fd,
+ NULL, 0,
+ argnums, ARRAY_SIZE(argnums));
+ return;
+}
+
+
+void libxl__xc_domain_saverestore_async_callback_done(libxl__egc *egc,
+ libxl__save_helper_state *shs, int return_value)
+{
+ shs->egc = egc;
+ libxl__srm_callout_sendreply(return_value, shs);
+ shs->egc = 0;
+}
+
+void libxl__save_helper_init(libxl__save_helper_state *shs)
+{
+ libxl__ao_abortable_init(&shs->abrt);
+ libxl__ev_fd_init(&shs->readable);
+ libxl__ev_child_init(&shs->child);
+}
+
+/*----- helper execution -----*/
+
+/* This function can not fail. */
+static int dup_cloexec(libxl__gc *gc, int fd, const char *what)
+{
+ int dup_fd = fd;
+
+ if (fd <= 2) {
+ dup_fd = dup(fd);
+ if (dup_fd < 0) {
+ LOGE(ERROR,"dup %s", what);
+ exit(-1);
+ }
+ }
+ libxl_fd_set_cloexec(CTX, dup_fd, 0);
+
+ return dup_fd;
+}
+
+/*
+ * Both save and restore share four parameters:
+ * 1) Path to libxl-save-helper.
+ * 2) --[restore|save]-domain.
+ * 3) stream file descriptor.
+ * 4) back channel file descriptor.
+ * n) save/restore specific parameters.
+ * 5) A \0 at the end.
+ */
+#define HELPER_NR_ARGS 5
+static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs,
+ const char *mode_arg,
+ int stream_fd, int back_channel_fd,
+ const int *preserve_fds, int num_preserve_fds,
+ const unsigned long *argnums, int num_argnums)
+{
+ STATE_AO_GC(shs->ao);
+ const char *args[HELPER_NR_ARGS + num_argnums];
+ const char **arg = args;
+ int i, rc;
+
+ /* Resources we must free */
+ libxl__carefd *childs_pipes[2] = { 0,0 };
+
+ /* Convenience aliases */
+ const uint32_t domid = shs->domid;
+
+ shs->rc = 0;
+ shs->completed = 0;
+ shs->pipes[0] = shs->pipes[1] = 0;
+ libxl__save_helper_init(shs);
+
+ shs->abrt.ao = shs->ao;
+ shs->abrt.callback = helper_stop;
+ rc = libxl__ao_abortable_register(&shs->abrt);
+ if (rc) goto out;
+
+ shs->stdin_what = GCSPRINTF("domain %"PRIu32" save/restore helper"
+ " stdin pipe", domid);
+ shs->stdout_what = GCSPRINTF("domain %"PRIu32" save/restore helper"
+ " stdout pipe", domid);
+
+ *arg++ = getenv("LIBXL_SAVE_HELPER") ?: LIBEXEC_BIN "/" "libxl-save-helper";
+ *arg++ = mode_arg;
+ const char **stream_fd_arg = arg++;
+ const char **back_channel_fd_arg = arg++;
+ for (i=0; i<num_argnums; i++)
+ *arg++ = GCSPRINTF("%lu", argnums[i]);
+ *arg++ = 0;
+ assert(arg == args + ARRAY_SIZE(args));
+
+ libxl__carefd_begin();
+ int childfd;
+ for (childfd=0; childfd<2; childfd++) {
+ /* Setting up the pipe for the child's fd childfd */
+ int fds[2];
+ if (libxl_pipe(CTX,fds)) {
+ rc = ERROR_FAIL;
+ libxl__carefd_unlock();
+ goto out;
+ }
+ int childs_end = childfd==0 ? 0 /*read*/ : 1 /*write*/;
+ int our_end = childfd==0 ? 1 /*write*/ : 0 /*read*/;
+ childs_pipes[childfd] = libxl__carefd_record(CTX, fds[childs_end]);
+ shs->pipes[childfd] = libxl__carefd_record(CTX, fds[our_end]);
+ }
+ libxl__carefd_unlock();
+
+ pid_t pid = libxl__ev_child_fork(gc, &shs->child, helper_exited);
+ if (!pid) {
+ stream_fd = dup_cloexec(gc, stream_fd, "migration stream fd");
+ *stream_fd_arg = GCSPRINTF("%d", stream_fd);
+
+ if (back_channel_fd >= 0)
+ back_channel_fd = dup_cloexec(gc, back_channel_fd,
+ "migration back channel fd");
+ *back_channel_fd_arg = GCSPRINTF("%d", back_channel_fd);
+
+ for (i=0; i<num_preserve_fds; i++)
+ if (preserve_fds[i] >= 0) {
+ assert(preserve_fds[i] > 2);
+ libxl_fd_set_cloexec(CTX, preserve_fds[i], 0);
+ }
+
+ libxl__exec(gc,
+ libxl__carefd_fd(childs_pipes[0]),
+ libxl__carefd_fd(childs_pipes[1]),
+ -1,
+ args[0], (char**)args, 0);
+ }
+
+ libxl__carefd_close(childs_pipes[0]);
+ libxl__carefd_close(childs_pipes[1]);
+
+ rc = libxl__ev_fd_register(gc, &shs->readable, helper_stdout_readable,
+ libxl__carefd_fd(shs->pipes[1]), POLLIN|POLLPRI);
+ if (rc) goto out;
+ return;
+
+ out:
+ libxl__carefd_close(childs_pipes[0]);
+ libxl__carefd_close(childs_pipes[1]);
+ helper_failed(egc, shs, rc);;
+}
+
+static void helper_failed(libxl__egc *egc, libxl__save_helper_state *shs,
+ int rc)
+{
+ STATE_AO_GC(shs->ao);
+
+ if (!shs->rc)
+ shs->rc = rc;
+
+ libxl__ev_fd_deregister(gc, &shs->readable);
+
+ if (!libxl__save_helper_inuse(shs)) {
+ helper_done(egc, shs);
+ return;
+ }
+
+ libxl__kill(gc, shs->child.pid, SIGKILL, "save/restore helper");
+}
+
+static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
+{
+ libxl__save_helper_state *shs = CONTAINER_OF(abrt, *shs, abrt);
+ STATE_AO_GC(shs->ao);
+
+ if (!libxl__save_helper_inuse(shs)) {
+ helper_failed(egc, shs, rc);
+ return;
+ }
+
+ if (!shs->rc)
+ shs->rc = rc;
+
+ libxl__kill(gc, shs->child.pid, SIGTERM, "save/restore helper");
+}
+
+void libxl__save_helper_abort(libxl__egc *egc,
+ libxl__save_helper_state *shs)
+{
+ helper_stop(egc, &shs->abrt, ERROR_FAIL);
+}
+
+static void helper_stdout_readable(libxl__egc *egc, libxl__ev_fd *ev,
+ int fd, short events, short revents)
+{
+ libxl__save_helper_state *shs = CONTAINER_OF(ev, *shs, readable);
+ STATE_AO_GC(shs->ao);
+ int rc, errnoval;
+
+ if (revents & (POLLERR|POLLPRI)) {
+ LOGD(ERROR, shs->domid, "%s signaled POLLERR|POLLPRI (%#x)",
+ shs->stdout_what, revents);
+ rc = ERROR_FAIL;
+ out:
+ /* this is here because otherwise we bypass the decl of msg[] */
+ helper_failed(egc, shs, rc);
+ return;
+ }
+
+ uint16_t msglen;
+ errnoval = libxl_read_exactly(CTX, fd, &msglen, sizeof(msglen),
+ shs->stdout_what, "ipc msg header");
+ if (errnoval) { rc = ERROR_FAIL; goto out; }
+
+ unsigned char msg[msglen];
+ errnoval = libxl_read_exactly(CTX, fd, msg, msglen,
+ shs->stdout_what, "ipc msg body");
+ if (errnoval) { rc = ERROR_FAIL; goto out; }
+
+ shs->egc = egc;
+ shs->recv_callback(msg, msglen, shs);
+ shs->egc = 0;
+ return;
+}
+
+static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
+ pid_t pid, int status)
+{
+ libxl__save_helper_state *shs = CONTAINER_OF(ch, *shs, child);
+ STATE_AO_GC(shs->ao);
+
+ /* Convenience aliases */
+ const uint32_t domid = shs->domid;
+
+ const char *what =
+ GCSPRINTF("domain %"PRIu32" save/restore helper", domid);
+
+ if (status) {
+ libxl_report_child_exitstatus(CTX, XTL_ERROR, what, pid, status);
+ if (!shs->rc)
+ shs->rc = ERROR_FAIL;
+ }
+
+ if (shs->need_results) {
+ if (!shs->rc) {
+ LOGD(ERROR,shs->domid,"%s exited without providing results",what);
+ shs->rc = ERROR_FAIL;
+ }
+ }
+
+ if (!shs->completed) {
+ if (!shs->rc) {
+ LOGD(ERROR,shs->domid,"%s exited without signaling completion",what);
+ shs->rc = ERROR_FAIL;
+ }
+ }
+
+ helper_done(egc, shs);
+ return;
+}
+
+static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs)
+{
+ STATE_AO_GC(shs->ao);
+
+ libxl__ao_abortable_deregister(&shs->abrt);
+ libxl__ev_fd_deregister(gc, &shs->readable);
+ libxl__carefd_close(shs->pipes[0]); shs->pipes[0] = 0;
+ libxl__carefd_close(shs->pipes[1]); shs->pipes[1] = 0;
+ assert(!libxl__save_helper_inuse(shs));
+
+ shs->egc = egc;
+ shs->completion_callback(egc, shs->caller_state,
+ shs->rc, shs->retval, shs->errnoval);
+ shs->egc = 0;
+}
+
+/*----- generic helpers for the autogenerated code -----*/
+
+const libxl__srm_save_autogen_callbacks*
+libxl__srm_callout_get_callbacks_save(void *user)
+{
+ libxl__save_helper_state *shs = user;
+ return &shs->callbacks.save.a;
+}
+
+const libxl__srm_restore_autogen_callbacks*
+libxl__srm_callout_get_callbacks_restore(void *user)
+{
+ libxl__save_helper_state *shs = user;
+ return &shs->callbacks.restore.a;
+}
+
+void libxl__srm_callout_sendreply(int r, void *user)
+{
+ libxl__save_helper_state *shs = user;
+ libxl__egc *egc = shs->egc;
+ STATE_AO_GC(shs->ao);
+ int errnoval;
+
+ errnoval = libxl_write_exactly(CTX, libxl__carefd_fd(shs->pipes[0]),
+ &r, sizeof(r), shs->stdin_what,
+ "callback return value");
+ if (errnoval)
+ helper_failed(egc, shs, ERROR_FAIL);
+}
+
+void libxl__srm_callout_callback_log(uint32_t level, uint32_t errnoval,
+ const char *context, const char *formatted, void *user)
+{
+ libxl__save_helper_state *shs = user;
+ STATE_AO_GC(shs->ao);
+ xtl_log(CTX->lg, level, errnoval, context, "%s", formatted);
+}
+
+void libxl__srm_callout_callback_progress(const char *context,
+ const char *doing_what, unsigned long done,
+ unsigned long total, void *user)
+{
+ libxl__save_helper_state *shs = user;
+ STATE_AO_GC(shs->ao);
+ xtl_progress(CTX->lg, context, doing_what, done, total);
+}
+
+int libxl__srm_callout_callback_complete(int retval, int errnoval,
+ void *user)
+{
+ libxl__save_helper_state *shs = user;
+ STATE_AO_GC(shs->ao);
+
+ shs->completed = 1;
+ shs->retval = retval;
+ shs->errnoval = errnoval;
+ libxl__ev_fd_deregister(gc, &shs->readable);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2012 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+/*
+ * The libxl-save-helper utility speaks a protocol to its caller for
+ * the callbacks. The protocol is as follows.
+ *
+ * The helper talks on stdin and stdout, in binary in machine
+ * endianness. The helper speaks first, and only when it has a
+ * callback to make. It writes a 16-bit number being the message
+ * length, and then the message body.
+ *
+ * Each message starts with a 16-bit number indicating which of the
+ * messages it is, and then some arguments in a binary marshalled form.
+ * If the callback does not need a reply (it returns void), the helper
+ * just continues. Otherwise the helper waits for its caller to send a
+ * single int which is to be the return value from the callback.
+ *
+ * Where feasible the stubs and callbacks have prototypes identical to
+ * those required by xc_domain_save and xc_domain_restore, so that the
+ * autogenerated functions can be used/provided directly.
+ *
+ * The actual messages are in the array @msgs in libxl_save_msgs_gen.pl
+ */
+
+#include "libxl_osdeps.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "libxl.h"
+#include "libxl_utils.h"
+
+#include "xenctrl.h"
+#include "xenguest.h"
+#include "_libxl_save_msgs_helper.h"
+
+/*----- logger -----*/
+
+__attribute__((format(printf, 5, 0)))
+static void tellparent_vmessage(xentoollog_logger *logger_in,
+ xentoollog_level level,
+ int errnoval,
+ const char *context,
+ const char *format,
+ va_list al)
+{
+ char *formatted;
+ int r = vasprintf(&formatted, format, al);
+ if (r < 0) { perror("memory allocation failed during logging"); exit(-1); }
+ helper_stub_log(level, errnoval, context, formatted, 0);
+ free(formatted);
+}
+
+static void tellparent_progress(struct xentoollog_logger *logger_in,
+ const char *context,
+ const char *doing_what, int percent,
+ unsigned long done, unsigned long total)
+{
+ helper_stub_progress(context, doing_what, done, total, 0);
+}
+
+static void tellparent_destroy(struct xentoollog_logger *logger_in)
+{
+ abort();
+}
+
+/*----- globals -----*/
+
+static const char *program = "libxl-save-helper";
+static xentoollog_logger logger = {
+ tellparent_vmessage,
+ tellparent_progress,
+ tellparent_destroy,
+};
+static xc_interface *xch;
+static int io_fd;
+
+/*----- error handling -----*/
+
+static void fail(int errnoval, const char *fmt, ...)
+ __attribute__((noreturn,format(printf,2,3)));
+static void fail(int errnoval, const char *fmt, ...)
+{
+ va_list al;
+ va_start(al,fmt);
+ xtl_logv(&logger,XTL_ERROR,errnoval,program,fmt,al);
+ exit(-1);
+}
+
+static int read_exactly(int fd, void *buf, size_t len)
+/* returns 0 if we get eof, even if we got it midway through; 1 if ok */
+{
+ while (len) {
+ ssize_t r = read(fd, buf, len);
+ if (r<=0) return r;
+ assert(r <= len);
+ len -= r;
+ buf = (char*)buf + r;
+ }
+ return 1;
+}
+
+static void *xmalloc(size_t sz)
+{
+ if (!sz) return 0;
+ void *r = malloc(sz);
+ if (!r) { perror("memory allocation failed"); exit(-1); }
+ return r;
+}
+
+/*----- signal handling -----*/
+
+static int unwriteable_fd;
+
+static void save_signal_handler(int num)
+{
+ /*
+ * We want to be able to interrupt save. But the code in libxc
+ * which does the actual saving is straight-through, and we need
+ * to execute its error path to put the guest back to sanity.
+ *
+ * So what we do is this: when we get the signal, we dup2
+ * the result of open("/dev/null",O_RDONLY) onto the output fd.
+ *
+ * This is guaranteed to 1. interrupt libxc's write (causing it to
+ * return short, or maybe EINTR); 2. make the next write give
+ * EBADF, so that: 3. at latest, libxc will notice when it next
+ * tries to write data and will then go into its cleanup path.
+ *
+ * We make no effort here to sanitise the resulting errors.
+ * That's libxl's job.
+ */
+ int esave = errno;
+
+ int r = dup2(unwriteable_fd, io_fd);
+ if (r != io_fd)
+ /* we can't write an xtl message because we might end up
+ * interleaving on our control stream; we can't use stdio
+ * because it's not async-signal-safe */
+ abort();
+
+ errno = esave;
+}
+
+static void setup_signals(void (*handler)(int))
+{
+ struct sigaction sa;
+ sigset_t spmask;
+ int r;
+
+ unwriteable_fd = open("/dev/null",O_RDONLY);
+ if (unwriteable_fd < 0) fail(errno,"open /dev/null for reading");
+
+ LIBXL_FILLZERO(sa);
+ sa.sa_handler = handler;
+ sigemptyset(&sa.sa_mask);
+ r = sigaction(SIGTERM, &sa, 0);
+ if (r) fail(errno,"sigaction SIGTERM failed");
+
+ sigemptyset(&spmask);
+ sigaddset(&spmask,SIGTERM);
+ r = sigprocmask(SIG_UNBLOCK,&spmask,0);
+ if (r) fail(errno,"sigprocmask unblock SIGTERM failed");
+}
+
+/*----- helper functions called by autogenerated stubs -----*/
+
+unsigned char * helper_allocbuf(int len, void *user)
+{
+ return xmalloc(len);
+}
+
+static void transmit(const unsigned char *msg, int len, void *user)
+{
+ while (len) {
+ int r = write(1, msg, len);
+ if (r<0) { perror("write"); exit(-1); }
+ assert(r >= 0);
+ assert(r <= len);
+ len -= r;
+ msg += r;
+ }
+}
+
+void helper_transmitmsg(unsigned char *msg_freed, int len_in, void *user)
+{
+ assert(len_in < 64*1024);
+ uint16_t len = len_in;
+ transmit((const void*)&len, sizeof(len), user);
+ transmit(msg_freed, len, user);
+ free(msg_freed);
+}
+
+int helper_getreply(void *user)
+{
+ int v;
+ int r = read_exactly(0, &v, sizeof(v));
+ if (r<=0) exit(-2);
+ return v;
+}
+
+/*----- other callbacks -----*/
+
+static void startup(const char *op) {
+ xtl_log(&logger,XTL_DEBUG,0,program,"starting %s",op);
+
+ xch = xc_interface_open(&logger,&logger,0);
+ if (!xch) fail(errno,"xc_interface_open failed");
+}
+
+static void complete(int retval) {
+ int errnoval = retval ? errno : 0; /* suppress irrelevant errnos */
+ xtl_log(&logger,XTL_DEBUG,errnoval,program,"complete r=%d",retval);
+ helper_stub_complete(retval,errnoval,0);
+ xc_interface_close(xch);
+ exit(0);
+}
+
+int main(int argc, char **argv)
+{
+ int r;
+ int send_back_fd, recv_fd;
+
+#define NEXTARG (++argv, assert(*argv), *argv)
+
+ const char *mode = *++argv;
+ assert(mode);
+
+ if (!strcmp(mode,"--save-domain")) {
+ static struct save_callbacks cb;
+
+ io_fd = atoi(NEXTARG);
+ recv_fd = atoi(NEXTARG);
+ uint32_t dom = strtoul(NEXTARG,0,10);
+ uint32_t flags = strtoul(NEXTARG,0,10);
+ unsigned cbflags = strtoul(NEXTARG,0,10);
+ xc_stream_type_t stream_type = strtoul(NEXTARG,0,10);
+ assert(!*++argv);
+
+ helper_setcallbacks_save(&cb, cbflags);
+
+ startup("save");
+ setup_signals(save_signal_handler);
+
+ r = xc_domain_save(xch, io_fd, dom, flags, &cb, stream_type, recv_fd);
+ complete(r);
+
+ } else if (!strcmp(mode,"--restore-domain")) {
+ static struct restore_callbacks cb;
+
+ io_fd = atoi(NEXTARG);
+ send_back_fd = atoi(NEXTARG);
+ uint32_t dom = strtoul(NEXTARG,0,10);
+ unsigned store_evtchn = strtoul(NEXTARG,0,10);
+ domid_t store_domid = strtoul(NEXTARG,0,10);
+ unsigned console_evtchn = strtoul(NEXTARG,0,10);
+ domid_t console_domid = strtoul(NEXTARG,0,10);
+ unsigned cbflags = strtoul(NEXTARG,0,10);
+ xc_stream_type_t stream_type = strtoul(NEXTARG,0,10);
+ assert(!*++argv);
+
+ helper_setcallbacks_restore(&cb, cbflags);
+
+ unsigned long store_mfn = 0;
+ unsigned long console_mfn = 0;
+
+ startup("restore");
+ setup_signals(SIG_DFL);
+
+ r = xc_domain_restore(xch, io_fd, dom, store_evtchn, &store_mfn,
+ store_domid, console_evtchn, &console_mfn,
+ console_domid, stream_type, &cb, send_back_fd);
+ helper_stub_restore_results(store_mfn,console_mfn,0);
+ complete(r);
+
+ } else {
+ assert(!"unexpected mode argument");
+ }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+#!/usr/bin/perl -w
+
+use warnings;
+use strict;
+use POSIX;
+
+our $debug = 0; # produce copious debugging output at run-time?
+
+our @msgs = (
+ # flags:
+ # s - applicable to save
+ # r - applicable to restore
+ # c - function pointer in callbacks struct rather than fixed function
+ # x - function pointer is in struct {save,restore}_callbacks
+ # and its null-ness needs to be passed through to the helper's xc
+ # W - needs a return value; callback is synchronous
+ # A - needs a return value; callback is asynchronous
+ [ 'sr', "log", [qw(uint32_t level
+ uint32_t errnoval
+ STRING context
+ STRING formatted)] ],
+ [ 'sr', "progress", [qw(STRING context
+ STRING doing_what),
+ 'unsigned long', 'done',
+ 'unsigned long', 'total'] ],
+ [ 'srcxA', "suspend", [] ],
+ [ 'srcxA', "postcopy", [] ],
+ [ 'srcxA', "checkpoint", [] ],
+ [ 'srcxA', "wait_checkpoint", [] ],
+ [ 'scxA', "switch_qemu_logdirty", [qw(uint32_t domid
+ unsigned enable)] ],
+ [ 'rcxW', "static_data_done", [qw(unsigned missing)] ],
+ [ 'rcx', "restore_results", ['xen_pfn_t', 'store_gfn',
+ 'xen_pfn_t', 'console_gfn'] ],
+ [ 'srW', "complete", [qw(int retval
+ int errnoval)] ],
+);
+
+#----------------------------------------
+
+our %cbs;
+our %func;
+our %func_ah;
+our @outfuncs;
+our %out_decls;
+our %out_body;
+our $msgnum = 0;
+
+die unless @ARGV==1;
+die if $ARGV[0] =~ m/^-/;
+
+our ($intendedout) = @ARGV;
+
+$intendedout =~ m/([a-z]+)\.([ch])$/ or die;
+my ($want_ah, $ch) = ($1, $2);
+
+my $declprefix = '';
+
+foreach my $ah (qw(callout helper)) {
+ $out_body{$ah} .=
+ <<END_BOTH.($ah eq 'callout' ? <<END_CALLOUT : <<END_HELPER);
+#include "libxl_osdeps.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <limits.h>
+END_BOTH
+
+#include "libxl_internal.h"
+
+END_CALLOUT
+
+#include <xenctrl.h>
+#include <xenguest.h>
+#include "_libxl_save_msgs_${ah}.h"
+
+END_HELPER
+}
+
+die $want_ah unless defined $out_body{$want_ah};
+
+sub f_decl ($$$$) {
+ my ($name, $ah, $c_rtype, $c_decl) = @_;
+ $out_decls{$name} = "${declprefix}$c_rtype $name$c_decl;\n";
+ $func{$name} = "$c_rtype $name$c_decl\n{\n" . ($func{$name} || '');
+ $func_ah{$name} = $ah;
+}
+
+sub f_more ($$) {
+ my ($name, $addbody) = @_;
+ $func{$name} ||= '';
+ $func{$name} .= $addbody;
+ push @outfuncs, $name;
+}
+
+our $libxl = "libxl__srm";
+our $callback = "${libxl}_callout_callback";
+our $receiveds = "${libxl}_callout_received";
+our $sendreply = "${libxl}_callout_sendreply";
+our $getcallbacks = "${libxl}_callout_get_callbacks";
+our $enumcallbacks = "${libxl}_callout_enumcallbacks";
+sub cbtype ($) { "${libxl}_".$_[0]."_autogen_callbacks"; };
+
+f_decl($sendreply, 'callout', 'void', "(int r, void *user)");
+
+our $helper = "helper";
+our $encode = "${helper}_stub";
+our $allocbuf = "${helper}_allocbuf";
+our $transmit = "${helper}_transmitmsg";
+our $getreply = "${helper}_getreply";
+our $setcallbacks = "${helper}_setcallbacks";
+
+f_decl($allocbuf, 'helper', 'unsigned char *', '(int len, void *user)');
+f_decl($transmit, 'helper', 'void',
+ '(unsigned char *msg_freed, int len, void *user)');
+f_decl($getreply, 'helper', 'int', '(void *user)');
+
+sub typeid ($) { my ($t) = @_; $t =~ s/\W/_/; return $t; };
+
+$out_body{'callout'} .= <<END;
+static int bytes_get(const unsigned char **msg,
+ const unsigned char *const endmsg,
+ void *result, int rlen)
+{
+ if (endmsg - *msg < rlen) return 0;
+ memcpy(result, *msg, rlen);
+ *msg += rlen;
+ return 1;
+}
+
+END
+$out_body{'helper'} .= <<END;
+static void bytes_put(unsigned char *const buf, int *len,
+ const void *value, int vlen)
+{
+ assert(vlen < INT_MAX/2 - *len);
+ if (buf)
+ memcpy(buf + *len, value, vlen);
+ *len += vlen;
+}
+
+END
+
+foreach my $simpletype (qw(int uint16_t uint32_t unsigned), 'unsigned long', 'xen_pfn_t') {
+ my $typeid = typeid($simpletype);
+ $out_body{'callout'} .= <<END;
+static int ${typeid}_get(const unsigned char **msg,
+ const unsigned char *const endmsg,
+ $simpletype *result)
+{
+ return bytes_get(msg, endmsg, result, sizeof(*result));
+}
+
+END
+ $out_body{'helper'} .= <<END;
+static void ${typeid}_put(unsigned char *const buf, int *len,
+ const $simpletype value)
+{
+ bytes_put(buf, len, &value, sizeof(value));
+}
+
+END
+}
+
+$out_body{'callout'} .= <<END;
+static int BLOCK_get(const unsigned char **msg,
+ const unsigned char *const endmsg,
+ const uint8_t **result, uint32_t *result_size)
+{
+ if (!uint32_t_get(msg, endmsg, result_size)) return 0;
+ if (endmsg - *msg < *result_size) return 0;
+ *result = (const void*)*msg;
+ *msg += *result_size;
+ return 1;
+}
+
+static int STRING_get(const unsigned char **msg,
+ const unsigned char *const endmsg,
+ const char **result)
+{
+ const uint8_t *data;
+ uint32_t datalen;
+ if (!BLOCK_get(msg, endmsg, &data, &datalen)) return 0;
+ if (datalen == 0) return 0;
+ if (data[datalen-1] != '\\0') return 0;
+ *result = (const void*)data;
+ return 1;
+}
+
+END
+$out_body{'helper'} .= <<END;
+static void BLOCK_put(unsigned char *const buf,
+ int *len,
+ const uint8_t *bytes, uint32_t size)
+{
+ uint32_t_put(buf, len, size);
+ bytes_put(buf, len, bytes, size);
+}
+
+static void STRING_put(unsigned char *const buf,
+ int *len,
+ const char *string)
+{
+ size_t slen = strlen(string);
+ assert(slen < INT_MAX / 4);
+ assert(slen < (uint32_t)0x40000000);
+ BLOCK_put(buf, len, (const void*)string, slen+1);
+}
+
+END
+
+foreach my $sr (qw(save restore)) {
+ f_decl("${getcallbacks}_${sr}", 'callout',
+ "const ".cbtype($sr)." *",
+ "(void *data)");
+
+ f_decl("${receiveds}_${sr}", 'callout', 'int',
+ "(const unsigned char *msg, uint32_t len, void *user)");
+
+ f_decl("${enumcallbacks}_${sr}", 'callout', 'unsigned',
+ "(const ".cbtype($sr)." *cbs)");
+ f_more("${enumcallbacks}_${sr}", " unsigned cbflags = 0;\n");
+
+ f_decl("${setcallbacks}_${sr}", 'helper', 'void',
+ "(struct ${sr}_callbacks *cbs, unsigned cbflags)");
+
+ f_more("${receiveds}_${sr}",
+ <<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS);
+ const unsigned char *const endmsg = msg + len;
+ uint16_t mtype;
+ if (!uint16_t_get(&msg, endmsg, &mtype)) return 0;
+END_ALWAYS
+ fprintf(stderr,"libxl callout receiver: got len=%u mtype=%u\\n",len,mtype);
+END_DEBUG
+ switch (mtype) {
+
+END_ALWAYS
+
+ $cbs{$sr} = "typedef struct ".cbtype($sr)." {\n";
+}
+
+foreach my $msginfo (@msgs) {
+ my ($flags, $name, $args) = @$msginfo;
+ $msgnum++;
+
+ my $f_more_sr = sub {
+ my ($contents_spec, $fnamebase) = @_;
+ $fnamebase ||= "${receiveds}";
+ foreach my $sr (qw(save restore)) {
+ $sr =~ m/^./;
+ next unless $flags =~ m/$&/;
+ my $contents = (!ref $contents_spec) ? $contents_spec :
+ $contents_spec->($sr);
+ f_more("${fnamebase}_${sr}", $contents);
+ }
+ };
+
+ $f_more_sr->(" case $msgnum: { /* $name */\n");
+ if ($flags =~ m/W/) {
+ $f_more_sr->(" int r;\n");
+ }
+
+ my $c_rtype_helper = $flags =~ m/[WA]/ ? 'int' : 'void';
+ my $c_rtype_callout = $flags =~ m/W/ ? 'int' : 'void';
+ my $c_decl = '(';
+ my $c_callback_args = '';
+
+ f_more("${encode}_$name",
+ <<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS);
+ unsigned char *buf = 0;
+ int len = 0, allocd = 0;
+
+END_ALWAYS
+ fprintf(stderr,"libxl-save-helper: encoding $name\\n");
+END_DEBUG
+ for (;;) {
+ uint16_t_put(buf, &len, $msgnum /* $name */);
+END_ALWAYS
+
+ my @args = @$args;
+ my $c_recv = '';
+ my ($argtype, $arg);
+ while (($argtype, $arg, @args) = @args) {
+ my $typeid = typeid($argtype);
+ my $c_args = "$arg";
+ my $c_get_args = "&$arg";
+ if ($argtype eq 'STRING') {
+ $c_decl .= "const char *$arg, ";
+ $f_more_sr->(" const char *$arg;\n");
+ } elsif ($argtype eq 'BLOCK') {
+ $c_decl .= "const uint8_t *$arg, uint32_t ${arg}_size, ";
+ $c_args .= ", ${arg}_size";
+ $c_get_args .= ", &${arg}_size";
+ $f_more_sr->(" const uint8_t *$arg;\n".
+ " uint32_t ${arg}_size;\n");
+ } else {
+ $c_decl .= "$argtype $arg, ";
+ $f_more_sr->(" $argtype $arg;\n");
+ }
+ $c_callback_args .= "$c_args, ";
+ $c_recv.=
+ " if (!${typeid}_get(&msg, endmsg, $c_get_args)) return 0;\n";
+ f_more("${encode}_$name", " ${typeid}_put(buf, &len, $c_args);\n");
+ }
+ $f_more_sr->($c_recv);
+ $c_decl .= "void *user)";
+ $c_callback_args .= "user";
+
+ $f_more_sr->(" if (msg != endmsg) return 0;\n");
+
+ my $c_callback;
+ if ($flags !~ m/c/) {
+ $c_callback = "${callback}_$name";
+ } else {
+ $f_more_sr->(sub {
+ my ($sr) = @_;
+ $cbs{$sr} .= " $c_rtype_callout (*${name})$c_decl;\n";
+ return
+ " const ".cbtype($sr)." *const cbs =\n".
+ " ${getcallbacks}_${sr}(user);\n";
+ });
+ $c_callback = "cbs->${name}";
+ }
+ my $c_make_callback = "$c_callback($c_callback_args)";
+ if ($flags !~ m/W/) {
+ $f_more_sr->(" $c_make_callback;\n");
+ } else {
+ $f_more_sr->(" r = $c_make_callback;\n".
+ " $sendreply(r, user);\n");
+ f_decl($sendreply, 'callout', 'void', '(int r, void *user)');
+ }
+ if ($flags =~ m/x/) {
+ my $c_v = "(1u<<$msgnum)";
+ my $c_cb = "cbs->$name";
+ $f_more_sr->(" if ($c_cb) cbflags |= $c_v;\n", $enumcallbacks);
+ $f_more_sr->(" if (cbflags & $c_v) $c_cb = ${encode}_${name};\n",
+ $setcallbacks);
+ }
+ $f_more_sr->(" return 1;\n }\n\n");
+ f_decl("${callback}_$name", 'callout', $c_rtype_callout, $c_decl);
+ f_decl("${encode}_$name", 'helper', $c_rtype_helper, $c_decl);
+ f_more("${encode}_$name",
+" if (buf) break;
+ buf = ${helper}_allocbuf(len, user);
+ assert(buf);
+ allocd = len;
+ len = 0;
+ }
+ assert(len == allocd);
+ ${transmit}(buf, len, user);
+");
+ if ($flags =~ m/[WA]/) {
+ f_more("${encode}_$name",
+ (<<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS));
+ int r = ${helper}_getreply(user);
+END_ALWAYS
+ fprintf(stderr,"libxl-save-helper: $name got reply %d\\n",r);
+END_DEBUG
+ return r;
+END_ALWAYS
+ }
+}
+
+print "/* AUTOGENERATED by $0 DO NOT EDIT */\n\n" or die $!;
+
+foreach my $sr (qw(save restore)) {
+ f_more("${enumcallbacks}_${sr}",
+ " return cbflags;\n");
+ f_more("${receiveds}_${sr}",
+ " default:\n".
+ " return 0;\n".
+ " }\n");
+ $cbs{$sr} .= "} ".cbtype($sr).";\n\n";
+ if ($ch eq 'h') {
+ print $cbs{$sr} or die $!;
+ print "struct ${sr}_callbacks;\n";
+ }
+}
+
+if ($ch eq 'c') {
+ foreach my $name (@outfuncs) {
+ next unless defined $func{$name};
+ $func{$name} .= "}\n\n";
+ $out_body{$func_ah{$name}} .= $func{$name};
+ delete $func{$name};
+ }
+ print $out_body{$want_ah} or die $!;
+} else {
+ foreach my $name (sort keys %out_decls) {
+ next unless $func_ah{$name} eq $want_ah;
+ print $out_decls{$name} or die $!;
+ }
+}
+
+close STDOUT or die $!;
--- /dev/null
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+static int libxl__set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid,
+ uint32_t vcpuid,
+ const libxl_bitmap *cpumap_hard,
+ const libxl_bitmap *cpumap_soft,
+ unsigned flags)
+{
+ GC_INIT(ctx);
+ libxl_bitmap hard, soft;
+ int rc;
+
+ libxl_bitmap_init(&hard);
+ libxl_bitmap_init(&soft);
+
+ if (!cpumap_hard && !cpumap_soft && !flags) {
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ /*
+ * Xen wants writable hard and/or soft cpumaps, to put back in them
+ * the effective hard and/or soft affinity that will be used.
+ */
+ if (cpumap_hard) {
+ rc = libxl_cpu_bitmap_alloc(ctx, &hard, 0);
+ if (rc)
+ goto out;
+
+ libxl__bitmap_copy_best_effort(gc, &hard, cpumap_hard);
+ flags |= XEN_VCPUAFFINITY_HARD;
+ }
+ if (cpumap_soft) {
+ rc = libxl_cpu_bitmap_alloc(ctx, &soft, 0);
+ if (rc)
+ goto out;
+
+ libxl__bitmap_copy_best_effort(gc, &soft, cpumap_soft);
+ flags |= XEN_VCPUAFFINITY_SOFT;
+ }
+
+ if (xc_vcpu_setaffinity(ctx->xch, domid, vcpuid,
+ cpumap_hard ? hard.map : NULL,
+ cpumap_soft ? soft.map : NULL,
+ flags)) {
+ LOGED(ERROR, domid, "Setting vcpu affinity");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /*
+ * Let's check the results. Hard affinity will never be empty, but it
+ * is possible that Xen will use something different from what we asked
+ * for various reasons. If that's the case, report it.
+ */
+ if (cpumap_hard &&
+ !libxl_bitmap_equal(cpumap_hard, &hard, 0))
+ LOGD(DEBUG, domid, "New hard affinity for vcpu %d has unreachable cpus", vcpuid);
+ /*
+ * Soft affinity can both be different from what asked and empty. Check
+ * for (and report) both.
+ */
+ if (cpumap_soft) {
+ if (!libxl_bitmap_equal(cpumap_soft, &soft, 0))
+ LOGD(DEBUG, domid, "New soft affinity for vcpu %d has unreachable cpus",
+ vcpuid);
+ if (libxl_bitmap_is_empty(&soft))
+ LOGD(WARN, domid, "All cpus in soft affinity of vcpu %d are unreachable."
+ " Only hard affinity will be considered for scheduling",
+ vcpuid);
+ }
+
+ rc = 0;
+ out:
+ libxl_bitmap_dispose(&hard);
+ libxl_bitmap_dispose(&soft);
+ GC_FREE;
+ return rc;
+}
+
+int libxl_set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid,
+ const libxl_bitmap *cpumap_hard,
+ const libxl_bitmap *cpumap_soft)
+{
+ return libxl__set_vcpuaffinity(ctx, domid, vcpuid, cpumap_hard,
+ cpumap_soft, 0);
+}
+
+int libxl_set_vcpuaffinity_force(libxl_ctx *ctx, uint32_t domid,
+ uint32_t vcpuid,
+ const libxl_bitmap *cpumap_hard,
+ const libxl_bitmap *cpumap_soft)
+{
+ return libxl__set_vcpuaffinity(ctx, domid, vcpuid, cpumap_hard,
+ cpumap_soft, XEN_VCPUAFFINITY_FORCE);
+}
+
+int libxl_set_vcpuaffinity_all(libxl_ctx *ctx, uint32_t domid,
+ unsigned int max_vcpus,
+ const libxl_bitmap *cpumap_hard,
+ const libxl_bitmap *cpumap_soft)
+{
+ GC_INIT(ctx);
+ int i, rc = 0;
+
+ for (i = 0; i < max_vcpus; i++) {
+ if (libxl_set_vcpuaffinity(ctx, domid, i, cpumap_hard, cpumap_soft)) {
+ LOGD(WARN, domid, "Failed to set affinity for %d", i);
+ rc = ERROR_FAIL;
+ }
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_domain_set_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
+ libxl_bitmap *nodemap)
+{
+ GC_INIT(ctx);
+ if (xc_domain_node_setaffinity(ctx->xch, domid, nodemap->map)) {
+ LOGED(ERROR, domid, "Setting node affinity");
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ GC_FREE;
+ return 0;
+}
+
+int libxl_domain_get_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
+ libxl_bitmap *nodemap)
+{
+ GC_INIT(ctx);
+ if (xc_domain_node_getaffinity(ctx->xch, domid, nodemap->map)) {
+ LOGED(ERROR, domid, "Getting node affinity");
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ GC_FREE;
+ return 0;
+}
+
+int libxl_get_scheduler(libxl_ctx *ctx)
+{
+ int r, sched;
+
+ GC_INIT(ctx);
+ r = xc_sched_id(ctx->xch, &sched);
+ if (r != 0) {
+ LOGE(ERROR, "getting current scheduler id");
+ sched = ERROR_FAIL;
+ }
+ GC_FREE;
+ return sched;
+}
+
+static int sched_arinc653_domain_set(libxl__gc *gc, uint32_t domid,
+ const libxl_domain_sched_params *scinfo)
+{
+ /* Currently, the ARINC 653 scheduler does not take any domain-specific
+ configuration, so we simply return success. */
+ return 0;
+}
+
+static int sched_null_domain_set(libxl__gc *gc, uint32_t domid,
+ const libxl_domain_sched_params *scinfo)
+{
+ /* There aren't any domain-specific parameters to be set. */
+ return 0;
+}
+
+static int sched_null_domain_get(libxl__gc *gc, uint32_t domid,
+ libxl_domain_sched_params *scinfo)
+{
+ /* There aren't any domain-specific parameters to return. */
+ return 0;
+}
+
+static int sched_credit_domain_get(libxl__gc *gc, uint32_t domid,
+ libxl_domain_sched_params *scinfo)
+{
+ struct xen_domctl_sched_credit sdom;
+ int rc;
+
+ rc = xc_sched_credit_domain_get(CTX->xch, domid, &sdom);
+ if (rc != 0) {
+ LOGED(ERROR, domid, "Getting domain sched credit");
+ return ERROR_FAIL;
+ }
+
+ libxl_domain_sched_params_init(scinfo);
+ scinfo->sched = LIBXL_SCHEDULER_CREDIT;
+ scinfo->weight = sdom.weight;
+ scinfo->cap = sdom.cap;
+
+ return 0;
+}
+
+static int sched_credit_domain_set(libxl__gc *gc, uint32_t domid,
+ const libxl_domain_sched_params *scinfo)
+{
+ struct xen_domctl_sched_credit sdom;
+ xc_domaininfo_t domaininfo;
+ int rc;
+
+ rc = xc_domain_getinfolist(CTX->xch, domid, 1, &domaininfo);
+ if (rc < 0) {
+ LOGED(ERROR, domid, "Getting domain info list");
+ return ERROR_FAIL;
+ }
+ if (rc != 1 || domaininfo.domain != domid)
+ return ERROR_INVAL;
+
+ rc = xc_sched_credit_domain_get(CTX->xch, domid, &sdom);
+ if (rc != 0) {
+ LOGED(ERROR, domid, "Getting domain sched credit");
+ return ERROR_FAIL;
+ }
+
+ if (scinfo->weight != LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT) {
+ if (scinfo->weight < 1 || scinfo->weight > 65535) {
+ LOGD(ERROR, domid, "Cpu weight out of range, "
+ "valid values are within range from 1 to 65535");
+ return ERROR_INVAL;
+ }
+ sdom.weight = scinfo->weight;
+ }
+
+ if (scinfo->cap != LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT) {
+ if (scinfo->cap < 0
+ || scinfo->cap > (domaininfo.max_vcpu_id + 1) * 100) {
+ LOGD(ERROR, domid, "Cpu cap out of range, "
+ "valid range is from 0 to %d for specified number of vcpus",
+ ((domaininfo.max_vcpu_id + 1) * 100));
+ return ERROR_INVAL;
+ }
+ sdom.cap = scinfo->cap;
+ }
+
+ rc = xc_sched_credit_domain_set(CTX->xch, domid, &sdom);
+ if ( rc < 0 ) {
+ LOGED(ERROR, domid, "Setting domain sched credit");
+ return ERROR_FAIL;
+ }
+
+ return 0;
+}
+
+static int sched_ratelimit_check(libxl__gc *gc, int ratelimit)
+{
+ if (ratelimit != 0 &&
+ (ratelimit < XEN_SYSCTL_SCHED_RATELIMIT_MIN ||
+ ratelimit > XEN_SYSCTL_SCHED_RATELIMIT_MAX)) {
+ LOG(ERROR, "Ratelimit out of range, valid range is from %d to %d",
+ XEN_SYSCTL_SCHED_RATELIMIT_MIN, XEN_SYSCTL_SCHED_RATELIMIT_MAX);
+ return ERROR_INVAL;
+ }
+
+ return 0;
+}
+
+int libxl_sched_credit_params_get(libxl_ctx *ctx, uint32_t poolid,
+ libxl_sched_credit_params *scinfo)
+{
+ struct xen_sysctl_credit_schedule sparam;
+ int r, rc;
+ GC_INIT(ctx);
+
+ r = xc_sched_credit_params_get(ctx->xch, poolid, &sparam);
+ if (r < 0) {
+ LOGE(ERROR, "getting Credit scheduler parameters");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ scinfo->tslice_ms = sparam.tslice_ms;
+ scinfo->ratelimit_us = sparam.ratelimit_us;
+ scinfo->vcpu_migr_delay_us = sparam.vcpu_migr_delay_us;
+
+ rc = 0;
+ out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_sched_credit_params_set(libxl_ctx *ctx, uint32_t poolid,
+ libxl_sched_credit_params *scinfo)
+{
+ struct xen_sysctl_credit_schedule sparam;
+ int r, rc;
+ GC_INIT(ctx);
+
+ if (scinfo->tslice_ms < XEN_SYSCTL_CSCHED_TSLICE_MIN
+ || scinfo->tslice_ms > XEN_SYSCTL_CSCHED_TSLICE_MAX) {
+ LOG(ERROR, "Time slice out of range, valid range is from %d to %d",
+ XEN_SYSCTL_CSCHED_TSLICE_MIN, XEN_SYSCTL_CSCHED_TSLICE_MAX);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ rc = sched_ratelimit_check(gc, scinfo->ratelimit_us);
+ if (rc) {
+ goto out;
+ }
+ if (scinfo->ratelimit_us > scinfo->tslice_ms*1000) {
+ LOG(ERROR, "Ratelimit cannot be greater than timeslice");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ if (scinfo->vcpu_migr_delay_us > XEN_SYSCTL_CSCHED_MGR_DLY_MAX_US) {
+ LOG(ERROR, "vcpu migration delay should be >= 0 and <= %dus",
+ XEN_SYSCTL_CSCHED_MGR_DLY_MAX_US);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ sparam.tslice_ms = scinfo->tslice_ms;
+ sparam.ratelimit_us = scinfo->ratelimit_us;
+ sparam.vcpu_migr_delay_us = scinfo->vcpu_migr_delay_us;
+
+ r = xc_sched_credit_params_set(ctx->xch, poolid, &sparam);
+ if ( r < 0 ) {
+ LOGE(ERROR, "Setting Credit scheduler parameters");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ scinfo->tslice_ms = sparam.tslice_ms;
+ scinfo->ratelimit_us = sparam.ratelimit_us;
+ scinfo->vcpu_migr_delay_us = sparam.vcpu_migr_delay_us;
+
+ rc = 0;
+ out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_sched_credit2_params_get(libxl_ctx *ctx, uint32_t poolid,
+ libxl_sched_credit2_params *scinfo)
+{
+ struct xen_sysctl_credit2_schedule sparam;
+ int r, rc;
+ GC_INIT(ctx);
+
+ r = xc_sched_credit2_params_get(ctx->xch, poolid, &sparam);
+ if (r < 0) {
+ LOGE(ERROR, "getting Credit2 scheduler parameters");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ scinfo->ratelimit_us = sparam.ratelimit_us;
+
+ rc = 0;
+ out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_sched_credit2_params_set(libxl_ctx *ctx, uint32_t poolid,
+ libxl_sched_credit2_params *scinfo)
+{
+ struct xen_sysctl_credit2_schedule sparam;
+ int r, rc;
+ GC_INIT(ctx);
+
+ rc = sched_ratelimit_check(gc, scinfo->ratelimit_us);
+ if (rc) goto out;
+
+ sparam.ratelimit_us = scinfo->ratelimit_us;
+
+ r = xc_sched_credit2_params_set(ctx->xch, poolid, &sparam);
+ if (r < 0) {
+ LOGE(ERROR, "Setting Credit2 scheduler parameters");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ scinfo->ratelimit_us = sparam.ratelimit_us;
+
+ rc = 0;
+ out:
+ GC_FREE;
+ return rc;
+}
+
+static int sched_credit2_domain_get(libxl__gc *gc, uint32_t domid,
+ libxl_domain_sched_params *scinfo)
+{
+ struct xen_domctl_sched_credit2 sdom;
+ int rc;
+
+ rc = xc_sched_credit2_domain_get(CTX->xch, domid, &sdom);
+ if (rc != 0) {
+ LOGED(ERROR, domid, "Getting domain sched credit2");
+ return ERROR_FAIL;
+ }
+
+ libxl_domain_sched_params_init(scinfo);
+ scinfo->sched = LIBXL_SCHEDULER_CREDIT2;
+ scinfo->weight = sdom.weight;
+ scinfo->cap = sdom.cap;
+
+ return 0;
+}
+
+static int sched_credit2_domain_set(libxl__gc *gc, uint32_t domid,
+ const libxl_domain_sched_params *scinfo)
+{
+ struct xen_domctl_sched_credit2 sdom;
+ xc_domaininfo_t info;
+ int rc;
+
+ rc = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
+ if (rc < 0) {
+ LOGED(ERROR, domid, "Getting domain info");
+ return ERROR_FAIL;
+ }
+ if (rc != 1 || info.domain != domid)
+ return ERROR_INVAL;
+
+ rc = xc_sched_credit2_domain_get(CTX->xch, domid, &sdom);
+ if (rc != 0) {
+ LOGED(ERROR, domid, "Getting domain sched credit2");
+ return ERROR_FAIL;
+ }
+
+ if (scinfo->weight != LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT) {
+ if (scinfo->weight < 1 || scinfo->weight > 65535) {
+ LOGD(ERROR, domid, "Cpu weight out of range, "
+ "valid values are within range from 1 to 65535");
+ return ERROR_INVAL;
+ }
+ sdom.weight = scinfo->weight;
+ }
+
+ if (scinfo->cap != LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT) {
+ if (scinfo->cap < 0
+ || scinfo->cap > (info.max_vcpu_id + 1) * 100) {
+ LOGD(ERROR, domid, "Cpu cap out of range, "
+ "valid range is from 0 to %d for specified number of vcpus",
+ ((info.max_vcpu_id + 1) * 100));
+ return ERROR_INVAL;
+ }
+ sdom.cap = scinfo->cap;
+ }
+
+ rc = xc_sched_credit2_domain_set(CTX->xch, domid, &sdom);
+ if ( rc < 0 ) {
+ LOGED(ERROR, domid, "Setting domain sched credit2");
+ return ERROR_FAIL;
+ }
+
+ return 0;
+}
+
+static int sched_rtds_validate_params(libxl__gc *gc, int period, int budget)
+{
+ int rc;
+
+ if (period < 1) {
+ LOG(ERROR, "Invalid VCPU period of %d (it should be >= 1)", period);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ if (budget < 1) {
+ LOG(ERROR, "Invalid VCPU budget of %d (it should be >= 1)", budget);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ if (budget > period) {
+ LOG(ERROR, "VCPU budget must be smaller than or equal to period, "
+ "but %d > %d", budget, period);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ rc = 0;
+out:
+ return rc;
+}
+
+/* Get the RTDS scheduling parameters of vcpu(s) */
+static int sched_rtds_vcpu_get(libxl__gc *gc, uint32_t domid,
+ libxl_vcpu_sched_params *scinfo)
+{
+ uint32_t num_vcpus;
+ int i, r, rc;
+ xc_dominfo_t info;
+ struct xen_domctl_schedparam_vcpu *vcpus;
+
+ r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
+ if (r < 0) {
+ LOGED(ERROR, domid, "Getting domain info");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (scinfo->num_vcpus <= 0) {
+ rc = ERROR_INVAL;
+ goto out;
+ } else {
+ num_vcpus = scinfo->num_vcpus;
+ GCNEW_ARRAY(vcpus, num_vcpus);
+ for (i = 0; i < num_vcpus; i++) {
+ if (scinfo->vcpus[i].vcpuid < 0 ||
+ scinfo->vcpus[i].vcpuid > info.max_vcpu_id) {
+ LOGD(ERROR, domid, "VCPU index is out of range, "
+ "valid values are within range from 0 to %d",
+ info.max_vcpu_id);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ vcpus[i].vcpuid = scinfo->vcpus[i].vcpuid;
+ }
+ }
+
+ r = xc_sched_rtds_vcpu_get(CTX->xch, domid, vcpus, num_vcpus);
+ if (r != 0) {
+ LOGED(ERROR, domid, "Getting vcpu sched rtds");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ scinfo->sched = LIBXL_SCHEDULER_RTDS;
+ for (i = 0; i < num_vcpus; i++) {
+ scinfo->vcpus[i].period = vcpus[i].u.rtds.period;
+ scinfo->vcpus[i].budget = vcpus[i].u.rtds.budget;
+ scinfo->vcpus[i].extratime =
+ !!(vcpus[i].u.rtds.flags & XEN_DOMCTL_SCHEDRT_extra);
+ scinfo->vcpus[i].vcpuid = vcpus[i].vcpuid;
+ }
+ rc = 0;
+out:
+ return rc;
+}
+
+/* Get the RTDS scheduling parameters of all vcpus of a domain */
+static int sched_rtds_vcpu_get_all(libxl__gc *gc, uint32_t domid,
+ libxl_vcpu_sched_params *scinfo)
+{
+ uint32_t num_vcpus;
+ int i, r, rc;
+ xc_dominfo_t info;
+ struct xen_domctl_schedparam_vcpu *vcpus;
+
+ r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
+ if (r < 0) {
+ LOGED(ERROR, domid, "Getting domain info");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (scinfo->num_vcpus > 0) {
+ rc = ERROR_INVAL;
+ goto out;
+ } else {
+ num_vcpus = info.max_vcpu_id + 1;
+ GCNEW_ARRAY(vcpus, num_vcpus);
+ for (i = 0; i < num_vcpus; i++)
+ vcpus[i].vcpuid = i;
+ }
+
+ r = xc_sched_rtds_vcpu_get(CTX->xch, domid, vcpus, num_vcpus);
+ if (r != 0) {
+ LOGED(ERROR, domid, "Getting vcpu sched rtds");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ scinfo->sched = LIBXL_SCHEDULER_RTDS;
+ scinfo->num_vcpus = num_vcpus;
+ scinfo->vcpus = libxl__calloc(NOGC, num_vcpus,
+ sizeof(libxl_sched_params));
+
+ for (i = 0; i < num_vcpus; i++) {
+ scinfo->vcpus[i].period = vcpus[i].u.rtds.period;
+ scinfo->vcpus[i].budget = vcpus[i].u.rtds.budget;
+ scinfo->vcpus[i].extratime =
+ !!(vcpus[i].u.rtds.flags & XEN_DOMCTL_SCHEDRT_extra);
+ scinfo->vcpus[i].vcpuid = vcpus[i].vcpuid;
+ }
+ rc = 0;
+out:
+ return rc;
+}
+
+/* Set the RTDS scheduling parameters of vcpu(s) */
+static int sched_rtds_vcpu_set(libxl__gc *gc, uint32_t domid,
+ const libxl_vcpu_sched_params *scinfo)
+{
+ int r, rc;
+ int i;
+ uint16_t max_vcpuid;
+ xc_dominfo_t info;
+ struct xen_domctl_schedparam_vcpu *vcpus;
+
+ r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
+ if (r < 0) {
+ LOGED(ERROR, domid, "Getting domain info");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ max_vcpuid = info.max_vcpu_id;
+
+ if (scinfo->num_vcpus <= 0) {
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ for (i = 0; i < scinfo->num_vcpus; i++) {
+ if (scinfo->vcpus[i].vcpuid < 0 ||
+ scinfo->vcpus[i].vcpuid > max_vcpuid) {
+ LOGD(ERROR, domid, "Invalid VCPU %d: valid range is [0, %d]",
+ scinfo->vcpus[i].vcpuid, max_vcpuid);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ rc = sched_rtds_validate_params(gc, scinfo->vcpus[i].period,
+ scinfo->vcpus[i].budget);
+ if (rc) {
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ }
+ GCNEW_ARRAY(vcpus, scinfo->num_vcpus);
+ for (i = 0; i < scinfo->num_vcpus; i++) {
+ vcpus[i].vcpuid = scinfo->vcpus[i].vcpuid;
+ vcpus[i].u.rtds.period = scinfo->vcpus[i].period;
+ vcpus[i].u.rtds.budget = scinfo->vcpus[i].budget;
+ if (scinfo->vcpus[i].extratime)
+ vcpus[i].u.rtds.flags |= XEN_DOMCTL_SCHEDRT_extra;
+ else
+ vcpus[i].u.rtds.flags &= ~XEN_DOMCTL_SCHEDRT_extra;
+ }
+
+ r = xc_sched_rtds_vcpu_set(CTX->xch, domid,
+ vcpus, scinfo->num_vcpus);
+ if (r != 0) {
+ LOGED(ERROR, domid, "Setting vcpu sched rtds");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = 0;
+out:
+ return rc;
+}
+
+/* Set the RTDS scheduling parameters of all vcpus of a domain */
+static int sched_rtds_vcpu_set_all(libxl__gc *gc, uint32_t domid,
+ const libxl_vcpu_sched_params *scinfo)
+{
+ int r, rc;
+ int i;
+ uint16_t max_vcpuid;
+ xc_dominfo_t info;
+ struct xen_domctl_schedparam_vcpu *vcpus;
+ uint32_t num_vcpus;
+
+ r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
+ if (r < 0) {
+ LOGED(ERROR, domid, "Getting domain info");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ max_vcpuid = info.max_vcpu_id;
+
+ if (scinfo->num_vcpus != 1) {
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ if (sched_rtds_validate_params(gc, scinfo->vcpus[0].period,
+ scinfo->vcpus[0].budget)) {
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ num_vcpus = max_vcpuid + 1;
+ GCNEW_ARRAY(vcpus, num_vcpus);
+ for (i = 0; i < num_vcpus; i++) {
+ vcpus[i].vcpuid = i;
+ vcpus[i].u.rtds.period = scinfo->vcpus[0].period;
+ vcpus[i].u.rtds.budget = scinfo->vcpus[0].budget;
+ if (scinfo->vcpus[0].extratime)
+ vcpus[i].u.rtds.flags |= XEN_DOMCTL_SCHEDRT_extra;
+ else
+ vcpus[i].u.rtds.flags &= ~XEN_DOMCTL_SCHEDRT_extra;
+ }
+
+ r = xc_sched_rtds_vcpu_set(CTX->xch, domid,
+ vcpus, num_vcpus);
+ if (r != 0) {
+ LOGED(ERROR, domid, "Setting vcpu sched rtds");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ rc = 0;
+out:
+ return rc;
+}
+
+static int sched_rtds_domain_get(libxl__gc *gc, uint32_t domid,
+ libxl_domain_sched_params *scinfo)
+{
+ struct xen_domctl_sched_rtds sdom;
+ int rc;
+
+ rc = xc_sched_rtds_domain_get(CTX->xch, domid, &sdom);
+ if (rc != 0) {
+ LOGED(ERROR, domid, "Getting domain sched rtds");
+ return ERROR_FAIL;
+ }
+
+ libxl_domain_sched_params_init(scinfo);
+
+ scinfo->sched = LIBXL_SCHEDULER_RTDS;
+ scinfo->period = sdom.period;
+ scinfo->budget = sdom.budget;
+
+ return 0;
+}
+
+static int sched_rtds_domain_set(libxl__gc *gc, uint32_t domid,
+ const libxl_domain_sched_params *scinfo)
+{
+ struct xen_domctl_sched_rtds sdom;
+ int rc;
+
+ rc = xc_sched_rtds_domain_get(CTX->xch, domid, &sdom);
+ if (rc != 0) {
+ LOGED(ERROR, domid, "Getting domain sched rtds");
+ return ERROR_FAIL;
+ }
+ if (scinfo->period != LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT)
+ sdom.period = scinfo->period;
+ if (scinfo->budget != LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT)
+ sdom.budget = scinfo->budget;
+ /* Set extratime by default */
+ if (scinfo->extratime)
+ sdom.flags |= XEN_DOMCTL_SCHEDRT_extra;
+ else
+ sdom.flags &= ~XEN_DOMCTL_SCHEDRT_extra;
+ if (sched_rtds_validate_params(gc, sdom.period, sdom.budget))
+ return ERROR_INVAL;
+
+ rc = xc_sched_rtds_domain_set(CTX->xch, domid, &sdom);
+ if (rc < 0) {
+ LOGED(ERROR, domid, "Setting domain sched rtds");
+ return ERROR_FAIL;
+ }
+
+ return 0;
+}
+
+int libxl_domain_sched_params_set(libxl_ctx *ctx, uint32_t domid,
+ const libxl_domain_sched_params *scinfo)
+{
+ GC_INIT(ctx);
+ libxl_scheduler sched = scinfo->sched;
+ int ret;
+
+ if (sched == LIBXL_SCHEDULER_UNKNOWN)
+ sched = libxl__domain_scheduler(gc, domid);
+
+ switch (sched) {
+ case LIBXL_SCHEDULER_SEDF:
+ LOGD(ERROR, domid, "SEDF scheduler no longer available");
+ ret=ERROR_FEATURE_REMOVED;
+ break;
+ case LIBXL_SCHEDULER_CREDIT:
+ ret=sched_credit_domain_set(gc, domid, scinfo);
+ break;
+ case LIBXL_SCHEDULER_CREDIT2:
+ ret=sched_credit2_domain_set(gc, domid, scinfo);
+ break;
+ case LIBXL_SCHEDULER_ARINC653:
+ ret=sched_arinc653_domain_set(gc, domid, scinfo);
+ break;
+ case LIBXL_SCHEDULER_RTDS:
+ ret=sched_rtds_domain_set(gc, domid, scinfo);
+ break;
+ case LIBXL_SCHEDULER_NULL:
+ ret=sched_null_domain_set(gc, domid, scinfo);
+ break;
+ default:
+ LOGD(ERROR, domid, "Unknown scheduler");
+ ret=ERROR_INVAL;
+ break;
+ }
+
+ GC_FREE;
+ return ret;
+}
+
+int libxl_vcpu_sched_params_set(libxl_ctx *ctx, uint32_t domid,
+ const libxl_vcpu_sched_params *scinfo)
+{
+ GC_INIT(ctx);
+ libxl_scheduler sched = scinfo->sched;
+ int rc;
+
+ if (sched == LIBXL_SCHEDULER_UNKNOWN)
+ sched = libxl__domain_scheduler(gc, domid);
+
+ switch (sched) {
+ case LIBXL_SCHEDULER_SEDF:
+ LOGD(ERROR, domid, "SEDF scheduler no longer available");
+ rc = ERROR_FEATURE_REMOVED;
+ break;
+ case LIBXL_SCHEDULER_CREDIT:
+ case LIBXL_SCHEDULER_CREDIT2:
+ case LIBXL_SCHEDULER_ARINC653:
+ case LIBXL_SCHEDULER_NULL:
+ LOGD(ERROR, domid, "per-VCPU parameter setting not supported for this scheduler");
+ rc = ERROR_INVAL;
+ break;
+ case LIBXL_SCHEDULER_RTDS:
+ rc = sched_rtds_vcpu_set(gc, domid, scinfo);
+ break;
+ default:
+ LOGD(ERROR, domid, "Unknown scheduler");
+ rc = ERROR_INVAL;
+ break;
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_vcpu_sched_params_set_all(libxl_ctx *ctx, uint32_t domid,
+ const libxl_vcpu_sched_params *scinfo)
+{
+ GC_INIT(ctx);
+ libxl_scheduler sched = scinfo->sched;
+ int rc;
+
+ if (sched == LIBXL_SCHEDULER_UNKNOWN)
+ sched = libxl__domain_scheduler(gc, domid);
+
+ switch (sched) {
+ case LIBXL_SCHEDULER_SEDF:
+ LOGD(ERROR, domid, "SEDF scheduler no longer available");
+ rc = ERROR_FEATURE_REMOVED;
+ break;
+ case LIBXL_SCHEDULER_CREDIT:
+ case LIBXL_SCHEDULER_CREDIT2:
+ case LIBXL_SCHEDULER_ARINC653:
+ case LIBXL_SCHEDULER_NULL:
+ LOGD(ERROR, domid, "per-VCPU parameter setting not supported for this scheduler");
+ rc = ERROR_INVAL;
+ break;
+ case LIBXL_SCHEDULER_RTDS:
+ rc = sched_rtds_vcpu_set_all(gc, domid, scinfo);
+ break;
+ default:
+ LOGD(ERROR, domid, "Unknown scheduler");
+ rc = ERROR_INVAL;
+ break;
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_domain_sched_params_get(libxl_ctx *ctx, uint32_t domid,
+ libxl_domain_sched_params *scinfo)
+{
+ GC_INIT(ctx);
+ int ret;
+
+ libxl_domain_sched_params_init(scinfo);
+
+ scinfo->sched = libxl__domain_scheduler(gc, domid);
+
+ switch (scinfo->sched) {
+ case LIBXL_SCHEDULER_SEDF:
+ LOGD(ERROR, domid, "SEDF scheduler no longer available");
+ ret=ERROR_FEATURE_REMOVED;
+ break;
+ case LIBXL_SCHEDULER_CREDIT:
+ ret=sched_credit_domain_get(gc, domid, scinfo);
+ break;
+ case LIBXL_SCHEDULER_CREDIT2:
+ ret=sched_credit2_domain_get(gc, domid, scinfo);
+ break;
+ case LIBXL_SCHEDULER_RTDS:
+ ret=sched_rtds_domain_get(gc, domid, scinfo);
+ break;
+ case LIBXL_SCHEDULER_NULL:
+ ret=sched_null_domain_get(gc, domid, scinfo);
+ break;
+ default:
+ LOGD(ERROR, domid, "Unknown scheduler");
+ ret=ERROR_INVAL;
+ break;
+ }
+
+ GC_FREE;
+ return ret;
+}
+
+int libxl_vcpu_sched_params_get(libxl_ctx *ctx, uint32_t domid,
+ libxl_vcpu_sched_params *scinfo)
+{
+ GC_INIT(ctx);
+ int rc;
+
+ scinfo->sched = libxl__domain_scheduler(gc, domid);
+
+ switch (scinfo->sched) {
+ case LIBXL_SCHEDULER_SEDF:
+ LOGD(ERROR, domid, "SEDF scheduler is no longer available");
+ rc = ERROR_FEATURE_REMOVED;
+ break;
+ case LIBXL_SCHEDULER_CREDIT:
+ case LIBXL_SCHEDULER_CREDIT2:
+ case LIBXL_SCHEDULER_ARINC653:
+ case LIBXL_SCHEDULER_NULL:
+ LOGD(ERROR, domid, "per-VCPU parameter getting not supported for this scheduler");
+ rc = ERROR_INVAL;
+ break;
+ case LIBXL_SCHEDULER_RTDS:
+ rc = sched_rtds_vcpu_get(gc, domid, scinfo);
+ break;
+ default:
+ LOGD(ERROR, domid, "Unknown scheduler");
+ rc = ERROR_INVAL;
+ break;
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_vcpu_sched_params_get_all(libxl_ctx *ctx, uint32_t domid,
+ libxl_vcpu_sched_params *scinfo)
+{
+ GC_INIT(ctx);
+ int rc;
+
+ scinfo->sched = libxl__domain_scheduler(gc, domid);
+
+ switch (scinfo->sched) {
+ case LIBXL_SCHEDULER_SEDF:
+ LOGD(ERROR, domid, "SEDF scheduler is no longer available");
+ rc = ERROR_FEATURE_REMOVED;
+ break;
+ case LIBXL_SCHEDULER_CREDIT:
+ case LIBXL_SCHEDULER_CREDIT2:
+ case LIBXL_SCHEDULER_ARINC653:
+ case LIBXL_SCHEDULER_NULL:
+ LOGD(ERROR, domid, "per-VCPU parameter getting not supported for this scheduler");
+ rc = ERROR_INVAL;
+ break;
+ case LIBXL_SCHEDULER_RTDS:
+ rc = sched_rtds_vcpu_get_all(gc, domid, scinfo);
+ break;
+ default:
+ LOGD(ERROR, domid, "Unknown scheduler");
+ rc = ERROR_INVAL;
+ break;
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+#ifndef LIBXL__SR_STREAM_FORMAT_H
+#define LIBXL__SR_STREAM_FORMAT_H
+
+/*
+ * C structures for the Migration v2 stream format.
+ * See docs/specs/libxl-migration-stream.pandoc
+ */
+
+#include <stdint.h>
+
+typedef struct libxl__sr_hdr
+{
+ uint64_t ident;
+ uint32_t version;
+ uint32_t options;
+} libxl__sr_hdr;
+
+#define RESTORE_STREAM_IDENT 0x4c6962786c466d74UL
+#define RESTORE_STREAM_VERSION 0x00000002U
+
+#define RESTORE_OPT_BIG_ENDIAN (1u << 0)
+#define RESTORE_OPT_LEGACY (1u << 1)
+
+
+typedef struct libxl__sr_rec_hdr
+{
+ uint32_t type;
+ uint32_t length;
+} libxl__sr_rec_hdr;
+
+/* All records must be aligned up to an 8 octet boundary */
+#define REC_ALIGN_ORDER 3U
+
+#define REC_TYPE_END 0x00000000U
+#define REC_TYPE_LIBXC_CONTEXT 0x00000001U
+#define REC_TYPE_EMULATOR_XENSTORE_DATA 0x00000002U
+#define REC_TYPE_EMULATOR_CONTEXT 0x00000003U
+#define REC_TYPE_CHECKPOINT_END 0x00000004U
+#define REC_TYPE_CHECKPOINT_STATE 0x00000005U
+
+typedef struct libxl__sr_emulator_hdr
+{
+ uint32_t id;
+ uint32_t index;
+} libxl__sr_emulator_hdr;
+
+#define EMULATOR_UNKNOWN 0x00000000U
+#define EMULATOR_QEMU_TRADITIONAL 0x00000001U
+#define EMULATOR_QEMU_UPSTREAM 0x00000002U
+
+typedef struct libxl_sr_checkpoint_state
+{
+ uint32_t id;
+} libxl_sr_checkpoint_state;
+
+#define CHECKPOINT_NEW 0x00000000U
+#define CHECKPOINT_SVM_SUSPENDED 0x00000001U
+#define CHECKPOINT_SVM_READY 0x00000002U
+#define CHECKPOINT_SVM_RESUMED 0x00000003U
+
+#endif /* LIBXL__SR_STREAM_FORMAT_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2015 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*
+ * Infrastructure for reading and acting on the contents of a libxl
+ * migration stream. There are a lot of moving parts here.
+ *
+ * The logic revolves around two actions; reading another record from
+ * the stream, and processing the records. The stream_continue()
+ * function is responsible for choosing the next action to perform.
+ *
+ * The exact order of reading and processing is controlled by 'phase'.
+ * All complete records are held in the record_queue before being
+ * processed, and all records will be processed in queue order.
+ *
+ * Internal states:
+ * running phase in_ record incoming
+ * checkpoint _queue _record
+ *
+ * Undefined undef undef undef undef undef
+ * Idle false undef false 0 0
+ * Active true NORMAL false 0/1 0/partial
+ * Active true BUFFERING true any 0/partial
+ * Active true UNBUFFERING true any 0
+ *
+ * While reading data from the stream, 'dc' is active and a callback
+ * is expected. Most actions in process_record() start a callback of
+ * their own. Those which don't return out and stream_continue() sets
+ * up the next action.
+ *
+ * PHASE_NORMAL:
+ * This phase is used for regular migration or resume from file.
+ * Records are read one at time and immediately processed. (The
+ * record queue will not contain more than a single record.)
+ *
+ * PHASE_BUFFERING:
+ * This phase is used in checkpointed streams, when libxc signals
+ * the presence of a checkpoint in the stream. Records are read and
+ * buffered until a CHECKPOINT_END record has been read.
+ *
+ * PHASE_UNBUFFERING:
+ * Once a CHECKPOINT_END record has been read, all buffered records
+ * are processed.
+ *
+ * Note:
+ * Record buffers are not allocated from a GC; they are allocated
+ * and tracked manually. This is to avoid OOM with Remus where the
+ * AO lives for the lifetime of the process. Per-checkpoint AO's
+ * might be an avenue to explore.
+ *
+ * Entry points from outside:
+ * - libxl__stream_read_init()
+ * - Initialises state. Must be called once before _start()
+ * - libxl__stream_read_start()
+ * - Starts reading records from the stream, and acting on them.
+ * - libxl__stream_read_start_checkpoint()
+ * - Starts buffering records at a checkpoint. Must be called on
+ * a running stream.
+ *
+ * There are several chains of event:
+ *
+ * 1) Starting a stream follows:
+ * - libxl__stream_read_start()
+ * - stream_header_done()
+ * - stream_continue()
+ *
+ * 2) Reading a record follows:
+ * - stream_continue()
+ * - record_header_done()
+ * - record_body_done()
+ * - stream_continue()
+ *
+ * 3) Processing a record had several chains to follow, depending on
+ * the record in question.
+ * 3a) "Simple" record:
+ * - process_record()
+ * - stream_continue()
+ * 3b) LIBXC record:
+ * - process_record()
+ * - libxl__xc_domain_restore()
+ * - libxl__xc_domain_restore_done()
+ * - stream_continue()
+ * 3c) EMULATOR record:
+ * - process_record()
+ * - stream_write_emulator()
+ * - stream_write_emulator_done()
+ * - stream_continue()
+ *
+ * Depending on the contents of the stream, there are likely to be several
+ * parallel tasks being managed. check_all_finished() is used to join all
+ * tasks in both success and error cases.
+ *
+ * Failover for remus
+ * - We buffer all records until a CHECKPOINT_END record is received
+ * - We will consume the buffered records when a CHECKPOINT_END record
+ * is received
+ * - If we find some internal error, then rc or retval is not 0 in
+ * libxl__xc_domain_restore_done(). In this case, we don't resume the
+ * guest
+ * - If we need to do failover from primary, then rc and retval are both
+ * 0 in libxl__xc_domain_restore_done(). In this case, the buffered
+ * state will be dropped, because we haven't received a CHECKPOINT_END
+ * record, and therefore the buffered state is inconsistent. In
+ * libxl__xc_domain_restore_done(), we just complete the stream and
+ * stream->completion_callback() will be called to resume the guest
+ *
+ * For back channel stream:
+ * - libxl__stream_read_start()
+ * - Set up the stream to running state
+ *
+ * - libxl__stream_read_continue()
+ * - Set up reading the next record from a started stream.
+ * Add some codes to process_record() to handle the record.
+ * Then call stream->checkpoint_callback() to return.
+ */
+
+/* Success/error/cleanup handling. */
+static void stream_complete(libxl__egc *egc,
+ libxl__stream_read_state *stream, int rc);
+static void checkpoint_done(libxl__egc *egc,
+ libxl__stream_read_state *stream, int rc);
+static void stream_done(libxl__egc *egc,
+ libxl__stream_read_state *stream, int rc);
+static void conversion_done(libxl__egc *egc,
+ libxl__conversion_helper_state *chs, int rc);
+static void check_all_finished(libxl__egc *egc,
+ libxl__stream_read_state *stream, int rc);
+
+/* Event chain for first iteration, from _start(). */
+static void stream_header_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval);
+static void stream_continue(libxl__egc *egc,
+ libxl__stream_read_state *stream);
+static void setup_read_record(libxl__egc *egc,
+ libxl__stream_read_state *stream);
+static void record_header_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval);
+static void record_body_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval);
+static bool process_record(libxl__egc *egc,
+ libxl__stream_read_state *stream);
+
+/* Event chain for processing an emulator blob. */
+static void write_emulator_blob(libxl__egc *egc,
+ libxl__stream_read_state *stream,
+ libxl__sr_record_buf *rec);
+static void write_emulator_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval);
+
+/* Handlers for checkpoint state mini-loop */
+static void checkpoint_state_done(libxl__egc *egc,
+ libxl__stream_read_state *stream, int rc);
+
+/*----- Helpers -----*/
+
+/* Helper to set up reading some data from the stream. */
+static int setup_read(libxl__stream_read_state *stream,
+ const char *what, void *ptr, size_t nr_bytes,
+ libxl__datacopier_callback cb)
+{
+ libxl__datacopier_state *dc = &stream->dc;
+
+ dc->readwhat = what;
+ dc->readbuf = ptr;
+ dc->bytes_to_read = nr_bytes;
+ dc->used = 0;
+ dc->callback = cb;
+
+ return libxl__datacopier_start(dc);
+}
+
+static void free_record(libxl__sr_record_buf *rec)
+{
+ if (rec) {
+ free(rec->body);
+ free(rec);
+ }
+}
+
+/*----- Entrypoints -----*/
+
+void libxl__stream_read_init(libxl__stream_read_state *stream)
+{
+ assert(stream->ao);
+
+ stream->shs.ao = stream->ao;
+ libxl__save_helper_init(&stream->shs);
+
+ stream->chs.ao = stream->ao;
+ libxl__conversion_helper_init(&stream->chs);
+
+ stream->rc = 0;
+ stream->running = false;
+ stream->in_checkpoint = false;
+ stream->sync_teardown = false;
+ FILLZERO(stream->dc);
+ FILLZERO(stream->hdr);
+ LIBXL_STAILQ_INIT(&stream->record_queue);
+ stream->phase = SRS_PHASE_NORMAL;
+ stream->recursion_guard = false;
+ stream->incoming_record = NULL;
+ FILLZERO(stream->emu_dc);
+ stream->emu_carefd = NULL;
+}
+
+void libxl__stream_read_start(libxl__egc *egc,
+ libxl__stream_read_state *stream)
+{
+ libxl__datacopier_state *dc = &stream->dc;
+ STATE_AO_GC(stream->ao);
+ int rc = 0;
+
+ libxl__stream_read_init(stream);
+
+ stream->running = true;
+ stream->phase = SRS_PHASE_NORMAL;
+
+ if (stream->legacy) {
+ /*
+ * Convert the legacy stream.
+ *
+ * This results in a fork()/exec() of conversion helper script. It is
+ * passed the exiting stream->fd as an input, and returns the
+ * transformed stream via a new pipe. The fd of this new pipe then
+ * replaces stream->fd, to make the rest of the stream read code
+ * agnostic to whether legacy conversion is happening or not.
+ */
+ libxl__conversion_helper_state *chs = &stream->chs;
+
+ chs->legacy_fd = stream->fd;
+ chs->hvm =
+ (stream->dcs->guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM);
+ chs->completion_callback = conversion_done;
+
+ rc = libxl__convert_legacy_stream(egc, &stream->chs);
+
+ if (rc) {
+ LOG(ERROR, "Failed to start the legacy stream conversion helper");
+ goto err;
+ }
+
+ /* There should be no interaction of COLO backchannels and legacy
+ * stream conversion. */
+ assert(!stream->back_channel);
+
+ /* Confirm *dc is still zeroed out, while we shuffle stream->fd. */
+ assert(dc->ao == NULL);
+ assert(stream->chs.v2_carefd);
+ stream->fd = libxl__carefd_fd(stream->chs.v2_carefd);
+ stream->dcs->libxc_fd = stream->fd;
+ }
+ /* stream->fd is now a v2 stream. */
+
+ dc->ao = stream->ao;
+ dc->copywhat = "restore v2 stream";
+ dc->readfd = stream->fd;
+ dc->writefd = -1;
+
+ if (stream->back_channel)
+ return;
+
+ /* Start reading the stream header. */
+ rc = setup_read(stream, "stream header",
+ &stream->hdr, sizeof(stream->hdr),
+ stream_header_done);
+ if (rc)
+ goto err;
+
+ assert(!rc);
+ return;
+
+ err:
+ assert(rc);
+ stream_complete(egc, stream, rc);
+}
+
+void libxl__stream_read_start_checkpoint(libxl__egc *egc,
+ libxl__stream_read_state *stream)
+{
+ assert(stream->running);
+ assert(!stream->in_checkpoint);
+
+ stream->in_checkpoint = true;
+ stream->phase = SRS_PHASE_BUFFERING;
+
+ /*
+ * Libxc has handed control of the fd to us. Start reading some
+ * libxl records out of it.
+ */
+ stream_continue(egc, stream);
+}
+
+void libxl__stream_read_abort(libxl__egc *egc,
+ libxl__stream_read_state *stream, int rc)
+{
+ assert(rc);
+
+ if (stream->running)
+ stream_complete(egc, stream, rc);
+}
+
+/*----- Event logic -----*/
+
+static void stream_header_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval)
+{
+ libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc);
+ libxl__sr_hdr *hdr = &stream->hdr;
+ STATE_AO_GC(dc->ao);
+
+ if (rc)
+ goto err;
+
+ hdr->ident = be64toh(hdr->ident);
+ hdr->version = be32toh(hdr->version);
+ hdr->options = be32toh(hdr->options);
+
+ if (hdr->ident != RESTORE_STREAM_IDENT) {
+ rc = ERROR_FAIL;
+ LOG(ERROR,
+ "Invalid ident: expected 0x%016"PRIx64", got 0x%016"PRIx64,
+ RESTORE_STREAM_IDENT, hdr->ident);
+ goto err;
+ }
+ if (hdr->version != RESTORE_STREAM_VERSION) {
+ rc = ERROR_FAIL;
+ LOG(ERROR, "Unexpected Version: expected %"PRIu32", got %"PRIu32,
+ RESTORE_STREAM_VERSION, hdr->version);
+ goto err;
+ }
+ if (hdr->options & RESTORE_OPT_BIG_ENDIAN) {
+ rc = ERROR_FAIL;
+ LOG(ERROR, "Unable to handle big endian streams");
+ goto err;
+ }
+
+ LOG(DEBUG, "Stream v%"PRIu32"%s", hdr->version,
+ hdr->options & RESTORE_OPT_LEGACY ? " (from legacy)" : "");
+
+ stream_continue(egc, stream);
+ return;
+
+ err:
+ assert(rc);
+ stream_complete(egc, stream, rc);
+}
+
+static void stream_continue(libxl__egc *egc,
+ libxl__stream_read_state *stream)
+{
+ STATE_AO_GC(stream->ao);
+
+ /*
+ * Must not mutually recurse with process_record().
+ *
+ * For records whose processing function is synchronous
+ * (e.g. TOOLSTACK), process_record() does not start another async
+ * operation, and a further operation should be started.
+ *
+ * A naive solution, which would function in general, would be for
+ * process_record() to call stream_continue(). However, this
+ * would allow the content of the stream to cause mutual
+ * recursion, and possibly for us to fall off our stack.
+ *
+ * Instead, process_record() indicates with its return value
+ * whether a further operation needs to start, and the
+ * recursion_guard is in place to catch any code paths which get
+ * this wrong.
+ */
+ assert(stream->recursion_guard == false);
+ stream->recursion_guard = true;
+
+ switch (stream->phase) {
+ case SRS_PHASE_NORMAL:
+ /*
+ * Normal phase (regular migration or restore from file):
+ *
+ * logically:
+ * do { read_record(); process_record(); } while ( not END );
+ *
+ * Alternate between reading a record from the stream, and
+ * processing the record. There should never be two records
+ * in the queue.
+ */
+ if (LIBXL_STAILQ_EMPTY(&stream->record_queue))
+ setup_read_record(egc, stream);
+ else {
+ if (process_record(egc, stream))
+ setup_read_record(egc, stream);
+
+ /*
+ * process_record() had better have consumed the one and
+ * only record in the queue.
+ */
+ assert(LIBXL_STAILQ_EMPTY(&stream->record_queue));
+ }
+ break;
+
+ case SRS_PHASE_BUFFERING: {
+ /*
+ * Buffering phase (checkpointed streams only):
+ *
+ * logically:
+ * do { read_record(); } while ( not CHECKPOINT_END );
+ *
+ * Read and buffer all records from the stream until a
+ * CHECKPOINT_END record is encountered. We need to peek at
+ * the tail to spot the CHECKPOINT_END record, and switch to
+ * the unbuffering phase.
+ */
+ libxl__sr_record_buf *rec = LIBXL_STAILQ_LAST(
+ &stream->record_queue, libxl__sr_record_buf, entry);
+
+ assert(stream->in_checkpoint);
+
+ if (!rec || (rec->hdr.type != REC_TYPE_CHECKPOINT_END)) {
+ setup_read_record(egc, stream);
+ break;
+ }
+
+ /*
+ * There are now some number of buffered records, with a
+ * CHECKPOINT_END at the end. Start processing them all.
+ */
+ stream->phase = SRS_PHASE_UNBUFFERING;
+ }
+ /* FALLTHROUGH */
+ case SRS_PHASE_UNBUFFERING:
+ /*
+ * Unbuffering phase (checkpointed streams only):
+ *
+ * logically:
+ * do { process_record(); } while ( not CHECKPOINT_END );
+ *
+ * Process all records collected during the buffering phase.
+ */
+ assert(stream->in_checkpoint);
+
+ while (process_record(egc, stream))
+ ; /*
+ * Nothing! process_record() helpfully tells us if no specific
+ * futher actions have been set up, in which case we want to go
+ * ahead and process the next record.
+ */
+ break;
+
+ default:
+ abort();
+ }
+
+ assert(stream->recursion_guard == true);
+ stream->recursion_guard = false;
+}
+
+static void setup_read_record(libxl__egc *egc,
+ libxl__stream_read_state *stream)
+{
+ libxl__sr_record_buf *rec = NULL;
+ STATE_AO_GC(stream->ao);
+ int rc;
+
+ assert(stream->incoming_record == NULL);
+ stream->incoming_record = rec = libxl__zalloc(NOGC, sizeof(*rec));
+
+ rc = setup_read(stream, "record header",
+ &rec->hdr, sizeof(rec->hdr),
+ record_header_done);
+ if (rc)
+ goto err;
+ return;
+
+ err:
+ assert(rc);
+ stream_complete(egc, stream, rc);
+}
+
+static void record_header_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval)
+{
+ libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc);
+ libxl__sr_record_buf *rec = stream->incoming_record;
+ STATE_AO_GC(dc->ao);
+
+ if (rc)
+ goto err;
+
+ /* No body? All done. */
+ if (rec->hdr.length == 0) {
+ record_body_done(egc, dc, 0, 0, 0);
+ return;
+ }
+
+ size_t bytes_to_read = ROUNDUP(rec->hdr.length, REC_ALIGN_ORDER);
+ rec->body = libxl__malloc(NOGC, bytes_to_read);
+
+ rc = setup_read(stream, "record body",
+ rec->body, bytes_to_read,
+ record_body_done);
+ if (rc)
+ goto err;
+ return;
+
+ err:
+ assert(rc);
+ stream_complete(egc, stream, rc);
+}
+
+static void record_body_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval)
+{
+ libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc);
+ libxl__sr_record_buf *rec = stream->incoming_record;
+ STATE_AO_GC(dc->ao);
+
+ if (rc)
+ goto err;
+
+ LIBXL_STAILQ_INSERT_TAIL(&stream->record_queue, rec, entry);
+ stream->incoming_record = NULL;
+
+ stream_continue(egc, stream);
+ return;
+
+ err:
+ assert(rc);
+ stream_complete(egc, stream, rc);
+}
+
+/*
+ * Returns a boolean indicating whether a further action should be set
+ * up by the caller. This is needed to prevent mutual recursion with
+ * stream_continue().
+ *
+ * It is a bug for this function to ever call stream_continue() or
+ * setup_read_record().
+ */
+static bool process_record(libxl__egc *egc,
+ libxl__stream_read_state *stream)
+{
+ STATE_AO_GC(stream->ao);
+ libxl__domain_create_state *dcs = stream->dcs;
+ libxl__sr_record_buf *rec;
+ libxl_sr_checkpoint_state *srcs;
+ bool further_action_needed = false;
+ int rc = 0;
+
+ /* Pop a record from the head of the queue. */
+ assert(!LIBXL_STAILQ_EMPTY(&stream->record_queue));
+ rec = LIBXL_STAILQ_FIRST(&stream->record_queue);
+ LIBXL_STAILQ_REMOVE_HEAD(&stream->record_queue, entry);
+
+ LOG(DEBUG, "Record: %u, length %u", rec->hdr.type, rec->hdr.length);
+
+ switch (rec->hdr.type) {
+
+ case REC_TYPE_END:
+ stream_complete(egc, stream, 0);
+ break;
+
+ case REC_TYPE_LIBXC_CONTEXT:
+ libxl__xc_domain_restore(egc, dcs, &stream->shs);
+ break;
+
+ case REC_TYPE_EMULATOR_XENSTORE_DATA:
+ if (dcs->guest_config->b_info.type != LIBXL_DOMAIN_TYPE_HVM) {
+ rc = ERROR_FAIL;
+ LOG(ERROR,
+ "Received a xenstore emulator record when none was expected");
+ goto err;
+ }
+
+ if (rec->hdr.length < sizeof(libxl__sr_emulator_hdr)) {
+ rc = ERROR_FAIL;
+ LOG(ERROR,
+ "Emulator xenstore data record too short to contain header");
+ goto err;
+ }
+
+ rc = libxl__restore_emulator_xenstore_data(dcs,
+ rec->body + sizeof(libxl__sr_emulator_hdr),
+ rec->hdr.length - sizeof(libxl__sr_emulator_hdr));
+ if (rc)
+ goto err;
+
+ /*
+ * libxl__restore_emulator_xenstore_data() is a synchronous function.
+ * Request that our caller queues another action for us.
+ */
+ further_action_needed = true;
+ break;
+
+ case REC_TYPE_EMULATOR_CONTEXT:
+ if (dcs->guest_config->b_info.type != LIBXL_DOMAIN_TYPE_HVM) {
+ rc = ERROR_FAIL;
+ LOG(ERROR,
+ "Received an emulator context record when none was expected");
+ goto err;
+ }
+
+ write_emulator_blob(egc, stream, rec);
+ break;
+
+ case REC_TYPE_CHECKPOINT_END:
+ if (!stream->in_checkpoint) {
+ LOG(ERROR, "Unexpected CHECKPOINT_END record in stream");
+ rc = ERROR_FAIL;
+ goto err;
+ }
+ checkpoint_done(egc, stream, 0);
+ break;
+
+ case REC_TYPE_CHECKPOINT_STATE:
+ if (!stream->in_checkpoint_state) {
+ LOG(ERROR, "Unexpected CHECKPOINT_STATE record in stream");
+ rc = ERROR_FAIL;
+ goto err;
+ }
+
+ srcs = rec->body;
+ checkpoint_state_done(egc, stream, srcs->id);
+ break;
+
+ default:
+ LOG(ERROR, "Unrecognised record 0x%08x", rec->hdr.type);
+ rc = ERROR_FAIL;
+ goto err;
+ }
+
+ assert(!rc);
+ free_record(rec);
+ return further_action_needed;
+
+ err:
+ assert(rc);
+ free_record(rec);
+ stream_complete(egc, stream, rc);
+ return false;
+}
+
+static void write_emulator_blob(libxl__egc *egc,
+ libxl__stream_read_state *stream,
+ libxl__sr_record_buf *rec)
+{
+ libxl__domain_create_state *dcs = stream->dcs;
+ libxl__datacopier_state *dc = &stream->emu_dc;
+ libxl__sr_emulator_hdr *emu_hdr;
+ STATE_AO_GC(stream->ao);
+ char path[256];
+ int rc = 0, writefd;
+
+ if (rec->hdr.length < sizeof(*emu_hdr)) {
+ rc = ERROR_FAIL;
+ LOG(ERROR, "Emulator record too short to contain header");
+ goto err;
+ }
+ emu_hdr = rec->body;
+
+ sprintf(path, LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", dcs->guest_domid);
+
+ assert(stream->emu_carefd == NULL);
+ libxl__carefd_begin();
+ writefd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ stream->emu_carefd = libxl__carefd_opened(CTX, writefd);
+
+ if (writefd == -1) {
+ rc = ERROR_FAIL;
+ LOGE(ERROR, "unable to open %s", path);
+ goto err;
+ }
+
+ FILLZERO(*dc);
+ dc->ao = stream->ao;
+ dc->writewhat = "qemu save file";
+ dc->copywhat = "restore v2 stream";
+ dc->writefd = writefd;
+ dc->readfd = -1;
+ dc->maxsz = -1;
+ dc->callback = write_emulator_done;
+
+ rc = libxl__datacopier_start(dc);
+ if (rc)
+ goto err;
+
+ libxl__datacopier_prefixdata(egc, dc,
+ rec->body + sizeof(*emu_hdr),
+ rec->hdr.length - sizeof(*emu_hdr));
+ return;
+
+ err:
+ assert(rc);
+ stream_complete(egc, stream, rc);
+}
+
+static void write_emulator_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval)
+{
+ libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, emu_dc);
+ STATE_AO_GC(dc->ao);
+
+ libxl__carefd_close(stream->emu_carefd);
+ stream->emu_carefd = NULL;
+
+ if (rc)
+ goto err;
+
+ stream_continue(egc, stream);
+ return;
+
+ err:
+ assert(rc);
+ stream_complete(egc, stream, rc);
+}
+
+/*----- Success/error/cleanup handling. -----*/
+
+static void stream_complete(libxl__egc *egc,
+ libxl__stream_read_state *stream, int rc)
+{
+ assert(stream->running);
+
+ if (stream->in_checkpoint) {
+ assert(rc);
+
+ /*
+ * If an error is encountered while in a checkpoint, pass it
+ * back to libxc. The failure will come back around to us via
+ * libxl__xc_domain_restore_done()
+ */
+ checkpoint_done(egc, stream, rc);
+ return;
+ }
+
+ if (stream->in_checkpoint_state) {
+ assert(rc);
+
+ /*
+ * If an error is encountered while in a checkpoint, pass it
+ * back to libxc. The failure will come back around to us via
+ * 1. normal stream
+ * libxl__xc_domain_restore_done()
+ * 2. back_channel stream
+ * libxl__stream_read_abort()
+ */
+ checkpoint_state_done(egc, stream, rc);
+ return;
+ }
+
+ stream_done(egc, stream, rc);
+}
+
+static void checkpoint_done(libxl__egc *egc,
+ libxl__stream_read_state *stream, int rc)
+{
+ int ret;
+
+ assert(stream->in_checkpoint);
+
+ if (rc == 0)
+ ret = XGR_CHECKPOINT_SUCCESS;
+ else if (stream->phase == SRS_PHASE_BUFFERING)
+ ret = XGR_CHECKPOINT_FAILOVER;
+ else
+ ret = XGR_CHECKPOINT_ERROR;
+
+ stream->checkpoint_callback(egc, stream, ret);
+
+ stream->in_checkpoint = false;
+ stream->phase = SRS_PHASE_NORMAL;
+}
+
+static void stream_done(libxl__egc *egc,
+ libxl__stream_read_state *stream, int rc)
+{
+ libxl__sr_record_buf *rec, *trec;
+
+ assert(stream->running);
+ assert(!stream->in_checkpoint);
+ assert(!stream->in_checkpoint_state);
+ stream->running = false;
+
+ if (stream->incoming_record)
+ free_record(stream->incoming_record);
+
+ if (stream->emu_carefd)
+ libxl__carefd_close(stream->emu_carefd);
+
+ /* If we started a conversion helper, we took ownership of its carefd. */
+ if (stream->chs.v2_carefd)
+ libxl__carefd_close(stream->chs.v2_carefd);
+
+ /* The record queue had better be empty if the stream believes
+ * itself to have been successful. */
+ assert(LIBXL_STAILQ_EMPTY(&stream->record_queue) || stream->rc);
+
+ LIBXL_STAILQ_FOREACH_SAFE(rec, &stream->record_queue, entry, trec)
+ free_record(rec);
+
+ if (!stream->back_channel) {
+ /*
+ * 1. In stream_done(), stream->running is set to false, so
+ * the stream itself is not in use.
+ * 2. Read stream is a back channel stream, this means it is
+ * only used by primary(save side) to read records sent by
+ * secondary(restore side), so it doesn't have restore helper.
+ * 3. Back channel stream doesn't support legacy stream, so
+ * there is no conversion helper.
+ * So we don't need invoke check_all_finished here
+ */
+ check_all_finished(egc, stream, rc);
+ }
+}
+
+void libxl__xc_domain_restore_done(libxl__egc *egc, void *dcs_void,
+ int rc, int retval, int errnoval)
+{
+ libxl__domain_create_state *dcs = dcs_void;
+ libxl__stream_read_state *stream = &dcs->srs;
+ STATE_AO_GC(dcs->ao);
+
+ /* convenience aliases */
+ const int checkpointed_stream = dcs->restore_params.checkpointed_stream;
+
+ if (rc)
+ goto err;
+
+ if (retval) {
+ LOGEV(ERROR, errnoval, "restoring domain");
+ rc = ERROR_FAIL;
+ goto err;
+ }
+
+ err:
+ check_all_finished(egc, stream, rc);
+
+ /*
+ * This function is the callback associated with the save helper
+ * task, not the stream task. We do not know whether the stream is
+ * alive, and check_all_finished() may have torn it down around us.
+ * If the stream is not still alive, we must not continue any work.
+ */
+ if (libxl__stream_read_inuse(stream)) {
+ switch (checkpointed_stream) {
+ case LIBXL_CHECKPOINTED_STREAM_COLO:
+ if (stream->completion_callback) {
+ /*
+ * restore, just build the secondary vm, don't close
+ * the stream
+ */
+ stream->completion_callback(egc, stream, 0);
+ } else {
+ /* failover, just close the stream */
+ stream_complete(egc, stream, 0);
+ }
+ break;
+ case LIBXL_CHECKPOINTED_STREAM_REMUS:
+ /*
+ * Failover from primary. Domain state is currently at a
+ * consistent checkpoint, complete the stream, and call
+ * stream->completion_callback() to resume the guest.
+ */
+ stream_complete(egc, stream, 0);
+ break;
+ case LIBXL_CHECKPOINTED_STREAM_NONE:
+ /*
+ * Libxc has indicated that it is done with the stream.
+ * Resume reading libxl records from it.
+ */
+ stream_continue(egc, stream);
+ break;
+ }
+ }
+}
+
+static void conversion_done(libxl__egc *egc,
+ libxl__conversion_helper_state *chs, int rc)
+{
+ libxl__stream_read_state *stream = CONTAINER_OF(chs, *stream, chs);
+
+ check_all_finished(egc, stream, rc);
+}
+
+static void check_all_finished(libxl__egc *egc,
+ libxl__stream_read_state *stream, int rc)
+{
+ STATE_AO_GC(stream->ao);
+
+ /*
+ * In the case of a failure, the _abort()'s below might cancel
+ * synchronously on top of us, or asynchronously at a later point.
+ *
+ * We must avoid the situation where all _abort() cancel
+ * synchronously and the completion_callback() gets called twice;
+ * once by the first error and once by the final stacked abort(),
+ * both of whom will find that all of the tasks have stopped.
+ *
+ * To avoid this problem, any stacked re-entry into this function is
+ * ineligible to fire the completion callback. The outermost
+ * instance will take care of completing, once the stack has
+ * unwound.
+ */
+ if (stream->sync_teardown)
+ return;
+
+ if (!stream->rc && rc) {
+ /* First reported failure. Tear everything down. */
+ stream->rc = rc;
+ stream->sync_teardown = true;
+
+ libxl__stream_read_abort(egc, stream, rc);
+ libxl__save_helper_abort(egc, &stream->shs);
+ libxl__conversion_helper_abort(egc, &stream->chs, rc);
+
+ stream->sync_teardown = false;
+ }
+
+ /* Don't fire the callback until all our parallel tasks have stopped. */
+ if (libxl__stream_read_inuse(stream) ||
+ libxl__save_helper_inuse(&stream->shs) ||
+ libxl__conversion_helper_inuse(&stream->chs))
+ return;
+
+ if (stream->completion_callback)
+ /* back channel stream doesn't have completion_callback() */
+ stream->completion_callback(egc, stream, stream->rc);
+}
+
+/*----- Checkpoint state handlers -----*/
+
+void libxl__stream_read_checkpoint_state(libxl__egc *egc,
+ libxl__stream_read_state *stream)
+{
+ assert(stream->running);
+ assert(!stream->in_checkpoint);
+ assert(!stream->in_checkpoint_state);
+ stream->in_checkpoint_state = true;
+
+ setup_read_record(egc, stream);
+}
+
+static void checkpoint_state_done(libxl__egc *egc,
+ libxl__stream_read_state *stream, int rc)
+{
+ assert(stream->in_checkpoint_state);
+ stream->in_checkpoint_state = false;
+ stream->checkpoint_callback(egc, stream, rc);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2015 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*
+ * Infrastructure for writing a domain to a libxl migration v2 stream.
+ *
+ * Entry points from outside:
+ * - libxl__stream_write_start()
+ * - Start writing a stream from the start.
+ * - libxl__stream_write_start_checkpoint()
+ * - Write the records which form a checkpoint into a stream.
+ *
+ * In normal operation, there are two tasks running at once; this
+ * stream processing, and the libxl-save-helper. check_all_finished()
+ * is used to join all the tasks in both success and error cases.
+ *
+ * Nomenclature for event callbacks:
+ * - $FOO_done(): Completion callback for $FOO
+ * - write_$FOO(): Set up the datacopier to write a $FOO
+ * - $BAR_header(): A $BAR record header only
+ * - $BAR_record(): A complete $BAR record with header and content
+ *
+ * The main loop for a plain VM writes:
+ * - Stream header
+ * - Libxc record
+ * - (optional) Emulator xenstore record
+ * - if (hvm)
+ * - Emulator context record
+ * - End record
+ *
+ * For checkpointed stream, there is a second loop which is triggered by a
+ * save-helper checkpoint callback. It writes:
+ * - (optional) Emulator xenstore record
+ * - if (hvm)
+ * - Emulator context record
+ * - Checkpoint end record
+ *
+ * For back channel stream:
+ * - libxl__stream_write_start()
+ * - Set up the stream to running state
+ *
+ * - Use libxl__stream_write_checkpoint_state to write the record. When the
+ * record is written out, call stream->checkpoint_callback() to return.
+ */
+
+/* Success/error/cleanup handling. */
+static void stream_success(libxl__egc *egc,
+ libxl__stream_write_state *stream);
+static void stream_complete(libxl__egc *egc,
+ libxl__stream_write_state *stream, int rc);
+static void stream_done(libxl__egc *egc,
+ libxl__stream_write_state *stream, int rc);
+static void checkpoint_done(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ int rc);
+static void check_all_finished(libxl__egc *egc,
+ libxl__stream_write_state *stream, int rc);
+
+/* Event chain for a plain VM. */
+static void stream_header_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval);
+static void libxc_header_done(libxl__egc *egc,
+ libxl__stream_write_state *stream);
+/* libxl__xc_domain_save_done() lives here, event-order wise. */
+static void write_emulator_xenstore_record(libxl__egc *egc,
+ libxl__stream_write_state *stream);
+static void emulator_xenstore_record_done(libxl__egc *egc,
+ libxl__stream_write_state *stream);
+static void write_emulator_context_record(libxl__egc *egc,
+ libxl__stream_write_state *stream);
+static void emulator_context_read_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval);
+static void emulator_context_record_done(libxl__egc *egc,
+ libxl__stream_write_state *stream);
+static void write_end_record(libxl__egc *egc,
+ libxl__stream_write_state *stream);
+
+/* Event chain unique to checkpointed streams. */
+static void write_checkpoint_end_record(libxl__egc *egc,
+ libxl__stream_write_state *stream);
+static void checkpoint_end_record_done(libxl__egc *egc,
+ libxl__stream_write_state *stream);
+
+/* checkpoint state */
+static void write_checkpoint_state_done(libxl__egc *egc,
+ libxl__stream_write_state *stream);
+static void checkpoint_state_done(libxl__egc *egc,
+ libxl__stream_write_state *stream, int rc);
+
+/*----- Helpers -----*/
+
+static void write_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval);
+
+/* Generic helper to set up writing some data to the stream. */
+static void setup_generic_write(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ const char *what,
+ libxl__sr_rec_hdr *hdr,
+ libxl__sr_emulator_hdr *emu_hdr,
+ void *body,
+ sws_record_done_cb cb)
+{
+ static const uint8_t zero_padding[1U << REC_ALIGN_ORDER] = { 0 };
+
+ libxl__datacopier_state *dc = &stream->dc;
+ int rc;
+
+ assert(stream->record_done_callback == NULL);
+
+ dc->writewhat = what;
+ dc->used = 0;
+ dc->callback = write_done;
+ rc = libxl__datacopier_start(dc);
+
+ if (rc) {
+ stream_complete(egc, stream, rc);
+ return;
+ }
+
+ size_t padsz = ROUNDUP(hdr->length, REC_ALIGN_ORDER) - hdr->length;
+ uint32_t length = hdr->length;
+
+ /* Insert header */
+ libxl__datacopier_prefixdata(egc, dc, hdr, sizeof(*hdr));
+
+ /* Optional emulator sub-header */
+ if (emu_hdr) {
+ assert(length >= sizeof(*emu_hdr));
+ libxl__datacopier_prefixdata(egc, dc, emu_hdr, sizeof(*emu_hdr));
+ length -= sizeof(*emu_hdr);
+ }
+
+ /* Optional body */
+ if (body)
+ libxl__datacopier_prefixdata(egc, dc, body, length);
+
+ /* Any required padding */
+ if (padsz > 0)
+ libxl__datacopier_prefixdata(egc, dc,
+ zero_padding, padsz);
+ stream->record_done_callback = cb;
+}
+
+/* Helper to set up writing a regular record to the stream. */
+static void setup_write(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ const char *what,
+ libxl__sr_rec_hdr *hdr,
+ void *body,
+ sws_record_done_cb cb)
+{
+ setup_generic_write(egc, stream, what, hdr, NULL, body, cb);
+}
+
+/* Helper to set up writing a record with an emulator prefix to the stream. */
+static void setup_emulator_write(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ const char *what,
+ libxl__sr_rec_hdr *hdr,
+ libxl__sr_emulator_hdr *emu_hdr,
+ void *body,
+ sws_record_done_cb cb)
+{
+ assert(stream->emu_sub_hdr.id != EMULATOR_UNKNOWN);
+ setup_generic_write(egc, stream, what, hdr, emu_hdr, body, cb);
+}
+
+
+static void write_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval)
+{
+ libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, dc);
+ STATE_AO_GC(stream->ao);
+ sws_record_done_cb cb = stream->record_done_callback;
+
+ stream->record_done_callback = NULL;
+
+ if (onwrite || errnoval)
+ stream_complete(egc, stream, rc ?: ERROR_FAIL);
+ else
+ cb(egc, stream);
+}
+
+/*----- Entrypoints -----*/
+
+void libxl__stream_write_init(libxl__stream_write_state *stream)
+{
+ assert(stream->ao);
+
+ stream->shs.ao = stream->ao;
+ libxl__save_helper_init(&stream->shs);
+
+ stream->rc = 0;
+ stream->running = false;
+ stream->in_checkpoint = false;
+ stream->sync_teardown = false;
+ FILLZERO(stream->dc);
+ stream->record_done_callback = NULL;
+ FILLZERO(stream->emu_dc);
+ stream->emu_carefd = NULL;
+ FILLZERO(stream->emu_rec_hdr);
+ FILLZERO(stream->emu_sub_hdr);
+ stream->emu_body = NULL;
+ stream->device_model_version = LIBXL_DEVICE_MODEL_VERSION_UNKNOWN;
+}
+
+void libxl__stream_write_start(libxl__egc *egc,
+ libxl__stream_write_state *stream)
+{
+ libxl__datacopier_state *dc = &stream->dc;
+ libxl__domain_save_state *dss = stream->dss;
+ STATE_AO_GC(stream->ao);
+ struct libxl__sr_hdr hdr;
+ int rc = 0;
+
+ libxl__stream_write_init(stream);
+
+ stream->running = true;
+
+ dc->ao = ao;
+ dc->readfd = -1;
+ dc->writewhat = "stream header";
+ dc->copywhat = "save v2 stream";
+ dc->writefd = stream->fd;
+ dc->maxsz = -1;
+ dc->callback = stream_header_done;
+
+ if (stream->back_channel)
+ return;
+
+ if (dss->type == LIBXL_DOMAIN_TYPE_HVM) {
+ stream->device_model_version =
+ libxl__device_model_version_running(gc, dss->domid);
+ switch (stream->device_model_version) {
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+ stream->emu_sub_hdr.id = EMULATOR_QEMU_TRADITIONAL;
+ break;
+
+ case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+ stream->emu_sub_hdr.id = EMULATOR_QEMU_UPSTREAM;
+ break;
+
+ default:
+ rc = ERROR_FAIL;
+ LOGD(ERROR, dss->domid, "Unknown emulator for HVM domain");
+ goto err;
+ }
+ stream->emu_sub_hdr.index = 0;
+ }
+
+ rc = libxl__datacopier_start(dc);
+ if (rc)
+ goto err;
+
+ FILLZERO(hdr);
+ hdr.ident = htobe64(RESTORE_STREAM_IDENT);
+ hdr.version = htobe32(RESTORE_STREAM_VERSION);
+ hdr.options = htobe32(0);
+
+ libxl__datacopier_prefixdata(egc, dc, &hdr, sizeof(hdr));
+ return;
+
+ err:
+ assert(rc);
+ stream_complete(egc, stream, rc);
+}
+
+void libxl__stream_write_start_checkpoint(libxl__egc *egc,
+ libxl__stream_write_state *stream)
+{
+ assert(stream->running);
+ assert(!stream->in_checkpoint);
+ assert(!stream->back_channel);
+ stream->in_checkpoint = true;
+
+ write_emulator_xenstore_record(egc, stream);
+}
+
+void libxl__stream_write_abort(libxl__egc *egc,
+ libxl__stream_write_state *stream, int rc)
+{
+ assert(rc);
+
+ if (stream->running)
+ stream_complete(egc, stream, rc);
+}
+
+/*----- Event logic -----*/
+
+static void stream_header_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval)
+{
+ libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, dc);
+ STATE_AO_GC(stream->ao);
+ struct libxl__sr_rec_hdr rec;
+
+ if (rc || errnoval) {
+ stream_complete(egc, stream, rc ?: ERROR_FAIL);
+ return;
+ }
+
+ FILLZERO(rec);
+ rec.type = REC_TYPE_LIBXC_CONTEXT;
+
+ setup_write(egc, stream, "libxc header",
+ &rec, NULL, libxc_header_done);
+}
+
+static void libxc_header_done(libxl__egc *egc,
+ libxl__stream_write_state *stream)
+{
+ libxl__xc_domain_save(egc, stream->dss, &stream->shs);
+}
+
+void libxl__xc_domain_save_done(libxl__egc *egc, void *dss_void,
+ int rc, int retval, int errnoval)
+{
+ libxl__domain_save_state *dss = dss_void;
+ libxl__stream_write_state *stream = &dss->sws;
+ STATE_AO_GC(dss->ao);
+
+ if (rc)
+ goto err;
+
+ if (retval) {
+ LOGEVD(ERROR, errnoval, dss->domid, "saving domain: %s",
+ dss->dsps.guest_responded ?
+ "domain responded to suspend request" :
+ "domain did not respond to suspend request");
+ if (!dss->dsps.guest_responded)
+ rc = ERROR_GUEST_TIMEDOUT;
+ else if (dss->rc)
+ rc = dss->rc;
+ else
+ rc = ERROR_FAIL;
+ goto err;
+ }
+
+ err:
+ check_all_finished(egc, stream, rc);
+
+ /*
+ * This function is the callback associated with the save helper
+ * task, not the stream task. We do not know whether the stream is
+ * alive, and check_all_finished() may have torn it down around us.
+ * If the stream is not still alive, we must not continue any work.
+ */
+ if (libxl__stream_write_inuse(stream)) {
+ if (dss->checkpointed_stream != LIBXL_CHECKPOINTED_STREAM_NONE)
+ /*
+ * For remus, if libxl__xc_domain_save_done() completes,
+ * there was an error sending data to the secondary.
+ * Resume the primary ASAP. The caller doesn't care of the
+ * return value (Please refer to libxl__remus_teardown())
+ */
+ stream_complete(egc, stream, 0);
+ else
+ write_emulator_xenstore_record(egc, stream);
+ }
+}
+
+static void write_emulator_xenstore_record(libxl__egc *egc,
+ libxl__stream_write_state *stream)
+{
+ libxl__domain_save_state *dss = stream->dss;
+ STATE_AO_GC(stream->ao);
+ struct libxl__sr_rec_hdr rec;
+ int rc;
+ char *buf = NULL;
+ uint32_t len = 0;
+
+ if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
+ emulator_xenstore_record_done(egc, stream);
+ return;
+ }
+
+ rc = libxl__save_emulator_xenstore_data(dss, &buf, &len);
+ if (rc)
+ goto err;
+
+ /* No record? - All done. */
+ if (len == 0) {
+ emulator_xenstore_record_done(egc, stream);
+ return;
+ }
+
+ FILLZERO(rec);
+ rec.type = REC_TYPE_EMULATOR_XENSTORE_DATA;
+ rec.length = len + sizeof(stream->emu_sub_hdr);
+
+ setup_emulator_write(egc, stream, "emulator xenstore record",
+ &rec, &stream->emu_sub_hdr, buf,
+ emulator_xenstore_record_done);
+ return;
+
+ err:
+ assert(rc);
+ stream_complete(egc, stream, rc);
+}
+
+static void emulator_xenstore_record_done(libxl__egc *egc,
+ libxl__stream_write_state *stream)
+{
+ libxl__domain_save_state *dss = stream->dss;
+
+ if (dss->type == LIBXL_DOMAIN_TYPE_HVM)
+ write_emulator_context_record(egc, stream);
+ else {
+ if (stream->in_checkpoint)
+ write_checkpoint_end_record(egc, stream);
+ else
+ write_end_record(egc, stream);
+ }
+}
+
+static void write_emulator_context_record(libxl__egc *egc,
+ libxl__stream_write_state *stream)
+{
+ libxl__domain_save_state *dss = stream->dss;
+ libxl__datacopier_state *dc = &stream->emu_dc;
+ STATE_AO_GC(stream->ao);
+ struct libxl__sr_rec_hdr *rec = &stream->emu_rec_hdr;
+ struct stat st;
+ int rc;
+
+ if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
+ emulator_context_record_done(egc, stream);
+ return;
+ }
+
+ /* Convenience aliases */
+ const char *const filename = dss->dsps.dm_savefile;
+
+ libxl__carefd_begin();
+ int readfd = open(filename, O_RDONLY);
+ stream->emu_carefd = libxl__carefd_opened(CTX, readfd);
+ if (readfd == -1) {
+ rc = ERROR_FAIL;
+ LOGED(ERROR, dss->domid, "unable to open %s", filename);
+ goto err;
+ }
+
+ if (fstat(readfd, &st)) {
+ rc = ERROR_FAIL;
+ LOGED(ERROR, dss->domid, "unable to fstat %s", filename);
+ goto err;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ rc = ERROR_FAIL;
+ LOGD(ERROR, dss->domid, "%s is not a plain file!", filename);
+ goto err;
+ }
+
+ rec->type = REC_TYPE_EMULATOR_CONTEXT;
+ rec->length = st.st_size + sizeof(stream->emu_sub_hdr);
+ stream->emu_body = libxl__malloc(NOGC, st.st_size);
+
+ FILLZERO(*dc);
+ dc->ao = stream->ao;
+ dc->readwhat = "qemu save file";
+ dc->copywhat = "save v2 stream";
+ dc->readfd = readfd;
+ dc->writefd = -1;
+ dc->maxsz = -1;
+ dc->readbuf = stream->emu_body;
+ dc->bytes_to_read = st.st_size;
+ dc->callback = emulator_context_read_done;
+
+ rc = libxl__datacopier_start(dc);
+ if (rc)
+ goto err;
+
+ return;
+
+ err:
+ assert(rc);
+ stream_complete(egc, stream, rc);
+}
+
+static void emulator_context_read_done(libxl__egc *egc,
+ libxl__datacopier_state *dc,
+ int rc, int onwrite, int errnoval)
+{
+ libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, emu_dc);
+ STATE_AO_GC(stream->ao);
+
+ if (rc || onwrite || errnoval) {
+ stream_complete(egc, stream, rc ?: ERROR_FAIL);
+ return;
+ }
+
+ libxl__carefd_close(stream->emu_carefd);
+ stream->emu_carefd = NULL;
+
+ setup_emulator_write(egc, stream, "emulator record",
+ &stream->emu_rec_hdr,
+ &stream->emu_sub_hdr,
+ stream->emu_body,
+ emulator_context_record_done);
+}
+
+static void emulator_context_record_done(libxl__egc *egc,
+ libxl__stream_write_state *stream)
+{
+ free(stream->emu_body);
+ stream->emu_body = NULL;
+
+ if (stream->in_checkpoint)
+ write_checkpoint_end_record(egc, stream);
+ else
+ write_end_record(egc, stream);
+}
+
+static void write_end_record(libxl__egc *egc,
+ libxl__stream_write_state *stream)
+{
+ struct libxl__sr_rec_hdr rec;
+
+ FILLZERO(rec);
+ rec.type = REC_TYPE_END;
+
+ setup_write(egc, stream, "end record",
+ &rec, NULL, stream_success);
+}
+
+static void write_checkpoint_end_record(libxl__egc *egc,
+ libxl__stream_write_state *stream)
+{
+ struct libxl__sr_rec_hdr rec;
+
+ FILLZERO(rec);
+ rec.type = REC_TYPE_CHECKPOINT_END;
+
+ setup_write(egc, stream, "checkpoint end record",
+ &rec, NULL, checkpoint_end_record_done);
+}
+
+static void checkpoint_end_record_done(libxl__egc *egc,
+ libxl__stream_write_state *stream)
+{
+ checkpoint_done(egc, stream, 0);
+}
+
+/*----- Success/error/cleanup handling. -----*/
+
+static void stream_success(libxl__egc *egc, libxl__stream_write_state *stream)
+{
+ stream_complete(egc, stream, 0);
+}
+
+static void stream_complete(libxl__egc *egc,
+ libxl__stream_write_state *stream, int rc)
+{
+ assert(stream->running);
+
+ if (stream->in_checkpoint) {
+ assert(rc);
+
+ /*
+ * If an error is encountered while in a checkpoint, pass it
+ * back to libxc. The failure will come back around to us via
+ * libxl__xc_domain_save_done()
+ */
+ checkpoint_done(egc, stream, rc);
+ return;
+ }
+
+ if (stream->in_checkpoint_state) {
+ assert(rc);
+
+ /*
+ * If an error is encountered while in a checkpoint, pass it
+ * back to libxc. The failure will come back around to us via
+ * 1. normal stream
+ * libxl__xc_domain_save_done()
+ * 2. back_channel stream
+ * libxl__stream_write_abort()
+ */
+ checkpoint_state_done(egc, stream, rc);
+ return;
+ }
+
+ stream_done(egc, stream, rc);
+}
+
+static void stream_done(libxl__egc *egc,
+ libxl__stream_write_state *stream, int rc)
+{
+ assert(stream->running);
+ assert(!stream->in_checkpoint_state);
+ stream->running = false;
+
+ if (stream->emu_carefd)
+ libxl__carefd_close(stream->emu_carefd);
+ free(stream->emu_body);
+
+ if (!stream->back_channel) {
+ /*
+ * 1. In stream_done(), stream->running is set to false, so
+ * the stream itself is not in use.
+ * 2. Write stream is a back channel stream, this means it
+ * is only used by secondary(restore side) to send records
+ * back, so it doesn't have save helper.
+ * So we don't need invoke check_all_finished here
+ */
+ check_all_finished(egc, stream, rc);
+ }
+}
+
+static void checkpoint_done(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ int rc)
+{
+ assert(stream->in_checkpoint);
+
+ stream->in_checkpoint = false;
+ stream->checkpoint_callback(egc, stream, rc);
+}
+
+static void check_all_finished(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ int rc)
+{
+ STATE_AO_GC(stream->ao);
+
+ /*
+ * In the case of a failure, the _abort()'s below might cancel
+ * synchronously on top of us, or asynchronously at a later point.
+ *
+ * We must avoid the situation where all _abort() cancel
+ * synchronously and the completion_callback() gets called twice;
+ * once by the first error and once by the final stacked abort(),
+ * both of whom will find that all of the tasks have stopped.
+ *
+ * To avoid this problem, any stacked re-entry into this function is
+ * ineligible to fire the completion callback. The outermost
+ * instance will take care of completing, once the stack has
+ * unwound.
+ */
+ if (stream->sync_teardown)
+ return;
+
+ if (!stream->rc && rc) {
+ /* First reported failure. Tear everything down. */
+ stream->rc = rc;
+ stream->sync_teardown = true;
+
+ libxl__stream_write_abort(egc, stream, rc);
+ libxl__save_helper_abort(egc, &stream->shs);
+
+ stream->sync_teardown = false;
+ }
+
+ /* Don't fire the callback until all our parallel tasks have stopped. */
+ if (libxl__stream_write_inuse(stream) ||
+ libxl__save_helper_inuse(&stream->shs))
+ return;
+
+ if (stream->completion_callback)
+ /* back channel stream doesn't have completion_callback() */
+ stream->completion_callback(egc, stream, stream->rc);
+}
+
+/*----- checkpoint state -----*/
+
+void libxl__stream_write_checkpoint_state(libxl__egc *egc,
+ libxl__stream_write_state *stream,
+ libxl_sr_checkpoint_state *srcs)
+{
+ struct libxl__sr_rec_hdr rec;
+
+ assert(stream->running);
+ assert(!stream->in_checkpoint);
+ assert(!stream->in_checkpoint_state);
+ stream->in_checkpoint_state = true;
+
+ FILLZERO(rec);
+ rec.type = REC_TYPE_CHECKPOINT_STATE;
+ rec.length = sizeof(*srcs);
+
+ setup_write(egc, stream, "checkpoint state", &rec,
+ srcs, write_checkpoint_state_done);
+}
+
+static void write_checkpoint_state_done(libxl__egc *egc,
+ libxl__stream_write_state *stream)
+{
+ checkpoint_state_done(egc, stream, 0);
+}
+
+static void checkpoint_state_done(libxl__egc *egc,
+ libxl__stream_write_state *stream, int rc)
+{
+ assert(stream->in_checkpoint_state);
+ stream->in_checkpoint_state = false;
+ stream->checkpoint_callback(egc, stream, rc);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * fdevent test helpr for the libxl event system
+ */
+
+#include "libxl_internal.h"
+
+#include "libxl_test_fdevent.h"
+
+typedef struct {
+ libxl__ao *ao;
+ libxl__ev_fd fd;
+ libxl__ao_abortable abrt;
+} libxl__test_fdevent;
+
+static void fdevent_complete(libxl__egc *egc, libxl__test_fdevent *tfe,
+ int rc);
+
+static void tfe_init(libxl__test_fdevent *tfe, libxl__ao *ao)
+{
+ tfe->ao = ao;
+ libxl__ev_fd_init(&tfe->fd);
+ libxl__ao_abortable_init(&tfe->abrt);
+}
+
+static void tfe_cleanup(libxl__gc *gc, libxl__test_fdevent *tfe)
+{
+ libxl__ev_fd_deregister(gc, &tfe->fd);
+ libxl__ao_abortable_deregister(&tfe->abrt);
+}
+
+static void tfe_fd_cb(libxl__egc *egc, libxl__ev_fd *ev,
+ int fd, short events, short revents)
+{
+ libxl__test_fdevent *tfe = CONTAINER_OF(ev,*tfe,fd);
+ STATE_AO_GC(tfe->ao);
+ fdevent_complete(egc, tfe, 0);
+}
+
+static void tfe_abrt_cb(libxl__egc *egc, libxl__ao_abortable *abrt,
+ int rc)
+{
+ libxl__test_fdevent *tfe = CONTAINER_OF(abrt,*tfe,abrt);
+ STATE_AO_GC(tfe->ao);
+ fdevent_complete(egc, tfe, rc);
+}
+
+static void fdevent_complete(libxl__egc *egc, libxl__test_fdevent *tfe,
+ int rc)
+{
+ STATE_AO_GC(tfe->ao);
+ tfe_cleanup(gc, tfe);
+ libxl__ao_complete(egc, ao, rc);
+}
+
+int libxl_test_fdevent(libxl_ctx *ctx, int fd, short events,
+ libxl_asyncop_how *ao_how)
+{
+ int rc;
+ libxl__test_fdevent *tfe;
+
+ AO_CREATE(ctx, 0, ao_how);
+ GCNEW(tfe);
+
+ tfe_init(tfe, ao);
+
+ rc = libxl__ev_fd_register(gc, &tfe->fd, tfe_fd_cb, fd, events);
+ if (rc) goto out;
+
+ tfe->abrt.ao = ao;
+ tfe->abrt.callback = tfe_abrt_cb;
+ rc = libxl__ao_abortable_register(&tfe->abrt);
+ if (rc) goto out;
+
+ return AO_INPROGRESS;
+
+ out:
+ tfe_cleanup(gc, tfe);
+ return AO_CREATE_FAIL(rc);
+}
--- /dev/null
+#ifndef TEST_FDEVENT_H
+#define TEST_FDEVENT_H
+
+#include <pthread.h>
+
+int libxl_test_fdevent(libxl_ctx *ctx, int fd, short events,
+ libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+/* This operation waits for one of the poll events to occur on fd, and
+ * then completes successfully. (Or, it can be aborted.) */
+
+#endif /*TEST_FDEVENT_H*/
--- /dev/null
+/*
+ * timedereg test case for the libxl event system
+ *
+ * To run this test:
+ * ./test_timedereg
+ * Success:
+ * program takes a few seconds, prints some debugging output and exits 0
+ * Failure:
+ * crash
+ *
+ * set up [0]-group timeouts 0 1 2
+ * wait for timeout 1 to occur
+ * deregister 0 and 2. 1 is supposed to be deregistered already
+ * register [1]-group 0 1 2
+ * deregister 1 (should be a no-op)
+ * wait for [1]-group 0 1 2 in turn
+ * on final callback assert that all have been deregistered
+ */
+
+#include "libxl_internal.h"
+
+#include "libxl_test_timedereg.h"
+
+#define NTIMES 3
+static const int ms[2][NTIMES] = { { 2000,1000,2000 }, { 1000,2000,3000 } };
+static libxl__ev_time et[2][NTIMES];
+static libxl__ao *tao;
+static int seq;
+
+static void occurs(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs, int rc);
+
+static void regs(libxl__ao *ao, int j)
+{
+ AO_GC;
+ int rc, i;
+ LOG(DEBUG,"regs(%d)", j);
+ for (i=0; i<NTIMES; i++) {
+ rc = libxl__ev_time_register_rel(ao, &et[j][i], occurs, ms[j][i]);
+ assert(!rc);
+ }
+}
+
+int libxl_test_timedereg(libxl_ctx *ctx, libxl_asyncop_how *ao_how)
+{
+ int i;
+ AO_CREATE(ctx, 0, ao_how);
+
+ tao = ao;
+
+ for (i=0; i<NTIMES; i++) {
+ libxl__ev_time_init(&et[0][i]);
+ libxl__ev_time_init(&et[1][i]);
+ }
+
+ regs(ao, 0);
+
+ return AO_INPROGRESS;
+}
+
+static void occurs(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs, int rc)
+{
+ EGC_GC;
+ int i;
+
+ int off = ev - &et[0][0];
+ LOG(DEBUG,"occurs[%d][%d] seq=%d rc=%d", off/NTIMES, off%NTIMES, seq, rc);
+
+ assert(rc == ERROR_TIMEDOUT);
+
+ switch (seq) {
+ case 0:
+ assert(ev == &et[0][1]);
+ libxl__ev_time_deregister(gc, &et[0][0]);
+ libxl__ev_time_deregister(gc, &et[0][2]);
+ regs(tao, 1);
+ libxl__ev_time_deregister(gc, &et[0][1]);
+ break;
+
+ case 1:
+ case 2:
+ assert(ev == &et[1][seq-1]);
+ break;
+
+ case 3:
+ assert(ev == &et[1][2]);
+ for (i=0; i<NTIMES; i++) {
+ assert(!libxl__ev_time_isregistered(&et[0][i]));
+ assert(!libxl__ev_time_isregistered(&et[1][i]));
+ }
+ libxl__ao_complete(egc, tao, 0);
+ return;
+
+ default:
+ abort();
+ }
+
+ seq++;
+}
--- /dev/null
+#ifndef TEST_TIMEDEREG_H
+#define TEST_TIMEDEREG_H
+
+#include <pthread.h>
+
+int libxl_test_timedereg(libxl_ctx *ctx, libxl_asyncop_how *ao_how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+#endif /*TEST_TIMEDEREG_H*/
--- /dev/null
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+/* TMEM is gone. Leave some stubs here. */
+
+char *libxl_tmem_list(libxl_ctx *ctx, uint32_t domid, int use_long)
+{
+ GC_INIT(ctx);
+ LOGED(ERROR, domid, "Can not get tmem list");
+ GC_FREE;
+ return NULL;
+}
+
+int libxl_tmem_freeze(libxl_ctx *ctx, uint32_t domid)
+{
+ GC_INIT(ctx);
+ LOGED(ERROR, domid, "Can not freeze tmem pools");
+ GC_FREE;
+ return ERROR_FAIL;
+}
+
+int libxl_tmem_thaw(libxl_ctx *ctx, uint32_t domid)
+{
+ GC_INIT(ctx);
+ LOGED(ERROR, domid, "Can not thaw tmem pools");
+ GC_FREE;
+ return ERROR_FAIL;
+}
+
+int libxl_tmem_set(libxl_ctx *ctx, uint32_t domid, char* name, uint32_t set)
+{
+ GC_INIT(ctx);
+ LOGED(ERROR, domid, "Can not set tmem %s", name);
+ GC_FREE;
+ return ERROR_FAIL;
+}
+
+int libxl_tmem_shared_auth(libxl_ctx *ctx, uint32_t domid,
+ char* uuid, int auth)
+{
+ GC_INIT(ctx);
+ LOGED(ERROR, domid, "Can not set tmem shared auth");
+ GC_FREE;
+ return ERROR_FAIL;
+}
+
+int libxl_tmem_freeable(libxl_ctx *ctx)
+{
+ GC_INIT(ctx);
+ LOGE(ERROR, "Can not get tmem freeable memory");
+ GC_FREE;
+ return ERROR_FAIL;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+# -*- python -*-
+#
+# Builtin libxl types
+#
+
+namespace("libxl_")
+
+libxl_defbool = Builtin("defbool", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, copy_fn=None,
+ check_default_fn="libxl__defbool_is_default")
+libxl_domid = Builtin("domid", json_gen_fn = "yajl_gen_integer", json_parse_fn = "libxl__uint32_parse_json",
+ json_parse_type = "JSON_INTEGER", autogenerate_json = False, copy_fn=None)
+libxl_devid = Builtin("devid", json_gen_fn = "yajl_gen_integer", json_parse_fn = "libxl__int_parse_json",
+ json_parse_type = "JSON_INTEGER", autogenerate_json = False, signed = True, init_val="-1",
+ copy_fn=None)
+libxl_uuid = Builtin("uuid", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, check_default_fn="libxl_uuid_is_nil",
+ copy_fn="libxl_uuid_copy")
+libxl_mac = Builtin("mac", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, check_default_fn="libxl__mac_is_default",
+ copy_fn="libxl_mac_copy")
+libxl_bitmap = Builtin("bitmap", json_parse_type="JSON_ARRAY", dispose_fn="libxl_bitmap_dispose", passby=PASS_BY_REFERENCE,
+ check_default_fn="libxl_bitmap_is_empty", copy_fn="libxl_bitmap_copy_alloc")
+libxl_cpuid_policy_list = Builtin("cpuid_policy_list", dispose_fn="libxl_cpuid_dispose", passby=PASS_BY_REFERENCE,
+ json_parse_type="JSON_ARRAY", check_default_fn="libxl__cpuid_policy_is_empty",
+ copy_fn="libxl_cpuid_policy_list_copy")
+
+libxl_string_list = Builtin("string_list", dispose_fn="libxl_string_list_dispose", passby=PASS_BY_REFERENCE,
+ json_parse_type="JSON_ARRAY", check_default_fn="libxl__string_list_is_empty",
+ copy_fn="libxl_string_list_copy")
+libxl_key_value_list = Builtin("key_value_list", dispose_fn="libxl_key_value_list_dispose", passby=PASS_BY_REFERENCE,
+ json_parse_type="JSON_MAP", check_default_fn="libxl__key_value_list_is_empty",
+ copy_fn="libxl_key_value_list_copy")
+libxl_hwcap = Builtin("hwcap", passby=PASS_BY_REFERENCE, json_parse_type="JSON_ARRAY",
+ check_default_fn="libxl__hwcap_is_default", copy_fn="libxl_hwcap_copy")
+libxl_ms_vm_genid = Builtin("ms_vm_genid", passby=PASS_BY_REFERENCE, check_default_fn="libxl_ms_vm_genid_is_zero",
+ copy_fn="libxl_ms_vm_genid_copy")
+
+#
+# Specific integer types
+#
+
+MemKB = UInt(64, init_val = "LIBXL_MEMKB_DEFAULT", json_gen_fn = "libxl__uint64_gen_json")
+
+#
+# Constants / Enumerations
+#
+
+libxl_error = Enumeration("error", [
+ (-1, "NONSPECIFIC"),
+ (-2, "VERSION"),
+ (-3, "FAIL"),
+ (-4, "NI"),
+ (-5, "NOMEM"),
+ (-6, "INVAL"),
+ (-7, "BADFAIL"),
+ (-8, "GUEST_TIMEDOUT"),
+ (-9, "TIMEDOUT"),
+ (-10, "NOPARAVIRT"),
+ (-11, "NOT_READY"),
+ (-12, "OSEVENT_REG_FAIL"),
+ (-13, "BUFFERFULL"),
+ (-14, "UNKNOWN_CHILD"),
+ (-15, "LOCK_FAIL"),
+ (-16, "JSON_CONFIG_EMPTY"),
+ (-17, "DEVICE_EXISTS"),
+ (-18, "CHECKPOINT_DEVOPS_DOES_NOT_MATCH"),
+ (-19, "CHECKPOINT_DEVICE_NOT_SUPPORTED"),
+ (-20, "VNUMA_CONFIG_INVALID"),
+ (-21, "DOMAIN_NOTFOUND"),
+ (-22, "ABORTED"),
+ (-23, "NOTFOUND"),
+ (-24, "DOMAIN_DESTROYED"), # Target domain ceased to exist during op
+ (-25, "FEATURE_REMOVED"), # For functionality that has been removed
+ (-26, "PROTOCOL_ERROR_QMP"),
+ (-27, "UNKNOWN_QMP_ERROR"),
+ (-28, "QMP_GENERIC_ERROR"), # unspecified qmp error
+ (-29, "QMP_COMMAND_NOT_FOUND"), # the requested command has not been found
+ (-30, "QMP_DEVICE_NOT_ACTIVE"), # a device has failed to be become active
+ (-31, "QMP_DEVICE_NOT_FOUND"), # the requested device has not been found
+ (-32, "QEMU_API"), # QEMU's replies don't contains expected members
+ ], value_namespace = "")
+
+libxl_domain_type = Enumeration("domain_type", [
+ (-1, "INVALID"),
+ (1, "HVM"),
+ (2, "PV"),
+ (3, "PVH"),
+ ], init_val = "LIBXL_DOMAIN_TYPE_INVALID")
+
+libxl_rdm_reserve_strategy = Enumeration("rdm_reserve_strategy", [
+ (0, "ignore"),
+ (1, "host"),
+ ])
+
+libxl_rdm_reserve_policy = Enumeration("rdm_reserve_policy", [
+ (-1, "invalid"),
+ (0, "strict"),
+ (1, "relaxed"),
+ ], init_val = "LIBXL_RDM_RESERVE_POLICY_INVALID")
+
+libxl_channel_connection = Enumeration("channel_connection", [
+ (0, "UNKNOWN"),
+ (1, "PTY"),
+ (2, "SOCKET"), # a listening Unix domain socket
+ ])
+
+libxl_device_model_version = Enumeration("device_model_version", [
+ (0, "UNKNOWN"),
+ (1, "QEMU_XEN_TRADITIONAL"), # Historical qemu-xen device model (qemu-dm)
+ (2, "QEMU_XEN"), # Upstream based qemu-xen device model
+ ])
+
+libxl_console_type = Enumeration("console_type", [
+ (0, "UNKNOWN"),
+ (1, "SERIAL"),
+ (2, "PV"),
+ (3, "VUART"),
+ ])
+
+libxl_disk_format = Enumeration("disk_format", [
+ (0, "UNKNOWN"),
+ (1, "QCOW"),
+ (2, "QCOW2"),
+ (3, "VHD"),
+ (4, "RAW"),
+ (5, "EMPTY"),
+ (6, "QED"),
+ ])
+
+libxl_disk_backend = Enumeration("disk_backend", [
+ (0, "UNKNOWN"),
+ (1, "PHY"),
+ (2, "TAP"),
+ (3, "QDISK"),
+ ])
+
+libxl_nic_type = Enumeration("nic_type", [
+ (0, "UNKNOWN"),
+ (1, "VIF_IOEMU"),
+ (2, "VIF"),
+ ])
+
+libxl_action_on_shutdown = Enumeration("action_on_shutdown", [
+ (1, "DESTROY"),
+
+ (2, "RESTART"),
+ (3, "RESTART_RENAME"),
+
+ (4, "PRESERVE"),
+
+ (5, "COREDUMP_DESTROY"),
+ (6, "COREDUMP_RESTART"),
+
+ (7, "SOFT_RESET"),
+ ], init_val = "LIBXL_ACTION_ON_SHUTDOWN_DESTROY")
+
+libxl_trigger = Enumeration("trigger", [
+ (0, "UNKNOWN"),
+ (1, "POWER"),
+ (2, "SLEEP"),
+ (3, "NMI"),
+ (4, "INIT"),
+ (5, "RESET"),
+ (6, "S3RESUME"),
+ ])
+
+libxl_tsc_mode = Enumeration("tsc_mode", [
+ (0, "default"),
+ (1, "always_emulate"),
+ (2, "native"),
+ (3, "native_paravirt"),
+ ])
+
+libxl_gfx_passthru_kind = Enumeration("gfx_passthru_kind", [
+ (0, "default"),
+ (1, "igd"),
+ ])
+
+# Consistent with the values defined for HVM_PARAM_TIMER_MODE.
+libxl_timer_mode = Enumeration("timer_mode", [
+ (-1, "unknown"),
+ (0, "delay_for_missed_ticks"),
+ (1, "no_delay_for_missed_ticks"),
+ (2, "no_missed_ticks_pending"),
+ (3, "one_missed_tick_pending"),
+ ], init_val = "LIBXL_TIMER_MODE_DEFAULT",
+ check_default_fn = "libxl__timer_mode_is_default")
+
+libxl_bios_type = Enumeration("bios_type", [
+ (0, "unknown"),
+ (1, "rombios"),
+ (2, "seabios"),
+ (3, "ovmf"),
+ ])
+
+# Consistent with values defined in domctl.h
+# Except unknown which we have made up
+libxl_scheduler = Enumeration("scheduler", [
+ (0, "unknown"),
+ (4, "sedf"),
+ (5, "credit"),
+ (6, "credit2"),
+ (7, "arinc653"),
+ (8, "rtds"),
+ (9, "null"),
+ ])
+
+# Consistent with SHUTDOWN_* in sched.h (apart from UNKNOWN)
+libxl_shutdown_reason = Enumeration("shutdown_reason", [
+ (-1, "unknown"),
+ (0, "poweroff"),
+ (1, "reboot"),
+ (2, "suspend"),
+ (3, "crash"),
+ (4, "watchdog"),
+ (5, "soft_reset"),
+ ], init_val = "LIBXL_SHUTDOWN_REASON_UNKNOWN")
+
+libxl_vga_interface_type = Enumeration("vga_interface_type", [
+ (0, "UNKNOWN"),
+ (1, "CIRRUS"),
+ (2, "STD"),
+ (3, "NONE"),
+ (4, "QXL"),
+ ], init_val = "LIBXL_VGA_INTERFACE_TYPE_UNKNOWN")
+
+libxl_vendor_device = Enumeration("vendor_device", [
+ (0, "NONE"),
+ (1, "XENSERVER"),
+ ])
+
+libxl_viridian_enlightenment = Enumeration("viridian_enlightenment", [
+ (0, "base"),
+ (1, "freq"),
+ (2, "time_ref_count"),
+ (3, "reference_tsc"),
+ (4, "hcall_remote_tlb_flush"),
+ (5, "apic_assist"),
+ (6, "crash_ctl"),
+ (7, "synic"),
+ (8, "stimer"),
+ (9, "hcall_ipi"),
+ ])
+
+libxl_hdtype = Enumeration("hdtype", [
+ (1, "IDE"),
+ (2, "AHCI"),
+ ], init_val = "LIBXL_HDTYPE_IDE")
+
+# Consistent with the values defined for migration_stream.
+libxl_checkpointed_stream = Enumeration("checkpointed_stream", [
+ (0, "NONE"),
+ (1, "REMUS"),
+ (2, "COLO"),
+ ])
+
+libxl_vuart_type = Enumeration("vuart_type", [
+ (0, "unknown"),
+ (1, "sbsa_uart"),
+ ])
+
+libxl_vkb_backend = Enumeration("vkb_backend", [
+ (0, "UNKNOWN"),
+ (1, "QEMU"),
+ (2, "LINUX")
+ ])
+
+libxl_passthrough = Enumeration("passthrough", [
+ (0, "default"),
+ (1, "disabled"),
+ (2, "enabled"), # becomes {sync,share}_pt once defaults are evaluated
+ (3, "sync_pt"),
+ (4, "share_pt"),
+ ])
+
+#
+# Complex libxl types
+#
+
+libxl_ioport_range = Struct("ioport_range", [
+ ("first", uint32),
+ ("number", uint32),
+ ])
+
+libxl_iomem_range = Struct("iomem_range", [
+ # start host frame number to be mapped to the guest
+ ("start", uint64),
+ # number of frames to be mapped
+ ("number", uint64),
+ # guest frame number used as a start for the mapping
+ ("gfn", uint64, {'init_val': "LIBXL_INVALID_GFN"}),
+ ])
+
+libxl_vga_interface_info = Struct("vga_interface_info", [
+ ("kind", libxl_vga_interface_type),
+ ])
+
+libxl_vnc_info = Struct("vnc_info", [
+ ("enable", libxl_defbool),
+ # "address:port" that should be listened on
+ ("listen", string),
+ ("passwd", string),
+ ("display", integer),
+ # If set then try to find an unused port
+ ("findunused", libxl_defbool),
+ ])
+
+libxl_spice_info = Struct("spice_info", [
+ ("enable", libxl_defbool),
+ # At least one of spice port or spicetls_post must be given
+ ("port", integer),
+ ("tls_port", integer),
+ # Interface to bind to
+ ("host", string),
+ # enable client connection with no password
+ ("disable_ticketing", libxl_defbool),
+ ("passwd", string),
+ ("agent_mouse", libxl_defbool),
+ ("vdagent", libxl_defbool),
+ ("clipboard_sharing", libxl_defbool),
+ ("usbredirection", integer),
+ ("image_compression", string),
+ ("streaming_video", string),
+ ])
+
+libxl_sdl_info = Struct("sdl_info", [
+ ("enable", libxl_defbool),
+ ("opengl", libxl_defbool),
+ ("display", string),
+ ("xauthority", string),
+ ])
+
+libxl_dominfo = Struct("dominfo",[
+ ("uuid", libxl_uuid),
+ ("domid", libxl_domid),
+ ("ssidref", uint32),
+ ("ssid_label", string),
+ ("running", bool),
+ ("blocked", bool),
+ ("paused", bool),
+ ("shutdown", bool),
+ ("dying", bool),
+ ("never_stop", bool),
+
+ # Valid iff ->shutdown is true.
+ #
+ # Otherwise set to a value guaranteed not to clash with any valid
+ # LIBXL_SHUTDOWN_REASON_* constant.
+ ("shutdown_reason", libxl_shutdown_reason),
+ ("outstanding_memkb", MemKB),
+ ("current_memkb", MemKB),
+ ("shared_memkb", MemKB),
+ ("paged_memkb", MemKB),
+ ("max_memkb", MemKB),
+ ("cpu_time", uint64),
+ ("vcpu_max_id", uint32),
+ ("vcpu_online", uint32),
+ ("cpupool", uint32),
+ ("domain_type", libxl_domain_type),
+ ], dir=DIR_OUT)
+
+libxl_cpupoolinfo = Struct("cpupoolinfo", [
+ ("poolid", uint32),
+ ("pool_name", string),
+ ("sched", libxl_scheduler),
+ ("n_dom", uint32),
+ ("cpumap", libxl_bitmap)
+ ], dir=DIR_OUT)
+
+libxl_channelinfo = Struct("channelinfo", [
+ ("backend", string),
+ ("backend_id", uint32),
+ ("frontend", string),
+ ("frontend_id", uint32),
+ ("devid", libxl_devid),
+ ("state", integer),
+ ("evtch", integer),
+ ("rref", integer),
+ ("u", KeyedUnion(None, libxl_channel_connection, "connection",
+ [("unknown", None),
+ ("pty", Struct(None, [("path", string),])),
+ ("socket", None),
+ ])),
+ ], dir=DIR_OUT)
+
+libxl_vminfo = Struct("vminfo", [
+ ("uuid", libxl_uuid),
+ ("domid", libxl_domid),
+ ], dir=DIR_OUT)
+
+libxl_version_info = Struct("version_info", [
+ ("xen_version_major", integer),
+ ("xen_version_minor", integer),
+ ("xen_version_extra", string),
+ ("compiler", string),
+ ("compile_by", string),
+ ("compile_domain", string),
+ ("compile_date", string),
+ ("capabilities", string),
+ ("changeset", string),
+ ("virt_start", uint64),
+ ("pagesize", integer),
+ ("commandline", string),
+ ("build_id", string),
+ ], dir=DIR_OUT)
+
+libxl_domain_create_info = Struct("domain_create_info",[
+ ("type", libxl_domain_type),
+ ("hap", libxl_defbool),
+ ("oos", libxl_defbool),
+ ("ssidref", uint32),
+ ("ssid_label", string),
+ ("name", string),
+ ("domid", libxl_domid),
+ ("uuid", libxl_uuid),
+ ("xsdata", libxl_key_value_list),
+ ("platformdata", libxl_key_value_list),
+ ("poolid", uint32),
+ ("pool_name", string),
+ ("run_hotplug_scripts",libxl_defbool),
+ ("driver_domain",libxl_defbool),
+ ("passthrough", libxl_passthrough),
+ ("xend_suspend_evtchn_compat",libxl_defbool),
+ ], dir=DIR_IN)
+
+libxl_domain_restore_params = Struct("domain_restore_params", [
+ ("checkpointed_stream", integer),
+ ("stream_version", uint32, {'init_val': '1'}),
+ ("colo_proxy_script", string),
+ ("userspace_colo_proxy", libxl_defbool),
+ ])
+
+libxl_sched_params = Struct("sched_params",[
+ ("vcpuid", integer, {'init_val': 'LIBXL_SCHED_PARAM_VCPU_INDEX_DEFAULT'}),
+ ("weight", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT'}),
+ ("cap", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT'}),
+ ("period", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT'}),
+ ("extratime", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT'}),
+ ("budget", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT'}),
+ ])
+
+libxl_vcpu_sched_params = Struct("vcpu_sched_params",[
+ ("sched", libxl_scheduler),
+ ("vcpus", Array(libxl_sched_params, "num_vcpus")),
+ ])
+
+libxl_domain_sched_params = Struct("domain_sched_params",[
+ ("sched", libxl_scheduler),
+ ("weight", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT'}),
+ ("cap", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT'}),
+ ("period", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT'}),
+ ("budget", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT'}),
+ ("extratime", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT'}),
+
+ # The following three parameters ('slice' and 'latency') are deprecated,
+ # and will have no effect if used, since the SEDF scheduler has been removed.
+ # Note that 'period' and 'extratime' was an SDF parameter too, but it is still effective
+ # as they are now used (together with 'budget') by the RTDS scheduler.
+ ("slice", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_SLICE_DEFAULT'}),
+ ("latency", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_LATENCY_DEFAULT'}),
+ ])
+
+libxl_vnode_info = Struct("vnode_info", [
+ ("memkb", MemKB),
+ ("distances", Array(uint32, "num_distances")), # distances from this node to other nodes
+ ("pnode", uint32), # physical node of this node
+ ("vcpus", libxl_bitmap), # vcpus in this node
+ ])
+
+libxl_gic_version = Enumeration("gic_version", [
+ (0, "DEFAULT"),
+ (0x20, "v2"),
+ (0x30, "v3")
+ ], init_val = "LIBXL_GIC_VERSION_DEFAULT")
+
+libxl_tee_type = Enumeration("tee_type", [
+ (0, "none"),
+ (1, "optee")
+ ], init_val = "LIBXL_TEE_TYPE_NONE")
+
+libxl_rdm_reserve = Struct("rdm_reserve", [
+ ("strategy", libxl_rdm_reserve_strategy),
+ ("policy", libxl_rdm_reserve_policy),
+ ])
+
+# Consistent with the values defined for HVM_PARAM_ALTP2M
+libxl_altp2m_mode = Enumeration("altp2m_mode", [
+ (0, "disabled"),
+ (1, "mixed"),
+ (2, "external"),
+ (3, "limited"),
+ ], init_val = "LIBXL_ALTP2M_MODE_DISABLED")
+
+libxl_domain_build_info = Struct("domain_build_info",[
+ ("max_vcpus", integer),
+ ("avail_vcpus", libxl_bitmap),
+ ("cpumap", libxl_bitmap),
+ ("nodemap", libxl_bitmap),
+ ("vcpu_hard_affinity", Array(libxl_bitmap, "num_vcpu_hard_affinity")),
+ ("vcpu_soft_affinity", Array(libxl_bitmap, "num_vcpu_soft_affinity")),
+ ("numa_placement", libxl_defbool),
+ ("tsc_mode", libxl_tsc_mode),
+ ("max_memkb", MemKB),
+ ("target_memkb", MemKB),
+ ("video_memkb", MemKB),
+ ("shadow_memkb", MemKB),
+ ("iommu_memkb", MemKB),
+ ("rtc_timeoffset", uint32),
+ ("exec_ssidref", uint32),
+ ("exec_ssid_label", string),
+ ("localtime", libxl_defbool),
+ ("disable_migrate", libxl_defbool),
+ ("cpuid", libxl_cpuid_policy_list),
+ ("blkdev_start", string),
+
+ ("vnuma_nodes", Array(libxl_vnode_info, "num_vnuma_nodes")),
+
+ ("max_grant_frames", uint32, {'init_val': 'LIBXL_MAX_GRANT_DEFAULT'}),
+ ("max_maptrack_frames", uint32, {'init_val': 'LIBXL_MAX_GRANT_DEFAULT'}),
+
+ ("device_model_version", libxl_device_model_version),
+ ("device_model_stubdomain", libxl_defbool),
+ ("stubdomain_memkb", MemKB),
+ ("stubdomain_kernel", string),
+ ("stubdomain_ramdisk", string),
+ # if you set device_model you must set device_model_version too
+ ("device_model", string),
+ ("device_model_ssidref", uint32),
+ ("device_model_ssid_label", string),
+ ("device_model_user", string),
+
+ # extra parameters pass directly to qemu, NULL terminated
+ ("extra", libxl_string_list),
+ # extra parameters pass directly to qemu for PV guest, NULL terminated
+ ("extra_pv", libxl_string_list),
+ # extra parameters pass directly to qemu for HVM guest, NULL terminated
+ ("extra_hvm", libxl_string_list),
+ # parameters for all type of scheduler
+ ("sched_params", libxl_domain_sched_params),
+
+ ("ioports", Array(libxl_ioport_range, "num_ioports")),
+ ("irqs", Array(uint32, "num_irqs")),
+ ("iomem", Array(libxl_iomem_range, "num_iomem")),
+ ("claim_mode", libxl_defbool),
+ ("event_channels", uint32),
+ ("kernel", string),
+ ("cmdline", string),
+ ("ramdisk", string),
+ # Given the complexity of verifying the validity of a device tree,
+ # libxl doesn't do any security check on it. It's the responsibility
+ # of the caller to provide only trusted device tree.
+ # Note that the partial device tree should avoid to use the phandle
+ # 65000 which is reserved by the toolstack.
+ ("device_tree", string),
+ ("acpi", libxl_defbool),
+ ("bootloader", string),
+ ("bootloader_args", libxl_string_list),
+ ("timer_mode", libxl_timer_mode),
+ ("nested_hvm", libxl_defbool),
+ ("apic", libxl_defbool),
+ ("dm_restrict", libxl_defbool),
+ ("tee", libxl_tee_type),
+ ("u", KeyedUnion(None, libxl_domain_type, "type",
+ [("hvm", Struct(None, [("firmware", string),
+ ("bios", libxl_bios_type),
+ ("pae", libxl_defbool),
+ ("apic", libxl_defbool, {'deprecated_by': 'apic'}),
+ # The following acpi field is deprecated.
+ # Please use the unified acpi field above
+ # which works for both x86 and ARM.
+ ("acpi", libxl_defbool),
+ ("acpi_s3", libxl_defbool),
+ ("acpi_s4", libxl_defbool),
+ ("acpi_laptop_slate",libxl_defbool),
+ ("nx", libxl_defbool),
+ ("viridian", libxl_defbool),
+ ("viridian_enable", libxl_bitmap),
+ ("viridian_disable", libxl_bitmap),
+ ("timeoffset", string),
+ ("hpet", libxl_defbool),
+ ("vpt_align", libxl_defbool),
+ ("mmio_hole_memkb", MemKB),
+ ("timer_mode", libxl_timer_mode, {'deprecated_by': 'timer_mode'}),
+ ("nested_hvm", libxl_defbool, {'deprecated_by': 'nested_hvm'}),
+ # The u.hvm.altp2m field is used solely
+ # for x86 HVM guests and is maintained
+ # for legacy purposes.
+ ("altp2m", libxl_defbool),
+ ("system_firmware", string),
+ ("smbios_firmware", string),
+ ("acpi_firmware", string),
+ ("hdtype", libxl_hdtype),
+ ("nographic", libxl_defbool),
+ ("vga", libxl_vga_interface_info),
+ ("vnc", libxl_vnc_info),
+ # keyboard layout, default is en-us keyboard
+ ("keymap", string),
+ ("sdl", libxl_sdl_info),
+ ("spice", libxl_spice_info),
+
+ ("gfx_passthru", libxl_defbool),
+ ("gfx_passthru_kind", libxl_gfx_passthru_kind),
+
+ ("serial", string),
+ ("boot", string),
+ ("usb", libxl_defbool),
+ ("usbversion", integer),
+ # usbdevice:
+ # - "tablet" for absolute mouse,
+ # - "mouse" for PS/2 protocol relative mouse
+ ("usbdevice", string),
+ ("vkb_device", libxl_defbool),
+ ("soundhw", string),
+ ("xen_platform_pci", libxl_defbool),
+ ("usbdevice_list", libxl_string_list),
+ ("vendor_device", libxl_vendor_device),
+ # See libxl_ms_vm_genid_generate()
+ ("ms_vm_genid", libxl_ms_vm_genid),
+ ("serial_list", libxl_string_list),
+ ("rdm", libxl_rdm_reserve),
+ ("rdm_mem_boundary_memkb", MemKB),
+ ("mca_caps", uint64),
+ ])),
+ ("pv", Struct(None, [("kernel", string, {'deprecated_by': 'kernel'}),
+ ("slack_memkb", MemKB),
+ ("bootloader", string, {'deprecated_by': 'bootloader'}),
+ ("bootloader_args", libxl_string_list, {'deprecated_by': 'bootloader_args'}),
+ ("cmdline", string, {'deprecated_by': 'cmdline'}),
+ ("ramdisk", string, {'deprecated_by': 'ramdisk'}),
+ ("features", string, {'const': True}),
+ # Use host's E820 for PCI passthrough.
+ ("e820_host", libxl_defbool),
+ ])),
+ ("pvh", Struct(None, [("pvshim", libxl_defbool),
+ ("pvshim_path", string),
+ ("pvshim_cmdline", string),
+ ("pvshim_extra", string), # eg "loglvl=all guest_loglvl=all apic_verbosity=debug e820-verbose"
+ ])),
+ ("invalid", None),
+ ], keyvar_init_val = "LIBXL_DOMAIN_TYPE_INVALID")),
+
+
+ ("arch_arm", Struct(None, [("gic_version", libxl_gic_version),
+ ("vuart", libxl_vuart_type),
+ ])),
+ # Alternate p2m is not bound to any architecture or guest type, as it is
+ # supported by x86 HVM and ARM support is planned.
+ ("altp2m", libxl_altp2m_mode),
+
+ ], dir=DIR_IN,
+ copy_deprecated_fn="libxl__domain_build_info_copy_deprecated",
+)
+
+libxl_device_vfb = Struct("device_vfb", [
+ ("backend_domid", libxl_domid),
+ ("backend_domname",string),
+ ("devid", libxl_devid),
+ ("vnc", libxl_vnc_info),
+ ("sdl", libxl_sdl_info),
+ # set keyboard layout, default is en-us keyboard
+ ("keymap", string),
+ ])
+
+libxl_device_vkb = Struct("device_vkb", [
+ ("backend_domid", libxl_domid),
+ ("backend_domname", string),
+ ("devid", libxl_devid),
+ ("backend_type", libxl_vkb_backend),
+ ("unique_id", string),
+ ("feature_disable_keyboard", bool),
+ ("feature_disable_pointer", bool),
+ ("feature_abs_pointer", bool),
+ ("feature_raw_pointer", bool),
+ ("feature_multi_touch", bool),
+ ("width", uint32),
+ ("height", uint32),
+ ("multi_touch_width", uint32),
+ ("multi_touch_height", uint32),
+ ("multi_touch_num_contacts", uint32)
+ ])
+
+libxl_device_disk = Struct("device_disk", [
+ ("backend_domid", libxl_domid),
+ ("backend_domname", string),
+ ("pdev_path", string),
+ ("vdev", string),
+ ("backend", libxl_disk_backend),
+ ("format", libxl_disk_format),
+ ("script", string),
+ ("removable", integer),
+ ("readwrite", integer),
+ ("is_cdrom", integer),
+ ("direct_io_safe", bool),
+ ("discard_enable", libxl_defbool),
+ # Note that the COLO configuration settings should be considered unstable.
+ # They may change incompatibly in future versions of Xen.
+ ("colo_enable", libxl_defbool),
+ ("colo_restore_enable", libxl_defbool),
+ ("colo_host", string),
+ ("colo_port", integer),
+ ("colo_export", string),
+ ("active_disk", string),
+ ("hidden_disk", string)
+ ])
+
+libxl_device_nic = Struct("device_nic", [
+ ("backend_domid", libxl_domid),
+ ("backend_domname", string),
+ ("devid", libxl_devid),
+ ("mtu", integer),
+ ("model", string),
+ ("mac", libxl_mac),
+ ("ip", string),
+ ("bridge", string),
+ ("ifname", string),
+ ("script", string),
+ ("nictype", libxl_nic_type),
+ ("rate_bytes_per_interval", uint64),
+ ("rate_interval_usecs", uint32),
+ ("gatewaydev", string),
+ # Note that the COLO configuration settings should be considered unstable.
+ # They may change incompatibly in future versions of Xen.
+ ("coloft_forwarddev", string),
+ ("colo_sock_mirror_id", string),
+ ("colo_sock_mirror_ip", string),
+ ("colo_sock_mirror_port", string),
+ ("colo_sock_compare_pri_in_id", string),
+ ("colo_sock_compare_pri_in_ip", string),
+ ("colo_sock_compare_pri_in_port", string),
+ ("colo_sock_compare_sec_in_id", string),
+ ("colo_sock_compare_sec_in_ip", string),
+ ("colo_sock_compare_sec_in_port", string),
+ ("colo_sock_compare_notify_id", string),
+ ("colo_sock_compare_notify_ip", string),
+ ("colo_sock_compare_notify_port", string),
+ ("colo_sock_redirector0_id", string),
+ ("colo_sock_redirector0_ip", string),
+ ("colo_sock_redirector0_port", string),
+ ("colo_sock_redirector1_id", string),
+ ("colo_sock_redirector1_ip", string),
+ ("colo_sock_redirector1_port", string),
+ ("colo_sock_redirector2_id", string),
+ ("colo_sock_redirector2_ip", string),
+ ("colo_sock_redirector2_port", string),
+ ("colo_filter_mirror_queue", string),
+ ("colo_filter_mirror_outdev", string),
+ ("colo_filter_redirector0_queue", string),
+ ("colo_filter_redirector0_indev", string),
+ ("colo_filter_redirector0_outdev", string),
+ ("colo_filter_redirector1_queue", string),
+ ("colo_filter_redirector1_indev", string),
+ ("colo_filter_redirector1_outdev", string),
+ ("colo_compare_pri_in", string),
+ ("colo_compare_sec_in", string),
+ ("colo_compare_out", string),
+ ("colo_compare_notify_dev", string),
+ ("colo_sock_sec_redirector0_id", string),
+ ("colo_sock_sec_redirector0_ip", string),
+ ("colo_sock_sec_redirector0_port", string),
+ ("colo_sock_sec_redirector1_id", string),
+ ("colo_sock_sec_redirector1_ip", string),
+ ("colo_sock_sec_redirector1_port", string),
+ ("colo_filter_sec_redirector0_queue", string),
+ ("colo_filter_sec_redirector0_indev", string),
+ ("colo_filter_sec_redirector0_outdev", string),
+ ("colo_filter_sec_redirector1_queue", string),
+ ("colo_filter_sec_redirector1_indev", string),
+ ("colo_filter_sec_redirector1_outdev", string),
+ ("colo_filter_sec_rewriter0_queue", string),
+ ("colo_checkpoint_host", string),
+ ("colo_checkpoint_port", string)
+ ])
+
+libxl_device_pci = Struct("device_pci", [
+ ("func", uint8),
+ ("dev", uint8),
+ ("bus", uint8),
+ ("domain", integer),
+ ("vdevfn", uint32),
+ ("vfunc_mask", uint32),
+ ("msitranslate", bool),
+ ("power_mgmt", bool),
+ ("permissive", bool),
+ ("seize", bool),
+ ("rdm_policy", libxl_rdm_reserve_policy),
+ ])
+
+libxl_device_rdm = Struct("device_rdm", [
+ ("start", uint64),
+ ("size", uint64),
+ ("policy", libxl_rdm_reserve_policy),
+ ])
+
+libxl_usbctrl_type = Enumeration("usbctrl_type", [
+ (0, "AUTO"),
+ (1, "PV"),
+ (2, "DEVICEMODEL"),
+ (3, "QUSB"),
+ ])
+
+libxl_usbdev_type = Enumeration("usbdev_type", [
+ (1, "hostdev"),
+ ])
+
+libxl_device_usbctrl = Struct("device_usbctrl", [
+ ("type", libxl_usbctrl_type),
+ ("devid", libxl_devid),
+ ("version", integer),
+ ("ports", integer),
+ ("backend_domid", libxl_domid),
+ ("backend_domname", string),
+ ])
+
+libxl_device_usbdev = Struct("device_usbdev", [
+ ("ctrl", libxl_devid),
+ ("port", integer),
+ ("u", KeyedUnion(None, libxl_usbdev_type, "type",
+ [("hostdev", Struct(None, [
+ ("hostbus", uint8),
+ ("hostaddr", uint8)])),
+ ])),
+ ])
+
+libxl_device_dtdev = Struct("device_dtdev", [
+ ("path", string),
+ ])
+
+libxl_device_vtpm = Struct("device_vtpm", [
+ ("backend_domid", libxl_domid),
+ ("backend_domname", string),
+ ("devid", libxl_devid),
+ ("uuid", libxl_uuid),
+])
+
+libxl_device_p9 = Struct("device_p9", [
+ ("backend_domid", libxl_domid),
+ ("backend_domname", string),
+ ("tag", string),
+ ("path", string),
+ ("security_model", string),
+ ("devid", libxl_devid),
+])
+
+libxl_device_pvcallsif = Struct("device_pvcallsif", [
+ ("backend_domid", libxl_domid),
+ ("backend_domname", string),
+ ("devid", libxl_devid),
+])
+
+libxl_device_channel = Struct("device_channel", [
+ ("backend_domid", libxl_domid),
+ ("backend_domname", string),
+ ("devid", libxl_devid),
+ ("name", string),
+ ("u", KeyedUnion(None, libxl_channel_connection, "connection",
+ [("unknown", None),
+ ("pty", None),
+ ("socket", Struct(None, [("path", string)])),
+ ])),
+])
+
+libxl_connector_param = Struct("connector_param", [
+ ("unique_id", string),
+ ("width", uint32),
+ ("height", uint32)
+ ])
+
+libxl_device_vdispl = Struct("device_vdispl", [
+ ("backend_domid", libxl_domid),
+ ("backend_domname", string),
+ ("devid", libxl_devid),
+ ("be_alloc", bool),
+ ("connectors", Array(libxl_connector_param, "num_connectors"))
+ ])
+
+libxl_vsnd_pcm_format = Enumeration("vsnd_pcm_format", [
+ (1, "S8"),
+ (2, "U8"),
+ (3, "S16_LE"),
+ (4, "S16_BE"),
+ (5, "U16_LE"),
+ (6, "U16_BE"),
+ (7, "S24_LE"),
+ (8, "S24_BE"),
+ (9, "U24_LE"),
+ (10, "U24_BE"),
+ (11, "S32_LE"),
+ (12, "S32_BE"),
+ (13, "U32_LE"),
+ (14, "U32_BE"),
+ (15, "F32_LE"),
+ (16, "F32_BE"),
+ (17, "F64_LE"),
+ (18, "F64_BE"),
+ (19, "IEC958_SUBFRAME_LE"),
+ (20, "IEC958_SUBFRAME_BE"),
+ (21, "MU_LAW"),
+ (22, "A_LAW"),
+ (23, "IMA_ADPCM"),
+ (24, "MPEG"),
+ (25, "GSM")
+ ])
+
+libxl_vsnd_params = Struct("vsnd_params", [
+ ("sample_rates", Array(uint32, "num_sample_rates")),
+ ("sample_formats", Array(libxl_vsnd_pcm_format, "num_sample_formats")),
+ ("channels_min", uint32),
+ ("channels_max", uint32),
+ ("buffer_size", uint32)
+ ])
+
+libxl_vsnd_stream_type = Enumeration("vsnd_stream_type", [
+ (1, "P"),
+ (2, "C")
+ ])
+
+libxl_vsnd_stream = Struct("vsnd_stream", [
+ ("unique_id", string),
+ ("type", libxl_vsnd_stream_type),
+ ("params", libxl_vsnd_params)
+ ])
+
+libxl_vsnd_pcm = Struct("vsnd_pcm", [
+ ("name", string),
+ ("params", libxl_vsnd_params),
+ ("streams", Array(libxl_vsnd_stream, "num_vsnd_streams"))
+ ])
+
+libxl_device_vsnd = Struct("device_vsnd", [
+ ("backend_domid", libxl_domid),
+ ("backend_domname", string),
+ ("devid", libxl_devid),
+ ("short_name", string),
+ ("long_name", string),
+ ("params", libxl_vsnd_params),
+ ("pcms", Array(libxl_vsnd_pcm, "num_vsnd_pcms"))
+ ])
+
+libxl_domain_config = Struct("domain_config", [
+ ("c_info", libxl_domain_create_info),
+ ("b_info", libxl_domain_build_info),
+
+ ("disks", Array(libxl_device_disk, "num_disks")),
+ ("nics", Array(libxl_device_nic, "num_nics")),
+ ("pcidevs", Array(libxl_device_pci, "num_pcidevs")),
+ ("rdms", Array(libxl_device_rdm, "num_rdms")),
+ ("dtdevs", Array(libxl_device_dtdev, "num_dtdevs")),
+ ("vfbs", Array(libxl_device_vfb, "num_vfbs")),
+ ("vkbs", Array(libxl_device_vkb, "num_vkbs")),
+ ("vtpms", Array(libxl_device_vtpm, "num_vtpms")),
+ ("p9s", Array(libxl_device_p9, "num_p9s")),
+ ("pvcallsifs", Array(libxl_device_pvcallsif, "num_pvcallsifs")),
+ ("vdispls", Array(libxl_device_vdispl, "num_vdispls")),
+ ("vsnds", Array(libxl_device_vsnd, "num_vsnds")),
+ # a channel manifests as a console with a name,
+ # see docs/misc/channels.txt
+ ("channels", Array(libxl_device_channel, "num_channels")),
+ ("usbctrls", Array(libxl_device_usbctrl, "num_usbctrls")),
+ ("usbdevs", Array(libxl_device_usbdev, "num_usbdevs")),
+
+ ("on_poweroff", libxl_action_on_shutdown),
+ ("on_reboot", libxl_action_on_shutdown),
+ ("on_watchdog", libxl_action_on_shutdown),
+ ("on_crash", libxl_action_on_shutdown),
+ ("on_soft_reset", libxl_action_on_shutdown),
+ ], dir=DIR_IN)
+
+libxl_diskinfo = Struct("diskinfo", [
+ ("backend", string),
+ ("backend_id", uint32),
+ ("frontend", string),
+ ("frontend_id", uint32),
+ ("devid", libxl_devid),
+ ("state", integer),
+ ("evtch", integer),
+ ("rref", integer),
+ ], dir=DIR_OUT)
+
+libxl_nicinfo = Struct("nicinfo", [
+ ("backend", string),
+ ("backend_id", uint32),
+ ("frontend", string),
+ ("frontend_id", uint32),
+ ("devid", libxl_devid),
+ ("state", integer),
+ ("evtch", integer),
+ ("rref_tx", integer),
+ ("rref_rx", integer),
+ ], dir=DIR_OUT)
+
+libxl_vtpminfo = Struct("vtpminfo", [
+ ("backend", string),
+ ("backend_id", uint32),
+ ("frontend", string),
+ ("frontend_id", uint32),
+ ("devid", libxl_devid),
+ ("state", integer),
+ ("evtch", integer),
+ ("rref", integer),
+ ("uuid", libxl_uuid),
+ ], dir=DIR_OUT)
+
+libxl_usbctrlinfo = Struct("usbctrlinfo", [
+ ("type", libxl_usbctrl_type),
+ ("devid", libxl_devid),
+ ("version", integer),
+ ("ports", integer),
+ ("backend", string),
+ ("backend_id", uint32),
+ ("frontend", string),
+ ("frontend_id", uint32),
+ ("state", integer),
+ ("evtch", integer),
+ ("ref_urb", integer),
+ ("ref_conn", integer),
+ ], dir=DIR_OUT)
+
+libxl_vcpuinfo = Struct("vcpuinfo", [
+ ("vcpuid", uint32),
+ ("cpu", uint32),
+ ("online", bool),
+ ("blocked", bool),
+ ("running", bool),
+ ("vcpu_time", uint64), # total vcpu time ran (ns)
+ ("cpumap", libxl_bitmap), # current hard cpu affinity
+ ("cpumap_soft", libxl_bitmap), # current soft cpu affinity
+ ], dir=DIR_OUT)
+
+libxl_physinfo = Struct("physinfo", [
+ ("threads_per_core", uint32),
+ ("cores_per_socket", uint32),
+
+ ("max_cpu_id", uint32),
+ ("nr_cpus", uint32),
+ ("cpu_khz", uint32),
+
+ ("total_pages", uint64),
+ ("free_pages", uint64),
+ ("scrub_pages", uint64),
+ ("outstanding_pages", uint64),
+ ("sharing_freed_pages", uint64),
+ ("sharing_used_frames", uint64),
+ ("max_possible_mfn", uint64),
+
+ ("nr_nodes", uint32),
+ ("hw_cap", libxl_hwcap),
+
+ ("cap_hvm", bool),
+ ("cap_pv", bool),
+ ("cap_hvm_directio", bool), # No longer HVM specific
+ ("cap_hap", bool),
+ ("cap_shadow", bool),
+ ("cap_iommu_hap_pt_share", bool),
+ ], dir=DIR_OUT)
+
+libxl_connectorinfo = Struct("connectorinfo", [
+ ("unique_id", string),
+ ("width", uint32),
+ ("height", uint32),
+ ("req_evtch", integer),
+ ("req_rref", integer),
+ ("evt_evtch", integer),
+ ("evt_rref", integer),
+ ], dir=DIR_OUT)
+
+libxl_vdisplinfo = Struct("vdisplinfo", [
+ ("backend", string),
+ ("backend_id", uint32),
+ ("frontend", string),
+ ("frontend_id", uint32),
+ ("devid", libxl_devid),
+ ("state", integer),
+ ("be_alloc", bool),
+ ("connectors", Array(libxl_connectorinfo, "num_connectors"))
+ ], dir=DIR_OUT)
+
+libxl_streaminfo = Struct("streaminfo", [
+ ("req_evtch", integer),
+ ("req_rref", integer)
+ ])
+
+libxl_pcminfo = Struct("pcminfo", [
+ ("streams", Array(libxl_streaminfo, "num_vsnd_streams"))
+ ])
+
+libxl_vsndinfo = Struct("vsndinfo", [
+ ("backend", string),
+ ("backend_id", uint32),
+ ("frontend", string),
+ ("frontend_id", uint32),
+ ("devid", libxl_devid),
+ ("state", integer),
+ ("pcms", Array(libxl_pcminfo, "num_vsnd_pcms"))
+ ])
+
+libxl_vkbinfo = Struct("vkbinfo", [
+ ("backend", string),
+ ("backend_id", uint32),
+ ("frontend", string),
+ ("frontend_id", uint32),
+ ("devid", libxl_devid),
+ ("state", integer),
+ ("evtch", integer),
+ ("rref", integer)
+ ], dir=DIR_OUT)
+
+# NUMA node characteristics: size and free are how much memory it has, and how
+# much of it is free, respectively. dists is an array of distances from this
+# node to each other node.
+libxl_numainfo = Struct("numainfo", [
+ ("size", uint64),
+ ("free", uint64),
+ ("dists", Array(uint32, "num_dists")),
+ ], dir=DIR_OUT)
+
+libxl_cputopology = Struct("cputopology", [
+ ("core", uint32),
+ ("socket", uint32),
+ ("node", uint32),
+ ], dir=DIR_OUT)
+
+libxl_pcitopology = Struct("pcitopology", [
+ ("seg", uint16),
+ ("bus", uint8),
+ ("devfn", uint8),
+ ("node", uint32),
+ ], dir=DIR_OUT)
+
+libxl_sched_credit_params = Struct("sched_credit_params", [
+ ("tslice_ms", integer),
+ ("ratelimit_us", integer),
+ ("vcpu_migr_delay_us", integer),
+ ], dispose_fn=None)
+
+libxl_sched_credit2_params = Struct("sched_credit2_params", [
+ ("ratelimit_us", integer),
+ ], dispose_fn=None)
+
+libxl_domain_remus_info = Struct("domain_remus_info",[
+ ("interval", integer),
+ ("allow_unsafe", libxl_defbool),
+ ("blackhole", libxl_defbool),
+ ("compression", libxl_defbool),
+ ("netbuf", libxl_defbool),
+ ("netbufscript", string),
+ ("diskbuf", libxl_defbool),
+ ("colo", libxl_defbool),
+ ("userspace_colo_proxy", libxl_defbool)
+ ])
+
+libxl_event_type = Enumeration("event_type", [
+ (1, "DOMAIN_SHUTDOWN"),
+ (2, "DOMAIN_DEATH"),
+ (3, "DISK_EJECT"),
+ (4, "OPERATION_COMPLETE"),
+ (5, "DOMAIN_CREATE_CONSOLE_AVAILABLE"),
+ ])
+
+libxl_ev_user = UInt(64)
+
+libxl_ev_link = Builtin("ev_link", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, private=True)
+
+libxl_event = Struct("event",[
+ ("link", libxl_ev_link),
+ # for use by libxl; caller may use this once the event has been
+ # returned by libxl_event_{check,wait}
+ ("domid", libxl_domid),
+ ("domuuid", libxl_uuid),
+ ("for_user", libxl_ev_user),
+ ("u", KeyedUnion(None, libxl_event_type, "type",
+ [("domain_shutdown", Struct(None, [
+ ("shutdown_reason", uint8),
+ ])),
+ ("domain_death", None),
+ ("disk_eject", Struct(None, [
+ ("vdev", string),
+ ("disk", libxl_device_disk),
+ ])),
+ ("operation_complete", Struct(None, [
+ ("rc", integer),
+ ])),
+ ("domain_create_console_available", None),
+ ]))])
+
+libxl_psr_cmt_type = Enumeration("psr_cmt_type", [
+ (1, "CACHE_OCCUPANCY"),
+ (2, "TOTAL_MEM_COUNT"),
+ (3, "LOCAL_MEM_COUNT"),
+ ])
+
+libxl_psr_cbm_type = Enumeration("psr_cbm_type", [
+ (0, "UNKNOWN"),
+ (1, "L3_CBM"),
+ (2, "L3_CBM_CODE"),
+ (3, "L3_CBM_DATA"),
+ (4, "L2_CBM"),
+ (5, "MBA_THRTL"),
+ ])
+
+libxl_psr_cat_info = Struct("psr_cat_info", [
+ ("id", uint32),
+ ("cos_max", uint32),
+ ("cbm_len", uint32),
+ ("cdp_enabled", bool),
+ ])
+
+libxl_psr_feat_type = Enumeration("psr_feat_type", [
+ (1, "CAT"),
+ (2, "MBA"),
+ ])
+
+libxl_psr_hw_info = Struct("psr_hw_info", [
+ ("id", uint32),
+ ("u", KeyedUnion(None, libxl_psr_feat_type, "type",
+ [("cat", Struct(None, [
+ ("cos_max", uint32),
+ ("cbm_len", uint32),
+ ("cdp_enabled", bool),
+ ])),
+ ("mba", Struct(None, [
+ ("cos_max", uint32),
+ ("thrtl_max", uint32),
+ ("linear", bool),
+ ])),
+ ]))
+ ], dir=DIR_OUT)
--- /dev/null
+namespace("libxl__")
+hidden(True)
+
+libxl_domid = Builtin("domid", namespace="libxl_", json_gen_fn = "yajl_gen_integer",
+ json_parse_fn = "libxl__uint32_parse_json", json_parse_type = "JSON_INTEGER",
+ autogenerate_json = False, copy_fn = None)
+
+libxl__qmp_message_type = Enumeration("qmp_message_type", [
+ (1, "QMP"),
+ (2, "return"),
+ (3, "error"),
+ (4, "event"),
+ (5, "invalid"),
+ ])
+
+# Consider adding to QEMU_BACKEND in libxl_internal.h
+libxl__device_kind = Enumeration("device_kind", [
+ (0, "NONE"),
+ (1, "VIF"),
+ (2, "VBD"),
+ (3, "QDISK"),
+ (4, "PCI"),
+ (5, "VFB"),
+ (6, "VKBD"),
+ (7, "CONSOLE"),
+ (8, "VTPM"),
+ (9, "VUSB"),
+ (10, "QUSB"),
+ (11, "9PFS"),
+ (12, "VDISPL"),
+ (13, "VUART"),
+ (14, "PVCALLS"),
+ (15, "VSND"),
+ (16, "VINPUT"),
+ ])
+
+libxl__console_backend = Enumeration("console_backend", [
+ (1, "XENCONSOLED"),
+ (2, "IOEMU"),
+ ])
+
+libxl__device_console = Struct("device_console", [
+ ("backend_domid", libxl_domid),
+ ("devid", integer),
+ ("consback", libxl__console_backend),
+ ("output", string),
+ # A regular console has no name.
+ # A console with a name is called a 'channel', see docs/misc/channels.txt
+ ("name", string),
+ ("connection", string),
+ ("path", string),
+ ])
+
+libxl__device_action = Enumeration("device_action", [
+ (1, "ADD"),
+ (2, "REMOVE"),
+ ])
--- /dev/null
+/*
+ * Copyright (C) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ * Author Chunyan Liu <cyliu@suse.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+#include <inttypes.h>
+#include <xen/io/usbif.h>
+
+#define USBBACK_INFO_PATH "/libxl/usbback"
+
+#define USBHUB_CLASS_CODE 9
+
+static int usbback_is_loaded(libxl__gc *gc)
+{
+ int r;
+ struct stat st;
+
+ r = lstat(SYSFS_USBBACK_DRIVER, &st);
+
+ if (r == 0)
+ return 1;
+ if (r < 0 && errno == ENOENT)
+ return 0;
+ LOGE(ERROR, "Accessing %s", SYSFS_USBBACK_DRIVER);
+ return ERROR_FAIL;
+}
+
+static int libxl__device_usbctrl_setdefault(libxl__gc *gc, uint32_t domid,
+ libxl_device_usbctrl *usbctrl,
+ bool hotplug)
+{
+ int rc;
+ libxl_domain_type domtype = libxl__domain_type(gc, domid);
+
+ if (usbctrl->type == LIBXL_USBCTRL_TYPE_AUTO) {
+ if (domtype != LIBXL_DOMAIN_TYPE_HVM) {
+ rc = usbback_is_loaded(gc);
+ if (rc < 0)
+ goto out;
+ usbctrl->type = rc ? LIBXL_USBCTRL_TYPE_PV
+ : LIBXL_USBCTRL_TYPE_QUSB;
+ } else {
+ /* FIXME: See if we can detect PV frontend */
+ usbctrl->type = LIBXL_USBCTRL_TYPE_DEVICEMODEL;
+ }
+ }
+
+ switch (usbctrl->type) {
+ case LIBXL_USBCTRL_TYPE_PV:
+ case LIBXL_USBCTRL_TYPE_QUSB:
+ if (!usbctrl->version)
+ usbctrl->version = 2;
+ if (usbctrl->version < 1 || usbctrl->version > 2) {
+ LOG(ERROR,
+ "USB version for paravirtualized devices must be 1 or 2");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ if (!usbctrl->ports)
+ usbctrl->ports = 8;
+ if (usbctrl->ports < 1 || usbctrl->ports > USBIF_MAX_PORTNR) {
+ LOG(ERROR, "Number of ports for USB controller is limited to %u",
+ USBIF_MAX_PORTNR);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ break;
+ case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
+ if (!usbctrl->version)
+ usbctrl->version = 2;
+ switch (usbctrl->version) {
+ case 1:
+ /* uhci controller in qemu has fixed number of ports. */
+ if (usbctrl->ports && usbctrl->ports != 2) {
+ LOG(ERROR,
+ "Number of ports for USB controller of version 1 is always 2");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ usbctrl->ports = 2;
+ break;
+ case 2:
+ /* ehci controller in qemu has fixed number of ports. */
+ if (usbctrl->ports && usbctrl->ports != 6) {
+ LOG(ERROR,
+ "Number of ports for USB controller of version 2 is always 6");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ usbctrl->ports = 6;
+ break;
+ case 3:
+ if (!usbctrl->ports)
+ usbctrl->ports = 8;
+ /* xhci controller in qemu supports up to 15 ports. */
+ if (usbctrl->ports > 15) {
+ LOG(ERROR,
+ "Number of ports for USB controller of version 3 is limited to 15");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ break;
+ default:
+ LOG(ERROR, "Illegal USB version");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ break;
+ default:
+ break;
+ }
+
+ rc = libxl__resolve_domid(gc, usbctrl->backend_domname,
+ &usbctrl->backend_domid);
+
+out:
+ return rc;
+}
+
+static int libxl__device_from_usbctrl(libxl__gc *gc, uint32_t domid,
+ libxl_device_usbctrl *usbctrl,
+ libxl__device *device)
+{
+ device->backend_devid = usbctrl->devid;
+ device->backend_domid = usbctrl->backend_domid;
+ switch (usbctrl->type) {
+ case LIBXL_USBCTRL_TYPE_PV:
+ device->backend_kind = LIBXL__DEVICE_KIND_VUSB;
+ break;
+ case LIBXL_USBCTRL_TYPE_QUSB:
+ device->backend_kind = LIBXL__DEVICE_KIND_QUSB;
+ break;
+ case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
+ device->backend_kind = LIBXL__DEVICE_KIND_NONE;
+ break;
+ default:
+ abort(); /* can't really happen. */
+ }
+ device->devid = usbctrl->devid;
+ device->domid = domid;
+ device->kind = LIBXL__DEVICE_KIND_VUSB;
+
+ return 0;
+}
+
+static const char *vusb_be_from_xs_libxl_type(libxl__gc *gc,
+ const char *libxl_path,
+ libxl_usbctrl_type type)
+{
+ const char *be_path = NULL, *tmp;
+ int r;
+
+ if (type == LIBXL_USBCTRL_TYPE_AUTO) {
+ r = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/type", libxl_path), &tmp);
+ if (r || libxl_usbctrl_type_from_string(tmp, &type))
+ goto out;
+ }
+
+ if (type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
+ be_path = libxl_path;
+ goto out;
+ }
+
+ r = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path),
+ &be_path);
+ if (r)
+ be_path = NULL;
+
+out:
+ return be_path;
+}
+
+/* Add usbctrl information to xenstore.
+ *
+ * Adding a usb controller will add a new 'qusb' or 'vusb' device in xenstore,
+ * and add corresponding frontend, backend information to it. According to
+ * "update_json", decide whether to update json config file.
+ */
+static int libxl__device_usbctrl_add_xenstore(libxl__gc *gc, uint32_t domid,
+ libxl_device_usbctrl *usbctrl,
+ bool update_json)
+{
+ libxl__device *device;
+ flexarray_t *front = NULL;
+ flexarray_t *back;
+ xs_transaction_t t = XBT_NULL;
+ int i, rc;
+ libxl_domain_config d_config;
+ libxl_device_usbctrl usbctrl_saved;
+ libxl__flock *lock = NULL;
+
+ libxl_domain_config_init(&d_config);
+ libxl_device_usbctrl_init(&usbctrl_saved);
+ libxl_device_usbctrl_copy(CTX, &usbctrl_saved, usbctrl);
+
+ GCNEW(device);
+ rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device);
+ if (rc) goto out;
+
+ back = flexarray_make(gc, 12, 1);
+
+ if (device->backend_kind != LIBXL__DEVICE_KIND_NONE) {
+ front = flexarray_make(gc, 4, 1);
+
+ flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
+ flexarray_append_pair(back, "online", "1");
+ flexarray_append_pair(back, "state",
+ GCSPRINTF("%d", XenbusStateInitialising));
+ flexarray_append_pair(front, "backend-id",
+ GCSPRINTF("%d", usbctrl->backend_domid));
+ flexarray_append_pair(front, "state",
+ GCSPRINTF("%d", XenbusStateInitialising));
+ }
+
+ flexarray_append_pair(back, "type",
+ (char *)libxl_usbctrl_type_to_string(usbctrl->type));
+ flexarray_append_pair(back, "usb-ver", GCSPRINTF("%d", usbctrl->version));
+ flexarray_append_pair(back, "num-ports", GCSPRINTF("%d", usbctrl->ports));
+ flexarray_append_pair(back, "port", "");
+ for (i = 0; i < usbctrl->ports; i++)
+ flexarray_append_pair(back, GCSPRINTF("port/%d", i + 1), "");
+
+ if (update_json) {
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
+ device_add_domain_config(gc, &d_config, &libxl__usbctrl_devtype,
+ &usbctrl_saved);
+
+ rc = libxl__dm_check_start(gc, &d_config, domid);
+ if (rc) goto out;
+
+ if (usbctrl->type == LIBXL_USBCTRL_TYPE_QUSB) {
+ if (!libxl__query_qemu_backend(gc, domid, usbctrl->backend_domid,
+ "qusb", false)) {
+ LOGD(ERROR, domid, "backend type not supported by device model");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+ }
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+
+ rc = libxl__device_exists(gc, t, device);
+ if (rc < 0) goto out;
+ if (rc == 1) {
+ /* already exists in xenstore */
+ LOGD(ERROR, domid, "device already exists in xenstore");
+ rc = ERROR_DEVICE_EXISTS;
+ goto out;
+ }
+
+ if (update_json) {
+ rc = libxl__set_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+ }
+
+ libxl__device_generic_add(gc, t, device,
+ libxl__xs_kvs_of_flexarray(gc, back),
+ libxl__xs_kvs_of_flexarray(gc, front),
+ NULL);
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc < 0) goto out;
+ }
+
+out:
+ libxl__xs_transaction_abort(gc, &t);
+ if (lock) libxl__unlock_file(lock);
+ libxl_device_usbctrl_dispose(&usbctrl_saved);
+ libxl_domain_config_dispose(&d_config);
+ return rc;
+}
+
+static const char *vusb_be_from_xs_libxl(libxl__gc *gc, const char *libxl_path)
+{
+ return vusb_be_from_xs_libxl_type(gc, libxl_path, LIBXL_USBCTRL_TYPE_AUTO);
+}
+
+static void libxl__device_usbctrl_del_xenstore(libxl__gc *gc, uint32_t domid,
+ libxl_device_usbctrl *usbctrl)
+{
+ const char *libxl_path, *be_path;
+ xs_transaction_t t = XBT_NULL;
+ int rc;
+
+ libxl_path = libxl__domain_device_libxl_path(gc, domid, usbctrl->devid,
+ LIBXL__DEVICE_KIND_VUSB);
+ be_path = vusb_be_from_xs_libxl_type(gc, libxl_path, usbctrl->type);
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+
+ libxl__xs_path_cleanup(gc, t, be_path);
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc < 0) goto out;
+ }
+
+ return;
+
+out:
+ libxl__xs_transaction_abort(gc, &t);
+}
+
+static char *pvusb_get_device_type(libxl_usbctrl_type type)
+{
+ switch (type) {
+ case LIBXL_USBCTRL_TYPE_PV:
+ return "vusb";
+ case LIBXL_USBCTRL_TYPE_QUSB:
+ return "qusb";
+ default:
+ return NULL;
+ }
+}
+
+/* Send qmp commands to create a usb controller in qemu.
+ *
+ * Depending on the speed (usbctrl->version) we create:
+ * - piix3-usb-uhci (version=1), always 2 ports
+ * - usb-ehci (version=2), always 6 ports
+ * - nec-usb-xhci (version=3), up to 15 ports
+ */
+static int libxl__device_usbctrl_add_hvm(libxl__egc *egc, libxl__ev_qmp *qmp,
+ libxl_device_usbctrl *usbctrl)
+{
+ EGC_GC;
+ libxl__json_object *qmp_args = NULL;
+
+ switch (usbctrl->version) {
+ case 1:
+ libxl__qmp_param_add_string(gc, &qmp_args,
+ "driver", "piix3-usb-uhci");
+ break;
+ case 2:
+ libxl__qmp_param_add_string(gc, &qmp_args,
+ "driver", "usb-ehci");
+ break;
+ case 3:
+ libxl__qmp_param_add_string(gc, &qmp_args,
+ "driver", "nec-usb-xhci");
+ libxl__qmp_param_add_string(gc, &qmp_args, "p2",
+ GCSPRINTF("%d", usbctrl->ports));
+ libxl__qmp_param_add_string(gc, &qmp_args, "p3",
+ GCSPRINTF("%d", usbctrl->ports));
+ break;
+ default:
+ abort(); /* Should not be possible. */
+ }
+
+ libxl__qmp_param_add_string(gc, &qmp_args, "id",
+ GCSPRINTF("xenusb-%d", usbctrl->devid));
+
+ return libxl__ev_qmp_send(egc, qmp, "device_add", qmp_args);
+}
+
+/* Send qmp commands to delete a usb controller in qemu. */
+static int libxl__device_usbctrl_del_hvm(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ int devid)
+{
+ EGC_GC;
+ libxl__json_object *qmp_args = NULL;
+
+ libxl__qmp_param_add_string(gc, &qmp_args,
+ "id", GCSPRINTF("xenusb-%d", devid));
+
+ return libxl__ev_qmp_send(egc, qmp, "device_del", qmp_args);
+}
+
+/* Send qmp commands to create a usb device in qemu. */
+static int libxl__device_usbdev_add_hvm(libxl__egc *egc, libxl__ev_qmp *qmp,
+ libxl_device_usbdev *usbdev)
+{
+ EGC_GC;
+ libxl__json_object *qmp_args = NULL;
+
+ libxl__qmp_param_add_string(gc, &qmp_args, "id",
+ GCSPRINTF("xenusb-%d-%d", usbdev->u.hostdev.hostbus,
+ usbdev->u.hostdev.hostaddr));
+ libxl__qmp_param_add_string(gc, &qmp_args, "driver", "usb-host");
+ libxl__qmp_param_add_string(gc, &qmp_args, "bus",
+ GCSPRINTF("xenusb-%d.0", usbdev->ctrl));
+ libxl__qmp_param_add_string(gc, &qmp_args, "port",
+ GCSPRINTF("%d", usbdev->port));
+ libxl__qmp_param_add_string(gc, &qmp_args, "hostbus",
+ GCSPRINTF("%d", usbdev->u.hostdev.hostbus));
+ libxl__qmp_param_add_string(gc, &qmp_args, "hostaddr",
+ GCSPRINTF("%d", usbdev->u.hostdev.hostaddr));
+
+ return libxl__ev_qmp_send(egc, qmp, "device_add", qmp_args);
+}
+
+/* Send qmp commands to delete a usb device in qemu. */
+static int libxl__device_usbdev_del_hvm(libxl__egc *egc, libxl__ev_qmp *qmp,
+ libxl_device_usbdev *usbdev)
+{
+ EGC_GC;
+ libxl__json_object *qmp_args = NULL;
+
+ libxl__qmp_param_add_string(gc, &qmp_args, "id",
+ GCSPRINTF("xenusb-%d-%d", usbdev->u.hostdev.hostbus,
+ usbdev->u.hostdev.hostaddr));
+
+ return libxl__ev_qmp_send(egc, qmp, "device_del", qmp_args);
+}
+
+static LIBXL_DEFINE_UPDATE_DEVID(usbctrl)
+
+static void device_usbctrl_add_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void device_usbctrl_add_qmp_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *r, int rc);
+static void device_usbctrl_add_done(libxl__egc *egc,
+ libxl__ao_device *aodev, int rc);
+
+/* AO operation to add a usb controller.
+ *
+ * Generally, it does:
+ * 1) fill in necessary usb controler information with default value
+ * 2) write usb controller frontend/backend info to xenstore, update json
+ * config file if necessary.
+ * 3) wait for device connection. PVUSB frontend and backend driver will
+ * probe xenstore paths and build connection between frontend and backend.
+ *
+ * Before calling this function, aodev should be properly filled:
+ * aodev->ao, aodev->callback, aodev->update_json, ...
+ */
+static void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid,
+ libxl_device_usbctrl *usbctrl,
+ libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ libxl__device *device;
+ int rc;
+
+ /* Store *usbctrl to be used by callbacks */
+ aodev->device_config = usbctrl;
+ aodev->device_type = &libxl__usbctrl_devtype;
+
+ rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl,
+ aodev->update_json);
+ if (rc < 0) goto out;
+
+ rc = libxl__device_usbctrl_update_devid(gc, domid, usbctrl);
+ if (rc) goto out;
+
+ rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl,
+ aodev->update_json);
+ if (rc) goto out;
+
+ GCNEW(device);
+ rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device);
+ if (rc) goto outrm;
+ aodev->dev = device;
+
+ if (device->backend_kind == LIBXL__DEVICE_KIND_NONE) {
+ libxl__ev_qmp *const qmp = &aodev->qmp;
+
+ rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
+ device_usbctrl_add_timeout,
+ LIBXL_QMP_CMD_TIMEOUT * 1000);
+ if (rc) goto outrm;
+
+ qmp->ao = ao;
+ qmp->domid = domid;
+ qmp->payload_fd = -1;
+ qmp->callback = device_usbctrl_add_qmp_cb;
+ rc = libxl__device_usbctrl_add_hvm(egc, qmp, usbctrl);
+ if (rc) goto outrm;
+ return;
+ }
+
+ aodev->action = LIBXL__DEVICE_ACTION_ADD;
+ libxl__wait_device_connection(egc, aodev);
+ return;
+
+outrm:
+ libxl__device_usbctrl_del_xenstore(gc, domid, usbctrl);
+out:
+ device_usbctrl_add_done(egc, aodev, rc);
+}
+
+static void device_usbctrl_add_timeout(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ EGC_GC;
+ libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
+
+ if (rc == ERROR_TIMEDOUT)
+ LOGD(ERROR, aodev->dev->domid, "Adding usbctrl to QEMU timed out");
+ device_usbctrl_add_qmp_cb(egc, &aodev->qmp, NULL, rc);
+}
+
+static void device_usbctrl_add_qmp_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *r,
+ int rc)
+{
+ EGC_GC;
+ libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
+ libxl_device_usbctrl *const usbctrl = aodev->device_config;
+
+ if (rc)
+ libxl__device_usbctrl_del_xenstore(gc, aodev->dev->domid, usbctrl);
+
+ device_usbctrl_add_done(egc, aodev, rc);
+}
+
+static void device_usbctrl_add_done(libxl__egc *egc,
+ libxl__ao_device *aodev,
+ int rc)
+{
+ EGC_GC;
+ libxl__ev_qmp_dispose(gc, &aodev->qmp);
+ libxl__ev_time_deregister(gc, &aodev->timeout);
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+LIBXL_DEFINE_DEVICE_ADD(usbctrl)
+static LIBXL_DEFINE_DEVICES_ADD(usbctrl)
+LIBXL_DEFINE_DEVICE_REMOVE_CUSTOM(usbctrl)
+
+static int libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc, uint32_t domid,
+ libxl_devid usbctrl,
+ libxl_device_usbdev **usbdevs,
+ int *num);
+
+static void libxl__device_usbdev_remove(libxl__egc *egc,
+ uint32_t domid, libxl_device_usbdev *usbdev, libxl__ao_device *aodev);
+
+static void device_usbctrl_usbdevs_removed(libxl__egc *,
+ libxl__multidev *, int rc);
+static void device_usbctrl_remove_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void device_usbctrl_remove_qmp_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *resp, int rc);
+static void device_usbctrl_remove_done(libxl__egc *egc,
+ libxl__ao_device *, int rc);
+
+typedef struct {
+ libxl__multidev multidev;
+ libxl__ao_device *aodev;
+} usbctrl_remove_state;
+
+/* AO function to remove a usb controller.
+ *
+ * Generally, it does:
+ * 1) check if the usb controller exists or not
+ * 2) remove all usb devices under controller
+ * 3) remove usb controller information from xenstore
+ *
+ * Before calling this function, aodev should be properly filled:
+ * aodev->ao, aodev->dev, aodev->callback, ...
+ */
+void libxl__initiate_device_usbctrl_remove(libxl__egc *egc,
+ libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ libxl_device_usbdev *usbdevs = NULL;
+ int num_usbdev = 0;
+ int i, rc;
+ uint32_t domid = aodev->dev->domid;
+ int usbctrl_devid = aodev->dev->devid;
+ libxl_device_usbctrl *usbctrl;
+ usbctrl_remove_state *ucrs;
+
+ GCNEW(ucrs);
+ ucrs->aodev = aodev;
+ ucrs->multidev.callback = device_usbctrl_usbdevs_removed;
+ libxl__multidev_begin(ao, &ucrs->multidev);
+
+ GCNEW(usbctrl);
+ libxl_device_usbctrl_init(usbctrl);
+ rc = libxl_devid_to_device_usbctrl(CTX, domid, usbctrl_devid,
+ usbctrl);
+ if (rc) goto out;
+
+ /* Store *usbctrl to be used by callbacks */
+ aodev->device_config = usbctrl;
+ aodev->device_type = &libxl__usbctrl_devtype;
+
+ /* Remove usb devices first */
+ rc = libxl__device_usbdev_list_for_usbctrl(gc, domid, usbctrl_devid,
+ &usbdevs, &num_usbdev);
+ if (rc) goto out;
+
+ for (i = 0; i < num_usbdev; i++) {
+ libxl__ao_device *usbdev_aodev =
+ libxl__multidev_prepare(&ucrs->multidev);
+ usbdev_aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+ libxl__device_usbdev_remove(egc, domid, &usbdevs[i], usbdev_aodev);
+ }
+
+out:
+ libxl__multidev_prepared(egc, &ucrs->multidev, rc); /* must be last */
+}
+
+static void device_usbctrl_usbdevs_removed(libxl__egc *egc,
+ libxl__multidev *multidev,
+ int rc)
+{
+ usbctrl_remove_state *ucrs =
+ CONTAINER_OF(multidev, *ucrs, multidev);
+ libxl__ao_device *aodev = ucrs->aodev;
+ STATE_AO_GC(aodev->ao);
+ libxl_device_usbctrl *const usbctrl = aodev->device_config;
+
+ if (rc) goto out;
+
+ /* Remove usbctrl */
+ if (usbctrl->type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
+ libxl__ev_qmp *const qmp = &aodev->qmp;
+
+ rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
+ device_usbctrl_remove_timeout,
+ LIBXL_QMP_CMD_TIMEOUT * 1000);
+ if (rc) goto out;
+
+ qmp->ao = ao;
+ qmp->domid = aodev->dev->domid;
+ qmp->callback = device_usbctrl_remove_qmp_cb;
+ qmp->payload_fd = -1;
+ rc = libxl__device_usbctrl_del_hvm(egc, qmp, aodev->dev->devid);
+ if (rc) goto out;
+ return;
+ }
+
+ libxl_device_usbctrl_dispose(usbctrl);
+
+ /* Remove usbctrl */
+ libxl__initiate_device_generic_remove(egc, aodev); /* must be last */
+ return;
+out:
+ device_usbctrl_remove_done(egc, aodev, rc); /* must be last */
+}
+
+static void device_usbctrl_remove_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
+{
+ EGC_GC;
+ libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
+
+ if (rc == ERROR_TIMEDOUT)
+ LOGD(ERROR, aodev->dev->domid,
+ "Removing usbctrl from QEMU timed out");
+ device_usbctrl_remove_qmp_cb(egc, &aodev->qmp, NULL, rc);
+}
+
+static void device_usbctrl_remove_qmp_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *resp,
+ int rc)
+{
+ EGC_GC;
+ libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
+ libxl_device_usbctrl *const usbctrl = aodev->device_config;
+
+ if (!rc)
+ libxl__device_usbctrl_del_xenstore(gc, aodev->dev->domid, usbctrl);
+
+ device_usbctrl_remove_done(egc, aodev, rc);
+}
+
+static void device_usbctrl_remove_done(libxl__egc *egc,
+ libxl__ao_device *aodev,
+ int rc)
+{
+ EGC_GC;
+ libxl_device_usbctrl *const usbctrl = aodev->device_config;
+
+ libxl_device_usbctrl_dispose(usbctrl);
+ libxl__ev_qmp_dispose(gc, &aodev->qmp);
+ libxl__ev_time_deregister(gc, &aodev->timeout);
+
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+static int libxl__usbctrl_from_xenstore(libxl__gc *gc,
+ const char *libxl_path,
+ libxl_devid devid,
+ libxl_device_usbctrl *usbctrl_r)
+{
+ int rc;
+ const char *tmp;
+ const char *be_path;
+
+#define READ_SUBPATH(path, subpath) ({ \
+ rc = libxl__xs_read_checked(gc, XBT_NULL, \
+ GCSPRINTF("%s/" subpath, path), \
+ &tmp); \
+ if (rc) goto out; \
+ (char *)tmp; \
+ })
+
+#define READ_SUBPATH_INT(path, subpath) ({ \
+ rc = libxl__xs_read_checked(gc, XBT_NULL, \
+ GCSPRINTF("%s/" subpath, path), \
+ &tmp); \
+ if (rc) goto out; \
+ tmp ? atoi(tmp) : -1; \
+ })
+
+ usbctrl_r->devid = devid;
+ libxl_usbctrl_type_from_string(READ_SUBPATH(libxl_path, "type"),
+ &usbctrl_r->type);
+ if (usbctrl_r->type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
+ be_path = libxl_path;
+ rc = libxl__get_domid(gc, &usbctrl_r->backend_domid);
+ } else {
+ be_path = READ_SUBPATH(libxl_path, "backend");
+ if (!be_path) goto out;
+ rc = libxl__backendpath_parse_domid(gc, be_path,
+ &usbctrl_r->backend_domid);
+ }
+ if (rc) goto out;
+ usbctrl_r->version = READ_SUBPATH_INT(be_path, "usb-ver");
+ usbctrl_r->ports = READ_SUBPATH_INT(be_path, "num-ports");
+
+#undef READ_SUBPATH
+#undef READ_SUBPATH_INT
+out:
+ if (rc)
+ libxl_device_usbctrl_dispose(usbctrl_r);
+ return rc;
+}
+
+int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_usbctrl *usbctrl,
+ libxl_usbctrlinfo *usbctrlinfo)
+{
+ GC_INIT(ctx);
+ const char *fe_path, *be_path, *tmp;
+ const char *libxl_path;
+ int rc;
+
+ usbctrlinfo->devid = usbctrl->devid;
+
+#define READ_SUBPATH(path, subpath) ({ \
+ rc = libxl__xs_read_mandatory(gc, XBT_NULL, \
+ GCSPRINTF("%s/" subpath, path), \
+ &tmp); \
+ if (rc) goto out; \
+ (char *)tmp; \
+ })
+
+#define READ_SUBPATH_INT(path, subpath) ({ \
+ rc = libxl__xs_read_checked(gc, XBT_NULL, \
+ GCSPRINTF("%s/" subpath, path), \
+ &tmp); \
+ if (rc) goto out; \
+ tmp ? atoi(tmp) : -1; \
+ })
+
+ libxl_path = libxl__domain_device_libxl_path(gc, domid, usbctrl->devid,
+ LIBXL__DEVICE_KIND_VUSB);
+ libxl_usbctrl_type_from_string(READ_SUBPATH(libxl_path, "type"),
+ &usbctrlinfo->type);
+
+ if (usbctrlinfo->type != LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
+ fe_path = libxl__domain_device_frontend_path(gc, domid, usbctrl->devid,
+ LIBXL__DEVICE_KIND_VUSB);
+ be_path = READ_SUBPATH(libxl_path, "backend");
+ usbctrlinfo->backend = libxl__strdup(NOGC, be_path);
+ rc = libxl__backendpath_parse_domid(gc, be_path,
+ &usbctrlinfo->backend_id);
+ if (rc) goto out;
+ usbctrlinfo->state = READ_SUBPATH_INT(fe_path, "state");
+ usbctrlinfo->evtch = READ_SUBPATH_INT(fe_path, "event-channel");
+ usbctrlinfo->ref_urb = READ_SUBPATH_INT(fe_path, "urb-ring-ref");
+ usbctrlinfo->ref_conn = READ_SUBPATH_INT(fe_path, "urb-ring-ref");
+ usbctrlinfo->frontend = libxl__strdup(NOGC, fe_path);
+ usbctrlinfo->frontend_id = domid;
+ usbctrlinfo->ports = READ_SUBPATH_INT(be_path, "num-ports");
+ usbctrlinfo->version = READ_SUBPATH_INT(be_path, "usb-ver");
+ } else {
+ usbctrlinfo->ports = READ_SUBPATH_INT(libxl_path, "num-ports");
+ usbctrlinfo->version = READ_SUBPATH_INT(libxl_path, "usb-ver");
+ rc = libxl__get_domid(gc, &usbctrlinfo->backend_id);
+ if (rc) goto out;
+ }
+
+#undef READ_SUBPATH
+#undef READ_SUBPATH_INT
+
+ rc = 0;
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+
+static char *usbdev_busaddr_to_busid(libxl__gc *gc, int bus, int addr)
+{
+ DIR *dir;
+ char *busid = NULL;
+ struct dirent *de;
+
+ /* invalid hostbus or hostaddr */
+ if (bus < 1 || addr < 1)
+ return NULL;
+
+ dir = opendir(SYSFS_USB_DEV);
+ if (!dir) {
+ LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV);
+ return NULL;
+ }
+
+ for (;;) {
+ char *filename;
+ void *buf;
+ int busnum = -1;
+ int devnum = -1;
+
+ errno = 0;
+ de = readdir(dir);
+ if (!de && errno) {
+ LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV);
+ break;
+ }
+ if (!de)
+ break;
+
+ if (!strcmp(de->d_name, ".") ||
+ !strcmp(de->d_name, ".."))
+ continue;
+
+ filename = GCSPRINTF(SYSFS_USB_DEV "/%s/devnum", de->d_name);
+ if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+ devnum = atoi(buf);
+
+ filename = GCSPRINTF(SYSFS_USB_DEV "/%s/busnum", de->d_name);
+ if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+ busnum = atoi(buf);
+
+ if (bus == busnum && addr == devnum) {
+ busid = libxl__strdup(gc, de->d_name);
+ break;
+ }
+ }
+
+ closedir(dir);
+ return busid;
+}
+
+static int usbdev_busaddr_from_busid(libxl__gc *gc, const char *busid,
+ uint8_t *bus, uint8_t *addr)
+{
+ char *filename;
+ void *buf;
+
+ filename = GCSPRINTF(SYSFS_USB_DEV "/%s/busnum", busid);
+ if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+ *bus = atoi(buf);
+ else
+ return ERROR_FAIL;
+
+ filename = GCSPRINTF(SYSFS_USB_DEV "/%s/devnum", busid);
+ if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+ *addr = atoi(buf);
+ else
+ return ERROR_FAIL;
+
+ return 0;
+}
+
+static int get_assigned_devices(libxl__gc *gc,
+ libxl_device_usbdev **list, int *num)
+{
+ char **domlist;
+ unsigned int ndom = 0;
+ int i, j, k;
+ int rc;
+
+ *list = NULL;
+ *num = 0;
+
+ domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &ndom);
+ for (i = 0; i < ndom; i++) {
+ char *libxl_vusbs_path;
+ char **usbctrls;
+ unsigned int nc = 0;
+ uint32_t domid = atoi(domlist[i]);
+
+ libxl_vusbs_path = GCSPRINTF("%s/device/%s",
+ libxl__xs_libxl_path(gc, domid),
+ libxl__device_kind_to_string(LIBXL__DEVICE_KIND_VUSB));
+ usbctrls = libxl__xs_directory(gc, XBT_NULL,
+ libxl_vusbs_path, &nc);
+
+ for (j = 0; j < nc; j++) {
+ libxl_device_usbdev *tmp = NULL;
+ int nd = 0;
+
+ rc = libxl__device_usbdev_list_for_usbctrl(gc, domid,
+ atoi(usbctrls[j]),
+ &tmp, &nd);
+ if (rc) goto out;
+
+ if (!nd) continue;
+
+ GCREALLOC_ARRAY(*list, *num + nd);
+ for (k = 0; k < nd; k++) {
+ libxl_device_usbdev_copy(CTX, *list + *num, tmp + k);
+ (*num)++;
+ }
+ }
+ }
+
+ return 0;
+
+out:
+ LOG(ERROR, "fail to get assigned devices");
+ return rc;
+}
+
+static bool is_usbdev_in_array(libxl_device_usbdev *usbdevs, int num,
+ libxl_device_usbdev *usbdev)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ if (usbdevs[i].u.hostdev.hostbus == usbdev->u.hostdev.hostbus &&
+ usbdevs[i].u.hostdev.hostaddr == usbdev->u.hostdev.hostaddr)
+ return true;
+ }
+
+ return false;
+}
+
+/* check if USB device type is assignable */
+static bool is_usbdev_assignable(libxl__gc *gc, libxl_device_usbdev *usbdev)
+{
+ int classcode;
+ char *filename;
+ void *buf = NULL;
+ char *busid = NULL;
+
+ busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
+ usbdev->u.hostdev.hostaddr);
+ if (!busid) return false;
+
+ filename = GCSPRINTF(SYSFS_USB_DEV "/%s/bDeviceClass", busid);
+ if (libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+ return false;
+
+ classcode = atoi(buf);
+ return classcode != USBHUB_CLASS_CODE;
+}
+
+/* get usb devices under certain usb controller */
+static int
+libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc,
+ uint32_t domid,
+ libxl_devid usbctrl,
+ libxl_device_usbdev **usbdevs,
+ int *num)
+{
+ const char *libxl_path, *be_path, *num_devs;
+ int n, i, rc;
+
+ *usbdevs = NULL;
+ *num = 0;
+
+ libxl_path = libxl__domain_device_libxl_path(gc, domid, usbctrl,
+ LIBXL__DEVICE_KIND_VUSB);
+
+ be_path = vusb_be_from_xs_libxl(gc, libxl_path);
+ if (!be_path) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/num-ports", be_path),
+ &num_devs);
+ if (rc) goto out;
+
+ n = num_devs ? atoi(num_devs) : 0;
+
+ for (i = 0; i < n; i++) {
+ const char *busid;
+ libxl_device_usbdev *usbdev;
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/port/%d", be_path, i + 1),
+ &busid);
+ if (rc) goto out;
+
+ if (busid && strcmp(busid, "")) {
+ GCREALLOC_ARRAY(*usbdevs, *num + 1);
+ usbdev = *usbdevs + *num;
+ (*num)++;
+ libxl_device_usbdev_init(usbdev);
+ usbdev->ctrl = usbctrl;
+ usbdev->port = i + 1;
+ usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
+ rc = usbdev_busaddr_from_busid(gc, busid,
+ &usbdev->u.hostdev.hostbus,
+ &usbdev->u.hostdev.hostaddr);
+ if (rc) goto out;
+ }
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+/* get all usb devices of the domain */
+libxl_device_usbdev *
+libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num)
+{
+ GC_INIT(ctx);
+ libxl_device_usbdev *usbdevs = NULL;
+ const char *libxl_vusbs_path;
+ char **usbctrls;
+ unsigned int nc = 0;
+ int i, j;
+
+ *num = 0;
+
+ libxl_vusbs_path = GCSPRINTF("%s/device/%s",
+ libxl__xs_libxl_path(gc, domid),
+ libxl__device_kind_to_string(
+ LIBXL__DEVICE_KIND_VUSB));
+ usbctrls = libxl__xs_directory(gc, XBT_NULL, libxl_vusbs_path, &nc);
+
+ for (i = 0; i < nc; i++) {
+ int rc, nd = 0;
+ libxl_device_usbdev *tmp = NULL;
+
+ rc = libxl__device_usbdev_list_for_usbctrl(gc, domid,
+ atoi(usbctrls[i]),
+ &tmp, &nd);
+ if (rc || !nd) continue;
+
+ usbdevs = libxl__realloc(NOGC, usbdevs,
+ sizeof(*usbdevs) * (*num + nd));
+ for (j = 0; j < nd; j++) {
+ libxl_device_usbdev_copy(ctx, usbdevs + *num, tmp + j);
+ (*num)++;
+ }
+ }
+
+ GC_FREE;
+ return usbdevs;
+}
+
+static char *vusb_get_port_path(libxl__gc *gc, uint32_t domid,
+ libxl_usbctrl_type type, int ctrl, int port)
+{
+ char *path;
+
+ if (type == LIBXL_USBCTRL_TYPE_DEVICEMODEL)
+ path = GCSPRINTF("%s/device/%s", libxl__xs_libxl_path(gc, domid),
+ libxl__device_kind_to_string(LIBXL__DEVICE_KIND_VUSB));
+ else
+ path = GCSPRINTF("%s/backend/%s/%d",
+ libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID),
+ pvusb_get_device_type(type), domid);
+
+ return GCSPRINTF("%s/%d/port/%d", path, ctrl, port);
+}
+
+/* find first unused controller:port and give that to usb device */
+static int
+libxl__device_usbdev_set_default_usbctrl(libxl__gc *gc, uint32_t domid,
+ libxl_device_usbdev *usbdev)
+{
+ libxl_device_usbctrl *usbctrls = NULL;
+ int numctrl = 0;
+ int i, j, rc;
+
+ usbctrls = libxl_device_usbctrl_list(CTX, domid, &numctrl);
+ if (!numctrl || !usbctrls) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ for (i = 0; i < numctrl; i++) {
+ for (j = 0; j < usbctrls[i].ports; j++) {
+ const char *path, *tmp;
+
+ path = vusb_get_port_path(gc, domid, usbctrls[i].type,
+ usbctrls[i].devid, j + 1);
+ rc = libxl__xs_read_checked(gc, XBT_NULL, path, &tmp);
+ if (rc) goto out;
+
+ if (tmp && !strcmp(tmp, "")) {
+ usbdev->ctrl = usbctrls[i].devid;
+ usbdev->port = j + 1;
+ rc = 0;
+ goto out;
+ }
+ }
+ }
+
+ /* no available controller:port */
+ rc = ERROR_FAIL;
+
+out:
+ libxl_device_usbctrl_list_free(usbctrls, numctrl);
+ return rc;
+}
+
+/* Fill in usb information with default value.
+ *
+ * Generally, it does:
+ * 1) if "controller" is not specified:
+ * - if "port" is not specified, try to find an available controller:port,
+ * if found, use that; otherwise, create a new controller, use this
+ * controller and its first port
+ * - if "port" is specified, report error.
+ * 2) if "controller" is specified, but port is not specified:
+ * try to find an available port under this controller, if found, use
+ * that, otherwise, report error.
+ * 3) if both "controller" and "port" are specified:
+ * check the controller:port is available, if not, report error.
+ */
+static int libxl__device_usbdev_setdefault(libxl__gc *gc,
+ uint32_t domid,
+ libxl_device_usbdev *usbdev,
+ bool update_json)
+{
+ int rc;
+
+ if (!usbdev->type)
+ usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
+
+ if (usbdev->ctrl == -1) {
+ if (usbdev->port) {
+ LOGD(ERROR, domid,
+ "USB controller must be specified if you specify port");
+ return ERROR_INVAL;
+ }
+
+ rc = libxl__device_usbdev_set_default_usbctrl(gc, domid, usbdev);
+ /* If no existing controller to host this usb device, add a new one */
+ if (rc) {
+ libxl_device_usbctrl *usbctrl;
+
+ GCNEW(usbctrl);
+ libxl_device_usbctrl_init(usbctrl);
+ rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl,
+ update_json);
+ if (rc < 0) goto out;
+
+ rc = libxl__device_usbctrl_update_devid(gc, domid, usbctrl);
+ if (rc) goto out;
+
+ rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl,
+ update_json);
+ if (rc) goto out;
+
+ usbdev->ctrl = usbctrl->devid;
+ usbdev->port = 1;
+ }
+ } else {
+ /* A controller was specified; look it up */
+ const char *libxl_path, *be_path, *tmp;
+
+ libxl_path = libxl__domain_device_libxl_path(gc, domid, usbdev->ctrl,
+ LIBXL__DEVICE_KIND_VUSB);
+
+ be_path = vusb_be_from_xs_libxl(gc, libxl_path);
+ if (!be_path) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (usbdev->port) {
+ /* A specific port was requested; see if it's available */
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/port/%d",
+ be_path, usbdev->port),
+ &tmp);
+ if (rc) goto out;
+
+ if (tmp && strcmp(tmp, "")) {
+ LOGD(ERROR, domid, "The controller port isn't available");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ } else {
+ /* No port was requested. Choose free port. */
+ int i, ports;
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/num-ports", be_path), &tmp);
+ if (rc) goto out;
+
+ ports = tmp ? atoi(tmp) : 0;
+
+ for (i = 0; i < ports; i++) {
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/port/%d", be_path, i + 1),
+ &tmp);
+ if (rc) goto out;
+
+ if (tmp && !strcmp(tmp, "")) {
+ usbdev->port = i + 1;
+ break;
+ }
+ }
+
+ if (!usbdev->port) {
+ LOGD(ERROR, domid, "No available port under specified controller");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+/* Add usb information to xenstore
+ *
+ * Adding a usb device won't create new 'qusb'/'vusb' device, but only write
+ * the device busid to the controller:port in xenstore.
+ */
+static int libxl__device_usbdev_add_xenstore(libxl__gc *gc, uint32_t domid,
+ libxl_device_usbdev *usbdev,
+ libxl_usbctrl_type type,
+ bool update_json)
+{
+ char *be_path, *busid;
+ int rc;
+ xs_transaction_t t = XBT_NULL;
+ libxl_domain_config d_config;
+ libxl_device_usbdev usbdev_saved;
+ libxl__flock *lock = NULL;
+
+ libxl_domain_config_init(&d_config);
+ libxl_device_usbdev_init(&usbdev_saved);
+ libxl_device_usbdev_copy(CTX, &usbdev_saved, usbdev);
+
+ busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
+ usbdev->u.hostdev.hostaddr);
+ if (!busid) {
+ LOGD(DEBUG, domid, "Fail to get busid of usb device");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (update_json) {
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
+ device_add_domain_config(gc, &d_config, &libxl__usbdev_devtype,
+ &usbdev_saved);
+
+ rc = libxl__dm_check_start(gc, &d_config, domid);
+ if (rc) goto out;
+ }
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+
+ if (update_json) {
+ rc = libxl__set_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+ }
+
+ be_path = vusb_get_port_path(gc, domid, type, usbdev->ctrl,
+ usbdev->port);
+
+ LOGD(DEBUG, domid, "Adding usb device %s to xenstore: controller %d, port %d",
+ busid, usbdev->ctrl, usbdev->port);
+
+ rc = libxl__xs_write_checked(gc, t, be_path, busid);
+ if (rc) goto out;
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc < 0) goto out;
+ }
+
+ rc = 0;
+
+out:
+ if (lock) libxl__unlock_file(lock);
+ libxl_device_usbdev_dispose(&usbdev_saved);
+ libxl_domain_config_dispose(&d_config);
+ return rc;
+}
+
+static int libxl__device_usbdev_remove_xenstore(libxl__gc *gc, uint32_t domid,
+ libxl_device_usbdev *usbdev,
+ libxl_usbctrl_type type)
+{
+ char *be_path;
+
+ be_path = vusb_get_port_path(gc, domid, type, usbdev->ctrl, usbdev->port);
+
+ LOGD(DEBUG, domid, "Removing usb device from xenstore: controller %d, port %d",
+ usbdev->ctrl, usbdev->port);
+
+ return libxl__xs_write_checked(gc, XBT_NULL, be_path, "");
+}
+
+static char *usbdev_busid_from_ctrlport(libxl__gc *gc, uint32_t domid,
+ libxl_device_usbdev *usbdev,
+ libxl_usbctrl_type type)
+{
+ return libxl__xs_read(gc, XBT_NULL,
+ vusb_get_port_path(gc, domid, type, usbdev->ctrl,
+ usbdev->port));
+}
+
+/* get original driver path of usb interface, stored in @drvpath */
+static int usbintf_get_drvpath(libxl__gc *gc, const char *intf, char **drvpath)
+{
+ char *spath, *dp = NULL;
+
+ spath = GCSPRINTF(SYSFS_USB_DEV "/%s/driver", intf);
+
+ /* Find the canonical path to the driver. */
+ dp = libxl__zalloc(gc, PATH_MAX);
+ dp = realpath(spath, dp);
+ if (!dp && errno != ENOENT) {
+ LOGE(ERROR, "get realpath failed: '%s'", spath);
+ return ERROR_FAIL;
+ }
+
+ *drvpath = dp;
+
+ return 0;
+}
+
+static int unbind_usbintf(libxl__gc *gc, const char *intf)
+{
+ char *path;
+ int fd = -1;
+ int rc;
+
+ path = GCSPRINTF(SYSFS_USB_DEV "/%s/driver/unbind", intf);
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ LOGE(ERROR, "open file failed: '%s'", path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (libxl_write_exactly(CTX, fd, intf, strlen(intf), path, intf)) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ if (fd >= 0) close(fd);
+ return rc;
+}
+
+static int bind_usbintf(libxl__gc *gc, const char *intf, const char *drvpath)
+{
+ char *bind_path, *intf_path;
+ struct stat st;
+ int fd = -1;
+ int rc, r;
+
+ intf_path = GCSPRINTF("%s/%s", drvpath, intf);
+
+ /* check through lstat, if intf already exists under drvpath,
+ * it's already bound, return directly; if it doesn't exist,
+ * continue to do bind work; otherwise, return error.
+ */
+ r = lstat(intf_path, &st);
+ if (r == 0)
+ return 0;
+ if (r < 0 && errno != ENOENT)
+ return ERROR_FAIL;
+
+ bind_path = GCSPRINTF("%s/bind", drvpath);
+
+ fd = open(bind_path, O_WRONLY);
+ if (fd < 0) {
+ LOGE(ERROR, "open file failed: '%s'", bind_path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ if (libxl_write_exactly(CTX, fd, intf, strlen(intf), bind_path, intf)) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ if (fd >= 0) close(fd);
+ return rc;
+}
+
+/* Is usb interface bound to usbback? */
+static int usbintf_is_assigned(libxl__gc *gc, char *intf)
+{
+ char *spath;
+ int r;
+ struct stat st;
+
+ spath = GCSPRINTF(SYSFS_USBBACK_DRIVER "/%s", intf);
+ r = lstat(spath, &st);
+
+ if (r == 0)
+ return 1;
+ if (r < 0 && errno == ENOENT)
+ return 0;
+ LOGE(ERROR, "Accessing %s", spath);
+ return -1;
+}
+
+static int usbdev_get_all_interfaces(libxl__gc *gc, const char *busid,
+ char ***intfs, int *num)
+{
+ DIR *dir;
+ char *buf;
+ struct dirent *de;
+ int rc;
+
+ *intfs = NULL;
+ *num = 0;
+
+ buf = GCSPRINTF("%s:", busid);
+
+ dir = opendir(SYSFS_USB_DEV);
+ if (!dir) {
+ LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV);
+ return ERROR_FAIL;
+ }
+
+ for (;;) {
+ errno = 0;
+ de = readdir(dir);
+
+ if (!de && errno) {
+ LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ if (!de)
+ break;
+
+ if (!strcmp(de->d_name, ".") ||
+ !strcmp(de->d_name, ".."))
+ continue;
+
+ if (!strncmp(de->d_name, buf, strlen(buf))) {
+ GCREALLOC_ARRAY(*intfs, *num + 1);
+ (*intfs)[*num] = libxl__strdup(gc, de->d_name);
+ (*num)++;
+ }
+ }
+
+ rc = 0;
+
+out:
+ closedir(dir);
+ return rc;
+}
+
+/* Encode usb interface so that it could be written to xenstore as a key.
+ *
+ * Since xenstore key cannot include '.' or ':', we'll change '.' to '_',
+ * change ':' to '@'. For example, 3-1:2.1 will be encoded to 3-1@2_1.
+ * This will be used to save original driver of USB device to xenstore.
+ */
+static char *usb_interface_xenstore_encode(libxl__gc *gc, const char *busid)
+{
+ char *str = libxl__strdup(gc, busid);
+ int i, len = strlen(str);
+
+ for (i = 0; i < len; i++) {
+ if (str[i] == '.') str[i] = '_';
+ if (str[i] == ':') str[i] = '@';
+ }
+ return str;
+}
+
+/* Unbind USB device from "usbback" driver.
+ *
+ * If there are many interfaces under USB device, check each interface,
+ * unbind from "usbback" driver.
+ */
+static int usbback_dev_unassign(libxl__gc *gc, const char *busid)
+{
+ char **intfs = NULL;
+ int i, num = 0;
+ int rc;
+
+ rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
+ if (rc) goto out;
+
+ for (i = 0; i < num; i++) {
+ char *intf = intfs[i];
+
+ /* check if the USB interface is already bound to "usbback" */
+ if (usbintf_is_assigned(gc, intf) > 0) {
+ /* unbind interface from usbback driver */
+ rc = unbind_usbintf(gc, intf);
+ if (rc) {
+ LOGE(ERROR, "Couldn't unbind %s from usbback", intf);
+ goto out;
+ }
+ }
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+/* rebind USB device to original driver.
+ *
+ * If there are many interfaces under USB device, for reach interface,
+ * read driver_path from xenstore (if there is) and rebind to its
+ * original driver, then remove driver_path information from xenstore.
+ */
+static int usbdev_rebind(libxl__gc *gc, const char *busid)
+{
+ char **intfs = NULL;
+ char *usbdev_encode = NULL;
+ char *path = NULL;
+ int i, num = 0;
+ int rc;
+
+ rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
+ if (rc) goto out;
+
+ usbdev_encode = usb_interface_xenstore_encode(gc, busid);
+
+ for (i = 0; i < num; i++) {
+ char *intf = intfs[i];
+ char *usbintf_encode = NULL;
+ const char *drvpath;
+
+ /* rebind USB interface to its originial driver */
+ usbintf_encode = usb_interface_xenstore_encode(gc, intf);
+ path = GCSPRINTF(USBBACK_INFO_PATH "/%s/%s/driver_path",
+ usbdev_encode, usbintf_encode);
+ rc = libxl__xs_read_checked(gc, XBT_NULL, path, &drvpath);
+ if (rc) goto out;
+
+ if (drvpath) {
+ rc = bind_usbintf(gc, intf, drvpath);
+ if (rc) {
+ LOGE(ERROR, "Couldn't rebind %s to %s", intf, drvpath);
+ goto out;
+ }
+ }
+ }
+
+out:
+ path = GCSPRINTF(USBBACK_INFO_PATH "/%s", usbdev_encode);
+ libxl__xs_rm_checked(gc, XBT_NULL, path);
+ return rc;
+}
+
+
+/* Bind USB device to "usbback" driver.
+ *
+ * If there are many interfaces under USB device, check each interface,
+ * unbind from original driver and bind to "usbback" driver.
+ */
+static int usbback_dev_assign(libxl__gc *gc, const char *busid)
+{
+ char **intfs = NULL;
+ int num = 0, i;
+ int rc;
+ char *usbdev_encode = NULL;
+
+ rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
+ if (rc) return rc;
+
+ usbdev_encode = usb_interface_xenstore_encode(gc, busid);
+
+ for (i = 0; i < num; i++) {
+ char *intf = intfs[i];
+ char *drvpath = NULL;
+
+ /* already assigned to usbback */
+ if (usbintf_is_assigned(gc, intf) > 0)
+ continue;
+
+ rc = usbintf_get_drvpath(gc, intf, &drvpath);
+ if (rc) goto out;
+
+ if (drvpath) {
+ /* write driver path to xenstore for later rebinding */
+ char *usbintf_encode = NULL;
+ char *path;
+
+ usbintf_encode = usb_interface_xenstore_encode(gc, intf);
+ path = GCSPRINTF(USBBACK_INFO_PATH "/%s/%s/driver_path",
+ usbdev_encode, usbintf_encode);
+ rc = libxl__xs_write_checked(gc, XBT_NULL, path, drvpath);
+ if (rc) goto out;
+
+ /* unbind interface from original driver */
+ rc = unbind_usbintf(gc, intf);
+ if (rc) goto out;
+ }
+
+ /* bind interface to usbback */
+ rc = bind_usbintf(gc, intf, SYSFS_USBBACK_DRIVER);
+ if (rc) {
+ LOG(ERROR, "Couldn't bind %s to %s", intf, SYSFS_USBBACK_DRIVER);
+ goto out;
+ }
+ }
+
+ return 0;
+
+out:
+ /* some interfaces might be bound to usbback, unbind it and
+ * rebind it to its original driver
+ */
+ usbback_dev_unassign(gc, busid);
+ usbdev_rebind(gc, busid);
+ return rc;
+}
+
+static void device_usbdev_add_qmp_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *r, int rc);
+static void device_usbdev_add_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void device_usbdev_add_done(libxl__egc *egc,
+ libxl__ao_device *aodev, int rc);
+
+/* AO operation to add a usb device.
+ *
+ * Generally, it does:
+ * 1) check if the usb device type is assignable
+ * 2) check if the usb device is already assigned to a domain
+ * 3) add 'busid' of the usb device to xenstore contoller/port/.
+ * (PVUSB driver watches the xenstore changes and will detect that.)
+ * 4) unbind usb device from original driver and bind to usbback.
+ * If usb device has many interfaces, then:
+ * - unbind each interface from its original driver and bind to usbback.
+ * - store the original driver to xenstore for later rebinding when
+ * detaching the device.
+ *
+ * Before calling this function, aodev should be properly filled:
+ * aodev->ao, aodev->callback, aodev->update_json, ...
+ */
+static void libxl__device_usbdev_add(libxl__egc *egc, uint32_t domid,
+ libxl_device_usbdev *usbdev,
+ libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ int rc;
+ libxl_device_usbdev *assigned;
+ int num_assigned;
+ libxl_device_usbctrl usbctrl;
+ char *busid;
+ bool has_callback = false;
+
+ libxl_device_usbctrl_init(&usbctrl);
+
+ /* Store *usbdev to be used by callbacks */
+ aodev->device_config = usbdev;
+ aodev->device_type = &libxl__usbdev_devtype;
+
+ /* Currently only support adding USB device from Dom0 backend.
+ * So, if USB controller is specified, check its backend domain,
+ * if it's not Dom0, report error.
+ */
+ if (usbdev->ctrl != -1) {
+ rc = libxl_devid_to_device_usbctrl(CTX, domid, usbdev->ctrl,
+ &usbctrl);
+ if (rc) goto out;
+
+ if (usbctrl.backend_domid != LIBXL_TOOLSTACK_DOMID) {
+ LOGD(ERROR, domid,
+ "Don't support adding USB device from non-Dom0 backend");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ libxl_device_usbctrl_dispose(&usbctrl);
+ }
+
+ /* check usb device is assignable type */
+ if (!is_usbdev_assignable(gc, usbdev)) {
+ LOGD(ERROR, domid, "USB device is not assignable.");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /* check usb device is already assigned */
+ rc = get_assigned_devices(gc, &assigned, &num_assigned);
+ if (rc) {
+ LOGD(ERROR, domid, "cannot determine if device is assigned,"
+ " refusing to continue");
+ goto out;
+ }
+
+ if (is_usbdev_in_array(assigned, num_assigned, usbdev)) {
+ LOGD(ERROR, domid, "USB device already attached to a domain");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ /* fill default values, e.g, if usbdev->ctrl and usbdev->port
+ * not specified, choose available controller:port and fill in. */
+ rc = libxl__device_usbdev_setdefault(gc, domid, usbdev,
+ aodev->update_json);
+ if (rc) goto out;
+
+ rc = libxl_devid_to_device_usbctrl(CTX, domid, usbdev->ctrl, &usbctrl);
+ if (rc) goto out;
+
+ /* do actual adding usb device operation */
+ switch (usbctrl.type) {
+ case LIBXL_USBCTRL_TYPE_PV:
+ busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
+ usbdev->u.hostdev.hostaddr);
+ if (!busid) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
+ LIBXL_USBCTRL_TYPE_PV,
+ aodev->update_json);
+ if (rc) goto out;
+
+ rc = usbback_dev_assign(gc, busid);
+ if (rc) {
+ libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
+ LIBXL_USBCTRL_TYPE_PV);
+ goto out;
+ }
+ break;
+ case LIBXL_USBCTRL_TYPE_QUSB:
+ rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
+ LIBXL_USBCTRL_TYPE_QUSB,
+ aodev->update_json);
+ if (rc) goto out;
+
+ break;
+ case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
+ rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
+ LIBXL_USBCTRL_TYPE_DEVICEMODEL,
+ aodev->update_json);
+ if (rc) goto out;
+
+ rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
+ device_usbdev_add_timeout,
+ LIBXL_QMP_CMD_TIMEOUT * 1000);
+ if (rc) goto out;
+
+ aodev->qmp.ao = ao;
+ aodev->qmp.domid = domid;
+ aodev->qmp.callback = device_usbdev_add_qmp_cb;
+ aodev->qmp.payload_fd = -1;
+ rc = libxl__device_usbdev_add_hvm(egc, &aodev->qmp, usbdev);
+ if (rc) {
+ libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
+ LIBXL_USBCTRL_TYPE_DEVICEMODEL);
+ goto out;
+ }
+ has_callback = true;
+ break;
+ default:
+ LOGD(ERROR, domid, "Unsupported usb controller type");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ libxl_device_usbctrl_dispose(&usbctrl);
+ /* Only call _done if no callback have been setup */
+ if (!has_callback)
+ device_usbdev_add_done(egc, aodev, rc); /* must be last */
+}
+
+static void device_usbdev_add_timeout(libxl__egc *egc,
+ libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ EGC_GC;
+ libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
+
+ if (rc == ERROR_TIMEDOUT)
+ LOGD(ERROR, aodev->qmp.domid,
+ "Adding usbdev to QEMU timed out");
+ device_usbdev_add_qmp_cb(egc, &aodev->qmp, NULL, rc);
+}
+
+static void device_usbdev_add_qmp_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *r,
+ int rc)
+{
+ EGC_GC;
+ libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
+ libxl_device_usbdev *const usbdev = aodev->device_config;
+
+ if (rc)
+ libxl__device_usbdev_remove_xenstore(gc, qmp->domid,
+ usbdev, LIBXL_USBCTRL_TYPE_DEVICEMODEL);
+ device_usbdev_add_done(egc, aodev, rc); /* must be last */
+}
+
+static void device_usbdev_add_done(libxl__egc *egc,
+ libxl__ao_device *aodev,
+ int rc)
+{
+ EGC_GC;
+
+ libxl__ev_time_deregister(gc, &aodev->timeout);
+ libxl__ev_qmp_dispose(gc, &aodev->qmp);
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+LIBXL_DEFINE_DEVICE_ADD(usbdev)
+static LIBXL_DEFINE_DEVICES_ADD(usbdev)
+
+static void device_usbdev_remove_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void device_usbdev_remove_qmp_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp, const libxl__json_object *r, int rc);
+static void device_usbdev_remove_done(libxl__egc *egc,
+ libxl__ao_device *aodev, int rc);
+
+/* Operation to remove usb device.
+ *
+ * Generally, it does:
+ * 1) check if the usb device is assigned to the domain
+ * 2) remove the usb device from xenstore controller/port.
+ * 3) unbind usb device from usbback and rebind to its original driver.
+ * If usb device has many interfaces, do it to each interface.
+ *
+ * Before calling this function, aodev should be properly filled:
+ * aodev->ao, aodev->callback, ...
+ */
+static void libxl__device_usbdev_remove(libxl__egc *egc, uint32_t domid,
+ libxl_device_usbdev *usbdev,
+ libxl__ao_device *aodev)
+{
+ STATE_AO_GC(aodev->ao);
+ int rc;
+ char *busid;
+ libxl_device_usbctrl usbctrl;
+ bool has_callback = false;
+
+ /* Store *usbdev to be used by callbacks */
+ aodev->device_config = usbdev;
+ aodev->device_type = &libxl__usbdev_devtype;
+
+ libxl_device_usbctrl_init(&usbctrl);
+
+ if (usbdev->ctrl < 0 || usbdev->port < 1) {
+ LOGD(ERROR, domid, "Invalid USB device");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl_devid_to_device_usbctrl(CTX, domid, usbdev->ctrl, &usbctrl);
+ if (rc) goto out;
+
+ if (usbctrl.backend_domid != LIBXL_TOOLSTACK_DOMID) {
+ LOGD(ERROR, domid,
+ "Don't support removing USB device from non-Dom0 backend");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ /* do actual removing usb device operation */
+ switch (usbctrl.type) {
+ case LIBXL_USBCTRL_TYPE_PV:
+ busid = usbdev_busid_from_ctrlport(gc, domid, usbdev, usbctrl.type);
+ if (!busid) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ /* Things are done in order of:
+ * unbind USB device from usbback,
+ * remove USB device from xenstore,
+ * rebind USB device to original driver.
+ * It is to balance simplicity with robustness in case of failure:
+ * - We unbind all interfaces before rebinding any interfaces, so
+ * that we never get into a situation where some interfaces are
+ * assigned to usbback and some are assigned to the original drivers.
+ * - We also unbind the interfaces before removing the pvusb xenstore
+ * nodes, so that if the unbind fails in the middle, the device still
+ * shows up in xl usb-list, and the user can re-try removing it.
+ */
+ rc = usbback_dev_unassign(gc, busid);
+ if (rc) {
+ LOGD(ERROR, domid, "Error removing device from guest."
+ " Try running usbdev-detach again.");
+ goto out;
+ }
+
+ rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
+ LIBXL_USBCTRL_TYPE_PV);
+ if (rc) {
+ LOGD(ERROR, domid, "Error removing device from guest."
+ " Try running usbdev-detach again.");
+ goto out;
+ }
+
+ rc = usbdev_rebind(gc, busid);
+ if (rc) {
+ LOGD(ERROR, domid, "USB device removed from guest, but couldn't"
+ " re-bind to domain 0. Try removing and re-inserting"
+ " the USB device or reloading the driver modules.");
+ goto out;
+ }
+
+ break;
+ case LIBXL_USBCTRL_TYPE_QUSB:
+ rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
+ LIBXL_USBCTRL_TYPE_QUSB);
+ if (rc) goto out;
+
+ break;
+ case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
+ rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
+ LIBXL_USBCTRL_TYPE_DEVICEMODEL);
+ if (rc) goto out;
+
+ rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
+ device_usbdev_remove_timeout,
+ LIBXL_QMP_CMD_TIMEOUT * 1000);
+ if (rc) goto out;
+
+ aodev->qmp.ao = ao;
+ aodev->qmp.domid = domid;
+ aodev->qmp.callback = device_usbdev_remove_qmp_cb;
+ aodev->qmp.payload_fd = -1;
+ rc = libxl__device_usbdev_del_hvm(egc, &aodev->qmp, usbdev);
+ if (rc) {
+ libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
+ LIBXL_USBCTRL_TYPE_DEVICEMODEL,
+ false);
+ goto out;
+ }
+ has_callback = true;
+ break;
+ default:
+ LOGD(ERROR, domid, "Unsupported usb controller type");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ libxl_device_usbctrl_dispose(&usbctrl);
+ /* Only call _done if no callback have been setup */
+ if (!has_callback)
+ device_usbdev_remove_done(egc, aodev, rc); /* must be last */
+}
+
+static void device_usbdev_remove_timeout(libxl__egc *egc,
+ libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
+{
+ EGC_GC;
+ libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
+
+ if (rc == ERROR_TIMEDOUT)
+ LOGD(ERROR, aodev->qmp.domid,
+ "Removing usbdev from QEMU timed out");
+ device_usbdev_remove_qmp_cb(egc, &aodev->qmp, NULL, rc);
+}
+
+static void device_usbdev_remove_qmp_cb(libxl__egc *egc,
+ libxl__ev_qmp *qmp,
+ const libxl__json_object *r,
+ int rc)
+{
+ EGC_GC;
+ libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
+ libxl_device_usbdev *const usbdev = aodev->device_config;
+
+ if (rc) {
+ libxl__device_usbdev_add_xenstore(gc, qmp->domid, usbdev,
+ LIBXL_USBCTRL_TYPE_DEVICEMODEL,
+ false);
+ }
+
+ device_usbdev_remove_done(egc, aodev, rc); /* must be last */
+}
+
+static void device_usbdev_remove_done(libxl__egc *egc,
+ libxl__ao_device *aodev,
+ int rc)
+{
+ EGC_GC;
+
+ libxl__ev_time_deregister(gc, &aodev->timeout);
+ libxl__ev_qmp_dispose(gc, &aodev->qmp);
+ aodev->rc = rc;
+ aodev->callback(egc, aodev);
+}
+
+int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_usbdev *usbdev,
+ const libxl_asyncop_how *ao_how)
+
+{
+ AO_CREATE(ctx, domid, ao_how);
+ libxl__ao_device *aodev;
+
+ GCNEW(aodev);
+ libxl__prepare_ao_device(ao, aodev);
+ aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+ aodev->callback = device_addrm_aocomplete;
+ libxl__device_usbdev_remove(egc, domid, usbdev, aodev);
+
+ return AO_INPROGRESS;
+}
+
+int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx,
+ uint32_t domid,
+ int ctrl,
+ int port,
+ libxl_device_usbdev *usbdev)
+{
+ GC_INIT(ctx);
+ const char *libxl_path, *be_path, *busid;
+ int rc;
+
+ libxl_path = libxl__domain_device_libxl_path(gc, domid, ctrl,
+ LIBXL__DEVICE_KIND_VUSB);
+ be_path = vusb_be_from_xs_libxl(gc, libxl_path);
+ if (!be_path) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/port/%d", be_path, port),
+ &busid);
+ if (rc) goto out;
+
+ if (!busid || !strcmp(busid, "")) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ usbdev->ctrl = ctrl;
+ usbdev->port = port;
+ usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
+ rc = usbdev_busaddr_from_busid(gc, busid,
+ &usbdev->u.hostdev.hostbus,
+ &usbdev->u.hostdev.hostaddr);
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+static int libxl_device_usbctrl_compare(const libxl_device_usbctrl *d1,
+ const libxl_device_usbctrl *d2)
+{
+ return COMPARE_USBCTRL(d1, d2);
+}
+
+static int libxl_device_usbctrl_dm_needed(void *e, unsigned domid)
+{
+ libxl_device_usbctrl *elem = e;
+
+ return elem->type == LIBXL_USBCTRL_TYPE_QUSB &&
+ elem->backend_domid == domid;
+}
+
+static int libxl_device_usbdev_compare(const libxl_device_usbdev *d1,
+ const libxl_device_usbdev *d2)
+{
+ return COMPARE_USB(d1, d2);
+}
+
+void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ libxl_device_usbdev_dispose(&list[i]);
+ free(list);
+}
+
+#define libxl__device_usbctrl_update_devid NULL
+
+LIBXL_DEFINE_DEVID_TO_DEVICE(usbctrl)
+LIBXL_DEFINE_DEVICE_LIST(usbctrl)
+DEFINE_DEVICE_TYPE_STRUCT(usbctrl, VUSB,
+ .from_xenstore = (device_from_xenstore_fn_t)libxl__usbctrl_from_xenstore,
+ .dm_needed = libxl_device_usbctrl_dm_needed
+);
+
+#define libxl__device_from_usbdev NULL
+#define libxl__device_usbdev_update_devid NULL
+
+DEFINE_DEVICE_TYPE_STRUCT(usbdev, VUSB);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <ctype.h>
+
+#include "libxl_internal.h"
+#include "_paths.h"
+
+#ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
+const
+#endif
+char *libxl_basename(const char *name)
+{
+ const char *filename;
+ if (name == NULL)
+ return strdup(".");
+ if (name[0] == '\0')
+ return strdup(".");
+
+ filename = strrchr(name, '/');
+ if (filename)
+ return strdup(filename+1);
+ return strdup(name);
+}
+
+unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus)
+{
+ /* 256 pages (1MB) per vcpu,
+ plus 1 page per MiB of RAM for the P2M map,
+ plus 1 page per MiB of RAM to shadow the resident processes.
+ This is higher than the minimum that Xen would allocate if no value
+ were given (but the Xen minimum is for safety, not performance).
+ */
+ return 4 * (256 * smp_cpus + 2 * (maxmem_kb / 1024));
+}
+
+char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid)
+{
+ unsigned int len;
+ char path[strlen("/local/domain") + 12];
+ char *s;
+
+ snprintf(path, sizeof(path), "/local/domain/%d/name", domid);
+ s = xs_read(ctx->xsh, XBT_NULL, path, &len);
+ return s;
+}
+
+char *libxl__domid_to_name(libxl__gc *gc, uint32_t domid)
+{
+ char *s = libxl_domid_to_name(CTX, domid);
+ libxl__ptr_add(gc, s);
+ return s;
+}
+
+int libxl_name_to_domid(libxl_ctx *ctx, const char *name,
+ uint32_t *domid)
+{
+ int i, nb_domains;
+ char *domname;
+ libxl_dominfo *dominfo;
+ int ret = ERROR_INVAL;
+
+ dominfo = libxl_list_domain(ctx, &nb_domains);
+ if (!dominfo)
+ return ERROR_NOMEM;
+
+ for (i = 0; i < nb_domains; i++) {
+ domname = libxl_domid_to_name(ctx, dominfo[i].domid);
+ if (!domname)
+ continue;
+ if (strcmp(domname, name) == 0) {
+ *domid = dominfo[i].domid;
+ ret = 0;
+ free(domname);
+ break;
+ }
+ free(domname);
+ }
+ libxl_dominfo_list_free(dominfo, nb_domains);
+ return ret;
+}
+
+int libxl_domain_qualifier_to_domid(libxl_ctx *ctx, const char *name,
+ uint32_t *domid)
+{
+ int i, rv;
+ for (i=0; name[i]; i++) {
+ if (!CTYPE(isdigit, name[i])) {
+ goto nondigit_found;
+ }
+ }
+ *domid = strtoul(name, NULL, 10);
+ return 0;
+
+ nondigit_found:
+ /* this could also check for uuids */
+ rv = libxl_name_to_domid(ctx, name, domid);
+ return rv;
+}
+
+static int qualifier_to_id(const char *p, uint32_t *id_r)
+{
+ int i, alldigit;
+
+ alldigit = 1;
+ for (i = 0; p[i]; i++) {
+ if (!isdigit((uint8_t)p[i])) {
+ alldigit = 0;
+ break;
+ }
+ }
+
+ if (i > 0 && alldigit) {
+ *id_r = strtoul(p, NULL, 10);
+ return 0;
+ } else {
+ /* check here if it's a uuid and do proper conversion */
+ }
+ return 1;
+}
+
+int libxl_cpupool_qualifier_to_cpupoolid(libxl_ctx *ctx, const char *p,
+ uint32_t *poolid_r,
+ int *was_name_r)
+{
+ int was_name;
+
+ was_name = qualifier_to_id(p, poolid_r);
+ if (was_name_r) *was_name_r = was_name;
+ return was_name ? libxl_name_to_cpupoolid(ctx, p, poolid_r) : 0;
+}
+
+char *libxl_cpupoolid_to_name(libxl_ctx *ctx, uint32_t poolid)
+{
+ unsigned int len;
+ char path[strlen("/local/pool") + 12];
+ char *s;
+
+ snprintf(path, sizeof(path), "/local/pool/%d/name", poolid);
+ s = xs_read(ctx->xsh, XBT_NULL, path, &len);
+ if (!s && (poolid == 0))
+ return strdup("Pool-0");
+ return s;
+}
+
+/* This is a bit horrid but without xs_exists it seems like the only way. */
+int libxl_cpupoolid_is_valid(libxl_ctx *ctx, uint32_t poolid)
+{
+ int ret;
+ char *s = libxl_cpupoolid_to_name(ctx, poolid);
+
+ ret = (s != NULL);
+ free(s);
+ return ret;
+}
+
+char *libxl__cpupoolid_to_name(libxl__gc *gc, uint32_t poolid)
+{
+ char *s = libxl_cpupoolid_to_name(CTX, poolid);
+ libxl__ptr_add(gc, s);
+ return s;
+}
+
+int libxl_name_to_cpupoolid(libxl_ctx *ctx, const char *name,
+ uint32_t *poolid)
+{
+ int i, nb_pools;
+ char *poolname;
+ libxl_cpupoolinfo *poolinfo;
+ int ret = ERROR_INVAL;
+
+ poolinfo = libxl_list_cpupool(ctx, &nb_pools);
+ if (!poolinfo)
+ return ERROR_NOMEM;
+
+ for (i = 0; i < nb_pools; i++) {
+ if (ret && ((poolname = libxl_cpupoolid_to_name(ctx,
+ poolinfo[i].poolid)) != NULL)) {
+ if (strcmp(poolname, name) == 0) {
+ *poolid = poolinfo[i].poolid;
+ ret = 0;
+ }
+ free(poolname);
+ }
+ }
+ libxl_cpupoolinfo_list_free(poolinfo, nb_pools);
+ return ret;
+}
+
+int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid)
+{
+ GC_INIT(ctx);
+ char * stubdom_id_s;
+ int ret;
+
+ stubdom_id_s = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/image/device-model-domid",
+ libxl__xs_get_dompath(gc, guest_domid)));
+ if (stubdom_id_s)
+ ret = atoi(stubdom_id_s);
+ else
+ ret = 0;
+ GC_FREE;
+ return ret;
+}
+
+int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid)
+{
+ GC_INIT(ctx);
+ char *target, *endptr;
+ uint32_t value;
+ int ret = 0;
+
+ target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/target",
+ libxl__xs_get_dompath(gc, domid)));
+ if (!target)
+ goto out;
+ value = strtol(target, &endptr, 10);
+ if (*endptr != '\0')
+ goto out;
+ if (target_domid)
+ *target_domid = value;
+ ret = 1;
+out:
+ GC_FREE;
+ return ret;
+}
+
+static int logrename(libxl__gc *gc, const char *old, const char *new)
+{
+ int r;
+
+ r = rename(old, new);
+ if (r) {
+ if (errno == ENOENT) return 0; /* ok */
+
+ LOGE(ERROR, "failed to rotate logfile - "
+ "could not rename %s to %s", old, new);
+ return ERROR_FAIL;
+ }
+ return 0;
+}
+
+int libxl_create_logfile(libxl_ctx *ctx, const char *name, char **full_name)
+{
+ GC_INIT(ctx);
+ struct stat stat_buf;
+ char *logfile, *logfile_new;
+ int i, rc;
+
+ logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log", name);
+ if (stat(logfile, &stat_buf) == 0) {
+ /* file exists, rotate */
+ logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log.10", name);
+ unlink(logfile);
+ for (i = 9; i > 0; i--) {
+ logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log.%d", name, i);
+ logfile_new = GCSPRINTF(XEN_LOG_DIR "/%s.log.%d", name, i + 1);
+ rc = logrename(gc, logfile, logfile_new);
+ if (rc)
+ goto out;
+ }
+ logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log", name);
+ logfile_new = GCSPRINTF(XEN_LOG_DIR "/%s.log.1", name);
+
+ rc = logrename(gc, logfile, logfile_new);
+ if (rc)
+ goto out;
+ } else {
+ if (errno != ENOENT)
+ LOGE(WARN, "problem checking existence of logfile %s, "
+ "which might have needed to be rotated",
+ name);
+ }
+ *full_name = strdup(logfile);
+ rc = 0;
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_string_to_backend(libxl_ctx *ctx, char *s, libxl_disk_backend *backend)
+{
+ char *p;
+ int rc = 0;
+
+ if (!strcmp(s, "phy")) {
+ *backend = LIBXL_DISK_BACKEND_PHY;
+ } else if (!strcmp(s, "file")) {
+ *backend = LIBXL_DISK_BACKEND_TAP;
+ } else if (!strcmp(s, "qdisk")) {
+ *backend = LIBXL_DISK_BACKEND_QDISK;
+ } else if (!strcmp(s, "tap")) {
+ p = strchr(s, ':');
+ if (!p) {
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ p++;
+ if (!strcmp(p, "vhd")) {
+ *backend = LIBXL_DISK_BACKEND_TAP;
+ } else if (!strcmp(p, "qcow")) {
+ *backend = LIBXL_DISK_BACKEND_QDISK;
+ } else if (!strcmp(p, "qcow2")) {
+ *backend = LIBXL_DISK_BACKEND_QDISK;
+ } else if (!strcmp(p, "qed")) {
+ *backend = LIBXL_DISK_BACKEND_QDISK;
+ }
+ }
+out:
+ return rc;
+}
+
+int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
+ void **data_r, int *datalen_r) {
+ GC_INIT(ctx);
+ FILE *f = 0;
+ uint8_t *data = 0;
+ int datalen = 0;
+ int e;
+ struct stat stab;
+ ssize_t rs;
+
+ f = fopen(filename, "r");
+ if (!f) {
+ if (errno == ENOENT) return ENOENT;
+ LOGE(ERROR, "failed to open %s", filename);
+ goto xe;
+ }
+
+ if (fstat(fileno(f), &stab)) {
+ LOGE(ERROR, "failed to fstat %s", filename);
+ goto xe;
+ }
+
+ if (!S_ISREG(stab.st_mode)) {
+ LOGE(ERROR, "%s is not a plain file", filename);
+ errno = ENOTTY;
+ goto xe;
+ }
+
+ if (stab.st_size > INT_MAX) {
+ LOG(ERROR, "file %s is far too large", filename);
+ errno = EFBIG;
+ goto xe;
+ }
+
+ datalen = stab.st_size;
+
+ if (stab.st_size && data_r) {
+ data = malloc(datalen);
+ if (!data) goto xe;
+
+ rs = fread(data, 1, datalen, f);
+ if (rs != datalen) {
+ if (ferror(f))
+ LOGE(ERROR, "failed to read %s", filename);
+ else if (feof(f))
+ LOG(ERROR, "%s changed size while we were reading it",
+ filename);
+ else
+ abort();
+ goto xe;
+ }
+ }
+
+ if (fclose(f)) {
+ f = 0;
+ LOGE(ERROR, "failed to close %s", filename);
+ goto xe;
+ }
+
+ if (data_r) *data_r = data;
+ if (datalen_r) *datalen_r = datalen;
+
+ GC_FREE;
+ return 0;
+
+ xe:
+ GC_FREE;
+ e = errno;
+ assert(e != ENOENT);
+ if (f) fclose(f);
+ free(data);
+ return e;
+}
+
+int libxl__read_sysfs_file_contents(libxl__gc *gc, const char *filename,
+ void **data_r, int *datalen_r)
+{
+ FILE *f = 0;
+ uint8_t *data = 0;
+ int datalen = 0;
+ int e;
+ struct stat stab;
+ ssize_t rs;
+
+ f = fopen(filename, "r");
+ if (!f) {
+ if (errno == ENOENT) return ENOENT;
+ LOGE(ERROR, "failed to open %s", filename);
+ goto xe;
+ }
+
+ if (fstat(fileno(f), &stab)) {
+ LOGE(ERROR, "failed to fstat %s", filename);
+ goto xe;
+ }
+
+ if (!S_ISREG(stab.st_mode)) {
+ LOGE(ERROR, "%s is not a plain file", filename);
+ errno = ENOTTY;
+ goto xe;
+ }
+
+ if (stab.st_size > INT_MAX) {
+ LOG(ERROR, "file %s is far too large", filename);
+ errno = EFBIG;
+ goto xe;
+ }
+
+ datalen = stab.st_size;
+
+ if (stab.st_size && data_r) {
+ data = libxl__malloc(gc, datalen);
+
+ /* For sysfs file, datalen is always PAGE_SIZE. 'read'
+ * will return the number of bytes of the actual content,
+ * rs <= datalen is expected.
+ */
+ rs = fread(data, 1, datalen, f);
+ if (rs < datalen) {
+ if (ferror(f)) {
+ LOGE(ERROR, "failed to read %s", filename);
+ goto xe;
+ }
+
+ datalen = rs;
+ data = libxl__realloc(gc, data, datalen);
+ }
+ }
+
+ if (fclose(f)) {
+ f = 0;
+ LOGE(ERROR, "failed to close %s", filename);
+ goto xe;
+ }
+
+ if (data_r) *data_r = data;
+ if (datalen_r) *datalen_r = datalen;
+
+ return 0;
+
+ xe:
+ e = errno;
+ assert(e != ENOENT);
+ if (f) fclose(f);
+ return e;
+}
+
+
+#define READ_WRITE_EXACTLY(rw, zero_is_eof, constdata) \
+ \
+ int libxl_##rw##_exactly(libxl_ctx *ctx, int fd, \
+ constdata void *data, ssize_t sz, \
+ const char *source, const char *what) { \
+ ssize_t got; \
+ GC_INIT(ctx); \
+ \
+ while (sz > 0) { \
+ got = rw(fd, data, sz); \
+ if (got == -1) { \
+ if (errno == EINTR) continue; \
+ if (!ctx) { GC_FREE; return errno; } \
+ LOGE(ERROR, "failed to "#rw" %s%s%s", \
+ what ? what : "", what ? " from " : "", source); \
+ GC_FREE; \
+ return errno; \
+ } \
+ if (got == 0) { \
+ if (!ctx) { GC_FREE; return EPROTO; } \
+ LOG(ERROR, zero_is_eof \
+ ? "file/stream truncated reading %s%s%s" \
+ : "file/stream write returned 0! writing %s%s%s", \
+ what ? what : "", what ? " from " : "", source); \
+ GC_FREE; \
+ return EPROTO; \
+ } \
+ sz -= got; \
+ data = (char*)data + got; \
+ } \
+ GC_FREE; \
+ return 0; \
+ }
+
+READ_WRITE_EXACTLY(read, 1, /* */)
+READ_WRITE_EXACTLY(write, 0, const)
+
+int libxl__remove_file(libxl__gc *gc, const char *path)
+{
+ for (;;) {
+ int r = unlink(path);
+ if (!r) return 0;
+ if (errno == ENOENT) return 0;
+ if (errno == EINTR) continue;
+ LOGE(ERROR, "failed to remove file %s", path);
+ return ERROR_FAIL;
+ }
+}
+
+int libxl__remove_file_or_directory(libxl__gc *gc, const char *path)
+{
+ for (;;) {
+ int r = rmdir(path);
+ if (!r) return 0;
+ if (errno == ENOENT) return 0;
+ if (errno == ENOTEMPTY) return libxl__remove_directory(gc, path);
+ if (errno == ENOTDIR) return libxl__remove_file(gc, path);
+ if (errno == EINTR) continue;
+ LOGE(ERROR, "failed to remove %s", path);
+ return ERROR_FAIL;
+ }
+}
+
+int libxl__remove_directory(libxl__gc *gc, const char *dirpath)
+{
+ int rc = 0;
+ DIR *d = 0;
+
+ d = opendir(dirpath);
+ if (!d) {
+ if (errno == ENOENT)
+ goto out;
+
+ LOGE(ERROR, "failed to opendir %s for removal", dirpath);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ struct dirent *de;
+
+ for (;;) {
+ errno = 0;
+ de = readdir(d);
+ if (!de && errno) {
+ LOGE(ERROR, "failed to readdir %s for removal", dirpath);
+ rc = ERROR_FAIL;
+ break;
+ }
+ if (!de)
+ break;
+
+ if (!strcmp(de->d_name, ".") ||
+ !strcmp(de->d_name, ".."))
+ continue;
+
+ const char *subpath = GCSPRINTF("%s/%s", dirpath, de->d_name);
+ if (libxl__remove_file_or_directory(gc, subpath))
+ rc = ERROR_FAIL;
+ }
+
+ for (;;) {
+ int r = rmdir(dirpath);
+ if (!r) break;
+ if (errno == ENOENT) goto out;
+ if (errno == EINTR) continue;
+ LOGE(ERROR, "failed to remove emptied directory %s", dirpath);
+ rc = ERROR_FAIL;
+ }
+
+ out:
+ if (d) closedir(d);
+
+ return rc;
+}
+
+int libxl_pipe(libxl_ctx *ctx, int pipes[2])
+{
+ GC_INIT(ctx);
+ int ret = 0;
+ if (pipe(pipes) < 0) {
+ LOG(ERROR, "Failed to create a pipe");
+ ret = -1;
+ }
+ GC_FREE;
+ return ret;
+}
+
+int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits)
+{
+ GC_INIT(ctx);
+ int sz;
+
+ sz = (n_bits + 7) / 8;
+ bitmap->map = libxl__calloc(NOGC, sizeof(*bitmap->map), sz);
+ bitmap->size = sz;
+
+ GC_FREE;
+ return 0;
+}
+
+void libxl_bitmap_init(libxl_bitmap *map)
+{
+ memset(map, '\0', sizeof(*map));
+}
+
+void libxl_bitmap_dispose(libxl_bitmap *map)
+{
+ if (!map)
+ return;
+
+ free(map->map);
+ map->map = NULL;
+ map->size = 0;
+}
+
+void libxl_bitmap_copy(libxl_ctx *ctx, libxl_bitmap *dptr,
+ const libxl_bitmap *sptr)
+{
+ int sz;
+
+ assert(dptr->size == sptr->size);
+ sz = dptr->size = sptr->size;
+ memcpy(dptr->map, sptr->map, sz * sizeof(*dptr->map));
+}
+
+/* This function copies X bytes from source to destination bitmap,
+ * where X is the smaller of the two sizes.
+ *
+ * If destination's size is larger than source, the extra bytes are
+ * untouched.
+ */
+void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr,
+ const libxl_bitmap *sptr)
+{
+ int sz;
+
+ sz = dptr->size < sptr->size ? dptr->size : sptr->size;
+ memcpy(dptr->map, sptr->map, sz * sizeof(*dptr->map));
+}
+
+void libxl_bitmap_copy_alloc(libxl_ctx *ctx,
+ libxl_bitmap *dptr,
+ const libxl_bitmap *sptr)
+{
+ GC_INIT(ctx);
+
+ dptr->map = libxl__calloc(NOGC, sptr->size, sizeof(*sptr->map));
+ dptr->size = sptr->size;
+ memcpy(dptr->map, sptr->map, sptr->size * sizeof(*sptr->map));
+
+ GC_FREE;
+}
+
+int libxl_bitmap_is_full(const libxl_bitmap *bitmap)
+{
+ int i;
+
+ for (i = 0; i < bitmap->size; i++)
+ if (bitmap->map[i] != (uint8_t)-1)
+ return 0;
+ return 1;
+}
+
+int libxl_bitmap_is_empty(const libxl_bitmap *bitmap)
+{
+ int i;
+
+ for (i = 0; i < bitmap->size; i++)
+ if (bitmap->map[i])
+ return 0;
+ return 1;
+}
+
+int libxl_bitmap_test(const libxl_bitmap *bitmap, int bit)
+{
+ if (bit >= bitmap->size * 8)
+ return 0;
+ return (bitmap->map[bit / 8] & (1 << (bit & 7))) ? 1 : 0;
+}
+
+void libxl_bitmap_set(libxl_bitmap *bitmap, int bit)
+{
+ if (bit >= bitmap->size * 8)
+ return;
+ bitmap->map[bit / 8] |= 1 << (bit & 7);
+}
+
+void libxl_bitmap_reset(libxl_bitmap *bitmap, int bit)
+{
+ if (bit >= bitmap->size * 8)
+ return;
+ bitmap->map[bit / 8] &= ~(1 << (bit & 7));
+}
+
+int libxl_bitmap_or(libxl_ctx *ctx, libxl_bitmap *or_map,
+ const libxl_bitmap *map1, const libxl_bitmap *map2)
+{
+ GC_INIT(ctx);
+ int rc;
+ uint32_t i;
+ const libxl_bitmap *large_map;
+ const libxl_bitmap *small_map;
+
+ if (map1->size > map2->size) {
+ large_map = map1;
+ small_map = map2;
+ } else {
+ large_map = map2;
+ small_map = map1;
+ }
+
+ rc = libxl_bitmap_alloc(ctx, or_map, large_map->size * 8);
+ if (rc)
+ goto out;
+
+ /*
+ * If bitmaps aren't the same size, their union (logical or) will
+ * be size of larger bit map. Any bit past the end of the
+ * smaller bit map, will match the larger one.
+ */
+ for (i = 0; i < small_map->size; i++)
+ or_map->map[i] = (small_map->map[i] | large_map->map[i]);
+
+ for (i = small_map->size; i < large_map->size; i++)
+ or_map->map[i] = large_map->map[i];
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_bitmap_and(libxl_ctx *ctx, libxl_bitmap *and_map,
+ const libxl_bitmap *map1, const libxl_bitmap *map2)
+{
+ GC_INIT(ctx);
+ int rc;
+ uint32_t i;
+ const libxl_bitmap *large_map;
+ const libxl_bitmap *small_map;
+
+ if (map1->size > map2->size) {
+ large_map = map1;
+ small_map = map2;
+ } else {
+ large_map = map2;
+ small_map = map1;
+ }
+
+ rc = libxl_bitmap_alloc(ctx, and_map, small_map->size * 8);
+ if (rc)
+ goto out;
+
+ /*
+ * If bitmaps aren't same size, their 'and' will be size of
+ * smaller bit map
+ */
+ for (i = 0; i < and_map->size; i++)
+ and_map->map[i] = (large_map->map[i] & small_map->map[i]);
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_bitmap_count_set(const libxl_bitmap *bitmap)
+{
+ int i, nr_set_bits = 0;
+ libxl_for_each_set_bit(i, *bitmap)
+ nr_set_bits++;
+
+ return nr_set_bits;
+}
+
+/* NB. caller is responsible for freeing the memory */
+char *libxl_bitmap_to_hex_string(libxl_ctx *ctx, const libxl_bitmap *bitmap)
+{
+ GC_INIT(ctx);
+ int i = bitmap->size;
+ char *p = libxl__zalloc(NOGC, bitmap->size * 2 + 3);
+ char *q = p;
+ strncpy(p, "0x", 3);
+ p += 2;
+ while(--i >= 0) {
+ sprintf(p, "%02x", bitmap->map[i]);
+ p += 2;
+ }
+ *p = '\0';
+ GC_FREE;
+ return q;
+}
+
+int libxl_cpu_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *cpumap, int max_cpus)
+{
+ GC_INIT(ctx);
+ int rc = 0;
+
+ if (max_cpus < 0) {
+ rc = ERROR_INVAL;
+ LOG(ERROR, "invalid number of cpus provided");
+ goto out;
+ }
+ if (max_cpus == 0)
+ max_cpus = libxl_get_max_cpus(ctx);
+ if (max_cpus < 0) {
+ LOG(ERROR, "failed to retrieve the maximum number of cpus");
+ rc = max_cpus;
+ goto out;
+ }
+ /* This can't fail: no need to check and log */
+ libxl_bitmap_alloc(ctx, cpumap, max_cpus);
+
+ out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_node_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *nodemap,
+ int max_nodes)
+{
+ GC_INIT(ctx);
+ int rc = 0;
+
+ if (max_nodes < 0) {
+ rc = ERROR_INVAL;
+ LOG(ERROR, "invalid number of nodes provided");
+ goto out;
+ }
+
+ if (max_nodes == 0)
+ max_nodes = libxl_get_max_nodes(ctx);
+ if (max_nodes < 0) {
+ LOG(ERROR, "failed to retrieve the maximum number of nodes");
+ rc = max_nodes;
+ goto out;
+ }
+ /* This can't fail: no need to check and log */
+ libxl_bitmap_alloc(ctx, nodemap, max_nodes);
+
+ out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl__count_physical_sockets(libxl__gc *gc, int *sockets)
+{
+ int rc;
+ libxl_physinfo info;
+
+ libxl_physinfo_init(&info);
+
+ rc = libxl_get_physinfo(CTX, &info);
+ if (rc)
+ return rc;
+
+ *sockets = info.nr_cpus / info.threads_per_core
+ / info.cores_per_socket;
+
+ libxl_physinfo_dispose(&info);
+ return 0;
+}
+
+int libxl_socket_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *socketmap,
+ int max_sockets)
+{
+ GC_INIT(ctx);
+ int rc = 0;
+
+ if (max_sockets < 0) {
+ rc = ERROR_INVAL;
+ LOG(ERROR, "invalid number of sockets provided");
+ goto out;
+ }
+
+ if (max_sockets == 0) {
+ rc = libxl__count_physical_sockets(gc, &max_sockets);
+ if (rc) {
+ LOGE(ERROR, "failed to get system socket count");
+ goto out;
+ }
+ }
+ /* This can't fail: no need to check and log */
+ libxl_bitmap_alloc(ctx, socketmap, max_sockets);
+
+ out:
+ GC_FREE;
+ return rc;
+
+}
+
+int libxl_get_online_socketmap(libxl_ctx *ctx, libxl_bitmap *socketmap)
+{
+ libxl_cputopology *tinfo = NULL;
+ int nr_cpus = 0, i, rc = 0;
+
+ tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
+ if (tinfo == NULL) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ libxl_bitmap_set_none(socketmap);
+ for (i = 0; i < nr_cpus; i++)
+ if (tinfo[i].socket != XEN_INVALID_SOCKET_ID
+ && !libxl_bitmap_test(socketmap, tinfo[i].socket))
+ libxl_bitmap_set(socketmap, tinfo[i].socket);
+
+ out:
+ libxl_cputopology_list_free(tinfo, nr_cpus);
+ return rc;
+}
+
+int libxl_nodemap_to_cpumap(libxl_ctx *ctx,
+ const libxl_bitmap *nodemap,
+ libxl_bitmap *cpumap)
+{
+ libxl_cputopology *tinfo = NULL;
+ int nr_cpus = 0, i, rc = 0;
+
+ tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
+ if (tinfo == NULL) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ libxl_bitmap_set_none(cpumap);
+ for (i = 0; i < nr_cpus; i++) {
+ if (libxl_bitmap_test(nodemap, tinfo[i].node))
+ libxl_bitmap_set(cpumap, i);
+ }
+ out:
+ libxl_cputopology_list_free(tinfo, nr_cpus);
+ return rc;
+}
+
+int libxl_node_to_cpumap(libxl_ctx *ctx, int node,
+ libxl_bitmap *cpumap)
+{
+ libxl_bitmap nodemap;
+ int rc = 0;
+
+ libxl_bitmap_init(&nodemap);
+
+ rc = libxl_node_bitmap_alloc(ctx, &nodemap, 0);
+ if (rc)
+ goto out;
+
+ libxl_bitmap_set_none(&nodemap);
+ libxl_bitmap_set(&nodemap, node);
+
+ rc = libxl_nodemap_to_cpumap(ctx, &nodemap, cpumap);
+
+ out:
+ libxl_bitmap_dispose(&nodemap);
+ return rc;
+}
+
+int libxl_cpumap_to_nodemap(libxl_ctx *ctx,
+ const libxl_bitmap *cpumap,
+ libxl_bitmap *nodemap)
+{
+ libxl_cputopology *tinfo = NULL;
+ int nr_cpus = 0, i, rc = 0;
+
+ tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
+ if (tinfo == NULL) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ libxl_bitmap_set_none(nodemap);
+ libxl_for_each_set_bit(i, *cpumap) {
+ if (i >= nr_cpus)
+ break;
+ libxl_bitmap_set(nodemap, tinfo[i].node);
+ }
+ out:
+ libxl_cputopology_list_free(tinfo, nr_cpus);
+ return rc;
+}
+
+int libxl_get_max_cpus(libxl_ctx *ctx)
+{
+ int max_cpus = xc_get_max_cpus(ctx->xch);
+
+ return max_cpus < 0 ? ERROR_FAIL : max_cpus;
+}
+
+int libxl_get_online_cpus(libxl_ctx *ctx)
+{
+ int online_cpus = xc_get_online_cpus(ctx->xch);
+
+ return online_cpus < 0 ? ERROR_FAIL : online_cpus;
+}
+
+int libxl_get_max_nodes(libxl_ctx *ctx)
+{
+ int max_nodes = xc_get_max_nodes(ctx->xch);
+
+ return max_nodes < 0 ? ERROR_FAIL : max_nodes;
+}
+
+int libxl__enum_from_string(const libxl_enum_string_table *t,
+ const char *s, int *e)
+{
+ if (!t) return ERROR_INVAL;
+
+ for( ; t->s; t++) {
+ if (!strcasecmp(t->s, s)) {
+ *e = t->v;
+ return 0;
+ }
+ }
+ return ERROR_FAIL;
+}
+
+void libxl_cputopology_list_free(libxl_cputopology *list, int nr)
+{
+ int i;
+ for (i = 0; i < nr; i++)
+ libxl_cputopology_dispose(&list[i]);
+ free(list);
+}
+
+void libxl_pcitopology_list_free(libxl_pcitopology *list, int nr)
+{
+ int i;
+ for (i = 0; i < nr; i++)
+ libxl_pcitopology_dispose(&list[i]);
+ free(list);
+}
+
+void libxl_numainfo_list_free(libxl_numainfo *list, int nr)
+{
+ int i;
+ for (i = 0; i < nr; i++)
+ libxl_numainfo_dispose(&list[i]);
+ free(list);
+}
+
+void libxl_vcpuinfo_list_free(libxl_vcpuinfo *list, int nr)
+{
+ int i;
+ for (i = 0; i < nr; i++)
+ libxl_vcpuinfo_dispose(&list[i]);
+ free(list);
+}
+
+int libxl__sendmsg_fds(libxl__gc *gc, int carrier,
+ const char data,
+ int nfds, const int fds[], const char *what) {
+ struct msghdr msg = { 0 };
+ struct cmsghdr *cmsg;
+ size_t spaceneeded = nfds * sizeof(fds[0]);
+ char control[CMSG_SPACE(spaceneeded)];
+ const size_t datalen = 1;
+ struct iovec iov;
+ int r;
+
+ iov.iov_base = (void*)&data;
+ iov.iov_len = datalen;
+
+ /* compose the message */
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ /* attach open fd */
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(spaceneeded);
+ memcpy(CMSG_DATA(cmsg), fds, spaceneeded);
+
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ while (1) {
+ r = sendmsg(carrier, &msg, 0);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EWOULDBLOCK) {
+ return ERROR_NOT_READY;
+ }
+ LOGE(ERROR, "failed to send fd-carrying message (%s)", what);
+ return ERROR_FAIL;
+ }
+ if (r != datalen) {
+ LOG(ERROR, "sendmsg have written %d instead of %zu",
+ r, datalen);
+ return ERROR_FAIL;
+ }
+ break;
+ };
+
+ return 0;
+}
+
+int libxl__recvmsg_fds(libxl__gc *gc, int carrier,
+ void *databuf, size_t datalen,
+ int nfds, int fds[], const char *what)
+{
+ struct msghdr msg = { 0 };
+ struct cmsghdr *cmsg;
+ size_t spaceneeded = nfds * sizeof(fds[0]);
+ char control[CMSG_SPACE(spaceneeded)];
+ struct iovec iov;
+ int r;
+
+ iov.iov_base = databuf;
+ iov.iov_len = datalen;
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ for (;;) {
+ r = recvmsg(carrier, &msg, 0);
+ if (r < 0) {
+ if (errno == EINTR) continue;
+ if (errno == EWOULDBLOCK) return -1;
+ LOGE(ERROR,"recvmsg failed (%s)", what);
+ return ERROR_FAIL;
+ }
+ if (r == 0) {
+ LOG(ERROR,"recvmsg got EOF (%s)", what);
+ return ERROR_FAIL;
+ }
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (cmsg->cmsg_len <= CMSG_LEN(0)) {
+ LOG(ERROR,"recvmsg got no control msg"
+ " when expecting fds (%s)", what);
+ return ERROR_FAIL;
+ }
+ if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ LOG(ERROR, "recvmsg got unexpected"
+ " cmsg_level %d (!=%d) or _type %d (!=%d) (%s)",
+ cmsg->cmsg_level, SOL_SOCKET,
+ cmsg->cmsg_type, SCM_RIGHTS,
+ what);
+ return ERROR_FAIL;
+ }
+ if (cmsg->cmsg_len != CMSG_LEN(spaceneeded) ||
+ msg.msg_controllen != cmsg->cmsg_len) {
+ LOG(ERROR, "recvmsg got unexpected"
+ " number of fds or extra control data"
+ " (%ld bytes' worth, expected %ld) (%s)",
+ (long)CMSG_LEN(spaceneeded), (long)cmsg->cmsg_len,
+ what);
+ int i, fd;
+ unsigned char *p;
+ for (i=0, p=CMSG_DATA(cmsg);
+ CMSG_SPACE(i * sizeof(fds[0]));
+ i++, i+=sizeof(fd)) {
+ memcpy(&fd, p, sizeof(fd));
+ close(fd);
+ }
+ return ERROR_FAIL;
+ }
+ memcpy(fds, CMSG_DATA(cmsg), spaceneeded);
+ return 0;
+ }
+}
+
+void libxl_dominfo_list_free(libxl_dominfo *list, int nr)
+{
+ int i;
+ for (i = 0; i < nr; i++)
+ libxl_dominfo_dispose(&list[i]);
+ free(list);
+}
+
+void libxl_vminfo_list_free(libxl_vminfo *list, int nr)
+{
+ int i;
+ for (i = 0; i < nr; i++)
+ libxl_vminfo_dispose(&list[i]);
+ free(list);
+}
+
+void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nr)
+{
+ int i;
+ for (i = 0; i < nr; i++)
+ libxl_cpupoolinfo_dispose(&list[i]);
+ free(list);
+}
+
+int libxl_domid_valid_guest(uint32_t domid)
+{
+ /* returns 1 if the value _could_ be a valid guest domid, 0 otherwise
+ * does not check whether the domain actually exists */
+ return domid > 0 && domid < DOMID_FIRST_RESERVED;
+}
+
+void libxl_string_copy(libxl_ctx *ctx, char **dst, char * const*src)
+{
+ GC_INIT(ctx);
+
+ if (*src)
+ *dst = libxl__strdup(NOGC, *src);
+ else
+ *dst = NULL;
+
+ GC_FREE;
+}
+
+/*
+ * Fill @buf with @len random bytes.
+ */
+int libxl__random_bytes(libxl__gc *gc, uint8_t *buf, size_t len)
+{
+ static const char *dev = "/dev/urandom";
+ int fd;
+ int ret;
+
+ fd = open(dev, O_RDONLY);
+ if (fd < 0) {
+ LOGE(ERROR, "failed to open \"%s\"", dev);
+ return ERROR_FAIL;
+ }
+ ret = libxl_fd_set_cloexec(CTX, fd, 1);
+ if (ret) {
+ close(fd);
+ return ERROR_FAIL;
+ }
+
+ ret = libxl_read_exactly(CTX, fd, buf, len, dev, NULL);
+
+ close(fd);
+
+ return ret;
+}
+
+int libxl__prepare_sockaddr_un(libxl__gc *gc,
+ struct sockaddr_un *un, const char *path,
+ const char *what)
+{
+ if (sizeof(un->sun_path) - 1 <= strlen(path)) {
+ LOG(ERROR, "UNIX socket path '%s' is too long for %s", path, what);
+ LOG(DEBUG, "Path must be less than %zu bytes", sizeof(un->sun_path) - 1);
+ return ERROR_INVAL;
+ }
+ memset(un, 0, sizeof(struct sockaddr_un));
+ un->sun_family = AF_UNIX;
+ strncpy(un->sun_path, path, sizeof(un->sun_path) - 1);
+ return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2008,2010 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#if defined(__linux__)
+
+int libxl_uuid_is_nil(const libxl_uuid *uuid)
+{
+ return uuid_is_null(uuid->uuid);
+}
+
+void libxl_uuid_generate(libxl_uuid *uuid)
+{
+ uuid_generate(uuid->uuid);
+}
+
+int libxl_uuid_from_string(libxl_uuid *uuid, const char *in)
+{
+ return uuid_parse(in, uuid->uuid);
+}
+
+void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst,
+ const libxl_uuid *src)
+{
+ uuid_copy(dst->uuid, src->uuid);
+}
+
+void libxl_uuid_clear(libxl_uuid *uuid)
+{
+ uuid_clear(uuid->uuid);
+}
+
+int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2)
+{
+ return uuid_compare(uuid1->uuid, uuid2->uuid);
+}
+
+const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid)
+{
+ return uuid->uuid;
+}
+
+uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid)
+{
+ return uuid->uuid;
+}
+
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+
+int libxl_uuid_is_nil(const libxl_uuid *uuid)
+{
+ uint32_t status;
+ uuid_t nat_uuid;
+
+ uuid_dec_be(uuid->uuid, &nat_uuid);
+
+ return uuid_is_nil(&nat_uuid, &status);
+}
+
+void libxl_uuid_generate(libxl_uuid *uuid)
+{
+ uint32_t status;
+ uuid_t nat_uuid;
+
+ uuid_create(&nat_uuid, &status);
+ assert(status == uuid_s_ok);
+
+ uuid_enc_be(uuid->uuid, &nat_uuid);
+}
+
+#ifdef __FreeBSD__
+int libxl_uuid_from_string(libxl_uuid *uuid, const char *in)
+{
+ uint32_t status;
+ uuid_t nat_uuid;
+
+ uuid_from_string(in, &nat_uuid, &status);
+ if (status != uuid_s_ok)
+ return ERROR_FAIL;
+ uuid_enc_be(uuid->uuid, &nat_uuid);
+
+ return 0;
+}
+#else
+#define LIBXL__UUID_PTRS(uuid) &uuid[0], &uuid[1], &uuid[2], &uuid[3], \
+ &uuid[4], &uuid[5], &uuid[6], &uuid[7], \
+ &uuid[8], &uuid[9], &uuid[10],&uuid[11], \
+ &uuid[12],&uuid[13],&uuid[14],&uuid[15]
+int libxl_uuid_from_string(libxl_uuid *uuid, const char *in)
+{
+ if ( sscanf(in, LIBXL_UUID_FMT, LIBXL__UUID_PTRS(uuid->uuid)) != sizeof(uuid->uuid) )
+ return -1;
+ return 0;
+}
+#undef LIBXL__UUID_PTRS
+#endif
+
+void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst,
+ const libxl_uuid *src)
+{
+ memcpy(&dst->uuid, &src->uuid, sizeof(dst->uuid));
+}
+
+void libxl_uuid_clear(libxl_uuid *uuid)
+{
+ memset(&uuid->uuid, 0, sizeof(uuid->uuid));
+}
+
+#ifdef __FreeBSD__
+int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2)
+{
+ uuid_t nat_uuid1, nat_uuid2;
+
+ uuid_dec_be(uuid1->uuid, &nat_uuid1);
+ uuid_dec_be(uuid2->uuid, &nat_uuid2);
+
+ return uuid_compare(&nat_uuid1, &nat_uuid2, NULL);
+}
+#else
+int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2)
+{
+ return memcmp(uuid1->uuid, uuid2->uuid, sizeof(uuid1->uuid));
+}
+#endif
+
+const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid)
+{
+
+ return uuid->uuid;
+}
+
+uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid)
+{
+
+ return uuid->uuid;
+}
+#else
+
+#error "Please update libxl_uuid.c for your OS"
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2016 EPAM Systems Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_internal.h"
+
+#include <xen/io/displif.h>
+
+static int libxl__device_vdispl_setdefault(libxl__gc *gc, uint32_t domid,
+ libxl_device_vdispl *vdispl,
+ bool hotplug)
+{
+ return libxl__resolve_domid(gc, vdispl->backend_domname,
+ &vdispl->backend_domid);
+}
+
+static int libxl__vdispl_from_xenstore(libxl__gc *gc, const char *libxl_path,
+ libxl_devid devid,
+ libxl_device_vdispl *vdispl)
+{
+ const char *be_path;
+ int rc;
+
+ vdispl->devid = devid;
+ rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path),
+ &be_path);
+ if (rc) return rc;
+
+ return libxl__backendpath_parse_domid(gc, be_path, &vdispl->backend_domid);
+}
+
+static void libxl__update_config_vdispl(libxl__gc *gc,
+ libxl_device_vdispl *dst,
+ libxl_device_vdispl *src)
+{
+ dst->devid = src->devid;
+ dst->be_alloc = src->be_alloc;
+}
+
+static int libxl_device_vdispl_compare(const libxl_device_vdispl *d1,
+ const libxl_device_vdispl *d2)
+{
+ return COMPARE_DEVID(d1, d2);
+}
+
+static void libxl__device_vdispl_add(libxl__egc *egc, uint32_t domid,
+ libxl_device_vdispl *vdispl,
+ libxl__ao_device *aodev)
+{
+ libxl__device_add_async(egc, domid, &libxl__vdispl_devtype, vdispl, aodev);
+}
+
+static int libxl__set_xenstore_vdispl(libxl__gc *gc, uint32_t domid,
+ libxl_device_vdispl *vdispl,
+ flexarray_t *back, flexarray_t *front,
+ flexarray_t *ro_front)
+{
+ int i;
+
+ flexarray_append_pair(ro_front, XENDISPL_FIELD_BE_ALLOC,
+ GCSPRINTF("%d", vdispl->be_alloc));
+
+ for (i = 0; i < vdispl->num_connectors; i++) {
+ flexarray_append_pair(ro_front, GCSPRINTF("%d/"XENDISPL_FIELD_RESOLUTION, i),
+ GCSPRINTF("%d"XENDISPL_RESOLUTION_SEPARATOR"%d", vdispl->connectors[i].width,
+ vdispl->connectors[i].height));
+ flexarray_append_pair(ro_front, GCSPRINTF("%d/"XENDISPL_FIELD_UNIQUE_ID, i),
+ vdispl->connectors[i].unique_id);
+ }
+
+ return 0;
+}
+
+static int libxl__device_vdispl_getconnectors(libxl_ctx *ctx,
+ const char *path,
+ libxl_vdisplinfo *info)
+{
+ GC_INIT(ctx);
+ char *connector = NULL;
+ char *connector_path;
+ int i, rc;
+
+ info->num_connectors = 0;
+
+ connector_path = GCSPRINTF("%s/%d", path, info->num_connectors);
+
+ while ((connector = xs_read(ctx->xsh, XBT_NULL, connector_path, NULL)) !=
+ NULL) {
+ free(connector);
+ connector_path = GCSPRINTF("%s/%d", path, ++info->num_connectors);
+ }
+
+ info->connectors = libxl__calloc(NOGC, info->num_connectors,
+ sizeof(*info->connectors));
+
+ for (i = 0; i < info->num_connectors; i++) {
+ char *value;
+ char *value_path;
+
+ value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_UNIQUE_ID, path, i);
+ info->connectors[i].unique_id = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
+ if (info->connectors[i].unique_id == NULL) { rc = ERROR_FAIL; goto out; }
+
+ value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_RESOLUTION, path, i);
+ value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
+ if (value == NULL) { rc = ERROR_FAIL; goto out; }
+
+ rc = sscanf(value, "%u"XENDISPL_RESOLUTION_SEPARATOR"%u", &info->connectors[i].width,
+ &info->connectors[i].height);
+ free(value);
+
+ if (rc != 2) {
+ rc = ERROR_FAIL; goto out;
+ }
+
+ value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_REQ_RING_REF, path, i);
+ value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
+ info->connectors[i].req_rref = value ? strtoul(value, NULL, 10) : -1;
+ free(value);
+
+ value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_REQ_CHANNEL, path, i);
+ value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
+ info->connectors[i].req_evtch = value ? strtoul(value, NULL, 10) : -1;
+ free(value);
+
+ value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_EVT_RING_REF, path, i);
+ value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
+ info->connectors[i].evt_rref = value ? strtoul(value, NULL, 10) : -1;
+ free(value);
+
+ value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_EVT_CHANNEL, path, i);
+ value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
+ info->connectors[i].evt_evtch = value ? strtoul(value, NULL, 10) : -1;
+ free(value);
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+int libxl_device_vdispl_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_vdispl *vdispl,
+ libxl_vdisplinfo *info)
+{
+ GC_INIT(ctx);
+ char *libxl_path, *devpath;
+ char *val;
+ int rc;
+
+ libxl_vdisplinfo_init(info);
+ info->devid = vdispl->devid;
+
+ devpath = libxl__domain_device_frontend_path(gc, domid, info->devid,
+ LIBXL__DEVICE_KIND_VDISPL);
+ libxl_path = libxl__domain_device_libxl_path(gc, domid, info->devid,
+ LIBXL__DEVICE_KIND_VDISPL);
+
+ info->backend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path),
+ NULL);
+ if (!info->backend) { rc = ERROR_FAIL; goto out; }
+
+ rc = libxl__backendpath_parse_domid(gc, info->backend, &info->backend_id);
+ if (rc) goto out;
+
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", devpath));
+ info->state = val ? strtoul(val, NULL, 10) : -1;
+
+ info->frontend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/frontend", libxl_path),
+ NULL);
+ info->frontend_id = domid;
+
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/"XENDISPL_FIELD_BE_ALLOC, devpath));
+ info->be_alloc = val ? strtoul(val, NULL, 10) : 0;
+
+ rc = libxl__device_vdispl_getconnectors(ctx, devpath, info);
+ if (rc) goto out;
+
+ rc = 0;
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+static LIBXL_DEFINE_DEVICE_FROM_TYPE(vdispl)
+static LIBXL_DEFINE_UPDATE_DEVID(vdispl)
+static LIBXL_DEFINE_DEVICES_ADD(vdispl)
+
+LIBXL_DEFINE_DEVID_TO_DEVICE(vdispl)
+LIBXL_DEFINE_DEVICE_ADD(vdispl)
+LIBXL_DEFINE_DEVICE_REMOVE(vdispl)
+LIBXL_DEFINE_DEVICE_LIST(vdispl)
+
+DEFINE_DEVICE_TYPE_STRUCT(vdispl, VDISPL,
+ .update_config = (device_update_config_fn_t)libxl__update_config_vdispl,
+ .from_xenstore = (device_from_xenstore_fn_t)libxl__vdispl_from_xenstore,
+ .set_xenstore_config = (device_set_xenstore_config_fn_t)
+ libxl__set_xenstore_vdispl
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2016 EPAM Systems Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_internal.h"
+
+#include <xen/io/kbdif.h>
+
+static int libxl__device_vkb_setdefault(libxl__gc *gc, uint32_t domid,
+ libxl_device_vkb *vkb, bool hotplug)
+{
+ if (vkb->backend_type == LIBXL_VKB_BACKEND_UNKNOWN) {
+ vkb->backend_type = LIBXL_VKB_BACKEND_QEMU;
+ }
+
+ return libxl__resolve_domid(gc, vkb->backend_domname, &vkb->backend_domid);
+}
+
+static int libxl__device_vkb_dm_needed(void *e, uint32_t domid)
+{
+ libxl_device_vkb *elem = e;
+
+ return elem->backend_type == LIBXL_VKB_BACKEND_QEMU;
+}
+
+static int libxl__set_xenstore_vkb(libxl__gc *gc, uint32_t domid,
+ libxl_device_vkb *vkb,
+ flexarray_t *back, flexarray_t *front,
+ flexarray_t *ro_front)
+{
+ if (vkb->unique_id) {
+ flexarray_append_pair(back, XENKBD_FIELD_UNIQUE_ID, vkb->unique_id);
+ }
+
+ if (vkb->feature_disable_keyboard) {
+ flexarray_append_pair(back, XENKBD_FIELD_FEAT_DSBL_KEYBRD,
+ GCSPRINTF("%u", vkb->feature_disable_keyboard));
+ }
+
+ if (vkb->feature_disable_pointer) {
+ flexarray_append_pair(back, XENKBD_FIELD_FEAT_DSBL_POINTER,
+ GCSPRINTF("%u", vkb->feature_disable_pointer));
+ }
+
+ if (vkb->feature_abs_pointer) {
+ flexarray_append_pair(back, XENKBD_FIELD_FEAT_ABS_POINTER,
+ GCSPRINTF("%u", vkb->feature_abs_pointer));
+ }
+
+ if (vkb->feature_raw_pointer) {
+ flexarray_append_pair(back, XENKBD_FIELD_FEAT_RAW_POINTER,
+ GCSPRINTF("%u", vkb->feature_raw_pointer));
+ }
+
+ if (vkb->feature_multi_touch) {
+ flexarray_append_pair(back, XENKBD_FIELD_FEAT_MTOUCH,
+ GCSPRINTF("%u", vkb->feature_multi_touch));
+ flexarray_append_pair(back, XENKBD_FIELD_MT_WIDTH,
+ GCSPRINTF("%u", vkb->multi_touch_width));
+ flexarray_append_pair(back, XENKBD_FIELD_MT_HEIGHT,
+ GCSPRINTF("%u", vkb->multi_touch_height));
+ flexarray_append_pair(back, XENKBD_FIELD_MT_NUM_CONTACTS,
+ GCSPRINTF("%u", vkb->multi_touch_num_contacts));
+ }
+
+ if (vkb->width) {
+ flexarray_append_pair(back, XENKBD_FIELD_WIDTH,
+ GCSPRINTF("%u", vkb->width));
+ }
+
+ if (vkb->height) {
+ flexarray_append_pair(back, XENKBD_FIELD_HEIGHT,
+ GCSPRINTF("%u", vkb->height));
+ }
+
+ return 0;
+}
+
+static int libxl__vkb_from_xenstore(libxl__gc *gc, const char *libxl_path,
+ libxl_devid devid,
+ libxl_device_vkb *vkb)
+{
+ const char *be_path, *fe_path, *tmp;
+ libxl__device dev;
+ int rc;
+
+ vkb->devid = devid;
+
+ rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path),
+ &be_path);
+ if (rc) goto out;
+
+ rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+ GCSPRINTF("%s/frontend", libxl_path),
+ &fe_path);
+ if (rc) goto out;
+
+ rc = libxl__backendpath_parse_domid(gc, be_path, &vkb->backend_domid);
+ if (rc) goto out;
+
+ rc = libxl__parse_backend_path(gc, be_path, &dev);
+ if (rc) goto out;
+
+ vkb->backend_type = dev.backend_kind == LIBXL__DEVICE_KIND_VINPUT ?
+ LIBXL_VKB_BACKEND_LINUX : LIBXL_VKB_BACKEND_QEMU;
+
+ vkb->unique_id = xs_read(CTX->xsh, XBT_NULL, GCSPRINTF("%s/"XENKBD_FIELD_UNIQUE_ID, be_path), NULL);
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENKBD_FIELD_FEAT_DSBL_KEYBRD,
+ be_path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ vkb->feature_disable_keyboard = strtoul(tmp, NULL, 0);
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENKBD_FIELD_FEAT_DSBL_POINTER,
+ be_path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ vkb->feature_disable_pointer = strtoul(tmp, NULL, 0);
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENKBD_FIELD_FEAT_ABS_POINTER,
+ be_path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ vkb->feature_abs_pointer = strtoul(tmp, NULL, 0);
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENKBD_FIELD_FEAT_RAW_POINTER,
+ be_path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ vkb->feature_raw_pointer = strtoul(tmp, NULL, 0);
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENKBD_FIELD_FEAT_MTOUCH,
+ be_path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ vkb->feature_multi_touch = strtoul(tmp, NULL, 0);
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENKBD_FIELD_MT_WIDTH,
+ be_path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ vkb->multi_touch_width = strtoul(tmp, NULL, 0);
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENKBD_FIELD_MT_HEIGHT,
+ be_path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ vkb->multi_touch_height = strtoul(tmp, NULL, 0);
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENKBD_FIELD_MT_NUM_CONTACTS,
+ be_path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ vkb->multi_touch_num_contacts = strtoul(tmp, NULL, 0);
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENKBD_FIELD_WIDTH,
+ be_path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ vkb->width = strtoul(tmp, NULL, 0);
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENKBD_FIELD_HEIGHT,
+ be_path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ vkb->height = strtoul(tmp, NULL, 0);
+ }
+
+ rc = 0;
+
+out:
+
+ return rc;
+}
+
+static int libxl__device_from_vkb(libxl__gc *gc, uint32_t domid,
+ libxl_device_vkb *type, libxl__device *device)
+{
+ device->backend_devid = type->devid;
+ device->backend_domid = type->backend_domid;
+ device->backend_kind = type->backend_type == LIBXL_VKB_BACKEND_LINUX ?
+ LIBXL__DEVICE_KIND_VINPUT : LIBXL__DEVICE_KIND_VKBD;
+ device->devid = type->devid;
+ device->domid = domid;
+ device->kind = LIBXL__DEVICE_KIND_VKBD;
+
+ return 0;
+}
+
+int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb,
+ const libxl_asyncop_how *ao_how)
+{
+ AO_CREATE(ctx, domid, ao_how);
+ int rc;
+
+ rc = libxl__device_add(gc, domid, &libxl__vkb_devtype, vkb);
+ if (rc) {
+ LOGD(ERROR, domid, "Unable to add vkb device");
+ goto out;
+ }
+
+out:
+ libxl__ao_complete(egc, ao, rc);
+ return AO_INPROGRESS;
+}
+
+int libxl_devid_to_device_vkb(libxl_ctx *ctx, uint32_t domid,
+ int devid, libxl_device_vkb *vkb)
+{
+ GC_INIT(ctx);
+
+ libxl_device_vkb *vkbs = NULL;
+ int n, i;
+ int rc;
+
+ libxl_device_vkb_init(vkb);
+
+ vkbs = libxl__device_list(gc, &libxl__vkb_devtype, domid, &n);
+
+ if (!vkbs) { rc = ERROR_NOTFOUND; goto out; }
+
+ for (i = 0; i < n; ++i) {
+ if (devid == vkbs[i].devid) {
+ libxl_device_vkb_copy(ctx, vkb, &vkbs[i]);
+ rc = 0;
+ goto out;
+ }
+ }
+
+ rc = ERROR_NOTFOUND;
+
+out:
+
+ if (vkbs)
+ libxl__device_list_free(&libxl__vkb_devtype, vkbs, n);
+
+ GC_FREE;
+ return rc;
+}
+
+int libxl_device_vkb_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_vkb *vkb,
+ libxl_vkbinfo *info)
+{
+ GC_INIT(ctx);
+ char *libxl_path, *dompath, *devpath;
+ char *val;
+ int rc;
+
+ libxl_vkbinfo_init(info);
+ dompath = libxl__xs_get_dompath(gc, domid);
+ info->devid = vkb->devid;
+
+ devpath = libxl__domain_device_frontend_path(gc, domid, info->devid,
+ LIBXL__DEVICE_KIND_VKBD);
+ libxl_path = libxl__domain_device_libxl_path(gc, domid, info->devid,
+ LIBXL__DEVICE_KIND_VKBD);
+
+ info->backend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path),
+ NULL);
+ if (!info->backend) { rc = ERROR_FAIL; goto out; }
+
+ rc = libxl__backendpath_parse_domid(gc, info->backend, &info->backend_id);
+ if (rc) goto out;
+
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", devpath));
+ info->state = val ? strtoul(val, NULL, 10) : -1;
+
+ info->frontend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/frontend", libxl_path),
+ NULL);
+ info->frontend_id = domid;
+
+ val = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENKBD_FIELD_EVT_CHANNEL, devpath));
+ info->evtch = val ? strtoul(val, NULL, 10) : -1;
+
+ val = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENKBD_FIELD_RING_GREF, devpath));
+ info->rref = val ? strtoul(val, NULL, 10) : -1;
+
+ rc = 0;
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+static LIBXL_DEFINE_UPDATE_DEVID(vkb)
+
+#define libxl__add_vkbs NULL
+#define libxl_device_vkb_compare NULL
+
+LIBXL_DEFINE_DEVICE_LIST(vkb)
+LIBXL_DEFINE_DEVICE_REMOVE(vkb)
+
+DEFINE_DEVICE_TYPE_STRUCT(vkb, VKBD,
+ .skip_attach = 1,
+ .dm_needed = libxl__device_vkb_dm_needed,
+ .set_xenstore_config = (device_set_xenstore_config_fn_t)
+ libxl__set_xenstore_vkb,
+ .from_xenstore = (device_from_xenstore_fn_t)libxl__vkb_from_xenstore
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2014 Citrix Ltd.
+ * Author Wei Liu <wei.liu2@citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+#include "libxl_osdeps.h" /* must come before any other headers */
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+#include <stdlib.h>
+
+#include <xenctrl_dom.h>
+
+bool libxl__vnuma_configured(const libxl_domain_build_info *b_info)
+{
+ return b_info->num_vnuma_nodes != 0;
+}
+
+/* Sort vmemranges in ascending order with "start" */
+static int compare_vmemrange(const void *a, const void *b)
+{
+ const xen_vmemrange_t *x = a, *y = b;
+ if (x->start < y->start)
+ return -1;
+ if (x->start > y->start)
+ return 1;
+ return 0;
+}
+
+/* Check if a vcpu has an hard (or soft) affinity set in such
+ * a way that it does not match the pnode to which the vcpu itself
+ * is assigned to.
+ */
+static int check_vnuma_affinity(libxl__gc *gc,
+ unsigned int vcpu,
+ unsigned int pnode,
+ unsigned int num_affinity,
+ const libxl_bitmap *affinity,
+ const char *kind)
+{
+ libxl_bitmap nodemap;
+ int rc = 0;
+
+ libxl_bitmap_init(&nodemap);
+
+ rc = libxl_node_bitmap_alloc(CTX, &nodemap, 0);
+ if (rc) {
+ LOG(ERROR, "Can't allocate nodemap");
+ goto out;
+ }
+
+ rc = libxl_cpumap_to_nodemap(CTX, affinity, &nodemap);
+ if (rc) {
+ LOG(ERROR, "Can't convert Vcpu %d affinity to nodemap", vcpu);
+ goto out;
+ }
+
+ if (libxl_bitmap_count_set(&nodemap) != 1 ||
+ !libxl_bitmap_test(&nodemap, pnode))
+ LOG(WARN, "Vcpu %d %s affinity and vnuma info mismatch", vcpu, kind);
+
+out:
+ libxl_bitmap_dispose(&nodemap);
+ return rc;
+}
+
+/* Check if vNUMA configuration is valid:
+ * 1. all pnodes inside vnode_to_pnode array are valid
+ * 2. each vcpu belongs to one and only one vnode
+ * 3. each vmemrange is valid and doesn't overlap with any other
+ * 4. local distance cannot be larger than remote distance
+ *
+ * Check also, if any hard or soft affinity is specified, whether
+ * they match with the vNUMA related bits (namely vcpus to vnodes
+ * mappings and vnodes to pnodes association). If that does not
+ * hold, however, just print a warning, as that has "only"
+ * performance implications.
+ */
+int libxl__vnuma_config_check(libxl__gc *gc,
+ const libxl_domain_build_info *b_info,
+ const libxl__domain_build_state *state)
+{
+ int nr_nodes = 0, rc = ERROR_VNUMA_CONFIG_INVALID;
+ unsigned int i, j;
+ libxl_numainfo *ninfo = NULL;
+ uint64_t total_memkb = 0;
+ libxl_bitmap cpumap;
+ libxl_vnode_info *v;
+
+ libxl_bitmap_init(&cpumap);
+
+ /* Check pnode specified is valid */
+ ninfo = libxl_get_numainfo(CTX, &nr_nodes);
+ if (!ninfo) {
+ LOG(ERROR, "libxl_get_numainfo failed");
+ goto out;
+ }
+
+ for (i = 0; i < b_info->num_vnuma_nodes; i++) {
+ uint32_t pnode;
+
+ v = &b_info->vnuma_nodes[i];
+ pnode = v->pnode;
+
+ /* The pnode specified is not valid? */
+ if (pnode >= nr_nodes) {
+ LOG(ERROR, "Invalid pnode %"PRIu32" specified", pnode);
+ goto out;
+ }
+
+ total_memkb += v->memkb;
+ }
+
+ if (total_memkb != b_info->max_memkb) {
+ LOG(ERROR, "Amount of memory mismatch (0x%"PRIx64" != 0x%"PRIx64")",
+ total_memkb, b_info->max_memkb);
+ goto out;
+ }
+
+ /* Check vcpu mapping */
+ libxl_cpu_bitmap_alloc(CTX, &cpumap, b_info->max_vcpus);
+ for (i = 0; i < b_info->num_vnuma_nodes; i++) {
+ v = &b_info->vnuma_nodes[i];
+ libxl_for_each_set_bit(j, v->vcpus) {
+ if (!libxl_bitmap_test(&cpumap, j))
+ libxl_bitmap_set(&cpumap, j);
+ else {
+ LOG(ERROR, "Vcpu %d assigned more than once", j);
+ goto out;
+ }
+ }
+ }
+
+ for (i = 0; i < b_info->max_vcpus; i++) {
+ if (!libxl_bitmap_test(&cpumap, i)) {
+ LOG(ERROR, "Vcpu %d is not assigned to any vnode", i);
+ goto out;
+ }
+ }
+
+ /* Check whether vcpu affinity (if any) matches vnuma configuration */
+ for (i = 0; i < b_info->num_vnuma_nodes; i++) {
+ v = &b_info->vnuma_nodes[i];
+ libxl_for_each_set_bit(j, v->vcpus) {
+ if (b_info->num_vcpu_hard_affinity > j)
+ check_vnuma_affinity(gc, j, v->pnode,
+ b_info->num_vcpu_hard_affinity,
+ &b_info->vcpu_hard_affinity[j],
+ "hard");
+ if (b_info->num_vcpu_soft_affinity > j)
+ check_vnuma_affinity(gc, j, v->pnode,
+ b_info->num_vcpu_soft_affinity,
+ &b_info->vcpu_soft_affinity[j],
+ "soft");
+ }
+ }
+
+ /* Check vmemranges */
+ qsort(state->vmemranges, state->num_vmemranges, sizeof(xen_vmemrange_t),
+ compare_vmemrange);
+
+ for (i = 0; i < state->num_vmemranges; i++) {
+ if (state->vmemranges[i].end < state->vmemranges[i].start) {
+ LOG(ERROR, "Vmemrange end < start");
+ goto out;
+ }
+ }
+
+ for (i = 0; i < state->num_vmemranges - 1; i++) {
+ if (state->vmemranges[i].end > state->vmemranges[i+1].start) {
+ LOG(ERROR,
+ "Vmemranges overlapped, 0x%"PRIx64"-0x%"PRIx64", 0x%"PRIx64"-0x%"PRIx64,
+ state->vmemranges[i].start, state->vmemranges[i].end,
+ state->vmemranges[i+1].start, state->vmemranges[i+1].end);
+ goto out;
+ }
+ }
+
+ /* Check vdistances */
+ for (i = 0; i < b_info->num_vnuma_nodes; i++) {
+ uint32_t local_distance, remote_distance;
+
+ v = &b_info->vnuma_nodes[i];
+ local_distance = v->distances[i];
+
+ for (j = 0; j < v->num_distances; j++) {
+ if (i == j) continue;
+ remote_distance = v->distances[j];
+ if (local_distance > remote_distance) {
+ LOG(ERROR,
+ "Distance from %u to %u smaller than %u's local distance",
+ i, j, i);
+ goto out;
+ }
+ }
+ }
+
+ rc = 0;
+out:
+ libxl_numainfo_list_free(ninfo, nr_nodes);
+ libxl_bitmap_dispose(&cpumap);
+ return rc;
+}
+
+int libxl__vnuma_build_vmemrange_pv_generic(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_build_info *b_info,
+ libxl__domain_build_state *state)
+{
+ int i;
+ uint64_t next;
+ xen_vmemrange_t *v = NULL;
+
+ /* Generate one vmemrange for each virtual node. */
+ GCREALLOC_ARRAY(v, b_info->num_vnuma_nodes);
+ next = 0;
+ for (i = 0; i < b_info->num_vnuma_nodes; i++) {
+ libxl_vnode_info *p = &b_info->vnuma_nodes[i];
+
+ v[i].start = next;
+ v[i].end = next + (p->memkb << 10);
+ v[i].flags = 0;
+ v[i].nid = i;
+
+ next = v[i].end;
+ }
+
+ state->vmemranges = v;
+ state->num_vmemranges = i;
+
+ return 0;
+}
+
+/* Build vmemranges for PV guest */
+int libxl__vnuma_build_vmemrange_pv(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_build_info *b_info,
+ libxl__domain_build_state *state)
+{
+ assert(state->vmemranges == NULL);
+ return libxl__arch_vnuma_build_vmemrange(gc, domid, b_info, state);
+}
+
+/* Build vmemranges for HVM guest */
+int libxl__vnuma_build_vmemrange_hvm(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_build_info *b_info,
+ libxl__domain_build_state *state,
+ struct xc_dom_image *dom)
+{
+ uint64_t hole_start, hole_end, next;
+ int nid, nr_vmemrange;
+ xen_vmemrange_t *vmemranges;
+ int rc;
+
+ /* Derive vmemranges from vnode size and memory hole.
+ *
+ * Guest physical address space layout:
+ * [0, hole_start) [hole_start, hole_end) [hole_end, highmem_end)
+ */
+ hole_start = dom->lowmem_end < dom->mmio_start ?
+ dom->lowmem_end : dom->mmio_start;
+ hole_end = (dom->mmio_start + dom->mmio_size) > (1ULL << 32) ?
+ (dom->mmio_start + dom->mmio_size) : (1ULL << 32);
+
+ assert(state->vmemranges == NULL);
+
+ next = 0;
+ nr_vmemrange = 0;
+ vmemranges = NULL;
+ for (nid = 0; nid < b_info->num_vnuma_nodes; nid++) {
+ libxl_vnode_info *p = &b_info->vnuma_nodes[nid];
+ uint64_t remaining_bytes = p->memkb << 10;
+
+ /* Consider video ram belongs to vnode 0 */
+ if (nid == 0) {
+ if (p->memkb < b_info->video_memkb) {
+ LOGD(ERROR, domid, "vnode 0 too small to contain video ram");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+ remaining_bytes -= (b_info->video_memkb << 10);
+ }
+
+ while (remaining_bytes > 0) {
+ uint64_t count = remaining_bytes;
+
+ if (next >= hole_start && next < hole_end)
+ next = hole_end;
+ if ((next < hole_start) && (next + remaining_bytes >= hole_start))
+ count = hole_start - next;
+
+ GCREALLOC_ARRAY(vmemranges, nr_vmemrange+1);
+ vmemranges[nr_vmemrange].start = next;
+ vmemranges[nr_vmemrange].end = next + count;
+ vmemranges[nr_vmemrange].flags = 0;
+ vmemranges[nr_vmemrange].nid = nid;
+
+ nr_vmemrange++;
+ remaining_bytes -= count;
+ next += count;
+ }
+ }
+
+ state->vmemranges = vmemranges;
+ state->num_vmemranges = nr_vmemrange;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2016 EPAM Systems Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_internal.h"
+
+#include <xen/io/sndif.h>
+
+static int libxl__device_vsnd_setdefault(libxl__gc *gc, uint32_t domid,
+ libxl_device_vsnd *vsnd,
+ bool hotplug)
+{
+ return libxl__resolve_domid(gc, vsnd->backend_domname,
+ &vsnd->backend_domid);
+}
+
+static int libxl__device_from_vsnd(libxl__gc *gc, uint32_t domid,
+ libxl_device_vsnd *vsnd,
+ libxl__device *device)
+{
+ device->backend_devid = vsnd->devid;
+ device->backend_domid = vsnd->backend_domid;
+ device->backend_kind = LIBXL__DEVICE_KIND_VSND;
+ device->devid = vsnd->devid;
+ device->domid = domid;
+ device->kind = LIBXL__DEVICE_KIND_VSND;
+
+ return 0;
+}
+
+static int libxl__sample_rates_from_string(libxl__gc *gc, const char *str,
+ libxl_vsnd_params *params)
+{
+ char *tmp = libxl__strdup(gc, str);
+
+ params->num_sample_rates = 0;
+ params->sample_rates = NULL;
+
+ char *p = strtok(tmp, " ,");
+
+ while (p != NULL) {
+ params->sample_rates = libxl__realloc(NOGC, params->sample_rates,
+ sizeof(*params->sample_rates) *
+ (params->num_sample_rates + 1));
+ params->sample_rates[params->num_sample_rates++] = strtoul(p, NULL, 0);
+ p = strtok(NULL, " ,");
+ }
+
+ return 0;
+}
+
+static int libxl__sample_formats_from_string(libxl__gc *gc, const char *str,
+ libxl_vsnd_params *params)
+{
+ int rc;
+ char *tmp = libxl__strdup(gc, str);
+
+ params->num_sample_formats = 0;
+ params->sample_formats = NULL;
+
+ char *p = strtok(tmp, " ,");
+
+ while (p != NULL) {
+ params->sample_formats = libxl__realloc(NOGC, params->sample_formats,
+ sizeof(*params->sample_formats) *
+ (params->num_sample_formats + 1));
+
+ libxl_vsnd_pcm_format format;
+
+ rc = libxl_vsnd_pcm_format_from_string(p, &format);
+ if (rc) goto out;
+
+ params->sample_formats[params->num_sample_formats++] = format;
+ p = strtok(NULL, " ,");
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static int libxl__params_from_xenstore(libxl__gc *gc, const char *path,
+ libxl_vsnd_params *params)
+{
+ const char *tmp;
+ int rc;
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENSND_FIELD_SAMPLE_RATES,
+ path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ rc = libxl__sample_rates_from_string(gc, tmp, params);
+ if (rc) goto out;
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENSND_FIELD_SAMPLE_FORMATS,
+ path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ rc = libxl__sample_formats_from_string(gc, tmp, params);
+ if (rc) goto out;
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENSND_FIELD_CHANNELS_MIN,
+ path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ params->channels_min = strtoul(tmp, NULL, 0);
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENSND_FIELD_CHANNELS_MAX,
+ path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ params->channels_max = strtoul(tmp, NULL, 0);
+ }
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENSND_FIELD_BUFFER_SIZE,
+ path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ params->buffer_size = strtoul(tmp, NULL, 0);
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static int libxl__stream_from_xenstore(libxl__gc *gc, const char *path,
+ libxl_vsnd_stream *stream)
+{
+ const char *tmp;
+ int rc;
+
+ stream->unique_id = xs_read(CTX->xsh, XBT_NULL,
+ GCSPRINTF("%s/"XENSND_FIELD_STREAM_UNIQUE_ID,
+ path), NULL);
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/"XENSND_FIELD_TYPE,
+ path), &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ libxl_vsnd_stream_type type;
+
+ rc = libxl_vsnd_stream_type_from_string(tmp, &type);
+ if (rc) goto out;
+
+ stream->type = type;
+ }
+
+ rc = libxl__params_from_xenstore(gc, path, &stream->params);
+ if (rc) goto out;
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static int libxl__pcm_from_xenstore(libxl__gc *gc, const char *path,
+ libxl_vsnd_pcm *pcm)
+{
+ const char *tmp;
+ int rc;
+
+ pcm->name = xs_read(CTX->xsh, XBT_NULL,
+ GCSPRINTF("%s/"XENSND_FIELD_DEVICE_NAME, path), NULL);
+
+ rc = libxl__params_from_xenstore(gc, path, &pcm->params);
+ if (rc) goto out;
+
+ pcm->streams = NULL;
+ pcm->num_vsnd_streams = 0;
+
+ do {
+ char *stream_path = GCSPRINTF("%s/%d", path, pcm->num_vsnd_streams);
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL, stream_path, &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ pcm->streams = libxl__realloc(NOGC, pcm->streams,
+ sizeof(*pcm->streams) *
+ (++pcm->num_vsnd_streams));
+
+ libxl_vsnd_stream_init(&pcm->streams[pcm->num_vsnd_streams - 1]);
+
+ rc = libxl__stream_from_xenstore(gc, stream_path,
+ &pcm->streams[pcm->num_vsnd_streams
+ - 1]);
+ if (rc) goto out;
+ }
+ } while (tmp);
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static int libxl__vsnd_from_xenstore(libxl__gc *gc, const char *libxl_path,
+ libxl_devid devid,
+ libxl_device_vsnd *vsnd)
+{
+ const char *tmp;
+ const char *fe_path;
+ int rc;
+
+ vsnd->devid = devid;
+ rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path),
+ &tmp);
+ if (rc) goto out;
+
+ rc = libxl__backendpath_parse_domid(gc, tmp, &vsnd->backend_domid);
+ if (rc) goto out;
+
+ rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+ GCSPRINTF("%s/frontend", libxl_path),
+ &fe_path);
+ if (rc) goto out;
+
+ vsnd->short_name = xs_read(CTX->xsh, XBT_NULL,
+ GCSPRINTF("%s/"XENSND_FIELD_VCARD_SHORT_NAME,
+ fe_path), NULL);
+
+ vsnd->long_name = xs_read(CTX->xsh, XBT_NULL,
+ GCSPRINTF("%s/"XENSND_FIELD_VCARD_LONG_NAME,
+ fe_path), NULL);
+
+ rc = libxl__params_from_xenstore(gc, fe_path, &vsnd->params);
+ if (rc) goto out;
+
+ vsnd->pcms = NULL;
+ vsnd->num_vsnd_pcms = 0;
+
+ do {
+ char *pcm_path = GCSPRINTF("%s/%d", fe_path, vsnd->num_vsnd_pcms);
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL, pcm_path, &tmp);
+ if (rc) goto out;
+
+ if (tmp) {
+ vsnd->pcms = libxl__realloc(NOGC, vsnd->pcms, sizeof(*vsnd->pcms) *
+ (++vsnd->num_vsnd_pcms));
+
+ libxl_vsnd_pcm_init(&vsnd->pcms[vsnd->num_vsnd_pcms - 1]);
+
+ rc = libxl__pcm_from_xenstore(gc, pcm_path,
+ &vsnd->pcms[vsnd->num_vsnd_pcms - 1]);
+ if (rc) goto out;
+ }
+ } while (tmp);
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static void libxl__update_config_vsnd(libxl__gc *gc,
+ libxl_device_vsnd *dst,
+ libxl_device_vsnd *src)
+{
+ dst->devid = src->devid;
+}
+
+static int libxl_device_vsnd_compare(const libxl_device_vsnd *d1,
+ const libxl_device_vsnd *d2)
+{
+ return COMPARE_DEVID(d1, d2);
+}
+
+static void libxl__device_vsnd_add(libxl__egc *egc, uint32_t domid,
+ libxl_device_vsnd *vsnd,
+ libxl__ao_device *aodev)
+{
+ libxl__device_add_async(egc, domid, &libxl__vsnd_devtype, vsnd, aodev);
+}
+
+static unsigned int libxl__rates_to_str_vsnd(char *str, uint32_t *sample_rates,
+ int num_sample_rates)
+{
+ unsigned int len;
+ int i;
+
+ len = 0;
+
+ if (num_sample_rates == 0) goto out;
+
+ for (i = 0; i < num_sample_rates - 1; i++) {
+ if (str) {
+ len += sprintf(&str[len], "%u,", sample_rates[i]);
+ } else {
+ len += snprintf(NULL, 0, "%u,", sample_rates[i]);
+ }
+ }
+
+ if (str) {
+ len += sprintf(&str[len], "%u", sample_rates[i]);
+ } else {
+ len += snprintf(NULL, 0, "%u", sample_rates[i]);
+ }
+
+out:
+ return len;
+}
+
+static unsigned int libxl__formats_to_str_vsnd(char *str,
+ libxl_vsnd_pcm_format *sample_formats,
+ int num_sample_formats)
+{
+ unsigned int len;
+ int i;
+
+ len = 0;
+
+ if (num_sample_formats == 0) goto out;
+
+ for (i = 0; i < num_sample_formats - 1; i++) {
+ if (str) {
+ len += sprintf(&str[len], "%s,",
+ libxl_vsnd_pcm_format_to_string(sample_formats[i]));
+ } else {
+ len += snprintf(NULL, 0, "%s,",
+ libxl_vsnd_pcm_format_to_string(sample_formats[i]));
+ }
+ }
+
+ if (str) {
+ len += sprintf(&str[len], "%s",
+ libxl_vsnd_pcm_format_to_string(sample_formats[i]));
+ } else {
+ len += snprintf(NULL, 0, "%s",
+ libxl_vsnd_pcm_format_to_string(sample_formats[i]));
+ }
+
+out:
+ return len;
+}
+
+static int libxl__set_params_vsnd(libxl__gc *gc, char *path,
+ libxl_vsnd_params *params, flexarray_t *front)
+{
+ char *buffer;
+ int len;
+ int rc;
+
+ if (params->sample_rates) {
+ /* calculate required string size */
+ len = libxl__rates_to_str_vsnd(NULL, params->sample_rates,
+ params->num_sample_rates);
+
+ if (len) {
+ buffer = libxl__malloc(gc, len + 1);
+
+ libxl__rates_to_str_vsnd(buffer, params->sample_rates,
+ params->num_sample_rates);
+ rc = flexarray_append_pair(front,
+ GCSPRINTF("%s"XENSND_FIELD_SAMPLE_RATES,
+ path), buffer);
+ if (rc) goto out;
+ }
+ }
+
+ if (params->sample_formats) {
+ /* calculate required string size */
+ len = libxl__formats_to_str_vsnd(NULL, params->sample_formats,
+ params->num_sample_formats);
+
+ if (len) {
+ buffer = libxl__malloc(gc, len + 1);
+
+ libxl__formats_to_str_vsnd(buffer, params->sample_formats,
+ params->num_sample_formats);
+ rc = flexarray_append_pair(front,
+ GCSPRINTF("%s"XENSND_FIELD_SAMPLE_FORMATS,
+ path), buffer);
+ if (rc) goto out;
+ }
+ }
+
+ if (params->channels_min) {
+ rc = flexarray_append_pair(front,
+ GCSPRINTF("%s"XENSND_FIELD_CHANNELS_MIN, path),
+ GCSPRINTF("%u", params->channels_min));
+ if (rc) goto out;
+ }
+
+ if (params->channels_max) {
+ rc = flexarray_append_pair(front,
+ GCSPRINTF("%s"XENSND_FIELD_CHANNELS_MAX, path),
+ GCSPRINTF("%u", params->channels_max));
+ if (rc) goto out;
+ }
+
+ if (params->buffer_size) {
+ rc = flexarray_append_pair(front,
+ GCSPRINTF("%s"XENSND_FIELD_BUFFER_SIZE, path),
+ GCSPRINTF("%u", params->buffer_size));
+ if (rc) goto out;
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static int libxl__set_streams_vsnd(libxl__gc *gc, char *path,
+ libxl_vsnd_stream *streams,
+ int num_streams, flexarray_t *front)
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < num_streams; i++) {
+ rc = flexarray_append_pair(front,
+ GCSPRINTF("%s%d/"XENSND_FIELD_STREAM_UNIQUE_ID, path, i),
+ streams[i].unique_id);
+ if (rc) goto out;
+
+ const char *type = libxl_vsnd_stream_type_to_string(streams[i].type);
+
+ if (type) {
+ rc = flexarray_append_pair(front,
+ GCSPRINTF("%s%d/"XENSND_FIELD_TYPE, path, i),
+ (char *)type);
+ if (rc) goto out;
+ }
+
+ rc = libxl__set_params_vsnd(gc, GCSPRINTF("%s%d/", path, i),
+ &streams[i].params, front);
+ if (rc) goto out;
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static int libxl__set_pcms_vsnd(libxl__gc *gc, libxl_vsnd_pcm *pcms,
+ int num_pcms, flexarray_t *front)
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < num_pcms; i++) {
+ if (pcms[i].name) {
+ rc = flexarray_append_pair(front,
+ GCSPRINTF("%d/"XENSND_FIELD_DEVICE_NAME, i),
+ pcms[i].name);
+ if (rc) goto out;
+ }
+
+ char *path = GCSPRINTF("%d/", i);
+
+ rc = libxl__set_params_vsnd(gc, path, &pcms[i].params, front);
+ if (rc) goto out;
+
+ rc = libxl__set_streams_vsnd(gc, path, pcms[i].streams,
+ pcms[i].num_vsnd_streams, front);
+ if (rc) goto out;
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static int libxl__set_xenstore_vsnd(libxl__gc *gc, uint32_t domid,
+ libxl_device_vsnd *vsnd,
+ flexarray_t *back, flexarray_t *front,
+ flexarray_t *ro_front)
+{
+ int rc;
+
+ if (vsnd->long_name) {
+ rc = flexarray_append_pair(front, XENSND_FIELD_VCARD_LONG_NAME,
+ vsnd->long_name);
+ if (rc) goto out;
+ }
+
+ if (vsnd->short_name) {
+ rc = flexarray_append_pair(front, XENSND_FIELD_VCARD_SHORT_NAME,
+ vsnd->short_name);
+ if (rc) goto out;
+ }
+
+ rc = libxl__set_params_vsnd(gc, "", &vsnd->params, front);
+ if (rc) goto out;
+
+ rc = libxl__set_pcms_vsnd(gc, vsnd->pcms, vsnd->num_vsnd_pcms, front);
+ if (rc) goto out;
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static int libxl__device_stream_getinfo(libxl__gc *gc, const char *path,
+ libxl_vsnd_pcm* pcm,
+ libxl_pcminfo *info)
+{
+ const char *tmp;
+ int i;
+ int rc;
+
+ info->num_vsnd_streams = pcm->num_vsnd_streams;
+ info->streams = libxl__malloc(NOGC, sizeof(*info->streams) * info->num_vsnd_streams);
+
+ for (i = 0; i < info->num_vsnd_streams; i++)
+ {
+ libxl_streaminfo_init(&info->streams[i]);
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/%d/"XENSND_FIELD_RING_REF,
+ path, i), &tmp);
+ if (rc) goto out;
+
+ info->streams[i].req_rref = tmp ? strtoul(tmp, NULL, 10) : -1;
+
+ rc = libxl__xs_read_checked(gc, XBT_NULL,
+ GCSPRINTF("%s/%d/"XENSND_FIELD_EVT_CHNL,
+ path, i), &tmp);
+ if (rc) goto out;
+
+ info->streams[i].req_evtch = tmp ? strtoul(tmp, NULL, 10) : -1;
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static int libxl__device_pcm_getinfo(libxl__gc *gc, const char *path,
+ const libxl_device_vsnd *vsnd,
+ libxl_vsndinfo *info)
+{
+ int i;
+ int rc;
+
+ info->num_vsnd_pcms = vsnd->num_vsnd_pcms;
+ info->pcms = libxl__malloc(NOGC, sizeof(*info->pcms) * info->num_vsnd_pcms);
+
+ for (i = 0; i < info->num_vsnd_pcms; i++)
+ {
+ libxl_pcminfo_init(&info->pcms[i]);
+
+ rc = libxl__device_stream_getinfo(gc, GCSPRINTF("%s/%d", path, i),
+ &vsnd->pcms[i], &info->pcms[i]);
+ if (rc) goto out;
+ }
+
+ rc = 0;
+
+out:
+ return rc;
+}
+
+int libxl_device_vsnd_getinfo(libxl_ctx *ctx, uint32_t domid,
+ const libxl_device_vsnd *vsnd,
+ libxl_vsndinfo *info)
+{
+ GC_INIT(ctx);
+ char *libxl_path, *dompath, *devpath;
+ const char *val;
+ int rc;
+
+ libxl_vsndinfo_init(info);
+ dompath = libxl__xs_get_dompath(gc, domid);
+ info->devid = vsnd->devid;
+
+ devpath = libxl__domain_device_frontend_path(gc, domid, info->devid,
+ LIBXL__DEVICE_KIND_VSND);
+ libxl_path = libxl__domain_device_libxl_path(gc, domid, info->devid,
+ LIBXL__DEVICE_KIND_VSND);
+
+ info->backend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path), NULL);
+
+ rc = libxl__backendpath_parse_domid(gc, info->backend, &info->backend_id);
+ if (rc) goto out;
+
+ val = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/state", devpath), NULL);
+
+ info->state = val ? strtoul(val, NULL, 10) : -1;
+
+ info->frontend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/frontend", libxl_path), NULL);
+
+ info->frontend_id = domid;
+
+ rc = libxl__device_pcm_getinfo(gc, devpath, vsnd, info);
+ if (rc) goto out;
+
+ rc = 0;
+
+out:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_devid_to_device_vsnd(libxl_ctx *ctx, uint32_t domid,
+ int devid, libxl_device_vsnd *vsnd)
+{
+ GC_INIT(ctx);
+
+ libxl_device_vsnd *vsnds = NULL;
+ int n, i;
+ int rc;
+
+ libxl_device_vsnd_init(vsnd);
+
+ vsnds = libxl__device_list(gc, &libxl__vsnd_devtype, domid, &n);
+
+ if (!vsnds) { rc = ERROR_NOTFOUND; goto out; }
+
+ for (i = 0; i < n; ++i) {
+ if (devid == vsnds[i].devid) {
+ libxl_device_vsnd_copy(ctx, vsnd, &vsnds[i]);
+ rc = 0;
+ goto out;
+ }
+ }
+
+ rc = ERROR_NOTFOUND;
+
+out:
+ if (vsnds)
+ libxl__device_list_free(&libxl__vsnd_devtype, vsnds, n);
+
+ GC_FREE;
+ return rc;
+}
+
+static LIBXL_DEFINE_UPDATE_DEVID(vsnd)
+static LIBXL_DEFINE_DEVICES_ADD(vsnd)
+
+LIBXL_DEFINE_DEVICE_ADD(vsnd)
+LIBXL_DEFINE_DEVICE_REMOVE(vsnd)
+LIBXL_DEFINE_DEVICE_LIST(vsnd)
+
+DEFINE_DEVICE_TYPE_STRUCT(vsnd, VSND,
+ .update_config = (device_update_config_fn_t) libxl__update_config_vsnd,
+ .from_xenstore = (device_from_xenstore_fn_t) libxl__vsnd_from_xenstore,
+ .set_xenstore_config = (device_set_xenstore_config_fn_t)
+ libxl__set_xenstore_vsnd
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2016 SUSE Linux GmbH
+ * Author Juergen Gross <jgross@suse.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+static int libxl__device_vtpm_setdefault(libxl__gc *gc, uint32_t domid,
+ libxl_device_vtpm *vtpm, bool hotplug)
+{
+ int rc;
+ if (libxl_uuid_is_nil(&vtpm->uuid)) {
+ libxl_uuid_generate(&vtpm->uuid);
+ }
+ rc = libxl__resolve_domid(gc, vtpm->backend_domname, &vtpm->backend_domid);
+ return rc;
+}
+
+static void libxl__update_config_vtpm(libxl__gc *gc, libxl_device_vtpm *dst,
+ libxl_device_vtpm *src)
+{
+ dst->devid = src->devid;
+ libxl_uuid_copy(CTX, &dst->uuid, &src->uuid);
+}
+
+static int libxl__set_xenstore_vtpm(libxl__gc *gc, uint32_t domid,
+ libxl_device_vtpm *vtpm,
+ flexarray_t *back, flexarray_t *front,
+ flexarray_t *ro_front)
+{
+ flexarray_append_pair(back, "handle", GCSPRINTF("%d", vtpm->devid));
+ flexarray_append_pair(back, "uuid",
+ GCSPRINTF(LIBXL_UUID_FMT,
+ LIBXL_UUID_BYTES(vtpm->uuid)));
+ flexarray_append_pair(back, "resume", "False");
+
+ flexarray_append_pair(front, "handle", GCSPRINTF("%d", vtpm->devid));
+
+ return 0;
+}
+
+static void libxl__device_vtpm_add(libxl__egc *egc, uint32_t domid,
+ libxl_device_vtpm *vtpm,
+ libxl__ao_device *aodev)
+{
+ libxl__device_add_async(egc, domid, &libxl__vtpm_devtype, vtpm, aodev);
+}
+
+static int libxl__vtpm_from_xenstore(libxl__gc *gc, const char *libxl_path,
+ libxl_devid devid,
+ libxl_device_vtpm *vtpm)
+{
+ int rc;
+ const char *be_path;
+ char *uuid;
+
+ vtpm->devid = devid;
+
+ rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path),
+ &be_path);
+ if (rc) return rc;
+
+ rc = libxl__backendpath_parse_domid(gc, be_path, &vtpm->backend_domid);
+ if (rc) return rc;
+
+ uuid = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/uuid", be_path));
+ if (uuid) {
+ if(libxl_uuid_from_string(&(vtpm->uuid), uuid)) {
+ LOGD(ERROR, vtpm->backend_domid, "%s/uuid is a malformed uuid?? "
+ "(%s) Probably a bug!!\n", be_path, uuid);
+ return ERROR_FAIL;
+ }
+ }
+
+ return 0;
+}
+
+int libxl_device_vtpm_getinfo(libxl_ctx *ctx,
+ uint32_t domid,
+ const libxl_device_vtpm *vtpm,
+ libxl_vtpminfo *vtpminfo)
+{
+ GC_INIT(ctx);
+ char *libxl_path, *vtpmpath;
+ char *val;
+ int rc = 0;
+
+ libxl_vtpminfo_init(vtpminfo);
+ vtpminfo->devid = vtpm->devid;
+
+ vtpmpath = libxl__domain_device_frontend_path(gc, domid, vtpminfo->devid,
+ LIBXL__DEVICE_KIND_VTPM);
+ libxl_path = libxl__domain_device_libxl_path(gc, domid, vtpminfo->devid,
+ LIBXL__DEVICE_KIND_VTPM);
+ vtpminfo->backend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/backend", libxl_path), NULL);
+ if (!vtpminfo->backend) {
+ goto err;
+ }
+
+ rc = libxl__backendpath_parse_domid(gc, vtpminfo->backend,
+ &vtpminfo->backend_id);
+ if (rc) goto exit;
+
+ val = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/state", vtpmpath));
+ vtpminfo->state = val ? strtoul(val, NULL, 10) : -1;
+
+ val = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/event-channel", vtpmpath));
+ vtpminfo->evtch = val ? strtoul(val, NULL, 10) : -1;
+
+ val = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/ring-ref", vtpmpath));
+ vtpminfo->rref = val ? strtoul(val, NULL, 10) : -1;
+
+ vtpminfo->frontend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/frontend", libxl_path), NULL);
+ vtpminfo->frontend_id = domid;
+
+ val = libxl__xs_read(gc, XBT_NULL,
+ GCSPRINTF("%s/uuid", libxl_path));
+ if(val == NULL) {
+ LOGD(ERROR, domid, "%s/uuid does not exist!", vtpminfo->backend);
+ goto err;
+ }
+ if(libxl_uuid_from_string(&(vtpminfo->uuid), val)) {
+ LOGD(ERROR, domid,
+ "%s/uuid is a malformed uuid?? (%s) Probably a bug!\n",
+ vtpminfo->backend, val);
+ goto err;
+ }
+
+ goto exit;
+err:
+ rc = ERROR_FAIL;
+exit:
+ GC_FREE;
+ return rc;
+}
+
+int libxl_devid_to_device_vtpm(libxl_ctx *ctx,
+ uint32_t domid,
+ int devid,
+ libxl_device_vtpm *vtpm)
+{
+ GC_INIT(ctx);
+ libxl_device_vtpm *vtpms;
+ int nb, i;
+ int rc;
+
+ vtpms = libxl__device_list(gc, &libxl__vtpm_devtype, domid, &nb);
+ if (!vtpms)
+ return ERROR_FAIL;
+
+ libxl_device_vtpm_init(vtpm);
+ rc = 1;
+ for (i = 0; i < nb; ++i) {
+ if(devid == vtpms[i].devid) {
+ vtpm->backend_domid = vtpms[i].backend_domid;
+ vtpm->devid = vtpms[i].devid;
+ libxl_uuid_copy(ctx, &vtpm->uuid, &vtpms[i].uuid);
+ rc = 0;
+ break;
+ }
+ }
+
+ libxl__device_list_free(&libxl__vtpm_devtype, vtpms, nb);
+ GC_FREE;
+ return rc;
+}
+
+static int libxl_device_vtpm_compare(const libxl_device_vtpm *d1,
+ const libxl_device_vtpm *d2)
+{
+ return COMPARE_DEVID(d1, d2);
+}
+
+int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
+ libxl_uuid* uuid, libxl_device_vtpm *vtpm)
+{
+ GC_INIT(ctx);
+ libxl_device_vtpm *vtpms;
+ int nb, i;
+ int rc;
+
+ vtpms = libxl__device_list(gc, &libxl__vtpm_devtype, domid, &nb);
+ if (!vtpms)
+ return ERROR_FAIL;
+
+ memset(vtpm, 0, sizeof (libxl_device_vtpm));
+ rc = 1;
+ for (i = 0; i < nb; ++i) {
+ if(!libxl_uuid_compare(uuid, &vtpms[i].uuid)) {
+ vtpm->backend_domid = vtpms[i].backend_domid;
+ vtpm->devid = vtpms[i].devid;
+ libxl_uuid_copy(ctx, &vtpm->uuid, &vtpms[i].uuid);
+ rc = 0;
+ break;
+ }
+ }
+
+ libxl__device_list_free(&libxl__vtpm_devtype, vtpms, nb);
+ GC_FREE;
+ return rc;
+}
+
+static void libxl_device_vtpm_update_config(libxl__gc *gc, void *d, void *s)
+{
+ libxl__update_config_vtpm(gc, d, s);
+}
+
+static LIBXL_DEFINE_UPDATE_DEVID(vtpm)
+static LIBXL_DEFINE_DEVICE_FROM_TYPE(vtpm)
+static LIBXL_DEFINE_DEVICES_ADD(vtpm)
+
+LIBXL_DEFINE_DEVICE_ADD(vtpm)
+LIBXL_DEFINE_DEVICE_REMOVE(vtpm)
+LIBXL_DEFINE_DEVICE_LIST(vtpm)
+
+DEFINE_DEVICE_TYPE_STRUCT(vtpm, VTPM,
+ .update_config = libxl_device_vtpm_update_config,
+ .from_xenstore = (device_from_xenstore_fn_t)libxl__vtpm_from_xenstore,
+ .set_xenstore_config = (device_set_xenstore_config_fn_t)
+ libxl__set_xenstore_vtpm,
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+
--- /dev/null
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+
+#include <xenctrl_dom.h>
+
+int libxl__arch_domain_prepare_config(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ struct xen_domctl_createdomain *config)
+{
+ switch(d_config->c_info.type) {
+ case LIBXL_DOMAIN_TYPE_HVM:
+ config->arch.emulation_flags = (XEN_X86_EMU_ALL & ~XEN_X86_EMU_VPCI);
+ break;
+ case LIBXL_DOMAIN_TYPE_PVH:
+ config->arch.emulation_flags = XEN_X86_EMU_LAPIC;
+ break;
+ case LIBXL_DOMAIN_TYPE_PV:
+ config->arch.emulation_flags = 0;
+ break;
+ default:
+ abort();
+ }
+
+ return 0;
+}
+
+int libxl__arch_domain_save_config(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ libxl__domain_build_state *state,
+ const struct xen_domctl_createdomain *config)
+{
+ return 0;
+}
+
+static const char *e820_names(int type)
+{
+ switch (type) {
+ case E820_RAM: return "RAM";
+ case E820_RESERVED: return "Reserved";
+ case E820_ACPI: return "ACPI";
+ case E820_NVS: return "ACPI NVS";
+ case E820_UNUSABLE: return "Unusable";
+ default: break;
+ }
+ return "Unknown";
+}
+
+static int e820_sanitize(libxl__gc *gc, struct e820entry src[],
+ uint32_t *nr_entries,
+ unsigned long map_limitkb,
+ unsigned long balloon_kb)
+{
+ uint64_t delta_kb = 0, start = 0, start_kb = 0, last = 0, ram_end;
+ uint32_t i, idx = 0, nr;
+ struct e820entry e820[E820MAX];
+
+ if (!src || !map_limitkb || !nr_entries)
+ return ERROR_INVAL;
+
+ nr = *nr_entries;
+ if (!nr)
+ return ERROR_INVAL;
+
+ if (nr > E820MAX)
+ return ERROR_NOMEM;
+
+ /* Weed out anything under 1MB */
+ for (i = 0; i < nr; i++) {
+ if (src[i].addr > 0x100000)
+ continue;
+
+ src[i].type = 0;
+ src[i].size = 0;
+ src[i].addr = -1ULL;
+ }
+
+ /* Find the lowest and highest entry in E820, skipping over
+ * undesired entries. */
+ start = -1ULL;
+ last = 0;
+ for (i = 0; i < nr; i++) {
+ if ((src[i].type == E820_RAM) ||
+ (src[i].type == E820_UNUSABLE) ||
+ (src[i].type == 0))
+ continue;
+
+ start = src[i].addr < start ? src[i].addr : start;
+ last = src[i].addr + src[i].size > last ?
+ src[i].addr + src[i].size > last : last;
+ }
+ if (start > 1024)
+ start_kb = start >> 10;
+
+ /* Add the memory RAM region for the guest */
+ e820[idx].addr = 0;
+ e820[idx].size = (uint64_t)map_limitkb << 10;
+ e820[idx].type = E820_RAM;
+
+ /* .. and trim if neccessary */
+ if (start_kb && map_limitkb > start_kb) {
+ delta_kb = map_limitkb - start_kb;
+ if (delta_kb)
+ e820[idx].size -= (uint64_t)(delta_kb << 10);
+ }
+ /* Note: We don't touch balloon_kb here. Will add it at the end. */
+ ram_end = e820[idx].addr + e820[idx].size;
+ idx ++;
+
+ LOG(DEBUG, "Memory: %"PRIu64"kB End of RAM: " \
+ "0x%"PRIx64" (PFN) Delta: %"PRIu64"kB, PCI start: %"PRIu64"kB " \
+ "(0x%"PRIx64" PFN), Balloon %"PRIu64"kB\n", (uint64_t)map_limitkb,
+ ram_end >> 12, delta_kb, start_kb ,start >> 12,
+ (uint64_t)balloon_kb);
+
+
+ /* This whole code below is to guard against if the Intel IGD is passed into
+ * the guest. If we don't pass in IGD, this whole code can be ignored.
+ *
+ * The reason for this code is that Intel boxes fill their E820 with
+ * E820_RAM amongst E820_RESERVED and we can't just ditch those E820_RAM.
+ * That is b/c any "gaps" in the E820 is considered PCI I/O space by
+ * Linux and it would be utilized by the Intel IGD as I/O space while
+ * in reality it was an RAM region.
+ *
+ * What this means is that we have to walk the E820 and for any region
+ * that is RAM and below 4GB and above ram_end, needs to change its type
+ * to E820_UNUSED. We also need to move some of the E820_RAM regions if
+ * the overlap with ram_end. */
+ for (i = 0; i < nr; i++) {
+ uint64_t end = src[i].addr + src[i].size;
+
+ /* We don't care about E820_UNUSABLE, but we need to
+ * change the type to zero b/c the loop after this
+ * sticks E820_UNUSABLE on the guest's E820 but ignores
+ * the ones with type zero. */
+ if ((src[i].type == E820_UNUSABLE) ||
+ /* Any region that is within the "RAM region" can
+ * be safely ditched. */
+ (end < ram_end)) {
+ src[i].type = 0;
+ continue;
+ }
+
+ /* Look only at RAM regions. */
+ if (src[i].type != E820_RAM)
+ continue;
+
+ /* We only care about RAM regions below 4GB. */
+ if (src[i].addr >= (1ULL<<32))
+ continue;
+
+ /* E820_RAM overlaps with our RAM region. Move it */
+ if (src[i].addr < ram_end) {
+ uint64_t delta;
+
+ src[i].type = E820_UNUSABLE;
+ delta = ram_end - src[i].addr;
+ /* The end < ram_end should weed this out */
+ if (src[i].size < delta)
+ src[i].type = 0;
+ else {
+ src[i].size -= delta;
+ src[i].addr = ram_end;
+ }
+ if (src[i].addr + src[i].size != end) {
+ /* We messed up somewhere */
+ src[i].type = 0;
+ LOGE(ERROR, "Computed E820 wrongly. Continuing on.");
+ }
+ }
+ /* Lastly, convert the RAM to UNSUABLE. Look in the Linux kernel
+ at git commit 2f14ddc3a7146ea4cd5a3d1ecd993f85f2e4f948
+ "xen/setup: Inhibit resource API from using System RAM E820
+ gaps as PCI mem gaps" for full explanation. */
+ if (end > ram_end)
+ src[i].type = E820_UNUSABLE;
+ }
+
+ /* Check if there is a region between ram_end and start. */
+ if (start > ram_end) {
+ int add_unusable = 1;
+ for (i = 0; i < nr && add_unusable; i++) {
+ if (src[i].type != E820_UNUSABLE)
+ continue;
+ if (ram_end != src[i].addr)
+ continue;
+ if (start != src[i].addr + src[i].size) {
+ /* there is one, adjust it */
+ src[i].size = start - src[i].addr;
+ }
+ add_unusable = 0;
+ }
+ /* .. and if not present, add it in. This is to guard against
+ the Linux guest assuming that the gap between the end of
+ RAM region and the start of the E820_[ACPI,NVS,RESERVED]
+ is PCI I/O space. Which it certainly is _not_. */
+ if (add_unusable) {
+ e820[idx].type = E820_UNUSABLE;
+ e820[idx].addr = ram_end;
+ e820[idx].size = start - ram_end;
+ idx++;
+ }
+ }
+ /* Almost done: copy them over, ignoring the undesireable ones */
+ for (i = 0; i < nr; i++) {
+ if ((src[i].type == E820_RAM) ||
+ (src[i].type == 0))
+ continue;
+
+ e820[idx].type = src[i].type;
+ e820[idx].addr = src[i].addr;
+ e820[idx].size = src[i].size;
+ idx++;
+ }
+ /* At this point we have the mapped RAM + E820 entries from src. */
+ if (balloon_kb || delta_kb) {
+ /* and if we truncated the RAM region, then add it to the end. */
+ e820[idx].type = E820_RAM;
+ e820[idx].addr = (uint64_t)(1ULL << 32) > last ?
+ (uint64_t)(1ULL << 32) : last;
+ /* also add the balloon memory to the end. */
+ e820[idx].size = (uint64_t)(delta_kb << 10) +
+ (uint64_t)(balloon_kb << 10);
+ idx++;
+
+ }
+ nr = idx;
+
+ for (i = 0; i < nr; i++) {
+ LOG(DEBUG, ":\t[%"PRIx64" -> %"PRIx64"] %s", e820[i].addr >> 12,
+ (e820[i].addr + e820[i].size) >> 12, e820_names(e820[i].type));
+ }
+
+ /* Done: copy the sanitized version. */
+ *nr_entries = nr;
+ memcpy(src, e820, nr * sizeof(struct e820entry));
+ return 0;
+}
+
+static int e820_host_sanitize(libxl__gc *gc,
+ libxl_domain_build_info *b_info,
+ struct e820entry map[],
+ uint32_t *nr)
+{
+ int rc;
+
+ rc = xc_get_machine_memory_map(CTX->xch, map, *nr);
+ if (rc < 0)
+ return ERROR_FAIL;
+
+ *nr = rc;
+
+ rc = e820_sanitize(gc, map, nr, b_info->target_memkb,
+ (b_info->max_memkb - b_info->target_memkb) +
+ b_info->u.pv.slack_memkb);
+ return rc;
+}
+
+static int libxl__e820_alloc(libxl__gc *gc, uint32_t domid,
+ libxl_domain_config *d_config)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ int rc;
+ uint32_t nr;
+ struct e820entry map[E820MAX];
+ libxl_domain_build_info *b_info;
+
+ if (d_config == NULL || d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV)
+ return ERROR_INVAL;
+
+ b_info = &d_config->b_info;
+ if (!libxl_defbool_val(b_info->u.pv.e820_host))
+ return ERROR_INVAL;
+
+ nr = E820MAX;
+ rc = e820_host_sanitize(gc, b_info, map, &nr);
+ if (rc)
+ return ERROR_FAIL;
+
+ rc = xc_domain_set_memory_map(ctx->xch, domid, map, nr);
+
+ if (rc < 0)
+ return ERROR_FAIL;
+
+ return 0;
+}
+
+static unsigned long timer_mode(const libxl_domain_build_info *info)
+{
+ const libxl_timer_mode mode = info->timer_mode;
+ assert(mode >= LIBXL_TIMER_MODE_DELAY_FOR_MISSED_TICKS &&
+ mode <= LIBXL_TIMER_MODE_ONE_MISSED_TICK_PENDING);
+ return ((unsigned long)mode);
+}
+
+static int hvm_set_viridian_features(libxl__gc *gc, uint32_t domid,
+ const libxl_domain_build_info *info)
+{
+ libxl_bitmap enlightenments;
+ libxl_viridian_enlightenment v;
+ uint64_t mask = 0;
+
+ libxl_bitmap_init(&enlightenments);
+ libxl_bitmap_alloc(CTX, &enlightenments,
+ LIBXL_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE_WIDTH);
+
+ if (libxl_defbool_val(info->u.hvm.viridian)) {
+ /* Enable defaults */
+ libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE);
+ libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_FREQ);
+ libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT);
+ libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST);
+ libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_CRASH_CTL);
+ }
+
+ libxl_for_each_set_bit(v, info->u.hvm.viridian_enable) {
+ if (libxl_bitmap_test(&info->u.hvm.viridian_disable, v)) {
+ LOG(ERROR, "%s group both enabled and disabled",
+ libxl_viridian_enlightenment_to_string(v));
+ goto err;
+ }
+ if (libxl_viridian_enlightenment_to_string(v)) /* check validity */
+ libxl_bitmap_set(&enlightenments, v);
+ }
+
+ libxl_for_each_set_bit(v, info->u.hvm.viridian_disable)
+ if (libxl_viridian_enlightenment_to_string(v)) /* check validity */
+ libxl_bitmap_reset(&enlightenments, v);
+
+ /* The base set is a pre-requisite for all others */
+ if (!libxl_bitmap_is_empty(&enlightenments) &&
+ !libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE)) {
+ LOG(ERROR, "base group not enabled");
+ goto err;
+ }
+
+ libxl_for_each_set_bit(v, enlightenments)
+ LOG(DETAIL, "%s group enabled", libxl_viridian_enlightenment_to_string(v));
+
+ if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE)) {
+ mask |= HVMPV_base_freq;
+
+ if (!libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_FREQ))
+ mask |= HVMPV_no_freq;
+ }
+
+ if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT))
+ mask |= HVMPV_time_ref_count;
+
+ if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_REFERENCE_TSC))
+ mask |= HVMPV_reference_tsc;
+
+ if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_HCALL_REMOTE_TLB_FLUSH))
+ mask |= HVMPV_hcall_remote_tlb_flush;
+
+ if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST))
+ mask |= HVMPV_apic_assist;
+
+ if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_CRASH_CTL))
+ mask |= HVMPV_crash_ctl;
+
+ if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_SYNIC))
+ mask |= HVMPV_synic;
+
+ if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_STIMER))
+ mask |= HVMPV_time_ref_count | HVMPV_synic | HVMPV_stimer;
+
+ if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_HCALL_IPI))
+ mask |= HVMPV_hcall_ipi;
+
+ if (mask != 0 &&
+ xc_hvm_param_set(CTX->xch,
+ domid,
+ HVM_PARAM_VIRIDIAN,
+ mask) != 0) {
+ LOGE(ERROR, "Couldn't set viridian feature mask (0x%"PRIx64")", mask);
+ goto err;
+ }
+
+ libxl_bitmap_dispose(&enlightenments);
+ return 0;
+
+err:
+ libxl_bitmap_dispose(&enlightenments);
+ return ERROR_FAIL;
+}
+
+static int hvm_set_conf_params(libxl__gc *gc, uint32_t domid,
+ const libxl_domain_build_info *info)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ xc_interface *xch = ctx->xch;
+ int ret = ERROR_FAIL;
+ unsigned int altp2m = info->altp2m;
+
+ switch(info->type) {
+ case LIBXL_DOMAIN_TYPE_HVM:
+ /* The config parameter "altp2m" replaces the parameter "altp2mhvm". For
+ * legacy reasons, both parameters are accepted on x86 HVM guests.
+ *
+ * If the legacy field info->u.hvm.altp2m is set, activate altp2m.
+ * Otherwise set altp2m based on the field info->altp2m. */
+ if (info->altp2m == LIBXL_ALTP2M_MODE_DISABLED &&
+ libxl_defbool_val(info->u.hvm.altp2m))
+ altp2m = libxl_defbool_val(info->u.hvm.altp2m);
+
+ if (xc_hvm_param_set(xch, domid, HVM_PARAM_HPET_ENABLED,
+ libxl_defbool_val(info->u.hvm.hpet))) {
+ LOG(ERROR, "Couldn't set HVM_PARAM_HPET_ENABLED");
+ goto out;
+ }
+ if (xc_hvm_param_set(xch, domid, HVM_PARAM_VPT_ALIGN,
+ libxl_defbool_val(info->u.hvm.vpt_align))) {
+ LOG(ERROR, "Couldn't set HVM_PARAM_VPT_ALIGN");
+ goto out;
+ }
+ if (info->u.hvm.mca_caps &&
+ xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_MCA_CAP,
+ info->u.hvm.mca_caps)) {
+ LOG(ERROR, "Couldn't set HVM_PARAM_MCA_CAP");
+ goto out;
+ }
+
+ /* Fallthrough */
+ case LIBXL_DOMAIN_TYPE_PVH:
+ if (xc_hvm_param_set(xch, domid, HVM_PARAM_TIMER_MODE,
+ timer_mode(info))) {
+ LOG(ERROR, "Couldn't set HVM_PARAM_TIMER_MODE");
+ goto out;
+ }
+ if (xc_hvm_param_set(xch, domid, HVM_PARAM_NESTEDHVM,
+ libxl_defbool_val(info->nested_hvm))) {
+ LOG(ERROR, "Couldn't set HVM_PARAM_NESTEDHVM");
+ goto out;
+ }
+ if (xc_hvm_param_set(xch, domid, HVM_PARAM_ALTP2M, altp2m)) {
+ LOG(ERROR, "Couldn't set HVM_PARAM_ALTP2M");
+ goto out;
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+ ret = 0;
+
+ out:
+ return ret;
+}
+
+int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
+ uint32_t domid)
+{
+ const libxl_domain_build_info *info = &d_config->b_info;
+ int ret = 0;
+ int tsc_mode;
+ uint32_t rtc_timeoffset;
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+
+ if (info->type != LIBXL_DOMAIN_TYPE_PV &&
+ (ret = hvm_set_conf_params(gc, domid, info)) != 0)
+ goto out;
+
+ if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+ (ret = hvm_set_viridian_features(gc, domid, info)) != 0)
+ goto out;
+
+ if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PV)
+ xc_domain_set_memmap_limit(ctx->xch, domid,
+ (d_config->b_info.max_memkb +
+ d_config->b_info.u.pv.slack_memkb));
+
+ switch (d_config->b_info.tsc_mode) {
+ case LIBXL_TSC_MODE_DEFAULT:
+ tsc_mode = 0;
+ break;
+ case LIBXL_TSC_MODE_ALWAYS_EMULATE:
+ tsc_mode = 1;
+ break;
+ case LIBXL_TSC_MODE_NATIVE:
+ tsc_mode = 2;
+ break;
+ case LIBXL_TSC_MODE_NATIVE_PARAVIRT:
+ LOGD(ERROR, domid, "TSC Mode native_paravirt (a.k.a PVRDTSCP) has been removed");
+ ret = ERROR_FEATURE_REMOVED;
+ goto out;
+ default:
+ abort();
+ }
+
+ if (xc_domain_set_tsc_info(ctx->xch, domid, tsc_mode, 0, 0, 0)) {
+ LOGE(ERROR, "xc_domain_set_tsc_info() failed");
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ rtc_timeoffset = d_config->b_info.rtc_timeoffset;
+ if (libxl_defbool_val(d_config->b_info.localtime)) {
+ time_t t;
+ struct tm *tm, result;
+
+ t = time(NULL);
+ tm = localtime_r(&t, &result);
+
+ if (!tm) {
+ LOGED(ERROR, domid, "Failed to call localtime_r");
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ rtc_timeoffset += tm->tm_gmtoff;
+ }
+
+ if (rtc_timeoffset)
+ xc_domain_set_time_offset(ctx->xch, domid, rtc_timeoffset);
+
+ if (d_config->b_info.type != LIBXL_DOMAIN_TYPE_PV) {
+ unsigned long shadow = DIV_ROUNDUP(d_config->b_info.shadow_memkb,
+ 1024);
+ xc_shadow_control(ctx->xch, domid, XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION,
+ NULL, 0, &shadow, 0, NULL);
+ }
+
+ if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_PV &&
+ libxl_defbool_val(d_config->b_info.u.pv.e820_host)) {
+ ret = libxl__e820_alloc(gc, domid, d_config);
+ if (ret) {
+ LOGED(ERROR, domid, "Failed while collecting E820 with: %d (errno:%d)\n",
+ ret, errno);
+ }
+ }
+
+out:
+ return ret;
+}
+
+int libxl__arch_extra_memory(libxl__gc *gc,
+ const libxl_domain_build_info *info,
+ uint64_t *out)
+{
+ *out = LIBXL_MAXMEM_CONSTANT;
+
+ return 0;
+}
+
+int libxl__arch_domain_init_hw_description(libxl__gc *gc,
+ libxl_domain_build_info *info,
+ libxl__domain_build_state *state,
+ struct xc_dom_image *dom)
+{
+ return 0;
+}
+
+int libxl__arch_build_dom_finish(libxl__gc *gc,
+ libxl_domain_build_info *info,
+ struct xc_dom_image *dom,
+ libxl__domain_build_state *state)
+{
+ return 0;
+}
+
+/* Return 0 on success, ERROR_* on failure. */
+int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_build_info *b_info,
+ libxl__domain_build_state *state)
+{
+ int nid, nr_vmemrange, rc;
+ uint32_t nr_e820, e820_count;
+ struct e820entry map[E820MAX];
+ xen_vmemrange_t *vmemranges;
+ unsigned int array_size;
+
+ /* If e820_host is not set, call the generic function */
+ if (!(b_info->type == LIBXL_DOMAIN_TYPE_PV &&
+ libxl_defbool_val(b_info->u.pv.e820_host)))
+ return libxl__vnuma_build_vmemrange_pv_generic(gc, domid, b_info,
+ state);
+
+ assert(state->vmemranges == NULL);
+
+ nr_e820 = E820MAX;
+ rc = e820_host_sanitize(gc, b_info, map, &nr_e820);
+ if (rc) goto out;
+
+ e820_count = 0;
+ nr_vmemrange = 0;
+ vmemranges = NULL;
+ array_size = 0;
+ for (nid = 0; nid < b_info->num_vnuma_nodes; nid++) {
+ libxl_vnode_info *p = &b_info->vnuma_nodes[nid];
+ uint64_t remaining_bytes = (p->memkb << 10), bytes;
+
+ while (remaining_bytes > 0) {
+ if (e820_count >= nr_e820) {
+ rc = ERROR_NOMEM;
+ goto out;
+ }
+
+ /* Skip non RAM region */
+ if (map[e820_count].type != E820_RAM) {
+ e820_count++;
+ continue;
+ }
+
+ if (nr_vmemrange >= array_size) {
+ array_size += 32;
+ GCREALLOC_ARRAY(vmemranges, array_size);
+ }
+
+ bytes = map[e820_count].size >= remaining_bytes ?
+ remaining_bytes : map[e820_count].size;
+
+ vmemranges[nr_vmemrange].start = map[e820_count].addr;
+ vmemranges[nr_vmemrange].end = map[e820_count].addr + bytes;
+
+ if (map[e820_count].size >= remaining_bytes) {
+ map[e820_count].addr += bytes;
+ map[e820_count].size -= bytes;
+ } else {
+ e820_count++;
+ }
+
+ remaining_bytes -= bytes;
+
+ vmemranges[nr_vmemrange].flags = 0;
+ vmemranges[nr_vmemrange].nid = nid;
+ nr_vmemrange++;
+ }
+ }
+
+ state->vmemranges = vmemranges;
+ state->num_vmemranges = nr_vmemrange;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq)
+{
+ int ret;
+
+ ret = xc_physdev_map_pirq(CTX->xch, domid, irq, &irq);
+ if (ret)
+ return ret;
+
+ ret = xc_domain_irq_permission(CTX->xch, domid, irq, 1);
+
+ return ret;
+}
+
+/*
+ * Here we're just trying to set these kinds of e820 mappings:
+ *
+ * #1. Low memory region
+ *
+ * Low RAM starts at least from 1M to make sure all standard regions
+ * of the PC memory map, like BIOS, VGA memory-mapped I/O and vgabios,
+ * have enough space.
+ * Note: Those stuffs below 1M are still constructed with multiple
+ * e820 entries by hvmloader. At this point we don't change anything.
+ *
+ * #2. RDM region if it exists
+ *
+ * #3. High memory region if it exists
+ *
+ * Note: these regions are not overlapping since we already check
+ * to adjust them. Please refer to libxl__domain_device_construct_rdm().
+ */
+#define GUEST_LOW_MEM_START_DEFAULT 0x100000
+static int domain_construct_memmap(libxl__gc *gc,
+ libxl_domain_config *d_config,
+ uint32_t domid,
+ struct xc_dom_image *dom)
+{
+ int rc = 0;
+ unsigned int nr = 0, i;
+ /* We always own at least one lowmem entry. */
+ unsigned int e820_entries = 1;
+ struct e820entry *e820 = NULL;
+ uint64_t highmem_size =
+ dom->highmem_end ? dom->highmem_end - (1ull << 32) : 0;
+ uint32_t lowmem_start = dom->device_model ? GUEST_LOW_MEM_START_DEFAULT : 0;
+ unsigned page_size = XC_DOM_PAGE_SIZE(dom);
+
+ /* Add all rdm entries. */
+ for (i = 0; i < d_config->num_rdms; i++)
+ if (d_config->rdms[i].policy != LIBXL_RDM_RESERVE_POLICY_INVALID)
+ e820_entries++;
+
+ /* Add the HVM special pages to PVH memmap as RESERVED. */
+ if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PVH)
+ e820_entries++;
+
+ /* If we should have a highmem range. */
+ if (highmem_size)
+ e820_entries++;
+
+ for (i = 0; i < MAX_ACPI_MODULES; i++)
+ if (dom->acpi_modules[i].length)
+ e820_entries++;
+
+ if (e820_entries >= E820MAX) {
+ LOGD(ERROR, domid, "Ooops! Too many entries in the memory map!");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ e820 = libxl__malloc(gc, sizeof(struct e820entry) * e820_entries);
+
+ /* Low memory */
+ e820[nr].addr = lowmem_start;
+ e820[nr].size = dom->lowmem_end - lowmem_start;
+ e820[nr].type = E820_RAM;
+ nr++;
+
+ /* RDM mapping */
+ for (i = 0; i < d_config->num_rdms; i++) {
+ if (d_config->rdms[i].policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
+ continue;
+
+ e820[nr].addr = d_config->rdms[i].start;
+ e820[nr].size = d_config->rdms[i].size;
+ e820[nr].type = E820_RESERVED;
+ nr++;
+ }
+
+ /* HVM special pages */
+ if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PVH) {
+ e820[nr].addr = (X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES)
+ << XC_PAGE_SHIFT;
+ e820[nr].size = X86_HVM_NR_SPECIAL_PAGES << XC_PAGE_SHIFT;
+ e820[nr].type = E820_RESERVED;
+ nr++;
+ }
+
+ for (i = 0; i < MAX_ACPI_MODULES; i++) {
+ if (dom->acpi_modules[i].length) {
+ e820[nr].addr = dom->acpi_modules[i].guest_addr_out & ~(page_size - 1);
+ e820[nr].size = dom->acpi_modules[i].length +
+ (dom->acpi_modules[i].guest_addr_out & (page_size - 1));
+ e820[nr].type = E820_ACPI;
+ nr++;
+ }
+ }
+
+ /* High memory */
+ if (highmem_size) {
+ e820[nr].addr = ((uint64_t)1 << 32);
+ e820[nr].size = highmem_size;
+ e820[nr].type = E820_RAM;
+ }
+
+ if (xc_domain_set_memory_map(CTX->xch, domid, e820, e820_entries) != 0) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ dom->e820 = e820;
+ dom->e820_entries = e820_entries;
+
+out:
+ return rc;
+}
+
+int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_config *d_config,
+ struct xc_dom_image *dom)
+{
+ libxl_domain_build_info *const info = &d_config->b_info;
+ int rc;
+
+ if (info->type == LIBXL_DOMAIN_TYPE_PV)
+ return 0;
+
+ if (info->type == LIBXL_DOMAIN_TYPE_PVH) {
+ rc = libxl__dom_load_acpi(gc, info, dom);
+ if (rc != 0) {
+ LOGE(ERROR, "libxl_dom_load_acpi failed");
+ return rc;
+ }
+ }
+
+ rc = domain_construct_memmap(gc, d_config, domid, dom);
+ if (rc != 0)
+ LOGE(ERROR, "setting domain memory map failed");
+
+ return rc;
+}
+
+void libxl__arch_domain_create_info_setdefault(libxl__gc *gc,
+ libxl_domain_create_info *c_info)
+{
+}
+
+void libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
+ libxl_domain_build_info *b_info)
+{
+ libxl_defbool_setdefault(&b_info->acpi, true);
+}
+
+int libxl__arch_passthrough_mode_setdefault(libxl__gc *gc,
+ uint32_t domid,
+ libxl_domain_config *d_config,
+ const libxl_physinfo *physinfo)
+{
+ int rc;
+ libxl_domain_create_info *const c_info = &d_config->c_info;
+
+ if (c_info->passthrough != LIBXL_PASSTHROUGH_DISABLED &&
+ c_info->type == LIBXL_DOMAIN_TYPE_PVH) {
+ LOGD(ERROR, domid,
+ "passthrough not yet supported for x86 PVH guests\n");
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ const char *whynot_pt_share =
+ c_info->type == LIBXL_DOMAIN_TYPE_PV ? "not valid for PV domain" :
+ !physinfo->cap_iommu_hap_pt_share ? "not supported on this platform" :
+ !libxl_defbool_val(d_config->c_info.hap) ?"only valid for HAP guests":
+ NULL;
+
+ if (c_info->passthrough == LIBXL_PASSTHROUGH_ENABLED) {
+ c_info->passthrough = whynot_pt_share
+ ? LIBXL_PASSTHROUGH_SYNC_PT : LIBXL_PASSTHROUGH_SHARE_PT;
+ }
+
+ if (c_info->passthrough == LIBXL_PASSTHROUGH_SHARE_PT && whynot_pt_share) {
+ LOGD(ERROR, domid,
+ "passthrough=\"share_pt\" %s\n",
+ whynot_pt_share);
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ rc = 0;
+ out:
+ return rc;
+}
+
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+#include <xen/hvm/hvm_info_table.h>
+#include <xen/hvm/e820.h>
+#include "libacpi/libacpi.h"
+
+#include <xenctrl_dom.h>
+
+ /* Number of pages holding ACPI tables */
+#define NUM_ACPI_PAGES 16
+
+struct libxl_acpi_ctxt {
+ struct acpi_ctxt c;
+
+ unsigned int page_size;
+ unsigned int page_shift;
+
+ /* Memory allocator */
+ unsigned long alloc_base_paddr;
+ unsigned long alloc_base_vaddr;
+ unsigned long alloc_currp;
+ unsigned long alloc_end;
+};
+
+extern const unsigned char dsdt_pvh[];
+extern const unsigned int dsdt_pvh_len;
+
+/* Assumes contiguous physical space */
+static unsigned long virt_to_phys(struct acpi_ctxt *ctxt, void *v)
+{
+ struct libxl_acpi_ctxt *libxl_ctxt =
+ CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c);
+
+ return (((unsigned long)v - libxl_ctxt->alloc_base_vaddr) +
+ libxl_ctxt->alloc_base_paddr);
+}
+
+static void *mem_alloc(struct acpi_ctxt *ctxt,
+ uint32_t size, uint32_t align)
+{
+ struct libxl_acpi_ctxt *libxl_ctxt =
+ CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c);
+ unsigned long s, e;
+
+ /* Align to at least 16 bytes. */
+ if (align < 16)
+ align = 16;
+
+ s = (libxl_ctxt->alloc_currp + align) & ~((unsigned long)align - 1);
+ e = s + size - 1;
+
+ /* TODO: Reallocate memory */
+ if ((e < s) || (e >= libxl_ctxt->alloc_end))
+ return NULL;
+
+ while (libxl_ctxt->alloc_currp >> libxl_ctxt->page_shift !=
+ e >> libxl_ctxt->page_shift)
+ libxl_ctxt->alloc_currp += libxl_ctxt->page_size;
+
+ libxl_ctxt->alloc_currp = e;
+
+ return (void *)s;
+}
+
+static void acpi_mem_free(struct acpi_ctxt *ctxt,
+ void *v, uint32_t size)
+{
+}
+
+static uint32_t acpi_lapic_id(unsigned cpu)
+{
+ return cpu * 2;
+}
+
+static int init_acpi_config(libxl__gc *gc,
+ struct xc_dom_image *dom,
+ const libxl_domain_build_info *b_info,
+ struct acpi_config *config)
+{
+ xc_interface *xch = dom->xch;
+ uint32_t domid = dom->guest_domid;
+ xc_dominfo_t info;
+ struct hvm_info_table *hvminfo;
+ int i, r, rc;
+
+ config->dsdt_anycpu = config->dsdt_15cpu = dsdt_pvh;
+ config->dsdt_anycpu_len = config->dsdt_15cpu_len = dsdt_pvh_len;
+
+ r = xc_domain_getinfo(xch, domid, 1, &info);
+ if (r < 0) {
+ LOG(ERROR, "getdomaininfo failed (rc=%d)", r);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ hvminfo = libxl__zalloc(gc, sizeof(*hvminfo));
+
+ hvminfo->apic_mode = libxl_defbool_val(b_info->apic);
+
+ if (dom->nr_vnodes) {
+ unsigned int *vcpu_to_vnode, *vdistance;
+ struct xen_vmemrange *vmemrange;
+ struct acpi_numa *numa = &config->numa;
+
+ r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes,
+ &numa->nr_vmemranges,
+ &hvminfo->nr_vcpus, NULL, NULL, NULL);
+ if (r) {
+ LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ vmemrange = libxl__zalloc(gc, dom->nr_vmemranges * sizeof(*vmemrange));
+ vdistance = libxl__zalloc(gc, dom->nr_vnodes * sizeof(*vdistance));
+ vcpu_to_vnode = libxl__zalloc(gc, hvminfo->nr_vcpus *
+ sizeof(*vcpu_to_vnode));
+ r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes,
+ &numa->nr_vmemranges, &hvminfo->nr_vcpus,
+ vmemrange, vdistance, vcpu_to_vnode);
+ if (r) {
+ LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ numa->vmemrange = vmemrange;
+ numa->vdistance = vdistance;
+ numa->vcpu_to_vnode = vcpu_to_vnode;
+ } else {
+ hvminfo->nr_vcpus = info.max_vcpu_id + 1;
+ }
+
+ for (i = 0; i < hvminfo->nr_vcpus; i++)
+ hvminfo->vcpu_online[i / 8] |= 1 << (i & 7);
+
+ config->hvminfo = hvminfo;
+
+ config->lapic_base_address = LAPIC_BASE_ADDRESS;
+ config->lapic_id = acpi_lapic_id;
+ config->acpi_revision = 5;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+int libxl__dom_load_acpi(libxl__gc *gc,
+ const libxl_domain_build_info *b_info,
+ struct xc_dom_image *dom)
+{
+ struct acpi_config config = {0};
+ struct libxl_acpi_ctxt libxl_ctxt;
+ int rc = 0, acpi_pages_num;
+ void *acpi_pages;
+ unsigned long page_mask;
+
+ if (b_info->type != LIBXL_DOMAIN_TYPE_PVH)
+ goto out;
+
+ libxl_ctxt.page_size = XC_DOM_PAGE_SIZE(dom);
+ libxl_ctxt.page_shift = XC_DOM_PAGE_SHIFT(dom);
+ page_mask = (1UL << libxl_ctxt.page_shift) - 1;
+
+ libxl_ctxt.c.mem_ops.alloc = mem_alloc;
+ libxl_ctxt.c.mem_ops.v2p = virt_to_phys;
+ libxl_ctxt.c.mem_ops.free = acpi_mem_free;
+
+ rc = init_acpi_config(gc, dom, b_info, &config);
+ if (rc) {
+ LOG(ERROR, "init_acpi_config failed (rc=%d)", rc);
+ goto out;
+ }
+
+ config.rsdp = (unsigned long)libxl__malloc(gc, libxl_ctxt.page_size);
+ config.infop = (unsigned long)libxl__malloc(gc, libxl_ctxt.page_size);
+ /* Pages to hold ACPI tables */
+ acpi_pages = libxl__malloc(gc, (NUM_ACPI_PAGES + 1) *
+ libxl_ctxt.page_size);
+
+ /*
+ * Set up allocator memory.
+ * Start next to acpi_info page to avoid fracturing e820.
+ */
+ libxl_ctxt.alloc_base_paddr = ACPI_INFO_PHYSICAL_ADDRESS +
+ libxl_ctxt.page_size;
+ libxl_ctxt.alloc_base_vaddr = libxl_ctxt.alloc_currp =
+ (unsigned long)acpi_pages;
+ libxl_ctxt.alloc_end = (unsigned long)acpi_pages +
+ (NUM_ACPI_PAGES * libxl_ctxt.page_size);
+
+ /* Build the tables. */
+ rc = acpi_build_tables(&libxl_ctxt.c, &config);
+ if (rc) {
+ LOG(ERROR, "acpi_build_tables failed with %d", rc);
+ goto out;
+ }
+
+ /* Calculate how many pages are needed for the tables. */
+ acpi_pages_num =
+ ((libxl_ctxt.alloc_currp - (unsigned long)acpi_pages)
+ >> libxl_ctxt.page_shift) +
+ ((libxl_ctxt.alloc_currp & page_mask) ? 1 : 0);
+
+ dom->acpi_modules[0].data = (void *)config.rsdp;
+ dom->acpi_modules[0].length = 64;
+ /*
+ * Some Linux versions cannot properly process hvm_start_info.rsdp_paddr
+ * and so we need to put RSDP in location that can be discovered by ACPI's
+ * standard search method, in R-O BIOS memory (we chose last 64 bytes)
+ */
+ if (strcmp(dom->parms.guest_os, "linux") ||
+ elf_xen_feature_get(XENFEAT_linux_rsdp_unrestricted,
+ dom->parms.f_supported))
+ dom->acpi_modules[0].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS +
+ (1 + acpi_pages_num) * libxl_ctxt.page_size;
+ else
+ dom->acpi_modules[0].guest_addr_out = 0x100000 - 64;
+
+ dom->acpi_modules[1].data = (void *)config.infop;
+ dom->acpi_modules[1].length = 4096;
+ dom->acpi_modules[1].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS;
+
+ dom->acpi_modules[2].data = acpi_pages;
+ dom->acpi_modules[2].length = acpi_pages_num << libxl_ctxt.page_shift;
+ dom->acpi_modules[2].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS +
+ libxl_ctxt.page_size;
+
+out:
+ return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef LIBXL_X86_ACPI_H
+#define LIBXL_X86_ACPI_H
+
+#include "libxl_internal.h"
+
+#define ASSERT(x) assert(x)
+
+static inline int test_bit(unsigned int b, const void *p)
+{
+ return !!(((const uint8_t *)p)[b>>3] & (1u<<(b&7)));
+}
+
+#endif /* LIBXL_X_86_ACPI_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+char **libxl__xs_kvs_of_flexarray(libxl__gc *gc, flexarray_t *array)
+{
+ char **kvs;
+ int i, length;
+
+ if (!array)
+ return NULL;
+
+ length = array->count;
+ if (!length)
+ return NULL;
+
+ kvs = libxl__calloc(gc, length + 2, sizeof(char *));
+ if (kvs) {
+ for (i = 0; i < length; i += 2) {
+ void *ptr;
+
+ flexarray_get(array, i, &ptr);
+ kvs[i] = (char *) ptr;
+ flexarray_get(array, i + 1, &ptr);
+ kvs[i + 1] = (char *) ptr;
+ }
+ kvs[i] = NULL;
+ kvs[i + 1] = NULL;
+ }
+ return kvs;
+}
+
+int libxl__xs_writev_perms(libxl__gc *gc, xs_transaction_t t,
+ const char *dir, char *kvs[],
+ struct xs_permissions *perms,
+ unsigned int num_perms)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char *path;
+ int i;
+
+ if (!kvs)
+ return 0;
+
+ for (i = 0; kvs[i] != NULL; i += 2) {
+ path = GCSPRINTF("%s/%s", dir, kvs[i]);
+ if (path && kvs[i + 1]) {
+ int length = strlen(kvs[i + 1]);
+ xs_write(ctx->xsh, t, path, kvs[i + 1], length);
+ if (perms)
+ xs_set_permissions(ctx->xsh, t, path, perms, num_perms);
+ }
+ }
+ return 0;
+}
+
+int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t,
+ const char *dir, char *kvs[])
+{
+ return libxl__xs_writev_perms(gc, t, dir, kvs, NULL, 0);
+}
+
+int libxl__xs_writev_atonce(libxl__gc *gc,
+ const char *dir, char *kvs[])
+{
+ int rc;
+ xs_transaction_t t = XBT_NULL;
+
+ for (;;) {
+ rc = libxl__xs_transaction_start(gc, &t);
+ if (rc) goto out;
+
+ rc = libxl__xs_writev(gc, t, dir, kvs);
+ if (rc) goto out;
+
+ rc = libxl__xs_transaction_commit(gc, &t);
+ if (!rc) break;
+ if (rc<0) goto out;
+ }
+
+out:
+ libxl__xs_transaction_abort(gc, &t);
+
+ return rc;
+
+}
+
+int libxl__xs_vprintf(libxl__gc *gc, xs_transaction_t t,
+ const char *path, const char *fmt, va_list ap)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char *s;
+ bool ok;
+
+ s = libxl__vsprintf(gc, fmt, ap);
+
+ ok = xs_write(ctx->xsh, t, path, s, strlen(s));
+ if (!ok) {
+ LOGE(ERROR, "xenstore write failed: `%s' = `%s'", path, s);
+ return ERROR_FAIL;
+ }
+
+ return 0;
+}
+
+int libxl__xs_printf(libxl__gc *gc, xs_transaction_t t,
+ const char *path, const char *fmt, ...)
+{
+ va_list ap;
+ int rc;
+
+ va_start(ap, fmt);
+ rc = libxl__xs_vprintf(gc, t, path, fmt, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+char * libxl__xs_read(libxl__gc *gc, xs_transaction_t t, const char *path)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char *ptr;
+
+ ptr = xs_read(ctx->xsh, t, path, NULL);
+ libxl__ptr_add(gc, ptr);
+ return ptr;
+}
+
+char *libxl__xs_get_dompath(libxl__gc *gc, uint32_t domid)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char *s = xs_get_domain_path(ctx->xsh, domid);
+ if (!s) {
+ LOGED(ERROR, domid, "Failed to get dompath");
+ return NULL;
+ }
+ libxl__ptr_add(gc, s);
+ return s;
+}
+
+char **libxl__xs_directory(libxl__gc *gc, xs_transaction_t t,
+ const char *path, unsigned int *nb)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ char **ret = NULL;
+ ret = xs_directory(ctx->xsh, t, path, nb);
+ libxl__ptr_add(gc, ret);
+ return ret;
+}
+
+int libxl__xs_mknod(libxl__gc *gc, xs_transaction_t t,
+ const char *path, struct xs_permissions *perms,
+ unsigned int num_perms)
+{
+ libxl_ctx *ctx = libxl__gc_owner(gc);
+ bool ok;
+
+ ok = xs_write(ctx->xsh, t, path, "", 0);
+ if (!ok) {
+ LOGE(ERROR, "xenstore write failed: `%s' = ''", path);
+ return ERROR_FAIL;
+ }
+
+ ok = xs_set_permissions(ctx->xsh, t, path, perms, num_perms);
+ if (!ok) {
+ LOGE(ERROR, "xenstore set permissions failed on `%s'", path);
+ return ERROR_FAIL;
+ }
+
+ return 0;
+}
+
+char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t domid)
+{
+ char *s = GCSPRINTF("/libxl/%i", domid);
+ if (!s)
+ LOGD(ERROR, domid, "cannot allocate create paths");
+ return s;
+}
+
+int libxl__xs_read_mandatory(libxl__gc *gc, xs_transaction_t t,
+ const char *path, const char **result_out)
+{
+ char *result = libxl__xs_read(gc, t, path);
+ if (!result) {
+ LOGE(ERROR, "xenstore read failed: `%s'", path);
+ return ERROR_FAIL;
+ }
+ *result_out = result;
+ return 0;
+}
+
+int libxl__xs_read_checked(libxl__gc *gc, xs_transaction_t t,
+ const char *path, const char **result_out)
+{
+ char *result = libxl__xs_read(gc, t, path);
+ if (!result && errno != ENOENT) {
+ LOGE(ERROR, "xenstore read failed: `%s'", path);
+ return ERROR_FAIL;
+ }
+ *result_out = result;
+ return 0;
+}
+
+int libxl__xs_write_checked(libxl__gc *gc, xs_transaction_t t,
+ const char *path, const char *string)
+{
+ size_t length = strlen(string);
+ if (!xs_write(CTX->xsh, t, path, string, length)) {
+ LOGE(ERROR, "xenstore write failed: `%s' = `%s'", path, string);
+ return ERROR_FAIL;
+ }
+ return 0;
+}
+
+int libxl__xs_rm_checked(libxl__gc *gc, xs_transaction_t t, const char *path)
+{
+ if (!xs_rm(CTX->xsh, t, path)) {
+ if (errno == ENOENT)
+ return 0;
+
+ LOGE(ERROR, "xenstore rm failed: `%s'", path);
+ return ERROR_FAIL;
+ }
+ return 0;
+}
+
+int libxl__xs_transaction_start(libxl__gc *gc, xs_transaction_t *t)
+{
+ assert(!*t);
+ *t = xs_transaction_start(CTX->xsh);
+ if (!*t) {
+ LOGE(ERROR, "could not create xenstore transaction");
+ return ERROR_FAIL;
+ }
+ return 0;
+}
+
+int libxl__xs_transaction_commit(libxl__gc *gc, xs_transaction_t *t)
+{
+ assert(*t);
+
+ if (!xs_transaction_end(CTX->xsh, *t, 0)) {
+ *t = 0;
+ if (errno == EAGAIN)
+ return +1;
+
+ LOGE(ERROR, "could not commit xenstore transaction");
+ return ERROR_FAIL;
+ }
+
+ *t = 0;
+ return 0;
+}
+
+void libxl__xs_transaction_abort(libxl__gc *gc, xs_transaction_t *t)
+{
+ if (!*t)
+ return;
+
+ if (!xs_transaction_end(CTX->xsh, *t, 1))
+ LOGE(ERROR, "could not abort xenstore transaction");
+
+ *t = 0;
+}
+
+int libxl__xs_path_cleanup(libxl__gc *gc, xs_transaction_t t,
+ const char *user_path)
+{
+ unsigned int nb = 0;
+ char *path, *last, *val;
+ int rc;
+
+ /* A path and transaction must be provided by the caller */
+ assert(user_path && t);
+
+ path = libxl__strdup(gc, user_path);
+ if (!xs_rm(CTX->xsh, t, path)) {
+ if (errno != ENOENT)
+ LOGE(DEBUG, "unable to remove path %s", path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ for (last = strrchr(path, '/'); last != NULL; last = strrchr(path, '/')) {
+ *last = '\0';
+
+ if (!strlen(path)) break;
+
+ val = libxl__xs_read(gc, t, path);
+ if (!val || strlen(val) != 0) break;
+
+ if (!libxl__xs_directory(gc, t, path, &nb) || nb != 0) break;
+
+ if (!xs_rm(CTX->xsh, t, path)) {
+ if (errno != ENOENT)
+ LOGE(DEBUG, "unable to remove path %s", path);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+ rc = 0;
+
+out:
+ return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2009 Citrix Ltd.
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <stdlib.h>
+
+#ifdef NEED_OWN_ASPRINTF
+
+int vasprintf(char **buffer, const char *fmt, va_list ap)
+{
+ int size = 0;
+ int nchars;
+
+ *buffer = 0;
+
+ nchars = vsnprintf(*buffer, 0, fmt, ap);
+
+ if (nchars >= size)
+ {
+ char *tmpbuff;
+ /* Reallocate buffer now that we know how much space is needed. */
+ size = nchars+1;
+ tmpbuff = (char*)realloc(*buffer, size);
+
+
+ if (tmpbuff == NULL) { /* we need to free it*/
+ free(*buffer);
+ return -1;
+ }
+
+ *buffer=tmpbuff;
+ /* Try again. */
+ nchars = vsnprintf(*buffer, size, fmt, ap);
+ }
+
+ if (nchars < 0) return nchars;
+ return size;
+}
+
+int asprintf(char **buffer, char *fmt, ...)
+{
+ int status;
+ va_list ap;
+
+ va_start (ap, fmt);
+ status = vasprintf (buffer, fmt, ap);
+ va_end (ap);
+ return status;
+}
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+#include "test_common.h"
+
+libxl_ctx *ctx;
+
+void test_common_setup(int level)
+{
+ xentoollog_logger_stdiostream *logger_s
+ = xtl_createlogger_stdiostream(stderr, level, 0);
+ assert(logger_s);
+
+ xentoollog_logger *logger = (xentoollog_logger*)logger_s;
+
+ int rc = libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, logger);
+ assert(!rc);
+}
+
+struct timeval now;
+
+void test_common_get_now(void)
+{
+ int r = gettimeofday(&now, 0); assert(!r);
+}
+
+int poll_nfds, poll_nfds_allocd;
+struct pollfd *poll_fds;
+int poll_timeout;
+
+void test_common_beforepoll(void)
+{
+ for (;;) {
+ test_common_get_now();
+
+ poll_timeout = -1;
+ poll_nfds = poll_nfds_allocd;
+ int rc = libxl_osevent_beforepoll(ctx, &poll_nfds, poll_fds,
+ &poll_timeout, now);
+ if (!rc) return;
+ assert(rc == ERROR_BUFFERFULL);
+
+ assert(poll_nfds > poll_nfds_allocd);
+ poll_fds = realloc(poll_fds, poll_nfds * sizeof(poll_fds[0]));
+ assert(poll_fds);
+ poll_nfds_allocd = poll_nfds;
+ }
+}
+
+void test_common_dopoll(void) {
+ errno = 0;
+ int r = poll(poll_fds, poll_nfds, poll_timeout);
+ fprintf(stderr, "poll: r=%d errno=%s\n", r, strerror(errno));
+}
+
+void test_common_afterpoll(void)
+{
+ test_common_get_now();
+ libxl_osevent_afterpoll(ctx, poll_nfds, poll_fds, now);
+}
--- /dev/null
+#ifndef TEST_COMMON_H
+#define TEST_COMMON_H
+
+#include "libxl.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+void test_common_setup(int level);
+
+extern libxl_ctx *ctx;
+
+void test_common_get_now(void);
+
+extern struct timeval now;
+
+void test_common_beforepoll(void);
+void test_common_dopoll(void);
+void test_common_afterpoll(void);
+
+extern int poll_nfds, poll_nfds_allocd;
+extern struct pollfd *poll_fds;
+extern int poll_timeout;
+
+#endif /*TEST_COMMON_H*/
--- /dev/null
+#include "test_common.h"
+#include "libxl_test_fdevent.h"
+
+int main(int argc, char **argv) {
+ int rc, i;
+ libxl_asyncop_how how;
+ libxl_event *event;
+
+ test_common_setup(XTL_DEBUG);
+
+ how.callback = NULL;
+ how.u.for_event = 1;
+
+ int fd = open("/dev/null", O_RDONLY);
+ assert(fd > 0);
+
+ rc = libxl_test_fdevent(ctx, fd, POLLIN, &how);
+ assert(!rc);
+
+ test_common_beforepoll();
+
+ rc = libxl_ao_abort(ctx, &how);
+ assert(!rc);
+
+ rc = libxl_event_check(ctx, &event, LIBXL_EVENTMASK_ALL, 0,0);
+ assert(!rc);
+ assert(event);
+ assert(event->for_user == how.u.for_event);
+ assert(event->type == LIBXL_EVENT_TYPE_OPERATION_COMPLETE);
+ assert(event->u.operation_complete.rc == ERROR_ABORTED);
+
+ close(fd);
+
+ test_common_dopoll();
+
+ for (i=0; i<poll_nfds; i++) {
+ if (poll_fds[i].fd == fd && (poll_fds[i].revents & POLLNVAL)) {
+ fprintf(stderr, "POLLNVAL on fd=%d in slot i=%d as expected\n",
+ fd, i);
+ goto found;
+ }
+ }
+ abort();
+ found:;
+
+ int fd2 = open("/dev/null", O_RDONLY);
+ assert(fd2 == fd);
+
+ how.u.for_event++;
+ rc = libxl_test_fdevent(ctx, fd, POLLIN, &how);
+ assert(!rc);
+
+ test_common_afterpoll();
+
+ fprintf(stderr, "complete\n");
+}
--- /dev/null
+#include "test_common.h"
+#include "libxl_test_timedereg.h"
+
+int main(int argc, char **argv) {
+ int rc;
+
+ test_common_setup(XTL_DEBUG);
+
+ rc = libxl_test_timedereg(ctx, 0);
+ assert(!rc);
+}
USELIBS_vchan := toollog store gnttab evtchn
LIBS_LIBS += stat
USELIBS_stat := ctrl store
+LIBS_LIBS += light
+USELIBS_light := toollog evtchn toolcore ctrl store hypfs guest
XEN_ROOT = $(CURDIR)/../..
include $(XEN_ROOT)/tools/Rules.mk
-MAJOR = 4.15
-MINOR = 0
-
XLUMAJOR = 4.15
XLUMINOR = 0
-Wno-declaration-after-statement -Wformat-nonliteral
CFLAGS += -I. -fPIC
-ifeq ($(CONFIG_Linux),y)
-LIBUUID_LIBS += -luuid
-endif
-
-LIBXL_LIBS =
-LIBXL_LIBS = $(LDLIBS_libxentoollog) $(LDLIBS_libxenevtchn) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxenhypfs) $(LDLIBS_libxenstore) $(LDLIBS_libxentoolcore) $(PTYFUNCS_LIBS) $(LIBUUID_LIBS)
-ifeq ($(CONFIG_LIBNL),y)
-LIBXL_LIBS += $(LIBNL3_LIBS)
-endif
-ifeq ($(CONFIG_Linux),y)
-LIBXL_LIBS += -lrt
-endif
-
-CFLAGS_LIBXL += $(CFLAGS_libxentoollog)
-CFLAGS_LIBXL += $(CFLAGS_libxentoolcore)
-CFLAGS_LIBXL += $(CFLAGS_libxenevtchn)
-CFLAGS_LIBXL += $(CFLAGS_libxenctrl)
-CFLAGS_LIBXL += $(CFLAGS_libxenguest)
-CFLAGS_LIBXL += $(CFLAGS_libxenhypfs)
-CFLAGS_LIBXL += $(CFLAGS_libxenstore)
-ifeq ($(CONFIG_LIBNL),y)
-CFLAGS_LIBXL += $(LIBNL3_CFLAGS)
-endif
-CFLAGS_LIBXL += -Wshadow
-ifeq ($(debug),y)
-CFLAGS_LIBXL += -DCONFIG_DEBUG
-endif
-
-LIBXL_LIBS-$(CONFIG_ARM) += -lfdt
-
CFLAGS += $(PTHREAD_CFLAGS)
LDFLAGS += $(PTHREAD_LDFLAGS)
-LIBXL_LIBS += $(PTHREAD_LIBS)
-LIBXL_LIBS += $(LIBXL_LIBS-y)
LIBXLU_LIBS = $(LDLIBS_libxenlight)
-LIBXL_OBJS-y = osdeps.o libxl_paths.o libxl_bootloader.o flexarray.o
-
-ifeq ($(CONFIG_LIBNL),y)
-LIBXL_OBJS-y += libxl_netbuffer.o
-else
-LIBXL_OBJS-y += libxl_nonetbuffer.o
-endif
-
-ifeq ($(CONFIG_X86),y)
-LIBXL_OBJS-y += libxl_convert_callout.o
-else
-LIBXL_OBJS-y += libxl_no_convert_callout.o
-endif
-
-LIBXL_OBJS-y += libxl_remus.o libxl_checkpoint_device.o libxl_remus_disk_drbd.o
-
-ifeq ($(CONFIG_LIBNL),y)
-LIBXL_OBJS-y += libxl_colo_restore.o libxl_colo_save.o
-LIBXL_OBJS-y += libxl_colo_qdisk.o
-LIBXL_OBJS-y += libxl_colo_proxy.o
-LIBXL_OBJS-y += libxl_colo_nic.o
-else
-LIBXL_OBJS-y += libxl_no_colo.o
-endif
-
-ACPI_PATH = $(XEN_ROOT)/tools/libacpi
-DSDT_FILES-$(CONFIG_X86) = dsdt_pvh.c
-ACPI_OBJS = $(patsubst %.c,%.o,$(DSDT_FILES-y)) build.o static_tables.o
-$(DSDT_FILES-y): acpi
-$(ACPI_OBJS): CFLAGS += -I. -DLIBACPI_STDUTILS=\"$(CURDIR)/libxl_x86_acpi.h\"
-vpath build.c $(ACPI_PATH)/
-vpath static_tables.c $(ACPI_PATH)/
-LIBXL_OBJS-$(CONFIG_X86) += $(ACPI_OBJS)
-
-.PHONY: acpi
-acpi:
- $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) DSDT_FILES="$(DSDT_FILES-y)"
-
-LIBXL_OBJS-$(CONFIG_X86) += libxl_cpuid.o libxl_x86.o libxl_psr.o libxl_x86_acpi.o
-LIBXL_OBJS-$(CONFIG_ARM) += libxl_nocpuid.o libxl_arm.o libxl_libfdt_compat.o
-ifeq ($(CONFIG_ARM_64),y)
-DSDT_FILES-y = dsdt_anycpu_arm.c
-LIBXL_OBJS-y += libxl_arm_acpi.o $(patsubst %.c,%.o,$(DSDT_FILES-y))
-dsdt_anycpu_arm.c:
- $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) DSDT_FILES="$(DSDT_FILES-y)"
-else
-LIBXL_OBJS-$(CONFIG_ARM) += libxl_arm_no_acpi.o
-endif
-
-ifeq ($(CONFIG_NetBSD),y)
-LIBXL_OBJS-y += libxl_netbsd.o
-else
-ifeq ($(CONFIG_Linux),y)
-LIBXL_OBJS-y += libxl_linux.o
-else
-ifeq ($(CONFIG_FreeBSD),y)
-LIBXL_OBJS-y += libxl_freebsd.o
-else
-$(error Your Operating System is not supported by libxenlight, \
-please check libxl_linux.c and libxl_netbsd.c to see how to get it ported)
-endif
-endif
-endif
-
ifeq ($(FLEX),)
%.c %.h:: %.l
$(warning Flex is needed to rebuild some libxl parsers and \
scanners, please install it an rerun configure)
endif
-LIBXL_LIBS += -lyajl
-
-LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \
- libxl_dom.o libxl_exec.o libxl_xshelp.o libxl_device.o \
- libxl_internal.o libxl_utils.o libxl_uuid.o \
- libxl_json.o libxl_aoutils.o libxl_numa.o libxl_vnuma.o \
- libxl_stream_read.o libxl_stream_write.o \
- libxl_save_callout.o _libxl_save_msgs_callout.o \
- libxl_qmp.o libxl_event.o libxl_fork.o \
- libxl_dom_suspend.o libxl_dom_save.o libxl_usb.o \
- libxl_vtpm.o libxl_nic.o libxl_disk.o libxl_console.o \
- libxl_cpupool.o libxl_mem.o libxl_sched.o libxl_tmem.o \
- libxl_9pfs.o libxl_domain.o libxl_vdispl.o \
- libxl_pvcalls.o libxl_vsnd.o libxl_vkb.o $(LIBXL_OBJS-y)
-LIBXL_OBJS += libxl_genid.o
-LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o
-
-LIBXL_TESTS += timedereg
-LIBXL_TESTS_PROGS = $(LIBXL_TESTS) fdderegrace
-LIBXL_TESTS_INSIDE = $(LIBXL_TESTS) fdevent
-
-# Each entry FOO in LIBXL_TESTS has two main .c files:
-# libxl_test_FOO.c "inside libxl" code to support the test case
-# test_FOO.c "outside libxl" code to exercise the test case
-# Conventionally there will also be:
-# libxl_test_FOO.h interface between the "inside" and "outside" parts
-# The "inside libxl" file is compiled exactly like a piece of libxl, and the
-# "outside libxl" file is compiled exactly like a piece of application
-# code. They must share information via explicit libxl entrypoints.
-# Unlike proper parts of libxl, it is permissible for libxl_test_FOO.c
-# to use private global variables for its state. Note that all the
-# "inside" parts are compiled into a single test library, so their
-# symbol names must be unique.
-#
-# To run these tests, either use LD_PRELOAD to get libxenlight_test.so
-# loaded, or rename it to libxenlight.so so it is the target of the
-# appropriate symlinks.
-
-LIBXL_TEST_OBJS += $(foreach t, $(LIBXL_TESTS_INSIDE),libxl_test_$t.o)
-TEST_PROG_OBJS += $(foreach t, $(LIBXL_TESTS_PROGS),test_$t.o) test_common.o
-TEST_PROGS += $(foreach t, $(LIBXL_TESTS_PROGS),test_$t)
-
-$(LIBXL_OBJS) $(LIBXL_TEST_OBJS): CFLAGS += $(CFLAGS_LIBXL) -include $(XEN_ROOT)/tools/config.h
-
-AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h _libxl_list.h _paths.h \
- libxlu_disk_l.h _libxl_save_msgs_callout.h _libxl_save_msgs_helper.h
+AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h libxlu_disk_l.h
AUTOSRCS= libxlu_cfg_y.c libxlu_cfg_l.c
-AUTOSRCS += _libxl_save_msgs_callout.c _libxl_save_msgs_helper.c
LIBXLU_OBJS = libxlu_cfg_y.o libxlu_cfg_l.o libxlu_cfg.o \
libxlu_disk_l.o libxlu_disk.o libxlu_vif.o libxlu_pci.o
$(LIBXLU_OBJS): CFLAGS += $(CFLAGS_libxenctrl) # For xentoollog.h
-$(TEST_PROG_OBJS) _libxl.api-for-check: CFLAGS += $(CFLAGS_libxentoollog) $(CFLAGS_libxentoolcore)
-
-CLIENTS = testidl libxl-save-helper
-
-libxl_dom.o: CFLAGS += -I$(XEN_ROOT)/tools # include libacpi/x86.h
-libxl_x86_acpi.o: CFLAGS += -I$(XEN_ROOT)/tools
-
-SAVE_HELPER_OBJS = libxl_save_helper.o _libxl_save_msgs_helper.o
-$(SAVE_HELPER_OBJS): CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenevtchn) $(CFLAGS_libxenguest)
-
-PKG_CONFIG = xenlight.pc xlutil.pc
-PKG_CONFIG_VERSION := $(MAJOR).$(MINOR)
+PKG_CONFIG = xlutil.pc
ifneq ($(CONFIG_LIBXC_MINIOS),y)
PKG_CONFIG_INST := $(PKG_CONFIG)
-xenlight.pc: PKG_CONFIG_NAME = Xenlight
-xenlight.pc: PKG_CONFIG_DESC = The Xenlight library for Xen hypervisor
-xenlight.pc: PKG_CONFIG_VERSION = $(MAJOR).$(MINOR)
-xenlight.pc: PKG_CONFIG_VARS = xenfirmwaredir=$(XENFIRMWAREDIR) libexec_bin=$(LIBEXEC_BIN)
-xenlight.pc: PKG_CONFIG_USELIBS = $(SHLIB_libxenlight)
-xenlight.pc: PKG_CONFIG_LIB = xenlight
-xenlight.pc: PKG_CONFIG_REQPRIV = xentoollog,xenevtchn,xencontrol,xenguest,xenstore,xenhypfs
xlutil.pc: PKG_CONFIG_NAME = Xlutil
xlutil.pc: PKG_CONFIG_DESC = The xl utility library for Xen hypervisor
xlutil.pc: PKG_CONFIG_VERSION = $(XLUMAJOR).$(XLUMINOR)
PKG_CONFIG_LOCAL := $(foreach pc,$(PKG_CONFIG),$(PKG_CONFIG_DIR)/$(pc))
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_NAME = Xenlight
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_DESC = The Xenlight library for Xen hypervisor
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_VERSION = $(MAJOR).$(MINOR)
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_VARS = xenfirmwaredir=$(XENFIRMWAREDIR) libexec_bin=$(LIBEXEC_BIN)
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_USELIBS = $(SHLIB_libxenlight)
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_LIB = xenlight
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_REQPRIV = xentoollog,xenevtchn,xencontrol,xenguest,xenstore,xenhypfs
$(PKG_CONFIG_DIR)/xlutil.pc: PKG_CONFIG_NAME = Xlutil
$(PKG_CONFIG_DIR)/xlutil.pc: PKG_CONFIG_DESC = The xl utility library for Xen hypervisor
$(PKG_CONFIG_DIR)/xlutil.pc: PKG_CONFIG_VERSION = $(XLUMAJOR).$(XLUMINOR)
$(PKG_CONFIG_LOCAL): PKG_CONFIG_LIBDIR = $(CURDIR)
$(PKG_CONFIG_LOCAL): PKG_CONFIG_CFLAGS_LOCAL = $(CFLAGS_xeninclude)
-testidl.o: CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenlight)
-testidl.c: libxl_types.idl gentest.py libxl.h $(AUTOINCS)
- $(PYTHON) gentest.py libxl_types.idl testidl.c.new
- mv testidl.c.new testidl.c
-
.PHONY: all
-all: $(CLIENTS) $(TEST_PROGS) $(PKG_CONFIG) $(PKG_CONFIG_LOCAL) \
- libxenlight.so libxenlight.a libxlutil.so libxlutil.a \
- $(AUTOSRCS) $(AUTOINCS)
+all: libxlutil.so libxlutil.a $(AUTOSRCS) $(AUTOINCS) $(PKG_CONFIG) $(PKG_CONFIG_LOCAL)
-$(LIBXL_OBJS) $(LIBXLU_OBJS) $(SAVE_HELPER_OBJS) \
- $(LIBXL_TEST_OBJS) $(TEST_PROG_OBJS): \
- $(AUTOINCS) libxl.api-ok
+$(LIBXLU_OBJS): $(AUTOINCS)
%.c %.h:: %.y
@rm -f $*.[ch]
genpath-target = $(call buildmakevars2header,_paths.h)
$(eval $(genpath-target))
-libxl.api-ok: check-libxl-api-rules _libxl.api-for-check
- $(PERL) $^
- touch $@
-
-_%.api-for-check: %.h $(AUTOINCS)
- $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_$*.o) -c -E $< $(APPEND_CFLAGS) \
- -DLIBXL_EXTERNAL_CALLERS_ONLY=LIBXL_EXTERNAL_CALLERS_ONLY \
- >$@.new
- mv -f $@.new $@
-
-_libxl_list.h: $(XEN_INCLUDE)/xen-external/bsd-sys-queue-h-seddery $(XEN_INCLUDE)/xen-external/bsd-sys-queue.h
- $(PERL) $^ --prefix=libxl >$@.new
- $(call move-if-changed,$@.new,$@)
-
-_libxl_save_msgs_helper.c _libxl_save_msgs_callout.c \
-_libxl_save_msgs_helper.h _libxl_save_msgs_callout.h: \
- libxl_save_msgs_gen.pl
- $(PERL) -w $< $@ >$@.new
- $(call move-if-changed,$@.new,$@)
-
-libxl.h: _libxl_types.h _libxl_list.h
-libxl_json.h: _libxl_types_json.h
-libxl_internal.h: _libxl_types_internal.h _libxl_types_private.h _libxl_types_internal_private.h _paths.h
-libxl_internal_json.h: _libxl_types_internal_json.h
-xl.h: _paths.h
-
-$(LIBXL_OBJS) $(LIBXL_TEST_OBJS) $(LIBXLU_OBJS) \
- $(TEST_PROG_OBJS) $(SAVE_HELPER_OBJS): libxl.h
-$(LIBXL_OBJS) $(LIBXL_TEST_OBJS): libxl_internal.h
-
-_libxl_type%.h _libxl_type%_json.h _libxl_type%_private.h _libxl_type%.c: libxl_type%.idl gentypes.py idl.py
- $(eval stem = $(notdir $*))
- $(PYTHON) gentypes.py libxl_type$(stem).idl __libxl_type$(stem).h __libxl_type$(stem)_private.h \
- __libxl_type$(stem)_json.h __libxl_type$(stem).c
- $(call move-if-changed,__libxl_type$(stem).h,_libxl_type$(stem).h)
- $(call move-if-changed,__libxl_type$(stem)_private.h,_libxl_type$(stem)_private.h)
- $(call move-if-changed,__libxl_type$(stem)_json.h,_libxl_type$(stem)_json.h)
- $(call move-if-changed,__libxl_type$(stem).c,_libxl_type$(stem).c)
-
-libxenlight.so: libxenlight.so.$(MAJOR)
- $(SYMLINK_SHLIB) $< $@
-
-libxenlight.so.$(MAJOR): libxenlight.so.$(MAJOR).$(MINOR)
- $(SYMLINK_SHLIB) $< $@
-
-libxenlight.so.$(MAJOR).$(MINOR): $(LIBXL_OBJS)
- $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenlight.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LIBXL_LIBS) $(APPEND_LDFLAGS)
-
-libxenlight_test.so: $(LIBXL_OBJS) $(LIBXL_TEST_OBJS)
- $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenlight.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LIBXL_LIBS) $(APPEND_LDFLAGS)
-
-libxenlight.a: $(LIBXL_OBJS)
- $(AR) rcs libxenlight.a $^
-
libxlutil.so: libxlutil.so.$(XLUMAJOR)
$(SYMLINK_SHLIB) $< $@
libxlutil.so.$(XLUMAJOR): libxlutil.so.$(XLUMAJOR).$(XLUMINOR)
$(SYMLINK_SHLIB) $< $@
-libxlutil.so.$(XLUMAJOR).$(XLUMINOR): $(LIBXLU_OBJS) libxenlight.so
+libxlutil.so.$(XLUMAJOR).$(XLUMINOR): $(LIBXLU_OBJS)
$(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxlutil.so.$(XLUMAJOR) $(SHLIB_LDFLAGS) -o $@ $(LIBXLU_OBJS) $(LIBXLU_LIBS) $(APPEND_LDFLAGS)
libxlutil.a: $(LIBXLU_OBJS)
$(AR) rcs libxlutil.a $^
-test_%: test_%.o test_common.o libxenlight_test.so
- $(CC) $(LDFLAGS) -o $@ $^ $(filter-out %libxenlight.so, $(LDLIBS_libxenlight)) $(LDLIBS_libxentoollog) $(LDLIBS_libxentoolcore) -lyajl $(APPEND_LDFLAGS)
-
-libxl-save-helper: $(SAVE_HELPER_OBJS) libxenlight.so
- $(CC) $(LDFLAGS) -o $@ $(SAVE_HELPER_OBJS) $(LDLIBS_libxentoollog) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxentoolcore) $(APPEND_LDFLAGS)
-
-testidl: testidl.o libxenlight.so
- $(CC) $(LDFLAGS) -o $@ testidl.o $(LDLIBS_libxenlight) $(LDLIBS_libxentoollog) $(LDLIBS_libxentoolcore) $(APPEND_LDFLAGS)
-
.PHONY: install
install: all
$(INSTALL_DIR) $(DESTDIR)$(libdir)
$(INSTALL_DIR) $(DESTDIR)$(includedir)
- $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN)
- $(INSTALL_PROG) libxl-save-helper $(DESTDIR)$(LIBEXEC_BIN)
- $(INSTALL_SHLIB) libxenlight.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)
- $(SYMLINK_SHLIB) libxenlight.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)/libxenlight.so.$(MAJOR)
- $(SYMLINK_SHLIB) libxenlight.so.$(MAJOR) $(DESTDIR)$(libdir)/libxenlight.so
- $(INSTALL_DATA) libxenlight.a $(DESTDIR)$(libdir)
$(INSTALL_SHLIB) libxlutil.so.$(XLUMAJOR).$(XLUMINOR) $(DESTDIR)$(libdir)
$(SYMLINK_SHLIB) libxlutil.so.$(XLUMAJOR).$(XLUMINOR) $(DESTDIR)$(libdir)/libxlutil.so.$(XLUMAJOR)
$(SYMLINK_SHLIB) libxlutil.so.$(XLUMAJOR) $(DESTDIR)$(libdir)/libxlutil.so
$(INSTALL_DATA) libxlutil.a $(DESTDIR)$(libdir)
- $(INSTALL_DATA) libxl.h libxl_event.h libxl_json.h _libxl_types.h _libxl_types_json.h _libxl_list.h libxl_utils.h libxl_uuid.h libxlutil.h $(DESTDIR)$(includedir)
- $(INSTALL_DATA) xenlight.pc $(DESTDIR)$(PKG_INSTALLDIR)
+ $(INSTALL_DATA) libxlutil.h $(DESTDIR)$(includedir)
$(INSTALL_DATA) xlutil.pc $(DESTDIR)$(PKG_INSTALLDIR)
.PHONY: uninstall
uninstall:
- rm -f $(addprefix $(DESTDIR)$(PKG_INSTALLDIR)/,xlutil.pc xenlight.pc)
- rm -f $(addprefix $(DESTDIR)$(includedir)/,libxl.h libxl_event.h libxl_json.h _libxl_types.h _libxl_types_json.h _libxl_list.h libxl_utils.h libxl_uuid.h libxlutil.h)
+ rm -f $(DESTDIR)$(PKG_INSTALLDIR)/xlutil.pc
+ rm -f $(DESTDIR)$(includedir)/libxlutil.h
rm -f $(DESTDIR)$(libdir)/libxlutil.a
rm -f $(DESTDIR)$(libdir)/libxlutil.so
rm -f $(DESTDIR)$(libdir)/libxlutil.so.$(XLUMAJOR)
rm -f $(DESTDIR)$(libdir)/libxlutil.so.$(XLUMAJOR).$(XLUMINOR)
- rm -f $(DESTDIR)$(libdir)/libxenlight.a
- rm -f $(DESTDIR)$(libdir)/libxenlight.so
- rm -f $(DESTDIR)$(libdir)/libxenlight.so.$(MAJOR)
- rm -f $(DESTDIR)$(libdir)/libxenlight.so.$(MAJOR).$(MINOR)
- rm -f $(DESTDIR)$(LIBEXEC_BIN)/libxl-save-helper
.PHONY: clean
clean:
- $(RM) -f _*.h *.o *.so* *.a $(CLIENTS) $(DEPS_RM)
- $(RM) -f _*.c *.pyc _paths.*.tmp _*.api-for-check
- $(RM) -f testidl.c.new testidl.c *.api-ok
- $(RM) -f $(TEST_PROGS)
- $(RM) -f xenlight.pc
+ $(RM) -f _*.h *.o *.so* *.a $(DEPS_RM)
$(RM) -f xlutil.pc
- $(RM) -rf __pycache__
- $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) clean
distclean: clean
+++ /dev/null
-#!/usr/bin/perl -w
-use strict;
-our $needed=0;
-our $speclineoffset=0;
-our $specfile;
-while (<>) {
- if (m/^\# (\d+) \"(.*)\"$/) {
- $speclineoffset = $1 - $. -1;
- $specfile = $2;
- }
- my $file = defined($specfile) ? $specfile : $ARGV;
- my $line = $speclineoffset + $.;
- if (m/libxl_asyncop_how[^;]/) {
- $needed=1;
- }
- if (m/LIBXL_EXTERNAL_CALLERS_ONLY/) {
- $needed=0;
- }
- next unless $needed;
- if (m/\;/) {
- die "$file:$line:missing LIBXL_EXTERNAL_CALLERS_ONLY";
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_internal.h"
-#include <stdarg.h>
-
-/*
- * It is safe to store gc in the struct because:
- * - If it an actual gc, then the flexarray should not be used after the gc
- * have been freed.
- * - If it is a NOGC, then this point to a structure embedded in libxl_ctx,
- * therefore will survive across several libxl calls.
- */
-
-flexarray_t *flexarray_make(libxl__gc *gc, int size, int autogrow)
-{
- flexarray_t *array;
-
- GCNEW(array);
- array->size = size;
- array->autogrow = autogrow;
- array->count = 0;
- array->gc = gc;
- GCNEW_ARRAY(array->data, size);
-
- return array;
-}
-
-void flexarray_free(flexarray_t *array)
-{
- assert(!libxl__gc_is_real(array->gc));
- free(array->data);
- free(array);
-}
-
-void flexarray_grow(flexarray_t *array, int extents)
-{
- int newsize;
- libxl__gc *gc = array->gc;
-
- newsize = array->size + extents;
- GCREALLOC_ARRAY(array->data, newsize);
- array->size += extents;
-}
-
-int flexarray_set(flexarray_t *array, unsigned int idx, void *ptr)
-{
- if (idx >= array->size) {
- int newsize;
- if (!array->autogrow)
- return 1;
- newsize = (array->size * 2 < idx) ? idx + 1 : array->size * 2;
- flexarray_grow(array, newsize - array->size);
- }
- if ( idx + 1 > array->count )
- array->count = idx + 1;
- array->data[idx] = ptr;
- return 0;
-}
-
-int flexarray_append(flexarray_t *array, void *ptr)
-{
- return flexarray_set(array, array->count, ptr);
-}
-
-int flexarray_append_pair(flexarray_t *array, void *ptr1, void *ptr2)
-{
- int rc = flexarray_append(array, ptr1);
- if (!rc)
- rc = flexarray_append(array, ptr2);
- return rc;
-}
-
-int flexarray_vappend(flexarray_t *array, ...)
-{
- va_list va;
- void *ptr;
- int ret;
-
- va_start(va, array);
- for(ret = 0; (ptr = va_arg(va, void *)); ret++) {
- if ( flexarray_append(array, ptr) )
- break;
- }
- va_end(va);
- return ret;
-}
-
-int flexarray_get(flexarray_t *array, int idx, void **ptr)
-{
- if (idx >= array->size)
- return 1;
- *ptr = array->data[idx];
- return 0;
-}
-
-void **flexarray_contents(flexarray_t *array)
-{
- void **data;
- data = array->data;
- if (!libxl__gc_is_real(array->gc))
- free(array);
- return data;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#ifndef FLEXARRAY_H
-#define FLEXARRAY_H
-
-struct libxl__gc;
-
-typedef struct flexarray {
- int size;
- int autogrow;
- unsigned int count;
- void **data; /* array of pointer */
- struct libxl__gc *gc;
-} flexarray_t;
-
-/*
- * NOGC can be used with flexarrays, but flexarray_free will need to be called
- * to free the struct. The content of the flexarray will not be freed through
- * flexarray_free.
- */
-_hidden flexarray_t *flexarray_make(struct libxl__gc *gc_opt,
- int size, int autogrow);
-_hidden void flexarray_free(flexarray_t *array);
-_hidden void flexarray_grow(flexarray_t *array, int extents);
-_hidden int flexarray_set(flexarray_t *array, unsigned int index, void *ptr);
-_hidden int flexarray_append(flexarray_t *array, void *ptr);
-_hidden int flexarray_append_pair(flexarray_t *array, void *ptr1, void *ptr2);
-_hidden int flexarray_vappend(flexarray_t *array, ...);
-_hidden int flexarray_get(flexarray_t *array, int index, void **ptr);
-
-_hidden void **flexarray_contents(flexarray_t *array);
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-#!/usr/bin/python
-
-from __future__ import print_function
-
-import os
-import sys
-import re
-import random
-
-import idl
-
-def randomize_char(c):
- if random.random() < 0.5:
- return str.lower(c)
- else:
- return str.upper(c)
-
-def randomize_case(s):
- r = [randomize_char(c) for c in s]
- return "".join(r)
-
-def randomize_enum(e):
- return random.choice([v.name for v in e.values])
-
-handcoded = ["libxl_bitmap", "libxl_key_value_list",
- "libxl_cpuid_policy_list", "libxl_string_list"]
-
-def gen_rand_init(ty, v, indent = " ", parent = None):
- s = ""
- if isinstance(ty, idl.Enumeration):
- s += "%s = %s;\n" % (ty.pass_arg(v, parent is None), randomize_enum(ty))
- elif isinstance(ty, idl.Array):
- if parent is None:
- raise Exception("Array type must have a parent")
- s += "%s = test_rand(8);\n" % (parent + ty.lenvar.name)
- s += "%s = calloc(%s, sizeof(*%s));\n" % \
- (v, parent + ty.lenvar.name, v)
- s += "assert(%s);\n" % (v, )
- s += "{\n"
- s += " int i;\n"
- s += " for (i=0; i<%s; i++)\n" % (parent + ty.lenvar.name)
- s += gen_rand_init(ty.elem_type, v+"[i]",
- indent + " ", parent)
- s += "}\n"
- elif isinstance(ty, idl.KeyedUnion):
- if parent is None:
- raise Exception("KeyedUnion type must have a parent")
- s += gen_rand_init(ty.keyvar.type, parent + ty.keyvar.name, indent, parent)
- s += "switch (%s) {\n" % (parent + ty.keyvar.name)
- for f in ty.fields:
- (nparent,fexpr) = ty.member(v, f, parent is None)
- s += "case %s:\n" % f.enumname
- if f.type is not None:
- s += gen_rand_init(f.type, fexpr, indent + " ", nparent)
- s += " break;\n"
- s += "}\n"
- elif isinstance(ty, idl.Struct) \
- and (parent is None or ty.json_gen_fn is None):
- for f in [f for f in ty.fields if not f.const]:
- (nparent,fexpr) = ty.member(v, f, parent is None)
- s += gen_rand_init(f.type, fexpr, "", nparent)
- elif hasattr(ty, "rand_init") and ty.rand_init is not None:
- s += "%s(%s);\n" % (ty.rand_init,
- ty.pass_arg(v, isref=parent is None,
- passby=idl.PASS_BY_REFERENCE))
- elif ty.typename in ["libxl_uuid", "libxl_mac", "libxl_hwcap", "libxl_ms_vm_genid"]:
- s += "rand_bytes((uint8_t *)%s, sizeof(*%s));\n" % (v,v)
- elif ty.typename in ["libxl_domid", "libxl_devid"] or isinstance(ty, idl.Number):
- s += "%s = test_rand(sizeof(%s) * 8);\n" % \
- (ty.pass_arg(v, parent is None),
- ty.pass_arg(v, parent is None))
- elif ty.typename in ["bool"]:
- s += "%s = test_rand(2);\n" % v
- elif ty.typename in ["libxl_defbool"]:
- s += "libxl_defbool_set(%s, test_rand(2));\n" % v
- elif ty.typename in ["char *"]:
- s += "%s = rand_str();\n" % v
- elif ty.private:
- pass
- elif ty.typename in handcoded:
- raise Exception("Gen for handcoded %s" % ty.typename)
- else:
- raise Exception("Cannot randomly init %s" % ty.typename)
-
- if s != "":
- s = indent + s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-if __name__ == '__main__':
- if len(sys.argv) < 3:
- print("Usage: gentest.py <idl> <implementation>", file=sys.stderr)
- sys.exit(1)
-
- random.seed(os.getenv('LIBXL_TESTIDL_SEED'))
-
- (builtins,types) = idl.parse(sys.argv[1])
-
- impl = sys.argv[2]
- f = open(impl, "w")
- f.write("""
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include "libxl.h"
-#include "libxl_utils.h"
-
-static int test_rand(unsigned max)
-{
- /* We are not using rand() for its cryptographic properies. */
- return rand() % max;
-}
-
-static char *rand_str(void)
-{
- int i, sz = test_rand(32);
- char *s = malloc(sz+1);
- assert(s);
- for (i=0; i<sz; i++)
- s[i] = 'a' + test_rand(26);
- s[i] = '\\0';
- return s;
-}
-
-static void rand_bytes(uint8_t *p, size_t sz)
-{
- int i;
- for (i=0; i<sz; i++)
- p[i] = test_rand(256);
-}
-
-static void libxl_bitmap_rand_init(libxl_bitmap *bitmap)
-{
- int i;
- bitmap->size = test_rand(16);
- bitmap->map = calloc(bitmap->size, sizeof(*bitmap->map));
- assert(bitmap->map);
- libxl_for_each_bit(i, *bitmap) {
- if (test_rand(2))
- libxl_bitmap_set(bitmap, i);
- else
- libxl_bitmap_reset(bitmap, i);
- }
-}
-
-static void libxl_key_value_list_rand_init(libxl_key_value_list *pkvl)
-{
- int i, nr_kvp = test_rand(16);
- libxl_key_value_list kvl = calloc(nr_kvp+1, 2*sizeof(char *));
- assert(kvl);
-
- for (i = 0; i<2*nr_kvp; i += 2) {
- kvl[i] = rand_str();
- if (test_rand(8))
- kvl[i+1] = rand_str();
- else
- kvl[i+1] = NULL;
- }
- kvl[i] = NULL;
- kvl[i+1] = NULL;
- *pkvl = kvl;
-}
-
-static void libxl_cpuid_policy_list_rand_init(libxl_cpuid_policy_list *pp)
-{
- int i, nr_policies = test_rand(16);
- struct {
- const char *n;
- int w;
- } options[] = {
- /* A random selection from libxl_cpuid_parse_config */
- {"maxleaf", 32},
- {"family", 8},
- {"model", 8},
- {"stepping", 4},
- {"localapicid", 8},
- {"proccount", 8},
- {"clflush", 8},
- {"brandid", 8},
- {"f16c", 1},
- {"avx", 1},
- {"osxsave", 1},
- {"xsave", 1},
- {"aes", 1},
- {"popcnt", 1},
- {"movbe", 1},
- {"x2apic", 1},
- {"sse4.2", 1},
- {"sse4.1", 1},
- {"dca", 1},
- {"pdcm", 1},
- {"procpkg", 6},
- };
- const int nr_options = sizeof(options)/sizeof(options[0]);
- char buf[64];
- libxl_cpuid_policy_list p = NULL;
-
- for (i = 0; i < nr_policies; i++) {
- int opt = test_rand(nr_options);
- int val = test_rand(1<<options[opt].w);
- snprintf(buf, 64, \"%s=%#x\", options[opt].n, val);
- libxl_cpuid_parse_config(&p, buf);
- }
- *pp = p;
-}
-
-static void libxl_string_list_rand_init(libxl_string_list *p)
-{
- int i, nr = test_rand(16);
- libxl_string_list l = calloc(nr+1, sizeof(char *));
- assert(l);
-
- for (i = 0; i<nr; i++) {
- l[i] = rand_str();
- }
- l[i] = NULL;
- *p = l;
-}
-""")
- for ty in builtins + types:
- if isinstance(ty, idl.Number): continue
- if ty.typename not in handcoded:
- f.write("static void %s_rand_init(%s);\n" % \
- (ty.typename,
- ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
- f.write("static void %s_rand_init(%s)\n" % \
- (ty.typename,
- ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
- f.write("{\n")
- f.write(gen_rand_init(ty, "p"))
- f.write("}\n")
- f.write("\n")
- ty.rand_init = "%s_rand_init" % ty.typename
-
- f.write("""
-int main(int argc, char **argv)
-{
-""")
-
- for ty in types:
- f.write(" %s %s_val, %s_val_new;\n" % \
- (ty.typename, ty.typename, ty.typename))
- f.write("""
- int rc;
- char *s, *new_s, *json_string;
- xentoollog_logger_stdiostream *logger;
- libxl_ctx *ctx;
-
- logger = xtl_createlogger_stdiostream(stderr, XTL_DETAIL, 0);
- if (!logger) exit(1);
-
- if (libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, (xentoollog_logger*)logger)) {
- fprintf(stderr, "cannot init xl context\\n");
- exit(1);
- }
-""")
- f.write(" printf(\"Testing TYPE_to/from_json()\\n\");\n")
- f.write(" printf(\"----------------------\\n\");\n")
- f.write(" printf(\"\\n\");\n")
- for ty in [t for t in types if t.json_gen_fn is not None]:
- arg = ty.typename + "_val"
- f.write(" %s_rand_init(%s);\n" % (ty.typename, \
- ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
- if not isinstance(ty, idl.Enumeration):
- iters = random.randrange(1,10)
- while iters > 0:
- f.write(" %s_init(%s_new);\n" % (ty.typename, \
- ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
- iters -= 1
- f.write(" s = %s_to_json(ctx, %s);\n" % \
- (ty.typename, ty.pass_arg(arg, isref=False)))
- f.write(" printf(\"%%s: %%s\\n\", \"%s\", s);\n" % ty.typename)
- f.write(" if (s == NULL) abort();\n")
- f.write(" rc = %s_from_json(ctx, &%s_val_new, s);\n" % \
- (ty.typename, ty.typename))
- f.write(" if (rc) abort();\n")
- f.write(" new_s = %s_to_json(ctx, %s_new);\n" % \
- (ty.typename, ty.pass_arg(arg, isref=False)))
- f.write(" if (new_s == NULL) abort();\n")
- f.write(" if (strcmp(s, new_s)) {\n")
- f.write(" printf(\"Huh? Regenerated string different from original string.\\n\");\n")
- f.write(" printf(\"Regenerated string: %s\\n\", new_s);\n")
- f.write(" abort();\n")
- f.write(" }\n")
- f.write(" free(s);\n")
- f.write(" free(new_s);\n")
- if ty.dispose_fn is not None:
- iters = random.randrange(1,10)
- f.write(" %s(&%s_val);\n" % (ty.dispose_fn, ty.typename))
- while iters > 0:
- f.write(" %s(&%s_val_new);\n" % (ty.dispose_fn, ty.typename))
- iters -= 1
- f.write("\n")
-
- f.write(" printf(\"Testing TYPE_copy()\\n\");\n")
- f.write(" printf(\"----------------------\\n\");\n")
- f.write(" printf(\"\\n\");\n")
- for ty in [t for t in types if t.copy_fn is not None]:
- f.write(" printf(\"Testing %s_copy, \");\n" % ty.typename)
- arg = ty.typename + "_val"
- f.write(" %s_init(%s);\n" % (ty.typename, \
- ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
- f.write(" %s_rand_init(%s);\n" % (ty.typename, \
- ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
- f.write(" %s_init(%s_new);\n" % (ty.typename, \
- ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
- f.write(" %s_copy(ctx, %s_new, %s);\n" % (ty.typename, \
- ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE), \
- ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
- f.write(" s = %s_to_json(ctx, %s);\n" % \
- (ty.typename, ty.pass_arg(arg, isref=False)))
- f.write(" if (s == NULL) abort();\n")
- f.write(" new_s = %s_to_json(ctx, %s_new);\n" % \
- (ty.typename, ty.pass_arg(arg, isref=False)))
- f.write(" if (new_s == NULL) abort();\n")
- f.write(" if (strcmp(s, new_s)) {\n")
- f.write(" printf(\"Huh? Deep copy for %s failed. Regenerated string different from original string.\\n\");\n" \
- % ty.typename)
- f.write(" printf(\"Original string: %s\\n\", s);\n")
- f.write(" printf(\"Regenerated string: %s\\n\", new_s);\n")
- f.write(" abort();\n")
- f.write(" }\n")
- f.write(" free(s);\n")
- f.write(" free(new_s);\n")
- if ty.dispose_fn is not None:
- f.write(" %s(&%s_val);\n" % (ty.dispose_fn, ty.typename))
- f.write(" %s(&%s_val_new);\n" % (ty.dispose_fn, ty.typename))
- f.write(" printf(\"done\\n\");\n")
- f.write("\n")
-
- f.write(" printf(\"\\n\");\n")
- f.write(" printf(\"Testing Enumerations\\n\");\n")
- f.write(" printf(\"--------------------\\n\");\n")
- f.write(" printf(\"\\n\");\n")
- for ty in [t for t in types if isinstance(t,idl.Enumeration)]:
- f.write(" printf(\"%s -- to string:\\n\");\n" % (ty.typename))
- for v in ty.values:
- f.write(" printf(\"\\t%s = %%d = \\\"%%s\\\"\\n\", " \
- "%s, %s_to_string(%s));\n" % \
- (v.valuename, v.name, ty.typename, v.name))
- f.write("\n")
-
- f.write(" printf(\"%s -- to JSON:\\n\");\n" % (ty.typename))
- for v in ty.values:
- f.write(" json_string = %s_to_json(ctx, %s);\n" % \
- (ty.typename, v.name))
- f.write(" printf(\"\\t%s = %%d = %%s\", " \
- "%s, json_string);\n" %\
- (v.valuename, v.name))
- f.write(" free(json_string);\n");
- f.write(" json_string = NULL;\n");
- f.write("\n")
-
- f.write(" printf(\"%s -- from string:\\n\");\n" % (ty.typename))
- for v in [v.valuename for v in ty.values] + ["AN INVALID VALUE"]:
- n = randomize_case(v)
- f.write(" %s_val = -1;\n" % (ty.typename))
- f.write(" rc = %s_from_string(\"%s\", &%s_val);\n" %\
- (ty.typename, n, ty.typename))
-
- f.write(" printf(\"\\t%s = \\\"%%s\\\" = %%d (rc %%d)\\n\", " \
- "\"%s\", %s_val, rc);\n" %\
- (v, n, ty.typename))
- f.write("\n")
-
- f.write("""
-
- libxl_ctx_free(ctx);
- xtl_logger_destroy((xentoollog_logger*)logger);
-
- return 0;
-}
-""")
+++ /dev/null
-#!/usr/bin/python
-
-from __future__ import print_function
-
-import sys
-import re
-
-import idl
-
-def libxl_C_instance_of(ty, instancename):
- if isinstance(ty, idl.Aggregate) and ty.typename is None:
- if instancename is None:
- return libxl_C_type_define(ty)
- else:
- return libxl_C_type_define(ty) + " " + instancename
-
- s = ""
- if isinstance(ty, idl.Array):
- s += libxl_C_instance_of(ty.lenvar.type, ty.lenvar.name) + ";\n"
-
- return s + ty.typename + " " + instancename
-
-def libxl_C_type_define(ty, indent = ""):
- s = ""
- if isinstance(ty, idl.Enumeration):
- if ty.typename is None:
- s += "enum {\n"
- else:
- s += "typedef enum %s {\n" % ty.typename
-
- for v in ty.values:
- x = "%s = %d" % (v.name, v.value)
- x = x.replace("\n", "\n ")
- s += " " + x + ",\n"
- if ty.typename is None:
- s += "}"
- else:
- s += "} %s" % ty.typename
-
- elif isinstance(ty, idl.Aggregate):
- if isinstance(ty, idl.KeyedUnion):
- s += libxl_C_instance_of(ty.keyvar.type, ty.keyvar.name) + ";\n"
-
- if ty.typename is None:
- s += "%s {\n" % ty.kind
- else:
- s += "typedef %s %s {\n" % (ty.kind, ty.typename)
-
- for f in ty.fields:
- if isinstance(ty, idl.KeyedUnion) and f.type is None: continue
-
- x = libxl_C_instance_of(f.type, f.name)
- if f.const:
- x = "const " + x
- x = x.replace("\n", "\n ")
- s += " " + x + ";\n"
- if ty.typename is None:
- s += "}"
- else:
- s += "} %s" % ty.typename
- else:
- raise NotImplementedError("%s" % type(ty))
- return s.replace("\n", "\n%s" % indent)
-
-def libxl_C_type_dispose(ty, v, indent = " ", parent = None):
- s = ""
- if isinstance(ty, idl.KeyedUnion):
- if parent is None:
- raise Exception("KeyedUnion type must have a parent")
- s += "switch (%s) {\n" % (parent + ty.keyvar.name)
- for f in ty.fields:
- (nparent,fexpr) = ty.member(v, f, parent is None)
- s += "case %s:\n" % f.enumname
- if f.type is not None:
- s += libxl_C_type_dispose(f.type, fexpr, indent + " ", nparent)
- s += " break;\n"
- s += "}\n"
- elif isinstance(ty, idl.Array):
- if parent is None:
- raise Exception("Array type must have a parent")
- if ty.elem_type.dispose_fn is not None:
- s += "{\n"
- s += " int i;\n"
- s += " for (i=0; i<%s; i++)\n" % (parent + ty.lenvar.name)
- s += libxl_C_type_dispose(ty.elem_type, v+"[i]",
- indent + " ", parent)
- if ty.dispose_fn is not None:
- if ty.elem_type.dispose_fn is not None:
- s += " "
- s += "%s(%s);\n" % (ty.dispose_fn, ty.pass_arg(v, parent is None))
- if ty.elem_type.dispose_fn is not None:
- s += "}\n"
- elif isinstance(ty, idl.Struct) and (parent is None or ty.dispose_fn is None):
- for f in [f for f in ty.fields if not f.const]:
- (nparent,fexpr) = ty.member(v, f, parent is None)
- s += libxl_C_type_dispose(f.type, fexpr, "", nparent)
- else:
- if ty.dispose_fn is not None:
- s += "%s(%s);\n" % (ty.dispose_fn, ty.pass_arg(v, parent is None))
-
- if s != "":
- s = indent + s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_type_copy(ty, v, w, indent = " ", vparent = None, wparent = None):
- s = ""
-
- if vparent is None:
- s += "GC_INIT(ctx);\n";
-
- if isinstance(ty, idl.KeyedUnion):
- if vparent is None or wparent is None:
- raise Exception("KeyedUnion type must have a parent")
- s += "%s = %s;\n" % ((vparent + ty.keyvar.name), (wparent + ty.keyvar.name))
- s += "switch (%s) {\n" % (wparent + ty.keyvar.name)
- for f in ty.fields:
- (vnparent,vfexpr) = ty.member(v, f, vparent is None)
- (wnparent,wfexpr) = ty.member(w, f, wparent is None)
- s += "case %s:\n" % f.enumname
- if f.type is not None:
- s += libxl_C_type_copy(f.type, vfexpr, wfexpr, indent + " ",
- vnparent, wnparent)
- s += " break;\n"
- s += "}\n"
- elif isinstance(ty, idl.Array):
- if vparent is None or wparent is None:
- raise Exception("Array type must have a parent")
- s += "%s = libxl__calloc(NOGC, %s, sizeof(*%s));\n" % (ty.pass_arg(v, vparent is None),
- (wparent + ty.lenvar.name),
- ty.pass_arg(w, wparent is None))
- s += "%s = %s;\n" % ((vparent + ty.lenvar.name), (wparent + ty.lenvar.name))
- s += "{\n"
- s += " int i;\n"
- s += " for (i=0; i<%s; i++)\n" % (wparent + ty.lenvar.name)
- s += libxl_C_type_copy(ty.elem_type, v+"[i]", w+"[i]",
- indent + " ", vparent, wparent)
- s += "}\n"
- elif isinstance(ty, idl.Struct) and ((vparent is None and wparent is None) or ty.copy_fn is None):
- for f in [f for f in ty.fields if not f.const and not f.type.private]:
- (vnparent,vfexpr) = ty.member(v, f, vparent is None)
- (wnparent,wfexpr) = ty.member(w, f, wparent is None)
- s += libxl_C_type_copy(f.type, vfexpr, wfexpr, "", vnparent, wnparent)
- else:
- if ty.copy_fn is not None:
- s += "%s(ctx, %s, %s);\n" % (ty.copy_fn,
- ty.pass_arg(v, vparent is None, passby=idl.PASS_BY_REFERENCE),
- ty.pass_arg(w, wparent is None, passby=idl.PASS_BY_REFERENCE))
-
- else:
- s += "%s = %s;\n" % (ty.pass_arg(v, vparent is None, passby=idl.PASS_BY_VALUE),
- ty.pass_arg(w, wparent is None, passby=idl.PASS_BY_VALUE))
-
- if vparent is None:
- s += "GC_FREE;\n"
-
- if s != "":
- s = indent + s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_init_members(ty, nesting = 0):
- """Returns a list of members of ty which require a separate init"""
-
- if isinstance(ty, idl.Aggregate):
- return [f for f in ty.fields if not f.const and isinstance(f.type,idl.KeyedUnion)]
- else:
- return []
-
-def libxl_C_type_do_init(ty, pass_arg, need_zero=True, indent=" "):
- s=indent
- if ty.init_val is not None:
- s+= "%s = %s;\n" % (pass_arg(idl.PASS_BY_VALUE), ty.init_val)
- elif ty.init_fn is not None:
- s+= "%s(%s);\n" % (ty.init_fn, pass_arg(idl.PASS_BY_REFERENCE))
- elif need_zero:
- ptr = pass_arg(idl.PASS_BY_REFERENCE)
- s+= "memset(%s, 0, sizeof(*%s));\n" % (ptr, ptr)
- else:
- s=""
- return s
-
-def _libxl_C_type_init(ty, v, indent = " ", parent = None, subinit=False):
- s = ""
- if isinstance(ty, idl.KeyedUnion):
- if parent is None:
- raise Exception("KeyedUnion type must have a parent")
- if subinit:
- s += "switch (%s) {\n" % (parent + ty.keyvar.name)
- for f in ty.fields:
- (nparent,fexpr) = ty.member(v, f, parent is None)
- s += "case %s:\n" % f.enumname
- if f.type is not None:
- s += _libxl_C_type_init(f.type, fexpr, " ", nparent)
- s += " break;\n"
- s += "}\n"
- else:
- if ty.keyvar.init_val:
- s += "%s = %s;\n" % (parent + ty.keyvar.name, ty.keyvar.init_val)
- elif ty.keyvar.type.init_val:
- s += "%s = %s;\n" % (parent + ty.keyvar.name, ty.keyvar.type.init_val)
- elif isinstance(ty, idl.Struct) and (parent is None or ty.init_fn is None):
- for f in [f for f in ty.fields if not f.const]:
- (nparent,fexpr) = ty.member(v, f, parent is None)
- if f.init_val is not None:
- s += "%s = %s;\n" % (fexpr, f.init_val)
- else:
- s += _libxl_C_type_init(f.type, fexpr, "", nparent)
- else:
- if ty.init_val is not None:
- s += "%s = %s;\n" % (ty.pass_arg(v, parent is None), ty.init_val)
- elif ty.init_fn is not None:
- s += "%s(%s);\n" % (ty.init_fn, ty.pass_arg(v, parent is None))
-
- if s != "":
- s = indent + s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_type_init(ty):
- s = ""
- s += "void %s(%s)\n" % (ty.init_fn, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE))
- s += "{\n"
- s += " memset(p, '\\0', sizeof(*p));\n"
- s += _libxl_C_type_init(ty, "p")
- s += "}\n"
- s += "\n"
- return s
-
-def libxl_C_type_member_init(ty, field):
- if not isinstance(field.type, idl.KeyedUnion):
- raise Exception("Only KeyedUnion is supported for member init")
-
- ku = field.type
-
- s = ""
- s += "void %s(%s, %s)\n" % (ty.init_fn + "_" + ku.keyvar.name,
- ty.make_arg("p", passby=idl.PASS_BY_REFERENCE),
- ku.keyvar.type.make_arg(ku.keyvar.name))
- s += "{\n"
-
- if ku.keyvar.init_val is not None:
- init_val = ku.keyvar.init_val
- elif ku.keyvar.type.init_val is not None:
- init_val = ku.keyvar.type.init_val
- else:
- init_val = None
-
- (nparent,fexpr) = ty.member(ty.pass_arg("p"), ku.keyvar, isref=True)
- if init_val is not None:
- s += " assert(%s == %s);\n" % (fexpr, init_val)
- else:
- s += " assert(!%s);\n" % (fexpr)
- s += " %s = %s;\n" % (fexpr, ku.keyvar.name)
-
- (nparent,fexpr) = ty.member(ty.pass_arg("p"), field, isref=True)
- s += _libxl_C_type_init(ku, fexpr, parent=nparent, subinit=True)
- s += "}\n"
- s += "\n"
- return s
-
-def libxl_C_type_gen_map_key(f, parent, indent = ""):
- s = ""
- if isinstance(f.type, idl.KeyedUnion):
- s += "switch (%s) {\n" % (parent + f.type.keyvar.name)
- for x in f.type.fields:
- v = f.type.keyvar.name + "." + x.name
- s += "case %s:\n" % x.enumname
- s += " s = yajl_gen_string(hand, (const unsigned char *)\"%s\", sizeof(\"%s\")-1);\n" % (v, v)
- s += " if (s != yajl_gen_status_ok)\n"
- s += " goto out;\n"
- s += " break;\n"
- s += "}\n"
- else:
- s += "s = yajl_gen_string(hand, (const unsigned char *)\"%s\", sizeof(\"%s\")-1);\n" % (f.name, f.name)
- s += "if (s != yajl_gen_status_ok)\n"
- s += " goto out;\n"
- if s != "":
- s = indent + s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_type_copy_deprecated(field, v, indent = " ", vparent = None):
- s = ""
-
- if isinstance(field.type, idl.KeyedUnion):
- if vparent is None:
- raise Exception("KeyedUnion type must have a parent")
- s += "switch (%s) {\n" % (vparent + field.type.keyvar.name)
- for f in [f for f in field.type.fields if not f.const]:
- (vnparent,vfexpr) = ty.member(v, f, vparent is None)
- s += "case %s:\n" % f.enumname
- if f.type is not None:
- s += libxl_C_type_copy_deprecated(f, vfexpr, indent, vnparent)
- s+= " break;\n"
- s+="}\n";
- elif isinstance(field.type, idl.Array) and field.deprecated_by:
- raise Exception("Array type is not supported for deprecation")
- elif isinstance(field.type, idl.Struct) and field.type.copy_fn is None:
- for f in [f for f in field.type.fields if not f.const]:
- (vnparent,vfexpr) = ty.member(v, f, vparent is None)
- s += libxl_C_type_copy_deprecated(f, vfexpr, "", vnparent)
- elif field.deprecated_by is not None:
- if field.type.check_default_fn is None:
- raise Exception(
-"Deprecated field %s type doesn't have a default value checker" % field.name)
- field_pass = lambda by: field.type.pass_arg(v, vparent is None,
- passby=by)
- field_val = field_pass(idl.PASS_BY_VALUE)
- field_ptr = field_pass(idl.PASS_BY_REFERENCE)
- s+= "if (!%s(&p->%s) && !%s(%s))\n" % (field.type.check_default_fn,
- field.deprecated_by,
- field.type.check_default_fn,
- field_ptr)
- s+= " return -EINVAL;\n"
- s+="(void) (&p->%s == %s);\n" % (field.deprecated_by, field_ptr)
- s+= "if (%s(&p->%s)) {\n" % (field.type.check_default_fn,
- field.deprecated_by)
- s+= " "
- if field.type.copy_fn is not None:
- s+= "%s(ctx, &p->%s, %s);\n" % (field.type.copy_fn,
- field.deprecated_by, field_ptr)
- else:
- s+= "p->%s = %s;\n" % (field.deprecated_by, field_val)
-
- if field.type.dispose_fn is not None:
- s+= " %s(%s);\n" % (field.type.dispose_fn,
- field.type.pass_arg(v, vparent is None))
- s+=libxl_C_type_do_init(field.type, field_pass)
- s+= "}\n"
-
- if s != "":
- s = indent + s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def get_init_val(f):
- if f.init_val is not None:
- return f.init_val
- elif f.type.init_val is not None:
- return f.type.init_val
- return None
-
-def get_default_expr(f, nparent, fexpr):
- if isinstance(f.type, idl.Aggregate):
- return "1 /* always generate JSON output for aggregate type */"
-
- if isinstance(f.type, idl.Array):
- return "%s && %s" % (fexpr, nparent + f.type.lenvar.name)
-
- init_val = get_init_val(f)
- if init_val is not None:
- return "%s != %s" % (fexpr, init_val)
-
- if f.type.check_default_fn:
- return "!%s(&%s)" % (f.type.check_default_fn, fexpr)
-
- return "%s" % fexpr
-
-def libxl_C_type_gen_json(ty, v, indent = " ", parent = None):
- s = ""
- if parent is None:
- s += "yajl_gen_status s;\n"
-
- if isinstance(ty, idl.Array):
- if parent is None:
- raise Exception("Array type must have a parent")
- s += "{\n"
- s += " int i;\n"
- s += " s = yajl_gen_array_open(hand);\n"
- s += " if (s != yajl_gen_status_ok)\n"
- s += " goto out;\n"
- s += " for (i=0; i<%s; i++) {\n" % (parent + ty.lenvar.name)
- s += libxl_C_type_gen_json(ty.elem_type, v+"[i]",
- indent + " ", parent)
- s += " }\n"
- s += " s = yajl_gen_array_close(hand);\n"
- s += " if (s != yajl_gen_status_ok)\n"
- s += " goto out;\n"
- s += "}\n"
- elif isinstance(ty, idl.Enumeration):
- s += "s = libxl__yajl_gen_enum(hand, %s_to_string(%s));\n" % (ty.typename, ty.pass_arg(v, parent is None))
- s += "if (s != yajl_gen_status_ok)\n"
- s += " goto out;\n"
- elif isinstance(ty, idl.KeyedUnion):
- if parent is None:
- raise Exception("KeyedUnion type must have a parent")
- s += "switch (%s) {\n" % (parent + ty.keyvar.name)
- for f in ty.fields:
- (nparent,fexpr) = ty.member(v, f, parent is None)
- s += "case %s:\n" % f.enumname
- if f.type is not None:
- s += libxl_C_type_gen_json(f.type, fexpr, indent + " ", nparent)
- else:
- s += " s = yajl_gen_map_open(hand);\n"
- s += " if (s != yajl_gen_status_ok)\n"
- s += " goto out;\n"
- s += " s = yajl_gen_map_close(hand);\n"
- s += " if (s != yajl_gen_status_ok)\n"
- s += " goto out;\n"
- s += " break;\n"
- s += "}\n"
- elif isinstance(ty, idl.Struct) and (parent is None or ty.json_gen_fn is None):
- s += "s = yajl_gen_map_open(hand);\n"
- s += "if (s != yajl_gen_status_ok)\n"
- s += " goto out;\n"
- for f in [f for f in ty.fields if not f.const and not f.type.private]:
- (nparent,fexpr) = ty.member(v, f, parent is None)
- default_expr = get_default_expr(f, nparent, fexpr)
- s += "if (%s) {\n" % default_expr
-
- s += libxl_C_type_gen_map_key(f, nparent, " ")
- s += libxl_C_type_gen_json(f.type, fexpr, " ", nparent)
-
- s += "}\n"
-
- s += "s = yajl_gen_map_close(hand);\n"
- s += "if (s != yajl_gen_status_ok)\n"
- s += " goto out;\n"
- else:
- if ty.json_gen_fn is not None:
- s += "s = %s(hand, %s);\n" % (ty.json_gen_fn, ty.pass_arg(v, parent is None))
- s += "if (s != yajl_gen_status_ok)\n"
- s += " goto out;\n"
-
- if parent is None:
- s += "out:\n"
- s += "return s;\n"
-
- if s != "":
- s = indent + s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_type_to_json(ty, v, indent = " "):
- s = ""
- gen = "(libxl__gen_json_callback)&%s_gen_json" % ty.typename
- s += "return libxl__object_to_json(ctx, \"%s\", %s, (void *)%s);\n" % (ty.typename, gen, ty.pass_arg(v, passby=idl.PASS_BY_REFERENCE))
-
- if s != "":
- s = indent + s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_type_parse_json(ty, w, v, indent = " ", parent = None, discriminator = None):
- s = ""
- if parent is None:
- s += "int rc = 0;\n"
- s += "const libxl__json_object *x __attribute__((__unused__)) = o;\n"
-
- if isinstance(ty, idl.Array):
- if parent is None:
- raise Exception("Array type must have a parent")
- if discriminator is not None:
- raise Exception("Only KeyedUnion can have discriminator")
- lenvar = parent + ty.lenvar.name
- s += "{\n"
- s += " libxl__json_object *t;\n"
- s += " int i;\n"
- s += " if (!libxl__json_object_is_array(x)) {\n"
- s += " rc = -1;\n"
- s += " goto out;\n"
- s += " }\n"
- s += " %s = x->u.array->count;\n" % lenvar
- s += " %s = libxl__calloc(NOGC, %s, sizeof(*%s));\n" % (v, lenvar, v)
- s += " if (!%s && %s != 0) {\n" % (v, lenvar)
- s += " rc = -1;\n"
- s += " goto out;\n"
- s += " }\n"
- s += " for (i=0; (t=libxl__json_array_get(x,i)); i++) {\n"
- s += libxl_C_type_do_init(ty.elem_type,
- lambda by: ("&" if by == idl.PASS_BY_REFERENCE else "")+
- ("%s[i]" % v),
- need_zero=False, indent=indent+" ")
- s += libxl_C_type_parse_json(ty.elem_type, "t", v+"[i]",
- indent + " ", parent)
- s += " }\n"
- s += " if (i != %s) {\n" % lenvar
- s += " rc = -1;\n"
- s += " goto out;\n"
- s += " }\n"
- s += "}\n"
- elif isinstance(ty, idl.Enumeration):
- if discriminator is not None:
- raise Exception("Only KeyedUnion can have discriminator")
- s += "{\n"
- s += " const char *enum_str;\n"
- s += " if (!libxl__json_object_is_string(%s)) {\n" % w
- s += " rc = -1;\n"
- s += " goto out;\n"
- s += " }\n"
- s += " enum_str = libxl__json_object_get_string(%s);\n" % w
- s += " rc = %s_from_string(enum_str, %s);\n" % (ty.typename, ty.pass_arg(v, parent is None, idl.PASS_BY_REFERENCE))
- s += " if (rc)\n"
- s += " goto out;\n"
- s += "}\n"
- elif isinstance(ty, idl.KeyedUnion):
- if parent is None:
- raise Exception("KeyedUnion type must have a parent")
- if discriminator is None:
- raise Excpetion("KeyedUnion type must have a discriminator")
- for f in ty.fields:
- if f.enumname != discriminator:
- continue
- (nparent,fexpr) = ty.member(v, f, parent is None)
- if f.type is not None:
- s += libxl_C_type_parse_json(f.type, w, fexpr, indent + " ", nparent)
- elif isinstance(ty, idl.Struct) and (parent is None or ty.json_parse_fn is None):
- if discriminator is not None:
- raise Exception("Only KeyedUnion can have discriminator")
- for f in [f for f in ty.fields if not f.const and not f.type.private]:
- saved_var_name = "saved_%s" % f.name
- s += "{\n"
- s += " const libxl__json_object *%s = x;\n" % saved_var_name
- if isinstance(f.type, idl.KeyedUnion):
- for x in f.type.fields:
- s += " x = libxl__json_map_get(\"%s\", %s, JSON_MAP);\n" % \
- (f.type.keyvar.name + "." + x.name, w)
- s += " if (x) {\n"
- (nparent, fexpr) = ty.member(v, f.type.keyvar, parent is None)
- s += " %s_init_%s(%s, %s);\n" % (ty.typename, f.type.keyvar.name, v, x.enumname)
- (nparent,fexpr) = ty.member(v, f, parent is None)
- s += libxl_C_type_parse_json(f.type, "x", fexpr, " ", nparent, x.enumname)
- s += " }\n"
- else:
- s += " x = libxl__json_map_get(\"%s\", %s, %s);\n" % (f.name, w, f.type.json_parse_type)
- s += " if (x) {\n"
- (nparent,fexpr) = ty.member(v, f, parent is None)
- s += libxl_C_type_parse_json(f.type, "x", fexpr, " ", nparent)
- s += " }\n"
- s += " x = %s;\n" % saved_var_name
- s += "}\n"
- else:
- if discriminator is not None:
- raise Exception("Only KeyedUnion can have discriminator")
- if ty.json_parse_fn is not None:
- s += "rc = %s(gc, %s, &%s);\n" % (ty.json_parse_fn, w, v)
- s += "if (rc)\n"
- s += " goto out;\n"
-
- if parent is None:
- s += "out:\n"
- s += "return rc;\n"
-
- if s != "":
- s = indent +s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_type_from_json(ty, v, w, indent = " "):
- s = ""
- parse = "(libxl__json_parse_callback)&%s_parse_json" % (ty.namespace + "_" + ty.rawname)
- s += "return libxl__object_from_json(ctx, \"%s\", %s, %s, %s);\n" % (ty.typename, parse, v, w)
-
- if s != "":
- s = indent + s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_enum_to_string(ty, e, indent = " "):
- s = ""
- s += "switch(%s) {\n" % e
- for v in ty.values:
- s += " case %s:\n" % (v.name)
- s += " return \"%s\";\n" % (v.valuename.lower())
- s += " default:\n "
- s += " return NULL;\n"
- s += "}\n"
-
- if s != "":
- s = indent + s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_enum_strings(ty, indent=""):
- s = ""
- s += "libxl_enum_string_table %s_string_table[] = {\n" % (ty.typename)
- for v in ty.values:
- s += " { .s = \"%s\", .v = %s },\n" % (v.valuename.lower(), v.name)
- s += " { NULL, -1 },\n"
- s += "};\n"
- s += "\n"
-
- if s != "":
- s = indent + s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_enum_from_string(ty, str, e, indent = " "):
- s = ""
- s += "return libxl__enum_from_string(%s_string_table,\n" % ty.typename
- s += " %s, (int *)%s);\n" % (str, e)
-
- if s != "":
- s = indent + s
- return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-
-if __name__ == '__main__':
- if len(sys.argv) != 6:
- print("Usage: gentypes.py <idl> <header> <header-private> <header-json> <implementation>", file=sys.stderr)
- sys.exit(1)
-
- (_, idlname, header, header_private, header_json, impl) = sys.argv
-
- (builtins,types) = idl.parse(idlname)
-
- print("outputting libxl type definitions to %s" % header)
-
- f = open(header, "w")
-
- header_define = header.upper().replace('.','_')
- f.write("""#ifndef %s
-#define %s
-
-/*
- * DO NOT EDIT.
- *
- * This file is autogenerated by
- * "%s"
- */
-
-""" % (header_define, header_define, " ".join(sys.argv)))
-
- for ty in types:
- f.write(libxl_C_type_define(ty) + ";\n")
- if ty.dispose_fn is not None:
- f.write("%svoid %s(%s);\n" % (ty.hidden(), ty.dispose_fn, ty.make_arg("p")))
- if ty.copy_deprecated_fn is not None:
- f.write("%sint %s(libxl_ctx *ctx, %s);\n" % (ty.hidden(),
- ty.copy_deprecated_fn,
- ty.make_arg("p")))
- if ty.copy_fn is not None:
- f.write("%svoid %s(libxl_ctx *ctx, %s, const %s);\n" % (ty.hidden(), ty.copy_fn,
- ty.make_arg("dst"), ty.make_arg("src")))
- if ty.init_fn is not None:
- f.write("%svoid %s(%s);\n" % (ty.hidden(), ty.init_fn, ty.make_arg("p")))
- for field in libxl_init_members(ty):
- if not isinstance(field.type, idl.KeyedUnion):
- raise Exception("Only KeyedUnion is supported for member init")
- ku = field.type
- f.write("%svoid %s(%s, %s);\n" % (ty.hidden(), ty.init_fn + "_" + ku.keyvar.name,
- ty.make_arg("p"),
- ku.keyvar.type.make_arg(ku.keyvar.name)))
- if ty.json_gen_fn is not None:
- f.write("%schar *%s_to_json(libxl_ctx *ctx, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p")))
- if ty.json_parse_fn is not None:
- f.write("%sint %s_from_json(libxl_ctx *ctx, %s, const char *s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
- if isinstance(ty, idl.Enumeration):
- f.write("%sconst char *%s_to_string(%s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p")))
- f.write("%sint %s_from_string(const char *s, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("e", passby=idl.PASS_BY_REFERENCE)))
- f.write("%sextern libxl_enum_string_table %s_string_table[];\n" % (ty.hidden(), ty.typename))
- f.write("\n")
-
- f.write("""#endif /* %s */\n""" % (header_define))
- f.close()
-
- print("outputting libxl JSON definitions to %s" % header_json)
-
- f = open(header_json, "w")
-
- header_json_define = header_json.upper().replace('.','_')
- f.write("""#ifndef %s
-#define %s
-
-/*
- * DO NOT EDIT.
- *
- * This file is autogenerated by
- * "%s"
- */
-
-""" % (header_json_define, header_json_define, " ".join(sys.argv)))
-
- for ty in [ty for ty in types if ty.json_gen_fn is not None]:
- f.write("%syajl_gen_status %s_gen_json(yajl_gen hand, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
-
- f.write("\n")
- f.write("""#endif /* %s */\n""" % header_json_define)
- f.close()
-
- print("outputting libxl type internal definitions to %s" % header_private)
-
- f = open(header_private, "w")
-
- header_private_define = header_private.upper().replace('.','_')
- f.write("""#ifndef %s
-#define %s
-
-/*
- * DO NOT EDIT.
- *
- * This file is autogenerated by
- * "%s"
- */
-
-""" % (header_private_define, header_private_define, " ".join(sys.argv)))
-
- for ty in [ty for ty in types if ty.json_parse_fn is not None]:
- f.write("%sint %s_parse_json(libxl__gc *gc, const libxl__json_object *o, %s);\n" % \
- (ty.hidden(), ty.namespace + "_" + ty.rawname,
- ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
-
- f.write("\n")
- f.write("""#endif /* %s */\n""" % header_json_define)
- f.close()
-
- print("outputting libxl type implementations to %s" % impl)
-
- f = open(impl, "w")
- f.write("""
-/* DO NOT EDIT.
- *
- * This file is autogenerated by
- * "%s"
- */
-
-#include "libxl_osdeps.h"
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "libxl_internal.h"
-
-
-""" % " ".join(sys.argv))
-
- for ty in [t for t in types if t.dispose_fn is not None and t.autogenerate_dispose_fn]:
- f.write("void %s(%s)\n" % (ty.dispose_fn, ty.make_arg("p")))
- f.write("{\n")
- f.write(" if (!p) return;\n")
- f.write(libxl_C_type_dispose(ty, "p"))
- f.write(" memset(p, 0, sizeof(*p));\n")
- f.write("}\n")
- f.write("\n")
-
- for ty in [t for t in types if t.copy_fn and t.autogenerate_copy_fn]:
- f.write("void %s(libxl_ctx *ctx, %s, const %s)\n" % (ty.copy_fn,
- ty.make_arg("dst", passby=idl.PASS_BY_REFERENCE),
- ty.make_arg("src", passby=idl.PASS_BY_REFERENCE)))
- f.write("{\n")
- f.write(libxl_C_type_copy(ty, "dst", "src"))
- f.write("}\n")
- f.write("\n")
-
- for ty in [t for t in types if t.copy_deprecated_fn]:
- f.write("int %s(libxl_ctx *ctx, %s)\n" % (ty.copy_deprecated_fn,
- ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
- f.write("{\n")
- for field in [field for field in ty.fields if not field.const]:
- (vnparent,vfexpr) = ty.member("p", field, True)
- f.write(libxl_C_type_copy_deprecated(field, vfexpr,
- vparent = vnparent))
- f.write(" return 0;\n")
- f.write("}\n")
- f.write("\n")
-
- for ty in [t for t in types if t.init_fn is not None and t.autogenerate_init_fn]:
- f.write(libxl_C_type_init(ty))
- for field in libxl_init_members(ty):
- f.write(libxl_C_type_member_init(ty, field))
-
- for ty in [t for t in types if isinstance(t,idl.Enumeration)]:
- f.write("const char *%s_to_string(%s e)\n" % (ty.typename, ty.typename))
- f.write("{\n")
- f.write(libxl_C_enum_to_string(ty, "e"))
- f.write("}\n")
- f.write("\n")
-
- f.write(libxl_C_enum_strings(ty))
-
- f.write("int %s_from_string(const char *s, %s *e)\n" % (ty.typename, ty.typename))
- f.write("{\n")
- f.write(libxl_C_enum_from_string(ty, "s", "e"))
- f.write("}\n")
- f.write("\n")
-
- for ty in [t for t in types if t.json_gen_fn is not None]:
- f.write("yajl_gen_status %s_gen_json(yajl_gen hand, %s)\n" % (ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
- f.write("{\n")
- f.write(libxl_C_type_gen_json(ty, "p"))
- f.write("}\n")
- f.write("\n")
-
- f.write("char *%s_to_json(libxl_ctx *ctx, %s)\n" % (ty.typename, ty.make_arg("p")))
- f.write("{\n")
- f.write(libxl_C_type_to_json(ty, "p"))
- f.write("}\n")
- f.write("\n")
-
- for ty in [t for t in types if t.json_parse_fn is not None]:
- f.write("int %s_parse_json(libxl__gc *gc, const libxl__json_object *%s, %s)\n" % \
- (ty.namespace + "_" + ty.rawname,"o",ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
- f.write("{\n")
- f.write(libxl_C_type_parse_json(ty, "o", "p"))
- f.write("}\n")
- f.write("\n")
-
- f.write("int %s_from_json(libxl_ctx *ctx, %s, const char *s)\n" % (ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
- f.write("{\n")
- if not isinstance(ty, idl.Enumeration):
- f.write(" %s_init(p);\n" % ty.typename)
- f.write(libxl_C_type_from_json(ty, "p", "s"))
- f.write("}\n")
- f.write("\n")
-
- f.close()
+++ /dev/null
-from __future__ import print_function
-
-import sys
-
-PASS_BY_VALUE = 1
-PASS_BY_REFERENCE = 2
-
-DIR_NONE = 0
-DIR_IN = 1
-DIR_OUT = 2
-DIR_BOTH = 3
-
-_default_namespace = ""
-def namespace(s):
- if type(s) != str:
- raise TypeError("Require a string for the default namespace.")
- global _default_namespace
- _default_namespace = s
-
-def _get_default_namespace():
- global _default_namespace
- return _default_namespace
-
-_default_hidden = False
-def hidden(b):
- global _default_hidden
- _default_hidden = b
-
-def _get_default_hidden():
- global _default_hidden
- return _default_hidden
-
-class Type(object):
- def __init__(self, typename, **kwargs):
- self.namespace = kwargs.setdefault('namespace',
- _get_default_namespace())
- self._hidden = kwargs.setdefault('hidden', _get_default_hidden())
- self.dir = kwargs.setdefault('dir', DIR_BOTH)
- if self.dir not in [DIR_NONE, DIR_IN, DIR_OUT, DIR_BOTH]:
- raise ValueError
-
- self.passby = kwargs.setdefault('passby', PASS_BY_VALUE)
- if self.passby not in [PASS_BY_VALUE, PASS_BY_REFERENCE]:
- raise ValueError
-
- self.private = kwargs.setdefault('private', False)
-
- if typename is None: # Anonymous type
- self.typename = None
- self.rawname = None
- elif self.namespace is None: # e.g. system provided types
- self.typename = typename
- self.rawname = typename
- else:
- self.typename = self.namespace + typename
- self.rawname = typename
-
- if self.typename is not None:
- self.dispose_fn = kwargs.setdefault('dispose_fn', self.typename + "_dispose")
- else:
- self.dispose_fn = kwargs.setdefault('dispose_fn', None)
-
- self.autogenerate_dispose_fn = kwargs.setdefault('autogenerate_dispose_fn', True)
-
- if self.typename is not None:
- self.copy_fn = kwargs.setdefault('copy_fn', self.typename + "_copy")
- else:
- self.copy_fn = kwargs.setdefault('copy_fn', None)
-
- self.autogenerate_copy_fn = kwargs.setdefault('autogenerate_copy_fn', True)
-
- self.init_fn = kwargs.setdefault('init_fn', None)
- self.init_val = kwargs.setdefault('init_val', None)
- self.autogenerate_init_fn = kwargs.setdefault('autogenerate_init_fn', False)
-
- self.check_default_fn = kwargs.setdefault('check_default_fn', None)
- self.copy_deprecated_fn = kwargs.setdefault('copy_deprecated_fn',
- None)
-
- if self.typename is not None and not self.private:
- self.json_gen_fn = kwargs.setdefault('json_gen_fn', self.typename + "_gen_json")
- self.json_parse_type = kwargs.setdefault('json_parse_type', "JSON_ANY")
- if self.namespace is not None:
- self.json_parse_fn = kwargs.setdefault('json_parse_fn',
- self.namespace + "_" + self.rawname + "_parse_json")
- else:
- self.json_parse_fn = kwargs.setdefault('json_parse_fn',
- self.typename + "_parse_json")
- else:
- self.json_gen_fn = kwargs.setdefault('json_gen_fn', None)
- self.json_parse_type = kwargs.setdefault('json_parse_type', None)
- self.json_parse_fn = kwargs.setdefault('json_parse_fn', None)
-
- self.autogenerate_json = kwargs.setdefault('autogenerate_json', True)
-
- def marshal_in(self):
- return self.dir in [DIR_IN, DIR_BOTH]
- def marshal_out(self):
- return self.dir in [DIR_OUT, DIR_BOTH]
-
- def hidden(self):
- if self._hidden:
- return "_hidden "
- else:
- return ""
-
- def make_arg(self, n, passby=None):
- if passby is None: passby = self.passby
-
- if passby == PASS_BY_REFERENCE:
- return "%s *%s" % (self.typename, n)
- else:
- return "%s %s" % (self.typename, n)
-
- def pass_arg(self, n, isref=None, passby=None):
- if passby is None: passby = self.passby
- if isref is None: isref = self.passby == PASS_BY_REFERENCE
-
- if passby == PASS_BY_REFERENCE:
- if isref:
- return "%s" % (n)
- else:
- return "&%s" % (n)
- else:
- if isref:
- return "*%s" % (n)
- else:
- return "%s" % (n)
-
-class Builtin(Type):
- """Builtin type"""
- def __init__(self, typename, **kwargs):
- kwargs.setdefault('dispose_fn', None)
- kwargs.setdefault('autogenerate_dispose_fn', False)
- kwargs.setdefault('autogenerate_json', False)
- Type.__init__(self, typename, **kwargs)
-
-class Number(Builtin):
- def __init__(self, ctype, **kwargs):
- kwargs.setdefault('namespace', None)
- kwargs.setdefault('dispose_fn', None)
- kwargs.setdefault('copy_fn', None)
- kwargs.setdefault('signed', False)
- kwargs.setdefault('json_gen_fn', "yajl_gen_integer")
- kwargs.setdefault('json_parse_type', "JSON_INTEGER")
- # json_parse_fn might be overriden on specific type
- kwargs.setdefault('json_parse_fn', "libxl__int_parse_json")
- self.signed = kwargs['signed']
- Builtin.__init__(self, ctype, **kwargs)
-
-class UInt(Number):
- def __init__(self, w, **kwargs):
- kwargs.setdefault('namespace', None)
- kwargs.setdefault('dispose_fn', None)
- kwargs.setdefault('json_parse_fn', "libxl__uint%d_parse_json" % w)
- kwargs.setdefault('copy_fn', None)
- Number.__init__(self, "uint%d_t" % w, **kwargs)
-
- self.width = w
-
-class EnumerationValue(object):
- def __init__(self, enum, value, name, **kwargs):
- self.enum = enum
-
- self.valuename = str.upper(name)
- self.rawname = str.upper(enum.rawname) + "_" + self.valuename
- self.name = str.upper(enum.value_namespace) + self.rawname
- self.value = value
-
-class Enumeration(Type):
- def __init__(self, typename, values, **kwargs):
- kwargs.setdefault('dispose_fn', None)
- kwargs.setdefault('copy_fn', None)
- kwargs.setdefault('json_parse_type', "JSON_STRING")
- Type.__init__(self, typename, **kwargs)
-
- self.value_namespace = kwargs.setdefault('value_namespace',
- self.namespace)
-
- self.values = []
- for v in values:
- # (value, name)
- (num,name) = v
- self.values.append(EnumerationValue(self, num, name,
- typename=self.rawname))
- def lookup(self, name):
- for v in self.values:
- if v.valuename == str.upper(name):
- return v
- return ValueError
-
-class Field(object):
- """An element of an Aggregate type"""
- def __init__(self, type, name, **kwargs):
- self.type = type
- self.name = name
- self.const = kwargs.setdefault('const', False)
- self.enumname = kwargs.setdefault('enumname', None)
- self.init_val = kwargs.setdefault('init_val', None)
- self.deprecated_by = kwargs.setdefault('deprecated_by', None)
-
-class Aggregate(Type):
- """A type containing a collection of other types"""
- def __init__(self, kind, typename, fields, **kwargs):
- kwargs.setdefault('json_parse_type', "JSON_MAP")
- Type.__init__(self, typename, **kwargs)
-
- if self.typename is not None:
- self.init_fn = kwargs.setdefault('init_fn', self.typename + "_init")
- else:
- self.init_fn = kwargs.setdefault('init_fn', None)
-
- self.autogenerate_init_fn = kwargs.setdefault('autogenerate_init_fn', True)
-
- self.kind = kind
-
- self.fields = []
- for f in fields:
- # (name, type[, {kw args}])
- if len(f) == 2:
- n,t = f
- kw = {}
- elif len(f) == 3:
- n,t,kw = f
- else:
- raise ValueError
- if n is None:
- raise ValueError
- self.fields.append(Field(t,n,**kw))
-
- # Returns a tuple (stem, field-expr)
- #
- # field-expr is a C expression for a field "f" within the struct
- # "v".
- #
- # stem is the stem common to both "f" and any other sibbling field
- # within the "v".
- def member(self, v, f, isref):
- if isref:
- deref = v + "->"
- else:
- deref = v + "."
-
- if f.name is None: # Anonymous
- return (deref, deref)
- else:
- return (deref, deref + f.name)
-
-class Struct(Aggregate):
- def __init__(self, name, fields, **kwargs):
- kwargs.setdefault('passby', PASS_BY_REFERENCE)
- Aggregate.__init__(self, "struct", name, fields, **kwargs)
-
- def has_fields(self):
- return len(self.fields) != 0
-
-class Union(Aggregate):
- def __init__(self, name, fields, **kwargs):
- # Generally speaking some intelligence is required to free a
- # union therefore any specific instance of this class will
- # need to provide an explicit destructor function.
- kwargs.setdefault('passby', PASS_BY_REFERENCE)
- kwargs.setdefault('dispose_fn', None)
- Aggregate.__init__(self, "union", name, fields, **kwargs)
-
-class KeyedUnion(Aggregate):
- """A union which is keyed of another variable in the parent structure"""
- def __init__(self, name, keyvar_type, keyvar_name, fields, **kwargs):
- Aggregate.__init__(self, "union", name, [], **kwargs)
-
- if not isinstance(keyvar_type, Enumeration):
- raise ValueError
-
- kv_kwargs = dict([(x.lstrip('keyvar_'),y) for (x,y) in kwargs.items() if x.startswith('keyvar_')])
-
- self.keyvar = Field(keyvar_type, keyvar_name, **kv_kwargs)
-
- for f in fields:
- # (name, enum, type)
- e, ty = f
- ev = keyvar_type.lookup(e)
- en = ev.name
- self.fields.append(Field(ty, e, enumname=en))
-
-#
-# Standard Types
-#
-
-void = Builtin("void *", namespace = None)
-bool = Builtin("bool", namespace = None,
- copy_fn=None,
- json_gen_fn = "yajl_gen_bool",
- json_parse_type = "JSON_BOOL",
- json_parse_fn = "libxl__bool_parse_json",
- autogenerate_json = False)
-
-size_t = Number("size_t", namespace = None)
-
-integer = Number("int", namespace = None, signed = True)
-
-uint8 = UInt(8)
-uint16 = UInt(16)
-uint32 = UInt(32)
-uint64 = UInt(64, json_gen_fn = "libxl__uint64_gen_json")
-
-string = Builtin("char *", namespace = None, copy_fn = "libxl_string_copy", dispose_fn = "free",
- json_gen_fn = "libxl__string_gen_json",
- json_parse_type = "JSON_STRING | JSON_NULL",
- json_parse_fn = "libxl__string_parse_json",
- autogenerate_json = False,
- check_default_fn="libxl__string_is_default")
-
-class Array(Type):
- """An array of the same type"""
- def __init__(self, elem_type, lenvar_name, **kwargs):
- kwargs.setdefault('dispose_fn', 'free')
- kwargs.setdefault('json_parse_type', 'JSON_ARRAY')
- Type.__init__(self, namespace=elem_type.namespace, typename=elem_type.rawname + " *", **kwargs)
-
- lv_kwargs = dict([(x.lstrip('lenvar_'),y) for (x,y) in kwargs.items() if x.startswith('lenvar_')])
-
- self.lenvar = Field(integer, lenvar_name, **lv_kwargs)
- self.elem_type = elem_type
-
-class OrderedDict(dict):
- """A dictionary which remembers insertion order.
-
- push to back on duplicate insertion"""
-
- def __init__(self):
- dict.__init__(self)
- self.__ordered = []
-
- def __setitem__(self, key, value):
- try:
- self.__ordered.remove(key)
- except ValueError:
- pass
-
- self.__ordered.append(key)
- dict.__setitem__(self, key, value)
-
- def ordered_keys(self):
- return self.__ordered
- def ordered_values(self):
- return [self[x] for x in self.__ordered]
- def ordered_items(self):
- return [(x,self[x]) for x in self.__ordered]
-
-def parse(f):
- print("Parsing %s" % f, file=sys.stderr)
-
- globs = {}
- locs = OrderedDict()
-
- for n,t in globals().items():
- if isinstance(t, Type):
- globs[n] = t
- elif isinstance(t,type(object)) and issubclass(t, Type):
- globs[n] = t
- elif n in ['PASS_BY_REFERENCE', 'PASS_BY_VALUE',
- 'DIR_NONE', 'DIR_IN', 'DIR_OUT', 'DIR_BOTH',
- 'namespace', 'hidden']:
- globs[n] = t
-
- try:
- exec(compile(open(f).read(), f, 'exec'), globs, locs)
- except SyntaxError as e:
- raise SyntaxError("Errors were found at line %d while processing %s:\n\t%s"
- % (e.lineno, f, e.text))
-
- types = [t for t in locs.ordered_values() if isinstance(t,Type)]
-
- builtins = [t for t in types if isinstance(t,Builtin)]
- types = [t for t in types if not isinstance(t,Builtin)]
-
- return (builtins,types)
+++ /dev/null
-libxl IDL
----------
-
-Each type in the libxl interface is represented by an object of type
-idl.Type (or a subclass thereof). Every local variable defined by the
-.idl file must be an instance of idl.Type (e.g. you may not define
-Python functions or any other construct other than defining variables)
-
-The name of the type must be passed as the first argument to the
-constructor when defining a new type. The name given should not
-contain the initial namespace element (e.g. "libxl_"). See below for
-how to specify a namespace.
-
-The Type.typename contains the C name of the type _including_ the
-namespace element while Type.rawname is always set to the 'base' name
-of the type.
-
-The idl.Type base class has several other properties which apply to
-all types. The properties are set by passing a named parameter to the
-constructor.
-
-Type.namespace: (default: "libxl_")
-
- The namespace in which the type resides. Usually this is "libxl_" but
- system defined and builtin types may differ.
-
- If the typename is not None then the namespace is prepended to the
- type.
-
-Type.passby: (default: idl.PASS_BY_VALUE)
-
- Defines the manner in which a type should be passed to C
- functions. Valid values for this fields are:
- idl.PASS_BY_VALUE
- idl.PASS_BY_REFERENCE
-
-Type.dispose_fn: (default: typename + "_dispose" or None if type == None)
-
- The name of the C function which will free all dynamically allocated
- memory contained within this type (but not the type itself).
-
-Type.autogenerate_dispose_fn: (default: True)
-
- Indicates if the above named Type.dispose_fn should be
- autogenerated.
-
-Type.copy_fn: (default: typename + "_copy" or None if type == None)
-
- The name of the C function which will deep copy all fields within
- this type.
-
-Type.autogenerate_copy_fn: (default: True)
-
- Indicates if the above named Type.copy_fn should be
- autogenerated.
-
-Type.autogenerate_copy_fn
-
-Type.init_val: (default: None)
-
- C expression for the value to initialise instances of this type to.
-
- If present takes precendence over init_fn (see below).
-
-Type.init_fn: (default: typename + "_init" if dir in [IN, BOTH] and
- type != None)
-
- The name of the C function which will initialist Type.
-
-Type.autogenerate_init_fn: (default: True if dir in [IN, BOTH])
-
- Indicates if the above named Type.init_fn should be
- autogenerated.
-
-Type.json_gen_fn: (default: typename + "_gen_json" or None if type == None)
-
- The name of the C function which will generate a YAJL data structure
- representing this type.
-
-Type.json_parse_fn: (default: typename + "_parse_json" or None if type == None)
-
- The name of the C function which will parse a libxl JSON structure
- representing this type to C type.
-
-Type.autogenerate_json: (default: True)
-
- Indicates if the above named Type.json_*_fn should be autogenerated.
-
-Type.check_default_fn:
-
- If it's set then calling this function shall return true if this type
- has been set to default value (internal libxl implementation).
-
- If this is not set, that means we can check the type against init_val
- (if it has one) or zero to determine whether the value is default
- value.
-
-Other simple type-Classes
--------------------------
-
-idl.Builtin
-
- Instances of this class represent types which are predefined within
- the system.
-
-idl.UInt
-
- Instances of this class represent the standard uint<N>_t types.
-
- The <N> for a given instance must be passed to the constructor and is
- then available in UInt.width
-
-Complex type-Classes
---------------------
-
-idl.Enumeration
-
- A class representing an enumeration (named integer values).
- This class has one property besides the ones defined for the Type
- class:
-
- Enumeration.value_namespace: (default: namespace)
-
- The namespace in which the values of the Enumeration (see below) reside.
- This prefix is prepended to the name of the value.
-
- The values are available in the list Enumeration.values. Each
- element in the list is of type idl.EnumerationValue.
-
- Each EnumerationValue has the following properties:
-
- EnumerationValue.enum Reference to containing Enumeration
- EnumerationValue.name The C name of this value, including
- the namespace and typename of the
- containing Enumeration (e.g.
- "LIBXL_FOOENUM_VALUE")
- EnumerationValue.rawname The C name of this value, excluding
- the namespace but including the
- typename of the containing
- Enumeration (e.g. "FOOENUM_VALUE")
- EnumerationValue.valuename The name of this value, excluding the
- name of the containing Enumeration
- and any namespace (e.g. "VALUE")
- EnumerationValue.value The integer value associated with this name.
-
-idl.Aggregate
-
- Base class for type-Classes which contain a number of other types
- (e.g. structs and unions).
-
- The contained types are available in the list Aggregate.fields. Each
- element in the list is of type idl.Field representing a member of the
- aggregate.
-
- Each field has the following properties:
-
- Field.type The type of the member (a idl.Type).
- Field.name The name of the member (can be None for anonymous
- fields).
- Field.const Boolean, true if the member is const.
- Field.init_val The initialisation value for this field. Takes
- precendence over both Field.type.init_val and
- Field.type.init_fn.
-
-idl.Struct
-
- A subclass of idl.Aggregate representing the C struct type.
-
- Struct.kind == "struct"
-
-idl.Union
-
- A subclass of idl.Aggregate representing the C union type.
-
- Union.kind == "union"
-
-idl.KeyedUnion
-
- A subclass of idl.Aggregate which represents the C union type
- where the currently valid member of the union can be determined based
- upon another member in the containing type. An idl.KeyedUnion must
- always be a member of a containing idl.Aggregate type.
-
- The KeyedUnion.keyvar contains an idl.Field, this is the member of
- the containing type which determines the valid member of the
- union. The idl.Field.type of the keyvar must be an Enumeration type.
-
-idl.Array
-
- A class representing an array of similar elements. An idl.Array must
- always be an idl.Field of a containing idl.Aggregate.
-
- idl.Array.elem_type contains an idl.Type which is the type of each
- element of the array.
-
- idl.Array.len_var contains an idl.Field which is added to the parent
- idl.Aggregate and will contain the length of the array. The field
- MUST be named num_ARRAYNAME.
-
-Standard Types
---------------
-
-Several standard types a predefined. They are
-
-void (void pointer type)
-bool
-size_t
-integer 24 bit signed integer.
-
-uint{8,16,32,64} uint{8,16,32,64}_t
-
-domid Domain ID
-
-string NULL terminated string
+++ /dev/null
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-int libxl_ctx_alloc(libxl_ctx **pctx, int version,
- unsigned flags, xentoollog_logger * lg)
-{
- libxl_ctx *ctx = NULL;
- libxl__gc gc_buf, *gc = NULL;
- int rc;
-
- if (version != LIBXL_VERSION) { rc = ERROR_VERSION; goto out; }
-
- ctx = malloc(sizeof(*ctx));
- if (!ctx) {
- xtl_log(lg, XTL_ERROR, errno, "libxl",
- "%s:%d:%s: Failed to allocate context\n",
- __FILE__, __LINE__, __func__);
- rc = ERROR_NOMEM; goto out;
- }
-
- memset(ctx, 0, sizeof(libxl_ctx));
- ctx->lg = lg;
-
- /* First initialise pointers etc. (cannot fail) */
-
- ctx->nogc_gc.alloc_maxsize = -1;
- ctx->nogc_gc.owner = ctx;
-
- LIBXL_TAILQ_INIT(&ctx->occurred);
-
- ctx->osevent_hooks = 0;
-
- ctx->poller_app = 0;
- LIBXL_LIST_INIT(&ctx->pollers_event);
- LIBXL_LIST_INIT(&ctx->pollers_idle);
- LIBXL_LIST_INIT(&ctx->pollers_active);
-
- LIBXL_LIST_INIT(&ctx->efds);
- LIBXL_TAILQ_INIT(&ctx->etimes);
-
- ctx->watch_slots = 0;
- LIBXL_SLIST_INIT(&ctx->watch_freeslots);
- libxl__ev_fd_init(&ctx->watch_efd);
-
- ctx->xce = 0;
- LIBXL_LIST_INIT(&ctx->evtchns_waiting);
- libxl__ev_fd_init(&ctx->evtchn_efd);
-
- LIBXL_LIST_INIT(&ctx->aos_inprogress);
-
- LIBXL_TAILQ_INIT(&ctx->death_list);
- libxl__ev_xswatch_init(&ctx->death_watch);
-
- ctx->childproc_hooks = &libxl__childproc_default_hooks;
- ctx->childproc_user = 0;
-
- ctx->sigchld_selfpipe[0] = -1;
- ctx->sigchld_selfpipe[1] = -1;
- libxl__ev_fd_init(&ctx->sigchld_selfpipe_efd);
-
- /* The mutex is special because we can't idempotently destroy it */
-
- if (libxl__init_recursive_mutex(ctx, &ctx->lock) < 0) {
- LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Failed to initialize mutex");
- free(ctx);
- ctx = 0;
- rc = ERROR_FAIL;
- goto out;
- }
-
- /* Now ctx is safe for ctx_free; failures simply set rc and "goto out" */
- LIBXL_INIT_GC(gc_buf,ctx);
- gc = &gc_buf;
- /* Now gc is useable */
-
- rc = libxl__atfork_init(ctx);
- if (rc) goto out;
-
- ctx->poller_app = libxl__poller_get(gc);
- if (!ctx->poller_app) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- ctx->xch = xc_interface_open(lg,lg,0);
- if (!ctx->xch) {
- LOGEV(ERROR, errno, "cannot open libxc handle");
- rc = ERROR_FAIL; goto out;
- }
-
- ctx->xsh = xs_daemon_open();
- if (!ctx->xsh)
- ctx->xsh = xs_domain_open();
- if (!ctx->xsh) {
- LOGEV(ERROR, errno, "cannot connect to xenstore");
- rc = ERROR_FAIL; goto out;
- }
-
- *pctx = ctx;
- return 0;
-
- out:
- if (gc) libxl__free_all(gc);
- libxl_ctx_free(ctx);
- *pctx = NULL;
- return rc;
-}
-
-static void free_disable_deaths(libxl__gc *gc,
- struct libxl__evgen_domain_death_list *l) {
- libxl_evgen_domain_death *death;
- while ((death = LIBXL_TAILQ_FIRST(l)))
- libxl__evdisable_domain_death(gc, death);
-}
-
-static void discard_events(struct libxl__event_list *l) {
- /* doesn't bother unlinking from the list, so l is corrupt on return */
- libxl_event *ev, *next;
- LIBXL_TAILQ_FOREACH_SAFE(ev, l, link, next)
- libxl_event_free(0, ev);
-}
-
-int libxl_ctx_free(libxl_ctx *ctx)
-{
- if (!ctx) return 0;
-
- int i;
- GC_INIT(ctx);
-
- CTX_LOCK;
- assert(!ctx->osevent_in_hook);
- CTX->osevent_in_hook += 1000; /* make violations easier to debug */
-
- /* Deregister all libxl__ev_KINDs: */
-
- free_disable_deaths(gc, &CTX->death_list);
- free_disable_deaths(gc, &CTX->death_reported);
-
- libxl_evgen_disk_eject *eject;
- while ((eject = LIBXL_LIST_FIRST(&CTX->disk_eject_evgens)))
- libxl__evdisable_disk_eject(gc, eject);
-
- libxl_childproc_setmode(CTX,0,0);
- for (i = 0; i < ctx->watch_nslots; i++)
- assert(!libxl__watch_slot_contents(gc, i));
- assert(!libxl__ev_fd_isregistered(&ctx->watch_efd));
- assert(!libxl__ev_fd_isregistered(&ctx->evtchn_efd));
- assert(!libxl__ev_fd_isregistered(&ctx->sigchld_selfpipe_efd));
-
- /* Now there should be no more events requested from the application: */
-
- assert(LIBXL_LIST_EMPTY(&ctx->efds));
- assert(LIBXL_TAILQ_EMPTY(&ctx->etimes));
- assert(LIBXL_LIST_EMPTY(&ctx->evtchns_waiting));
- assert(LIBXL_LIST_EMPTY(&ctx->aos_inprogress));
-
- if (ctx->xch) xc_interface_close(ctx->xch);
- libxl_version_info_dispose(&ctx->version_info);
- if (ctx->xsh) xs_daemon_close(ctx->xsh);
- if (ctx->xce) xenevtchn_close(ctx->xce);
-
- libxl__poller_put(ctx, ctx->poller_app);
- ctx->poller_app = NULL;
- assert(LIBXL_LIST_EMPTY(&ctx->pollers_event));
- assert(LIBXL_LIST_EMPTY(&ctx->pollers_active));
- libxl__poller *poller, *poller_tmp;
- LIBXL_LIST_FOREACH_SAFE(poller, &ctx->pollers_idle, entry, poller_tmp) {
- libxl__poller_dispose(poller);
- free(poller);
- }
-
- free(ctx->watch_slots);
-
- discard_events(&ctx->occurred);
-
- /* If we have outstanding children, then the application inherits
- * them; we wish the application good luck with understanding
- * this if and when it reaps them. */
- libxl__sigchld_notneeded(gc);
- libxl__pipe_close(ctx->sigchld_selfpipe);
-
- CTX_UNLOCK;
- pthread_mutex_destroy(&ctx->lock);
-
- GC_FREE;
- free(ctx);
- return 0;
-}
-
-void libxl_string_list_dispose(libxl_string_list *psl)
-{
- int i;
- libxl_string_list sl = *psl;
-
- if (!sl)
- return;
-
- for (i = 0; sl[i] != NULL; i++) {
- free(sl[i]);
- sl[i] = NULL;
- }
- free(sl);
- *psl = NULL;
-}
-
-void libxl_string_list_copy(libxl_ctx *ctx,
- libxl_string_list *dst,
- const libxl_string_list *src)
-{
- GC_INIT(ctx);
- int i, len;
-
- if (!*src) {
- *dst = NULL;
- goto out;
- }
-
- len = libxl_string_list_length(src);
- /* one extra slot for sentinel */
- *dst = libxl__calloc(NOGC, len + 1, sizeof(char *));
-
- for (i = 0; i < len; i++)
- (*dst)[i] = libxl__strdup(NOGC, (*src)[i]);
-
-out:
- GC_FREE;
-}
-
-int libxl_string_list_length(const libxl_string_list *psl)
-{
- int i = 0;
-
- if (*psl)
- while ((*psl)[i])
- i++;
-
- return i;
-}
-
-int libxl_key_value_list_length(const libxl_key_value_list *pkvl)
-{
- int i = 0;
- libxl_key_value_list kvl = *pkvl;
-
- if (kvl) {
- while (kvl[2 * i]) /* Only checks keys */
- i++;
- }
-
- return i;
-}
-
-void libxl_key_value_list_dispose(libxl_key_value_list *pkvl)
-{
- int i;
- libxl_key_value_list kvl = *pkvl;
-
- if (!kvl)
- return;
-
- for (i = 0; kvl[i] != NULL; i += 2) {
- free(kvl[i]);
- kvl[i] = NULL;
- if (kvl[i + 1]) {
- free(kvl[i + 1]);
- kvl[i+1] = NULL;
- }
- }
- free(kvl);
- *pkvl = NULL;
-}
-
-void libxl_key_value_list_copy(libxl_ctx *ctx,
- libxl_key_value_list *dst,
- const libxl_key_value_list *src)
-{
- GC_INIT(ctx);
- int i, len;
-
- if (*src == NULL) {
- *dst = NULL;
- goto out;
- }
-
- len = libxl_key_value_list_length(src);
- /* one extra slot for sentinel */
- *dst = libxl__calloc(NOGC, len * 2 + 1, sizeof(char *));
-
- for (i = 0; i < len * 2; i += 2) {
- (*dst)[i] = libxl__strdup(NOGC, (*src)[i]);
- if ((*src)[i+1])
- (*dst)[i+1] = libxl__strdup(NOGC, (*src)[i+1]);
- else
- (*dst)[i+1] = NULL;
- }
-
-out:
- GC_FREE;
-}
-
-void libxl_defbool_set(libxl_defbool *db, bool b)
-{
- db->val = b ? LIBXL__DEFBOOL_TRUE : LIBXL__DEFBOOL_FALSE;
-}
-
-void libxl_defbool_unset(libxl_defbool *db)
-{
- db->val = LIBXL__DEFBOOL_DEFAULT;
-}
-
-bool libxl_defbool_is_default(libxl_defbool db)
-{
- return !db.val;
-}
-
-void libxl_defbool_setdefault(libxl_defbool *db, bool b)
-{
- if (libxl_defbool_is_default(*db))
- libxl_defbool_set(db, b);
-}
-
-bool libxl_defbool_val(libxl_defbool db)
-{
- assert(!libxl_defbool_is_default(db));
- return db.val > 0;
-}
-
-const char *libxl_defbool_to_string(libxl_defbool b)
-{
- if (b.val < 0)
- return LIBXL__DEFBOOL_STR_FALSE;
- else if (b.val > 0)
- return LIBXL__DEFBOOL_STR_TRUE;
- else
- return LIBXL__DEFBOOL_STR_DEFAULT;
-}
-
-/******************************************************************************/
-int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo)
-{
- xc_physinfo_t xcphysinfo = { 0 };
- int rc;
- long l;
- GC_INIT(ctx);
-
- rc = xc_physinfo(ctx->xch, &xcphysinfo);
- if (rc != 0) {
- LOGE(ERROR, "getting physinfo");
- GC_FREE;
- return ERROR_FAIL;
- }
- physinfo->threads_per_core = xcphysinfo.threads_per_core;
- physinfo->cores_per_socket = xcphysinfo.cores_per_socket;
- physinfo->max_cpu_id = xcphysinfo.max_cpu_id;
- physinfo->nr_cpus = xcphysinfo.nr_cpus;
- physinfo->cpu_khz = xcphysinfo.cpu_khz;
- physinfo->total_pages = xcphysinfo.total_pages;
- physinfo->free_pages = xcphysinfo.free_pages;
- physinfo->scrub_pages = xcphysinfo.scrub_pages;
- physinfo->outstanding_pages = xcphysinfo.outstanding_pages;
- physinfo->max_possible_mfn = xcphysinfo.max_mfn;
- l = xc_sharing_freed_pages(ctx->xch);
- if (l < 0 && errno == ENOSYS) {
- l = 0;
- } else if (l < 0) {
- LOGEV(ERROR, l, "getting sharing freed pages");
- GC_FREE;
- return ERROR_FAIL;
- }
- physinfo->sharing_freed_pages = l;
- l = xc_sharing_used_frames(ctx->xch);
- if (l < 0 && errno == ENOSYS) {
- l = 0;
- } else if (l < 0) {
- LOGEV(ERROR, l, "getting sharing used frames");
- GC_FREE;
- return ERROR_FAIL;
- }
- physinfo->sharing_used_frames = l;
- physinfo->nr_nodes = xcphysinfo.nr_nodes;
- memcpy(physinfo->hw_cap,xcphysinfo.hw_cap, sizeof(physinfo->hw_cap));
-
- physinfo->cap_hvm = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_hvm);
- physinfo->cap_pv = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_pv);
- physinfo->cap_hvm_directio =
- !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_directio);
- physinfo->cap_hap = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_hap);
- physinfo->cap_shadow =
- !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_shadow);
- physinfo->cap_iommu_hap_pt_share =
- !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_iommu_hap_pt_share);
-
- GC_FREE;
- return 0;
-}
-
-libxl_cputopology *libxl_get_cpu_topology(libxl_ctx *ctx, int *nb_cpu_out)
-{
- GC_INIT(ctx);
- xc_cputopo_t *cputopo;
- libxl_cputopology *ret = NULL;
- int i;
- unsigned num_cpus = 0;
-
- /* Setting buffer to NULL makes the call return number of CPUs */
- if (xc_cputopoinfo(ctx->xch, &num_cpus, NULL))
- {
- LOGE(ERROR, "Unable to determine number of CPUS");
- goto out;
- }
-
- cputopo = libxl__zalloc(gc, sizeof(*cputopo) * num_cpus);
-
- if (xc_cputopoinfo(ctx->xch, &num_cpus, cputopo)) {
- LOGE(ERROR, "CPU topology info hypercall failed");
- goto out;
- }
-
- ret = libxl__zalloc(NOGC, sizeof(libxl_cputopology) * num_cpus);
-
- for (i = 0; i < num_cpus; i++) {
-#define V(map, i, invalid) ( cputopo[i].map == invalid) ? \
- LIBXL_CPUTOPOLOGY_INVALID_ENTRY : cputopo[i].map
- ret[i].core = V(core, i, XEN_INVALID_CORE_ID);
- ret[i].socket = V(socket, i, XEN_INVALID_SOCKET_ID);
- ret[i].node = V(node, i, XEN_INVALID_NODE_ID);
-#undef V
- }
-
- *nb_cpu_out = num_cpus;
-
- out:
- GC_FREE;
- return ret;
-}
-
-libxl_pcitopology *libxl_get_pci_topology(libxl_ctx *ctx, int *num_devs)
-{
- GC_INIT(ctx);
- physdev_pci_device_t *devs;
- uint32_t *nodes;
- libxl_pcitopology *ret = NULL;
- int i, rc;
-
- *num_devs = libxl__pci_numdevs(gc);
- if (*num_devs < 0) {
- LOG(ERROR, "Unable to determine number of PCI devices, rc %d",
- *num_devs);
- goto out;
- }
-
- devs = libxl__zalloc(gc, sizeof(*devs) * *num_devs);
- nodes = libxl__zalloc(gc, sizeof(*nodes) * *num_devs);
-
- rc = libxl__pci_topology_init(gc, devs, *num_devs);
- if (rc) {
- LOG(ERROR, "Cannot initialize PCI hypercall structure, rc %d", rc);
- goto out;
- }
-
- if (xc_pcitopoinfo(ctx->xch, *num_devs, devs, nodes) != 0) {
- LOGE(ERROR, "PCI topology info hypercall failed");
- goto out;
- }
-
- ret = libxl__zalloc(NOGC, sizeof(libxl_pcitopology) * *num_devs);
-
- for (i = 0; i < *num_devs; i++) {
- ret[i].seg = devs[i].seg;
- ret[i].bus = devs[i].bus;
- ret[i].devfn = devs[i].devfn;
- ret[i].node = ((nodes[i] == XEN_INVALID_NODE_ID) ||
- (nodes[i] == XEN_INVALID_DEV)) ?
- LIBXL_PCITOPOLOGY_INVALID_ENTRY : nodes[i];
- }
-
- out:
- GC_FREE;
- return ret;
-}
-
-libxl_numainfo *libxl_get_numainfo(libxl_ctx *ctx, int *nr)
-{
- GC_INIT(ctx);
- xc_meminfo_t *meminfo;
- uint32_t *distance;
- libxl_numainfo *ret = NULL;
- int i, j;
- unsigned num_nodes = 0;
-
- if (xc_numainfo(ctx->xch, &num_nodes, NULL, NULL)) {
- LOGE(ERROR, "Unable to determine number of nodes");
- goto out;
- }
-
- meminfo = libxl__zalloc(gc, sizeof(*meminfo) * num_nodes);
- distance = libxl__zalloc(gc, sizeof(*distance) * num_nodes * num_nodes);
-
- if (xc_numainfo(ctx->xch, &num_nodes, meminfo, distance)) {
- LOGE(ERROR, "getting numainfo");
- goto out;
- }
-
- *nr = num_nodes;
-
- ret = libxl__zalloc(NOGC, sizeof(libxl_numainfo) * num_nodes);
- for (i = 0; i < num_nodes; i++)
- ret[i].dists = libxl__calloc(NOGC, num_nodes, sizeof(*distance));
-
- for (i = 0; i < num_nodes; i++) {
-#define V(val, invalid) (val == invalid) ? \
- LIBXL_NUMAINFO_INVALID_ENTRY : val
- ret[i].size = V(meminfo[i].memsize, XEN_INVALID_MEM_SZ);
- ret[i].free = V(meminfo[i].memfree, XEN_INVALID_MEM_SZ);
- ret[i].num_dists = num_nodes;
- for (j = 0; j < ret[i].num_dists; j++) {
- unsigned idx = i * num_nodes + j;
- ret[i].dists[j] = V(distance[idx], XEN_INVALID_NODE_DIST);
- }
-#undef V
- }
-
- out:
- GC_FREE;
- return ret;
-}
-
-static int libxl__xc_version_wrap(libxl__gc *gc, libxl_version_info *info,
- xen_build_id_t *build)
-{
- int r;
-
- r = xc_version(CTX->xch, XENVER_build_id, build);
- switch (r) {
- case -EPERM:
- case -ENODATA:
- case 0:
- info->build_id = libxl__strdup(NOGC, "");
- break;
-
- case -ENOBUFS:
- break;
-
- default:
- if (r > 0) {
- unsigned int i;
-
- info->build_id = libxl__zalloc(NOGC, (r * 2) + 1);
-
- for (i = 0; i < r ; i++)
- snprintf(&info->build_id[i * 2], 3, "%02hhx", build->buf[i]);
-
- r = 0;
- }
- break;
- }
- return r;
-}
-
-const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx)
-{
- GC_INIT(ctx);
- union {
- xen_extraversion_t xen_extra;
- xen_compile_info_t xen_cc;
- xen_changeset_info_t xen_chgset;
- xen_capabilities_info_t xen_caps;
- xen_platform_parameters_t p_parms;
- xen_commandline_t xen_commandline;
- xen_build_id_t build_id;
- } u;
- long xen_version;
- int r;
- libxl_version_info *info = &ctx->version_info;
-
- if (info->xen_version_extra != NULL)
- goto out;
-
- xen_version = xc_version(ctx->xch, XENVER_version, NULL);
- info->xen_version_major = xen_version >> 16;
- info->xen_version_minor = xen_version & 0xFF;
-
- xc_version(ctx->xch, XENVER_extraversion, &u.xen_extra);
- info->xen_version_extra = libxl__strdup(NOGC, u.xen_extra);
-
- xc_version(ctx->xch, XENVER_compile_info, &u.xen_cc);
- info->compiler = libxl__strdup(NOGC, u.xen_cc.compiler);
- info->compile_by = libxl__strdup(NOGC, u.xen_cc.compile_by);
- info->compile_domain = libxl__strdup(NOGC, u.xen_cc.compile_domain);
- info->compile_date = libxl__strdup(NOGC, u.xen_cc.compile_date);
-
- xc_version(ctx->xch, XENVER_capabilities, &u.xen_caps);
- info->capabilities = libxl__strdup(NOGC, u.xen_caps);
-
- xc_version(ctx->xch, XENVER_changeset, &u.xen_chgset);
- info->changeset = libxl__strdup(NOGC, u.xen_chgset);
-
- xc_version(ctx->xch, XENVER_platform_parameters, &u.p_parms);
- info->virt_start = u.p_parms.virt_start;
-
- info->pagesize = xc_version(ctx->xch, XENVER_pagesize, NULL);
-
- xc_version(ctx->xch, XENVER_commandline, &u.xen_commandline);
- info->commandline = libxl__strdup(NOGC, u.xen_commandline);
-
- u.build_id.len = sizeof(u) - sizeof(u.build_id);
- r = libxl__xc_version_wrap(gc, info, &u.build_id);
- if (r == -ENOBUFS) {
- xen_build_id_t *build_id;
-
- build_id = libxl__zalloc(gc, info->pagesize);
- build_id->len = info->pagesize - sizeof(*build_id);
- r = libxl__xc_version_wrap(gc, info, build_id);
- if (r) LOGEV(ERROR, r, "getting build_id");
- }
- out:
- GC_FREE;
- return info;
-}
-
-int libxl_send_sysrq(libxl_ctx *ctx, uint32_t domid, char sysrq)
-{
- GC_INIT(ctx);
- char *dompath = libxl__xs_get_dompath(gc, domid);
-
- libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/control/sysrq", dompath),
- "%c", sysrq);
-
- GC_FREE;
- return 0;
-}
-
-int libxl_send_debug_keys(libxl_ctx *ctx, char *keys)
-{
- int ret;
- GC_INIT(ctx);
- ret = xc_send_debug_keys(ctx->xch, keys);
- if ( ret < 0 ) {
- LOGE(ERROR, "sending debug keys");
- GC_FREE;
- return ERROR_FAIL;
- }
- GC_FREE;
- return 0;
-}
-
-int libxl_set_parameters(libxl_ctx *ctx, char *params)
-{
- int ret;
- GC_INIT(ctx);
- char *par, *val, *end, *path;
- xenhypfs_handle *hypfs;
-
- hypfs = xenhypfs_open(ctx->lg, 0);
- if (!hypfs) {
- LOGE(ERROR, "opening Xen hypfs");
- ret = ERROR_FAIL;
- goto out;
- }
-
- while (isblank(*params))
- params++;
-
- for (par = params; *par; par = end) {
- end = strchr(par, ' ');
- if (!end)
- end = par + strlen(par);
-
- val = strchr(par, '=');
- if (val > end)
- val = NULL;
- if (!val && !strncmp(par, "no", 2)) {
- path = libxl__sprintf(gc, "/params/%s", par + 2);
- path[end - par - 2 + 8] = 0;
- val = "no";
- par += 2;
- } else {
- path = libxl__sprintf(gc, "/params/%s", par);
- path[val - par + 8] = 0;
- val = libxl__strndup(gc, val + 1, end - val - 1);
- }
-
- LOG(DEBUG, "setting node \"%s\" to value \"%s\"", path, val);
- ret = xenhypfs_write(hypfs, path, val);
- if (ret < 0) {
- LOGE(ERROR, "setting parameters");
- ret = ERROR_FAIL;
- goto out;
- }
-
- while (isblank(*end))
- end++;
- }
-
- ret = 0;
-
-out:
- xenhypfs_close(hypfs);
- GC_FREE;
- return ret;
-}
-
-static int fd_set_flags(libxl_ctx *ctx, int fd,
- int fcntlgetop, int fcntlsetop, const char *fl,
- int flagmask, int set_p)
-{
- int flags, r;
- GC_INIT(ctx);
-
- flags = fcntl(fd, fcntlgetop);
- if (flags == -1) {
- LOGE(ERROR, "fcntl(,F_GET%s) failed", fl);
- GC_FREE;
- return ERROR_FAIL;
- }
-
- if (set_p)
- flags |= flagmask;
- else
- flags &= ~flagmask;
-
- r = fcntl(fd, fcntlsetop, flags);
- if (r == -1) {
- LOGE(ERROR, "fcntl(,F_SET%s) failed", fl);
- GC_FREE;
- return ERROR_FAIL;
- }
-
- GC_FREE;
- return 0;
-}
-
-int libxl_fd_set_cloexec(libxl_ctx *ctx, int fd, int cloexec)
- { return fd_set_flags(ctx,fd, F_GETFD,F_SETFD,"FD", FD_CLOEXEC, cloexec); }
-
-int libxl_fd_set_nonblock(libxl_ctx *ctx, int fd, int nonblock)
- { return fd_set_flags(ctx,fd, F_GETFL,F_SETFL,"FL", O_NONBLOCK, nonblock); }
-
-int libxl__fd_flags_modify_save(libxl__gc *gc, int fd,
- int mask, int val, int *r_oldflags)
-{
- int rc, ret, fdfl;
-
- fdfl = fcntl(fd, F_GETFL);
- if (fdfl < 0) {
- LOGE(ERROR, "failed to fcntl.F_GETFL for fd %d", fd);
- rc = ERROR_FAIL;
- goto out_err;
- }
-
- LOG(DEBUG, "fnctl F_GETFL flags for fd %d are 0x%x", fd, fdfl);
-
- if (r_oldflags)
- *r_oldflags = fdfl;
-
- fdfl &= mask;
- fdfl |= val;
-
- LOG(DEBUG, "fnctl F_SETFL of fd %d to 0x%x", fd, fdfl);
-
- ret = fcntl(fd, F_SETFL, fdfl);
- if (ret < 0) {
- LOGE(ERROR, "failed to fcntl.F_SETFL for fd %d", fd);
- rc = ERROR_FAIL;
- goto out_err;
- }
-
- rc = 0;
-
-out_err:
- return rc;
-}
-
-int libxl__fd_flags_restore(libxl__gc *gc, int fd, int fdfl)
-{
- int ret, rc;
-
- LOG(DEBUG, "fnctl F_SETFL of fd %d to 0x%x", fd, fdfl);
-
- ret = fcntl(fd, F_SETFL, fdfl);
- if (ret < 0) {
- LOGE(ERROR, "failed to fcntl.F_SETFL for fd %d", fd);
- rc = ERROR_FAIL;
- goto out_err;
- }
-
- rc = 0;
-
-out_err:
- return rc;
-
-}
-
-void libxl_hwcap_copy(libxl_ctx *ctx,libxl_hwcap *dst, const libxl_hwcap *src)
-{
- int i;
-
- for (i = 0; i < 8; i++)
- (*dst)[i] = (*src)[i];
-}
-
-void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src)
-{
- int i;
-
- for (i = 0; i < 6; i++)
- (*dst)[i] = (*src)[i];
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-/*
- * libxl API compatibility
- *
- * From Xen 4.2 onwards the API of libxl will be maintained in a
- * stable manner. This means that it should be possible to write an
- * application against the API provided by libxl in Xen 4.2 and expect
- * that it will continue to compile against future versions of Xen
- * without source modification.
- *
- * In order to make such compatibility possible it is required that
- * application which want to be exposed to a particular API #define
- * LIBXL_API_VERSION before including libxl.h or any other libxl
- * header. The syntax of the LIBXL_API_VERSION is:
- * 0xVVSSEE
- * where ($(XEN_xxx) from xen/Makefile):
- * VV is the Xen major release number, $(XEN_VERSION)
- * SS is the Xen sub version number, $(XEN_SUBVERSION)
- * EE is the Xen extra version digit, first numeric part of
- * $(XEN_EXTRAVERSION) not including the leading "."
- * For example the first stable API version, supported by Xen 4.2.0,
- * is 0x040200.
- *
- * Lack of LIBXL_API_VERSION means "the latest" which will
- * change. Specifying an unknown LIBXL_API_VERSION will result in a
- * compile time error.
- *
- * Identical versions of the libxl API will represented by the version
- * containing the earliest instance of that API. e.g. if 4.2.0 and
- * 4.3.0 contain an identical libxl API then only LIBXL_API_VERSION
- * 0x040200 will be valid.
- *
- * We will try especially hard to avoid changing the API during a
- * stable series, i.e. it should be unusual for the last byte of
- * LIBXL_API_VERSION to be non-zero.
- *
- * In the event that a change is required which cannot be made
- * backwards compatible in this manner a #define of the form
- * LIBXL_HAVE_<interface> will always be added in order to make it
- * possible to write applications which build against any version of
- * libxl. Such changes are expected to be exceptional and used as a
- * last resort. The barrier for backporting such a change to a stable
- * branch will be very high.
- *
- * These guarantees apply only to stable releases of Xen. When an
- * incompatible change is made in the unstable tree then
- * LIBXL_API_VERSION will be bumped to the next expected stable
- * release number on the first such change only. Applications which
- * want to support building against Xen unstable are expected to track
- * API changes in that tree until it is released as a stable release.
- *
- * API compatibility will be maintained for all versions of Xen using
- * the same $(XEN_VERSION) (e.g. throughout a major release).
- */
-
-/* LIBXL_HAVE_PHYSINFO_CAP_PV
- *
- * If this is defined, libxl_physinfo has a "cap_pv" field.
- */
-#define LIBXL_HAVE_PHYSINFO_CAP_PV 1
-
-/* LIBXL_HAVE_CONSOLE_NOTIFY_FD
- *
- * If this is defined, libxl_console_exec and
- * libxl_primary_console_exe take a notify_fd parameter. That
- * parameter will be used to notify the caller that the console is connected.
- */
-#define LIBXL_HAVE_CONSOLE_NOTIFY_FD 1
-
-/* LIBXL_HAVE_CONST_COPY_AND_LENGTH_FUNCTIONS
- *
- * If this is defined, the copy functions have constified src parameter and the
- * length functions accept constified parameter.
- */
-#define LIBXL_HAVE_CONST_COPY_AND_LENGTH_FUNCTIONS 1
-
-/* LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONST_B_INFO
- *
- * If this is defined, libxl_domain_need_memory no longer modifies
- * the b_info paseed in.
- */
-#define LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONST_B_INFO 1
-
-/* LIBXL_HAVE_VNUMA
- *
- * If this is defined the type libxl_vnode_info exists, and a
- * field 'vnuma_nodes' is present in libxl_domain_build_info.
- */
-#define LIBXL_HAVE_VNUMA 1
-
-/* LIBXL_HAVE_USERDATA_UNLINK
- *
- * If it is defined, libxl has a library function called
- * libxl_userdata_unlink.
- */
-#define LIBXL_HAVE_USERDATA_UNLINK 1
-
-/* LIBXL_HAVE_CPUPOOL_QUALIFIER_TO_CPUPOOLID
- *
- * If this is defined, libxl has a library function called
- * libxl_cpupool_qualifier_to_cpupoolid, which takes in a CPU pool
- * qualifier in the form of number or string, then returns the ID of
- * that CPU pool.
- */
-#define LIBXL_HAVE_CPUPOOL_QUALIFIER_TO_CPUPOOLID 1
-
-/* LIBXL_HAVE_CPUPOOL_ADD_REM_CPUMAP
- *
- * If this is defined, libxl has two library functions called
- * libxl_cpupool_cpuadd_cpumap and libxl_cpupool_cpuremove_cpumap,
- * which allow to add to or remove from a cpupool all the cpus
- * specified in a bitmap.
- */
-#define LIBXL_HAVE_CPUPOOL_ADD_REM_CPUMAP 1
-
-/*
- *
- * LIBXL_HAVE_BITMAP_AND_OR
- *
- * If this is defined, libxl has two library functions, libxl_bitmap_and
- * and libxl_bitmap_or to compute the logical and and or of two bitmaps
- */
-#define LIBXL_HAVE_BITMAP_AND_OR 1
-
-/*
- * LIBXL_HAVE_FIRMWARE_PASSTHROUGH indicates the feature for
- * passing in SMBIOS and ACPI firmware to HVM guests is present
- * in the library.
- */
-#define LIBXL_HAVE_FIRMWARE_PASSTHROUGH 1
-
-/*
- * LIBXL_HAVE_DOMAIN_NODEAFFINITY indicates that a 'nodemap' field
- * (of libxl_bitmap type) is present in libxl_domain_build_info,
- * containing the node-affinity for the domain.
- */
-#define LIBXL_HAVE_DOMAIN_NODEAFFINITY 1
-
-/*
- * LIBXL_HAVE_PVUSB indicates functions for plugging in USB devices
- * through pvusb -- both hotplug and at domain creation time..
- */
-#define LIBXL_HAVE_PVUSB 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE indicates that the
- * libxl_vendor_device field is present in the hvm sections of
- * libxl_domain_build_info. This field tells libxl which
- * flavour of xen-pvdevice to enable in QEMU.
- */
-#define LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE 1
-
-/*
- * The libxl_domain_build_info has the event_channels field.
- */
-#define LIBXL_HAVE_BUILDINFO_EVENT_CHANNELS 1
-
-/*
- * libxl_domain_build_info has the u.hvm.ms_vm_genid field.
- */
-#define LIBXL_HAVE_BUILDINFO_HVM_MS_VM_GENID 1
-
-/*
- * LIBXL_HAVE_VCPUINFO_SOFT_AFFINITY indicates that a 'cpumap_soft'
- * field (of libxl_bitmap type) is present in libxl_vcpuinfo,
- * containing the soft affinity of a vcpu.
- */
-#define LIBXL_HAVE_VCPUINFO_SOFT_AFFINITY 1
-
-/*
- * LIBXL_HAVE_SET_VCPUAFFINITY_FORCE indicates that the
- * libxl_set_vcpuaffinity_force() library call is available.
- */
-#define LIBXL_HAVE_SET_VCPUAFFINITY_FORCE 1
-
-/*
- * LIBXL_HAVE_DEVICE_DISK_DIRECT_IO_SAFE indicates that a
- * 'direct_io_safe' field (of boolean type) is present in
- * libxl_device_disk.
- */
-#define LIBXL_HAVE_DEVICE_DISK_DIRECT_IO_SAFE 1
-
-/*
- * The libxl_device_disk has the discard_enable field.
- */
-#define LIBXL_HAVE_LIBXL_DEVICE_DISK_DISCARD_ENABLE 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_IOMEM_START_GFN indicates that it is possible
- * to specify the start guest frame number used to map a range of I/O
- * memory machine frame numbers via the 'gfn' field (of type uint64)
- * of the 'iomem' structure. An array of iomem structures is embedded
- * in libxl_domain_build_info and used to map the indicated memory
- * ranges during domain build.
- */
-#define LIBXL_HAVE_BUILDINFO_IOMEM_START_GFN 1
-
-/*
- * LIBXL_HAVE_SCHED_RTDS indicates that the RTDS real time scheduler
- * is available. A 'budget' field added in libxl_domain_sched_params.
- */
-#define LIBXL_HAVE_SCHED_RTDS 1
-
-/*
- * LIBXL_HAVE_SCHED_NULL indicates that the 'null' static scheduler
- * is available.
- */
-#define LIBXL_HAVE_SCHED_NULL 1
-
-/*
- * libxl_domain_build_info has u.hvm.viridian_enable and _disable bitmaps
- * of the specified width.
- */
-#define LIBXL_HAVE_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE 1
-#define LIBXL_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE_WIDTH 64
-
-/*
- * libxl_domain_build_info has the u.hvm.mmio_hole_memkb field.
- */
-#define LIBXL_HAVE_BUILDINFO_HVM_MMIO_HOLE_MEMKB 1
-
-/*
- * libxl_domain_info returns ERROR_DOMAIN_NOTFOUND if the domain
- * is not present, instead of ERROR_INVAL.
- */
-#define LIBXL_HAVE_ERROR_DOMAIN_NOTFOUND 1
-
-/*
- * libxl_domain_build_info has device_tree and libxl_device_dtdev
- * exists. This mean Device Tree passthrough is supported for ARM
- */
-#define LIBXL_HAVE_DEVICETREE_PASSTHROUGH 1
-
-/*
- * libxl_domain_build_info has device_model_user to specify the user to
- * run the device model with. See docs/misc/qemu-deprivilege.txt.
- */
-#define LIBXL_HAVE_DEVICE_MODEL_USER 1
-
-/*
- * libxl_vcpu_sched_params is used to store per-vcpu params.
- */
-#define LIBXL_HAVE_VCPU_SCHED_PARAMS 1
-
-/*
- * LIBXL_HAVE_SCHED_RTDS_VCPU_PARAMS indicates RTDS scheduler
- * now supports per-vcpu settings.
- */
-#define LIBXL_HAVE_SCHED_RTDS_VCPU_PARAMS 1
-
-/*
- * LIBXL_HAVE_SCHED_RTDS_VCPU_EXTRA indicates RTDS scheduler
- * now supports per-vcpu extratime settings.
- */
-#define LIBXL_HAVE_SCHED_RTDS_VCPU_EXTRA 1
-
-/*
- * libxl_domain_build_info has the arm.gic_version field.
- */
-#define LIBXL_HAVE_BUILDINFO_ARM_GIC_VERSION 1
-
-/*
- * libxl_domain_build_info has the arch_arm.tee field.
- */
-#define LIBXL_HAVE_BUILDINFO_ARCH_ARM_TEE 1
-
-/*
- * LIBXL_HAVE_SOFT_RESET indicates that libxl supports performing
- * 'soft reset' for domains and there is 'soft_reset' shutdown reason
- * in enum libxl_shutdown_reason.
- */
-#define LIBXL_HAVE_SOFT_RESET 1
-
-/*
- * LIBXL_HAVE_APIC_ASSIST indicates that the 'apic_assist' value
- * is present in the viridian enlightenment enumeration.
- */
-#define LIBXL_HAVE_APIC_ASSIST 1
-
-/*
- * LIBXL_HAVE_BUILD_ID means that libxl_version_info has the extra
- * field for the hypervisor build_id.
- */
-#define LIBXL_HAVE_BUILD_ID 1
-
-/*
- * LIBXL_HAVE_QEMU_MONITOR_COMMAND indiactes the availability of the
- * libxl_qemu_monitor_command() function.
- */
-#define LIBXL_HAVE_QEMU_MONITOR_COMMAND 1
-
-/*
- * LIBXL_HAVE_SCHED_CREDIT2_PARAMS indicates the existance of a
- * libxl_sched_credit2_params structure, containing Credit2 scheduler
- * wide parameters (i.e., the ratelimiting value).
- */
-#define LIBXL_HAVE_SCHED_CREDIT2_PARAMS 1
-
-/*
- * LIBXL_HAVE_SCHED_CREDIT_MIGR_DELAY indicates that there is a field
- * in libxl_sched_credit_params called vcpu_migr_delay_us which controls
- * the resistance of the vCPUs of the cpupool to migrations among pCPUs.
- */
-#define LIBXL_HAVE_SCHED_CREDIT_MIGR_DELAY
-
-/*
- * LIBXL_HAVE_VIRIDIAN_CRASH_CTL indicates that the 'crash_ctl' value
- * is present in the viridian enlightenment enumeration.
- */
-#define LIBXL_HAVE_VIRIDIAN_CRASH_CTL 1
-
-/*
- * LIBXL_HAVE_VIRIDIAN_SYNIC indicates that the 'synic' value
- * is present in the viridian enlightenment enumeration.
- */
-#define LIBXL_HAVE_VIRIDIAN_SYNIC 1
-
-/*
- * LIBXL_HAVE_VIRIDIAN_STIMER indicates that the 'stimer' value
- * is present in the viridian enlightenment enumeration.
- */
-#define LIBXL_HAVE_VIRIDIAN_STIMER 1
-
-/*
- * LIBXL_HAVE_VIRIDIAN_HCALL_IPI indicates that the 'hcall_ipi' value
- * is present in the viridian enlightenment enumeration.
- */
-#define LIBXL_HAVE_VIRIDIAN_HCALL_IPI 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE indicates that
- * libxl_domain_build_info has the u.hvm.acpi_laptop_slate field.
- */
-#define LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE 1
-
-/*
- * LIBXL_HAVE_P9S indicates that the p9 field in IDL has been changed to p9s
- */
-#define LIBXL_HAVE_P9S 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_ARM_VUART indicates that the toolstack supports virtual UART
- * for ARM.
- */
-#define LIBXL_HAVE_BUILDINFO_ARM_VUART 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_GRANT_LIMITS indicates that libxl_domain_build_info
- * has the max_grant_frames and max_maptrack_frames fields.
- */
-#define LIBXL_HAVE_BUILDINFO_GRANT_LIMITS 1
-
-#define LIBXL_MAX_GRANT_DEFAULT (~(uint32_t)0)
-#define LIBXL_MAX_GRANT_FRAMES_DEFAULT 32 /* deprecated */
-#define LIBXL_MAX_MAPTRACK_FRAMES_DEFAULT 1024 /* deprecated */
-/*
- * LIBXL_HAVE_BUILDINFO_GRANT_DEFAULT indicates that the default
- * values of max_grant_frames and max_maptrack_frames fields in
- * libxl_domain_build_info are the special sentinel value
- * LIBXL_MAX_GRANT_DEFAULT rather than the fixed values above.
- * This means to use the hypervisor's default.
- */
-#define LIBXL_HAVE_BUILDINFO_GRANT_DEFAULT 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_* indicates that libxl_domain_build_info has
- * the field represented by the '*'. The original position of those
- * fields is:
- * - u.hvm.timer_mode
- * - u.hvm.apic
- * - u.hvm.nested_hvm
- * - u.pv.bootloader
- * - u.pv.bootloader_args
- */
-#define LIBXL_HAVE_BUILDINFO_TIMER_MODE 1
-#define LIBXL_HAVE_BUILDINFO_APIC 1
-#define LIBXL_HAVE_BUILDINFO_NESTED_HVM 1
-#define LIBXL_HAVE_BUILDINFO_BOOTLOADER 1
-#define LIBXL_HAVE_BUILDINFO_BOOTLOADER_ARGS 1
-
-/*
- * LIBXL_HAVE_EXTENDED_VKB indicates that libxl_device_vkb has extended fields:
- * - unique_id;
- * - feature_disable_keyboard;
- * - feature_disable_pointer;
- * - feature_abs_pointer;
- * - feature_raw_pointer;
- * - feature_multi_touch;
- * - width;
- * - height;
- * - multi_touch_width;
- * - multi_touch_height;
- * - multi_touch_num_contacts.
- */
-#define LIBXL_HAVE_EXTENDED_VKB 1
-
-/*
- * LIBXL_HAVE_PHYSINFO_CAP_HAP_SHADOW indicates that libxl_physinfo has
- * cap_hap and cap_shadow fields reflecting the hardware and Xen availability
- * of Hardware Assisted, and Shadow paging support.
- */
-#define LIBXL_HAVE_PHYSINFO_CAP_HAP_SHADOW 1
-
-/*
- * LIBXL_HAVE_PHYSINFO_CAP_IOMMU_HAP_PT_SHARE indicates that libxl_physinfo
- * has a cap_iommu_hap_pt_share field that indicates whether the hardware
- * supports sharing the IOMMU and HAP page tables.
- */
-#define LIBXL_HAVE_PHYSINFO_CAP_IOMMU_HAP_PT_SHARE 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_IOMMU_MEMKB indicates thate libxl_domain_build_info
- * has an iommu_memkb field which should be set with the amount of memory
- * overhead needed by the domain for populating IOMMU page tables.
- */
-#define LIBXL_HAVE_BUILDINFO_IOMMU_MEMKB 1
-
-/*
- * LIBXL_HAVE_CREATEINFO_PASSTHROUGH indicates that
- * libxl_domain_create_info has a passthrough field (which is a
- * libxl_passthrough enumeration) that indicates whether device pass-
- * through is enabled for the domain and, if so, whether the IOMMU and
- * HAP page tables may be shared or not.
- */
-#define LIBXL_HAVE_CREATEINFO_PASSTHROUGH 1
-
-/*
- * LIBXL_HAVE_DISK_SAFE_REMOVE indicates that the
- * libxl_device_disk_safe_remove() function is defined.
- */
-#define LIBXL_HAVE_DISK_SAFE_REMOVE 1
-
-/*
- * libxl ABI compatibility
- *
- * The only guarantee which libxl makes regarding ABI compatibility
- * across releases is that the SONAME will always be bumped whenever
- * the ABI is changed in an incompatible way.
- *
- * This applies within stable branches as well as
- * development branches. It is possible that a new stable release of
- * Xen may require a rebuild of applications using the
- * library. However per the API compatibility gaurantees such a
- * rebuild should not normally require any source level changes.
- *
- * As with the API compatiblity the SONAME will only be bumped for the
- * first ABI incompatible change in a development branch.
- */
-
-/*
- * libxl memory management
- *
- * From the point of view of the application (ie, libxl's caller),
- * struct libxl_ctx* is threadsafe, and all returned allocated
- * structures are obtained from malloc(), and must be freed by the
- * caller either directly or by calling an appropriate free function
- * provided by libxl. Ie the application does not get automatic
- * assistance from libxl in managing these allocations.
- *
- * Specific details are in the header comments which should be found
- * in libxl.h or libxlutil.h, next to the relevant function
- * declarations.
- *
- * Internally, libxl has a garbage collection scheme which allows much libxl
- * code to allocate strings etc. for internal use without needing to
- * free them. These are called "temporary allocations".
- *
- * The pool for these temporary allocations, along with any other
- * thread-specific data which is private to libxl but shared between
- * libxl functions (such as the current xenstore transaction), is
- * stored in the "gc context" which is a special enhanced context
- * structure allocated automatically by convenience macros at every
- * entry to libxl.
- *
- * Every libxl function falls into one of these categories:
- *
- * 1. Public functions (declared in libxl.h, libxlutil.h), which may
- * be called by libxl applications. If a public function returns
- * any allocated object to its caller, that object must have come
- * from malloc.
- *
- * The definitions of public functions MUST use the gc context
- * initialisation macros (or do the equivalent work themselves).
- * These macros will ensure that all temporary allocations will be
- * automatically freed before the function returns to its caller.
- *
- * A public function may be called from within libxl; the call
- * context initialisation macros will make sure that the internal
- * caller's context is reused (eg, so that the same xenstore
- * transaction is used). But in-libxl callers of libxl public
- * functions should note that any libxl public function may cause
- * recursively reentry into libxl via the application's event
- * callback hook.
- *
- * Public functions have names like libxl_foobar.
- *
- * 2. Private functions, which may not be called by libxl
- * applications; they are not declared in libxl.h or libxlutil.h
- * and they may not be called other than by other libxl functions.
- *
- * Private functions should not use the gc context initialisation
- * macros.
- *
- * Private functions have names like libxl__foobar (NB, two underscores).
- * Also the declaration of such functions must be preceeded by the _hidden
- * macro.
- *
- * Allocations made by a libxl function fall into one of the following
- * categories (where "object" includes any memory allocation):
- *
- * (a) Objects which are not returned to the function's caller.
- * These should be allocated from the temporary pool.
- *
- * (b) Objects which are intended for return to the calling
- * application. This includes all allocated objects returned by
- * any public function.
- *
- * It may also include objects allocated by an internal function
- * specifically for eventual return by the function's external
- * callers, but this situation should be clearly documented in
- * comments.
- *
- * These should be allocated from malloc() et al. and comments
- * near the function declaration should explain the memory
- * ownership. If a simple free() by the application is not
- * sufficient, a suitable public freeing function should be
- * provided.
- *
- * (c) Internal objects whose size and/or lifetime dictate explicit
- * memory management within libxl. This includes objects which
- * will be embedded in opaque structures which will be returned to
- * the libxl caller (more generally, any internal object whose
- * lifetime exceeds the libxl entrypoint which creates it) and
- * objects which are so large or numerous that explicit memory
- * management is required.
- *
- * These should be allocated from malloc() et al., and freed
- * explicitly at the appropriate point. The situation should be
- * documented in comments.
- *
- * (d) Objects which are allocated by internal-only functions and
- * returned to the function's (therefore, internal) caller but are
- * strictly for internal use by other parts of libxl. These
- * should be allocated from the temporary pool.
- *
- * Where a function's primary purpose is to return such an object,
- * it should have a libxl__gc * as it's first argument.
- *
- * Note that there are two ways to change an allocation from this
- * category to the "public" category. Either the implementation
- * is kept internal and a wrapper function duplicates all memory
- * allocations so that they are suitable for return to external
- * callers or the implementation uses plain malloc() et al calls
- * and an internal wrapper adds the relevant pointers to the gc.
- * The latter method is preferred for obvious performance reasons.
- *
- * No temporary objects allocated from the pool may be explicitly freed.
- * Therefore public functions which initialize a libxl__gc MUST call
- * libxl__free_all() before returning.
- *
- * Memory allocation failures are not handled gracefully. If malloc
- * (or realloc) fails, libxl will cause the entire process to print
- * a message to stderr and exit with status 255.
- */
-/*
- * libxl types
- *
- * Most libxl types are defined by the libxl IDL (see
- * libxl_types.idl). The library provides a common set of methods for
- * initialising and freeing these types.
- *
- * IDL-generated libxl types should be used as follows: the user must
- * always call the "init" function before using a type, even if the
- * variable is simply being passed by reference as an out parameter
- * to a libxl function. The user must always calls "dispose" exactly
- * once afterwards, to clean up, regardless of whether operations on
- * this object succeeded or failed. See the xl code for examples.
- *
- * "init" and "dispose" are idempotent.
- *
- * void libxl_<type>_init(<type> *p):
- *
- * Initialises the members of "p" to all defaults. These may either
- * be special value which indicates to the library that it should
- * select an appropriate default when using this field or actual
- * default values.
- *
- * Some fields within a data type (e.g. unions) cannot be sensibly
- * initialised without further information. In these cases a
- * separate subfield initialisation function is provided (see
- * below).
- *
- * An instance which has been initialised using this method can
- * always be safely passed to the dispose function (see
- * below). This is true even if the data type contains fields which
- * require a separate call to a subfield initialisation function.
- *
- * This method is provided for any aggregate type which is used as
- * an input parameter.
- *
- * void libxl_<type>_init_<subfield>(<type> *p, subfield):
- *
- * Initialise those parts of "p" which are not initialised by the
- * main init function due to the unknown value of "subfield". Sets
- * p->subfield as well as initialising any fields to their default
- * values.
- *
- * p->subfield must not have been previously initialised.
- *
- * This method is provided for any aggregate type.
- *
- * void libxl_<type>_dispose(instance *p):
- *
- * Frees any dynamically allocated memory used by the members of
- * "p" but not the storage used by "p" itself (this allows for the
- * allocation of arrays of types and for the composition of types).
- *
- * char *libxl_<type>_to_json(instance *p)
- *
- * Generates a JSON object from "p" in the form of a NULL terminated
- * string.
- *
- * <type *> libxl_<type>_from_json(const char *json)
- * int libxl_<type>_from_json(const char *json)
- *
- * Parses "json" and returns:
- *
- * an int value, if <type> is enumeration type. The value is the enum value
- * representing the respective string in "json".
- *
- * an instance of <type>, if <type> is aggregate type. The returned
- * instance has its fields filled in by the parser according to "json".
- *
- * If the parsing fails, caller cannot rely on the value / instance
- * returned.
- */
-#ifndef LIBXL_H
-#define LIBXL_H
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <sys/wait.h> /* for pid_t */
-
-#include <xentoollog.h>
-
-typedef struct libxl__ctx libxl_ctx;
-
-#include <libxl_uuid.h>
-#include <_libxl_list.h>
-
-/* API compatibility. */
-#ifdef LIBXL_API_VERSION
-#if LIBXL_API_VERSION != 0x040200 && LIBXL_API_VERSION != 0x040300 && \
- LIBXL_API_VERSION != 0x040400 && LIBXL_API_VERSION != 0x040500 && \
- LIBXL_API_VERSION != 0x040700 && LIBXL_API_VERSION != 0x040800 && \
- LIBXL_API_VERSION != 0x041300 && LIBXL_API_VERSION != 0x041400
-#error Unknown LIBXL_API_VERSION
-#endif
-#endif
-
-/* LIBXL_HAVE_RETRIEVE_DOMAIN_CONFIGURATION
- *
- * If this is defined we have libxl_retrieve_domain_configuration which
- * returns the current configuration of a domain, which can be used to
- * rebuild a domain.
- */
-#define LIBXL_HAVE_RETRIEVE_DOMAIN_CONFIGURATION 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_VCPU_AFFINITY_ARRAYS
- *
- * If this is defined, then the libxl_domain_build_info structure will
- * contain two arrays of libxl_bitmap-s, with all the necessary information
- * to set the hard affinity (vcpu_hard_affinity) and the soft affinity
- * (vcpu_soft_affinity) of the VCPUs.
- *
- * Note that, if the vcpu_hard_affinity array is used, libxl will ignore
- * the content of the cpumap field of libxl_domain_build_info. That is to
- * say, if the array is allocated and used by the caller, it is it and
- * only it that determines the hard affinity of the domain's VCPUs.
- *
- * The number of libxl_bitmap-s in the arrays should be equal to the
- * maximum number of VCPUs of the domain. If there only are N elements in
- * an array, with N smaller the the maximum number of VCPUs, the hard or
- * soft affinity (depending on which array we are talking about) will be
- * set only for the first N VCPUs. The other VCPUs will just have affinity,
- * both hard and soft, with all the host PCPUs.
- * Each bitmap should be big enough to accommodate the maximum number of
- * PCPUs of the host.
- */
-#define LIBXL_HAVE_BUILDINFO_VCPU_AFFINITY_ARRAYS 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_VKB_DEVICE
- *
- * If this is defined, then the libxl_domain_build_info structure will
- * contain a boolean hvm.vkb_device which instructs libxl whether to include
- * a vkbd at build time or not.
- */
-#define LIBXL_HAVE_BUILDINFO_VKB_DEVICE 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST
- *
- * If this is defined, then the libxl_domain_build_info structure will
- * contain hvm.usbdevice_list, a libxl_string_list type that contains
- * a list of USB devices to specify on the qemu command-line.
- *
- * If it is set, callers may use either hvm.usbdevice or
- * hvm.usbdevice_list, but not both; if both are set, libxl will
- * throw an error.
- *
- * If this is not defined, callers can only use hvm.usbdevice. Note
- * that this means only one device can be added at domain build time.
- */
-#define LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_USBVERSION
- *
- * If this is defined, then the libxl_domain_build_info structure will
- * contain hvm.usbversion, a integer type that contains a USB
- * controller version to specify on the qemu upstream command-line.
- *
- * If it is set, callers may use hvm.usbversion to specify if the usb
- * controller is usb1, usb2 or usb3.
- *
- * If this is not defined, the hvm.usbversion field does not exist.
- */
-#define LIBXL_HAVE_BUILDINFO_USBVERSION 1
-
-/*
- * LIBXL_HAVE_DEVICE_BACKEND_DOMNAME
- *
- * If this is defined, libxl_device_* structures containing a backend_domid
- * field also contain a backend_domname field. If backend_domname is set, it is
- * resolved to a domain ID when the device is used and takes precedence over the
- * backend_domid field.
- *
- * If this is not defined, the backend_domname field does not exist.
- */
-#define LIBXL_HAVE_DEVICE_BACKEND_DOMNAME 1
-
-/*
- * LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG
- *
- * This argument was erroneously "const" in the 4.2 release despite
- * the requirement for the callback to free the event.
- */
-#if LIBXL_API_VERSION != 0x040200
-#define LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG 1
-#endif
-
-/*
- * LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
- *
- * The return value of libxl_basename is malloc'ed but the erroneously
- * marked as "const" in releases before 4.5.
- */
-#if !defined(LIBXL_API_VERSION) || LIBXL_API_VERSION >= 0x040500
-#define LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE 1
-#endif
-
-/*
- * LIBXL_HAVE_PHYSINFO_OUTSTANDING_PAGES
- *
- * If this is defined, libxl_physinfo structure will contain an uint64 field
- * called outstanding_pages, containing the number of pages claimed but not
- * yet allocated for all domains.
- */
-#define LIBXL_HAVE_PHYSINFO_OUTSTANDING_PAGES 1
-
-/*
- * LIBXL_HAVE_PHYSINFO_MAX_POSSIBLE_MFN
- *
- * If this is defined, libxl_physinfo structure will contain an uint64 field
- * called max_possible_mfn, containing the highest possible mfn on this host,
- * possibly taking memory hotplug into account.
- */
-#define LIBXL_HAVE_PHYSINFO_MAX_POSSIBLE_MFN 1
-
-/*
- * LIBXL_HAVE_DOMINFO_OUTSTANDING_MEMKB 1
- *
- * If this is defined, libxl_dominfo will contain a MemKB type field called
- * outstanding_memkb, containing the amount of claimed but not yet allocated
- * memory for a specific domain.
- */
-#define LIBXL_HAVE_DOMINFO_OUTSTANDING_MEMKB 1
-
-/*
- * LIBXL_HAVE_DOMINFO_NEVER_STOP
- *
- * If this is defined, libxl_dominfo will contain a flag called never_stop
- * indicating that the specific domain should never be stopped by the
- * toolstack.
- */
-#define LIBXL_HAVE_DOMINFO_NEVER_STOP 1
-
-/*
- * LIBXL_HAVE_QXL
- *
- * If defined, then the libxl_vga_interface_type will contain another value:
- * "QXL". This value define if qxl vga is supported.
- *
- * If this is not defined, the qxl vga support is missed.
- */
-#define LIBXL_HAVE_QXL 1
-
-/*
- * LIBXL_HAVE_SPICE_VDAGENT
- *
- * If defined, then the libxl_spice_info structure will contain a boolean type:
- * vdagent and clipboard_sharing. These values define if Spice vdagent and
- * clipboard sharing are enabled.
- *
- * If this is not defined, the Spice vdagent support is ignored.
- */
-#define LIBXL_HAVE_SPICE_VDAGENT 1
-
-/*
- * LIBXL_HAVE_SPICE_USBREDIRECTION
- *
- * If defined, then the libxl_spice_info structure will contain an integer type
- * field: usbredirection. This value defines if Spice usbredirection is enabled
- * and with how much channels.
- *
- * If this is not defined, the Spice usbredirection support is ignored.
- */
-#define LIBXL_HAVE_SPICE_USBREDIREDIRECTION 1
-
-/*
- * LIBXL_HAVE_SPICE_IMAGECOMPRESSION
- *
- * If defined, then the libxl_spice_info structure will contain a string type
- * field: image_compression. This value defines what Spice image compression
- * is used.
- *
- * If this is not defined, the Spice image compression setting support is ignored.
- */
-#define LIBXL_HAVE_SPICE_IMAGECOMPRESSION 1
-
-/*
- * LIBXL_HAVE_SPICE_STREAMINGVIDEO
- *
- * If defined, then the libxl_spice_info structure will contain a string type
- * field: streaming_video. This value defines what Spice streaming video setting
- * is used.
- *
- * If this is not defined, the Spice streaming video setting support is ignored.
- */
-#define LIBXL_HAVE_SPICE_STREAMINGVIDEO 1
-
-/*
- * LIBXL_HAVE_HVM_HDTYPE
- *
- * If defined, then the u.hvm structure will contain a enum type
- * hdtype.
- */
-#define LIBXL_HAVE_HVM_HDTYPE 1
-
-/*
- * LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS 1
- *
- * If this is defined, libxl_domain_create_restore()'s API has changed to
- * include a params structure.
- */
-#define LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS 1
-
-/*
- * LIBXL_HAVE_DOMAIN_CREATE_RESTORE_SEND_BACK_FD 1
- *
- * If this is defined, libxl_domain_create_restore()'s API includes the
- * send_back_fd param. This is used only with COLO, for the libxl migration
- * back channel; other callers should pass -1.
- */
-#define LIBXL_HAVE_DOMAIN_CREATE_RESTORE_SEND_BACK_FD 1
-
-/*
- * LIBXL_HAVE_DRIVER_DOMAIN_CREATION 1
- *
- * If this is defined, libxl_domain_create_info contains a driver_domain
- * field that can be used to tell libxl that the domain that is going
- * to be created is a driver domain, so the necessary actions are taken.
- */
-#define LIBXL_HAVE_DRIVER_DOMAIN_CREATION 1
-
-/*
- * LIBXL_HAVE_SIGCHLD_SELECTIVE_REAP
- *
- * If this is defined:
- *
- * Firstly, the enum libxl_sigchld_owner (in libxl_event.h) has the
- * value libxl_sigchld_owner_libxl_always_selective_reap which may be
- * passed to libxl_childproc_setmode in hooks->chldmode.
- *
- * Secondly, the function libxl_childproc_sigchld_occurred exists.
- */
-#define LIBXL_HAVE_SIGCHLD_OWNER_SELECTIVE_REAP 1
-
-/*
- * LIBXL_HAVE_SIGCHLD_SHARING
- *
- * If this is defined, it is permissible for multiple libxl ctxs
- * to simultaneously "own" SIGCHLD. See "Subprocess handling"
- * in libxl_event.h.
- */
-#define LIBXL_HAVE_SIGCHLD_SHARING 1
-
-/*
- * LIBXL_HAVE_NO_SUSPEND_RESUME
- *
- * Is this is defined then the platform has no support for saving,
- * restoring or migrating a domain. In this case the related functions
- * should be expected to return failure. That is:
- * - libxl_domain_suspend
- * - libxl_domain_resume
- * - libxl_domain_remus_start
- */
-#if defined(__arm__) || defined(__aarch64__)
-#define LIBXL_HAVE_NO_SUSPEND_RESUME 1
-#endif
-
-/*
- * LIBXL_HAVE_DOMAIN_SUSPEND_ONLY
- *
- * If this is defined, function libxl_domains_suspend_only() is available.
- */
-
-#define LIBXL_HAVE_DOMAIN_SUSPEND_ONLY 1
-
-/*
- * LIBXL_HAVE_DEVICE_PCI_SEIZE
- *
- * If this is defined, then the libxl_device_pci struct will contain
- * the "seize" boolean field. If this field is set, libxl_pci_add will
- * check to see if the device is currently assigned to pciback, and if not,
- * it will attempt to do so (unbinding the device from the existing driver).
- */
-#define LIBXL_HAVE_DEVICE_PCI_SEIZE 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_KERNEL
- *
- * If this is defined, then the libxl_domain_build_info structure will
- * contain 'kernel', 'ramdisk', 'cmdline' fields. 'kernel' is a string
- * to indicate kernel image location, 'ramdisk' is a string to indicate
- * ramdisk location, 'cmdline' is a string to indicate the paramters which
- * would be appended to kernel image.
- *
- * Both PV guest and HVM guest can use these fields for direct kernel boot.
- * But for compatibility reason, u.pv.kernel, u.pv.ramdisk and u.pv.cmdline
- * still exist.
- */
-#define LIBXL_HAVE_BUILDINFO_KERNEL 1
-
-/*
- * LIBXL_HAVE_DEVICE_CHANNEL
- *
- * If this is defined, then the libxl_device_channel struct exists
- * and channels can be attached to a domain. Channels manifest as consoles
- * with names, see docs/misc/console.txt.
- */
-#define LIBXL_HAVE_DEVICE_CHANNEL 1
-
-/*
- * LIBXL_HAVE_AO_ABORT indicates the availability of libxl_ao_abort
- */
-#define LIBXL_HAVE_AO_ABORT 1
-
-/* Functions annotated with LIBXL_EXTERNAL_CALLERS_ONLY may not be
- * called from within libxl itself. Callers outside libxl, who
- * do not #include libxl_internal.h, are fine. */
-#ifndef LIBXL_EXTERNAL_CALLERS_ONLY
-#define LIBXL_EXTERNAL_CALLERS_ONLY /* disappears for callers outside libxl */
-#endif
-
-/*
- * LIBXL_HAVE_UUID_COPY_CTX_PARAM
- *
- * If this is defined, libxl_uuid_copy has changed to take a libxl_ctx
- * structure.
- */
-#define LIBXL_HAVE_UUID_COPY_CTX_PARAM 1
-
-/*
- * LIBXL_HAVE_SSID_LABEL
- *
- * If this is defined, then libxl IDL contains string of XSM security
- * label in all XSM related structures.
- *
- * If set this string takes precedence over the numeric field.
- */
-#define LIBXL_HAVE_SSID_LABEL 1
-
-/*
- * LIBXL_HAVE_CPUPOOL_NAME
- *
- * If this is defined, then libxl IDL contains string of CPU pool
- * name in all CPU pool related structures.
- *
- * If set this string takes precedence over the numeric field.
- */
-#define LIBXL_HAVE_CPUPOOL_NAME 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_SERIAL_LIST
- *
- * If this is defined, then the libxl_domain_build_info structure will
- * contain hvm.serial_list, a libxl_string_list type that contains
- * a list of serial ports to specify on the qemu command-line.
- *
- * If it is set, callers may use either hvm.serial or
- * hvm.serial_list, but not both; if both are set, libxl will
- * throw an error.
- *
- * If this is not defined, callers can only use hvm.serial. Note
- * that this means only one serial port can be added at domain build time.
- */
-#define LIBXL_HAVE_BUILDINFO_SERIAL_LIST 1
-
-/*
- * LIBXL_HAVE_ALTP2M
- * If this is defined, then libxl supports alternate p2m functionality.
- */
-#define LIBXL_HAVE_ALTP2M 1
-
-/*
- * LIBXL_HAVE_REMUS
- * If this is defined, then libxl supports remus.
- */
-#define LIBXL_HAVE_REMUS 1
-
-/*
- * LIBXL_HAVE_COLO_USERSPACE_PROXY
- * If this is defined, then libxl supports COLO userspace proxy.
- */
-#define LIBXL_HAVE_COLO_USERSPACE_PROXY 1
-
-typedef uint8_t libxl_mac[6];
-#define LIBXL_MAC_FMT "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
-#define LIBXL_MAC_FMTLEN ((2*6)+5) /* 6 hex bytes plus 5 colons */
-#define LIBXL_MAC_BYTES(mac) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
-void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src);
-
-#if defined(__i386__) || defined(__x86_64__)
-/*
- * LIBXL_HAVE_PSR_CMT
- *
- * If this is defined, the Cache Monitoring Technology feature is supported.
- */
-#define LIBXL_HAVE_PSR_CMT 1
-
-/*
- * LIBXL_HAVE_PSR_MBM
- *
- * If this is defined, the Memory Bandwidth Monitoring feature is supported.
- */
-#define LIBXL_HAVE_PSR_MBM 1
-
-/*
- * LIBXL_HAVE_PSR_CAT
- *
- * If this is defined, the Cache Allocation Technology feature is supported.
- */
-#define LIBXL_HAVE_PSR_CAT 1
-
-/*
- * LIBXL_HAVE_PSR_CDP
- *
- * If this is defined, the Code and Data Prioritization feature is supported.
- */
-#define LIBXL_HAVE_PSR_CDP 1
-
-/*
- * LIBXL_HAVE_PSR_L2_CAT
- *
- * If this is defined, the L2 Cache Allocation Technology feature is supported.
- */
-#define LIBXL_HAVE_PSR_L2_CAT 1
-
-/*
- * LIBXL_HAVE_PSR_GENERIC
- *
- * If this is defined, the Memory Bandwidth Allocation feature is supported.
- * The following public functions are available:
- * libxl_psr_{set/get}_val
- * libxl_psr_get_hw_info
- * libxl_psr_hw_info_list_free
- */
-#define LIBXL_HAVE_PSR_GENERIC 1
-
-/*
- * LIBXL_HAVE_MCA_CAPS
- *
- * If this is defined, setting MCA capabilities for HVM domain is supported.
- */
-#define LIBXL_HAVE_MCA_CAPS 1
-#endif
-
-/*
- * LIBXL_HAVE_PCITOPOLOGY
- *
- * If this is defined, then interface to query hypervisor about PCI device
- * topology is available.
- */
-#define LIBXL_HAVE_PCITOPOLOGY 1
-
-/*
- * LIBXL_HAVE_SOCKET_BITMAP
- *
- * If this is defined, then libxl_socket_bitmap_alloc and
- * libxl_get_online_socketmap exist.
- */
-#define LIBXL_HAVE_SOCKET_BITMAP 1
-
-/*
- * LIBXL_HAVE_SRM_V2
- *
- * If this is defined, then the libxl_domain_create_restore() interface takes
- * a "stream_version" parameter and supports a value of 2.
- *
- * libxl_domain_suspend() will produce a v2 stream.
- */
-#define LIBXL_HAVE_SRM_V2 1
-
-/*
- * LIBXL_HAVE_SRM_V1
- *
- * In the case that LIBXL_HAVE_SRM_V2 is set, LIBXL_HAVE_SRM_V1
- * indicates that libxl_domain_create_restore() can handle a "stream_version"
- * parameter of 1, and convert the stream format automatically.
- */
-#define LIBXL_HAVE_SRM_V1 1
-
-/*
- * libxl_domain_build_info has the u.hvm.gfx_passthru_kind field and
- * the libxl_gfx_passthru_kind enumeration is defined.
-*/
-#define LIBXL_HAVE_GFX_PASSTHRU_KIND
-
-/*
- * LIBXL_HAVE_CHECKPOINTED_STREAM
- *
- * If this is defined, then libxl_checkpointed_stream exists.
- */
-#define LIBXL_HAVE_CHECKPOINTED_STREAM 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_HVM_SYSTEM_FIRMWARE
- *
- * libxl_domain_build_info has u.hvm.system_firmware field which can be use
- * to provide a different firmware blob (like SeaBIOS or OVMF).
- */
-#define LIBXL_HAVE_BUILDINFO_HVM_SYSTEM_FIRMWARE
-
-/*
- * ERROR_REMUS_XXX error code only exists from Xen 4.5, Xen 4.6 and it
- * is changed to ERROR_CHECKPOINT_XXX in Xen 4.7
- */
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION >= 0x040500 \
- && LIBXL_API_VERSION < 0x040700
-#define ERROR_REMUS_DEVOPS_DOES_NOT_MATCH \
- ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH
-#define ERROR_REMUS_DEVICE_NOT_SUPPORTED \
- ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED
-#endif
-
-/*
- * LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN
- *
- * In the case that LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN is set the
- * libxl_vga_interface_type enumeration type contains a
- * LIBXL_VGA_INTERFACE_TYPE_UNKNOWN identifier. This is used to signal
- * that a libxl_vga_interface_type type has not been initialized yet.
- */
-#define LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN 1
-
-/*
- * LIBXL_HAVE_BYTEARRAY_UUID
- *
- * If this is defined, the internal member of libxl_uuid is defined
- * as a 16 byte array that contains the UUID in big endian format.
- * Also, the same structure layout is used across all OSes.
- */
-#define LIBXL_HAVE_BYTEARRAY_UUID 1
-
-/*
- * LIBXL_HAVE_MEMKB_64BITS
- *
- * If this is defined libxl_set_memory_target(), libxl_domain_setmaxmem()
- * and libxl_wait_for_free_memory() will take a 64 bit value for the memory
- * size parameter.
- * From Xen 4.8 on libxl_get_memory_target(), libxl_domain_need_memory() and
- * libxl_get_free_memory() return the memory size in a 64 bit value, too.
- */
-#define LIBXL_HAVE_MEMKB_64BITS 1
-
-/*
- * LIBXL_HAVE_QED
- *
- * If this is defined QED disk formats can be used for both HVM and PV guests.
- */
-#define LIBXL_HAVE_QED 1
-
-/*
- * LIBXL_HAVE_SET_PARAMETERS
- *
- * If this is defined setting hypervisor parameters is supported.
- */
-#define LIBXL_HAVE_SET_PARAMETERS 1
-
-/*
- * LIBXL_HAVE_PV_SHIM
- *
- * If this is defined, libxl_domain_build_info's pvh type information
- * contains members pvshim, pvshim_path, pvshim_cmdline, pvshim_extra.
- */
-#define LIBXL_HAVE_PV_SHIM 1
-
-/*
- * LIBXL_HAVE_PVCALLS
- *
- * If this is defined, libxl supports creating pvcalls interfaces.
- */
-#define LIBXL_HAVE_PVCALLS 1
-
-/*
- * LIBXL_HAVE_FN_USING_QMP_ASYNC
- *
- * This define indicates that some function's API has changed and have an
- * extra parameter "ao_how" which means that the function can be executed
- * asynchronously. Those functions are:
- * libxl_domain_pause()
- * libxl_domain_unpause()
- * libxl_send_trigger()
- * libxl_set_vcpuonline()
- * libxl_retrieve_domain_configuration()
- * libxl_qemu_monitor_command()
- * libxl_domain_shutdown()
- * libxl_domain_reboot()
- */
-#define LIBXL_HAVE_FN_USING_QMP_ASYNC 1
-
-/*
- * LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONFIG
- *
- * If this is set, libxl_domain_need_memory takes a
- * libxl_domain_config* (non-const) and uint32_t domid_for_logging
- * (instead of a const libxl_domain_build_info*).
- *
- * If this is set, there is no need to call
- * libxl_get_required_shadow_memory and instead the caller should
- * simply leave shadow_memkb set to LIBXL_MEMKB_DEFAULT and allow
- * libxl to fill in a suitable default in the usual way.
- */
-#define LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONFIG
-
-/*
- * LIBXL_HAVE_CREATEINFO_DOMID
- *
- * libxl_domain_create_new() and libxl_domain_create_restore() will use
- * a domid specified in libxl_domain_create_info.
- */
-#define LIBXL_HAVE_CREATEINFO_DOMID
-
-/*
- * LIBXL_HAVE_CREATEINFO_XEND_SUSPEND_EVTCHN_COMPAT
- *
- * libxl_domain_create_info contains a boolean 'xend_suspend_evtchn_compat'
- * value to control creation of the xenstore path for a domain's suspend
- * event channel.
- */
-#define LIBXL_HAVE_CREATEINFO_XEND_SUSPEND_EVTCHN_COMPAT
-
-typedef char **libxl_string_list;
-void libxl_string_list_dispose(libxl_string_list *sl);
-int libxl_string_list_length(const libxl_string_list *sl);
-void libxl_string_list_copy(libxl_ctx *ctx, libxl_string_list *dst,
- const libxl_string_list *src);
-
-typedef char **libxl_key_value_list;
-void libxl_key_value_list_dispose(libxl_key_value_list *kvl);
-int libxl_key_value_list_length(const libxl_key_value_list *kvl);
-void libxl_key_value_list_copy(libxl_ctx *ctx,
- libxl_key_value_list *dst,
- const libxl_key_value_list *src);
-
-typedef uint32_t libxl_hwcap[8];
-void libxl_hwcap_copy(libxl_ctx *ctx, libxl_hwcap *dst, const libxl_hwcap *src);
-
-typedef uint64_t libxl_ev_user;
-
-typedef struct {
- uint32_t size; /* number of bytes in map */
- uint8_t *map;
-} libxl_bitmap;
-void libxl_bitmap_init(libxl_bitmap *map);
-void libxl_bitmap_dispose(libxl_bitmap *map);
-
-/*
- * libxl_cpuid_policy is opaque in the libxl ABI. Users of both libxl and
- * libxc may not make assumptions about xc_xend_cpuid.
- */
-typedef struct xc_xend_cpuid libxl_cpuid_policy;
-typedef libxl_cpuid_policy * libxl_cpuid_policy_list;
-void libxl_cpuid_dispose(libxl_cpuid_policy_list *cpuid_list);
-int libxl_cpuid_policy_list_length(const libxl_cpuid_policy_list *l);
-void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
- libxl_cpuid_policy_list *dst,
- const libxl_cpuid_policy_list *src);
-
-#define LIBXL_PCI_FUNC_ALL (~0U)
-
-typedef uint32_t libxl_domid;
-typedef int libxl_devid;
-
-/*
- * Formatting Enumerations.
- *
- * Each enumeration type libxl_E declares an associated lookup table
- * libxl_E_string_table and a lookup function libxl_E_from_string.
- */
-typedef struct {
- const char *s;
- int v;
-} libxl_enum_string_table;
-
-struct libxl_event;
-typedef LIBXL_TAILQ_ENTRY(struct libxl_event) libxl_ev_link;
-
-/*
- * A boolean variable with an explicit default state.
- *
- * Users should treat this struct as opaque and use the following
- * defined macros and accessor functions.
- *
- * To allow users of the library to naively select all defaults this
- * state is represented as 0. False is < 0 and True is > 0.
- */
-typedef struct {
- int val;
-} libxl_defbool;
-
-void libxl_defbool_set(libxl_defbool *db, bool b);
-/* Resets to default */
-void libxl_defbool_unset(libxl_defbool *db);
-/* Sets db only if it is currently == default */
-void libxl_defbool_setdefault(libxl_defbool *db, bool b);
-bool libxl_defbool_is_default(libxl_defbool db);
-/* db must not be == default */
-bool libxl_defbool_val(libxl_defbool db);
-
-const char *libxl_defbool_to_string(libxl_defbool b);
-
-#define LIBXL_TIMER_MODE_DEFAULT -1
-#define LIBXL_MEMKB_DEFAULT ~0ULL
-
-/*
- * We'd like to set a memory boundary to determine if we need to check
- * any overlap with reserved device memory.
- */
-#define LIBXL_RDM_MEM_BOUNDARY_MEMKB_DEFAULT (2048 * 1024)
-
-#define LIBXL_MS_VM_GENID_LEN 16
-typedef struct {
- uint8_t bytes[LIBXL_MS_VM_GENID_LEN];
-} libxl_ms_vm_genid;
-
-#include "_libxl_types.h"
-
-const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx);
-
-/*
- * Some libxl operations can take a long time. These functions take a
- * parameter to control their concurrency:
- * libxl_asyncop_how *ao_how
- *
- * If ao_how==NULL, the function will be synchronous.
- *
- * If ao_how!=NULL, the function will set the operation going, and if
- * this is successful will return 0. In this case the zero error
- * response does NOT mean that the operation was successful; it just
- * means that it has been successfully started. It will finish later,
- * perhaps with an error.
- *
- * If ao_how->callback!=NULL, the callback will be called when the
- * operation completes. The same rules as for libxl_event_hooks
- * apply, including the reentrancy rules and the possibility of
- * "disaster", except that libxl calls ao_how->callback instead of
- * libxl_event_hooks.event_occurs. (See libxl_event.h.)
- *
- * If ao_how->callback==NULL, a libxl_event will be generated which
- * can be obtained from libxl_event_wait or libxl_event_check. The
- * event will have type OPERATION_COMPLETE (which is not used
- * elsewhere).
- *
- * Note that it is possible for an asynchronous operation which is to
- * result in a callback to complete during its initiating function
- * call. In this case the initiating function will return 0
- * indicating the at the operation is "in progress", even though by
- * the time it returns the operation is complete and the callback has
- * already happened.
- *
- * The application must set and use ao_how->for_event (which will be
- * copied into libxl_event.for_user) or ao_how->for_callback (passed
- * to the callback) to determine which operation finished, and it must
- * of course check the rc value for errors.
- *
- * *ao_how does not need to remain valid after the initiating function
- * returns. All other parameters must remain valid for the lifetime of
- * the asynchronous operation, unless otherwise specified.
- *
- * Callbacks may occur on any thread in which the application calls
- * libxl.
- */
-
-typedef struct {
- void (*callback)(libxl_ctx *ctx, int rc, void *for_callback);
- union {
- libxl_ev_user for_event; /* used if callback==NULL */
- void *for_callback; /* passed to callback */
- } u;
-} libxl_asyncop_how;
-
-/*
- * Some more complex asynchronous operations can report intermediate
- * progress. How this is to be reported is controlled, for each
- * function, by a parameter
- * libxl_asyncprogress_how *aop_FOO_how;
- * for each kind of progress FOO supported by that function. Each
- * such kind of progress is associated with an event type.
- *
- * The function description will document whether, when, and how
- * many times, the intermediate progress will be reported, and
- * what the corresponding event type(s) are.
- *
- * If aop_FOO_how==NULL, intermediate progress reports are discarded.
- *
- * If aop_FOO_how->callback==NULL, intermediate progress reports
- * generate libxl events which can be obtained from libxl_event_wait
- * or libxl_event_check.
- *
- * If aop_FOO_how->callback!=NULL, libxl will report intermediate
- * progress by calling callback(ctx, &event, for_callback).
- *
- * The rules for these events are otherwise the same as those for
- * ordinary events. The reentrancy and threading rules for the
- * callback are the same as those for ao completion callbacks.
- *
- * Note that the callback, if provided, is responsible for freeing
- * the event.
- *
- * If callbacks are requested, they will be made, and returned, before
- * the long-running libxl operation is considered finished (so if the
- * long-running libxl operation was invoked with ao_how==NULL then any
- * callbacks will occur strictly before the long-running operation
- * returns). However, the callbacks may occur on any thread.
- *
- * In general, otherwise, no promises are made about the relative
- * order of callbacks in a multithreaded program. In particular
- * different callbacks relating to the same long-running operation may
- * be delivered out of order.
- */
-
-typedef struct {
- void (*callback)(libxl_ctx *ctx, libxl_event*, void *for_callback);
- libxl_ev_user for_event; /* always used */
- void *for_callback; /* passed to callback */
-} libxl_asyncprogress_how;
-
-/*
- * It is sometimes possible to abort an asynchronous operation.
- *
- * libxl_ao_abort searches for an ongoing asynchronous operation whose
- * ao_how is identical to *how, and tries to abort it. The return
- * values from libxl_ao_abort are as follows:
- *
- * 0
- *
- * The operation was found, and attempts are being made to cut it
- * short. However, it may still take some time to stop. It is
- * also possible that the operation will nevertheless complete
- * successfully.
- *
- * ERROR_NOTFOUND
- *
- * No matching ongoing operation was found. This might happen
- * for an actual operation if the operation has already completed
- * (perhaps on another thread). The call to libxl_ao_abort has
- * had no effect.
- *
- * ERROR_ABORTED
- *
- * The operation has already been the subject of at least one
- * call to libxl_ao_abort.
- *
- * If the operation was indeed cut short due to the abort request, it
- * will complete, at some point in the future, with ERROR_ABORTED. In
- * that case, depending on the operation it have performed some of the
- * work in question and left the operation half-done. Consult the
- * documentation for individual operations.
- *
- * Note that an aborted operation might still fail for other reasons
- * even after the abort was requested.
- *
- * If your application is multithreaded you must not reuse an
- * ao_how->for_event or ao_how->for_callback value (with a particular
- * ao_how->callback) unless you are sure that none of your other
- * threads are going to abort the previous operation using that
- * value; otherwise you risk aborting the wrong operation if the
- * intended target of the abort request completes in the meantime.
- *
- * It is possible to abort even an operation which is being performed
- * synchronously, but since in that case how==NULL you had better only
- * have one such operation, because it is not possible to tell them
- * apart (and libxl_ao_abort will abort only the first one it finds).
- * (And, if you want to do this, obviously the abort would have to be
- * requested on a different thread.)
- */
-int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-
-#define LIBXL_VERSION 0
-
-/* context functions */
-int libxl_ctx_alloc(libxl_ctx **pctx, int version,
- unsigned flags /* none currently defined */,
- xentoollog_logger *lg);
-int libxl_ctx_free(libxl_ctx *ctx /* 0 is OK */);
-
-/* domain related functions */
-
-#define INVALID_DOMID ~0
-#define RANDOM_DOMID (INVALID_DOMID - 1)
-
-/* If the result is ERROR_ABORTED, the domain may or may not exist
- * (in a half-created state). *domid will be valid and will be the
- * domain id, or INVALID_DOMID, as appropriate */
-
-int libxl_domain_create_new(libxl_ctx *ctx, libxl_domain_config *d_config,
- uint32_t *domid,
- const libxl_asyncop_how *ao_how,
- const libxl_asyncprogress_how *aop_console_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config,
- uint32_t *domid, int restore_fd,
- int send_back_fd,
- const libxl_domain_restore_params *params,
- const libxl_asyncop_how *ao_how,
- const libxl_asyncprogress_how *aop_console_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040400
-
-static inline int libxl_domain_create_restore_0x040200(
- libxl_ctx *ctx, libxl_domain_config *d_config,
- uint32_t *domid, int restore_fd,
- const libxl_asyncop_how *ao_how,
- const libxl_asyncprogress_how *aop_console_how)
- LIBXL_EXTERNAL_CALLERS_ONLY
-{
- libxl_domain_restore_params params;
- int ret;
-
- libxl_domain_restore_params_init(¶ms);
-
- ret = libxl_domain_create_restore(
- ctx, d_config, domid, restore_fd, -1, ¶ms, ao_how, aop_console_how);
-
- libxl_domain_restore_params_dispose(¶ms);
- return ret;
-}
-
-#define libxl_domain_create_restore libxl_domain_create_restore_0x040200
-
-#elif defined(LIBXL_API_VERSION) && LIBXL_API_VERSION >= 0x040400 \
- && LIBXL_API_VERSION < 0x040700
-
-static inline int libxl_domain_create_restore_0x040400(
- libxl_ctx *ctx, libxl_domain_config *d_config,
- uint32_t *domid, int restore_fd,
- const libxl_domain_restore_params *params,
- const libxl_asyncop_how *ao_how,
- const libxl_asyncprogress_how *aop_console_how)
- LIBXL_EXTERNAL_CALLERS_ONLY
-{
- return libxl_domain_create_restore(ctx, d_config, domid, restore_fd,
- -1, params, ao_how, aop_console_how);
-}
-
-#define libxl_domain_create_restore libxl_domain_create_restore_0x040400
-
-#endif
-
-int libxl_domain_soft_reset(libxl_ctx *ctx,
- libxl_domain_config *d_config,
- uint32_t domid,
- const libxl_asyncop_how *ao_how,
- const libxl_asyncprogress_how
- *aop_console_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
- /* A progress report will be made via ao_console_how, of type
- * domain_create_console_available, when the domain's primary
- * console is available and can be connected to.
- */
-
-void libxl_domain_config_init(libxl_domain_config *d_config);
-void libxl_domain_config_dispose(libxl_domain_config *d_config);
-
-/*
- * Retrieve domain configuration and filled it in d_config. The
- * returned configuration can be used to rebuild a domain. It only
- * works with DomU.
- */
-int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
- libxl_domain_config *d_config,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-static inline int libxl_retrieve_domain_configuration_0x041200(
- libxl_ctx *ctx, uint32_t domid, libxl_domain_config *d_config)
-{
- return libxl_retrieve_domain_configuration(ctx, domid, d_config, NULL);
-}
-#define libxl_retrieve_domain_configuration \
- libxl_retrieve_domain_configuration_0x041200
-#endif
-
-int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd,
- int flags, /* LIBXL_SUSPEND_* */
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-#define LIBXL_SUSPEND_DEBUG 1
-#define LIBXL_SUSPEND_LIVE 2
-
-/*
- * Only suspend domain, do not save its state to file, do not destroy it.
- * Suspended domain can be resumed with libxl_domain_resume()
- */
-int libxl_domain_suspend_only(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* @param suspend_cancel [from xenctrl.h:xc_domain_resume( @param fast )]
- * If this parameter is true, use co-operative resume. The guest
- * must support this.
- */
-int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * This function doesn't return unless something has gone wrong with
- * the replication to the secondary. If this function returns then the
- * caller should resume the (primary) domain.
- */
-int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
- uint32_t domid, int send_fd, int recv_fd,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-static inline int libxl_domain_shutdown_0x041200(libxl_ctx *ctx,
- uint32_t domid)
-{
- return libxl_domain_shutdown(ctx, domid, NULL);
-}
-#define libxl_domain_shutdown libxl_domain_shutdown_0x041200
-static inline int libxl_domain_reboot_0x041200(libxl_ctx *ctx,
- uint32_t domid)
-{
- return libxl_domain_reboot(ctx, domid, NULL);
-}
-#define libxl_domain_reboot libxl_domain_reboot_0x041200
-#endif
-
-int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid, libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid);
-
-/* get max. number of cpus supported by hypervisor */
-int libxl_get_max_cpus(libxl_ctx *ctx);
-
-/* get the actual number of currently online cpus on the host */
-int libxl_get_online_cpus(libxl_ctx *ctx);
- /* Beware that no locking or serialization is provided by libxl,
- * so the information can be outdated as far as the function
- * returns. If there are other entities in the system capable
- * of onlining/offlining CPUs, it is up to the application
- * to guarantee consistency, if that is important. */
-
-/* get max. number of NUMA nodes supported by hypervisor */
-int libxl_get_max_nodes(libxl_ctx *ctx);
-
-int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid,
- const char *old_name, const char *new_name);
-
- /* if old_name is NULL, any old name is OK; otherwise we check
- * transactionally that the domain has the old old name; if
- * trans is not 0 we use caller's transaction and caller must do retries */
-
-int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-static inline int libxl_domain_pause_0x041200(
- libxl_ctx *ctx, uint32_t domid)
-{
- return libxl_domain_pause(ctx, domid, NULL);
-}
-static inline int libxl_domain_unpause_0x041200(
- libxl_ctx *ctx, uint32_t domid)
-{
- return libxl_domain_unpause(ctx, domid, NULL);
-}
-#define libxl_domain_pause libxl_domain_pause_0x041200
-#define libxl_domain_unpause libxl_domain_unpause_0x041200
-#endif
-
-
-int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
- const char *filename,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t target_memkb);
-int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid, int64_t target_memkb, int relative, int enforce);
-int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid, uint64_t *out_target);
-int libxl_get_memory_target_0x040700(libxl_ctx *ctx, uint32_t domid,
- uint32_t *out_target)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * WARNING
- * This memory management API is unstable even in Xen 4.2.
- * It has a numer of deficiencies and we intend to replace it.
- *
- * The semantics of these functions should not be relied on to be very
- * coherent or stable. We will however endeavour to keep working
- * existing programs which use them in roughly the same way as libxl.
- */
-/* how much free memory in the system a domain needs to be built */
-int libxl_domain_need_memory(libxl_ctx *ctx,
- libxl_domain_config *config
- /* ^ will be partially defaulted */,
- uint32_t domid_for_logging /* INVALID_DOMID ok */,
- uint64_t *need_memkb);
-int libxl_domain_need_memory_0x041200(libxl_ctx *ctx,
- const libxl_domain_build_info *b_info_in,
- uint64_t *need_memkb);
-int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
- const libxl_domain_build_info *b_info_in,
- uint32_t *need_memkb)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-/* how much free memory is available in the system */
-int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb);
-int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-/* wait for a given amount of memory to be free in the system */
-int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid, uint64_t memory_kb, int wait_secs);
-/*
- * Wait for the memory target of a domain to be reached. Does not
- * decrement wait_secs if the domain is making progress toward reaching
- * the target. If the domain is not making progress, wait_secs is
- * decremented. If the timeout expires before the target is reached, the
- * function returns ERROR_FAIL.
- *
- * Older versions of this function (Xen 4.5 and older), decremented
- * wait_secs even if the domain was making progress, resulting in far
- * lower overall wait times. To make sure that your calling routine
- * works with new and old implementations of the function, pass enough
- * time for the guest to reach its target as an argument.
- */
-int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs);
-
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040800
-#define libxl_get_memory_target libxl_get_memory_target_0x040700
-#define libxl_domain_need_memory libxl_domain_need_memory_0x040700
-#define libxl_get_free_memory libxl_get_free_memory_0x040700
-#elif defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-#define libxl_domain_need_memory libxl_domain_need_memory_0x041200
-#endif
-
-int libxl_vncviewer_exec(libxl_ctx *ctx, uint32_t domid, int autopass);
-
-/*
- * If notify_fd is not -1, xenconsole will write 0x00 to it to nofity
- * the caller that it has connected to the guest console.
- */
-int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num,
- libxl_console_type type, int notify_fd);
-/* libxl_primary_console_exec finds the domid and console number
- * corresponding to the primary console of the given vm, then calls
- * libxl_console_exec with the right arguments (domid might be different
- * if the guest is using stubdoms).
- * This function can be called after creating the device model, in
- * case of HVM guests, and before libxl_run_bootloader in case of PV
- * guests using pygrub.
- * If notify_fd is not -1, xenconsole will write 0x00 to it to nofity
- * the caller that it has connected to the guest console.
- */
-int libxl_primary_console_exec(libxl_ctx *ctx, uint32_t domid_vm,
- int notify_fd);
-
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040800
-
-static inline int libxl_console_exec_0x040700(libxl_ctx *ctx,
- uint32_t domid, int cons_num,
- libxl_console_type type)
-{
- return libxl_console_exec(ctx, domid, cons_num, type, -1);
-}
-#define libxl_console_exec libxl_console_exec_0x040700
-
-static inline int libxl_primary_console_exec_0x040700(libxl_ctx *ctx,
- uint32_t domid_vm)
-{
- return libxl_primary_console_exec(ctx, domid_vm, -1);
-}
-#define libxl_primary_console_exec libxl_primary_console_exec_0x040700
-
-#endif
-
-/* libxl_console_get_tty retrieves the specified domain's console tty path
- * and stores it in path. Caller is responsible for freeing the memory.
- */
-int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num,
- libxl_console_type type, char **path);
-
-/* libxl_primary_console_get_tty retrieves the specified domain's primary
- * console tty path and stores it in path. Caller is responsible for freeing
- * the memory.
- */
-int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm, char **path);
-
-/* May be called with info_r == NULL to check for domain's existence.
- * Returns ERROR_DOMAIN_NOTFOUND if domain does not exist (used to return
- * ERROR_INVAL for this scenario). */
-int libxl_domain_info(libxl_ctx*, libxl_dominfo *info_r,
- uint32_t domid);
-
-/* These functions each return (on success) an array of elements,
- * and the length via the int* out parameter. These arrays and
- * their contents come from malloc, and must be freed with the
- * corresponding libxl_THING_list_free function.
- */
-libxl_dominfo * libxl_list_domain(libxl_ctx*, int *nb_domain_out);
-void libxl_dominfo_list_free(libxl_dominfo *list, int nb_domain);
-
-libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx*, int *nb_pool_out);
-void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nb_pool);
-
-libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out);
-void libxl_vminfo_list_free(libxl_vminfo *list, int nb_vm);
-
-#define LIBXL_CPUTOPOLOGY_INVALID_ENTRY (~(uint32_t)0)
-libxl_cputopology *libxl_get_cpu_topology(libxl_ctx *ctx, int *nb_cpu_out);
-void libxl_cputopology_list_free(libxl_cputopology *, int nb_cpu);
-
-#define LIBXL_PCITOPOLOGY_INVALID_ENTRY (~(uint32_t)0)
-libxl_pcitopology *libxl_get_pci_topology(libxl_ctx *ctx, int *num_devs);
-void libxl_pcitopology_list_free(libxl_pcitopology *, int num_devs);
-
-#define LIBXL_NUMAINFO_INVALID_ENTRY (~(uint32_t)0)
-libxl_numainfo *libxl_get_numainfo(libxl_ctx *ctx, int *nr);
-void libxl_numainfo_list_free(libxl_numainfo *, int nr);
-
-libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
- int *nb_vcpu, int *nr_cpus_out);
-void libxl_vcpuinfo_list_free(libxl_vcpuinfo *, int nr_vcpus);
-
-/*
- * Devices
- * =======
- *
- * Each device is represented by a libxl_device_<TYPE> data structure
- * which is defined via the IDL. In addition some devices have an
- * additional data type libxl_device_<TYPE>_getinfo which contains
- * further runtime information about the device.
- *
- * In addition to the general methods available for libxl types (see
- * "libxl types" above) a common set of methods are available for each
- * device type. These are described below.
- *
- * Querying
- * --------
- *
- * libxl_device_<type>_list(ctx, domid, nr):
- *
- * Returns an array of libxl_device_<type> length nr representing
- * the devices attached to the specified domain.
- *
- * libxl_device_<type>_getinfo(ctx, domid, device, info):
- *
- * Initialises info with details of the given device which must be
- * attached to the specified domain.
- *
- * Creation / Control
- * ------------------
- *
- * libxl_device_<type>_add(ctx, domid, device):
- *
- * Adds the given device to the specified domain. This can be called
- * while the guest is running (hotplug) or before boot (coldplug).
- *
- * This function only sets up the device but does not wait for the
- * domain to connect to the device and therefore cannot block on the
- * guest.
- *
- * device is an in/out parameter: fields left unspecified when the
- * structure is passed in are filled in with appropriate values for
- * the device created.
- *
- * libxl_device_<type>_destroy(ctx, domid, device):
- *
- * Removes the given device from the specified domain without guest
- * co-operation. It is guest specific what affect this will have on
- * a running guest.
- *
- * This function does not interact with the guest and therefore
- * cannot block on the guest.
- *
- * libxl_device_<type>_remove(ctx, domid, device):
- *
- * Removes the given device from the specified domain by performing
- * an orderly unplug with guest co-operation. This requires that the
- * guest is running.
- *
- * This method is currently synchronous and therefore can block
- * while interacting with the guest. There is a time-out of 10s on
- * this interaction after which libxl_device_<type>_destroy()
- * semantics apply.
- *
- * libxl_device_<type>_safe_remove(ctx, domid, device):
- *
- * This has the same semantics as libxl_device_<type>_remove() but,
- * in the event of hitting the 10s time-out, this function will fail.
- *
- * Controllers
- * -----------
- *
- * Most devices are treated individually. Some classes of device,
- * however, like USB or SCSI, inherently have the need to have a
- * hierarchy of different levels, with lower-level devices "attached"
- * to higher-level ones. USB for instance has "controllers" at the
- * top, which have buses, on which are devices, which consist of
- * multiple interfaces. SCSI has "hosts" at the top, then buses,
- * targets, and LUNs.
- *
- * In that case, for each <class>, there will be a set of functions
- * and types for each <level>. For example, for <class>=usb, there
- * may be <levels> ctrl (controller) and dev (device), with ctrl being
- * level 0.
- *
- * libxl_device_<class><level0>_<function> will act more or
- * less like top-level non-bus devices: they will either create or
- * accept a libxl_devid which will be unique within the
- * <class><level0> libxl_devid namespace.
- *
- * Lower-level devices must have a unique way to be identified. One
- * way to do this would be to name it via the name of the next level
- * up plus an index; for instance, <ctrl devid, port number>. Another
- * way would be to have another devid namespace for that level. This
- * identifier will be used for queries and removals.
- *
- * Lower-level devices will include in their
- * libxl_device_<class><level> struct a field referring to the unique
- * index of the level above. For instance, libxl_device_usbdev might
- * contain the controller devid.
- *
- * In the case where there are multiple different ways to implement a
- * given device -- for instance, one which is fully PV and one which
- * uses an emulator -- the controller will contain a field which
- * specifies what type of implementation is used. The implementations
- * of individual devices will be known by the controller to which they
- * are attached.
- *
- * If libxl_device_<class><level>_add receives an empty reference to
- * the level above, it may return an error. Or it may (but is not
- * required to) automatically choose a suitable device in the level
- * above to which to attach the new device at this level. It may also
- * (but is not required to) automatically create a new device at the
- * level above if no suitable devices exist. Each class should
- * document its behavior.
- *
- * libxl_device_<class><level>_list will list all devices of <class>
- * at <level> in the domain. For example, libxl_device_usbctrl_list
- * will list all usb controllers; libxl_class_usbdev_list will list
- * all usb devices across all controllers.
- *
- * For each class, the domain config file will contain a single list
- * for each level. libxl will first iterate through the list of
- * top-level devices, then iterate through each level down in turn,
- * adding devices to devices in the level above. For instance, there
- * will be one list for all usb controllers, and one list for all usb
- * devices.
- *
- * If libxl_device_<class><level>_add automatically creates
- * higher-level devices as necessary, then it is permissible for the
- * higher-level lists to be empty and the device list to have devices
- * with the field containing a reference to the higher level device
- * uninitialized.
- */
-
-/* Disks */
-int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid,
- libxl_device_disk *disk,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_disk_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_disk *disk,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_disk_destroy(libxl_ctx *ctx, uint32_t domid,
- libxl_device_disk *disk,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_disk_safe_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_disk *disk,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_disk *libxl_device_disk_list(libxl_ctx *ctx,
- uint32_t domid, int *num)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-void libxl_device_disk_list_free(libxl_device_disk* list, int num)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_disk *disk, libxl_diskinfo *diskinfo)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * Insert a CD-ROM device. A device corresponding to disk must already
- * be attached to the guest.
- */
-int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * USB
- *
- * For each device removed or added, one of these protocols is available:
- * - PV (i.e., PVUSB)
- * - DEVICEMODEL (i.e, qemu)
- *
- * PV is available for either PV or HVM domains. DEVICEMODEL is only
- * available for HVM domains. The caller can additionally specify
- * "AUTO", in which case the library will try to determine the best
- * protocol automatically.
- *
- * At the moment, the only protocol implemented is PV.
- *
- * One can add/remove USB controllers to/from guest, and attach/detach USB
- * devices to/from USB controllers.
- *
- * To add USB controllers and USB devices, one can adding USB controllers
- * first and then attaching USB devices to some USB controller, or adding
- * USB devices to guest directly, it will automatically create a USB
- * controller for USB devices to attach.
- *
- * To remove USB controllers or USB devices, one can remove USB devices
- * under USB controller one by one and then remove USB controller, or
- * remove USB controller directly, it will remove all USB devices under
- * it automatically.
- *
- */
-/* USB Controllers*/
-int libxl_device_usbctrl_add(libxl_ctx *ctx, uint32_t domid,
- libxl_device_usbctrl *usbctrl,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-int libxl_device_usbctrl_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_usbctrl *usbctrl,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-int libxl_device_usbctrl_destroy(libxl_ctx *ctx, uint32_t domid,
- libxl_device_usbctrl *usbctrl,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_usbctrl *libxl_device_usbctrl_list(libxl_ctx *ctx,
- uint32_t domid, int *num);
-
-void libxl_device_usbctrl_list_free(libxl_device_usbctrl *list, int nr);
-
-
-int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_usbctrl *usbctrl,
- libxl_usbctrlinfo *usbctrlinfo);
-
-/* USB Devices */
-
-int libxl_device_usbdev_add(libxl_ctx *ctx, uint32_t domid,
- libxl_device_usbdev *usbdev,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_usbdev *usbdev,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_usbdev *
-libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num);
-
-void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr);
-
-/* Network Interfaces */
-int libxl_device_nic_add(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_nic_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_nic *nic,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_nic_destroy(libxl_ctx *ctx, uint32_t domid,
- libxl_device_nic *nic,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx,
- uint32_t domid, int *num)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-void libxl_device_nic_list_free(libxl_device_nic* list, int num)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_nic *nic, libxl_nicinfo *nicinfo)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * Virtual Channels
- * Channels manifest as consoles with names, see docs/misc/channels.txt
- */
-libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx,
- uint32_t domid,
- int *num);
-int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_channel *channel,
- libxl_channelinfo *channelinfo);
-
-/* Virtual TPMs */
-int libxl_device_vtpm_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vtpm_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_vtpm *vtpm,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vtpm_destroy(libxl_ctx *ctx, uint32_t domid,
- libxl_device_vtpm *vtpm,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_vtpm *libxl_device_vtpm_list(libxl_ctx *ctx,
- uint32_t domid, int *num)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-void libxl_device_vtpm_list_free(libxl_device_vtpm*, int num)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vtpm_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_vtpm *vtpm, libxl_vtpminfo *vtpminfo)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* Virtual displays */
-int libxl_device_vdispl_add(libxl_ctx *ctx, uint32_t domid,
- libxl_device_vdispl *displ,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vdispl_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_vdispl *vdispl,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vdispl_destroy(libxl_ctx *ctx, uint32_t domid,
- libxl_device_vdispl *vdispl,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_vdispl *libxl_device_vdispl_list(libxl_ctx *ctx,
- uint32_t domid, int *num)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-void libxl_device_vdispl_list_free(libxl_device_vdispl* list, int num)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vdispl_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_vdispl *vdispl,
- libxl_vdisplinfo *vdisplinfo)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* Virtual sounds */
-int libxl_device_vsnd_add(libxl_ctx *ctx, uint32_t domid,
- libxl_device_vsnd *vsnd,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vsnd_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_vsnd *vsnd,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vsnd_destroy(libxl_ctx *ctx, uint32_t domid,
- libxl_device_vsnd *vsnd,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_vsnd *libxl_device_vsnd_list(libxl_ctx *ctx,
- uint32_t domid, int *num)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-void libxl_device_vsnd_list_free(libxl_device_vsnd* list, int num)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vsnd_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_vsnd *vsnd,
- libxl_vsndinfo *vsndlinfo)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* Keyboard */
-int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vkb_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_vkb *vkb,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vkb_destroy(libxl_ctx *ctx, uint32_t domid,
- libxl_device_vkb *vkb,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_vkb *libxl_device_vkb_list(libxl_ctx *ctx,
- uint32_t domid, int *num)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-void libxl_device_vkb_list_free(libxl_device_vkb* list, int num)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vkb_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_vkb *vkb,
- libxl_vkbinfo *vkbinfo)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* Framebuffer */
-int libxl_device_vfb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vfb_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_vfb *vfb,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vfb_destroy(libxl_ctx *ctx, uint32_t domid,
- libxl_device_vfb *vfb,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* 9pfs */
-int libxl_device_p9_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_p9 *p9,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_p9_destroy(libxl_ctx *ctx, uint32_t domid,
- libxl_device_p9 *p9,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* pvcalls interface */
-int libxl_device_pvcallsif_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_pvcallsif *pvcallsif,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_pvcallsif_destroy(libxl_ctx *ctx, uint32_t domid,
- libxl_device_pvcallsif *pvcallsif,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* PCI Passthrough */
-int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid,
- libxl_device_pci *pcidev,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_pci *pcidev,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_pci_destroy(libxl_ctx *ctx, uint32_t domid,
- libxl_device_pci *pcidev,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_pci *libxl_device_pci_list(libxl_ctx *ctx, uint32_t domid,
- int *num);
-
-/*
- * Turns the current process into a backend device service daemon
- * for a driver domain.
- *
- * From a libxl API point of view, this starts a long-running
- * operation. That operation consists of "being a driver domain"
- * and never completes.
- *
- * Attempting to abort this operation is not advisable; proper
- * shutdown of the driver domain task is not supported.
- */
-int libxl_device_events_handler(libxl_ctx *ctx,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * Functions related to making devices assignable -- that is, bound to
- * the pciback driver, ready to be given to a guest via
- * libxl_pci_device_add.
- *
- * - ..._add() will unbind the device from its current driver (if
- * already bound) and re-bind it to pciback; at that point it will be
- * ready to be assigned to a VM. If rebind is set, it will store the
- * path to the old driver in xenstore so that it can be handed back to
- * dom0 on restore.
- *
- * - ..._remove() will unbind the device from pciback, and if
- * rebind is non-zero, attempt to assign it back to the driver
- * from whence it came.
- *
- * - ..._list() will return a list of the PCI devices available to be
- * assigned.
- *
- * add and remove are idempotent: if the device in question is already
- * added or is not bound, the functions will emit a warning but return
- * SUCCESS.
- */
-int libxl_device_pci_assignable_add(libxl_ctx *ctx, libxl_device_pci *pcidev, int rebind);
-int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci *pcidev, int rebind);
-libxl_device_pci *libxl_device_pci_assignable_list(libxl_ctx *ctx, int *num);
-
-/* CPUID handling */
-int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str);
-int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid,
- const char* str);
-#if LIBXL_API_VERSION < 0x041400
-/*
- * Dropped from the API in Xen 4.14. At the time of writing, these functions
- * don't appear to ever have had external callers.
- *
- * These have always been used internally during domain construction, and
- * can't easily be used externally because of their implicit parameters in
- * other pieces of global state.
- *
- * Furthermore, an API user can't usefully determine whether they get
- * libxl_cpuid (the real implementation) or libxl_nocpuid (no-op stubs).
- *
- * The internal behaviour of these functions also needs to change. Therefore
- * for simplicitly, provide the no-op stubs. Yes technically this is an API
- * change in some cases for existing software, but there is 0 of that in
- * practice.
- */
-static inline void libxl_cpuid_apply_policy(libxl_ctx *ctx __attribute__((unused)),
- uint32_t domid __attribute__((unused)))
-{}
-static inline void libxl_cpuid_set(libxl_ctx *ctx __attribute__((unused)),
- uint32_t domid __attribute__((unused)),
- libxl_cpuid_policy_list cpuid __attribute__((unused)))
-{}
-#endif
-
-/*
- * Functions for allowing users of libxl to store private data
- * relating to a domain. The data is an opaque sequence of bytes and
- * is not interpreted or used by libxl.
- *
- * Data is indexed by the userdata userid, which is a short printable
- * ASCII string. The following list is a registry of userdata userids
- * (the registry may be updated by posting a patch to xen-devel):
- *
- * userid Data contents
- * "xl" domain config file in xl format, Unix line endings
- * "libvirt-xml" domain config file in libvirt XML format. See
- * http://libvirt.org/formatdomain.html
- * "domain-userdata-lock" lock file to protect domain userdata in libxl.
- * It's a per-domain lock. Applications should
- * not touch this file.
- * "libxl-json" libxl_domain_config object in JSON format, generated
- * by libxl. Applications should not access this file
- * directly. This file is protected by domain-userdata-lock
- * for against Read-Modify-Write operation and domain
- * destruction.
- *
- * libxl does not enforce the registration of userdata userids or the
- * semantics of the data. For specifications of the data formats
- * see the code or documentation for the libxl caller in question.
- */
-int libxl_userdata_store(libxl_ctx *ctx, uint32_t domid,
- const char *userdata_userid,
- const uint8_t *data, int datalen)
- LIBXL_EXTERNAL_CALLERS_ONLY;
- /* If datalen==0, data is not used and the user data for
- * that domain and userdata_userid is deleted. */
-int libxl_userdata_retrieve(libxl_ctx *ctx, uint32_t domid,
- const char *userdata_userid,
- uint8_t **data_r, int *datalen_r)
- LIBXL_EXTERNAL_CALLERS_ONLY;
- /* On successful return, *data_r is from malloc.
- * If there is no data for that domain and userdata_userid,
- * *data_r and *datalen_r will be set to 0.
- * data_r and datalen_r may be 0.
- * On error return, *data_r and *datalen_r are undefined.
- */
-int libxl_userdata_unlink(libxl_ctx *ctx, uint32_t domid,
- const char *userdata_userid);
-
-
-int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo);
-int libxl_set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid,
- const libxl_bitmap *cpumap_hard,
- const libxl_bitmap *cpumap_soft);
-int libxl_set_vcpuaffinity_force(libxl_ctx *ctx, uint32_t domid,
- uint32_t vcpuid,
- const libxl_bitmap *cpumap_hard,
- const libxl_bitmap *cpumap_soft);
-int libxl_set_vcpuaffinity_all(libxl_ctx *ctx, uint32_t domid,
- unsigned int max_vcpus,
- const libxl_bitmap *cpumap_hard,
- const libxl_bitmap *cpumap_soft);
-
-#if defined (LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040500
-
-#define libxl_set_vcpuaffinity(ctx, domid, vcpuid, map) \
- libxl_set_vcpuaffinity((ctx), (domid), (vcpuid), (map), NULL)
-#define libxl_set_vcpuaffinity_all(ctx, domid, max_vcpus, map) \
- libxl_set_vcpuaffinity_all((ctx), (domid), (max_vcpus), (map), NULL)
-
-#endif
-
-int libxl_domain_set_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
- libxl_bitmap *nodemap);
-int libxl_domain_get_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
- libxl_bitmap *nodemap);
-int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid,
- libxl_bitmap *cpumap,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-static inline int libxl_set_vcpuonline_0x041200(libxl_ctx *ctx,
- uint32_t domid,
- libxl_bitmap *cpumap)
-{
- return libxl_set_vcpuonline(ctx, domid, cpumap, NULL);
-}
-#define libxl_set_vcpuonline libxl_set_vcpuonline_0x041200
-#endif
-
-/* A return value less than 0 should be interpreted as a libxl_error, while a
- * return value greater than or equal to 0 should be interpreted as a
- * libxl_scheduler. */
-int libxl_get_scheduler(libxl_ctx *ctx);
-
-/* Per-scheduler parameters */
-int libxl_sched_credit_params_get(libxl_ctx *ctx, uint32_t poolid,
- libxl_sched_credit_params *scinfo);
-int libxl_sched_credit_params_set(libxl_ctx *ctx, uint32_t poolid,
- libxl_sched_credit_params *scinfo);
-int libxl_sched_credit2_params_get(libxl_ctx *ctx, uint32_t poolid,
- libxl_sched_credit2_params *scinfo);
-int libxl_sched_credit2_params_set(libxl_ctx *ctx, uint32_t poolid,
- libxl_sched_credit2_params *scinfo);
-
-/* Scheduler Per-domain parameters */
-
-#define LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT -1
-#define LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT -1
-#define LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT -1
-#define LIBXL_DOMAIN_SCHED_PARAM_SLICE_DEFAULT -1
-#define LIBXL_DOMAIN_SCHED_PARAM_LATENCY_DEFAULT -1
-#define LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT -1
-#define LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT -1
-
-/* Per-VCPU parameters */
-#define LIBXL_SCHED_PARAM_VCPU_INDEX_DEFAULT -1
-
-/* Get the per-domain scheduling parameters.
- * For schedulers that support per-vcpu settings (e.g., RTDS),
- * calling *_domain_get functions will get default scheduling
- * parameters.
- */
-int libxl_domain_sched_params_get(libxl_ctx *ctx, uint32_t domid,
- libxl_domain_sched_params *params);
-
-/* Set the per-domain scheduling parameters.
- * For schedulers that support per-vcpu settings (e.g., RTDS),
- * calling *_domain_set functions will set all vcpus with the same
- * scheduling parameters.
- */
-int libxl_domain_sched_params_set(libxl_ctx *ctx, uint32_t domid,
- const libxl_domain_sched_params *params);
-
-/* Get the per-vcpu scheduling parameters */
-int libxl_vcpu_sched_params_get(libxl_ctx *ctx, uint32_t domid,
- libxl_vcpu_sched_params *params);
-
-/* Get the per-vcpu scheduling parameters of all vcpus of a domain */
-int libxl_vcpu_sched_params_get_all(libxl_ctx *ctx, uint32_t domid,
- libxl_vcpu_sched_params *params);
-
-/* Set the per-vcpu scheduling parameters */
-int libxl_vcpu_sched_params_set(libxl_ctx *ctx, uint32_t domid,
- const libxl_vcpu_sched_params *params);
-
-/* Set the per-vcpu scheduling parameters of all vcpus of a domain */
-int libxl_vcpu_sched_params_set_all(libxl_ctx *ctx, uint32_t domid,
- const libxl_vcpu_sched_params *params);
-
-int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid,
- libxl_trigger trigger, uint32_t vcpuid,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-static inline int libxl_send_trigger_0x041200(
- libxl_ctx *ctx, uint32_t domid, libxl_trigger trigger, uint32_t vcpuid)
-{
- return libxl_send_trigger(ctx, domid, trigger, vcpuid, NULL);
-}
-#define libxl_send_trigger libxl_send_trigger_0x041200
-#endif
-int libxl_send_sysrq(libxl_ctx *ctx, uint32_t domid, char sysrq);
-int libxl_send_debug_keys(libxl_ctx *ctx, char *keys);
-int libxl_set_parameters(libxl_ctx *ctx, char *params);
-
-typedef struct libxl__xen_console_reader libxl_xen_console_reader;
-
-libxl_xen_console_reader *
- libxl_xen_console_read_start(libxl_ctx *ctx, int clear);
-int libxl_xen_console_read_line(libxl_ctx *ctx,
- libxl_xen_console_reader *cr,
- char **line_r);
-void libxl_xen_console_read_finish(libxl_ctx *ctx,
- libxl_xen_console_reader *cr);
-
-uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid);
-
-char *libxl_tmem_list(libxl_ctx *ctx, uint32_t domid, int use_long);
-int libxl_tmem_freeze(libxl_ctx *ctx, uint32_t domid);
-int libxl_tmem_thaw(libxl_ctx *ctx, uint32_t domid);
-int libxl_tmem_set(libxl_ctx *ctx, uint32_t domid, char* name,
- uint32_t set);
-int libxl_tmem_shared_auth(libxl_ctx *ctx, uint32_t domid, char* uuid,
- int auth);
-int libxl_tmem_freeable(libxl_ctx *ctx);
-
-int libxl_get_freecpus(libxl_ctx *ctx, libxl_bitmap *cpumap);
-
-/*
- * Set poolid to LIBXL_CPUOOL_POOLID_ANY to have Xen choose a
- * free poolid for you.
- */
-#define LIBXL_CPUPOOL_POOLID_ANY 0xFFFFFFFF
-int libxl_cpupool_create(libxl_ctx *ctx, const char *name,
- libxl_scheduler sched,
- libxl_bitmap cpumap, libxl_uuid *uuid,
- uint32_t *poolid);
-int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid);
-int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid);
-int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu);
-int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus);
-int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid,
- const libxl_bitmap *cpumap);
-int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu);
-int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus);
-int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid,
- const libxl_bitmap *cpumap);
-int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid);
-int libxl_cpupool_info(libxl_ctx *ctx, libxl_cpupoolinfo *info, uint32_t poolid);
-
-int libxl_domid_valid_guest(uint32_t domid);
-
-int libxl_flask_context_to_sid(libxl_ctx *ctx, char *buf, size_t len,
- uint32_t *ssidref);
-int libxl_flask_sid_to_context(libxl_ctx *ctx, uint32_t ssidref, char **buf,
- size_t *len);
-int libxl_flask_getenforce(libxl_ctx *ctx);
-int libxl_flask_setenforce(libxl_ctx *ctx, int mode);
-int libxl_flask_loadpolicy(libxl_ctx *ctx, void *policy, uint32_t size);
-
-int libxl_ms_vm_genid_generate(libxl_ctx *ctx, libxl_ms_vm_genid *id);
-bool libxl_ms_vm_genid_is_zero(const libxl_ms_vm_genid *id);
-void libxl_ms_vm_genid_copy(libxl_ctx *ctx, libxl_ms_vm_genid *dst,
- const libxl_ms_vm_genid *src);
-
-#if defined(__i386__) || defined(__x86_64__)
-int libxl_psr_cmt_attach(libxl_ctx *ctx, uint32_t domid);
-int libxl_psr_cmt_detach(libxl_ctx *ctx, uint32_t domid);
-int libxl_psr_cmt_domain_attached(libxl_ctx *ctx, uint32_t domid);
-int libxl_psr_cmt_enabled(libxl_ctx *ctx);
-int libxl_psr_cmt_get_total_rmid(libxl_ctx *ctx, uint32_t *total_rmid);
-int libxl_psr_cmt_get_l3_cache_size(libxl_ctx *ctx,
- uint32_t socketid,
- uint32_t *l3_cache_size);
-int libxl_psr_cmt_get_cache_occupancy(libxl_ctx *ctx,
- uint32_t domid,
- uint32_t socketid,
- uint32_t *l3_cache_occupancy);
-
-int libxl_psr_cmt_type_supported(libxl_ctx *ctx, libxl_psr_cmt_type type);
-int libxl_psr_cmt_get_sample(libxl_ctx *ctx,
- uint32_t domid,
- libxl_psr_cmt_type type,
- uint64_t scope,
- uint64_t *sample_r,
- uint64_t *tsc_r);
-
-/*
- * Function to set a domain's cbm. It operates on a single or multiple
- * target(s) defined in 'target_map'. The definition of 'target_map' is
- * related to 'type':
- * 'L3_CBM': 'target_map' specifies all the sockets to be operated on.
- */
-int libxl_psr_cat_set_cbm(libxl_ctx *ctx, uint32_t domid,
- libxl_psr_cbm_type type, libxl_bitmap *target_map,
- uint64_t cbm);
-/*
- * Function to get a domain's cbm. It operates on a single 'target'.
- * The definition of 'target' is related to 'type':
- * 'L3_CBM': 'target' specifies which socket to be operated on.
- */
-int libxl_psr_cat_get_cbm(libxl_ctx *ctx, uint32_t domid,
- libxl_psr_cbm_type type, uint32_t target,
- uint64_t *cbm_r);
-
-/*
- * On success, the function returns an array of elements in 'info',
- * and the length in 'nr'.
- */
-int libxl_psr_cat_get_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
- unsigned int *nr, unsigned int lvl);
-int libxl_psr_cat_get_l3_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
- int *nr);
-void libxl_psr_cat_info_list_free(libxl_psr_cat_info *list, int nr);
-
-typedef enum libxl_psr_cbm_type libxl_psr_type;
-
-/*
- * Function to set a domain's value. It operates on a single or multiple
- * target(s) defined in 'target_map'. 'target_map' specifies all the sockets
- * to be operated on.
- */
-int libxl_psr_set_val(libxl_ctx *ctx, uint32_t domid,
- libxl_psr_type type, libxl_bitmap *target_map,
- uint64_t val);
-/*
- * Function to get a domain's cbm. It operates on a single 'target'.
- * 'target' specifies which socket to be operated on.
- */
-int libxl_psr_get_val(libxl_ctx *ctx, uint32_t domid,
- libxl_psr_type type, unsigned int target,
- uint64_t *val);
-/*
- * On success, the function returns an array of elements in 'info',
- * and the length in 'nr'.
- */
-int libxl_psr_get_hw_info(libxl_ctx *ctx, libxl_psr_feat_type type,
- unsigned int lvl, unsigned int *nr,
- libxl_psr_hw_info **info);
-void libxl_psr_hw_info_list_free(libxl_psr_hw_info *list, unsigned int nr);
-#endif
-
-/* misc */
-
-/* Each of these sets or clears the flag according to whether the
- * 2nd parameter is nonzero. On failure, they log, and
- * return ERROR_FAIL, but also leave errno valid. */
-int libxl_fd_set_cloexec(libxl_ctx *ctx, int fd, int cloexec);
-int libxl_fd_set_nonblock(libxl_ctx *ctx, int fd, int nonblock);
-
-/*
- * Issue a qmp monitor command to the device model of the specified domain.
- * The function returns the output of the command in a new allocated buffer
- * via output.
- */
-int libxl_qemu_monitor_command(libxl_ctx *ctx, uint32_t domid,
- const char *command_line, char **output,
- const libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-static inline int libxl_qemu_monitor_command_0x041200(libxl_ctx *ctx,
- uint32_t domid, const char *command_line, char **output)
-{
- return libxl_qemu_monitor_command(ctx, domid, command_line, output,
- NULL);
-}
-#define libxl_qemu_monitor_command libxl_qemu_monitor_command_0x041200
-#endif
-
-#include <libxl_event.h>
-
-/*
- * This function is for use only during host initialisation. If it is
- * invoked on a host with running domains, or concurrent libxl
- * processes then the system may malfuntion.
- */
-int libxl_clear_domid_history(libxl_ctx *ctx);
-
-#endif /* LIBXL_H */
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2017 Aporeto
- * Author Stefano Stabellini <stefano@aporeto.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-static int libxl__device_p9_setdefault(libxl__gc *gc, uint32_t domid,
- libxl_device_p9 *p9, bool hotplug)
-{
- return libxl__resolve_domid(gc, p9->backend_domname, &p9->backend_domid);
-}
-
-static int libxl__set_xenstore_p9(libxl__gc *gc, uint32_t domid,
- libxl_device_p9 *p9,
- flexarray_t *back, flexarray_t *front,
- flexarray_t *ro_front)
-{
- flexarray_append_pair(back, "path", p9->path);
- flexarray_append_pair(back, "security_model", p9->security_model);
-
- flexarray_append_pair(front, "tag", p9->tag);
-
- return 0;
-}
-
-#define libxl__add_p9s NULL
-#define libxl_device_p9_list NULL
-#define libxl_device_p9_compare NULL
-
-static LIBXL_DEFINE_UPDATE_DEVID(p9)
-static LIBXL_DEFINE_DEVICE_FROM_TYPE(p9)
-
-LIBXL_DEFINE_DEVICE_REMOVE(p9)
-
-DEFINE_DEVICE_TYPE_STRUCT(p9, 9PFS,
- .skip_attach = 1,
- .set_xenstore_config = (device_set_xenstore_config_fn_t)
- libxl__set_xenstore_p9,
-);
+++ /dev/null
-/*
- * Copyright (C) 2010 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*----- xswait -----*/
-
-static libxl__ev_xswatch_callback xswait_xswatch_callback;
-static libxl__ev_time_callback xswait_timeout_callback;
-static void xswait_report_error(libxl__egc*, libxl__xswait_state*, int rc);
-
-void libxl__xswait_init(libxl__xswait_state *xswa)
-{
- libxl__ev_time_init(&xswa->time_ev);
- libxl__ev_xswatch_init(&xswa->watch_ev);
-}
-
-void libxl__xswait_stop(libxl__gc *gc, libxl__xswait_state *xswa)
-{
- libxl__ev_time_deregister(gc, &xswa->time_ev);
- libxl__ev_xswatch_deregister(gc, &xswa->watch_ev);
-}
-
-bool libxl__xswait_inuse(const libxl__xswait_state *xswa)
-{
- bool time_inuse = libxl__ev_time_isregistered(&xswa->time_ev);
- bool watch_inuse = libxl__ev_xswatch_isregistered(&xswa->watch_ev);
- assert(time_inuse == watch_inuse);
- return time_inuse;
-}
-
-int libxl__xswait_start(libxl__gc *gc, libxl__xswait_state *xswa)
-{
- int rc;
-
- rc = libxl__ev_time_register_rel(xswa->ao, &xswa->time_ev,
- xswait_timeout_callback, xswa->timeout_ms);
- if (rc) goto err;
-
- rc = libxl__ev_xswatch_register(gc, &xswa->watch_ev,
- xswait_xswatch_callback, xswa->path);
- if (rc) goto err;
-
- return 0;
-
- err:
- libxl__xswait_stop(gc, xswa);
- return rc;
-}
-
-void xswait_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *xsw,
- const char *watch_path, const char *event_path)
-{
- EGC_GC;
- libxl__xswait_state *xswa = CONTAINER_OF(xsw, *xswa, watch_ev);
- int rc;
- const char *data;
-
- if (xswa->path[0] == '@') {
- data = 0;
- } else {
- rc = libxl__xs_read_checked(gc, XBT_NULL, xswa->path, &data);
- if (rc) { xswait_report_error(egc, xswa, rc); return; }
- }
-
- xswa->callback(egc, xswa, 0, data);
-}
-
-void xswait_timeout_callback(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- EGC_GC;
- libxl__xswait_state *xswa = CONTAINER_OF(ev, *xswa, time_ev);
- LOG(DEBUG, "%s: xswait timeout (path=%s)", xswa->what, xswa->path);
- xswait_report_error(egc, xswa, rc);
-}
-
-static void xswait_report_error(libxl__egc *egc, libxl__xswait_state *xswa,
- int rc)
-{
- EGC_GC;
- libxl__xswait_stop(gc, xswa);
- xswa->callback(egc, xswa, rc, 0);
-}
-
-
-/*----- data copier -----*/
-
-void libxl__datacopier_init(libxl__datacopier_state *dc)
-{
- assert(dc->ao);
- libxl__ao_abortable_init(&dc->abrt);
- libxl__ev_fd_init(&dc->toread);
- libxl__ev_fd_init(&dc->towrite);
- LIBXL_TAILQ_INIT(&dc->bufs);
-}
-
-void libxl__datacopier_kill(libxl__datacopier_state *dc)
-{
- STATE_AO_GC(dc->ao);
- libxl__datacopier_buf *buf, *tbuf;
-
- libxl__ao_abortable_deregister(&dc->abrt);
- libxl__ev_fd_deregister(gc, &dc->toread);
- libxl__ev_fd_deregister(gc, &dc->towrite);
- LIBXL_TAILQ_FOREACH_SAFE(buf, &dc->bufs, entry, tbuf)
- free(buf);
- LIBXL_TAILQ_INIT(&dc->bufs);
-}
-
-static void datacopier_callback(libxl__egc *egc, libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval)
-{
- libxl__datacopier_kill(dc);
- dc->callback(egc, dc, rc, onwrite, errnoval);
-}
-
-static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev,
- int fd, short events, short revents);
-
-static void datacopier_check_state(libxl__egc *egc, libxl__datacopier_state *dc)
-{
- STATE_AO_GC(dc->ao);
- int rc;
-
- if (dc->used && !dc->readbuf) {
- if (!libxl__ev_fd_isregistered(&dc->towrite)) {
- rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable,
- dc->writefd, POLLOUT);
- if (rc) {
- LOG(ERROR, "unable to establish write event on %s"
- " during copy of %s", dc->writewhat, dc->copywhat);
- datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
- return;
- }
- }
- } else if (!libxl__ev_fd_isregistered(&dc->toread) ||
- dc->bytes_to_read == 0) {
- /* we have had eof */
- datacopier_callback(egc, dc, 0, 0, 0);
- return;
- } else {
- /* nothing buffered, but still reading */
- libxl__ev_fd_deregister(gc, &dc->towrite);
- }
-}
-
-void libxl__datacopier_prefixdata(libxl__egc *egc, libxl__datacopier_state *dc,
- const void *data, size_t len)
-{
- EGC_GC;
- libxl__datacopier_buf *buf;
- const uint8_t *ptr;
-
- /*
- * It is safe for this to be called immediately after _start, as
- * is documented in the public comment. _start's caller must have
- * the ctx locked, so other threads don't get to mess with the
- * contents, and the fd events cannot happen reentrantly. So we
- * are guaranteed to beat the first data from the read fd.
- */
-
- assert(len < dc->maxsz - dc->used);
-
- for (ptr = data; len; len -= buf->used, ptr += buf->used) {
- buf = libxl__malloc(NOGC, sizeof(*buf));
- buf->used = min(len, sizeof(buf->buf));
- memcpy(buf->buf, ptr, buf->used);
-
- dc->used += buf->used;
- LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry);
- }
-}
-
-static int datacopier_pollhup_handled(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int fd, short revents, int onwrite)
-{
- STATE_AO_GC(dc->ao);
-
- if (dc->callback_pollhup && (revents & POLLHUP)) {
- LOG(DEBUG, "received POLLHUP on fd %d: %s during copy of %s",
- fd, onwrite ? dc->writewhat : dc->readwhat, dc->copywhat);
- libxl__datacopier_kill(dc);
- dc->callback_pollhup(egc, dc, ERROR_FAIL, onwrite, -1);
- return 1;
- }
- return 0;
-}
-
-static void datacopier_abort(libxl__egc *egc, libxl__ao_abortable *abrt,
- int rc)
-{
- libxl__datacopier_state *dc = CONTAINER_OF(abrt, *dc, abrt);
- STATE_AO_GC(dc->ao);
-
- datacopier_callback(egc, dc, rc, -1, 0);
-}
-
-static void datacopier_readable(libxl__egc *egc, libxl__ev_fd *ev,
- int fd, short events, short revents) {
- libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, toread);
- STATE_AO_GC(dc->ao);
-
- if (datacopier_pollhup_handled(egc, dc, fd, revents, 0))
- return;
-
- if (revents & ~(POLLIN|POLLHUP)) {
- LOG(ERROR, "unexpected poll event 0x%x on fd %d (expected POLLIN "
- "and/or POLLHUP) reading %s during copy of %s",
- revents, fd, dc->readwhat, dc->copywhat);
- datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
- return;
- }
- assert(revents & (POLLIN|POLLHUP));
- for (;;) {
- libxl__datacopier_buf *buf = NULL;
- int r;
-
- if (dc->readbuf) {
- r = read(ev->fd, dc->readbuf + dc->used, dc->bytes_to_read);
- } else {
- while (dc->used >= dc->maxsz) {
- libxl__datacopier_buf *rm = LIBXL_TAILQ_FIRST(&dc->bufs);
- dc->used -= rm->used;
- assert(dc->used >= 0);
- LIBXL_TAILQ_REMOVE(&dc->bufs, rm, entry);
- free(rm);
- }
-
- buf = LIBXL_TAILQ_LAST(&dc->bufs, libxl__datacopier_bufs);
- if (!buf || buf->used >= sizeof(buf->buf)) {
- buf = libxl__malloc(NOGC, sizeof(*buf));
- buf->used = 0;
- LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry);
- }
- r = read(ev->fd, buf->buf + buf->used,
- min_t(size_t, sizeof(buf->buf) - buf->used,
- (dc->bytes_to_read == -1) ? SIZE_MAX : dc->bytes_to_read));
- }
- if (r < 0) {
- if (errno == EINTR) continue;
- assert(errno);
- if (errno == EWOULDBLOCK) {
- if (revents & POLLHUP) {
- LOG(ERROR,
- "poll reported HUP but fd read gave EWOULDBLOCK"
- " on %s during copy of %s",
- dc->readwhat, dc->copywhat);
- datacopier_callback(egc, dc, ERROR_FAIL, -1, 0);
- return;
- }
- break;
- }
- LOGE(ERROR, "error reading %s during copy of %s",
- dc->readwhat, dc->copywhat);
- datacopier_callback(egc, dc, ERROR_FAIL, 0, errno);
- return;
- }
- if (r == 0) {
- if (dc->callback_pollhup) {
- /* It might be that this "eof" is actually a HUP. If
- * the caller cares about the difference,
- * double-check using poll(2). */
- struct pollfd hupchk;
- hupchk.fd = ev->fd;
- hupchk.events = POLLIN;
- hupchk.revents = 0;
- r = poll(&hupchk, 1, 0);
- if (r < 0)
- LIBXL__EVENT_DISASTER(gc,
- "unexpected failure polling fd for datacopier eof hup check",
- errno, 0);
- if (datacopier_pollhup_handled(egc, dc, fd, hupchk.revents, 0))
- return;
- }
- libxl__ev_fd_deregister(gc, &dc->toread);
- break;
- }
- if (dc->log) {
- int wrote = fwrite(buf->buf + buf->used, 1, r, dc->log);
- if (wrote != r) {
- assert(ferror(dc->log));
- assert(errno);
- LOGE(ERROR, "error logging %s", dc->copywhat);
- datacopier_callback(egc, dc, ERROR_FAIL, 0, errno);
- return;
- }
- }
- if (!dc->readbuf) {
- buf->used += r;
- assert(buf->used <= sizeof(buf->buf));
- }
- dc->used += r;
- if (dc->bytes_to_read > 0)
- dc->bytes_to_read -= r;
- if (dc->bytes_to_read == 0)
- break;
- }
- datacopier_check_state(egc, dc);
-}
-
-static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev,
- int fd, short events, short revents) {
- libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, towrite);
- STATE_AO_GC(dc->ao);
-
- if (datacopier_pollhup_handled(egc, dc, fd, revents, 1))
- return;
-
- if (revents & ~POLLOUT) {
- LOG(ERROR, "unexpected poll event 0x%x on fd %d (should be POLLOUT)"
- " writing %s during copy of %s",
- revents, fd, dc->writewhat, dc->copywhat);
- datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
- return;
- }
- assert(revents & POLLOUT);
- for (;;) {
- libxl__datacopier_buf *buf = LIBXL_TAILQ_FIRST(&dc->bufs);
- if (!buf)
- break;
- if (!buf->used) {
- LIBXL_TAILQ_REMOVE(&dc->bufs, buf, entry);
- free(buf);
- continue;
- }
- int r = write(ev->fd, buf->buf, buf->used);
- if (r < 0) {
- if (errno == EINTR) continue;
- if (errno == EWOULDBLOCK) break;
- assert(errno);
- LOGE(ERROR, "error writing to %s during copy of %s",
- dc->writewhat, dc->copywhat);
- datacopier_callback(egc, dc, ERROR_FAIL, 1, errno);
- return;
- }
- assert(r > 0);
- assert(r <= buf->used);
- buf->used -= r;
- dc->used -= r;
- assert(dc->used >= 0);
- memmove(buf->buf, buf->buf+r, buf->used);
- }
- datacopier_check_state(egc, dc);
-}
-
-int libxl__datacopier_start(libxl__datacopier_state *dc)
-{
- int rc;
- STATE_AO_GC(dc->ao);
-
- libxl__datacopier_init(dc);
-
- assert(dc->readfd >= 0 || dc->writefd >= 0);
- assert(!(dc->readbuf && dc->bytes_to_read == -1));
-
- dc->abrt.ao = ao;
- dc->abrt.callback = datacopier_abort;
- rc = libxl__ao_abortable_register(&dc->abrt);
- if (rc) goto out;
-
- if (dc->readfd >= 0) {
- rc = libxl__ev_fd_register(gc, &dc->toread, datacopier_readable,
- dc->readfd, POLLIN);
- if (rc) goto out;
- }
-
- if (dc->writefd >= 0) {
- rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable,
- dc->writefd, POLLOUT);
- if (rc) goto out;
- }
-
- return 0;
-
- out:
- libxl__datacopier_kill(dc);
- return rc;
-}
-
-/*----- openpty -----*/
-
-/* implementation */
-
-static void openpty_cleanup(libxl__openpty_state *op)
-{
- int i;
-
- for (i=0; i<op->count; i++) {
- libxl__openpty_result *res = &op->results[i];
- libxl__carefd_close(res->master); res->master = 0;
- libxl__carefd_close(res->slave); res->slave = 0;
- }
-}
-
-static void openpty_exited(libxl__egc *egc, libxl__ev_child *child,
- pid_t pid, int status) {
- libxl__openpty_state *op = CONTAINER_OF(child, *op, child);
- STATE_AO_GC(op->ao);
-
- if (status) {
- /* Perhaps the child gave us the fds and then exited nonzero.
- * Well that would be odd but we don't really care. */
- libxl_report_child_exitstatus(CTX, op->rc ? LIBXL__LOG_ERROR
- : LIBXL__LOG_WARNING,
- "openpty child", pid, status);
- }
- if (op->rc)
- openpty_cleanup(op);
- op->callback(egc, op);
-}
-
-int libxl__openptys(libxl__openpty_state *op,
- struct termios *termp,
- struct winsize *winp) {
- /*
- * This is completely crazy. openpty calls grantpt which the spec
- * says may fork, and may not be called with a SIGCHLD handler.
- * Now our application may have a SIGCHLD handler so that's bad.
- * We could perhaps block it but we'd need to block it on all
- * threads. This is just Too Hard.
- *
- * So instead, we run openpty in a child process. That child
- * process then of course has only our own thread and our own
- * signal handlers. We pass the fds back.
- *
- * Since our only current caller actually wants two ptys, we
- * support calling openpty multiple times for a single fork.
- */
- STATE_AO_GC(op->ao);
- int count = op->count;
- int r, i, rc, sockets[2], ptyfds[count][2];
- libxl__carefd *for_child = 0;
- pid_t pid = -1;
-
- for (i=0; i<count; i++) {
- ptyfds[i][0] = ptyfds[i][1] = -1;
- libxl__openpty_result *res = &op->results[i];
- assert(!res->master);
- assert(!res->slave);
- }
- sockets[0] = sockets[1] = -1; /* 0 is for us, 1 for our child */
-
- libxl__carefd_begin();
- r = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
- if (r) { sockets[0] = sockets[1] = -1; }
- for_child = libxl__carefd_opened(CTX, sockets[1]);
- if (r) { LOGE(ERROR,"socketpair failed"); rc = ERROR_FAIL; goto out; }
-
- pid = libxl__ev_child_fork(gc, &op->child, openpty_exited);
- if (pid == -1) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (!pid) {
- /* child */
- close(sockets[0]);
- signal(SIGCHLD, SIG_DFL);
-
- for (i=0; i<count; i++) {
- r = openpty(&ptyfds[i][0], &ptyfds[i][1], NULL, termp, winp);
- if (r) { LOGE(ERROR,"openpty failed"); _exit(-1); }
- }
- rc = libxl__sendmsg_fds(gc, sockets[1], '\0',
- 2*count, &ptyfds[0][0], "ptys");
- if (rc) { LOGE(ERROR,"sendmsg to parent failed"); _exit(-1); }
- _exit(0);
- }
-
- libxl__carefd_close(for_child);
- for_child = 0;
-
- /* this should be fast so do it synchronously */
-
- libxl__carefd_begin();
- char buf[1];
- rc = libxl__recvmsg_fds(gc, sockets[0], buf,1,
- 2*count, &ptyfds[0][0], "ptys");
- if (!rc) {
- for (i=0; i<count; i++) {
- libxl__openpty_result *res = &op->results[i];
- res->master = libxl__carefd_record(CTX, ptyfds[i][0]);
- res->slave = libxl__carefd_record(CTX, ptyfds[i][1]);
- }
- }
- /* now the pty fds are in the carefds, if they were ever open */
- libxl__carefd_unlock();
- if (rc)
- goto out;
-
- rc = 0;
-
- out:
- if (sockets[0] >= 0) close(sockets[0]);
- libxl__carefd_close(for_child);
- if (libxl__ev_child_inuse(&op->child)) {
- op->rc = rc;
- /* we will get a callback when the child dies */
- return 0;
- }
-
- assert(rc);
- openpty_cleanup(op);
- return rc;
-}
-
-/*----- async exec -----*/
-
-static void async_exec_timeout(libxl__egc *egc,
- libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- libxl__async_exec_state *aes = CONTAINER_OF(ev, *aes, time);
- STATE_AO_GC(aes->ao);
-
- if (!aes->rc)
- aes->rc = rc;
-
- libxl__ev_time_deregister(gc, &aes->time);
-
- assert(libxl__ev_child_inuse(&aes->child));
- LOG(ERROR, "killing execution of %s because of timeout", aes->what);
-
- if (kill(aes->child.pid, SIGKILL)) {
- LOGEV(ERROR, errno, "unable to kill %s [%ld]",
- aes->what, (unsigned long)aes->child.pid);
- }
-
- return;
-}
-
-static void async_exec_done(libxl__egc *egc,
- libxl__ev_child *child,
- pid_t pid, int status)
-{
- libxl__async_exec_state *aes = CONTAINER_OF(child, *aes, child);
- STATE_AO_GC(aes->ao);
-
- libxl__ev_time_deregister(gc, &aes->time);
-
- if (status) {
- if (!aes->rc)
- libxl_report_child_exitstatus(CTX, LIBXL__LOG_ERROR,
- aes->what, pid, status);
- }
-
- aes->callback(egc, aes, aes->rc, status);
-}
-
-void libxl__async_exec_init(libxl__async_exec_state *aes)
-{
- libxl__ev_time_init(&aes->time);
- libxl__ev_child_init(&aes->child);
-}
-
-int libxl__async_exec_start(libxl__async_exec_state *aes)
-{
- pid_t pid;
-
- /* Convenience aliases */
- libxl__ao *ao = aes->ao;
- AO_GC;
- libxl__ev_child *const child = &aes->child;
- char ** const args = aes->args;
-
- aes->rc = 0;
-
- /* Set execution timeout */
- if (libxl__ev_time_register_rel(ao, &aes->time,
- async_exec_timeout,
- aes->timeout_ms)) {
- LOG(ERROR, "unable to register timeout for executing: %s", aes->what);
- goto out;
- }
-
- LOG(DEBUG, "forking to execute: %s ", aes->what);
-
- /* Fork and exec */
- pid = libxl__ev_child_fork(gc, child, async_exec_done);
- if (pid == -1) {
- LOG(ERROR, "unable to fork");
- goto out;
- }
-
- if (!pid) {
- /* child */
- libxl__exec(gc, aes->stdfds[0], aes->stdfds[1],
- aes->stdfds[2], args[0], args, aes->env);
- }
-
- return 0;
-
-out:
- return ERROR_FAIL;
-}
-
-bool libxl__async_exec_inuse(const libxl__async_exec_state *aes)
-{
- bool time_inuse = libxl__ev_time_isregistered(&aes->time);
- bool child_inuse = libxl__ev_child_inuse(&aes->child);
- assert(time_inuse == child_inuse);
- return child_inuse;
-}
-
-void libxl__kill(libxl__gc *gc, pid_t pid, int sig, const char *what)
-{
- int r = kill(pid, sig);
- if (r) LOGE(WARN, "failed to kill() %s [%lu] (signal %d)",
- what, (unsigned long)pid, sig);
-}
-
-/* Generic function to signal (HUP) a pid stored in xenstore */
-int libxl__kill_xs_path(libxl__gc *gc, const char *xs_path_pid,
- const char *what)
-{
- const char *xs_pid;
- int ret, pid;
-
- ret = libxl__xs_read_checked(gc, XBT_NULL, xs_path_pid, &xs_pid);
- if (ret || !xs_pid) {
- LOG(ERROR, "unable to find %s pid in %s", what, xs_path_pid);
- ret = ret ? : ERROR_FAIL;
- goto out;
- }
- pid = atoi(xs_pid);
-
- ret = kill(pid, SIGHUP);
- if (ret < 0 && errno == ESRCH) {
- LOG(ERROR, "%s already exited", what);
- ret = 0;
- } else if (ret == 0) {
- LOG(DEBUG, "%s signaled", what);
- ret = 0;
- } else {
- LOGE(ERROR, "failed to kill %s [%d]", what, pid);
- ret = ERROR_FAIL;
- goto out;
- }
-
-out:
- return ret;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2012 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#ifndef LIBXL_ARCH_H
-#define LIBXL_ARCH_H
-
-/* fill the arch specific configuration for the domain */
-_hidden
-int libxl__arch_domain_prepare_config(libxl__gc *gc,
- libxl_domain_config *d_config,
- struct xen_domctl_createdomain *config);
-
-/* save the arch specific configuration for the domain */
-_hidden
-int libxl__arch_domain_save_config(libxl__gc *gc,
- libxl_domain_config *d_config,
- libxl__domain_build_state *state,
- const struct xen_domctl_createdomain *config);
-
-/* arch specific internal domain creation function */
-_hidden
-int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
- uint32_t domid);
-
-/* setup arch specific hardware description, i.e. DTB on ARM */
-_hidden
-int libxl__arch_domain_init_hw_description(libxl__gc *gc,
- libxl_domain_build_info *info,
- libxl__domain_build_state *state,
- struct xc_dom_image *dom);
-/* finalize arch specific hardware description. */
-_hidden
-int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_config *d_config,
- struct xc_dom_image *dom);
-
-/* perform any pending hardware initialization */
-_hidden
-int libxl__arch_build_dom_finish(libxl__gc *gc,
- libxl_domain_build_info *info,
- struct xc_dom_image *dom,
- libxl__domain_build_state *state);
-
-/* build vNUMA vmemrange with arch specific information */
-_hidden
-int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_build_info *b_info,
- libxl__domain_build_state *state);
-
-/* arch specific irq map function */
-_hidden
-int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq);
-
-_hidden
-void libxl__arch_domain_create_info_setdefault(libxl__gc *gc,
- libxl_domain_create_info *c_info);
-
-_hidden
-void libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
- libxl_domain_build_info *b_info);
-
-_hidden
-int libxl__arch_passthrough_mode_setdefault(libxl__gc *gc,
- uint32_t domid /* for logging, only */,
- libxl_domain_config *d_config,
- const libxl_physinfo *physinfo);
-
-_hidden
-int libxl__arch_extra_memory(libxl__gc *gc,
- const libxl_domain_build_info *info,
- uint64_t *out);
-
-#if defined(__i386__) || defined(__x86_64__)
-
-#define LAPIC_BASE_ADDRESS 0xfee00000
-#define ACPI_INFO_PHYSICAL_ADDRESS 0xfc000000
-
-int libxl__dom_load_acpi(libxl__gc *gc,
- const libxl_domain_build_info *b_info,
- struct xc_dom_image *dom);
-#endif
-
-#endif
+++ /dev/null
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-#include "libxl_libfdt_compat.h"
-#include "libxl_arm.h"
-
-#include <xenctrl_dom.h>
-#include <stdbool.h>
-#include <libfdt.h>
-#include <assert.h>
-#include <xen/device_tree_defs.h>
-
-static const char *gicv_to_string(libxl_gic_version gic_version)
-{
- switch (gic_version) {
- case LIBXL_GIC_VERSION_V2:
- return "V2";
- case LIBXL_GIC_VERSION_V3:
- return "V3";
- default:
- return "unknown";
- }
-}
-
-int libxl__arch_domain_prepare_config(libxl__gc *gc,
- libxl_domain_config *d_config,
- struct xen_domctl_createdomain *config)
-{
- uint32_t nr_spis = 0;
- unsigned int i;
- uint32_t vuart_irq;
- bool vuart_enabled = false;
-
- /*
- * If pl011 vuart is enabled then increment the nr_spis to allow allocation
- * of SPI VIRQ for pl011.
- */
- if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
- nr_spis += (GUEST_VPL011_SPI - 32) + 1;
- vuart_irq = GUEST_VPL011_SPI;
- vuart_enabled = true;
- }
-
- for (i = 0; i < d_config->b_info.num_irqs; i++) {
- uint32_t irq = d_config->b_info.irqs[i];
- uint32_t spi;
-
- /*
- * This check ensures the if user has requested pass-through of a certain irq
- * which conflicts with vpl011 irq then it flags an error to indicate to the
- * user that the specific HW irq cannot be used as it is dedicated for vpl011.
- *
- * TODO:
- * The vpl011 irq should be assigned such that it never conflicts with user
- * specified irqs thereby preventing its pass-through. This TODO is for
- * implementing that logic in future.
- */
- if (vuart_enabled && irq == vuart_irq) {
- LOG(ERROR, "Physical IRQ %u conflicting with pl011 SPI\n", irq);
- return ERROR_FAIL;
- }
-
- if (irq < 32)
- continue;
-
- spi = irq - 32;
-
- if (nr_spis <= spi)
- nr_spis = spi + 1;
- }
-
- LOG(DEBUG, "Configure the domain");
-
- config->arch.nr_spis = nr_spis;
- LOG(DEBUG, " - Allocate %u SPIs", nr_spis);
-
- switch (d_config->b_info.arch_arm.gic_version) {
- case LIBXL_GIC_VERSION_DEFAULT:
- config->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_NATIVE;
- break;
- case LIBXL_GIC_VERSION_V2:
- config->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_V2;
- break;
- case LIBXL_GIC_VERSION_V3:
- config->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_V3;
- break;
- default:
- LOG(ERROR, "Unknown GIC version %d",
- d_config->b_info.arch_arm.gic_version);
- return ERROR_FAIL;
- }
-
- switch (d_config->b_info.tee) {
- case LIBXL_TEE_TYPE_NONE:
- config->arch.tee_type = XEN_DOMCTL_CONFIG_TEE_NONE;
- break;
- case LIBXL_TEE_TYPE_OPTEE:
- config->arch.tee_type = XEN_DOMCTL_CONFIG_TEE_OPTEE;
- break;
- default:
- LOG(ERROR, "Unknown TEE type %d",
- d_config->b_info.tee);
- return ERROR_FAIL;
- }
-
- return 0;
-}
-
-int libxl__arch_domain_save_config(libxl__gc *gc,
- libxl_domain_config *d_config,
- libxl__domain_build_state *state,
- const struct xen_domctl_createdomain *config)
-{
- switch (config->arch.gic_version) {
- case XEN_DOMCTL_CONFIG_GIC_V2:
- d_config->b_info.arch_arm.gic_version = LIBXL_GIC_VERSION_V2;
- break;
- case XEN_DOMCTL_CONFIG_GIC_V3:
- d_config->b_info.arch_arm.gic_version = LIBXL_GIC_VERSION_V3;
- break;
- default:
- LOG(ERROR, "Unexpected gic version %u", config->arch.gic_version);
- return ERROR_FAIL;
- }
-
- state->clock_frequency = config->arch.clock_frequency;
-
- return 0;
-}
-
-int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
- uint32_t domid)
-{
- return 0;
-}
-
-int libxl__arch_extra_memory(libxl__gc *gc,
- const libxl_domain_build_info *info,
- uint64_t *out)
-{
- int rc = 0;
- uint64_t size = 0;
-
- if (libxl_defbool_val(info->acpi)) {
- rc = libxl__get_acpi_size(gc, info, &size);
- if (rc < 0) {
- rc = ERROR_FAIL;
- goto out;
- }
- }
-
- *out = LIBXL_MAXMEM_CONSTANT + DIV_ROUNDUP(size, 1024);
-out:
- return rc;
-}
-
-static struct arch_info {
- const char *guest_type;
- const char *timer_compat;
- const char *cpu_compat;
-} arch_info[] = {
- {"xen-3.0-armv7l", "arm,armv7-timer", "arm,cortex-a15" },
- {"xen-3.0-aarch64", "arm,armv8-timer", "arm,armv8" },
-};
-
-typedef uint32_t be32;
-typedef be32 gic_interrupt[3];
-
-#define PROP_INITRD_START "linux,initrd-start"
-#define PROP_INITRD_END "linux,initrd-end"
-
-static void set_cell(be32 **cellp, int size, uint64_t val)
-{
- int cells = size;
-
- while (size--) {
- (*cellp)[size] = cpu_to_fdt32(val);
- val >>= 32;
- }
-
- (*cellp) += cells;
-}
-
-static void set_interrupt(gic_interrupt interrupt, unsigned int irq,
- unsigned int cpumask, unsigned int level)
-{
- be32 *cells = interrupt;
- int is_ppi = (irq < 32);
-
- /* SGIs are not describe in the device tree */
- assert(irq >= 16);
-
- irq -= (is_ppi) ? 16: 32; /* PPIs start at 16, SPIs at 32 */
-
- /* See linux Documentation/devictree/bindings/arm/gic.txt */
- set_cell(&cells, 1, is_ppi); /* is a PPI? */
- set_cell(&cells, 1, irq);
- set_cell(&cells, 1, (cpumask << 8) | level);
-}
-
-static void set_range(be32 **cellp,
- int address_cells, int size_cells,
- uint64_t address, uint64_t size)
-{
- set_cell(cellp, address_cells, address);
- set_cell(cellp, size_cells, size);
-}
-
-static int fdt_property_compat(libxl__gc *gc, void *fdt, unsigned nr_compat, ...)
-{
- const char *compats[nr_compat];
- int i;
- size_t sz;
- va_list ap;
- char *compat, *p;
-
- va_start(ap, nr_compat);
- sz = 0;
- for (i = 0; i < nr_compat; i++) {
- const char *c = va_arg(ap, const char *);
- compats[i] = c;
- sz += strlen(compats[i]) + 1;
- }
- va_end(ap);
-
- p = compat = libxl__zalloc(gc, sz);
- for (i = 0; i < nr_compat; i++) {
- strcpy(p, compats[i]);
- p += strlen(compats[i]) + 1;
- }
-
- return fdt_property(fdt, "compatible", compat, sz);
-}
-
-static int fdt_property_interrupts(libxl__gc *gc, void *fdt,
- gic_interrupt *intr,
- unsigned num_irq)
-{
- int res;
-
- res = fdt_property(fdt, "interrupts", intr, sizeof (intr[0]) * num_irq);
- if (res) return res;
-
- res = fdt_property_cell(fdt, "interrupt-parent", GUEST_PHANDLE_GIC);
- if (res) return res;
-
- return 0;
-}
-
-static int fdt_property_regs(libxl__gc *gc, void *fdt,
- unsigned addr_cells,
- unsigned size_cells,
- unsigned num_regs, ...)
-{
- uint32_t regs[num_regs*(addr_cells+size_cells)];
- be32 *cells = ®s[0];
- int i;
- va_list ap;
- uint64_t base, size;
-
- va_start(ap, num_regs);
- for (i = 0 ; i < num_regs; i++) {
- base = addr_cells ? va_arg(ap, uint64_t) : 0;
- size = size_cells ? va_arg(ap, uint64_t) : 0;
- set_range(&cells, addr_cells, size_cells, base, size);
- }
- va_end(ap);
-
- return fdt_property(fdt, "reg", regs, sizeof(regs));
-}
-
-static int make_root_properties(libxl__gc *gc,
- const libxl_version_info *vers,
- void *fdt)
-{
- int res;
-
- res = fdt_property_string(fdt, "model", GCSPRINTF("XENVM-%d.%d",
- vers->xen_version_major,
- vers->xen_version_minor));
- if (res) return res;
-
- res = fdt_property_compat(gc, fdt, 2,
- GCSPRINTF("xen,xenvm-%d.%d",
- vers->xen_version_major,
- vers->xen_version_minor),
- "xen,xenvm");
- if (res) return res;
-
- res = fdt_property_cell(fdt, "interrupt-parent", GUEST_PHANDLE_GIC);
- if (res) return res;
-
- res = fdt_property_cell(fdt, "#address-cells", GUEST_ROOT_ADDRESS_CELLS);
- if (res) return res;
-
- res = fdt_property_cell(fdt, "#size-cells", GUEST_ROOT_SIZE_CELLS);
- if (res) return res;
-
- return 0;
-}
-
-static int make_chosen_node(libxl__gc *gc, void *fdt, bool ramdisk,
- libxl__domain_build_state *state,
- const libxl_domain_build_info *info)
-{
- int res;
-
- /* See linux Documentation/devicetree/... */
- res = fdt_begin_node(fdt, "chosen");
- if (res) return res;
-
- if (state->pv_cmdline) {
- LOG(DEBUG, "/chosen/bootargs = %s", state->pv_cmdline);
- res = fdt_property_string(fdt, "bootargs", state->pv_cmdline);
- if (res) return res;
- }
-
- if (ramdisk) {
- uint64_t dummy = 0;
- LOG(DEBUG, "/chosen adding placeholder linux,initrd properties");
- res = fdt_property(fdt, PROP_INITRD_START, &dummy, sizeof(dummy));
- if (res) return res;
- res = fdt_property(fdt, PROP_INITRD_END, &dummy, sizeof(dummy));
- if (res) return res;
- }
-
- if (libxl_defbool_val(info->acpi)) {
- const uint64_t acpi_base = GUEST_ACPI_BASE;
- const char *name = GCSPRINTF("module@%"PRIx64, acpi_base);
-
- res = fdt_begin_node(fdt, name);
- if (res) return res;
-
- res = fdt_property_compat(gc, fdt, 2, "xen,guest-acpi",
- "multiboot,module");
- if (res) return res;
-
- res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
- 1, 0, 0);
- if (res) return res;
-
- res = fdt_end_node(fdt);
- if (res) return res;
- }
-
- res = fdt_end_node(fdt);
- if (res) return res;
-
- return 0;
-}
-
-static int make_cpus_node(libxl__gc *gc, void *fdt, int nr_cpus,
- const struct arch_info *ainfo)
-{
- int res, i;
- uint64_t mpidr_aff;
-
- res = fdt_begin_node(fdt, "cpus");
- if (res) return res;
-
- res = fdt_property_cell(fdt, "#address-cells", 1);
- if (res) return res;
-
- res = fdt_property_cell(fdt, "#size-cells", 0);
- if (res) return res;
-
- for (i = 0; i < nr_cpus; i++) {
- const char *name;
-
- mpidr_aff = libxl__compute_mpdir(i);
- name = GCSPRINTF("cpu@%"PRIx64, mpidr_aff);
-
- res = fdt_begin_node(fdt, name);
- if (res) return res;
-
- res = fdt_property_string(fdt, "device_type", "cpu");
- if (res) return res;
-
- res = fdt_property_compat(gc, fdt, 1, ainfo->cpu_compat);
- if (res) return res;
-
- res = fdt_property_string(fdt, "enable-method", "psci");
- if (res) return res;
-
- res = fdt_property_regs(gc, fdt, 1, 0, 1, mpidr_aff);
- if (res) return res;
-
- res = fdt_end_node(fdt);
- if (res) return res;
- }
-
- res = fdt_end_node(fdt);
- if (res) return res;
-
- return 0;
-}
-
-static int make_psci_node(libxl__gc *gc, void *fdt)
-{
- int res;
-
- res = fdt_begin_node(fdt, "psci");
- if (res) return res;
-
- res = fdt_property_compat(gc, fdt, 3, "arm,psci-1.0",
- "arm,psci-0.2", "arm,psci");
- if (res) return res;
-
- res = fdt_property_string(fdt, "method", "hvc");
- if (res) return res;
-
- res = fdt_property_cell(fdt, "cpu_off", PSCI_cpu_off);
- if (res) return res;
-
- res = fdt_property_cell(fdt, "cpu_on", PSCI_cpu_on);
- if (res) return res;
-
- res = fdt_end_node(fdt);
- if (res) return res;
-
- return 0;
-}
-
-static int make_optee_node(libxl__gc *gc, void *fdt)
-{
- int res;
- LOG(DEBUG, "Creating OP-TEE node in dtb");
-
- res = fdt_begin_node(fdt, "firmware");
- if (res) return res;
-
- res = fdt_begin_node(fdt, "optee");
- if (res) return res;
-
- res = fdt_property_compat(gc, fdt, 1, "linaro,optee-tz");
- if (res) return res;
-
- res = fdt_property_string(fdt, "method", "hvc");
- if (res) return res;
-
- res = fdt_end_node(fdt);
- if (res) return res;
-
- res = fdt_end_node(fdt);
- if (res) return res;
-
- return 0;
-}
-
-static int make_memory_nodes(libxl__gc *gc, void *fdt,
- const struct xc_dom_image *dom)
-{
- int res, i;
- const char *name;
- const uint64_t bankbase[] = GUEST_RAM_BANK_BASES;
-
- for (i = 0; i < GUEST_RAM_BANKS; i++) {
- name = GCSPRINTF("memory@%"PRIx64, bankbase[i]);
-
- LOG(DEBUG, "Creating placeholder node /%s", name);
-
- res = fdt_begin_node(fdt, name);
- if (res) return res;
-
- res = fdt_property_string(fdt, "device_type", "memory");
- if (res) return res;
-
- res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
- 1, 0, 0);
- if (res) return res;
-
- res = fdt_end_node(fdt);
- if (res) return res;
- }
-
- return 0;
-}
-
-static int make_gicv2_node(libxl__gc *gc, void *fdt,
- uint64_t gicd_base, uint64_t gicd_size,
- uint64_t gicc_base, uint64_t gicc_size)
-{
- int res;
- const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base);
-
- res = fdt_begin_node(fdt, name);
- if (res) return res;
-
- res = fdt_property_compat(gc, fdt, 2,
- "arm,cortex-a15-gic",
- "arm,cortex-a9-gic");
- if (res) return res;
-
-
- res = fdt_property_cell(fdt, "#interrupt-cells", 3);
- if (res) return res;
-
- res = fdt_property_cell(fdt, "#address-cells", 0);
- if (res) return res;
-
- res = fdt_property(fdt, "interrupt-controller", NULL, 0);
- if (res) return res;
-
- res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
- 2,
- gicd_base, gicd_size,
- gicc_base, gicc_size);
- if (res) return res;
-
- res = fdt_property_cell(fdt, "linux,phandle", GUEST_PHANDLE_GIC);
- if (res) return res;
-
- res = fdt_property_cell(fdt, "phandle", GUEST_PHANDLE_GIC);
- if (res) return res;
-
- res = fdt_end_node(fdt);
- if (res) return res;
-
- return 0;
-}
-
-static int make_gicv3_node(libxl__gc *gc, void *fdt)
-{
- int res;
- const uint64_t gicd_base = GUEST_GICV3_GICD_BASE;
- const uint64_t gicd_size = GUEST_GICV3_GICD_SIZE;
- const uint64_t gicr0_base = GUEST_GICV3_GICR0_BASE;
- const uint64_t gicr0_size = GUEST_GICV3_GICR0_SIZE;
- const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base);
-
- res = fdt_begin_node(fdt, name);
- if (res) return res;
-
- res = fdt_property_compat(gc, fdt, 1, "arm,gic-v3");
- if (res) return res;
-
- res = fdt_property_cell(fdt, "#interrupt-cells", 3);
- if (res) return res;
-
- res = fdt_property_cell(fdt, "#address-cells", 0);
- if (res) return res;
-
- res = fdt_property(fdt, "interrupt-controller", NULL, 0);
- if (res) return res;
-
- res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
- 2,
- gicd_base, gicd_size,
- gicr0_base, gicr0_size);
- if (res) return res;
-
- res = fdt_property_cell(fdt, "linux,phandle", GUEST_PHANDLE_GIC);
- if (res) return res;
-
- res = fdt_property_cell(fdt, "phandle", GUEST_PHANDLE_GIC);
- if (res) return res;
-
- res = fdt_end_node(fdt);
- if (res) return res;
-
- return 0;
-}
-
-static int make_timer_node(libxl__gc *gc, void *fdt,
- const struct arch_info *ainfo,
- uint32_t frequency)
-{
- int res;
- gic_interrupt ints[3];
-
- res = fdt_begin_node(fdt, "timer");
- if (res) return res;
-
- res = fdt_property_compat(gc, fdt, 1, ainfo->timer_compat);
- if (res) return res;
-
- set_interrupt(ints[0], GUEST_TIMER_PHYS_S_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
- set_interrupt(ints[1], GUEST_TIMER_PHYS_NS_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
- set_interrupt(ints[2], GUEST_TIMER_VIRT_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
-
- res = fdt_property_interrupts(gc, fdt, ints, 3);
- if (res) return res;
-
- if ( frequency )
- fdt_property_u32(fdt, "clock-frequency", frequency);
-
- res = fdt_end_node(fdt);
- if (res) return res;
-
- return 0;
-}
-
-static int make_hypervisor_node(libxl__gc *gc, void *fdt,
- const libxl_version_info *vers)
-{
- int res;
- gic_interrupt intr;
-
- /* See linux Documentation/devicetree/bindings/arm/xen.txt */
- res = fdt_begin_node(fdt, "hypervisor");
- if (res) return res;
-
- res = fdt_property_compat(gc, fdt, 2,
- GCSPRINTF("xen,xen-%d.%d",
- vers->xen_version_major,
- vers->xen_version_minor),
- "xen,xen");
- if (res) return res;
-
- /* reg 0 is grant table space */
- res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
- 1,GUEST_GNTTAB_BASE, GUEST_GNTTAB_SIZE);
- if (res) return res;
-
- /*
- * interrupts is evtchn upcall:
- * - Active-low level-sensitive
- * - All cpus
- */
- set_interrupt(intr, GUEST_EVTCHN_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
-
- res = fdt_property_interrupts(gc, fdt, &intr, 1);
- if (res) return res;
-
- res = fdt_end_node(fdt);
- if (res) return res;
-
- return 0;
-}
-
-static int make_vpl011_uart_node(libxl__gc *gc, void *fdt,
- const struct arch_info *ainfo,
- struct xc_dom_image *dom)
-{
- int res;
- gic_interrupt intr;
-
- res = fdt_begin_node(fdt, "sbsa-pl011");
- if (res) return res;
-
- res = fdt_property_compat(gc, fdt, 1, "arm,sbsa-uart");
- if (res) return res;
-
- res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
- 1,
- GUEST_PL011_BASE, GUEST_PL011_SIZE);
- if (res) return res;
-
- set_interrupt(intr, GUEST_VPL011_SPI, 0xf, DT_IRQ_TYPE_LEVEL_HIGH);
-
- res = fdt_property_interrupts(gc, fdt, &intr, 1);
- if (res) return res;
-
- /* Use a default baud rate of 115200. */
- fdt_property_u32(fdt, "current-speed", 115200);
-
- res = fdt_end_node(fdt);
- if (res) return res;
-
- return 0;
-}
-
-static const struct arch_info *get_arch_info(libxl__gc *gc,
- const struct xc_dom_image *dom)
-{
- int i;
-
- for (i=0; i < ARRAY_SIZE(arch_info); i++) {
- const struct arch_info *info = &arch_info[i];
- if (!strcmp(dom->guest_type, info->guest_type))
- return info;
- }
- LOG(ERROR, "Unable to find arch FDT info for %s", dom->guest_type);
- return NULL;
-}
-
-static void debug_dump_fdt(libxl__gc *gc, void *fdt)
-{
- int fd = -1, rc, r;
-
- const char *dtb = getenv("LIBXL_DEBUG_DUMP_DTB");
-
- if (!dtb) goto out;
-
- fd = open(dtb, O_CREAT|O_TRUNC|O_WRONLY, 0666);
- if (fd < 0) {
- LOGE(DEBUG, "cannot open %s for LIBXL_DEBUG_DUMP_DTB", dtb);
- goto out;
- }
-
- rc = libxl_write_exactly(CTX, fd, fdt, fdt_totalsize(fdt), dtb, "dtb");
- if (rc < 0) goto out;
-
-out:
- if (fd >= 0) {
- r = close(fd);
- if (r < 0) LOGE(DEBUG, "failed to close DTB debug dump output");
- }
-}
-
-#ifdef ENABLE_PARTIAL_DEVICE_TREE
-
-static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size)
-{
- int r;
-
- if (fdt_magic(fdt) != FDT_MAGIC) {
- LOG(ERROR, "Partial FDT is not a valid Flat Device Tree");
- return ERROR_FAIL;
- }
-
- r = fdt_check_header(fdt);
- if (r) {
- LOG(ERROR, "Failed to check the partial FDT (%d)", r);
- return ERROR_FAIL;
- }
-
- if (fdt_totalsize(fdt) > size) {
- LOG(ERROR, "Partial FDT totalsize is too big");
- return ERROR_FAIL;
- }
-
- return 0;
-}
-
-static int copy_properties(libxl__gc *gc, void *fdt, void *pfdt,
- int nodeoff)
-{
- int propoff, nameoff, r;
- const struct fdt_property *prop;
-
- for (propoff = fdt_first_property_offset(pfdt, nodeoff);
- propoff >= 0;
- propoff = fdt_next_property_offset(pfdt, propoff)) {
-
- if (!(prop = fdt_get_property_by_offset(pfdt, propoff, NULL))) {
- return -FDT_ERR_INTERNAL;
- }
-
- nameoff = fdt32_to_cpu(prop->nameoff);
- r = fdt_property(fdt, fdt_string(pfdt, nameoff),
- prop->data, fdt32_to_cpu(prop->len));
- if (r) return r;
- }
-
- /* FDT_ERR_NOTFOUND => There is no more properties for this node */
- return (propoff != -FDT_ERR_NOTFOUND)? propoff : 0;
-}
-
-/* Copy a node from the partial device tree to the guest device tree */
-static int copy_node(libxl__gc *gc, void *fdt, void *pfdt,
- int nodeoff, int depth)
-{
- int r;
-
- r = fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL));
- if (r) return r;
-
- r = copy_properties(gc, fdt, pfdt, nodeoff);
- if (r) return r;
-
- for (nodeoff = fdt_first_subnode(pfdt, nodeoff);
- nodeoff >= 0;
- nodeoff = fdt_next_subnode(pfdt, nodeoff)) {
- r = copy_node(gc, fdt, pfdt, nodeoff, depth + 1);
- if (r) return r;
- }
-
- if (nodeoff != -FDT_ERR_NOTFOUND)
- return nodeoff;
-
- r = fdt_end_node(fdt);
- if (r) return r;
-
- return 0;
-}
-
-static int copy_node_by_path(libxl__gc *gc, const char *path,
- void *fdt, void *pfdt)
-{
- int nodeoff, r;
- const char *name = strrchr(path, '/');
-
- if (!name)
- return -FDT_ERR_INTERNAL;
-
- name++;
-
- /*
- * The FDT function to look at a node doesn't take into account the
- * unit (i.e anything after @) when search by name. Check if the
- * name exactly matches.
- */
- nodeoff = fdt_path_offset(pfdt, path);
- if (nodeoff < 0)
- return nodeoff;
-
- if (strcmp(fdt_get_name(pfdt, nodeoff, NULL), name))
- return -FDT_ERR_NOTFOUND;
-
- r = copy_node(gc, fdt, pfdt, nodeoff, 0);
- if (r) return r;
-
- return 0;
-}
-
-/*
- * The partial device tree is not copied entirely. Only the relevant bits are
- * copied to the guest device tree:
- * - /passthrough node
- * - /aliases node
- */
-static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt)
-{
- int r;
-
- r = copy_node_by_path(gc, "/passthrough", fdt, pfdt);
- if (r < 0) {
- LOG(ERROR, "Can't copy the node \"/passthrough\" from the partial FDT");
- return r;
- }
-
- r = copy_node_by_path(gc, "/aliases", fdt, pfdt);
- if (r < 0 && r != -FDT_ERR_NOTFOUND) {
- LOG(ERROR, "Can't copy the node \"/aliases\" from the partial FDT");
- return r;
- }
-
- return 0;
-}
-
-#else
-
-static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size)
-{
- LOG(ERROR, "partial device tree not supported");
-
- return ERROR_FAIL;
-}
-
-static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt)
-{
- /*
- * We should never be here when the partial device tree is not
- * supported.
- * */
- return -FDT_ERR_INTERNAL;
-}
-
-#endif /* ENABLE_PARTIAL_DEVICE_TREE */
-
-#define FDT_MAX_SIZE (1<<20)
-
-static int libxl__prepare_dtb(libxl__gc *gc, libxl_domain_build_info *info,
- libxl__domain_build_state *state,
- struct xc_dom_image *dom)
-{
- void *fdt = NULL;
- void *pfdt = NULL;
- int rc, res;
- size_t fdt_size = 0;
- int pfdt_size = 0;
-
- const libxl_version_info *vers;
- const struct arch_info *ainfo;
-
- vers = libxl_get_version_info(CTX);
- if (vers == NULL) return ERROR_FAIL;
-
- ainfo = get_arch_info(gc, dom);
- if (ainfo == NULL) return ERROR_FAIL;
-
- LOG(DEBUG, "constructing DTB for Xen version %d.%d guest",
- vers->xen_version_major, vers->xen_version_minor);
- LOG(DEBUG, " - vGIC version: %s",
- gicv_to_string(info->arch_arm.gic_version));
-
- if (info->device_tree) {
- LOG(DEBUG, " - Partial device tree provided: %s", info->device_tree);
-
- rc = libxl_read_file_contents(CTX, info->device_tree,
- &pfdt, &pfdt_size);
- if (rc) {
- LOGEV(ERROR, rc, "failed to read the partial device file %s",
- info->device_tree);
- return ERROR_FAIL;
- }
- libxl__ptr_add(gc, pfdt);
-
- if (check_partial_fdt(gc, pfdt, pfdt_size))
- return ERROR_FAIL;
- }
-
-/*
- * Call "call" handling FDT_ERR_*. Will either:
- * - loop back to retry_resize
- * - set rc and goto out
- * - fall through successfully
- *
- * On FDT_ERR_NOSPACE we start again from scratch rather than
- * realloc+libfdt_open_into because "call" may have failed half way
- * through a series of steps leaving the partial tree in an
- * inconsistent state, e.g. leaving a node open.
- */
-#define FDT( call ) do { \
- int fdt_res = (call); \
- if (fdt_res == -FDT_ERR_NOSPACE && fdt_size < FDT_MAX_SIZE) \
- goto next_resize; \
- else if (fdt_res < 0) { \
- LOG(ERROR, "FDT: %s failed: %d = %s", \
- #call, fdt_res, fdt_strerror(fdt_res)); \
- rc = ERROR_FAIL; \
- goto out; \
- } \
-} while(0)
-
- for (;;) {
-next_resize:
- if (fdt_size) {
- fdt_size <<= 1;
- LOG(DEBUG, "Increasing FDT size to %zd and retrying", fdt_size);
- } else {
- fdt_size = 4096;
- }
-
- fdt = libxl__realloc(gc, fdt, fdt_size);
-
- FDT( fdt_create(fdt, fdt_size) );
-
- FDT( fdt_finish_reservemap(fdt) );
-
- FDT( fdt_begin_node(fdt, "") );
-
- FDT( make_root_properties(gc, vers, fdt) );
- FDT( make_chosen_node(gc, fdt, !!dom->modules[0].blob, state, info) );
- FDT( make_cpus_node(gc, fdt, info->max_vcpus, ainfo) );
- FDT( make_psci_node(gc, fdt) );
-
- FDT( make_memory_nodes(gc, fdt, dom) );
-
- switch (info->arch_arm.gic_version) {
- case LIBXL_GIC_VERSION_V2:
- FDT( make_gicv2_node(gc, fdt,
- GUEST_GICD_BASE, GUEST_GICD_SIZE,
- GUEST_GICC_BASE, GUEST_GICC_SIZE) );
- break;
- case LIBXL_GIC_VERSION_V3:
- FDT( make_gicv3_node(gc, fdt) );
- break;
- default:
- LOG(ERROR, "Unknown GIC version %s",
- gicv_to_string(info->arch_arm.gic_version));
- rc = ERROR_FAIL;
- goto out;
- }
-
- FDT( make_timer_node(gc, fdt, ainfo, state->clock_frequency) );
- FDT( make_hypervisor_node(gc, fdt, vers) );
-
- if (info->arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART)
- FDT( make_vpl011_uart_node(gc, fdt, ainfo, dom) );
-
- if (info->tee == LIBXL_TEE_TYPE_OPTEE)
- FDT( make_optee_node(gc, fdt) );
-
- if (pfdt)
- FDT( copy_partial_fdt(gc, fdt, pfdt) );
-
- FDT( fdt_end_node(fdt) );
-
- FDT( fdt_finish(fdt) );
- break;
- }
-#undef FDT
-
- LOG(DEBUG, "fdt total size %d", fdt_totalsize(fdt));
-
- res = xc_dom_devicetree_mem(dom, fdt, fdt_totalsize(fdt));
- if (res) {
- LOGE(ERROR, "xc_dom_devicetree_mem failed");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-int libxl__arch_domain_init_hw_description(libxl__gc *gc,
- libxl_domain_build_info *info,
- libxl__domain_build_state *state,
- struct xc_dom_image *dom)
-{
- int rc;
- uint64_t val;
-
- if (info->type != LIBXL_DOMAIN_TYPE_PVH) {
- LOG(ERROR, "Unsupported Arm guest type %s",
- libxl_domain_type_to_string(info->type));
- return ERROR_INVAL;
- }
-
- /* Set the value of domain param HVM_PARAM_CALLBACK_IRQ. */
- val = MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI,
- HVM_PARAM_CALLBACK_IRQ_TYPE_MASK);
- /* Active-low level-sensitive */
- val |= MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_LOW_LEVEL,
- HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_MASK);
- val |= GUEST_EVTCHN_PPI;
- rc = xc_hvm_param_set(dom->xch, dom->guest_domid, HVM_PARAM_CALLBACK_IRQ,
- val);
- if (rc)
- return rc;
-
- rc = libxl__prepare_dtb(gc, info, state, dom);
- if (rc) goto out;
-
- if (!libxl_defbool_val(info->acpi)) {
- LOG(DEBUG, "Generating ACPI tables is disabled by user.");
- rc = 0;
- goto out;
- }
-
- if (strcmp(dom->guest_type, "xen-3.0-aarch64")) {
- /* ACPI is only supported for 64-bit guest currently. */
- LOG(ERROR, "Can not enable libxl option 'acpi' for %s", dom->guest_type);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__prepare_acpi(gc, info, dom);
-
-out:
- return rc;
-}
-
-static void finalise_one_node(libxl__gc *gc, void *fdt, const char *uname,
- uint64_t base, uint64_t size)
-{
- int node, res;
- const char *name = GCSPRINTF("%s@%"PRIx64, uname, base);
-
- node = fdt_path_offset(fdt, name);
- assert(node > 0);
-
- if (size == 0) {
- LOG(DEBUG, "Nopping out placeholder node %s", name);
- fdt_nop_node(fdt, node);
- } else {
- uint32_t regs[GUEST_ROOT_ADDRESS_CELLS+GUEST_ROOT_SIZE_CELLS];
- be32 *cells = ®s[0];
-
- LOG(DEBUG, "Populating placeholder node %s", name);
-
- set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS, base, size);
-
- res = fdt_setprop_inplace(fdt, node, "reg", regs, sizeof(regs));
- assert(!res);
- }
-}
-
-int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_config *d_config,
- struct xc_dom_image *dom)
-{
- void *fdt = dom->devicetree_blob;
- int i;
- const uint64_t bankbase[] = GUEST_RAM_BANK_BASES;
-
- const struct xc_dom_seg *ramdisk = dom->modules[0].blob ?
- &dom->modules[0].seg : NULL;
-
- if (ramdisk) {
- int chosen, res;
- uint64_t val;
-
- /* Neither the fdt_path_offset() nor either of the
- * fdt_setprop_inplace() calls can fail. If they do then
- * make_chosen_node() (see above) has got something very
- * wrong.
- */
- chosen = fdt_path_offset(fdt, "/chosen");
- assert(chosen > 0);
-
- LOG(DEBUG, "/chosen updating initrd properties to cover "
- "%"PRIx64"-%"PRIx64,
- ramdisk->vstart, ramdisk->vend);
-
- val = cpu_to_fdt64(ramdisk->vstart);
- res = fdt_setprop_inplace(fdt, chosen, PROP_INITRD_START,
- &val, sizeof(val));
- assert(!res);
-
- val = cpu_to_fdt64(ramdisk->vend);
- res = fdt_setprop_inplace(fdt, chosen, PROP_INITRD_END,
- &val, sizeof(val));
- assert(!res);
-
- }
-
- for (i = 0; i < GUEST_RAM_BANKS; i++) {
- const uint64_t size = (uint64_t)dom->rambank_size[i] << XC_PAGE_SHIFT;
-
- finalise_one_node(gc, fdt, "/memory", bankbase[i], size);
- }
-
- if (dom->acpi_modules[0].data) {
- finalise_one_node(gc, fdt, "/chosen/module", GUEST_ACPI_BASE,
- dom->acpi_modules[0].length);
- }
-
- debug_dump_fdt(gc, fdt);
-
- return 0;
-}
-
-int libxl__arch_build_dom_finish(libxl__gc *gc,
- libxl_domain_build_info *info,
- struct xc_dom_image *dom,
- libxl__domain_build_state *state)
-{
- int rc = 0, ret;
-
- if (info->arch_arm.vuart != LIBXL_VUART_TYPE_SBSA_UART) {
- rc = 0;
- goto out;
- }
-
- ret = xc_dom_vuart_init(CTX->xch,
- XEN_DOMCTL_VUART_TYPE_VPL011,
- dom->guest_domid,
- dom->console_domid,
- dom->vuart_gfn,
- &state->vuart_port);
- if (ret < 0) {
- rc = ERROR_FAIL;
- LOG(ERROR, "xc_dom_vuart_init failed\n");
- }
-
-out:
- return rc;
-}
-
-int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_build_info *info,
- libxl__domain_build_state *state)
-{
- return libxl__vnuma_build_vmemrange_pv_generic(gc, domid, info, state);
-}
-
-int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq)
-{
- return xc_domain_bind_pt_spi_irq(CTX->xch, domid, irq, irq);
-}
-
-void libxl__arch_domain_create_info_setdefault(libxl__gc *gc,
- libxl_domain_create_info *c_info)
-{
- /*
- * Arm guest are now considered as PVH by the toolstack. To allow
- * compatibility with previous toolstack, PV guest are automatically
- * converted to PVH.
- */
- if (c_info->type == LIBXL_DOMAIN_TYPE_PV) {
- LOG(WARN, "Converting PV guest to PVH.");
- LOG(WARN, "Arm guest are now PVH.");
- LOG(WARN, "Please fix your configuration file/toolstack.");
-
- c_info->type = LIBXL_DOMAIN_TYPE_PVH;
- /* All other fields can remain untouched */
- }
-}
-
-void libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
- libxl_domain_build_info *b_info)
-{
- /* ACPI is disabled by default */
- libxl_defbool_setdefault(&b_info->acpi, false);
-
- if (b_info->type != LIBXL_DOMAIN_TYPE_PV)
- return;
-
- LOG(DEBUG, "Converting build_info to PVH");
-
- /* Re-initialize type to PVH and all associated fields to defaults. */
- memset(&b_info->u, '\0', sizeof(b_info->u));
- b_info->type = LIBXL_DOMAIN_TYPE_INVALID;
- libxl_domain_build_info_init_type(b_info, LIBXL_DOMAIN_TYPE_PVH);
-}
-
-int libxl__arch_passthrough_mode_setdefault(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_config *d_config,
- const libxl_physinfo *physinfo)
-{
- int rc;
- libxl_domain_create_info *const c_info = &d_config->c_info;
-
- if (c_info->passthrough == LIBXL_PASSTHROUGH_ENABLED) {
- c_info->passthrough = LIBXL_PASSTHROUGH_SHARE_PT;
- }
-
- switch (c_info->passthrough) {
- case LIBXL_PASSTHROUGH_DISABLED:
- case LIBXL_PASSTHROUGH_SHARE_PT:
- break;
-
- default:
- LOGD(ERROR, domid,
- "passthrough=\"%s\" not supported on ARM\n",
- libxl_passthrough_to_string(c_info->passthrough));
- rc = ERROR_INVAL;
- goto out;
- }
-
- rc = 0;
- out:
- return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2016 Linaro Ltd.
- *
- * Author: Shannon Zhao <shannon.zhao@linaro.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-
-#include <xenctrl_dom.h>
-
-_hidden
-int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
- struct xc_dom_image *dom);
-
-_hidden
-int libxl__get_acpi_size(libxl__gc *gc,
- const libxl_domain_build_info *info,
- uint64_t *out);
-
-static inline uint64_t libxl__compute_mpdir(unsigned int cpuid)
-{
- /*
- * According to ARM CPUs bindings, the reg field should match
- * the MPIDR's affinity bits. We will use AFF0 and AFF1 when
- * constructing the reg value of the guest at the moment, for it
- * is enough for the current max vcpu number.
- */
- return (cpuid & 0x0f) | (((cpuid >> 4) & 0xff) << 8);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * ARM DomU ACPI generation
- *
- * Copyright (C) 2016 Linaro Ltd.
- *
- * Author: Shannon Zhao <shannon.zhao@linaro.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_arm.h"
-
-#include <stdint.h>
-
-/* Below typedefs are useful for the headers under acpi/ */
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef uint64_t u64;
-typedef int64_t s64;
-
-#include <acpi/acconfig.h>
-#include <acpi/actbl.h>
-
-#ifndef BITS_PER_LONG
-#ifdef _LP64
-#define BITS_PER_LONG 64
-#else
-#define BITS_PER_LONG 32
-#endif
-#endif
-#define ACPI_MACHINE_WIDTH BITS_PER_LONG
-#define COMPILER_DEPENDENT_INT64 int64_t
-#define COMPILER_DEPENDENT_UINT64 uint64_t
-
-#include <acpi/actypes.h>
-
-_hidden
-extern const unsigned char dsdt_anycpu_arm[];
-_hidden
-extern const int dsdt_anycpu_arm_len;
-
-#define ACPI_OEM_ID "Xen\0\0"
-#define ACPI_OEM_TABLE_ID "ARM\0\0\0\0"
-#define ACPI_ASL_COMPILER_ID "XL\0"
-
-enum {
- RSDP,
- XSDT,
- GTDT,
- MADT,
- FADT,
- DSDT,
- MAX_TABLE_NUMS,
-};
-
-struct acpitable {
- uint64_t addr;
- size_t size;
-};
-
-static int libxl__estimate_madt_size(libxl__gc *gc,
- const libxl_domain_build_info *info,
- size_t *size)
-{
- int rc = 0;
-
- switch (info->arch_arm.gic_version) {
- case LIBXL_GIC_VERSION_V2:
- *size = sizeof(struct acpi_table_madt) +
- ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus +
- sizeof(struct acpi_madt_generic_distributor);
- break;
- case LIBXL_GIC_VERSION_V3:
- *size = sizeof(struct acpi_table_madt) +
- ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus +
- sizeof(struct acpi_madt_generic_distributor) +
- sizeof(struct acpi_madt_generic_redistributor);
- break;
- default:
- LOG(ERROR, "Unknown GIC version");
- *size = 0;
- rc = ERROR_FAIL;
- break;
- }
-
- return rc;
-}
-
-int libxl__get_acpi_size(libxl__gc *gc,
- const libxl_domain_build_info *info,
- uint64_t *out)
-{
- uint64_t size;
- int rc = 0;
-
-
- rc = libxl__estimate_madt_size(gc, info, &size);
- if (rc < 0)
- goto out;
-
- *out = ROUNDUP(size, 3) +
- ROUNDUP(sizeof(struct acpi_table_rsdp), 3) +
- ROUNDUP(sizeof(struct acpi_table_xsdt), 3) +
- ROUNDUP(sizeof(struct acpi_table_gtdt), 3) +
- ROUNDUP(sizeof(struct acpi_table_fadt), 3) +
- ROUNDUP(sizeof(dsdt_anycpu_arm_len), 3);
-
-out:
- return rc;
-}
-
-static int libxl__allocate_acpi_tables(libxl__gc *gc,
- libxl_domain_build_info *info,
- struct xc_dom_image *dom,
- struct acpitable acpitables[])
-{
- int rc;
- size_t size;
-
- acpitables[RSDP].addr = GUEST_ACPI_BASE;
- acpitables[RSDP].size = sizeof(struct acpi_table_rsdp);
- dom->acpi_modules[0].length += ROUNDUP(acpitables[RSDP].size, 3);
-
- acpitables[XSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
- /*
- * Currently only 3 tables(GTDT, FADT, MADT) are pointed by XSDT. Alloc
- * entries for them.
- */
- acpitables[XSDT].size = sizeof(struct acpi_table_xsdt) +
- sizeof(uint64_t) * 2;
- dom->acpi_modules[0].length += ROUNDUP(acpitables[XSDT].size, 3);
-
- acpitables[GTDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
- acpitables[GTDT].size = sizeof(struct acpi_table_gtdt);
- dom->acpi_modules[0].length += ROUNDUP(acpitables[GTDT].size, 3);
-
- acpitables[MADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
-
- rc = libxl__estimate_madt_size(gc, info, &size);
- if (rc < 0)
- goto out;
-
- acpitables[MADT].size = size;
- dom->acpi_modules[0].length += ROUNDUP(acpitables[MADT].size, 3);
-
- acpitables[FADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
- acpitables[FADT].size = sizeof(struct acpi_table_fadt);
- dom->acpi_modules[0].length += ROUNDUP(acpitables[FADT].size, 3);
-
- acpitables[DSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
- acpitables[DSDT].size = dsdt_anycpu_arm_len;
- dom->acpi_modules[0].length += ROUNDUP(acpitables[DSDT].size, 3);
-
- assert(dom->acpi_modules[0].length <= GUEST_ACPI_SIZE);
- dom->acpi_modules[0].data = libxl__zalloc(gc, dom->acpi_modules[0].length);
-
- rc = 0;
-out:
- return rc;
-}
-
-static void calculate_checksum(void *table, uint32_t checksum_offset,
- uint32_t length)
-{
- uint8_t *p, sum = 0;
-
- p = table;
- p[checksum_offset] = 0;
-
- while (length--)
- sum = sum + *p++;
-
- p = table;
- p[checksum_offset] = -sum;
-}
-
-static void make_acpi_rsdp(libxl__gc *gc, struct xc_dom_image *dom,
- struct acpitable acpitables[])
-{
- uint64_t offset = acpitables[RSDP].addr - GUEST_ACPI_BASE;
- struct acpi_table_rsdp *rsdp = (void *)dom->acpi_modules[0].data + offset;
-
- memcpy(rsdp->signature, "RSD PTR ", sizeof(rsdp->signature));
- BUILD_BUG_ON(sizeof(ACPI_OEM_ID) != sizeof(rsdp->oem_id));
- memcpy(rsdp->oem_id, ACPI_OEM_ID, sizeof(rsdp->oem_id));
- rsdp->length = acpitables[RSDP].size;
- rsdp->revision = 0x02;
- rsdp->xsdt_physical_address = acpitables[XSDT].addr;
- calculate_checksum(rsdp,
- offsetof(struct acpi_table_rsdp, extended_checksum),
- acpitables[RSDP].size);
-}
-
-static void make_acpi_header(struct acpi_table_header *h, const char *sig,
- size_t len, uint8_t rev)
-{
- memcpy(h->signature, sig, 4);
- h->length = len;
- h->revision = rev;
- BUILD_BUG_ON(sizeof(ACPI_OEM_ID) != sizeof(h->oem_id));
- memcpy(h->oem_id, ACPI_OEM_ID, sizeof(h->oem_id));
- BUILD_BUG_ON(sizeof(ACPI_OEM_TABLE_ID) != sizeof(h->oem_table_id));
- memcpy(h->oem_table_id, ACPI_OEM_TABLE_ID, sizeof(h->oem_table_id));
- h->oem_revision = 0;
- BUILD_BUG_ON(sizeof(ACPI_ASL_COMPILER_ID) != sizeof(h->asl_compiler_id));
- memcpy(h->asl_compiler_id, ACPI_ASL_COMPILER_ID,
- sizeof(h->asl_compiler_id));
- h->asl_compiler_revision = 0;
- h->checksum = 0;
-}
-
-static void make_acpi_xsdt(libxl__gc *gc, struct xc_dom_image *dom,
- struct acpitable acpitables[])
-{
- uint64_t offset = acpitables[XSDT].addr - GUEST_ACPI_BASE;
- struct acpi_table_xsdt *xsdt = (void *)dom->acpi_modules[0].data + offset;
-
- xsdt->table_offset_entry[0] = acpitables[MADT].addr;
- xsdt->table_offset_entry[1] = acpitables[GTDT].addr;
- xsdt->table_offset_entry[2] = acpitables[FADT].addr;
- make_acpi_header(&xsdt->header, "XSDT", acpitables[XSDT].size, 1);
- calculate_checksum(xsdt, offsetof(struct acpi_table_header, checksum),
- acpitables[XSDT].size);
-}
-
-static void make_acpi_gtdt(libxl__gc *gc, struct xc_dom_image *dom,
- struct acpitable acpitables[])
-{
- uint64_t offset = acpitables[GTDT].addr - GUEST_ACPI_BASE;
- struct acpi_table_gtdt *gtdt = (void *)dom->acpi_modules[0].data + offset;
-
- gtdt->non_secure_el1_interrupt = GUEST_TIMER_PHYS_NS_PPI;
- gtdt->non_secure_el1_flags =
- (ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE)
- |(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY);
- gtdt->virtual_timer_interrupt = GUEST_TIMER_VIRT_PPI;
- gtdt->virtual_timer_flags =
- (ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE)
- |(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY);
-
- gtdt->counter_block_addresss = ~((uint64_t)0);
- gtdt->counter_read_block_address = ~((uint64_t)0);
-
- make_acpi_header(>dt->header, "GTDT", acpitables[GTDT].size, 2);
- calculate_checksum(gtdt, offsetof(struct acpi_table_header, checksum),
- acpitables[GTDT].size);
-}
-
-static void make_acpi_madt_gicc(void *table, int nr_cpus, uint64_t gicc_base)
-{
- int i;
- struct acpi_madt_generic_interrupt *gicc = table;
-
- for (i = 0; i < nr_cpus; i++) {
- gicc->header.type = ACPI_MADT_TYPE_GENERIC_INTERRUPT;
- gicc->header.length = ACPI_MADT_GICC_SIZE_v5;
- gicc->base_address = gicc_base;
- gicc->cpu_interface_number = i;
- gicc->arm_mpidr = libxl__compute_mpdir(i);
- gicc->uid = i;
- gicc->flags = ACPI_MADT_ENABLED;
- gicc = table + ACPI_MADT_GICC_SIZE_v5;
- }
-}
-
-static void make_acpi_madt_gicd(void *table, uint64_t gicd_base,
- uint8_t gic_version)
-{
- struct acpi_madt_generic_distributor *gicd = table;
-
- gicd->header.type = ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR;
- gicd->header.length = sizeof(*gicd);
- gicd->base_address = gicd_base;
- /* This version field has no meaning before ACPI 5.1 errata. */
- gicd->version = gic_version;
-}
-
-static void make_acpi_madt_gicr(void *table, uint64_t gicr_base,
- uint64_t gicr_size)
-{
- struct acpi_madt_generic_redistributor *gicr = table;
-
- gicr->header.type = ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR;
- gicr->header.length = sizeof(*gicr);
- gicr->base_address = gicr_base;
- gicr->length = gicr_size;
-}
-
-static int make_acpi_madt(libxl__gc *gc, struct xc_dom_image *dom,
- libxl_domain_build_info *info,
- struct acpitable acpitables[])
-{
- uint64_t offset = acpitables[MADT].addr - GUEST_ACPI_BASE;
- void *table = dom->acpi_modules[0].data + offset;
- struct acpi_table_madt *madt = table;
- int rc = 0;
-
- switch (info->arch_arm.gic_version) {
- case LIBXL_GIC_VERSION_V2:
- table += sizeof(struct acpi_table_madt);
- make_acpi_madt_gicc(table, info->max_vcpus, GUEST_GICC_BASE);
-
- table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus;
- make_acpi_madt_gicd(table, GUEST_GICD_BASE, ACPI_MADT_GIC_VERSION_V2);
- break;
- case LIBXL_GIC_VERSION_V3:
- table += sizeof(struct acpi_table_madt);
- make_acpi_madt_gicc(table, info->max_vcpus, 0);
-
- table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus;
- make_acpi_madt_gicd(table, GUEST_GICV3_GICD_BASE,
- ACPI_MADT_GIC_VERSION_V3);
-
- table += sizeof(struct acpi_madt_generic_distributor);
- make_acpi_madt_gicr(table, GUEST_GICV3_GICR0_BASE,
- GUEST_GICV3_GICR0_SIZE);
- break;
- default:
- LOG(ERROR, "Unknown GIC version");
- rc = ERROR_FAIL;
- goto out;
- }
-
- make_acpi_header(&madt->header, "APIC", acpitables[MADT].size, 3);
- calculate_checksum(madt, offsetof(struct acpi_table_header, checksum),
- acpitables[MADT].size);
-
-out:
- return rc;
-}
-
-static void make_acpi_fadt(libxl__gc *gc, struct xc_dom_image *dom,
- struct acpitable acpitables[])
-{
- uint64_t offset = acpitables[FADT].addr - GUEST_ACPI_BASE;
- struct acpi_table_fadt *fadt = (void *)dom->acpi_modules[0].data + offset;
-
- /* Hardware Reduced = 1 and use PSCI 0.2+ and with HVC */
- fadt->flags = ACPI_FADT_HW_REDUCED;
- fadt->arm_boot_flags = ACPI_FADT_PSCI_COMPLIANT | ACPI_FADT_PSCI_USE_HVC;
-
- /* ACPI v5.1 (fadt->revision.fadt->minor_revision) */
- fadt->minor_revision = 0x1;
- fadt->dsdt = acpitables[DSDT].addr;
-
- make_acpi_header(&fadt->header, "FACP", acpitables[FADT].size, 5);
- calculate_checksum(fadt, offsetof(struct acpi_table_header, checksum),
- acpitables[FADT].size);
-}
-
-static void make_acpi_dsdt(libxl__gc *gc, struct xc_dom_image *dom,
- struct acpitable acpitables[])
-{
- uint64_t offset = acpitables[DSDT].addr - GUEST_ACPI_BASE;
- void *dsdt = dom->acpi_modules[0].data + offset;
-
- memcpy(dsdt, dsdt_anycpu_arm, dsdt_anycpu_arm_len);
-}
-
-int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
- struct xc_dom_image *dom)
-{
- const libxl_version_info *vers;
- int rc = 0;
- struct acpitable acpitables[MAX_TABLE_NUMS];
-
- vers = libxl_get_version_info(CTX);
- if (vers == NULL) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- LOG(DEBUG, "constructing ACPI tables for Xen version %d.%d guest",
- vers->xen_version_major, vers->xen_version_minor);
-
- dom->acpi_modules[0].data = NULL;
- dom->acpi_modules[0].length = 0;
- dom->acpi_modules[0].guest_addr_out = GUEST_ACPI_BASE;
-
- rc = libxl__allocate_acpi_tables(gc, info, dom, acpitables);
- if (rc)
- goto out;
-
- make_acpi_rsdp(gc, dom, acpitables);
- make_acpi_xsdt(gc, dom, acpitables);
- make_acpi_gtdt(gc, dom, acpitables);
- rc = make_acpi_madt(gc, dom, info, acpitables);
- if (rc)
- goto out;
-
- make_acpi_fadt(gc, dom, acpitables);
- make_acpi_dsdt(gc, dom, acpitables);
-
-out:
- return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * ARM DomU ACPI generation
- *
- * Copyright (C) 2016 Linaro Ltd.
- *
- * Author: Shannon Zhao <shannon.zhao@linaro.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_arm.h"
-
-int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
- struct xc_dom_image *dom)
-{
- return ERROR_FAIL;
-}
-
-int libxl__get_acpi_size(libxl__gc *gc,
- const libxl_domain_build_info *info,
- uint64_t *out)
-{
- return ERROR_FAIL;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2010 Citrix Ltd.
- * Author Ian Campbell <ian.campbell@citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <termios.h>
-#ifdef HAVE_UTMP_H
-#include <utmp.h>
-#endif
-
-#ifdef INCLUDE_LIBUTIL_H
-#include INCLUDE_LIBUTIL_H
-#endif
-
-#include "libxl_internal.h"
-
-#define BOOTLOADER_BUF_OUT 65536
-#define BOOTLOADER_BUF_IN 4096
-
-static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op);
-static void bootloader_keystrokes_copyfail(libxl__egc *egc,
- libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
-static void bootloader_display_copyfail(libxl__egc *egc,
- libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
-static void bootloader_domaindeath(libxl__egc*, libxl__domaindeathcheck *dc,
- int rc);
-static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child,
- pid_t pid, int status);
-
-/*----- bootloader arguments -----*/
-
-static void bootloader_arg(libxl__bootloader_state *bl, const char *arg)
-{
- assert(bl->nargs < bl->argsspace);
- bl->args[bl->nargs++] = arg;
-}
-
-static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl,
- const char *bootloader_path)
-{
- const libxl_domain_build_info *info = bl->info;
-
- bl->argsspace = 9 + libxl_string_list_length(&info->bootloader_args);
-
- GCNEW_ARRAY(bl->args, bl->argsspace);
-
-#define ARG(arg) bootloader_arg(bl, (arg))
-
- ARG(bootloader_path);
-
- if (info->kernel)
- ARG(GCSPRINTF("--kernel=%s", info->kernel));
- if (info->ramdisk)
- ARG(GCSPRINTF("--ramdisk=%s", info->ramdisk));
- if (info->cmdline && *info->cmdline != '\0')
- ARG(GCSPRINTF("--args=%s", info->cmdline));
-
- ARG(GCSPRINTF("--output=%s", bl->outputpath));
- ARG("--output-format=simple0");
- ARG(GCSPRINTF("--output-directory=%s", bl->outputdir));
-
- if (info->bootloader_args) {
- char **p = info->bootloader_args;
- while (*p) {
- ARG(*p);
- p++;
- }
- }
-
- ARG(bl->dls.diskpath);
-
- /* Sentinel for execv */
- ARG(NULL);
-
-#undef ARG
-}
-
-/*----- synchronous subroutines -----*/
-
-static int setup_xenconsoled_pty(libxl__egc *egc, libxl__bootloader_state *bl,
- char *slave_path, size_t slave_path_len)
-{
- STATE_AO_GC(bl->ao);
- struct termios termattr;
- int r, rc;
- int slave = libxl__carefd_fd(bl->ptys[1].slave);
- int master = libxl__carefd_fd(bl->ptys[1].master);
-
- r = ttyname_r(slave, slave_path, slave_path_len);
- if (r == -1) {
- LOGED(ERROR, bl->domid, "ttyname_r failed");
- rc = ERROR_FAIL;
- goto out;
- }
-
- /*
- * On Solaris, the pty master side will get cranky if we try
- * to write to it while there is no slave. To work around this,
- * keep the slave descriptor open until we're done. Set it
- * to raw terminal parameters, otherwise it will echo back
- * characters, which will confuse the I/O loop below.
- * Furthermore, a raw master pty device has no terminal
- * semantics on Solaris, so don't try to set any attributes
- * for it.
- */
- tcgetattr(master, &termattr);
- cfmakeraw(&termattr);
- tcsetattr(master, TCSANOW, &termattr);
-
- return 0;
-
- out:
- return rc;
-}
-
-static const char *bootloader_result_command(libxl__gc *gc, const char *buf,
- const char *prefix, size_t prefixlen, uint32_t domid) {
- if (strncmp(buf, prefix, prefixlen))
- return 0;
-
- const char *rhs = buf + prefixlen;
- if (!CTYPE(isspace,*rhs))
- return 0;
-
- while (CTYPE(isspace,*rhs))
- rhs++;
-
- LOGD(DEBUG, domid, "bootloader output contained %s %s", prefix, rhs);
-
- return rhs;
-}
-
-static int parse_bootloader_result(libxl__egc *egc,
- libxl__bootloader_state *bl)
-{
- STATE_AO_GC(bl->ao);
- char buf[PATH_MAX*2];
- FILE *f = 0;
- int rc = ERROR_FAIL;
-
- f = fopen(bl->outputpath, "r");
- if (!f) {
- LOGED(ERROR, bl->domid, "open bootloader output file %s",
- bl->outputpath);
- goto out;
- }
-
- for (;;) {
- /* Read a nul-terminated "line" and put the result in
- * buf, and its length (not including the nul) in l */
- int l = 0, c;
- while ((c = getc(f)) != EOF && c != '\0') {
- if (l < sizeof(buf)-1)
- buf[l] = c;
- l++;
- }
- if (c == EOF) {
- if (ferror(f)) {
- LOGED(ERROR, bl->domid, "read bootloader output file %s",
- bl->outputpath);
- goto out;
- }
- if (!l)
- break;
- }
- if (l >= sizeof(buf)) {
- LOGD(WARN, bl->domid, "bootloader output contained"
- " overly long item `%.150s...'", buf);
- continue;
- }
- buf[l] = 0;
-
- const char *rhs;
-#define COMMAND(s) ((rhs = bootloader_result_command(gc, buf, s, sizeof(s)-1, bl->domid)))
-
- if (COMMAND("kernel")) {
- bl->kernel->path = libxl__strdup(gc, rhs);
- libxl__file_reference_map(bl->kernel);
- unlink(bl->kernel->path);
- } else if (COMMAND("ramdisk")) {
- bl->ramdisk->path = libxl__strdup(gc, rhs);
- libxl__file_reference_map(bl->ramdisk);
- unlink(bl->ramdisk->path);
- } else if (COMMAND("args")) {
- bl->cmdline = libxl__strdup(gc, rhs);
- } else if (l) {
- LOGD(WARN, bl->domid,
- "unexpected output from bootloader: `%s'", buf);
- }
- }
- rc = 0;
-
- out:
- if (f) fclose(f);
- return rc;
-}
-
-
-/*----- init and cleanup -----*/
-
-void libxl__bootloader_init(libxl__bootloader_state *bl)
-{
- assert(bl->ao);
- bl->rc = 0;
- bl->dls.diskpath = NULL;
- bl->openpty.ao = bl->ao;
- bl->dls.ao = bl->ao;
- bl->ptys[0].master = bl->ptys[0].slave = 0;
- bl->ptys[1].master = bl->ptys[1].slave = 0;
- libxl__ev_child_init(&bl->child);
- libxl__domaindeathcheck_init(&bl->deathcheck);
- bl->keystrokes.ao = bl->ao; libxl__datacopier_init(&bl->keystrokes);
- bl->display.ao = bl->ao; libxl__datacopier_init(&bl->display);
- bl->got_pollhup = 0;
-}
-
-static void bootloader_cleanup(libxl__egc *egc, libxl__bootloader_state *bl)
-{
- STATE_AO_GC(bl->ao);
- int i;
-
- if (bl->outputpath) libxl__remove_file(gc, bl->outputpath);
- if (bl->outputdir) libxl__remove_directory(gc, bl->outputdir);
-
- libxl__domaindeathcheck_stop(gc,&bl->deathcheck);
- libxl__datacopier_kill(&bl->keystrokes);
- libxl__datacopier_kill(&bl->display);
- for (i=0; i<2; i++) {
- libxl__carefd_close(bl->ptys[i].master);
- libxl__carefd_close(bl->ptys[i].slave);
- }
- if (bl->display.log) {
- fclose(bl->display.log);
- bl->display.log = NULL;
- }
-}
-
-static void bootloader_setpaths(libxl__gc *gc, libxl__bootloader_state *bl)
-{
- uint32_t domid = bl->domid;
- bl->outputdir = GCSPRINTF(XEN_RUN_DIR "/bootloader.%"PRIu32".d", domid);
- bl->outputpath = GCSPRINTF(XEN_RUN_DIR "/bootloader.%"PRIu32".out", domid);
-}
-
-/* Callbacks */
-
-static void bootloader_local_detached_cb(libxl__egc *egc,
- libxl__disk_local_state *dls,
- int rc);
-
-static void bootloader_callback(libxl__egc *egc, libxl__bootloader_state *bl,
- int rc)
-{
- if (!bl->rc)
- bl->rc = rc;
-
- bootloader_cleanup(egc, bl);
-
- bl->dls.callback = bootloader_local_detached_cb;
- libxl__device_disk_local_initiate_detach(egc, &bl->dls);
-}
-
-static void bootloader_local_detached_cb(libxl__egc *egc,
- libxl__disk_local_state *dls,
- int rc)
-{
- STATE_AO_GC(dls->ao);
- libxl__bootloader_state *bl = CONTAINER_OF(dls, *bl, dls);
-
- if (rc) {
- LOGD(ERROR, bl->domid,
- "unable to detach locally attached disk");
- if (!bl->rc)
- bl->rc = rc;
- }
-
- bl->callback(egc, bl, bl->rc);
-}
-
-/* might be called at any time, provided it's init'd */
-static void bootloader_stop(libxl__egc *egc,
- libxl__bootloader_state *bl, int rc)
-{
- STATE_AO_GC(bl->ao);
- int r;
-
- libxl__datacopier_kill(&bl->keystrokes);
- libxl__datacopier_kill(&bl->display);
- if (libxl__ev_child_inuse(&bl->child)) {
- r = kill(bl->child.pid, SIGTERM);
- if (r) LOGED(WARN, bl->domid, "%sfailed to kill bootloader [%lu]",
- rc ? "after failure, " : "", (unsigned long)bl->child.pid);
- }
- if (!bl->rc)
- bl->rc = rc;
-}
-
-/*----- main flow of control -----*/
-
-/* Callbacks */
-
-static void bootloader_disk_attached_cb(libxl__egc *egc,
- libxl__disk_local_state *dls,
- int rc);
-
-void libxl__bootloader_run(libxl__egc *egc, libxl__bootloader_state *bl)
-{
- STATE_AO_GC(bl->ao);
- const libxl_domain_build_info *info = bl->info;
- uint32_t domid = bl->domid;
- char *logfile_tmp = NULL;
- int rc, r;
-
- libxl__bootloader_init(bl);
-
- if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
- LOGD(DEBUG, domid, "not a PV/PVH domain, skipping bootloader");
- rc = 0;
- goto out_ok;
- }
-
- if (!info->bootloader) {
- LOGD(DEBUG, domid,
- "no bootloader configured, using user supplied kernel");
- bl->kernel->path = bl->info->kernel;
- bl->ramdisk->path = bl->info->ramdisk;
- bl->cmdline = bl->info->cmdline;
- rc = 0;
- goto out_ok;
- }
-
- if (!bl->disk) {
- LOGD(ERROR, domid, "cannot run bootloader with no boot disk");
- rc = ERROR_FAIL;
- goto out;
- }
-
- bootloader_setpaths(gc, bl);
-
- const char *logfile_leaf = GCSPRINTF("bootloader.%"PRIu32, domid);
- rc = libxl_create_logfile(CTX, logfile_leaf, &logfile_tmp);
- if (rc) goto out;
-
- /* Transfer ownership of log filename to bl and the gc */
- bl->logfile = logfile_tmp;
- libxl__ptr_add(gc, logfile_tmp);
- logfile_tmp = NULL;
-
- bl->display.log = fopen(bl->logfile, "a");
- if (!bl->display.log) {
- LOGED(ERROR, domid,
- "failed to create bootloader logfile %s", bl->logfile);
- rc = ERROR_FAIL;
- goto out;
- }
-
- for (;;) {
- r = mkdir(bl->outputdir, 0600);
- if (!r) break;
- if (errno == EINTR) continue;
- if (errno == EEXIST) break;
- LOGED(ERROR, domid,
- "failed to create bootloader dir %s", bl->outputdir);
- rc = ERROR_FAIL;
- goto out;
- }
-
- for (;;) {
- r = open(bl->outputpath, O_WRONLY|O_CREAT|O_TRUNC, 0600);
- if (r>=0) { close(r); break; }
- if (errno == EINTR) continue;
- LOGED(ERROR, domid,
- "failed to precreate bootloader output %s", bl->outputpath);
- rc = ERROR_FAIL;
- goto out;
- }
-
-
- /* This sets the state of the dls struct from Undefined to Idle */
- libxl__device_disk_local_init(&bl->dls);
- bl->dls.ao = ao;
- bl->dls.in_disk = bl->disk;
- bl->dls.blkdev_start = info->blkdev_start;
- bl->dls.callback = bootloader_disk_attached_cb;
- libxl__device_disk_local_initiate_attach(egc, &bl->dls);
- return;
-
- out:
- assert(rc);
- out_ok:
- free(logfile_tmp);
- bootloader_callback(egc, bl, rc);
-}
-
-static void bootloader_disk_attached_cb(libxl__egc *egc,
- libxl__disk_local_state *dls,
- int rc)
-{
- STATE_AO_GC(dls->ao);
- libxl__bootloader_state *bl = CONTAINER_OF(dls, *bl, dls);
- const libxl_domain_build_info *info = bl->info;
- const char *bootloader;
-
- if (rc) {
- LOGD(ERROR, bl->domid,
- "failed to attach local disk for bootloader execution");
- goto out;
- }
-
- LOGD(DEBUG, bl->domid,
- "Config bootloader value: %s", info->bootloader);
-
- if ( !strcmp(info->bootloader, "/usr/bin/pygrub") )
- LOGD(WARN, bl->domid,
- "bootloader='/usr/bin/pygrub' is deprecated; use " \
- "bootloader='pygrub' instead");
-
- bootloader = info->bootloader;
-
- /* If the full path is not specified, check in the libexec path */
- if ( bootloader[0] != '/' ) {
- const char *bltmp;
- struct stat st;
-
- bltmp = libxl__abs_path(gc, bootloader, libxl__private_bindir_path());
- /* Check to see if the file exists in this location; if not,
- * fall back to checking the path */
- LOGD(DEBUG, bl->domid,
- "Checking for bootloader in libexec path: %s", bltmp);
-
- if ( lstat(bltmp, &st) )
- LOGD(DEBUG, bl->domid,
- "%s doesn't exist, falling back to config path",
- bltmp);
- else
- bootloader = bltmp;
- }
-
- make_bootloader_args(gc, bl, bootloader);
-
- bl->openpty.ao = ao;
- bl->openpty.callback = bootloader_gotptys;
- bl->openpty.count = 2;
- bl->openpty.results = bl->ptys;
- rc = libxl__openptys(&bl->openpty, 0,0);
- if (rc) goto out;
-
- return;
-
- out:
- assert(rc);
- bootloader_callback(egc, bl, rc);
-}
-
-static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op)
-{
- libxl__bootloader_state *bl = CONTAINER_OF(op, *bl, openpty);
- STATE_AO_GC(bl->ao);
- int rc, r;
- char *const env[] = { "TERM", "vt100", NULL };
-
- if (bl->openpty.rc) {
- rc = bl->openpty.rc;
- goto out;
- }
-
- /*
- * We need to present the bootloader's tty as a pty slave that xenconsole
- * can access. Since the bootloader itself needs a pty slave,
- * we end up with a connection like this:
- *
- * xenconsole -- (slave pty1 master) <-> (master pty2 slave) -- bootloader
- *
- * where we copy characters between the two master fds, as well as
- * listening on the bootloader's fifo for the results.
- */
-
- char *dom_console_xs_path;
- char dom_console_slave_tty_path[PATH_MAX];
- rc = setup_xenconsoled_pty(egc, bl,
- &dom_console_slave_tty_path[0],
- sizeof(dom_console_slave_tty_path));
- if (rc) goto out;
-
- char *dompath = libxl__xs_get_dompath(gc, bl->domid);
- if (!dompath) { rc = ERROR_FAIL; goto out; }
-
- dom_console_xs_path = GCSPRINTF("%s/console/tty", dompath);
-
- rc = libxl__xs_printf(gc, XBT_NULL, dom_console_xs_path, "%s",
- dom_console_slave_tty_path);
- if (rc) {
- LOGED(ERROR, bl->domid, "xs write console path %s := %s failed",
- dom_console_xs_path, dom_console_slave_tty_path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- bl->deathcheck.what = "stopping bootloader";
- bl->deathcheck.domid = bl->domid;
- bl->deathcheck.callback = bootloader_domaindeath;
- rc = libxl__domaindeathcheck_start(ao, &bl->deathcheck);
- if (rc) goto out;
-
- if (bl->console_available)
- bl->console_available(egc, bl);
-
- int bootloader_master = libxl__carefd_fd(bl->ptys[0].master);
- int xenconsole_master = libxl__carefd_fd(bl->ptys[1].master);
-
- libxl_fd_set_nonblock(CTX, bootloader_master, 1);
- libxl_fd_set_nonblock(CTX, xenconsole_master, 1);
-
- bl->keystrokes.writefd = bl->display.readfd = bootloader_master;
- bl->keystrokes.writewhat = bl->display.readwhat = "bootloader pty";
-
- bl->keystrokes.readfd = bl->display.writefd = xenconsole_master;
- bl->keystrokes.readwhat = bl->display.writewhat = "xenconsole client pty";
-
- bl->keystrokes.ao = ao;
- bl->keystrokes.maxsz = BOOTLOADER_BUF_OUT;
- bl->keystrokes.bytes_to_read = -1;
- bl->keystrokes.copywhat =
- GCSPRINTF("bootloader input for domain %"PRIu32, bl->domid);
- bl->keystrokes.callback = bootloader_keystrokes_copyfail;
- bl->keystrokes.callback_pollhup = bootloader_keystrokes_copyfail;
- /* pollhup gets called with errnoval==-1 which is not otherwise
- * possible since errnos are nonnegative, so it's unambiguous */
- rc = libxl__datacopier_start(&bl->keystrokes);
- if (rc) goto out;
-
- bl->display.ao = ao;
- bl->display.maxsz = BOOTLOADER_BUF_IN;
- bl->display.bytes_to_read = -1;
- bl->display.copywhat =
- GCSPRINTF("bootloader output for domain %"PRIu32, bl->domid);
- bl->display.callback = bootloader_display_copyfail;
- bl->display.callback_pollhup = bootloader_display_copyfail;
- rc = libxl__datacopier_start(&bl->display);
- if (rc) goto out;
-
- LOGD(DEBUG, bl->domid, "executing bootloader: %s", bl->args[0]);
- for (const char **blarg = bl->args;
- *blarg;
- blarg++)
- LOGD(DEBUG, bl->domid, " bootloader arg: %s", *blarg);
-
- struct termios termattr;
-
- pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished);
- if (pid == -1) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (!pid) {
- /* child */
- r = login_tty(libxl__carefd_fd(bl->ptys[0].slave));
- if (r) { LOGED(ERROR, bl->domid, "login_tty failed"); exit(-1); }
- libxl__exec(gc, -1, -1, -1, bl->args[0], (char **) bl->args, env);
- }
-
- /* parent */
-
- /*
- * On Solaris, the master pty side does not have terminal semantics,
- * so don't try to set any attributes, as it will fail.
- */
-#if !defined(__sun__)
- tcgetattr(bootloader_master, &termattr);
- cfmakeraw(&termattr);
- tcsetattr(bootloader_master, TCSANOW, &termattr);
-#endif
-
- return;
-
- out:
- bootloader_callback(egc, bl, rc);
-}
-
-/* perhaps one of these will be called, but perhaps not */
-static void bootloader_copyfail(libxl__egc *egc, const char *which,
- libxl__bootloader_state *bl, int ondisplay,
- int rc, int onwrite, int errnoval)
-{
- STATE_AO_GC(bl->ao);
-
- if (errnoval==-1) {
- /* POLLHUP */
- if (!!ondisplay != !!onwrite) {
- rc = 0;
- bl->got_pollhup = 1;
- } else {
- LOGD(ERROR, bl->domid, "unexpected POLLHUP on %s", which);
- }
- } else if (!rc) {
- LOGD(ERROR, bl->domid, "unexpected eof copying %s", which);
- rc = ERROR_FAIL;
- }
-
- bootloader_stop(egc, bl, rc);
-}
-static void bootloader_keystrokes_copyfail(libxl__egc *egc,
- libxl__datacopier_state *dc, int rc, int onwrite, int errnoval)
-{
- libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, keystrokes);
- bootloader_copyfail(egc, "bootloader input", bl, 0, rc,onwrite,errnoval);
-}
-static void bootloader_display_copyfail(libxl__egc *egc,
- libxl__datacopier_state *dc, int rc, int onwrite, int errnoval)
-{
- libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, display);
- bootloader_copyfail(egc, "bootloader output", bl, 1, rc,onwrite,errnoval);
-}
-
-static void bootloader_domaindeath(libxl__egc *egc,
- libxl__domaindeathcheck *dc,
- int rc)
-{
- libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, deathcheck);
- bootloader_stop(egc, bl, rc);
-}
-
-static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child,
- pid_t pid, int status)
-{
- libxl__bootloader_state *bl = CONTAINER_OF(child, *bl, child);
- STATE_AO_GC(bl->ao);
- int rc;
-
- libxl__datacopier_kill(&bl->keystrokes);
- libxl__datacopier_kill(&bl->display);
-
- if (status) {
- if (bl->got_pollhup && WIFSIGNALED(status) && WTERMSIG(status)==SIGTERM)
- LOGD(ERROR, bl->domid, "got POLLHUP, sent SIGTERM");
- LOGD(ERROR, bl->domid,
- "bootloader failed - consult logfile %s", bl->logfile);
- libxl_report_child_exitstatus(CTX, XTL_ERROR, "bootloader",
- pid, status);
- rc = ERROR_FAIL;
- goto out;
- } else {
- LOGD(DEBUG, bl->domid, "bootloader completed");
- }
-
- if (bl->rc) {
- /* datacopier went wrong */
- rc = bl->rc;
- goto out;
- }
-
- rc = parse_bootloader_result(egc, bl);
- if (rc) goto out;
-
- rc = 0;
- LOGD(DEBUG, bl->domid, "bootloader execution successful");
-
- out:
- bootloader_callback(egc, bl, rc);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2014 FUJITSU LIMITED
- * Author: Yang Hongyang <yanghy@cn.fujitsu.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*----- setup() and teardown() -----*/
-
-/* callbacks */
-
-static void all_devices_setup_cb(libxl__egc *egc,
- libxl__multidev *multidev,
- int rc);
-static void device_setup_iterate(libxl__egc *egc,
- libxl__ao_device *aodev);
-static void devices_teardown_cb(libxl__egc *egc,
- libxl__multidev *multidev,
- int rc);
-
-/* checkpoint device setup and teardown */
-
-static libxl__checkpoint_device* checkpoint_device_init(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- libxl__device_kind kind,
- void *libxl_dev)
-{
- libxl__checkpoint_device *dev = NULL;
-
- STATE_AO_GC(cds->ao);
- GCNEW(dev);
- dev->backend_dev = libxl_dev;
- dev->kind = kind;
- dev->cds = cds;
-
- return dev;
-}
-
-static void checkpoint_devices_setup(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds);
-
-void libxl__checkpoint_devices_setup(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds)
-{
- int i;
-
- STATE_AO_GC(cds->ao);
-
- cds->num_devices = 0;
- cds->num_nics = 0;
- cds->num_disks = 0;
-
- if (cds->device_kind_flags & (1 << LIBXL__DEVICE_KIND_VIF))
- cds->nics = libxl__device_list(gc, &libxl__nic_devtype, cds->domid,
- &cds->num_nics);
-
- if (cds->device_kind_flags & (1 << LIBXL__DEVICE_KIND_VBD))
- cds->disks = libxl__device_list(gc, &libxl__disk_devtype, cds->domid,
- &cds->num_disks);
-
- if (cds->num_nics == 0 && cds->num_disks == 0)
- goto out;
-
- GCNEW_ARRAY(cds->devs, cds->num_nics + cds->num_disks);
-
- for (i = 0; i < cds->num_nics; i++) {
- cds->devs[cds->num_devices++] = checkpoint_device_init(egc, cds,
- LIBXL__DEVICE_KIND_VIF,
- &cds->nics[i]);
- }
-
- for (i = 0; i < cds->num_disks; i++) {
- cds->devs[cds->num_devices++] = checkpoint_device_init(egc, cds,
- LIBXL__DEVICE_KIND_VBD,
- &cds->disks[i]);
- }
-
- checkpoint_devices_setup(egc, cds);
-
- return;
-
-out:
- cds->callback(egc, cds, 0);
-}
-
-static void checkpoint_devices_setup(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds)
-{
- int i, rc;
-
- STATE_AO_GC(cds->ao);
-
- libxl__multidev_begin(ao, &cds->multidev);
- cds->multidev.callback = all_devices_setup_cb;
- for (i = 0; i < cds->num_devices; i++) {
- libxl__checkpoint_device *dev = cds->devs[i];
- dev->ops_index = -1;
- libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);
-
- dev->aodev.rc = ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED;
- dev->aodev.callback = device_setup_iterate;
- device_setup_iterate(egc,&dev->aodev);
- }
-
- rc = 0;
- libxl__multidev_prepared(egc, &cds->multidev, rc);
-}
-
-
-static void device_setup_iterate(libxl__egc *egc, libxl__ao_device *aodev)
-{
- libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
- EGC_GC;
-
- if (aodev->rc != ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED &&
- aodev->rc != ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH)
- /* might be success or disaster */
- goto out;
-
- do {
- dev->ops = dev->cds->ops[++dev->ops_index];
- if (!dev->ops) {
- libxl_device_nic * nic = NULL;
- libxl_device_disk * disk = NULL;
- uint32_t domid = INVALID_DOMID;
- int devid;
- if (dev->kind == LIBXL__DEVICE_KIND_VIF) {
- nic = (libxl_device_nic *)dev->backend_dev;
- domid = nic->backend_domid;
- devid = nic->devid;
- } else if (dev->kind == LIBXL__DEVICE_KIND_VBD) {
- disk = (libxl_device_disk *)dev->backend_dev;
- domid = disk->backend_domid;
- devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
- } else {
- LOGD(ERROR, domid, "device kind not handled by checkpoint: %s",
- libxl__device_kind_to_string(dev->kind));
- aodev->rc = ERROR_FAIL;
- goto out;
- }
- LOGD(ERROR, domid, "device not handled by checkpoint"
- " (device=%s:%"PRId32"/%"PRId32")",
- libxl__device_kind_to_string(dev->kind),
- domid, devid);
- aodev->rc = ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED;
- goto out;
- }
- } while (dev->ops->kind != dev->kind);
-
- /* found the next ops_index to try */
- assert(dev->aodev.callback == device_setup_iterate);
- dev->ops->setup(egc,dev);
- return;
-
- out:
- libxl__multidev_one_callback(egc,aodev);
-}
-
-static void all_devices_setup_cb(libxl__egc *egc,
- libxl__multidev *multidev,
- int rc)
-{
- STATE_AO_GC(multidev->ao);
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *const cds =
- CONTAINER_OF(multidev, *cds, multidev);
-
- cds->callback(egc, cds, rc);
-}
-
-void libxl__checkpoint_devices_teardown(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds)
-{
- int i;
- libxl__checkpoint_device *dev;
-
- STATE_AO_GC(cds->ao);
-
- libxl__multidev_begin(ao, &cds->multidev);
- cds->multidev.callback = devices_teardown_cb;
- for (i = 0; i < cds->num_devices; i++) {
- dev = cds->devs[i];
- if (!dev->ops || !dev->matched)
- continue;
-
- libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);
- dev->ops->teardown(egc,dev);
- }
-
- libxl__multidev_prepared(egc, &cds->multidev, 0);
-}
-
-static void devices_teardown_cb(libxl__egc *egc,
- libxl__multidev *multidev,
- int rc)
-{
- STATE_AO_GC(multidev->ao);
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *const cds =
- CONTAINER_OF(multidev, *cds, multidev);
-
- /* clean nic */
- libxl__device_list_free(&libxl__nic_devtype, cds->nics, cds->num_nics);
- cds->nics = NULL;
- cds->num_nics = 0;
-
- /* clean disk */
- libxl__device_list_free(&libxl__disk_devtype, cds->disks, cds->num_disks);
- cds->disks = NULL;
- cds->num_disks = 0;
-
- cds->callback(egc, cds, rc);
-}
-
-/*----- checkpointing APIs -----*/
-
-/* callbacks */
-
-static void devices_checkpoint_cb(libxl__egc *egc,
- libxl__multidev *multidev,
- int rc);
-
-/* API implementations */
-
-#define define_checkpoint_api(api) \
-void libxl__checkpoint_devices_##api(libxl__egc *egc, \
- libxl__checkpoint_devices_state *cds) \
-{ \
- int i; \
- libxl__checkpoint_device *dev; \
- \
- STATE_AO_GC(cds->ao); \
- \
- libxl__multidev_begin(ao, &cds->multidev); \
- cds->multidev.callback = devices_checkpoint_cb; \
- for (i = 0; i < cds->num_devices; i++) { \
- dev = cds->devs[i]; \
- if (!dev->matched || !dev->ops->api) \
- continue; \
- libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);\
- dev->ops->api(egc,dev); \
- } \
- \
- libxl__multidev_prepared(egc, &cds->multidev, 0); \
-}
-
-define_checkpoint_api(postsuspend);
-
-define_checkpoint_api(preresume);
-
-define_checkpoint_api(commit);
-
-static void devices_checkpoint_cb(libxl__egc *egc,
- libxl__multidev *multidev,
- int rc)
-{
- STATE_AO_GC(multidev->ao);
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *const cds =
- CONTAINER_OF(multidev, *cds, multidev);
-
- cds->callback(egc, cds, rc);
-}
+++ /dev/null
-/*
- * Copyright (C) 2016 FUJITSU LIMITED
- * Author: Wen Congyang <wency@cn.fujitsu.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#ifndef LIBXL_COLO_H
-#define LIBXL_COLO_H
-
-#include "libxl_internal.h"
-
-/* Maximum time(5s) to wait for colo proxy checkpoit */
-#define COLO_PROXY_CHECKPOINT_TIMEOUT 5000000
-
-#define ASYNC_CALL(egc, ao, child, param, func, callback) do { \
- int pid = -1; \
- STATE_AO_GC(ao); \
- \
- pid = libxl__ev_child_fork(gc, child, callback); \
- if (pid == -1) { \
- LOGD(ERROR, ao->domid, "unable to fork"); \
- goto out; \
- } \
- \
- if (!pid) { \
- /* child */ \
- func(param); \
- /* notreached */ \
- abort(); \
- } \
- \
- return; \
-out: \
- callback(egc, child, -1, 1); \
-} while (0)
-
-enum {
- LIBXL_COLO_SETUPED,
- LIBXL_COLO_SUSPENDED,
- LIBXL_COLO_RESUMED,
-};
-
-struct libxl__colo_device_nic {
- int devid;
- const char *vif;
-};
-
-struct libxl__colo_qdisk {
- bool setuped;
-};
-
-struct libxl__colo_proxy_state {
- /* set by caller of colo_proxy_setup */
- struct libxl__ao *ao;
-
- int sock_fd;
- int index;
- /*
- * Private, True means use userspace colo proxy
- * False means use kernel colo proxy.
- */
- bool is_userspace_proxy;
- const char *checkpoint_host;
- const char *checkpoint_port;
-};
-
-struct libxl__colo_save_state {
- int send_fd;
- int recv_fd;
- char *colo_proxy_script;
-
- /* private */
- libxl__stream_read_state srs;
- void (*callback)(libxl__egc *, libxl__colo_save_state *, int);
- bool svm_running;
- bool paused;
-
- /* private, used by qdisk block replication */
- bool qdisk_used;
- bool qdisk_setuped;
-
- /* private, used by colo-proxy */
- libxl__colo_proxy_state cps;
- libxl__ev_child child;
-};
-
-
-typedef void libxl__colo_callback(struct libxl__egc *egc,
- libxl__colo_restore_state *crs, int rc);
-
-struct libxl__colo_restore_state {
- /* must set by caller of libxl__colo_(setup|teardown) */
- struct libxl__ao *ao;
- uint32_t domid;
- int send_back_fd;
- int recv_fd;
- int hvm;
- libxl__colo_callback *callback;
- char *colo_proxy_script;
-
- /* private, colo restore checkpoint state */
- libxl__domain_create_cb *saved_cb;
- void *crcs;
-
- /* private, used by qdisk block replication */
- bool qdisk_used;
- bool qdisk_setuped;
- const char *host;
- const char *port;
-
- /* private, used by colo-proxy */
- libxl__colo_proxy_state cps;
-};
-
-int init_subkind_qdisk(struct libxl__checkpoint_devices_state *cds);
-
-void cleanup_subkind_qdisk(struct libxl__checkpoint_devices_state *cds);
-
-int init_subkind_colo_nic(struct libxl__checkpoint_devices_state *cds);
-
-void cleanup_subkind_colo_nic(struct libxl__checkpoint_devices_state *cds);
-
-extern void libxl__colo_restore_setup(struct libxl__egc *egc,
- libxl__colo_restore_state *crs);
-extern void libxl__colo_restore_teardown(struct libxl__egc *egc, void *dcs_void,
- int ret, int retval, int errnoval);
-extern void libxl__colo_save_setup(struct libxl__egc *egc,
- struct libxl__colo_save_state *css);
-extern void libxl__colo_save_teardown(struct libxl__egc *egc,
- struct libxl__colo_save_state *css,
- int rc);
-extern int colo_proxy_setup(libxl__colo_proxy_state *cps);
-extern void colo_proxy_teardown(libxl__colo_proxy_state *cps);
-extern void colo_proxy_preresume(libxl__colo_proxy_state *cps);
-extern void colo_proxy_postresume(libxl__colo_proxy_state *cps);
-extern int colo_proxy_checkpoint(libxl__colo_proxy_state *cps,
- unsigned int timeout_us);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2016 FUJITSU LIMITED
- * Author: Wen Congyang <wency@cn.fujitsu.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-enum {
- primary,
- secondary,
-};
-
-/* ========== init() and cleanup() ========== */
-
-int init_subkind_colo_nic(libxl__checkpoint_devices_state *cds)
-{
- return 0;
-}
-
-void cleanup_subkind_colo_nic(libxl__checkpoint_devices_state *cds)
-{
-}
-
-/* ========== helper functions ========== */
-
-static void colo_save_setup_script_cb(libxl__egc *egc,
- libxl__async_exec_state *aes,
- int rc, int status);
-static void colo_save_teardown_script_cb(libxl__egc *egc,
- libxl__async_exec_state *aes,
- int rc, int status);
-
-/*
- * If the device has a vifname, then use that instead of
- * the vifX.Y format.
- * it must ONLY be used for remus because if driver domains
- * were in use it would constitute a security vulnerability.
- */
-static const char *get_vifname(libxl__checkpoint_device *dev,
- const libxl_device_nic *nic)
-{
- const char *vifname = NULL;
- const char *path;
- int rc;
-
- STATE_AO_GC(dev->cds->ao);
-
- /* Convenience aliases */
- const uint32_t domid = dev->cds->domid;
-
- path = GCSPRINTF("%s/vifname",
- libxl__domain_device_backend_path(gc, 0,
- domid, nic->devid, LIBXL__DEVICE_KIND_VIF));
- rc = libxl__xs_read_checked(gc, XBT_NULL, path, &vifname);
- if (!rc && !vifname) {
- vifname = libxl__device_nic_devname(gc, domid,
- nic->devid,
- nic->nictype);
- }
-
- return vifname;
-}
-
-/*
- * the script needs the following env & args
- * $vifname
- * $forwarddev
- * $mode(primary/secondary)
- * $index
- * $bridge
- * setup/teardown as command line arg.
- */
-static void setup_async_exec(libxl__checkpoint_device *dev, char *op,
- libxl__colo_proxy_state *cps, int side,
- char *colo_proxy_script)
-{
- int arraysize, nr = 0;
- char **env = NULL, **args = NULL;
- libxl__colo_device_nic *colo_nic = dev->concrete_data;
- libxl__checkpoint_devices_state *cds = dev->cds;
- libxl__async_exec_state *aes = &dev->aodev.aes;
- const libxl_device_nic *nic = dev->backend_dev;
-
- STATE_AO_GC(cds->ao);
-
- /* Convenience aliases */
- const char *const vif = colo_nic->vif;
-
- arraysize = 11;
- GCNEW_ARRAY(env, arraysize);
- env[nr++] = "vifname";
- env[nr++] = libxl__strdup(gc, vif);
- env[nr++] = "forwarddev";
- env[nr++] = libxl__strdup(gc, nic->coloft_forwarddev);
- env[nr++] = "mode";
- if (side == primary)
- env[nr++] = "primary";
- else
- env[nr++] = "secondary";
- env[nr++] = "index";
- env[nr++] = GCSPRINTF("%d", cps->index);
- env[nr++] = "bridge";
- env[nr++] = libxl__strdup(gc, nic->bridge);
- env[nr++] = NULL;
- assert(nr == arraysize);
-
- arraysize = 3; nr = 0;
- GCNEW_ARRAY(args, arraysize);
- args[nr++] = colo_proxy_script;
- args[nr++] = op;
- args[nr++] = NULL;
- assert(nr == arraysize);
-
- aes->ao = dev->cds->ao;
- aes->what = GCSPRINTF("%s %s", args[0], args[1]);
- aes->env = env;
- aes->args = args;
- aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
- aes->stdfds[0] = -1;
- aes->stdfds[1] = -1;
- aes->stdfds[2] = -1;
-
- if (!strcmp(op, "teardown"))
- aes->callback = colo_save_teardown_script_cb;
- else
- aes->callback = colo_save_setup_script_cb;
-}
-
-/* ========== setup() and teardown() ========== */
-
-static void colo_nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev,
- libxl__colo_proxy_state *cps, int side,
- char *colo_proxy_script)
-{
- int rc;
- libxl__colo_device_nic *colo_nic;
- const libxl_device_nic *nic = dev->backend_dev;
-
- STATE_AO_GC(dev->cds->ao);
-
- /*
- * thers's no subkind of nic devices, so nic ops is always matched
- * with nic devices, we begin to setup the nic device
- */
- dev->matched = 1;
-
- if (!nic->coloft_forwarddev) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- GCNEW(colo_nic);
- dev->concrete_data = colo_nic;
- colo_nic->devid = nic->devid;
- colo_nic->vif = get_vifname(dev, nic);
- if (!colo_nic->vif) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- setup_async_exec(dev, "setup", cps, side, colo_proxy_script);
- rc = libxl__async_exec_start(&dev->aodev.aes);
- if (rc)
- goto out;
-
- return;
-
-out:
- dev->aodev.rc = rc;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-static void colo_save_setup_script_cb(libxl__egc *egc,
- libxl__async_exec_state *aes,
- int rc, int status)
-{
- libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
- libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
- libxl__colo_device_nic *colo_nic = dev->concrete_data;
- libxl__checkpoint_devices_state *cds = dev->cds;
- const char *out_path_base, *hotplug_error = NULL;
-
- EGC_GC;
-
- /* Convenience aliases */
- const uint32_t domid = cds->domid;
- const int devid = colo_nic->devid;
- const char *const vif = colo_nic->vif;
-
- if (status && !rc)
- rc = ERROR_FAIL;
- if (rc)
- goto out;
-
- out_path_base = GCSPRINTF("%s/colo_proxy/%d",
- libxl__xs_libxl_path(gc, domid), devid);
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/hotplug-error", out_path_base),
- &hotplug_error);
- if (rc)
- goto out;
-
- if (hotplug_error) {
- LOGD(ERROR, domid, "colo_proxy script %s setup failed for vif %s: %s",
- aes->args[0], vif, hotplug_error);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-
-out:
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-static void colo_nic_teardown(libxl__egc *egc, libxl__checkpoint_device *dev,
- libxl__colo_proxy_state *cps, int side,
- char *colo_proxy_script)
-{
- int rc;
- libxl__colo_device_nic *colo_nic = dev->concrete_data;
-
- if (!colo_nic || !colo_nic->vif) {
- /* colo nic has not yet been set up, just return */
- rc = 0;
- goto out;
- }
-
- setup_async_exec(dev, "teardown", cps, side, colo_proxy_script);
-
- rc = libxl__async_exec_start(&dev->aodev.aes);
- if (rc)
- goto out;
-
- return;
-
-out:
- dev->aodev.rc = rc;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-static void colo_save_teardown_script_cb(libxl__egc *egc,
- libxl__async_exec_state *aes,
- int rc, int status)
-{
- libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
-
- if (status && !rc)
- rc = ERROR_FAIL;
- else
- rc = 0;
-
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-/* ======== primary ======== */
-
-static void colo_nic_save_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
- libxl__colo_save_state *css = dev->cds->concrete_data;
-
- colo_nic_setup(egc, dev, &css->cps, primary, css->colo_proxy_script);
-}
-
-static void colo_nic_save_teardown(libxl__egc *egc,
- libxl__checkpoint_device *dev)
-{
- libxl__colo_save_state *css = dev->cds->concrete_data;
-
- colo_nic_teardown(egc, dev, &css->cps, primary, css->colo_proxy_script);
-}
-
-const libxl__checkpoint_device_instance_ops colo_save_device_nic = {
- .kind = LIBXL__DEVICE_KIND_VIF,
- .setup = colo_nic_save_setup,
- .teardown = colo_nic_save_teardown,
-};
-
-/* ======== secondary ======== */
-
-static void colo_nic_restore_setup(libxl__egc *egc,
- libxl__checkpoint_device *dev)
-{
- libxl__colo_restore_state *crs = dev->cds->concrete_data;
-
- colo_nic_setup(egc, dev, &crs->cps, secondary, crs->colo_proxy_script);
-}
-
-static void colo_nic_restore_teardown(libxl__egc *egc,
- libxl__checkpoint_device *dev)
-{
- libxl__colo_restore_state *crs = dev->cds->concrete_data;
-
- colo_nic_teardown(egc, dev, &crs->cps, secondary, crs->colo_proxy_script);
-}
-
-const libxl__checkpoint_device_instance_ops colo_restore_device_nic = {
- .kind = LIBXL__DEVICE_KIND_VIF,
- .setup = colo_nic_restore_setup,
- .teardown = colo_nic_restore_teardown,
-};
+++ /dev/null
-/*
- * Copyright (C) 2016 FUJITSU LIMITED
- * Author: Yang Hongyang <hongyang.yang@easystack.cn>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#include <netlink/netlink.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-/* Consistent with the new COLO netlink channel in kernel side */
-#define NETLINK_COLO 28
-#define COLO_DEFAULT_WAIT_TIME 500000
-
-enum colo_netlink_op {
- COLO_QUERY_CHECKPOINT = (NLMSG_MIN_TYPE + 1),
- COLO_CHECKPOINT,
- COLO_FAILOVER,
- COLO_PROXY_INIT,
- COLO_PROXY_RESET, /* UNUSED, will be used for continuous FT */
-};
-
-/* ========= colo-proxy: helper functions ========== */
-
-static int colo_proxy_send(libxl__colo_proxy_state *cps, uint8_t *buff,
- uint64_t size, int type)
-{
- struct sockaddr_nl sa;
- struct nlmsghdr msg;
- struct iovec iov;
- struct msghdr mh;
- int ret;
-
- STATE_AO_GC(cps->ao);
-
- memset(&sa, 0, sizeof(sa));
- sa.nl_family = AF_NETLINK;
- sa.nl_pid = 0;
- sa.nl_groups = 0;
-
- msg.nlmsg_len = NLMSG_SPACE(0);
- msg.nlmsg_flags = NLM_F_REQUEST;
- if (type == COLO_PROXY_INIT)
- msg.nlmsg_flags |= NLM_F_ACK;
- msg.nlmsg_seq = 0;
- msg.nlmsg_pid = cps->index;
- msg.nlmsg_type = type;
-
- iov.iov_base = &msg;
- iov.iov_len = msg.nlmsg_len;
-
- mh.msg_name = &sa;
- mh.msg_namelen = sizeof(sa);
- mh.msg_iov = &iov;
- mh.msg_iovlen = 1;
- mh.msg_control = NULL;
- mh.msg_controllen = 0;
- mh.msg_flags = 0;
-
- ret = sendmsg(cps->sock_fd, &mh, 0);
- if (ret <= 0) {
- LOGD(ERROR, ao->domid, "can't send msg to kernel by netlink: %s",
- strerror(errno));
- }
-
- return ret;
-}
-
-static int colo_userspace_proxy_send(libxl__colo_proxy_state *cps,
- uint8_t *buff,
- uint32_t size)
-{
- int ret = 0;
- uint32_t len = 0;
-
- len = htonl(size);
- ret = send(cps->sock_fd, (uint8_t *)&len, sizeof(len), 0);
- if (ret != sizeof(len)) {
- goto err;
- }
-
- ret = send(cps->sock_fd, (uint8_t *)buff, size, 0);
- if (ret != size) {
- goto err;
- }
-
-err:
- return ret;
-}
-
-static int colo_userspace_proxy_recv(libxl__colo_proxy_state *cps,
- char *buff,
- unsigned int timeout_us)
-{
- struct timeval tv;
- int ret;
- uint32_t len = 0;
- uint32_t size = 0;
-
- STATE_AO_GC(cps->ao);
-
- if (timeout_us) {
- tv.tv_sec = timeout_us / 1000000;
- tv.tv_usec = timeout_us % 1000000;
- ret = setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv,
- sizeof(tv));
- if (ret < 0) {
- LOGD(ERROR, ao->domid,
- "colo_userspace_proxy_recv setsockopt error: %s",
- strerror(errno));
- }
- }
-
- ret = recv(cps->sock_fd, (uint8_t *)&len, sizeof(len), 0);
- if (ret < 0) {
- goto err;
- }
-
- size = ntohl(len);
- ret = recv(cps->sock_fd, buff, size, 0);
-
-err:
- return ret;
-}
-
-/* error: return -1, otherwise return 0 */
-static int64_t colo_proxy_recv(libxl__colo_proxy_state *cps, uint8_t **buff,
- unsigned int timeout_us)
-{
- struct sockaddr_nl sa;
- struct iovec iov;
- struct msghdr mh = {
- .msg_name = &sa,
- .msg_namelen = sizeof(sa),
- .msg_iov = &iov,
- .msg_iovlen = 1,
- };
- struct timeval tv;
- uint32_t size = 16384;
- int64_t len = 0;
- int ret;
-
- STATE_AO_GC(cps->ao);
- uint8_t *tmp = libxl__malloc(NOGC, size);
-
- if (timeout_us) {
- tv.tv_sec = timeout_us / 1000000;
- tv.tv_usec = timeout_us % 1000000;
- setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
- }
-
- iov.iov_base = tmp;
- iov.iov_len = size;
-next:
- ret = recvmsg(cps->sock_fd, &mh, 0);
- if (ret <= 0) {
- if (errno != EAGAIN && errno != EWOULDBLOCK)
- LOGED(ERROR, ao->domid, "can't recv msg from kernel by netlink");
- goto err;
- }
-
- len += ret;
- if (mh.msg_flags & MSG_TRUNC) {
- size += 16384;
- tmp = libxl__realloc(NOGC, tmp, size);
- iov.iov_base = tmp + len;
- iov.iov_len = size - len;
- goto next;
- }
-
- *buff = tmp;
- ret = len;
- goto out;
-
-err:
- free(tmp);
- *buff = NULL;
-
-out:
- if (timeout_us) {
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
- }
- return ret;
-}
-
-/* ========= colo-proxy: setup and teardown ========== */
-
-int colo_proxy_setup(libxl__colo_proxy_state *cps)
-{
- int skfd = 0;
- struct sockaddr_nl sa;
- struct nlmsghdr *h;
- int i = 1;
- int ret = ERROR_FAIL;
- uint8_t *buff = NULL;
- int64_t size;
-
- STATE_AO_GC(cps->ao);
-
- /* If enable userspace proxy mode, we don't need setup kernel proxy */
- if (cps->is_userspace_proxy) {
- struct sockaddr_in addr;
- int port;
- char recvbuff[1024];
- const char sendbuf[] = "COLO_USERSPACE_PROXY_INIT";
-
- memset(&addr, 0, sizeof(addr));
- port = atoi(cps->checkpoint_port);
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = inet_addr(cps->checkpoint_host);
-
- skfd = socket(AF_INET, SOCK_STREAM, 0);
- if (skfd < 0) {
- LOGD(ERROR, ao->domid, "can not create a TCP socket: %s",
- strerror(errno));
- goto out;
- }
-
- cps->sock_fd = skfd;
-
- if (connect(skfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
- LOGD(ERROR, ao->domid, "connect error");
- goto out;
- }
-
- ret = colo_userspace_proxy_send(cps, (uint8_t *)sendbuf, strlen(sendbuf));
- if (ret < 0)
- goto out;
-
- ret = colo_userspace_proxy_recv(cps, recvbuff, COLO_DEFAULT_WAIT_TIME);
- if (ret < 0) {
- LOGD(ERROR, ao->domid, "Can't recv msg from qemu colo-compare: %s",
- strerror(errno));
- goto out;
- }
-
- return 0;
- }
-
- skfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_COLO);
- if (skfd < 0) {
- LOGD(ERROR, ao->domid, "can not create a netlink socket: %s", strerror(errno));
- goto out;
- }
- cps->sock_fd = skfd;
- memset(&sa, 0, sizeof(sa));
- sa.nl_family = AF_NETLINK;
- sa.nl_groups = 0;
-retry:
- sa.nl_pid = i++;
-
- if (i > 10) {
- LOGD(ERROR, ao->domid, "netlink bind error");
- goto out;
- }
-
- ret = bind(skfd, (struct sockaddr *)&sa, sizeof(sa));
- if (ret < 0 && errno == EADDRINUSE) {
- LOGD(ERROR, ao->domid, "colo index %d has already in used", sa.nl_pid);
- goto retry;
- } else if (ret < 0) {
- LOGD(ERROR, ao->domid, "netlink bind error");
- goto out;
- }
-
- cps->index = sa.nl_pid;
- ret = colo_proxy_send(cps, NULL, 0, COLO_PROXY_INIT);
- if (ret < 0)
- goto out;
-
- /* receive ack */
- size = colo_proxy_recv(cps, &buff, 500000);
- if (size < 0) {
- LOGD(ERROR, ao->domid, "Can't recv msg from kernel by netlink: %s",
- strerror(errno));
- goto out;
- }
-
- if (size) {
- h = (struct nlmsghdr *)buff;
- if (h->nlmsg_type == NLMSG_ERROR) {
- /* ack's type is NLMSG_ERROR */
- struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
-
- if (size - sizeof(*h) < sizeof(*err)) {
- LOGD(ERROR, ao->domid, "NLMSG_LENGTH is too short");
- goto out;
- }
-
- if (err->error) {
- LOGD(ERROR, ao->domid, "NLMSG_ERROR contains error %d", err->error);
- goto out;
- }
- }
- }
-
- ret = 0;
-
-out:
- free(buff);
- if (ret) {
- close(cps->sock_fd);
- cps->sock_fd = -1;
- }
- return ret;
-}
-
-void colo_proxy_teardown(libxl__colo_proxy_state *cps)
-{
- /*
- * If enable userspace proxy mode,
- * we don't need teardown kernel proxy
- */
- if (cps->is_userspace_proxy)
- return;
-
- if (cps->sock_fd >= 0) {
- close(cps->sock_fd);
- cps->sock_fd = -1;
- }
-}
-
-/* ========= colo-proxy: preresume, postresume and checkpoint ========== */
-
-void colo_proxy_preresume(libxl__colo_proxy_state *cps)
-{
- /*
- * If enable userspace proxy mode,
- * we don't need preresume kernel proxy
- */
- if (cps->is_userspace_proxy) {
- const char sendbuf[] = "COLO_CHECKPOINT";
- colo_userspace_proxy_send(cps,
- (uint8_t *)sendbuf,
- strlen(sendbuf));
- return;
- }
-
- colo_proxy_send(cps, NULL, 0, COLO_CHECKPOINT);
- /* TODO: need to handle if the call fails... */
-}
-
-void colo_proxy_postresume(libxl__colo_proxy_state *cps)
-{
- /* nothing to do... */
-}
-
-typedef struct colo_msg {
- bool is_checkpoint;
-} colo_msg;
-
-/*
- * Return value:
- * -1: error
- * 0: no checkpoint event is received before timeout
- * 1: do checkpoint
- */
-int colo_proxy_checkpoint(libxl__colo_proxy_state *cps,
- unsigned int timeout_us)
-{
- uint8_t *buff = NULL;
- int64_t size;
- struct nlmsghdr *h;
- struct colo_msg *m;
- int ret = -1;
- char recvbuff[1024];
-
- STATE_AO_GC(cps->ao);
-
- /*
- * Enable userspace proxy to periodical checkpoint mode,
- * sleeping temporarily for colo userspace proxy mode.
- * then we will use socket recv instead of this usleep.
- * In other words, we use socket communicate with Qemu
- * Proxy part(colo-compare), for example, notify checkpoint
- * event.
- */
- if (cps->is_userspace_proxy) {
- ret = colo_userspace_proxy_recv(cps, recvbuff, timeout_us);
- if (ret <= 0) {
- ret = 0;
- goto out;
- }
-
- if (!strcmp(recvbuff, "DO_CHECKPOINT")) {
- ret = 1;
- } else {
- LOGD(ERROR, ao->domid, "receive qemu colo-compare checkpoint error");
- ret = 0;
- }
- goto out;
- }
-
- size = colo_proxy_recv(cps, &buff, timeout_us);
-
- /* timeout, return no checkpoint message. */
- if (size <= 0) {
- ret = 0;
- goto out;
- }
-
- h = (struct nlmsghdr *) buff;
-
- if (h->nlmsg_type == NLMSG_ERROR) {
- LOGD(ERROR, ao->domid, "receive NLMSG_ERROR");
- goto out;
- }
-
- if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*m))) {
- LOGD(ERROR, ao->domid, "NLMSG_LENGTH is too short");
- goto out;
- }
-
- m = NLMSG_DATA(h);
-
- ret = m->is_checkpoint ? 1 : 0;
-
-out:
- free(buff);
- return ret;
-}
+++ /dev/null
-/*
- * Copyright (C) 2016 FUJITSU LIMITED
- * Author: Wen Congyang <wency@cn.fujitsu.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/* ========== init() and cleanup() ========== */
-
-int init_subkind_qdisk(libxl__checkpoint_devices_state *cds)
-{
- /*
- * We don't know if we use qemu block replication, so
- * we cannot start block replication here.
- */
- return 0;
-}
-
-void cleanup_subkind_qdisk(libxl__checkpoint_devices_state *cds)
-{
-}
-
-/* ========== setup() and teardown() ========== */
-
-static void colo_qdisk_setup(libxl__egc *egc, libxl__checkpoint_device *dev,
- bool primary)
-{
- const libxl_device_disk *disk = dev->backend_dev;
- int ret, rc = 0;
- libxl__colo_qdisk *colo_qdisk = NULL;
- char port[32];
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *const cds = dev->cds;
- const char *host = disk->colo_host;
- const char *export_name = disk->colo_export;
- const int domid = cds->domid;
-
- STATE_AO_GC(dev->cds->ao);
-
- if (disk->backend != LIBXL_DISK_BACKEND_QDISK ||
- !libxl_defbool_val(disk->colo_enable) ||
- !host || !export_name || (disk->colo_port <= 0) ||
- !disk->active_disk || !disk->hidden_disk) {
- rc = ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH;
- goto out;
- }
-
- dev->matched = true;
-
- GCNEW(colo_qdisk);
- dev->concrete_data = colo_qdisk;
-
- if (primary) {
- libxl__colo_save_state *css = cds->concrete_data;
-
- css->qdisk_used = true;
- /* NBD server is not ready, so we cannot start block replication now */
- goto out;
- } else {
- libxl__colo_restore_state *crs = cds->concrete_data;
- sprintf(port, "%d", disk->colo_port);
-
- if (!crs->qdisk_used) {
- /* start nbd server */
- ret = libxl__qmp_nbd_server_start(gc, domid, host, port);
- if (ret) {
- rc = ERROR_FAIL;
- goto out;
- }
- crs->host = host;
- crs->port = port;
- } else {
- if (strcmp(crs->host, host) || strcmp(crs->port, port)) {
- LOGD(ERROR, domid, "The host and port of all disks must be the same");
- rc = ERROR_FAIL;
- goto out;
- }
- }
-
- crs->qdisk_used = true;
-
- ret = libxl__qmp_nbd_server_add(gc, domid, export_name);
- if (ret)
- rc = ERROR_FAIL;
-
- colo_qdisk->setuped = true;
- }
-
-out:
- dev->aodev.rc = rc;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-static void colo_qdisk_teardown(libxl__egc *egc, libxl__checkpoint_device *dev,
- bool primary)
-{
- int ret, rc = 0;
- const libxl__colo_qdisk *colo_qdisk = dev->concrete_data;
- const libxl_device_disk *disk = dev->backend_dev;
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *const cds = dev->cds;
- const int domid = cds->domid;
- const char *export_name = disk->colo_export;
-
- EGC_GC;
-
- if (primary) {
- if (!colo_qdisk->setuped)
- goto out;
-
- /*
- * There is no way to get the child name, but we know it is children.1
- */
- ret = libxl__qmp_x_blockdev_change(gc, domid, export_name,
- "children.1", NULL);
- if (ret)
- rc = ERROR_FAIL;
- } else {
- libxl__colo_restore_state *crs = cds->concrete_data;
-
- if (crs->qdisk_used) {
- ret = libxl__qmp_nbd_server_stop(gc, domid);
- if (ret)
- rc = ERROR_FAIL;
- }
- }
-
-out:
- dev->aodev.rc = rc;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-/* ========== checkpointing APIs ========== */
-
-static void colo_qdisk_save_preresume(libxl__egc *egc,
- libxl__checkpoint_device *dev)
-{
- libxl__colo_qdisk *colo_qdisk = dev->concrete_data;
- const libxl_device_disk *disk = dev->backend_dev;
- int ret, rc = 0;
- char *node = NULL;
- char *cmd = NULL;
-
- /* Convenience aliases */
- const int domid = dev->cds->domid;
- const char *host = disk->colo_host;
- int port = disk->colo_port;
- const char *export_name = disk->colo_export;
-
- EGC_GC;
-
- if (colo_qdisk->setuped)
- goto out;
-
- /* qmp command doesn't support the driver "nbd" */
- node = GCSPRINTF("colo_node%d",
- libxl__device_disk_dev_number(disk->vdev, NULL, NULL));
- cmd = GCSPRINTF("drive_add -n buddy driver=replication,mode=primary,"
- "file.driver=nbd,file.host=%s,file.port=%d,"
- "file.export=%s,node-name=%s",
- host, port, export_name, node);
- ret = libxl__qmp_hmp(gc, domid, cmd, NULL);
- if (ret)
- rc = ERROR_FAIL;
-
- ret = libxl__qmp_x_blockdev_change(gc, domid, export_name, NULL, node);
- if (ret)
- rc = ERROR_FAIL;
-
- colo_qdisk->setuped = true;
-
-out:
- dev->aodev.rc = rc;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-/* ======== primary ======== */
-
-static void colo_qdisk_save_setup(libxl__egc *egc,
- libxl__checkpoint_device *dev)
-{
- colo_qdisk_setup(egc, dev, true);
-}
-
-static void colo_qdisk_save_teardown(libxl__egc *egc,
- libxl__checkpoint_device *dev)
-{
- colo_qdisk_teardown(egc, dev, true);
-}
-
-const libxl__checkpoint_device_instance_ops colo_save_device_qdisk = {
- .kind = LIBXL__DEVICE_KIND_VBD,
- .setup = colo_qdisk_save_setup,
- .teardown = colo_qdisk_save_teardown,
- .preresume = colo_qdisk_save_preresume,
-};
-
-/* ======== secondary ======== */
-
-static void colo_qdisk_restore_setup(libxl__egc *egc,
- libxl__checkpoint_device *dev)
-{
- colo_qdisk_setup(egc, dev, false);
-}
-
-static void colo_qdisk_restore_teardown(libxl__egc *egc,
- libxl__checkpoint_device *dev)
-{
- colo_qdisk_teardown(egc, dev, false);
-}
-
-const libxl__checkpoint_device_instance_ops colo_restore_device_qdisk = {
- .kind = LIBXL__DEVICE_KIND_VBD,
- .setup = colo_qdisk_restore_setup,
- .teardown = colo_qdisk_restore_teardown,
-};
+++ /dev/null
-/*
- * Copyright (C) 2016 FUJITSU LIMITED
- * Author: Wen Congyang <wency@cn.fujitsu.com>
- * Yang Hongyang <hongyang.yang@easystack.cn>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-#include "libxl_sr_stream_format.h"
-
-typedef struct libxl__colo_restore_checkpoint_state libxl__colo_restore_checkpoint_state;
-struct libxl__colo_restore_checkpoint_state {
- libxl__domain_suspend_state dsps;
- libxl__logdirty_switch lds;
- libxl__colo_restore_state *crs;
- libxl__stream_write_state sws;
- int status;
- bool preresume;
- /* used for teardown */
- int teardown_devices;
- int saved_rc;
- char *state_file;
-
- void (*callback)(libxl__egc *,
- libxl__colo_restore_checkpoint_state *,
- int);
-};
-
-extern const libxl__checkpoint_device_instance_ops colo_restore_device_nic;
-extern const libxl__checkpoint_device_instance_ops colo_restore_device_qdisk;
-
-static const libxl__checkpoint_device_instance_ops *colo_restore_ops[] = {
- &colo_restore_device_nic,
- &colo_restore_device_qdisk,
- NULL,
-};
-
-/* ===================== colo: common functions ===================== */
-
-static void colo_enable_logdirty(libxl__colo_restore_state *crs, libxl__egc *egc)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
- libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
- /* Convenience aliases */
- const uint32_t domid = crs->domid;
- libxl__logdirty_switch *const lds = &crcs->lds;
-
- EGC_GC;
-
- /* we need to know which pages are dirty to restore the guest */
- if (xc_shadow_control(CTX->xch, domid,
- XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY,
- NULL, 0, NULL, 0, NULL) < 0) {
- LOGD(ERROR, domid, "cannot enable secondary vm's logdirty");
- lds->callback(egc, lds, ERROR_FAIL);
- return;
- }
-
- if (crs->hvm) {
- libxl__domain_common_switch_qemu_logdirty(egc, domid, 1, lds);
- return;
- }
-
- lds->callback(egc, lds, 0);
-}
-
-static void colo_disable_logdirty(libxl__colo_restore_state *crs,
- libxl__egc *egc)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
- libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
- /* Convenience aliases */
- const uint32_t domid = crs->domid;
- libxl__logdirty_switch *const lds = &crcs->lds;
-
- EGC_GC;
-
- /* we need to know which pages are dirty to restore the guest */
- if (xc_shadow_control(CTX->xch, domid, XEN_DOMCTL_SHADOW_OP_OFF,
- NULL, 0, NULL, 0, NULL) < 0)
- LOGD(WARN, domid, "cannot disable secondary vm's logdirty");
-
- if (crs->hvm) {
- libxl__domain_common_switch_qemu_logdirty(egc, domid, 0, lds);
- return;
- }
-
- lds->callback(egc, lds, 0);
-}
-
-static void colo_resume_vm(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs,
- int restore_device_model)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
- int rc;
-
- /* Convenience aliases */
- libxl__colo_restore_state *const crs = crcs->crs;
-
- EGC_GC;
-
- if (!crs->saved_cb) {
- /* TODO: sync mmu for hvm? */
- if (restore_device_model) {
- rc = libxl__qmp_restore(gc, crs->domid, crcs->state_file);
- if (rc) {
- LOGD(ERROR, crs->domid,
- "cannot restore device model for secondary vm");
- crcs->callback(egc, crcs, rc);
- return;
- }
- }
- rc = libxl__domain_resume_deprecated(gc, crs->domid, 0);
- if (rc)
- LOGD(ERROR, crs->domid, "cannot resume secondary vm");
-
- crcs->callback(egc, crcs, rc);
- return;
- }
-
- libxl__xc_domain_restore_done(egc, dcs, 0, 0, 0);
-
- return;
-}
-
-static int init_device_subkind(libxl__checkpoint_devices_state *cds)
-{
- /* init device subkind-specific state in the libxl ctx */
- int rc;
- STATE_AO_GC(cds->ao);
-
- rc = init_subkind_colo_nic(cds);
- if (rc) goto out;
-
- rc = init_subkind_qdisk(cds);
- if (rc) {
- cleanup_subkind_colo_nic(cds);
- goto out;
- }
-
- rc = 0;
-out:
- return rc;
-}
-
-static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds)
-{
- /* cleanup device subkind-specific state in the libxl ctx */
- STATE_AO_GC(cds->ao);
-
- cleanup_subkind_colo_nic(cds);
- cleanup_subkind_qdisk(cds);
-}
-
-/* ================ colo: setup restore environment ================ */
-
-static void libxl__colo_domain_create_cb(libxl__egc *egc,
- libxl__domain_create_state *dcs,
- int rc, uint32_t domid);
-
-static int init_dsps(libxl__domain_suspend_state *dsps)
-{
- int rc = ERROR_FAIL;
- libxl_domain_type type;
-
- STATE_AO_GC(dsps->ao);
-
- libxl__xswait_init(&dsps->pvcontrol);
- libxl__ev_evtchn_init(&dsps->guest_evtchn);
- libxl__ev_xswatch_init(&dsps->guest_watch);
- libxl__ev_time_init(&dsps->guest_timeout);
-
- type = libxl__domain_type(gc, dsps->domid);
- if (type == LIBXL_DOMAIN_TYPE_INVALID)
- goto out;
-
- dsps->type = type;
-
- dsps->guest_evtchn.port = -1;
- dsps->guest_evtchn_lockfd = -1;
- dsps->guest_responded = 0;
- dsps->dm_savefile = libxl__device_model_savefile(gc, dsps->domid);
-
- /* Secondary vm is not created, so we cannot get evtchn port */
-
- rc = 0;
-
-out:
- return rc;
-}
-
-/*
- * checkpoint callbacks are called in the following order:
- * 1. resume
- * 2. wait checkpoint
- * 3. suspend
- * 4. checkpoint
- */
-static void libxl__colo_restore_domain_resume_callback(void *data);
-static void libxl__colo_restore_domain_wait_checkpoint_callback(void *data);
-static void libxl__colo_restore_domain_suspend_callback(void *data);
-static void libxl__colo_restore_domain_checkpoint_callback(void *data);
-
-void libxl__colo_restore_setup(libxl__egc *egc,
- libxl__colo_restore_state *crs)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
- libxl__colo_restore_checkpoint_state *crcs;
- int rc = ERROR_FAIL;
-
- /* Convenience aliases */
- libxl__srm_restore_autogen_callbacks *const callbacks =
- &dcs->srs.shs.callbacks.restore.a;
- const int domid = crs->domid;
-
- STATE_AO_GC(crs->ao);
-
- GCNEW(crcs);
- crs->crcs = crcs;
- crcs->crs = crs;
- crs->qdisk_setuped = false;
- crs->qdisk_used = false;
- if (dcs->colo_proxy_script)
- crs->colo_proxy_script = libxl__strdup(gc, dcs->colo_proxy_script);
- else
- crs->colo_proxy_script = GCSPRINTF("%s/colo-proxy-setup",
- libxl__xen_script_dir_path());
-
- /* setup dsps */
- crcs->dsps.ao = ao;
- crcs->dsps.domid = domid;
- if (init_dsps(&crcs->dsps))
- goto out;
-
- callbacks->postcopy = libxl__colo_restore_domain_resume_callback;
- callbacks->wait_checkpoint = libxl__colo_restore_domain_wait_checkpoint_callback;
- callbacks->suspend = libxl__colo_restore_domain_suspend_callback;
- callbacks->checkpoint = libxl__colo_restore_domain_checkpoint_callback;
-
- /*
- * Secondary vm is running in colo mode, so we need to call
- * libxl__xc_domain_restore_done() to create secondary vm.
- * But we will exit in domain_create_cb(). So replace the
- * callback here.
- */
- crs->saved_cb = dcs->callback;
- dcs->callback = libxl__colo_domain_create_cb;
- crcs->state_file = GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", domid);
- crcs->status = LIBXL_COLO_SETUPED;
-
- libxl__logdirty_init(&crcs->lds);
- crcs->lds.ao = ao;
-
- crcs->sws.fd = crs->send_back_fd;
- crcs->sws.ao = ao;
- crcs->sws.back_channel = true;
-
- dcs->cds.concrete_data = crs;
-
- libxl__stream_write_start(egc, &crcs->sws);
-
- rc = 0;
-
-out:
- crs->callback(egc, crs, rc);
- return;
-}
-
-static void libxl__colo_domain_create_cb(libxl__egc *egc,
- libxl__domain_create_state *dcs,
- int rc, uint32_t domid)
-{
- libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
-
- crcs->callback(egc, crcs, rc);
-}
-
-/* ================ colo: teardown restore environment ================ */
-
-static void colo_restore_teardown_devices_done(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds, int rc);
-static void do_failover(libxl__egc *egc, libxl__colo_restore_state *crs);
-static void do_failover_done(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state* crcs,
- int rc);
-static void colo_disable_logdirty_done(libxl__egc *egc,
- libxl__logdirty_switch *lds,
- int rc);
-static void libxl__colo_restore_teardown_done(libxl__egc *egc,
- libxl__colo_restore_state *crs,
- int rc);
-
-void libxl__colo_restore_teardown(libxl__egc *egc, void *dcs_void,
- int ret, int retval, int errnoval)
-{
- libxl__domain_create_state *dcs = dcs_void;
- libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
- int rc = 1;
-
- /* convenience aliases */
- libxl__colo_restore_state *const crs = &dcs->crs;
- EGC_GC;
-
- if (ret == 0 && retval == 0)
- rc = 0;
-
- LOGD(INFO, crs->domid, "%s", rc ? "colo fails" : "failover");
-
- libxl__stream_write_abort(egc, &crcs->sws, 1);
- if (crs->saved_cb) {
- /* crcs->status is LIBXL_COLO_SETUPED */
- dcs->srs.completion_callback = NULL;
- }
- libxl__xc_domain_restore_done(egc, dcs, ret, retval, errnoval);
-
- if (crs->qdisk_setuped) {
- libxl__qmp_stop_replication(gc, crs->domid, false);
- crs->qdisk_setuped = false;
- }
-
- crcs->saved_rc = rc;
- if (!crcs->teardown_devices) {
- colo_restore_teardown_devices_done(egc, &dcs->cds, 0);
- return;
- }
-
- dcs->cds.callback = colo_restore_teardown_devices_done;
- libxl__checkpoint_devices_teardown(egc, &dcs->cds);
-}
-
-static void colo_restore_teardown_devices_done(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds, int rc)
-{
- libxl__colo_restore_state *crs = cds->concrete_data;
- libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
- libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-
- EGC_GC;
-
- if (rc)
- LOGD(ERROR, cds->domid, "COLO: failed to teardown device for guest,"
- " rc %d", rc);
-
- if (crcs->teardown_devices)
- cleanup_device_subkind(cds);
-
- colo_proxy_teardown(&crs->cps);
-
- rc = crcs->saved_rc;
- if (!rc) {
- crcs->callback = do_failover_done;
- do_failover(egc, crs);
- return;
- }
-
- libxl__colo_restore_teardown_done(egc, crs, rc);
-}
-
-static void do_failover(libxl__egc *egc, libxl__colo_restore_state *crs)
-{
- libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
- /* Convenience aliases */
- const int status = crcs->status;
- libxl__logdirty_switch *const lds = &crcs->lds;
-
- EGC_GC;
-
- switch(status) {
- case LIBXL_COLO_SETUPED:
- /*
- * We will come here only when reading emulator xenstore data or
- * emulator context fails, and libxl__xc_domain_restore_done()
- * is not called. In this case, the migration is not finished,
- * so we cannot do failover.
- */
- LOGD(ERROR, crs->domid, "migration fails");
- crcs->callback(egc, crcs, ERROR_FAIL);
- return;
- case LIBXL_COLO_SUSPENDED:
- case LIBXL_COLO_RESUMED:
- /* disable logdirty first */
- lds->callback = colo_disable_logdirty_done;
- colo_disable_logdirty(crs, egc);
- return;
- default:
- LOGD(ERROR, crs->domid, "invalid status: %d", status);
- crcs->callback(egc, crcs, ERROR_FAIL);
- }
-}
-
-static void do_failover_done(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state* crcs,
- int rc)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-
- /* Convenience aliases */
- libxl__colo_restore_state *const crs = crcs->crs;
-
- EGC_GC;
-
- if (rc)
- LOGD(ERROR, crs->domid, "cannot do failover");
-
- libxl__colo_restore_teardown_done(egc, crs, rc);
-}
-
-static void colo_disable_logdirty_done(libxl__egc *egc,
- libxl__logdirty_switch *lds,
- int rc)
-{
- libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
-
- EGC_GC;
-
- if (rc)
- LOGD(WARN, crcs->crs->domid, "cannot disable logdirty");
-
- if (crcs->status == LIBXL_COLO_SUSPENDED) {
- /*
- * failover when reading state from master, so no need to
- * call libxl__qmp_restore().
- */
- colo_resume_vm(egc, crcs, 0);
- return;
- }
-
- /* If we cannot disable logdirty, we still can do failover */
- crcs->callback(egc, crcs, 0);
-}
-
-static void libxl__colo_restore_teardown_done(libxl__egc *egc,
- libxl__colo_restore_state *crs,
- int rc)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
- EGC_GC;
-
- /* convenience aliases */
- const int domid = crs->domid;
- const libxl_ctx *const ctx = libxl__gc_owner(gc);
- xc_interface *const xch = ctx->xch;
-
- if (!rc)
- /* failover, no need to destroy the secondary vm */
- goto out;
-
- xc_domain_destroy(xch, domid);
-
-out:
- if (crs->saved_cb) {
- dcs->callback = crs->saved_cb;
- crs->saved_cb = NULL;
- }
-
- dcs->callback(egc, dcs, rc, crs->domid);
-}
-
-static void colo_common_write_stream_done(libxl__egc *egc,
- libxl__stream_write_state *stream,
- int rc);
-static void colo_common_read_stream_done(libxl__egc *egc,
- libxl__stream_read_state *stream,
- int rc);
-
-/* ======================== colo: checkpoint ======================= */
-
-/*
- * Do the following things when resuming secondary vm:
- * 1. read emulator xenstore data
- * 2. read emulator context
- * 3. REC_TYPE_CHECKPOINT_END
- */
-static void libxl__colo_restore_domain_checkpoint_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
- libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
- libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
-
- crcs->callback = NULL;
- dcs->srs.checkpoint_callback = colo_common_read_stream_done;
- libxl__stream_read_start_checkpoint(shs->egc, &dcs->srs);
-}
-
-/* ===================== colo: resume secondary vm ===================== */
-
-/*
- * Do the following things when resuming secondary vm the first time:
- * 1. resume secondary vm
- * 2. enable log dirty
- * 3. setup checkpoint devices
- * 4. write CHECKPOINT_SVM_READY
- * 5. unpause secondary vm
- * 6. write CHECKPOINT_SVM_RESUMED
- *
- * Do the following things when resuming secondary vm:
- * 1. write CHECKPOINT_SVM_READY
- * 2. resume secondary vm
- * 3. write CHECKPOINT_SVM_RESUMED
- */
-static void colo_send_svm_ready(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs);
-static void colo_send_svm_ready_done(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs,
- int rc);
-static void colo_restore_preresume_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-static void colo_restore_resume_vm(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs);
-static void colo_resume_vm_done(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs,
- int rc);
-static void colo_write_svm_resumed(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs);
-static void colo_enable_logdirty_done(libxl__egc *egc,
- libxl__logdirty_switch *lds,
- int retval);
-static void colo_reenable_logdirty(libxl__egc *egc,
- libxl__logdirty_switch *lds,
- int rc);
-static void colo_reenable_logdirty_done(libxl__egc *egc,
- libxl__logdirty_switch *lds,
- int rc);
-static void colo_setup_checkpoint_devices(libxl__egc *egc,
- libxl__colo_restore_state *crs);
-static void colo_restore_setup_cds_done(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-static void colo_unpause_svm(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs);
-
-static void libxl__colo_restore_domain_resume_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
- libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
- libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
-
- if (crcs->teardown_devices)
- colo_send_svm_ready(shs->egc, crcs);
- else
- colo_restore_resume_vm(shs->egc, crcs);
-}
-
-static void colo_send_svm_ready(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs)
-{
- libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_READY };
-
- crcs->callback = colo_send_svm_ready_done;
- crcs->sws.checkpoint_callback = colo_common_write_stream_done;
- libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs);
-}
-
-static void colo_send_svm_ready_done(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs,
- int rc)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *cds = &dcs->cds;
-
- if (!crcs->preresume) {
- crcs->preresume = true;
- colo_unpause_svm(egc, crcs);
- return;
- }
-
- cds->callback = colo_restore_preresume_cb;
- libxl__checkpoint_devices_preresume(egc, cds);
-}
-
-static void colo_restore_preresume_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__colo_restore_state *crs = cds->concrete_data;
- libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
- libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
- /* Convenience aliases */
- libxl__save_helper_state *const shs = &dcs->srs.shs;
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, crs->domid, "preresume fails");
- goto out;
- }
-
- if (crs->qdisk_setuped) {
- if (libxl__qmp_colo_do_checkpoint(gc, crs->domid)) {
- LOGD(ERROR, crs->domid, "doing checkpoint fails");
- goto out;
- }
- }
-
- if (!crs->cps.is_userspace_proxy)
- colo_proxy_preresume(&crs->cps);
-
- colo_restore_resume_vm(egc, crcs);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-static void colo_restore_resume_vm(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs)
-{
-
- crcs->callback = colo_resume_vm_done;
- colo_resume_vm(egc, crcs, 1);
-}
-
-static void colo_resume_vm_done(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs,
- int rc)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-
- /* Convenience aliases */
- libxl__colo_restore_state *const crs = crcs->crs;
- libxl__logdirty_switch *const lds = &crcs->lds;
- libxl__save_helper_state *const shs = &dcs->srs.shs;
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, crs->domid, "cannot resume secondary vm");
- goto out;
- }
-
- crcs->status = LIBXL_COLO_RESUMED;
-
- colo_proxy_postresume(&crs->cps);
-
- /* avoid calling stream->completion_callback() more than once */
- if (crs->saved_cb) {
- dcs->callback = crs->saved_cb;
- crs->saved_cb = NULL;
-
- dcs->srs.completion_callback = NULL;
-
- lds->callback = colo_enable_logdirty_done;
- colo_enable_logdirty(crs, egc);
- return;
- }
-
- colo_write_svm_resumed(egc, crcs);
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-static void colo_write_svm_resumed(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs)
-{
- libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_RESUMED };
-
- crcs->callback = NULL;
- crcs->sws.checkpoint_callback = colo_common_write_stream_done;
- libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs);
-}
-
-static void colo_enable_logdirty_done(libxl__egc *egc,
- libxl__logdirty_switch *lds,
- int rc)
-{
- libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
-
- /* Convenience aliases */
- libxl__colo_restore_state *const crs = crcs->crs;
-
- EGC_GC;
-
- if (rc) {
- /*
- * log-dirty already enabled? There's no test op,
- * so attempt to disable then reenable it
- */
- lds->callback = colo_reenable_logdirty;
- colo_disable_logdirty(crs, egc);
- return;
- }
-
- colo_setup_checkpoint_devices(egc, crs);
-}
-
-static void colo_reenable_logdirty(libxl__egc *egc,
- libxl__logdirty_switch *lds,
- int rc)
-{
- libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
- libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-
- /* Convenience aliases */
- libxl__colo_restore_state *const crs = crcs->crs;
- libxl__save_helper_state *const shs = &dcs->srs.shs;
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, crs->domid, "cannot enable logdirty");
- goto out;
- }
-
- lds->callback = colo_reenable_logdirty_done;
- colo_enable_logdirty(crs, egc);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-static void colo_reenable_logdirty_done(libxl__egc *egc,
- libxl__logdirty_switch *lds,
- int rc)
-{
- libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
- libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-
- /* Convenience aliases */
- libxl__save_helper_state *const shs = &dcs->srs.shs;
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, crcs->crs->domid, "cannot enable logdirty");
- goto out;
- }
-
- colo_setup_checkpoint_devices(egc, crcs->crs);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-/*
- * We cannot setup checkpoint devices in libxl__colo_restore_setup(),
- * because the guest is not ready.
- */
-static void colo_setup_checkpoint_devices(libxl__egc *egc,
- libxl__colo_restore_state *crs)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
- libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *cds = &dcs->cds;
- libxl__save_helper_state *const shs = &dcs->srs.shs;
-
- STATE_AO_GC(crs->ao);
-
- if (crs->cps.is_userspace_proxy)
- cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VBD);
- else
- cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VIF) |
- (1 << LIBXL__DEVICE_KIND_VBD);
-
- cds->callback = colo_restore_setup_cds_done;
- cds->ao = ao;
- cds->domid = crs->domid;
- cds->ops = colo_restore_ops;
-
- crs->cps.ao = ao;
- if (!crs->cps.is_userspace_proxy) {
- if (colo_proxy_setup(&crs->cps)) {
- LOGD(ERROR, cds->domid, "COLO: failed to setup colo proxy for guest");
- goto out;
- }
- }
-
- if (init_device_subkind(cds))
- goto out;
-
- crcs->teardown_devices = 1;
-
- libxl__checkpoint_devices_setup(egc, cds);
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-static void colo_restore_setup_cds_done(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__colo_restore_state *crs = cds->concrete_data;
- libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
- libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
- /* Convenience aliases */
- libxl__save_helper_state *const shs = &dcs->srs.shs;
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, cds->domid, "COLO: failed to setup device for guest");
- goto out;
- }
-
- if (crs->qdisk_used && !crs->qdisk_setuped) {
- if (libxl__qmp_start_replication(gc, crs->domid, false)) {
- LOGD(ERROR, cds->domid, "starting replication fails");
- goto out;
- }
- crs->qdisk_setuped = true;
- }
-
- colo_send_svm_ready(egc, crcs);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-static void colo_unpause_svm(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
- int rc;
-
- /* Convenience aliases */
- const uint32_t domid = crcs->crs->domid;
- libxl__save_helper_state *const shs = &dcs->srs.shs;
-
- EGC_GC;
-
- /* We have enabled secondary vm's logdirty, so we can unpause it now */
- rc = libxl__domain_unpause_deprecated(gc, domid);
- if (rc) {
- LOGD(ERROR, domid, "cannot unpause secondary vm");
- goto out;
- }
-
- colo_write_svm_resumed(egc, crcs);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-/* ===================== colo: wait new checkpoint ===================== */
-
-static void colo_restore_commit_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-static void colo_stream_read_done(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs,
- int real_size);
-
-static void libxl__colo_restore_domain_wait_checkpoint_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
- libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *cds = &dcs->cds;
-
- cds->callback = colo_restore_commit_cb;
- libxl__checkpoint_devices_commit(shs->egc, cds);
-}
-
-static void colo_restore_commit_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__colo_restore_state *crs = cds->concrete_data;
- libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
- libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, crs->domid, "commit fails");
- goto out;
- }
-
- crcs->callback = colo_stream_read_done;
- dcs->srs.checkpoint_callback = colo_common_read_stream_done;
- libxl__stream_read_checkpoint_state(egc, &dcs->srs);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, 0);
-}
-
-static void colo_stream_read_done(libxl__egc *egc,
- libxl__colo_restore_checkpoint_state *crcs,
- int id)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
- int ok = 0;
-
- EGC_GC;
-
- if (id != CHECKPOINT_NEW) {
- LOGD(ERROR, crcs->crs->domid, "invalid section: %d", id);
- goto out;
- }
-
- ok = 1;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok);
-}
-
-/* ===================== colo: suspend secondary vm ===================== */
-
-/*
- * Do the following things when resuming secondary vm:
- * 1. suspend secondary vm
- * 2. send CHECKPOINT_SVM_SUSPENDED
- */
-static void colo_suspend_vm_done(libxl__egc *egc,
- libxl__domain_suspend_state *dsps,
- int ok);
-static void colo_restore_postsuspend_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-
-static void libxl__colo_restore_domain_suspend_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
- libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
- libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
-
- STATE_AO_GC(dcs->ao);
-
- /* Convenience aliases */
- libxl__domain_suspend_state *const dsps = &crcs->dsps;
-
- /* suspend secondary vm */
- dsps->callback_common_done = colo_suspend_vm_done;
-
- libxl__domain_suspend(shs->egc, dsps);
-}
-
-static void colo_suspend_vm_done(libxl__egc *egc,
- libxl__domain_suspend_state *dsps,
- int rc)
-{
- libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(dsps, *crcs, dsps);
- libxl__colo_restore_state *crs = crcs->crs;
- libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *cds = &dcs->cds;
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, crs->domid, "cannot suspend secondary vm");
- goto out;
- }
-
- crcs->status = LIBXL_COLO_SUSPENDED;
-
- if (libxl__qmp_query_xen_replication_status(gc, crs->domid)) {
- LOGD(ERROR, crs->domid, "replication error occurs when secondary vm is running");
- goto out;
- }
-
- cds->callback = colo_restore_postsuspend_cb;
- libxl__checkpoint_devices_postsuspend(egc, cds);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, 0);
-}
-
-static void colo_restore_postsuspend_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__colo_restore_state *crs = cds->concrete_data;
- libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
- libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
- libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_SUSPENDED };
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, crs->domid, "postsuspend fails");
- goto out;
- }
-
- crcs->callback = NULL;
- crcs->sws.checkpoint_callback = colo_common_write_stream_done;
- libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, !rc);
-}
-
-/* ===================== colo: common callback ===================== */
-
-static void colo_common_write_stream_done(libxl__egc *egc,
- libxl__stream_write_state *stream,
- int rc)
-{
- libxl__colo_restore_checkpoint_state *crcs =
- CONTAINER_OF(stream, *crcs, sws);
- libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
- int ok;
-
- EGC_GC;
-
- if (rc < 0) {
- /* TODO: it may be a internal error, but we don't know */
- LOGD(ERROR, crcs->crs->domid, "sending data fails");
- ok = 2;
- goto out;
- }
-
- if (!crcs->callback) {
- /* Everythins is OK */
- ok = 1;
- goto out;
- }
-
- crcs->callback(egc, crcs, 0);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok);
-}
-
-static void colo_common_read_stream_done(libxl__egc *egc,
- libxl__stream_read_state *stream,
- int rc)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(stream, *dcs, srs);
- libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
- int ok;
-
- EGC_GC;
-
- if (rc < 0) {
- /* TODO: it may be a internal error, but we don't know */
- LOGD(ERROR, crcs->crs->domid, "reading data fails");
- ok = 2;
- goto out;
- }
-
- if (!crcs->callback) {
- /* Everythins is OK */
- ok = 1;
- goto out;
- }
-
- /* rc contains the id */
- crcs->callback(egc, crcs, rc);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok);
-}
+++ /dev/null
-/*
- * Copyright (C) 2016 FUJITSU LIMITED
- * Author: Wen Congyang <wency@cn.fujitsu.com>
- * Yang Hongyang <hongyang.yang@easystack.cn>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-extern const libxl__checkpoint_device_instance_ops colo_save_device_nic;
-extern const libxl__checkpoint_device_instance_ops colo_save_device_qdisk;
-
-static const libxl__checkpoint_device_instance_ops *colo_ops[] = {
- &colo_save_device_nic,
- &colo_save_device_qdisk,
- NULL,
-};
-
-/* ================= helper functions ================= */
-
-static int init_device_subkind(libxl__checkpoint_devices_state *cds)
-{
- /* init device subkind-specific state in the libxl ctx */
- int rc;
- STATE_AO_GC(cds->ao);
-
- rc = init_subkind_colo_nic(cds);
- if (rc) goto out;
-
- rc = init_subkind_qdisk(cds);
- if (rc) {
- cleanup_subkind_colo_nic(cds);
- goto out;
- }
-
- rc = 0;
-out:
- return rc;
-}
-
-static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds)
-{
- /* cleanup device subkind-specific state in the libxl ctx */
- STATE_AO_GC(cds->ao);
-
- cleanup_subkind_colo_nic(cds);
- cleanup_subkind_qdisk(cds);
-}
-
-/* ================= colo: setup save environment ================= */
-
-static void colo_save_setup_done(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-static void colo_save_setup_failed(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-/*
- * checkpoint callbacks are called in the following order:
- * 1. suspend
- * 2. checkpoint
- * 3. resume
- * 4. wait checkpoint
- */
-static void libxl__colo_save_domain_suspend_callback(void *data);
-static void libxl__colo_save_domain_checkpoint_callback(void *data);
-static void libxl__colo_save_domain_resume_callback(void *data);
-static void libxl__colo_save_domain_wait_checkpoint_callback(void *data);
-
-void libxl__colo_save_setup(libxl__egc *egc, libxl__colo_save_state *css)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *const cds = &dss->cds;
- libxl__srm_save_autogen_callbacks *const callbacks =
- &dss->sws.shs.callbacks.save.a;
-
- STATE_AO_GC(dss->ao);
-
- if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
- LOGD(ERROR, dss->domid, "COLO only supports hvm now");
- goto out;
- }
-
- css->send_fd = dss->fd;
- css->recv_fd = dss->recv_fd;
- css->svm_running = false;
- css->paused = true;
- css->qdisk_setuped = false;
- css->qdisk_used = false;
- libxl__ev_child_init(&css->child);
- css->cps.is_userspace_proxy =
- libxl_defbool_val(dss->remus->userspace_colo_proxy);
-
- if (dss->remus->netbufscript)
- css->colo_proxy_script = libxl__strdup(gc, dss->remus->netbufscript);
- else
- css->colo_proxy_script = GCSPRINTF("%s/colo-proxy-setup",
- libxl__xen_script_dir_path());
-
- cds->ops = colo_ops;
- cds->callback = colo_save_setup_done;
- cds->ao = ao;
- cds->domid = dss->domid;
- cds->concrete_data = css;
-
- /* If enable userspace proxy mode, we don't need VIF */
- if (css->cps.is_userspace_proxy) {
- cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VBD);
-
- /* Use this args we can connect to qemu colo-compare */
- cds->nics = libxl__device_list(gc, &libxl__nic_devtype,
- cds->domid, &cds->num_nics);
- if (cds->num_nics > 0) {
- css->cps.checkpoint_host = cds->nics[0].colo_checkpoint_host;
- css->cps.checkpoint_port = cds->nics[0].colo_checkpoint_port;
- }
- } else {
- cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VIF) |
- (1 << LIBXL__DEVICE_KIND_VBD);
- }
-
- css->srs.ao = ao;
- css->srs.fd = css->recv_fd;
- css->srs.back_channel = true;
- libxl__stream_read_start(egc, &css->srs);
- css->cps.ao = ao;
- if (colo_proxy_setup(&css->cps)) {
- LOGD(ERROR, cds->domid, "COLO: failed to setup colo proxy for guest");
- goto out;
- }
-
- if (init_device_subkind(cds))
- goto out;
-
- callbacks->suspend = libxl__colo_save_domain_suspend_callback;
- callbacks->checkpoint = libxl__colo_save_domain_checkpoint_callback;
- callbacks->postcopy = libxl__colo_save_domain_resume_callback;
- callbacks->wait_checkpoint = libxl__colo_save_domain_wait_checkpoint_callback;
-
- libxl__checkpoint_devices_setup(egc, &dss->cds);
-
- return;
-
-out:
- dss->callback(egc, dss, ERROR_FAIL);
-}
-
-static void colo_save_setup_done(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__colo_save_state *css = cds->concrete_data;
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
- EGC_GC;
-
- if (!rc) {
- libxl__domain_save(egc, dss);
- return;
- }
-
- LOGD(ERROR, dss->domid, "COLO: failed to setup device for guest");
- cds->callback = colo_save_setup_failed;
- libxl__checkpoint_devices_teardown(egc, cds);
-}
-
-static void colo_save_setup_failed(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__colo_save_state *css = cds->concrete_data;
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
- STATE_AO_GC(cds->ao);
-
- if (rc)
- LOGD(ERROR, cds->domid,
- "COLO: failed to teardown device after setup failed"
- " for guest, rc %d", rc);
-
- cleanup_device_subkind(cds);
- dss->callback(egc, dss, rc);
-}
-
-/* ================= colo: teardown save environment ================= */
-
-static void colo_teardown_done(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-
-void libxl__colo_save_teardown(libxl__egc *egc,
- libxl__colo_save_state *css,
- int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
- EGC_GC;
-
- LOGD(WARN, dss->domid,
- "COLO: Domain suspend terminated with rc %d,"
- " teardown COLO devices...", rc);
-
- libxl__stream_read_abort(egc, &css->srs, 1);
-
- if (css->qdisk_setuped) {
- libxl__qmp_stop_replication(gc, dss->domid, true);
- css->qdisk_setuped = false;
- }
-
- dss->cds.callback = colo_teardown_done;
- libxl__checkpoint_devices_teardown(egc, &dss->cds);
- return;
-}
-
-static void colo_teardown_done(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__colo_save_state *css = cds->concrete_data;
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
- cleanup_device_subkind(cds);
- colo_proxy_teardown(&css->cps);
- dss->callback(egc, dss, rc);
-}
-
-static void colo_common_write_stream_done(libxl__egc *egc,
- libxl__stream_write_state *stream,
- int rc);
-static void colo_common_read_stream_done(libxl__egc *egc,
- libxl__stream_read_state *stream,
- int rc);
-
-/* ===================== colo: suspend primary vm ===================== */
-
-static void colo_read_svm_suspended_done(libxl__egc *egc,
- libxl__colo_save_state *css,
- int id);
-/*
- * Do the following things when suspending primary vm:
- * 1. suspend primary vm
- * 2. do postsuspend
- * 3. read CHECKPOINT_SVM_SUSPENDED
- * 4. read secondary vm's dirty pages
- */
-static void colo_suspend_primary_vm_done(libxl__egc *egc,
- libxl__domain_suspend_state *dsps,
- int ok);
-static void colo_postsuspend_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-
-static void libxl__colo_save_domain_suspend_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__egc *egc = shs->egc;
- libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
- libxl__domain_save_state *dss = sws->dss;
-
- /* Convenience aliases */
- libxl__domain_suspend_state *dsps = &dss->dsps;
-
- dsps->callback_common_done = colo_suspend_primary_vm_done;
- libxl__domain_suspend(egc, dsps);
-}
-
-static void colo_suspend_primary_vm_done(libxl__egc *egc,
- libxl__domain_suspend_state *dsps,
- int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, dss->domid, "cannot suspend primary vm");
- goto out;
- }
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *const cds = &dss->cds;
-
- cds->callback = colo_postsuspend_cb;
- libxl__checkpoint_devices_postsuspend(egc, cds);
- return;
-
-out:
- dss->rc = rc;
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-static void colo_postsuspend_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__colo_save_state *css = cds->concrete_data;
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, dss->domid, "postsuspend fails");
- goto out;
- }
-
- if (!css->svm_running) {
- rc = 0;
- goto out;
- }
-
- /*
- * read CHECKPOINT_SVM_SUSPENDED
- */
- css->callback = colo_read_svm_suspended_done;
- css->srs.checkpoint_callback = colo_common_read_stream_done;
- libxl__stream_read_checkpoint_state(egc, &css->srs);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-static void colo_read_svm_suspended_done(libxl__egc *egc,
- libxl__colo_save_state *css,
- int id)
-{
- int ok = 0;
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
- EGC_GC;
-
- if (id != CHECKPOINT_SVM_SUSPENDED) {
- LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id,
- CHECKPOINT_SVM_SUSPENDED);
- goto out;
- }
-
- if (!css->paused &&
- libxl__qmp_query_xen_replication_status(gc, dss->domid)) {
- LOGD(ERROR, dss->domid,
- "replication error occurs when primary vm is running");
- goto out;
- }
-
- ok = 1;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
-}
-
-/* ===================== colo: send tailbuf ========================== */
-
-static void libxl__colo_save_domain_checkpoint_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
- libxl__domain_save_state *dss = sws->dss;
-
- /* Convenience aliases */
- libxl__colo_save_state *const css = &dss->css;
-
- /* write emulator xenstore data, emulator context, and checkpoint end */
- css->callback = NULL;
- dss->sws.checkpoint_callback = colo_common_write_stream_done;
- libxl__stream_write_start_checkpoint(shs->egc, &dss->sws);
-}
-
-/* ===================== colo: resume primary vm ===================== */
-
-/*
- * Do the following things when resuming primary vm:
- * 1. read CHECKPOINT_SVM_READY
- * 2. do preresume
- * 3. resume primary vm
- * 4. read CHECKPOINT_SVM_RESUMED
- */
-static void colo_read_svm_ready_done(libxl__egc *egc,
- libxl__colo_save_state *css,
- int id);
-static void colo_preresume_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-static void colo_read_svm_resumed_done(libxl__egc *egc,
- libxl__colo_save_state *css,
- int id);
-
-static void libxl__colo_save_domain_resume_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__egc *egc = shs->egc;
- libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
- libxl__domain_save_state *dss = sws->dss;
-
- /* Convenience aliases */
- libxl__colo_save_state *const css = &dss->css;
-
- EGC_GC;
-
- /* read CHECKPOINT_SVM_READY */
- css->callback = colo_read_svm_ready_done;
- css->srs.checkpoint_callback = colo_common_read_stream_done;
- libxl__stream_read_checkpoint_state(egc, &css->srs);
-}
-
-static void colo_read_svm_ready_done(libxl__egc *egc,
- libxl__colo_save_state *css,
- int id)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
- EGC_GC;
-
- if (id != CHECKPOINT_SVM_READY) {
- LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id,
- CHECKPOINT_SVM_READY);
- goto out;
- }
-
- colo_proxy_preresume(&css->cps);
-
- css->svm_running = true;
- dss->cds.callback = colo_preresume_cb;
- libxl__checkpoint_devices_preresume(egc, &dss->cds);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-static void colo_preresume_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__colo_save_state *css = cds->concrete_data;
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, dss->domid, "preresume fails");
- goto out;
- }
-
- if (css->qdisk_used && !css->qdisk_setuped) {
- if (libxl__qmp_start_replication(gc, dss->domid, true)) {
- LOGD(ERROR, dss->domid, "starting replication fails");
- goto out;
- }
- css->qdisk_setuped = true;
- }
-
- if (!css->paused) {
- if (libxl__qmp_colo_do_checkpoint(gc, dss->domid)) {
- LOGD(ERROR, dss->domid, "doing checkpoint fails");
- goto out;
- }
- }
-
- /* Resumes the domain and the device model */
- if (libxl__domain_resume_deprecated(gc, dss->domid, /* Fast Suspend */1)) {
- LOGD(ERROR, dss->domid, "cannot resume primary vm");
- goto out;
- }
-
- /*
- * The guest should be paused before doing colo because there is
- * no disk migration.
- */
- if (css->paused) {
- rc = libxl__domain_unpause_deprecated(gc, dss->domid);
- if (rc) {
- LOGD(ERROR, dss->domid, "cannot unpause primary vm");
- goto out;
- }
- css->paused = false;
- }
-
- /* read CHECKPOINT_SVM_RESUMED */
- css->callback = colo_read_svm_resumed_done;
- css->srs.checkpoint_callback = colo_common_read_stream_done;
- libxl__stream_read_checkpoint_state(egc, &css->srs);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-static void colo_read_svm_resumed_done(libxl__egc *egc,
- libxl__colo_save_state *css,
- int id)
-{
- int ok = 0;
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
- EGC_GC;
-
- if (id != CHECKPOINT_SVM_RESUMED) {
- LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id,
- CHECKPOINT_SVM_RESUMED);
- goto out;
- }
-
- colo_proxy_postresume(&css->cps);
-
- ok = 1;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
-}
-
-/* ===================== colo: wait new checkpoint ===================== */
-
-static void colo_start_new_checkpoint(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-static void colo_proxy_async_wait_for_checkpoint(libxl__colo_save_state *css);
-static void colo_proxy_async_call_done(libxl__egc *egc,
- libxl__ev_child *child,
- int pid,
- int status);
-
-static void colo_proxy_wait_for_checkpoint(libxl__egc *egc,
- libxl__colo_save_state *css)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
- ASYNC_CALL(egc, dss->cds.ao, &css->child, css,
- colo_proxy_async_wait_for_checkpoint,
- colo_proxy_async_call_done);
-}
-
-static void colo_proxy_async_wait_for_checkpoint(libxl__colo_save_state *css)
-{
- int req;
-
- req = colo_proxy_checkpoint(&css->cps, COLO_PROXY_CHECKPOINT_TIMEOUT);
- if (req < 0) {
- /* some error happens */
- _exit(1);
- } else {
- /* req == 0: no checkpoint is needed, do a checkpoint every 5s */
- /* req > 0: net packets is not consistent, we need to start a
- * checkpoint
- */
- _exit(0);
- }
-}
-
-static void colo_proxy_async_call_done(libxl__egc *egc,
- libxl__ev_child *child,
- int pid,
- int status)
-{
- libxl__colo_save_state *css = CONTAINER_OF(child, *css, child);
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
- EGC_GC;
-
- if (status) {
- LOGD(ERROR, dss->domid, "failed to wait for new checkpoint");
- colo_start_new_checkpoint(egc, &dss->cds, ERROR_FAIL);
- return;
- }
-
- colo_start_new_checkpoint(egc, &dss->cds, 0);
-}
-
-/*
- * Do the following things:
- * 1. do commit
- * 2. wait for a new checkpoint
- * 3. write CHECKPOINT_NEW
- */
-static void colo_device_commit_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-
-static void libxl__colo_save_domain_wait_checkpoint_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
- libxl__domain_save_state *dss = sws->dss;
- libxl__egc *egc = dss->sws.shs.egc;
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *const cds = &dss->cds;
-
- cds->callback = colo_device_commit_cb;
- libxl__checkpoint_devices_commit(egc, cds);
-}
-
-static void colo_device_commit_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__colo_save_state *css = cds->concrete_data;
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, dss->domid, "commit fails");
- goto out;
- }
-
- colo_proxy_wait_for_checkpoint(egc, css);
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-static void colo_start_new_checkpoint(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__colo_save_state *css = cds->concrete_data;
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
- libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_NEW };
-
- if (rc)
- goto out;
-
- /* write CHECKPOINT_NEW */
- css->callback = NULL;
- dss->sws.checkpoint_callback = colo_common_write_stream_done;
- libxl__stream_write_checkpoint_state(egc, &dss->sws, &srcs);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-/* ===================== colo: common callback ===================== */
-
-static void colo_common_write_stream_done(libxl__egc *egc,
- libxl__stream_write_state *stream,
- int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(stream, *dss, sws);
- int ok;
-
- /* Convenience aliases */
- libxl__colo_save_state *const css = &dss->css;
-
- EGC_GC;
-
- if (rc < 0) {
- /* TODO: it may be a internal error, but we don't know */
- LOGD(ERROR, dss->domid, "sending data fails");
- ok = 0;
- goto out;
- }
-
- if (!css->callback) {
- /* Everythins is OK */
- ok = 1;
- goto out;
- }
-
- css->callback(egc, css, 0);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
-}
-
-static void colo_common_read_stream_done(libxl__egc *egc,
- libxl__stream_read_state *stream,
- int rc)
-{
- libxl__colo_save_state *css = CONTAINER_OF(stream, *css, srs);
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
- int ok;
-
- EGC_GC;
-
- if (rc < 0) {
- /* TODO: it may be a internal error, but we don't know */
- LOGD(ERROR, dss->domid, "reading data fails");
- ok = 0;
- goto out;
- }
-
- if (!css->callback) {
- /* Everythins is OK */
- ok = 1;
- goto out;
- }
-
- /* rc contains the id */
- css->callback(egc, css, rc);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
-}
+++ /dev/null
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-int libxl__console_tty_path(libxl__gc *gc, uint32_t domid, int cons_num,
- libxl_console_type type, char **tty_path)
-{
- int rc;
- char *dom_path;
-
- dom_path = libxl__xs_get_dompath(gc, domid);
- if (!dom_path) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- switch (type) {
- case LIBXL_CONSOLE_TYPE_SERIAL:
- *tty_path = GCSPRINTF("%s/serial/%d/tty", dom_path, cons_num);
- rc = 0;
- break;
- case LIBXL_CONSOLE_TYPE_PV:
- if (cons_num == 0)
- *tty_path = GCSPRINTF("%s/console/tty", dom_path);
- else
- *tty_path = GCSPRINTF("%s/tty",
- libxl__domain_device_frontend_path(gc, domid,
- cons_num, LIBXL__DEVICE_KIND_CONSOLE));
- rc = 0;
- break;
- default:
- rc = ERROR_INVAL;
- goto out;
- }
-
-out:
- return rc;
-}
-
-int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num,
- libxl_console_type type, int notify_fd)
-{
- GC_INIT(ctx);
- char *p = GCSPRINTF("%s/xenconsole", libxl__private_bindir_path());
- char *domid_s = GCSPRINTF("%d", domid);
- char *cons_num_s = GCSPRINTF("%d", cons_num);
- char *notify_fd_s;
- char *cons_type_s;
-
- switch (type) {
- case LIBXL_CONSOLE_TYPE_PV:
- cons_type_s = "pv";
- break;
- case LIBXL_CONSOLE_TYPE_SERIAL:
- cons_type_s = "serial";
- break;
- case LIBXL_CONSOLE_TYPE_VUART:
- cons_type_s = "vuart";
- break;
- default:
- goto out;
- }
-
- if (notify_fd != -1) {
- notify_fd_s = GCSPRINTF("%d", notify_fd);
- execl(p, p, domid_s, "--num", cons_num_s, "--type", cons_type_s,
- "--start-notify-fd", notify_fd_s, (void *)NULL);
- } else {
- execl(p, p, domid_s, "--num", cons_num_s, "--type", cons_type_s,
- (void *)NULL);
- }
-
-out:
- GC_FREE;
- return ERROR_FAIL;
-}
-
-int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num,
- libxl_console_type type, char **path)
-{
- GC_INIT(ctx);
- char *tty_path;
- char *tty;
- int rc;
-
- rc = libxl__console_tty_path(gc, domid, cons_num, type, &tty_path);
- if (rc) {
- LOGD(ERROR, domid, "Failed to get tty path\n");
- goto out;
- }
-
- tty = libxl__xs_read(gc, XBT_NULL, tty_path);
- if (!tty || tty[0] == '\0') {
- LOGED(ERROR, domid, "Unable to read console tty path `%s'",
- tty_path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- *path = libxl__strdup(NOGC, tty);
- rc = 0;
-out:
- GC_FREE;
- return rc;
-}
-
-static int libxl__primary_console_find(libxl_ctx *ctx, uint32_t domid_vm,
- uint32_t *domid, int *cons_num,
- libxl_console_type *type)
-{
- GC_INIT(ctx);
- uint32_t stubdomid = libxl_get_stubdom_id(ctx, domid_vm);
- int rc;
-
- if (stubdomid) {
- *domid = stubdomid;
- *cons_num = STUBDOM_CONSOLE_SERIAL;
- *type = LIBXL_CONSOLE_TYPE_PV;
- } else {
- switch (libxl__domain_type(gc, domid_vm)) {
- case LIBXL_DOMAIN_TYPE_HVM:
- *domid = domid_vm;
- *cons_num = 0;
- *type = LIBXL_CONSOLE_TYPE_SERIAL;
- break;
- case LIBXL_DOMAIN_TYPE_PVH:
- case LIBXL_DOMAIN_TYPE_PV:
- *domid = domid_vm;
- *cons_num = 0;
- *type = LIBXL_CONSOLE_TYPE_PV;
- break;
- case LIBXL_DOMAIN_TYPE_INVALID:
- rc = ERROR_INVAL;
- goto out;
- default: abort();
- }
- }
-
- rc = 0;
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl_primary_console_exec(libxl_ctx *ctx, uint32_t domid_vm, int notify_fd)
-{
- uint32_t domid;
- int cons_num;
- libxl_console_type type;
- int rc;
-
- rc = libxl__primary_console_find(ctx, domid_vm, &domid, &cons_num, &type);
- if ( rc ) return rc;
- return libxl_console_exec(ctx, domid, cons_num, type, notify_fd);
-}
-
-int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm,
- char **path)
-{
- uint32_t domid;
- int cons_num;
- libxl_console_type type;
- int rc;
-
- rc = libxl__primary_console_find(ctx, domid_vm, &domid, &cons_num, &type);
- if ( rc ) return rc;
- return libxl_console_get_tty(ctx, domid, cons_num, type, path);
-}
-
-int libxl_vncviewer_exec(libxl_ctx *ctx, uint32_t domid, int autopass)
-{
- GC_INIT(ctx);
- const char *vnc_port;
- const char *vnc_listen = NULL, *vnc_pass = NULL;
- int port = 0, autopass_fd = -1;
- char *vnc_bin, *args[] = {
- "vncviewer",
- NULL, /* hostname:display */
- NULL, /* -autopass */
- NULL,
- };
-
- vnc_port = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF(
- "/local/domain/%d/console/vnc-port", domid));
- if (!vnc_port) {
- LOGD(ERROR, domid, "Cannot get vnc-port");
- goto x_fail;
- }
-
- port = atoi(vnc_port) - 5900;
-
- vnc_listen = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("/local/domain/%d/console/vnc-listen",
- domid));
-
- if ( autopass )
- vnc_pass = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("/local/domain/%d/console/vnc-pass",
- domid));
-
- if ( NULL == vnc_listen )
- vnc_listen = "localhost";
-
- if ( (vnc_bin = getenv("VNCVIEWER")) )
- args[0] = vnc_bin;
-
- args[1] = GCSPRINTF("%s:%d", vnc_listen, port);
-
- if ( vnc_pass ) {
- char tmpname[] = "/tmp/vncautopass.XXXXXX";
- autopass_fd = mkstemp(tmpname);
- if ( autopass_fd < 0 ) {
- LOGED(ERROR, domid, "mkstemp %s failed", tmpname);
- goto x_fail;
- }
-
- if ( unlink(tmpname) ) {
- /* should never happen */
- LOGED(ERROR, domid, "unlink %s failed", tmpname);
- goto x_fail;
- }
-
- if ( libxl_write_exactly(ctx, autopass_fd, vnc_pass, strlen(vnc_pass),
- tmpname, "vnc password") )
- goto x_fail;
-
- if ( lseek(autopass_fd, SEEK_SET, 0) ) {
- LOGED(ERROR, domid, "rewind %s (autopass) failed", tmpname);
- goto x_fail;
- }
-
- args[2] = "-autopass";
- }
-
- libxl__exec(gc, autopass_fd, -1, -1, args[0], args, NULL);
-
- x_fail:
- GC_FREE;
- return ERROR_FAIL;
-}
-
-int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
- libxl__device_console *console,
- libxl__domain_build_state *state,
- libxl__device *device)
-{
- flexarray_t *front, *ro_front;
- flexarray_t *back;
- int rc;
-
- if (console->devid && state) {
- rc = ERROR_INVAL;
- goto out;
- }
- if (!console->devid && (console->name || console->path)) {
- LOGD(ERROR, domid, "Primary console has invalid configuration");
- rc = ERROR_INVAL;
- goto out;
- }
-
- front = flexarray_make(gc, 16, 1);
- ro_front = flexarray_make(gc, 16, 1);
- back = flexarray_make(gc, 16, 1);
-
- device->backend_devid = console->devid;
- device->backend_domid = console->backend_domid;
- device->backend_kind = LIBXL__DEVICE_KIND_CONSOLE;
- device->devid = console->devid;
- device->domid = domid;
- device->kind = LIBXL__DEVICE_KIND_CONSOLE;
-
- flexarray_append(back, "frontend-id");
- flexarray_append(back, GCSPRINTF("%d", domid));
- flexarray_append(back, "online");
- flexarray_append(back, "1");
- flexarray_append(back, "state");
- flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
- flexarray_append(back, "protocol");
- flexarray_append(back, LIBXL_XENCONSOLE_PROTOCOL);
-
- if (console->name) {
- flexarray_append(ro_front, "name");
- flexarray_append(ro_front, console->name);
- flexarray_append(back, "name");
- flexarray_append(back, console->name);
- }
- if (console->connection) {
- flexarray_append(back, "connection");
- flexarray_append(back, console->connection);
- }
- if (console->path) {
- flexarray_append(back, "path");
- flexarray_append(back, console->path);
- }
-
- flexarray_append(front, "backend-id");
- flexarray_append(front, GCSPRINTF("%d", console->backend_domid));
-
- flexarray_append(ro_front, "limit");
- flexarray_append(ro_front, GCSPRINTF("%d", LIBXL_XENCONSOLE_LIMIT));
- flexarray_append(ro_front, "type");
- if (console->consback == LIBXL__CONSOLE_BACKEND_XENCONSOLED)
- flexarray_append(ro_front, "xenconsoled");
- else
- flexarray_append(ro_front, "ioemu");
- flexarray_append(ro_front, "output");
- flexarray_append(ro_front, console->output);
- flexarray_append(ro_front, "tty");
- if (state && state->console_tty)
- flexarray_append(ro_front, state->console_tty);
- else
- flexarray_append(ro_front, "");
-
- if (state) {
- flexarray_append(ro_front, "port");
- flexarray_append(ro_front, GCSPRINTF("%"PRIu32, state->console_port));
- flexarray_append(ro_front, "ring-ref");
- flexarray_append(ro_front, GCSPRINTF("%lu", state->console_mfn));
- } else {
- flexarray_append(front, "state");
- flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
- flexarray_append(front, "protocol");
- flexarray_append(front, LIBXL_XENCONSOLE_PROTOCOL);
- }
- libxl__device_generic_add(gc, XBT_NULL, device,
- libxl__xs_kvs_of_flexarray(gc, back),
- libxl__xs_kvs_of_flexarray(gc, front),
- libxl__xs_kvs_of_flexarray(gc, ro_front));
- rc = 0;
-out:
- return rc;
-}
-
-int libxl__device_vuart_add(libxl__gc *gc, uint32_t domid,
- libxl__device_console *console,
- libxl__domain_build_state *state)
-{
- libxl__device device;
- flexarray_t *ro_front;
- flexarray_t *back;
- int rc;
-
- ro_front = flexarray_make(gc, 16, 1);
- back = flexarray_make(gc, 16, 1);
-
- device.backend_devid = console->devid;
- device.backend_domid = console->backend_domid;
- device.backend_kind = LIBXL__DEVICE_KIND_VUART;
- device.devid = console->devid;
- device.domid = domid;
- device.kind = LIBXL__DEVICE_KIND_VUART;
-
- flexarray_append(back, "frontend-id");
- flexarray_append(back, GCSPRINTF("%d", domid));
- flexarray_append(back, "online");
- flexarray_append(back, "1");
- flexarray_append(back, "state");
- flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
- flexarray_append(back, "protocol");
- flexarray_append(back, LIBXL_XENCONSOLE_PROTOCOL);
-
- flexarray_append(ro_front, "port");
- flexarray_append(ro_front, GCSPRINTF("%"PRIu32, state->vuart_port));
- flexarray_append(ro_front, "ring-ref");
- flexarray_append(ro_front, GCSPRINTF("%"PRIu_xen_pfn, state->vuart_gfn));
- flexarray_append(ro_front, "limit");
- flexarray_append(ro_front, GCSPRINTF("%d", LIBXL_XENCONSOLE_LIMIT));
- flexarray_append(ro_front, "type");
- flexarray_append(ro_front, "xenconsoled");
-
- rc = libxl__device_generic_add(gc, XBT_NULL, &device,
- libxl__xs_kvs_of_flexarray(gc, back),
- NULL,
- libxl__xs_kvs_of_flexarray(gc, ro_front));
- return rc;
-}
-
-int libxl__init_console_from_channel(libxl__gc *gc,
- libxl__device_console *console,
- int dev_num,
- libxl_device_channel *channel)
-{
- int rc;
-
- libxl__device_console_init(console);
-
- /* Perform validation first, allocate second. */
-
- if (channel->devid == -1)
- channel->devid = dev_num;
-
- if (!channel->name) {
- LOG(ERROR, "channel %d has no name", channel->devid);
- return ERROR_INVAL;
- }
-
- if (channel->backend_domname) {
- rc = libxl_domain_qualifier_to_domid(CTX, channel->backend_domname,
- &channel->backend_domid);
- if (rc < 0) return rc;
- }
-
- /* The xenstore 'output' node tells the backend what to connect the console
- to. If the channel has "connection = pty" then the "output" node will be
- set to "pty". If the channel has "connection = socket" then the "output"
- node will be set to "chardev:libxl-channel%d". This tells the qemu
- backend to proxy data between the console ring and the character device
- with id "libxl-channel%d". These character devices are currently defined
- on the qemu command-line via "-chardev" options in libxl_dm.c */
-
- switch (channel->connection) {
- case LIBXL_CHANNEL_CONNECTION_UNKNOWN:
- LOG(ERROR, "channel %d has no defined connection; "
- "to where should it be connected?", channel->devid);
- return ERROR_INVAL;
- case LIBXL_CHANNEL_CONNECTION_PTY:
- console->connection = libxl__strdup(NOGC, "pty");
- console->output = libxl__sprintf(NOGC, "pty");
- break;
- case LIBXL_CHANNEL_CONNECTION_SOCKET:
- if (!channel->u.socket.path) {
- LOG(ERROR, "channel %d has no path", channel->devid);
- return ERROR_INVAL;
- }
- console->connection = libxl__strdup(NOGC, "socket");
- console->path = libxl__strdup(NOGC, channel->u.socket.path);
- console->output = libxl__sprintf(NOGC, "chardev:libxl-channel%d",
- channel->devid);
- break;
- default:
- /* We've forgotten to add the clause */
- LOG(ERROR, "%s: missing implementation for channel connection %d",
- __func__, channel->connection);
- abort();
- }
-
- console->devid = channel->devid;
- console->consback = LIBXL__CONSOLE_BACKEND_IOEMU;
- console->backend_domid = channel->backend_domid;
- console->name = libxl__strdup(NOGC, channel->name);
-
- return 0;
-}
-
-static int libxl__device_channel_from_xenstore(libxl__gc *gc,
- const char *libxl_path,
- libxl_device_channel *channel)
-{
- const char *tmp;
- int rc;
-
- libxl_device_channel_init(channel);
-
- rc = libxl__xs_read_checked(NOGC, XBT_NULL,
- GCSPRINTF("%s/name", libxl_path),
- (const char **)(&channel->name));
- if (rc) goto out;
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/connection", libxl_path), &tmp);
- if (rc) goto out;
- if (!strcmp(tmp, "pty")) {
- channel->connection = LIBXL_CHANNEL_CONNECTION_PTY;
- } else if (!strcmp(tmp, "socket")) {
- channel->connection = LIBXL_CHANNEL_CONNECTION_SOCKET;
- rc = libxl__xs_read_checked(NOGC, XBT_NULL,
- GCSPRINTF("%s/path", libxl_path),
- (const char **)(&channel->u.socket.path));
- if (rc) goto out;
- } else {
- rc = ERROR_INVAL;
- goto out;
- }
-
- rc = 0;
- out:
- return rc;
-}
-
-static int libxl__append_channel_list(libxl__gc *gc,
- uint32_t domid,
- libxl_device_channel **channels,
- int *nchannels)
-{
- char *libxl_dir_path = NULL;
- char **dir = NULL;
- unsigned int n = 0, devid = 0;
- libxl_device_channel *next = NULL;
- int rc = 0, i;
-
- libxl_dir_path = GCSPRINTF("%s/device/%s",
- libxl__xs_libxl_path(gc, domid),
- libxl__device_kind_to_string(
- LIBXL__DEVICE_KIND_CONSOLE));
- dir = libxl__xs_directory(gc, XBT_NULL, libxl_dir_path, &n);
- if (!dir || !n)
- goto out;
-
- for (i = 0; i < n; i++) {
- const char *libxl_path, *name;
- libxl_device_channel *tmp;
-
- libxl_path = GCSPRINTF("%s/%s", libxl_dir_path, dir[i]);
- name = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/name", libxl_path));
- /* 'channels' are consoles with names, so ignore all consoles
- without names */
- if (!name) continue;
- tmp = realloc(*channels,
- sizeof(libxl_device_channel) * (*nchannels + devid + 1));
- if (!tmp) {
- rc = ERROR_NOMEM;
- goto out;
- }
- *channels = tmp;
- next = *channels + *nchannels + devid;
- rc = libxl__device_channel_from_xenstore(gc, libxl_path, next);
- if (rc) goto out;
- next->devid = devid;
- devid++;
- }
- *nchannels += devid;
- return 0;
-
- out:
- return rc;
-}
-
-libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx,
- uint32_t domid,
- int *num)
-{
- GC_INIT(ctx);
- libxl_device_channel *channels = NULL;
- int rc;
-
- *num = 0;
-
- rc = libxl__append_channel_list(gc, domid, &channels, num);
- if (rc) goto out_err;
-
- GC_FREE;
- return channels;
-
-out_err:
- LOGD(ERROR, domid, "Unable to list channels");
- while (*num) {
- (*num)--;
- libxl_device_channel_dispose(&channels[*num]);
- }
- free(channels);
- return NULL;
-}
-
-int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_channel *channel,
- libxl_channelinfo *channelinfo)
-{
- GC_INIT(ctx);
- char *fe_path, *libxl_path;
- char *val;
- int rc;
-
- channelinfo->devid = channel->devid;
-
- fe_path = libxl__domain_device_frontend_path(gc, domid,
- channelinfo->devid + 1,
- LIBXL__DEVICE_KIND_CONSOLE);
- libxl_path = libxl__domain_device_libxl_path(gc, domid,
- channelinfo->devid + 1,
- LIBXL__DEVICE_KIND_CONSOLE);
-
- channelinfo->backend = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path), NULL);
- if (!channelinfo->backend) {
- GC_FREE;
- return ERROR_FAIL;
- }
- rc = libxl__backendpath_parse_domid(gc, channelinfo->backend,
- &channelinfo->backend_id);
- if (rc) goto out;
-
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
- channelinfo->state = val ? strtoul(val, NULL, 10) : -1;
- channelinfo->frontend = libxl__strdup(NOGC, fe_path);
- channelinfo->frontend_id = domid;
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
- channelinfo->rref = val ? strtoul(val, NULL, 10) : -1;
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/port", fe_path));
- channelinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
-
- channelinfo->connection = channel->connection;
- switch (channel->connection) {
- case LIBXL_CHANNEL_CONNECTION_PTY:
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tty", fe_path));
- /*
- * It is obviously very wrong for this value to be in the
- * frontend. But in XSA-175 we don't want to re-engineer
- * this because other xenconsole code elsewhere (some
- * even out of tree, perhaps) expects this node to be
- * here.
- *
- * FE/pty is readonly for the guest. It always exists if
- * FE does because libxl__device_console_add
- * unconditionally creates it and nothing deletes it.
- *
- * The guest can delete the whole FE (which it has write
- * privilege on) but the containing directories
- * /local/GUEST[/device[/console]] are also RO for the
- * guest. So if the guest deletes FE it cannot recreate
- * it.
- *
- * Therefore the guest cannot cause FE/pty to contain bad
- * data, although it can cause it to not exist.
- */
- if (!val) val = "/NO-SUCH-PATH";
- channelinfo->u.pty.path = strdup(val);
- break;
- default:
- break;
- }
- rc = 0;
- out:
- GC_FREE;
- return rc;
-}
-
-static int libxl__device_vfb_setdefault(libxl__gc *gc, uint32_t domid,
- libxl_device_vfb *vfb, bool hotplug)
-{
- int rc;
-
- libxl_defbool_setdefault(&vfb->vnc.enable, true);
- if (libxl_defbool_val(vfb->vnc.enable)) {
- if (!vfb->vnc.listen) {
- vfb->vnc.listen = strdup("127.0.0.1");
- if (!vfb->vnc.listen) return ERROR_NOMEM;
- }
-
- libxl_defbool_setdefault(&vfb->vnc.findunused, true);
- } else {
- libxl_defbool_setdefault(&vfb->vnc.findunused, false);
- }
-
- libxl_defbool_setdefault(&vfb->sdl.enable, false);
- libxl_defbool_setdefault(&vfb->sdl.opengl, false);
-
- rc = libxl__resolve_domid(gc, vfb->backend_domname, &vfb->backend_domid);
- return rc;
-}
-
-int libxl_device_vfb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- int rc;
-
- rc = libxl__device_add(gc, domid, &libxl__vfb_devtype, vfb);
- if (rc) {
- LOGD(ERROR, domid, "Unable to add vfb device");
- goto out;
- }
-
-out:
- libxl__ao_complete(egc, ao, rc);
- return AO_INPROGRESS;
-}
-
-static int libxl__set_xenstore_vfb(libxl__gc *gc, uint32_t domid,
- libxl_device_vfb *vfb,
- flexarray_t *back, flexarray_t *front,
- flexarray_t *ro_front)
-{
- flexarray_append_pair(back, "vnc",
- libxl_defbool_val(vfb->vnc.enable) ? "1" : "0");
- flexarray_append_pair(back, "vnclisten", vfb->vnc.listen);
- flexarray_append_pair(back, "vncpasswd", vfb->vnc.passwd);
- flexarray_append_pair(back, "vncdisplay",
- GCSPRINTF("%d", vfb->vnc.display));
- flexarray_append_pair(back, "vncunused",
- libxl_defbool_val(vfb->vnc.findunused) ? "1" : "0");
- flexarray_append_pair(back, "sdl",
- libxl_defbool_val(vfb->sdl.enable) ? "1" : "0");
- flexarray_append_pair(back, "opengl",
- libxl_defbool_val(vfb->sdl.opengl) ? "1" : "0");
- if (vfb->sdl.xauthority) {
- flexarray_append_pair(back, "xauthority", vfb->sdl.xauthority);
- }
- if (vfb->sdl.display) {
- flexarray_append_pair(back, "display", vfb->sdl.display);
- }
-
- return 0;
-}
-
-/* The following functions are defined:
- * libxl_device_vfb_remove
- * libxl_device_vfb_destroy
- */
-
-/* channel/console hotunplug is not implemented. There are 2 possibilities:
- * 1. add support for secondary consoles to xenconsoled
- * 2. dynamically add/remove qemu chardevs via qmp messages. */
-
-#define libxl__add_vfbs NULL
-#define libxl_device_vfb_list NULL
-#define libxl_device_vfb_compare NULL
-
-static LIBXL_DEFINE_UPDATE_DEVID(vfb)
-static LIBXL_DEFINE_DEVICE_FROM_TYPE(vfb)
-
-/* vfb */
-LIBXL_DEFINE_DEVICE_REMOVE(vfb)
-
-DEFINE_DEVICE_TYPE_STRUCT(vfb, VFB,
- .skip_attach = 1,
- .set_xenstore_config = (device_set_xenstore_config_fn_t)
- libxl__set_xenstore_vfb,
-);
-
-libxl_xen_console_reader *
- libxl_xen_console_read_start(libxl_ctx *ctx, int clear)
-{
- GC_INIT(ctx);
- libxl_xen_console_reader *cr;
- unsigned int size = 16384;
-
- cr = libxl__zalloc(NOGC, sizeof(libxl_xen_console_reader));
- cr->buffer = libxl__zalloc(NOGC, size);
- cr->size = size;
- cr->count = size;
- cr->clear = clear;
- cr->incremental = 1;
-
- GC_FREE;
- return cr;
-}
-
-/* return values: *line_r
- * 1 success, whole line obtained from buffer non-0
- * 0 no more lines available right now 0
- * negative error code ERROR_* 0
- * On success *line_r is updated to point to a nul-terminated
- * string which is valid until the next call on the same console
- * reader. The libxl caller may overwrite parts of the string
- * if it wishes. */
-int libxl_xen_console_read_line(libxl_ctx *ctx,
- libxl_xen_console_reader *cr,
- char **line_r)
-{
- int ret;
- GC_INIT(ctx);
-
- memset(cr->buffer, 0, cr->size);
- ret = xc_readconsolering(ctx->xch, cr->buffer, &cr->count,
- cr->clear, cr->incremental, &cr->index);
- if (ret < 0) {
- LOGE(ERROR, "reading console ring buffer");
- GC_FREE;
- return ERROR_FAIL;
- }
- if (!ret) {
- if (cr->count) {
- *line_r = cr->buffer;
- ret = 1;
- } else {
- *line_r = NULL;
- ret = 0;
- }
- }
-
- GC_FREE;
- return ret;
-}
-
-void libxl_xen_console_read_finish(libxl_ctx *ctx,
- libxl_xen_console_reader *cr)
-{
- free(cr->buffer);
- free(cr);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2014 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-/*
- * Infrastructure for converting a legacy migration stream into a
- * libxl v2 stream.
- *
- * This is done by fork()ing the python conversion script, which takes
- * in a legacy stream, and puts out a suitably-formatted v2 stream.
- */
-
-static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
- pid_t pid, int status);
-static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc);
-static void helper_done(libxl__egc *egc,
- libxl__conversion_helper_state *chs);
-
-/*----- Entrypoints -----*/
-
-void libxl__conversion_helper_init(libxl__conversion_helper_state *chs)
-{
- assert(chs->ao);
-
- chs->v2_carefd = NULL;
- chs->rc = 0;
- libxl__ao_abortable_init(&chs->abrt);
- libxl__ev_child_init(&chs->child);
-}
-
-int libxl__convert_legacy_stream(libxl__egc *egc,
- libxl__conversion_helper_state *chs)
-{
- STATE_AO_GC(chs->ao);
- libxl__carefd *child_in = NULL, *child_out = NULL;
- int rc = 0;
-
- chs->abrt.ao = chs->ao;
- chs->abrt.callback = helper_stop;
- rc = libxl__ao_abortable_register(&chs->abrt);
- if (rc) goto err;
-
- libxl__carefd_begin();
- int fds[2];
- if (libxl_pipe(CTX, fds)) {
- rc = ERROR_FAIL;
- libxl__carefd_unlock();
- goto err;
- }
- child_out = libxl__carefd_record(CTX, fds[0]);
- child_in = libxl__carefd_record(CTX, fds[1]);
- libxl__carefd_unlock();
-
- pid_t pid = libxl__ev_child_fork(gc, &chs->child, helper_exited);
- if (!pid) {
- char * const args[] =
- {
- getenv("LIBXL_CONVERT_HELPER") ?:
- LIBEXEC_BIN "/convert-legacy-stream",
- "--in", GCSPRINTF("%d", chs->legacy_fd),
- "--out", GCSPRINTF("%d", fds[1]),
- /*
- * The width calculation is an assumption for the common
- * case. The conversion script needs to know the width of
- * the toolstack which saved the legacy stream.
- *
- * In the overwhelming majority of cases, the width of the
- * saving toolstack will be the same as our current
- * width. To avoid extending the libxl API with a
- * parameter intended to disappear shortly, this option
- * has not been exposed to the caller.
- *
- * If more complicated conversion is required, the
- * conversion script can be instantiated manually, which
- * will bypass all of this conversion logic.
- */
- "--width", sizeof(unsigned long) == 8 ? "64" : "32",
-
- "--guest", chs->hvm ? "hvm" : "pv",
- "--format", "libxl",
- /* "--verbose", */
- NULL,
- };
-
- libxl_fd_set_cloexec(CTX, chs->legacy_fd, 0);
- libxl_fd_set_cloexec(CTX, libxl__carefd_fd(child_in), 0);
-
- libxl__exec(gc,
- -1, -1, -1,
- args[0], args, NULL);
- }
-
- libxl__carefd_close(child_in);
- chs->v2_carefd = child_out;
-
- assert(!rc);
- return rc;
-
- err:
- libxl__ao_abortable_deregister(&chs->abrt);
- assert(rc);
- return rc;
-}
-
-void libxl__conversion_helper_abort(libxl__egc *egc,
- libxl__conversion_helper_state *chs,
- int rc)
-{
- STATE_AO_GC(chs->ao);
- assert(rc);
-
- if (libxl__conversion_helper_inuse(chs)) {
-
- if (!chs->rc)
- chs->rc = rc;
-
- libxl__kill(gc, chs->child.pid, SIGTERM, "conversion helper");
- }
-}
-
-/*----- State handling -----*/
-
-static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
-{
- libxl__conversion_helper_state *chs = CONTAINER_OF(abrt, *chs, abrt);
- STATE_AO_GC(chs->ao);
-
- libxl__conversion_helper_abort(egc, chs, rc);
-}
-
-static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
- pid_t pid, int status)
-{
- libxl__conversion_helper_state *chs = CONTAINER_OF(ch, *chs, child);
- STATE_AO_GC(chs->ao);
-
- if (status) {
- libxl_report_child_exitstatus(
- CTX, chs->rc ? XTL_DEBUG : XTL_ERROR,
- "conversion helper", pid, status);
-
- if (!chs->rc)
- chs->rc = ERROR_FAIL;
- }
-
- helper_done(egc, chs);
-}
-
-static void helper_done(libxl__egc *egc,
- libxl__conversion_helper_state *chs)
-{
- STATE_AO_GC(chs->ao);
-
- assert(!libxl__conversion_helper_inuse(chs));
-
- libxl__ao_abortable_deregister(&chs->abrt);
-
- chs->completion_callback(egc, chs, chs->rc);
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl)
-{
- return !libxl_cpuid_policy_list_length(pl);
-}
-
-void libxl_cpuid_dispose(libxl_cpuid_policy_list *p_cpuid_list)
-{
- int i, j;
- libxl_cpuid_policy_list cpuid_list = *p_cpuid_list;
-
- if (cpuid_list == NULL)
- return;
- for (i = 0; cpuid_list[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
- for (j = 0; j < 4; j++)
- if (cpuid_list[i].policy[j] != NULL) {
- free(cpuid_list[i].policy[j]);
- cpuid_list[i].policy[j] = NULL;
- }
- }
- free(cpuid_list);
- *p_cpuid_list = NULL;
- return;
-}
-
-#define CPUID_REG_INV 0
-#define CPUID_REG_EAX 1
-#define CPUID_REG_EBX 2
-#define CPUID_REG_ECX 3
-#define CPUID_REG_EDX 4
-
-/* mapping CPUID features to names
- * holds a "name" for each feature, specified by the "leaf" number (and an
- * optional "subleaf" in ECX), the "reg"ister (EAX-EDX) used and a number of
- * bits starting with "bit" and being "length" bits long.
- * Used for the static structure describing all features.
- */
-struct cpuid_flags {
- char* name;
- uint32_t leaf;
- uint32_t subleaf;
- int reg;
- int bit;
- int length;
-};
-
-/* go through the dynamic array finding the entry for a specified leaf.
- * if no entry exists, allocate one and return that.
- */
-static libxl_cpuid_policy_list cpuid_find_match(libxl_cpuid_policy_list *list,
- uint32_t leaf, uint32_t subleaf)
-{
- int i = 0;
-
- if (*list != NULL) {
- for (i = 0; (*list)[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
- if ((*list)[i].input[0] == leaf && (*list)[i].input[1] == subleaf)
- return *list + i;
- }
- }
- *list = realloc(*list, sizeof((*list)[0]) * (i + 2));
- (*list)[i].input[0] = leaf;
- (*list)[i].input[1] = subleaf;
- memset((*list)[i].policy, 0, 4 * sizeof(char*));
- (*list)[i + 1].input[0] = XEN_CPUID_INPUT_UNUSED;
- return *list + i;
-}
-
-/* parse a single key=value pair and translate it into the libxc
- * used interface using 32-characters strings for each register.
- * Will overwrite earlier entries and thus can be called multiple
- * times.
- */
-int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str)
-{
-#define NA XEN_CPUID_INPUT_UNUSED
- static const struct cpuid_flags cpuid_flags[] = {
- {"maxleaf", 0x00000000, NA, CPUID_REG_EAX, 0, 32},
- /* the following two entries are subject to tweaking later in the code */
- {"stepping", 0x00000001, NA, CPUID_REG_EAX, 0, 4},
- {"model", 0x00000001, NA, CPUID_REG_EAX, 4, 8},
- {"family", 0x00000001, NA, CPUID_REG_EAX, 8, 8},
-
- {"brandid", 0x00000001, NA, CPUID_REG_EBX, 0, 8},
- {"clflush", 0x00000001, NA, CPUID_REG_EBX, 8, 8},
- {"proccount", 0x00000001, NA, CPUID_REG_EBX, 16, 8},
- {"localapicid", 0x00000001, NA, CPUID_REG_EBX, 24, 8},
-
- {"sse3", 0x00000001, NA, CPUID_REG_ECX, 0, 1},
- {"pclmulqdq", 0x00000001, NA, CPUID_REG_ECX, 1, 1},
- {"dtes64", 0x00000001, NA, CPUID_REG_ECX, 2, 1},
- {"monitor", 0x00000001, NA, CPUID_REG_ECX, 3, 1},
- {"dscpl", 0x00000001, NA, CPUID_REG_ECX, 4, 1},
- {"vmx", 0x00000001, NA, CPUID_REG_ECX, 5, 1},
- {"smx", 0x00000001, NA, CPUID_REG_ECX, 6, 1},
- {"est", 0x00000001, NA, CPUID_REG_ECX, 7, 1},
- {"tm2", 0x00000001, NA, CPUID_REG_ECX, 8, 1},
- {"ssse3", 0x00000001, NA, CPUID_REG_ECX, 9, 1},
- {"cntxid", 0x00000001, NA, CPUID_REG_ECX, 10, 1},
- {"fma", 0x00000001, NA, CPUID_REG_ECX, 12, 1},
- {"cmpxchg16", 0x00000001, NA, CPUID_REG_ECX, 13, 1},
- {"xtpr", 0x00000001, NA, CPUID_REG_ECX, 14, 1},
- {"pdcm", 0x00000001, NA, CPUID_REG_ECX, 15, 1},
- {"pcid", 0x00000001, NA, CPUID_REG_ECX, 17, 1},
- {"dca", 0x00000001, NA, CPUID_REG_ECX, 18, 1},
- /* Linux uses sse4_{1,2}. Keep sse4.{1,2} for compatibility */
- {"sse4_1", 0x00000001, NA, CPUID_REG_ECX, 19, 1},
- {"sse4.1", 0x00000001, NA, CPUID_REG_ECX, 19, 1},
- {"sse4_2", 0x00000001, NA, CPUID_REG_ECX, 20, 1},
- {"sse4.2", 0x00000001, NA, CPUID_REG_ECX, 20, 1},
- {"x2apic", 0x00000001, NA, CPUID_REG_ECX, 21, 1},
- {"movbe", 0x00000001, NA, CPUID_REG_ECX, 22, 1},
- {"popcnt", 0x00000001, NA, CPUID_REG_ECX, 23, 1},
- {"tsc-deadline", 0x00000001, NA, CPUID_REG_ECX, 24, 1},
- {"aes", 0x00000001, NA, CPUID_REG_ECX, 25, 1},
- {"xsave", 0x00000001, NA, CPUID_REG_ECX, 26, 1},
- {"osxsave", 0x00000001, NA, CPUID_REG_ECX, 27, 1},
- {"avx", 0x00000001, NA, CPUID_REG_ECX, 28, 1},
- {"f16c", 0x00000001, NA, CPUID_REG_ECX, 29, 1},
- {"rdrand", 0x00000001, NA, CPUID_REG_ECX, 30, 1},
- {"hypervisor", 0x00000001, NA, CPUID_REG_ECX, 31, 1},
-
- {"fpu", 0x00000001, NA, CPUID_REG_EDX, 0, 1},
- {"vme", 0x00000001, NA, CPUID_REG_EDX, 1, 1},
- {"de", 0x00000001, NA, CPUID_REG_EDX, 2, 1},
- {"pse", 0x00000001, NA, CPUID_REG_EDX, 3, 1},
- {"tsc", 0x00000001, NA, CPUID_REG_EDX, 4, 1},
- {"msr", 0x00000001, NA, CPUID_REG_EDX, 5, 1},
- {"pae", 0x00000001, NA, CPUID_REG_EDX, 6, 1},
- {"mce", 0x00000001, NA, CPUID_REG_EDX, 7, 1},
- {"cmpxchg8", 0x00000001, NA, CPUID_REG_EDX, 8, 1},
- {"apic", 0x00000001, NA, CPUID_REG_EDX, 9, 1},
- {"sysenter", 0x00000001, NA, CPUID_REG_EDX, 11, 1},
- {"mtrr", 0x00000001, NA, CPUID_REG_EDX, 12, 1},
- {"pge", 0x00000001, NA, CPUID_REG_EDX, 13, 1},
- {"mca", 0x00000001, NA, CPUID_REG_EDX, 14, 1},
- {"cmov", 0x00000001, NA, CPUID_REG_EDX, 15, 1},
- {"pat", 0x00000001, NA, CPUID_REG_EDX, 16, 1},
- {"pse36", 0x00000001, NA, CPUID_REG_EDX, 17, 1},
- {"psn", 0x00000001, NA, CPUID_REG_EDX, 18, 1},
- {"clfsh", 0x00000001, NA, CPUID_REG_EDX, 19, 1},
- {"ds", 0x00000001, NA, CPUID_REG_EDX, 21, 1},
- {"acpi", 0x00000001, NA, CPUID_REG_EDX, 22, 1},
- {"mmx", 0x00000001, NA, CPUID_REG_EDX, 23, 1},
- {"fxsr", 0x00000001, NA, CPUID_REG_EDX, 24, 1},
- {"sse", 0x00000001, NA, CPUID_REG_EDX, 25, 1},
- {"sse2", 0x00000001, NA, CPUID_REG_EDX, 26, 1},
- {"ss", 0x00000001, NA, CPUID_REG_EDX, 27, 1},
- {"htt", 0x00000001, NA, CPUID_REG_EDX, 28, 1},
- {"tm", 0x00000001, NA, CPUID_REG_EDX, 29, 1},
- {"ia64", 0x00000001, NA, CPUID_REG_EDX, 30, 1},
- {"pbe", 0x00000001, NA, CPUID_REG_EDX, 31, 1},
-
- {"arat", 0x00000006, NA, CPUID_REG_EAX, 2, 1},
-
- {"fsgsbase", 0x00000007, 0, CPUID_REG_EBX, 0, 1},
- {"tsc_adjust", 0x00000007, 0, CPUID_REG_EBX, 1, 1},
- {"bmi1", 0x00000007, 0, CPUID_REG_EBX, 3, 1},
- {"hle", 0x00000007, 0, CPUID_REG_EBX, 4, 1},
- {"avx2", 0x00000007, 0, CPUID_REG_EBX, 5, 1},
- {"smep", 0x00000007, 0, CPUID_REG_EBX, 7, 1},
- {"bmi2", 0x00000007, 0, CPUID_REG_EBX, 8, 1},
- {"erms", 0x00000007, 0, CPUID_REG_EBX, 9, 1},
- {"invpcid", 0x00000007, 0, CPUID_REG_EBX, 10, 1},
- {"rtm", 0x00000007, 0, CPUID_REG_EBX, 11, 1},
- {"cmt", 0x00000007, 0, CPUID_REG_EBX, 12, 1},
- {"mpx", 0x00000007, 0, CPUID_REG_EBX, 14, 1},
- {"avx512f", 0x00000007, 0, CPUID_REG_EBX, 16, 1},
- {"avx512dq", 0x00000007, 0, CPUID_REG_EBX, 17, 1},
- {"rdseed", 0x00000007, 0, CPUID_REG_EBX, 18, 1},
- {"adx", 0x00000007, 0, CPUID_REG_EBX, 19, 1},
- {"smap", 0x00000007, 0, CPUID_REG_EBX, 20, 1},
- {"avx512-ifma", 0x00000007, 0, CPUID_REG_EBX, 21, 1},
- {"clflushopt", 0x00000007, 0, CPUID_REG_EBX, 23, 1},
- {"clwb", 0x00000007, 0, CPUID_REG_EBX, 24, 1},
- {"avx512pf", 0x00000007, 0, CPUID_REG_EBX, 26, 1},
- {"avx512er", 0x00000007, 0, CPUID_REG_EBX, 27, 1},
- {"avx512cd", 0x00000007, 0, CPUID_REG_EBX, 28, 1},
- {"sha", 0x00000007, 0, CPUID_REG_EBX, 29, 1},
- {"avx512bw", 0x00000007, 0, CPUID_REG_EBX, 30, 1},
- {"avx512vl", 0x00000007, 0, CPUID_REG_EBX, 31, 1},
-
- {"prefetchwt1", 0x00000007, 0, CPUID_REG_ECX, 0, 1},
- {"avx512-vbmi", 0x00000007, 0, CPUID_REG_ECX, 1, 1},
- {"umip", 0x00000007, 0, CPUID_REG_ECX, 2, 1},
- {"pku", 0x00000007, 0, CPUID_REG_ECX, 3, 1},
- {"ospke", 0x00000007, 0, CPUID_REG_ECX, 4, 1},
- {"avx512-vbmi2", 0x00000007, 0, CPUID_REG_ECX, 6, 1},
- {"cet-ss", 0x00000007, 0, CPUID_REG_ECX, 7, 1},
- {"gfni", 0x00000007, 0, CPUID_REG_ECX, 8, 1},
- {"vaes", 0x00000007, 0, CPUID_REG_ECX, 9, 1},
- {"vpclmulqdq", 0x00000007, 0, CPUID_REG_ECX, 10, 1},
- {"avx512-vnni", 0x00000007, 0, CPUID_REG_ECX, 11, 1},
- {"avx512-bitalg",0x00000007, 0, CPUID_REG_ECX, 12, 1},
- {"avx512-vpopcntdq",0x00000007,0,CPUID_REG_ECX, 14, 1},
- {"tsxldtrk", 0x00000007, 0, CPUID_REG_ECX, 16, 1},
- {"rdpid", 0x00000007, 0, CPUID_REG_ECX, 22, 1},
- {"cldemote", 0x00000007, 0, CPUID_REG_ECX, 25, 1},
-
- {"avx512-4vnniw",0x00000007, 0, CPUID_REG_EDX, 2, 1},
- {"avx512-4fmaps",0x00000007, 0, CPUID_REG_EDX, 3, 1},
- {"avx512-vp2intersect",0x00000007,0,CPUID_REG_EDX,8, 1},
- {"srbds-ctrl", 0x00000007, 0, CPUID_REG_EDX, 9, 1},
- {"md-clear", 0x00000007, 0, CPUID_REG_EDX, 10, 1},
- {"serialize", 0x00000007, 0, CPUID_REG_EDX, 14, 1},
- {"cet-ibt", 0x00000007, 0, CPUID_REG_EDX, 20, 1},
- {"ibrsb", 0x00000007, 0, CPUID_REG_EDX, 26, 1},
- {"stibp", 0x00000007, 0, CPUID_REG_EDX, 27, 1},
- {"l1d-flush", 0x00000007, 0, CPUID_REG_EDX, 28, 1},
- {"arch-caps", 0x00000007, 0, CPUID_REG_EDX, 29, 1},
- {"core-caps", 0x00000007, 0, CPUID_REG_EDX, 30, 1},
- {"ssbd", 0x00000007, 0, CPUID_REG_EDX, 31, 1},
-
- {"avx512-bf16", 0x00000007, 1, CPUID_REG_EAX, 5, 1},
-
- {"lahfsahf", 0x80000001, NA, CPUID_REG_ECX, 0, 1},
- {"cmplegacy", 0x80000001, NA, CPUID_REG_ECX, 1, 1},
- {"svm", 0x80000001, NA, CPUID_REG_ECX, 2, 1},
- {"extapic", 0x80000001, NA, CPUID_REG_ECX, 3, 1},
- {"altmovcr8", 0x80000001, NA, CPUID_REG_ECX, 4, 1},
- {"abm", 0x80000001, NA, CPUID_REG_ECX, 5, 1},
- {"sse4a", 0x80000001, NA, CPUID_REG_ECX, 6, 1},
- {"misalignsse", 0x80000001, NA, CPUID_REG_ECX, 7, 1},
- {"3dnowprefetch",0x80000001, NA, CPUID_REG_ECX, 8, 1},
- {"osvw", 0x80000001, NA, CPUID_REG_ECX, 9, 1},
- {"ibs", 0x80000001, NA, CPUID_REG_ECX, 10, 1},
- {"xop", 0x80000001, NA, CPUID_REG_ECX, 11, 1},
- {"skinit", 0x80000001, NA, CPUID_REG_ECX, 12, 1},
- {"wdt", 0x80000001, NA, CPUID_REG_ECX, 13, 1},
- {"lwp", 0x80000001, NA, CPUID_REG_ECX, 15, 1},
- {"fma4", 0x80000001, NA, CPUID_REG_ECX, 16, 1},
- {"nodeid", 0x80000001, NA, CPUID_REG_ECX, 19, 1},
- {"tbm", 0x80000001, NA, CPUID_REG_ECX, 21, 1},
- {"topoext", 0x80000001, NA, CPUID_REG_ECX, 22, 1},
- {"perfctr_core", 0x80000001, NA, CPUID_REG_ECX, 23, 1},
- {"perfctr_nb", 0x80000001, NA, CPUID_REG_ECX, 24, 1},
-
- {"syscall", 0x80000001, NA, CPUID_REG_EDX, 11, 1},
- {"nx", 0x80000001, NA, CPUID_REG_EDX, 20, 1},
- {"mmxext", 0x80000001, NA, CPUID_REG_EDX, 22, 1},
- {"ffxsr", 0x80000001, NA, CPUID_REG_EDX, 25, 1},
- {"page1gb", 0x80000001, NA, CPUID_REG_EDX, 26, 1},
- {"rdtscp", 0x80000001, NA, CPUID_REG_EDX, 27, 1},
- {"lm", 0x80000001, NA, CPUID_REG_EDX, 29, 1},
- {"3dnowext", 0x80000001, NA, CPUID_REG_EDX, 30, 1},
- {"3dnow", 0x80000001, NA, CPUID_REG_EDX, 31, 1},
-
- {"procpkg", 0x00000004, 0, CPUID_REG_EAX, 26, 6},
-
- {"invtsc", 0x80000007, NA, CPUID_REG_EDX, 8, 1},
-
- {"clzero", 0x80000008, NA, CPUID_REG_EBX, 0, 1},
- {"rstr-fp-err-ptrs", 0x80000008, NA, CPUID_REG_EBX, 2, 1},
- {"wbnoinvd", 0x80000008, NA, CPUID_REG_EBX, 9, 1},
- {"ibpb", 0x80000008, NA, CPUID_REG_EBX, 12, 1},
- {"ppin", 0x80000008, NA, CPUID_REG_EBX, 23, 1},
-
- {"nc", 0x80000008, NA, CPUID_REG_ECX, 0, 8},
- {"apicidsize", 0x80000008, NA, CPUID_REG_ECX, 12, 4},
-
- {"svm_npt", 0x8000000a, NA, CPUID_REG_EDX, 0, 1},
- {"svm_lbrv", 0x8000000a, NA, CPUID_REG_EDX, 1, 1},
- {"svm_nrips", 0x8000000a, NA, CPUID_REG_EDX, 3, 1},
- {"svm_tscrate", 0x8000000a, NA, CPUID_REG_EDX, 4, 1},
- {"svm_vmcbclean",0x8000000a, NA, CPUID_REG_EDX, 5, 1},
- {"svm_decode", 0x8000000a, NA, CPUID_REG_EDX, 7, 1},
- {"svm_pausefilt",0x8000000a, NA, CPUID_REG_EDX, 10, 1},
-
- {"maxhvleaf", 0x40000000, NA, CPUID_REG_EAX, 0, 8},
-
- {NULL, 0, NA, CPUID_REG_INV, 0, 0}
- };
-#undef NA
- char *sep, *val, *endptr;
- int i;
- const struct cpuid_flags *flag;
- struct xc_xend_cpuid *entry;
- unsigned long num;
- char flags[33], *resstr;
-
- sep = strchr(str, '=');
- if (sep == NULL) {
- return 1;
- } else {
- val = sep + 1;
- }
- for (flag = cpuid_flags; flag->name != NULL; flag++) {
- if(!strncmp(str, flag->name, sep - str) && flag->name[sep - str] == 0)
- break;
- }
- if (flag->name == NULL) {
- return 2;
- }
- entry = cpuid_find_match(cpuid, flag->leaf, flag->subleaf);
- resstr = entry->policy[flag->reg - 1];
- num = strtoull(val, &endptr, 0);
- flags[flag->length] = 0;
- if (endptr != val) {
- /* if this was a valid number, write the binary form into the string */
- for (i = 0; i < flag->length; i++) {
- flags[flag->length - 1 - i] = "01"[!!(num & (1 << i))];
- }
- } else {
- switch(val[0]) {
- case 'x': case 'k': case 's':
- memset(flags, val[0], flag->length);
- break;
- default:
- return 3;
- }
- }
-
- if (resstr == NULL) {
- resstr = strdup("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
- }
-
- /* the family and model entry is potentially split up across
- * two fields in Fn0000_0001_EAX, so handle them here separately.
- */
- if (!strncmp(str, "family", sep - str)) {
- if (num < 16) {
- memcpy(resstr + (32 - 4) - flag->bit, flags + 4, 4);
- memcpy(resstr + (32 - 8) - 20, "00000000", 8);
- } else {
- num -= 15;
- memcpy(resstr + (32 - 4) - flag->bit, "1111", 4);
- for (i = 0; i < 7; i++) {
- flags[7 - i] = "01"[num & 1];
- num >>= 1;
- }
- memcpy(resstr + (32 - 8) - 20, flags, 8);
- }
- } else if (!strncmp(str, "model", sep - str)) {
- memcpy(resstr + (32 - 4) - 16, flags, 4);
- memcpy(resstr + (32 - 4) - flag->bit, flags + 4, 4);
- } else {
- memcpy(resstr + (32 - flag->length) - flag->bit, flags,
- flag->length);
- }
- entry->policy[flag->reg - 1] = resstr;
-
- return 0;
-}
-
-/* parse a single list item from the legacy Python xend syntax, where
- * the strings for each register were directly exposed to the user.
- * Used for maintaining compatibility with older config files
- */
-int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid,
- const char* str)
-{
- char *endptr;
- unsigned long value;
- uint32_t leaf, subleaf = XEN_CPUID_INPUT_UNUSED;
- struct xc_xend_cpuid *entry;
-
- /* parse the leaf number */
- value = strtoul(str, &endptr, 0);
- if (str == endptr) {
- return 1;
- }
- leaf = value;
- /* check for an optional subleaf number */
- if (*endptr == ',') {
- str = endptr + 1;
- value = strtoul(str, &endptr, 0);
- if (str == endptr) {
- return 2;
- }
- subleaf = value;
- }
- if (*endptr != ':') {
- return 3;
- }
- str = endptr + 1;
- entry = cpuid_find_match(cpuid, leaf, subleaf);
- for (str = endptr + 1; *str != 0;) {
- if (str[0] != 'e' || str[2] != 'x') {
- return 4;
- }
- value = str[1] - 'a';
- endptr = strchr(str, '=');
- if (value > 3 || endptr == NULL) {
- return 4;
- }
- str = endptr + 1;
- endptr = strchr(str, ',');
- if (endptr == NULL) {
- endptr = strchr(str, 0);
- }
- if (endptr - str != 32) {
- return 5;
- }
- entry->policy[value] = calloc(32 + 1, 1);
- strncpy(entry->policy[value], str, 32);
- entry->policy[value][32] = 0;
- if (*endptr == 0) {
- break;
- }
- for (str = endptr + 1; *str == ' ' || *str == '\n'; str++);
- }
- return 0;
-}
-
-void libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool restore,
- libxl_domain_build_info *info)
-{
- bool pae = true;
- bool itsc;
- bool nested_virt = libxl_defbool_val(info->nested_hvm);
-
- /*
- * For PV guests, PAE is Xen-controlled (it is the 'p' that differentiates
- * the xen-3.0-x86_32 and xen-3.0-x86_32p ABIs). It is mandatory as Xen
- * is 64bit only these days.
- *
- * For PVH guests, there is no top-level PAE control in the domain config,
- * so is treated as always available.
- *
- * HVM guests get a top-level choice of whether PAE is available.
- */
- if (info->type == LIBXL_DOMAIN_TYPE_HVM)
- pae = libxl_defbool_val(info->u.hvm.pae);
-
- /*
- * Advertising Invariant TSC to a guest means that the TSC frequency won't
- * change at any point in the future.
- *
- * We do not have enough information about potential migration
- * destinations to know whether advertising ITSC is safe, but if the guest
- * isn't going to migrate, then the current hardware is all that matters.
- *
- * Alternatively, an internal property of vTSC is that the values read are
- * invariant. Advertise ITSC when we know the domain will have emualted
- * TSC everywhere it goes.
- */
- itsc = (libxl_defbool_val(info->disable_migrate) ||
- info->tsc_mode == LIBXL_TSC_MODE_ALWAYS_EMULATE);
-
- xc_cpuid_apply_policy(ctx->xch, domid, restore, NULL, 0,
- pae, itsc, nested_virt, info->cpuid);
-}
-
-static const char *input_names[2] = { "leaf", "subleaf" };
-static const char *policy_names[4] = { "eax", "ebx", "ecx", "edx" };
-/*
- * Aiming for:
- * [
- * { 'leaf': 'val-eax',
- * 'subleaf': 'val-ecx',
- * 'eax': 'filter',
- * 'ebx': 'filter',
- * 'ecx': 'filter',
- * 'edx': 'filter' },
- * { 'leaf': 'val-eax', ..., 'eax': 'filter', ... },
- * ... etc ...
- * ]
- */
-
-yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
- libxl_cpuid_policy_list *pcpuid)
-{
- libxl_cpuid_policy_list cpuid = *pcpuid;
- yajl_gen_status s;
- int i, j;
-
- s = yajl_gen_array_open(hand);
- if (s != yajl_gen_status_ok) goto out;
-
- if (cpuid == NULL) goto empty;
-
- for (i = 0; cpuid[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
- s = yajl_gen_map_open(hand);
- if (s != yajl_gen_status_ok) goto out;
-
- for (j = 0; j < 2; j++) {
- if (cpuid[i].input[j] != XEN_CPUID_INPUT_UNUSED) {
- s = libxl__yajl_gen_asciiz(hand, input_names[j]);
- if (s != yajl_gen_status_ok) goto out;
- s = yajl_gen_integer(hand, cpuid[i].input[j]);
- if (s != yajl_gen_status_ok) goto out;
- }
- }
-
- for (j = 0; j < 4; j++) {
- if (cpuid[i].policy[j] != NULL) {
- s = libxl__yajl_gen_asciiz(hand, policy_names[j]);
- if (s != yajl_gen_status_ok) goto out;
- s = yajl_gen_string(hand,
- (const unsigned char *)cpuid[i].policy[j], 32);
- if (s != yajl_gen_status_ok) goto out;
- }
- }
- s = yajl_gen_map_close(hand);
- if (s != yajl_gen_status_ok) goto out;
- }
-
-empty:
- s = yajl_gen_array_close(hand);
-out:
- return s;
-}
-
-int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
- const libxl__json_object *o,
- libxl_cpuid_policy_list *p)
-{
- int i, size;
- libxl_cpuid_policy_list l;
- flexarray_t *array;
-
- if (!libxl__json_object_is_array(o))
- return ERROR_FAIL;
-
- array = libxl__json_object_get_array(o);
- if (!array->count)
- return 0;
-
- size = array->count;
- /* need one extra slot as sentinel */
- l = *p = libxl__calloc(NOGC, size + 1, sizeof(libxl_cpuid_policy));
-
- l[size].input[0] = XEN_CPUID_INPUT_UNUSED;
- l[size].input[1] = XEN_CPUID_INPUT_UNUSED;
-
- for (i = 0; i < size; i++) {
- const libxl__json_object *t;
- int j;
-
- if (flexarray_get(array, i, (void**)&t) != 0)
- return ERROR_FAIL;
-
- if (!libxl__json_object_is_map(t))
- return ERROR_FAIL;
-
- for (j = 0; j < ARRAY_SIZE(l[0].input); j++) {
- const libxl__json_object *r;
-
- r = libxl__json_map_get(input_names[j], t, JSON_INTEGER);
- if (!r)
- l[i].input[j] = XEN_CPUID_INPUT_UNUSED;
- else
- l[i].input[j] = libxl__json_object_get_integer(r);
- }
-
- for (j = 0; j < ARRAY_SIZE(l[0].policy); j++) {
- const libxl__json_object *r;
-
- r = libxl__json_map_get(policy_names[j], t, JSON_STRING);
- if (!r)
- l[i].policy[j] = NULL;
- else
- l[i].policy[j] =
- libxl__strdup(NOGC, libxl__json_object_get_string(r));
- }
- }
-
- return 0;
-}
-
-int libxl_cpuid_policy_list_length(const libxl_cpuid_policy_list *pl)
-{
- int i = 0;
- libxl_cpuid_policy_list l = *pl;
-
- if (l) {
- while (l[i].input[0] != XEN_CPUID_INPUT_UNUSED)
- i++;
- }
-
- return i;
-}
-
-void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
- libxl_cpuid_policy_list *dst,
- const libxl_cpuid_policy_list *src)
-{
- GC_INIT(ctx);
- int i, j, len;
-
- if (*src == NULL) {
- *dst = NULL;
- goto out;
- }
-
- len = libxl_cpuid_policy_list_length(src);
- /* one extra slot for sentinel */
- *dst = libxl__calloc(NOGC, len + 1, sizeof(libxl_cpuid_policy));
- (*dst)[len].input[0] = XEN_CPUID_INPUT_UNUSED;
- (*dst)[len].input[1] = XEN_CPUID_INPUT_UNUSED;
-
- for (i = 0; i < len; i++) {
- for (j = 0; j < 2; j++)
- (*dst)[i].input[j] = (*src)[i].input[j];
- for (j = 0; j < 4; j++)
- if ((*src)[i].policy[j])
- (*dst)[i].policy[j] =
- libxl__strdup(NOGC, (*src)[i].policy[j]);
- else
- (*dst)[i].policy[j] = NULL;
- }
-
-out:
- GC_FREE;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-/* Returns:
- * 0 - success
- * ERROR_FAIL + errno == ENOENT - no entry found
- * ERROR_$FOO + errno != ENOENT - other failure
- */
-static int cpupool_info(libxl__gc *gc,
- libxl_cpupoolinfo *info,
- uint32_t poolid,
- bool exact /* exactly poolid or >= poolid */)
-{
- xc_cpupoolinfo_t *xcinfo;
- int rc = ERROR_FAIL;
-
- xcinfo = xc_cpupool_getinfo(CTX->xch, poolid);
- if (xcinfo == NULL)
- {
- if (exact || errno != ENOENT)
- LOGE(ERROR, "failed to get info for cpupool%d", poolid);
- return ERROR_FAIL;
- }
-
- if (exact && xcinfo->cpupool_id != poolid)
- {
- LOG(ERROR, "got info for cpupool%d, wanted cpupool%d\n",
- xcinfo->cpupool_id, poolid);
- goto out;
- }
-
- info->poolid = xcinfo->cpupool_id;
- info->pool_name = libxl_cpupoolid_to_name(CTX, info->poolid);
- if (!info->pool_name) {
- rc = ERROR_FAIL;
- goto out;
- }
- info->sched = xcinfo->sched_id;
- info->n_dom = xcinfo->n_dom;
- rc = libxl_cpu_bitmap_alloc(CTX, &info->cpumap, 0);
- if (rc)
- goto out;
-
- memcpy(info->cpumap.map, xcinfo->cpumap, info->cpumap.size);
-
- rc = 0;
-out:
- xc_cpupool_infofree(CTX->xch, xcinfo);
- return rc;
-}
-
-int libxl_cpupool_info(libxl_ctx *ctx,
- libxl_cpupoolinfo *info, uint32_t poolid)
-{
- GC_INIT(ctx);
- int rc = cpupool_info(gc, info, poolid, true);
- GC_FREE;
- return rc;
-}
-
-libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx *ctx, int *nb_pool_out)
-{
- GC_INIT(ctx);
- libxl_cpupoolinfo info, *ptr;
-
- int i;
- uint32_t poolid;
-
- ptr = NULL;
-
- poolid = 0;
- for (i = 0;; i++) {
- libxl_cpupoolinfo_init(&info);
- if (cpupool_info(gc, &info, poolid, false)) {
- libxl_cpupoolinfo_dispose(&info);
- if (errno != ENOENT) goto out;
- break;
- }
-
- ptr = libxl__realloc(NOGC, ptr, (i+1) * sizeof(libxl_cpupoolinfo));
- ptr[i] = info;
- poolid = info.poolid + 1;
- /* Don't dispose of info because it will be returned to caller */
- }
-
- *nb_pool_out = i;
-
- GC_FREE;
- return ptr;
-
-out:
- libxl_cpupoolinfo_list_free(ptr, i);
- *nb_pool_out = 0;
- GC_FREE;
- return NULL;
-}
-
-int libxl_get_freecpus(libxl_ctx *ctx, libxl_bitmap *cpumap)
-{
- int ncpus;
-
- ncpus = libxl_get_max_cpus(ctx);
- if (ncpus < 0)
- return ncpus;
-
- cpumap->map = xc_cpupool_freeinfo(ctx->xch);
- if (cpumap->map == NULL)
- return ERROR_FAIL;
-
- cpumap->size = (ncpus + 7) / 8;
-
- return 0;
-}
-
-int libxl_cpupool_create(libxl_ctx *ctx, const char *name,
- libxl_scheduler sched,
- libxl_bitmap cpumap, libxl_uuid *uuid,
- uint32_t *poolid)
-{
- GC_INIT(ctx);
- int rc;
- int i;
- xs_transaction_t t;
- char *uuid_string;
- uint32_t xcpoolid;
-
- /* Accept '0' as 'any poolid' for backwards compatibility */
- if ( *poolid == LIBXL_CPUPOOL_POOLID_ANY
- || *poolid == 0 )
- xcpoolid = XC_CPUPOOL_POOLID_ANY;
- else
- xcpoolid = *poolid;
-
- uuid_string = libxl__uuid2string(gc, *uuid);
- if (!uuid_string) {
- GC_FREE;
- return ERROR_NOMEM;
- }
-
- rc = xc_cpupool_create(ctx->xch, &xcpoolid, sched);
- if (rc) {
- LOGEV(ERROR, rc, "Could not create cpupool");
- GC_FREE;
- return ERROR_FAIL;
- }
- *poolid = xcpoolid;
-
- libxl_for_each_bit(i, cpumap)
- if (libxl_bitmap_test(&cpumap, i)) {
- rc = xc_cpupool_addcpu(ctx->xch, *poolid, i);
- if (rc) {
- LOGEV(ERROR, rc, "Error moving cpu to cpupool");
- libxl_cpupool_destroy(ctx, *poolid);
- GC_FREE;
- return ERROR_FAIL;
- }
- }
-
- for (;;) {
- t = xs_transaction_start(ctx->xsh);
-
- xs_mkdir(ctx->xsh, t, GCSPRINTF("/local/pool/%d", *poolid));
- libxl__xs_printf(gc, t,
- GCSPRINTF("/local/pool/%d/uuid", *poolid),
- "%s", uuid_string);
- libxl__xs_printf(gc, t,
- GCSPRINTF("/local/pool/%d/name", *poolid),
- "%s", name);
-
- if (xs_transaction_end(ctx->xsh, t, 0) || (errno != EAGAIN)) {
- GC_FREE;
- return 0;
- }
- }
-}
-
-int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid)
-{
- GC_INIT(ctx);
- int rc, i;
- xc_cpupoolinfo_t *info;
- xs_transaction_t t;
- libxl_bitmap cpumap;
-
- info = xc_cpupool_getinfo(ctx->xch, poolid);
- if (info == NULL) {
- GC_FREE;
- return ERROR_NOMEM;
- }
-
- rc = ERROR_INVAL;
- if ((info->cpupool_id != poolid) || (info->n_dom))
- goto out;
-
- rc = libxl_cpu_bitmap_alloc(ctx, &cpumap, 0);
- if (rc)
- goto out;
-
- memcpy(cpumap.map, info->cpumap, cpumap.size);
- libxl_for_each_bit(i, cpumap)
- if (libxl_bitmap_test(&cpumap, i)) {
- rc = xc_cpupool_removecpu(ctx->xch, poolid, i);
- if (rc) {
- LOGEV(ERROR, rc, "Error removing cpu from cpupool");
- rc = ERROR_FAIL;
- goto out1;
- }
- }
-
- rc = xc_cpupool_destroy(ctx->xch, poolid);
- if (rc) {
- LOGEV(ERROR, rc, "Could not destroy cpupool");
- rc = ERROR_FAIL;
- goto out1;
- }
-
- for (;;) {
- t = xs_transaction_start(ctx->xsh);
-
- xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF("/local/pool/%d", poolid));
-
- if (xs_transaction_end(ctx->xsh, t, 0) || (errno != EAGAIN))
- break;
- }
-
- rc = 0;
-
-out1:
- libxl_bitmap_dispose(&cpumap);
-out:
- xc_cpupool_infofree(ctx->xch, info);
- GC_FREE;
-
- return rc;
-}
-
-int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid)
-{
- GC_INIT(ctx);
- xs_transaction_t t;
- xc_cpupoolinfo_t *info;
- int rc;
-
- info = xc_cpupool_getinfo(ctx->xch, poolid);
- if (info == NULL) {
- GC_FREE;
- return ERROR_NOMEM;
- }
-
- rc = ERROR_INVAL;
- if (info->cpupool_id != poolid)
- goto out;
-
- rc = 0;
-
- for (;;) {
- t = xs_transaction_start(ctx->xsh);
-
- libxl__xs_printf(gc, t,
- GCSPRINTF("/local/pool/%d/name", poolid),
- "%s", name);
-
- if (xs_transaction_end(ctx->xsh, t, 0))
- break;
-
- if (errno == EAGAIN)
- continue;
-
- rc = ERROR_FAIL;
- break;
- }
-
-out:
- xc_cpupool_infofree(ctx->xch, info);
- GC_FREE;
-
- return rc;
-}
-
-int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu)
-{
- GC_INIT(ctx);
- int rc = 0;
-
- rc = xc_cpupool_addcpu(ctx->xch, poolid, cpu);
- if (rc) {
- LOGE(ERROR, "Error moving cpu %d to cpupool", cpu);
- rc = ERROR_FAIL;
- }
-
- GC_FREE;
- return rc;
-}
-
-int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid,
- const libxl_bitmap *cpumap)
-{
- int c, ncpus = 0, rc = 0;
-
- libxl_for_each_set_bit(c, *cpumap) {
- if (!libxl_cpupool_cpuadd(ctx, poolid, c))
- ncpus++;
- }
-
- if (ncpus != libxl_bitmap_count_set(cpumap))
- rc = ERROR_FAIL;
-
- return rc;
-}
-
-int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus)
-{
- int rc = 0;
- int cpu, nr;
- libxl_bitmap freemap;
- libxl_cputopology *topology;
-
- if (libxl_get_freecpus(ctx, &freemap)) {
- return ERROR_FAIL;
- }
-
- topology = libxl_get_cpu_topology(ctx, &nr);
- if (!topology) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- *cpus = 0;
- for (cpu = 0; cpu < nr; cpu++) {
- if (libxl_bitmap_test(&freemap, cpu) && (topology[cpu].node == node) &&
- !libxl_cpupool_cpuadd(ctx, poolid, cpu)) {
- (*cpus)++;
- }
- libxl_cputopology_dispose(&topology[cpu]);
- }
-
- free(topology);
-out:
- libxl_bitmap_dispose(&freemap);
- return rc;
-}
-
-int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu)
-{
- GC_INIT(ctx);
- int rc = 0;
-
- rc = xc_cpupool_removecpu(ctx->xch, poolid, cpu);
- if (rc) {
- LOGE(ERROR, "Error removing cpu %d from cpupool", cpu);
- rc = ERROR_FAIL;
- }
-
- GC_FREE;
- return rc;
-}
-
-int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid,
- const libxl_bitmap *cpumap)
-{
- int c, ncpus = 0, rc = 0;
-
- libxl_for_each_set_bit(c, *cpumap) {
- if (!libxl_cpupool_cpuremove(ctx, poolid, c))
- ncpus++;
- }
-
- if (ncpus != libxl_bitmap_count_set(cpumap))
- rc = ERROR_FAIL;
-
- return rc;
-}
-
-int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus)
-{
- int ret = 0;
- int n_pools;
- int p;
- int cpu, nr_cpus;
- libxl_cputopology *topology;
- libxl_cpupoolinfo *poolinfo;
-
- poolinfo = libxl_list_cpupool(ctx, &n_pools);
- if (!poolinfo) {
- return ERROR_NOMEM;
- }
-
- topology = libxl_get_cpu_topology(ctx, &nr_cpus);
- if (!topology) {
- ret = ERROR_FAIL;
- goto out;
- }
-
- *cpus = 0;
- for (p = 0; p < n_pools; p++) {
- if (poolinfo[p].poolid == poolid) {
- for (cpu = 0; cpu < nr_cpus; cpu++) {
- if ((topology[cpu].node == node) &&
- libxl_bitmap_test(&poolinfo[p].cpumap, cpu) &&
- !libxl_cpupool_cpuremove(ctx, poolid, cpu)) {
- (*cpus)++;
- }
- }
- }
- }
-
- libxl_cputopology_list_free(topology, nr_cpus);
-
-out:
- libxl_cpupoolinfo_list_free(poolinfo, n_pools);
-
- return ret;
-}
-
-int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid)
-{
- GC_INIT(ctx);
- int rc;
-
- rc = xc_cpupool_movedomain(ctx->xch, poolid, domid);
- if (rc) {
- LOGEVD(ERROR, rc, domid, "Error moving domain to cpupool");
- GC_FREE;
- return ERROR_FAIL;
- }
-
- GC_FREE;
- return 0;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2010 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- * Author Gianni Tedesco <gianni.tedesco@citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-
-#include <xenctrl_dom.h>
-#include <xenguest.h>
-#include <xen/hvm/hvm_info_table.h>
-#include <xen/hvm/e820.h>
-
-#include <xen-xsm/flask/flask.h>
-
-int libxl__domain_create_info_setdefault(libxl__gc *gc,
- libxl_domain_create_info *c_info,
- const libxl_physinfo *info)
-{
- if (!c_info->type) {
- LOG(ERROR, "domain type unspecified");
- return ERROR_INVAL;
- }
-
- libxl__arch_domain_create_info_setdefault(gc, c_info);
-
- if (c_info->type != LIBXL_DOMAIN_TYPE_PV) {
- if (info->cap_hap) {
- libxl_defbool_setdefault(&c_info->hap, true);
- } else if (info->cap_shadow) {
- libxl_defbool_setdefault(&c_info->hap, false);
- } else {
- LOG(ERROR, "neither hap nor shadow paging available");
- return ERROR_INVAL;
- }
-
- libxl_defbool_setdefault(&c_info->oos, true);
- }
-
- libxl_defbool_setdefault(&c_info->run_hotplug_scripts, true);
- libxl_defbool_setdefault(&c_info->driver_domain, false);
-
- if (!c_info->ssidref)
- c_info->ssidref = SECINITSID_DOMU;
-
- libxl_defbool_setdefault(&c_info->xend_suspend_evtchn_compat, false);
-
- return 0;
-}
-
-void libxl__rdm_setdefault(libxl__gc *gc, libxl_domain_build_info *b_info)
-{
- if (b_info->u.hvm.rdm.policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
- b_info->u.hvm.rdm.policy = LIBXL_RDM_RESERVE_POLICY_RELAXED;
-
- if (b_info->u.hvm.rdm_mem_boundary_memkb == LIBXL_MEMKB_DEFAULT)
- b_info->u.hvm.rdm_mem_boundary_memkb =
- LIBXL_RDM_MEM_BOUNDARY_MEMKB_DEFAULT;
-}
-
-int libxl__domain_build_info_setdefault(libxl__gc *gc,
- libxl_domain_build_info *b_info)
-{
- int i, rc;
-
- if (b_info->type != LIBXL_DOMAIN_TYPE_HVM &&
- b_info->type != LIBXL_DOMAIN_TYPE_PV &&
- b_info->type != LIBXL_DOMAIN_TYPE_PVH) {
- LOG(ERROR, "invalid domain type");
- return ERROR_INVAL;
- }
-
- /* Copy deprecated options to it's new position. */
- rc = libxl__domain_build_info_copy_deprecated(CTX, b_info);
- if (rc) {
- LOG(ERROR, "Unable to copy deprecated fields");
- return rc;
- }
-
- libxl_defbool_setdefault(&b_info->device_model_stubdomain, false);
-
- if (libxl_defbool_val(b_info->device_model_stubdomain) &&
- !b_info->device_model_ssidref)
- b_info->device_model_ssidref = SECINITSID_DOMDM;
-
- if (!b_info->device_model_version) {
- if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
- if (libxl_defbool_val(b_info->device_model_stubdomain)) {
- b_info->device_model_version =
- LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
- } else {
- b_info->device_model_version = libxl__default_device_model(gc);
- }
- } else {
- b_info->device_model_version =
- LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
- }
- if (b_info->device_model_version
- == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
- const char *dm;
-
- dm = libxl__domain_device_model(gc, b_info);
- rc = access(dm, X_OK);
- if (rc < 0) {
- /* qemu-xen unavailable, use qemu-xen-traditional */
- if (errno == ENOENT) {
- LOGE(INFO, "qemu-xen is unavailable"
- ", using qemu-xen-traditional instead");
- b_info->device_model_version =
- LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
- } else {
- LOGE(ERROR, "qemu-xen access error");
- return ERROR_FAIL;
- }
- }
- }
- }
-
- if (b_info->blkdev_start == NULL)
- b_info->blkdev_start = libxl__strdup(NOGC, "xvda");
-
- if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
- if (!b_info->u.hvm.bios)
- switch (b_info->device_model_version) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- b_info->u.hvm.bios = LIBXL_BIOS_TYPE_ROMBIOS; break;
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- b_info->u.hvm.bios = LIBXL_BIOS_TYPE_SEABIOS; break;
- default:
- LOG(ERROR, "unknown device model version");
- return ERROR_INVAL;
- }
-
- /* Enforce BIOS<->Device Model version relationship */
- switch (b_info->device_model_version) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- if (b_info->u.hvm.bios != LIBXL_BIOS_TYPE_ROMBIOS) {
- LOG(ERROR, "qemu-xen-traditional requires bios=rombios.");
- return ERROR_INVAL;
- }
- break;
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- if (b_info->u.hvm.bios == LIBXL_BIOS_TYPE_ROMBIOS) {
- LOG(ERROR, "qemu-xen does not support bios=rombios.");
- return ERROR_INVAL;
- }
- break;
- default:abort();
- }
-
- /* Check HVM direct boot parameters, we should honour ->ramdisk and
- * ->cmdline iff ->kernel is set.
- */
- if (!b_info->kernel && (b_info->ramdisk || b_info->cmdline)) {
- LOG(ERROR, "direct boot parameters specified but kernel missing");
- return ERROR_INVAL;
- }
- }
-
- if (b_info->type == LIBXL_DOMAIN_TYPE_HVM &&
- libxl_defbool_val(b_info->device_model_stubdomain)) {
- if (!b_info->stubdomain_kernel) {
- switch (b_info->device_model_version) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- b_info->stubdomain_kernel =
- libxl__abs_path(NOGC, "ioemu-stubdom.gz", libxl__xenfirmwaredir_path());
- break;
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- b_info->stubdomain_kernel =
- libxl__abs_path(NOGC,
- "qemu-stubdom-linux-kernel",
- libxl__xenfirmwaredir_path());
- break;
- default:
- abort();
- }
- }
- if (!b_info->stubdomain_ramdisk) {
- switch (b_info->device_model_version) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- break;
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- b_info->stubdomain_ramdisk =
- libxl__abs_path(NOGC,
- "qemu-stubdom-linux-rootfs",
- libxl__xenfirmwaredir_path());
- break;
- default:
- abort();
- }
- }
- }
-
- if (!b_info->max_vcpus)
- b_info->max_vcpus = 1;
- if (!b_info->avail_vcpus.size) {
- if (libxl_cpu_bitmap_alloc(CTX, &b_info->avail_vcpus, 1)) {
- LOG(ERROR, "unable to allocate avail_vcpus bitmap");
- return ERROR_FAIL;
- }
- libxl_bitmap_set(&b_info->avail_vcpus, 0);
- } else if (b_info->avail_vcpus.size > HVM_MAX_VCPUS) {
- LOG(ERROR, "avail_vcpus bitmap contains too many VCPUS");
- return ERROR_FAIL;
- }
-
- /* In libxl internals, we want to deal with vcpu_hard_affinity only! */
- if (b_info->cpumap.size && !b_info->num_vcpu_hard_affinity) {
- b_info->vcpu_hard_affinity = libxl__calloc(gc, b_info->max_vcpus,
- sizeof(libxl_bitmap));
- for (i = 0; i < b_info->max_vcpus; i++) {
- if (libxl_cpu_bitmap_alloc(CTX, &b_info->vcpu_hard_affinity[i], 0)) {
- LOG(ERROR, "failed to allocate vcpu hard affinity bitmap");
- return ERROR_FAIL;
- }
- libxl_bitmap_copy(CTX, &b_info->vcpu_hard_affinity[i],
- &b_info->cpumap);
- }
- b_info->num_vcpu_hard_affinity = b_info->max_vcpus;
- }
-
- libxl_defbool_setdefault(&b_info->numa_placement, true);
-
- if (b_info->max_memkb == LIBXL_MEMKB_DEFAULT)
- b_info->max_memkb = 32 * 1024;
- if (b_info->target_memkb == LIBXL_MEMKB_DEFAULT)
- b_info->target_memkb = b_info->max_memkb;
-
- if (b_info->stubdomain_memkb == LIBXL_MEMKB_DEFAULT) {
- if (libxl_defbool_val(b_info->device_model_stubdomain)) {
- if (libxl__stubdomain_is_linux(b_info))
- b_info->stubdomain_memkb = LIBXL_LINUX_STUBDOM_MEM * 1024;
- else
- b_info->stubdomain_memkb = 28 * 1024; // MiniOS
- } else {
- b_info->stubdomain_memkb = 0; // no stubdomain
- }
- }
-
- libxl_defbool_setdefault(&b_info->claim_mode, false);
-
- libxl_defbool_setdefault(&b_info->localtime, false);
-
- libxl_defbool_setdefault(&b_info->disable_migrate, false);
-
- for (i = 0 ; i < b_info->num_iomem; i++)
- if (b_info->iomem[i].gfn == LIBXL_INVALID_GFN)
- b_info->iomem[i].gfn = b_info->iomem[i].start;
-
- if (!b_info->event_channels)
- b_info->event_channels = 1023;
-
- libxl__arch_domain_build_info_setdefault(gc, b_info);
- libxl_defbool_setdefault(&b_info->dm_restrict, false);
-
- if (b_info->iommu_memkb == LIBXL_MEMKB_DEFAULT)
- /* Normally defaulted in libxl__domain_create_info_setdefault */
- b_info->iommu_memkb = 0;
-
- switch (b_info->type) {
- case LIBXL_DOMAIN_TYPE_HVM:
- if (b_info->shadow_memkb == LIBXL_MEMKB_DEFAULT)
- /* Normally defaulted in libxl__domain_create_info_setdefault */
- b_info->shadow_memkb = 0;
- if (b_info->u.hvm.mmio_hole_memkb == LIBXL_MEMKB_DEFAULT)
- b_info->u.hvm.mmio_hole_memkb = 0;
-
- if (b_info->u.hvm.vga.kind == LIBXL_VGA_INTERFACE_TYPE_UNKNOWN) {
- b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_CIRRUS;
- }
-
- if (!b_info->u.hvm.hdtype)
- b_info->u.hvm.hdtype = LIBXL_HDTYPE_IDE;
-
- switch (b_info->device_model_version) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- switch (b_info->u.hvm.vga.kind) {
- case LIBXL_VGA_INTERFACE_TYPE_NONE:
- if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
- b_info->video_memkb = 0;
- break;
- case LIBXL_VGA_INTERFACE_TYPE_QXL:
- LOG(ERROR,"qemu upstream required for qxl vga");
- return ERROR_INVAL;
- break;
- case LIBXL_VGA_INTERFACE_TYPE_STD:
- if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
- b_info->video_memkb = 8 * 1024;
- if (b_info->video_memkb < 8 * 1024) {
- LOG(ERROR, "videoram must be at least 8 MB for STDVGA on QEMU_XEN_TRADITIONAL");
- return ERROR_INVAL;
- }
- break;
- case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
- default:
- if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
- b_info->video_memkb = 4 * 1024;
- if (b_info->video_memkb != 4 * 1024)
- LOG(WARN, "ignoring videoram other than 4 MB for CIRRUS on QEMU_XEN_TRADITIONAL");
- break;
- }
- break;
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- default:
- switch (b_info->u.hvm.vga.kind) {
- case LIBXL_VGA_INTERFACE_TYPE_NONE:
- if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
- b_info->video_memkb = 0;
- break;
- case LIBXL_VGA_INTERFACE_TYPE_QXL:
- if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) {
- b_info->video_memkb = (128 * 1024);
- } else if (b_info->video_memkb < (128 * 1024)) {
- LOG(ERROR,
- "128 Mib videoram is the minimum for qxl default");
- return ERROR_INVAL;
- }
- break;
- case LIBXL_VGA_INTERFACE_TYPE_STD:
- if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
- b_info->video_memkb = 16 * 1024;
- if (b_info->video_memkb < 16 * 1024) {
- LOG(ERROR, "videoram must be at least 16 MB for STDVGA on QEMU_XEN");
- return ERROR_INVAL;
- }
- break;
- case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
- default:
- if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
- b_info->video_memkb = 8 * 1024;
- if (b_info->video_memkb < 8 * 1024) {
- LOG(ERROR, "videoram must be at least 8 MB for CIRRUS on QEMU_XEN");
- return ERROR_INVAL;
- }
- break;
- }
- break;
- }
-
- libxl_defbool_setdefault(&b_info->u.hvm.pae, true);
- libxl_defbool_setdefault(&b_info->u.hvm.acpi, true);
- libxl_defbool_setdefault(&b_info->u.hvm.acpi_s3, true);
- libxl_defbool_setdefault(&b_info->u.hvm.acpi_s4, true);
- libxl_defbool_setdefault(&b_info->u.hvm.acpi_laptop_slate, false);
- libxl_defbool_setdefault(&b_info->u.hvm.nx, true);
- libxl_defbool_setdefault(&b_info->u.hvm.viridian, false);
- libxl_defbool_setdefault(&b_info->u.hvm.hpet, true);
- libxl_defbool_setdefault(&b_info->u.hvm.vpt_align, true);
- libxl_defbool_setdefault(&b_info->u.hvm.altp2m, false);
- libxl_defbool_setdefault(&b_info->u.hvm.usb, false);
- libxl_defbool_setdefault(&b_info->u.hvm.vkb_device, true);
- libxl_defbool_setdefault(&b_info->u.hvm.xen_platform_pci, true);
-
- libxl_defbool_setdefault(&b_info->u.hvm.spice.enable, false);
- if (!libxl_defbool_val(b_info->u.hvm.spice.enable) &&
- (b_info->u.hvm.spice.usbredirection > 0) ){
- b_info->u.hvm.spice.usbredirection = 0;
- LOG(WARN, "spice disabled, disabling usbredirection");
- }
-
- if (!b_info->u.hvm.usbversion &&
- (b_info->u.hvm.spice.usbredirection > 0) )
- b_info->u.hvm.usbversion = 2;
-
- if ((b_info->u.hvm.usbversion || b_info->u.hvm.spice.usbredirection) &&
- ( libxl_defbool_val(b_info->u.hvm.usb)
- || b_info->u.hvm.usbdevice_list
- || b_info->u.hvm.usbdevice) ){
- LOG(ERROR,"usbversion and/or usbredirection cannot be "
- "enabled with usb and/or usbdevice parameters.");
- return ERROR_INVAL;
- }
-
- if (!b_info->u.hvm.boot)
- b_info->u.hvm.boot = libxl__strdup(NOGC, "cda");
-
- libxl_defbool_setdefault(&b_info->u.hvm.vnc.enable, true);
- if (libxl_defbool_val(b_info->u.hvm.vnc.enable)) {
- libxl_defbool_setdefault(&b_info->u.hvm.vnc.findunused, true);
- if (!b_info->u.hvm.vnc.listen)
- b_info->u.hvm.vnc.listen = libxl__strdup(NOGC, "127.0.0.1");
- }
-
- libxl_defbool_setdefault(&b_info->u.hvm.sdl.enable, false);
- if (libxl_defbool_val(b_info->u.hvm.sdl.enable)) {
- libxl_defbool_setdefault(&b_info->u.hvm.sdl.opengl, false);
- }
-
- if (libxl_defbool_val(b_info->u.hvm.spice.enable)) {
- libxl_defbool_setdefault(&b_info->u.hvm.spice.disable_ticketing,
- false);
- libxl_defbool_setdefault(&b_info->u.hvm.spice.agent_mouse, true);
- libxl_defbool_setdefault(&b_info->u.hvm.spice.vdagent, false);
- libxl_defbool_setdefault(&b_info->u.hvm.spice.clipboard_sharing,
- false);
- }
-
- libxl_defbool_setdefault(&b_info->u.hvm.nographic, false);
-
- libxl_defbool_setdefault(&b_info->u.hvm.gfx_passthru, false);
-
- libxl__rdm_setdefault(gc, b_info);
- break;
- case LIBXL_DOMAIN_TYPE_PV:
- libxl_defbool_setdefault(&b_info->u.pv.e820_host, false);
- if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
- b_info->video_memkb = 0;
- if (b_info->shadow_memkb == LIBXL_MEMKB_DEFAULT)
- /* Normally defaulted in libxl__domain_create_info_setdefault */
- b_info->shadow_memkb = 0;
- if (b_info->u.pv.slack_memkb == LIBXL_MEMKB_DEFAULT)
- b_info->u.pv.slack_memkb = 0;
- break;
- case LIBXL_DOMAIN_TYPE_PVH:
- libxl_defbool_setdefault(&b_info->u.pvh.pvshim, false);
- if (libxl_defbool_val(b_info->u.pvh.pvshim)) {
- if (!b_info->u.pvh.pvshim_path)
- b_info->u.pvh.pvshim_path =
- libxl__sprintf(NOGC, "%s/%s",
- libxl__xenfirmwaredir_path(),
- PVSHIM_BASENAME);
- if (!b_info->u.pvh.pvshim_cmdline)
- b_info->u.pvh.pvshim_cmdline =
- libxl__strdup(NOGC, PVSHIM_CMDLINE);
- }
-
- break;
- default:
- LOG(ERROR, "invalid domain type %s in create info",
- libxl_domain_type_to_string(b_info->type));
- return ERROR_INVAL;
- }
-
- /* Configuration fields shared between PVH and HVM. */
- if (b_info->type != LIBXL_DOMAIN_TYPE_PV) {
- if (libxl__timer_mode_is_default(&b_info->timer_mode))
- b_info->timer_mode = LIBXL_TIMER_MODE_NO_DELAY_FOR_MISSED_TICKS;
-
- libxl_defbool_setdefault(&b_info->apic, true);
- libxl_defbool_setdefault(&b_info->nested_hvm, false);
- }
-
- return 0;
-}
-
-static void init_console_info(libxl__gc *gc,
- libxl__device_console *console,
- int dev_num)
-{
- libxl__device_console_init(console);
- console->devid = dev_num;
- console->consback = LIBXL__CONSOLE_BACKEND_XENCONSOLED;
- console->output = libxl__strdup(NOGC, "pty");
- /* console->{name,connection,path} are NULL on normal consoles.
- Only 'channels' when mapped to consoles have a string name. */
-}
-
-int libxl__domain_build(libxl__gc *gc,
- libxl_domain_config *d_config,
- uint32_t domid,
- libxl__domain_build_state *state)
-{
- libxl_domain_build_info *const info = &d_config->b_info;
- char **vments = NULL, **localents = NULL;
- struct timeval start_time;
- int i, ret;
-
- ret = libxl__build_pre(gc, domid, d_config, state);
- if (ret)
- goto out;
-
- gettimeofday(&start_time, NULL);
-
- switch (info->type) {
- case LIBXL_DOMAIN_TYPE_HVM:
- ret = libxl__build_hvm(gc, domid, d_config, state);
- if (ret)
- goto out;
-
- vments = libxl__calloc(gc, 7, sizeof(char *));
- vments[0] = "rtc/timeoffset";
- vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : "";
- vments[2] = "image/ostype";
- vments[3] = "hvm";
- vments[4] = "start_time";
- vments[5] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
-
- localents = libxl__calloc(gc, 13, sizeof(char *));
- i = 0;
- localents[i++] = "platform/acpi";
- localents[i++] = libxl__acpi_defbool_val(info) ? "1" : "0";
- localents[i++] = "platform/acpi_s3";
- localents[i++] = libxl_defbool_val(info->u.hvm.acpi_s3) ? "1" : "0";
- localents[i++] = "platform/acpi_s4";
- localents[i++] = libxl_defbool_val(info->u.hvm.acpi_s4) ? "1" : "0";
- localents[i++] = "platform/acpi_laptop_slate";
- localents[i++] = libxl_defbool_val(info->u.hvm.acpi_laptop_slate) ? "1" : "0";
- if (info->u.hvm.mmio_hole_memkb) {
- uint64_t max_ram_below_4g =
- (1ULL << 32) - (info->u.hvm.mmio_hole_memkb << 10);
-
- if (max_ram_below_4g <= HVM_BELOW_4G_MMIO_START) {
- localents[i++] = "platform/mmio_hole_size";
- localents[i++] =
- GCSPRINTF("%"PRIu64,
- info->u.hvm.mmio_hole_memkb << 10);
- }
- }
- localents[i++] = "platform/device-model";
- localents[i++] = (char *)libxl_device_model_version_to_string(info->device_model_version);
-
- break;
- case LIBXL_DOMAIN_TYPE_PV:
- ret = libxl__build_pv(gc, domid, d_config, state);
- if (ret)
- goto out;
-
- vments = libxl__calloc(gc, 11, sizeof(char *));
- i = 0;
- vments[i++] = "image/ostype";
- vments[i++] = "linux";
- vments[i++] = "image/kernel";
- vments[i++] = (char *) state->pv_kernel.path;
- vments[i++] = "start_time";
- vments[i++] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
- if (state->pv_ramdisk.path) {
- vments[i++] = "image/ramdisk";
- vments[i++] = (char *) state->pv_ramdisk.path;
- }
- if (state->pv_cmdline) {
- vments[i++] = "image/cmdline";
- vments[i++] = (char *) state->pv_cmdline;
- }
-
- break;
- case LIBXL_DOMAIN_TYPE_PVH:
- state->shim_path = info->u.pvh.pvshim_path;
- state->shim_cmdline = GCSPRINTF("%s%s%s",
- info->u.pvh.pvshim_cmdline,
- info->u.pvh.pvshim_extra ? " " : "",
- info->u.pvh.pvshim_extra ? info->u.pvh.pvshim_extra : "");
-
- ret = libxl__build_hvm(gc, domid, d_config, state);
- if (ret)
- goto out;
-
- vments = libxl__calloc(gc, 3, sizeof(char *));
- vments[0] = "start_time";
- vments[1] = GCSPRINTF("%"PRIu64".%02ld",
- (uint64_t)start_time.tv_sec,
- (long)start_time.tv_usec/10000);
-
- break;
- default:
- ret = ERROR_INVAL;
- goto out;
- }
- ret = libxl__build_post(gc, domid, info, state, vments, localents);
-out:
- return ret;
-}
-
-int libxl__domain_make(libxl__gc *gc, libxl_domain_config *d_config,
- libxl__domain_build_state *state,
- uint32_t *domid, bool soft_reset)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- int ret, rc, nb_vm;
- const char *dom_type;
- char *uuid_string;
- char *dom_path, *vm_path, *libxl_path;
- struct xs_permissions roperm[2];
- struct xs_permissions rwperm[1];
- struct xs_permissions noperm[1];
- xs_transaction_t t = 0;
- libxl_vminfo *vm_list;
-
- /* convenience aliases */
- libxl_domain_create_info *info = &d_config->c_info;
- libxl_domain_build_info *b_info = &d_config->b_info;
-
- assert(soft_reset || *domid == INVALID_DOMID);
-
- uuid_string = libxl__uuid2string(gc, info->uuid);
- if (!uuid_string) {
- rc = ERROR_NOMEM;
- goto out;
- }
-
- if (!soft_reset) {
- struct xen_domctl_createdomain create = {
- .ssidref = info->ssidref,
- .max_vcpus = b_info->max_vcpus,
- .max_evtchn_port = b_info->event_channels,
- .max_grant_frames = b_info->max_grant_frames,
- .max_maptrack_frames = b_info->max_maptrack_frames,
- };
-
- if (info->type != LIBXL_DOMAIN_TYPE_PV) {
- create.flags |= XEN_DOMCTL_CDF_hvm;
- create.flags |=
- libxl_defbool_val(info->hap) ? XEN_DOMCTL_CDF_hap : 0;
- create.flags |=
- libxl_defbool_val(info->oos) ? 0 : XEN_DOMCTL_CDF_oos_off;
- }
-
- assert(info->passthrough != LIBXL_PASSTHROUGH_DEFAULT);
- LOG(DETAIL, "passthrough: %s",
- libxl_passthrough_to_string(info->passthrough));
-
- if (info->passthrough != LIBXL_PASSTHROUGH_DISABLED)
- create.flags |= XEN_DOMCTL_CDF_iommu;
-
- if (info->passthrough == LIBXL_PASSTHROUGH_SYNC_PT)
- create.iommu_opts |= XEN_DOMCTL_IOMMU_no_sharept;
-
- /* Ultimately, handle is an array of 16 uint8_t, same as uuid */
- libxl_uuid_copy(ctx, (libxl_uuid *)&create.handle, &info->uuid);
-
- ret = libxl__arch_domain_prepare_config(gc, d_config, &create);
- if (ret < 0) {
- LOGED(ERROR, *domid, "fail to get domain config");
- rc = ERROR_FAIL;
- goto out;
- }
-
- for (;;) {
- uint32_t local_domid;
- bool recent;
-
- if (info->domid == RANDOM_DOMID) {
- uint16_t v;
-
- ret = libxl__random_bytes(gc, (void *)&v, sizeof(v));
- if (ret < 0)
- break;
-
- v &= DOMID_MASK;
- if (!libxl_domid_valid_guest(v))
- continue;
-
- local_domid = v;
- } else {
- local_domid = info->domid; /* May not be valid */
- }
-
- ret = xc_domain_create(ctx->xch, &local_domid, &create);
- if (ret < 0) {
- /*
- * If we generated a random domid and creation failed
- * because that domid already exists then simply try
- * again.
- */
- if (errno == EEXIST && info->domid == RANDOM_DOMID)
- continue;
-
- LOGED(ERROR, local_domid, "domain creation fail");
- rc = ERROR_FAIL;
- goto out;
- }
-
- /* A new domain now exists */
- *domid = local_domid;
-
- rc = libxl__is_domid_recent(gc, local_domid, &recent);
- if (rc)
- goto out;
-
- /* The domid is not recent, so we're done */
- if (!recent)
- break;
-
- /*
- * If the domid was specified then there's no point in
- * trying again.
- */
- if (libxl_domid_valid_guest(info->domid)) {
- LOGED(ERROR, local_domid, "domain id recently used");
- rc = ERROR_FAIL;
- goto out;
- }
-
- /*
- * The domain is recent and so cannot be used. Clear domid
- * here since, if xc_domain_destroy() fails below there is
- * little point calling it again in the error path.
- */
- *domid = INVALID_DOMID;
-
- ret = xc_domain_destroy(ctx->xch, local_domid);
- if (ret < 0) {
- LOGED(ERROR, local_domid, "domain destroy fail");
- rc = ERROR_FAIL;
- goto out;
- }
-
- /* The domain was successfully destroyed, so we can try again */
- }
-
- rc = libxl__arch_domain_save_config(gc, d_config, state, &create);
- if (rc < 0)
- goto out;
- }
-
- /*
- * If soft_reset is set the the domid will have been valid on entry.
- * If it was not set then xc_domain_create() should have assigned a
- * valid value. Either way, if we reach this point, domid should be
- * valid.
- */
- assert(libxl_domid_valid_guest(*domid));
-
- ret = xc_cpupool_movedomain(ctx->xch, info->poolid, *domid);
- if (ret < 0) {
- LOGED(ERROR, *domid, "domain move fail");
- rc = ERROR_FAIL;
- goto out;
- }
-
- dom_path = libxl__xs_get_dompath(gc, *domid);
- if (!dom_path) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- vm_path = GCSPRINTF("/vm/%s", uuid_string);
- if (!vm_path) {
- LOGD(ERROR, *domid, "cannot allocate create paths");
- rc = ERROR_FAIL;
- goto out;
- }
-
- libxl_path = libxl__xs_libxl_path(gc, *domid);
- if (!libxl_path) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- noperm[0].id = 0;
- noperm[0].perms = XS_PERM_NONE;
-
- roperm[0].id = 0;
- roperm[0].perms = XS_PERM_NONE;
- roperm[1].id = *domid;
- roperm[1].perms = XS_PERM_READ;
-
- rwperm[0].id = *domid;
- rwperm[0].perms = XS_PERM_NONE;
-
-retry_transaction:
- t = xs_transaction_start(ctx->xsh);
-
- xs_rm(ctx->xsh, t, dom_path);
- libxl__xs_mknod(gc, t, dom_path, roperm, ARRAY_SIZE(roperm));
-
- xs_rm(ctx->xsh, t, vm_path);
- libxl__xs_mknod(gc, t, vm_path, roperm, ARRAY_SIZE(roperm));
-
- xs_rm(ctx->xsh, t, libxl_path);
- libxl__xs_mknod(gc, t, libxl_path, noperm, ARRAY_SIZE(noperm));
- libxl__xs_mknod(gc, t, GCSPRINTF("%s/device", libxl_path),
- noperm, ARRAY_SIZE(noperm));
-
- xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
- rc = libxl__domain_rename(gc, *domid, 0, info->name, t);
- if (rc)
- goto out;
-
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/cpu", dom_path),
- roperm, ARRAY_SIZE(roperm));
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/memory", dom_path),
- roperm, ARRAY_SIZE(roperm));
-
- if (!libxl_defbool_val(info->xend_suspend_evtchn_compat)) {
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/device", dom_path),
- roperm, ARRAY_SIZE(roperm));
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/device/suspend/event-channel",
- dom_path),
- rwperm, ARRAY_SIZE(rwperm));
- } else {
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/device", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
- }
-
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/control", dom_path),
- roperm, ARRAY_SIZE(roperm));
- if (info->type == LIBXL_DOMAIN_TYPE_HVM)
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/hvmloader", dom_path),
- roperm, ARRAY_SIZE(roperm));
-
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/control/shutdown", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/control/feature-poweroff", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/control/feature-reboot", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/control/feature-suspend", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
- if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/control/feature-s3", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/control/feature-s4", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
- }
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/control/sysrq", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
-
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/data", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/drivers", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/feature", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/attr", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
- libxl__xs_mknod(gc, t,
- GCSPRINTF("%s/error", dom_path),
- rwperm, ARRAY_SIZE(rwperm));
-
- if (libxl_defbool_val(info->driver_domain)) {
- /*
- * Create a local "libxl" directory for each guest, since we might want
- * to use libxl from inside the guest
- */
- libxl__xs_mknod(gc, t, GCSPRINTF("%s/libxl", dom_path), rwperm,
- ARRAY_SIZE(rwperm));
- /*
- * Create a local "device-model" directory for each guest, since we
- * might want to use Qemu from inside the guest
- */
- libxl__xs_mknod(gc, t, GCSPRINTF("%s/device-model", dom_path), rwperm,
- ARRAY_SIZE(rwperm));
- }
-
- vm_list = libxl_list_vm(ctx, &nb_vm);
- if (!vm_list) {
- LOGD(ERROR, *domid, "cannot get number of running guests");
- rc = ERROR_FAIL;
- goto out;
- }
- libxl_vminfo_list_free(vm_list, nb_vm);
-
- xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
- xs_write(ctx->xsh, t, GCSPRINTF("%s/name", vm_path), info->name, strlen(info->name));
-
- libxl__xs_writev(gc, t, dom_path, info->xsdata);
- libxl__xs_writev(gc, t, GCSPRINTF("%s/platform", dom_path), info->platformdata);
-
- xs_write(ctx->xsh, t, GCSPRINTF("%s/control/platform-feature-multiprocessor-suspend", dom_path), "1", 1);
- xs_write(ctx->xsh, t, GCSPRINTF("%s/control/platform-feature-xs_reset_watches", dom_path), "1", 1);
-
- dom_type = libxl_domain_type_to_string(info->type);
- xs_write(ctx->xsh, t, GCSPRINTF("%s/type", libxl_path), dom_type,
- strlen(dom_type));
-
- if (!xs_transaction_end(ctx->xsh, t, 0)) {
- if (errno == EAGAIN) {
- t = 0;
- goto retry_transaction;
- }
- LOGED(ERROR, *domid, "domain creation ""xenstore transaction commit failed");
- rc = ERROR_FAIL;
- goto out;
- }
- t = 0;
-
- rc = 0;
- out:
- if (t) xs_transaction_end(ctx->xsh, t, 1);
- return rc;
-}
-
-static int store_libxl_entry(libxl__gc *gc, uint32_t domid,
- libxl_domain_build_info *b_info)
-{
- char *path = NULL;
-
- path = libxl__xs_libxl_path(gc, domid);
- path = GCSPRINTF("%s/dm-version", path);
- return libxl__xs_printf(gc, XBT_NULL, path, "%s",
- libxl_device_model_version_to_string(b_info->device_model_version));
-}
-
-void libxl__domain_build_state_init(libxl__domain_build_state *state)
-{
- state->dm_monitor_fd = -1;
-}
-
-void libxl__domain_build_state_dispose(libxl__domain_build_state *state)
-{
- libxl__file_reference_unmap(&state->pv_kernel);
- libxl__file_reference_unmap(&state->pv_ramdisk);
- if (state->dm_monitor_fd >= 0) {
- close(state->dm_monitor_fd);
- state->dm_monitor_fd = -1;
- }
-}
-
-/*----- main domain creation -----*/
-
-/* We have a linear control flow; only one event callback is
- * outstanding at any time. Each initiation and callback function
- * arranges for the next to be called, as the very last thing it
- * does. (If that particular sub-operation is not needed, a
- * function will call the next event callback directly.)
- */
-
-/* Event callbacks, in this order: */
-static void domcreate_bootloader_console_available(libxl__egc *egc,
- libxl__bootloader_state *bl);
-static void domcreate_console_available(libxl__egc *egc,
- libxl__domain_create_state *dcs);
-
-static void domcreate_bootloader_done(libxl__egc *egc,
- libxl__bootloader_state *bl,
- int rc);
-static void libxl__colo_restore_setup_done(libxl__egc *egc,
- libxl__colo_restore_state *crs,
- int rc);
-static void domcreate_stream_done(libxl__egc *egc,
- libxl__stream_read_state *srs,
- int ret);
-static void domcreate_rebuild_done(libxl__egc *egc,
- libxl__domain_create_state *dcs,
- int ret);
-static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *aodevs,
- int ret);
-static void domcreate_devmodel_started(libxl__egc *egc,
- libxl__dm_spawn_state *dmss,
- int rc);
-static void domcreate_attach_devices(libxl__egc *egc,
- libxl__multidev *multidev,
- int ret);
-static void console_xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa,
- int rc, const char *p);
-
-/* Our own function to clean up and call the user's callback.
- * The final call in the sequence. */
-static void domcreate_complete(libxl__egc *egc,
- libxl__domain_create_state *dcs,
- int rc);
-
-/* If creation is not successful, this callback will be executed
- * when domain destruction is finished */
-static void domcreate_destruction_cb(libxl__egc *egc,
- libxl__domain_destroy_state *dds,
- int rc);
-
-static bool ok_to_default_memkb_in_create(libxl__gc *gc)
-{
- /*
- * This is a fudge. We are trying to find whether the caller
- * calls the old version of libxl_domain_need_memory. If they do
- * then, because it only gets the b_info, and because it can't
- * update the b_info (because it's const), it will base its
- * calculations on defaulting shadow_memkb and iommu_memkb to 0
- * In that case we probably shouldn't default them differently
- * during libxl_domain_create.
- *
- * The result is that the behaviour with old callers is the same
- * as in 4.13: no additional memory is allocated for shadow and
- * iommu (unless the caller set shadow_memkb, eg from a call to
- * libxl_get_required_shadow_memory).
- */
- return !CTX->libxl_domain_need_memory_0x041200_called ||
- CTX->libxl_domain_need_memory_called;
- /*
- * Treat mixed callers as new callers. Presumably they know what
- * they are doing.
- */
-}
-
-static unsigned long libxl__get_required_iommu_memory(unsigned long maxmem_kb)
-{
- unsigned long iommu_pages = 0, mem_pages = maxmem_kb / 4;
- unsigned int level;
-
- /* Assume a 4 level page table with 512 entries per level */
- for (level = 0; level < 4; level++)
- {
- mem_pages = DIV_ROUNDUP(mem_pages, 512);
- iommu_pages += mem_pages;
- }
-
- return iommu_pages * 4;
-}
-
-int libxl__domain_config_setdefault(libxl__gc *gc,
- libxl_domain_config *d_config,
- uint32_t domid /* for logging, only */)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- int ret;
- bool pod_enabled = false;
- libxl_domain_create_info *c_info = &d_config->c_info;
-
- libxl_physinfo physinfo;
- ret = libxl_get_physinfo(CTX, &physinfo);
- if (ret) goto error_out;
-
- if (d_config->c_info.ssid_label) {
- char *s = d_config->c_info.ssid_label;
- ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
- &d_config->c_info.ssidref);
- if (ret) {
- if (errno == ENOSYS) {
- LOGD(WARN, domid, "XSM Disabled: init_seclabel not supported");
- ret = 0;
- } else {
- LOGD(ERROR, domid, "Invalid init_seclabel: %s", s);
- goto error_out;
- }
- }
- }
-
- if (d_config->b_info.exec_ssid_label) {
- char *s = d_config->b_info.exec_ssid_label;
- ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
- &d_config->b_info.exec_ssidref);
- if (ret) {
- if (errno == ENOSYS) {
- LOGD(WARN, domid, "XSM Disabled: seclabel not supported");
- ret = 0;
- } else {
- LOGD(ERROR, domid, "Invalid seclabel: %s", s);
- goto error_out;
- }
- }
- }
-
- if (d_config->b_info.device_model_ssid_label) {
- char *s = d_config->b_info.device_model_ssid_label;
- ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
- &d_config->b_info.device_model_ssidref);
- if (ret) {
- if (errno == ENOSYS) {
- LOGD(WARN, domid,
- "XSM Disabled: device_model_stubdomain_seclabel not supported");
- ret = 0;
- } else {
- LOGD(ERROR, domid, "Invalid device_model_stubdomain_seclabel: %s", s);
- goto error_out;
- }
- }
- }
-
- if (d_config->c_info.pool_name) {
- d_config->c_info.poolid = -1;
- libxl_cpupool_qualifier_to_cpupoolid(ctx, d_config->c_info.pool_name,
- &d_config->c_info.poolid,
- NULL);
- }
- if (!libxl_cpupoolid_is_valid(ctx, d_config->c_info.poolid)) {
- LOGD(ERROR, domid, "Illegal pool specified: %s",
- d_config->c_info.pool_name);
- ret = ERROR_INVAL;
- goto error_out;
- }
-
- ret = libxl__domain_create_info_setdefault(gc, &d_config->c_info,
- &physinfo);
- if (ret) {
- LOGD(ERROR, domid, "Unable to set domain create info defaults");
- goto error_out;
- }
-
- bool need_pt = d_config->num_pcidevs || d_config->num_dtdevs;
- if (c_info->passthrough == LIBXL_PASSTHROUGH_DEFAULT) {
- c_info->passthrough = need_pt
- ? LIBXL_PASSTHROUGH_ENABLED : LIBXL_PASSTHROUGH_DISABLED;
- }
-
- bool iommu_enabled = physinfo.cap_hvm_directio;
- if (c_info->passthrough != LIBXL_PASSTHROUGH_DISABLED && !iommu_enabled) {
- LOGD(ERROR, domid,
- "passthrough not supported on this platform\n");
- ret = ERROR_INVAL;
- goto error_out;
- }
-
- if (c_info->passthrough == LIBXL_PASSTHROUGH_DISABLED && need_pt) {
- LOGD(ERROR, domid,
- "passthrough disabled but devices are specified");
- ret = ERROR_INVAL;
- goto error_out;
- }
-
- ret = libxl__arch_passthrough_mode_setdefault(gc,domid,d_config,&physinfo);
- if (ret) goto error_out;
-
- /* An explicit setting should now have been chosen */
- assert(c_info->passthrough != LIBXL_PASSTHROUGH_DEFAULT);
- assert(c_info->passthrough != LIBXL_PASSTHROUGH_ENABLED);
-
- /* If target_memkb is smaller than max_memkb, the subsequent call
- * to libxc when building HVM domain will enable PoD mode.
- */
- pod_enabled = (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV) &&
- (d_config->b_info.target_memkb < d_config->b_info.max_memkb);
-
- /* We cannot have PoD and PCI device assignment at the same time
- * for HVM guest. It was reported that IOMMU cannot work with PoD
- * enabled because it needs to populated entire page table for
- * guest. To stay on the safe side, we disable PCI device
- * assignment when PoD is enabled.
- */
- if (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
- d_config->num_pcidevs && pod_enabled) {
- ret = ERROR_INVAL;
- LOGD(ERROR, domid,
- "PCI device assignment for HVM guest failed due to PoD enabled");
- goto error_out;
- }
-
- /* Disallow PoD and vNUMA to be enabled at the same time because PoD
- * pool is not vNUMA-aware yet.
- */
- if (pod_enabled && d_config->b_info.num_vnuma_nodes) {
- ret = ERROR_INVAL;
- LOGD(ERROR, domid, "Cannot enable PoD and vNUMA at the same time");
- goto error_out;
- }
-
- /* PV vNUMA is not yet supported because there is an issue with
- * cpuid handling.
- */
- if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_PV &&
- d_config->b_info.num_vnuma_nodes) {
- ret = ERROR_INVAL;
- LOGD(ERROR, domid, "PV vNUMA is not yet supported");
- goto error_out;
- }
-
- if (d_config->b_info.shadow_memkb == LIBXL_MEMKB_DEFAULT
- && ok_to_default_memkb_in_create(gc))
- d_config->b_info.shadow_memkb =
- libxl_get_required_shadow_memory(d_config->b_info.max_memkb,
- d_config->b_info.max_vcpus);
-
- /* No IOMMU reservation is needed if passthrough mode is not 'sync_pt' */
- if (d_config->b_info.iommu_memkb == LIBXL_MEMKB_DEFAULT
- && ok_to_default_memkb_in_create(gc))
- d_config->b_info.iommu_memkb =
- (d_config->c_info.passthrough == LIBXL_PASSTHROUGH_SYNC_PT)
- ? libxl__get_required_iommu_memory(d_config->b_info.max_memkb)
- : 0;
-
- ret = libxl__domain_build_info_setdefault(gc, &d_config->b_info);
- if (ret) {
- LOGD(ERROR, domid, "Unable to set domain build info defaults");
- goto error_out;
- }
-
- if (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
- (libxl_defbool_val(d_config->b_info.nested_hvm) &&
- ((d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM &&
- libxl_defbool_val(d_config->b_info.u.hvm.altp2m)) ||
- (d_config->b_info.altp2m != LIBXL_ALTP2M_MODE_DISABLED)))) {
- ret = ERROR_INVAL;
- LOGD(ERROR, domid, "nestedhvm and altp2mhvm cannot be used together");
- goto error_out;
- }
-
- if (((d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM &&
- libxl_defbool_val(d_config->b_info.u.hvm.altp2m)) ||
- (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
- d_config->b_info.altp2m != LIBXL_ALTP2M_MODE_DISABLED)) &&
- pod_enabled) {
- ret = ERROR_INVAL;
- LOGD(ERROR, domid, "Cannot enable PoD and ALTP2M at the same time");
- goto error_out;
- }
-
- ret = 0;
- error_out:
- return ret;
-}
-
-static void initiate_domain_create(libxl__egc *egc,
- libxl__domain_create_state *dcs)
-{
- STATE_AO_GC(dcs->ao);
- uint32_t domid;
- int i, ret;
-
- /* convenience aliases */
- libxl_domain_config *const d_config = dcs->guest_config;
- libxl__domain_build_state *dbs = &dcs->build_state;
-
- libxl__xswait_init(&dcs->console_xswait);
-
- domid = dcs->domid;
- libxl__domain_build_state_init(dbs);
- dbs->restore = dcs->restore_fd >= 0;
-
- ret = libxl__domain_config_setdefault(gc,d_config,domid);
- if (ret) goto error_out;
-
- ret = libxl__domain_make(gc, d_config, dbs, &domid, dcs->soft_reset);
- if (ret) {
- LOGD(ERROR, domid, "cannot make domain: %d", ret);
- dcs->guest_domid = domid;
- ret = ERROR_FAIL;
- goto error_out;
- }
-
- dcs->guest_domid = domid;
- dcs->sdss.dm.guest_domid = 0; /* means we haven't spawned */
-
- /* post-4.13 todo: move these next bits of defaulting to
- * libxl__domain_config_setdefault */
-
- /*
- * Set the dm version quite early so that libxl doesn't have to pass the
- * build info around just to know if the domain has a device model or not.
- */
- store_libxl_entry(gc, domid, &d_config->b_info);
-
- for (i = 0; i < d_config->num_disks; i++) {
- ret = libxl__disk_devtype.set_default(gc, domid, &d_config->disks[i],
- false);
- if (ret) {
- LOGD(ERROR, domid, "Unable to set disk defaults for disk %d", i);
- goto error_out;
- }
- }
-
- dcs->bl.ao = ao;
- libxl_device_disk *bootdisk =
- d_config->num_disks > 0 ? &d_config->disks[0] : NULL;
-
- /*
- * The devid has to be set before launching the device model. For the
- * hotplug case this is done in libxl_device_nic_add but on domain
- * creation this is called too late.
- * Make two runs over configured NICs in order to avoid duplicate IDs
- * in case the caller partially assigned IDs.
- */
- ret = libxl__device_nic_set_devids(gc, d_config, domid);
- if (ret)
- goto error_out;
-
- if (dbs->restore || dcs->soft_reset) {
- LOGD(DEBUG, domid, "restoring, not running bootloader");
- domcreate_bootloader_done(egc, &dcs->bl, 0);
- } else {
- LOGD(DEBUG, domid, "running bootloader");
- dcs->bl.callback = domcreate_bootloader_done;
- dcs->bl.console_available = domcreate_bootloader_console_available;
- dcs->bl.info = &d_config->b_info;
- dcs->bl.disk = bootdisk;
- dcs->bl.domid = dcs->guest_domid;
-
- dcs->bl.kernel = &dbs->pv_kernel;
- dcs->bl.ramdisk = &dbs->pv_ramdisk;
-
- libxl__bootloader_run(egc, &dcs->bl);
- }
- return;
-
-error_out:
- assert(ret);
- domcreate_complete(egc, dcs, ret);
-}
-
-static void domcreate_bootloader_console_available(libxl__egc *egc,
- libxl__bootloader_state *bl)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(bl, *dcs, bl);
- STATE_AO_GC(bl->ao);
- domcreate_console_available(egc, dcs);
-}
-
-static void domcreate_console_available(libxl__egc *egc,
- libxl__domain_create_state *dcs) {
- libxl__ao_progress_report(egc, dcs->ao, &dcs->aop_console_how,
- NEW_EVENT(egc, DOMAIN_CREATE_CONSOLE_AVAILABLE,
- dcs->guest_domid,
- dcs->aop_console_how.for_event));
-}
-
-static void domcreate_bootloader_done(libxl__egc *egc,
- libxl__bootloader_state *bl,
- int rc)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(bl, *dcs, bl);
- STATE_AO_GC(bl->ao);
-
- /* convenience aliases */
- const uint32_t domid = dcs->guest_domid;
- libxl_domain_config *const d_config = dcs->guest_config;
- const int restore_fd = dcs->restore_fd;
- libxl__domain_build_state *const state = &dcs->build_state;
- const int checkpointed_stream = dcs->restore_params.checkpointed_stream;
- libxl__colo_restore_state *const crs = &dcs->crs;
- libxl_domain_build_info *const info = &d_config->b_info;
- libxl__srm_restore_autogen_callbacks *const callbacks =
- &dcs->srs.shs.callbacks.restore.a;
-
- if (rc) {
- domcreate_rebuild_done(egc, dcs, rc);
- return;
- }
-
- /* consume bootloader outputs. state->pv_{kernel,ramdisk} have
- * been initialised by the bootloader already.
- */
- state->pv_cmdline = bl->cmdline;
-
- /* We might be going to call libxl__spawn_local_dm, or _spawn_stub_dm.
- * Fill in any field required by either, including both relevant
- * callbacks (_spawn_stub_dm will overwrite our trespass if needed). */
- dcs->sdss.dm.spawn.ao = ao;
- dcs->sdss.dm.guest_config = dcs->guest_config;
- dcs->sdss.dm.build_state = &dcs->build_state;
- dcs->sdss.dm.callback = domcreate_devmodel_started;
- dcs->sdss.callback = domcreate_devmodel_started;
-
- if (restore_fd < 0 && !dcs->soft_reset) {
- rc = libxl__domain_build(gc, d_config, domid, state);
- domcreate_rebuild_done(egc, dcs, rc);
- return;
- }
-
- /* Prepare environment for domcreate_stream_done */
- dcs->srs.dcs = dcs;
-
- /* Restore */
- callbacks->static_data_done = libxl__srm_callout_callback_static_data_done;
- callbacks->restore_results = libxl__srm_callout_callback_restore_results;
-
- /* COLO only supports HVM now because it does not work very
- * well with pv drivers:
- * 1. We need to resume vm in the slow path. In this case we
- * need to disconnect/reconnect backend and frontend. It
- * will take too much time and the performance is very slow.
- * 2. PV disk cannot reuse block replication that is implemented
- * in QEMU.
- */
- if (info->type != LIBXL_DOMAIN_TYPE_HVM &&
- checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) {
- LOGD(ERROR, domid, "COLO only supports HVM, unable to restore domain");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__build_pre(gc, domid, d_config, state);
- if (rc)
- goto out;
-
- dcs->srs.ao = ao;
- dcs->srs.fd = restore_fd;
- dcs->srs.legacy = (dcs->restore_params.stream_version == 1);
- dcs->srs.back_channel = false;
- dcs->srs.completion_callback = domcreate_stream_done;
-
- if (restore_fd >= 0) {
- switch (checkpointed_stream) {
- case LIBXL_CHECKPOINTED_STREAM_COLO:
- /* colo restore setup */
- crs->ao = ao;
- crs->domid = domid;
- crs->send_back_fd = dcs->send_back_fd;
- crs->recv_fd = restore_fd;
- crs->hvm = (info->type != LIBXL_DOMAIN_TYPE_PV);
- crs->callback = libxl__colo_restore_setup_done;
- libxl__colo_restore_setup(egc, crs);
- break;
- case LIBXL_CHECKPOINTED_STREAM_REMUS:
- libxl__remus_restore_setup(egc, dcs);
- /* fall through */
- case LIBXL_CHECKPOINTED_STREAM_NONE:
- libxl__stream_read_start(egc, &dcs->srs);
- }
- return;
- }
-
- out:
- domcreate_stream_done(egc, &dcs->srs, rc);
-}
-
-static void libxl__colo_restore_setup_done(libxl__egc *egc,
- libxl__colo_restore_state *crs,
- int rc)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, dcs->guest_domid, "colo restore setup fails: %d", rc);
- domcreate_stream_done(egc, &dcs->srs, rc);
- return;
- }
-
- libxl__stream_read_start(egc, &dcs->srs);
-}
-
-int libxl__srm_callout_callback_static_data_done(unsigned int missing,
- void *user)
-{
- libxl__save_helper_state *shs = user;
- libxl__domain_create_state *dcs = shs->caller_state;
- STATE_AO_GC(dcs->ao);
- libxl_ctx *ctx = libxl__gc_owner(gc);
-
- libxl_domain_config *d_config = dcs->guest_config;
- libxl_domain_build_info *info = &d_config->b_info;
-
- /*
- * CPUID/MSR information is not present in pre Xen-4.14 streams.
- *
- * Libxl used to always regenerate the CPUID policy from first principles
- * on migrate. Continue to do so for backwards compatibility when the
- * stream doesn't contain any CPUID data.
- */
- if (missing & XGR_SDD_MISSING_CPUID)
- libxl__cpuid_legacy(ctx, dcs->guest_domid, true, info);
-
- return 0;
-}
-
-void libxl__srm_callout_callback_restore_results(xen_pfn_t store_mfn,
- xen_pfn_t console_mfn, void *user)
-{
- libxl__save_helper_state *shs = user;
- libxl__domain_create_state *dcs = shs->caller_state;
- STATE_AO_GC(dcs->ao);
- libxl__domain_build_state *const state = &dcs->build_state;
-
- state->store_mfn = store_mfn;
- state->console_mfn = console_mfn;
- shs->need_results = 0;
-}
-
-static void domcreate_stream_done(libxl__egc *egc,
- libxl__stream_read_state *srs,
- int ret)
-{
- /* NB perhaps only srs->dcs is valid; eg in the case of an
- * early branch to domcreate_bootloader_done's `out' block */
- libxl__domain_create_state *dcs = srs->dcs;
- STATE_AO_GC(dcs->ao);
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char **vments = NULL, **localents = NULL;
- struct timeval start_time;
- int i, esave;
-
- /* convenience aliases */
- const uint32_t domid = dcs->guest_domid;
- libxl_domain_config *const d_config = dcs->guest_config;
- libxl_domain_build_info *const info = &d_config->b_info;
- libxl__domain_build_state *const state = &dcs->build_state;
- const int fd = dcs->restore_fd;
-
- if (ret)
- goto out;
-
- gettimeofday(&start_time, NULL);
-
- switch (info->type) {
- case LIBXL_DOMAIN_TYPE_HVM:
- vments = libxl__calloc(gc, 7, sizeof(char *));
- vments[0] = "rtc/timeoffset";
- vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : "";
- vments[2] = "image/ostype";
- vments[3] = "hvm";
- vments[4] = "start_time";
- vments[5] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
- break;
- case LIBXL_DOMAIN_TYPE_PV:
- vments = libxl__calloc(gc, 11, sizeof(char *));
- i = 0;
- vments[i++] = "image/ostype";
- vments[i++] = "linux";
- vments[i++] = "image/kernel";
- vments[i++] = (char *) state->pv_kernel.path;
- vments[i++] = "start_time";
- vments[i++] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
- if (state->pv_ramdisk.path) {
- vments[i++] = "image/ramdisk";
- vments[i++] = (char *) state->pv_ramdisk.path;
- }
- if (state->pv_cmdline) {
- vments[i++] = "image/cmdline";
- vments[i++] = (char *) state->pv_cmdline;
- }
- break;
- case LIBXL_DOMAIN_TYPE_PVH:
- vments = libxl__calloc(gc, 3, sizeof(char *));
- vments[0] = "start_time";
- vments[1] = GCSPRINTF("%"PRIu64".%02ld",
- (uint64_t)start_time.tv_sec,
- (long)start_time.tv_usec/10000);
- break;
- default:
- ret = ERROR_INVAL;
- goto out;
- }
-
- /*
- * The scheduler on the sending domain may be different than the
- * scheduler running here. Setting the scheduler to UNKNOWN will
- * cause the code to take to take whatever parameters are
- * available in that scheduler, while discarding the rest.
- */
- info->sched_params.sched = LIBXL_SCHEDULER_UNKNOWN;
-
- ret = libxl__build_post(gc, domid, info, state, vments, localents);
- if (ret)
- goto out;
-
- if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
- state->saved_state = GCSPRINTF(
- LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", domid);
- }
-
-out:
- if (info->type == LIBXL_DOMAIN_TYPE_PV) {
- libxl__file_reference_unmap(&state->pv_kernel);
- libxl__file_reference_unmap(&state->pv_ramdisk);
- }
-
- /* fd == -1 here means we're doing soft reset. */
- if (fd != -1) {
- esave = errno;
- libxl_fd_set_nonblock(ctx, fd, 0);
- errno = esave;
- }
- domcreate_rebuild_done(egc, dcs, ret);
-}
-
-static void domcreate_rebuild_done(libxl__egc *egc,
- libxl__domain_create_state *dcs,
- int ret)
-{
- STATE_AO_GC(dcs->ao);
-
- /* convenience aliases */
- const uint32_t domid = dcs->guest_domid;
- libxl_domain_config *const d_config = dcs->guest_config;
-
- if (ret) {
- LOGD(ERROR, domid, "cannot (re-)build domain: %d", ret);
- ret = ERROR_FAIL;
- goto error_out;
- }
-
- store_libxl_entry(gc, domid, &d_config->b_info);
-
- libxl__multidev_begin(ao, &dcs->multidev);
- dcs->multidev.callback = domcreate_launch_dm;
- libxl__add_disks(egc, ao, domid, d_config, &dcs->multidev);
- libxl__multidev_prepared(egc, &dcs->multidev, 0);
-
- return;
-
- error_out:
- assert(ret);
- domcreate_complete(egc, dcs, ret);
-}
-
-static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *multidev,
- int ret)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev);
- STATE_AO_GC(dcs->ao);
- int i;
-
- /* convenience aliases */
- const uint32_t domid = dcs->guest_domid;
- libxl_domain_config *const d_config = dcs->guest_config;
- libxl__domain_build_state *const state = &dcs->build_state;
-
- if (ret) {
- LOGD(ERROR, domid, "unable to add disk devices");
- goto error_out;
- }
-
- for (i = 0; i < d_config->b_info.num_ioports; i++) {
- libxl_ioport_range *io = &d_config->b_info.ioports[i];
-
- LOGD(DEBUG, domid, "ioports %"PRIx32"-%"PRIx32,
- io->first, io->first + io->number - 1);
-
- ret = xc_domain_ioport_permission(CTX->xch, domid,
- io->first, io->number, 1);
- if (ret < 0) {
- LOGED(ERROR, domid,
- "failed give domain access to ioports %"PRIx32"-%"PRIx32,
- io->first, io->first + io->number - 1);
- ret = ERROR_FAIL;
- goto error_out;
- }
- }
-
- for (i = 0; i < d_config->b_info.num_irqs; i++) {
- int irq = d_config->b_info.irqs[i];
-
- LOGD(DEBUG, domid, "irq %d", irq);
-
- ret = irq >= 0 ? libxl__arch_domain_map_irq(gc, domid, irq)
- : -EOVERFLOW;
- if (ret) {
- LOGED(ERROR, domid, "failed give domain access to irq %d", irq);
- ret = ERROR_FAIL;
- goto error_out;
- }
- }
-
- for (i = 0; i < d_config->b_info.num_iomem; i++) {
- libxl_iomem_range *io = &d_config->b_info.iomem[i];
-
- LOGD(DEBUG, domid, "iomem %"PRIx64"-%"PRIx64,
- io->start, io->start + io->number - 1);
-
- ret = xc_domain_iomem_permission(CTX->xch, domid,
- io->start, io->number, 1);
- if (ret < 0) {
- LOGED(ERROR, domid,
- "failed give domain access to iomem range %"PRIx64"-%"PRIx64,
- io->start, io->start + io->number - 1);
- ret = ERROR_FAIL;
- goto error_out;
- }
- ret = xc_domain_memory_mapping(CTX->xch, domid,
- io->gfn, io->start,
- io->number, 1);
- if (ret < 0) {
- LOGED(ERROR, domid,
- "failed to map to domain iomem range %"PRIx64"-%"PRIx64
- " to guest address %"PRIx64,
- io->start, io->start + io->number - 1, io->gfn);
- ret = ERROR_FAIL;
- goto error_out;
- }
- }
-
- /* For both HVM and PV the 0th console is a regular console. We
- map channels to IOEMU consoles starting at 1 */
- for (i = 0; i < d_config->num_channels; i++) {
- libxl__device_console console;
- libxl__device device;
- ret = libxl__init_console_from_channel(gc, &console, i + 1,
- &d_config->channels[i]);
- if ( ret ) {
- libxl__device_console_dispose(&console);
- goto error_out;
- }
- libxl__device_console_add(gc, domid, &console, NULL, &device);
- libxl__device_console_dispose(&console);
- }
-
- for (i = 0; i < d_config->num_p9s; i++)
- libxl__device_add(gc, domid, &libxl__p9_devtype, &d_config->p9s[i]);
-
- for (i = 0; i < d_config->num_pvcallsifs; i++)
- libxl__device_add(gc, domid, &libxl__pvcallsif_devtype,
- &d_config->pvcallsifs[i]);
-
- switch (d_config->c_info.type) {
- case LIBXL_DOMAIN_TYPE_HVM:
- {
- libxl__device_console console;
- libxl__device device;
- libxl_device_vkb vkb;
-
- init_console_info(gc, &console, 0);
- console.backend_domid = state->console_domid;
- libxl__device_console_add(gc, domid, &console, state, &device);
- libxl__device_console_dispose(&console);
-
- if (libxl_defbool_val(d_config->b_info.u.hvm.vkb_device)) {
- libxl_device_vkb_init(&vkb);
- libxl__device_add(gc, domid, &libxl__vkb_devtype, &vkb);
- libxl_device_vkb_dispose(&vkb);
- }
-
- dcs->sdss.dm.guest_domid = domid;
- if (libxl_defbool_val(d_config->b_info.device_model_stubdomain))
- libxl__spawn_stub_dm(egc, &dcs->sdss);
- else
- libxl__spawn_local_dm(egc, &dcs->sdss.dm);
-
- /*
- * Handle the domain's (and the related stubdomain's) access to
- * the VGA framebuffer.
- */
- ret = libxl__grant_vga_iomem_permission(gc, domid, d_config);
- if ( ret )
- goto error_out;
-
- return;
- }
- case LIBXL_DOMAIN_TYPE_PV:
- case LIBXL_DOMAIN_TYPE_PVH:
- {
- libxl__device_console console, vuart;
- libxl__device device;
-
- for (i = 0; i < d_config->num_vfbs; i++) {
- libxl__device_add(gc, domid, &libxl__vfb_devtype,
- &d_config->vfbs[i]);
- }
-
- for (i = 0; i < d_config->num_vkbs; i++) {
- libxl__device_add(gc, domid, &libxl__vkb_devtype,
- &d_config->vkbs[i]);
- }
-
- if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
- init_console_info(gc, &vuart, 0);
- vuart.backend_domid = state->console_domid;
- libxl__device_vuart_add(gc, domid, &vuart, state);
- libxl__device_console_dispose(&vuart);
- }
-
- init_console_info(gc, &console, 0);
- console.backend_domid = state->console_domid;
- libxl__device_console_add(gc, domid, &console, state, &device);
- libxl__device_console_dispose(&console);
-
- ret = libxl__need_xenpv_qemu(gc, d_config);
- if (ret < 0)
- goto error_out;
- if (ret) {
- dcs->sdss.dm.guest_domid = domid;
- libxl__spawn_local_dm(egc, &dcs->sdss.dm);
- return;
- } else {
- assert(!dcs->sdss.dm.guest_domid);
- domcreate_devmodel_started(egc, &dcs->sdss.dm, 0);
- return;
- }
- }
- default:
- ret = ERROR_INVAL;
- goto error_out;
- }
- abort(); /* not reached */
-
- error_out:
- assert(ret);
- domcreate_complete(egc, dcs, ret);
-}
-
-static void libxl__add_dtdevs(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
- libxl_domain_config *d_config,
- libxl__multidev *multidev)
-{
- AO_GC;
- libxl__ao_device *aodev = libxl__multidev_prepare(multidev);
- int i, rc = 0;
-
- for (i = 0; i < d_config->num_dtdevs; i++) {
- const libxl_device_dtdev *dtdev = &d_config->dtdevs[i];
-
- LOGD(DEBUG, domid, "Assign device \"%s\" to domain", dtdev->path);
- rc = xc_assign_dt_device(CTX->xch, domid, dtdev->path);
- if (rc < 0) {
- LOGD(ERROR, domid, "xc_assign_dtdevice failed: %d", rc);
- goto out;
- }
- }
-
-out:
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-#define libxl_device_dtdev_list NULL
-#define libxl_device_dtdev_compare NULL
-#define libxl__device_from_dtdev NULL
-#define libxl__device_dtdev_setdefault NULL
-#define libxl__device_dtdev_update_devid NULL
-static DEFINE_DEVICE_TYPE_STRUCT(dtdev, NONE);
-
-const libxl__device_type *device_type_tbl[] = {
- &libxl__disk_devtype,
- &libxl__nic_devtype,
- &libxl__vtpm_devtype,
- &libxl__usbctrl_devtype,
- &libxl__usbdev_devtype,
- &libxl__pcidev_devtype,
- &libxl__dtdev_devtype,
- &libxl__vdispl_devtype,
- &libxl__vsnd_devtype,
- NULL
-};
-
-static void domcreate_devmodel_started(libxl__egc *egc,
- libxl__dm_spawn_state *dmss,
- int ret)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(dmss, *dcs, sdss.dm);
- STATE_AO_GC(dmss->spawn.ao);
- int domid = dcs->guest_domid;
-
- if (ret) {
- LOGD(ERROR, domid, "device model did not start: %d", ret);
- goto error_out;
- }
-
- dcs->device_type_idx = -1;
- domcreate_attach_devices(egc, &dcs->multidev, 0);
- return;
-
-error_out:
- assert(ret);
- domcreate_complete(egc, dcs, ret);
-}
-
-static void domcreate_attach_devices(libxl__egc *egc,
- libxl__multidev *multidev,
- int ret)
-{
- libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev);
- STATE_AO_GC(dcs->ao);
- int domid = dcs->guest_domid;
- libxl_domain_config *const d_config = dcs->guest_config;
- const libxl__device_type *dt;
- char *tty_path;
-
- if (ret) {
- LOGD(ERROR, domid, "unable to add %s devices",
- libxl__device_kind_to_string(device_type_tbl[dcs->device_type_idx]->type));
- goto error_out;
- }
-
- dcs->device_type_idx++;
- dt = device_type_tbl[dcs->device_type_idx];
- if (dt) {
- if (*libxl__device_type_get_num(dt, d_config) > 0 && !dt->skip_attach) {
- /* Attach devices */
- libxl__multidev_begin(ao, &dcs->multidev);
- dcs->multidev.callback = domcreate_attach_devices;
- dt->add(egc, ao, domid, d_config, &dcs->multidev);
- libxl__multidev_prepared(egc, &dcs->multidev, 0);
- return;
- }
-
- domcreate_attach_devices(egc, &dcs->multidev, 0);
- return;
- }
-
- ret = libxl__console_tty_path(gc, domid, 0, LIBXL_CONSOLE_TYPE_PV, &tty_path);
- if (ret) {
- LOG(ERROR, "failed to get domain %d console tty path",
- domid);
- goto error_out;
- }
-
- dcs->console_xswait.ao = ao;
- dcs->console_xswait.what = GCSPRINTF("domain %d console tty", domid);
- dcs->console_xswait.path = tty_path;
- dcs->console_xswait.timeout_ms = LIBXL_INIT_TIMEOUT * 1000;
- dcs->console_xswait.callback = console_xswait_callback;
- ret = libxl__xswait_start(gc, &dcs->console_xswait);
- if (ret) {
- LOG(ERROR, "unable to set up watch for domain %d console tty path",
- domid);
- goto error_out;
- }
-
- return;
-
-error_out:
- assert(ret);
- domcreate_complete(egc, dcs, ret);
-}
-
-static void console_xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa,
- int rc, const char *p)
-{
- EGC_GC;
- libxl__domain_create_state *dcs = CONTAINER_OF(xswa, *dcs, console_xswait);
-
- if (rc) {
- if (rc == ERROR_TIMEDOUT)
- LOG(ERROR, "%s: timed out", xswa->what);
- goto out;
- }
-
- if (p && p[0] != '\0') {
- domcreate_console_available(egc, dcs);
- goto out;
- }
-
- return;
-
-out:
- libxl__xswait_stop(gc, xswa);
- domcreate_complete(egc, dcs, rc);
-}
-
-static void domcreate_complete(libxl__egc *egc,
- libxl__domain_create_state *dcs,
- int rc)
-{
- STATE_AO_GC(dcs->ao);
- libxl_domain_config *const d_config = dcs->guest_config;
- libxl_domain_config *d_config_saved = &dcs->guest_config_saved;
-
- libxl__xswait_stop(gc, &dcs->console_xswait);
-
- libxl__domain_build_state_dispose(&dcs->build_state);
-
- if (!rc && d_config->b_info.exec_ssidref)
- rc = xc_flask_relabel_domain(CTX->xch, dcs->guest_domid, d_config->b_info.exec_ssidref);
-
- bool retain_domain = !rc || rc == ERROR_ABORTED;
-
- if (retain_domain) {
- libxl__flock *lock;
-
- /* Note that we hold CTX lock at this point so only need to
- * take data store lock
- */
- lock = libxl__lock_domain_userdata(gc, dcs->guest_domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- } else {
- libxl__update_domain_configuration(gc, d_config_saved, d_config);
- int cfg_rc = libxl__set_domain_configuration
- (gc, dcs->guest_domid, d_config_saved);
- if (!rc)
- rc = cfg_rc;
- libxl__unlock_file(lock);
- }
- }
-
- libxl_domain_config_dispose(d_config_saved);
-
- if (!retain_domain) {
- if (dcs->guest_domid > 0) {
- dcs->dds.ao = ao;
- dcs->dds.domid = dcs->guest_domid;
- dcs->dds.callback = domcreate_destruction_cb;
- libxl__domain_destroy(egc, &dcs->dds);
- return;
- }
- dcs->guest_domid = INVALID_DOMID;
- }
- dcs->callback(egc, dcs, rc, dcs->guest_domid);
-}
-
-static void domcreate_destruction_cb(libxl__egc *egc,
- libxl__domain_destroy_state *dds,
- int rc)
-{
- STATE_AO_GC(dds->ao);
- libxl__domain_create_state *dcs = CONTAINER_OF(dds, *dcs, dds);
-
- if (rc)
- LOGD(ERROR, dds->domid, "unable to destroy domain following failed creation");
-
- dcs->callback(egc, dcs, ERROR_FAIL, dcs->guest_domid);
-}
-
-/*----- application-facing domain creation interface -----*/
-
-typedef struct {
- libxl__domain_create_state dcs;
- uint32_t *domid_out;
-} libxl__app_domain_create_state;
-
-typedef struct {
- libxl__app_domain_create_state cdcs;
- libxl__domain_destroy_state dds;
- libxl__domain_save_state dss;
- char *toolstack_buf;
- uint32_t toolstack_len;
-} libxl__domain_soft_reset_state;
-
-static void domain_create_cb(libxl__egc *egc,
- libxl__domain_create_state *dcs,
- int rc, uint32_t domid);
-
-static int do_domain_create(libxl_ctx *ctx, libxl_domain_config *d_config,
- uint32_t *domid, int restore_fd, int send_back_fd,
- const libxl_domain_restore_params *params,
- const libxl_asyncop_how *ao_how,
- const libxl_asyncprogress_how *aop_console_how)
-{
- AO_CREATE(ctx, 0, ao_how);
- libxl__app_domain_create_state *cdcs;
- int rc;
-
- GCNEW(cdcs);
- cdcs->dcs.ao = ao;
- cdcs->dcs.guest_config = d_config;
- libxl_domain_config_init(&cdcs->dcs.guest_config_saved);
- libxl_domain_config_copy(ctx, &cdcs->dcs.guest_config_saved, d_config);
- cdcs->dcs.restore_fd = cdcs->dcs.libxc_fd = restore_fd;
- cdcs->dcs.send_back_fd = send_back_fd;
- if (restore_fd >= 0) {
- cdcs->dcs.restore_params = *params;
- rc = libxl__fd_flags_modify_save(gc, cdcs->dcs.restore_fd,
- ~(O_NONBLOCK|O_NDELAY), 0,
- &cdcs->dcs.restore_fdfl);
- if (rc < 0) goto out_err;
- }
- cdcs->dcs.callback = domain_create_cb;
- cdcs->dcs.domid = INVALID_DOMID;
- cdcs->dcs.soft_reset = false;
-
- if (cdcs->dcs.restore_params.checkpointed_stream ==
- LIBXL_CHECKPOINTED_STREAM_COLO) {
- cdcs->dcs.colo_proxy_script =
- cdcs->dcs.restore_params.colo_proxy_script;
- cdcs->dcs.crs.cps.is_userspace_proxy =
- libxl_defbool_val(cdcs->dcs.restore_params.userspace_colo_proxy);
- } else {
- cdcs->dcs.colo_proxy_script = NULL;
- cdcs->dcs.crs.cps.is_userspace_proxy = false;
- }
-
- libxl__ao_progress_gethow(&cdcs->dcs.aop_console_how, aop_console_how);
- cdcs->domid_out = domid;
-
- initiate_domain_create(egc, &cdcs->dcs);
-
- return AO_INPROGRESS;
-
- out_err:
- return AO_CREATE_FAIL(rc);
-
-}
-
-static void domain_soft_reset_cb(libxl__egc *egc,
- libxl__domain_destroy_state *dds,
- int rc)
-{
- STATE_AO_GC(dds->ao);
- libxl__domain_soft_reset_state *srs = CONTAINER_OF(dds, *srs, dds);
- libxl__app_domain_create_state *cdcs = &srs->cdcs;
- char *savefile, *restorefile;
-
- if (rc) {
- LOGD(ERROR, dds->domid, "destruction of domain failed.");
- goto error;
- }
-
- cdcs->dcs.guest_domid = dds->domid;
- rc = libxl__restore_emulator_xenstore_data(&cdcs->dcs, srs->toolstack_buf,
- srs->toolstack_len);
- if (rc) {
- LOGD(ERROR, dds->domid, "failed to restore toolstack record.");
- goto error;
- }
-
- if (cdcs->dcs.guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
- savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", dds->domid);
- restorefile = GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%d",
- dds->domid);
- rc = rename(savefile, restorefile);
- if (rc) {
- LOGD(ERROR, dds->domid, "failed to rename dm save file.");
- goto error;
- }
- }
-
- initiate_domain_create(egc, &cdcs->dcs);
- return;
-
-error:
- domcreate_complete(egc, &cdcs->dcs, rc);
-}
-
-static void soft_reset_dm_suspended(libxl__egc *egc,
- libxl__domain_suspend_state *dsps,
- int rc);
-static int do_domain_soft_reset(libxl_ctx *ctx,
- libxl_domain_config *d_config,
- uint32_t domid,
- const libxl_asyncop_how *ao_how,
- const libxl_asyncprogress_how
- *aop_console_how)
-{
- AO_CREATE(ctx, 0, ao_how);
- libxl__domain_soft_reset_state *srs;
- libxl__app_domain_create_state *cdcs;
- libxl__domain_create_state *dcs;
- libxl__domain_build_state *state;
- libxl__domain_save_state *dss;
- const char *console_tty, *xs_store_mfn, *xs_console_mfn;
- char *dom_path;
- uint32_t domid_out;
- int rc;
-
- GCNEW(srs);
- cdcs = &srs->cdcs;
- dcs = &cdcs->dcs;
- state = &dcs->build_state;
- dss = &srs->dss;
-
- srs->cdcs.dcs.ao = ao;
- srs->cdcs.dcs.guest_config = d_config;
- libxl_domain_config_init(&srs->cdcs.dcs.guest_config_saved);
- libxl_domain_config_copy(ctx, &srs->cdcs.dcs.guest_config_saved,
- d_config);
- cdcs->dcs.restore_fd = -1;
- cdcs->dcs.domid = domid;
- cdcs->dcs.soft_reset = true;
- cdcs->dcs.callback = domain_create_cb;
- libxl__ao_progress_gethow(&srs->cdcs.dcs.aop_console_how,
- aop_console_how);
- cdcs->domid_out = &domid_out;
-
- dom_path = libxl__xs_get_dompath(gc, domid);
- if (!dom_path) {
- LOGD(ERROR, domid, "failed to read domain path");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/store/ring-ref", dom_path),
- &xs_store_mfn);
- if (rc) {
- LOGD(ERROR, domid, "failed to read store/ring-ref.");
- goto out;
- }
- state->store_mfn = xs_store_mfn ? atol(xs_store_mfn): 0;
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/console/ring-ref", dom_path),
- &xs_console_mfn);
- if (rc) {
- LOGD(ERROR, domid, "failed to read console/ring-ref.");
- goto out;
- }
- state->console_mfn = xs_console_mfn ? atol(xs_console_mfn): 0;
-
- rc = libxl__xs_read_mandatory(gc, XBT_NULL,
- GCSPRINTF("%s/console/tty", dom_path),
- &console_tty);
- if (rc) {
- LOGD(ERROR, domid, "failed to read console/tty.");
- goto out;
- }
- state->console_tty = libxl__strdup(gc, console_tty);
-
- dss->ao = ao;
- dss->domid = dss->dsps.domid = domid;
- dss->dsps.dm_savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d",
- domid);
-
- rc = libxl__save_emulator_xenstore_data(dss, &srs->toolstack_buf,
- &srs->toolstack_len);
- if (rc) {
- LOGD(ERROR, domid, "failed to save toolstack record.");
- goto out;
- }
-
- dss->dsps.ao = ao;
- dss->dsps.callback_device_model_done = soft_reset_dm_suspended;
- libxl__domain_suspend_device_model(egc, &dss->dsps); /* must be last */
-
- return AO_INPROGRESS;
-
- out:
- return AO_CREATE_FAIL(rc);
-}
-
-static void soft_reset_dm_suspended(libxl__egc *egc,
- libxl__domain_suspend_state *dsps,
- int rc)
-{
- STATE_AO_GC(dsps->ao);
- libxl__domain_soft_reset_state *srs =
- CONTAINER_OF(dsps, *srs, dss.dsps);
- libxl__app_domain_create_state *cdcs = &srs->cdcs;
-
- /*
- * Ask all backends to disconnect by removing the domain from
- * xenstore. On the creation path the domain will be introduced to
- * xenstore again with probably different store/console/...
- * channels.
- */
- xs_release_domain(CTX->xsh, cdcs->dcs.domid);
-
- srs->dds.ao = ao;
- srs->dds.domid = cdcs->dcs.domid;
- srs->dds.callback = domain_soft_reset_cb;
- srs->dds.soft_reset = true;
- libxl__domain_destroy(egc, &srs->dds);
-}
-
-static void domain_create_cb(libxl__egc *egc,
- libxl__domain_create_state *dcs,
- int rc, uint32_t domid)
-{
- libxl__app_domain_create_state *cdcs = CONTAINER_OF(dcs, *cdcs, dcs);
- int flrc;
- STATE_AO_GC(cdcs->dcs.ao);
-
- *cdcs->domid_out = domid;
-
- if (dcs->restore_fd >= 0) {
- flrc = libxl__fd_flags_restore(gc,
- dcs->restore_fd, dcs->restore_fdfl);
- /*
- * If restore has failed already then report that error not
- * this one.
- */
- if (flrc && !rc) rc = flrc;
- }
-
- libxl__ao_complete(egc, ao, rc);
-}
-
-
-static void set_disk_colo_restore(libxl_domain_config *d_config)
-{
- int i;
-
- for (i = 0; i < d_config->num_disks; i++)
- libxl_defbool_set(&d_config->disks[i].colo_restore_enable, true);
-}
-
-static void unset_disk_colo_restore(libxl_domain_config *d_config)
-{
- int i;
-
- for (i = 0; i < d_config->num_disks; i++)
- libxl_defbool_set(&d_config->disks[i].colo_restore_enable, false);
-}
-
-int libxl_domain_create_new(libxl_ctx *ctx, libxl_domain_config *d_config,
- uint32_t *domid,
- const libxl_asyncop_how *ao_how,
- const libxl_asyncprogress_how *aop_console_how)
-{
- unset_disk_colo_restore(d_config);
- return do_domain_create(ctx, d_config, domid, -1, -1, NULL,
- ao_how, aop_console_how);
-}
-
-int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config,
- uint32_t *domid, int restore_fd,
- int send_back_fd,
- const libxl_domain_restore_params *params,
- const libxl_asyncop_how *ao_how,
- const libxl_asyncprogress_how *aop_console_how)
-{
- if (params->checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) {
- set_disk_colo_restore(d_config);
- } else {
- unset_disk_colo_restore(d_config);
- }
-
- return do_domain_create(ctx, d_config, domid, restore_fd, send_back_fd,
- params, ao_how, aop_console_how);
-}
-
-int libxl_domain_soft_reset(libxl_ctx *ctx,
- libxl_domain_config *d_config,
- uint32_t domid,
- const libxl_asyncop_how *ao_how,
- const libxl_asyncprogress_how
- *aop_console_how)
-{
- libxl_domain_build_info *const info = &d_config->b_info;
-
- if (info->type != LIBXL_DOMAIN_TYPE_HVM) return ERROR_INVAL;
-
- return do_domain_soft_reset(ctx, d_config, domid, ao_how,
- aop_console_how);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-static char *libxl__device_frontend_path(libxl__gc *gc, libxl__device *device)
-{
- char *dom_path = libxl__xs_get_dompath(gc, device->domid);
-
- /* Console 0 is a special case */
- if (device->kind == LIBXL__DEVICE_KIND_CONSOLE && device->devid == 0)
- return GCSPRINTF("%s/%s", dom_path,
- libxl__device_kind_to_string(device->kind));
-
- if (device->kind == LIBXL__DEVICE_KIND_VUART)
- return GCSPRINTF("%s/%s/%d", dom_path,
- libxl__device_kind_to_string(device->kind),
- device->devid);
-
- return GCSPRINTF("%s/device/%s/%d", dom_path,
- libxl__device_kind_to_string(device->kind),
- device->devid);
-}
-
-char *libxl__domain_device_frontend_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
- libxl__device_kind device_kind)
-{
- char *dom_path = libxl__xs_get_dompath(gc, domid);
-
- return GCSPRINTF("%s/device/%s/%d", dom_path,
- libxl__device_kind_to_string(device_kind), devid);
-}
-
-char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device)
-{
- char *dom_path = libxl__xs_get_dompath(gc, device->backend_domid);
-
- return GCSPRINTF("%s/backend/%s/%u/%d", dom_path,
- libxl__device_kind_to_string(device->backend_kind),
- device->domid, device->devid);
-}
-
-char *libxl__domain_device_backend_path(libxl__gc *gc, uint32_t backend_domid,
- uint32_t domid, uint32_t devid,
- libxl__device_kind backend_kind)
-{
- char *dom_path = libxl__xs_get_dompath(gc, backend_domid);
-
- return GCSPRINTF("%s/backend/%s/%u/%d", dom_path,
- libxl__device_kind_to_string(backend_kind),
- domid, devid);
-}
-
-char *libxl__device_libxl_path(libxl__gc *gc, libxl__device *device)
-{
- char *libxl_dom_path = libxl__xs_libxl_path(gc, device->domid);
-
- return GCSPRINTF("%s/device/%s/%d", libxl_dom_path,
- libxl__device_kind_to_string(device->kind),
- device->devid);
-}
-
-char *libxl__domain_device_libxl_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
- libxl__device_kind device_kind)
-{
- char *libxl_dom_path = libxl__xs_libxl_path(gc, domid);
-
- return GCSPRINTF("%s/device/%s/%d", libxl_dom_path,
- libxl__device_kind_to_string(device_kind), devid);
-}
-
-/* Returns 1 if device exists, 0 if not, ERROR_* (<0) on error. */
-int libxl__device_exists(libxl__gc *gc, xs_transaction_t t,
- libxl__device *device)
-{
- int rc;
- char *be_path = libxl__device_libxl_path(gc, device);
- const char *dir;
-
- rc = libxl__xs_read_checked(gc, t, be_path, &dir);
-
- if (rc)
- return rc;
-
- if (dir)
- return 1;
- return 0;
-}
-
-int libxl__parse_backend_path(libxl__gc *gc,
- const char *path,
- libxl__device *dev)
-{
- /* /local/domain/<domid>/backend/<kind>/<domid>/<devid> */
- char strkind[16]; /* Longest is actually "console" */
- int rc = sscanf(path, "/local/domain/%d/backend/%15[^/]/%u/%d",
- &dev->backend_domid,
- strkind,
- &dev->domid,
- &dev->devid);
-
- if (rc != 4)
- return ERROR_FAIL;
-
- return libxl__device_kind_from_string(strkind, &dev->backend_kind);
-}
-
-int libxl__nic_type(libxl__gc *gc, libxl__device *dev, libxl_nic_type *nictype)
-{
- char *snictype, *be_path;
- int rc = 0;
-
- be_path = libxl__device_backend_path(gc, dev);
- snictype = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/%s", be_path, "type"));
- if (!snictype) {
- LOGED(ERROR, dev->domid, "unable to read nictype from %s", be_path);
- rc = ERROR_FAIL;
- goto out;
- }
- rc = libxl_nic_type_from_string(snictype, nictype);
- if (rc) {
- LOGED(ERROR, dev->domid, "unable to parse nictype from %s", be_path);
- goto out;
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t,
- libxl__device *device, char **bents, char **fents, char **ro_fents)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char *frontend_path = NULL, *backend_path = NULL, *libxl_path;
- struct xs_permissions frontend_perms[2];
- struct xs_permissions ro_frontend_perms[2];
- struct xs_permissions backend_perms[2];
- int create_transaction = t == XBT_NULL;
- int libxl_only = device->backend_kind == LIBXL__DEVICE_KIND_NONE;
- int rc;
-
- if (libxl_only) {
- /* bents should be set as this is used to setup libxl_path content. */
- assert(!fents && !ro_fents);
- } else {
- frontend_path = libxl__device_frontend_path(gc, device);
- backend_path = libxl__device_backend_path(gc, device);
- }
- libxl_path = libxl__device_libxl_path(gc, device);
-
- frontend_perms[0].id = device->domid;
- frontend_perms[0].perms = XS_PERM_NONE;
- frontend_perms[1].id = device->backend_domid;
- frontend_perms[1].perms = XS_PERM_READ;
-
- ro_frontend_perms[0].id = backend_perms[0].id = device->backend_domid;
- ro_frontend_perms[0].perms = backend_perms[0].perms = XS_PERM_NONE;
- ro_frontend_perms[1].id = backend_perms[1].id = device->domid;
- ro_frontend_perms[1].perms = backend_perms[1].perms = XS_PERM_READ;
-
-retry_transaction:
- if (create_transaction)
- t = xs_transaction_start(ctx->xsh);
-
- /* FIXME: read frontend_path and check state before removing stuff */
-
- rc = libxl__xs_rm_checked(gc, t, libxl_path);
- if (rc) goto out;
-
- if (!libxl_only) {
- rc = libxl__xs_write_checked(gc, t, GCSPRINTF("%s/frontend",libxl_path),
- frontend_path);
- if (rc) goto out;
-
- rc = libxl__xs_write_checked(gc, t, GCSPRINTF("%s/backend",libxl_path),
- backend_path);
- if (rc) goto out;
- }
-
- /* xxx much of this function lacks error checks! */
-
- if (fents || ro_fents) {
- xs_rm(ctx->xsh, t, frontend_path);
- xs_mkdir(ctx->xsh, t, frontend_path);
- /* Console 0 is a special case. It doesn't use the regular PV
- * state machine but also the frontend directory has
- * historically contained other information, such as the
- * vnc-port, which we don't want the guest fiddling with.
- */
- if ((device->kind == LIBXL__DEVICE_KIND_CONSOLE && device->devid == 0) ||
- (device->kind == LIBXL__DEVICE_KIND_VUART))
- xs_set_permissions(ctx->xsh, t, frontend_path,
- ro_frontend_perms, ARRAY_SIZE(ro_frontend_perms));
- else
- xs_set_permissions(ctx->xsh, t, frontend_path,
- frontend_perms, ARRAY_SIZE(frontend_perms));
- xs_write(ctx->xsh, t, GCSPRINTF("%s/backend", frontend_path),
- backend_path, strlen(backend_path));
- if (fents)
- libxl__xs_writev_perms(gc, t, frontend_path, fents,
- frontend_perms, ARRAY_SIZE(frontend_perms));
- if (ro_fents)
- libxl__xs_writev_perms(gc, t, frontend_path, ro_fents,
- ro_frontend_perms, ARRAY_SIZE(ro_frontend_perms));
- }
-
- if (bents) {
- if (!libxl_only) {
- xs_rm(ctx->xsh, t, backend_path);
- xs_mkdir(ctx->xsh, t, backend_path);
- xs_set_permissions(ctx->xsh, t, backend_path, backend_perms,
- ARRAY_SIZE(backend_perms));
- xs_write(ctx->xsh, t, GCSPRINTF("%s/frontend", backend_path),
- frontend_path, strlen(frontend_path));
- libxl__xs_writev(gc, t, backend_path, bents);
- }
-
- /*
- * We make a copy of everything for the backend in the libxl
- * path as well. This means we don't need to trust the
- * backend. Ideally this information would not be used and we
- * would use the information from the json configuration
- * instead. But there are still places in libxl that try to
- * reconstruct a config from xenstore.
- *
- * For devices without PV backend (e.g. USB devices emulated via qemu)
- * only the libxl path is written.
- *
- * This duplication will typically produces duplicate keys
- * which will go out of date, but that's OK because nothing
- * reads those. For example, there is usually
- * /libxl/$guest/device/$kind/$devid/state
- * which starts out containing XenbusStateInitialising ("1")
- * just like the copy in
- * /local/domain/$driverdom/backend/$guest/$kind/$devid/state
- * but which won't ever be updated.
- *
- * This duplication is superfluous and messy but as discussed
- * the proper fix is more intrusive than we want to do now.
- */
- rc = libxl__xs_writev(gc, t, libxl_path, bents);
- if (rc) goto out;
- }
-
- if (!create_transaction)
- return 0;
-
- if (!xs_transaction_end(ctx->xsh, t, 0)) {
- if (errno == EAGAIN)
- goto retry_transaction;
- else {
- LOGED(ERROR, device->domid, "xs transaction failed");
- return ERROR_FAIL;
- }
- }
- return 0;
-
- out:
- if (create_transaction && t)
- libxl__xs_transaction_abort(gc, &t);
- return rc;
-}
-
-typedef struct {
- libxl__gc *gc;
- libxl_device_disk *disk;
- struct stat stab;
-} disk_try_backend_args;
-
-static int disk_try_backend(disk_try_backend_args *a,
- libxl_disk_backend backend)
- {
- libxl__gc *gc = a->gc;
- /* returns 0 (ie, DISK_BACKEND_UNKNOWN) on failure, or
- * backend on success */
-
- switch (backend) {
- case LIBXL_DISK_BACKEND_PHY:
- if (a->disk->format != LIBXL_DISK_FORMAT_RAW) {
- goto bad_format;
- }
-
- if (libxl_defbool_val(a->disk->colo_enable))
- goto bad_colo;
-
- if (a->disk->backend_domid != LIBXL_TOOLSTACK_DOMID) {
- LOG(DEBUG, "Disk vdev=%s, is using a storage driver domain, "
- "skipping physical device check", a->disk->vdev);
- return backend;
- }
-
- if (a->disk->script) {
- LOG(DEBUG, "Disk vdev=%s, uses script=... assuming phy backend",
- a->disk->vdev);
- return backend;
- }
-
- if (libxl__try_phy_backend(a->stab.st_mode))
- return backend;
-
- LOG(DEBUG, "Disk vdev=%s, backend phy unsuitable as phys path not a "
- "block device", a->disk->vdev);
- return 0;
-
- case LIBXL_DISK_BACKEND_TAP:
- LOG(DEBUG, "Disk vdev=%s, backend tap unsuitable because blktap "
- "not available", a->disk->vdev);
- return 0;
-
- case LIBXL_DISK_BACKEND_QDISK:
- if (a->disk->script) goto bad_script;
- return backend;
-
- default:
- LOG(DEBUG, "Disk vdev=%s, backend %d unknown", a->disk->vdev, backend);
- return 0;
-
- }
- abort(); /* notreached */
-
- bad_format:
- LOG(DEBUG, "Disk vdev=%s, backend %s unsuitable due to format %s",
- a->disk->vdev,
- libxl_disk_backend_to_string(backend),
- libxl_disk_format_to_string(a->disk->format));
- return 0;
-
- bad_script:
- LOG(DEBUG, "Disk vdev=%s, backend %s not compatible with script=...",
- a->disk->vdev, libxl_disk_backend_to_string(backend));
- return 0;
-
- bad_colo:
- LOG(DEBUG, "Disk vdev=%s, backend %s not compatible with colo",
- a->disk->vdev, libxl_disk_backend_to_string(backend));
- return 0;
-}
-
-int libxl__backendpath_parse_domid(libxl__gc *gc, const char *be_path,
- libxl_domid *domid_out) {
- int r;
- unsigned int domid_sc;
- char delim_sc;
-
- r = sscanf(be_path, "/local/domain/%u%c", &domid_sc, &delim_sc);
- if (!(r==2 && delim_sc=='/')) {
- LOG(ERROR, "internal error: backend path %s unparseable!", be_path);
- return ERROR_FAIL;
- }
- *domid_out = domid_sc;
- return 0;
-}
-
-int libxl__device_disk_set_backend(libxl__gc *gc, libxl_device_disk *disk) {
- libxl_disk_backend ok;
- disk_try_backend_args a;
-
- a.gc = gc;
- a.disk = disk;
-
- LOG(DEBUG, "Disk vdev=%s spec.backend=%s", disk->vdev,
- libxl_disk_backend_to_string(disk->backend));
-
- if (disk->format == LIBXL_DISK_FORMAT_EMPTY) {
- if (!disk->is_cdrom) {
- LOG(ERROR, "Disk vdev=%s is empty but not cdrom", disk->vdev);
- return ERROR_INVAL;
- }
- if (disk->pdev_path != NULL && strcmp(disk->pdev_path, "")) {
- LOG(ERROR,
- "Disk vdev=%s is empty but an image has been provided: %s",
- disk->vdev, disk->pdev_path);
- return ERROR_INVAL;
- }
- memset(&a.stab, 0, sizeof(a.stab));
- } else if ((disk->backend == LIBXL_DISK_BACKEND_UNKNOWN ||
- disk->backend == LIBXL_DISK_BACKEND_PHY) &&
- disk->backend_domid == LIBXL_TOOLSTACK_DOMID &&
- !disk->script) {
- if (stat(disk->pdev_path, &a.stab)) {
- LOGE(ERROR, "Disk vdev=%s failed to stat: %s",
- disk->vdev, disk->pdev_path);
- return ERROR_INVAL;
- }
- }
-
- if (disk->backend != LIBXL_DISK_BACKEND_UNKNOWN) {
- ok= disk_try_backend(&a, disk->backend);
- } else {
- ok=
- disk_try_backend(&a, LIBXL_DISK_BACKEND_PHY) ?:
- disk_try_backend(&a, LIBXL_DISK_BACKEND_QDISK) ?:
- disk_try_backend(&a, LIBXL_DISK_BACKEND_TAP);
- if (ok)
- LOG(DEBUG, "Disk vdev=%s, using backend %s",
- disk->vdev,
- libxl_disk_backend_to_string(ok));
- }
- if (!ok) {
- LOG(ERROR, "no suitable backend for disk %s", disk->vdev);
- return ERROR_INVAL;
- }
- disk->backend = ok;
- return 0;
-}
-
-char *libxl__device_disk_string_of_format(libxl_disk_format format)
-{
- switch (format) {
- case LIBXL_DISK_FORMAT_QCOW: return "qcow";
- case LIBXL_DISK_FORMAT_QCOW2: return "qcow2";
- case LIBXL_DISK_FORMAT_VHD: return "vhd";
- case LIBXL_DISK_FORMAT_RAW:
- case LIBXL_DISK_FORMAT_EMPTY: return "aio";
- case LIBXL_DISK_FORMAT_QED: return "qed";
- default: return NULL;
- }
-}
-
-char *libxl__device_disk_string_of_backend(libxl_disk_backend backend)
-{
- switch (backend) {
- case LIBXL_DISK_BACKEND_QDISK: return "qdisk";
- case LIBXL_DISK_BACKEND_TAP: return "phy";
- case LIBXL_DISK_BACKEND_PHY: return "phy";
- default: return NULL;
- }
-}
-
-const char *libxl__qemu_disk_format_string(libxl_disk_format format)
-{
- switch (format) {
- case LIBXL_DISK_FORMAT_QCOW: return "qcow";
- case LIBXL_DISK_FORMAT_QCOW2: return "qcow2";
- case LIBXL_DISK_FORMAT_VHD: return "vpc";
- case LIBXL_DISK_FORMAT_RAW: return "raw";
- case LIBXL_DISK_FORMAT_EMPTY: return NULL;
- case LIBXL_DISK_FORMAT_QED: return "qed";
- default: return NULL;
- }
-}
-
-int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor)
-{
- struct stat buf;
- if (stat(physpath, &buf) < 0)
- return -1;
- if (!S_ISBLK(buf.st_mode))
- return -1;
- *major = major(buf.st_rdev);
- *minor = minor(buf.st_rdev);
- return 0;
-}
-
-static int device_virtdisk_matches(const char *virtpath, const char *devtype,
- int *index_r, int max_index,
- int *partition_r, int max_partition) {
- const char *p;
- char *ep;
- int tl, c;
- long pl;
-
- tl = strlen(devtype);
- if (memcmp(virtpath, devtype, tl))
- return 0;
-
- /* We decode the drive letter as if it were in base 52
- * with digits a-zA-Z, more or less */
- *index_r = -1;
- p = virtpath + tl;
- for (;;) {
- c = *p++;
- if (c >= 'a' && c <= 'z') {
- c -= 'a';
- } else {
- --p;
- break;
- }
- (*index_r)++;
- (*index_r) *= 26;
- (*index_r) += c;
-
- if (*index_r > max_index)
- return 0;
- }
-
- if (!*p) {
- *partition_r = 0;
- return 1;
- }
-
- if (*p=='0')
- return 0; /* leading zeroes not permitted in partition number */
-
- pl = strtoul(p, &ep, 10);
- if (pl > max_partition || *ep)
- return 0;
-
- *partition_r = pl;
- return 1;
-}
-
-int libxl__device_disk_dev_number(const char *virtpath, int *pdisk,
- int *ppartition)
-{
- int disk, partition;
- char *ep;
- unsigned long ul;
- int chrused;
-
- chrused = -1;
- if ((sscanf(virtpath, "d%ip%i%n", &disk, &partition, &chrused) >= 2
- && chrused == strlen(virtpath) && disk < (1<<20) && partition < 256)
- ||
- device_virtdisk_matches(virtpath, "xvd",
- &disk, (1<<20)-1,
- &partition, 255)) {
- if (pdisk) *pdisk = disk;
- if (ppartition) *ppartition = partition;
- if (disk <= 15 && partition <= 15)
- return (202 << 8) | (disk << 4) | partition;
- else
- return (1 << 28) | (disk << 8) | partition;
- }
-
- errno = 0;
- ul = strtoul(virtpath, &ep, 0);
- if (!errno && !*ep && ul <= INT_MAX) {
- /* FIXME: should parse ul to determine these. */
- if (pdisk || ppartition)
- return -1;
- return ul;
- }
-
- if (device_virtdisk_matches(virtpath, "hd",
- &disk, 3,
- &partition, 63)) {
- if (pdisk) *pdisk = disk;
- if (ppartition) *ppartition = partition;
- return ((disk<2 ? 3 : 22) << 8) | ((disk & 1) << 6) | partition;
- }
- if (device_virtdisk_matches(virtpath, "sd",
- &disk, 15,
- &partition, 15)) {
- if (pdisk) *pdisk = disk;
- if (ppartition) *ppartition = partition;
- return (8 << 8) | (disk << 4) | partition;
- }
- return -1;
-}
-
-static char *encode_disk_name(char *ptr, unsigned int n)
-{
- if (n >= 26)
- ptr = encode_disk_name(ptr, n / 26 - 1);
- *ptr = 'a' + n % 26;
- return ptr + 1;
-}
-
-char *libxl__devid_to_vdev(libxl__gc *gc, int devid)
-{
- unsigned int minor;
- int offset;
- int nr_parts;
- char *ptr = NULL;
-/* Same as in Linux.
- * encode_disk_name might end up using up to 29 bytes (BUFFER_SIZE - 3)
- * including the trailing \0.
- *
- * The code is safe because 26 raised to the power of 28 (that is the
- * maximum offset that can be stored in the allocated buffer as a
- * string) is far greater than UINT_MAX on 64 bits so offset cannot be
- * big enough to exhaust the available bytes in ret. */
-#define BUFFER_SIZE 32
- char *ret = libxl__zalloc(gc, BUFFER_SIZE);
-
-#define EXT_SHIFT 28
-#define EXTENDED (1<<EXT_SHIFT)
-#define VDEV_IS_EXTENDED(dev) ((dev)&(EXTENDED))
-#define BLKIF_MINOR_EXT(dev) ((dev)&(~EXTENDED))
-/* the size of the buffer to store the device name is 32 bytes to match the
- * equivalent buffer in the Linux kernel code */
-
- if (!VDEV_IS_EXTENDED(devid)) {
- minor = devid & 0xff;
- nr_parts = 16;
- } else {
- minor = BLKIF_MINOR_EXT(devid);
- nr_parts = 256;
- }
- offset = minor / nr_parts;
-
- strcpy(ret, "xvd");
- ptr = encode_disk_name(ret + 3, offset);
- if (minor % nr_parts == 0)
- *ptr = 0;
- else
- /* overflow cannot happen, thanks to the upper bound */
- snprintf(ptr, ret + 32 - ptr,
- "%d", minor & (nr_parts - 1));
- return ret;
-#undef BUFFER_SIZE
-#undef EXT_SHIFT
-#undef EXTENDED
-#undef VDEV_IS_EXTENDED
-#undef BLKIF_MINOR_EXT
-}
-
-/* Device AO operations */
-
-void libxl__prepare_ao_device(libxl__ao *ao, libxl__ao_device *aodev)
-{
- aodev->ao = ao;
- aodev->rc = 0;
- aodev->dev = NULL;
- aodev->num_exec = 0;
- /* Initialize timer for QEMU Bodge */
- libxl__ev_time_init(&aodev->timeout);
- /*
- * Initialize xs_watch, because it's not used on all possible
- * execution paths, but it's unconditionally destroyed when finished.
- */
- libxl__xswait_init(&aodev->xswait);
- aodev->active = 1;
- /* We init this here because we might call device_hotplug_done
- * without actually calling any hotplug script */
- libxl__async_exec_init(&aodev->aes);
- libxl__ev_child_init(&aodev->child);
-
- libxl__ev_qmp_init(&aodev->qmp);
-}
-
-/* multidev */
-
-void libxl__multidev_begin(libxl__ao *ao, libxl__multidev *multidev)
-{
- AO_GC;
-
- multidev->ao = ao;
- multidev->array = 0;
- multidev->used = multidev->allocd = 0;
-
- /* We allocate an aodev to represent the operation of preparing
- * all of the other operations. This operation is completed when
- * we have started all the others (ie, when the user calls
- * _prepared). That arranges automatically that
- * (i) we do not think we have finished even if one of the
- * operations completes while we are still preparing
- * (ii) if we are starting zero operations, we do still
- * make the callback as soon as we know this fact
- * (iii) we have a nice consistent way to deal with any
- * error that might occur while deciding what to initiate
- */
- multidev->preparation = libxl__multidev_prepare(multidev);
-}
-
-void libxl__multidev_prepare_with_aodev(libxl__multidev *multidev,
- libxl__ao_device *aodev) {
- STATE_AO_GC(multidev->ao);
-
- aodev->multidev = multidev;
- aodev->callback = libxl__multidev_one_callback;
- libxl__prepare_ao_device(ao, aodev);
-
- if (multidev->used >= multidev->allocd) {
- multidev->allocd = multidev->used * 2 + 5;
- GCREALLOC_ARRAY(multidev->array, multidev->allocd);
- }
- multidev->array[multidev->used++] = aodev;
-}
-
-libxl__ao_device *libxl__multidev_prepare(libxl__multidev *multidev) {
- STATE_AO_GC(multidev->ao);
- libxl__ao_device *aodev;
-
- GCNEW(aodev);
- libxl__multidev_prepare_with_aodev(multidev, aodev);
-
- return aodev;
-}
-
-void libxl__multidev_one_callback(libxl__egc *egc, libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- libxl__multidev *multidev = aodev->multidev;
- int i, error = 0;
-
- aodev->active = 0;
-
- for (i = 0; i < multidev->used; i++) {
- if (multidev->array[i]->active)
- return;
-
- if (multidev->array[i]->rc)
- error = multidev->array[i]->rc;
- }
-
- multidev->callback(egc, multidev, error);
- return;
-}
-
-void libxl__multidev_prepared(libxl__egc *egc,
- libxl__multidev *multidev, int rc)
-{
- multidev->preparation->rc = rc;
- libxl__multidev_one_callback(egc, multidev->preparation);
-}
-
-/******************************************************************************/
-
-int libxl__device_destroy(libxl__gc *gc, libxl__device *dev)
-{
- const char *be_path = NULL;
- const char *fe_path = NULL;
- const char *libxl_path = libxl__device_libxl_path(gc, dev);
- xs_transaction_t t = 0;
- int rc;
- uint32_t domid;
- int libxl_only = dev->backend_kind == LIBXL__DEVICE_KIND_NONE;
-
- if (!libxl_only) {
- be_path = libxl__device_backend_path(gc, dev);
- fe_path = libxl__device_frontend_path(gc, dev);
- }
-
- rc = libxl__get_domid(gc, &domid);
- if (rc) goto out;
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
-
- if (domid == LIBXL_TOOLSTACK_DOMID) {
- /*
- * The toolstack domain is in charge of removing the
- * frontend and libxl paths.
- */
- if (!libxl_only)
- libxl__xs_path_cleanup(gc, t, fe_path);
- libxl__xs_path_cleanup(gc, t, libxl_path);
- }
- if (dev->backend_domid == domid && !libxl_only) {
- /*
- * The driver domain is in charge of removing what it can
- * from the backend path.
- */
- libxl__xs_path_cleanup(gc, t, be_path);
- }
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc < 0) goto out;
- }
-
-out:
- libxl__xs_transaction_abort(gc, &t);
- return rc;
-}
-
-/* Callback for device destruction */
-
-static void devices_remove_callback(libxl__egc *egc,
- libxl__multidev *multidev, int rc);
-
-void libxl__devices_destroy(libxl__egc *egc, libxl__devices_remove_state *drs)
-{
- STATE_AO_GC(drs->ao);
- uint32_t domid = drs->domid;
- char *path;
- unsigned int num_kinds, num_dev_xsentries;
- char **kinds = NULL, **devs = NULL;
- int i, j, rc = 0;
- libxl__device *dev;
- libxl__multidev *multidev = &drs->multidev;
- libxl__ao_device *aodev;
- libxl__device_kind kind;
-
- libxl__multidev_begin(ao, multidev);
- multidev->callback = devices_remove_callback;
-
- path = GCSPRINTF("/libxl/%d/device", domid);
- kinds = libxl__xs_directory(gc, XBT_NULL, path, &num_kinds);
- if (!kinds) {
- if (errno != ENOENT) {
- LOGE(ERROR, "unable to get xenstore device listing %s", path);
- goto out;
- }
- num_kinds = 0;
- }
- for (i = 0; i < num_kinds; i++) {
- if (libxl__device_kind_from_string(kinds[i], &kind))
- continue;
-
- path = GCSPRINTF("/libxl/%d/device/%s", domid, kinds[i]);
- devs = libxl__xs_directory(gc, XBT_NULL, path, &num_dev_xsentries);
- if (!devs)
- continue;
- for (j = 0; j < num_dev_xsentries; j++) {
- path = GCSPRINTF("/libxl/%d/device/%s/%s/backend",
- domid, kinds[i], devs[j]);
- path = libxl__xs_read(gc, XBT_NULL, path);
- GCNEW(dev);
- if (path && libxl__parse_backend_path(gc, path, dev) == 0) {
- dev->domid = domid;
- dev->kind = kind;
- dev->devid = atoi(devs[j]);
- if (dev->backend_kind == LIBXL__DEVICE_KIND_CONSOLE ||
- dev->backend_kind == LIBXL__DEVICE_KIND_VUART) {
- /* Currently console devices can be destroyed
- * synchronously by just removing xenstore entries,
- * this is what libxl__device_destroy does.
- */
- libxl__device_destroy(gc, dev);
- continue;
- }
- aodev = libxl__multidev_prepare(multidev);
- aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
- aodev->dev = dev;
- aodev->force = drs->force;
- if (dev->kind == LIBXL__DEVICE_KIND_VUSB)
- libxl__initiate_device_usbctrl_remove(egc, aodev);
- else
- libxl__initiate_device_generic_remove(egc, aodev);
- }
- }
- }
-
-out:
- libxl__multidev_prepared(egc, multidev, rc);
-}
-
-/* Callbacks for device related operations */
-
-/*
- * device_backend_callback is the main callback entry point, for both device
- * addition and removal. It gets called if we reach the desired state
- * (XenbusStateClosed or XenbusStateInitWait). After that, all this
- * functions get called in the order displayed below.
- *
- * If new device types are added, they should only need to modify the
- * specific hotplug scripts call, which can be found in each OS specific
- * file. If this new devices don't need a hotplug script, no modification
- * should be needed.
- */
-
-/* This callback is part of the Qemu devices Badge */
-static void device_qemu_timeout(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs, int rc);
-
-static void device_backend_callback(libxl__egc *egc, libxl__ev_devstate *ds,
- int rc);
-
-static void device_backend_cleanup(libxl__gc *gc,
- libxl__ao_device *aodev);
-
-static void device_hotplug(libxl__egc *egc, libxl__ao_device *aodev);
-
-static void device_hotplug_child_death_cb(libxl__egc *egc,
- libxl__async_exec_state *aes,
- int rc, int status);
-
-static void device_destroy_be_watch_cb(libxl__egc *egc,
- libxl__xswait_state *xswait,
- int rc, const char *data);
-
-static void device_hotplug_done(libxl__egc *egc, libxl__ao_device *aodev);
-
-static void device_hotplug_clean(libxl__gc *gc, libxl__ao_device *aodev);
-
-void libxl__wait_device_connection(libxl__egc *egc, libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- char *be_path = libxl__device_backend_path(gc, aodev->dev);
- char *state_path = GCSPRINTF("%s/state", be_path);
- int rc = 0;
-
- if (QEMU_BACKEND(aodev->dev)) {
- /*
- * If Qemu is not running, there's no point in waiting for
- * it to change the state of the device.
- *
- * If Qemu is running, it will set the state of the device to
- * 4 directly, without waiting in state 2 for any hotplug execution.
- */
- device_hotplug(egc, aodev);
- return;
- }
-
- rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds,
- device_backend_callback,
- state_path, XenbusStateInitWait,
- LIBXL_INIT_TIMEOUT * 1000);
- if (rc) {
- LOGD(ERROR, aodev->dev->domid, "unable to initialize device %s", be_path);
- goto out;
- }
-
- return;
-
-out:
- aodev->rc = rc;
- device_hotplug_done(egc, aodev);
- return;
-}
-
-void libxl__initiate_device_generic_remove(libxl__egc *egc,
- libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- xs_transaction_t t = 0;
- char *be_path = libxl__device_backend_path(gc, aodev->dev);
- char *state_path = GCSPRINTF("%s/state", be_path);
- char *online_path = GCSPRINTF("%s/online", be_path);
- const char *state;
- libxl_dominfo info;
- uint32_t my_domid, domid = aodev->dev->domid;
- int rc = 0;
-
- libxl_dominfo_init(&info);
-
- rc = libxl__get_domid(gc, &my_domid);
- if (rc) {
- LOGD(ERROR, domid, "unable to get my domid");
- goto out;
- }
-
- if (my_domid == LIBXL_TOOLSTACK_DOMID) {
- rc = libxl_domain_info(CTX, &info, domid);
- if (rc) {
- LOGD(ERROR, domid, "unable to get info for domain %d", domid);
- goto out;
- }
- if (QEMU_BACKEND(aodev->dev) &&
- (info.paused || info.dying || info.shutdown)) {
- /*
- * TODO: 4.2 Bodge due to QEMU, see comment on top of
- * libxl__initiate_device_generic_remove in libxl_internal.h
- */
- rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
- device_qemu_timeout,
- LIBXL_QEMU_BODGE_TIMEOUT * 1000);
- if (rc) {
- LOGD(ERROR, domid, "unable to register timeout for Qemu device %s",
- be_path);
- goto out;
- }
- goto out_success;
- }
- }
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) {
- LOGD(ERROR, domid, "unable to start transaction");
- goto out;
- }
-
- if (aodev->force.flag == LIBXL__FORCE_ON)
- libxl__xs_path_cleanup(gc, t,
- libxl__device_frontend_path(gc, aodev->dev));
-
- rc = libxl__xs_read_checked(gc, t, state_path, &state);
- if (rc) {
- LOGD(ERROR, domid, "unable to read device state from path %s", state_path);
- goto out;
- }
-
- /* if state_path is empty, assume backend is gone (backend domain
- * shutdown?), cleanup frontend only; rc=0 */
- if (!state) {
- LOG(INFO, "backend %s already removed, cleanup frontend only", be_path);
- goto out;
- }
-
- rc = libxl__xs_write_checked(gc, t, online_path, "0");
- if (rc)
- goto out;
-
- /*
- * Check if device is already in "closed" state, in which case
- * it should not be changed.
- */
- if (state && atoi(state) != XenbusStateClosed) {
- rc = libxl__xs_write_checked(gc, t, state_path, GCSPRINTF("%d", XenbusStateClosing));
- if (rc) {
- LOGD(ERROR, domid, "unable to write to xenstore path %s", state_path);
- goto out;
- }
- }
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc < 0) goto out;
- }
-
- rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds,
- device_backend_callback,
- state_path, XenbusStateClosed,
- LIBXL_DESTROY_TIMEOUT * 1000);
- if (rc) {
- LOGD(ERROR, domid, "unable to remove device %s", be_path);
- goto out;
- }
-
-out_success:
- libxl_dominfo_dispose(&info);
- return;
-
-out:
- aodev->rc = rc;
- libxl_dominfo_dispose(&info);
- libxl__xs_transaction_abort(gc, &t);
- device_hotplug_done(egc, aodev);
- return;
-}
-
-static void device_qemu_timeout(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs, int rc)
-{
- libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
- STATE_AO_GC(aodev->ao);
- char *be_path = libxl__device_backend_path(gc, aodev->dev);
- char *state_path = GCSPRINTF("%s/state", be_path);
- const char *xs_state;
- xs_transaction_t t = 0;
-
- if (rc != ERROR_TIMEDOUT)
- goto out;
-
- libxl__ev_time_deregister(gc, &aodev->timeout);
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) {
- LOGD(ERROR, aodev->dev->domid, "unable to start transaction");
- goto out;
- }
-
- /*
- * Check that the state path exists and is actually different than
- * 6 before unconditionally setting it. If Qemu runs on a driver
- * domain it is possible that the driver domain has already cleaned
- * the backend path if the device has reached state 6.
- */
- rc = libxl__xs_read_checked(gc, XBT_NULL, state_path, &xs_state);
- if (rc) goto out;
-
- if (xs_state && atoi(xs_state) != XenbusStateClosed) {
- rc = libxl__xs_write_checked(gc, XBT_NULL, state_path, GCSPRINTF("%d", XenbusStateClosed));
- if (rc) goto out;
- }
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc < 0) goto out;
- }
-
- device_hotplug(egc, aodev);
- return;
-
-out:
- libxl__xs_transaction_abort(gc, &t);
- aodev->rc = rc;
- device_hotplug_done(egc, aodev);
-}
-
-static void device_backend_callback(libxl__egc *egc, libxl__ev_devstate *ds,
- int rc) {
- libxl__ao_device *aodev = CONTAINER_OF(ds, *aodev, backend_ds);
- STATE_AO_GC(aodev->ao);
-
- LOGD(DEBUG, aodev->dev->domid, "calling device_backend_cleanup");
- device_backend_cleanup(gc, aodev);
-
- if (rc == ERROR_TIMEDOUT &&
- aodev->action == LIBXL__DEVICE_ACTION_REMOVE &&
- aodev->force.flag == LIBXL__FORCE_AUTO) {
- LOGD(DEBUG, aodev->dev->domid, "Timeout reached, initiating forced remove");
- aodev->force.flag = LIBXL__FORCE_ON;
- libxl__initiate_device_generic_remove(egc, aodev);
- return;
- }
-
- if (rc) {
- LOGD(ERROR, aodev->dev->domid, "unable to %s device with path %s",
- libxl__device_action_to_string(aodev->action),
- libxl__device_backend_path(gc, aodev->dev));
- goto out;
- }
-
- device_hotplug(egc, aodev);
- return;
-
-out:
- aodev->rc = rc;
- device_hotplug_done(egc, aodev);
- return;
-}
-
-static void device_backend_cleanup(libxl__gc *gc, libxl__ao_device *aodev)
-{
- if (!aodev) return;
- libxl__ev_devstate_cancel(gc, &aodev->backend_ds);
-}
-
-static void device_hotplug(libxl__egc *egc, libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- libxl__async_exec_state *aes = &aodev->aes;
- char *be_path = libxl__device_backend_path(gc, aodev->dev);
- char **args = NULL, **env = NULL;
- int rc = 0;
- int hotplug, nullfd = -1;
- uint32_t domid;
-
- /*
- * If device is attached from a driver domain don't try to execute
- * hotplug scripts
- */
- rc = libxl__get_domid(gc, &domid);
- if (rc) {
- LOGD(ERROR, aodev->dev->domid, "Failed to get domid");
- goto out;
- }
- if (aodev->dev->backend_domid != domid) {
- LOGD(DEBUG, aodev->dev->domid,
- "Backend domid %d, domid %d, assuming driver domains",
- aodev->dev->backend_domid, domid);
-
- if (aodev->action != LIBXL__DEVICE_ACTION_REMOVE) {
- LOG(DEBUG, "Not a remove, not executing hotplug scripts");
- goto out;
- }
-
- aodev->xswait.ao = ao;
- aodev->xswait.what = "removal of backend path";
- aodev->xswait.path = be_path;
- aodev->xswait.timeout_ms = LIBXL_DESTROY_TIMEOUT * 1000;
- aodev->xswait.callback = device_destroy_be_watch_cb;
- rc = libxl__xswait_start(gc, &aodev->xswait);
- if (rc) {
- LOGD(ERROR, aodev->dev->domid,
- "Setup of backend removal watch failed (path %s)", be_path);
- goto out;
- }
-
- return;
- }
-
- /* Check if we have to execute hotplug scripts for this device
- * and return the necessary args/env vars for execution */
- hotplug = libxl__get_hotplug_script_info(gc, aodev->dev, &args, &env,
- aodev->action,
- aodev->num_exec);
- switch (hotplug) {
- case 0:
- /* no hotplug script to execute */
- LOGD(DEBUG, aodev->dev->domid, "No hotplug script to execute");
- goto out;
- case 1:
- /* execute hotplug script */
- break;
- default:
- /* everything else is an error */
- LOGD(ERROR, aodev->dev->domid,
- "unable to get args/env to execute hotplug script for "
- "device %s", libxl__device_backend_path(gc, aodev->dev));
- rc = hotplug;
- goto out;
- }
-
- assert(args != NULL);
- LOGD(DEBUG, aodev->dev->domid, "calling hotplug script: %s %s", args[0], args[1]);
- LOGD(DEBUG, aodev->dev->domid, "extra args:");
- {
- const char *arg;
- unsigned int x;
-
- for (x = 2; (arg = args[x]); x++)
- LOGD(DEBUG, aodev->dev->domid, "\t%s", arg);
- }
- LOGD(DEBUG, aodev->dev->domid, "env:");
- if (env != NULL) {
- const char *k, *v;
- unsigned int x;
-
- for (x = 0; (k = env[x]); x += 2) {
- v = env[x+1];
- LOGD(DEBUG, aodev->dev->domid, "\t%s: %s", k, v);
- }
- }
-
- nullfd = open("/dev/null", O_RDONLY);
- if (nullfd < 0) {
- LOGD(ERROR, aodev->dev->domid, "unable to open /dev/null for hotplug script");
- rc = ERROR_FAIL;
- goto out;
- }
-
- aes->ao = ao;
- aes->what = GCSPRINTF("%s %s", args[0], args[1]);
- aes->env = env;
- aes->args = args;
- aes->callback = device_hotplug_child_death_cb;
- aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
- aes->stdfds[0] = nullfd;
- aes->stdfds[1] = 2;
- aes->stdfds[2] = -1;
-
- rc = libxl__async_exec_start(aes);
- if (rc)
- goto out;
-
- close(nullfd);
- assert(libxl__async_exec_inuse(&aodev->aes));
-
- return;
-
-out:
- if (nullfd >= 0) close(nullfd);
- aodev->rc = rc;
- device_hotplug_done(egc, aodev);
- return;
-}
-
-static void device_hotplug_child_death_cb(libxl__egc *egc,
- libxl__async_exec_state *aes,
- int rc, int status)
-{
- libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
- STATE_AO_GC(aodev->ao);
- char *be_path = libxl__device_backend_path(gc, aodev->dev);
- char *hotplug_error;
-
- device_hotplug_clean(gc, aodev);
-
- if (status && !rc) {
- hotplug_error = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/hotplug-error", be_path));
- if (hotplug_error)
- LOG(ERROR, "script: %s", hotplug_error);
- rc = ERROR_FAIL;
- }
-
- if (rc) {
- if (!aodev->rc)
- aodev->rc = rc;
- if (aodev->action == LIBXL__DEVICE_ACTION_ADD)
- /*
- * Only fail on device connection, on disconnection
- * ignore error, and continue with the remove process
- */
- goto error;
- }
-
- /* Increase num_exec and call hotplug scripts again if necessary
- * If no more executions are needed, device_hotplug will call
- * device_hotplug_done breaking the loop.
- */
- aodev->num_exec++;
- device_hotplug(egc, aodev);
-
- return;
-
-error:
- assert(aodev->rc);
- device_hotplug_done(egc, aodev);
-}
-
-static void device_destroy_be_watch_cb(libxl__egc *egc,
- libxl__xswait_state *xswait,
- int rc, const char *dir)
-{
- libxl__ao_device *aodev = CONTAINER_OF(xswait, *aodev, xswait);
- STATE_AO_GC(aodev->ao);
-
- if (rc) {
- if (rc == ERROR_TIMEDOUT)
- LOGD(ERROR, aodev->dev->domid,
- "timed out while waiting for %s to be removed",
- xswait->path);
- aodev->rc = rc;
- goto out;
- }
-
- if (dir) {
- /* backend path still exists, wait a little longer... */
- return;
- }
-
-out:
- /* We are done, backend path no longer exists */
- device_hotplug_done(egc, aodev);
-}
-
-static void device_hotplug_done(libxl__egc *egc, libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- int rc;
-
- device_hotplug_clean(gc, aodev);
-
- /* Clean xenstore if it's a disconnection */
- if (aodev->action == LIBXL__DEVICE_ACTION_REMOVE &&
- (aodev->force.flag == LIBXL__FORCE_ON || !aodev->rc)) {
- rc = libxl__device_destroy(gc, aodev->dev);
- if (!aodev->rc)
- aodev->rc = rc;
- }
-
- aodev->callback(egc, aodev);
- return;
-}
-
-static void device_hotplug_clean(libxl__gc *gc, libxl__ao_device *aodev)
-{
- /* Clean events and check reentrancy */
- libxl__ev_time_deregister(gc, &aodev->timeout);
- libxl__xswait_stop(gc, &aodev->xswait);
- assert(!libxl__async_exec_inuse(&aodev->aes));
-}
-
-static void devices_remove_callback(libxl__egc *egc,
- libxl__multidev *multidev, int rc)
-{
- libxl__devices_remove_state *drs = CONTAINER_OF(multidev, *drs, multidev);
- STATE_AO_GC(drs->ao);
-
- drs->callback(egc, drs, rc);
- return;
-}
-
-int libxl__wait_for_device_model_deprecated(libxl__gc *gc,
- uint32_t domid, char *state,
- libxl__spawn_starting *spawning,
- int (*check_callback)(libxl__gc *gc,
- uint32_t domid,
- const char *state,
- void *userdata),
- void *check_callback_userdata)
-{
- char *path;
- uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
-
- path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
- return libxl__xenstore_child_wait_deprecated(gc, domid,
- LIBXL_DEVICE_MODEL_START_TIMEOUT,
- "Device Model", path, state, spawning,
- check_callback, check_callback_userdata);
-}
-
-int libxl__wait_for_backend(libxl__gc *gc, const char *be_path,
- const char *state)
-{
- int watchdog = 100;
- const char *p, *path = GCSPRINTF("%s/state", be_path);
- int rc;
-
- while (watchdog-- > 0) {
- rc = libxl__xs_read_checked(gc, XBT_NULL, path, &p);
- if (rc) return rc;
-
- if (p == NULL) {
- LOG(ERROR, "Backend %s does not exist", be_path);
- return ERROR_FAIL;
- }
-
- if (!strcmp(p, state))
- return 0;
-
- usleep(100000);
- }
-
- LOG(ERROR, "Backend %s not ready", be_path);
- return ERROR_FAIL;
-}
-
-/* generic callback for devices that only need to set ao_complete */
-void device_addrm_aocomplete(libxl__egc *egc, libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
-
- if (aodev->rc) {
- if (aodev->dev) {
- LOGD(ERROR, aodev->dev->domid, "Unable to %s %s with id %u",
- libxl__device_action_to_string(aodev->action),
- libxl__device_kind_to_string(aodev->dev->kind),
- aodev->dev->devid);
- } else {
- LOG(ERROR, "unable to %s device",
- libxl__device_action_to_string(aodev->action));
- }
- goto out;
- }
-
-out:
- libxl__ao_complete(egc, ao, aodev->rc);
- return;
-}
-
-/* common function to get next device id */
-int libxl__device_nextid(libxl__gc *gc, uint32_t domid,
- libxl__device_kind device)
-{
- char *libxl_dom_path, **l;
- unsigned int nb;
- int nextid = -1;
-
- if (!(libxl_dom_path = libxl__xs_libxl_path(gc, domid)))
- return nextid;
-
- l = libxl__xs_directory(gc, XBT_NULL,
- GCSPRINTF("%s/device/%s", libxl_dom_path,
- libxl__device_kind_to_string(device)), &nb);
- if (l == NULL || nb == 0)
- nextid = 0;
- else
- nextid = strtoul(l[nb - 1], NULL, 10) + 1;
-
- return nextid;
-}
-
-static void device_complete(libxl__egc *egc, libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
-
- LOG(DEBUG, "device %s %s %s",
- libxl__device_backend_path(gc, aodev->dev),
- libxl__device_action_to_string(aodev->action),
- aodev->rc ? "failed" : "succeed");
-
- libxl__nested_ao_free(aodev->ao);
-}
-
-static void qdisk_spawn_outcome(libxl__egc *egc, libxl__dm_spawn_state *dmss,
- int rc)
-{
- STATE_AO_GC(dmss->spawn.ao);
-
- LOGD(DEBUG, dmss->guest_domid, "qdisk backend spawn %s",
- rc ? "failed" : "succeed");
-
- libxl__nested_ao_free(dmss->spawn.ao);
-}
-
-/*
- * Data structures used to track devices handled by driver domains
- */
-
-/*
- * Structure that describes a device handled by a driver domain
- */
-typedef struct libxl__ddomain_device {
- libxl__device *dev;
- LIBXL_SLIST_ENTRY(struct libxl__ddomain_device) next;
-} libxl__ddomain_device;
-
-/*
- * Structure that describes a domain and it's associated devices
- */
-typedef struct libxl__ddomain_guest {
- uint32_t domid;
- int num_qdisks;
- LIBXL_SLIST_HEAD(, struct libxl__ddomain_device) devices;
- LIBXL_SLIST_ENTRY(struct libxl__ddomain_guest) next;
-} libxl__ddomain_guest;
-
-/*
- * Main structure used by a driver domain to keep track of devices
- * currently in use
- */
-typedef struct {
- libxl__ao *ao;
- libxl__ev_xswatch watch;
- LIBXL_SLIST_HEAD(, struct libxl__ddomain_guest) guests;
-} libxl__ddomain;
-
-static libxl__ddomain_guest *search_for_guest(libxl__ddomain *ddomain,
- uint32_t domid)
-{
- libxl__ddomain_guest *dguest;
-
- LIBXL_SLIST_FOREACH(dguest, &ddomain->guests, next) {
- if (dguest->domid == domid)
- return dguest;
- }
- return NULL;
-}
-
-static libxl__ddomain_device *search_for_device(libxl__ddomain_guest *dguest,
- libxl__device *dev)
-{
- libxl__ddomain_device *ddev;
-
- LIBXL_SLIST_FOREACH(ddev, &dguest->devices, next) {
-#define LIBXL_DEVICE_CMP(dev1, dev2, entry) (dev1->entry == dev2->entry)
- if (LIBXL_DEVICE_CMP(ddev->dev, dev, backend_devid) &&
- LIBXL_DEVICE_CMP(ddev->dev, dev, backend_domid) &&
- LIBXL_DEVICE_CMP(ddev->dev, dev, devid) &&
- LIBXL_DEVICE_CMP(ddev->dev, dev, domid) &&
- LIBXL_DEVICE_CMP(ddev->dev, dev, backend_kind) &&
- LIBXL_DEVICE_CMP(ddev->dev, dev, kind))
- return ddev;
-#undef LIBXL_DEVICE_CMP
- }
-
- return NULL;
-}
-
-static void check_and_maybe_remove_guest(libxl__gc *gc,
- libxl__ddomain *ddomain,
- libxl__ddomain_guest *dguest)
-{
- assert(ddomain);
-
- if (dguest != NULL && LIBXL_SLIST_FIRST(&dguest->devices) == NULL) {
- LIBXL_SLIST_REMOVE(&ddomain->guests, dguest, libxl__ddomain_guest,
- next);
- LOGD(DEBUG, dguest->domid, "Removed domain from the list of active guests");
- /* Clear any leftovers in libxl/<domid> */
- libxl__xs_rm_checked(gc, XBT_NULL,
- GCSPRINTF("libxl/%u", dguest->domid));
- free(dguest);
- }
-}
-
-/*
- * The following comment applies to both add_device and remove_device.
- *
- * If the return value is greater than 0, it means there's no ao dispatched,
- * so the free of the nested ao should be done by the parent when it has
- * finished.
- */
-static int add_device(libxl__egc *egc, libxl__ao *ao,
- libxl__ddomain_guest *dguest,
- libxl__device *dev)
-{
- AO_GC;
- libxl__ao_device *aodev;
- libxl__ddomain_device *ddev;
- libxl__dm_spawn_state *dmss;
- int rc = 0;
-
- /*
- * New device addition, allocate a struct to hold it and add it
- * to the list of active devices for a given guest.
- */
- ddev = libxl__zalloc(NOGC, sizeof(*ddev));
- ddev->dev = libxl__zalloc(NOGC, sizeof(*ddev->dev));
- *ddev->dev = *dev;
- LIBXL_SLIST_INSERT_HEAD(&dguest->devices, ddev, next);
- LOGD(DEBUG, dev->domid, "Added device %s to the list of active devices",
- libxl__device_backend_path(gc, dev));
-
- switch(dev->backend_kind) {
- case LIBXL__DEVICE_KIND_QDISK:
- if (dguest->num_qdisks == 0) {
- GCNEW(dmss);
- dmss->guest_domid = dev->domid;
- dmss->spawn.ao = ao;
- dmss->callback = qdisk_spawn_outcome;
-
- libxl__spawn_qdisk_backend(egc, dmss);
- }
- dguest->num_qdisks++;
- break;
- default:
- GCNEW(aodev);
- libxl__prepare_ao_device(ao, aodev);
- /*
- * Clone the libxl__device to avoid races if remove_device is called
- * before the device addition has finished.
- */
- GCNEW(aodev->dev);
- *aodev->dev = *dev;
- aodev->action = LIBXL__DEVICE_ACTION_ADD;
- aodev->callback = device_complete;
- libxl__wait_device_connection(egc, aodev);
- break;
- }
-
- return rc;
-}
-
-static int remove_device(libxl__egc *egc, libxl__ao *ao,
- libxl__ddomain_guest *dguest,
- libxl__ddomain_device *ddev)
-{
- AO_GC;
- libxl__device *dev = ddev->dev;
- libxl__ao_device *aodev;
- int rc = 0;
-
- switch(ddev->dev->backend_kind) {
- case LIBXL__DEVICE_KIND_QDISK:
- if (--dguest->num_qdisks == 0) {
- rc = libxl__destroy_qdisk_backend(gc, dev->domid);
- if (rc)
- goto out;
- }
- libxl__device_destroy(gc, dev);
- /* Return > 0, no ao has been dispatched */
- rc = 1;
- break;
- default:
- GCNEW(aodev);
- libxl__prepare_ao_device(ao, aodev);
- /*
- * Clone the libxl__device to avoid races if there's a add_device
- * running in parallel.
- */
- GCNEW(aodev->dev);
- *aodev->dev = *dev;
- aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
- aodev->callback = device_complete;
- libxl__initiate_device_generic_remove(egc, aodev);
- break;
- }
-
- /*
- * Removal of an active device, remove it from the list and
- * free it's data structures if they are no longer needed.
- *
- * NB: the freeing is safe because all the async ops launched
- * above or from add_device make a copy of the data they use, so
- * there's no risk of dereferencing.
- */
- LIBXL_SLIST_REMOVE(&dguest->devices, ddev, libxl__ddomain_device,
- next);
- LOGD(DEBUG, dev->domid, "Removed device %s from the list of active devices",
- libxl__device_backend_path(gc, dev));
-
- free(ddev->dev);
- free(ddev);
-
-out:
- return rc;
-}
-
-static void backend_watch_callback(libxl__egc *egc, libxl__ev_xswatch *watch,
- const char *watch_path,
- const char *event_path)
-{
- libxl__ddomain *ddomain = CONTAINER_OF(watch, *ddomain, watch);
- libxl__ao *nested_ao = libxl__nested_ao_create(ddomain->ao);
- STATE_AO_GC(nested_ao);
- char *p, *path;
- const char *sstate, *sonline;
- int state, online, rc;
- libxl__device *dev;
- libxl__ddomain_device *ddev = NULL;
- libxl__ddomain_guest *dguest = NULL;
- bool free_ao = false;
-
- /* Check if event_path ends with "state" or "online" and truncate it. */
- path = libxl__strdup(gc, event_path);
- p = strrchr(path, '/');
- if (p == NULL)
- goto skip;
- if (strcmp(p, "/state") != 0 && strcmp(p, "/online") != 0)
- goto skip;
- /* Truncate the string so it points to the backend directory. */
- *p = '\0';
-
- /* Fetch the value of the state and online nodes. */
- rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/state", path),
- &sstate);
- if (rc || !sstate)
- goto skip;
- state = atoi(sstate);
-
- rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/online", path),
- &sonline);
- if (rc || !sonline)
- goto skip;
- online = atoi(sonline);
-
- GCNEW(dev);
- rc = libxl__parse_backend_path(gc, path, dev);
- if (rc)
- goto skip;
-
- dguest = search_for_guest(ddomain, dev->domid);
- if (dguest == NULL && state == XenbusStateClosed) {
- /*
- * Spurious state change, device has already been disconnected
- * or never attached.
- */
- goto skip;
- }
- if (dguest == NULL) {
- /* Create a new guest struct and initialize it */
- dguest = libxl__zalloc(NOGC, sizeof(*dguest));
- dguest->domid = dev->domid;
- LIBXL_SLIST_INIT(&dguest->devices);
- LIBXL_SLIST_INSERT_HEAD(&ddomain->guests, dguest, next);
- LOGD(DEBUG, dguest->domid, "Added domain to the list of active guests");
- }
- ddev = search_for_device(dguest, dev);
- if (ddev == NULL && state == XenbusStateClosed) {
- /*
- * Spurious state change, device has already been disconnected
- * or never attached.
- */
- goto skip;
- } else if (ddev == NULL) {
- rc = add_device(egc, nested_ao, dguest, dev);
- if (rc > 0)
- free_ao = true;
- } else if (state == XenbusStateClosed && online == 0) {
- rc = remove_device(egc, nested_ao, dguest, ddev);
- if (rc > 0)
- free_ao = true;
- check_and_maybe_remove_guest(gc, ddomain, dguest);
- }
-
- if (free_ao)
- libxl__nested_ao_free(nested_ao);
-
- return;
-
-skip:
- libxl__nested_ao_free(nested_ao);
- check_and_maybe_remove_guest(gc, ddomain, dguest);
- return;
-}
-
-/* Handler of events for device driver domains */
-int libxl_device_events_handler(libxl_ctx *ctx,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, 0, ao_how);
- int rc;
- uint32_t domid;
- libxl__ddomain ddomain;
- char *be_path;
- char **kinds = NULL, **domains = NULL, **devs = NULL;
- const char *sstate;
- char *state_path;
- int state;
- unsigned int nkinds, ndomains, ndevs;
- int i, j, k;
-
- ddomain.ao = ao;
- LIBXL_SLIST_INIT(&ddomain.guests);
-
- rc = libxl__get_domid(gc, &domid);
- if (rc) {
- LOG(ERROR, "unable to get domain id");
- goto out;
- }
-
- /*
- * We use absolute paths because we want xswatch to also return
- * absolute paths that can be parsed by libxl__parse_backend_path.
- */
- be_path = GCSPRINTF("/local/domain/%u/backend", domid);
- rc = libxl__ev_xswatch_register(gc, &ddomain.watch, backend_watch_callback,
- be_path);
- if (rc) goto out;
-
- kinds = libxl__xs_directory(gc, XBT_NULL, be_path, &nkinds);
- if (kinds) {
- for (i = 0; i < nkinds; i++) {
- domains = libxl__xs_directory(gc, XBT_NULL,
- GCSPRINTF("%s/%s", be_path, kinds[i]), &ndomains);
- if (!domains)
- continue;
- for (j = 0; j < ndomains; j++) {
- devs = libxl__xs_directory(gc, XBT_NULL,
- GCSPRINTF("%s/%s/%s", be_path, kinds[i], domains[j]), &ndevs);
- if (!devs)
- continue;
- for (k = 0; k < ndevs; k++) {
- state_path = GCSPRINTF("%s/%s/%s/%s/state",
- be_path, kinds[i], domains[j], devs[k]);
- rc = libxl__xs_read_checked(gc, XBT_NULL, state_path, &sstate);
- if (rc || !sstate)
- continue;
- state = atoi(sstate);
- if (state == XenbusStateInitWait)
- backend_watch_callback(egc, &ddomain.watch,
- be_path, state_path);
- }
- }
- }
- }
-
- return AO_INPROGRESS;
-
-out:
- return AO_CREATE_FAIL(rc);
-}
-
-void device_add_domain_config(libxl__gc *gc, libxl_domain_config *d_config,
- const libxl__device_type *dt, const void *dev)
-{
- int *num_dev;
- unsigned int i;
- void *item = NULL;
-
- num_dev = libxl__device_type_get_num(dt, d_config);
-
- /* Check for existing device */
- for (i = 0; i < *num_dev; i++) {
- if (dt->compare(libxl__device_type_get_elem(dt, d_config, i), dev)) {
- item = libxl__device_type_get_elem(dt, d_config, i);
- }
- }
-
- if (!item) {
- void **devs = libxl__device_type_get_ptr(dt, d_config);
- *devs = libxl__realloc(NOGC, *devs,
- dt->dev_elem_size * (*num_dev + 1));
- item = libxl__device_type_get_elem(dt, d_config, *num_dev);
- (*num_dev)++;
- } else {
- dt->dispose(item);
- }
-
- dt->init(item);
- dt->copy(CTX, item, dev);
-}
-
-void libxl__device_add_async(libxl__egc *egc, uint32_t domid,
- const libxl__device_type *dt, void *type,
- libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- flexarray_t *back;
- flexarray_t *front, *ro_front;
- libxl__device *device;
- xs_transaction_t t = XBT_NULL;
- libxl_domain_config d_config;
- void *type_saved;
- libxl__flock *lock = NULL;
- int rc;
-
- libxl_domain_config_init(&d_config);
-
- type_saved = libxl__malloc(gc, dt->dev_elem_size);
-
- dt->init(type_saved);
- dt->copy(CTX, type_saved, type);
-
- if (dt->set_default) {
- rc = dt->set_default(gc, domid, type, aodev->update_json);
- if (rc) goto out;
- }
-
- if (dt->update_devid) {
- rc = dt->update_devid(gc, domid, type);
- if (rc) goto out;
- }
-
- if (dt->update_config)
- dt->update_config(gc, type_saved, type);
-
- GCNEW(device);
- rc = dt->to_device(gc, domid, type, device);
- if (rc) goto out;
-
- if (aodev->update_json) {
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- rc = libxl__get_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
-
- device_add_domain_config(gc, &d_config, dt, type_saved);
-
- rc = libxl__dm_check_start(gc, &d_config, domid);
- if (rc) goto out;
- }
-
- back = flexarray_make(gc, 16, 1);
- front = flexarray_make(gc, 16, 1);
- ro_front = flexarray_make(gc, 16, 1);
-
- flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
- flexarray_append_pair(back, "online", "1");
- flexarray_append_pair(back, "state",
- GCSPRINTF("%d", XenbusStateInitialising));
-
- flexarray_append_pair(front, "backend-id",
- GCSPRINTF("%d", device->backend_domid));
- flexarray_append_pair(front, "state",
- GCSPRINTF("%d", XenbusStateInitialising));
-
- if (dt->set_xenstore_config)
- dt->set_xenstore_config(gc, domid, type, back, front, ro_front);
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
-
- rc = libxl__device_exists(gc, t, device);
- if (rc < 0) goto out;
- if (rc == 1) { /* already exists in xenstore */
- LOGD(ERROR, domid, "device already exists in xenstore");
- aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
- rc = ERROR_DEVICE_EXISTS;
- goto out;
- }
-
- if (aodev->update_json) {
- rc = libxl__set_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
- }
-
- libxl__device_generic_add(gc, t, device,
- libxl__xs_kvs_of_flexarray(gc, back),
- libxl__xs_kvs_of_flexarray(gc, front),
- libxl__xs_kvs_of_flexarray(gc, ro_front));
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc < 0) goto out;
- }
-
- aodev->dev = device;
- aodev->action = LIBXL__DEVICE_ACTION_ADD;
- libxl__wait_device_connection(egc, aodev);
-
- rc = 0;
-
-out:
- libxl__xs_transaction_abort(gc, &t);
- if (lock) libxl__unlock_file(lock);
- dt->dispose(type_saved);
- libxl_domain_config_dispose(&d_config);
- aodev->rc = rc;
- if (rc) aodev->callback(egc, aodev);
- return;
-}
-
-int libxl__device_add(libxl__gc *gc, uint32_t domid,
- const libxl__device_type *dt, void *type)
-{
- flexarray_t *back;
- flexarray_t *front, *ro_front;
- libxl__device *device;
- int rc;
-
- if (dt->set_default) {
- rc = dt->set_default(gc, domid, type, false);
- if (rc) goto out;
- }
-
- if (dt->update_devid) {
- rc = dt->update_devid(gc, domid, type);
- if (rc) goto out;
- }
-
- GCNEW(device);
- rc = dt->to_device(gc, domid, type, device);
- if (rc) goto out;
-
- back = flexarray_make(gc, 16, 1);
- front = flexarray_make(gc, 16, 1);
- ro_front = flexarray_make(gc, 16, 1);
-
- flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
- flexarray_append_pair(back, "online", "1");
- flexarray_append_pair(back, "state",
- GCSPRINTF("%d", XenbusStateInitialising));
- flexarray_append_pair(front, "backend-id",
- libxl__sprintf(gc, "%d", device->backend_domid));
- flexarray_append_pair(front, "state",
- GCSPRINTF("%d", XenbusStateInitialising));
-
- if (dt->set_xenstore_config)
- dt->set_xenstore_config(gc, domid, type, back, front, ro_front);
-
- rc = libxl__device_generic_add(gc, XBT_NULL, device,
- libxl__xs_kvs_of_flexarray(gc, back),
- libxl__xs_kvs_of_flexarray(gc, front),
- libxl__xs_kvs_of_flexarray(gc, ro_front));
- if (rc) goto out;
-
- rc = 0;
-
-out:
- return rc;
-}
-
-void *libxl__device_list(libxl__gc *gc, const libxl__device_type *dt,
- uint32_t domid, int *num)
-{
- void *r = NULL;
- void *list = NULL;
- void *item = NULL;
- char *libxl_path;
- char **dir = NULL;
- unsigned int ndirs = 0;
- unsigned int ndevs = 0;
- int rc;
-
- *num = 0;
-
- libxl_path = GCSPRINTF("%s/device/%s",
- libxl__xs_libxl_path(gc, domid),
- libxl__device_kind_to_string(dt->type));
-
- dir = libxl__xs_directory(gc, XBT_NULL, libxl_path, &ndirs);
-
- if (dir && ndirs) {
- if (dt->get_num) {
- if (ndirs != 1) {
- LOGD(ERROR, domid, "multiple entries in %s\n", libxl_path);
- rc = ERROR_FAIL;
- goto out;
- }
- rc = dt->get_num(gc, GCSPRINTF("%s/%s", libxl_path, *dir), &ndevs);
- if (rc) goto out;
- } else {
- ndevs = ndirs;
- }
- list = libxl__malloc(NOGC, dt->dev_elem_size * ndevs);
- item = list;
-
- while (*num < ndevs) {
- dt->init(item);
-
- if (dt->from_xenstore) {
- int nr = dt->get_num ? *num : atoi(*dir);
- char *device_libxl_path = GCSPRINTF("%s/%s", libxl_path, *dir);
- rc = dt->from_xenstore(gc, device_libxl_path, nr, item);
- if (rc) goto out;
- }
-
- item = (uint8_t *)item + dt->dev_elem_size;
- ++(*num);
- if (!dt->get_num)
- ++dir;
- }
- }
-
- r = list;
- list = NULL;
-
-out:
-
- if (list) {
- libxl__device_list_free(dt, list, *num);
- *num = 0;
- }
-
- return r;
-}
-
-void libxl__device_list_free(const libxl__device_type *dt,
- void *list, int num)
-{
- int i;
-
- for (i = 0; i < num; i++)
- dt->dispose((uint8_t*)list + i * dt->dev_elem_size);
-
- free(list);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-#define BACKEND_STRING_SIZE 5
-
-static void disk_eject_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
- const char *wpath, const char *epath) {
- EGC_GC;
- libxl_evgen_disk_eject *evg = (void*)w;
- const char *backend;
- char *value;
- char backend_type[BACKEND_STRING_SIZE+1];
- int rc;
-
- value = libxl__xs_read(gc, XBT_NULL, wpath);
-
- if (!value || strcmp(value, "eject"))
- return;
-
- if (libxl__xs_printf(gc, XBT_NULL, wpath, "")) {
- LIBXL__EVENT_DISASTER(gc, "xs_write failed acknowledging eject",
- errno, LIBXL_EVENT_TYPE_DISK_EJECT);
- return;
- }
-
- libxl_event *ev = NEW_EVENT(egc, DISK_EJECT, evg->domid, evg->user);
- libxl_device_disk *disk = &ev->u.disk_eject.disk;
-
- rc = libxl__xs_read_checked(gc, XBT_NULL, evg->be_ptr_path, &backend);
- if (rc) {
- LIBXL__EVENT_DISASTER(gc, "xs_read failed reading be_ptr_path",
- errno, LIBXL_EVENT_TYPE_DISK_EJECT);
- return;
- }
- if (!backend) {
- /* device has been removed, not simply ejected */
- return;
- }
-
- sscanf(backend,
- "/local/domain/%d/backend/%" TOSTRING(BACKEND_STRING_SIZE)
- "[a-z]/%*d/%*d",
- &disk->backend_domid, backend_type);
- if (!strcmp(backend_type, "tap") || !strcmp(backend_type, "vbd")) {
- disk->backend = LIBXL_DISK_BACKEND_TAP;
- } else if (!strcmp(backend_type, "qdisk")) {
- disk->backend = LIBXL_DISK_BACKEND_QDISK;
- } else {
- disk->backend = LIBXL_DISK_BACKEND_UNKNOWN;
- }
-
- disk->pdev_path = strdup(""); /* xxx fixme malloc failure */
- disk->format = LIBXL_DISK_FORMAT_EMPTY;
- /* this value is returned to the user: do not free right away */
- disk->vdev = libxl__strdup(NOGC, evg->vdev);
- disk->removable = 1;
- disk->readwrite = 0;
- disk->is_cdrom = 1;
-
- libxl__event_occurred(egc, ev);
-}
-
-int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t guest_domid,
- const char *vdev, libxl_ev_user user,
- libxl_evgen_disk_eject **evgen_out) {
- GC_INIT(ctx);
- CTX_LOCK;
- int rc;
- char *path;
- libxl_evgen_disk_eject *evg = NULL;
-
- evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; }
- memset(evg, 0, sizeof(*evg));
- evg->user = user;
- evg->domid = guest_domid;
- LIBXL_LIST_INSERT_HEAD(&CTX->disk_eject_evgens, evg, entry);
-
- uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid);
-
- if (!domid)
- domid = guest_domid;
-
- int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
-
- path = GCSPRINTF("%s/eject",
- libxl__domain_device_frontend_path(gc, domid, devid,
- LIBXL__DEVICE_KIND_VBD));
- if (!path) { rc = ERROR_NOMEM; goto out; }
-
- const char *libxl_path = libxl__domain_device_frontend_path(gc, domid, devid,
- LIBXL__DEVICE_KIND_VBD);
- evg->be_ptr_path = libxl__sprintf(NOGC, "%s/backend", libxl_path);
-
- const char *configured_vdev;
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/dev", libxl_path), &configured_vdev);
- if (rc) goto out;
-
- evg->vdev = libxl__strdup(NOGC, configured_vdev);
-
- rc = libxl__ev_xswatch_register(gc, &evg->watch,
- disk_eject_xswatch_callback, path);
- if (rc) goto out;
-
- *evgen_out = evg;
- CTX_UNLOCK;
- GC_FREE;
- return 0;
-
- out:
- if (evg)
- libxl__evdisable_disk_eject(gc, evg);
- CTX_UNLOCK;
- GC_FREE;
- return rc;
-}
-
-void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) {
- CTX_LOCK;
-
- LIBXL_LIST_REMOVE(evg, entry);
-
- if (libxl__ev_xswatch_isregistered(&evg->watch))
- libxl__ev_xswatch_deregister(gc, &evg->watch);
-
- free(evg->vdev);
- free(evg->be_ptr_path);
- free(evg);
-
- CTX_UNLOCK;
-}
-
-void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) {
- GC_INIT(ctx);
- libxl__evdisable_disk_eject(gc, evg);
- GC_FREE;
-}
-
-static int libxl__device_disk_setdefault(libxl__gc *gc, uint32_t domid,
- libxl_device_disk *disk, bool hotplug)
-{
- int rc;
-
- libxl_defbool_setdefault(&disk->discard_enable, !!disk->readwrite);
- libxl_defbool_setdefault(&disk->colo_enable, false);
- libxl_defbool_setdefault(&disk->colo_restore_enable, false);
-
- rc = libxl__resolve_domid(gc, disk->backend_domname, &disk->backend_domid);
- if (rc < 0) return rc;
-
- /* Force Qdisk backend for CDROM devices of guests with a device model. */
- if (disk->is_cdrom != 0 &&
- libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) {
- if (!(disk->backend == LIBXL_DISK_BACKEND_QDISK ||
- disk->backend == LIBXL_DISK_BACKEND_UNKNOWN)) {
- LOGD(ERROR, domid, "Backend for CD devices on HVM guests must be Qdisk");
- return ERROR_FAIL;
- }
- disk->backend = LIBXL_DISK_BACKEND_QDISK;
- }
-
- rc = libxl__device_disk_set_backend(gc, disk);
- return rc;
-}
-
-static int libxl__device_from_disk(libxl__gc *gc, uint32_t domid,
- const libxl_device_disk *disk,
- libxl__device *device)
-{
- int devid;
-
- devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
- if (devid==-1) {
- LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
- disk->vdev);
- return ERROR_INVAL;
- }
-
- device->backend_domid = disk->backend_domid;
- device->backend_devid = devid;
-
- switch (disk->backend) {
- case LIBXL_DISK_BACKEND_PHY:
- device->backend_kind = LIBXL__DEVICE_KIND_VBD;
- break;
- case LIBXL_DISK_BACKEND_TAP:
- device->backend_kind = LIBXL__DEVICE_KIND_VBD;
- break;
- case LIBXL_DISK_BACKEND_QDISK:
- device->backend_kind = LIBXL__DEVICE_KIND_QDISK;
- break;
- default:
- LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
- disk->backend);
- return ERROR_INVAL;
- }
-
- device->domid = domid;
- device->devid = devid;
- device->kind = LIBXL__DEVICE_KIND_VBD;
-
- return 0;
-}
-
-/* Specific function called directly only by local disk attach,
- * all other users should instead use the regular
- * libxl__device_disk_add wrapper
- *
- * The (optionally) passed function get_vdev will be used to
- * set the vdev the disk should be attached to. When it is set the caller
- * must also pass get_vdev_user, which will be passed to get_vdev.
- *
- * The passed get_vdev function is also in charge of printing
- * the corresponding error message when appropiate.
- */
-static void device_disk_add(libxl__egc *egc, uint32_t domid,
- libxl_device_disk *disk,
- libxl__ao_device *aodev,
- char *get_vdev(libxl__gc *, void *,
- xs_transaction_t),
- void *get_vdev_user)
-{
- STATE_AO_GC(aodev->ao);
- flexarray_t *front = NULL;
- flexarray_t *back = NULL;
- char *dev = NULL, *script;
- libxl__device *device;
- int rc;
- libxl_ctx *ctx = gc->owner;
- xs_transaction_t t = XBT_NULL;
- libxl_domain_config d_config;
- libxl_device_disk disk_saved;
- libxl__flock *lock = NULL;
-
- libxl_domain_config_init(&d_config);
- libxl_device_disk_init(&disk_saved);
- libxl_device_disk_copy(ctx, &disk_saved, disk);
-
- libxl_domain_type type = libxl__domain_type(gc, domid);
- if (type == LIBXL_DOMAIN_TYPE_INVALID) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- /*
- * get_vdev != NULL -> local attach
- * get_vdev == NULL -> block attach
- *
- * We don't care about local attach state because it's only
- * intermediate state.
- */
- if (!get_vdev && aodev->update_json) {
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- rc = libxl__get_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
-
- device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
- &disk_saved);
-
- rc = libxl__dm_check_start(gc, &d_config, domid);
- if (rc) goto out;
- }
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
-
- if (get_vdev) {
- assert(get_vdev_user);
- disk->vdev = get_vdev(gc, get_vdev_user, t);
- if (disk->vdev == NULL) {
- rc = ERROR_FAIL;
- goto out;
- }
- }
-
- rc = libxl__device_disk_setdefault(gc, domid, disk, aodev->update_json);
- if (rc) goto out;
-
- front = flexarray_make(gc, 16, 1);
- back = flexarray_make(gc, 16, 1);
-
- GCNEW(device);
- rc = libxl__device_from_disk(gc, domid, disk, device);
- if (rc != 0) {
- LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
- disk->vdev);
- goto out;
- }
-
- rc = libxl__device_exists(gc, t, device);
- if (rc < 0) goto out;
- if (rc == 1) { /* already exists in xenstore */
- LOGD(ERROR, domid, "device already exists in xenstore");
- aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
- rc = ERROR_DEVICE_EXISTS;
- goto out;
- }
-
- switch (disk->backend) {
- case LIBXL_DISK_BACKEND_PHY:
- dev = disk->pdev_path;
-
- flexarray_append(back, "params");
- flexarray_append(back, dev);
-
- script = libxl__abs_path(gc, disk->script?: "block",
- libxl__xen_script_dir_path());
- flexarray_append_pair(back, "script", script);
-
- assert(device->backend_kind == LIBXL__DEVICE_KIND_VBD);
- break;
-
- case LIBXL_DISK_BACKEND_TAP:
- LOG(ERROR, "blktap is not supported");
- rc = ERROR_FAIL;
- goto out;
- case LIBXL_DISK_BACKEND_QDISK:
- flexarray_append(back, "params");
- flexarray_append(back, GCSPRINTF("%s:%s",
- libxl__device_disk_string_of_format(disk->format),
- disk->pdev_path ? : ""));
- if (libxl_defbool_val(disk->colo_enable)) {
- flexarray_append(back, "colo-host");
- flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_host));
- flexarray_append(back, "colo-port");
- flexarray_append(back, libxl__sprintf(gc, "%d", disk->colo_port));
- flexarray_append(back, "colo-export");
- flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_export));
- flexarray_append(back, "active-disk");
- flexarray_append(back, libxl__sprintf(gc, "%s", disk->active_disk));
- flexarray_append(back, "hidden-disk");
- flexarray_append(back, libxl__sprintf(gc, "%s", disk->hidden_disk));
- }
- assert(device->backend_kind == LIBXL__DEVICE_KIND_QDISK);
- break;
- default:
- LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
- disk->backend);
- rc = ERROR_INVAL;
- goto out;
- }
-
- flexarray_append(back, "frontend-id");
- flexarray_append(back, GCSPRINTF("%d", domid));
- flexarray_append(back, "online");
- flexarray_append(back, "1");
- flexarray_append(back, "removable");
- flexarray_append(back, GCSPRINTF("%d", (disk->removable) ? 1 : 0));
- flexarray_append(back, "bootable");
- flexarray_append(back, GCSPRINTF("%d", 1));
- flexarray_append(back, "state");
- flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
- flexarray_append(back, "dev");
- flexarray_append(back, disk->vdev);
- flexarray_append(back, "type");
- flexarray_append(back, libxl__device_disk_string_of_backend(disk->backend));
- flexarray_append(back, "mode");
- flexarray_append(back, disk->readwrite ? "w" : "r");
- flexarray_append(back, "device-type");
- flexarray_append(back, disk->is_cdrom ? "cdrom" : "disk");
- if (disk->direct_io_safe) {
- flexarray_append(back, "direct-io-safe");
- flexarray_append(back, "1");
- }
- flexarray_append_pair(back, "discard-enable",
- libxl_defbool_val(disk->discard_enable) ?
- "1" : "0");
-
- flexarray_append(front, "backend-id");
- flexarray_append(front, GCSPRINTF("%d", disk->backend_domid));
- flexarray_append(front, "state");
- flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
- flexarray_append(front, "virtual-device");
- flexarray_append(front, GCSPRINTF("%d", device->devid));
- flexarray_append(front, "device-type");
- flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk");
-
- /*
- * Old PV kernel disk frontends before 2.6.26 rely on tool stack to
- * write disk native protocol to frontend node. Xend does this, port
- * this behaviour to xl.
- *
- * New kernels write this node themselves. In that case it just
- * overwrites an existing node which is OK.
- */
- if (type == LIBXL_DOMAIN_TYPE_PV) {
- const char *protocol =
- xc_domain_get_native_protocol(ctx->xch, domid);
- if (protocol) {
- flexarray_append(front, "protocol");
- flexarray_append(front, libxl__strdup(gc, protocol));
- }
- }
-
- if (!get_vdev && aodev->update_json) {
- rc = libxl__set_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
- }
-
- libxl__device_generic_add(gc, t, device,
- libxl__xs_kvs_of_flexarray(gc, back),
- libxl__xs_kvs_of_flexarray(gc, front),
- NULL);
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc < 0) goto out;
- }
-
- aodev->dev = device;
- aodev->action = LIBXL__DEVICE_ACTION_ADD;
- libxl__wait_device_connection(egc, aodev);
-
- rc = 0;
-
-out:
- libxl__xs_transaction_abort(gc, &t);
- if (lock) libxl__unlock_file(lock);
- libxl_device_disk_dispose(&disk_saved);
- libxl_domain_config_dispose(&d_config);
- aodev->rc = rc;
- if (rc) aodev->callback(egc, aodev);
- return;
-}
-
-static void libxl__device_disk_add(libxl__egc *egc, uint32_t domid,
- libxl_device_disk *disk,
- libxl__ao_device *aodev)
-{
- device_disk_add(egc, domid, disk, aodev, NULL, NULL);
-}
-
-static int libxl__disk_from_xenstore(libxl__gc *gc, const char *libxl_path,
- libxl_devid devid,
- libxl_device_disk *disk)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- unsigned int len;
- char *tmp;
- int rc;
-
- const char *backend_path;
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path),
- &backend_path);
- if (rc) goto out;
-
- if (!backend_path) {
- LOG(ERROR, "disk %s does not exist (no backend path", libxl_path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__backendpath_parse_domid(gc, backend_path, &disk->backend_domid);
- if (rc) {
- LOG(ERROR, "Unable to fetch device backend domid from %s", backend_path);
- goto out;
- }
-
- /*
- * "params" may not be present; but everything else must be.
- * colo releated entries(colo-host, colo-port, colo-export,
- * active-disk and hidden-disk) are present only if colo is
- * enabled.
- */
- tmp = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/params", libxl_path), &len);
- if (tmp && strchr(tmp, ':')) {
- disk->pdev_path = strdup(strchr(tmp, ':') + 1);
- free(tmp);
- } else {
- disk->pdev_path = tmp;
- }
-
- tmp = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/colo-host", libxl_path), &len);
- if (tmp) {
- libxl_defbool_set(&disk->colo_enable, true);
- disk->colo_host = tmp;
-
- tmp = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/colo-port", libxl_path), &len);
- if (!tmp) {
- LOG(ERROR, "Missing xenstore node %s/colo-port", libxl_path);
- goto cleanup;
- }
- disk->colo_port = atoi(tmp);
-
-#define XS_READ_COLO(param, item) do { \
- tmp = xs_read(ctx->xsh, XBT_NULL, \
- GCSPRINTF("%s/"#param"", libxl_path), &len); \
- if (!tmp) { \
- LOG(ERROR, "Missing xenstore node %s/"#param"", libxl_path); \
- goto cleanup; \
- } \
- disk->item = tmp; \
-} while (0)
- XS_READ_COLO(colo-export, colo_export);
- XS_READ_COLO(active-disk, active_disk);
- XS_READ_COLO(hidden-disk, hidden_disk);
-#undef XS_READ_COLO
- } else {
- libxl_defbool_set(&disk->colo_enable, false);
- }
-
- tmp = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/type", libxl_path));
- if (!tmp) {
- LOG(ERROR, "Missing xenstore node %s/type", libxl_path);
- goto cleanup;
- }
- libxl_string_to_backend(ctx, tmp, &(disk->backend));
-
- disk->vdev = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/dev", libxl_path), &len);
- if (!disk->vdev) {
- LOG(ERROR, "Missing xenstore node %s/dev", libxl_path);
- goto cleanup;
- }
-
- tmp = libxl__xs_read(gc, XBT_NULL, libxl__sprintf
- (gc, "%s/removable", libxl_path));
- if (!tmp) {
- LOG(ERROR, "Missing xenstore node %s/removable", libxl_path);
- goto cleanup;
- }
- disk->removable = atoi(tmp);
-
- tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/mode", libxl_path));
- if (!tmp) {
- LOG(ERROR, "Missing xenstore node %s/mode", libxl_path);
- goto cleanup;
- }
- if (!strcmp(tmp, "w"))
- disk->readwrite = 1;
- else
- disk->readwrite = 0;
-
- tmp = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/device-type", libxl_path));
- if (!tmp) {
- LOG(ERROR, "Missing xenstore node %s/device-type", libxl_path);
- goto cleanup;
- }
- disk->is_cdrom = !strcmp(tmp, "cdrom");
-
- disk->format = LIBXL_DISK_FORMAT_UNKNOWN;
-
- return 0;
-cleanup:
- rc = ERROR_FAIL;
- out:
- libxl_device_disk_dispose(disk);
- return rc;
-}
-
-int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid,
- const char *vdev, libxl_device_disk *disk)
-{
- GC_INIT(ctx);
- char *libxl_path;
- int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
- int rc = ERROR_FAIL;
-
- if (devid < 0)
- return ERROR_INVAL;
-
- libxl_device_disk_init(disk);
-
- libxl_path = libxl__domain_device_libxl_path(gc, domid, devid,
- LIBXL__DEVICE_KIND_VBD);
-
- rc = libxl__disk_from_xenstore(gc, libxl_path, devid, disk);
-
- GC_FREE;
- return rc;
-}
-
-int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_disk *disk,
- libxl_diskinfo *diskinfo)
-{
- GC_INIT(ctx);
- char *fe_path, *libxl_path;
- char *val;
- int rc;
-
- diskinfo->backend = NULL;
-
- diskinfo->devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
-
- /* tap devices entries in xenstore are written as vbd devices. */
- fe_path = libxl__domain_device_frontend_path(gc, domid, diskinfo->devid,
- LIBXL__DEVICE_KIND_VBD);
- libxl_path = libxl__domain_device_libxl_path(gc, domid, diskinfo->devid,
- LIBXL__DEVICE_KIND_VBD);
- diskinfo->backend = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path), NULL);
- if (!diskinfo->backend) {
- GC_FREE;
- return ERROR_FAIL;
- }
- rc = libxl__backendpath_parse_domid(gc, diskinfo->backend,
- &diskinfo->backend_id);
- if (rc) goto out;
-
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
- diskinfo->state = val ? strtoul(val, NULL, 10) : -1;
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", fe_path));
- diskinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
- diskinfo->rref = val ? strtoul(val, NULL, 10) : -1;
- diskinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/frontend", libxl_path), NULL);
- diskinfo->frontend_id = domid;
-
- GC_FREE;
- return 0;
-
- out:
- free(diskinfo->backend);
- return rc;
-}
-
-typedef struct {
- libxl__ao *ao;
- libxl_domid domid;
- libxl_device_disk *disk;
- libxl_device_disk disk_saved;
- libxl__ev_slowlock qmp_lock;
- int dm_ver;
- libxl__ev_time time;
- libxl__ev_qmp qmp;
-} libxl__cdrom_insert_state;
-
-static void cdrom_insert_lock_acquired(libxl__egc *, libxl__ev_slowlock *,
- int rc);
-static void cdrom_insert_ejected(libxl__egc *egc, libxl__ev_qmp *,
- const libxl__json_object *, int rc);
-static void cdrom_insert_addfd_cb(libxl__egc *egc, libxl__ev_qmp *,
- const libxl__json_object *, int rc);
-static void cdrom_insert_inserted(libxl__egc *egc, libxl__ev_qmp *,
- const libxl__json_object *, int rc);
-static void cdrom_insert_timout(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc);
-static void cdrom_insert_done(libxl__egc *egc,
- libxl__cdrom_insert_state *cis,
- int rc);
-
-int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- int num = 0, i;
- libxl_device_disk *disks = NULL;
- int rc;
- libxl__cdrom_insert_state *cis;
-
- GCNEW(cis);
- cis->ao = ao;
- cis->domid = domid;
- cis->disk = disk;
- libxl_device_disk_init(&cis->disk_saved);
- libxl_device_disk_copy(ctx, &cis->disk_saved, disk);
- libxl__ev_devlock_init(&cis->qmp_lock);
- cis->qmp_lock.ao = ao;
- cis->qmp_lock.domid = domid;
- libxl__ev_time_init(&cis->time);
- libxl__ev_qmp_init(&cis->qmp);
- cis->qmp.ao = ao;
- cis->qmp.domid = domid;
- cis->qmp.payload_fd = -1;
-
- libxl_domain_type type = libxl__domain_type(gc, domid);
- if (type == LIBXL_DOMAIN_TYPE_INVALID) {
- rc = ERROR_FAIL;
- goto out;
- }
- if (type != LIBXL_DOMAIN_TYPE_HVM) {
- LOGD(ERROR, domid, "cdrom-insert requires an HVM domain");
- rc = ERROR_INVAL;
- goto out;
- }
-
- if (libxl_get_stubdom_id(ctx, domid) != 0) {
- LOGD(ERROR, domid, "cdrom-insert doesn't work for stub domains");
- rc = ERROR_INVAL;
- goto out;
- }
-
- cis->dm_ver = libxl__device_model_version_running(gc, domid);
- if (cis->dm_ver == -1) {
- LOGD(ERROR, domid, "Cannot determine device model version");
- rc = ERROR_FAIL;
- goto out;
- }
-
- disks = libxl__device_list(gc, &libxl__disk_devtype, domid, &num);
- for (i = 0; i < num; i++) {
- if (disks[i].is_cdrom && !strcmp(disk->vdev, disks[i].vdev))
- {
- /* Found. Set backend type appropriately. */
- disk->backend=disks[i].backend;
- break;
- }
- }
- if (i == num) {
- LOGD(ERROR, domid, "Virtual device not found");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__device_disk_setdefault(gc, domid, disk, false);
- if (rc) goto out;
-
- if (!disk->pdev_path) {
- disk->pdev_path = libxl__strdup(NOGC, "");
- disk->format = LIBXL_DISK_FORMAT_EMPTY;
- }
-
-out:
- libxl__device_list_free(&libxl__disk_devtype, disks, num);
- if (rc) {
- cdrom_insert_done(egc, cis, rc); /* must be last */
- } else {
- cis->qmp_lock.callback = cdrom_insert_lock_acquired;
- libxl__ev_slowlock_lock(egc, &cis->qmp_lock); /* must be last */
- }
- return AO_INPROGRESS;
-}
-
-static void cdrom_insert_lock_acquired(libxl__egc *egc,
- libxl__ev_slowlock *lock,
- int rc)
-{
- libxl__cdrom_insert_state *cis = CONTAINER_OF(lock, *cis, qmp_lock);
- STATE_AO_GC(cis->ao);
-
- if (rc) goto out;
-
- rc = libxl__ev_time_register_rel(ao, &cis->time,
- cdrom_insert_timout,
- LIBXL_HOTPLUG_TIMEOUT * 1000);
- if (rc) goto out;
-
- /* We need to eject the original image first.
- * JSON is not updated.
- */
-
- if (cis->dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
- libxl__json_object *args = NULL;
- int devid = libxl__device_disk_dev_number(cis->disk->vdev,
- NULL, NULL);
-
- QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", devid);
- cis->qmp.callback = cdrom_insert_ejected;
- rc = libxl__ev_qmp_send(egc, &cis->qmp, "eject", args);
- if (rc) goto out;
- } else {
- cdrom_insert_ejected(egc, &cis->qmp, NULL, 0); /* must be last */
- }
- return;
-
-out:
- cdrom_insert_done(egc, cis, rc); /* must be last */
-}
-
-static void cdrom_insert_ejected(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *response,
- int rc)
-{
- EGC_GC;
- libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
- libxl__flock *data_lock = NULL;
- libxl__device device;
- const char *be_path, *libxl_path;
- flexarray_t *empty = NULL;
- xs_transaction_t t = XBT_NULL;
- char *tmp;
- libxl_domain_config d_config;
- bool has_callback = false;
-
- /* convenience aliases */
- libxl_domid domid = cis->domid;
- libxl_device_disk *disk = cis->disk;
-
- libxl_domain_config_init(&d_config);
-
- if (rc) goto out;
-
- rc = libxl__device_from_disk(gc, domid, disk, &device);
- if (rc) goto out;
- be_path = libxl__device_backend_path(gc, &device);
- libxl_path = libxl__device_libxl_path(gc, &device);
-
- data_lock = libxl__lock_domain_userdata(gc, domid);
- if (!data_lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- empty = flexarray_make(gc, 4, 1);
- flexarray_append_pair(empty, "type",
- libxl__device_disk_string_of_backend(disk->backend));
- flexarray_append_pair(empty, "params", "");
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
- /* Sanity check: make sure the device exists before writing here */
- tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
- if (!tmp)
- {
- LOGD(ERROR, domid, "Internal error: %s does not exist",
- GCSPRINTF("%s/frontend", libxl_path));
- rc = ERROR_FAIL;
- goto out;
- }
-
- char **kvs = libxl__xs_kvs_of_flexarray(gc, empty);
-
- rc = libxl__xs_writev(gc, t, be_path, kvs);
- if (rc) goto out;
-
- rc = libxl__xs_writev(gc, t, libxl_path, kvs);
- if (rc) goto out;
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc < 0) goto out;
- }
-
- /*
- * Now that the drive is empty, we can insert the new media.
- */
-
- rc = libxl__get_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
-
- device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
- &cis->disk_saved);
-
- rc = libxl__dm_check_start(gc, &d_config, domid);
- if (rc) goto out;
-
- if (cis->dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN &&
- disk->format != LIBXL_DISK_FORMAT_EMPTY) {
- libxl__json_object *args = NULL;
-
- assert(qmp->payload_fd == -1);
- qmp->payload_fd = open(disk->pdev_path, O_RDONLY);
- if (qmp->payload_fd < 0) {
- LOGED(ERROR, domid, "Failed to open cdrom file %s",
- disk->pdev_path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- /* This free form parameter is not use by QEMU or libxl. */
- QMP_PARAMETERS_SPRINTF(&args, "opaque", "%s:%s",
- libxl_disk_format_to_string(disk->format),
- disk->pdev_path);
- qmp->callback = cdrom_insert_addfd_cb;
- rc = libxl__ev_qmp_send(egc, qmp, "add-fd", args);
- if (rc) goto out;
- has_callback = true;
- } else {
- has_callback = false;
- }
-
- rc = 0;
-
-out:
- libxl__xs_transaction_abort(gc, &t);
- libxl_domain_config_dispose(&d_config);
- if (data_lock) libxl__unlock_file(data_lock);
- if (rc) {
- cdrom_insert_done(egc, cis, rc); /* must be last */
- } else if (!has_callback) {
- /* Only called if no asynchronous callback are set. */
- cdrom_insert_inserted(egc, qmp, NULL, 0); /* must be last */
- }
-}
-
-static void cdrom_insert_addfd_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *response,
- int rc)
-{
- EGC_GC;
- libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
- libxl__json_object *args = NULL;
- const libxl__json_object *o;
- int devid;
- int fdset;
-
- /* convenience aliases */
- libxl_device_disk *disk = cis->disk;
-
- close(qmp->payload_fd);
- qmp->payload_fd = -1;
-
- if (rc) goto out;
-
- o = libxl__json_map_get("fdset-id", response, JSON_INTEGER);
- if (!o) {
- rc = ERROR_FAIL;
- goto out;
- }
- fdset = libxl__json_object_get_integer(o);
-
- devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
- QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", devid);
- QMP_PARAMETERS_SPRINTF(&args, "target", "/dev/fdset/%d", fdset);
- libxl__qmp_param_add_string(gc, &args, "arg",
- libxl__qemu_disk_format_string(disk->format));
- qmp->callback = cdrom_insert_inserted;
- rc = libxl__ev_qmp_send(egc, qmp, "change", args);
-out:
- if (rc)
- cdrom_insert_done(egc, cis, rc); /* must be last */
-}
-
-static void cdrom_insert_inserted(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *response,
- int rc)
-{
- EGC_GC;
- libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
- libxl__flock *data_lock = NULL;
- libxl_domain_config d_config;
- flexarray_t *insert = NULL;
- xs_transaction_t t = XBT_NULL;
- libxl__device device;
- const char *be_path, *libxl_path;
- char *tmp;
-
- /* convenience aliases */
- libxl_domid domid = cis->domid;
- libxl_device_disk *disk = cis->disk;
-
- libxl_domain_config_init(&d_config);
-
- if (rc) goto out;
-
- rc = libxl__device_from_disk(gc, domid, disk, &device);
- if (rc) goto out;
- be_path = libxl__device_backend_path(gc, &device);
- libxl_path = libxl__device_libxl_path(gc, &device);
-
- data_lock = libxl__lock_domain_userdata(gc, domid);
- if (!data_lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- rc = libxl__get_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
-
- device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
- &cis->disk_saved);
-
- insert = flexarray_make(gc, 4, 1);
- flexarray_append_pair(insert, "type",
- libxl__device_disk_string_of_backend(disk->backend));
- if (disk->format != LIBXL_DISK_FORMAT_EMPTY)
- flexarray_append_pair(insert, "params",
- GCSPRINTF("%s:%s",
- libxl__device_disk_string_of_format(disk->format),
- disk->pdev_path));
- else
- flexarray_append_pair(insert, "params", "");
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
- /* Sanity check: make sure the device exists before writing here */
- tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
- if (!tmp)
- {
- LOGD(ERROR, domid, "Internal error: %s does not exist",
- GCSPRINTF("%s/frontend", libxl_path));
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__set_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
-
- char **kvs = libxl__xs_kvs_of_flexarray(gc, insert);
-
- rc = libxl__xs_writev(gc, t, be_path, kvs);
- if (rc) goto out;
-
- rc = libxl__xs_writev(gc, t, libxl_path, kvs);
- if (rc) goto out;
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc < 0) goto out;
- }
-
- rc = 0;
-
-out:
- libxl__xs_transaction_abort(gc, &t);
- libxl_domain_config_dispose(&d_config);
- if (data_lock) libxl__unlock_file(data_lock);
- cdrom_insert_done(egc, cis, rc); /* must be last */
-}
-
-static void cdrom_insert_timout(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- EGC_GC;
- libxl__cdrom_insert_state *cis = CONTAINER_OF(ev, *cis, time);
- LOGD(ERROR, cis->domid, "cdrom insertion timed out");
- cdrom_insert_done(egc, cis, rc);
-}
-
-static void cdrom_insert_done(libxl__egc *egc,
- libxl__cdrom_insert_state *cis,
- int rc)
-{
- EGC_GC;
-
- libxl__ev_time_deregister(gc, &cis->time);
- libxl__ev_qmp_dispose(gc, &cis->qmp);
- if (cis->qmp.payload_fd >= 0) close(cis->qmp.payload_fd);
- libxl__ev_slowlock_unlock(gc, &cis->qmp_lock);
- libxl_device_disk_dispose(&cis->disk_saved);
- libxl__ao_complete(egc, cis->ao, rc);
-}
-
-/* libxl__alloc_vdev only works on the local domain, that is the domain
- * where the toolstack is running */
-static char * libxl__alloc_vdev(libxl__gc *gc, void *get_vdev_user,
- xs_transaction_t t)
-{
- const char *blkdev_start = (const char *) get_vdev_user;
- int devid = 0, disk = 0, part = 0;
-
- libxl__device_disk_dev_number(blkdev_start, &disk, &part);
- if (part != 0) {
- LOG(ERROR, "blkdev_start is invalid");
- return NULL;
- }
-
- do {
- devid = libxl__device_disk_dev_number(GCSPRINTF("d%dp0", disk),
- NULL, NULL);
- if (devid < 0)
- return NULL;
- if (libxl__xs_read(gc, t, GCSPRINTF("%s/backend",
- libxl__domain_device_libxl_path(gc,
- LIBXL_TOOLSTACK_DOMID, devid,
- LIBXL__DEVICE_KIND_VBD))) == NULL) {
- if (errno == ENOENT)
- return libxl__devid_to_vdev(gc, devid);
- else
- return NULL;
- }
- disk++;
- } while (1);
- return NULL;
-}
-
-/* Callbacks */
-
-char *libxl__device_disk_find_local_path(libxl__gc *gc,
- libxl_domid guest_domid,
- const libxl_device_disk *disk,
- bool qdisk_direct)
-{
- char *path = NULL;
-
- /* No local paths for driver domains */
- if (disk->backend_domname != NULL) {
- LOG(DEBUG, "Non-local backend, can't access locally.\n");
- goto out;
- }
-
- /*
- * If this is in raw format, and we're not using a script or a
- * driver domain, we can access the target path directly.
- */
- if (disk->format == LIBXL_DISK_FORMAT_RAW
- && disk->script == NULL) {
- path = libxl__strdup(gc, disk->pdev_path);
- LOG(DEBUG, "Directly accessing local RAW disk %s", path);
- goto out;
- }
-
- /*
- * If we're being called for a qemu path, we can pass the target
- * string directly as well
- */
- if (qdisk_direct && disk->backend == LIBXL_DISK_BACKEND_QDISK) {
- path = libxl__strdup(gc, disk->pdev_path);
- LOG(DEBUG, "Directly accessing local QDISK target %s", path);
- goto out;
- }
-
- /*
- * If the format isn't raw and / or we're using a script, then see
- * if the script has written a path to the "cooked" node
- */
- if (disk->script && guest_domid != INVALID_DOMID) {
- libxl__device device;
- char *be_path, *pdpath;
- int rc;
-
- LOGD(DEBUG, guest_domid,
- "Run from a script; checking for physical-device-path (vdev %s)",
- disk->vdev);
-
- rc = libxl__device_from_disk(gc, guest_domid, disk, &device);
- if (rc < 0)
- goto out;
-
- be_path = libxl__device_backend_path(gc, &device);
-
- pdpath = libxl__sprintf(gc, "%s/physical-device-path", be_path);
-
- LOGD(DEBUG, guest_domid, "Attempting to read node %s", pdpath);
- path = libxl__xs_read(gc, XBT_NULL, pdpath);
-
- if (path)
- LOGD(DEBUG, guest_domid, "Accessing cooked block device %s", path);
- else
- LOGD(DEBUG, guest_domid, "No physical-device-path, can't access locally.");
-
- goto out;
- }
-
- out:
- return path;
-}
-
-static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev);
-
-void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
- libxl__disk_local_state *dls)
-{
- STATE_AO_GC(dls->ao);
- int rc;
- const libxl_device_disk *in_disk = dls->in_disk;
- libxl_device_disk *disk = &dls->disk;
- const char *blkdev_start = dls->blkdev_start;
-
- assert(in_disk->pdev_path);
-
- disk->vdev = NULL;
-
- if (dls->diskpath)
- LOG(DEBUG, "Strange, dls->diskpath already set: %s", dls->diskpath);
-
- LOG(DEBUG, "Trying to find local path");
-
- dls->diskpath = libxl__device_disk_find_local_path(gc, INVALID_DOMID,
- in_disk, false);
- if (dls->diskpath) {
- LOG(DEBUG, "Local path found, executing callback.");
- dls->callback(egc, dls, 0);
- } else {
- LOG(DEBUG, "Local path not found, initiating attach.");
-
- memcpy(disk, in_disk, sizeof(libxl_device_disk));
- disk->pdev_path = libxl__strdup(gc, in_disk->pdev_path);
- if (in_disk->script != NULL)
- disk->script = libxl__strdup(gc, in_disk->script);
- disk->vdev = NULL;
-
- rc = libxl__device_disk_setdefault(gc, LIBXL_TOOLSTACK_DOMID, disk,
- false);
- if (rc) goto out;
-
- libxl__prepare_ao_device(ao, &dls->aodev);
- dls->aodev.callback = local_device_attach_cb;
- device_disk_add(egc, LIBXL_TOOLSTACK_DOMID, disk, &dls->aodev,
- libxl__alloc_vdev, (void *) blkdev_start);
- }
-
- return;
-
- out:
- assert(rc);
- dls->rc = rc;
- libxl__device_disk_local_initiate_detach(egc, dls);
- dls->callback(egc, dls, rc);
-}
-
-static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
- char *be_path = NULL;
- int rc;
- libxl__device device;
- libxl_device_disk *disk = &dls->disk;
-
- rc = aodev->rc;
- if (rc) {
- LOGE(ERROR, "unable locally attach device: %s", disk->pdev_path);
- goto out;
- }
-
- rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, &device);
- if (rc < 0)
- goto out;
- be_path = libxl__device_backend_path(gc, &device);
- rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected));
- if (rc < 0)
- goto out;
-
- dls->diskpath = GCSPRINTF("/dev/%s",
- libxl__devid_to_localdev(gc, device.devid));
- LOG(DEBUG, "locally attached disk %s", dls->diskpath);
-
- dls->callback(egc, dls, 0);
- return;
-
- out:
- assert(rc);
- dls->rc = rc;
- libxl__device_disk_local_initiate_detach(egc, dls);
- return;
-}
-
-/* Callbacks for local detach */
-
-static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev);
-
-void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
- libxl__disk_local_state *dls)
-{
- STATE_AO_GC(dls->ao);
- int rc = 0;
- libxl_device_disk *disk = &dls->disk;
- libxl__device *device;
- libxl__ao_device *aodev = &dls->aodev;
- libxl__prepare_ao_device(ao, aodev);
-
- if (!dls->diskpath) goto out;
-
- if (disk->vdev != NULL) {
- GCNEW(device);
- rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID,
- disk, device);
- if (rc != 0) goto out;
-
- aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
- aodev->dev = device;
- aodev->callback = local_device_detach_cb;
- aodev->force.flag = LIBXL__FORCE_AUTO;
- libxl__initiate_device_generic_remove(egc, aodev);
- return;
- }
-
-out:
- aodev->rc = rc;
- local_device_detach_cb(egc, aodev);
- return;
-}
-
-static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
- int rc;
-
- if (aodev->rc) {
- LOGED(ERROR, aodev->dev->domid, "Unable to %s %s with id %u",
- libxl__device_action_to_string(aodev->action),
- libxl__device_kind_to_string(aodev->dev->kind),
- aodev->dev->devid);
- goto out;
- }
-
-out:
- /*
- * If there was an error in dls->rc, it means we have been called from
- * a failed execution of libxl__device_disk_local_initiate_attach,
- * so return the original error.
- */
- rc = dls->rc ? dls->rc : aodev->rc;
- dls->callback(egc, dls, rc);
- return;
-}
-
-/* The following functions are defined:
- * libxl_device_disk_add
- * libxl__add_disks
- * libxl_device_disk_remove
- * libxl_device_disk_destroy
- * libxl_device_disk_safe_remove
- */
-LIBXL_DEFINE_DEVICE_ADD(disk)
-LIBXL_DEFINE_DEVICES_ADD(disk)
-LIBXL_DEFINE_DEVICE_REMOVE(disk)
-LIBXL_DEFINE_DEVICE_SAFE_REMOVE(disk)
-
-static int libxl_device_disk_compare(const libxl_device_disk *d1,
- const libxl_device_disk *d2)
-{
- return COMPARE_DISK(d1, d2);
-}
-
-/* Take care of removable device. We maintain invariant in the
- * insert / remove operation so that:
- * 1. if xenstore is "empty" while JSON is not, the result
- * is "empty"
- * 2. if xenstore has a different media than JSON, use the
- * one in JSON
- * 3. if xenstore and JSON have the same media, well, you
- * know the answer :-)
- *
- * Currently there is only one removable device -- CDROM.
- * Look for libxl_cdrom_insert for reference.
- */
-static void libxl_device_disk_merge(libxl_ctx *ctx, void *d1, void *d2)
-{
- GC_INIT(ctx);
- libxl_device_disk *src = d1;
- libxl_device_disk *dst = d2;
-
- if (src->removable) {
- if (!src->pdev_path || *src->pdev_path == '\0') {
- /* 1, no media in drive */
- free(dst->pdev_path);
- dst->pdev_path = libxl__strdup(NOGC, "");
- dst->format = LIBXL_DISK_FORMAT_EMPTY;
- } else {
- /* 2 and 3, use JSON, no need to touch anything */
- ;
- }
- }
-}
-
-static int libxl_device_disk_dm_needed(void *e, unsigned domid)
-{
- libxl_device_disk *elem = e;
-
- return elem->backend == LIBXL_DISK_BACKEND_QDISK &&
- elem->backend_domid == domid;
-}
-
-LIBXL_DEFINE_DEVICE_LIST(disk)
-
-#define libxl__device_disk_update_devid NULL
-
-DEFINE_DEVICE_TYPE_STRUCT(disk, VBD,
- .merge = libxl_device_disk_merge,
- .dm_needed = libxl_device_disk_dm_needed,
- .from_xenstore = (device_from_xenstore_fn_t)libxl__disk_from_xenstore,
- .skip_attach = 1,
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2010 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- * Author Gianni Tedesco <gianni.tedesco@citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#include <xenctrl_dom.h>
-#include <xen/hvm/e820.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <grp.h>
-
-static const char *libxl_tapif_script(libxl__gc *gc)
-{
-#if defined(__linux__) || defined(__FreeBSD__)
- return libxl__strdup(gc, "no");
-#else
- return GCSPRINTF("%s/qemu-ifup", libxl__xen_script_dir_path());
-#endif
-}
-
-const char *libxl__device_model_savefile(libxl__gc *gc, uint32_t domid)
-{
- return GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", domid);
-}
-
-static const char *qemu_xen_path(libxl__gc *gc)
-{
- return QEMU_XEN_PATH;
-}
-
-static int libxl__create_qemu_logfile(libxl__gc *gc, char *name)
-{
- char *logfile;
- int rc, logfile_w;
-
- rc = libxl_create_logfile(CTX, name, &logfile);
- if (rc) return rc;
-
- logfile_w = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644);
-
- if (logfile_w < 0) {
- LOGE(ERROR, "unable to open Qemu logfile: %s", logfile);
- free(logfile);
- return ERROR_FAIL;
- }
-
- free(logfile);
-
- return logfile_w;
-}
-
-/*
- * userlookup_helper_getpwnam(libxl__gc*, const char *user,
- * struct passwd **pwd_r);
- *
- * userlookup_helper_getpwuid(libxl__gc*, uid_t uid,
- * struct passwd **pwd_r);
- *
- * If the user is found, return 0 and set *pwd_r to the appropriat
- * value.
- *
- * If the user is not found but there are no errors, return 0
- * and set *pwd_r to NULL.
- *
- * On error, return a libxl-style error code.
- */
-#define DEFINE_USERLOOKUP_HELPER(NAME,SPEC_TYPE,STRUCTNAME,SYSCONF) \
- static int userlookup_helper_##NAME(libxl__gc *gc, \
- SPEC_TYPE spec, \
- struct STRUCTNAME *resultbuf, \
- struct STRUCTNAME **out) \
- { \
- struct STRUCTNAME *resultp = NULL; \
- char *buf = NULL; \
- long buf_size; \
- int r; \
- \
- buf_size = sysconf(SYSCONF); \
- if (buf_size < 0) { \
- buf_size = 2048; \
- LOG(DEBUG, \
- "sysconf failed, setting the initial buffer size to %ld", \
- buf_size); \
- } \
- \
- while (1) { \
- buf = libxl__realloc(gc, buf, buf_size); \
- r = NAME##_r(spec, resultbuf, buf, buf_size, &resultp); \
- if (r == ERANGE) { \
- buf_size += 128; \
- continue; \
- } \
- if (r != 0) { \
- LOGEV(ERROR, r, "Looking up username/uid with " #NAME); \
- return ERROR_FAIL; \
- } \
- *out = resultp; \
- return 0; \
- } \
- }
-
-DEFINE_USERLOOKUP_HELPER(getpwnam, const char*, passwd, _SC_GETPW_R_SIZE_MAX);
-DEFINE_USERLOOKUP_HELPER(getpwuid, uid_t, passwd, _SC_GETPW_R_SIZE_MAX);
-
-static int libxl__domain_get_device_model_uid(libxl__gc *gc,
- libxl__dm_spawn_state *dmss)
-{
- int guest_domid = dmss->guest_domid;
- libxl__domain_build_state *const state = dmss->build_state;
- const libxl_domain_build_info *b_info = &dmss->guest_config->b_info;
-
- struct passwd *user_base, user_pwbuf;
- int rc;
- char *user;
- uid_t intended_uid = -1;
- bool kill_by_uid;
-
- /* Only qemu-upstream can run as a different uid */
- if (b_info->device_model_version != LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN)
- return 0;
-
- /*
- * From this point onward, all paths should go through the `out`
- * label. The invariants should be:
- * - rc may be 0, or an error code.
- * - if rc is an error code, user and intended_uid are ignored.
- * - if rc is 0, user may be set or not set.
- * - if user is set, then intended_uid must be set to a UID matching
- * the username `user`, and kill_by_uid must be set to the appropriate
- * value. intended_uid will be checked for root (0).
- */
-
- /*
- * If device_model_user is present, set `-runas` even if
- * dm_restrict isn't in use
- */
- user = b_info->device_model_user;
- if (user) {
- rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base);
- if (rc)
- goto out;
-
- if (!user_base) {
- LOGD(ERROR, guest_domid, "Couldn't find device_model_user %s",
- user);
- rc = ERROR_INVAL;
- goto out;
- }
-
- intended_uid = user_base->pw_uid;
- kill_by_uid = true;
- rc = 0;
- goto out;
- }
-
- /*
- * If dm_restrict isn't set, and we don't have a specified user, don't
- * bother setting a `-runas` parameter.
- */
- if (!libxl_defbool_val(b_info->dm_restrict)) {
- LOGD(DEBUG, guest_domid,
- "dm_restrict disabled, starting QEMU as root");
- user = NULL; /* Should already be null, but just in case */
- kill_by_uid = false; /* Keep older versions of gcc happy */
- rc = 0;
- goto out;
- }
-
- /*
- * dm_restrict is set, but device_model_user isn't set; look for
- * QEMU_USER_BASE_RANGE
- */
- rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE,
- &user_pwbuf, &user_base);
- if (rc)
- goto out;
- if (user_base) {
- struct passwd *user_clash, user_clash_pwbuf;
-
- intended_uid = user_base->pw_uid + guest_domid;
- rc = userlookup_helper_getpwuid(gc, intended_uid,
- &user_clash_pwbuf, &user_clash);
- if (rc)
- goto out;
- if (user_clash) {
- LOGD(ERROR, guest_domid,
- "wanted to use uid %ld (%s + %d) but that is user %s !",
- (long)intended_uid, LIBXL_QEMU_USER_RANGE_BASE,
- guest_domid, user_clash->pw_name);
- rc = ERROR_INVAL;
- goto out;
- }
-
- LOGD(DEBUG, guest_domid, "using uid %ld", (long)intended_uid);
- user = GCSPRINTF("%ld:%ld", (long)intended_uid,
- (long)user_base->pw_gid);
- kill_by_uid = true;
- rc = 0;
- goto out;
- }
-
- /*
- * We couldn't find QEMU_USER_BASE_RANGE; look for
- * QEMU_USER_SHARED. NB for QEMU_USER_SHARED, all QEMU will run
- * as the same UID, we can't kill by uid; therefore don't set uid.
- */
- user = LIBXL_QEMU_USER_SHARED;
- rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base);
- if (rc)
- goto out;
- if (user_base) {
- LOGD(WARN, guest_domid, "Could not find user %s, falling back to %s",
- LIBXL_QEMU_USER_RANGE_BASE, LIBXL_QEMU_USER_SHARED);
- intended_uid = user_base->pw_uid;
- kill_by_uid = false;
- rc = 0;
- goto out;
- }
-
- /*
- * dm_depriv is set, but we can't find a non-root uid to run as;
- * fail domain creation
- */
- LOGD(ERROR, guest_domid,
- "Could not find user %s or range base pseudo-user %s, cannot restrict",
- LIBXL_QEMU_USER_SHARED, LIBXL_QEMU_USER_RANGE_BASE);
- rc = ERROR_INVAL;
-
-out:
- /* First, do a root check if appropriate */
- if (!rc) {
- if (user && intended_uid == 0) {
- LOGD(ERROR, guest_domid, "intended_uid is 0 (root)!");
- rc = ERROR_INVAL;
- }
- }
-
- /* Then do the final set, if still appropriate */
- if (!rc && user) {
- state->dm_runas = user;
- if (kill_by_uid)
- state->dm_kill_uid = GCSPRINTF("%ld", (long)intended_uid);
- }
-
- return rc;
-}
-
-/*
- * Look up "reaper UID". If present and non-root, returns 0 and sets
- * reaper_uid. Otherwise returns libxl-style error.
- */
-static int libxl__get_reaper_uid(libxl__gc *gc, uid_t *reaper_uid)
-{
- struct passwd *user_base, user_pwbuf;
- int rc;
-
- rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_REAPER,
- &user_pwbuf, &user_base);
- /*
- * Either there was an error, or we found a suitable user; stop
- * looking
- */
- if (rc || user_base)
- goto out;
-
- rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE,
- &user_pwbuf, &user_base);
- if (rc || user_base)
- goto out;
-
- LOG(WARN, "Couldn't find uid for reaper process");
- rc = ERROR_INVAL;
-
- out:
- /* First check to see if the discovered user maps to root */
- if (!rc) {
- if (user_base->pw_uid == 0) {
- LOG(ERROR, "UID for reaper process maps to root!");
- rc = ERROR_INVAL;
- }
- }
-
- /* If everything is OK, set reaper_uid as appropriate */
- if (!rc)
- *reaper_uid = user_base->pw_uid;
-
- return rc;
-}
-
-const char *libxl__domain_device_model(libxl__gc *gc,
- const libxl_domain_build_info *info)
-{
- const char *dm;
-
- if (libxl_defbool_val(info->device_model_stubdomain))
- return NULL;
-
- if (info->device_model) {
- dm = libxl__strdup(gc, info->device_model);
- } else {
- switch (info->device_model_version) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- dm = libxl__abs_path(gc, "qemu-dm", libxl__private_bindir_path());
- break;
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- dm = qemu_xen_path(gc);
- break;
- default:
- LOG(ERROR, "invalid device model version %d",
- info->device_model_version);
- dm = NULL;
- break;
- }
- }
- return dm;
-}
-
-static int
-libxl__xc_device_get_rdm(libxl__gc *gc,
- uint32_t flags,
- uint16_t seg,
- uint8_t bus,
- uint8_t devfn,
- unsigned int *nr_entries,
- struct xen_reserved_device_memory **xrdm)
-{
- int rc = 0, r;
-
- /*
- * We really can't presume how many entries we can get in advance.
- */
- *nr_entries = 0;
- r = xc_reserved_device_memory_map(CTX->xch, flags, seg, bus, devfn,
- NULL, nr_entries);
- assert(r <= 0);
- /* "0" means we have no any rdm entry. */
- if (!r) goto out;
-
- if (errno != ENOBUFS) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- GCNEW_ARRAY(*xrdm, *nr_entries);
- r = xc_reserved_device_memory_map(CTX->xch, flags, seg, bus, devfn,
- *xrdm, nr_entries);
- if (r)
- rc = ERROR_FAIL;
-
- out:
- if (rc) {
- *nr_entries = 0;
- *xrdm = NULL;
- LOG(ERROR, "Could not get reserved device memory maps.");
- }
- return rc;
-}
-
-/*
- * Check whether there exists rdm hole in the specified memory range.
- * Returns true if exists, else returns false.
- */
-static bool overlaps_rdm(uint64_t start, uint64_t memsize,
- uint64_t rdm_start, uint64_t rdm_size)
-{
- return (start + memsize > rdm_start) && (start < rdm_start + rdm_size);
-}
-
-static void
-add_rdm_entry(libxl__gc *gc, libxl_domain_config *d_config,
- uint64_t rdm_start, uint64_t rdm_size, int rdm_policy)
-{
- d_config->rdms = libxl__realloc(NOGC, d_config->rdms,
- (d_config->num_rdms+1) * sizeof(libxl_device_rdm));
-
- d_config->rdms[d_config->num_rdms].start = rdm_start;
- d_config->rdms[d_config->num_rdms].size = rdm_size;
- d_config->rdms[d_config->num_rdms].policy = rdm_policy;
- d_config->num_rdms++;
-}
-
-/*
- * Check reported RDM regions and handle potential gfn conflicts according
- * to user preferred policy.
- *
- * RDM can reside in address space beyond 4G theoretically, but we never
- * see this in real world. So in order to avoid breaking highmem layout
- * we don't solve highmem conflict. Note this means highmem rmrr could
- * still be supported if no conflict.
- *
- * But in the case of lowmem, RDM probably scatter the whole RAM space.
- * Especially multiple RDM entries would worsen this to lead a complicated
- * memory layout. And then its hard to extend hvm_info_table{} to work
- * hvmloader out. So here we're trying to figure out a simple solution to
- * avoid breaking existing layout. So when a conflict occurs,
- *
- * #1. Above a predefined boundary (default 2G)
- * - Move lowmem_end below reserved region to solve conflict;
- *
- * #2. Below a predefined boundary (default 2G)
- * - Check strict/relaxed policy.
- * "strict" policy leads to fail libxl.
- * "relaxed" policy issue a warning message and also mask this entry
- * INVALID to indicate we shouldn't expose this entry to hvmloader.
- * Note when both policies are specified on a given region, the per-device
- * policy should override the global policy.
- */
-int libxl__domain_device_construct_rdm(libxl__gc *gc,
- libxl_domain_config *d_config,
- uint64_t rdm_mem_boundary,
- struct xc_dom_image *dom)
-{
- int i, j, conflict, rc;
- struct xen_reserved_device_memory *xrdm = NULL;
- uint32_t strategy = d_config->b_info.u.hvm.rdm.strategy;
- uint16_t seg;
- uint8_t bus, devfn;
- uint64_t rdm_start, rdm_size;
- uint64_t highmem_end = dom->highmem_end;
-
- /*
- * We just want to construct RDM once since RDM is specific to the
- * given platform, so this shouldn't change again.
- */
- if (d_config->num_rdms)
- return 0;
-
- /* Might not expose rdm. */
- if (strategy == LIBXL_RDM_RESERVE_STRATEGY_IGNORE &&
- !d_config->num_pcidevs)
- return 0;
-
- /* Query all RDM entries in this platform */
- if (strategy == LIBXL_RDM_RESERVE_STRATEGY_HOST) {
- unsigned int nr_entries;
-
- /* Collect all rdm info if exist. */
- rc = libxl__xc_device_get_rdm(gc, XENMEM_RDM_ALL,
- 0, 0, 0, &nr_entries, &xrdm);
- if (rc)
- goto out;
- if (!nr_entries)
- return 0;
-
- assert(xrdm);
-
- for (i = 0; i < nr_entries; i++)
- {
- add_rdm_entry(gc, d_config,
- pfn_to_paddr(xrdm[i].start_pfn),
- pfn_to_paddr(xrdm[i].nr_pages),
- d_config->b_info.u.hvm.rdm.policy);
- }
- }
-
- /* Query RDM entries per-device */
- for (i = 0; i < d_config->num_pcidevs; i++) {
- unsigned int n, nr_entries;
-
- seg = d_config->pcidevs[i].domain;
- bus = d_config->pcidevs[i].bus;
- devfn = PCI_DEVFN(d_config->pcidevs[i].dev,
- d_config->pcidevs[i].func);
- nr_entries = 0;
- rc = libxl__xc_device_get_rdm(gc, 0,
- seg, bus, devfn, &nr_entries, &xrdm);
- if (rc)
- goto out;
- /* No RDM to associated with this device. */
- if (!nr_entries)
- continue;
-
- assert(xrdm);
-
- rc = libxl__device_pci_setdefault(gc, DOMID_INVALID,
- &d_config->pcidevs[i], false);
- if (rc)
- goto out;
-
- for (n = 0; n < nr_entries; ++n) {
- bool new = true;
-
- /*
- * Need to check whether this entry is already saved in the
- * array. This could come from two cases:
- *
- * - user may configure to get all RDMs in this platform,
- * which is already queried before this point
- * - or two assigned devices may share one RDM entry
- *
- * Different policies may be configured on the same RDM due to
- * above two cases. But we don't allow to assign such a group
- * of devices right now so it doesn't come true in our case.
- */
- for (j = 0; j < d_config->num_rdms; j++) {
- if (d_config->rdms[j].start
- == pfn_to_paddr(xrdm[n].start_pfn))
- {
- /*
- * So the per-device policy always override the
- * global policy in this case.
- */
- d_config->rdms[j].policy
- = d_config->pcidevs[i].rdm_policy;
- new = false;
- break;
- }
- }
-
- if (new)
- add_rdm_entry(gc, d_config,
- pfn_to_paddr(xrdm[n].start_pfn),
- pfn_to_paddr(xrdm[n].nr_pages),
- d_config->pcidevs[i].rdm_policy);
- }
- }
-
- /*
- * Next step is to check and avoid potential conflict between RDM
- * entries and guest RAM. To avoid intrusive impact to existing
- * memory layout {lowmem, mmio, highmem} which is passed around
- * various function blocks, below conflicts are not handled which
- * are rare and handling them would lead to a more scattered
- * layout:
- * - RDM in highmem area (>4G)
- * - RDM lower than a defined memory boundary (e.g. 2G)
- * Otherwise for conflicts between boundary and 4G, we'll simply
- * move lowmem end below reserved region to solve conflict.
- *
- * If a conflict is detected on a given RDM entry, an error will
- * be returned if 'strict' policy is specified. Instead, if
- * 'relaxed' policy specified, this conflict is treated just as a
- * warning, but we mark this RDM entry as INVALID to indicate that
- * this entry shouldn't be exposed to hvmloader.
- *
- * Firstly we should check the case of rdm < 4G because we may
- * need to expand highmem_end.
- */
- for (i = 0; i < d_config->num_rdms; i++) {
- rdm_start = d_config->rdms[i].start;
- rdm_size = d_config->rdms[i].size;
- conflict = overlaps_rdm(0, dom->lowmem_end, rdm_start, rdm_size);
-
- if (!conflict)
- continue;
-
- /* Just check if RDM > our memory boundary. */
- if (rdm_start > rdm_mem_boundary) {
- /*
- * For HAP guests round down to a 2Mb boundary to allow use
- * of large page mappings.
- */
- if (libxl_defbool_val(d_config->c_info.hap)
- && rdm_start > MB(2))
- rdm_start &= ~(MB(2) - 1);
- /*
- * We will move downwards lowmem_end so we have to expand
- * highmem_end.
- */
- if (!highmem_end)
- highmem_end = 1ull << 32;
- highmem_end += (dom->lowmem_end - rdm_start);
- /* Now move downwards lowmem_end. */
- dom->lowmem_end = rdm_start;
- }
- }
-
- /* Sync highmem_end. */
- dom->highmem_end = highmem_end;
-
- /*
- * Finally we can take same policy to check lowmem(< 2G) and
- * highmem adjusted above.
- */
- for (i = 0; i < d_config->num_rdms; i++) {
- rdm_start = d_config->rdms[i].start;
- rdm_size = d_config->rdms[i].size;
- /* Does this entry conflict with lowmem? */
- conflict = overlaps_rdm(0, dom->lowmem_end,
- rdm_start, rdm_size);
- /* Does this entry conflict with highmem? */
- if (highmem_end)
- conflict |= overlaps_rdm((1ULL << 32),
- highmem_end - (1ULL << 32),
- rdm_start, rdm_size);
-
- if (!conflict)
- continue;
-
- if (d_config->rdms[i].policy == LIBXL_RDM_RESERVE_POLICY_STRICT) {
- LOG(ERROR, "RDM conflict at 0x%"PRIx64".\n",
- d_config->rdms[i].start);
- goto out;
- } else {
- LOG(WARN, "Ignoring RDM conflict at 0x%"PRIx64".\n",
- d_config->rdms[i].start);
-
- /*
- * Then mask this INVALID to indicate we shouldn't expose this
- * to hvmloader.
- */
- d_config->rdms[i].policy = LIBXL_RDM_RESERVE_POLICY_INVALID;
- }
- }
-
- return 0;
-
- out:
- return ERROR_FAIL;
-}
-
-/* XSA-180 / CVE-2014-3672
- *
- * The QEMU shipped with Xen has a bodge. It checks for
- * XEN_QEMU_CONSOLE_LIMIT to see how much data QEMU is allowed
- * to write to stderr. We set that to 1MB if it is not set by
- * system administrator.
- */
-static void libxl__set_qemu_env_for_xsa_180(libxl__gc *gc,
- flexarray_t *dm_envs)
-{
- if (getenv("XEN_QEMU_CONSOLE_LIMIT")) return;
- flexarray_append_pair(dm_envs, "XEN_QEMU_CONSOLE_LIMIT", "1048576");
-}
-
-const libxl_vnc_info *libxl__dm_vnc(const libxl_domain_config *guest_config)
-{
- const libxl_vnc_info *vnc = NULL;
- if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
- vnc = &guest_config->b_info.u.hvm.vnc;
- } else if (guest_config->num_vfbs > 0) {
- vnc = &guest_config->vfbs[0].vnc;
- }
- return vnc && libxl_defbool_val(vnc->enable) ? vnc : NULL;
-}
-
-static const libxl_sdl_info *dm_sdl(const libxl_domain_config *guest_config)
-{
- const libxl_sdl_info *sdl = NULL;
- if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
- sdl = &guest_config->b_info.u.hvm.sdl;
- } else if (guest_config->num_vfbs > 0) {
- sdl = &guest_config->vfbs[0].sdl;
- }
- return sdl && libxl_defbool_val(sdl->enable) ? sdl : NULL;
-}
-
-static const char *dm_keymap(const libxl_domain_config *guest_config)
-{
- if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
- return guest_config->b_info.u.hvm.keymap;
- } else if (guest_config->num_vfbs > 0) {
- return guest_config->vfbs[0].keymap;
- } else
- return NULL;
-}
-
-static int libxl__build_device_model_args_old(libxl__gc *gc,
- const char *dm, int domid,
- const libxl_domain_config *guest_config,
- char ***args, char ***envs,
- const libxl__domain_build_state *state)
-{
- const libxl_domain_create_info *c_info = &guest_config->c_info;
- const libxl_domain_build_info *b_info = &guest_config->b_info;
- const libxl_device_nic *nics = guest_config->nics;
- const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
- const libxl_sdl_info *sdl = dm_sdl(guest_config);
- const int num_nics = guest_config->num_nics;
- const char *keymap = dm_keymap(guest_config);
- int i;
- flexarray_t *dm_args, *dm_envs;
- dm_args = flexarray_make(gc, 16, 1);
- dm_envs = flexarray_make(gc, 16, 1);
-
- assert(state->dm_monitor_fd == -1);
-
- libxl__set_qemu_env_for_xsa_180(gc, dm_envs);
-
- flexarray_vappend(dm_args, dm,
- "-d", GCSPRINTF("%d", domid), NULL);
-
- if (c_info->name)
- flexarray_vappend(dm_args, "-domain-name", c_info->name, NULL);
-
- if (vnc) {
- char *vncarg = NULL;
-
- flexarray_append(dm_args, "-vnc");
-
- /*
- * If vnc->listen is present and contains a :, and
- * - vnc->display is 0, use vnc->listen
- * - vnc->display is non-zero, be confused
- * If vnc->listen is present but doesn't, use vnc->listen:vnc->display.
- * If vnc->listen is not present, use 127.0.0.1:vnc->display
- * (Remembering that vnc->display already defaults to 0.)
- */
- if (vnc->listen) {
- if (strchr(vnc->listen, ':') != NULL) {
- if (vnc->display) {
- LOGD(ERROR, domid, "vncdisplay set, vnclisten contains display");
- return ERROR_INVAL;
- }
- vncarg = vnc->listen;
- } else {
- vncarg = GCSPRINTF("%s:%d", vnc->listen, vnc->display);
- }
- } else
- vncarg = GCSPRINTF("127.0.0.1:%d", vnc->display);
-
- if (vnc->passwd && vnc->passwd[0]) {
- vncarg = GCSPRINTF("%s,password", vncarg);
- }
-
- flexarray_append(dm_args, vncarg);
-
- if (libxl_defbool_val(vnc->findunused)) {
- flexarray_append(dm_args, "-vncunused");
- }
- } else if (!sdl) {
- /*
- * VNC is not enabled by default by qemu-xen-traditional,
- * however skipping -vnc causes SDL to be
- * (unexpectedly) enabled by default. If undesired, disable graphics at
- * all.
- */
- flexarray_append(dm_args, "-nographic");
- }
-
- if (sdl) {
- flexarray_append(dm_args, "-sdl");
- if (!libxl_defbool_val(sdl->opengl)) {
- flexarray_append(dm_args, "-disable-opengl");
- }
- if (sdl->display)
- flexarray_append_pair(dm_envs, "DISPLAY", sdl->display);
- if (sdl->xauthority)
- flexarray_append_pair(dm_envs, "XAUTHORITY", sdl->xauthority);
- }
- if (keymap) {
- flexarray_vappend(dm_args, "-k", keymap, NULL);
- }
- if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
- int ioemu_nics = 0;
- int nr_set_cpus = 0;
- char *s;
-
- if (b_info->kernel) {
- LOGD(ERROR, domid, "HVM direct kernel boot is not supported by "
- "qemu-xen-traditional");
- return ERROR_INVAL;
- }
-
- if (b_info->u.hvm.serial || b_info->u.hvm.serial_list) {
- if ( b_info->u.hvm.serial && b_info->u.hvm.serial_list )
- {
- LOGD(ERROR, domid, "Both serial and serial_list set");
- return ERROR_INVAL;
- }
- if (b_info->u.hvm.serial) {
- flexarray_vappend(dm_args,
- "-serial", b_info->u.hvm.serial, NULL);
- } else if (b_info->u.hvm.serial_list) {
- char **p;
- for (p = b_info->u.hvm.serial_list;
- *p;
- p++) {
- flexarray_vappend(dm_args,
- "-serial",
- *p, NULL);
- }
- }
- }
-
- if (libxl_defbool_val(b_info->u.hvm.nographic) && (!sdl && !vnc)) {
- flexarray_append(dm_args, "-nographic");
- }
-
- if (b_info->video_memkb) {
- flexarray_vappend(dm_args, "-videoram",
- GCSPRINTF("%d", libxl__sizekb_to_mb(b_info->video_memkb)),
- NULL);
- }
-
- switch (b_info->u.hvm.vga.kind) {
- case LIBXL_VGA_INTERFACE_TYPE_STD:
- flexarray_append(dm_args, "-std-vga");
- break;
- case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
- break;
- case LIBXL_VGA_INTERFACE_TYPE_NONE:
- flexarray_append_pair(dm_args, "-vga", "none");
- break;
- case LIBXL_VGA_INTERFACE_TYPE_QXL:
- break;
- default:
- LOGD(ERROR, domid, "Invalid emulated video card specified");
- return ERROR_INVAL;
- }
-
- if (b_info->u.hvm.boot) {
- flexarray_vappend(dm_args, "-boot", b_info->u.hvm.boot, NULL);
- }
- if (libxl_defbool_val(b_info->u.hvm.usb)
- || b_info->u.hvm.usbdevice
- || libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
- if (b_info->u.hvm.usbdevice
- && libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
- LOGD(ERROR, domid, "Both usbdevice and usbdevice_list set");
- return ERROR_INVAL;
- }
- flexarray_append(dm_args, "-usb");
- if (b_info->u.hvm.usbdevice) {
- flexarray_vappend(dm_args,
- "-usbdevice", b_info->u.hvm.usbdevice, NULL);
- } else if (b_info->u.hvm.usbdevice_list) {
- char **p;
- for (p = b_info->u.hvm.usbdevice_list;
- *p;
- p++) {
- flexarray_vappend(dm_args,
- "-usbdevice",
- *p, NULL);
- }
- }
- }
- if (b_info->u.hvm.soundhw) {
- flexarray_vappend(dm_args, "-soundhw", b_info->u.hvm.soundhw, NULL);
- }
- if (libxl__acpi_defbool_val(b_info)) {
- flexarray_append(dm_args, "-acpi");
- }
- if (b_info->max_vcpus > 1) {
- flexarray_vappend(dm_args, "-vcpus",
- GCSPRINTF("%d", b_info->max_vcpus),
- NULL);
- }
-
- nr_set_cpus = libxl_bitmap_count_set(&b_info->avail_vcpus);
- s = libxl_bitmap_to_hex_string(CTX, &b_info->avail_vcpus);
- flexarray_vappend(dm_args, "-vcpu_avail",
- GCSPRINTF("%s", s), NULL);
- free(s);
-
- for (i = 0; i < num_nics; i++) {
- if (nics[i].nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
- char *smac = GCSPRINTF(
- LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nics[i].mac));
- const char *ifname = libxl__device_nic_devname(gc,
- domid, nics[i].devid,
- LIBXL_NIC_TYPE_VIF_IOEMU);
- flexarray_vappend(dm_args,
- "-net",
- GCSPRINTF(
- "nic,vlan=%d,macaddr=%s,model=%s",
- nics[i].devid, smac, nics[i].model),
- "-net",
- GCSPRINTF(
- "tap,vlan=%d,ifname=%s,bridge=%s,"
- "script=%s,downscript=%s",
- nics[i].devid, ifname, nics[i].bridge,
- libxl_tapif_script(gc),
- libxl_tapif_script(gc)),
- NULL);
- ioemu_nics++;
- }
- }
- /* If we have no emulated nics, tell qemu not to create any */
- if ( ioemu_nics == 0 ) {
- flexarray_vappend(dm_args, "-net", "none", NULL);
- }
- if (libxl_defbool_val(b_info->u.hvm.gfx_passthru)) {
- switch (b_info->u.hvm.gfx_passthru_kind) {
- case LIBXL_GFX_PASSTHRU_KIND_DEFAULT:
- case LIBXL_GFX_PASSTHRU_KIND_IGD:
- flexarray_append(dm_args, "-gfx_passthru");
- break;
- default:
- LOGD(ERROR, domid, "unsupported gfx_passthru_kind.");
- return ERROR_INVAL;
- }
- }
- } else {
- if (!sdl && !vnc)
- flexarray_append(dm_args, "-nographic");
- }
-
- if (libxl_defbool_val(b_info->dm_restrict)) {
- LOGD(ERROR, domid,
- "dm_restrict not supported by qemu-xen-traditional");
- return ERROR_INVAL;
- }
-
- if (state->saved_state) {
- flexarray_vappend(dm_args, "-loadvm", state->saved_state, NULL);
- }
- for (i = 0; b_info->extra && b_info->extra[i] != NULL; i++)
- flexarray_append(dm_args, b_info->extra[i]);
- flexarray_append(dm_args, "-M");
- switch (b_info->type) {
- case LIBXL_DOMAIN_TYPE_PVH:
- case LIBXL_DOMAIN_TYPE_PV:
- flexarray_append(dm_args, "xenpv");
- for (i = 0; b_info->extra_pv && b_info->extra_pv[i] != NULL; i++)
- flexarray_append(dm_args, b_info->extra_pv[i]);
- break;
- case LIBXL_DOMAIN_TYPE_HVM:
- flexarray_append(dm_args, "xenfv");
- for (i = 0; b_info->extra_hvm && b_info->extra_hvm[i] != NULL; i++)
- flexarray_append(dm_args, b_info->extra_hvm[i]);
- break;
- default:
- abort();
- }
- flexarray_append(dm_args, NULL);
- *args = (char **) flexarray_contents(dm_args);
- flexarray_append(dm_envs, NULL);
- if (envs)
- *envs = (char **) flexarray_contents(dm_envs);
- return 0;
-}
-
-static char *dm_spice_options(libxl__gc *gc,
- const libxl_spice_info *spice)
-{
- char *opt;
-
- if (!spice->port && !spice->tls_port) {
- LOG(ERROR,
- "at least one of the spiceport or tls_port must be provided");
- return NULL;
- }
-
- if (!libxl_defbool_val(spice->disable_ticketing)) {
- if (!spice->passwd) {
- LOG(ERROR, "spice ticketing is enabled but missing password");
- return NULL;
- }
- else if (!spice->passwd[0]) {
- LOG(ERROR, "spice password can't be empty");
- return NULL;
- }
- }
- opt = GCSPRINTF("port=%d,tls-port=%d", spice->port, spice->tls_port);
- if (spice->host)
- opt = GCSPRINTF("%s,addr=%s", opt, spice->host);
- if (libxl_defbool_val(spice->disable_ticketing))
- opt = GCSPRINTF("%s,disable-ticketing", opt);
- else
- opt = GCSPRINTF("%s,password=%s", opt, spice->passwd);
- opt = GCSPRINTF("%s,agent-mouse=%s", opt,
- libxl_defbool_val(spice->agent_mouse) ? "on" : "off");
-
- if (!libxl_defbool_val(spice->clipboard_sharing))
- opt = GCSPRINTF("%s,disable-copy-paste", opt);
-
- if (spice->image_compression)
- opt = GCSPRINTF("%s,image-compression=%s", opt,
- spice->image_compression);
-
- if (spice->streaming_video)
- opt = GCSPRINTF("%s,streaming-video=%s", opt, spice->streaming_video);
-
- return opt;
-}
-
-static enum libxl_gfx_passthru_kind
-libxl__detect_gfx_passthru_kind(libxl__gc *gc,
- const libxl_domain_config *guest_config)
-{
- const libxl_domain_build_info *b_info = &guest_config->b_info;
-
- if (b_info->u.hvm.gfx_passthru_kind != LIBXL_GFX_PASSTHRU_KIND_DEFAULT)
- return b_info->u.hvm.gfx_passthru_kind;
-
- if (libxl__is_igd_vga_passthru(gc, guest_config)) {
- return LIBXL_GFX_PASSTHRU_KIND_IGD;
- }
-
- return LIBXL_GFX_PASSTHRU_KIND_DEFAULT;
-}
-
-/* colo mode */
-enum {
- LIBXL__COLO_NONE = 0,
- LIBXL__COLO_PRIMARY,
- LIBXL__COLO_SECONDARY,
-};
-
-static char *qemu_disk_scsi_drive_string(libxl__gc *gc, const char *target_path,
- int unit, const char *format,
- const libxl_device_disk *disk,
- int colo_mode, const char **id_ptr)
-{
- char *drive = NULL;
- char *common = GCSPRINTF("if=none,readonly=%s,cache=writeback",
- disk->readwrite ? "off" : "on");
- const char *exportname = disk->colo_export;
- const char *active_disk = disk->active_disk;
- const char *hidden_disk = disk->hidden_disk;
- const char *id;
-
- switch (colo_mode) {
- case LIBXL__COLO_NONE:
- id = GCSPRINTF("scsi0-hd%d", unit);
- drive = GCSPRINTF("file=%s,id=%s,format=%s,%s",
- target_path, id, format, common);
- break;
- case LIBXL__COLO_PRIMARY:
- id = exportname;
- drive = GCSPRINTF(
- "%s,id=%s,driver=quorum,"
- "children.0.file.filename=%s,"
- "children.0.driver=%s,"
- "read-pattern=fifo,"
- "vote-threshold=1",
- common, id, target_path, format);
- break;
- case LIBXL__COLO_SECONDARY:
- id = "top-colo";
- drive = GCSPRINTF(
- "%s,id=%s,driver=replication,"
- "mode=secondary,"
- "top-id=top-colo,"
- "file.driver=qcow2,"
- "file.file.filename=%s,"
- "file.backing.driver=qcow2,"
- "file.backing.file.filename=%s,"
- "file.backing.backing=%s",
- common, id, active_disk, hidden_disk, exportname);
- break;
- default:
- abort();
- }
-
- *id_ptr = id;
-
- return drive;
-}
-
-static char *qemu_disk_ide_drive_string(libxl__gc *gc, const char *target_path,
- int unit, const char *format,
- const libxl_device_disk *disk,
- int colo_mode)
-{
- char *drive = NULL;
- const char *exportname = disk->colo_export;
- const char *active_disk = disk->active_disk;
- const char *hidden_disk = disk->hidden_disk;
-
- assert(disk->readwrite); /* should have been checked earlier */
-
- switch (colo_mode) {
- case LIBXL__COLO_NONE:
- drive = GCSPRINTF
- ("file=%s,if=ide,index=%d,media=disk,format=%s,cache=writeback",
- target_path, unit, format);
- break;
- case LIBXL__COLO_PRIMARY:
- /*
- * primary:
- * -dirve if=ide,index=x,media=disk,cache=writeback,driver=quorum,\
- * id=exportname,\
- * children.0.file.filename=target_path,\
- * children.0.driver=format,\
- * read-pattern=fifo,\
- * vote-threshold=1
- */
- drive = GCSPRINTF(
- "if=ide,index=%d,media=disk,cache=writeback,driver=quorum,"
- "id=%s,"
- "children.0.file.filename=%s,"
- "children.0.driver=%s,"
- "read-pattern=fifo,"
- "vote-threshold=1",
- unit, exportname, target_path, format);
- break;
- case LIBXL__COLO_SECONDARY:
- /*
- * secondary:
- * -drive if=ide,index=x,media=disk,cache=writeback,driver=replication,\
- * mode=secondary,\
- * file.driver=qcow2,\
- * file.file.filename=active_disk,\
- * file.backing.driver=qcow2,\
- * file.backing.file.filename=hidden_disk,\
- * file.backing.backing=exportname,
- */
- drive = GCSPRINTF(
- "if=ide,index=%d,id=top-colo,media=disk,cache=writeback,"
- "driver=replication,"
- "mode=secondary,"
- "top-id=top-colo,"
- "file.driver=qcow2,"
- "file.file.filename=%s,"
- "file.backing.driver=qcow2,"
- "file.backing.file.filename=%s,"
- "file.backing.backing=%s",
- unit, active_disk, hidden_disk, exportname);
- break;
- default:
- abort();
- }
-
- return drive;
-}
-
-static int libxl__pre_open_qmp_socket(libxl__gc *gc, libxl_domid domid,
- int *fd_r)
-{
- int rc, r;
- int fd;
- struct sockaddr_un un;
- const char *path = libxl__qemu_qmp_path(gc, domid);
-
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd < 0) {
- LOGED(ERROR, domid, "socket() failed");
- return ERROR_FAIL;
- }
-
- rc = libxl__prepare_sockaddr_un(gc, &un, path, "QEMU's QMP socket");
- if (rc)
- goto out;
-
- rc = libxl__remove_file(gc, path);
- if (rc)
- goto out;
-
- r = bind(fd, (struct sockaddr *) &un, sizeof(un));
- if (r < 0) {
- LOGED(ERROR, domid, "bind('%s') failed", path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- r = listen(fd, 1);
- if (r < 0) {
- LOGED(ERROR, domid, "listen() failed");
- rc = ERROR_FAIL;
- goto out;
- }
-
- *fd_r = fd;
- rc = 0;
-
-out:
- if (rc && fd >= 0)
- close(fd);
- return rc;
-}
-
-static int libxl__build_device_model_args_new(libxl__gc *gc,
- const char *dm, int guest_domid,
- const libxl_domain_config *guest_config,
- char ***args, char ***envs,
- const libxl__domain_build_state *state,
- int *dm_state_fd)
-{
- const libxl_domain_create_info *c_info = &guest_config->c_info;
- const libxl_domain_build_info *b_info = &guest_config->b_info;
- const libxl_device_disk *disks = guest_config->disks;
- const libxl_device_nic *nics = guest_config->nics;
- const int num_disks = guest_config->num_disks;
- const int num_nics = guest_config->num_nics;
- const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
- const libxl_sdl_info *sdl = dm_sdl(guest_config);
- const char *keymap = dm_keymap(guest_config);
- char *machinearg;
- flexarray_t *dm_args, *dm_envs;
- int i, connection, devid;
- uint64_t ram_size;
- const char *path, *chardev;
- bool is_stubdom = libxl_defbool_val(b_info->device_model_stubdomain);
-
- dm_args = flexarray_make(gc, 16, 1);
- dm_envs = flexarray_make(gc, 16, 1);
-
- libxl__set_qemu_env_for_xsa_180(gc, dm_envs);
-
- flexarray_vappend(dm_args, dm,
- "-xen-domid",
- GCSPRINTF("%d", guest_domid), NULL);
- flexarray_append(dm_args, "-no-shutdown");
-
- /*
- * QMP access to qemu running in stubdomain is done over vchan. The
- * stubdomain init script adds the appropriate monitor options for
- * vchan-socket-proxy.
- */
- if (!is_stubdom) {
- flexarray_append(dm_args, "-chardev");
- if (state->dm_monitor_fd >= 0) {
- flexarray_append(dm_args,
- GCSPRINTF("socket,id=libxl-cmd,fd=%d,server,nowait",
- state->dm_monitor_fd));
-
- /*
- * Start QEMU with its "CPU" paused, it will not start any emulation
- * until the QMP command "cont" is used. This also prevent QEMU from
- * writing "running" to the "state" xenstore node so we only use this
- * flag when we have the QMP based startup notification.
- * */
- flexarray_append(dm_args, "-S");
- } else {
- flexarray_append(dm_args,
- GCSPRINTF("socket,id=libxl-cmd,"
- "path=%s,server,nowait",
- libxl__qemu_qmp_path(gc, guest_domid)));
- }
-
- flexarray_append(dm_args, "-mon");
- flexarray_append(dm_args, "chardev=libxl-cmd,mode=control");
-
- flexarray_append(dm_args, "-chardev");
- flexarray_append(dm_args,
- GCSPRINTF("socket,id=libxenstat-cmd,"
- "path=%s/qmp-libxenstat-%d,server,nowait",
- libxl__run_dir_path(), guest_domid));
-
- flexarray_append(dm_args, "-mon");
- flexarray_append(dm_args, "chardev=libxenstat-cmd,mode=control");
- }
-
- for (i = 0; i < guest_config->num_channels; i++) {
- connection = guest_config->channels[i].connection;
- devid = guest_config->channels[i].devid;
- switch (connection) {
- case LIBXL_CHANNEL_CONNECTION_PTY:
- chardev = GCSPRINTF("pty,id=libxl-channel%d", devid);
- break;
- case LIBXL_CHANNEL_CONNECTION_SOCKET:
- path = guest_config->channels[i].u.socket.path;
- chardev = GCSPRINTF("socket,id=libxl-channel%d,path=%s,"
- "server,nowait", devid, path);
- break;
- default:
- /* We've forgotten to add the clause */
- LOGD(ERROR, guest_domid, "%s: unknown channel connection %d",
- __func__, connection);
- return ERROR_INVAL;
- }
- flexarray_append(dm_args, "-chardev");
- flexarray_append(dm_args, (void*)chardev);
- }
-
- /*
- * Remove default devices created by qemu. Qemu will create only devices
- * defined by xen, since the devices not defined by xen are not usable.
- */
- flexarray_append(dm_args, "-nodefaults");
-
- /*
- * Do not use any of the user-provided config files in sysconfdir,
- * avoiding unknown and uncontrolled configuration.
- */
- flexarray_append(dm_args, "-no-user-config");
-
- if (b_info->type != LIBXL_DOMAIN_TYPE_HVM) {
- flexarray_append(dm_args, "-xen-attach");
- }
-
- if (c_info->name) {
- flexarray_vappend(dm_args, "-name", c_info->name, NULL);
- }
-
- if (vnc && !is_stubdom) {
- char *vncarg = NULL;
-
- flexarray_append(dm_args, "-vnc");
-
- /*
- * If vnc->listen is present and contains a :, and
- * - vnc->display is 0, use vnc->listen
- * - vnc->display is non-zero, be confused
- * If vnc->listen is present but doesn't, use vnc->listen:vnc->display.
- * If vnc->listen is not present, use 127.0.0.1:vnc->display
- * (Remembering that vnc->display already defaults to 0.)
- */
- if (vnc->listen) {
- if (strchr(vnc->listen, ':') != NULL) {
- if (vnc->display) {
- LOGD(ERROR, guest_domid,
- "vncdisplay set, vnclisten contains display");
- return ERROR_INVAL;
- }
- vncarg = vnc->listen;
- } else {
- vncarg = GCSPRINTF("%s:%d", vnc->listen, vnc->display);
- }
- } else
- vncarg = GCSPRINTF("127.0.0.1:%d", vnc->display);
-
- if (vnc->passwd && vnc->passwd[0]) {
- vncarg = GCSPRINTF("%s,password", vncarg);
- }
-
- if (libxl_defbool_val(vnc->findunused)) {
- /* This option asks to QEMU to try this number of port before to
- * give up. So QEMU will try ports between $display and $display +
- * 99. This option needs to be the last one of the vnc options. */
- vncarg = GCSPRINTF("%s,to=99", vncarg);
- }
-
- flexarray_append(dm_args, vncarg);
- } else if (!is_stubdom) {
- /*
- * Ensure that by default no vnc server is created.
- */
- flexarray_append_pair(dm_args, "-vnc", "none");
- }
-
- /*
- * Ensure that by default no display backend is created. Further
- * options given below might then enable more.
- */
- flexarray_append_pair(dm_args, "-display", "none");
-
- if (sdl && !is_stubdom) {
- flexarray_append(dm_args, "-sdl");
- if (sdl->display)
- flexarray_append_pair(dm_envs, "DISPLAY", sdl->display);
- if (sdl->xauthority)
- flexarray_append_pair(dm_envs, "XAUTHORITY", sdl->xauthority);
- }
-
- if (keymap) {
- flexarray_vappend(dm_args, "-k", keymap, NULL);
- }
-
- if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
- int ioemu_nics = 0;
-
- if (b_info->kernel)
- flexarray_vappend(dm_args, "-kernel", b_info->kernel, NULL);
-
- if (b_info->ramdisk)
- flexarray_vappend(dm_args, "-initrd", b_info->ramdisk, NULL);
-
- if (b_info->cmdline)
- flexarray_vappend(dm_args, "-append", b_info->cmdline, NULL);
-
- /* Find out early if one of the disk is on the scsi bus and add a scsi
- * controller. This is done ahead to keep the same behavior as previous
- * version of QEMU (have the controller on the same PCI slot). */
- for (i = 0; i < num_disks; i++) {
- if (disks[i].is_cdrom) {
- continue;
- }
- if (strncmp(disks[i].vdev, "sd", 2) == 0) {
- flexarray_vappend(dm_args, "-device", "lsi53c895a", NULL);
- break;
- }
- }
-
- if (b_info->u.hvm.serial || b_info->u.hvm.serial_list) {
- if ( b_info->u.hvm.serial && b_info->u.hvm.serial_list )
- {
- LOGD(ERROR, guest_domid, "Both serial and serial_list set");
- return ERROR_INVAL;
- } else {
- if (b_info->u.hvm.serial) {
- if (is_stubdom) {
- /* see spawn_stub_launch_dm() for connecting STUBDOM_CONSOLE_SERIAL */
- flexarray_vappend(dm_args,
- "-serial",
- GCSPRINTF("/dev/hvc%d", STUBDOM_CONSOLE_SERIAL),
- NULL);
- } else {
- flexarray_vappend(dm_args,
- "-serial", b_info->u.hvm.serial, NULL);
- }
- } else if (b_info->u.hvm.serial_list) {
- char **p;
- /* see spawn_stub_launch_dm() for connecting STUBDOM_CONSOLE_SERIAL */
- for (p = b_info->u.hvm.serial_list, i = 0;
- *p;
- p++, i++) {
- if (is_stubdom)
- flexarray_vappend(dm_args,
- "-serial",
- GCSPRINTF("/dev/hvc%d", STUBDOM_CONSOLE_SERIAL + i),
- NULL);
- else
- flexarray_vappend(dm_args,
- "-serial",
- *p, NULL);
- }
- }
- }
- }
-
- if (libxl_defbool_val(b_info->u.hvm.nographic) && (!sdl && !vnc)) {
- flexarray_append(dm_args, "-nographic");
- }
-
- if (libxl_defbool_val(b_info->u.hvm.spice.enable) && !is_stubdom) {
- const libxl_spice_info *spice = &b_info->u.hvm.spice;
- char *spiceoptions = dm_spice_options(gc, spice);
- if (!spiceoptions)
- return ERROR_INVAL;
-
- flexarray_append(dm_args, "-spice");
- flexarray_append(dm_args, spiceoptions);
- if (libxl_defbool_val(b_info->u.hvm.spice.vdagent)) {
- flexarray_vappend(dm_args, "-device", "virtio-serial",
- "-chardev", "spicevmc,id=vdagent,name=vdagent", "-device",
- "virtserialport,chardev=vdagent,name=com.redhat.spice.0",
- NULL);
- }
- }
-
- switch (b_info->u.hvm.vga.kind) {
- case LIBXL_VGA_INTERFACE_TYPE_STD:
- flexarray_append_pair(dm_args, "-device",
- GCSPRINTF("VGA,vgamem_mb=%d",
- libxl__sizekb_to_mb(b_info->video_memkb)));
- break;
- case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
- flexarray_append_pair(dm_args, "-device",
- GCSPRINTF("cirrus-vga,vgamem_mb=%d",
- libxl__sizekb_to_mb(b_info->video_memkb)));
- break;
- case LIBXL_VGA_INTERFACE_TYPE_NONE:
- break;
- case LIBXL_VGA_INTERFACE_TYPE_QXL:
- /* QXL have 2 ram regions, ram and vram */
- flexarray_append_pair(dm_args, "-device",
- GCSPRINTF("qxl-vga,vram_size_mb=%"PRIu64",ram_size_mb=%"PRIu64,
- (b_info->video_memkb/2/1024), (b_info->video_memkb/2/1024) ) );
- break;
- default:
- LOGD(ERROR, guest_domid, "Invalid emulated video card specified");
- return ERROR_INVAL;
- }
-
- if (b_info->u.hvm.boot) {
- flexarray_vappend(dm_args, "-boot",
- GCSPRINTF("order=%s", b_info->u.hvm.boot), NULL);
- }
- if (libxl_defbool_val(b_info->u.hvm.usb)
- || b_info->u.hvm.usbdevice
- || libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
- if (b_info->u.hvm.usbdevice
- && libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
- LOGD(ERROR, guest_domid, "Both usbdevice and usbdevice_list set");
- return ERROR_INVAL;
- }
- flexarray_append(dm_args, "-usb");
- if (b_info->u.hvm.usbdevice) {
- flexarray_vappend(dm_args,
- "-usbdevice", b_info->u.hvm.usbdevice, NULL);
- } else if (b_info->u.hvm.usbdevice_list) {
- char **p;
- for (p = b_info->u.hvm.usbdevice_list;
- *p;
- p++) {
- flexarray_vappend(dm_args,
- "-usbdevice",
- *p, NULL);
- }
- }
- } else if (b_info->u.hvm.usbversion) {
- switch (b_info->u.hvm.usbversion) {
- case 1:
- flexarray_vappend(dm_args,
- "-device", "piix3-usb-uhci,id=usb", NULL);
- break;
- case 2:
- flexarray_append_pair(dm_args, "-device",
- "ich9-usb-ehci1,id=usb,addr=0x1d.0x7,multifunction=on");
- for (i = 1; i < 4; i++)
- flexarray_append_pair(dm_args, "-device",
- GCSPRINTF("ich9-usb-uhci%d,masterbus=usb.0,"
- "firstport=%d,addr=0x1d.%#x,multifunction=on",
- i, 2*(i-1), i-1));
- break;
- case 3:
- flexarray_vappend(dm_args,
- "-device", "nec-usb-xhci,id=usb", NULL);
- break;
- default:
- LOGD(ERROR, guest_domid, "usbversion parameter is invalid, "
- "must be between 1 and 3");
- return ERROR_INVAL;
- }
- if (b_info->u.hvm.spice.usbredirection >= 0 &&
- b_info->u.hvm.spice.usbredirection < 5) {
- for (i = 1; i <= b_info->u.hvm.spice.usbredirection; i++)
- flexarray_vappend(dm_args, "-chardev",
- GCSPRINTF("spicevmc,name=usbredir,id=usbrc%d", i),
- "-device",
- GCSPRINTF("usb-redir,chardev=usbrc%d,"
- "id=usbrc%d", i, i), NULL);
- } else {
- LOGD(ERROR, guest_domid, "usbredirection parameter is invalid, "
- "it must be between 1 and 4");
- return ERROR_INVAL;
- }
- }
- if (b_info->u.hvm.soundhw) {
- flexarray_vappend(dm_args, "-soundhw", b_info->u.hvm.soundhw, NULL);
- }
- if (!libxl__acpi_defbool_val(b_info)) {
- flexarray_append(dm_args, "-no-acpi");
- }
- if (b_info->max_vcpus > 1) {
- flexarray_append(dm_args, "-smp");
- if (b_info->avail_vcpus.size) {
- int nr_set_cpus = 0;
- nr_set_cpus = libxl_bitmap_count_set(&b_info->avail_vcpus);
-
- flexarray_append(dm_args, GCSPRINTF("%d,maxcpus=%d",
- nr_set_cpus,
- b_info->max_vcpus));
- } else
- flexarray_append(dm_args, GCSPRINTF("%d", b_info->max_vcpus));
- }
- for (i = 0; i < num_nics; i++) {
- if (nics[i].nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
- char *smac = GCSPRINTF(LIBXL_MAC_FMT,
- LIBXL_MAC_BYTES(nics[i].mac));
- const char *ifname = libxl__device_nic_devname(gc,
- guest_domid, nics[i].devid,
- LIBXL_NIC_TYPE_VIF_IOEMU);
- flexarray_append(dm_args, "-device");
- flexarray_append(dm_args,
- GCSPRINTF("%s,id=nic%d,netdev=net%d,mac=%s",
- nics[i].model, nics[i].devid,
- nics[i].devid, smac));
- flexarray_append(dm_args, "-netdev");
- flexarray_append(dm_args,
- GCSPRINTF("type=tap,id=net%d,ifname=%s,"
- "script=%s,downscript=%s",
- nics[i].devid, ifname,
- libxl_tapif_script(gc),
- libxl_tapif_script(gc)));
-
- /* Userspace COLO Proxy need this */
-#define APPEND_COLO_SOCK_SERVER(sock_id, sock_ip, sock_port) ({ \
- if (nics[i].colo_##sock_id && \
- nics[i].colo_##sock_ip && \
- nics[i].colo_##sock_port) { \
- flexarray_append(dm_args, "-chardev"); \
- flexarray_append(dm_args, \
- GCSPRINTF("socket,id=%s,host=%s,port=%s,server,nowait", \
- nics[i].colo_##sock_id, \
- nics[i].colo_##sock_ip, \
- nics[i].colo_##sock_port)); \
- } \
-})
-
-#define APPEND_COLO_SOCK_CLIENT(sock_id, sock_ip, sock_port) ({ \
- if (nics[i].colo_##sock_id && \
- nics[i].colo_##sock_ip && \
- nics[i].colo_##sock_port) { \
- flexarray_append(dm_args, "-chardev"); \
- flexarray_append(dm_args, \
- GCSPRINTF("socket,id=%s,host=%s,port=%s", \
- nics[i].colo_##sock_id, \
- nics[i].colo_##sock_ip, \
- nics[i].colo_##sock_port)); \
- } \
-})
-
- if (state->saved_state) {
- /* secondary colo run */
-
- APPEND_COLO_SOCK_CLIENT(sock_sec_redirector0_id,
- sock_sec_redirector0_ip,
- sock_sec_redirector0_port);
-
- APPEND_COLO_SOCK_CLIENT(sock_sec_redirector1_id,
- sock_sec_redirector1_ip,
- sock_sec_redirector1_port);
-
- if (nics[i].colo_filter_sec_redirector0_queue &&
- nics[i].colo_filter_sec_redirector0_indev) {
- flexarray_append(dm_args, "-object");
- flexarray_append(dm_args,
- GCSPRINTF("filter-redirector,id=rs1,netdev=net%d,queue=%s,indev=%s",
- nics[i].devid,
- nics[i].colo_filter_sec_redirector0_queue,
- nics[i].colo_filter_sec_redirector0_indev));
- }
- if (nics[i].colo_filter_sec_redirector1_queue &&
- nics[i].colo_filter_sec_redirector1_outdev) {
- flexarray_append(dm_args, "-object");
- flexarray_append(dm_args,
- GCSPRINTF("filter-redirector,id=rs2,netdev=net%d,queue=%s,outdev=%s",
- nics[i].devid,
- nics[i].colo_filter_sec_redirector1_queue,
- nics[i].colo_filter_sec_redirector1_outdev));
- }
- if (nics[i].colo_filter_sec_rewriter0_queue) {
- flexarray_append(dm_args, "-object");
- flexarray_append(dm_args,
- GCSPRINTF("filter-rewriter,id=rs3,netdev=net%d,queue=%s",
- nics[i].devid,
- nics[i].colo_filter_sec_rewriter0_queue));
- }
- } else {
- /* primary colo run */
-
- APPEND_COLO_SOCK_SERVER(sock_mirror_id,
- sock_mirror_ip,
- sock_mirror_port);
-
- APPEND_COLO_SOCK_SERVER(sock_compare_pri_in_id,
- sock_compare_pri_in_ip,
- sock_compare_pri_in_port);
-
- APPEND_COLO_SOCK_SERVER(sock_compare_sec_in_id,
- sock_compare_sec_in_ip,
- sock_compare_sec_in_port);
-
- APPEND_COLO_SOCK_SERVER(sock_compare_notify_id,
- sock_compare_notify_ip,
- sock_compare_notify_port);
-
- APPEND_COLO_SOCK_SERVER(sock_redirector0_id,
- sock_redirector0_ip,
- sock_redirector0_port);
-
- APPEND_COLO_SOCK_CLIENT(sock_redirector1_id,
- sock_redirector1_ip,
- sock_redirector1_port);
-
- APPEND_COLO_SOCK_CLIENT(sock_redirector2_id,
- sock_redirector2_ip,
- sock_redirector2_port);
-
- if (nics[i].colo_filter_mirror_queue &&
- nics[i].colo_filter_mirror_outdev) {
- flexarray_append(dm_args, "-object");
- flexarray_append(dm_args,
- GCSPRINTF("filter-mirror,id=m1,netdev=net%d,queue=%s,outdev=%s",
- nics[i].devid,
- nics[i].colo_filter_mirror_queue,
- nics[i].colo_filter_mirror_outdev));
- }
- if (nics[i].colo_filter_redirector0_queue &&
- nics[i].colo_filter_redirector0_indev) {
- flexarray_append(dm_args, "-object");
- flexarray_append(dm_args,
- GCSPRINTF("filter-redirector,id=r1,netdev=net%d,queue=%s,indev=%s",
- nics[i].devid,
- nics[i].colo_filter_redirector0_queue,
- nics[i].colo_filter_redirector0_indev));
- }
- if (nics[i].colo_filter_redirector1_queue &&
- nics[i].colo_filter_redirector1_outdev) {
- flexarray_append(dm_args, "-object");
- flexarray_append(dm_args,
- GCSPRINTF("filter-redirector,id=r2,netdev=net%d,queue=%s,outdev=%s",
- nics[i].devid,
- nics[i].colo_filter_redirector1_queue,
- nics[i].colo_filter_redirector1_outdev));
- }
- if (nics[i].colo_compare_pri_in &&
- nics[i].colo_compare_sec_in &&
- nics[i].colo_compare_out &&
- nics[i].colo_compare_notify_dev) {
- flexarray_append(dm_args, "-object");
- flexarray_append(dm_args, "iothread,id=colo-compare-iothread-1");
- flexarray_append(dm_args, "-object");
- flexarray_append(dm_args,
- GCSPRINTF("colo-compare,id=c1,primary_in=%s,secondary_in=%s,outdev=%s,notify_dev=%s,iothread=colo-compare-iothread-1",
- nics[i].colo_compare_pri_in,
- nics[i].colo_compare_sec_in,
- nics[i].colo_compare_out,
- nics[i].colo_compare_notify_dev));
- }
- }
- ioemu_nics++;
-
-#undef APPEND_COLO_SOCK_SERVER
-#undef APPEND_COLO_SOCK_CLIENT
- }
- }
- /* If we have no emulated nics, tell qemu not to create any */
- if ( ioemu_nics == 0 ) {
- flexarray_append(dm_args, "-net");
- flexarray_append(dm_args, "none");
- }
- } else {
- if (!sdl && !vnc) {
- flexarray_append(dm_args, "-nographic");
- }
- }
-
- if (libxl_defbool_val(b_info->dm_restrict)) {
- char *chroot_dir = GCSPRINTF("%s/qemu-root-%d",
- libxl__run_dir_path(), guest_domid);
- int r;
-
- flexarray_append(dm_args, "-xen-domid-restrict");
-
- /*
- * Run QEMU in a chroot at XEN_RUN_DIR/qemu-root-<domid>
- *
- * There is no library function to do the equivalent of `rm
- * -rf`. However deprivileged QEMU in theory shouldn't be
- * able to write any files, as the chroot would be owned by
- * root, but it would be running as an unprivileged process.
- * So in theory, old chroots should always be empty.
- *
- * rmdir the directory before attempting to create
- * it; if it returns anything other than ENOENT, fail domain
- * creation.
- */
- r = rmdir(chroot_dir);
- if (r != 0 && errno != ENOENT) {
- LOGED(ERROR, guest_domid,
- "failed to remove existing chroot dir %s", chroot_dir);
- return ERROR_FAIL;
- }
-
- for (;;) {
- r = mkdir(chroot_dir, 0000);
- if (!r)
- break;
- if (errno == EINTR) continue;
- LOGED(ERROR, guest_domid,
- "failed to create chroot dir %s", chroot_dir);
- return ERROR_FAIL;
- }
-
- /* Add "-chroot [dir]" to command-line */
- flexarray_append(dm_args, "-chroot");
- flexarray_append(dm_args, chroot_dir);
- }
-
- if (state->saved_state) {
- if (is_stubdom) {
- /* Linux stubdomain must replace $STUBDOM_RESTORE_INCOMING_ARG
- * with the approriate fd:$num argument for the
- * STUBDOM_CONSOLE_RESTORE console 2.
- */
- flexarray_append(dm_args, "-incoming");
- flexarray_append(dm_args, "$STUBDOM_RESTORE_INCOMING_ARG");
- } else {
- /* This file descriptor is meant to be used by QEMU */
- *dm_state_fd = open(state->saved_state, O_RDONLY);
- flexarray_append(dm_args, "-incoming");
- flexarray_append(dm_args, GCSPRINTF("fd:%d",*dm_state_fd));
- }
- }
- for (i = 0; b_info->extra && b_info->extra[i] != NULL; i++)
- flexarray_append(dm_args, b_info->extra[i]);
-
- flexarray_append(dm_args, "-machine");
- switch (b_info->type) {
- case LIBXL_DOMAIN_TYPE_PVH:
- case LIBXL_DOMAIN_TYPE_PV:
- flexarray_append(dm_args, "xenpv");
- for (i = 0; b_info->extra_pv && b_info->extra_pv[i] != NULL; i++)
- flexarray_append(dm_args, b_info->extra_pv[i]);
- break;
- case LIBXL_DOMAIN_TYPE_HVM:
- if (!libxl_defbool_val(b_info->u.hvm.xen_platform_pci)) {
- /* Switching here to the machine "pc" which does not add
- * the xen-platform device instead of the default "xenfv" machine.
- */
- machinearg = libxl__strdup(gc, "pc,accel=xen");
- } else {
- machinearg = libxl__strdup(gc, "xenfv");
- }
- if (b_info->u.hvm.mmio_hole_memkb) {
- uint64_t max_ram_below_4g = (1ULL << 32) -
- (b_info->u.hvm.mmio_hole_memkb << 10);
-
- if (max_ram_below_4g > HVM_BELOW_4G_MMIO_START) {
- LOGD(WARN, guest_domid, "mmio_hole_memkb=%"PRIu64
- " invalid ignored.\n",
- b_info->u.hvm.mmio_hole_memkb);
- } else {
- machinearg = GCSPRINTF("%s,max-ram-below-4g=%"PRIu64,
- machinearg, max_ram_below_4g);
- }
- }
-
- if (libxl_defbool_val(b_info->u.hvm.gfx_passthru)) {
- enum libxl_gfx_passthru_kind gfx_passthru_kind =
- libxl__detect_gfx_passthru_kind(gc, guest_config);
- switch (gfx_passthru_kind) {
- case LIBXL_GFX_PASSTHRU_KIND_IGD:
- machinearg = GCSPRINTF("%s,igd-passthru=on", machinearg);
- break;
- case LIBXL_GFX_PASSTHRU_KIND_DEFAULT:
- LOGD(ERROR, guest_domid, "unable to detect required gfx_passthru_kind");
- return ERROR_FAIL;
- default:
- LOGD(ERROR, guest_domid, "invalid value for gfx_passthru_kind");
- return ERROR_INVAL;
- }
- }
-
- flexarray_append(dm_args, machinearg);
- for (i = 0; b_info->extra_hvm && b_info->extra_hvm[i] != NULL; i++)
- flexarray_append(dm_args, b_info->extra_hvm[i]);
- break;
- default:
- abort();
- }
-
- ram_size = libxl__sizekb_to_mb(b_info->max_memkb - b_info->video_memkb);
- flexarray_append(dm_args, "-m");
- flexarray_append(dm_args, GCSPRINTF("%"PRId64, ram_size));
-
- if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
- if (b_info->u.hvm.hdtype == LIBXL_HDTYPE_AHCI)
- flexarray_append_pair(dm_args, "-device", "ahci,id=ahci0");
- for (i = 0; i < num_disks; i++) {
- int disk, part;
- int dev_number =
- libxl__device_disk_dev_number(disks[i].vdev, &disk, &part);
- const char *format;
- char *drive;
- const char *target_path = NULL;
- int colo_mode;
-
- if (dev_number == -1) {
- LOGD(WARN, guest_domid, "unable to determine"" disk number for %s",
- disks[i].vdev);
- continue;
- }
-
- /*
- * If qemu isn't doing the interpreting, the parameter is
- * always raw
- */
- if (libxl_defbool_val(b_info->device_model_stubdomain))
- format = "host_device";
- else if (disks[i].backend == LIBXL_DISK_BACKEND_QDISK)
- format = libxl__qemu_disk_format_string(disks[i].format);
- else
- format = libxl__qemu_disk_format_string(LIBXL_DISK_FORMAT_RAW);
-
- if (disks[i].format == LIBXL_DISK_FORMAT_EMPTY) {
- if (!disks[i].is_cdrom) {
- LOGD(WARN, guest_domid, "Cannot support empty disk format for %s",
- disks[i].vdev);
- continue;
- }
- } else if (libxl_defbool_val(b_info->device_model_stubdomain)) {
- if (disk > 'z' - 'a') {
- LOGD(WARN, guest_domid,
- "Emulation of only first %d disks is supported with qemu-xen in stubdomain.\n"
- "Disk %d will be available via PV drivers but not as an emulated disk.",
- 'z' - 'a',
- disk);
- continue;
- }
- target_path = GCSPRINTF("/dev/xvd%c", 'a' + disk);
- } else {
- if (format == NULL) {
- LOGD(WARN, guest_domid,
- "Unable to determine disk image format: %s\n"
- "Disk will be available via PV drivers but not as an"
- "emulated disk.",
- disks[i].vdev);
- continue;
- }
-
- assert(disks[i].backend != LIBXL_DISK_BACKEND_TAP);
- target_path = libxl__device_disk_find_local_path(gc,
- guest_domid, &disks[i], true);
-
- if (!target_path) {
- LOGD(WARN, guest_domid, "No way to get local access disk to image: %s\n"
- "Disk will be available via PV drivers but not as an"
- "emulated disk.",
- disks[i].vdev);
- continue;
- }
- }
-
- if (disks[i].is_cdrom) {
- if (disk > 4) {
- LOGD(WARN, guest_domid, "Emulated CDROM can be only one of the first 4 disks.\n"
- "Disk %s will be available via PV drivers but not as an "
- "emulated disk.",
- disks[i].vdev);
- continue;
- }
- drive = libxl__sprintf(gc,
- "if=ide,index=%d,readonly=on,media=cdrom,id=ide-%i",
- disk, dev_number);
-
- if (target_path)
- drive = libxl__sprintf(gc, "%s,file=%s,format=%s",
- drive, target_path, format);
- } else {
- /*
- * Explicit sd disks are passed through as is.
- *
- * For other disks we translate devices 0..3 into
- * hd[a-d] and ignore the rest.
- */
-
- if (libxl_defbool_val(disks[i].colo_enable)) {
- if (libxl_defbool_val(disks[i].colo_restore_enable))
- colo_mode = LIBXL__COLO_SECONDARY;
- else
- colo_mode = LIBXL__COLO_PRIMARY;
- } else {
- colo_mode = LIBXL__COLO_NONE;
- }
-
- if (strncmp(disks[i].vdev, "sd", 2) == 0) {
- const char *drive_id;
- if (colo_mode == LIBXL__COLO_SECONDARY) {
- drive = libxl__sprintf
- (gc, "if=none,driver=%s,file=%s,id=%s,readonly=%s",
- format, target_path, disks[i].colo_export,
- disks[i].readwrite ? "off" : "on");
-
- flexarray_append(dm_args, "-drive");
- flexarray_append(dm_args, drive);
- }
- drive = qemu_disk_scsi_drive_string(gc, target_path, disk,
- format,
- &disks[i],
- colo_mode,
- &drive_id),
- flexarray_vappend(dm_args,
- "-drive", drive,
- "-device", GCSPRINTF("scsi-disk,drive=%s,scsi-id=%d",
- drive_id, disk),
- NULL);
- continue;
- } else if (disk < 6 && b_info->u.hvm.hdtype == LIBXL_HDTYPE_AHCI) {
- if (!disks[i].readwrite) {
- LOGD(ERROR, guest_domid,
- "qemu-xen doesn't support read-only AHCI disk drivers");
- return ERROR_INVAL;
- }
- flexarray_vappend(dm_args, "-drive",
- GCSPRINTF("file=%s,if=none,id=ahcidisk-%d,format=%s,cache=writeback",
- target_path, disk, format),
- "-device", GCSPRINTF("ide-hd,bus=ahci0.%d,unit=0,drive=ahcidisk-%d",
- disk, disk), NULL);
- continue;
- } else if (disk < 4) {
- if (!disks[i].readwrite) {
- LOGD(ERROR, guest_domid,
- "qemu-xen doesn't support read-only IDE disk drivers");
- return ERROR_INVAL;
- }
- if (colo_mode == LIBXL__COLO_SECONDARY) {
- drive = libxl__sprintf
- (gc, "if=none,driver=%s,file=%s,id=%s",
- format, target_path, disks[i].colo_export);
-
- flexarray_append(dm_args, "-drive");
- flexarray_append(dm_args, drive);
- }
- drive = qemu_disk_ide_drive_string(gc, target_path, disk,
- format,
- &disks[i],
- colo_mode);
- } else {
- LOGD(WARN, guest_domid, "Only 4 emulated IDE disks are supported.\n"
- "Disk %s will be available via PV drivers but not as an "
- "emulated disk.",
- disks[i].vdev);
- continue; /* Do not emulate this disk */
- }
-
- if (!drive)
- continue;
- }
-
- flexarray_append(dm_args, "-drive");
- flexarray_append(dm_args, drive);
- }
-
- switch (b_info->u.hvm.vendor_device) {
- case LIBXL_VENDOR_DEVICE_XENSERVER:
- flexarray_append(dm_args, "-device");
- flexarray_append(dm_args, "xen-pvdevice,device-id=0xc000");
- break;
- default:
- break;
- }
-
- if (state->dm_runas) {
- flexarray_append(dm_args, "-runas");
- flexarray_append(dm_args, state->dm_runas);
- }
- }
- flexarray_append(dm_args, NULL);
- *args = (char **) flexarray_contents(dm_args);
- flexarray_append(dm_envs, NULL);
- if (envs)
- *envs = (char **) flexarray_contents(dm_envs);
- return 0;
-}
-
-static int libxl__build_device_model_args(libxl__gc *gc,
- const char *dm, int guest_domid,
- const libxl_domain_config *guest_config,
- char ***args, char ***envs,
- const libxl__domain_build_state *state,
- int *dm_state_fd)
-/* dm_state_fd may be NULL iff caller knows we are using stubdom
- * and therefore will be passing a filename rather than a fd. */
-{
- switch (guest_config->b_info.device_model_version) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- return libxl__build_device_model_args_old(gc, dm,
- guest_domid, guest_config,
- args, envs,
- state);
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- if (!libxl_defbool_val(guest_config->b_info.device_model_stubdomain)) {
- assert(dm_state_fd != NULL);
- assert(*dm_state_fd < 0);
- }
- return libxl__build_device_model_args_new(gc, dm,
- guest_domid, guest_config,
- args, envs,
- state, dm_state_fd);
- default:
- LOGED(ERROR, guest_domid, "unknown device model version %d",
- guest_config->b_info.device_model_version);
- return ERROR_INVAL;
- }
-}
-
-static void libxl__dm_vifs_from_hvm_guest_config(libxl__gc *gc,
- libxl_domain_config * const guest_config,
- libxl_domain_config *dm_config)
-{
- int i, nr = guest_config->num_nics;
-
- GCNEW_ARRAY(dm_config->nics, nr);
-
- for (i=0; i<nr; i++) {
- dm_config->nics[i] = guest_config->nics[i];
- dm_config->nics[i].nictype = LIBXL_NIC_TYPE_VIF;
- if (dm_config->nics[i].ifname)
- dm_config->nics[i].ifname = GCSPRINTF("%s" TAP_DEVICE_SUFFIX,
- dm_config->nics[i].ifname);
- }
-
- dm_config->num_nics = nr;
-}
-
-static int libxl__vfb_and_vkb_from_hvm_guest_config(libxl__gc *gc,
- const libxl_domain_config *guest_config,
- libxl_device_vfb *vfb,
- libxl_device_vkb *vkb)
-{
- const libxl_domain_build_info *b_info = &guest_config->b_info;
-
- if (b_info->type != LIBXL_DOMAIN_TYPE_HVM)
- return ERROR_INVAL;
-
- libxl_device_vfb_init(vfb);
- libxl_device_vkb_init(vkb);
-
- vfb->backend_domid = 0;
- vfb->devid = 0;
- vfb->vnc = b_info->u.hvm.vnc;
- vfb->keymap = b_info->u.hvm.keymap;
- vfb->sdl = b_info->u.hvm.sdl;
-
- vkb->backend_domid = 0;
- vkb->devid = 0;
-
- return 0;
-}
-
-static int libxl__write_stub_dmargs(libxl__gc *gc,
- int dm_domid, int guest_domid,
- char **args, bool is_linux_stubdom)
-{
- struct xs_permissions roperm[2];
- xs_transaction_t t = XBT_NULL;
- char *dmargs;
- int rc;
-
- roperm[0].id = 0;
- roperm[0].perms = XS_PERM_NONE;
- roperm[1].id = dm_domid;
- roperm[1].perms = XS_PERM_READ;
-
- if (!is_linux_stubdom) {
- int dmargs_size = 0;
- int i = 0;
-
- while (args[i] != NULL) {
- dmargs_size = dmargs_size + strlen(args[i]) + 1;
- i++;
- }
-
- dmargs_size++;
- dmargs = (char *) libxl__malloc(gc, dmargs_size);
-
- i = 1;
- dmargs[0] = '\0';
- while (args[i] != NULL) {
- if (strcmp(args[i], "-sdl") && strcmp(args[i], "-M") && strcmp(args[i], "xenfv")) {
- strcat(dmargs, " ");
- strcat(dmargs, args[i]);
- }
- i++;
- }
- }
-
- for (;;) {
- const char *vm_path;
- char *path;
-
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
-
- rc = libxl__xs_read_mandatory(gc, t,
- GCSPRINTF("/local/domain/%d/vm",
- guest_domid),
- &vm_path);
- if (rc) goto out;
-
- if (is_linux_stubdom) {
- int i;
-
- path = GCSPRINTF("%s/image/dm-argv", vm_path);
-
- rc = libxl__xs_mknod(gc, t, path, roperm, ARRAY_SIZE(roperm));
- if (rc) goto out;
-
- for (i=1; args[i] != NULL; i++) {
- rc = libxl__xs_write_checked(gc, t,
- GCSPRINTF("%s/%03d", path, i),
- args[i]);
- if (rc) goto out;
- }
- } else {
- path = GCSPRINTF("%s/image/dmargs", vm_path);
-
- rc = libxl__xs_mknod(gc, t, path, roperm, ARRAY_SIZE(roperm));
- if (rc) goto out;
-
- rc = libxl__xs_write_checked(gc, t, path, dmargs);
- if (rc) goto out;
-
- rc = libxl__xs_mknod(gc, t, GCSPRINTF("%s/rtc/timeoffset", vm_path),
- roperm, ARRAY_SIZE(roperm));
- if (rc) goto out;
- }
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc<0) goto out;
- }
-
- return 0;
-
- out:
- libxl__xs_transaction_abort(gc, &t);
-
- return rc;
-}
-
-static int libxl__store_libxl_entry(libxl__gc *gc, uint32_t domid,
- const char *name, const char *value)
-{
- char *path = NULL;
-
- path = libxl__xs_libxl_path(gc, domid);
- path = libxl__sprintf(gc, "%s/%s", path, name);
- return libxl__xs_printf(gc, XBT_NULL, path, "%s", value);
-}
-
-static void dmss_init(libxl__dm_spawn_state *dmss)
-{
- libxl__ev_qmp_init(&dmss->qmp);
- libxl__ev_time_init(&dmss->timeout);
-}
-
-static void dmss_dispose(libxl__gc *gc, libxl__dm_spawn_state *dmss)
-{
- libxl__ev_qmp_dispose(gc, &dmss->qmp);
- libxl__ev_time_deregister(gc, &dmss->timeout);
-}
-
-static void spawn_stubdom_pvqemu_cb(libxl__egc *egc,
- libxl__dm_spawn_state *stubdom_dmss,
- int rc);
-
-static void spawn_stub_launch_dm(libxl__egc *egc,
- libxl__multidev *aodevs, int ret);
-
-static void stubdom_pvqemu_cb(libxl__egc *egc,
- libxl__multidev *aodevs,
- int rc);
-static void stubdom_pvqemu_unpaused(libxl__egc *egc,
- libxl__dm_resume_state *dmrs,
- int rc);
-
-static void stubdom_xswait_cb(libxl__egc *egc, libxl__xswait_state *xswait,
- int rc, const char *p);
-
-static void spawn_qmp_proxy(libxl__egc *egc,
- libxl__stub_dm_spawn_state *sdss);
-
-static void qmp_proxy_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
- const char *xsdata);
-
-static void qmp_proxy_startup_failed(libxl__egc *egc,
- libxl__spawn_state *spawn,
- int rc);
-
-static void qmp_proxy_detached(libxl__egc *egc,
- libxl__spawn_state *spawn);
-
-static void qmp_proxy_spawn_outcome(libxl__egc *egc,
- libxl__stub_dm_spawn_state *sdss,
- int rc);
-
-char *libxl__stub_dm_name(libxl__gc *gc, const char *guest_name)
-{
- return GCSPRINTF("%s-dm", guest_name);
-}
-
-void libxl__spawn_stub_dm(libxl__egc *egc, libxl__stub_dm_spawn_state *sdss)
-{
- STATE_AO_GC(sdss->dm.spawn.ao);
- libxl_ctx *ctx = libxl__gc_owner(gc);
- int ret;
- libxl_device_vfb *vfb;
- libxl_device_vkb *vkb;
- char **args;
- struct xs_permissions perm[2];
- xs_transaction_t t;
-
- /* convenience aliases */
- libxl_domain_config *const dm_config = &sdss->dm_config;
- libxl_domain_config *const guest_config = sdss->dm.guest_config;
- const int guest_domid = sdss->dm.guest_domid;
- libxl__domain_build_state *const d_state = sdss->dm.build_state;
- libxl__domain_build_state *const stubdom_state = &sdss->dm_state;
-
- /* Initialise private part of sdss */
- libxl__domain_build_state_init(stubdom_state);
- dmss_init(&sdss->dm);
- dmss_init(&sdss->pvqemu);
- libxl__xswait_init(&sdss->xswait);
-
- assert(libxl_defbool_val(guest_config->b_info.device_model_stubdomain));
-
- sdss->pvqemu.guest_domid = INVALID_DOMID;
-
- libxl_domain_create_info_init(&dm_config->c_info);
- dm_config->c_info.type = LIBXL_DOMAIN_TYPE_PV;
- dm_config->c_info.name = libxl__stub_dm_name(gc,
- libxl__domid_to_name(gc, guest_domid));
- /* When we are here to launch stubdom, ssidref is a valid value
- * already, no need to parse it again.
- */
- dm_config->c_info.ssidref = guest_config->b_info.device_model_ssidref;
- dm_config->c_info.ssid_label = NULL;
-
- libxl_uuid_generate(&dm_config->c_info.uuid);
-
- libxl_domain_build_info_init(&dm_config->b_info);
- libxl_domain_build_info_init_type(&dm_config->b_info, LIBXL_DOMAIN_TYPE_PV);
-
- dm_config->b_info.shadow_memkb = 0;
- dm_config->b_info.max_vcpus = 1;
- dm_config->b_info.max_memkb = guest_config->b_info.stubdomain_memkb;
- dm_config->b_info.max_memkb += guest_config->b_info.video_memkb;
- dm_config->b_info.target_memkb = dm_config->b_info.max_memkb;
-
- dm_config->b_info.max_grant_frames = guest_config->b_info.max_grant_frames;
- dm_config->b_info.max_maptrack_frames = 0;
-
- dm_config->b_info.u.pv.features = "";
-
- dm_config->b_info.device_model_version =
- guest_config->b_info.device_model_version;
- dm_config->b_info.device_model =
- guest_config->b_info.device_model;
- dm_config->b_info.extra = guest_config->b_info.extra;
- dm_config->b_info.extra_pv = guest_config->b_info.extra_pv;
- dm_config->b_info.extra_hvm = guest_config->b_info.extra_hvm;
-
- dm_config->disks = guest_config->disks;
- dm_config->num_disks = guest_config->num_disks;
-
- libxl__dm_vifs_from_hvm_guest_config(gc, guest_config, dm_config);
-
- dm_config->c_info.run_hotplug_scripts =
- guest_config->c_info.run_hotplug_scripts;
-
- ret = libxl__domain_config_setdefault(gc, dm_config, guest_domid);
- if (ret) goto out;
-
- if (libxl_defbool_val(guest_config->b_info.u.hvm.vnc.enable)
- || libxl_defbool_val(guest_config->b_info.u.hvm.spice.enable)
- || libxl_defbool_val(guest_config->b_info.u.hvm.sdl.enable)) {
- GCNEW(vfb);
- GCNEW(vkb);
- libxl__vfb_and_vkb_from_hvm_guest_config(gc, guest_config, vfb, vkb);
- dm_config->vfbs = vfb;
- dm_config->num_vfbs = 1;
- dm_config->vkbs = vkb;
- dm_config->num_vkbs = 1;
- }
-
- if (guest_config->b_info.stubdomain_kernel &&
- access(guest_config->b_info.stubdomain_kernel, R_OK) != 0) {
- LOGED(ERROR, guest_domid, "could not access stubdomain kernel %s",
- guest_config->b_info.stubdomain_kernel);
- ret = ERROR_INVAL;
- goto out;
- }
-
- if (guest_config->b_info.stubdomain_ramdisk &&
- access(guest_config->b_info.stubdomain_ramdisk, R_OK) != 0) {
- LOGED(ERROR, guest_domid, "could not access stubdomain ramdisk %s",
- guest_config->b_info.stubdomain_ramdisk);
- ret = ERROR_INVAL;
- goto out;
- }
-
- stubdom_state->pv_kernel.path = guest_config->b_info.stubdomain_kernel;
- stubdom_state->pv_ramdisk.path = guest_config->b_info.stubdomain_ramdisk;
-
- /* fixme: this function can leak the stubdom if it fails */
- ret = libxl__domain_make(gc, dm_config, stubdom_state,
- &sdss->pvqemu.guest_domid, false);
- if (ret)
- goto out;
- uint32_t dm_domid = sdss->pvqemu.guest_domid;
- ret = libxl__domain_build(gc, dm_config, dm_domid, stubdom_state);
- if (ret)
- goto out;
-
- ret = libxl__build_device_model_args(gc, "stubdom-dm", guest_domid,
- guest_config, &args, NULL,
- d_state, NULL);
- if (ret) {
- ret = ERROR_FAIL;
- goto out;
- }
-
- libxl__store_libxl_entry(gc, guest_domid, "dm-version",
- libxl_device_model_version_to_string(dm_config->b_info.device_model_version));
-
- libxl__write_stub_dmargs(gc, dm_domid, guest_domid, args,
- libxl__stubdomain_is_linux(&guest_config->b_info));
- libxl__xs_printf(gc, XBT_NULL,
- GCSPRINTF("%s/image/device-model-domid",
- libxl__xs_get_dompath(gc, guest_domid)),
- "%d", dm_domid);
- libxl__xs_printf(gc, XBT_NULL,
- GCSPRINTF("%s/target",
- libxl__xs_get_dompath(gc, dm_domid)),
- "%d", guest_domid);
- if (guest_config->b_info.device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
- /* qemu-xen is used as a dm in the stubdomain, so we set the bios
- * accroding to this */
- libxl__xs_printf(gc, XBT_NULL,
- libxl__sprintf(gc, "%s/hvmloader/bios",
- libxl__xs_get_dompath(gc, guest_domid)),
- "%s",
- libxl_bios_type_to_string(guest_config->b_info.u.hvm.bios));
- }
- ret = xc_domain_set_target(ctx->xch, dm_domid, guest_domid);
- if (ret<0) {
- LOGED(ERROR, guest_domid, "setting target domain %d -> %d",
- dm_domid, guest_domid);
- ret = ERROR_FAIL;
- goto out;
- }
- xs_set_target(ctx->xsh, dm_domid, guest_domid);
-
- perm[0].id = dm_domid;
- perm[0].perms = XS_PERM_NONE;
- perm[1].id = guest_domid;
- perm[1].perms = XS_PERM_READ;
-retry_transaction:
- t = xs_transaction_start(ctx->xsh);
- xs_mkdir(ctx->xsh, t, DEVICE_MODEL_XS_PATH(gc, dm_domid, guest_domid, ""));
- xs_set_permissions(ctx->xsh, t,
- DEVICE_MODEL_XS_PATH(gc, dm_domid, guest_domid, ""),
- perm, ARRAY_SIZE(perm));
- if (!xs_transaction_end(ctx->xsh, t, 0))
- if (errno == EAGAIN)
- goto retry_transaction;
-
- libxl__multidev_begin(ao, &sdss->multidev);
- sdss->multidev.callback = spawn_stub_launch_dm;
- libxl__add_disks(egc, ao, dm_domid, dm_config, &sdss->multidev);
- libxl__multidev_prepared(egc, &sdss->multidev, 0);
-
- return;
-
-out:
- assert(ret);
- spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, ret);
-}
-
-static void spawn_stub_launch_dm(libxl__egc *egc,
- libxl__multidev *multidev, int ret)
-{
- libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(multidev, *sdss, multidev);
- STATE_AO_GC(sdss->dm.spawn.ao);
- libxl_ctx *ctx = libxl__gc_owner(gc);
- int i, num_console = STUBDOM_SPECIAL_CONSOLES;
- libxl__device_console *console;
-
- /* convenience aliases */
- libxl_domain_config *const dm_config = &sdss->dm_config;
- libxl_domain_config *const guest_config = sdss->dm.guest_config;
- const int guest_domid = sdss->dm.guest_domid;
- libxl__domain_build_state *const d_state = sdss->dm.build_state;
- libxl__domain_build_state *const stubdom_state = &sdss->dm_state;
- uint32_t dm_domid = sdss->pvqemu.guest_domid;
- int need_qemu;
-
- if (ret) {
- LOGD(ERROR, guest_domid, "error connecting disk devices");
- goto out;
- }
-
- for (i = 0; i < dm_config->num_nics; i++) {
- /* We have to init the nic here, because we still haven't
- * called libxl_device_nic_add at this point, but qemu needs
- * the nic information to be complete.
- */
- ret = libxl__nic_devtype.set_default(gc, dm_domid, &dm_config->nics[i],
- false);
- if (ret)
- goto out;
- }
- if (dm_config->num_vfbs) {
- ret = libxl__device_add(gc, dm_domid, &libxl__vfb_devtype,
- &dm_config->vfbs[0]);
- if (ret) goto out;
- }
- if (dm_config->num_vkbs) {
- ret = libxl__device_add(gc, dm_domid, &libxl__vkb_devtype,
- &dm_config->vkbs[0]);
- if (ret) goto out;
- }
-
- if (guest_config->b_info.u.hvm.serial) {
- num_console++;
- } else if (guest_config->b_info.u.hvm.serial_list) {
- char **serial = guest_config->b_info.u.hvm.serial_list;
- while (*(serial++))
- num_console++;
- }
-
- console = libxl__calloc(gc, num_console, sizeof(libxl__device_console));
-
- for (i = 0; i < num_console; i++) {
- console[i].devid = i;
- console[i].consback = LIBXL__CONSOLE_BACKEND_IOEMU;
- /* STUBDOM_CONSOLE_LOGGING (console 0) is for minios logging
- * STUBDOM_CONSOLE_SAVE (console 1) is for writing the save file
- * STUBDOM_CONSOLE_RESTORE (console 2) is for reading the save file
- */
- switch (i) {
- char *filename;
- char *name;
- case STUBDOM_CONSOLE_LOGGING:
- name = GCSPRINTF("qemu-dm-%s",
- libxl_domid_to_name(ctx, guest_domid));
- ret = libxl_create_logfile(ctx, name, &filename);
- if (ret) goto out;
- console[i].output = GCSPRINTF("file:%s", filename);
- free(filename);
- /* will be changed back to LIBXL__CONSOLE_BACKEND_IOEMU if qemu
- * will be in use */
- console[i].consback = LIBXL__CONSOLE_BACKEND_XENCONSOLED;
- break;
- case STUBDOM_CONSOLE_SAVE:
- console[i].output = GCSPRINTF("file:%s",
- libxl__device_model_savefile(gc, guest_domid));
- break;
- case STUBDOM_CONSOLE_RESTORE:
- if (d_state->saved_state)
- console[i].output =
- GCSPRINTF("pipe:%s", d_state->saved_state);
- break;
- case STUBDOM_CONSOLE_SERIAL:
- if (guest_config->b_info.u.hvm.serial) {
- console[i].output = guest_config->b_info.u.hvm.serial;
- break;
- }
- /* fall-through */
- default:
- /* Serial_list is set, as otherwise num_consoles would be
- * smaller and consoles 0-2 are handled above. */
- assert(guest_config->b_info.u.hvm.serial_list);
- console[i].output = guest_config->b_info.u.hvm.serial_list[
- i-STUBDOM_CONSOLE_SERIAL];
- break;
- }
- }
-
- /*
- * Until xenconsoled learns how to handle multiple consoles, require qemu
- * in dom0 to serve consoles for a stubdomain - it require at least 3 of them.
- */
- need_qemu = 1 || libxl__need_xenpv_qemu(gc, &sdss->dm_config);
-
- for (i = 0; i < num_console; i++) {
- libxl__device device;
- if (need_qemu)
- console[i].consback = LIBXL__CONSOLE_BACKEND_IOEMU;
- ret = libxl__device_console_add(gc, dm_domid, &console[i],
- i == STUBDOM_CONSOLE_LOGGING ? stubdom_state : NULL,
- &device);
- if (ret)
- goto out;
- }
-
- sdss->qmp_proxy_spawn.ao = ao;
- if (libxl__stubdomain_is_linux(&guest_config->b_info)) {
- spawn_qmp_proxy(egc, sdss);
- } else {
- qmp_proxy_spawn_outcome(egc, sdss, 0);
- }
-
- return;
-
-out:
- assert(ret);
- qmp_proxy_spawn_outcome(egc, sdss, ret);
-}
-
-static void spawn_qmp_proxy(libxl__egc *egc,
- libxl__stub_dm_spawn_state *sdss)
-{
- STATE_AO_GC(sdss->qmp_proxy_spawn.ao);
- const uint32_t guest_domid = sdss->dm.guest_domid;
- const uint32_t dm_domid = sdss->pvqemu.guest_domid;
- const char *dom_path = libxl__xs_get_dompath(gc, dm_domid);
- char **args;
- int nr = 0;
- int rc, logfile_w, null;
-
- if (access(STUBDOM_QMP_PROXY_PATH, X_OK) < 0) {
- LOGED(ERROR, guest_domid, "qmp proxy %s is not executable", STUBDOM_QMP_PROXY_PATH);
- rc = ERROR_FAIL;
- goto out;
- }
-
- sdss->qmp_proxy_spawn.what = GCSPRINTF("domain %d device model qmp proxy", guest_domid);
- sdss->qmp_proxy_spawn.pidpath = GCSPRINTF("%s/image/qmp-proxy-pid", dom_path);
- sdss->qmp_proxy_spawn.xspath = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID,
- dm_domid, "/qmp-proxy-state");
- sdss->qmp_proxy_spawn.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
- sdss->qmp_proxy_spawn.midproc_cb = libxl__spawn_record_pid;
- sdss->qmp_proxy_spawn.confirm_cb = qmp_proxy_confirm;
- sdss->qmp_proxy_spawn.failure_cb = qmp_proxy_startup_failed;
- sdss->qmp_proxy_spawn.detached_cb = qmp_proxy_detached;
-
- const int arraysize = 6;
- GCNEW_ARRAY(args, arraysize);
- args[nr++] = STUBDOM_QMP_PROXY_PATH;
- args[nr++] = GCSPRINTF("--state-path=%s", sdss->qmp_proxy_spawn.xspath);
- args[nr++] = GCSPRINTF("%u", dm_domid);
- args[nr++] = GCSPRINTF("%s/device-model/%u/qmp-vchan", dom_path, guest_domid);
- args[nr++] = (char*)libxl__qemu_qmp_path(gc, guest_domid);
- args[nr++] = NULL;
- assert(nr == arraysize);
-
- logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qmp-proxy-%s",
- sdss->dm_config.c_info.name));
- if (logfile_w < 0) {
- rc = logfile_w;
- goto out;
- }
- null = open("/dev/null", O_RDWR);
- if (null < 0) {
- LOGED(ERROR, guest_domid, "unable to open /dev/null");
- rc = ERROR_FAIL;
- goto out_close;
- }
-
- rc = libxl__spawn_spawn(egc, &sdss->qmp_proxy_spawn);
- if (rc < 0)
- goto out_close;
- if (!rc) { /* inner child */
- setsid();
- libxl__exec(gc, null, null, logfile_w, STUBDOM_QMP_PROXY_PATH, args, NULL);
- /* unreachable */
- }
-
- rc = 0;
-
-out_close:
- if (logfile_w >= 0)
- close(logfile_w);
- if (null >= 0)
- close(null);
-out:
- if (rc)
- qmp_proxy_spawn_outcome(egc, sdss, rc);
-}
-
-static void qmp_proxy_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
- const char *xsdata)
-{
- STATE_AO_GC(spawn->ao);
-
- if (!xsdata)
- return;
-
- if (strcmp(xsdata, "running"))
- return;
-
- libxl__spawn_initiate_detach(gc, spawn);
-}
-
-static void qmp_proxy_startup_failed(libxl__egc *egc,
- libxl__spawn_state *spawn,
- int rc)
-{
- libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(spawn, *sdss, qmp_proxy_spawn);
- qmp_proxy_spawn_outcome(egc, sdss, rc);
-}
-
-static void qmp_proxy_detached(libxl__egc *egc,
- libxl__spawn_state *spawn)
-{
- libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(spawn, *sdss, qmp_proxy_spawn);
- qmp_proxy_spawn_outcome(egc, sdss, 0);
-}
-
-static void qmp_proxy_spawn_outcome(libxl__egc *egc,
- libxl__stub_dm_spawn_state *sdss,
- int rc)
-{
- STATE_AO_GC(sdss->qmp_proxy_spawn.ao);
- /*
- * Until xenconsoled learns how to handle multiple consoles, require qemu
- * in dom0 to serve consoles for a stubdomain - it require at least 3 of them.
- */
- int need_pvqemu = 1 || libxl__need_xenpv_qemu(gc, &sdss->dm_config);
-
- if (rc) goto out;
-
- if (need_pvqemu < 0) {
- rc = need_pvqemu;
- goto out;
- }
-
- sdss->pvqemu.spawn.ao = ao;
- sdss->pvqemu.guest_config = &sdss->dm_config;
- sdss->pvqemu.build_state = &sdss->dm_state;
- sdss->pvqemu.callback = spawn_stubdom_pvqemu_cb;
- if (need_pvqemu) {
- libxl__spawn_local_dm(egc, &sdss->pvqemu);
- } else {
- /* If dom0 qemu not needed, do not launch it */
- spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, 0);
- }
-
- return;
-
-out:
- assert(rc);
- spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, rc);
-}
-
-static void spawn_stubdom_pvqemu_cb(libxl__egc *egc,
- libxl__dm_spawn_state *stubdom_dmss,
- int rc)
-{
- libxl__stub_dm_spawn_state *sdss =
- CONTAINER_OF(stubdom_dmss, *sdss, pvqemu);
- STATE_AO_GC(sdss->dm.spawn.ao);
- uint32_t dm_domid = sdss->pvqemu.guest_domid;
- libxl_domain_config *d_config = stubdom_dmss->guest_config;
-
- if (rc) goto out;
-
- if (d_config->num_nics > 0) {
- libxl__multidev_begin(ao, &sdss->multidev);
- sdss->multidev.callback = stubdom_pvqemu_cb;
- libxl__add_nics(egc, ao, dm_domid, d_config, &sdss->multidev);
- libxl__multidev_prepared(egc, &sdss->multidev, 0);
- return;
- }
-
-out:
- stubdom_pvqemu_cb(egc, &sdss->multidev, rc);
-}
-
-static void stubdom_pvqemu_cb(libxl__egc *egc,
- libxl__multidev *multidev,
- int rc)
-{
- libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(multidev, *sdss, multidev);
- STATE_AO_GC(sdss->dm.spawn.ao);
- uint32_t dm_domid = sdss->pvqemu.guest_domid;
-
- if (rc) {
- LOGED(ERROR, sdss->dm.guest_domid,
- "error connecting nics devices");
- goto out;
- }
-
- sdss->pvqemu.dmrs.ao = ao;
- sdss->pvqemu.dmrs.domid = dm_domid;
- sdss->pvqemu.dmrs.callback = stubdom_pvqemu_unpaused;
- libxl__domain_unpause(egc, &sdss->pvqemu.dmrs); /* must be last */
- return;
-out:
- stubdom_pvqemu_unpaused(egc, &sdss->pvqemu.dmrs, rc);
-}
-
-static void stubdom_pvqemu_unpaused(libxl__egc *egc,
- libxl__dm_resume_state *dmrs,
- int rc)
-{
- libxl__stub_dm_spawn_state *sdss =
- CONTAINER_OF(dmrs, *sdss, pvqemu.dmrs);
- STATE_AO_GC(sdss->dm.spawn.ao);
- uint32_t dm_domid = sdss->pvqemu.guest_domid;
-
- if (rc) goto out;
-
- sdss->xswait.ao = ao;
- sdss->xswait.what = GCSPRINTF("Stubdom %u for %u startup",
- dm_domid, sdss->dm.guest_domid);
- sdss->xswait.path = DEVICE_MODEL_XS_PATH(gc, dm_domid, sdss->dm.guest_domid,
- "/state");
- sdss->xswait.timeout_ms = LIBXL_STUBDOM_START_TIMEOUT * 1000;
- sdss->xswait.callback = stubdom_xswait_cb;
- rc = libxl__xswait_start(gc, &sdss->xswait);
- if (rc) goto out;
-
- return;
-
- out:
- stubdom_xswait_cb(egc, &sdss->xswait, rc, NULL);
-}
-
-static void stubdom_xswait_cb(libxl__egc *egc, libxl__xswait_state *xswait,
- int rc, const char *p)
-{
- EGC_GC;
- libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(xswait, *sdss, xswait);
-
- if (rc) {
- if (rc == ERROR_TIMEDOUT)
- LOGD(ERROR, sdss->dm.guest_domid,
- "%s: startup timed out", xswait->what);
- goto out;
- }
-
- if (!p) return;
-
- if (strcmp(p, "running"))
- return;
- out:
- libxl__domain_build_state_dispose(&sdss->dm_state);
- libxl__xswait_stop(gc, xswait);
- dmss_dispose(gc, &sdss->dm);
- dmss_dispose(gc, &sdss->pvqemu);
- sdss->callback(egc, &sdss->dm, rc);
-}
-
-/* callbacks passed to libxl__spawn_spawn */
-static void device_model_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
- const char *xsdata);
-static void device_model_startup_failed(libxl__egc *egc,
- libxl__spawn_state *spawn,
- int rc);
-static void device_model_detached(libxl__egc *egc,
- libxl__spawn_state *spawn);
-static void device_model_qmp_cb(libxl__egc *egc, libxl__ev_qmp *ev,
- const libxl__json_object *response,
- int rc);
-
-/* our "next step" function, called from those callbacks and elsewhere */
-static void device_model_spawn_outcome(libxl__egc *egc,
- libxl__dm_spawn_state *dmss,
- int rc);
-static void device_model_postconfig_chardev(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
-static void device_model_postconfig_vnc(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
-static void device_model_postconfig_vnc_passwd(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
-static void devise_model_postconfig_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void device_model_postconfig_done(libxl__egc *egc,
- libxl__dm_spawn_state *dmss, int rc);
-
-void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state *dmss)
-{
- /* convenience aliases */
- const int domid = dmss->guest_domid;
- libxl__domain_build_state *const state = dmss->build_state;
- libxl__spawn_state *const spawn = &dmss->spawn;
-
- STATE_AO_GC(dmss->spawn.ao);
-
- libxl_ctx *ctx = CTX;
- libxl_domain_config *guest_config = dmss->guest_config;
- const libxl_domain_create_info *c_info = &guest_config->c_info;
- const libxl_domain_build_info *b_info = &guest_config->b_info;
- const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
- char *path;
- int logfile_w, null;
- int rc;
- char **args, **arg, **envs;
- xs_transaction_t t;
- char *vm_path;
- char **pass_stuff;
- const char *dm;
- int dm_state_fd = -1;
-
- dmss_init(dmss);
-
- if (libxl_defbool_val(b_info->device_model_stubdomain)) {
- abort();
- }
-
- dm = libxl__domain_device_model(gc, b_info);
- if (!dm) {
- rc = ERROR_FAIL;
- goto out;
- }
- if (access(dm, X_OK) < 0) {
- LOGED(ERROR, domid, "device model %s is not executable", dm);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__domain_get_device_model_uid(gc, dmss);
- if (rc)
- goto out;
-
- if (b_info->device_model_version
- == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN &&
- libxl_defbool_val(b_info->dm_restrict)) {
- /* If we have to use dm_restrict, QEMU needs to be new enough
- * and will have the new interface where we can pre-open the
- * QMP socket. */
- rc = libxl__pre_open_qmp_socket(gc, domid, &state->dm_monitor_fd);
- if (rc) goto out;
- }
-
- rc = libxl__build_device_model_args(gc, dm, domid, guest_config,
- &args, &envs, state,
- &dm_state_fd);
- if (rc)
- goto out;
-
- if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
- path = xs_get_domain_path(ctx->xsh, domid);
- libxl__xs_printf(gc, XBT_NULL,
- GCSPRINTF("%s/hvmloader/bios", path),
- "%s", libxl_bios_type_to_string(b_info->u.hvm.bios));
- /* Disable relocating memory to make the MMIO hole larger
- * unless we're running qemu-traditional and vNUMA is not
- * configured. */
- libxl__xs_printf(gc, XBT_NULL,
- GCSPRINTF("%s/hvmloader/allow-memory-relocate", path),
- "%d",
- b_info->device_model_version==LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL &&
- !libxl__vnuma_configured(b_info));
- free(path);
- }
-
- path = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid, "");
- xs_mkdir(ctx->xsh, XBT_NULL, path);
-
- if (b_info->type == LIBXL_DOMAIN_TYPE_HVM &&
- b_info->device_model_version
- == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL)
- libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/disable_pf", path),
- "%d", !libxl_defbool_val(b_info->u.hvm.xen_platform_pci));
-
- logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qemu-dm-%s",
- c_info->name));
- if (logfile_w < 0) {
- rc = logfile_w;
- goto out;
- }
- null = open("/dev/null", O_RDONLY);
- if (null < 0) {
- LOGED(ERROR, domid, "unable to open /dev/null");
- rc = ERROR_FAIL;
- goto out_close;
- }
-
- const char *dom_path = libxl__xs_get_dompath(gc, domid);
-
- /*
- * If we're starting the dm with a non-root UID, save the UID so
- * that we can reliably kill it and any subprocesses
- */
- if (state->dm_kill_uid)
- libxl__xs_printf(gc, XBT_NULL,
- GCSPRINTF("%s/image/device-model-kill-uid", dom_path),
- "%s", state->dm_kill_uid);
-
- if (vnc && vnc->passwd) {
- /* This xenstore key will only be used by qemu-xen-traditionnal.
- * The code to supply vncpasswd to qemu-xen is later. */
-retry_transaction:
- /* Find uuid and the write the vnc password to xenstore for qemu. */
- t = xs_transaction_start(ctx->xsh);
- vm_path = libxl__xs_read(gc,t,GCSPRINTF("%s/vm", dom_path));
- if (vm_path) {
- /* Now write the vncpassword into it. */
- pass_stuff = libxl__calloc(gc, 3, sizeof(char *));
- pass_stuff[0] = "vncpasswd";
- pass_stuff[1] = vnc->passwd;
- libxl__xs_writev(gc,t,vm_path,pass_stuff);
- if (!xs_transaction_end(ctx->xsh, t, 0))
- if (errno == EAGAIN)
- goto retry_transaction;
- }
- }
-
- LOGD(DEBUG, domid, "Spawning device-model %s with arguments:", dm);
- for (arg = args; *arg; arg++)
- LOGD(DEBUG, domid, " %s", *arg);
- if (*envs) {
- LOGD(DEBUG, domid, "Spawning device-model %s with additional environment:", dm);
- for (arg = envs; *arg; arg += 2)
- LOGD(DEBUG, domid, " %s=%s", arg[0], arg[1]);
- }
-
- spawn->what = GCSPRINTF("domain %d device model", domid);
- spawn->xspath = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid,
- "/state");
- spawn->timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
- spawn->pidpath = GCSPRINTF("%s/image/device-model-pid", dom_path);
- spawn->midproc_cb = libxl__spawn_record_pid;
- spawn->confirm_cb = device_model_confirm;
- spawn->failure_cb = device_model_startup_failed;
- spawn->detached_cb = device_model_detached;
-
- if (state->dm_monitor_fd >= 0) {
- /* There is a valid QMP socket available now,
- * use it to find out when QEMU is ready */
- dmss->qmp.ao = ao;
- dmss->qmp.callback = device_model_qmp_cb;
- dmss->qmp.domid = domid;
- dmss->qmp.payload_fd = -1;
- rc = libxl__ev_qmp_send(egc, &dmss->qmp, "query-status", NULL);
- if (rc) goto out_close;
- }
-
- rc = libxl__spawn_spawn(egc, spawn);
- if (rc < 0)
- goto out_close;
- if (!rc) { /* inner child */
- setsid();
- if (libxl_defbool_val(b_info->dm_restrict)) {
- rc = libxl__local_dm_preexec_restrict(gc);
- if (rc)
- _exit(-1);
- }
- libxl__exec(gc, null, logfile_w, logfile_w, dm, args, envs);
- }
-
- rc = 0;
-
-out_close:
- if (null >= 0) close(null);
- if (logfile_w >= 0) close(logfile_w);
-out:
- if (dm_state_fd >= 0) close(dm_state_fd);
- if (rc)
- device_model_spawn_outcome(egc, dmss, rc);
-}
-
-bool libxl__query_qemu_backend(libxl__gc *gc, uint32_t domid,
- uint32_t backend_id, const char *type, bool def)
-{
- char *path;
- char **dir;
- unsigned int n;
-
- path = GCSPRINTF("%s/device-model/%u/backends",
- libxl__xs_get_dompath(gc, backend_id), domid);
- dir = libxl__xs_directory(gc, XBT_NULL, path, &n);
- if (!dir)
- return def;
-
- path = GCSPRINTF("%s/device-model/%u/backends/%s",
- libxl__xs_get_dompath(gc, backend_id), domid, type);
- dir = libxl__xs_directory(gc, XBT_NULL, path, &n);
-
- return !!dir;
-}
-
-static void device_model_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
- const char *xsdata)
-{
- STATE_AO_GC(spawn->ao);
-
- if (!xsdata)
- return;
-
- if (strcmp(xsdata, "running"))
- return;
-
- libxl__spawn_initiate_detach(gc, spawn);
-}
-
-static void device_model_startup_failed(libxl__egc *egc,
- libxl__spawn_state *spawn,
- int rc)
-{
- libxl__dm_spawn_state *dmss = CONTAINER_OF(spawn, *dmss, spawn);
- device_model_spawn_outcome(egc, dmss, rc);
-}
-
-static void device_model_detached(libxl__egc *egc,
- libxl__spawn_state *spawn)
-{
- libxl__dm_spawn_state *dmss = CONTAINER_OF(spawn, *dmss, spawn);
- device_model_spawn_outcome(egc, dmss, 0);
-}
-
-static void device_model_qmp_cb(libxl__egc *egc, libxl__ev_qmp *ev,
- const libxl__json_object *response,
- int rc)
-{
- EGC_GC;
- libxl__dm_spawn_state *dmss = CONTAINER_OF(ev, *dmss, qmp);
- const libxl__json_object *o;
- const char *status;
- const char *expected_state;
-
- libxl__ev_qmp_dispose(gc, ev);
-
- if (rc)
- goto failed;
-
- o = libxl__json_map_get("status", response, JSON_STRING);
- if (!o) {
- LOGD(ERROR, ev->domid,
- "Missing 'status' in response to 'query-status'");
- LOGD(DEBUG, ev->domid, ".. instead, got: %s", JSON(response));
- rc = ERROR_QEMU_API;
- goto failed;
- }
- status = libxl__json_object_get_string(o);
- if (!dmss->build_state->saved_state)
- expected_state = "prelaunch";
- else
- expected_state = "paused";
- if (strcmp(status, expected_state)) {
- LOGD(ERROR, ev->domid, "Unexpected QEMU status: %s", status);
- rc = ERROR_NOT_READY;
- goto failed;
- }
-
- libxl__spawn_initiate_detach(gc, &dmss->spawn);
- return;
-
-failed:
- LOGD(ERROR, ev->domid, "QEMU did not start properly, rc=%d", rc);
- libxl__spawn_initiate_failure(egc, &dmss->spawn, rc);
-}
-
-static void device_model_spawn_outcome(libxl__egc *egc,
- libxl__dm_spawn_state *dmss,
- int rc)
-{
- STATE_AO_GC(dmss->spawn.ao);
- int ret2;
-
- /* Convenience aliases */
- libxl_domain_config *const d_config = dmss->guest_config;
-
- if (rc)
- LOGD(ERROR, dmss->guest_domid,
- "%s: spawn failed (rc=%d)", dmss->spawn.what, rc);
-
- libxl__domain_build_state *state = dmss->build_state;
-
- if (state->saved_state) {
- ret2 = unlink(state->saved_state);
- if (ret2) {
- LOGED(ERROR, dmss->guest_domid, "%s: failed to remove device-model state %s",
- dmss->spawn.what, state->saved_state);
- rc = ERROR_FAIL;
- goto out;
- }
- }
-
- /* Check if spawn failed */
- if (rc) goto out;
-
- if (d_config->b_info.device_model_version
- == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
- rc = libxl__ev_time_register_rel(ao, &dmss->timeout,
- devise_model_postconfig_timeout,
- LIBXL_QMP_CMD_TIMEOUT * 1000);
- if (rc) goto out;
- dmss->qmp.ao = ao;
- dmss->qmp.domid = dmss->guest_domid;
- dmss->qmp.payload_fd = -1;
- dmss->qmp.callback = device_model_postconfig_chardev;
- rc = libxl__ev_qmp_send(egc, &dmss->qmp, "query-chardev", NULL);
- if (rc) goto out;
- return;
- }
-
- out:
- device_model_postconfig_done(egc, dmss, rc); /* must be last */
-}
-
-static void device_model_postconfig_chardev(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
-{
- EGC_GC;
- libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp);
- const libxl__json_object *item = NULL;
- const libxl__json_object *o = NULL;
- int i = 0;
- const char *label;
- const char *filename;
- int port;
- char *endptr;
- const char *dompath;
- const char serial[] = "serial";
- const size_t seriall = sizeof(serial) - 1;
- const char pty[] = "pty:";
- const size_t ptyl = sizeof(pty) - 1;
-
- if (rc) goto out;
-
- /*
- * query-chardev response:
- * [{ 'label': 'str',
- * 'filename': 'str',
- * 'frontend-open': 'bool' }, ...]
- */
-
- for (i = 0; (item = libxl__json_array_get(response, i)); i++) {
- o = libxl__json_map_get("label", item, JSON_STRING);
- if (!o) goto protocol_error;
- label = libxl__json_object_get_string(o);
-
- /* Check if the "label" start with "serial". */
- if (!label || strncmp(label, serial, seriall))
- continue;
- port = strtol(label + seriall, &endptr, 10);
- if (*(label + seriall) == '\0' || *endptr != '\0') {
- LOGD(ERROR, qmp->domid,
- "Invalid serial port number: %s", label);
- rc = ERROR_QEMU_API;
- goto out;
- }
-
- o = libxl__json_map_get("filename", item, JSON_STRING);
- if (!o) goto protocol_error;
- filename = libxl__json_object_get_string(o);
-
- /* Check if filename start with "pty:" */
- if (!filename || strncmp(filename, pty, ptyl))
- continue;
-
- dompath = libxl__xs_get_dompath(gc, qmp->domid);
- if (!dompath) {
- rc = ERROR_FAIL;
- goto out;
- }
- rc = libxl__xs_printf(gc, XBT_NULL,
- GCSPRINTF("%s/serial/%d/tty", dompath, port),
- "%s", filename + ptyl);
- if (rc) goto out;
- }
-
- qmp->callback = device_model_postconfig_vnc;
- rc = libxl__ev_qmp_send(egc, qmp, "query-vnc", NULL);
- if (rc) goto out;
- return;
-
-protocol_error:
- rc = ERROR_QEMU_API;
- LOGD(ERROR, qmp->domid,
- "unexpected response to QMP cmd 'query-chardev', received:\n%s",
- JSON(response));
-out:
- device_model_postconfig_done(egc, dmss, rc); /* must be last */
-}
-
-static void device_model_postconfig_vnc(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
-{
- EGC_GC;
- libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp);
- const libxl_vnc_info *vnc = libxl__dm_vnc(dmss->guest_config);
- const libxl__json_object *o;
- libxl__json_object *args = NULL;
-
- if (rc) goto out;
-
- /*
- * query-vnc response:
- * { 'enabled': 'bool', '*host': 'str', '*service': 'str' }
- */
-
- o = libxl__json_map_get("enabled", response, JSON_BOOL);
- if (!o) goto protocol_error;
- if (libxl__json_object_get_bool(o)) {
- const char *addr, *port;
- const char *dompath;
-
- o = libxl__json_map_get("host", response, JSON_STRING);
- if (!o) goto protocol_error;
- addr = libxl__json_object_get_string(o);
- o = libxl__json_map_get("service", response, JSON_STRING);
- if (!o) goto protocol_error;
- port = libxl__json_object_get_string(o);
-
- dompath = libxl__xs_get_dompath(gc, qmp->domid);
- if (!dompath) {
- rc = ERROR_FAIL;
- goto out;
- }
- rc = libxl__xs_printf(gc, XBT_NULL,
- GCSPRINTF("%s/console/vnc-listen", dompath),
- "%s", addr);
- if (rc) goto out;
- rc = libxl__xs_printf(gc, XBT_NULL,
- GCSPRINTF("%s/console/vnc-port", dompath),
- "%s", port);
- if (rc) goto out;
- }
-
- if (vnc && vnc->passwd && vnc->passwd[0]) {
- qmp->callback = device_model_postconfig_vnc_passwd;
- libxl__qmp_param_add_string(gc, &args, "password", vnc->passwd);
- rc = libxl__ev_qmp_send(egc, qmp, "change-vnc-password", args);
- if (rc) goto out;
- return;
- }
-
- rc = 0;
- goto out;
-
-protocol_error:
- rc = ERROR_QEMU_API;
- LOGD(ERROR, qmp->domid,
- "unexpected response to QMP cmd 'query-vnc', received:\n%s",
- JSON(response));
-out:
- device_model_postconfig_done(egc, dmss, rc); /* must be last */
-}
-
-static void device_model_postconfig_vnc_passwd(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
-{
- EGC_GC;
- libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp);
- const libxl_vnc_info *vnc = libxl__dm_vnc(dmss->guest_config);
- const char *dompath;
-
- if (rc) goto out;
-
- dompath = libxl__xs_get_dompath(gc, qmp->domid);
- if (!dompath) {
- rc = ERROR_FAIL;
- goto out;
- }
- rc = libxl__xs_printf(gc, XBT_NULL,
- GCSPRINTF("%s/console/vnc-pass", dompath),
- "%s", vnc->passwd);
-
-out:
- device_model_postconfig_done(egc, dmss, rc); /* must be last */
-}
-
-void devise_model_postconfig_timeout(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- libxl__dm_spawn_state *dmss = CONTAINER_OF(ev, *dmss, timeout);
- device_model_postconfig_done(egc, dmss, rc); /* must be last */
-}
-
-
-static void device_model_postconfig_done(libxl__egc *egc,
- libxl__dm_spawn_state *dmss,
- int rc)
-{
- EGC_GC;
-
- if (rc)
- LOGD(ERROR, dmss->guest_domid,
- "Post DM startup configs failed, rc=%d", rc);
- dmss_dispose(gc, dmss);
- dmss->callback(egc, dmss, rc);
-}
-
-void libxl__spawn_qdisk_backend(libxl__egc *egc, libxl__dm_spawn_state *dmss)
-{
- STATE_AO_GC(dmss->spawn.ao);
- flexarray_t *dm_args, *dm_envs;
- char **args, **envs;
- const char *dm;
- int logfile_w, null = -1, rc;
- uint32_t domid = dmss->guest_domid;
-
- dmss_init(dmss);
-
- /* Always use qemu-xen as device model */
- dm = qemu_xen_path(gc);
-
- dm_args = flexarray_make(gc, 15, 1);
- dm_envs = flexarray_make(gc, 1, 1);
-
- flexarray_vappend(dm_args, dm, "-xen-domid",
- GCSPRINTF("%d", domid), NULL);
- flexarray_append(dm_args, "-xen-attach");
- flexarray_vappend(dm_args, "-name",
- GCSPRINTF("domain-%u", domid), NULL);
- flexarray_append(dm_args, "-nographic");
- flexarray_vappend(dm_args, "-M", "xenpv", NULL);
- flexarray_vappend(dm_args, "-monitor", "/dev/null", NULL);
- flexarray_vappend(dm_args, "-serial", "/dev/null", NULL);
- flexarray_vappend(dm_args, "-parallel", "/dev/null", NULL);
- flexarray_append(dm_args, NULL);
- args = (char **) flexarray_contents(dm_args);
-
- libxl__set_qemu_env_for_xsa_180(gc, dm_envs);
- envs = (char **) flexarray_contents(dm_envs);
-
- logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qdisk-%u", domid));
- if (logfile_w < 0) {
- rc = logfile_w;
- goto out;
- }
- null = open("/dev/null", O_RDONLY);
- if (null < 0) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- dmss->guest_config = NULL;
- /*
- * Clearly specify Qemu not using a saved state, so
- * device_model_spawn_outcome doesn't try to unlink it.
- */
- dmss->build_state = libxl__zalloc(gc, sizeof(*dmss->build_state));
- dmss->build_state->saved_state = 0;
-
- dmss->spawn.what = GCSPRINTF("domain %u Qdisk backend", domid);
- dmss->spawn.xspath = GCSPRINTF("device-model/%u/state", domid);
- dmss->spawn.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
- /*
- * We cannot save Qemu pid anywhere in the xenstore guest dir,
- * because we will call this from unprivileged driver domains,
- * so save it in the current domain libxl private dir.
- */
- dmss->spawn.pidpath = GCSPRINTF("libxl/%u/qdisk-backend-pid", domid);
- dmss->spawn.midproc_cb = libxl__spawn_record_pid;
- dmss->spawn.confirm_cb = device_model_confirm;
- dmss->spawn.failure_cb = device_model_startup_failed;
- dmss->spawn.detached_cb = device_model_detached;
- rc = libxl__spawn_spawn(egc, &dmss->spawn);
- if (rc < 0)
- goto out;
- if (!rc) { /* inner child */
- setsid();
- libxl__exec(gc, null, logfile_w, logfile_w, dm, args, envs);
- }
-
- rc = 0;
-out:
- dmss_dispose(gc, dmss);
- if (logfile_w >= 0) close(logfile_w);
- if (null >= 0) close(null);
- /* callback on error only, success goes via dmss->spawn.*_cb */
- if (rc) dmss->callback(egc, dmss, rc);
- return;
-}
-
-/* Generic function to signal a Qemu instance to exit */
-static int kill_device_model(libxl__gc *gc, const char *xs_path_pid)
-{
- return libxl__kill_xs_path(gc, xs_path_pid, "Device Model");
-}
-
-/* Helper to destroy a Qdisk backend */
-int libxl__destroy_qdisk_backend(libxl__gc *gc, uint32_t domid)
-{
- char *pid_path;
- int rc;
-
- pid_path = GCSPRINTF("libxl/%u/qdisk-backend-pid", domid);
-
- rc = kill_device_model(gc, pid_path);
- if (rc)
- goto out;
-
- libxl__xs_rm_checked(gc, XBT_NULL, pid_path);
- libxl__xs_rm_checked(gc, XBT_NULL,
- GCSPRINTF("device-model/%u", domid));
-
-out:
- return rc;
-}
-
-/* Asynchronous device model destroy functions */
-
-static int kill_device_model_uid_child(libxl__destroy_devicemodel_state *ddms,
- const char *dm_kill_uid_str);
-
-static void kill_device_model_uid_cb(libxl__egc *egc,
- libxl__ev_child *destroyer,
- pid_t pid, int status);
-
-/*
- * If we have a uid, we shouldn't kill by pid. This is because a
- * hostile QEMU might have exited, in which case the pid we have may
- * be that of another process.
- *
- * The running devicemodel has permission over a specific domain id;
- * this means that ideally we wouldn't the domain in question (freeing
- * up the domain id for reuse) until we're confident that we've killed
- * the domain.
- *
- * In general, destroy as much as we can; but return an error if there
- * are any errors, so that the domain destroy will be aborted, and the
- * domain itself will remain, giving the admin an opportunity to fix
- * any issues and re-try the domain destroy.
- */
-void libxl__destroy_device_model(libxl__egc *egc,
- libxl__destroy_devicemodel_state *ddms)
-{
- STATE_AO_GC(ddms->ao);
- int rc;
- int domid = ddms->domid;
- char *path;
- const char *dm_kill_uid_str = NULL;
- int reaper_pid;
-
- ddms->rc = 0;
-
- path = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid, "");
- rc = libxl__xs_rm_checked(gc, XBT_NULL, path);
- if (rc) {
- ACCUMULATE_RC(ddms->rc);
- LOGD(ERROR, domid, "xs_rm failed for %s", path);
- }
-
- /*
- * See if we should try to kill by uid
- */
- path = GCSPRINTF("/local/domain/%d/image/device-model-kill-uid", domid);
- rc = libxl__xs_read_checked(gc, XBT_NULL, path, &dm_kill_uid_str);
-
- /*
- * If there was an error here, accumulate the error and fall back
- * to killing by pid.
- */
- if (rc) {
- /*
- * Technically the state of the string passed to libxl__xs_read_checked() is
- * "undefined" in the case rc == 0 (according to libxl_internal.h). Set it to
- * NULL to prevent undefined behavior.
- */
- dm_kill_uid_str = NULL;
- ACCUMULATE_RC(ddms->rc);
- LOGD(ERROR, domid, "Reading dm UID path failed for %s", path);
- }
-
- /* The DM has its own uid; Attempt to kill all processes with that UID */
- if (dm_kill_uid_str) {
- LOGD(DEBUG, domid, "Found DM uid %s, destroying by uid",
- dm_kill_uid_str);
-
- reaper_pid = libxl__ev_child_fork(gc, &ddms->destroyer,
- kill_device_model_uid_cb);
- if (reaper_pid < 0) {
- rc = ERROR_FAIL;
- ACCUMULATE_RC(ddms->rc);
- /*
- * Note that if this fails, we still don't kill by pid, to
- * make sure that an untrusted DM has not "maliciously"
- * exited (potentially causing us to kill an unrelated
- * process which happened to get the same pid).
- */
- goto out;
- }
-
- if (!reaper_pid) { /* child */
- rc = kill_device_model_uid_child(ddms, dm_kill_uid_str);
- assert(rc <= 0 && rc >= -125);
- _exit(-rc);
- }
-
- /*
- * Parent of successful fork; execution will pick up in
- * kill_device_model_uid_cb when child exits
- */
- return;
- }
-
- /*
- * No uid to kill; attept to kill by pid.
- */
- LOGD(DEBUG, domid, "Didn't find dm UID; destroying by pid");
-
- path = GCSPRINTF("/local/domain/%d/image/device-model-pid", domid);
- rc = kill_device_model(gc, path);
-
- if (rc) {
- ACCUMULATE_RC(ddms->rc);
- LOGD(ERROR, domid, "Killing device model pid from path %s", path);
- }
-
-out:
- /*
- * NB that we always pass '0' here for the "status of exited
- * process"; since there is no process, it always "succeeds".
- * Errors are accumulated in ddms->rc and will be handled
- * correctly.
- */
- kill_device_model_uid_cb(egc, &ddms->destroyer, -1, 0);
- return;
-}
-
-/*
- * Note that this attempts to grab a file lock, so must be called from
- * a sub-process.
- */
-static int get_reaper_lock_and_uid(libxl__destroy_devicemodel_state *ddms,
- uid_t *reaper_uid)
-{
- STATE_AO_GC(ddms->ao);
- int domid = ddms->domid;
- int r;
- const char * lockfile;
- int fd; /* "leaked" into the general process context (even on failure) */
-
- /* Try to lock the "reaper uid" */
- lockfile = GCSPRINTF("%s/dm-reaper-lock", libxl__run_dir_path());
-
- /*
- * NB that since we've just forked, we can't have any
- * threads; so we don't need the libxl__carefd
- * infrastructure here.
- */
- fd = open(lockfile, O_RDWR|O_CREAT, 0644);
- if (fd < 0) {
- LOGED(ERROR, domid,
- "unexpected error while trying to open lockfile %s, errno=%d",
- lockfile, errno);
- return ERROR_FAIL;
- }
-
- /* Try to lock the file, retrying on EINTR */
- for (;;) {
- r = flock(fd, LOCK_EX);
- if (!r)
- break;
- if (errno != EINTR) {
- /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
- LOGED(ERROR, domid,
- "unexpected error while trying to lock %s, fd=%d, errno=%d",
- lockfile, fd, errno);
- return ERROR_FAIL;
- }
- }
-
- /*
- * Get reaper_uid. If we can't find such a uid, return an error.
- */
- return libxl__get_reaper_uid(gc, reaper_uid);
-}
-
-
-/*
- * Destroy all processes of the given uid by setresuid to the
- * specified uid and kill(-1). NB this MUST BE CALLED FROM A SEPARATE
- * PROCESS from the normal libxl process, and should exit immediately
- * after return. Returns a libxl-style error code that is guaranteed
- * to be >= -125.
- */
-static int kill_device_model_uid_child(libxl__destroy_devicemodel_state *ddms,
- const char *dm_kill_uid_str) {
- STATE_AO_GC(ddms->ao);
- int domid = ddms->domid;
- int r, rc = 0;
- uid_t dm_kill_uid = atoi(dm_kill_uid_str);
- uid_t reaper_uid;
-
- /*
- * Try to kill the devicemodel by uid. The safest way to do this
- * is to set euid == dm_uid, but the ruid to something else. If
- * we can't get a separate ruid, carry on trying to kill the
- * process anyway using dm_uid for the ruid. This is racy (the dm
- * may be able to kill(-1) us before we kill them), but worth
- * trying.
- *
- * NB: Even if we don't have a separate reaper_uid, the parent can
- * know whether we won the race by looking at the status variable;
- * so we don't strictly need to return failure in this case. But
- * if there's a misconfiguration, it's better to alert the
- * administator sooner rather than later; so if we fail to get a
- * reaper uid, report an error even if the kill succeeds.
- */
- rc = get_reaper_lock_and_uid(ddms, &reaper_uid);
- if (rc) {
- reaper_uid = dm_kill_uid;
- LOGD(WARN, domid, "Couldn't get separate reaper uid;"
- "carrying on with unsafe kill");
- }
-
- /*
- * Should never happen; but if it does, better to have the
- * toolstack crash with an error than nuking dom0.
- */
- assert(reaper_uid);
- assert(dm_kill_uid);
-
- LOGD(DEBUG, domid, "DM reaper: calling setresuid(%d, %d, 0)",
- reaper_uid, dm_kill_uid);
- r = setresuid(reaper_uid, dm_kill_uid, 0);
- if (r) {
- LOGED(ERROR, domid, "setresuid to (%d, %d, 0)",
- reaper_uid, dm_kill_uid);
- rc = rc ?: ERROR_FAIL;
- goto out;
- }
-
- /*
- * And kill everyone but me.
- *
- * NB that it's not clear from either POSIX or the Linux man page
- * that ESRCH would be returned with a pid value of -1, but it
- * doesn't hurt to check.
- */
- r = kill(-1, 9);
- if (r && errno != ESRCH) {
- LOGED(ERROR, domid, "kill(-1,9)");
- rc = rc ?: ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-static void kill_device_model_uid_cb(libxl__egc *egc,
- libxl__ev_child *destroyer,
- pid_t pid, int status)
-{
- libxl__destroy_devicemodel_state *ddms = CONTAINER_OF(destroyer, *ddms, destroyer);
- STATE_AO_GC(ddms->ao);
-
- if (status) {
- int rc = ERROR_FAIL;
-
- if (WIFEXITED(status) && WEXITSTATUS(status) <= 125)
- rc = -WEXITSTATUS(status);
-
- ACCUMULATE_RC(ddms->rc);
- libxl_report_child_exitstatus(CTX, XTL_ERROR,
- "async domain destroy", pid, status);
- }
-
- /* Always try to clean up qmp, even if something went wrong */
- libxl__qmp_cleanup(gc, ddms->domid);
-
- ddms->callback(egc, ddms, ddms->rc);
-}
-
-/* Return 0 if no dm needed, 1 if needed and <0 if error. */
-int libxl__need_xenpv_qemu(libxl__gc *gc, libxl_domain_config *d_config)
-{
- int idx, i, ret, num;
- uint32_t domid;
- const libxl__device_type *dt;
-
- ret = libxl__get_domid(gc, &domid);
- if (ret) {
- LOG(ERROR, "unable to get domain id");
- goto out;
- }
-
- if (d_config->num_vfbs > 0 || d_config->num_p9s > 0) {
- ret = 1;
- goto out;
- }
-
- for (idx = 0;; idx++) {
- dt = device_type_tbl[idx];
- if (!dt)
- break;
-
- num = *libxl__device_type_get_num(dt, d_config);
- if (!dt->dm_needed || !num)
- continue;
-
- for (i = 0; i < num; i++) {
- if (dt->dm_needed(libxl__device_type_get_elem(dt, d_config, i),
- domid)) {
- ret = 1;
- goto out;
- }
- }
- }
-
- for (i = 0; i < d_config->num_channels; i++) {
- if (d_config->channels[i].backend_domid == domid) {
- /* xenconsoled is limited to the first console only.
- Until this restriction is removed we must use qemu for
- secondary consoles which includes all channels. */
- ret = 1;
- goto out;
- }
- }
-
-out:
- return ret;
-}
-
-int libxl__dm_active(libxl__gc *gc, uint32_t domid)
-{
- char *pid, *dm_domid, *path;
-
- path = GCSPRINTF("/local/domain/%d/image/device-model-pid", domid);
- pid = libxl__xs_read(gc, XBT_NULL, path);
-
- if (pid)
- return true;
-
- path = GCSPRINTF("/local/domain/%d/image/device-model-domid", domid);
- dm_domid = libxl__xs_read(gc, XBT_NULL, path);
-
- return dm_domid != NULL;
-}
-
-int libxl__dm_check_start(libxl__gc *gc, libxl_domain_config *d_config,
- uint32_t domid)
-{
- int rc;
-
- if (libxl__dm_active(gc, domid))
- return 0;
-
- rc = libxl__need_xenpv_qemu(gc, d_config);
- if (rc < 0)
- goto out;
-
- if (!rc)
- return 0;
-
- LOGD(ERROR, domid, "device model required but not running");
- rc = ERROR_FAIL;
-
-out:
- return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <glob.h>
-
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-
-#include <xenctrl_dom.h>
-#include <xen/hvm/hvm_info_table.h>
-#include <xen/hvm/hvm_xs_strings.h>
-#include <xen/hvm/e820.h>
-
-#include "_paths.h"
-
-//#define DEBUG 1
-
-libxl_domain_type libxl__domain_type(libxl__gc *gc, uint32_t domid)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- xc_domaininfo_t info;
- int ret;
-
- ret = xc_domain_getinfolist(ctx->xch, domid, 1, &info);
- if (ret != 1 || info.domain != domid) {
- LOG(ERROR, "unable to get domain type for domid=%"PRIu32, domid);
- return LIBXL_DOMAIN_TYPE_INVALID;
- }
- if (info.flags & XEN_DOMINF_hvm_guest) {
- const char *type_path = GCSPRINTF("%s/type",
- libxl__xs_libxl_path(gc, domid));
- const char *type;
- libxl_domain_type t;
- int rc;
-
- rc = libxl__xs_read_mandatory(gc, XBT_NULL, type_path, &type);
- if (rc) {
- LOG(WARN,
- "unable to get domain type for domid=%"PRIu32", assuming HVM",
- domid);
- return LIBXL_DOMAIN_TYPE_HVM;
- }
-
- rc = libxl_domain_type_from_string(type, &t);
- if (rc) {
- LOG(WARN,
- "unable to get domain type for domid=%"PRIu32", assuming HVM",
- domid);
- return LIBXL_DOMAIN_TYPE_HVM;
- }
-
- return t;
- } else
- return LIBXL_DOMAIN_TYPE_PV;
-}
-
-int libxl__domain_cpupool(libxl__gc *gc, uint32_t domid)
-{
- xc_domaininfo_t info;
- int ret;
-
- ret = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
- if (ret != 1)
- {
- LOGE(ERROR, "getinfolist failed %d", ret);
- return ERROR_FAIL;
- }
- if (info.domain != domid)
- {
- LOGE(ERROR, "got info for dom%d, wanted dom%d\n", info.domain, domid);
- return ERROR_FAIL;
- }
- return info.cpupool;
-}
-
-libxl_scheduler libxl__domain_scheduler(libxl__gc *gc, uint32_t domid)
-{
- int cpupool = libxl__domain_cpupool(gc, domid);
- libxl_cpupoolinfo poolinfo;
- libxl_scheduler sched = LIBXL_SCHEDULER_UNKNOWN;
- int rc;
-
- if (cpupool < 0)
- return sched;
-
- libxl_cpupoolinfo_init(&poolinfo);
- rc = libxl_cpupool_info(CTX, &poolinfo, cpupool);
- if (rc < 0)
- goto out;
-
- sched = poolinfo.sched;
-
-out:
- libxl_cpupoolinfo_dispose(&poolinfo);
- return sched;
-}
-
-/*
- * Two NUMA placement candidates are compared by means of the following
- * heuristics:
-
- * - the number of vcpus runnable on the candidates is considered, and
- * candidates with fewer of them are preferred. If two candidate have
- * the same number of runnable vcpus,
- * - the amount of free memory in the candidates is considered, and the
- * candidate with greater amount of it is preferred.
- *
- * In fact, leaving larger memory holes, maximizes the probability of being
- * able to put other domains on the node. That hopefully means many domains
- * will benefit from local memory accesses, but also introduces the risk of
- * overloading large (from a memory POV) nodes. That's right the effect
- * that counting the vcpus able to run on the nodes tries to prevent.
- *
- * Note that this completely ignore the number of nodes each candidate span,
- * as the fact that fewer nodes is better is already accounted for in the
- * algorithm.
- */
-static int numa_cmpf(const libxl__numa_candidate *c1,
- const libxl__numa_candidate *c2)
-{
- if (c1->nr_vcpus != c2->nr_vcpus)
- return c1->nr_vcpus - c2->nr_vcpus;
-
- return c2->free_memkb - c1->free_memkb;
-}
-
-/* The actual automatic NUMA placement routine */
-static int numa_place_domain(libxl__gc *gc, uint32_t domid,
- libxl_domain_config *d_config)
-{
- libxl_domain_build_info *info = &d_config->b_info;
- int found;
- libxl__numa_candidate candidate;
- libxl_bitmap cpumap, cpupool_nodemap, *map;
- libxl_cpupoolinfo cpupool_info;
- int i, cpupool, rc = 0;
- uint64_t memkb;
-
- libxl__numa_candidate_init(&candidate);
- libxl_bitmap_init(&cpumap);
- libxl_bitmap_init(&cpupool_nodemap);
- libxl_cpupoolinfo_init(&cpupool_info);
-
- /*
- * Extract the cpumap from the cpupool the domain belong to. In fact,
- * it only makes sense to consider the cpus/nodes that are in there
- * for placement.
- */
- rc = cpupool = libxl__domain_cpupool(gc, domid);
- if (rc < 0)
- goto out;
- rc = libxl_cpupool_info(CTX, &cpupool_info, cpupool);
- if (rc)
- goto out;
- map = &cpupool_info.cpumap;
-
- /*
- * If there's a well defined hard affinity mask (i.e., the same one for all
- * the vcpus), we can try to run the placement considering only the pcpus
- * within such mask.
- */
- if (info->num_vcpu_hard_affinity)
- {
-#ifdef DEBUG
- int j;
-
- for (j = 0; j < info->num_vcpu_hard_affinity; j++)
- assert(libxl_bitmap_equal(&info->vcpu_hard_affinity[0],
- &info->vcpu_hard_affinity[j], 0));
-#endif /* DEBUG */
-
- rc = libxl_bitmap_and(CTX, &cpumap, &info->vcpu_hard_affinity[0],
- &cpupool_info.cpumap);
- if (rc)
- goto out;
-
- /* Hard affinity must contain at least one cpu of our cpupool */
- if (libxl_bitmap_is_empty(&cpumap)) {
- LOG(ERROR, "Hard affinity completely outside of domain's cpupool!");
- rc = ERROR_INVAL;
- goto out;
- }
- }
-
- rc = libxl__domain_need_memory_calculate(gc, info, &memkb);
- if (rc)
- goto out;
- if (libxl_node_bitmap_alloc(CTX, &cpupool_nodemap, 0)) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- /* Find the best candidate with enough free memory and at least
- * as much pcpus as the domain has vcpus. */
- rc = libxl__get_numa_candidate(gc, memkb, info->max_vcpus,
- 0, 0, map, numa_cmpf, &candidate, &found);
- if (rc)
- goto out;
-
- /* Not even a suitable placement candidate! Let's just don't touch the
- * domain's info->cpumap. It will have affinity with all nodes/cpus. */
- if (found == 0)
- goto out;
-
- /* Map the candidate's node map to the domain's info->nodemap */
- libxl__numa_candidate_get_nodemap(gc, &candidate, &info->nodemap);
-
- /* Avoid trying to set the affinity to nodes that might be in the
- * candidate's nodemap but out of our cpupool. */
- rc = libxl_cpumap_to_nodemap(CTX, &cpupool_info.cpumap,
- &cpupool_nodemap);
- if (rc)
- goto out;
-
- libxl_for_each_set_bit(i, info->nodemap) {
- if (!libxl_bitmap_test(&cpupool_nodemap, i))
- libxl_bitmap_reset(&info->nodemap, i);
- }
-
- LOG(DETAIL, "NUMA placement candidate with %d nodes, %d cpus and "
- "%"PRIu64" KB free selected", candidate.nr_nodes,
- candidate.nr_cpus, candidate.free_memkb / 1024);
-
- out:
- libxl__numa_candidate_dispose(&candidate);
- libxl_bitmap_dispose(&cpupool_nodemap);
- libxl_bitmap_dispose(&cpumap);
- libxl_cpupoolinfo_dispose(&cpupool_info);
- return rc;
-}
-
-int libxl__build_pre(libxl__gc *gc, uint32_t domid,
- libxl_domain_config *d_config, libxl__domain_build_state *state)
-{
- libxl_domain_build_info *const info = &d_config->b_info;
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char *xs_domid, *con_domid;
- int rc;
- uint64_t size;
-
- if (xc_domain_max_vcpus(ctx->xch, domid, info->max_vcpus) != 0) {
- LOG(ERROR, "Couldn't set max vcpu count");
- return ERROR_FAIL;
- }
-
- /*
- * Check if the domain has any CPU or node affinity already. If not, try
- * to build up the latter via automatic NUMA placement. In fact, in case
- * numa_place_domain() manage to find a placement, in info->nodemap is
- * updated accordingly; if it does not manage, info->nodemap is just left
- * alone. It is then the the subsequent call to
- * libxl_domain_set_nodeaffinity() that enacts the actual placement.
- *
- * As far as scheduling is concerned, we achieve NUMA-aware scheduling
- * by having the results of placement affect the soft affinity of all
- * the vcpus of the domain. Of course, we want that iff placement is
- * enabled and actually happens, so we only change info->cpumap_soft to
- * reflect the placement result if that is the case
- */
- if (libxl_defbool_val(info->numa_placement)) {
- if (info->cpumap.size || info->num_vcpu_soft_affinity)
- LOG(WARN, "Can't run NUMA placement, as a soft "
- "affinity has been specified explicitly");
- else if (info->nodemap.size)
- LOG(WARN, "Can't run NUMA placement, as the domain has "
- "NUMA node affinity set already");
- else {
- libxl_bitmap cpumap_soft;
-
- rc = libxl_node_bitmap_alloc(ctx, &info->nodemap, 0);
- if (rc)
- return rc;
- libxl_bitmap_set_any(&info->nodemap);
-
- rc = libxl_cpu_bitmap_alloc(ctx, &cpumap_soft, 0);
- if (rc)
- return rc;
-
- rc = numa_place_domain(gc, domid, d_config);
- if (rc) {
- libxl_bitmap_dispose(&cpumap_soft);
- return rc;
- }
-
- /*
- * All we need to do now is converting the result of automatic
- * placement from nodemap to cpumap, and then use such cpumap
- * as the soft affinity for all the vcpus of the domain.
- *
- * When calling libxl_set_vcpuaffinity_all(), it is ok to use
- * NULL as hard affinity, as we know we don't have one, or we
- * won't be here.
- */
- libxl_nodemap_to_cpumap(ctx, &info->nodemap, &cpumap_soft);
- libxl_set_vcpuaffinity_all(ctx, domid, info->max_vcpus,
- NULL, &cpumap_soft);
-
- libxl_bitmap_dispose(&cpumap_soft);
-
- /*
- * Placement has run, so avoid for it to be re-run, if this
- * same config we are using and building here is ever re-used.
- * This means that people re-using configs will get the same
- * results, consistently, across every re-use, which is what
- * we expect most people to want.
- */
- libxl_defbool_set(&info->numa_placement, false);
- }
- }
-
- if (info->nodemap.size)
- libxl_domain_set_nodeaffinity(ctx, domid, &info->nodemap);
-
- if (info->num_vcpu_hard_affinity || info->num_vcpu_soft_affinity) {
- libxl_bitmap *hard_affinity, *soft_affinity;
- int i, n_vcpus;
-
- n_vcpus = info->num_vcpu_hard_affinity > info->num_vcpu_soft_affinity ?
- info->num_vcpu_hard_affinity : info->num_vcpu_soft_affinity;
-
- for (i = 0; i < n_vcpus; i++) {
- /*
- * Prepare hard and soft affinity pointers in a way that allows
- * us to issue only one call to libxl_set_vcpuaffinity(), setting,
- * for each vcpu, both hard and soft affinity "atomically".
- */
- hard_affinity = NULL;
- if (info->num_vcpu_hard_affinity &&
- i < info->num_vcpu_hard_affinity)
- hard_affinity = &info->vcpu_hard_affinity[i];
-
- soft_affinity = NULL;
- if (info->num_vcpu_soft_affinity &&
- i < info->num_vcpu_soft_affinity)
- soft_affinity = &info->vcpu_soft_affinity[i];
-
- if (libxl_set_vcpuaffinity(ctx, domid, i,
- hard_affinity, soft_affinity)) {
- LOG(ERROR, "setting affinity failed on vcpu `%d'", i);
- return ERROR_FAIL;
- }
- }
- }
-
-
- rc = libxl__arch_extra_memory(gc, info, &size);
- if (rc < 0) {
- LOGE(ERROR, "Couldn't get arch extra constant memory size");
- return ERROR_FAIL;
- }
-
- if (xc_domain_setmaxmem(ctx->xch, domid, info->target_memkb + size) < 0) {
- LOGE(ERROR, "Couldn't set max memory");
- return ERROR_FAIL;
- }
-
- xs_domid = xs_read(ctx->xsh, XBT_NULL, "/tool/xenstored/domid", NULL);
- state->store_domid = xs_domid ? atoi(xs_domid) : 0;
- free(xs_domid);
-
- con_domid = xs_read(ctx->xsh, XBT_NULL, "/tool/xenconsoled/domid", NULL);
- state->console_domid = con_domid ? atoi(con_domid) : 0;
- free(con_domid);
-
- state->store_port = xc_evtchn_alloc_unbound(ctx->xch, domid, state->store_domid);
- state->console_port = xc_evtchn_alloc_unbound(ctx->xch, domid, state->console_domid);
-
- rc = libxl__arch_domain_create(gc, d_config, domid);
-
- /* Construct a CPUID policy, but only for brand new domains. Domains
- * being migrated-in/restored have CPUID handled during the
- * static_data_done() callback. */
- if (!state->restore)
- libxl__cpuid_legacy(ctx, domid, false, info);
-
- return rc;
-}
-
-static int set_vnuma_affinity(libxl__gc *gc, uint32_t domid,
- libxl_domain_build_info *info)
-{
- libxl_bitmap cpumap;
- libxl_vnode_info *v;
- unsigned int i, j;
- int rc = 0;
-
- libxl_bitmap_init(&cpumap);
-
- rc = libxl_cpu_bitmap_alloc(CTX, &cpumap, 0);
- if (rc) {
- LOG(ERROR, "Can't allocate nodemap");
- goto out;
- }
-
- /*
- * For each vcpu in each vnode, set its soft affinity to
- * the pcpus belonging to the pnode the vnode is on
- */
- for (i = 0; i < info->num_vnuma_nodes; i++) {
- v = &info->vnuma_nodes[i];
-
- rc = libxl_node_to_cpumap(CTX, v->pnode, &cpumap);
- if (rc) {
- LOG(ERROR, "Can't get cpumap for vnode %d", i);
- goto out;
- }
-
- libxl_for_each_set_bit(j, v->vcpus) {
- rc = libxl_set_vcpuaffinity(CTX, domid, j, NULL, &cpumap);
- if (rc) {
- LOG(ERROR, "Can't set cpu affinity for %d", j);
- goto out;
- }
- }
- }
-
-out:
- libxl_bitmap_dispose(&cpumap);
- return rc;
-}
-
-int libxl__build_post(libxl__gc *gc, uint32_t domid,
- libxl_domain_build_info *info,
- libxl__domain_build_state *state,
- char **vms_ents, char **local_ents)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char *dom_path, *vm_path;
- xs_transaction_t t;
- char **ents;
- int i, rc;
-
- if (info->num_vnuma_nodes && !info->num_vcpu_soft_affinity) {
- rc = set_vnuma_affinity(gc, domid, info);
- if (rc)
- return rc;
- }
-
- rc = libxl_domain_sched_params_set(CTX, domid, &info->sched_params);
- if (rc)
- return rc;
-
- if (info->type == LIBXL_DOMAIN_TYPE_HVM
- && !libxl_ms_vm_genid_is_zero(&info->u.hvm.ms_vm_genid)) {
- rc = libxl__ms_vm_genid_set(gc, domid,
- &info->u.hvm.ms_vm_genid);
- if (rc) {
- LOG(ERROR, "Failed to set VM Generation ID");
- return rc;
- }
- }
-
- ents = libxl__calloc(gc, 12 + (info->max_vcpus * 2) + 2, sizeof(char *));
- ents[0] = "memory/static-max";
- ents[1] = GCSPRINTF("%"PRId64, info->max_memkb);
- ents[2] = "memory/target";
- ents[3] = GCSPRINTF("%"PRId64, info->target_memkb -
- libxl__get_targetmem_fudge(gc, info));
- ents[4] = "memory/videoram";
- ents[5] = GCSPRINTF("%"PRId64, info->video_memkb);
- ents[6] = "domid";
- ents[7] = GCSPRINTF("%d", domid);
- ents[8] = "store/port";
- ents[9] = GCSPRINTF("%"PRIu32, state->store_port);
- ents[10] = "store/ring-ref";
- ents[11] = GCSPRINTF("%lu", state->store_mfn);
- for (i = 0; i < info->max_vcpus; i++) {
- ents[12+(i*2)] = GCSPRINTF("cpu/%d/availability", i);
- ents[12+(i*2)+1] = libxl_bitmap_test(&info->avail_vcpus, i)
- ? "online" : "offline";
- }
-
- dom_path = libxl__xs_get_dompath(gc, domid);
- if (!dom_path) {
- return ERROR_FAIL;
- }
-
- vm_path = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/vm", dom_path), NULL);
-retry_transaction:
- t = xs_transaction_start(ctx->xsh);
-
- libxl__xs_writev(gc, t, dom_path, ents);
- libxl__xs_writev(gc, t, dom_path, local_ents);
- libxl__xs_writev(gc, t, vm_path, vms_ents);
-
- if (!xs_transaction_end(ctx->xsh, t, 0))
- if (errno == EAGAIN)
- goto retry_transaction;
- xs_introduce_domain(ctx->xsh, domid, state->store_mfn, state->store_port);
- free(vm_path);
- return 0;
-}
-
-static int set_vnuma_info(libxl__gc *gc, uint32_t domid,
- const libxl_domain_build_info *info,
- const libxl__domain_build_state *state)
-{
- int rc = 0;
- unsigned int i, nr_vdistance;
- unsigned int *vcpu_to_vnode, *vnode_to_pnode, *vdistance = NULL;
-
- vcpu_to_vnode = libxl__calloc(gc, info->max_vcpus,
- sizeof(unsigned int));
- vnode_to_pnode = libxl__calloc(gc, info->num_vnuma_nodes,
- sizeof(unsigned int));
-
- nr_vdistance = info->num_vnuma_nodes * info->num_vnuma_nodes;
- vdistance = libxl__calloc(gc, nr_vdistance, sizeof(unsigned int));
-
- for (i = 0; i < info->num_vnuma_nodes; i++) {
- libxl_vnode_info *v = &info->vnuma_nodes[i];
- int j;
-
- /* vnode to pnode mapping */
- vnode_to_pnode[i] = v->pnode;
-
- /* vcpu to vnode mapping */
- libxl_for_each_set_bit(j, v->vcpus)
- vcpu_to_vnode[j] = i;
-
- /* node distances */
- assert(info->num_vnuma_nodes == v->num_distances);
- memcpy(vdistance + (i * info->num_vnuma_nodes),
- v->distances,
- v->num_distances * sizeof(unsigned int));
- }
-
- if (xc_domain_setvnuma(CTX->xch, domid, info->num_vnuma_nodes,
- state->num_vmemranges, info->max_vcpus,
- state->vmemranges, vdistance,
- vcpu_to_vnode, vnode_to_pnode) < 0) {
- LOGE(ERROR, "xc_domain_setvnuma failed");
- rc = ERROR_FAIL;
- }
-
- return rc;
-}
-
-static int libxl__build_dom(libxl__gc *gc, uint32_t domid,
- libxl_domain_config *d_config, libxl__domain_build_state *state,
- struct xc_dom_image *dom)
-{
- libxl_domain_build_info *const info = &d_config->b_info;
- uint64_t mem_kb;
- int ret;
-
- if ( (ret = xc_dom_boot_xen_init(dom, CTX->xch, domid)) != 0 ) {
- LOGE(ERROR, "xc_dom_boot_xen_init failed");
- goto out;
- }
-#ifdef GUEST_RAM_BASE
- if ( (ret = xc_dom_rambase_init(dom, GUEST_RAM_BASE)) != 0 ) {
- LOGE(ERROR, "xc_dom_rambase failed");
- goto out;
- }
-#endif
- if ( (ret = xc_dom_parse_image(dom)) != 0 ) {
- LOG(ERROR, "xc_dom_parse_image failed");
- goto out;
- }
- if ( (ret = libxl__arch_domain_init_hw_description(gc, info, state, dom)) != 0 ) {
- LOGE(ERROR, "libxl__arch_domain_init_hw_description failed");
- goto out;
- }
-
- mem_kb = dom->container_type == XC_DOM_HVM_CONTAINER ?
- (info->max_memkb - info->video_memkb) : info->target_memkb;
- if ( (ret = xc_dom_mem_init(dom, mem_kb / 1024)) != 0 ) {
- LOGE(ERROR, "xc_dom_mem_init failed");
- goto out;
- }
- if ( (ret = xc_dom_boot_mem_init(dom)) != 0 ) {
- LOGE(ERROR, "xc_dom_boot_mem_init failed");
- goto out;
- }
- if ( (ret = libxl__arch_domain_finalise_hw_description(gc, domid, d_config, dom)) != 0 ) {
- LOGE(ERROR, "libxl__arch_domain_finalise_hw_description failed");
- goto out;
- }
- if ( (ret = xc_dom_build_image(dom)) != 0 ) {
- LOGE(ERROR, "xc_dom_build_image failed");
- goto out;
- }
- if ( (ret = xc_dom_boot_image(dom)) != 0 ) {
- LOGE(ERROR, "xc_dom_boot_image failed");
- goto out;
- }
- if ( (ret = xc_dom_gnttab_init(dom)) != 0 ) {
- LOGE(ERROR, "xc_dom_gnttab_init failed");
- goto out;
- }
- if ((ret = libxl__arch_build_dom_finish(gc, info, dom, state)) != 0) {
- LOGE(ERROR, "libxl__arch_build_dom_finish failed");
- goto out;
- }
-
-out:
- return ret != 0 ? ERROR_FAIL : 0;
-}
-
-int libxl__build_pv(libxl__gc *gc, uint32_t domid,
- libxl_domain_config *d_config, libxl__domain_build_state *state)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- libxl_domain_build_info *const info = &d_config->b_info;
- struct xc_dom_image *dom;
- int ret;
- int flags = 0;
-
- xc_dom_loginit(ctx->xch);
-
- dom = xc_dom_allocate(ctx->xch, state->pv_cmdline, info->u.pv.features);
- if (!dom) {
- LOGE(ERROR, "xc_dom_allocate failed");
- return ERROR_FAIL;
- }
-
- dom->container_type = XC_DOM_PV_CONTAINER;
-
- LOG(DEBUG, "pv kernel mapped %d path %s", state->pv_kernel.mapped, state->pv_kernel.path);
-
- if (state->pv_kernel.mapped) {
- ret = xc_dom_kernel_mem(dom,
- state->pv_kernel.data,
- state->pv_kernel.size);
- if ( ret != 0) {
- LOGE(ERROR, "xc_dom_kernel_mem failed");
- goto out;
- }
- } else {
- ret = xc_dom_kernel_file(dom, state->pv_kernel.path);
- if ( ret != 0) {
- LOGE(ERROR, "xc_dom_kernel_file failed");
- goto out;
- }
- }
-
- if ( state->pv_ramdisk.path && strlen(state->pv_ramdisk.path) ) {
- if (state->pv_ramdisk.mapped) {
- if ( (ret = xc_dom_module_mem(dom, state->pv_ramdisk.data, state->pv_ramdisk.size, NULL)) != 0 ) {
- LOGE(ERROR, "xc_dom_ramdisk_mem failed");
- goto out;
- }
- } else {
- if ( (ret = xc_dom_module_file(dom, state->pv_ramdisk.path, NULL)) != 0 ) {
- LOGE(ERROR, "xc_dom_ramdisk_file failed");
- goto out;
- }
- }
- }
-
- dom->flags = flags;
- dom->console_evtchn = state->console_port;
- dom->console_domid = state->console_domid;
- dom->xenstore_evtchn = state->store_port;
- dom->xenstore_domid = state->store_domid;
- dom->claim_enabled = libxl_defbool_val(info->claim_mode);
- dom->max_vcpus = info->max_vcpus;
-
- if (info->num_vnuma_nodes != 0) {
- unsigned int i;
-
- ret = libxl__vnuma_build_vmemrange_pv(gc, domid, info, state);
- if (ret) {
- LOGE(ERROR, "cannot build vmemranges");
- goto out;
- }
- ret = libxl__vnuma_config_check(gc, info, state);
- if (ret) goto out;
-
- ret = set_vnuma_info(gc, domid, info, state);
- if (ret) goto out;
-
- dom->nr_vmemranges = state->num_vmemranges;
- dom->vmemranges = xc_dom_malloc(dom, sizeof(*dom->vmemranges) *
- dom->nr_vmemranges);
-
- for (i = 0; i < dom->nr_vmemranges; i++) {
- dom->vmemranges[i].start = state->vmemranges[i].start;
- dom->vmemranges[i].end = state->vmemranges[i].end;
- dom->vmemranges[i].flags = state->vmemranges[i].flags;
- dom->vmemranges[i].nid = state->vmemranges[i].nid;
- }
-
- dom->nr_vnodes = info->num_vnuma_nodes;
- dom->vnode_to_pnode = xc_dom_malloc(dom, sizeof(*dom->vnode_to_pnode) *
- dom->nr_vnodes);
- for (i = 0; i < info->num_vnuma_nodes; i++)
- dom->vnode_to_pnode[i] = info->vnuma_nodes[i].pnode;
- }
-
- ret = libxl__build_dom(gc, domid, d_config, state, dom);
- if (ret != 0)
- goto out;
-
- if (xc_dom_translated(dom)) {
- state->console_mfn = dom->console_pfn;
- state->store_mfn = dom->xenstore_pfn;
- state->vuart_gfn = dom->vuart_gfn;
- } else {
- state->console_mfn = xc_dom_p2m(dom, dom->console_pfn);
- state->store_mfn = xc_dom_p2m(dom, dom->xenstore_pfn);
- }
-
- ret = 0;
-out:
- xc_dom_release(dom);
- return ret == 0 ? 0 : ERROR_FAIL;
-}
-
-static int hvm_build_set_params(xc_interface *handle, uint32_t domid,
- libxl_domain_build_info *info,
- int store_evtchn, unsigned long *store_mfn,
- int console_evtchn, unsigned long *console_mfn,
- domid_t store_domid, domid_t console_domid)
-{
- struct hvm_info_table *va_hvm;
- uint8_t *va_map, sum;
- uint64_t str_mfn, cons_mfn;
- int i;
-
- if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
- va_map = xc_map_foreign_range(handle, domid,
- XC_PAGE_SIZE, PROT_READ | PROT_WRITE,
- HVM_INFO_PFN);
- if (va_map == NULL)
- return ERROR_FAIL;
-
- va_hvm = (struct hvm_info_table *)(va_map + HVM_INFO_OFFSET);
- va_hvm->apic_mode = libxl_defbool_val(info->apic);
- va_hvm->nr_vcpus = info->max_vcpus;
- memset(va_hvm->vcpu_online, 0, sizeof(va_hvm->vcpu_online));
- memcpy(va_hvm->vcpu_online, info->avail_vcpus.map, info->avail_vcpus.size);
- for (i = 0, sum = 0; i < va_hvm->length; i++)
- sum += ((uint8_t *) va_hvm)[i];
- va_hvm->checksum -= sum;
- munmap(va_map, XC_PAGE_SIZE);
- }
-
- xc_hvm_param_get(handle, domid, HVM_PARAM_STORE_PFN, &str_mfn);
- xc_hvm_param_get(handle, domid, HVM_PARAM_CONSOLE_PFN, &cons_mfn);
- xc_hvm_param_set(handle, domid, HVM_PARAM_STORE_EVTCHN, store_evtchn);
- xc_hvm_param_set(handle, domid, HVM_PARAM_CONSOLE_EVTCHN, console_evtchn);
-
- *store_mfn = str_mfn;
- *console_mfn = cons_mfn;
-
- return 0;
-}
-
-static int hvm_build_set_xs_values(libxl__gc *gc,
- uint32_t domid,
- struct xc_dom_image *dom,
- const libxl_domain_build_info *info)
-{
- char *path = NULL;
- int ret = 0;
-
- if (dom->smbios_module.guest_addr_out) {
- path = GCSPRINTF("/local/domain/%d/"HVM_XS_SMBIOS_PT_ADDRESS, domid);
-
- ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%"PRIx64,
- dom->smbios_module.guest_addr_out);
- if (ret)
- goto err;
-
- path = GCSPRINTF("/local/domain/%d/"HVM_XS_SMBIOS_PT_LENGTH, domid);
-
- ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%x",
- dom->smbios_module.length);
- if (ret)
- goto err;
- }
-
- /* Only one module can be passed. PVHv2 guests do not support this. */
- if (dom->acpi_modules[0].guest_addr_out &&
- info->type == LIBXL_DOMAIN_TYPE_HVM) {
- path = GCSPRINTF("/local/domain/%d/"HVM_XS_ACPI_PT_ADDRESS, domid);
-
- ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%"PRIx64,
- dom->acpi_modules[0].guest_addr_out);
- if (ret)
- goto err;
-
- path = GCSPRINTF("/local/domain/%d/"HVM_XS_ACPI_PT_LENGTH, domid);
-
- ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%x",
- dom->acpi_modules[0].length);
- if (ret)
- goto err;
- }
-
- return 0;
-
-err:
- LOG(ERROR, "failed to write firmware xenstore value, err: %d", ret);
- return ret;
-}
-
-static int libxl__load_hvm_firmware_module(libxl__gc *gc,
- const char *filename,
- const char *what,
- struct xc_hvm_firmware_module *m)
-{
- int datalen = 0;
- void *data = NULL;
- int r, rc;
-
- LOG(DEBUG, "Loading %s: %s", what, filename);
- r = libxl_read_file_contents(CTX, filename, &data, &datalen);
- if (r) {
- /*
- * Print a message only on ENOENT, other errors are logged by the
- * function libxl_read_file_contents().
- */
- if (r == ENOENT)
- LOGEV(ERROR, r, "failed to read %s file", what);
- rc = ERROR_FAIL;
- goto out;
- }
- libxl__ptr_add(gc, data);
- if (datalen) {
- /* Only accept non-empty files */
- m->data = data;
- m->length = datalen;
- } else {
- LOG(ERROR, "file %s for %s is empty", filename, what);
- rc = ERROR_INVAL;
- goto out;
- }
- rc = 0;
-out:
- return rc;
-}
-
-static int libxl__domain_firmware(libxl__gc *gc,
- libxl_domain_build_info *info,
- libxl__domain_build_state *state,
- struct xc_dom_image *dom)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- const char *firmware = NULL;
- int e, rc;
- int datalen = 0;
- void *data;
- const char *bios_filename = NULL;
-
- if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
- if (info->u.hvm.firmware) {
- firmware = info->u.hvm.firmware;
- } else {
- switch (info->device_model_version)
- {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- firmware = "hvmloader";
- break;
- default:
- LOG(ERROR, "invalid device model version %d",
- info->device_model_version);
- rc = ERROR_FAIL;
- goto out;
- }
- }
- }
-
- if (state->pv_kernel.path != NULL &&
- info->type == LIBXL_DOMAIN_TYPE_PVH) {
-
- if (state->shim_path) {
- rc = xc_dom_kernel_file(dom, state->shim_path);
- if (rc) {
- LOGE(ERROR, "xc_dom_kernel_file failed");
- goto out;
- }
-
- /* We've loaded the shim, so load the kernel as a secondary module */
- if (state->pv_kernel.mapped) {
- LOG(DEBUG, "xc_dom_module_mem, cmdline %s",
- state->pv_cmdline);
- rc = xc_dom_module_mem(dom, state->pv_kernel.data,
- state->pv_kernel.size, state->pv_cmdline);
- if (rc) {
- LOGE(ERROR, "xc_dom_kernel_mem failed");
- goto out;
- }
- } else {
- LOG(DEBUG, "xc_dom_module_file, path %s cmdline %s",
- state->pv_kernel.path, state->pv_cmdline);
- rc = xc_dom_module_file(dom, state->pv_kernel.path, state->pv_cmdline);
- if (rc) {
- LOGE(ERROR, "xc_dom_kernel_file failed");
- goto out;
- }
- }
- } else {
- /* No shim, so load the kernel directly */
- if (state->pv_kernel.mapped) {
- rc = xc_dom_kernel_mem(dom, state->pv_kernel.data,
- state->pv_kernel.size);
- if (rc) {
- LOGE(ERROR, "xc_dom_kernel_mem failed");
- goto out;
- }
- } else {
- rc = xc_dom_kernel_file(dom, state->pv_kernel.path);
- if (rc) {
- LOGE(ERROR, "xc_dom_kernel_file failed");
- goto out;
- }
- }
- }
-
- if (state->pv_ramdisk.path && strlen(state->pv_ramdisk.path)) {
- if (state->pv_ramdisk.mapped) {
- rc = xc_dom_module_mem(dom, state->pv_ramdisk.data,
- state->pv_ramdisk.size, NULL);
- if (rc) {
- LOGE(ERROR, "xc_dom_ramdisk_mem failed");
- goto out;
- }
- } else {
- rc = xc_dom_module_file(dom, state->pv_ramdisk.path, NULL);
- if (rc) {
- LOGE(ERROR, "xc_dom_ramdisk_file failed");
- goto out;
- }
- }
- }
- } else {
- /*
- * Only HVM guests should get here, PVH should always have a set
- * kernel at this point.
- */
- assert(info->type == LIBXL_DOMAIN_TYPE_HVM);
- rc = xc_dom_kernel_file(dom, libxl__abs_path(gc, firmware,
- libxl__xenfirmwaredir_path()));
- }
-
- if (rc != 0) {
- LOGE(ERROR, "xc_dom_{kernel_file/ramdisk_file} failed");
- goto out;
- }
-
- if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
- info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
- if (info->u.hvm.system_firmware) {
- bios_filename = info->u.hvm.system_firmware;
- } else {
- switch (info->u.hvm.bios) {
- case LIBXL_BIOS_TYPE_SEABIOS:
- bios_filename = libxl__seabios_path();
- break;
- case LIBXL_BIOS_TYPE_OVMF:
- bios_filename = libxl__ovmf_path();
- break;
- case LIBXL_BIOS_TYPE_ROMBIOS:
- default:
- abort();
- }
- }
- }
-
- if (bios_filename) {
- rc = libxl__load_hvm_firmware_module(gc, bios_filename, "BIOS",
- &dom->system_firmware_module);
- if (rc) goto out;
- }
-
- if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
- info->u.hvm.bios == LIBXL_BIOS_TYPE_ROMBIOS &&
- libxl__ipxe_path()) {
- const char *fp = libxl__ipxe_path();
- rc = xc_dom_module_file(dom, fp, "ipxe");
-
- if (rc) {
- LOGE(ERROR, "failed to load IPXE %s (%d)", fp, rc);
- rc = ERROR_FAIL;
- goto out;
- }
- }
-
- if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
- info->u.hvm.smbios_firmware) {
- data = NULL;
- e = libxl_read_file_contents(ctx, info->u.hvm.smbios_firmware,
- &data, &datalen);
- if (e) {
- LOGEV(ERROR, e, "failed to read SMBIOS firmware file %s",
- info->u.hvm.smbios_firmware);
- rc = ERROR_FAIL;
- goto out;
- }
- libxl__ptr_add(gc, data);
- if (datalen) {
- /* Only accept non-empty files */
- dom->smbios_module.data = data;
- dom->smbios_module.length = (uint32_t)datalen;
- }
- }
-
- if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
- info->u.hvm.acpi_firmware) {
- data = NULL;
- e = libxl_read_file_contents(ctx, info->u.hvm.acpi_firmware,
- &data, &datalen);
- if (e) {
- LOGEV(ERROR, e, "failed to read ACPI firmware file %s",
- info->u.hvm.acpi_firmware);
- rc = ERROR_FAIL;
- goto out;
- }
- libxl__ptr_add(gc, data);
- if (datalen) {
- /* Only accept a non-empty file */
- dom->acpi_modules[0].data = data;
- dom->acpi_modules[0].length = (uint32_t)datalen;
- }
- }
-
- return 0;
-out:
- assert(rc != 0);
- return rc;
-}
-
-int libxl__build_hvm(libxl__gc *gc, uint32_t domid,
- libxl_domain_config *d_config,
- libxl__domain_build_state *state)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- int rc;
- uint64_t mmio_start, lowmem_end, highmem_end, mem_size;
- libxl_domain_build_info *const info = &d_config->b_info;
- struct xc_dom_image *dom = NULL;
- bool device_model = info->type == LIBXL_DOMAIN_TYPE_HVM ? true : false;
-
- xc_dom_loginit(ctx->xch);
-
- /*
- * If PVH and we have a shim override, use the shim cmdline.
- * If PVH and no shim override, use the pv cmdline.
- * If not PVH, use info->cmdline.
- */
- dom = xc_dom_allocate(ctx->xch, info->type == LIBXL_DOMAIN_TYPE_PVH ?
- (state->shim_path ? state->shim_cmdline : state->pv_cmdline) :
- info->cmdline, NULL);
- if (!dom) {
- LOGE(ERROR, "xc_dom_allocate failed");
- rc = ERROR_NOMEM;
- goto out;
- }
-
- dom->container_type = XC_DOM_HVM_CONTAINER;
-
- /* The params from the configuration file are in Mb, which are then
- * multiplied by 1 Kb. This was then divided off when calling
- * the old xc_hvm_build_target_mem() which then turned them to bytes.
- * Do all this in one step here...
- */
- mem_size = (uint64_t)(info->max_memkb - info->video_memkb) << 10;
- dom->target_pages = (uint64_t)(info->target_memkb - info->video_memkb) >> 2;
- dom->claim_enabled = libxl_defbool_val(info->claim_mode);
- if (info->u.hvm.mmio_hole_memkb) {
- uint64_t max_ram_below_4g = (1ULL << 32) -
- (info->u.hvm.mmio_hole_memkb << 10);
-
- if (max_ram_below_4g < HVM_BELOW_4G_MMIO_START)
- dom->mmio_size = info->u.hvm.mmio_hole_memkb << 10;
- }
-
- rc = libxl__domain_firmware(gc, info, state, dom);
- if (rc != 0) {
- LOG(ERROR, "initializing domain firmware failed");
- goto out;
- }
-
- if (dom->target_pages == 0)
- dom->target_pages = mem_size >> XC_PAGE_SHIFT;
- if (dom->mmio_size == 0 && device_model)
- dom->mmio_size = HVM_BELOW_4G_MMIO_LENGTH;
- else if (dom->mmio_size == 0 && !device_model) {
-#if defined(__i386__) || defined(__x86_64__)
- /*
- * Make sure the local APIC page, the ACPI tables and the special pages
- * are inside the MMIO hole.
- */
- xen_paddr_t start =
- (X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES) <<
- XC_PAGE_SHIFT;
-
- start = min_t(xen_paddr_t, start, LAPIC_BASE_ADDRESS);
- start = min_t(xen_paddr_t, start, ACPI_INFO_PHYSICAL_ADDRESS);
- dom->mmio_size = GB(4) - start;
-#else
- assert(1);
-#endif
- }
- lowmem_end = mem_size;
- highmem_end = 0;
- mmio_start = (1ull << 32) - dom->mmio_size;
- if (lowmem_end > mmio_start)
- {
- highmem_end = (1ull << 32) + (lowmem_end - mmio_start);
- lowmem_end = mmio_start;
- }
- dom->lowmem_end = lowmem_end;
- dom->highmem_end = highmem_end;
- dom->mmio_start = mmio_start;
- dom->vga_hole_size = device_model ? LIBXL_VGA_HOLE_SIZE : 0;
- dom->device_model = device_model;
- dom->max_vcpus = info->max_vcpus;
- dom->console_domid = state->console_domid;
- dom->xenstore_domid = state->store_domid;
-
- rc = libxl__domain_device_construct_rdm(gc, d_config,
- info->u.hvm.rdm_mem_boundary_memkb*1024,
- dom);
- if (rc) {
- LOG(ERROR, "checking reserved device memory failed");
- goto out;
- }
-
- if (info->num_vnuma_nodes != 0) {
- int i;
-
- rc = libxl__vnuma_build_vmemrange_hvm(gc, domid, info, state, dom);
- if (rc != 0) {
- LOG(ERROR, "hvm build vmemranges failed");
- goto out;
- }
- rc = libxl__vnuma_config_check(gc, info, state);
- if (rc != 0) goto out;
- rc = set_vnuma_info(gc, domid, info, state);
- if (rc != 0) goto out;
-
- dom->nr_vmemranges = state->num_vmemranges;
- dom->vmemranges = libxl__malloc(gc, sizeof(*dom->vmemranges) *
- dom->nr_vmemranges);
-
- for (i = 0; i < dom->nr_vmemranges; i++) {
- dom->vmemranges[i].start = state->vmemranges[i].start;
- dom->vmemranges[i].end = state->vmemranges[i].end;
- dom->vmemranges[i].flags = state->vmemranges[i].flags;
- dom->vmemranges[i].nid = state->vmemranges[i].nid;
- }
-
- dom->nr_vnodes = info->num_vnuma_nodes;
- dom->vnode_to_pnode = libxl__malloc(gc, sizeof(*dom->vnode_to_pnode) *
- dom->nr_vnodes);
- for (i = 0; i < dom->nr_vnodes; i++)
- dom->vnode_to_pnode[i] = info->vnuma_nodes[i].pnode;
- }
-
- rc = libxl__build_dom(gc, domid, d_config, state, dom);
- if (rc != 0)
- goto out;
-
- rc = hvm_build_set_params(ctx->xch, domid, info, state->store_port,
- &state->store_mfn, state->console_port,
- &state->console_mfn, state->store_domid,
- state->console_domid);
- if (rc != 0) {
- LOG(ERROR, "hvm build set params failed");
- goto out;
- }
-
- rc = hvm_build_set_xs_values(gc, domid, dom, info);
- if (rc != 0) {
- LOG(ERROR, "hvm build set xenstore values failed");
- goto out;
- }
-
- xc_dom_release(dom);
- return 0;
-
-out:
- assert(rc != 0);
- if (dom != NULL) xc_dom_release(dom);
- return rc;
-}
-
-int libxl__qemu_traditional_cmd(libxl__gc *gc, uint32_t domid,
- const char *cmd)
-{
- char *path = NULL;
- uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
- path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/command");
- return libxl__xs_printf(gc, XBT_NULL, path, "%s", cmd);
-}
-
-/*==================== Miscellaneous ====================*/
-
-char *libxl__uuid2string(libxl__gc *gc, const libxl_uuid uuid)
-{
- return GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(uuid));
-}
-
-const char *libxl__userdata_path(libxl__gc *gc, uint32_t domid,
- const char *userdata_userid,
- const char *wh)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char *uuid_string, *path;
- libxl_dominfo info;
- int rc;
-
- libxl_dominfo_init(&info);
-
- rc = libxl_domain_info(ctx, &info, domid);
- if (rc) {
- LOGE(ERROR, "unable to find domain info for domain %"PRIu32, domid);
- path = NULL;
- goto out;
- }
- uuid_string = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid));
- path = GCSPRINTF(XEN_LIB_DIR "/userdata-%s.%u.%s.%s",
- wh, domid, uuid_string, userdata_userid);
-
- out:
- libxl_dominfo_dispose(&info);
- return path;
-}
-
-static int userdata_delete(libxl__gc *gc, const char *path)
-{
- int r;
- r = unlink(path);
- if (r) {
- LOGE(ERROR, "remove failed for %s", path);
- return errno;
- }
- return 0;
-}
-
-void libxl__userdata_destroyall(libxl__gc *gc, uint32_t domid)
-{
- const char *pattern;
- glob_t gl;
- int r, i;
-
- pattern = libxl__userdata_path(gc, domid, "*", "?");
- if (!pattern)
- goto out;
-
- gl.gl_pathc = 0;
- gl.gl_pathv = 0;
- gl.gl_offs = 0;
- r = glob(pattern, GLOB_ERR|GLOB_NOSORT|GLOB_MARK, 0, &gl);
- if (r == GLOB_NOMATCH)
- goto out;
- if (r)
- LOGE(ERROR, "glob failed for %s", pattern);
-
- /* Note: don't delete domain-userdata-lock, it will be handled by
- * unlock function.
- */
- for (i=0; i<gl.gl_pathc; i++) {
- if (!strstr(gl.gl_pathv[i], "domain-userdata-lock"))
- userdata_delete(gc, gl.gl_pathv[i]);
- }
- globfree(&gl);
-out:
- return;
-}
-
-int libxl__userdata_store(libxl__gc *gc, uint32_t domid,
- const char *userdata_userid,
- const uint8_t *data, int datalen)
-{
- const char *filename;
- const char *newfilename;
- int e, rc;
- int fd = -1;
-
- filename = libxl__userdata_path(gc, domid, userdata_userid, "d");
- if (!filename) {
- rc = ERROR_NOMEM;
- goto out;
- }
-
- if (!datalen) {
- rc = userdata_delete(gc, filename);
- goto out;
- }
-
- newfilename = libxl__userdata_path(gc, domid, userdata_userid, "n");
- if (!newfilename) {
- rc = ERROR_NOMEM;
- goto out;
- }
-
- rc = ERROR_FAIL;
-
- fd = open(newfilename, O_RDWR | O_CREAT | O_TRUNC, 0600);
- if (fd < 0)
- goto err;
-
- if (libxl_write_exactly(CTX, fd, data, datalen, "userdata", newfilename))
- goto err;
-
- if (close(fd) < 0) {
- fd = -1;
- goto err;
- }
- fd = -1;
-
- if (rename(newfilename, filename))
- goto err;
-
- rc = 0;
-
-err:
- if (fd >= 0) {
- e = errno;
- close(fd);
- errno = e;
- }
-
- if (rc)
- LOGE(ERROR, "cannot write/rename %s for %s", newfilename, filename);
-out:
- return rc;
-}
-
-int libxl_userdata_store(libxl_ctx *ctx, uint32_t domid,
- const char *userdata_userid,
- const uint8_t *data, int datalen)
-{
- GC_INIT(ctx);
- int rc;
- libxl__flock *lock;
-
- CTX_LOCK;
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- rc = libxl__userdata_store(gc, domid, userdata_userid,
- data, datalen);
-
- libxl__unlock_file(lock);
-
-out:
- CTX_UNLOCK;
- GC_FREE;
- return rc;
-}
-
-int libxl__userdata_retrieve(libxl__gc *gc, uint32_t domid,
- const char *userdata_userid,
- uint8_t **data_r, int *datalen_r)
-{
- const char *filename;
- int e, rc;
- int datalen = 0;
- void *data = 0;
-
- filename = libxl__userdata_path(gc, domid, userdata_userid, "d");
- if (!filename) {
- rc = ERROR_NOMEM;
- goto out;
- }
-
- e = libxl_read_file_contents(CTX, filename, data_r ? &data : 0, &datalen);
- if (e && errno != ENOENT) {
- rc = ERROR_FAIL;
- goto out;
- }
- if (!e && !datalen) {
- LOG(ERROR, "userdata file %s is empty", filename);
- if (data_r) assert(!*data_r);
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (data_r) *data_r = data;
- if (datalen_r) *datalen_r = datalen;
- rc = 0;
-
-out:
- return rc;
-}
-
-int libxl_userdata_retrieve(libxl_ctx *ctx, uint32_t domid,
- const char *userdata_userid,
- uint8_t **data_r, int *datalen_r)
-{
- GC_INIT(ctx);
- int rc;
- libxl__flock *lock;
-
- CTX_LOCK;
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- rc = libxl__userdata_retrieve(gc, domid, userdata_userid,
- data_r, datalen_r);
-
-
- libxl__unlock_file(lock);
-out:
- CTX_UNLOCK;
- GC_FREE;
- return rc;
-}
-
-int libxl_userdata_unlink(libxl_ctx *ctx, uint32_t domid,
- const char *userdata_userid)
-{
- GC_INIT(ctx);
- CTX_LOCK;
-
- int rc;
- libxl__flock *lock = NULL;
- const char *filename;
-
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- filename = libxl__userdata_path(gc, domid, userdata_userid, "d");
- if (!filename) {
- rc = ERROR_FAIL;
- goto out;
- }
- if (unlink(filename)) {
- LOGE(ERROR, "error deleting userdata file: %s", filename);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-out:
- if (lock)
- libxl__unlock_file(lock);
- CTX_UNLOCK;
- GC_FREE;
- return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#include <xen/errno.h>
-
-/*========================= Domain save ============================*/
-
-static void stream_done(libxl__egc *egc,
- libxl__stream_write_state *sws, int rc);
-static void domain_save_done(libxl__egc *egc,
- libxl__domain_save_state *dss, int rc);
-
-/*----- complicated callback, called by xc_domain_save -----*/
-
-/*
- * We implement the other end of protocol for controlling qemu-dm's
- * logdirty. There is no documentation for this protocol, but our
- * counterparty's implementation is in
- * qemu-xen-traditional.git:xenstore.c in the function
- * xenstore_process_logdirty_event
- */
-
-static void domain_suspend_switch_qemu_xen_traditional_logdirty
- (libxl__egc *egc, int domid, unsigned enable,
- libxl__logdirty_switch *lds);
-static void switch_logdirty_xswatch(libxl__egc *egc, libxl__ev_xswatch*,
- const char *watch_path, const char *event_path);
-static void domain_suspend_switch_qemu_xen_logdirty
- (libxl__egc *egc, int domid, unsigned enable,
- libxl__logdirty_switch *lds);
-static void switch_qemu_xen_logdirty_done(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *,
- int rc);
-static void switch_logdirty_timeout(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc);
-static void switch_logdirty_done(libxl__egc *egc,
- libxl__logdirty_switch *lds, int rc);
-
-void libxl__logdirty_init(libxl__logdirty_switch *lds)
-{
- lds->cmd_path = 0;
- libxl__ev_xswatch_init(&lds->watch);
- libxl__ev_time_init(&lds->timeout);
- libxl__ev_qmp_init(&lds->qmp);
-}
-
-void libxl__domain_common_switch_qemu_logdirty(libxl__egc *egc,
- int domid, unsigned enable,
- libxl__logdirty_switch *lds)
-{
- STATE_AO_GC(lds->ao);
-
- switch (libxl__device_model_version_running(gc, domid)) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- domain_suspend_switch_qemu_xen_traditional_logdirty(egc, domid, enable,
- lds);
- break;
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- domain_suspend_switch_qemu_xen_logdirty(egc, domid, enable, lds);
- break;
- default:
- LOGD(ERROR, domid, "logdirty switch failed"
- ", no valid device model version found, abandoning suspend");
- lds->callback(egc, lds, ERROR_FAIL);
- }
-}
-
-static void domain_suspend_switch_qemu_xen_traditional_logdirty
- (libxl__egc *egc, int domid, unsigned enable,
- libxl__logdirty_switch *lds)
-{
- STATE_AO_GC(lds->ao);
- int rc;
- xs_transaction_t t = 0;
- const char *got;
-
- if (!lds->cmd_path) {
- uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
- lds->cmd_path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid,
- "/logdirty/cmd");
- lds->ret_path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid,
- "/logdirty/ret");
- }
- lds->cmd = enable ? "enable" : "disable";
-
- rc = libxl__ev_xswatch_register(gc, &lds->watch,
- switch_logdirty_xswatch, lds->ret_path);
- if (rc) goto out;
-
- rc = libxl__ev_time_register_rel(ao, &lds->timeout,
- switch_logdirty_timeout, 10*1000);
- if (rc) goto out;
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
-
- rc = libxl__xs_read_checked(gc, t, lds->cmd_path, &got);
- if (rc) goto out;
-
- if (got) {
- const char *got_ret;
- rc = libxl__xs_read_checked(gc, t, lds->ret_path, &got_ret);
- if (rc) goto out;
-
- if (!got_ret || strcmp(got, got_ret)) {
- LOGD(ERROR, domid, "controlling logdirty: qemu was already sent"
- " command `%s' (xenstore path `%s') but result is `%s'",
- got, lds->cmd_path, got_ret ? got_ret : "<none>");
- rc = ERROR_FAIL;
- goto out;
- }
- rc = libxl__xs_rm_checked(gc, t, lds->cmd_path);
- if (rc) goto out;
- }
-
- rc = libxl__xs_rm_checked(gc, t, lds->ret_path);
- if (rc) goto out;
-
- rc = libxl__xs_write_checked(gc, t, lds->cmd_path, lds->cmd);
- if (rc) goto out;
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc<0) goto out;
- }
-
- /* OK, wait for some callback */
- return;
-
- out:
- LOGD(ERROR, domid, "logdirty switch failed (rc=%d), abandoning suspend",rc);
- libxl__xs_transaction_abort(gc, &t);
- switch_logdirty_done(egc,lds,rc);
-}
-
-static void switch_logdirty_xswatch(libxl__egc *egc, libxl__ev_xswatch *watch,
- const char *watch_path, const char *event_path)
-{
- libxl__logdirty_switch *lds = CONTAINER_OF(watch, *lds, watch);
- STATE_AO_GC(lds->ao);
- const char *got;
- xs_transaction_t t = 0;
- int rc;
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
-
- rc = libxl__xs_read_checked(gc, t, lds->ret_path, &got);
- if (rc) goto out;
-
- if (!got) {
- rc = +1;
- goto out;
- }
-
- if (strcmp(got, lds->cmd)) {
- LOG(ERROR,"logdirty switch: sent command `%s' but got reply `%s'"
- " (xenstore paths `%s' / `%s')", lds->cmd, got,
- lds->cmd_path, lds->ret_path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__xs_rm_checked(gc, t, lds->cmd_path);
- if (rc) goto out;
-
- rc = libxl__xs_rm_checked(gc, t, lds->ret_path);
- if (rc) goto out;
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc<0) goto out;
- }
-
- out:
- /* rc < 0: error
- * rc == 0: ok, we are done
- * rc == +1: need to keep waiting
- */
- libxl__xs_transaction_abort(gc, &t);
-
- if (rc <= 0) {
- if (rc < 0)
- LOG(ERROR,"logdirty switch: failed (rc=%d)",rc);
- switch_logdirty_done(egc,lds,rc);
- }
-}
-
-static void domain_suspend_switch_qemu_xen_logdirty
- (libxl__egc *egc, int domid, unsigned enable,
- libxl__logdirty_switch *lds)
-{
- STATE_AO_GC(lds->ao);
- int rc;
- libxl__json_object *args = NULL;
-
- /* Convenience aliases. */
- libxl__ev_qmp *const qmp = &lds->qmp;
-
- rc = libxl__ev_time_register_rel(ao, &lds->timeout,
- switch_logdirty_timeout, 10 * 1000);
- if (rc) goto out;
-
- qmp->ao = ao;
- qmp->domid = domid;
- qmp->payload_fd = -1;
- qmp->callback = switch_qemu_xen_logdirty_done;
- libxl__qmp_param_add_bool(gc, &args, "enable", enable);
- rc = libxl__ev_qmp_send(egc, qmp, "xen-set-global-dirty-log", args);
- if (rc) goto out;
-
- return;
-out:
- switch_qemu_xen_logdirty_done(egc, qmp, NULL, rc);
-}
-
-static void switch_qemu_xen_logdirty_done(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *r,
- int rc)
-{
- EGC_GC;
- libxl__logdirty_switch *lds = CONTAINER_OF(qmp, *lds, qmp);
-
- if (rc)
- LOGD(ERROR, qmp->domid,
- "logdirty switch failed (rc=%d), abandoning suspend",rc);
- switch_logdirty_done(egc, lds, rc);
-}
-
-static void switch_logdirty_timeout(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- libxl__logdirty_switch *lds = CONTAINER_OF(ev, *lds, timeout);
- STATE_AO_GC(lds->ao);
- LOG(ERROR,"logdirty switch: wait for device model timed out");
- switch_logdirty_done(egc,lds,ERROR_FAIL);
-}
-
-static void switch_logdirty_done(libxl__egc *egc,
- libxl__logdirty_switch *lds,
- int rc)
-{
- STATE_AO_GC(lds->ao);
-
- libxl__ev_xswatch_deregister(gc, &lds->watch);
- libxl__ev_time_deregister(gc, &lds->timeout);
- libxl__ev_qmp_dispose(gc, &lds->qmp);
-
- lds->callback(egc, lds, rc);
-}
-
-static void domain_suspend_switch_qemu_logdirty_done
- (libxl__egc *egc, libxl__logdirty_switch *lds, int rc);
-
-void libxl__domain_suspend_common_switch_qemu_logdirty
- (uint32_t domid, unsigned enable, void *user)
-{
- libxl__save_helper_state *shs = user;
- libxl__egc *egc = shs->egc;
- libxl__domain_save_state *dss = shs->caller_state;
-
- /* Convenience aliases. */
- libxl__logdirty_switch *const lds = &dss->logdirty;
-
- if (dss->type == LIBXL_DOMAIN_TYPE_PVH) {
- domain_suspend_switch_qemu_logdirty_done(egc, lds, 0);
- return;
- }
-
- lds->callback = domain_suspend_switch_qemu_logdirty_done;
- libxl__domain_common_switch_qemu_logdirty(egc, domid, enable, lds);
-}
-
-static void domain_suspend_switch_qemu_logdirty_done
- (libxl__egc *egc, libxl__logdirty_switch *lds, int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(lds, *dss, logdirty);
-
- if (rc) {
- dss->rc = rc;
- libxl__xc_domain_saverestore_async_callback_done(egc,
- &dss->sws.shs, -1);
- } else
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-/*----- callbacks, called by xc_domain_save -----*/
-
-/*
- * Expand the buffer 'buf' of length 'len', to append 'str' including its NUL
- * terminator.
- */
-static void append_string(libxl__gc *gc, char **buf, uint32_t *len,
- const char *str)
-{
- size_t extralen = strlen(str) + 1;
- char *new = libxl__realloc(gc, *buf, *len + extralen);
-
- *buf = new;
- memcpy(new + *len, str, extralen);
- *len += extralen;
-}
-
-int libxl__save_emulator_xenstore_data(libxl__domain_save_state *dss,
- char **callee_buf,
- uint32_t *callee_len)
-{
- STATE_AO_GC(dss->ao);
- const char *xs_root;
- char **entries, *buf = NULL;
- unsigned int nr_entries, i, j, len = 0;
- int rc;
-
- const uint32_t domid = dss->domid;
- const uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
-
- xs_root = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "");
-
- entries = libxl__xs_directory(gc, 0, GCSPRINTF("%s/physmap", xs_root),
- &nr_entries);
- if (!entries || nr_entries == 0) { rc = 0; goto out; }
-
- for (i = 0; i < nr_entries; ++i) {
- static const char *const physmap_subkeys[] = {
- "start_addr", "size", "name"
- };
-
- for (j = 0; j < ARRAY_SIZE(physmap_subkeys); ++j) {
- const char *key = GCSPRINTF("physmap/%s/%s",
- entries[i], physmap_subkeys[j]);
-
- const char *val =
- libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/%s", xs_root, key));
-
- if (!val) { rc = ERROR_FAIL; goto out; }
-
- append_string(gc, &buf, &len, key);
- append_string(gc, &buf, &len, val);
- }
- }
-
- rc = 0;
-
- out:
- if (!rc) {
- *callee_buf = buf;
- *callee_len = len;
- }
-
- return rc;
-}
-
-/*----- main code for saving, in order of execution -----*/
-
-void libxl__domain_save(libxl__egc *egc, libxl__domain_save_state *dss)
-{
- STATE_AO_GC(dss->ao);
- int rc, ret;
-
- /* Convenience aliases */
- const uint32_t domid = dss->domid;
- const libxl_domain_type type = dss->type;
- const int live = dss->live;
- const int debug = dss->debug;
- const libxl_domain_remus_info *const r_info = dss->remus;
- libxl__srm_save_autogen_callbacks *const callbacks =
- &dss->sws.shs.callbacks.save.a;
- unsigned int nr_vnodes = 0, nr_vmemranges = 0, nr_vcpus = 0;
- libxl__domain_suspend_state *dsps = &dss->dsps;
-
- if (dss->checkpointed_stream != LIBXL_CHECKPOINTED_STREAM_NONE && !r_info) {
- LOGD(ERROR, domid, "Migration stream is checkpointed, but there's no "
- "checkpoint info!");
- rc = ERROR_INVAL;
- goto out;
- }
-
- dss->rc = 0;
- libxl__logdirty_init(&dss->logdirty);
- dss->logdirty.ao = ao;
-
- dsps->ao = ao;
- dsps->domid = domid;
- dsps->live = !!live;
- rc = libxl__domain_suspend_init(egc, dsps, type);
- if (rc) goto out;
-
- dss->xcflags = (live ? XCFLAGS_LIVE : 0)
- | (debug ? XCFLAGS_DEBUG : 0);
-
- /* Disallow saving a guest with vNUMA configured because migration
- * stream does not preserve node information.
- *
- * Reject any domain which has vnuma enabled, even if the
- * configuration is empty. Only domains which have no vnuma
- * configuration at all are supported.
- */
- ret = xc_domain_getvnuma(CTX->xch, domid, &nr_vnodes, &nr_vmemranges,
- &nr_vcpus, NULL, NULL, NULL);
- if (ret != -1 || errno != EOPNOTSUPP) {
- LOGD(ERROR, domid, "Cannot save a guest with vNUMA configured");
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (dss->checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_NONE)
- callbacks->suspend = libxl__domain_suspend_callback;
-
- callbacks->switch_qemu_logdirty = libxl__domain_suspend_common_switch_qemu_logdirty;
-
- dss->sws.ao = dss->ao;
- dss->sws.dss = dss;
- dss->sws.fd = dss->fd;
- dss->sws.back_channel = false;
- dss->sws.completion_callback = stream_done;
-
- libxl__stream_write_start(egc, &dss->sws);
- return;
-
- out:
- domain_save_done(egc, dss, rc);
-}
-
-static void stream_done(libxl__egc *egc,
- libxl__stream_write_state *sws, int rc)
-{
- domain_save_done(egc, sws->dss, rc);
-}
-
-static void domain_save_done(libxl__egc *egc,
- libxl__domain_save_state *dss, int rc)
-{
- STATE_AO_GC(dss->ao);
-
- /* Convenience aliases */
- const uint32_t domid = dss->domid;
- libxl__domain_suspend_state *dsps = &dss->dsps;
-
- libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
-
- if (dsps->guest_evtchn.port > 0)
- xc_suspend_evtchn_release(CTX->xch, CTX->xce, domid,
- dsps->guest_evtchn.port, &dsps->guest_evtchn_lockfd);
-
- if (dss->remus) {
- /*
- * With Remus/COLO, if we reach this point, it means either
- * backup died or some network error occurred preventing us
- * from sending checkpoints. Teardown the network buffers and
- * release netlink resources. This is an async op.
- */
- if (libxl_defbool_val(dss->remus->colo))
- libxl__colo_save_teardown(egc, &dss->css, rc);
- else
- libxl__remus_teardown(egc, &dss->rs, rc);
- return;
- }
-
- dss->callback(egc, dss, rc);
-}
-
-/*========================= Domain restore ============================*/
-
-/*
- * Inspect the buffer between start and end, and return a pointer to the
- * character following the NUL terminator of start, or NULL if start is not
- * terminated before end.
- */
-static const char *next_string(const char *start, const char *end)
-{
- if (start >= end) return NULL;
-
- size_t total_len = end - start;
- size_t len = strnlen(start, total_len);
-
- if (len == total_len)
- return NULL;
- else
- return start + len + 1;
-}
-
-int libxl__restore_emulator_xenstore_data(libxl__domain_create_state *dcs,
- const char *ptr, uint32_t size)
-{
- STATE_AO_GC(dcs->ao);
- const char *next = ptr, *end = ptr + size, *key, *val;
- int rc;
-
- const uint32_t domid = dcs->guest_domid;
- const uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
- const char *xs_root = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "");
-
- while (next < end) {
- key = next;
- next = next_string(next, end);
-
- /* Sanitise 'key'. */
- if (!next) {
- rc = ERROR_FAIL;
- LOGD(ERROR, domid, "Key in xenstore data not NUL terminated");
- goto out;
- }
- if (key[0] == '\0') {
- rc = ERROR_FAIL;
- LOGD(ERROR, domid, "empty key found in xenstore data");
- goto out;
- }
- if (key[0] == '/') {
- rc = ERROR_FAIL;
- LOGD(ERROR, domid, "Key in xenstore data not relative");
- goto out;
- }
-
- val = next;
- next = next_string(next, end);
-
- /* Sanitise 'val'. */
- if (!next) {
- rc = ERROR_FAIL;
- LOGD(ERROR, domid, "Val in xenstore data not NUL terminated");
- goto out;
- }
-
- libxl__xs_printf(gc, XBT_NULL,
- GCSPRINTF("%s/%s", xs_root, key),
- "%s", val);
- }
-
- rc = 0;
-
- out:
- return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*====================== Domain suspend =======================*/
-
-int libxl__domain_suspend_init(libxl__egc *egc,
- libxl__domain_suspend_state *dsps,
- libxl_domain_type type)
-{
- STATE_AO_GC(dsps->ao);
- int rc = ERROR_FAIL;
- int port;
-
- /* Convenience aliases */
- const uint32_t domid = dsps->domid;
-
- libxl__xswait_init(&dsps->pvcontrol);
- libxl__ev_evtchn_init(&dsps->guest_evtchn);
- libxl__ev_xswatch_init(&dsps->guest_watch);
- libxl__ev_time_init(&dsps->guest_timeout);
- libxl__ev_qmp_init(&dsps->qmp);
-
- if (type == LIBXL_DOMAIN_TYPE_INVALID) goto out;
- dsps->type = type;
-
- dsps->guest_evtchn.port = -1;
- dsps->guest_evtchn_lockfd = -1;
- dsps->guest_responded = 0;
- dsps->dm_savefile = libxl__device_model_savefile(gc, domid);
-
- port = xs_suspend_evtchn_port(domid);
-
- if (port >= 0) {
- rc = libxl__ctx_evtchn_init(gc);
- if (rc) goto out;
-
- dsps->guest_evtchn.port =
- xc_suspend_evtchn_init_exclusive(CTX->xch, CTX->xce,
- domid, port, &dsps->guest_evtchn_lockfd);
-
- if (dsps->guest_evtchn.port < 0) {
- LOGD(WARN, domid, "Suspend event channel initialization failed");
- rc = ERROR_FAIL;
- goto out;
- }
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-/*----- callbacks, called by xc_domain_save -----*/
-
-void libxl__domain_suspend_device_model(libxl__egc *egc,
- libxl__domain_suspend_state *dsps)
-{
- STATE_AO_GC(dsps->ao);
- int rc = 0;
- uint32_t const domid = dsps->domid;
- const char *const filename = dsps->dm_savefile;
-
- switch (libxl__device_model_version_running(gc, domid)) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
- LOGD(DEBUG, domid, "Saving device model state to %s", filename);
- libxl__qemu_traditional_cmd(gc, domid, "save");
- libxl__wait_for_device_model_deprecated(gc, domid, "paused", NULL, NULL, NULL);
- break;
- }
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- /* calls dsps->callback_device_model_done when done */
- libxl__qmp_suspend_save(egc, dsps); /* must be last */
- return;
- default:
- rc = ERROR_INVAL;
- goto out;
- }
-
-out:
- if (rc)
- LOGD(ERROR, dsps->domid,
- "failed to suspend device model, rc=%d", rc);
- dsps->callback_device_model_done(egc, dsps, rc); /* must be last */
-}
-
-static void domain_suspend_common_wait_guest(libxl__egc *egc,
- libxl__domain_suspend_state *dsps);
-static void domain_suspend_common_guest_suspended(libxl__egc *egc,
- libxl__domain_suspend_state *dsps);
-
-static void domain_suspend_common_pvcontrol_suspending(libxl__egc *egc,
- libxl__xswait_state *xswa, int rc, const char *state);
-static void domain_suspend_common_wait_guest_evtchn(libxl__egc *egc,
- libxl__ev_evtchn *evev);
-static void suspend_common_wait_guest_watch(libxl__egc *egc,
- libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path);
-static void suspend_common_wait_guest_check(libxl__egc *egc,
- libxl__domain_suspend_state *dsps);
-static void suspend_common_wait_guest_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-
-static void domain_suspend_common_done(libxl__egc *egc,
- libxl__domain_suspend_state *dsps,
- int rc);
-
-static void domain_suspend_callback_common(libxl__egc *egc,
- libxl__domain_suspend_state *dsps);
-static void domain_suspend_callback_common_done(libxl__egc *egc,
- libxl__domain_suspend_state *dsps, int rc);
-
-/* calls dsps->callback_common_done when done */
-void libxl__domain_suspend(libxl__egc *egc,
- libxl__domain_suspend_state *dsps)
-{
- domain_suspend_callback_common(egc, dsps);
-}
-
-static bool domain_suspend_pvcontrol_acked(const char *state) {
- /* any value other than "suspend", including ENOENT (i.e. !state), is OK */
- if (!state) return 1;
- return strcmp(state,"suspend");
-}
-
-/* calls dsps->callback_common_done when done */
-static void domain_suspend_callback_common(libxl__egc *egc,
- libxl__domain_suspend_state *dsps)
-{
- STATE_AO_GC(dsps->ao);
- uint64_t hvm_s_state = 0, hvm_pvdrv = 0;
- int ret, rc;
-
- /* Convenience aliases */
- const uint32_t domid = dsps->domid;
-
- if (dsps->type != LIBXL_DOMAIN_TYPE_PV) {
- xc_hvm_param_get(CTX->xch, domid, HVM_PARAM_CALLBACK_IRQ, &hvm_pvdrv);
- xc_hvm_param_get(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, &hvm_s_state);
- }
-
- if ((hvm_s_state == 0) && (dsps->guest_evtchn.port >= 0)) {
- LOGD(DEBUG, domid, "issuing %s suspend request via event channel",
- dsps->type != LIBXL_DOMAIN_TYPE_PV ? "PVH/HVM" : "PV");
- ret = xenevtchn_notify(CTX->xce, dsps->guest_evtchn.port);
- if (ret < 0) {
- LOGD(ERROR, domid, "xenevtchn_notify failed ret=%d", ret);
- rc = ERROR_FAIL;
- goto err;
- }
-
- dsps->guest_evtchn.callback = domain_suspend_common_wait_guest_evtchn;
- rc = libxl__ev_evtchn_wait(gc, &dsps->guest_evtchn);
- if (rc) goto err;
-
- rc = libxl__ev_time_register_rel(ao, &dsps->guest_timeout,
- suspend_common_wait_guest_timeout,
- 60*1000);
- if (rc) goto err;
-
- return;
- }
-
- if (dsps->type == LIBXL_DOMAIN_TYPE_HVM && (!hvm_pvdrv || hvm_s_state)) {
- LOGD(DEBUG, domid, "Calling xc_domain_shutdown on HVM domain");
- ret = xc_domain_shutdown(CTX->xch, domid, SHUTDOWN_suspend);
- if (ret < 0) {
- LOGED(ERROR, domid, "xc_domain_shutdown failed");
- rc = ERROR_FAIL;
- goto err;
- }
- /* The guest does not (need to) respond to this sort of request. */
- dsps->guest_responded = 1;
- domain_suspend_common_wait_guest(egc, dsps);
- return;
- }
-
- LOGD(DEBUG, domid, "issuing %s suspend request via XenBus control node",
- dsps->type != LIBXL_DOMAIN_TYPE_PV ? "PVH/HVM" : "PV");
-
- dsps->pvcontrol.ao = ao;
- dsps->pvcontrol.callback = domain_suspend_common_pvcontrol_suspending;
- rc = libxl__domain_pvcontrol(egc, &dsps->pvcontrol, domid, "suspend");
- if (rc) goto err;
-
- return;
-
- err:
- domain_suspend_common_done(egc, dsps, rc);
-}
-
-static void domain_suspend_common_wait_guest_evtchn(libxl__egc *egc,
- libxl__ev_evtchn *evev)
-{
- libxl__domain_suspend_state *dsps = CONTAINER_OF(evev, *dsps, guest_evtchn);
- STATE_AO_GC(dsps->ao);
- /* If we should be done waiting, suspend_common_wait_guest_check
- * will end up calling domain_suspend_common_guest_suspended or
- * domain_suspend_common_done, both of which cancel the evtchn
- * wait as needed. So re-enable it now. */
- libxl__ev_evtchn_wait(gc, &dsps->guest_evtchn);
- suspend_common_wait_guest_check(egc, dsps);
-}
-
-static void domain_suspend_common_pvcontrol_suspending(libxl__egc *egc,
- libxl__xswait_state *xswa, int rc, const char *state)
-{
- libxl__domain_suspend_state *dsps = CONTAINER_OF(xswa, *dsps, pvcontrol);
- STATE_AO_GC(dsps->ao);
- xs_transaction_t t = 0;
-
- if (!rc && !domain_suspend_pvcontrol_acked(state))
- /* keep waiting */
- return;
-
- libxl__xswait_stop(gc, &dsps->pvcontrol);
-
- if (rc == ERROR_TIMEDOUT) {
- /*
- * Guest appears to not be responding. Cancel the suspend
- * request.
- *
- * We re-read the suspend node and clear it within a
- * transaction in order to handle the case where we race
- * against the guest catching up and acknowledging the request
- * at the last minute.
- */
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto err;
-
- rc = libxl__xs_read_checked(gc, t, xswa->path, &state);
- if (rc) goto err;
-
- if (domain_suspend_pvcontrol_acked(state))
- /* last minute ack */
- break;
-
- rc = libxl__xs_write_checked(gc, t, xswa->path, "");
- if (rc) goto err;
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) {
- LOGD(ERROR, dsps->domid,
- "guest didn't acknowledge suspend, cancelling request");
- goto err;
- }
- if (rc<0) goto err;
- }
- } else if (rc) {
- /* some error in xswait's read of xenstore, already logged */
- goto err;
- }
-
- assert(domain_suspend_pvcontrol_acked(state));
- LOGD(DEBUG, dsps->domid, "guest acknowledged suspend request");
-
- libxl__xs_transaction_abort(gc, &t);
- dsps->guest_responded = 1;
- domain_suspend_common_wait_guest(egc,dsps);
- return;
-
- err:
- libxl__xs_transaction_abort(gc, &t);
- domain_suspend_common_done(egc, dsps, rc);
- return;
-}
-
-static void domain_suspend_common_wait_guest(libxl__egc *egc,
- libxl__domain_suspend_state *dsps)
-{
- STATE_AO_GC(dsps->ao);
- int rc;
-
- LOGD(DEBUG, dsps->domid, "wait for the guest to suspend");
-
- rc = libxl__ev_xswatch_register(gc, &dsps->guest_watch,
- suspend_common_wait_guest_watch,
- "@releaseDomain");
- if (rc) goto err;
-
- rc = libxl__ev_time_register_rel(ao, &dsps->guest_timeout,
- suspend_common_wait_guest_timeout,
- 60*1000);
- if (rc) goto err;
- return;
-
- err:
- domain_suspend_common_done(egc, dsps, rc);
-}
-
-static void suspend_common_wait_guest_watch(libxl__egc *egc,
- libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path)
-{
- libxl__domain_suspend_state *dsps = CONTAINER_OF(xsw, *dsps, guest_watch);
- suspend_common_wait_guest_check(egc, dsps);
-}
-
-static void suspend_common_wait_guest_check(libxl__egc *egc,
- libxl__domain_suspend_state *dsps)
-{
- STATE_AO_GC(dsps->ao);
- xc_domaininfo_t info;
- int ret;
- int shutdown_reason;
-
- /* Convenience aliases */
- const uint32_t domid = dsps->domid;
-
- ret = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
- if (ret < 0) {
- LOGED(ERROR, domid, "unable to check for status of guest");
- goto err;
- }
-
- if (!(ret == 1 && info.domain == domid)) {
- LOGED(ERROR, domid, "guest we were suspending has been destroyed");
- goto err;
- }
-
- if (!(info.flags & XEN_DOMINF_shutdown))
- /* keep waiting */
- return;
-
- shutdown_reason = (info.flags >> XEN_DOMINF_shutdownshift)
- & XEN_DOMINF_shutdownmask;
- if (shutdown_reason != SHUTDOWN_suspend) {
- LOGD(DEBUG, domid, "guest we were suspending has shut down"
- " with unexpected reason code %d", shutdown_reason);
- goto err;
- }
-
- LOGD(DEBUG, domid, "guest has suspended");
- domain_suspend_common_guest_suspended(egc, dsps);
- return;
-
- err:
- domain_suspend_common_done(egc, dsps, ERROR_FAIL);
-}
-
-static void suspend_common_wait_guest_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
-{
- libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, guest_timeout);
- STATE_AO_GC(dsps->ao);
- if (rc == ERROR_TIMEDOUT) {
- LOGD(ERROR, dsps->domid, "guest did not suspend, timed out");
- rc = ERROR_GUEST_TIMEDOUT;
- }
- domain_suspend_common_done(egc, dsps, rc);
-}
-
-static void domain_suspend_common_guest_suspended(libxl__egc *egc,
- libxl__domain_suspend_state *dsps)
-{
- STATE_AO_GC(dsps->ao);
-
- libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
- libxl__ev_xswatch_deregister(gc, &dsps->guest_watch);
- libxl__ev_time_deregister(gc, &dsps->guest_timeout);
-
- if (dsps->type == LIBXL_DOMAIN_TYPE_HVM) {
- dsps->callback_device_model_done = domain_suspend_common_done;
- libxl__domain_suspend_device_model(egc, dsps); /* must be last */
- return;
- }
- domain_suspend_common_done(egc, dsps, 0);
-}
-
-static void domain_suspend_common_done(libxl__egc *egc,
- libxl__domain_suspend_state *dsps,
- int rc)
-{
- EGC_GC;
- assert(!libxl__xswait_inuse(&dsps->pvcontrol));
- libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
- libxl__ev_xswatch_deregister(gc, &dsps->guest_watch);
- libxl__ev_time_deregister(gc, &dsps->guest_timeout);
- libxl__ev_qmp_dispose(gc, &dsps->qmp);
- dsps->callback_common_done(egc, dsps, rc);
-}
-
-void libxl__domain_suspend_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__egc *egc = shs->egc;
- libxl__domain_save_state *dss = shs->caller_state;
- libxl__domain_suspend_state *dsps = &dss->dsps;
-
- dsps->callback_common_done = domain_suspend_callback_common_done;
- domain_suspend_callback_common(egc, dsps);
-}
-
-static void domain_suspend_callback_common_done(libxl__egc *egc,
- libxl__domain_suspend_state *dsps, int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
- dss->rc = rc;
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-/*======================= Domain resume ========================*/
-
-int libxl__domain_resume_device_model_deprecated(libxl__gc *gc, uint32_t domid)
-{
- const char *path, *state;
-
- switch (libxl__device_model_version_running(gc, domid)) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
- uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
-
- path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
- state = libxl__xs_read(gc, XBT_NULL, path);
- if (state != NULL && !strcmp(state, "paused")) {
- libxl__qemu_traditional_cmd(gc, domid, "continue");
- libxl__wait_for_device_model_deprecated(gc, domid, "running",
- NULL, NULL, NULL);
- }
- break;
- }
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- if (libxl__qmp_resume(gc, domid))
- return ERROR_FAIL;
- break;
- default:
- return ERROR_INVAL;
- }
-
- return 0;
-}
-
-int libxl__domain_resume_deprecated(libxl__gc *gc, uint32_t domid, int suspend_cancel)
-{
- int rc = 0;
-
- libxl_domain_type type = libxl__domain_type(gc, domid);
- if (type == LIBXL_DOMAIN_TYPE_INVALID) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (type == LIBXL_DOMAIN_TYPE_HVM) {
- rc = libxl__domain_resume_device_model_deprecated(gc, domid);
- if (rc) {
- LOGD(ERROR, domid, "failed to resume device model:%d", rc);
- goto out;
- }
- }
-
- if (xc_domain_resume(CTX->xch, domid, suspend_cancel)) {
- LOGED(ERROR, domid, "xc_domain_resume failed");
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (!xs_resume_domain(CTX->xsh, domid)) {
- LOGED(ERROR, domid, "xs_resume_domain failed");
- rc = ERROR_FAIL;
- }
-out:
- return rc;
-}
-
-static void dm_resume_init(libxl__dm_resume_state *dmrs)
-{
- libxl__ev_qmp_init(&dmrs->qmp);
- libxl__ev_time_init(&dmrs->time);
- libxl__ev_xswatch_init(&dmrs->watch);
-}
-
-static void dm_resume_dispose(libxl__gc *gc,
- libxl__dm_resume_state *dmrs)
-{
- libxl__ev_qmp_dispose(gc, &dmrs->qmp);
- libxl__ev_time_deregister(gc, &dmrs->time);
- libxl__ev_xswatch_deregister(gc, &dmrs->watch);
-}
-
-static void dm_resume_xswatch_cb(libxl__egc *egc,
- libxl__ev_xswatch *, const char *watch_path, const char *);
-static void dm_resume_qmp_done(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *, int rc);
-static void dm_resume_timeout(libxl__egc *egc,
- libxl__ev_time *, const struct timeval *, int rc);
-static void dm_resume_done(libxl__egc *egc,
- libxl__dm_resume_state *dmrs, int rc);
-
-void libxl__dm_resume(libxl__egc *egc,
- libxl__dm_resume_state *dmrs)
-{
- STATE_AO_GC(dmrs->ao);
- int rc = 0;
-
- /* Convenience aliases */
- libxl_domid domid = dmrs->domid;
- libxl__ev_qmp *qmp = &dmrs->qmp;
-
- dm_resume_init(dmrs);
-
- rc = libxl__ev_time_register_rel(dmrs->ao,
- &dmrs->time,
- dm_resume_timeout,
- LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000);
- if (rc) goto out;
-
- switch (libxl__device_model_version_running(gc, domid)) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
- uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
- const char *path, *state;
-
- path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
- rc = libxl__xs_read_checked(gc, XBT_NULL, path, &state);
- if (rc) goto out;
- if (!state || strcmp(state, "paused")) {
- /* already running */
- rc = 0;
- goto out;
- }
-
- rc = libxl__qemu_traditional_cmd(gc, domid, "continue");
- if (rc) goto out;
- rc = libxl__ev_xswatch_register(gc, &dmrs->watch,
- dm_resume_xswatch_cb,
- path);
- if (rc) goto out;
- break;
- }
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- qmp->ao = dmrs->ao;
- qmp->domid = domid;
- qmp->callback = dm_resume_qmp_done;
- qmp->payload_fd = -1;
- rc = libxl__ev_qmp_send(egc, qmp, "cont", NULL);
- if (rc) goto out;
- break;
- default:
- rc = ERROR_INVAL;
- goto out;
- }
-
- return;
-
-out:
- dm_resume_done(egc, dmrs, rc);
-}
-
-static void dm_resume_xswatch_cb(libxl__egc *egc,
- libxl__ev_xswatch *xsw,
- const char *watch_path,
- const char *event_path)
-{
- EGC_GC;
- libxl__dm_resume_state *dmrs = CONTAINER_OF(xsw, *dmrs, watch);
- int rc;
- const char *value;
-
- rc = libxl__xs_read_checked(gc, XBT_NULL, watch_path, &value);
- if (rc) goto out;
-
- if (!value || strcmp(value, "running"))
- return;
-
- rc = 0;
-out:
- dm_resume_done(egc, dmrs, rc);
-}
-
-static void dm_resume_qmp_done(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *response,
- int rc)
-{
- libxl__dm_resume_state *dmrs = CONTAINER_OF(qmp, *dmrs, qmp);
- dm_resume_done(egc, dmrs, rc);
-}
-
-static void dm_resume_timeout(libxl__egc *egc,
- libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- libxl__dm_resume_state *dmrs = CONTAINER_OF(ev, *dmrs, time);
- dm_resume_done(egc, dmrs, rc);
-}
-
-static void dm_resume_done(libxl__egc *egc,
- libxl__dm_resume_state *dmrs,
- int rc)
-{
- EGC_GC;
-
- if (rc) {
- LOGD(ERROR, dmrs->domid,
- "Failed to resume device model: rc=%d", rc);
- }
-
- dm_resume_dispose(gc, dmrs);
- dmrs->dm_resumed_callback(egc, dmrs, rc);
-}
-
-
-static void domain_resume_done(libxl__egc *egc,
- libxl__dm_resume_state *dmrs, int rc);
-
-void libxl__domain_resume(libxl__egc *egc,
- libxl__dm_resume_state *dmrs,
- bool suspend_cancel)
-{
- STATE_AO_GC(dmrs->ao);
- int rc = 0;
- libxl_domain_type type = libxl__domain_type(gc, dmrs->domid);
-
- if (type == LIBXL_DOMAIN_TYPE_INVALID) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (type != LIBXL_DOMAIN_TYPE_HVM) {
- rc = 0;
- goto out;
- }
-
- dmrs->suspend_cancel = suspend_cancel;
- dmrs->dm_resumed_callback = domain_resume_done;
- libxl__dm_resume(egc, dmrs); /* must be last */
- return;
-
-out:
- domain_resume_done(egc, dmrs, rc);
-}
-
-static void domain_resume_done(libxl__egc *egc,
- libxl__dm_resume_state *dmrs, int rc)
-{
- EGC_GC;
-
- /* Convenience aliases */
- libxl_domid domid = dmrs->domid;
-
- if (rc) goto out;
-
- if (xc_domain_resume(CTX->xch, domid, dmrs->suspend_cancel)) {
- LOGED(ERROR, domid, "xc_domain_resume failed");
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (!xs_resume_domain(CTX->xsh, domid)) {
- LOGED(ERROR, domid, "xs_resume_domain failed");
- rc = ERROR_FAIL;
- }
-out:
- dmrs->callback(egc, dmrs, rc);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-#define PAGE_TO_MEMKB(pages) ((pages) * 4)
-
-int libxl__domain_rename(libxl__gc *gc, uint32_t domid,
- const char *old_name, const char *new_name,
- xs_transaction_t trans)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char *dom_path = 0;
- const char *name_path;
- char *got_old_name;
- unsigned int got_old_len;
- xs_transaction_t our_trans = 0;
- uint32_t stub_dm_domid;
- const char *stub_dm_old_name = NULL, *stub_dm_new_name = NULL;
- int rc;
- libxl_dominfo info;
- char *uuid;
- const char *vm_name_path;
-
- libxl_dominfo_init(&info);
-
- dom_path = libxl__xs_get_dompath(gc, domid);
- if (!dom_path) goto x_nomem;
-
- name_path= GCSPRINTF("%s/name", dom_path);
- if (!name_path) goto x_nomem;
-
- stub_dm_domid = libxl_get_stubdom_id(CTX, domid);
- if (stub_dm_domid) {
- stub_dm_old_name = libxl__stub_dm_name(gc, old_name);
- stub_dm_new_name = libxl__stub_dm_name(gc, new_name);
- }
-
- retry_transaction:
- if (!trans) {
- trans = our_trans = xs_transaction_start(ctx->xsh);
- if (!our_trans) {
- LOGEVD(ERROR, errno, domid, "Create xs transaction for domain (re)name");
- goto x_fail;
- }
- }
-
- if (!new_name) {
- LOGD(ERROR, domid, "New domain name not specified");
- rc = ERROR_INVAL;
- goto x_rc;
- }
-
- if (new_name[0]) {
- /* nonempty names must be unique */
- uint32_t domid_e;
- rc = libxl_name_to_domid(ctx, new_name, &domid_e);
- if (rc == ERROR_INVAL) {
- /* no such domain, good */
- } else if (rc != 0) {
- LOGD(ERROR, domid, "Unexpected error checking for existing domain");
- goto x_rc;
- } else if (domid_e == domid) {
- /* domain already has this name, ok (but we do still
- * need the rest of the code as we may need to check
- * old_name, for example). */
- } else {
- LOGD(ERROR, domid, "Domain with name \"%s\" already exists.", new_name);
- rc = ERROR_INVAL;
- goto x_rc;
- }
- }
-
- if (old_name) {
- got_old_name = xs_read(ctx->xsh, trans, name_path, &got_old_len);
- if (!got_old_name) {
- LOGEVD(ERROR, errno, domid,
- "Check old name for domain allegedly named `%s'",
- old_name);
- goto x_fail;
- }
- if (strcmp(old_name, got_old_name)) {
- LOGD(ERROR, domid,
- "Allegedly named `%s' is actually named `%s' - racing ?",
- old_name,
- got_old_name);
- free(got_old_name);
- goto x_fail;
- }
- free(got_old_name);
- }
- if (!xs_write(ctx->xsh, trans, name_path,
- new_name, strlen(new_name))) {
- LOGD(ERROR, domid,
- "Failed to write new name `%s'"
- " for domain previously named `%s'",
- new_name,
- old_name);
- goto x_fail;
- }
-
- /* update /vm/<uuid>/name */
- rc = libxl_domain_info(ctx, &info, domid);
- if (rc)
- goto x_rc;
-
- uuid = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid));
- vm_name_path = GCSPRINTF("/vm/%s/name", uuid);
- if (libxl__xs_write_checked(gc, trans, vm_name_path, new_name))
- goto x_fail;
-
- if (stub_dm_domid) {
- rc = libxl__domain_rename(gc, stub_dm_domid,
- stub_dm_old_name,
- stub_dm_new_name,
- trans);
- if (rc) {
- LOGED(ERROR, domid, "Unable to rename stub-domain");
- goto x_rc;
- }
- }
-
- if (our_trans) {
- if (!xs_transaction_end(ctx->xsh, our_trans, 0)) {
- trans = our_trans = 0;
- if (errno != EAGAIN) {
- LOGD(ERROR, domid,
- "Failed to commit new name `%s'"
- " for domain previously named `%s'",
- new_name,
- old_name);
- goto x_fail;
- }
- LOGD(DEBUG, domid,
- "Need to retry rename transaction"
- " for domain (name_path=\"%s\", new_name=\"%s\")",
- name_path,
- new_name);
- goto retry_transaction;
- }
- our_trans = 0;
- }
-
- rc = 0;
- x_rc:
- if (our_trans) xs_transaction_end(ctx->xsh, our_trans, 1);
- libxl_dominfo_dispose(&info);
- return rc;
-
- x_fail: rc = ERROR_FAIL; goto x_rc;
- x_nomem: rc = ERROR_NOMEM; goto x_rc;
-}
-
-int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid,
- const char *old_name, const char *new_name)
-{
- GC_INIT(ctx);
- int rc;
- rc = libxl__domain_rename(gc, domid, old_name, new_name, XBT_NULL);
- GC_FREE;
- return rc;
-}
-
-static void domain_resume_done(libxl__egc *egc,
- libxl__dm_resume_state *,
- int rc);
-
-int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- libxl__dm_resume_state *dmrs;
-
- GCNEW(dmrs);
- dmrs->ao = ao;
- dmrs->domid = domid;
- dmrs->callback = domain_resume_done;
- libxl__domain_resume(egc, dmrs, suspend_cancel);
- return AO_INPROGRESS;
-}
-
-static void domain_resume_done(libxl__egc *egc,
- libxl__dm_resume_state *dmrs,
- int rc)
-{
- STATE_AO_GC(dmrs->ao);
- libxl__ao_complete(egc, ao, rc);
-}
-
-/*
- * Preserves a domain but rewrites xenstore etc to make it unique so
- * that the domain can be restarted.
- *
- * Does not modify info so that it may be reused.
- */
-int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid,
- libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid)
-{
- GC_INIT(ctx);
- struct xs_permissions roperm[2];
- xs_transaction_t t;
- char *preserved_name;
- char *uuid_string;
- char *vm_path;
- char *dom_path;
-
- int rc;
-
- preserved_name = GCSPRINTF("%s%s", info->name, name_suffix);
- if (!preserved_name) {
- GC_FREE;
- return ERROR_NOMEM;
- }
-
- uuid_string = libxl__uuid2string(gc, new_uuid);
- if (!uuid_string) {
- GC_FREE;
- return ERROR_NOMEM;
- }
-
- dom_path = libxl__xs_get_dompath(gc, domid);
- if (!dom_path) {
- GC_FREE;
- return ERROR_FAIL;
- }
-
- vm_path = GCSPRINTF("/vm/%s", uuid_string);
- if (!vm_path) {
- GC_FREE;
- return ERROR_FAIL;
- }
-
- roperm[0].id = 0;
- roperm[0].perms = XS_PERM_NONE;
- roperm[1].id = domid;
- roperm[1].perms = XS_PERM_READ;
-
- retry_transaction:
- t = xs_transaction_start(ctx->xsh);
-
- xs_rm(ctx->xsh, t, vm_path);
- xs_mkdir(ctx->xsh, t, vm_path);
- xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm));
-
- xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
- rc = libxl__domain_rename(gc, domid, info->name, preserved_name, t);
- if (rc) {
- GC_FREE;
- return rc;
- }
-
- xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
-
- if (!xs_transaction_end(ctx->xsh, t, 0))
- if (errno == EAGAIN)
- goto retry_transaction;
-
- GC_FREE;
- return 0;
-}
-
-void libxl__xcinfo2xlinfo(libxl_ctx *ctx,
- const xc_domaininfo_t *xcinfo,
- libxl_dominfo *xlinfo)
-{
- size_t size;
-
- memcpy(&(xlinfo->uuid), xcinfo->handle, sizeof(xen_domain_handle_t));
- xlinfo->domid = xcinfo->domain;
- xlinfo->ssidref = xcinfo->ssidref;
- if (libxl_flask_sid_to_context(ctx, xlinfo->ssidref,
- &xlinfo->ssid_label, &size) < 0)
- xlinfo->ssid_label = NULL;
-
- xlinfo->dying = !!(xcinfo->flags&XEN_DOMINF_dying);
- xlinfo->shutdown = !!(xcinfo->flags&XEN_DOMINF_shutdown);
- xlinfo->paused = !!(xcinfo->flags&XEN_DOMINF_paused);
- xlinfo->blocked = !!(xcinfo->flags&XEN_DOMINF_blocked);
- xlinfo->running = !!(xcinfo->flags&XEN_DOMINF_running);
- xlinfo->never_stop = !!(xcinfo->flags&XEN_DOMINF_xs_domain);
-
- if (xlinfo->shutdown)
- xlinfo->shutdown_reason = (xcinfo->flags>>XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask;
- else
- xlinfo->shutdown_reason = LIBXL_SHUTDOWN_REASON_UNKNOWN;
-
- xlinfo->outstanding_memkb = PAGE_TO_MEMKB(xcinfo->outstanding_pages);
- xlinfo->current_memkb = PAGE_TO_MEMKB(xcinfo->tot_pages);
- xlinfo->shared_memkb = PAGE_TO_MEMKB(xcinfo->shr_pages);
- xlinfo->paged_memkb = PAGE_TO_MEMKB(xcinfo->paged_pages);
- xlinfo->max_memkb = PAGE_TO_MEMKB(xcinfo->max_pages);
- xlinfo->cpu_time = xcinfo->cpu_time;
- xlinfo->vcpu_max_id = xcinfo->max_vcpu_id;
- xlinfo->vcpu_online = xcinfo->nr_online_vcpus;
- xlinfo->cpupool = xcinfo->cpupool;
- xlinfo->domain_type = (xcinfo->flags & XEN_DOMINF_hvm_guest) ?
- LIBXL_DOMAIN_TYPE_HVM : LIBXL_DOMAIN_TYPE_PV;
-}
-
-libxl_dominfo * libxl_list_domain(libxl_ctx *ctx, int *nb_domain_out)
-{
- libxl_dominfo *ptr = NULL;
- int i, ret;
- xc_domaininfo_t *info;
- int size = 0;
- uint32_t domid = 0;
- GC_INIT(ctx);
-
- GCNEW_ARRAY(info, 1024);
-
- while ((ret = xc_domain_getinfolist(ctx->xch, domid, 1024, info)) > 0) {
- ptr = libxl__realloc(NOGC, ptr, (size + ret) * sizeof(libxl_dominfo));
- for (i = 0; i < ret; i++) {
- libxl__xcinfo2xlinfo(ctx, &info[i], &ptr[size + i]);
- }
- domid = info[ret - 1].domain + 1;
- size += ret;
- }
-
- if (ret < 0) {
- LOGE(ERROR, "getting domain info list");
- free(ptr);
- GC_FREE;
- return NULL;
- }
-
- *nb_domain_out = size;
- GC_FREE;
- return ptr;
-}
-
-int libxl_domain_info(libxl_ctx *ctx, libxl_dominfo *info_r,
- uint32_t domid) {
- xc_domaininfo_t xcinfo;
- int ret;
- GC_INIT(ctx);
-
- ret = xc_domain_getinfolist(ctx->xch, domid, 1, &xcinfo);
- if (ret<0) {
- LOGED(ERROR, domid, "Getting domain info list");
- GC_FREE;
- return ERROR_FAIL;
- }
- if (ret==0 || xcinfo.domain != domid) {
- GC_FREE;
- return ERROR_DOMAIN_NOTFOUND;
- }
-
- if (info_r)
- libxl__xcinfo2xlinfo(ctx, &xcinfo, info_r);
- GC_FREE;
- return 0;
-}
-
-/* this API call only list VM running on this host. A VM can
- * be an aggregate of multiple domains. */
-libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out)
-{
- GC_INIT(ctx);
- libxl_dominfo *info;
- libxl_vminfo *ptr = NULL;
- int idx, i, n_doms;
-
- info = libxl_list_domain(ctx, &n_doms);
- if (!info)
- goto out;
-
- /*
- * Always make sure to allocate at least one element; if we don't and we
- * request zero, libxl__calloc (might) think its internal call to calloc
- * has failed (if it returns null), if so it would kill our process.
- */
- ptr = libxl__calloc(NOGC, n_doms ? n_doms : 1, sizeof(libxl_vminfo));
-
- for (idx = i = 0; i < n_doms; i++) {
- if (libxl_is_stubdom(ctx, info[i].domid, NULL))
- continue;
- ptr[idx].uuid = info[i].uuid;
- ptr[idx].domid = info[i].domid;
-
- idx++;
- }
- *nb_vm_out = idx;
- libxl_dominfo_list_free(info, n_doms);
-
-out:
- GC_FREE;
- return ptr;
-}
-
-static void remus_failover_cb(libxl__egc *egc,
- libxl__domain_save_state *dss, int rc);
-
-int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
- uint32_t domid, int send_fd, int recv_fd,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- libxl__domain_save_state *dss;
- int rc;
-
- libxl_domain_type type = libxl__domain_type(gc, domid);
- if (type == LIBXL_DOMAIN_TYPE_INVALID) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- /* The caller must set this defbool */
- if (libxl_defbool_is_default(info->colo)) {
- LOGD(ERROR, domid, "Colo mode must be enabled/disabled");
- rc = ERROR_FAIL;
- goto out;
- }
-
- libxl_defbool_setdefault(&info->allow_unsafe, false);
- libxl_defbool_setdefault(&info->blackhole, false);
- libxl_defbool_setdefault(&info->compression,
- !libxl_defbool_val(info->colo));
- libxl_defbool_setdefault(&info->netbuf, true);
- libxl_defbool_setdefault(&info->diskbuf, true);
-
- if (libxl_defbool_val(info->colo) &&
- libxl_defbool_val(info->compression)) {
- LOGD(ERROR, domid, "Cannot use memory checkpoint "
- "compression in COLO mode");
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (!libxl_defbool_val(info->allow_unsafe) &&
- (libxl_defbool_val(info->blackhole) ||
- !libxl_defbool_val(info->netbuf) ||
- !libxl_defbool_val(info->diskbuf))) {
- LOGD(ERROR, domid, "Unsafe mode must be enabled to replicate to /dev/null,"
- "disable network buffering and disk replication");
- rc = ERROR_FAIL;
- goto out;
- }
-
-
- GCNEW(dss);
- dss->ao = ao;
- dss->callback = remus_failover_cb;
- dss->domid = domid;
- dss->fd = send_fd;
- dss->recv_fd = recv_fd;
- dss->type = type;
- dss->live = 1;
- dss->debug = 0;
- dss->remus = info;
- if (libxl_defbool_val(info->colo))
- dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_COLO;
- else
- dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_REMUS;
-
- assert(info);
-
- /* Point of no return */
- if (libxl_defbool_val(info->colo))
- libxl__colo_save_setup(egc, &dss->css);
- else
- libxl__remus_setup(egc, &dss->rs);
- return AO_INPROGRESS;
-
- out:
- return AO_CREATE_FAIL(rc);
-}
-
-static void remus_failover_cb(libxl__egc *egc,
- libxl__domain_save_state *dss, int rc)
-{
- STATE_AO_GC(dss->ao);
- /*
- * With Remus, if we reach this point, it means either
- * backup died or some network error occurred preventing us
- * from sending checkpoints.
- */
- libxl__ao_complete(egc, ao, rc);
-}
-
-static void domain_suspend_cb(libxl__egc *egc,
- libxl__domain_save_state *dss, int rc)
-{
- STATE_AO_GC(dss->ao);
- int flrc;
-
- flrc = libxl__fd_flags_restore(gc, dss->fd, dss->fdfl);
- /* If suspend has failed already then report that error not this one. */
- if (flrc && !rc) rc = flrc;
-
- libxl__ao_complete(egc,ao,rc);
-
-}
-
-int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- int rc;
-
- libxl_domain_type type = libxl__domain_type(gc, domid);
- if (type == LIBXL_DOMAIN_TYPE_INVALID) {
- rc = ERROR_FAIL;
- goto out_err;
- }
-
- libxl__domain_save_state *dss;
- GCNEW(dss);
-
- dss->ao = ao;
- dss->callback = domain_suspend_cb;
-
- dss->domid = domid;
- dss->fd = fd;
- dss->type = type;
- dss->live = flags & LIBXL_SUSPEND_LIVE;
- dss->debug = flags & LIBXL_SUSPEND_DEBUG;
- dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE;
-
- rc = libxl__fd_flags_modify_save(gc, dss->fd,
- ~(O_NONBLOCK|O_NDELAY), 0,
- &dss->fdfl);
- if (rc < 0) goto out_err;
-
- libxl__domain_save(egc, dss);
- return AO_INPROGRESS;
-
- out_err:
- return AO_CREATE_FAIL(rc);
-}
-
-static void domain_suspend_empty_cb(libxl__egc *egc,
- libxl__domain_suspend_state *dss, int rc)
-{
- STATE_AO_GC(dss->ao);
- libxl__ao_complete(egc,ao,rc);
-}
-
-int libxl_domain_suspend_only(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- libxl__domain_suspend_state *dsps;
- int rc;
-
- libxl_domain_type type = libxl__domain_type(gc, domid);
- if (type == LIBXL_DOMAIN_TYPE_INVALID) {
- rc = ERROR_FAIL;
- goto out_err;
- }
-
- GCNEW(dsps);
- dsps->ao = ao;
- dsps->domid = domid;
- dsps->type = type;
- rc = libxl__domain_suspend_init(egc, dsps, type);
- if (rc < 0) goto out_err;
- dsps->callback_common_done = domain_suspend_empty_cb;
- libxl__domain_suspend(egc, dsps);
- return AO_INPROGRESS;
-
- out_err:
- return AO_CREATE_FAIL(rc);
-}
-
-int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- int r;
- r = xc_domain_pause(ctx->xch, domid);
- if (r < 0) {
- LOGED(ERROR, domid, "Pausing domain");
- return AO_CREATE_FAIL(ERROR_FAIL);
- }
- libxl__ao_complete(egc, ao, 0);
- return AO_INPROGRESS;
-}
-
-int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
- const char *filename,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- int ret, rc;
-
- ret = xc_domain_dumpcore(ctx->xch, domid, filename);
- if (ret<0) {
- LOGED(ERROR, domid, "Core dumping domain to %s", filename);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-out:
-
- libxl__ao_complete(egc, ao, rc);
-
- return AO_INPROGRESS;
-}
-
-int libxl__domain_unpause_deprecated(libxl__gc *gc, libxl_domid domid)
-{
- int r, rc;
-
- libxl_domain_type type = libxl__domain_type(gc, domid);
- if (type == LIBXL_DOMAIN_TYPE_INVALID) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (type == LIBXL_DOMAIN_TYPE_HVM) {
- rc = libxl__domain_resume_device_model_deprecated(gc, domid);
- if (rc < 0) {
- LOGD(ERROR, domid,
- "Failed to unpause device model for domain: %d", rc);
- goto out;
- }
- }
- r = xc_domain_unpause(CTX->xch, domid);
- if (r < 0) {
- LOGED(ERROR, domid, "Unpausing domain");
- rc = ERROR_FAIL;
- goto out;
- }
- rc = 0;
-out:
- return rc;
-}
-
-static void domain_unpause_done(libxl__egc *egc,
- libxl__dm_resume_state *,
- int rc);
-
-void libxl__domain_unpause(libxl__egc *egc,
- libxl__dm_resume_state *dmrs)
-{
- STATE_AO_GC(dmrs->ao);
- int rc = 0;
-
- /* Convenience aliases */
- libxl_domid domid = dmrs->domid;
-
- libxl_domain_type type = libxl__domain_type(gc, domid);
- if (type == LIBXL_DOMAIN_TYPE_INVALID) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (type == LIBXL_DOMAIN_TYPE_HVM) {
- dmrs->dm_resumed_callback = domain_unpause_done;
- libxl__dm_resume(egc, dmrs); /* must be last */
- return;
- }
- rc = 0;
-out:
- domain_unpause_done(egc, dmrs, rc);
-}
-
-static void domain_unpause_done(libxl__egc *egc,
- libxl__dm_resume_state *dmrs,
- int rc)
-{
- EGC_GC;
- int r;
-
- /* Convenience aliases */
- libxl_domid domid = dmrs->domid;
-
- if (rc) goto out;
-
- r = xc_domain_unpause(CTX->xch, domid);
- if (r < 0) {
- LOGED(ERROR, domid, "Unpausing domain");
- rc = ERROR_FAIL;
- goto out;
- }
- rc = 0;
-out:
- dmrs->callback(egc, dmrs, rc);
-}
-
-static void domain_unpause_ao_done(libxl__egc *egc,
- libxl__dm_resume_state *,
- int rc);
-
-int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- libxl__dm_resume_state *dmrs;
-
- GCNEW(dmrs);
- dmrs->ao = ao;
- dmrs->domid = domid;
- dmrs->callback = domain_unpause_ao_done;
- libxl__domain_unpause(egc, dmrs); /* must be last */
- return AO_INPROGRESS;
-}
-
-static void domain_unpause_ao_done(libxl__egc *egc,
- libxl__dm_resume_state *dmrs,
- int rc)
-{
- STATE_AO_GC(dmrs->ao);
-
- libxl__ao_complete(egc, ao, rc);
-}
-
-int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
-
- uint64_t pvdriver = 0;
- int ret;
-
- libxl_domain_type domtype = libxl__domain_type(gc, domid);
- if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
- return ERROR_FAIL;
-
- if (domtype != LIBXL_DOMAIN_TYPE_HVM)
- return 1;
-
- ret = xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver);
- if (ret<0) {
- LOGED(ERROR, domid, "Getting HVM callback IRQ");
- return ERROR_FAIL;
- }
- return !!pvdriver;
-}
-
-const char *libxl__domain_pvcontrol_xspath(libxl__gc *gc, uint32_t domid)
-{
- const char *dom_path;
-
- dom_path = libxl__xs_get_dompath(gc, domid);
- if (!dom_path)
- return NULL;
-
- return GCSPRINTF("%s/control/shutdown", dom_path);
-}
-
-char * libxl__domain_pvcontrol_read(libxl__gc *gc, xs_transaction_t t,
- uint32_t domid)
-{
- const char *shutdown_path;
-
- shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
- if (!shutdown_path)
- return NULL;
-
- return libxl__xs_read(gc, t, shutdown_path);
-}
-
-int libxl__domain_pvcontrol(libxl__egc *egc, libxl__xswait_state *pvcontrol,
- domid_t domid, const char *cmd)
-{
- STATE_AO_GC(pvcontrol->ao);
- const char *shutdown_path;
- int rc;
-
- rc = libxl__domain_pvcontrol_available(gc, domid);
- if (rc < 0)
- return rc;
-
- if (!rc)
- return ERROR_NOPARAVIRT;
-
- shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
- if (!shutdown_path)
- return ERROR_FAIL;
-
- rc = libxl__xs_printf(gc, XBT_NULL, shutdown_path, "%s", cmd);
- if (rc)
- return rc;
-
- pvcontrol->path = shutdown_path;
- pvcontrol->what = GCSPRINTF("guest acknowledgement of %s request", cmd);
- pvcontrol->timeout_ms = 60 * 1000;
- rc = libxl__xswait_start(gc, pvcontrol);
- if (rc)
- return rc;
-
- return 0;
-}
-
-static bool pvcontrol_acked(const char *state)
-{
- if (!state || !strcmp(state,""))
- return true;
-
- return false;
-}
-
-/* Xenstore watch callback prototype for the reboot/poweroff operations. */
-static void pvcontrol_cb(libxl__egc *egc, libxl__xswait_state *xswa, int rc,
- const char *state);
-
-int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- libxl__xswait_state *pvcontrol;
- int rc;
-
- GCNEW(pvcontrol);
- pvcontrol->ao = ao;
- pvcontrol->callback = pvcontrol_cb;
- rc = libxl__domain_pvcontrol(egc, pvcontrol, domid, "poweroff");
-
- return rc ? AO_CREATE_FAIL(rc) : AO_INPROGRESS;
-}
-
-int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- libxl__xswait_state *pvcontrol;
- int rc;
-
- GCNEW(pvcontrol);
- pvcontrol->ao = ao;
- pvcontrol->callback = pvcontrol_cb;
- rc = libxl__domain_pvcontrol(egc, pvcontrol, domid, "reboot");
-
- return rc ? AO_CREATE_FAIL(rc) : AO_INPROGRESS;
-}
-
-static void pvcontrol_cb(libxl__egc *egc, libxl__xswait_state *xswa, int rc,
- const char *state)
-{
- STATE_AO_GC(xswa->ao);
-
- if (!rc && !pvcontrol_acked(state))
- return;
-
- libxl__xswait_stop(gc, xswa);
-
- if (rc)
- LOG(ERROR, "guest didn't acknowledge control request: %d", rc);
-
- libxl__ao_complete(egc, ao, rc);
-}
-
-static void domain_death_occurred(libxl__egc *egc,
- libxl_evgen_domain_death **evg_upd,
- const char *why) {
- /* Removes **evg_upd from death_list and puts it on death_reported
- * and advances *evg_upd to the next entry.
- * Call sites in domain_death_xswatch_callback must use "continue". */
- EGC_GC;
- libxl_evgen_domain_death *const evg = *evg_upd;
-
- LOGD(DEBUG, evg->domid, "%s", why);
-
- libxl_evgen_domain_death *evg_next = LIBXL_TAILQ_NEXT(evg, entry);
- *evg_upd = evg_next;
-
- libxl_event *ev = NEW_EVENT(egc, DOMAIN_DEATH, evg->domid, evg->user);
-
- libxl__event_occurred(egc, ev);
-
- evg->death_reported = 1;
- LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
- LIBXL_TAILQ_INSERT_HEAD(&CTX->death_reported, evg, entry);
-}
-
-static void domain_death_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
- const char *wpath, const char *epath) {
- EGC_GC;
- libxl_evgen_domain_death *evg;
- int rc;
-
- CTX_LOCK;
-
- evg = LIBXL_TAILQ_FIRST(&CTX->death_list);
-
- for (;;) {
- if (!evg) goto out;
-
- int nentries = LIBXL_TAILQ_NEXT(evg, entry) ? 200 : 1;
- xc_domaininfo_t domaininfos[nentries];
- const xc_domaininfo_t *got = domaininfos, *gotend;
-
- rc = xc_domain_getinfolist(CTX->xch, evg->domid, nentries, domaininfos);
- if (rc == -1) {
- LIBXL__EVENT_DISASTER(gc, "xc_domain_getinfolist failed while"
- " processing @releaseDomain watch event",
- errno, 0);
- goto out;
- }
- gotend = &domaininfos[rc];
-
- LOGD(DEBUG, evg->domid, "[evg=%p] nentries=%d rc=%d %ld..%ld",
- evg, nentries, rc,
- rc>0 ? (long)domaininfos[0].domain : 0,
- rc>0 ? (long)domaininfos[rc-1].domain : 0);
-
- for (;;) {
- if (!evg) {
- LOG(DEBUG, "[evg=0] all reported");
- goto all_reported;
- }
-
- LOGD(DEBUG, evg->domid, "[evg=%p]"
- " got=domaininfos[%d] got->domain=%ld",
- evg, (int)(got - domaininfos),
- got < gotend ? (long)got->domain : -1L);
-
- if (!rc) {
- domain_death_occurred(egc, &evg, "empty list");
- continue;
- }
-
- if (got == gotend) {
- LOG(DEBUG, " got==gotend");
- break;
- }
-
- if (got->domain > evg->domid) {
- /* ie, the list doesn't contain evg->domid any more so
- * the domain has been destroyed */
- domain_death_occurred(egc, &evg, "missing from list");
- continue;
- }
-
- if (got->domain < evg->domid) {
- got++;
- continue;
- }
-
- assert(evg->domid == got->domain);
- LOGD(DEBUG, evg->domid, "Exists shutdown_reported=%d"" dominf.flags=%x",
- evg->shutdown_reported, got->flags);
-
- if (got->flags & XEN_DOMINF_dying) {
- domain_death_occurred(egc, &evg, "dying");
- continue;
- }
-
- if (!evg->shutdown_reported &&
- (got->flags & XEN_DOMINF_shutdown)) {
- libxl_event *ev = NEW_EVENT(egc, DOMAIN_SHUTDOWN,
- got->domain, evg->user);
-
- LOG(DEBUG, " shutdown reporting");
-
- ev->u.domain_shutdown.shutdown_reason =
- (got->flags >> XEN_DOMINF_shutdownshift) &
- XEN_DOMINF_shutdownmask;
- libxl__event_occurred(egc, ev);
-
- evg->shutdown_reported = 1;
- }
- evg = LIBXL_TAILQ_NEXT(evg, entry);
- }
-
- assert(rc); /* rc==0 results in us eating all evgs and quitting */
- }
- all_reported:
- out:
-
- LOG(DEBUG, "domain death search done");
-
- CTX_UNLOCK;
-}
-
-int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
- libxl_ev_user user, libxl_evgen_domain_death **evgen_out) {
- GC_INIT(ctx);
- libxl_evgen_domain_death *evg, *evg_search;
- int rc;
-
- CTX_LOCK;
-
- evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; }
- memset(evg, 0, sizeof(*evg));
- evg->domid = domid;
- evg->user = user;
-
- LIBXL_TAILQ_INSERT_SORTED(&ctx->death_list, entry, evg, evg_search, ,
- evg->domid > evg_search->domid);
-
- if (!libxl__ev_xswatch_isregistered(&ctx->death_watch)) {
- rc = libxl__ev_xswatch_register(gc, &ctx->death_watch,
- domain_death_xswatch_callback, "@releaseDomain");
- if (rc) { libxl__evdisable_domain_death(gc, evg); goto out; }
- }
-
- *evgen_out = evg;
- rc = 0;
-
- out:
- CTX_UNLOCK;
- GC_FREE;
- return rc;
-};
-
-void libxl__evdisable_domain_death(libxl__gc *gc,
- libxl_evgen_domain_death *evg) {
- CTX_LOCK;
-
- if (!evg->death_reported)
- LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
- else
- LIBXL_TAILQ_REMOVE(&CTX->death_reported, evg, entry);
-
- free(evg);
-
- if (!LIBXL_TAILQ_FIRST(&CTX->death_list) &&
- libxl__ev_xswatch_isregistered(&CTX->death_watch))
- libxl__ev_xswatch_deregister(gc, &CTX->death_watch);
-
- CTX_UNLOCK;
-}
-
-void libxl_evdisable_domain_death(libxl_ctx *ctx,
- libxl_evgen_domain_death *evg) {
- GC_INIT(ctx);
- libxl__evdisable_domain_death(gc, evg);
- GC_FREE;
-}
-
-/* Callbacks for libxl_domain_destroy */
-
-static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
- int rc);
-
-int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- libxl__domain_destroy_state *dds;
-
- GCNEW(dds);
- dds->ao = ao;
- dds->domid = domid;
- dds->callback = domain_destroy_cb;
- libxl__domain_destroy(egc, dds);
-
- return AO_INPROGRESS;
-}
-
-static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
- int rc)
-{
- STATE_AO_GC(dds->ao);
-
- if (rc)
- LOGD(ERROR, dds->domid, "Destruction of domain failed");
-
- libxl__ao_complete(egc, ao, rc);
-}
-
-/* Callbacks for libxl__domain_destroy */
-
-static void stubdom_destroy_callback(libxl__egc *egc,
- libxl__destroy_domid_state *dis,
- int rc);
-
-static void domain_destroy_callback(libxl__egc *egc,
- libxl__destroy_domid_state *dis,
- int rc);
-
-static void destroy_finish_check(libxl__egc *egc,
- libxl__domain_destroy_state *dds);
-
-void libxl__domain_destroy(libxl__egc *egc, libxl__domain_destroy_state *dds)
-{
- STATE_AO_GC(dds->ao);
- uint32_t stubdomid = libxl_get_stubdom_id(CTX, dds->domid);
-
- if (stubdomid) {
- dds->stubdom.ao = ao;
- dds->stubdom.domid = stubdomid;
- dds->stubdom.callback = stubdom_destroy_callback;
- dds->stubdom.soft_reset = false;
- libxl__destroy_domid(egc, &dds->stubdom);
- } else {
- dds->stubdom_finished = 1;
- }
-
- dds->domain.ao = ao;
- dds->domain.domid = dds->domid;
- dds->domain.callback = domain_destroy_callback;
- dds->domain.soft_reset = dds->soft_reset;
- libxl__destroy_domid(egc, &dds->domain);
-}
-
-static void stubdom_destroy_callback(libxl__egc *egc,
- libxl__destroy_domid_state *dis,
- int rc)
-{
- STATE_AO_GC(dis->ao);
- libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, stubdom);
- const char *savefile;
-
- if (rc) {
- LOGD(ERROR, dds->domain.domid, "Unable to destroy stubdom with domid %u",
- dis->domid);
- dds->rc = rc;
- }
-
- dds->stubdom_finished = 1;
- savefile = libxl__device_model_savefile(gc, dis->domid);
- rc = libxl__remove_file(gc, savefile);
- if (rc) {
- LOGD(ERROR, dds->domain.domid, "Failed to remove device-model savefile %s",
- savefile);
- }
-
- destroy_finish_check(egc, dds);
-}
-
-static void domain_destroy_callback(libxl__egc *egc,
- libxl__destroy_domid_state *dis,
- int rc)
-{
- STATE_AO_GC(dis->ao);
- libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, domain);
-
- if (rc) {
- LOGD(ERROR, dis->domid, "Unable to destroy guest");
- dds->rc = rc;
- }
-
- dds->domain_finished = 1;
- destroy_finish_check(egc, dds);
-}
-
-static void destroy_finish_check(libxl__egc *egc,
- libxl__domain_destroy_state *dds)
-{
- if (!(dds->domain_finished && dds->stubdom_finished))
- return;
-
- dds->callback(egc, dds, dds->rc);
-}
-
-/* Callbacks for libxl__destroy_domid */
-static void destroy_domid_pci_done(libxl__egc *egc,
- libxl__multidev *multidev,
- int rc);
-static void dm_destroy_cb(libxl__egc *egc,
- libxl__destroy_devicemodel_state *ddms,
- int rc);
-
-static void devices_destroy_cb(libxl__egc *egc,
- libxl__devices_remove_state *drs,
- int rc);
-
-static void domain_destroy_domid_cb(libxl__egc *egc,
- libxl__ev_child *destroyer,
- pid_t pid, int status);
-
-void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis)
-{
- STATE_AO_GC(dis->ao);
- uint32_t domid = dis->domid;
- int rc;
-
- libxl__ev_child_init(&dis->destroyer);
-
- rc = libxl_domain_info(CTX, NULL, domid);
- switch(rc) {
- case 0:
- break;
- case ERROR_DOMAIN_NOTFOUND:
- LOGD(ERROR, domid, "Non-existant domain");
- default:
- goto out;
- }
-
- libxl__multidev_begin(ao, &dis->multidev);
- dis->multidev.callback = destroy_domid_pci_done;
- libxl__device_pci_destroy_all(egc, domid, &dis->multidev);
- libxl__multidev_prepared(egc, &dis->multidev, 0);
- return;
-
-out:
- assert(rc);
- dis->callback(egc, dis, rc);
-}
-
-static void destroy_domid_pci_done(libxl__egc *egc,
- libxl__multidev *multidev,
- int rc)
-{
- STATE_AO_GC(multidev->ao);
- libxl__destroy_domid_state *dis =
- CONTAINER_OF(multidev, *dis, multidev);
- int dm_present;
- int r;
-
- /* Convenience aliases */
- libxl_domid domid = dis->domid;
-
- if (rc) {
- LOGD(ERROR, domid, "Pci shutdown failed");
- goto out;
- }
-
- r = xc_domain_pause(CTX->xch, domid);
- if (r < 0) {
- LOGEVD(ERROR, r, domid, "xc_domain_pause failed");
- rc = ERROR_FAIL;
- }
-
- switch (libxl__domain_type(gc, domid)) {
- case LIBXL_DOMAIN_TYPE_HVM:
- if (libxl_get_stubdom_id(CTX, domid)) {
- dm_present = 0;
- break;
- }
- /* fall through */
- case LIBXL_DOMAIN_TYPE_PVH:
- case LIBXL_DOMAIN_TYPE_PV:
- dm_present = libxl__dm_active(gc, domid);
- break;
- case LIBXL_DOMAIN_TYPE_INVALID:
- rc = ERROR_FAIL;
- goto out;
- default:
- abort();
- }
-
- if (dm_present) {
- dis->ddms.ao = ao;
- dis->ddms.domid = domid;
- dis->ddms.callback = dm_destroy_cb;
-
- libxl__destroy_device_model(egc, &dis->ddms);
- return;
- } else {
- dm_destroy_cb(egc, &dis->ddms, 0);
- return;
- }
-
-out:
- assert(rc);
- dis->callback(egc, dis, rc);
- return;
-}
-
-static void dm_destroy_cb(libxl__egc *egc,
- libxl__destroy_devicemodel_state *ddms,
- int rc)
-{
- libxl__destroy_domid_state *dis = CONTAINER_OF(ddms, *dis, ddms);
- STATE_AO_GC(dis->ao);
- uint32_t domid = dis->domid;
- uint32_t target_domid;
-
- if (rc < 0)
- LOGD(ERROR, domid, "libxl__destroy_device_model failed");
-
- if (libxl_is_stubdom(CTX, domid, &target_domid) &&
- libxl__stubdomain_is_linux_running(gc, target_domid)) {
- char *path = GCSPRINTF("/local/domain/%d/image/qmp-proxy-pid", domid);
-
- libxl__kill_xs_path(gc, path, "QMP Proxy");
- /* qmp-proxy for stubdom registers target_domid's QMP sockets. */
- libxl__qmp_cleanup(gc, target_domid);
- }
-
- dis->drs.ao = ao;
- dis->drs.domid = domid;
- dis->drs.callback = devices_destroy_cb;
- dis->drs.force.flag = LIBXL__FORCE_ON;
- libxl__devices_destroy(egc, &dis->drs);
-}
-
-static unsigned int libxl__get_domid_reuse_timeout(void)
-{
- const char *env_timeout = getenv("LIBXL_DOMID_REUSE_TIMEOUT");
-
- return env_timeout ? strtol(env_timeout, NULL, 0) :
- LIBXL_DOMID_REUSE_TIMEOUT;
-}
-
-char *libxl__domid_history_path(libxl__gc *gc, const char *suffix)
-{
- return GCSPRINTF("%s/domid-history%s", libxl__run_dir_path(),
- suffix ?: "");
-}
-
-int libxl_clear_domid_history(libxl_ctx *ctx)
-{
- GC_INIT(ctx);
- char *path;
- int rc = ERROR_FAIL;
-
- path = libxl__domid_history_path(gc, NULL);
- if (!path)
- goto out;
-
- if (unlink(path) < 0 && errno != ENOENT) {
- LOGE(ERROR, "failed to remove '%s'\n", path);
- goto out;
- }
-
- rc = 0;
-
-out:
- GC_FREE;
- return rc;
-}
-
-struct libxl__domid_history {
- long timeout;
- char *path;
- FILE *f;
- struct timespec ts;
-};
-
-static void libxl__domid_history_dispose(
- struct libxl__domid_history *ctxt)
-{
- if (ctxt->f) {
- fclose(ctxt->f);
- ctxt->f = NULL;
- }
-}
-
-static int libxl__open_domid_history(libxl__gc *gc,
- struct libxl__domid_history *ctxt)
-{
- ctxt->timeout = libxl__get_domid_reuse_timeout();
- ctxt->path = libxl__domid_history_path(gc, NULL);
-
- ctxt->f = fopen(ctxt->path, "r");
- if (!ctxt->f && errno != ENOENT) {
- LOGE(ERROR, "failed to open '%s'", ctxt->path);
- return ERROR_FAIL;
- }
-
- if (clock_gettime(CLOCK_MONOTONIC, &ctxt->ts)) {
- LOGE(ERROR, "failed to get time");
- libxl__domid_history_dispose(ctxt);
- return ERROR_FAIL;
- }
-
- return 0;
-}
-
-static int libxl__close_domid_history(libxl__gc *gc,
- struct libxl__domid_history *ctxt)
-{
- int r;
-
- if (!ctxt->f) return 0;
-
- r = fclose(ctxt->f);
- ctxt->f = NULL;
- if (r == EOF) {
- LOGE(ERROR, "failed to close '%s'", ctxt->path);
- return ERROR_FAIL;
- }
-
- return 0;
-}
-
-static int libxl__read_recent(libxl__gc *gc,
- struct libxl__domid_history *ctxt,
- unsigned long *sec, unsigned int *domid)
-{
- if (!ctxt->f) {
- *domid = INVALID_DOMID;
- return 0;
- }
-
- for (;;) {
- int r = fscanf(ctxt->f, "%lu %u", sec, domid);
-
- if (r == EOF) {
- if (ferror(ctxt->f)) {
- LOGE(ERROR, "failed to read from '%s'", ctxt->path);
- return ERROR_FAIL;
- }
-
- *domid = INVALID_DOMID;
- break;
- } else if (r == 2 && libxl_domid_valid_guest(*domid) &&
- ctxt->ts.tv_sec - *sec <= ctxt->timeout) {
- break;
- }
- }
-
- return 0;
-}
-
-static int libxl__mark_domid_recent(libxl__gc *gc, uint32_t domid)
-{
- libxl__flock *lock;
- struct libxl__domid_history ctxt = {};
- char *new;
- FILE *nf = NULL;
- int r, rc;
-
- lock = libxl__lock_domid_history(gc);
- if (!lock) {
- LOGED(ERROR, domid, "failed to acquire lock");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__open_domid_history(gc, &ctxt);
- if (rc) goto out;
-
- new = libxl__domid_history_path(gc, ".new");
- nf = fopen(new, "a");
- if (!nf) {
- LOGED(ERROR, domid, "failed to open '%s'", new);
- goto out;
- }
-
- for (;;) {
- unsigned long sec;
- unsigned int val;
-
- rc = libxl__read_recent(gc, &ctxt, &sec, &val);
- if (rc) goto out;
-
- if (val == INVALID_DOMID) /* EOF */
- break;
-
- r = fprintf(nf, "%lu %u\n", sec, val);
- if (r < 0) {
- LOGED(ERROR, domid, "failed to write to '%s'", new);
- goto out;
- }
- }
-
- r = fprintf(nf, "%lu %u\n", ctxt.ts.tv_sec, domid);
- if (r < 0) {
- LOGED(ERROR, domid, "failed to write to '%s'", new);
- goto out;
- }
-
- r = fclose(nf);
- nf = NULL;
- if (r == EOF) {
- LOGED(ERROR, domid, "failed to close '%s'", new);
- goto out;
- }
-
- rc = libxl__close_domid_history(gc, &ctxt);
- if (rc) goto out;
-
- r = rename(new, ctxt.path);
- if (r) {
- LOGE(ERROR, "failed to rename '%s' -> '%s'", new, ctxt.path);
- return ERROR_FAIL;
- }
-
-out:
- if (nf) fclose(nf);
- libxl__domid_history_dispose(&ctxt);
- if (lock) libxl__unlock_file(lock);
-
- return rc;
-}
-
-int libxl__is_domid_recent(libxl__gc *gc, uint32_t domid, bool *recent)
-{
- struct libxl__domid_history ctxt = {};
- int rc;
-
- rc = libxl__open_domid_history(gc, &ctxt);
- if (rc) goto out;
-
- *recent = false;
- for (;;) {
- unsigned long sec;
- unsigned int val;
-
- rc = libxl__read_recent(gc, &ctxt, &sec, &val);
- if (rc) goto out;
-
- if (val == INVALID_DOMID) /* EOF */
- break;
-
- if (val == domid && ctxt.ts.tv_sec - sec <= ctxt.timeout) {
- *recent = true;
- break;
- }
- }
-
- rc = libxl__close_domid_history(gc, &ctxt);
-
-out:
- libxl__domid_history_dispose(&ctxt);
-
- return rc;
-}
-
-static void devices_destroy_cb(libxl__egc *egc,
- libxl__devices_remove_state *drs,
- int rc)
-{
- STATE_AO_GC(drs->ao);
- libxl__destroy_domid_state *dis = CONTAINER_OF(drs, *dis, drs);
- libxl_ctx *ctx = CTX;
- uint32_t domid = dis->domid;
- char *dom_path;
- char *vm_path;
- libxl__flock *lock;
-
- dom_path = libxl__xs_get_dompath(gc, domid);
- if (!dom_path) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (rc < 0)
- LOGD(ERROR, domid, "libxl__devices_destroy failed");
-
- vm_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vm", dom_path));
- if (vm_path)
- if (!xs_rm(ctx->xsh, XBT_NULL, vm_path))
- LOGED(ERROR, domid, "xs_rm failed for %s", vm_path);
-
- if (!xs_rm(ctx->xsh, XBT_NULL, dom_path))
- LOGED(ERROR, domid, "xs_rm failed for %s", dom_path);
-
- xs_rm(ctx->xsh, XBT_NULL, libxl__xs_libxl_path(gc, domid));
- xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF( "/local/domain/%d/hvmloader", domid));
-
- /* This is async operation, we already hold CTX lock */
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
- libxl__userdata_destroyall(gc, domid);
-
- libxl__unlock_file(lock);
-
- /* Clean up qemu-save and qemu-resume files. They are
- * intermediate files created by libxc. Unfortunately they
- * don't fit in existing userdata scheme very well. In soft reset
- * case we need to keep the file.
- */
- if (!dis->soft_reset) {
- rc = libxl__remove_file(gc,
- libxl__device_model_savefile(gc, domid));
- if (rc < 0) goto out;
- }
- rc = libxl__remove_file(gc,
- GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", domid));
- if (rc < 0) goto out;
-
- rc = libxl__ev_child_fork(gc, &dis->destroyer, domain_destroy_domid_cb);
- if (rc < 0) goto out;
- if (!rc) { /* child */
- ctx->xch = xc_interface_open(ctx->lg,0,0);
- if (!ctx->xch) goto badchild;
-
- if (!dis->soft_reset) {
- rc = libxl__mark_domid_recent(gc, domid);
- if (rc) goto badchild;
- rc = xc_domain_destroy(ctx->xch, domid);
- } else {
- rc = xc_domain_pause(ctx->xch, domid);
- if (rc < 0) goto badchild;
- rc = xc_domain_soft_reset(ctx->xch, domid);
- if (rc < 0) goto badchild;
- rc = xc_domain_unpause(ctx->xch, domid);
- }
- if (rc < 0) goto badchild;
- _exit(0);
-
- badchild:
- if (errno > 0 && errno < 126) {
- _exit(errno);
- } else {
- LOGED(ERROR, domid,
- "xc_domain_destroy failed (with difficult errno value %d)",
- errno);
- _exit(-1);
- }
- }
- LOGD(DEBUG, domid, "Forked pid %ld for destroy of domain", (long)rc);
-
- return;
-
-out:
- dis->callback(egc, dis, rc);
- return;
-}
-
-static void domain_destroy_domid_cb(libxl__egc *egc,
- libxl__ev_child *destroyer,
- pid_t pid, int status)
-{
- libxl__destroy_domid_state *dis = CONTAINER_OF(destroyer, *dis, destroyer);
- STATE_AO_GC(dis->ao);
- int rc;
-
- if (status) {
- if (WIFEXITED(status) && WEXITSTATUS(status)<126) {
- LOGEVD(ERROR, WEXITSTATUS(status), dis->domid,
- "xc_domain_destroy failed");
- } else {
- libxl_report_child_exitstatus(CTX, XTL_ERROR,
- "async domain destroy", pid, status);
- }
- rc = ERROR_FAIL;
- goto out;
- }
- rc = 0;
-
- out:
- dis->callback(egc, dis, rc);
-}
-
-int libxl__get_domid(libxl__gc *gc, uint32_t *domid)
-{
- int rc;
- const char *xs_domid;
-
- rc = libxl__xs_read_checked(gc, XBT_NULL, DOMID_XS_PATH, &xs_domid);
- if (rc) goto out;
- if (!xs_domid) {
- LOG(ERROR, "failed to get own domid (%s)", DOMID_XS_PATH);
- rc = ERROR_FAIL;
- goto out;
- }
-
- *domid = atoi(xs_domid);
-
-out:
- return rc;
-}
-
-int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid)
-{
- if (!name)
- return 0;
- return libxl_domain_qualifier_to_domid(CTX, name, domid);
-}
-
-libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
- int *nr_vcpus_out, int *nr_cpus_out)
-{
- GC_INIT(ctx);
- libxl_vcpuinfo *ptr, *ret;
- xc_domaininfo_t domaininfo;
- xc_vcpuinfo_t vcpuinfo;
-
- if (xc_domain_getinfolist(ctx->xch, domid, 1, &domaininfo) != 1) {
- LOGED(ERROR, domid, "Getting infolist");
- GC_FREE;
- return NULL;
- }
-
- if (domaininfo.max_vcpu_id == XEN_INVALID_MAX_VCPU_ID) {
- GC_FREE;
- return NULL;
- }
-
- *nr_cpus_out = libxl_get_max_cpus(ctx);
- ret = ptr = libxl__calloc(NOGC, domaininfo.max_vcpu_id + 1,
- sizeof(libxl_vcpuinfo));
-
- for (*nr_vcpus_out = 0;
- *nr_vcpus_out <= domaininfo.max_vcpu_id;
- ++*nr_vcpus_out, ++ptr) {
- libxl_bitmap_init(&ptr->cpumap);
- if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap, 0))
- goto err;
- libxl_bitmap_init(&ptr->cpumap_soft);
- if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap_soft, 0))
- goto err;
- if (xc_vcpu_getinfo(ctx->xch, domid, *nr_vcpus_out, &vcpuinfo) == -1) {
- LOGED(ERROR, domid, "Getting vcpu info");
- goto err;
- }
-
- if (xc_vcpu_getaffinity(ctx->xch, domid, *nr_vcpus_out,
- ptr->cpumap.map, ptr->cpumap_soft.map,
- XEN_VCPUAFFINITY_SOFT|XEN_VCPUAFFINITY_HARD) == -1) {
- LOGED(ERROR, domid, "Getting vcpu affinity");
- goto err;
- }
- ptr->vcpuid = *nr_vcpus_out;
- ptr->cpu = vcpuinfo.cpu;
- ptr->online = !!vcpuinfo.online;
- ptr->blocked = !!vcpuinfo.blocked;
- ptr->running = !!vcpuinfo.running;
- ptr->vcpu_time = vcpuinfo.cpu_time;
- }
- GC_FREE;
- return ret;
-
-err:
- libxl_bitmap_dispose(&ptr->cpumap);
- libxl_bitmap_dispose(&ptr->cpumap_soft);
- free(ret);
- GC_FREE;
- return NULL;
-}
-
-static int libxl__set_vcpuonline_xenstore(libxl__gc *gc, uint32_t domid,
- const libxl_bitmap *cpumap,
- const libxl_dominfo *info)
-{
- char *dompath;
- xs_transaction_t t;
- int i, rc = ERROR_FAIL;
-
- if (!(dompath = libxl__xs_get_dompath(gc, domid)))
- goto out;
-
-retry_transaction:
- t = xs_transaction_start(CTX->xsh);
- for (i = 0; i <= info->vcpu_max_id; i++)
- libxl__xs_printf(gc, t,
- GCSPRINTF("%s/cpu/%u/availability", dompath, i),
- "%s", libxl_bitmap_test(cpumap, i) ? "online" : "offline");
- if (!xs_transaction_end(CTX->xsh, t, 0)) {
- if (errno == EAGAIN)
- goto retry_transaction;
- } else
- rc = 0;
-out:
- return rc;
-}
-
-static int qmp_parse_query_cpus(libxl__gc *gc,
- libxl_domid domid,
- const libxl__json_object *response,
- libxl_bitmap *const map)
-{
- int i;
- const libxl__json_object *cpu;
-
- libxl_bitmap_set_none(map);
- /* Parse response to QMP command "query-cpus":
- * [ { 'CPU': 'int',...} ]
- */
- for (i = 0; (cpu = libxl__json_array_get(response, i)); i++) {
- unsigned int cpu_index;
- const libxl__json_object *o;
-
- o = libxl__json_map_get("CPU", cpu, JSON_INTEGER);
- if (!o) {
- LOGD(ERROR, domid, "Failed to retrieve CPU index.");
- return ERROR_QEMU_API;
- }
-
- cpu_index = libxl__json_object_get_integer(o);
- libxl_bitmap_set(map, cpu_index);
- }
-
- return 0;
-}
-
-typedef struct set_vcpuonline_state {
- libxl__ev_qmp qmp;
- libxl__ev_time timeout;
- const libxl_bitmap *cpumap;
- libxl_dominfo info;
- libxl_bitmap final_map;
- int index; /* for loop on final_map */
-} set_vcpuonline_state;
-
-static void set_vcpuonline_qmp_cpus_queried(libxl__egc *,
- libxl__ev_qmp *, const libxl__json_object *, int rc);
-static void set_vcpuonline_qmp_add_cpu(libxl__egc *,
- libxl__ev_qmp *, const libxl__json_object *response, int rc);
-static void set_vcpuonline_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void set_vcpuonline_done(libxl__egc *egc,
- set_vcpuonline_state *svos, int rc);
-
-int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid,
- libxl_bitmap *cpumap,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- int rc, maxcpus;
- set_vcpuonline_state *svos;
-
- GCNEW(svos);
- libxl__ev_qmp_init(&svos->qmp);
- svos->qmp.ao = ao;
- svos->qmp.domid = domid;
- svos->qmp.payload_fd = -1;
- libxl__ev_time_init(&svos->timeout);
- svos->cpumap = cpumap;
- libxl_dominfo_init(&svos->info);
- libxl_bitmap_init(&svos->final_map);
-
- /* Convenience aliases */
- libxl_dominfo *info = &svos->info;
- libxl__ev_qmp *qmp = &svos->qmp;
-
- rc = libxl_domain_info(CTX, info, domid);
- if (rc < 0) {
- LOGED(ERROR, domid, "Getting domain info list");
- goto out;
- }
-
- maxcpus = libxl_bitmap_count_set(cpumap);
- if (maxcpus == 0)
- {
- LOGED(ERROR, domid, "Requested 0 VCPUs!");
- rc = ERROR_FAIL;
- goto out;
- }
- if (maxcpus > info->vcpu_max_id + 1)
- {
- LOGED(ERROR, domid, "Requested %d VCPUs, however maxcpus is %d!",
- maxcpus, info->vcpu_max_id + 1);
- rc = ERROR_FAIL;
- goto out;
- }
-
- switch (libxl__domain_type(gc, domid)) {
- case LIBXL_DOMAIN_TYPE_HVM:
- switch (libxl__device_model_version_running(gc, domid)) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- break;
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- rc = libxl__ev_time_register_rel(ao, &svos->timeout,
- set_vcpuonline_timeout,
- LIBXL_QMP_CMD_TIMEOUT * 1000);
- if (rc) goto out;
- qmp->callback = set_vcpuonline_qmp_cpus_queried;
- rc = libxl__ev_qmp_send(egc, qmp, "query-cpus", NULL);
- if (rc) goto out;
- return AO_INPROGRESS;
- default:
- rc = ERROR_INVAL;
- }
- break;
- case LIBXL_DOMAIN_TYPE_PVH:
- case LIBXL_DOMAIN_TYPE_PV:
- break;
- default:
- rc = ERROR_INVAL;
- }
-
-out:
- set_vcpuonline_done(egc, svos, rc); /* must be last */
- return AO_INPROGRESS;
-}
-
-static void set_vcpuonline_qmp_cpus_queried(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
-{
- EGC_GC;
- set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
- int i;
- libxl_bitmap current_map;
-
- /* Convenience aliases */
- libxl_bitmap *final_map = &svos->final_map;
-
- libxl_bitmap_init(¤t_map);
-
- if (rc) goto out;
-
- libxl_bitmap_alloc(CTX, ¤t_map, svos->info.vcpu_max_id + 1);
- rc = qmp_parse_query_cpus(gc, qmp->domid, response, ¤t_map);
- if (rc) goto out;
-
- libxl_bitmap_copy_alloc(CTX, final_map, svos->cpumap);
-
- libxl_for_each_set_bit(i, current_map) {
- libxl_bitmap_reset(final_map, i);
- }
-
-out:
- libxl_bitmap_dispose(¤t_map);
- svos->index = -1;
- set_vcpuonline_qmp_add_cpu(egc, qmp, NULL, rc); /* must be last */
-}
-
-static void set_vcpuonline_qmp_add_cpu(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
-{
- STATE_AO_GC(qmp->ao);
- set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
- libxl__json_object *args = NULL;
-
- /* Convenience aliases */
- libxl_bitmap *map = &svos->final_map;
-
- if (rc) goto out;
-
- while (libxl_bitmap_cpu_valid(map, ++svos->index)) {
- if (libxl_bitmap_test(map, svos->index)) {
- qmp->callback = set_vcpuonline_qmp_add_cpu;
- libxl__qmp_param_add_integer(gc, &args, "id", svos->index);
- rc = libxl__ev_qmp_send(egc, qmp, "cpu-add", args);
- if (rc) goto out;
- return;
- }
- }
-
-out:
- set_vcpuonline_done(egc, svos, rc);
-}
-
-static void set_vcpuonline_timeout(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- EGC_GC;
- set_vcpuonline_state *svos = CONTAINER_OF(ev, *svos, timeout);
-
- if (rc == ERROR_TIMEDOUT)
- LOGD(ERROR, svos->qmp.domid,
- "Setting CPU online in QEMU timed out");
-
- set_vcpuonline_done(egc, svos, rc);
-}
-
-static void set_vcpuonline_done(libxl__egc *egc,
- set_vcpuonline_state *svos,
- int rc)
-{
- STATE_AO_GC(svos->qmp.ao);
-
- /* Convenience aliases */
- libxl_domid domid = svos->qmp.domid;
-
- if (!rc)
- rc = libxl__set_vcpuonline_xenstore(gc, domid, svos->cpumap,
- &svos->info);
-
- libxl_bitmap_dispose(&svos->final_map);
- libxl_dominfo_dispose(&svos->info);
- libxl__ev_time_deregister(gc, &svos->timeout);
- libxl__ev_qmp_dispose(gc, &svos->qmp);
- libxl__ao_complete(egc, ao, rc);
-}
-
-static void domain_s3_resume_done(libxl__egc *egc, libxl__ev_qmp *qmp,
- const libxl__json_object *response,
- int rc);
-
-static void domain_s3_resume(libxl__ao *ao, libxl__egc *egc, int domid)
-{
- AO_GC;
- libxl__ev_qmp *qmp;
- int rc = 0;
- int r;
-
- GCNEW(qmp);
- libxl__ev_qmp_init(qmp);
- qmp->ao = ao;
- qmp->domid = domid;
- qmp->payload_fd = -1;
- qmp->callback = domain_s3_resume_done;
-
- switch (libxl__domain_type(gc, domid)) {
- case LIBXL_DOMAIN_TYPE_HVM:
- switch (libxl__device_model_version_running(gc, domid)) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- r = xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, 0);
- if (r) {
- LOGED(ERROR, domid, "Send trigger '%s' failed",
- libxl_trigger_to_string(LIBXL_TRIGGER_S3RESUME));
- rc = ERROR_FAIL;
- }
- break;
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- rc = libxl__ev_qmp_send(egc, qmp, "system_wakeup", NULL);
- if (rc) goto out;
- return;
- default:
- rc = ERROR_INVAL;
- break;
- }
- break;
- default:
- rc = ERROR_INVAL;
- break;
- }
-
-out:
- domain_s3_resume_done(egc, qmp, NULL, rc);
-}
-
-static void domain_s3_resume_done(libxl__egc *egc, libxl__ev_qmp *qmp,
- const libxl__json_object *response,
- int rc)
-{
- EGC_GC;
-
- if (rc)
- LOGD(ERROR, qmp->domid, "Send trigger '%s' failed, rc=%d",
- libxl_trigger_to_string(LIBXL_TRIGGER_S3RESUME), rc);
-
- libxl__ev_qmp_dispose(gc, qmp);
- libxl__ao_complete(egc, qmp->ao, rc);
-}
-
-int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid,
- libxl_trigger trigger, uint32_t vcpuid,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- int rc;
-
- switch (trigger) {
- case LIBXL_TRIGGER_POWER:
- rc = xc_domain_send_trigger(ctx->xch, domid,
- XEN_DOMCTL_SENDTRIGGER_POWER, vcpuid);
- break;
- case LIBXL_TRIGGER_SLEEP:
- rc = xc_domain_send_trigger(ctx->xch, domid,
- XEN_DOMCTL_SENDTRIGGER_SLEEP, vcpuid);
- break;
- case LIBXL_TRIGGER_NMI:
- rc = xc_domain_send_trigger(ctx->xch, domid,
- XEN_DOMCTL_SENDTRIGGER_NMI, vcpuid);
- break;
- case LIBXL_TRIGGER_INIT:
- rc = xc_domain_send_trigger(ctx->xch, domid,
- XEN_DOMCTL_SENDTRIGGER_INIT, vcpuid);
- break;
- case LIBXL_TRIGGER_RESET:
- rc = xc_domain_send_trigger(ctx->xch, domid,
- XEN_DOMCTL_SENDTRIGGER_RESET, vcpuid);
- break;
- case LIBXL_TRIGGER_S3RESUME:
- domain_s3_resume(ao, egc, domid); /* must be last */
- return AO_INPROGRESS;
- default:
- rc = -1;
- errno = EINVAL;
- break;
- }
-
- if (rc != 0) {
- LOGED(ERROR, domid, "Send trigger '%s' failed",
- libxl_trigger_to_string(trigger));
- rc = ERROR_FAIL;
- goto out;
- }
-
- libxl__ao_complete(egc, ao, rc);
- return AO_INPROGRESS;
-out:
- return AO_CREATE_FAIL(rc);
-}
-
-uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid)
-{
- GC_INIT(ctx);
- char *dompath = libxl__xs_get_dompath(gc, domid);
- char *vm_path, *start_time;
- uint32_t ret;
-
- vm_path = libxl__xs_read(
- gc, XBT_NULL, GCSPRINTF("%s/vm", dompath));
- start_time = libxl__xs_read(
- gc, XBT_NULL, GCSPRINTF("%s/start_time", vm_path));
- if (start_time == NULL) {
- LOGEVD(ERROR, -1, domid, "Can't get start time of domain");
- ret = -1;
- }else{
- ret = strtoul(start_time, NULL, 10);
- }
- GC_FREE;
- return ret;
-}
-
-static int libxl__update_avail_vcpus_xenstore(libxl__gc *gc, uint32_t domid,
- unsigned int max_vcpus,
- libxl_bitmap *map)
-{
- int rc;
- unsigned int i;
- const char *dompath;
-
- dompath = libxl__xs_get_dompath(gc, domid);
- if (!dompath) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- for (i = 0; i < max_vcpus; i++) {
- const char *path = GCSPRINTF("%s/cpu/%u/availability", dompath, i);
- const char *content;
- rc = libxl__xs_read_checked(gc, XBT_NULL, path, &content);
- if (rc) goto out;
- if (content && !strcmp(content, "online"))
- libxl_bitmap_set(map, i);
- }
-
- rc = 0;
-out:
- return rc;
-}
-
-typedef struct {
- libxl__ev_qmp qmp;
- libxl__ev_time timeout;
- libxl_domain_config *d_config; /* user pointer */
- libxl__ev_slowlock devlock;
- libxl_bitmap qemuu_cpus;
-} retrieve_domain_configuration_state;
-
-static void retrieve_domain_configuration_lock_acquired(
- libxl__egc *egc, libxl__ev_slowlock *, int rc);
-static void retrieve_domain_configuration_cpu_queried(
- libxl__egc *egc, libxl__ev_qmp *qmp,
- const libxl__json_object *response, int rc);
-static void retrieve_domain_configuration_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void retrieve_domain_configuration_end(libxl__egc *egc,
- retrieve_domain_configuration_state *rdcs, int rc);
-
-int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
- libxl_domain_config *d_config,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- retrieve_domain_configuration_state *rdcs;
-
- GCNEW(rdcs);
- libxl__ev_qmp_init(&rdcs->qmp);
- rdcs->qmp.ao = ao;
- rdcs->qmp.domid = domid;
- rdcs->qmp.payload_fd = -1;
- libxl__ev_time_init(&rdcs->timeout);
- rdcs->d_config = d_config;
- libxl_bitmap_init(&rdcs->qemuu_cpus);
- libxl__ev_devlock_init(&rdcs->devlock);
- rdcs->devlock.ao = ao;
- rdcs->devlock.domid = domid;
- rdcs->devlock.callback = retrieve_domain_configuration_lock_acquired;
- libxl__ev_slowlock_lock(egc, &rdcs->devlock);
- return AO_INPROGRESS;
-}
-
-static void retrieve_domain_configuration_lock_acquired(
- libxl__egc *egc, libxl__ev_slowlock *devlock, int rc)
-{
- retrieve_domain_configuration_state *rdcs =
- CONTAINER_OF(devlock, *rdcs, devlock);
- STATE_AO_GC(rdcs->qmp.ao);
- libxl__flock *lock = NULL;
- bool has_callback = false;
-
- /* Convenience aliases */
- libxl_domid domid = rdcs->qmp.domid;
- libxl_domain_config *const d_config = rdcs->d_config;
-
- if (rc) goto out;
-
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- rc = libxl__get_domain_configuration(gc, domid, d_config);
- if (rc) {
- LOGD(ERROR, domid, "Fail to get domain configuration");
- rc = ERROR_FAIL;
- goto out;
- }
-
- libxl__unlock_file(lock);
- lock = NULL;
-
- /* We start by querying QEMU, if it is running, for its cpumap as this
- * is a long operation. */
- if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM &&
- libxl__device_model_version_running(gc, domid) ==
- LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
- /* For QEMU upstream we always need to provide the number
- * of cpus present to QEMU whether they are online or not;
- * otherwise QEMU won't accept the saved state.
- */
- rc = libxl__ev_time_register_rel(ao, &rdcs->timeout,
- retrieve_domain_configuration_timeout,
- LIBXL_QMP_CMD_TIMEOUT * 1000);
- if (rc) goto out;
- libxl_bitmap_alloc(CTX, &rdcs->qemuu_cpus,
- d_config->b_info.max_vcpus);
- rdcs->qmp.callback = retrieve_domain_configuration_cpu_queried;
- rc = libxl__ev_qmp_send(egc, &rdcs->qmp, "query-cpus", NULL);
- if (rc) goto out;
- has_callback = true;
- }
-
-out:
- if (lock) libxl__unlock_file(lock);
- if (!has_callback)
- retrieve_domain_configuration_end(egc, rdcs, rc);
-}
-
-static void retrieve_domain_configuration_cpu_queried(
- libxl__egc *egc, libxl__ev_qmp *qmp,
- const libxl__json_object *response, int rc)
-{
- EGC_GC;
- retrieve_domain_configuration_state *rdcs =
- CONTAINER_OF(qmp, *rdcs, qmp);
-
- if (rc) goto out;
-
- rc = qmp_parse_query_cpus(gc, qmp->domid, response, &rdcs->qemuu_cpus);
-
-out:
- retrieve_domain_configuration_end(egc, rdcs, rc);
-}
-
-static void retrieve_domain_configuration_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
-{
- retrieve_domain_configuration_state *rdcs =
- CONTAINER_OF(ev, *rdcs, timeout);
-
- retrieve_domain_configuration_end(egc, rdcs, rc);
-}
-
-static void retrieve_domain_configuration_end(libxl__egc *egc,
- retrieve_domain_configuration_state *rdcs, int rc)
-{
- STATE_AO_GC(rdcs->qmp.ao);
- libxl__flock *lock = NULL;
-
- /* Convenience aliases */
- libxl_domain_config *const d_config = rdcs->d_config;
- libxl_domid domid = rdcs->qmp.domid;
-
- if (rc) goto out;
-
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- /* Domain name */
- {
- char *domname;
- domname = libxl_domid_to_name(CTX, domid);
- if (!domname) {
- LOGD(ERROR, domid, "Fail to get domain name");
- goto out;
- }
- free(d_config->c_info.name);
- d_config->c_info.name = domname; /* steals allocation */
- }
-
- /* Domain UUID */
- {
- libxl_dominfo info;
- libxl_dominfo_init(&info);
- rc = libxl_domain_info(CTX, &info, domid);
- if (rc) {
- LOGD(ERROR, domid, "Fail to get domain info");
- libxl_dominfo_dispose(&info);
- goto out;
- }
- libxl_uuid_copy(CTX, &d_config->c_info.uuid, &info.uuid);
- libxl_dominfo_dispose(&info);
- }
-
- /* VCPUs */
- {
- libxl_bitmap *map = &d_config->b_info.avail_vcpus;
- unsigned int max_vcpus = d_config->b_info.max_vcpus;
- libxl_device_model_version version;
-
- libxl_bitmap_dispose(map);
- libxl_bitmap_init(map);
- libxl_bitmap_alloc(CTX, map, max_vcpus);
- libxl_bitmap_set_none(map);
-
- switch (d_config->b_info.type) {
- case LIBXL_DOMAIN_TYPE_HVM:
- version = libxl__device_model_version_running(gc, domid);
- assert(version != LIBXL_DEVICE_MODEL_VERSION_UNKNOWN);
- switch (version) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- libxl_bitmap_copy(CTX, map, &rdcs->qemuu_cpus);
- break;
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- rc = libxl__update_avail_vcpus_xenstore(gc, domid,
- max_vcpus, map);
- break;
- default:
- abort();
- }
- break;
- case LIBXL_DOMAIN_TYPE_PVH:
- case LIBXL_DOMAIN_TYPE_PV:
- rc = libxl__update_avail_vcpus_xenstore(gc, domid,
- max_vcpus, map);
- break;
- default:
- abort();
- }
-
- if (rc) {
- LOGD(ERROR, domid, "Fail to update available cpu map");
- goto out;
- }
- }
-
-
- /* Memory limits:
- *
- * Currently there are three memory limits:
- * 1. "target" in xenstore (originally memory= in config file)
- * 2. "static-max" in xenstore (originally maxmem= in config file)
- * 3. "max_memkb" in hypervisor
- *
- * The third one is not visible and currently managed by
- * toolstack. In order to rebuild a domain we only need to have
- * "target" and "static-max".
- */
- {
- uint64_t target_memkb = 0, max_memkb = 0;
-
- /* "target" */
- rc = libxl__get_memory_target(gc, domid, &target_memkb, &max_memkb);
- if (rc) {
- LOGD(ERROR, domid, "Fail to get memory target");
- goto out;
- }
-
- /* libxl__get_targetmem_fudge() calculates the difference from
- * what is in xenstore to what we have in the domain build info.
- */
- d_config->b_info.target_memkb = target_memkb +
- libxl__get_targetmem_fudge(gc, &d_config->b_info);
-
- d_config->b_info.max_memkb = max_memkb;
- }
-
- /* Scheduler params */
- {
- libxl_domain_sched_params_dispose(&d_config->b_info.sched_params);
- rc = libxl_domain_sched_params_get(CTX, domid,
- &d_config->b_info.sched_params);
- if (rc) {
- LOGD(ERROR, domid, "Fail to get scheduler parameters");
- goto out;
- }
- }
-
- /* Devices: disk, nic, vtpm, pcidev etc. */
-
- /* The MERGE macro implements following logic:
- * 0. retrieve JSON (done by now)
- * 1. retrieve list of device from xenstore
- * 2. use xenstore entries as primary reference and compare JSON
- * entries with them.
- * a. if a device is present in xenstore and in JSON, merge the
- * two views.
- * b. if a device is not present in xenstore but in JSON, delete
- * it from the result.
- * c. it's impossible to have an entry present in xenstore but
- * not in JSON, because we maintain an invariant that every
- * entry in xenstore must have a corresponding entry in JSON.
- * 3. "merge" operates on "src" and "dst". "src" points to the
- * entry retrieved from xenstore while "dst" points to the entry
- * retrieve from JSON.
- */
- {
- const libxl__device_type *dt;
- int idx;
-
- for (idx = 0;; idx++) {
- void *p = NULL;
- void **devs;
- int i, j, num;
- int *num_dev;
-
- dt = device_type_tbl[idx];
- if (!dt)
- break;
-
- if (!dt->compare)
- continue;
-
- num_dev = libxl__device_type_get_num(dt, d_config);
- p = libxl__device_list(gc, dt, domid, &num);
- if (p == NULL) {
- LOGD(DEBUG, domid, "No %s from xenstore",
- libxl__device_kind_to_string(dt->type));
- }
- devs = libxl__device_type_get_ptr(dt, d_config);
-
- for (i = 0; i < *num_dev; i++) {
- void *q;
-
- q = libxl__device_type_get_elem(dt, d_config, i);
- for (j = 0; j < num; j++) {
- if (dt->compare(p + dt->dev_elem_size * j, q))
- break;
- }
-
- if (j < num) { /* found in xenstore */
- if (dt->merge)
- dt->merge(CTX, p + dt->dev_elem_size * j, q);
- } else { /* not found in xenstore */
- LOGD(WARN, domid,
- "Device present in JSON but not in xenstore, ignored");
-
- dt->dispose(q);
-
- for (j = i; j < *num_dev - 1; j++)
- memcpy(libxl__device_type_get_elem(dt, d_config, j),
- libxl__device_type_get_elem(dt, d_config, j+1),
- dt->dev_elem_size);
-
- /* rewind counters */
- (*num_dev)--;
- i--;
-
- *devs = libxl__realloc(NOGC, *devs,
- dt->dev_elem_size * *num_dev);
- }
- }
-
- for (i = 0; i < num; i++)
- dt->dispose(p + dt->dev_elem_size * i);
- free(p);
- }
- }
-
-out:
- libxl__ev_slowlock_unlock(gc, &rdcs->devlock);
- if (lock) libxl__unlock_file(lock);
- libxl_bitmap_dispose(&rdcs->qemuu_cpus);
- libxl__ev_qmp_dispose(gc, &rdcs->qmp);
- libxl__ev_time_deregister(gc, &rdcs->timeout);
- libxl__ao_complete(egc, ao, rc);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2011 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-/*
- * Internal event machinery for use by other parts of libxl
- */
-
-#include <poll.h>
-
-#include "libxl_internal.h"
-
-
-//#define DEBUG 1
-
-#ifdef DEBUG
-# define LIBXL__DBG_LOG(ctx, args, ...) \
- LIBXL__LOG((ctx), XTL_DEBUG, args, __VA_ARGS__)
-#else
-# define LIBXL__DBG_LOG(ctx, args, ...) ((void)0)
-#endif
-#define DBG(args, ...) LIBXL__DBG_LOG(CTX, args, __VA_ARGS__)
-
-
-static libxl__ao *ao_nested_root(libxl__ao *ao);
-
-static void ao__check_destroy(libxl_ctx *ctx, libxl__ao *ao);
-
-
-/*
- * osevent update baton handling
- *
- * We need the following property (the "unstale liveness property"):
- *
- * Whenever any thread is blocking as a result of being given an fd
- * set or timeout by libxl, at least one thread must be using an up to
- * date osevent set. It is OK for all but one threads to have stale
- * event sets, because so long as one waiting thread has the right
- * event set, any actually interesting event will, if nothing else,
- * wake that "right" thread up. It will then make some progress
- * and/or, if it exits, ensure that some other thread becomes the
- * "right" thread.
- *
- * For threads blocking outside libxl and which are receiving libxl's
- * fd and timeout information via the libxl_osevent_hooks callbacks,
- * libxl calls this function as soon as it becomes interested. It is
- * the responsiblity of a provider of these functions in a
- * multithreaded environment to make arrangements to wake up event
- * waiting thread(s) with stale event sets.
- *
- * Waiters outside libxl using _beforepoll are dealt with below.
- *
- * For the libxl event loop, the argument is as follows:
- *
- * The issue we are concerned about is libxl sleeping on an out of
- * date fd set, or too long a timeout, so that it doesn't make
- * progress. If the property above is satisfied, then if any thread
- * is waiting in libxl at least one such thread will be waiting on a
- * sufficient osevent set, so any relevant osevent will wake up a
- * libxl thread which will either handle the event, or arrange that at
- * least one other libxl thread has the right set.
- *
- * There are two calls to poll in libxl: one is the fd recheck, which
- * is not blocking. There is only the one blocking call, in
- * eventloop_iteration. poll runs with the ctx unlocked, so osevents
- * might be added after it unlocks the ctx - that is what we are
- * worried about.
- *
- * To demonstrate that the unstale liveness property is satisfied:
- *
- * We define a baton holder as follows: a libxl thread is a baton
- * holder if
- * (a) it has an egc or an ao and holds the ctx lock, or
- * (b) it has an active non-app poller and no osevents have been
- * added since it released the lock, or
- * (c) it has an active non-app poller which has been woken
- * (by writing to its pipe), so it will not sleep
- * We will maintain the invariant (the "baton invariant") that
- * whenever there is any active poller, there is at least
- * one baton holder. ("non-app" means simply "not poller_app".)
- *
- * No thread outside libxl can have an active non-app poller: pollers
- * are put on the active list by poller_get which is called in three
- * places: libxl_event_wait, which puts it before returning;
- * libxl__ao_create but only in the synchronous case, in which case
- * the poller is put before returning; and the poller_app, during
- * initialisation.
- *
- * So any time when all libxl threads are blocking (and therefore do
- * not have the ctx lock), the non-app active pollers belong to those
- * threads. If at least one is a baton holder (the invariant), that
- * thread has a good enough event set.
- *
- * Now we will demonstrate that the "baton invariant" is maintained:
- *
- * The rule is that any thread which might be the baton holder is
- * responsible for checking that there continues to be a baton holder
- * as needed.
- *
- * Firstly, consider the case when the baton holders (b) cease to be
- * baton holders because osevents are added.
- *
- * There are only two kinds of osevents: timeouts and fds. Every
- * other internal event source reduces to one of these eventually.
- * Both of these cases are handled (in the case of fd events, add and
- * modify, separately), calling pollers_note_osevent_added.
- *
- * This walks the poller_active list, marking the active pollers
- * osevents_added=1. Such a poller cannot be the baton holder. But
- * pollers_note_osevent_added is called only from ev_* functions,
- * which are only called from event-chain libxl code: ie, code with an
- * ao or an egc. So at this point we are a baton holder, and there is
- * still a baton holder.
- *
- * Secondly, consider the case where baton holders (a) cease to be
- * batton holders because they dispose of their egc or ao. We call
- * libxl__egc_ao_cleanup_1_baton on every exit path. We arrange that
- * everything that disposes of an egc or an ao checks that there is a
- * new baton holder by calling libxl__egc_ao_cleanup_1_baton.
- *
- * This function handles the invariant explicitly: if we have any
- * non-app active pollers it looks for one which is up to date (baton
- * holder category (b)), and failing that it picks a victim to turn
- * into the baton holder category (c) by waking it up. (Correctness
- * depends on this function not spotting its own thread as the
- * baton-holder, since it is on its way to not being the baton-holder,
- * so it must be called after the poller has been put back.)
- *
- * Thirdly, we must consider the case (c). A thread in category (c)
- * will reenter libxl when it gains the lock and necessarily then
- * becomes a baton holder in category (a).
- *
- * So the "baton invariant" is maintained.
- * QED (for waiters in libxl).
- *
- *
- * For waiters outside libxl which used libxl_osevent_beforepoll
- * to get the fd set:
- *
- * As above, adding an osevent involves having an egc or an ao.
- * It sets poller->osevents_added on all active pollers. Notably
- * it sets it on poller_app, which is always active.
- *
- * The thread which does this will dispose of its egc or ao before
- * exiting libxl so it will always wake up the poller_app if the last
- * call to _beforepoll was before the osevents were added. So the
- * application's fd set contains at least a wakeup in the form of the
- * poller_app fd. The application cannot sleep on the libxl fd set
- * until it has called _afterpoll which empties the pipe, and it
- * is expected to then call _beforepoll again before sleeping.
- *
- * So all the application's event waiting thread(s) will always have
- * an up to date osevent set, and will be woken up if necessary to
- * achieve this. (This is in contrast libxl's own event loop where
- * only one thread need be up to date, as discussed above.)
- */
-static void pollers_note_osevent_added(libxl_ctx *ctx) {
- libxl__poller *poller;
- LIBXL_LIST_FOREACH(poller, &ctx->pollers_active, active_entry)
- poller->osevents_added = 1;
-}
-
-static void baton_wake(libxl__gc *gc, libxl__poller *wake)
-{
- libxl__poller_wakeup(gc, wake);
-
- wake->osevents_added = 0;
- /* This serves to make _1_baton idempotent. It is OK even though
- * that poller may currently be sleeping on only old osevents,
- * because it is going to wake up because we've just prodded it,
- * and it pick up new osevents on its next iteration (or pass
- * on the baton). */
-}
-
-void libxl__egc_ao_cleanup_1_baton(libxl__gc *gc)
- /* Any poller we had must have been `put' already. */
-{
- libxl__poller *search, *wake=0;
-
- if (CTX->poller_app->osevents_added)
- baton_wake(gc, CTX->poller_app);
-
- LIBXL_LIST_FOREACH(search, &CTX->pollers_active, active_entry) {
- if (search == CTX->poller_app)
- /* This one is special. We can't give it the baton. */
- continue;
- if (!search->osevents_added)
- /* This poller is up to date and will wake up as needed. */
- return;
- if (!wake)
- wake = search;
- }
-
- if (!wake)
- /* no-one in libxl waiting for any events */
- return;
-
- baton_wake(gc, wake);
-}
-
-/*
- * The counter osevent_in_hook is used to ensure that the application
- * honours the reentrancy restriction documented in libxl_event.h.
- *
- * The application's registration hooks should be called ONLY via
- * these macros, with the ctx locked. Likewise all the "occurred"
- * entrypoints from the application should assert(!in_hook);
- *
- * During the hook call - including while the arguments are being
- * evaluated - ev->nexus is guaranteed to be valid and refer to the
- * nexus which is being used for this event registration. The
- * arguments should specify ev->nexus for the for_libxl argument and
- * ev->nexus->for_app_reg (or a pointer to it) for for_app_reg.
- */
-#define OSEVENT_HOOK_INTERN(retval, failedp, evkind, hookop, nexusop, ...) do { \
- if (CTX->osevent_hooks) { \
- CTX->osevent_in_hook++; \
- libxl__osevent_hook_nexi *nexi = &CTX->hook_##evkind##_nexi_idle; \
- osevent_hook_pre_##nexusop(gc, ev, nexi, &ev->nexus); \
- retval CTX->osevent_hooks->evkind##_##hookop \
- (CTX->osevent_user, __VA_ARGS__); \
- if ((failedp)) \
- osevent_hook_failed_##nexusop(gc, ev, nexi, &ev->nexus); \
- CTX->osevent_in_hook--; \
- } \
-} while (0)
-
-#define OSEVENT_HOOK(evkind, hookop, nexusop, ...) ({ \
- int osevent_hook_rc = 0; \
- OSEVENT_HOOK_INTERN(osevent_hook_rc =, !!osevent_hook_rc, \
- evkind, hookop, nexusop, __VA_ARGS__); \
- osevent_hook_rc; \
-})
-
-#define OSEVENT_HOOK_VOID(evkind, hookop, nexusop, ...) \
- OSEVENT_HOOK_INTERN(/* void */, 0, evkind, hookop, nexusop, __VA_ARGS__)
-
-/*
- * The application's calls to libxl_osevent_occurred_... may be
- * indefinitely delayed with respect to the rest of the program (since
- * they are not necessarily called with any lock held). So the
- * for_libxl value we receive may be (almost) arbitrarily old. All we
- * know is that it came from this ctx.
- *
- * Therefore we may not free the object referred to by any for_libxl
- * value until we free the whole libxl_ctx. And if we reuse it we
- * must be able to tell when an old use turns up, and discard the
- * stale event.
- *
- * Thus we cannot use the ev directly as the for_libxl value - we need
- * a layer of indirection.
- *
- * We do this by keeping a pool of libxl__osevent_hook_nexus structs,
- * and use pointers to them as for_libxl values. In fact, there are
- * two pools: one for fds and one for timeouts. This ensures that we
- * don't risk a type error when we upcast nexus->ev. In each nexus
- * the ev is either null or points to a valid libxl__ev_time or
- * libxl__ev_fd, as applicable.
- *
- * We /do/ allow ourselves to reassociate an old nexus with a new ev
- * as otherwise we would have to leak nexi. (This reassociation
- * might, of course, be an old ev being reused for a new purpose so
- * simply comparing the ev pointer is not sufficient.) Thus the
- * libxl_osevent_occurred functions need to check that the condition
- * allegedly signalled by this event actually exists.
- *
- * The nexi and the lists are all protected by the ctx lock.
- */
-
-struct libxl__osevent_hook_nexus {
- void *ev;
- void *for_app_reg;
- LIBXL_SLIST_ENTRY(libxl__osevent_hook_nexus) next;
-};
-
-static void *osevent_ev_from_hook_nexus(libxl_ctx *ctx,
- libxl__osevent_hook_nexus *nexus /* pass void *for_libxl */)
-{
- return nexus->ev;
-}
-
-static void osevent_release_nexus(libxl__gc *gc,
- libxl__osevent_hook_nexi *nexi_idle,
- libxl__osevent_hook_nexus *nexus)
-{
- nexus->ev = 0;
- LIBXL_SLIST_INSERT_HEAD(nexi_idle, nexus, next);
-}
-
-/*----- OSEVENT* hook functions for nexusop "alloc" -----*/
-static void osevent_hook_pre_alloc(libxl__gc *gc, void *ev,
- libxl__osevent_hook_nexi *nexi_idle,
- libxl__osevent_hook_nexus **nexus_r)
-{
- libxl__osevent_hook_nexus *nexus = LIBXL_SLIST_FIRST(nexi_idle);
- if (nexus) {
- LIBXL_SLIST_REMOVE_HEAD(nexi_idle, next);
- } else {
- nexus = libxl__zalloc(NOGC, sizeof(*nexus));
- }
- nexus->ev = ev;
- *nexus_r = nexus;
-}
-static void osevent_hook_failed_alloc(libxl__gc *gc, void *ev,
- libxl__osevent_hook_nexi *nexi_idle,
- libxl__osevent_hook_nexus **nexus)
-{
- osevent_release_nexus(gc, nexi_idle, *nexus);
-}
-
-/*----- OSEVENT* hook functions for nexusop "release" -----*/
-static void osevent_hook_pre_release(libxl__gc *gc, void *ev,
- libxl__osevent_hook_nexi *nexi_idle,
- libxl__osevent_hook_nexus **nexus)
-{
- osevent_release_nexus(gc, nexi_idle, *nexus);
-}
-static void osevent_hook_failed_release(libxl__gc *gc, void *ev,
- libxl__osevent_hook_nexi *nexi_idle,
- libxl__osevent_hook_nexus **nexus)
-{
- abort();
-}
-
-/*----- OSEVENT* hook functions for nexusop "noop" -----*/
-static void osevent_hook_pre_noop(libxl__gc *gc, void *ev,
- libxl__osevent_hook_nexi *nexi_idle,
- libxl__osevent_hook_nexus **nexus) { }
-static void osevent_hook_failed_noop(libxl__gc *gc, void *ev,
- libxl__osevent_hook_nexi *nexi_idle,
- libxl__osevent_hook_nexus **nexus) { }
-
-
-/*
- * fd events
- */
-
-int libxl__ev_fd_register(libxl__gc *gc, libxl__ev_fd *ev,
- libxl__ev_fd_callback *func,
- int fd, short events)
-{
- int rc;
-
- assert(fd >= 0);
-
- CTX_LOCK;
-
- DBG("ev_fd=%p register fd=%d events=%x", ev, fd, events);
-
- rc = OSEVENT_HOOK(fd,register, alloc, fd, &ev->nexus->for_app_reg,
- events, ev->nexus);
- if (rc) goto out;
-
- ev->fd = fd;
- ev->events = events;
- ev->func = func;
-
- LIBXL_LIST_INSERT_HEAD(&CTX->efds, ev, entry);
- pollers_note_osevent_added(CTX);
-
- rc = 0;
-
- out:
- CTX_UNLOCK;
- return rc;
-}
-
-int libxl__ev_fd_modify(libxl__gc *gc, libxl__ev_fd *ev, short events)
-{
- int rc;
-
- CTX_LOCK;
- assert(libxl__ev_fd_isregistered(ev));
-
- DBG("ev_fd=%p modify fd=%d events=%x", ev, ev->fd, events);
-
- rc = OSEVENT_HOOK(fd,modify, noop, ev->fd, &ev->nexus->for_app_reg, events);
- if (rc) goto out;
-
- if ((events & ~ev->events))
- pollers_note_osevent_added(CTX);
- ev->events = events;
-
- rc = 0;
- out:
- CTX_UNLOCK;
- return rc;
-}
-
-void libxl__ev_fd_deregister(libxl__gc *gc, libxl__ev_fd *ev)
-{
- CTX_LOCK;
- libxl__poller *poller;
-
- if (!libxl__ev_fd_isregistered(ev)) {
- DBG("ev_fd=%p deregister unregistered",ev);
- goto out;
- }
-
- DBG("ev_fd=%p deregister fd=%d", ev, ev->fd);
-
- OSEVENT_HOOK_VOID(fd,deregister, release, ev->fd, ev->nexus->for_app_reg);
- LIBXL_LIST_REMOVE(ev, entry);
- ev->fd = -1;
-
- LIBXL_LIST_FOREACH(poller, &CTX->pollers_active, active_entry)
- poller->fds_deregistered = 1;
-
- out:
- CTX_UNLOCK;
-}
-
-short libxl__fd_poll_recheck(libxl__egc *egc, int fd, short events) {
- struct pollfd check;
- int r;
- EGC_GC;
-
- for (;;) {
- check.fd = fd;
- check.events = events;
- r = poll(&check, 1, 0);
- DBG("poll recheck fd=%d r=%d revents=%#x", fd, r, check.revents);
- if (!r)
- break;
- if (r==1)
- break;
- assert(r<0);
- if (errno != EINTR) {
- LIBXL__EVENT_DISASTER(gc, "failed poll to check for fd", errno, 0);
- return 0;
- }
- }
- assert(!!r == !!check.revents);
- return check.revents;
-}
-
-/*
- * timeouts
- */
-
-
-int libxl__gettimeofday(libxl__gc *gc, struct timeval *now_r)
-{
- int rc = gettimeofday(now_r, 0);
- if (rc) {
- LOGE(ERROR, "gettimeofday failed");
- return ERROR_FAIL;
- }
- return 0;
-}
-
-static int time_rel_to_abs(libxl__gc *gc, int ms, struct timeval *abs_out)
-{
- int rc;
- struct timeval additional = {
- .tv_sec = ms / 1000,
- .tv_usec = (ms % 1000) * 1000
- };
- struct timeval now;
-
- rc = libxl__gettimeofday(gc, &now);
- if (rc) return rc;
-
- timeradd(&now, &additional, abs_out);
- return 0;
-}
-
-static int time_register_finite(libxl__gc *gc, libxl__ev_time *ev,
- struct timeval absolute)
-{
- int rc;
- libxl__ev_time *evsearch;
-
- rc = OSEVENT_HOOK(timeout,register, alloc, &ev->nexus->for_app_reg,
- absolute, ev->nexus);
- if (rc) return rc;
-
- ev->infinite = 0;
- ev->abs = absolute;
- LIBXL_TAILQ_INSERT_SORTED(&CTX->etimes, entry, ev, evsearch, /*empty*/,
- timercmp(&ev->abs, &evsearch->abs, >));
-
- pollers_note_osevent_added(CTX);
- return 0;
-}
-
-static void time_deregister(libxl__gc *gc, libxl__ev_time *ev)
-{
- libxl__ao_abortable_deregister(&ev->abrt);
-
- if (!ev->infinite) {
- struct timeval right_away = { 0, 0 };
- if (ev->nexus) /* only set if app provided hooks */
- ev->nexus->ev = 0;
- OSEVENT_HOOK_VOID(timeout,modify,
- noop /* release nexus in _occurred_ */,
- &ev->nexus->for_app_reg, right_away);
- LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry);
- }
-}
-
-static void time_done_debug(libxl__gc *gc, const char *func,
- libxl__ev_time *ev, int rc)
-{
-#ifdef DEBUG
- libxl__log(CTX, XTL_DEBUG, -1, __FILE__, 0, func, INVALID_DOMID,
- "ev_time=%p done rc=%d .func=%p infinite=%d abs=%lu.%06lu",
- ev, rc, ev->func, ev->infinite,
- (unsigned long)ev->abs.tv_sec, (unsigned long)ev->abs.tv_usec);
-#endif
-}
-
-static void time_aborted(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
-{
- libxl__ev_time *ev = CONTAINER_OF(abrt, *ev, abrt);
- EGC_GC;
-
- time_deregister(gc, ev);
- DBG("ev_time=%p aborted", ev);
- ev->func(egc, ev, &ev->abs, rc);
-}
-
-static int time_register_abortable(libxl__ao *ao, libxl__ev_time *ev)
-{
- ev->abrt.ao = ao;
- ev->abrt.callback = time_aborted;
- return libxl__ao_abortable_register(&ev->abrt);
-}
-
-int libxl__ev_time_register_abs(libxl__ao *ao, libxl__ev_time *ev,
- libxl__ev_time_callback *func,
- struct timeval absolute)
-{
- AO_GC;
- int rc;
-
- CTX_LOCK;
-
- DBG("ev_time=%p register abs=%lu.%06lu",
- ev, (unsigned long)absolute.tv_sec, (unsigned long)absolute.tv_usec);
-
- rc = time_register_abortable(ao, ev);
- if (rc) goto out;
-
- rc = time_register_finite(gc, ev, absolute);
- if (rc) goto out;
-
- ev->func = func;
-
- rc = 0;
- out:
- libxl__ao_abortable_deregister(&ev->abrt);
- time_done_debug(gc,__func__,ev,rc);
- CTX_UNLOCK;
- return rc;
-}
-
-
-int libxl__ev_time_register_rel(libxl__ao *ao, libxl__ev_time *ev,
- libxl__ev_time_callback *func,
- int milliseconds /* as for poll(2) */)
-{
- AO_GC;
- struct timeval absolute;
- int rc;
-
- CTX_LOCK;
-
- DBG("ev_time=%p register ms=%d", ev, milliseconds);
-
- rc = time_register_abortable(ao, ev);
- if (rc) goto out;
-
- if (milliseconds < 0) {
- ev->infinite = 1;
- } else {
- rc = time_rel_to_abs(gc, milliseconds, &absolute);
- if (rc) goto out;
-
- rc = time_register_finite(gc, ev, absolute);
- if (rc) goto out;
- }
-
- ev->func = func;
- rc = 0;
-
- out:
- if (!libxl__ev_time_isregistered(ev))
- libxl__ao_abortable_deregister(&ev->abrt);
- time_done_debug(gc,__func__,ev,rc);
- CTX_UNLOCK;
- return rc;
-}
-
-void libxl__ev_time_deregister(libxl__gc *gc, libxl__ev_time *ev)
-{
- CTX_LOCK;
-
- DBG("ev_time=%p deregister", ev);
-
- if (!libxl__ev_time_isregistered(ev))
- goto out;
-
- time_deregister(gc, ev);
- ev->func = 0;
-
- out:
- time_done_debug(gc,__func__,ev,0);
- CTX_UNLOCK;
- return;
-}
-
-static void time_occurs(libxl__egc *egc, libxl__ev_time *etime, int rc)
-{
- EGC_GC;
-
- DBG("ev_time=%p occurs abs=%lu.%06lu",
- etime, (unsigned long)etime->abs.tv_sec,
- (unsigned long)etime->abs.tv_usec);
-
- libxl__ev_time_callback *func = etime->func;
- etime->func = 0;
- func(egc, etime, &etime->abs, rc);
-}
-
-
-/*
- * xenstore watches
- */
-
-libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, int slotnum)
-{
- libxl__ev_watch_slot *slot = &CTX->watch_slots[slotnum];
- libxl__ev_watch_slot *slotcontents = LIBXL_SLIST_NEXT(slot, empty);
-
- if (slotcontents == NULL ||
- ((uintptr_t)slotcontents >= (uintptr_t)CTX->watch_slots &&
- (uintptr_t)slotcontents < (uintptr_t)(CTX->watch_slots +
- CTX->watch_nslots)))
- /* An empty slot has either a NULL pointer (end of the
- * free list), or a pointer to another entry in the array.
- * So we can do a bounds check to distinguish empty from
- * full slots.
- */
- /* We need to do the comparisons as uintptr_t because
- * comparing pointers which are not in the same object is
- * undefined behaviour; if the compiler managed to figure
- * out that watch_slots[0..watch_nslots-1] is all of the
- * whole array object it could prove that the above bounds
- * check was always true if it was legal, and remove it!
- *
- * uintptr_t because even on a machine with signed
- * pointers, objects do not cross zero; whereas on
- * machines with unsigned pointers, they may cross
- * 0x8bazillion.
- */
- return NULL;
-
- /* see comment near libxl__ev_watch_slot definition */
- return (void*)slotcontents;
-}
-
-static void libxl__set_watch_slot_contents(libxl__ev_watch_slot *slot,
- libxl__ev_xswatch *w)
-{
- /* we look a bit behind the curtain of LIBXL_SLIST, to explicitly
- * assign to the pointer that's the next link. See the comment
- * by the definition of libxl__ev_watch_slot */
- slot->empty.sle_next = (void*)w;
-}
-
-static void watchfd_callback(libxl__egc *egc, libxl__ev_fd *ev,
- int fd, short events, short revents)
-{
- EGC_GC;
-
- if (revents & (POLLERR|POLLHUP))
- LIBXL__EVENT_DISASTER(gc, "unexpected poll event on watch fd", 0, 0);
-
- for (;;) {
- char **event = xs_check_watch(CTX->xsh);
- if (!event) {
- if (errno == EAGAIN) break;
- if (errno == EINTR) continue;
- LIBXL__EVENT_DISASTER(gc, "cannot check/read watches", errno, 0);
- return;
- }
-
- const char *epath = event[0];
- const char *token = event[1];
- int slotnum;
- uint32_t counterval;
- int rc = sscanf(token, "%d/%"SCNx32, &slotnum, &counterval);
- if (rc != 2) {
- LOG(ERROR, "watch epath=%s token=%s: failed to parse token",
- epath, token);
- /* oh well */
- goto ignore;
- }
- if (slotnum < 0 || slotnum >= CTX->watch_nslots) {
- /* perhaps in the future we will make the watchslots array shrink */
- LIBXL__LOG(CTX, LIBXL__LOG_DEBUG, "watch epath=%s token=%s:"
- " slotnum %d out of range [0,%d>",
- epath, token, slotnum, CTX->watch_nslots);
- goto ignore;
- }
-
- libxl__ev_xswatch *w = libxl__watch_slot_contents(gc, slotnum);
-
- if (!w) {
- LOG(DEBUG, "watch epath=%s token=%s: empty slot", epath, token);
- goto ignore;
- }
-
- if (w->counterval != counterval) {
- LOG(DEBUG, "watch w=%p epath=%s token=%s: counter != %"PRIx32,
- w, epath, token, w->counterval);
- goto ignore;
- }
-
- /* Now it's possible, though unlikely, that this was an event
- * from a previous use of the same slot with the same counterval.
- *
- * In that case either:
- * - the event path is a child of the watch path, in
- * which case this watch would really have generated this
- * event if it had been registered soon enough and we are
- * OK to give this possibly-spurious event to the caller; or
- * - it is not, in which case we must suppress it as the
- * caller should not see events for unrelated paths.
- *
- * See also docs/misc/xenstore.txt.
- */
- if (!xs_path_is_subpath(w->path, epath)) {
- LOG(DEBUG, "watch w=%p wpath=%s token=%s: unexpected epath=%s",
- w, w->path, token, epath);
- goto ignore;
- }
-
- /* At last, we have checked everything! */
- LOG(DEBUG, "watch w=%p wpath=%s token=%s: event epath=%s",
- w, w->path, token, epath);
- w->callback(egc, w, w->path, epath);
-
- ignore:
- free(event);
- }
-}
-
-static char *watch_token(libxl__gc *gc, int slotnum, uint32_t counterval)
-{
- return GCSPRINTF("%d/%"PRIx32, slotnum, counterval);
-}
-
-static void watches_check_fd_deregister(libxl__gc *gc)
-{
- assert(CTX->nwatches>=0);
- if (!CTX->nwatches)
- libxl__ev_fd_deregister(gc, &CTX->watch_efd);
-}
-
-int libxl__ev_xswatch_register(libxl__gc *gc, libxl__ev_xswatch *w,
- libxl__ev_xswatch_callback *func,
- const char *path /* copied */)
-{
- libxl__ev_watch_slot *use = NULL;
- char *path_copy = NULL;
- int rc;
-
- CTX_LOCK;
-
- if (!libxl__ev_fd_isregistered(&CTX->watch_efd)) {
- rc = libxl__ev_fd_register(gc, &CTX->watch_efd, watchfd_callback,
- xs_fileno(CTX->xsh), POLLIN);
- if (rc) goto out_rc;
- }
-
- if (LIBXL_SLIST_EMPTY(&CTX->watch_freeslots)) {
- /* Free list is empty so there is not in fact a linked
- * free list in the array and we can safely realloc it */
- int newarraysize = (CTX->watch_nslots + 1) << 2;
- int i;
- libxl__ev_watch_slot *newarray =
- libxl__realloc(NOGC,
- CTX->watch_slots, sizeof(*newarray) * newarraysize);
- if (!newarray) goto out_nomem;
- for (i = CTX->watch_nslots; i < newarraysize; i++)
- LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots,
- &newarray[i], empty);
- CTX->watch_slots = newarray;
- CTX->watch_nslots = newarraysize;
- }
- use = LIBXL_SLIST_FIRST(&CTX->watch_freeslots);
- assert(use);
- LIBXL_SLIST_REMOVE_HEAD(&CTX->watch_freeslots, empty);
-
- path_copy = strdup(path);
- if (!path_copy) goto out_nomem;
-
- int slotnum = use - CTX->watch_slots;
- w->counterval = CTX->watch_counter++;
-
- const char *token = watch_token(gc, slotnum, w->counterval);
- LOG(DEBUG, "watch w=%p wpath=%s token=%s: register slotnum=%d",
- w, path, token, slotnum);
-
- if (!xs_watch(CTX->xsh, path, token)) {
- LOGEV(ERROR, errno, "create watch for path %s", path);
- rc = ERROR_FAIL;
- goto out_rc;
- }
-
- w->slotnum = slotnum;
- w->path = path_copy;
- w->callback = func;
- CTX->nwatches++;
- libxl__set_watch_slot_contents(use, w);
-
- CTX_UNLOCK;
- return 0;
-
- out_nomem:
- rc = ERROR_NOMEM;
- out_rc:
- if (use)
- LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, use, empty);
- free(path_copy);
- watches_check_fd_deregister(gc);
- CTX_UNLOCK;
- return rc;
-}
-
-void libxl__ev_xswatch_deregister(libxl__gc *gc, libxl__ev_xswatch *w)
-{
- /* it is legal to deregister from within _callback */
- CTX_LOCK;
-
- if (w->slotnum >= 0) {
- const char *token = watch_token(gc, w->slotnum, w->counterval);
-
- LOG(DEBUG, "watch w=%p wpath=%s token=%s: deregister slotnum=%d",
- w, w->path, token, w->slotnum);
-
- if (!xs_unwatch(CTX->xsh, w->path, token))
- /* Oh well, we will just get watch events forever more
- * and ignore them. But we should complain to the log. */
- LOGEV(ERROR, errno, "remove watch for path %s", w->path);
-
- libxl__ev_watch_slot *slot = &CTX->watch_slots[w->slotnum];
- LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, slot, empty);
- w->slotnum = -1;
- CTX->nwatches--;
- watches_check_fd_deregister(gc);
- } else {
- LOG(DEBUG, "watch w=%p: deregister unregistered", w);
- }
-
- free(w->path);
- w->path = NULL;
-
- CTX_UNLOCK;
-}
-
-/*
- * evtchn
- */
-
-static int evtchn_revents_check(libxl__egc *egc, int revents)
-{
- EGC_GC;
-
- if (revents & ~POLLIN) {
- LOG(ERROR, "unexpected poll event on event channel fd: %x", revents);
- LIBXL__EVENT_DISASTER(gc,
- "unexpected poll event on event channel fd", 0, 0);
- libxl__ev_fd_deregister(gc, &CTX->evtchn_efd);
- return ERROR_FAIL;
- }
-
- assert(revents & POLLIN);
-
- return 0;
-}
-
-static void evtchn_fd_callback(libxl__egc *egc, libxl__ev_fd *ev,
- int fd, short events, short revents)
-{
- EGC_GC;
- libxl__ev_evtchn *evev;
- int rc;
- xenevtchn_port_or_error_t port;
-
- rc = evtchn_revents_check(egc, revents);
- if (rc) return;
-
- for (;;) {
- /* Check the fd again. The incoming revent may no longer be
- * true, because the libxl ctx lock has not necessarily been
- * held continuously since someone noticed the fd. Normally
- * this wouldn't be a problem but evtchn devices don't always
- * honour O_NONBLOCK (see xenctrl.h). */
- revents = libxl__fd_poll_recheck(egc,fd,POLLIN);
- if (!revents)
- break;
- rc = evtchn_revents_check(egc, revents);
- if (rc) return;
-
- /* OK, that's that workaround done. We can actually check for
- * work for us to do: */
-
- port = xenevtchn_pending(CTX->xce);
- if (port < 0) {
- if (errno == EAGAIN)
- break;
- LIBXL__EVENT_DISASTER(gc,
- "unexpected failure fetching occurring event port number from evtchn",
- errno, 0);
- return;
- }
-
- LIBXL_LIST_FOREACH(evev, &CTX->evtchns_waiting, entry)
- if (port == evev->port)
- goto found;
- /* not found */
- DBG("ev_evtchn port=%d no-one cared", port);
- continue;
-
- found:
- DBG("ev_evtchn=%p port=%d signaled", evev, port);
- evev->waiting = 0;
- LIBXL_LIST_REMOVE(evev, entry);
- evev->callback(egc, evev);
- }
-}
-
-int libxl__ctx_evtchn_init(libxl__gc *gc) {
- xenevtchn_handle *xce;
- int rc, fd;
-
- if (CTX->xce)
- return 0;
-
- xce = xenevtchn_open(CTX->lg, 0);
- if (!xce) {
- LOGE(ERROR,"cannot open libxc evtchn handle");
- rc = ERROR_FAIL;
- goto out;
- }
-
- fd = xenevtchn_fd(xce);
- assert(fd >= 0);
-
- rc = libxl_fd_set_nonblock(CTX, fd, 1);
- if (rc) goto out;
-
- CTX->xce = xce;
- return 0;
-
- out:
- xenevtchn_close(xce);
- return rc;
-}
-
-static void evtchn_check_fd_deregister(libxl__gc *gc)
-{
- if (CTX->xce && LIBXL_LIST_EMPTY(&CTX->evtchns_waiting))
- libxl__ev_fd_deregister(gc, &CTX->evtchn_efd);
-}
-
-int libxl__ev_evtchn_wait(libxl__gc *gc, libxl__ev_evtchn *evev)
-{
- int r, rc;
-
- DBG("ev_evtchn=%p port=%d wait (was waiting=%d)",
- evev, evev->port, evev->waiting);
-
- rc = libxl__ctx_evtchn_init(gc);
- if (rc) goto out;
-
- if (!libxl__ev_fd_isregistered(&CTX->evtchn_efd)) {
- rc = libxl__ev_fd_register(gc, &CTX->evtchn_efd, evtchn_fd_callback,
- xenevtchn_fd(CTX->xce), POLLIN);
- if (rc) goto out;
- }
-
- if (evev->waiting)
- return 0;
-
- r = xenevtchn_unmask(CTX->xce, evev->port);
- if (r) {
- LOGE(ERROR,"cannot unmask event channel %d",evev->port);
- rc = ERROR_FAIL;
- goto out;
- }
-
- evev->waiting = 1;
- LIBXL_LIST_INSERT_HEAD(&CTX->evtchns_waiting, evev, entry);
- return 0;
-
- out:
- evtchn_check_fd_deregister(gc);
- return rc;
-}
-
-void libxl__ev_evtchn_cancel(libxl__gc *gc, libxl__ev_evtchn *evev)
-{
- DBG("ev_evtchn=%p port=%d cancel (was waiting=%d)",
- evev, evev->port, evev->waiting);
-
- if (!evev->waiting)
- return;
-
- evev->waiting = 0;
- LIBXL_LIST_REMOVE(evev, entry);
- evtchn_check_fd_deregister(gc);
-}
-
-/*
- * waiting for device state
- */
-
-static void devstate_callback(libxl__egc *egc, libxl__xswait_state *xsw,
- int rc, const char *sstate)
-{
- EGC_GC;
- libxl__ev_devstate *ds = CONTAINER_OF(xsw, *ds, w);
-
- if (rc) {
- if (rc == ERROR_TIMEDOUT)
- LOG(DEBUG, "backend %s wanted state %d "" timed out", ds->w.path,
- ds->wanted);
- goto out;
- }
- if (!sstate) {
- LOG(DEBUG, "backend %s wanted state %d"" but it was removed",
- ds->w.path, ds->wanted);
- rc = ERROR_INVAL;
- goto out;
- }
-
- int got = atoi(sstate);
- if (got == ds->wanted) {
- LOG(DEBUG, "backend %s wanted state %d ok", ds->w.path, ds->wanted);
- rc = 0;
- } else {
- LOG(DEBUG, "backend %s wanted state %d"" still waiting state %d",
- ds->w.path, ds->wanted, got);
- return;
- }
-
- out:
- libxl__ev_devstate_cancel(gc, ds);
- ds->callback(egc, ds, rc);
-}
-
-int libxl__ev_devstate_wait(libxl__ao *ao, libxl__ev_devstate *ds,
- libxl__ev_devstate_callback cb,
- const char *state_path, int state, int milliseconds)
-{
- AO_GC;
- int rc;
-
- libxl__xswait_init(&ds->w);
- ds->wanted = state;
- ds->callback = cb;
-
- ds->w.ao = ao;
- ds->w.what = GCSPRINTF("backend %s (hoping for state change to %d)",
- state_path, state);
- ds->w.path = state_path;
- ds->w.timeout_ms = milliseconds;
- ds->w.callback = devstate_callback;
- rc = libxl__xswait_start(gc, &ds->w);
- if (rc) goto out;
-
- return 0;
-
- out:
- libxl__ev_devstate_cancel(gc, ds);
- return rc;
-}
-
-/*
- * immediate non-reentrant callback
- */
-
-void libxl__ev_immediate_register(libxl__egc *egc, libxl__ev_immediate *ei)
-{
- LIBXL_STAILQ_INSERT_TAIL(&egc->ev_immediates, ei, entry);
-}
-
-/*
- * domain death/destruction
- */
-
-/*
- * We use a xenstore watch on the domain's path, rather than using an
- * @releaseDomain watch and asking the hypervisor. This is simpler
- * because turning @releaseDomain into domain-specific information is
- * complicated.
- *
- * It is also sufficient for our callers, which are generally trying
- * to do cleanup of their own execution state on domain death, for the
- * following reason: if the domain is destroyed then either (a) the
- * entries in xenstore have already been deleted, in which case the
- * test here works or (b) they have not in which case something has
- * gone very badly wrong and we are going to leak those xenstore
- * entries, in which case trying to avoid leaking other stuff is
- * futile.
- */
-
-void libxl__domaindeathcheck_init(libxl__domaindeathcheck *dc)
-{
- libxl__ao_abortable_init(&dc->abrt);
- libxl__ev_xswatch_init(&dc->watch);
-}
-
-void libxl__domaindeathcheck_stop(libxl__gc *gc, libxl__domaindeathcheck *dc)
-{
- libxl__ao_abortable_deregister(&dc->abrt);
- libxl__ev_xswatch_deregister(gc,&dc->watch);
-}
-
-static void domaindeathcheck_callback(libxl__egc *egc, libxl__ev_xswatch *w,
- const char *watch_path, const char *event_path)
-{
- libxl__domaindeathcheck *dc = CONTAINER_OF(w, *dc, watch);
- EGC_GC;
- const char *p = libxl__xs_read(gc, XBT_NULL, watch_path);
- if (p) return;
-
- libxl__domaindeathcheck_stop(gc,dc);
-
- if (errno!=ENOENT) {
- LIBXL__EVENT_DISASTER(gc,"failed to read xenstore"
- " for domain detach check", errno, 0);
- return;
- }
-
- LOG(ERROR,"%s: domain %"PRIu32" removed (%s no longer in xenstore)",
- dc->what, dc->domid, watch_path);
- dc->callback(egc, dc, ERROR_DOMAIN_DESTROYED);
-}
-
-static void domaindeathcheck_abort(libxl__egc *egc,
- libxl__ao_abortable *abrt,
- int rc)
-{
- libxl__domaindeathcheck *dc = CONTAINER_OF(abrt, *dc, abrt);
- EGC_GC;
-
- libxl__domaindeathcheck_stop(gc,dc);
- dc->callback(egc, dc, rc);
-}
-
-int libxl__domaindeathcheck_start(libxl__ao *ao,
- libxl__domaindeathcheck *dc)
-{
- AO_GC;
- int rc;
- const char *path = GCSPRINTF("/local/domain/%"PRIu32, dc->domid);
-
- libxl__domaindeathcheck_init(dc);
-
- dc->abrt.ao = ao;
- dc->abrt.callback = domaindeathcheck_abort;
- rc = libxl__ao_abortable_register(&dc->abrt);
- if (rc) goto out;
-
- rc = libxl__ev_xswatch_register(gc, &dc->watch,
- domaindeathcheck_callback, path);
- if (rc) goto out;
-
- return 0;
-
- out:
- libxl__domaindeathcheck_stop(gc,dc);
- return rc;
-}
-
-/*
- * osevent poll
- */
-
-static int beforepoll_internal(libxl__gc *gc, libxl__poller *poller,
- int *nfds_io, struct pollfd *fds,
- int *timeout_upd, struct timeval now)
-{
- libxl__ev_fd *efd;
- int rc;
-
- /*
- * We need to look at the fds we want twice: firstly, to count
- * them so we can make the rindex array big enough, and secondly
- * to actually fill the arrays in.
- *
- * To ensure correctness and avoid repeating the logic for
- * deciding which fds are relevant, we define a macro
- * REQUIRE_FDS( BODY )
- * which calls
- * do{
- * int req_fd;
- * int req_events;
- * BODY;
- * }while(0)
- * for each fd with a nonzero events. This is invoked twice.
- *
- * The definition of REQUIRE_FDS is simplified with the helper
- * macro
- * void REQUIRE_FD(int req_fd, int req_events, BODY);
- */
-
-#define REQUIRE_FDS(BODY) do{ \
- \
- LIBXL_LIST_FOREACH(efd, &CTX->efds, entry) \
- REQUIRE_FD(efd->fd, efd->events, BODY); \
- \
- REQUIRE_FD(poller->wakeup_pipe[0], POLLIN, BODY); \
- \
- }while(0)
-
-#define REQUIRE_FD(req_fd_, req_events_, BODY) do{ \
- int req_events = (req_events_); \
- int req_fd = (req_fd_); \
- if (req_events) { \
- BODY; \
- } \
- }while(0)
-
-
- /*
- * In order to be able to efficiently find the libxl__ev_fd for a
- * struct poll during _afterpoll, we maintain a shadow data
- * structure in CTX->fd_rindices: each fd corresponds to a slot in
- * fd_rindices, and each element in the rindices is three indices
- * into the fd array (for POLLIN, POLLPRI and POLLOUT).
- */
-
- if (*nfds_io) {
- /*
- * As an optimisation, we don't touch fd_rindex
- * if *nfds_io is zero on entry, since in that case the
- * caller just wanted to know how big an array to give us.
- *
- * If !*nfds_io, the unconditional parts below are guaranteed
- * not to mess with fd_rindex.
- */
-
- int maxfd = 0;
-
- REQUIRE_FDS({
- if (req_fd >= maxfd)
- maxfd = req_fd + 1;
- });
-
- /* make sure our array is as big as *nfds_io */
- if (poller->fd_rindices_allocd < maxfd) {
- assert(ARRAY_SIZE_OK(poller->fd_rindices, maxfd));
- poller->fd_rindices =
- libxl__realloc(NOGC, poller->fd_rindices,
- maxfd * sizeof(*poller->fd_rindices));
- memset(poller->fd_rindices + poller->fd_rindices_allocd,
- 0,
- (maxfd - poller->fd_rindices_allocd)
- * sizeof(*poller->fd_rindices));
- poller->fd_rindices_allocd = maxfd;
- }
- }
-
- int used = 0;
-
- REQUIRE_FDS({
- if (used < *nfds_io) {
- fds[used].fd = req_fd;
- fds[used].events = req_events;
- fds[used].revents = 0;
- assert(req_fd < poller->fd_rindices_allocd);
- if (req_events & POLLIN) poller->fd_rindices[req_fd][0] = used;
- if (req_events & POLLPRI) poller->fd_rindices[req_fd][1] = used;
- if (req_events & POLLOUT) poller->fd_rindices[req_fd][2] = used;
- }
- used++;
- });
-
- rc = used <= *nfds_io ? 0 : ERROR_BUFFERFULL;
-
- *nfds_io = used;
-
- poller->fds_deregistered = 0;
- poller->osevents_added = 0;
-
- libxl__ev_time *etime = LIBXL_TAILQ_FIRST(&CTX->etimes);
- if (etime) {
- int our_timeout;
- struct timeval rel;
- static struct timeval zero;
-
- timersub(&etime->abs, &now, &rel);
-
- if (timercmp(&rel, &zero, <)) {
- our_timeout = 0;
- } else if (rel.tv_sec >= 2000000) {
- our_timeout = 2000000000;
- } else {
- our_timeout = rel.tv_sec * 1000 + (rel.tv_usec + 999) / 1000;
- }
- if (*timeout_upd < 0 || our_timeout < *timeout_upd)
- *timeout_upd = our_timeout;
- }
-
- return rc;
-}
-
-int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
- struct pollfd *fds, int *timeout_upd,
- struct timeval now)
-{
- EGC_INIT(ctx);
- CTX_LOCK;
- int rc = beforepoll_internal(gc, ctx->poller_app,
- nfds_io, fds, timeout_upd, now);
- CTX_UNLOCK_EGC_FREE;
- return rc;
-}
-
-static int afterpoll_check_fd(libxl__poller *poller,
- const struct pollfd *fds, int nfds,
- int fd, int events)
- /* Returns mask of events which were requested and occurred. Will
- * return nonzero only once for each (poller,fd,events)
- * combination, until the next beforepoll. If events from
- * different combinations overlap, between one such combination
- * and all distinct combinations will produce nonzero returns. */
-{
- if (fd >= poller->fd_rindices_allocd)
- /* added after we went into poll, have to try again */
- return 0;
-
- events |= POLLERR | POLLHUP;
-
- int i, revents = 0;
- for (i=0; i<3; i++) {
- int *slotp = &poller->fd_rindices[fd][i];
- int slot = *slotp;
-
- if (slot >= nfds)
- /* stale slot entry (again, added afterwards), */
- /* or slot for which we have already returned nonzero */
- continue;
-
- if (fds[slot].fd != fd)
- /* again, stale slot entry */
- continue;
-
- assert(poller->fds_deregistered || !(fds[slot].revents & POLLNVAL));
-
- /* we mask in case requested events have changed */
- int slot_revents = fds[slot].revents & events;
- if (!slot_revents)
- /* this slot is for a different set of events */
- continue;
-
- revents |= slot_revents;
- *slotp = INT_MAX; /* so that next time we'll see slot >= nfds */
- }
-
- return revents;
-}
-
-static void fd_occurs(libxl__egc *egc, libxl__ev_fd *efd, short revents_ign)
-{
- short revents_current = libxl__fd_poll_recheck(egc, efd->fd, efd->events);
- EGC_GC;
-
- DBG("ev_fd=%p occurs fd=%d events=%x revents_ign=%x revents_current=%x",
- efd, efd->fd, efd->events, revents_ign, revents_current);
-
- if (revents_current)
- efd->func(egc, efd, efd->fd, efd->events, revents_current);
-}
-
-static void afterpoll_internal(libxl__egc *egc, libxl__poller *poller,
- int nfds, const struct pollfd *fds,
- struct timeval now)
-{
- /* May make callbacks into the application for child processes.
- * ctx must be locked exactly once */
- EGC_GC;
- libxl__ev_fd *efd;
-
- /*
- * Warning! Reentrancy hazards!
- *
- * Many parts of this function eventually call arbitrary callback
- * functions which may modify the event handling data structures.
- *
- * Of the data structures used here:
- *
- * egc, poller, now
- * are allocated by our caller and relate to the
- * current thread and its call stack down into the
- * event machinery; it is not freed until we return.
- * So it is safe.
- *
- * fds is either what application passed into
- * libxl_osevent_afterpoll (which, although this
- * isn't explicitly stated, clearly must remain
- * valid until libxl_osevent_afterpoll returns) or
- * it's poller->fd_polls which is modified only by
- * our (non-recursive) caller eventloop_iteration.
- *
- * CTX comes from our caller, and applications are
- * forbidden from destroying it while we are running.
- * So the ctx pointer itself is safe to use; now
- * for its contents:
- *
- * CTX->etimes is used in a simple reentrancy-safe manner.
- *
- * CTX->efds is more complicated; see below.
- */
-
- for (;;) {
- /* We restart our scan of fd events whenever we call a
- * callback function. This is necessary because such
- * a callback might make arbitrary changes to CTX->efds.
- * We invalidate the fd_rindices[] entries which were used
- * so that we don't call the same function again. */
- int revents;
-
- LIBXL_LIST_FOREACH(efd, &CTX->efds, entry) {
-
- if (!efd->events)
- continue;
-
- revents = afterpoll_check_fd(poller,fds,nfds,
- efd->fd,efd->events);
- if (revents)
- goto found_fd_event;
- }
- /* no ordinary fd events, then */
- break;
-
- found_fd_event:
- fd_occurs(egc, efd, revents);
- }
-
- for (;;) {
- libxl__ev_time *etime = LIBXL_TAILQ_FIRST(&CTX->etimes);
- if (!etime)
- break;
-
- assert(!etime->infinite);
-
- if (timercmp(&etime->abs, &now, >))
- break;
-
- time_deregister(gc, etime);
-
- time_occurs(egc, etime, ERROR_TIMEDOUT);
- }
-
- if (afterpoll_check_fd(poller,fds,nfds, poller->wakeup_pipe[0],POLLIN)) {
- poller->pipe_nonempty = 0;
- int e = libxl__self_pipe_eatall(poller->wakeup_pipe[0]);
- if (e) LIBXL__EVENT_DISASTER(gc, "read wakeup", e, 0);
- }
-}
-
-void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds,
- struct timeval now)
-{
- EGC_INIT(ctx);
- CTX_LOCK;
- afterpoll_internal(egc, ctx->poller_app, nfds, fds, now);
- CTX_UNLOCK_EGC_FREE;
-}
-
-/*
- * osevent hook and callback machinery
- */
-
-void libxl_osevent_register_hooks(libxl_ctx *ctx,
- const libxl_osevent_hooks *hooks,
- void *user)
-{
- GC_INIT(ctx);
- CTX_LOCK;
- assert(LIBXL_LIST_EMPTY(&ctx->efds));
- assert(LIBXL_TAILQ_EMPTY(&ctx->etimes));
- ctx->osevent_hooks = hooks;
- ctx->osevent_user = user;
- CTX_UNLOCK;
- GC_FREE;
-}
-
-
-void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl,
- int fd, short events_ign, short revents_ign)
-{
- EGC_INIT(ctx);
- CTX_LOCK;
- assert(!CTX->osevent_in_hook);
-
- libxl__ev_fd *ev = osevent_ev_from_hook_nexus(ctx, for_libxl);
- if (!ev) goto out;
- if (ev->fd != fd) goto out;
-
- fd_occurs(egc, ev, revents_ign);
-
- out:
- CTX_UNLOCK_EGC_FREE;
-}
-
-void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl)
-{
- EGC_INIT(ctx);
- CTX_LOCK;
- assert(!CTX->osevent_in_hook);
-
- libxl__osevent_hook_nexus *nexus = for_libxl;
- libxl__ev_time *ev = osevent_ev_from_hook_nexus(ctx, nexus);
-
- osevent_release_nexus(gc, &CTX->hook_timeout_nexi_idle, nexus);
-
- if (!ev) goto out;
- assert(!ev->infinite);
-
- LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry);
-
- time_occurs(egc, ev, ERROR_TIMEDOUT);
-
- out:
- CTX_UNLOCK_EGC_FREE;
-}
-
-void libxl__event_disaster(libxl__gc *gc, const char *msg, int errnoval,
- libxl_event_type type /* may be 0 */,
- const char *file, int line, const char *func)
-{
- libxl__log(CTX, XTL_CRITICAL, errnoval, file, line, func, INVALID_DOMID,
- "DISASTER in event loop: %s%s%s%s",
- msg,
- type ? " (relates to event type " : "",
- type ? libxl_event_type_to_string(type) : "",
- type ? ")" : "");
-
- if (CTX->event_hooks && CTX->event_hooks->disaster) {
- CTX->event_hooks->disaster(CTX->event_hooks_user, type, msg, errnoval);
- return;
- }
-
- const char verybad[] =
- "DISASTER in event loop not handled by libxl application";
- LIBXL__LOG(CTX, XTL_CRITICAL, verybad);
- fprintf(stderr, "libxl: fatal error, exiting program: %s\n", verybad);
- exit(-1);
-}
-
-static void egc_run_callbacks(libxl__egc *egc)
-{
- /*
- * The callbacks must happen with the ctx unlocked. See the
- * comment near #define EGC_GC in libxl_internal.h and those in
- * the definitions of libxl__egc, libxl__ao and libxl__aop.
- */
- EGC_GC;
- libxl_event *ev, *ev_tmp;
- libxl__aop_occurred *aop, *aop_tmp;
- libxl__ev_immediate *ei;
-
- while (!LIBXL_STAILQ_EMPTY(&egc->ev_immediates)) {
- ei = LIBXL_STAILQ_FIRST(&egc->ev_immediates);
- LIBXL_STAILQ_REMOVE_HEAD(&egc->ev_immediates, entry);
- CTX_LOCK;
- /* This callback is internal to libxl and expects CTX to be
- * locked. */
- ei->callback(egc, ei);
- CTX_UNLOCK;
- }
-
- LIBXL_TAILQ_FOREACH_SAFE(ev, &egc->occurred_for_callback, link, ev_tmp) {
- LIBXL_TAILQ_REMOVE(&egc->occurred_for_callback, ev, link);
- LOG(DEBUG,"event %p callback type=%s",
- ev, libxl_event_type_to_string(ev->type));
- CTX->event_hooks->event_occurs(CTX->event_hooks_user, ev);
- }
-
- LIBXL_TAILQ_FOREACH_SAFE(aop, &egc->aops_for_callback, entry, aop_tmp) {
- LIBXL_TAILQ_REMOVE(&egc->aops_for_callback, aop, entry);
- LOG(DEBUG,"ao %p: progress report: callback aop=%p", aop->ao, aop);
- aop->how->callback(CTX, aop->ev, aop->how->for_callback);
-
- CTX_LOCK;
- assert(aop->ao->magic == LIBXL__AO_MAGIC);
- aop->ao->progress_reports_outstanding--;
- libxl__ao_complete_check_progress_reports(egc, aop->ao);
- CTX_UNLOCK;
- }
-
- libxl__ao *ao, *ao_tmp;
- LIBXL_TAILQ_FOREACH_SAFE(ao, &egc->aos_for_callback,
- entry_for_callback, ao_tmp) {
- LIBXL_TAILQ_REMOVE(&egc->aos_for_callback, ao, entry_for_callback);
- LOG(DEBUG,"ao %p: completion callback", ao);
- ao->how.callback(CTX, ao->rc, ao->how.u.for_callback);
- CTX_LOCK;
- ao->notified = 1;
- ao__check_destroy(CTX, ao);
- CTX_UNLOCK;
- }
-}
-
-void libxl__egc_cleanup_2_ul_cb_gc(libxl__egc *egc)
-{
- EGC_GC;
- egc_run_callbacks(egc);
-
- libxl__free_all(gc);
-}
-
-/*
- * Event retrieval etc.
- */
-
-void libxl_event_register_callbacks(libxl_ctx *ctx,
- const libxl_event_hooks *hooks, void *user)
-{
- ctx->event_hooks = hooks;
- ctx->event_hooks_user = user;
-}
-
-void libxl__event_occurred(libxl__egc *egc, libxl_event *event)
-{
- EGC_GC;
-
- if (CTX->event_hooks &&
- (CTX->event_hooks->event_occurs_mask & (1UL << event->type))) {
- /* libxl__egc_cleanup will call the callback, just before exit
- * from libxl. This helps avoid reentrancy bugs: parts of
- * libxl that call libxl__event_occurred do not have to worry
- * that libxl might be reentered at that point. */
- LIBXL_TAILQ_INSERT_TAIL(&egc->occurred_for_callback, event, link);
- return;
- } else {
- libxl__poller *poller;
- LIBXL_TAILQ_INSERT_TAIL(&CTX->occurred, event, link);
- LIBXL_LIST_FOREACH(poller, &CTX->pollers_event, entry)
- libxl__poller_wakeup(gc, poller);
- }
-}
-
-void libxl_event_free(libxl_ctx *ctx, libxl_event *event)
-{
- /* Exceptionally, this function may be called from libxl, with ctx==0 */
- libxl_event_dispose(event);
- free(event);
-}
-
-libxl_event *libxl__event_new(libxl__egc *egc,
- libxl_event_type type, uint32_t domid,
- libxl_ev_user for_user)
-{
- EGC_GC;
- libxl_event *ev;
-
- ev = libxl__zalloc(NOGC,sizeof(*ev));
-
- libxl_event_init(ev);
- libxl_event_init_type(ev, type);
-
- ev->domid = domid;
- ev->for_user = for_user;
-
- return ev;
-}
-
-static int event_check_internal(libxl__egc *egc, libxl_event **event_r,
- unsigned long typemask,
- libxl_event_predicate *pred, void *pred_user)
-{
- EGC_GC;
- libxl_event *ev;
- int rc;
-
- LIBXL_TAILQ_FOREACH(ev, &CTX->occurred, link) {
- if (!(typemask & ((uint64_t)1 << ev->type)))
- continue;
-
- if (pred && !pred(ev, pred_user))
- continue;
-
- /* got one! */
- LIBXL_TAILQ_REMOVE(&CTX->occurred, ev, link);
- *event_r = ev;
- rc = 0;
- goto out;
- }
- rc = ERROR_NOT_READY;
-
- out:
- return rc;
-}
-
-int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r,
- uint64_t typemask,
- libxl_event_predicate *pred, void *pred_user)
-{
- EGC_INIT(ctx);
- CTX_LOCK;
- int rc = event_check_internal(egc, event_r, typemask, pred, pred_user);
- CTX_UNLOCK_EGC_FREE;
- return rc;
-}
-
-/*
- * Utilities for pipes (specifically, useful for self-pipes)
- */
-
-void libxl__pipe_close(int fds[2])
-{
- if (fds[0] >= 0) close(fds[0]);
- if (fds[1] >= 0) close(fds[1]);
- fds[0] = fds[1] = -1;
-}
-
-int libxl__pipe_nonblock(libxl_ctx *ctx, int fds[2])
-{
- int r, rc;
-
- r = libxl_pipe(ctx, fds);
- if (r) {
- fds[0] = fds[1] = -1;
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl_fd_set_nonblock(ctx, fds[0], 1);
- if (rc) goto out;
-
- rc = libxl_fd_set_nonblock(ctx, fds[1], 1);
- if (rc) goto out;
-
- return 0;
-
- out:
- libxl__pipe_close(fds);
- return rc;
-}
-
-int libxl__self_pipe_wakeup(int fd)
-{
- /* Called from signal handlers, so needs to be async-signal-safe */
- static const char buf[1] = "";
-
- for (;;) {
- int r = write(fd, buf, 1);
- if (r==1) return 0;
- assert(r==-1);
- if (errno == EINTR) continue;
- if (errno == EWOULDBLOCK) return 0;
- if (!errno) abort();
- return errno;
- }
-}
-
-int libxl__self_pipe_eatall(int fd)
-{
- char buf[256];
- for (;;) {
- int r = read(fd, buf, sizeof(buf));
- if (r == sizeof(buf)) continue;
- if (r >= 0) return 0;
- assert(r == -1);
- if (errno == EINTR) continue;
- if (errno == EWOULDBLOCK) return 0;
- assert(errno);
- return errno;
- }
-}
-
-/*
- * Manipulation of pollers
- */
-
-int libxl__poller_init(libxl__gc *gc, libxl__poller *p)
-{
- int rc;
- p->fd_polls = 0;
- p->fd_rindices = 0;
- p->fds_deregistered = 0;
-
- rc = libxl__pipe_nonblock(CTX, p->wakeup_pipe);
- if (rc) goto out;
-
- return 0;
-
- out:
- libxl__poller_dispose(p);
- return rc;
-}
-
-void libxl__poller_dispose(libxl__poller *p)
-{
- libxl__pipe_close(p->wakeup_pipe);
- free(p->fd_polls);
- free(p->fd_rindices);
-}
-
-libxl__poller *libxl__poller_get(libxl__gc *gc)
-{
- /* must be called with ctx locked */
- int rc;
-
- libxl__poller *p = LIBXL_LIST_FIRST(&CTX->pollers_idle);
- if (p) {
- LIBXL_LIST_REMOVE(p, entry);
- } else {
- p = libxl__zalloc(NOGC, sizeof(*p));
-
- rc = libxl__poller_init(gc, p);
- if (rc) {
- free(p);
- return NULL;
- }
- }
-
- LIBXL_LIST_INSERT_HEAD(&CTX->pollers_active, p,
- active_entry);
- return p;
-}
-
-void libxl__poller_put(libxl_ctx *ctx, libxl__poller *p)
-{
- if (!p) return;
- LIBXL_LIST_REMOVE(p, active_entry);
- LIBXL_LIST_INSERT_HEAD(&ctx->pollers_idle, p, entry);
-}
-
-void libxl__poller_wakeup(libxl__gc *gc, libxl__poller *p)
-{
- if (p->pipe_nonempty) return;
- p->pipe_nonempty = 1;
- int e = libxl__self_pipe_wakeup(p->wakeup_pipe[1]);
- if (e) LIBXL__EVENT_DISASTER(gc, "cannot poke watch pipe", e, 0);
-}
-
-/*
- * Main event loop iteration
- */
-
-static int eventloop_iteration(libxl__egc *egc, libxl__poller *poller) {
- /* The CTX must be locked EXACTLY ONCE so that this function
- * can unlock it when it polls.
- */
- EGC_GC;
- int rc, nfds;
- struct timeval now;
-
- rc = libxl__gettimeofday(gc, &now);
- if (rc) goto out;
-
- int timeout;
-
- for (;;) {
- nfds = poller->fd_polls_allocd;
- timeout = -1;
- rc = beforepoll_internal(gc, poller, &nfds, poller->fd_polls,
- &timeout, now);
- if (!rc) break;
- if (rc != ERROR_BUFFERFULL) goto out;
-
- struct pollfd *newarray =
- (nfds > INT_MAX / sizeof(struct pollfd) / 2) ? 0 :
- libxl__realloc(NOGC, poller->fd_polls, sizeof(*newarray) * nfds);
-
- if (!newarray) { rc = ERROR_NOMEM; goto out; }
-
- poller->fd_polls = newarray;
- poller->fd_polls_allocd = nfds;
- }
-
- CTX_UNLOCK;
- rc = poll(poller->fd_polls, nfds, timeout);
- CTX_LOCK;
-
- if (rc < 0) {
- if (errno == EINTR)
- return 0; /* will go round again if caller requires */
-
- LOGEV(ERROR, errno, "poll failed");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__gettimeofday(gc, &now);
- if (rc) goto out;
-
- afterpoll_internal(egc, poller, nfds, poller->fd_polls, now);
-
- rc = 0;
- out:
- return rc;
-}
-
-int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r,
- uint64_t typemask,
- libxl_event_predicate *pred, void *pred_user)
-{
- int rc;
- libxl__poller *poller = NULL;
-
- EGC_INIT(ctx);
- CTX_LOCK;
-
- poller = libxl__poller_get(gc);
- if (!poller) { rc = ERROR_FAIL; goto out; }
-
- for (;;) {
- rc = event_check_internal(egc, event_r, typemask, pred, pred_user);
- if (rc != ERROR_NOT_READY) goto out;
-
- rc = eventloop_iteration(egc, poller);
- if (rc) goto out;
-
- /* we unlock and cleanup the egc each time we go through this
- * loop, so that (a) we don't accumulate garbage and (b) any
- * events which are to be dispatched by callback are actually
- * delivered in a timely fashion. _1_baton will be
- * called to pass the baton iff we actually leave; otherwise
- * we are still carrying it.
- */
- CTX_UNLOCK;
- libxl__egc_cleanup_2_ul_cb_gc(egc);
- CTX_LOCK;
- }
-
- out:
- libxl__poller_put(ctx, poller);
-
- CTX_UNLOCK_EGC_FREE;
- return rc;
-}
-
-
-
-/*
- * The two possible state flow of an ao:
- *
- * Completion before initiator return:
- *
- * Initiator thread Possible other threads
- *
- * * ao_create allocates memory and
- * initialises the struct
- *
- * * the initiator function does its
- * work, setting up various internal
- * asynchronous operations -----------> * asynchronous operations
- * start to take place and
- * might cause ao completion
- * |
- * * initiator calls ao_inprogress |
- * - if synchronous, run event loop |
- * until the ao completes |
- * - ao completes on some thread
- * - completing thread releases the lock
- * <--------------'
- * - ao_inprogress takes the lock
- * - destroy the ao
- *
- *
- * Completion after initiator return (asynch. only):
- *
- *
- * Initiator thread Possible other threads
- *
- * * ao_create allocates memory and
- * initialises the struct
- *
- * * the initiator function does its
- * work, setting up various internal
- * asynchronous operations -----------> * asynchronous operations
- * start to take place and
- * might cause ao completion
- * |
- * * initiator calls ao_inprogress |
- * - observes event not yet done, |
- * - returns to caller |
- * |
- * - ao completes on some thread
- * - generate the event or call the callback
- * - destroy the ao
- */
-
-
-/*
- * A "manip" is a libxl public function manipulating this ao, which
- * has a pointer to it. We have to not destroy it while that's the
- * case, obviously. Callers must have the ctx locked, obviously.
- */
-static void ao__manip_enter(libxl__ao *ao)
-{
- assert(ao->manip_refcnt < INT_MAX);
- ao->manip_refcnt++;
-}
-
-static void ao__manip_leave(libxl_ctx *ctx, libxl__ao *ao)
-{
- assert(ao->manip_refcnt > 0);
- ao->manip_refcnt--;
- ao__check_destroy(ctx, ao);
-}
-
-static void ao__check_destroy(libxl_ctx *ctx, libxl__ao *ao)
-{
- if (!ao->manip_refcnt && ao->notified) {
- assert(ao->complete);
- libxl__ao__destroy(ctx, ao);
- }
-}
-
-void libxl__ao__destroy(libxl_ctx *ctx, libxl__ao *ao)
-{
- AO_GC;
- if (!ao) return;
- LOG(DEBUG,"ao %p: destroy",ao);
- libxl__poller_put(ctx, ao->poller);
- ao->magic = LIBXL__AO_MAGIC_DESTROYED;
- libxl__free_all(&ao->gc);
- free(ao);
-}
-
-void libxl__ao_create_fail(libxl__ao *ao)
-{
- AO_GC;
- LOG(DEBUG,"ao %p: create fail",ao);
- assert(ao->magic == LIBXL__AO_MAGIC);
- assert(ao->in_initiator);
- assert(!ao->complete);
- assert(!ao->progress_reports_outstanding);
- assert(!ao->aborting);
- LIBXL_LIST_REMOVE(ao, inprogress_entry);
- libxl__ao__destroy(CTX, ao);
-}
-
-libxl__gc *libxl__ao_inprogress_gc(libxl__ao *ao)
-{
- assert(ao);
- assert(ao->magic == LIBXL__AO_MAGIC);
- assert(!ao->complete);
- return &ao->gc;
-}
-
-void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc)
-{
- AO_GC;
- LOG(DEBUG,"ao %p: complete, rc=%d",ao,rc);
- assert(ao->magic == LIBXL__AO_MAGIC);
- assert(!ao->complete);
- assert(!ao->nested_root);
- assert(!ao->nested_progeny);
- ao->complete = 1;
- ao->rc = rc;
- LIBXL_LIST_REMOVE(ao, inprogress_entry);
- if (ao->outstanding_killed_child)
- LOG(DEBUG, "ao %p: .. but waiting for %d fork to exit",
- ao, ao->outstanding_killed_child);
- libxl__ao_complete_check_progress_reports(egc, ao);
-}
-
-static bool ao_work_outstanding(libxl__ao *ao)
-{
- /*
- * We don't consider an ao complete if it has any outstanding
- * callbacks. These callbacks might be outstanding on other
- * threads, queued up in the other threads' egc's. Those threads
- * will, after making the callback, take out the lock again,
- * decrement progress_reports_outstanding, and call
- * libxl__ao_complete_check_progress_reports.
- */
- return !ao->complete || ao->progress_reports_outstanding
- || ao->outstanding_killed_child;
-}
-
-void libxl__ao_complete_check_progress_reports(libxl__egc *egc, libxl__ao *ao)
-{
- EGC_GC;
- libxl_ctx *ctx = libxl__gc_owner(&egc->gc);
- assert(ao->progress_reports_outstanding >= 0);
-
- if (ao_work_outstanding(ao))
- return;
-
- if (ao->poller) {
- assert(ao->in_initiator);
- if (!ao->constructing)
- /* don't bother with this if we're not in the event loop */
- libxl__poller_wakeup(gc, ao->poller);
- } else if (ao->how.callback) {
- LOG(DEBUG, "ao %p: complete for callback", ao);
- LIBXL_TAILQ_INSERT_TAIL(&egc->aos_for_callback, ao, entry_for_callback);
- } else {
- libxl_event *ev;
- ev = NEW_EVENT(egc, OPERATION_COMPLETE, ao->domid, ao->how.u.for_event);
- if (ev) {
- ev->u.operation_complete.rc = ao->rc;
- libxl__event_occurred(egc, ev);
- }
- ao->notified = 1;
- }
-
- ao__check_destroy(ctx, ao);
-}
-
-libxl__ao *libxl__ao_create(libxl_ctx *ctx, uint32_t domid,
- const libxl_asyncop_how *how,
- const char *file, int line, const char *func)
-{
- libxl__ao *ao;
-
- ao = calloc(1, sizeof(*ao));
- if (!ao) goto out;
-
- ao->magic = LIBXL__AO_MAGIC;
- ao->constructing = 1;
- ao->in_initiator = 1;
- ao__manip_enter(ao);
- ao->poller = 0;
- ao->domid = domid;
- LIBXL_INIT_GC(ao->gc, ctx);
-
- if (how) {
- ao->how = *how;
- } else {
- ao->poller = libxl__poller_get(&ao->gc);
- if (!ao->poller) goto out;
- }
- libxl__log(ctx,XTL_DEBUG,-1,file,line,func,domid,
- "ao %p: create: how=%p callback=%p poller=%p",
- ao, how, ao->how.callback, ao->poller);
-
- LIBXL_LIST_INSERT_HEAD(&ctx->aos_inprogress, ao, inprogress_entry);
-
- return ao;
-
- out:
- if (ao) libxl__ao__destroy(ctx, ao);
- return NULL;
-}
-
-
-int libxl__ao_inprogress(libxl__ao *ao,
- const char *file, int line, const char *func)
-{
- AO_GC;
- int rc;
- uint32_t domid = ao->domid;
-
- assert(ao->magic == LIBXL__AO_MAGIC);
- assert(ao->constructing);
- assert(ao->in_initiator);
- ao->constructing = 0;
-
- if (ao->nested_root)
- domid = ao->nested_root->domid;
-
- libxl__log(CTX,XTL_DEBUG,-1,file,line,func,domid,
- "ao %p: inprogress: poller=%p, flags=%s%s%s%s",
- ao, ao->poller,
- ao->constructing ? "o" : "",
- ao->in_initiator ? "i" : "",
- ao->complete ? "c" : "",
- ao->notified ? "n" : "");
-
- if (ao->poller) {
- /* Caller wants it done synchronously. */
- /* We use a fresh gc, so that we can free things
- * each time round the loop. */
- libxl__egc egc;
- LIBXL_INIT_EGC(egc,CTX);
-
- for (;;) {
- assert(ao->magic == LIBXL__AO_MAGIC);
-
- if (!ao_work_outstanding(ao)) {
- rc = ao->rc;
- ao->notified = 1;
- break;
- }
-
- DBG("ao %p: not ready, waiting",ao);
-
- rc = eventloop_iteration(&egc,ao->poller);
- if (rc) {
- /* Oh dear, this is quite unfortunate. */
- LOG(ERROR,
- "Error waiting for"" event during long-running operation (rc=%d)",
- rc);
- sleep(1);
- /* It's either this or return ERROR_I_DONT_KNOW_WHETHER
- * _THE_THING_YOU_ASKED_FOR_WILL_BE_DONE_LATER_WHEN
- * _YOU_DIDNT_EXPECT_IT, since we don't have a
- * synchronous cancellation ability. */
- }
-
- /* The call to egc..1_baton is below, only if we are leaving. */
- CTX_UNLOCK;
- libxl__egc_cleanup_2_ul_cb_gc(&egc);
- CTX_LOCK;
- }
-
- /* Dispose of this early so libxl__egc_ao_cleanup_1_baton
- * doesn't mistake us for a baton-holder. No-one much is
- * going to look at this ao now so setting this to 0 is fine.
- * We can't call _baton below _leave because _leave destroys
- * our gc, which _baton needs. */
- libxl__poller_put(CTX, ao->poller);
- ao->poller = 0;
- } else {
- rc = 0;
- }
-
- libxl__egc_ao_cleanup_1_baton(gc);
- ao->in_initiator = 0;
- ao__manip_leave(CTX, ao);
-
- return rc;
-}
-
-
-/* abort requests */
-
-static int ao__abort(libxl_ctx *ctx, libxl__ao *parent)
-/* Temporarily unlocks ctx, which must be locked exactly once on entry. */
-{
- libxl__egc egc;
- LIBXL_INIT_EGC(egc,ctx);
-
- int rc;
- ao__manip_enter(parent);
-
- if (parent->aborting) {
- rc = ERROR_ABORTED;
- goto out;
- }
-
- parent->aborting = 1;
-
- if (LIBXL_LIST_EMPTY(&parent->abortables)) {
- LIBXL__LOG(ctx, LIBXL__LOG_DEBUG,
- "ao %p: abort requested and noted, but no-one interested",
- parent);
- rc = 0;
- goto out;
- }
-
- /* We keep calling abort hooks until there are none left */
- while (!LIBXL_LIST_EMPTY(&parent->abortables)) {
- assert(!parent->complete);
-
- libxl__ao_abortable *abrt = LIBXL_LIST_FIRST(&parent->abortables);
- assert(parent == ao_nested_root(abrt->ao));
-
- LIBXL_LIST_REMOVE(abrt, entry);
- abrt->registered = 0;
-
- LIBXL__LOG(ctx, LIBXL__LOG_DEBUG,
- "ao %p: abrt=%p: aborting", parent, abrt->ao);
- abrt->callback(&egc, abrt, ERROR_ABORTED);
-
- /* The call to egc..1_baton is in the out block below. */
- libxl__ctx_unlock(ctx);
- libxl__egc_cleanup_2_ul_cb_gc(&egc);
- libxl__ctx_lock(ctx);
- }
-
- rc = 0;
-
- out:
- libxl__egc_ao_cleanup_1_baton(&egc.gc);
- ao__manip_leave(ctx, parent);
- /* The call to egc..2_ul_cb_gc is above. This is sufficient
- * because only code inside the loop adds anything to the egc, and
- * we ensures that the egc is clean when we leave the loop. */
- return rc;
-}
-
-int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how)
-{
- libxl__ao *search;
- libxl__ctx_lock(ctx);
- int rc;
-
- LIBXL_LIST_FOREACH(search, &ctx->aos_inprogress, inprogress_entry) {
- if (how) {
- /* looking for ao to be reported by callback or event */
- if (search->poller)
- /* sync */
- continue;
- if (how->callback != search->how.callback)
- continue;
- if (how->callback
- ? (how->u.for_callback != search->how.u.for_callback)
- : (how->u.for_event != search->how.u.for_event))
- continue;
- } else {
- /* looking for synchronous call */
- if (!search->poller)
- /* async */
- continue;
- }
- goto found;
- }
- rc = ERROR_NOTFOUND;
- goto out;
-
- found:
- rc = ao__abort(ctx, search);
- out:
- libxl__ctx_unlock(ctx);
- return rc;
-}
-
-int libxl__ao_aborting(libxl__ao *ao)
-{
- libxl__ao *root = ao_nested_root(ao);
- AO_GC;
-
- if (root->aborting) {
- DBG("ao=%p: aborting at explicit check (root=%p)", ao, root);
- return ERROR_ABORTED;
- }
-
- return 0;
-}
-
-int libxl__ao_abortable_register(libxl__ao_abortable *abrt)
-{
- libxl__ao *ao = abrt->ao;
- libxl__ao *root = ao_nested_root(ao);
- AO_GC;
-
- if (root->aborting) {
- DBG("ao=%p: preemptively aborting ao_abortable registration %p (root=%p)",
- ao, abrt, root);
- return ERROR_ABORTED;
- }
-
- DBG("ao=%p, abrt=%p: registering (root=%p)", ao, abrt, root);
- LIBXL_LIST_INSERT_HEAD(&root->abortables, abrt, entry);
- abrt->registered = 1;
-
- return 0;
-}
-
-_hidden void libxl__ao_abortable_deregister(libxl__ao_abortable *abrt)
-{
- if (!abrt->registered)
- return;
-
- libxl__ao *ao = abrt->ao;
- libxl__ao *root __attribute__((unused)) = ao_nested_root(ao);
- AO_GC;
-
- DBG("ao=%p, abrt=%p: deregistering (root=%p)", ao, abrt, root);
- LIBXL_LIST_REMOVE(abrt, entry);
- abrt->registered = 0;
-}
-
-
-/* progress reporting */
-
-/* The application indicates a desire to ignore events by passing NULL
- * for how. But we want to copy *how. So we have this dummy function
- * whose address is stored in callback if the app passed how==NULL. */
-static void dummy_asyncprogress_callback_ignore
- (libxl_ctx *ctx, libxl_event *ev, void *for_callback) { }
-
-void libxl__ao_progress_gethow(libxl_asyncprogress_how *in_state,
- const libxl_asyncprogress_how *from_app) {
- if (from_app)
- *in_state = *from_app;
- else
- in_state->callback = dummy_asyncprogress_callback_ignore;
-}
-
-void libxl__ao_progress_report(libxl__egc *egc, libxl__ao *ao,
- const libxl_asyncprogress_how *how, libxl_event *ev)
-{
- AO_GC;
- assert(!ao->nested_root);
- if (how->callback == dummy_asyncprogress_callback_ignore) {
- LOG(DEBUG,"ao %p: progress report: ignored",ao);
- libxl_event_free(CTX,ev);
- /* ignore */
- } else if (how->callback) {
- libxl__aop_occurred *aop = libxl__zalloc(&egc->gc, sizeof(*aop));
- ao->progress_reports_outstanding++;
- aop->ao = ao;
- aop->ev = ev;
- aop->how = how;
- LIBXL_TAILQ_INSERT_TAIL(&egc->aops_for_callback, aop, entry);
- LOG(DEBUG,"ao %p: progress report: callback queued aop=%p",ao,aop);
- } else {
- LOG(DEBUG,"ao %p: progress report: event queued ev=%p type=%s",
- ao, ev, libxl_event_type_to_string(ev->type));
- libxl__event_occurred(egc, ev);
- }
-}
-
-
-/* nested ao */
-
-static libxl__ao *ao_nested_root(libxl__ao *ao) {
- libxl__ao *root = ao->nested_root ? : ao;
- assert(!root->nested_root);
- return root;
-}
-
-_hidden libxl__ao *libxl__nested_ao_create(libxl__ao *parent)
-{
- libxl__ao *child = NULL, *root;
- libxl_ctx *ctx = libxl__gc_owner(&parent->gc);
-
- assert(parent->magic == LIBXL__AO_MAGIC);
- root = ao_nested_root(parent);
-
- child = libxl__zalloc(&ctx->nogc_gc, sizeof(*child));
- child->magic = LIBXL__AO_MAGIC;
- child->nested_root = root;
- assert(root->nested_progeny < INT_MAX);
- root->nested_progeny++;
- LIBXL_INIT_GC(child->gc, ctx);
- libxl__gc *gc = &child->gc;
-
- LOG(DEBUG,"ao %p: nested ao, parent %p", child, parent);
- return child;
-}
-
-_hidden void libxl__nested_ao_free(libxl__ao *child)
-{
- assert(child->magic == LIBXL__AO_MAGIC);
- libxl__ao *root = child->nested_root;
- assert(root);
- assert(root->nested_progeny > 0);
- root->nested_progeny--;
- libxl_ctx *ctx = libxl__gc_owner(&child->gc);
- libxl__ao__destroy(ctx, child);
-}
-
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2011 Citrix Ltd.
- * Author Ian Jackson <ian.jackson@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#ifndef LIBXL_EVENT_H
-#define LIBXL_EVENT_H
-
-#include <libxl.h>
-#include <poll.h>
-#include <sys/time.h>
-
-/*======================================================================*/
-
-/*
- * Domain event handling - getting Xen events from libxl
- *
- * (Callers inside libxl may not call libxl_event_check or _wait.)
- */
-
-#define LIBXL_EVENTMASK_ALL (~(unsigned long)0)
-
-typedef int libxl_event_predicate(const libxl_event*, void *user);
- /* Return value is 0 if the event is unwanted or non-0 if it is.
- * Predicates are not allowed to fail.
- */
-
-int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r,
- uint64_t typemask,
- libxl_event_predicate *predicate, void *predicate_user)
- LIBXL_EXTERNAL_CALLERS_ONLY;
- /* Searches for an event, already-happened, which matches typemask
- * and predicate. predicate==0 matches any event.
- * libxl_event_check returns the event, which must then later be
- * freed by the caller using libxl_event_free.
- *
- * Returns ERROR_NOT_READY if no such event has happened.
- */
-
-int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r,
- uint64_t typemask,
- libxl_event_predicate *predicate, void *predicate_user)
- LIBXL_EXTERNAL_CALLERS_ONLY;
- /* Like libxl_event_check but blocks if no suitable events are
- * available, until some are. Uses libxl_osevent_beforepoll/
- * _afterpoll so may be inefficient if very many domains are being
- * handled by a single program.
- */
-
-void libxl_event_free(libxl_ctx *ctx, libxl_event *event);
-
-
-/* Alternatively or additionally, the application may also use this: */
-
-typedef struct libxl_event_hooks {
- uint64_t event_occurs_mask;
- void (*event_occurs)(void *user,
-#ifndef LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG
- const
-#endif
- libxl_event *event);
- void (*disaster)(void *user, libxl_event_type type,
- const char *msg, int errnoval);
-} libxl_event_hooks;
-
-void libxl_event_register_callbacks(libxl_ctx *ctx,
- const libxl_event_hooks *hooks, void *user);
- /*
- * Arranges that libxl will henceforth call event_occurs for any
- * events whose type is set in event_occurs_mask, rather than
- * queueing the event for retrieval by libxl_event_check/wait.
- * Events whose bit is clear in mask are not affected.
- *
- * event becomes owned by the application and must be freed, either
- * by event_occurs or later.
- *
- * event_occurs may be NULL if mask is 0.
- *
- * libxl_event_register_callback also provides a way for libxl to
- * report to the application that there was a problem reporting
- * events; this can occur due to lack of host memory during event
- * handling, or other wholly unrecoverable errors from system calls
- * made by libxl. This will not happen for frivolous reasons - only
- * if the system, or the Xen components of it, are badly broken.
- *
- * msg and errnoval will describe the action that libxl was trying
- * to do, and type specifies the type of libxl events which may be
- * missing. type may be 0 in which case events of all types may be
- * missing.
- *
- * disaster may be NULL. If it is, or if _register_callbacks has
- * not been called, errors of this kind are fatal to the entire
- * application: libxl will print messages to its logs and to stderr
- * and call exit(-1).
- *
- * If disaster returns, it may be the case that some or all future
- * libxl calls will return errors; likewise it may be the case that
- * no more events (of the specified type, if applicable) can be
- * produced. An application which supplies a disaster function
- * should normally react either by exiting, or by (when it has
- * returned to its main event loop) shutting down libxl with
- * libxl_ctx_free and perhaps trying to restart it with
- * libxl_ctx_init.
- *
- * In any case before calling disaster, libxl will have logged a
- * message with level XTL_CRITICAL.
- *
- * Reentrancy: it IS permitted to call libxl from within
- * event_occurs. It is NOT permitted to call libxl from within
- * disaster. The event_occurs and disaster callbacks may occur on
- * any thread in which the application calls libxl.
- *
- * libxl_event_register_callbacks may be called as many times, with
- * different parameters, as the application likes; the most recent
- * call determines the libxl behaviour. However it is NOT safe to
- * call _register_callbacks concurrently with, or reentrantly from,
- * any other libxl function, nor while any event-generation
- * facilities are enabled.
- */
-
-
-/*
- * Events are only generated if they have been requested.
- * The following functions request the generation of specific events.
- *
- * Each set of functions for controlling event generation has this form:
- *
- * typedef struct libxl__evgen_FOO libxl__evgen_FOO;
- * int libxl_evenable_FOO(libxl_ctx *ctx, FURTHER PARAMETERS,
- * libxl_ev_user user, libxl__evgen_FOO **evgen_out);
- * void libxl_evdisable_FOO(libxl_ctx *ctx, libxl__evgen_FOO *evgen);
- *
- * The evenable function arranges that the events (as described in the
- * doc comment for the individual function) will start to be generated
- * by libxl. On success, *evgen_out is set to a non-null pointer to
- * an opaque struct.
- *
- * The user value is returned in the generated events and may be
- * used by the caller for whatever it likes. The type ev_user is
- * guaranteed to be an unsigned integer type which is at least
- * as big as uint64_t and is also guaranteed to be big enough to
- * contain any intptr_t value.
- *
- * If it becomes desirable to stop generation of the relevant events,
- * or to reclaim the resources in libxl associated with the evgen
- * structure, the same evgen value should be passed to the evdisable
- * function. However, note that events which occurred prior to the
- * evdisable call may still be returned.
- *
- * The caller may enable identical events more than once. If they do
- * so, each actual occurrence will generate several events to be
- * returned by libxl_event_check, with the appropriate user value(s).
- * Aside from this, each occurrence of each event is returned by
- * libxl_event_check exactly once.
- *
- * An evgen is associated with the libxl_ctx used for its creation.
- * After libxl_ctx_free, all corresponding evgen handles become
- * invalid and must no longer be passed to evdisable.
- *
- * Applications should ensure that they eventually retrieve every
- * event using libxl_event_check or libxl_event_wait, since events
- * which occur but are not retrieved by the application will be queued
- * inside libxl indefinitely. libxl_event_check/_wait may be O(n)
- * where n is the number of queued events which do not match the
- * criteria specified in the arguments to check/wait.
- */
-
-typedef struct libxl__evgen_domain_death libxl_evgen_domain_death;
-int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
- libxl_ev_user, libxl_evgen_domain_death **evgen_out);
-void libxl_evdisable_domain_death(libxl_ctx *ctx, libxl_evgen_domain_death*);
- /* Arranges for the generation of DOMAIN_SHUTDOWN and DOMAIN_DEATH
- * events. A domain which is destroyed before it shuts down
- * may generate only a DEATH event.
- */
-
-typedef struct libxl__evgen_disk_eject libxl_evgen_disk_eject;
-int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t domid, const char *vdev,
- libxl_ev_user, libxl_evgen_disk_eject **evgen_out);
-void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject*);
- /* Arranges for the generation of DISK_EJECT events. A copy of the
- * string *vdev will be made for libxl's internal use, and a pointer
- * to this (or some other) copy will be returned as the vdev
- * member of event.u.
- */
-
-
-/*======================================================================*/
-
-/*
- * OS event handling - passing low-level OS events to libxl
- *
- * Event-driven programs must use these facilities to allow libxl
- * to become aware of readability/writeability of file descriptors
- * and the occurrence of timeouts.
- *
- * There are two approaches available. The first is appropriate for
- * simple programs handling reasonably small numbers of domains:
- *
- * for (;;) {
- * libxl_osevent_beforepoll(...)
- * poll();
- * libxl_osevent_afterpoll(...);
- * for (;;) {
- * r = libxl_event_check(...);
- * if (r==ERROR_NOT_READY) break;
- * if (r) goto error_out;
- * do something with the event;
- * }
- * }
- *
- * The second approach uses libxl_osevent_register_hooks and is
- * suitable for programs which are already using a callback-based
- * event library.
- *
- * An application may freely mix the two styles of interaction.
- *
- * (Callers inside libxl may not call libxl_osevent_... functions.)
- */
-
-struct pollfd;
-
-/* The caller should provide beforepoll with some space for libxl's
- * fds, and tell libxl how much space is available by setting *nfds_io.
- * fds points to the start of this space (and fds may be a pointer into
- * a larger array, for example, if the application has some fds of
- * its own that it is interested in).
- *
- * On return *nfds_io will in any case have been updated by libxl
- * according to how many fds libxl wants to poll on.
- *
- * If the space was sufficient, libxl fills in fds[0..<new
- * *nfds_io>] suitably for poll(2), updates *timeout_upd if needed,
- * and returns ok.
- *
- * If space was insufficient, fds[0..<old *nfds_io>] is undefined on
- * return; *nfds_io on return will be greater than the value on
- * entry; *timeout_upd may or may not have been updated; and
- * libxl_osevent_beforepoll returns ERROR_BUFERFULL. In this case
- * the application needs to make more space (enough space for
- * *nfds_io struct pollfd) and then call beforepoll again, before
- * entering poll(2). Typically this will involve calling realloc.
- *
- * The application may call beforepoll with fds==NULL and
- * *nfds_io==0 in order to find out how much space is needed.
- *
- * *timeout_upd is as for poll(2): it's in milliseconds, and
- * negative values mean no timeout (infinity).
- * libxl_osevent_beforepoll will only reduce the timeout, naturally.
- */
-int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
- struct pollfd *fds, int *timeout_upd,
- struct timeval now)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* nfds and fds[0..nfds] must be from the most recent call to
- * _beforepoll, as modified by poll. (It is therefore not possible
- * to have multiple threads simultaneously polling using this
- * interface.)
- *
- * This function actually performs all of the IO and other actions,
- * and generates events (libxl_event), which are implied by either
- * (a) the time of day or (b) both (i) the returned information from
- * _beforepoll, and (ii) the results from poll specified in
- * fds[0..nfds-1]. Generated events can then be retrieved by
- * libxl_event_check.
- */
-void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds,
- struct timeval now)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-
-typedef struct libxl_osevent_hooks {
- int (*fd_register)(void *user, int fd, void **for_app_registration_out,
- short events, void *for_libxl);
- int (*fd_modify)(void *user, int fd, void **for_app_registration_update,
- short events);
- void (*fd_deregister)(void *user, int fd, void *for_app_registration);
- int (*timeout_register)(void *user, void **for_app_registration_out,
- struct timeval abs, void *for_libxl);
- int (*timeout_modify)(void *user, void **for_app_registration_update,
- struct timeval abs)
- /* only ever called with abs={0,0}, meaning ASAP */;
- void (*timeout_deregister)(void *user, void *for_app_registration)
- /* will never be called */;
-} libxl_osevent_hooks;
-
-/* The application which calls register_fd_hooks promises to
- * maintain a register of fds and timeouts that libxl is interested
- * in, and make calls into libxl (libxl_osevent_occurred_*)
- * when those fd events and timeouts occur. This is more efficient
- * than _beforepoll/_afterpoll if there are many fds (which can
- * happen if the same libxl application is managing many domains).
- *
- * For an fd event, events is as for poll(). register or modify may
- * be called with events==0, in which case it must still work
- * normally, just not generate any events.
- *
- * For a timeout event, milliseconds is as for poll().
- * Specifically, negative values of milliseconds mean NO TIMEOUT.
- * This is used by libxl to temporarily disable a timeout.
- *
- * If the register or modify hook succeeds it may update
- * *for_app_registration_out/_update and must then return 0.
- * On entry to register, *for_app_registration_out is always NULL.
- *
- * A registration or modification hook may fail, in which case it
- * must leave the registration state of the fd or timeout unchanged.
- * It may then either return ERROR_OSEVENT_REG_FAIL or any positive
- * int. The value returned will be passed up through libxl and
- * eventually returned back to the application. When register
- * fails, any value stored into *for_registration_out is ignored by
- * libxl; when modify fails, any changed value stored into
- * *for_registration_update is honoured by libxl and will be passed
- * to future modify or deregister calls.
- *
- * libxl may want to register more than one callback for any one fd;
- * in that case: (i) each such registration will have at least one bit
- * set in revents which is unique to that registration; (ii) if an
- * event occurs which is relevant for multiple registrations the
- * application's event system may call libxl_osevent_occurred_fd
- * for one, some, or all of those registrations.
- *
- * If fd_modify is used, it is permitted for the application's event
- * system to still make calls to libxl_osevent_occurred_fd for the
- * "old" set of requested events; these will be safely ignored by
- * libxl.
- *
- * libxl will remember the value stored in *for_app_registration_out
- * (or *for_app_registration_update) by a successful call to
- * register (or modify), and pass it to subsequent calls to modify
- * or deregister.
- *
- * Note that the application must cope with a call from libxl to
- * timeout_modify racing with its own call to
- * libxl__osevent_occurred_timeout. libxl guarantees that
- * timeout_modify will only be called with abs={0,0} but the
- * application must still ensure that libxl's attempt to cause the
- * timeout to occur immediately is safely ignored even the timeout is
- * actually already in the process of occurring.
- *
- * timeout_deregister is not used because it forms part of a
- * deprecated unsafe mode of use of the API.
- *
- * osevent_register_hooks may be called only once for each libxl_ctx.
- * libxl may make calls to register/modify/deregister from within
- * any libxl function (indeed, it will usually call register from
- * register_event_hooks). Conversely, the application MUST NOT make
- * the event occurrence calls (libxl_osevent_occurred_*) into libxl
- * reentrantly from within libxl (for example, from within the
- * register/modify functions).
- *
- * Lock hierarchy: the register/modify/deregister functions may be
- * called with locks held. These locks (the "libxl internal locks")
- * are inside the libxl_ctx. Therefore, if those register functions
- * acquire any locks of their own ("caller register locks") outside
- * libxl, to avoid deadlock one of the following must hold for each
- * such caller register lock:
- * (a) "acquire libxl internal locks before caller register lock":
- * No libxl function may be called with the caller register
- * lock held.
- * (b) "acquire caller register lock before libxl internal locks":
- * No libxl function may be called _without_ the caller
- * register lock held.
- * Of these we would normally recommend (a).
- *
- * The value *hooks is not copied and must outlast the libxl_ctx.
- */
-void libxl_osevent_register_hooks(libxl_ctx *ctx,
- const libxl_osevent_hooks *hooks,
- void *user);
-
-/* It is NOT legal to call _occurred_ reentrantly within any libxl
- * function. Specifically it is NOT legal to call it from within
- * a register callback. Conversely, libxl MAY call register/deregister
- * from within libxl_event_occurred_call_*.
- */
-
-void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl,
- int fd, short events, short revents)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* Implicitly, on entry to this function the timeout has been
- * deregistered. If _occurred_timeout is called, libxl will not
- * call timeout_deregister; if it wants to requeue the timeout it
- * will call timeout_register again.
- */
-void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-
-/*======================================================================*/
-
-/*
- * Subprocess handling.
- *
- * Unfortunately the POSIX interface makes this very awkward.
- *
- * There are two possible arrangements for collecting statuses from
- * wait/waitpid.
- *
- * For naive programs:
- *
- * libxl will keep a SIGCHLD handler installed whenever it has an
- * active (unreaped) child. It will reap all children with
- * wait(); any children it does not recognise will be passed to
- * the application via an optional callback (and will result in
- * logged warnings if no callback is provided or the callback
- * denies responsibility for the child).
- *
- * libxl may have children whenever:
- *
- * - libxl is performing an operation which can be made
- * asynchronous; ie one taking a libxl_asyncop_how, even
- * if NULL is passed indicating that the operation is
- * synchronous; or
- *
- * - events of any kind are being generated, as requested
- * by libxl_evenable_....
- *
- * A multithreaded application which is naive in this sense may
- * block SIGCHLD on some of its threads, but there must be at
- * least one thread that has SIGCHLD unblocked. libxl will not
- * modify the blocking flag for SIGCHLD (except that it may create
- * internal service threads with all signals blocked).
- *
- * A naive program must only have at any one time only
- * one libxl context which might have children.
- *
- * For programs which run their own children alongside libxl's:
- *
- * A program which does this must call libxl_childproc_setmode.
- * There are three options:
- *
- * libxl_sigchld_owner_libxl:
- *
- * While any libxl operation which might use child processes
- * is running, works like libxl_sigchld_owner_libxl_always;
- * but, deinstalls the handler the rest of the time.
- *
- * In this mode, the application, while it uses any libxl
- * operation which might create or use child processes (see
- * above):
- * - Must not have any child processes running.
- * - Must not install a SIGCHLD handler.
- * - Must not reap any children.
- *
- * This is the default (i.e. if setmode is not called, or 0 is
- * passed for hooks).
- *
- * libxl_sigchld_owner_mainloop:
- *
- * The application must install a SIGCHLD handler and reap (at
- * least) all of libxl's children and pass their exit status to
- * libxl by calling libxl_childproc_exited. (If the application
- * has multiple libxl ctx's, it must call libxl_childproc_exited
- * on each ctx.)
- *
- * libxl_sigchld_owner_libxl_always:
- *
- * The application expects this libxl ctx to reap all of the
- * process's children, and provides a callback to be notified of
- * their exit statuses. The application must have only one
- * libxl_ctx configured this way.
- *
- * libxl_sigchld_owner_libxl_always_selective_reap:
- *
- * The application expects to reap all of its own children
- * synchronously, and does not use SIGCHLD. libxl is to install
- * a SIGCHLD handler. The application may have multiple
- * libxl_ctxs configured this way; in which case all of its ctxs
- * must be so configured.
- */
-
-
-typedef enum {
- /* libxl owns SIGCHLD whenever it has a child, and reaps
- * all children, including those not spawned by libxl. */
- libxl_sigchld_owner_libxl,
-
- /* Application promises to discover when SIGCHLD occurs and call
- * libxl_childproc_exited or libxl_childproc_sigchld_occurred (but
- * NOT from within a signal handler). libxl will not itself
- * arrange to (un)block or catch SIGCHLD. */
- libxl_sigchld_owner_mainloop,
-
- /* libxl owns SIGCHLD all the time, and the application is
- * relying on libxl's event loop for reaping its children too. */
- libxl_sigchld_owner_libxl_always,
-
- /* libxl owns SIGCHLD all the time, but it must only reap its own
- * children. The application will reap its own children
- * synchronously with waitpid, without the assistance of SIGCHLD. */
- libxl_sigchld_owner_libxl_always_selective_reap,
-} libxl_sigchld_owner;
-
-typedef struct {
- libxl_sigchld_owner chldowner;
-
- /* All of these are optional: */
-
- /* Called by libxl instead of fork. Should behave exactly like
- * fork, including setting errno etc. May NOT reenter into libxl.
- * Application may use this to discover pids of libxl's children,
- * for example.
- */
- pid_t (*fork_replacement)(void *user);
-
- /* With libxl_sigchld_owner_libxl, called by libxl when it has
- * reaped a pid. (Not permitted with _owner_mainloop.)
- *
- * Should return 0 if the child was recognised by the application
- * (or if the application does not keep those kind of records),
- * ERROR_UNKNOWN_CHILD if the application knows that the child is not
- * the application's; if it returns another error code it is a
- * disaster as described for libxl_event_register_callbacks.
- * (libxl will report unexpected children to its error log.)
- *
- * If not supplied, the application is assumed not to start
- * any children of its own.
- *
- * This function is NOT called from within the signal handler.
- * Rather it will be called from inside a libxl's event handling
- * code and thus only when libxl is running, for example from
- * within libxl_event_wait. (libxl uses the self-pipe trick
- * to implement this.)
- *
- * childproc_exited_callback may call back into libxl, but it
- * is best to avoid making long-running libxl calls as that might
- * stall the calling event loop while the nested operation
- * completes.
- */
- int (*reaped_callback)(pid_t, int status, void *user);
-} libxl_childproc_hooks;
-
-/* hooks may be 0 in which is equivalent to &{ libxl_sigchld_owner_libxl, 0, 0 }
- *
- * May not be called when libxl might have any child processes, or the
- * behaviour is undefined. So it is best to call this at
- * initialisation.
- *
- * The value *hooks is not copied and must outlast the libxl_ctx.
- */
-void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks *hooks,
- void *user);
-
-/*
- * This function is for an application which owns SIGCHLD and which
- * reaps all of the process's children, and dispatches the exit status
- * to the correct place inside the application.
- *
- * May be called only by an application which has called setmode with
- * chldowner == libxl_sigchld_owner_mainloop. If pid was a process started
- * by this instance of libxl, returns 0 after doing whatever
- * processing is appropriate. Otherwise silently returns
- * ERROR_UNKNOWN_CHILD. No other error returns are possible.
- *
- * May NOT be called from within a signal handler which might
- * interrupt any libxl operation. The application will almost
- * certainly need to use the self-pipe trick (or a working pselect or
- * ppoll) to implement this.
- */
-int libxl_childproc_reaped(libxl_ctx *ctx, pid_t, int status)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * This function is for an application which owns SIGCHLD but which
- * doesn't keep track of all of its own children in a manner suitable
- * for reaping all of them and then dispatching them.
- *
- * Such an the application must notify libxl, by calling this
- * function, that a SIGCHLD occurred. libxl will then check all its
- * children, reap any that are ready, and take any action necessary -
- * but it will not reap anything else.
- *
- * May be called only by an application which has called setmode with
- * chldowner == libxl_sigchld_owner_mainloop.
- *
- * May NOT be called from within a signal handler which might
- * interrupt any libxl operation (just like libxl_childproc_reaped).
- */
-void libxl_childproc_sigchld_occurred(libxl_ctx *ctx)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-
-/*
- * An application which initialises a libxl_ctx in a parent process
- * and then forks a child which does not quickly exec, must
- * instead libxl_postfork_child_noexec in the child. One call
- * on any existing (or specially made) ctx is sufficient; after
- * this all previously existing libxl_ctx's are invalidated and
- * must not be used - or even freed. It is harmless to call this
- * postfork function and then exec anyway.
- *
- * Until libxl_postfork_child_noexec has returned:
- * - No other libxl calls may be made.
- * - If any libxl ctx was configured handle the process's SIGCHLD,
- * the child may not create further (grand)child processes, nor
- * manipulate SIGCHLD.
- *
- * libxl_postfork_child_noexec may not reclaim all the resources
- * associated with the libxl ctx. This includes but is not limited
- * to: ordinary memory; files on disk and in /var/run; file
- * descriptors; memory mapped into the process from domains being
- * managed (grant maps); Xen event channels. Use of libxl in
- * processes which fork long-lived children is not recommended for
- * this reason. libxl_postfork_child_noexec is provided so that
- * an application can make further libxl calls in a child which
- * is going to exec or exit soon.
- */
-void libxl_postfork_child_noexec(libxl_ctx *ctx);
-
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-static void check_open_fds(const char *what)
-{
- const char *env_debug;
- int debug;
- int i, flags, badness = 0;
-
- env_debug = getenv("_LIBXL_DEBUG_EXEC_FDS");
- if (!env_debug) return;
-
- debug = strtol(env_debug, (char **) NULL, 10);
- if (debug <= 0) return;
-
- for (i = 4; i < 256; i++) {
-#ifdef __linux__
- ssize_t len;
- char path[PATH_MAX];
- char linkpath[PATH_MAX+1];
-#endif
- flags = fcntl(i, F_GETFD);
- if ( flags == -1 ) {
- if ( errno != EBADF )
- fprintf(stderr, "libxl: execing %s: fd %d flags returned %s (%d)\n",
- what, i, strerror(errno), errno);
- continue;
- }
-
- if ( flags & FD_CLOEXEC )
- continue;
-
- badness++;
-
-#ifdef __linux__
- snprintf(path, PATH_MAX, "/proc/%d/fd/%d", getpid(), i);
- len = readlink(path, linkpath, PATH_MAX);
- if (len > 0) {
- linkpath[len] = '\0';
- fprintf(stderr, "libxl: execing %s: fd %d is open to %s with flags %#x\n",
- what, i, linkpath, flags);
- } else
-#endif
- fprintf(stderr, "libxl: execing %s: fd %d is open with flags %#x\n",
- what, i, flags);
- }
- if (debug < 2) return;
- if (badness) abort();
-}
-
-void libxl__exec(libxl__gc *gc, int stdinfd, int stdoutfd, int stderrfd,
- const char *arg0, char *const args[], char *const env[])
- /* call this in the child */
-{
- if (stdinfd != -1)
- dup2(stdinfd, STDIN_FILENO);
- if (stdoutfd != -1)
- dup2(stdoutfd, STDOUT_FILENO);
- if (stderrfd != -1)
- dup2(stderrfd, STDERR_FILENO);
-
- if (stdinfd > 2)
- close(stdinfd);
- if (stdoutfd > 2 && stdoutfd != stdinfd)
- close(stdoutfd);
- if (stderrfd > 2 && stderrfd != stdinfd && stderrfd != stdoutfd)
- close(stderrfd);
-
- check_open_fds(arg0);
-
- signal(SIGPIPE, SIG_DFL);
- /* in case our caller set it to IGN. subprocesses are entitled
- * to assume they got DFL. */
-
- if (env != NULL) {
- for (int i = 0; env[i] != NULL && env[i+1] != NULL; i += 2) {
- if (setenv(env[i], env[i+1], 1) < 0) {
- LOGEV(ERROR, errno, "setting env vars (%s = %s)",
- env[i], env[i+1]);
- goto out;
- }
- }
- }
- execvp(arg0, args);
-
-out:
- fprintf(stderr, "libxl: cannot execute %s: %s\n", arg0, strerror(errno));
- _exit(-1);
-}
-
-void libxl_report_child_exitstatus(libxl_ctx *ctx,
- xentoollog_level level,
- const char *what, pid_t pid, int status)
-{
-
- if (WIFEXITED(status)) {
- int st = WEXITSTATUS(status);
- if (st)
- LIBXL__LOG(ctx, level, "%s [%ld] exited"
- " with error status %d", what, (unsigned long)pid, st);
- else
- LIBXL__LOG(ctx, level, "%s [%ld] unexpectedly"
- " exited status zero", what, (unsigned long)pid);
- } else if (WIFSIGNALED(status)) {
- int sig = WTERMSIG(status);
- const char *str = strsignal(sig);
- const char *coredump = WCOREDUMP(status) ? " (core dumped)" : "";
- if (str)
- LIBXL__LOG(ctx, level, "%s [%ld] died due to"
- " fatal signal %s%s", what, (unsigned long)pid,
- str, coredump);
- else
- LIBXL__LOG(ctx, level, "%s [%ld] died due to unknown"
- " fatal signal number %d%s", what, (unsigned long)pid,
- sig, coredump);
- } else {
- LIBXL__LOG(ctx, level, "%s [%ld] gave unknown"
- " wait status 0x%x", what, (unsigned long)pid, status);
- }
-}
-
-int libxl__spawn_record_pid(libxl__gc *gc, libxl__spawn_state *spawn, pid_t pid)
-{
- int r, rc;
-
- rc = libxl__ev_child_xenstore_reopen(gc, spawn->what);
- if (rc) goto out;
-
- r = libxl__xs_printf(gc, XBT_NULL, spawn->pidpath, "%d", pid);
- if (r) {
- LOGE(ERROR,
- "write %s = %d: xenstore write failed", spawn->pidpath, pid);
- rc = ERROR_FAIL; goto out;
- }
-
- rc = 0;
-
-out:
- return rc ? SIGTERM : 0;
-}
-
-int libxl__xenstore_child_wait_deprecated(libxl__gc *gc,
- uint32_t domid,
- uint32_t timeout, char *what,
- char *path, char *state,
- libxl__spawn_starting *spawning,
- int (*check_callback)(libxl__gc *gc,
- uint32_t domid,
- const char *state,
- void *userdata),
- void *check_callback_userdata)
-{
- char *p;
- unsigned int len;
- int rc = 0;
- struct xs_handle *xsh;
- int nfds;
- fd_set rfds;
- struct timeval tv;
- unsigned int num;
- char **l = NULL;
-
- xsh = xs_daemon_open();
- if (xsh == NULL) {
- LOG(ERROR, "Unable to open xenstore connection");
- goto err;
- }
-
- xs_watch(xsh, path, path);
- tv.tv_sec = timeout;
- tv.tv_usec = 0;
- nfds = xs_fileno(xsh) + 1;
- assert(!spawning);
-
- while (rc > 0 || (!rc && tv.tv_sec > 0)) {
- p = xs_read(xsh, XBT_NULL, path, &len);
- if ( NULL == p )
- goto again;
-
- if ( NULL != state && strcmp(p, state) )
- goto again;
-
- if ( NULL != check_callback ) {
- rc = (*check_callback)(gc, domid, p, check_callback_userdata);
- if ( rc > 0 )
- goto again;
- }
-
- free(p);
- xs_unwatch(xsh, path, path);
- xs_daemon_close(xsh);
- return rc;
-again:
- free(p);
- FD_ZERO(&rfds);
- FD_SET(xs_fileno(xsh), &rfds);
- rc = select(nfds, &rfds, NULL, NULL, &tv);
- if (rc > 0) {
- if (FD_ISSET(xs_fileno(xsh), &rfds)) {
- l = xs_read_watch(xsh, &num);
- if (l != NULL)
- free(l);
- else
- goto again;
- }
- }
- }
- LOG(ERROR, "%s not ready", what);
-
- xs_unwatch(xsh, path, path);
- xs_daemon_close(xsh);
-err:
- return -1;
-}
-
-
-/*----- spawn implementation -----*/
-
-/*
- * Full set of possible states of a libxl__spawn_state and its _detachable:
- *
- * detaching rc mid timeout xswatch
- * - Undefined undef undef - undef undef
- * - Idle any any Idle Idle Idle
- * - Attached OK 0 0 Active Active Active
- * - Attached Failed 0 non-0 Active Idle Idle
- * - Detaching 1 maybe Active Idle Idle
- * - Partial any any Idle Active/Idle Active/Idle
- *
- * When in states Detaching or Attached Failed, the middle process has
- * been sent a SIGKILL.
- *
- * The difference between Attached OK and Attached Failed is not
- * directly visible to callers - callers see these two the same,
- * although of course Attached OK will hopefully eventually result in
- * a call to detached_cb, whereas Attached Failed will end up
- * in a call to failure_cb.
- */
-
-/* Event callbacks. */
-static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
- int rc, const char *xsdata);
-static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
- pid_t pid, int status);
-
-/* Precondition: Partial. Results: Idle. */
-static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss);
-
-/* Precondition: Attached or Detaching; caller has logged failure reason.
- * Results: Detaching, or Attached Failed */
-static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc);
-
-void libxl__spawn_init(libxl__spawn_state *ss)
-{
- libxl__ev_child_init(&ss->mid);
- libxl__xswait_init(&ss->xswait);
-}
-
-int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *ss)
-{
- STATE_AO_GC(ss->ao);
- int r;
- pid_t child;
- int status, rc;
-
- libxl__spawn_init(ss);
- ss->rc = ss->detaching = 0;
-
- ss->xswait.ao = ao;
- ss->xswait.what = GCSPRINTF("%s startup", ss->what);
- ss->xswait.path = ss->xspath;
- ss->xswait.timeout_ms = ss->timeout_ms;
- ss->xswait.callback = spawn_watch_event;
- rc = libxl__xswait_start(gc, &ss->xswait);
- if (rc) goto out_err;
-
- pid_t middle = libxl__ev_child_fork(gc, &ss->mid, spawn_middle_death);
- if (middle ==-1) { rc = ERROR_FAIL; goto out_err; }
-
- if (middle) {
- /* parent */
- return 1;
- }
-
- /* we are now the middle process */
-
- pid_t (*fork_replacement)(void*) =
- CTX->childproc_hooks
- ? CTX->childproc_hooks->fork_replacement
- : 0;
- child =
- fork_replacement
- ? fork_replacement(CTX->childproc_user)
- : fork();
-
- if (child == -1)
- exit(255);
- if (!child) {
- return 0; /* caller runs child code */
- }
-
- int failsig = ss->midproc_cb(gc, ss, child);
- if (failsig) {
- kill(child, failsig);
- _exit(127);
- }
-
- for (;;) {
- pid_t got = waitpid(child, &status, 0);
- if (got == -1) {
- assert(errno == EINTR);
- continue;
- }
- assert(got == child);
- break;
- }
-
- r = (WIFEXITED(status) && WEXITSTATUS(status) <= 127 ? WEXITSTATUS(status) :
- WIFSIGNALED(status) && WTERMSIG(status) < 127 ? WTERMSIG(status)+128 :
- -1);
- _exit(r);
-
- out_err:
- spawn_cleanup(gc, ss);
- return rc;
-}
-
-static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss)
-{
- assert(!libxl__ev_child_inuse(&ss->mid));
- libxl__xswait_stop(gc, &ss->xswait);
-}
-
-static void spawn_detach(libxl__gc *gc, libxl__spawn_state *ss)
-/* Precondition: Attached or Detaching, but caller must have just set
- * at least one of detaching or rc.
- * Results: Detaching or Attached Failed */
-{
- int r;
-
- assert(libxl__ev_child_inuse(&ss->mid));
- assert(ss->detaching || ss->rc);
- libxl__xswait_stop(gc, &ss->xswait);
-
- pid_t child = ss->mid.pid;
- r = kill(child, SIGKILL);
- if (r && errno != ESRCH)
- LOGE(WARN, "%s: failed to kill intermediate child (pid=%lu)",
- ss->what, (unsigned long)child);
-}
-
-void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state *ss)
-{
- ss->detaching = 1;
- spawn_detach(gc, ss);
-}
-
-void libxl__spawn_initiate_failure(libxl__egc *egc, libxl__spawn_state *ss,
- int rc)
-/* The spawn state must be Attached on entry and will be Attached Failed
- * on return. */
-{
- spawn_fail(egc, ss, rc);
-}
-
-static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc)
-/* Caller must have logged. Must be last thing in calling function,
- * as it may make the callback. Precondition: Attached or Detaching. */
-{
- EGC_GC;
- assert(rc);
- if (!ss->rc)
- ss->rc = rc;
- spawn_detach(gc, ss);
-}
-
-static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
- int rc, const char *p)
-{
- /* On entry, is Attached. */
- EGC_GC;
- libxl__spawn_state *ss = CONTAINER_OF(xswa, *ss, xswait);
- if (rc) {
- if (rc == ERROR_TIMEDOUT)
- LOG(ERROR, "%s: startup timed out", ss->what);
- spawn_fail(egc, ss, rc); /* must be last */
- return;
- }
- LOG(DEBUG, "%s: spawn watch p=%s", ss->what, p);
- ss->confirm_cb(egc, ss, p); /* must be last */
-}
-
-static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
- pid_t pid, int status)
- /* On entry, is Attached or Detaching */
-{
- EGC_GC;
- libxl__spawn_state *ss = CONTAINER_OF(childw, *ss, mid);
-
- if ((ss->rc || ss->detaching) &&
- ((WIFEXITED(status) && WEXITSTATUS(status)==0) ||
- (WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL))) {
- /* as expected */
- const char *what = GCSPRINTF("%s (dying as expected)", ss->what);
- libxl_report_child_exitstatus(CTX, XTL_DEBUG, what, pid, status);
- } else if (!WIFEXITED(status)) {
- int loglevel = ss->detaching ? XTL_WARN : XTL_ERROR;
- const char *what =
- GCSPRINTF("%s intermediate process (startup monitor)", ss->what);
- libxl_report_child_exitstatus(CTX, loglevel, what, pid, status);
- ss->rc = ERROR_FAIL;
- } else {
- if (!status)
- LOG(ERROR, "%s [%ld]: unexpectedly exited with exit status 0,"
- " when we were waiting for it to confirm startup",
- ss->what, (unsigned long)pid);
- else if (status <= 127)
- LOG(ERROR, "%s [%ld]: failed startup with non-zero exit status %d",
- ss->what, (unsigned long)pid, status);
- else if (status < 255) {
- int sig = status - 128;
- const char *str = strsignal(sig);
- if (str)
- LOG(ERROR, "%s [%ld]: died during startup due to fatal"
- " signal %s", ss->what, (unsigned long)pid, str);
- else
- LOG(ERROR, "%s [%ld]: died during startup due to unknown fatal"
- " signal number %d", ss->what, (unsigned long)pid, sig);
- }
- ss->rc = ERROR_FAIL;
- }
-
- spawn_cleanup(gc, ss);
-
- if (ss->rc && !ss->detaching) {
- ss->failure_cb(egc, ss, ss->rc); /* must be last */
- return;
- }
-
- if (ss->rc && ss->detaching)
- LOG(WARN,"%s underlying machinery seemed to fail (%d),"
- " but its function seems to have been successful",
- ss->what, ss->rc);
-
- assert(ss->detaching);
- ss->detached_cb(egc, ss);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Author: Machon Gregory, <mbgrego@tycho.ncsc.mil>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-int libxl_flask_context_to_sid(libxl_ctx *ctx, char *buf, size_t len,
- uint32_t *ssidref)
-{
- int rc;
-
- rc = xc_flask_context_to_sid(ctx->xch, buf, len, ssidref);
-
- return rc;
-}
-
-int libxl_flask_sid_to_context(libxl_ctx *ctx, uint32_t ssidref,
- char **buf, size_t *len)
-{
- int rc;
- char tmp[XC_PAGE_SIZE];
-
- rc = xc_flask_sid_to_context(ctx->xch, ssidref, tmp, sizeof(tmp));
-
- if (!rc) {
- *len = strlen(tmp);
- *buf = strdup(tmp);
- }
-
- return rc;
-}
-
-int libxl_flask_getenforce(libxl_ctx *ctx)
-{
- int rc;
-
- rc = xc_flask_getenforce(ctx->xch);
-
- return rc;
-}
-
-int libxl_flask_setenforce(libxl_ctx *ctx, int mode)
-{
- int rc;
-
- rc = xc_flask_setenforce(ctx->xch, mode);
-
- return rc;
-}
-
-int libxl_flask_loadpolicy(libxl_ctx *ctx, void *policy, uint32_t size)
-{
-
- int rc;
-
- rc = xc_flask_load(ctx->xch, policy, size);
-
- return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2012 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-/*
- * Internal child process machinery for use by other parts of libxl
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*
- * carefd arrangements
- *
- * carefd_begin and _unlock take out the no_forking lock, which we
- * also take and release in our pthread_atfork handlers. So when this
- * lock is held the whole process cannot fork. We therefore protect
- * our fds from leaking into children made by other threads.
- *
- * We maintain a list of all the carefds, so that if the application
- * wants to fork a long-running but non-execing child, we can close
- * them all.
- *
- * So the record function sets CLOEXEC for the benefit of execing
- * children, and makes a note of the fd for the benefit of non-execing
- * ones.
- */
-
-struct libxl__carefd {
- LIBXL_LIST_ENTRY(libxl__carefd) entry;
- int fd;
-};
-
-static pthread_mutex_t no_forking = PTHREAD_MUTEX_INITIALIZER;
-static int atfork_registered;
-static LIBXL_LIST_HEAD(, libxl__carefd) carefds =
- LIBXL_LIST_HEAD_INITIALIZER(carefds);
-
-/* Protected against concurrency by no_forking. sigchld_users is
- * protected against being interrupted by SIGCHLD (and thus read
- * asynchronously by the signal handler) by sigchld_defer (see
- * below). */
-static bool sigchld_installed; /* 0 means not */
-static pthread_mutex_t sigchld_defer_mutex = PTHREAD_MUTEX_INITIALIZER;
-static LIBXL_LIST_HEAD(, libxl_ctx) sigchld_users =
- LIBXL_LIST_HEAD_INITIALIZER(sigchld_users);
-static struct sigaction sigchld_saved_action;
-
-static void sigchld_removehandler_core(void); /* idempotent */
-static void sigchld_user_remove(libxl_ctx *ctx); /* idempotent */
-static void sigchld_sethandler_raw(void (*handler)(int), struct sigaction *old);
-
-static void defer_sigchld(void);
-static void release_sigchld(void);
-
-static void atfork_lock(void)
-{
- int r = pthread_mutex_lock(&no_forking);
- assert(!r);
-}
-
-static void atfork_unlock(void)
-{
- int r = pthread_mutex_unlock(&no_forking);
- assert(!r);
-}
-
-int libxl__atfork_init(libxl_ctx *ctx)
-{
- int r, rc;
-
- atfork_lock();
- if (atfork_registered) { rc = 0; goto out; }
-
- r = pthread_atfork(atfork_lock, atfork_unlock, atfork_unlock);
- if (r) {
- assert(r == ENOMEM);
- libxl__alloc_failed(ctx, __func__, 0,0);
- }
-
- atfork_registered = 1;
- rc = 0;
- out:
- atfork_unlock();
- return rc;
-}
-
-void libxl__carefd_begin(void) { atfork_lock(); }
-void libxl__carefd_unlock(void) { atfork_unlock(); }
-
-libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd)
-{
- libxl__carefd *cf = 0;
-
- libxl_fd_set_cloexec(ctx, fd, 1);
- cf = libxl__zalloc(&ctx->nogc_gc, sizeof(*cf));
- cf->fd = fd;
- LIBXL_LIST_INSERT_HEAD(&carefds, cf, entry);
- return cf;
-}
-
-libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd)
-{
- libxl__carefd *cf = 0;
- int saved_errno = errno;
-
- if (fd >= 0)
- cf = libxl__carefd_record(ctx, fd);
- libxl__carefd_unlock();
- errno = saved_errno;
- return cf;
-}
-
-void libxl_postfork_child_noexec(libxl_ctx *ctx)
-{
- /*
- * Anything running without the no_forking lock (atfork_lock)
- * might be interrupted by fork. But libxl functions other than
- * this one are then forbidden to the child.
- *
- * Conversely, this function might interrupt any other libxl
- * operation (even though that other operation has the libxl ctx
- * lock). We don't take the lock ourselves, since we are running
- * in the child and if the lock is held the thread that took it no
- * longer exists. To prevent us being interrupted by another call
- * to ourselves (whether in another thread or by virtue of another
- * fork) we take the atfork lock ourselves.
- */
- libxl__carefd *cf, *cf_tmp;
- int r;
-
- atfork_lock();
-
- LIBXL_LIST_FOREACH_SAFE(cf, &carefds, entry, cf_tmp) {
- if (cf->fd >= 0) {
- r = close(cf->fd);
- if (r)
- LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_WARNING,
- "failed to close fd=%d"
- " (perhaps of another libxl ctx)", cf->fd);
- }
- free(cf);
- }
- LIBXL_LIST_INIT(&carefds);
-
- if (sigchld_installed) {
- /* We are in theory not at risk of concurrent execution of the
- * SIGCHLD handler, because the application should call
- * libxl_postfork_child_noexec before the child forks again.
- * (If the SIGCHLD was in flight in the parent at the time of
- * the fork, the thread it was delivered on exists only in the
- * parent so is not our concern.)
- *
- * But in case the application violated this rule (and did so
- * while multithreaded in the child), we use our deferral
- * machinery. The result is that the SIGCHLD may then be lost
- * (i.e. signaled to the now-defunct libxl ctx(s)). But at
- * least we won't execute undefined behaviour (by examining
- * the list in the signal handler concurrently with clearing
- * it here), and since we won't actually reap the new children
- * things will in fact go OK if the application doesn't try to
- * use SIGCHLD, but instead just waits for the child(ren). */
- defer_sigchld();
-
- LIBXL_LIST_INIT(&sigchld_users);
- /* After this the ->sigchld_user_registered entries in the
- * now-obsolete contexts may be lies. But that's OK because
- * no-one will look at them. */
-
- release_sigchld();
- sigchld_removehandler_core();
- }
-
- atfork_unlock();
-}
-
-int libxl__carefd_close(libxl__carefd *cf)
-{
- if (!cf) return 0;
- atfork_lock();
- int r = cf->fd < 0 ? 0 : close(cf->fd);
- int esave = errno;
- LIBXL_LIST_REMOVE(cf, entry);
- atfork_unlock();
- free(cf);
- errno = esave;
- return r;
-}
-
-int libxl__carefd_fd(const libxl__carefd *cf)
-{
- if (!cf) return -1;
- return cf->fd;
-}
-
-/*
- * Low-level functions for child process handling, including
- * the main SIGCHLD handler.
- */
-
-/* Like waitpid(,,WNOHANG) but handles all errors except ECHILD. */
-static pid_t checked_waitpid(libxl__egc *egc, pid_t want, int *status)
-{
- EGC_GC;
- for (;;) {
- pid_t got = waitpid(want, status, WNOHANG);
- if (got != -1)
- return got;
- if (errno == ECHILD)
- return got;
- if (errno == EINTR)
- continue;
- LIBXL__EVENT_DISASTER(gc, "waitpid() failed", errno, 0);
- return 0;
- }
-}
-
-static void sigchld_selfpipe_handler(libxl__egc *egc, libxl__ev_fd *ev,
- int fd, short events, short revents);
-
-static void sigchld_handler(int signo)
-{
- /* This function has to be reentrant! Luckily it is. */
-
- libxl_ctx *notify;
- int esave = errno;
-
- int r = pthread_mutex_lock(&sigchld_defer_mutex);
- assert(!r);
-
- LIBXL_LIST_FOREACH(notify, &sigchld_users, sigchld_users_entry) {
- int e = libxl__self_pipe_wakeup(notify->sigchld_selfpipe[1]);
- if (e) abort(); /* errors are probably EBADF, very bad */
- }
-
- r = pthread_mutex_unlock(&sigchld_defer_mutex);
- assert(!r);
-
- errno = esave;
-}
-
-static void sigchld_sethandler_raw(void (*handler)(int), struct sigaction *old)
-{
- struct sigaction ours;
- int r;
-
- memset(&ours,0,sizeof(ours));
- ours.sa_handler = handler;
- sigemptyset(&ours.sa_mask);
- ours.sa_flags = SA_NOCLDSTOP | SA_RESTART;
- r = sigaction(SIGCHLD, &ours, old);
- assert(!r);
-}
-
-/*
- * SIGCHLD deferral
- *
- * sigchld_defer and sigchld_release are a bit like using sigprocmask
- * to block the signal only they work for the whole process. Sadly
- * this has to be done by setting a special handler that records the
- * "pendingness" of the signal here in the program. How tedious.
- *
- * A property of this approach is that the signal handler itself
- * must be reentrant (see the comment in release_sigchld).
- *
- * Callers have the atfork_lock so there is no risk of concurrency
- * within these functions, aside from the risk of being interrupted by
- * the signal. We use sigchld_defer_mutex to guard against the
- * possibility of the real signal handler being still running on
- * another thread.
- */
-
-static volatile sig_atomic_t sigchld_occurred_while_deferred;
-
-static void sigchld_handler_when_deferred(int signo)
-{
- sigchld_occurred_while_deferred = 1;
-}
-
-static void defer_sigchld(void)
-{
- assert(sigchld_installed);
-
- sigchld_sethandler_raw(sigchld_handler_when_deferred, 0);
-
- /* Now _this thread_ cannot any longer be interrupted by the
- * signal, so we can take the mutex without risk of deadlock. If
- * another thread is in the signal handler, either it or we will
- * block and wait for the other. */
-
- int r = pthread_mutex_lock(&sigchld_defer_mutex);
- assert(!r);
-}
-
-static void release_sigchld(void)
-{
- assert(sigchld_installed);
-
- int r = pthread_mutex_unlock(&sigchld_defer_mutex);
- assert(!r);
-
- sigchld_sethandler_raw(sigchld_handler, 0);
- if (sigchld_occurred_while_deferred) {
- sigchld_occurred_while_deferred = 0;
- /* We might get another SIGCHLD here, in which case
- * sigchld_handler will be interrupted and re-entered.
- * This is OK. */
- sigchld_handler(SIGCHLD);
- }
-}
-
-/*
- * Meat of the child process handling.
- */
-
-static void sigchld_removehandler_core(void) /* idempotent */
-{
- struct sigaction was;
- int r;
-
- if (!sigchld_installed)
- return;
-
- r = sigaction(SIGCHLD, &sigchld_saved_action, &was);
- assert(!r);
- assert(!(was.sa_flags & SA_SIGINFO));
- assert(was.sa_handler == sigchld_handler);
-
- sigchld_installed = 0;
-}
-
-static void sigchld_installhandler_core(void) /* idempotent */
-{
- if (sigchld_installed)
- return;
-
- sigchld_installed = 1;
-
- sigchld_sethandler_raw(sigchld_handler, &sigchld_saved_action);
-
- assert(((void)"application must negotiate with libxl about SIGCHLD",
- !(sigchld_saved_action.sa_flags & SA_SIGINFO) &&
- (sigchld_saved_action.sa_handler == SIG_DFL ||
- sigchld_saved_action.sa_handler == SIG_IGN)));
-}
-
-static void sigchld_user_remove(libxl_ctx *ctx) /* idempotent */
-{
- if (!ctx->sigchld_user_registered)
- return;
-
- atfork_lock();
- defer_sigchld();
-
- LIBXL_LIST_REMOVE(ctx, sigchld_users_entry);
-
- release_sigchld();
-
- if (LIBXL_LIST_EMPTY(&sigchld_users))
- sigchld_removehandler_core();
-
- atfork_unlock();
-
- ctx->sigchld_user_registered = 0;
-}
-
-void libxl__sigchld_notneeded(libxl__gc *gc) /* non-reentrant, idempotent */
-{
- sigchld_user_remove(CTX);
- libxl__ev_fd_deregister(gc, &CTX->sigchld_selfpipe_efd);
-}
-
-int libxl__sigchld_needed(libxl__gc *gc) /* non-reentrant, idempotent */
-{
- int rc;
-
- if (CTX->sigchld_selfpipe[0] < 0) {
- rc = libxl__pipe_nonblock(CTX, CTX->sigchld_selfpipe);
- if (rc) goto out;
- }
- if (!libxl__ev_fd_isregistered(&CTX->sigchld_selfpipe_efd)) {
- rc = libxl__ev_fd_register(gc, &CTX->sigchld_selfpipe_efd,
- sigchld_selfpipe_handler,
- CTX->sigchld_selfpipe[0], POLLIN);
- if (rc) goto out;
- } else {
- rc = libxl__ev_fd_modify(gc, &CTX->sigchld_selfpipe_efd, POLLIN);
- if (rc) goto out;
- }
- if (!CTX->sigchld_user_registered) {
- atfork_lock();
-
- sigchld_installhandler_core();
-
- defer_sigchld();
-
- LIBXL_LIST_INSERT_HEAD(&sigchld_users, CTX, sigchld_users_entry);
-
- release_sigchld();
- atfork_unlock();
-
- CTX->sigchld_user_registered = 1;
- }
-
- rc = 0;
- out:
- return rc;
-}
-
-static bool chldmode_ours(libxl_ctx *ctx, bool creating)
-{
- switch (ctx->childproc_hooks->chldowner) {
- case libxl_sigchld_owner_libxl:
- return creating || !LIBXL_LIST_EMPTY(&ctx->children);
- case libxl_sigchld_owner_mainloop:
- return 0;
- case libxl_sigchld_owner_libxl_always:
- case libxl_sigchld_owner_libxl_always_selective_reap:
- return 1;
- }
- abort();
-}
-
-static void perhaps_sigchld_notneeded(libxl__gc *gc)
-{
- if (!chldmode_ours(CTX, 0))
- libxl__sigchld_notneeded(gc);
-}
-
-static int perhaps_sigchld_needed(libxl__gc *gc, bool creating)
-{
- int rc;
-
- if (chldmode_ours(CTX, creating)) {
- rc = libxl__sigchld_needed(gc);
- if (rc) return rc;
- }
- return 0;
-}
-
-static void childproc_reaped_ours(libxl__egc *egc, libxl__ev_child *ch,
- int status)
-{
- pid_t pid = ch->pid;
- LIBXL_LIST_REMOVE(ch, entry);
- ch->pid = -1;
- ch->callback(egc, ch, pid, status);
-}
-
-static int childproc_reaped(libxl__egc *egc, pid_t pid, int status)
-{
- EGC_GC;
- libxl__ev_child *ch;
-
- LIBXL_LIST_FOREACH(ch, &CTX->children, entry)
- if (ch->pid == pid)
- goto found;
-
- /* not found */
- return ERROR_UNKNOWN_CHILD;
-
- found:
- childproc_reaped_ours(egc, ch, status);
-
- perhaps_sigchld_notneeded(gc);
-
- return 0;
-}
-
-int libxl_childproc_reaped(libxl_ctx *ctx, pid_t pid, int status)
-{
- EGC_INIT(ctx);
- CTX_LOCK;
- assert(CTX->childproc_hooks->chldowner
- == libxl_sigchld_owner_mainloop);
- int rc = childproc_reaped(egc, pid, status);
- CTX_UNLOCK_EGC_FREE;
- return rc;
-}
-
-static void childproc_checkall(libxl__egc *egc)
-{
- EGC_GC;
- libxl__ev_child *ch;
-
- for (;;) {
- int status;
- pid_t got;
-
- LIBXL_LIST_FOREACH(ch, &CTX->children, entry) {
- got = checked_waitpid(egc, ch->pid, &status);
- if (got)
- goto found;
- }
- /* not found */
- return;
-
- found:
- if (got == -1) {
- LIBXL__EVENT_DISASTER
- (gc, "waitpid() gave ECHILD but we have a child",
- ECHILD, 0);
- /* it must have finished but we don't know its status */
- status = 255<<8; /* no wait.h macro for this! */
- assert(WIFEXITED(status));
- assert(WEXITSTATUS(status)==255);
- assert(!WIFSIGNALED(status));
- assert(!WIFSTOPPED(status));
- }
- childproc_reaped_ours(egc, ch, status);
- /* we need to restart the loop, as children may have been edited */
- }
-}
-
-void libxl_childproc_sigchld_occurred(libxl_ctx *ctx)
-{
- EGC_INIT(ctx);
- CTX_LOCK;
- assert(CTX->childproc_hooks->chldowner
- == libxl_sigchld_owner_mainloop);
- childproc_checkall(egc);
- CTX_UNLOCK_EGC_FREE;
-}
-
-static void sigchld_selfpipe_handler(libxl__egc *egc, libxl__ev_fd *ev,
- int fd, short events, short revents)
-{
- /* May make callbacks into the application for child processes.
- * So, this function may unlock and relock the CTX. This is OK
- * because event callback functions are always called with the CTX
- * locked exactly once, and from code which copes with reentrancy.
- * (See also the comment in afterpoll_internal.) */
- EGC_GC;
-
- int selfpipe = CTX->sigchld_selfpipe[0];
-
- if (revents & ~POLLIN) {
- LOG(ERROR, "unexpected poll event 0x%x on SIGCHLD self pipe", revents);
- LIBXL__EVENT_DISASTER(gc,
- "unexpected poll event on SIGCHLD self pipe",
- 0, 0);
- }
- assert(revents & POLLIN);
-
- int e = libxl__self_pipe_eatall(selfpipe);
- if (e) LIBXL__EVENT_DISASTER(gc, "read sigchld pipe", e, 0);
-
- if (CTX->childproc_hooks->chldowner
- == libxl_sigchld_owner_libxl_always_selective_reap) {
- childproc_checkall(egc);
- return;
- }
-
- while (chldmode_ours(CTX, 0) /* in case the app changes the mode */) {
- int status;
- pid_t pid = checked_waitpid(egc, -1, &status);
-
- if (pid == 0 || pid == -1 /* ECHILD */)
- return;
-
- int rc = childproc_reaped(egc, pid, status);
-
- if (rc) {
- if (CTX->childproc_hooks->reaped_callback) {
- CTX_UNLOCK;
- rc = CTX->childproc_hooks->reaped_callback
- (pid, status, CTX->childproc_user);
- CTX_LOCK;
- if (rc != 0 && rc != ERROR_UNKNOWN_CHILD) {
- char disasterbuf[200];
- snprintf(disasterbuf, sizeof(disasterbuf), " reported by"
- " libxl_childproc_hooks->reaped_callback"
- " (for pid=%lu, status=%d; error code %d)",
- (unsigned long)pid, status, rc);
- LIBXL__EVENT_DISASTER(gc, disasterbuf, 0, 0);
- return;
- }
- } else {
- rc = ERROR_UNKNOWN_CHILD;
- }
- if (rc)
- libxl_report_child_exitstatus(CTX, XTL_WARN,
- "unknown child", (long)pid, status);
- }
- }
-}
-
-pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *ch,
- libxl__ev_child_callback *death)
-{
- CTX_LOCK;
- int rc;
-
- perhaps_sigchld_needed(gc, 1);
-
- pid_t pid =
- CTX->childproc_hooks->fork_replacement
- ? CTX->childproc_hooks->fork_replacement(CTX->childproc_user)
- : fork();
- if (pid == -1) {
- LOGE(ERROR, "fork failed");
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (!pid) {
- /* woohoo! */
- if (CTX->xsh) {
- xs_daemon_destroy_postfork(CTX->xsh);
- CTX->xsh = NULL; /* turns mistakes into crashes */
- }
- /* Yes, CTX is left locked in the child. */
- return 0;
- }
-
- ch->pid = pid;
- ch->callback = death;
- LIBXL_LIST_INSERT_HEAD(&CTX->children, ch, entry);
- rc = pid;
-
- out:
- perhaps_sigchld_notneeded(gc);
- CTX_UNLOCK;
- return rc;
-}
-
-void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks *hooks,
- void *user)
-{
- GC_INIT(ctx);
- CTX_LOCK;
-
- assert(LIBXL_LIST_EMPTY(&CTX->children));
-
- if (!hooks)
- hooks = &libxl__childproc_default_hooks;
-
- ctx->childproc_hooks = hooks;
- ctx->childproc_user = user;
-
- perhaps_sigchld_notneeded(gc);
- perhaps_sigchld_needed(gc, 0); /* idempotent, ok to ignore errors for now */
-
- CTX_UNLOCK;
- GC_FREE;
-}
-
-const libxl_childproc_hooks libxl__childproc_default_hooks = {
- libxl_sigchld_owner_libxl, 0, 0
-};
-
-int libxl__ev_child_xenstore_reopen(libxl__gc *gc, const char *what) {
- int rc;
-
- assert(!CTX->xsh);
- CTX->xsh = xs_daemon_open();
- if (!CTX->xsh) {
- LOGE(ERROR, "%s: xenstore reopen failed", what);
- rc = ERROR_FAIL; goto out;
- }
-
- libxl_fd_set_cloexec(CTX, xs_fileno(CTX->xsh), 1);
-
- return 0;
-
- out:
- return rc;
-}
-
-typedef struct ev_child_killed {
- libxl__ao *ao;
- libxl__ev_child ch;
-} ev_child_killed;
-static void deregistered_child_callback(libxl__egc *, libxl__ev_child *,
- pid_t, int status);
-
-void libxl__ev_child_kill_deregister(libxl__ao *ao, libxl__ev_child *ch,
- int sig)
-{
- AO_GC;
-
- if (!libxl__ev_child_inuse(ch))
- return;
-
- pid_t pid = ch->pid;
-
- ev_child_killed *new_ch = GCNEW(new_ch);
- new_ch->ao = ao;
- new_ch->ch.pid = pid;
- new_ch->ch.callback = deregistered_child_callback;
- LIBXL_LIST_INSERT_HEAD(&CTX->children, &new_ch->ch, entry);
- ao->outstanding_killed_child++;
-
- LIBXL_LIST_REMOVE(ch, entry);
- ch->pid = -1;
- int r = kill(pid, sig);
- if (r)
- LOGED(ERROR, ao->domid,
- "failed to kill child [%ld] with signal %d",
- (unsigned long)pid, sig);
-}
-
-static void deregistered_child_callback(libxl__egc *egc,
- libxl__ev_child *ch,
- pid_t pid,
- int status)
-{
- ev_child_killed *ck = CONTAINER_OF(ch, *ck, ch);
- EGC_GC;
-
- libxl_report_child_exitstatus(CTX, XTL_ERROR,
- "killed fork (dying as expected)",
- pid, status);
- ck->ao->outstanding_killed_child--;
- libxl__ao_complete_check_progress_reports(egc, ck->ao);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2014
- * Author Roger Pau Monne <roger.pau@entel.upc.edu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-int libxl__try_phy_backend(mode_t st_mode)
-{
- if (S_ISREG(st_mode) || S_ISBLK(st_mode) || S_ISCHR(st_mode))
- return 1;
-
- return 0;
-}
-
-char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
-{
- /* This translation table has been copied from the FreeBSD blkfront code. */
- const static struct vdev_info {
- int major;
- int shift;
- int base;
- const char *name;
- } info[] = {
- {3, 6, 0, "ada"}, /* ide0 */
- {22, 6, 2, "ada"}, /* ide1 */
- {33, 6, 4, "ada"}, /* ide2 */
- {34, 6, 6, "ada"}, /* ide3 */
- {56, 6, 8, "ada"}, /* ide4 */
- {57, 6, 10, "ada"}, /* ide5 */
- {88, 6, 12, "ada"}, /* ide6 */
- {89, 6, 14, "ada"}, /* ide7 */
- {90, 6, 16, "ada"}, /* ide8 */
- {91, 6, 18, "ada"}, /* ide9 */
-
- {8, 4, 0, "da"}, /* scsi disk0 */
- {65, 4, 16, "da"}, /* scsi disk1 */
- {66, 4, 32, "da"}, /* scsi disk2 */
- {67, 4, 48, "da"}, /* scsi disk3 */
- {68, 4, 64, "da"}, /* scsi disk4 */
- {69, 4, 80, "da"}, /* scsi disk5 */
- {70, 4, 96, "da"}, /* scsi disk6 */
- {71, 4, 112, "da"}, /* scsi disk7 */
- {128, 4, 128, "da"}, /* scsi disk8 */
- {129, 4, 144, "da"}, /* scsi disk9 */
- {130, 4, 160, "da"}, /* scsi disk10 */
- {131, 4, 176, "da"}, /* scsi disk11 */
- {132, 4, 192, "da"}, /* scsi disk12 */
- {133, 4, 208, "da"}, /* scsi disk13 */
- {134, 4, 224, "da"}, /* scsi disk14 */
- {135, 4, 240, "da"}, /* scsi disk15 */
-
- {202, 4, 0, "xbd"}, /* xbd */
-
- {0, 0, 0, NULL},
- };
- int major = devid >> 8;
- int minor = devid & 0xff;
- int i;
-
- if (devid & (1 << 28))
- return GCSPRINTF("%s%d", "xbd", (devid & ((1 << 28) - 1)) >> 8);
-
- for (i = 0; info[i].major; i++)
- if (info[i].major == major)
- return GCSPRINTF("%s%d", info[i].name,
- info[i].base + (minor >> info[i].shift));
-
- return GCSPRINTF("%s%d", "xbd", minor >> 4);
-}
-
-/* Hotplug scripts caller functions */
-static int libxl__hotplug_env_nic(libxl__gc *gc, libxl__device *dev, char ***env,
- int num_exec)
-{
- int nr = 0;
- const int arraysize = 5;
- libxl_nic_type type;
-
- assert(dev->backend_kind == LIBXL__DEVICE_KIND_VIF);
-
- /*
- * On the first pass the PV interface is added to the bridge,
- * on the second pass the tap interface will also be added.
- */
- type = num_exec == 0 ? LIBXL_NIC_TYPE_VIF : LIBXL_NIC_TYPE_VIF_IOEMU;
-
- GCNEW_ARRAY(*env, arraysize);
- (*env)[nr++] = "iface_dev";
- (*env)[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
- dev->devid, type);
- (*env)[nr++] = "emulated";
- (*env)[nr++] = type == LIBXL_NIC_TYPE_VIF_IOEMU ? "1" : "0";
- (*env)[nr++] = NULL;
- assert(nr == arraysize);
-
- return 0;
-}
-
-static int libxl__hotplug_nic(libxl__gc *gc, libxl__device *dev,
- char ***args, char ***env,
- libxl__device_action action,
- int num_exec)
-{
- libxl_nic_type nictype;
- char *be_path = libxl__device_backend_path(gc, dev);
- char *script;
- int nr = 0, rc;
-
- rc = libxl__nic_type(gc, dev, &nictype);
- if (rc) {
- LOGD(ERROR, dev->domid, "error when fetching nic type");
- rc = ERROR_FAIL;
- goto out;
- }
-
- /*
- * For PV domains only one pass is needed (because there's no emulated
- * interface). For HVM domains two passes are needed in order to add
- * both the PV and the tap interfaces to the bridge.
- */
- if (nictype == LIBXL_NIC_TYPE_VIF && num_exec != 0) {
- rc = 0;
- goto out;
- }
-
- rc = libxl__hotplug_env_nic(gc, dev, env, num_exec);
- if (rc)
- goto out;
-
- script = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/%s", be_path, "script"));
- if (!script) {
- LOGEVD(ERROR, errno, dev->domid,
- "unable to read script from %s", be_path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- const int arraysize = 4;
- GCNEW_ARRAY(*args, arraysize);
- (*args)[nr++] = script;
- (*args)[nr++] = be_path;
- (*args)[nr++] = (char *) libxl__device_action_to_string(action);
- (*args)[nr++] = NULL;
- assert(nr == arraysize);
- rc = 1;
-
-out:
- return rc;
-}
-
-static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev,
- char ***args, char ***env,
- libxl__device_action action)
-{
- char *be_path = libxl__device_backend_path(gc, dev);
- char *script;
- int nr = 0, rc;
-
- script = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/%s", be_path, "script"));
- if (!script) {
- LOGEVD(ERROR, errno, dev->domid,
- "unable to read script from %s", be_path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- const int arraysize = 4;
- GCNEW_ARRAY(*args, arraysize);
- (*args)[nr++] = script;
- (*args)[nr++] = be_path;
- (*args)[nr++] = (char *) libxl__device_action_to_string(action);
- (*args)[nr++] = NULL;
- assert(nr == arraysize);
- rc = 1;
-
-out:
- return rc;
-}
-
-int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
- char ***args, char ***env,
- libxl__device_action action,
- int num_exec)
-{
- int rc;
-
- switch (dev->backend_kind) {
- case LIBXL__DEVICE_KIND_VIF:
- /*
- * If domain has a stubdom we don't have to execute hotplug scripts
- * for emulated interfaces
- */
- if ((num_exec > 1) ||
- (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
- rc = 0;
- goto out;
- }
- rc = libxl__hotplug_nic(gc, dev, args, env, action, num_exec);
- break;
- case LIBXL__DEVICE_KIND_VBD:
- if (num_exec != 0) {
- rc = 0;
- goto out;
- }
- rc = libxl__hotplug_disk(gc, dev, args, env, action);
- break;
- default:
- /* No need to execute any hotplug scripts */
- rc = 0;
- break;
- }
-
-out:
- return rc;
-}
-
-libxl_device_model_version libxl__default_device_model(libxl__gc *gc)
-{
- return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
-}
-
-int libxl__pci_numdevs(libxl__gc *gc)
-{
- return ERROR_NI;
-}
-
-int libxl__pci_topology_init(libxl__gc *gc,
- physdev_pci_device_t *devs,
- int num_devs)
-{
- return ERROR_NI;
-}
-
-int libxl__local_dm_preexec_restrict(libxl__gc *gc)
-{
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) 2014 Citrix Systems R&D Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#include <xenctrl.h>
-#include <xen/hvm/params.h>
-
-/*
- * Generate a random VM generation ID.
- *
- * Returns ERROR_FAIL if a suitable source of random numbers is not
- * available.
- *
- * See Microsoft's "Virtual Machine Generation ID" specification for
- * further details, including when a new generation ID is required.
- *
- * http://www.microsoft.com/en-us/download/details.aspx?id=30707
- */
-int libxl_ms_vm_genid_generate(libxl_ctx *ctx, libxl_ms_vm_genid *id)
-{
- GC_INIT(ctx);
- int ret;
-
- ret = libxl__random_bytes(gc, id->bytes, LIBXL_MS_VM_GENID_LEN);
-
- GC_FREE;
- return ret;
-}
-
-/*
- * Is this VM generation ID all zeros?
- */
-bool libxl_ms_vm_genid_is_zero(const libxl_ms_vm_genid *id)
-{
- static const libxl_ms_vm_genid zero;
-
- return memcmp(id->bytes, zero.bytes, LIBXL_MS_VM_GENID_LEN) == 0;
-}
-
-void libxl_ms_vm_genid_copy(libxl_ctx *ctx, libxl_ms_vm_genid *dst,
- const libxl_ms_vm_genid *src)
-{
- memcpy(dst, src, LIBXL_MS_VM_GENID_LEN);
-}
-
-int libxl__ms_vm_genid_set(libxl__gc *gc, uint32_t domid,
- const libxl_ms_vm_genid *id)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- const char *dom_path;
- uint64_t genid[2];
- uint64_t paddr = 0;
- int rc;
-
- memcpy(genid, id->bytes, LIBXL_MS_VM_GENID_LEN);
-
- /*
- * Set the "platform/generation-id" XenStore key to pass the ID to
- * hvmloader.
- */
- dom_path = libxl__xs_get_dompath(gc, domid);
- if (!dom_path) {
- rc = ERROR_FAIL;
- goto out;
- }
- rc = libxl__xs_printf(gc, XBT_NULL,
- GCSPRINTF("%s/platform/generation-id", dom_path),
- "%"PRIu64 ":%" PRIu64, genid[0], genid[1]);
- if (rc < 0)
- goto out;
-
- /*
- * Update the ID in guest memory (if available).
- */
- xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_VM_GENERATION_ID_ADDR, &paddr);
- if (paddr) {
- void *vaddr;
-
- vaddr = xc_map_foreign_range(ctx->xch, domid, XC_PAGE_SIZE,
- PROT_READ | PROT_WRITE,
- paddr >> XC_PAGE_SHIFT);
- if (vaddr == NULL) {
- rc = ERROR_FAIL;
- goto out;
- }
- memcpy(vaddr + (paddr & ~XC_PAGE_MASK), genid, 2 * sizeof(*genid));
- munmap(vaddr, XC_PAGE_SIZE);
-
- /*
- * The spec requires an ACPI Notify event is injected into the
- * guest when the generation ID is changed.
- *
- * This is only called for domains that are suspended or newly
- * created and they won't be in a state to receive such an
- * event.
- */
- }
-
- rc = 0;
-
- out:
- return rc;
-}
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-void libxl__alloc_failed(libxl_ctx *ctx, const char *func,
- size_t nmemb, size_t size) {
-#define M "libxl: FATAL ERROR: memory allocation failure"
-#define M_SIZE M " (%s, %lu x %lu)\n"
-#define M_NSIZE M " (%s)\n"
- if (size) {
- libxl__log(ctx, XTL_CRITICAL, ENOMEM, 0, 0, func, INVALID_DOMID,
- M_SIZE, func, (unsigned long)nmemb, (unsigned long)size);
- fprintf(stderr, M_SIZE, func, (unsigned long)nmemb,
- (unsigned long)size);
- } else {
- libxl__log(ctx, XTL_CRITICAL, ENOMEM, 0, 0, func, INVALID_DOMID,
- M_NSIZE, func);
- fprintf(stderr, M_NSIZE, func);
-
- }
-
- fflush(stderr);
- _exit(-1);
-#undef M_NSIZE
-#undef M_SIZE
-#undef M
-}
-
-void libxl__ptr_add(libxl__gc *gc, void *ptr)
-{
- int i;
-
- if (!libxl__gc_is_real(gc))
- return;
-
- if (!ptr)
- return;
-
- /* fast case: we have space in the array for storing the pointer */
- for (i = 0; i < gc->alloc_maxsize; i++) {
- if (!gc->alloc_ptrs[i]) {
- gc->alloc_ptrs[i] = ptr;
- return;
- }
- }
- int new_maxsize = gc->alloc_maxsize * 2 + 25;
- assert(new_maxsize < INT_MAX / sizeof(void*) / 2);
- gc->alloc_ptrs = realloc(gc->alloc_ptrs, new_maxsize * sizeof(void *));
- if (!gc->alloc_ptrs)
- libxl__alloc_failed(CTX, __func__, new_maxsize, sizeof(void*));
-
- gc->alloc_ptrs[gc->alloc_maxsize++] = ptr;
-
- while (gc->alloc_maxsize < new_maxsize)
- gc->alloc_ptrs[gc->alloc_maxsize++] = 0;
-
- return;
-}
-
-void libxl__free_all(libxl__gc *gc)
-{
- void *ptr;
- int i;
-
- assert(libxl__gc_is_real(gc));
-
- for (i = 0; i < gc->alloc_maxsize; i++) {
- ptr = gc->alloc_ptrs[i];
- gc->alloc_ptrs[i] = NULL;
- free(ptr);
- }
- free(gc->alloc_ptrs);
- gc->alloc_ptrs = 0;
- gc->alloc_maxsize = 0;
-}
-
-void *libxl__malloc(libxl__gc *gc, size_t size)
-{
- void *ptr = malloc(size);
- if (!ptr) libxl__alloc_failed(CTX, __func__, size, 1);
-
- libxl__ptr_add(gc, ptr);
- return ptr;
-}
-
-void *libxl__zalloc(libxl__gc *gc, size_t size)
-{
- void *ptr = calloc(size, 1);
- if (!ptr) libxl__alloc_failed(CTX, __func__, size, 1);
-
- libxl__ptr_add(gc, ptr);
- return ptr;
-}
-
-void *libxl__calloc(libxl__gc *gc, size_t nmemb, size_t size)
-{
- void *ptr = calloc(nmemb, size);
- if (!ptr) libxl__alloc_failed(CTX, __func__, nmemb, size);
-
- libxl__ptr_add(gc, ptr);
- return ptr;
-}
-
-void *libxl__realloc(libxl__gc *gc, void *ptr, size_t new_size)
-{
- void *new_ptr = realloc(ptr, new_size);
- int i = 0;
-
- if (new_ptr == NULL && new_size != 0)
- libxl__alloc_failed(CTX, __func__, new_size, 1);
-
- if (ptr == NULL) {
- libxl__ptr_add(gc, new_ptr);
- } else if (new_ptr != ptr && libxl__gc_is_real(gc)) {
- for (i = 0; ; i++) {
- assert(i < gc->alloc_maxsize);
- if (gc->alloc_ptrs[i] == ptr) {
- gc->alloc_ptrs[i] = new_ptr;
- break;
- }
- }
- }
-
- return new_ptr;
-}
-
-char *libxl__vsprintf(libxl__gc *gc, const char *fmt, va_list ap)
-{
- char *s;
- va_list aq;
- int ret;
-
- va_copy(aq, ap);
- ret = vsnprintf(NULL, 0, fmt, aq);
- va_end(aq);
-
- assert(ret >= 0);
-
- s = libxl__zalloc(gc, ret + 1);
- va_copy(aq, ap);
- ret = vsnprintf(s, ret + 1, fmt, aq);
- va_end(aq);
-
- return s;
-}
-
-char *libxl__sprintf(libxl__gc *gc, const char *fmt, ...)
-{
- char *s;
- va_list ap;
-
- va_start(ap, fmt);
- s = libxl__vsprintf(gc, fmt, ap);
- va_end(ap);
-
- return s;
-}
-
-char *libxl__strdup(libxl__gc *gc, const char *c)
-{
- char *s;
-
- if (!c) return NULL;
-
- s = strdup(c);
-
- if (!s) libxl__alloc_failed(CTX, __func__, strlen(c), 1);
-
- libxl__ptr_add(gc, s);
-
- return s;
-}
-
-char *libxl__strndup(libxl__gc *gc, const char *c, size_t n)
-{
- char *s;
-
- if (!c) return NULL;
-
- s = strndup(c, n);
-
- if (!s) libxl__alloc_failed(CTX, __func__, n, 1);
-
- libxl__ptr_add(gc, s);
-
- return s;
-}
-
-char *libxl__dirname(libxl__gc *gc, const char *s)
-{
- char *c = strrchr(s, '/');
-
- if (!c)
- return NULL;
-
- return libxl__strndup(gc, s, c - s);
-}
-
-void libxl__logv(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
- const char *file, int line, const char *func,
- uint32_t domid, const char *fmt, va_list ap)
-{
- /* WARNING this function may not call any libxl-provided
- * memory allocation function, as those may
- * call libxl__alloc_failed which calls libxl__logv. */
- char *enomem = "[out of memory formatting log message]";
- char *base = NULL;
- int rc, esave;
- char fileline[256];
- char domain[256];
-
- esave = errno;
-
- rc = vasprintf(&base, fmt, ap);
- if (rc<0) { base = enomem; goto x; }
-
- fileline[0] = 0;
- if (file) snprintf(fileline, sizeof(fileline), "%s:%d",file,line);
- fileline[sizeof(fileline)-1] = 0;
-
- domain[0] = 0;
- if (libxl_domid_valid_guest(domid))
- snprintf(domain, sizeof(domain), "Domain %"PRIu32":", domid);
- x:
- xtl_log(ctx->lg, msglevel, errnoval, "libxl",
- "%s%s%s%s%s" "%s",
- fileline, func&&file?":":"", func?func:"", func||file?": ":"",
- domain, base);
- if (base != enomem) free(base);
- errno = esave;
-}
-
-void libxl__log(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
- const char *file, int line, const char *func,
- uint32_t domid, const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- libxl__logv(ctx, msglevel, errnoval, file, line, func, domid, fmt, ap);
- va_end(ap);
-}
-
-char *libxl__abs_path(libxl__gc *gc, const char *s, const char *path)
-{
- if (s[0] == '/') return libxl__strdup(gc, s);
- return GCSPRINTF("%s/%s", path, s);
-}
-
-
-int libxl__file_reference_map(libxl__file_reference *f)
-{
- struct stat st_buf;
- int ret, fd;
- void *data;
-
- if (f->mapped)
- return 0;
-
- fd = open(f->path, O_RDONLY);
- if (fd < 0)
- return ERROR_FAIL;
-
- ret = fstat(fd, &st_buf);
- if (ret < 0)
- goto out;
-
- ret = -1;
- data = mmap(NULL, st_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (data == MAP_FAILED)
- goto out;
-
- f->mapped = 1;
- f->data = data;
- f->size = st_buf.st_size;
-
- ret = 0;
-out:
- close(fd);
-
- return ret == 0 ? 0 : ERROR_FAIL;
-}
-
-int libxl__file_reference_unmap(libxl__file_reference *f)
-{
- int ret;
-
- if (!f->mapped)
- return 0;
-
- ret = munmap(f->data, f->size);
- if (ret == 0) {
- f->mapped = 0;
- f->data = NULL;
- f->size = 0;
- return 0;
- }
-
- return ERROR_FAIL;
-}
-
-_hidden int libxl__parse_mac(const char *s, libxl_mac mac)
-{
- const char *tok;
- char *endptr;
- int i;
-
- for (i = 0, tok = s; *tok && (i < 6); ++i, tok = endptr) {
- mac[i] = strtol(tok, &endptr, 16);
- if (endptr != (tok + 2) || (*endptr != '\0' && *endptr != ':') )
- return ERROR_INVAL;
- if (*endptr == ':')
- endptr++;
- }
- if ( i != 6 )
- return ERROR_INVAL;
-
- return 0;
-}
-
-_hidden int libxl__compare_macs(libxl_mac *a, libxl_mac *b)
-{
- int i;
-
- for (i = 0; i<6; i++) {
- if ((*a)[i] != (*b)[i])
- return (*a)[i] - (*b)[i];
- }
-
- return 0;
-}
-
-_hidden int libxl__mac_is_default(libxl_mac *mac)
-{
- return (!(*mac)[0] && !(*mac)[1] && !(*mac)[2] &&
- !(*mac)[3] && !(*mac)[4] && !(*mac)[5]);
-}
-
-_hidden int libxl__init_recursive_mutex(libxl_ctx *ctx, pthread_mutex_t *lock)
-{
- GC_INIT(ctx);
- pthread_mutexattr_t attr;
- int rc = 0;
-
- if (pthread_mutexattr_init(&attr) != 0) {
- LOGE(ERROR, "Failed to init mutex attributes");
- rc = ERROR_FAIL;
- goto out;
- }
- if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
- LOGE(ERROR, "Failed to set mutex attributes");
- rc = ERROR_FAIL;
- goto out;
- }
- if (pthread_mutex_init(lock, &attr) != 0) {
- LOGE(ERROR, "Failed to init mutex");
- rc = ERROR_FAIL;
- goto out;
- }
-out:
- pthread_mutexattr_destroy(&attr);
- GC_FREE;
- return rc;
-}
-
-int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid)
-{
- char *path = NULL;
- char *dm_version = NULL;
- libxl_device_model_version value;
-
- path = libxl__xs_libxl_path(gc, domid);
- path = GCSPRINTF("%s/dm-version", path);
- dm_version = libxl__xs_read(gc, XBT_NULL, path);
- if (!dm_version) {
- return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
- }
-
- if (libxl_device_model_version_from_string(dm_version, &value) < 0) {
- LOGD(ERROR, domid, "fatal: %s contain a wrong value (%s)", path, dm_version);
- return -1;
- }
- return value;
-}
-
-/* Portability note: this lock utilises flock(2) so a proper implementation of
- * flock(2) is required.
- */
-libxl__flock *libxl__lock_file(libxl__gc *gc, const char *lockfile)
-{
- libxl__flock *lock;
- int fd;
- struct stat stab, fstab;
-
- lock = libxl__zalloc(NOGC, sizeof(libxl__flock));
- lock->path = libxl__strdup(NOGC, lockfile);
-
- while (true) {
- libxl__carefd_begin();
- fd = open(lockfile, O_RDWR|O_CREAT, 0666);
- if (fd < 0)
- LOGE(ERROR,
- "cannot open lockfile %s, errno=%d",
- lockfile, errno);
- lock->carefd = libxl__carefd_opened(CTX, fd);
- if (fd < 0) goto out;
-
- /* Lock the file in exclusive mode, wait indefinitely to
- * acquire the lock
- */
- while (flock(fd, LOCK_EX)) {
- switch (errno) {
- case EINTR:
- /* Signal received, retry */
- continue;
- default:
- /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
- LOGE(ERROR,
- "unexpected error while trying to lock %s, fd=%d, errno=%d",
- lockfile, fd, errno);
- goto out;
- }
- }
-
- if (fstat(fd, &fstab)) {
- LOGE(ERROR, "cannot fstat %s, fd=%d, errno=%d",
- lockfile, fd, errno);
- goto out;
- }
- if (stat(lockfile, &stab)) {
- if (errno != ENOENT) {
- LOGE(ERROR, "cannot stat %s, errno=%d", lockfile, errno);
- goto out;
- }
- } else {
- if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino)
- break;
- }
-
- libxl__carefd_close(lock->carefd);
- }
-
- return lock;
-
-out:
- if (lock) libxl__unlock_file(lock);
- return NULL;
-}
-
-void libxl__unlock_file(libxl__flock *lock)
-{
- /* It's important to unlink the file before closing fd to avoid
- * the following race (if close before unlink):
- *
- * P1 LOCK P2 UNLOCK
- * fd1 = open(lockfile)
- * close(fd2)
- * flock(fd1)
- * fstat and stat check success
- * unlink(lockfile)
- * return lock
- *
- * In above case P1 thinks it has got hold of the lock but
- * actually lock is released by P2 (lockfile unlinked).
- */
- if (lock->path) unlink(lock->path);
- if (lock->carefd) libxl__carefd_close(lock->carefd);
- free(lock->path);
- free(lock);
-}
-
-libxl__flock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid)
-{
- const char *lockfile;
- libxl__flock *lock;
-
- lockfile = libxl__userdata_path(gc, domid, "domain-userdata-lock", "l");
- if (!lockfile) return NULL;
-
- lock = libxl__lock_file(gc, lockfile);
-
- /* Check the domain is still there, if not we should release the
- * lock and clean up.
- */
- if (libxl_domain_info(CTX, NULL, domid)) {
- libxl__unlock_file(lock);
- return NULL;
- }
-
- return lock;
-}
-
-libxl__flock *libxl__lock_domid_history(libxl__gc *gc)
-{
- const char *lockfile;
-
- lockfile = libxl__domid_history_path(gc, ".lock");
- if (!lockfile) return NULL;
-
- return libxl__lock_file(gc, lockfile);
-}
-
-int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid,
- libxl_domain_config *d_config)
-{
- uint8_t *data = NULL;
- int rc, len;
-
- rc = libxl__userdata_retrieve(gc, domid, "libxl-json", &data, &len);
- if (rc) {
- LOGEVD(ERROR, rc, domid,
- "failed to retrieve domain configuration");
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (len == 0) {
- /* No logging, not necessary an error from caller's PoV. */
- rc = ERROR_JSON_CONFIG_EMPTY;
- goto out;
- }
- rc = libxl_domain_config_from_json(CTX, d_config, (const char *)data);
-
-out:
- free(data);
- return rc;
-}
-
-int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid,
- libxl_domain_config *d_config)
-{
- char *d_config_json;
- int rc;
-
- d_config_json = libxl_domain_config_to_json(CTX, d_config);
- if (!d_config_json) {
- LOGED(ERROR, domid,
- "failed to convert domain configuration to JSON");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__userdata_store(gc, domid, "libxl-json",
- (const uint8_t *)d_config_json,
- strlen(d_config_json) + 1 /* include '\0' */);
- if (rc) {
- LOGEVD(ERROR, rc, domid, "failed to store domain configuration");
- rc = ERROR_FAIL;
- goto out;
- }
-
-out:
- free(d_config_json);
- return rc;
-}
-
-void libxl__update_domain_configuration(libxl__gc *gc,
- libxl_domain_config *dst,
- const libxl_domain_config *src)
-{
- int i, idx, num;
- const libxl__device_type *dt;
-
- for (idx = 0;; idx++) {
- dt = device_type_tbl[idx];
- if (!dt)
- break;
-
- num = *libxl__device_type_get_num(dt, src);
- if (!dt->update_config || !num)
- continue;
-
- for (i = 0; i < num; i++)
- dt->update_config(gc, libxl__device_type_get_elem(dt, dst, i),
- libxl__device_type_get_elem(dt, src, i));
- }
-
- /* update guest UUID */
- libxl_uuid_copy(CTX, &dst->c_info.uuid, &src->c_info.uuid);
-
- /* video ram */
- dst->b_info.video_memkb = src->b_info.video_memkb;
-}
-
-static void ev_slowlock_init_internal(libxl__ev_slowlock *lock,
- const char *userdata_userid)
-{
- libxl__ev_child_init(&lock->child);
- lock->userdata_userid = userdata_userid;
- lock->path = NULL;
- lock->fd = -1;
- lock->held = false;
-}
-
-void libxl__ev_devlock_init(libxl__ev_slowlock *lock)
-{
- ev_slowlock_init_internal(lock, "libxl-device-changes-lock");
-}
-
-void libxl__ev_qmplock_init(libxl__ev_slowlock *lock)
-{
- ev_slowlock_init_internal(lock, "qmp-socket-lock");
-}
-
-static void ev_lock_prepare_fork(libxl__egc *egc, libxl__ev_slowlock *lock);
-static void ev_lock_child_callback(libxl__egc *egc, libxl__ev_child *child,
- pid_t pid, int status);
-
-void libxl__ev_slowlock_lock(libxl__egc *egc, libxl__ev_slowlock *lock)
-{
- STATE_AO_GC(lock->ao);
- const char *lockfile;
-
- lockfile = libxl__userdata_path(gc, lock->domid,
- lock->userdata_userid, "l");
- if (!lockfile) goto out;
- lock->path = libxl__strdup(NOGC, lockfile);
-
- ev_lock_prepare_fork(egc, lock);
- return;
-out:
- lock->callback(egc, lock, ERROR_LOCK_FAIL);
-}
-
-static void ev_lock_prepare_fork(libxl__egc *egc, libxl__ev_slowlock *lock)
-{
- STATE_AO_GC(lock->ao);
- pid_t pid;
- int fd;
-
- /* Convenience aliases */
- libxl_domid domid = lock->domid;
- const char *lockfile = lock->path;
-
- lock->fd = open(lockfile, O_RDWR|O_CREAT, 0666);
- if (lock->fd < 0) {
- LOGED(ERROR, domid, "cannot open lockfile %s", lockfile);
- goto out;
- }
- fd = lock->fd;
-
- /* Enable this optimisation only in releases, so the fork code is
- * exercised while libxl is built with debug=y. */
-#ifndef CONFIG_DEBUG
- /*
- * We try to grab the lock before forking as it is likely to be free.
- * Even though we are supposed to CTX_UNLOCK before attempting to grab
- * the ev_lock, it is fine to do a non-blocking request now with the
- * CTX_LOCK held as if that fails we'll try again in a fork (CTX_UNLOCK
- * will be called in libxl), that will avoid deadlocks.
- */
- int r = flock(fd, LOCK_EX | LOCK_NB);
- if (!r) {
- libxl_fd_set_cloexec(CTX, fd, 1);
- /* We held a lock, no need to fork but we need to check it. */
- ev_lock_child_callback(egc, &lock->child, 0, 0);
- return;
- }
-#endif
-
- pid = libxl__ev_child_fork(gc, &lock->child, ev_lock_child_callback);
- if (pid < 0)
- goto out;
- if (!pid) {
- /* child */
- int exit_val = 0;
-
- /* Lock the file in exclusive mode, wait indefinitely to
- * acquire the lock */
- while (flock(fd, LOCK_EX)) {
- switch (errno) {
- case EINTR:
- /* Signal received, retry */
- continue;
- default:
- /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
- LOGED(ERROR, domid,
- "unexpected error while trying to lock %s, fd=%d",
- lockfile, fd);
- exit_val = 1;
- break;
- }
- }
- _exit(exit_val);
- }
-
- /* Now that the child has the fd, set cloexec in the parent to prevent
- * more leakage than necessary */
- libxl_fd_set_cloexec(CTX, fd, 1);
- return;
-out:
- libxl__ev_slowlock_unlock(gc, lock);
- lock->callback(egc, lock, ERROR_LOCK_FAIL);
-}
-
-static void ev_lock_child_callback(libxl__egc *egc, libxl__ev_child *child,
- pid_t pid, int status)
-{
- EGC_GC;
- libxl__ev_slowlock *lock = CONTAINER_OF(child, *lock, child);
- struct stat stab, fstab;
- int rc = ERROR_LOCK_FAIL;
-
- /* Convenience aliases */
- int fd = lock->fd;
- const char *lockfile = lock->path;
- libxl_domid domid = lock->domid;
-
- if (status) {
- libxl_report_child_exitstatus(CTX, XTL_ERROR, "flock child",
- pid, status);
- goto out;
- }
-
- if (fstat(fd, &fstab)) {
- LOGED(ERROR, domid, "cannot fstat %s, fd=%d", lockfile, fd);
- goto out;
- }
- if (stat(lockfile, &stab)) {
- if (errno != ENOENT) {
- LOGED(ERROR, domid, "cannot stat %s", lockfile);
- goto out;
- }
- } else {
- if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino) {
- /* We held the lock */
- lock->held = true;
- rc = 0;
- goto out;
- }
- }
-
- /* We didn't grab the lock, let's try again */
- flock(lock->fd, LOCK_UN);
- close(lock->fd);
- lock->fd = -1;
- ev_lock_prepare_fork(egc, lock);
- return;
-
-out:
- if (lock->held) {
- /* Check the domain is still there, if not we should release the
- * lock and clean up. */
- if (libxl_domain_info(CTX, NULL, domid))
- rc = ERROR_LOCK_FAIL;
- }
- if (rc) {
- LOGD(ERROR, domid, "Failed to grab lock for %s",
- lock->userdata_userid);
- libxl__ev_slowlock_unlock(gc, lock);
- }
- lock->callback(egc, lock, rc);
-}
-
-void libxl__ev_slowlock_unlock(libxl__gc *gc, libxl__ev_slowlock *lock)
-{
- int r;
-
- assert(!libxl__ev_child_inuse(&lock->child));
-
- /* See the rationale in libxl__unlock_domain_userdata()
- * about why we do unlink() before unlock(). */
-
- if (lock->path && lock->held)
- unlink(lock->path);
-
- if (lock->fd >= 0) {
- /* We need to call unlock as the fd may have leaked into other
- * processes */
- r = flock(lock->fd, LOCK_UN);
- if (r)
- LOGED(ERROR, lock->domid, "failed to unlock fd=%d, path=%s",
- lock->fd, lock->path);
- close(lock->fd);
- }
- free(lock->path);
- ev_slowlock_init_internal(lock, lock->userdata_userid);
-}
-
-void libxl__ev_slowlock_dispose(libxl__gc *gc, libxl__ev_slowlock *lock)
-{
- libxl__ev_child_kill_deregister(lock->ao, &lock->child, SIGKILL);
- libxl__ev_slowlock_unlock(gc, lock);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#ifndef LIBXL_INTERNAL_H
-#define LIBXL_INTERNAL_H
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "xentoolcore_internal.h"
-
-#include "libxl_sr_stream_format.h"
-
-#include <assert.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <ctype.h>
-
-#include <sys/mman.h>
-#include <poll.h>
-#include <sys/select.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/file.h>
-#include <sys/ioctl.h>
-
-#include <xenevtchn.h>
-#include <xenstore.h>
-#define XC_WANT_COMPAT_MAP_FOREIGN_API
-#include <xenctrl.h>
-#include <xenguest.h>
-#include <xenhypfs.h>
-#include <xenctrl_dom.h>
-
-#include <xen-tools/libs.h>
-
-#include "xentoollog.h"
-
-#include <xen/io/xenbus.h>
-
-#ifdef LIBXL_H
-# error libxl.h should be included via libxl_internal.h, not separately
-#endif
-#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)
-# define LIBXL_EXTERNAL_CALLERS_ONLY \
- __attribute__((warning("may not be called from within libxl")))
-#endif
-
-#include "libxl.h"
-#include "_paths.h"
-#include "_libxl_save_msgs_callout.h"
-
-#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
-#define _hidden __attribute__((visibility("hidden")))
-#define _protected __attribute__((visibility("protected")))
-#else
-#define _hidden
-#define _protected
-#endif
-
-#include "flexarray.h"
-#include "libxl_utils.h"
-
-#include "libxl_json.h"
-
-#include "_libxl_types_internal.h"
-#include "_libxl_types_internal_json.h"
-
-#define LIBXL_INIT_TIMEOUT 10
-#define LIBXL_DESTROY_TIMEOUT 10
-#define LIBXL_HOTPLUG_TIMEOUT 40
-/* QEMU may be slow to load and start due to a bug in Linux where the I/O
- * subsystem sometime produce high latency under load. */
-#define LIBXL_DEVICE_MODEL_START_TIMEOUT 60
-#define LIBXL_DEVICE_MODEL_SAVE_FILE XEN_LIB_DIR "/qemu-save" /* .$domid */
-#define LIBXL_DEVICE_MODEL_RESTORE_FILE XEN_LIB_DIR "/qemu-resume" /* .$domid */
-#define LIBXL_QMP_CMD_TIMEOUT 10
-#define LIBXL_STUBDOM_START_TIMEOUT 30
-#define LIBXL_QEMU_BODGE_TIMEOUT 2
-#define LIBXL_XENCONSOLE_LIMIT 1048576
-#define LIBXL_XENCONSOLE_PROTOCOL "vt100"
-#define LIBXL_MAXMEM_CONSTANT 1024
-#define LIBXL_PV_EXTRA_MEMORY 1024
-#define LIBXL_HVM_EXTRA_MEMORY 2048
-#define LIBXL_MIN_DOM0_MEM (128*1024)
-#define LIBXL_INVALID_GFN (~(uint64_t)0)
-#define LIBXL_VGA_HOLE_SIZE 0x20
-/* use 0 as the domid of the toolstack domain for now */
-#define LIBXL_TOOLSTACK_DOMID 0
-#define QEMU_SIGNATURE "DeviceModelRecord0002"
-#define STUBDOM_CONSOLE_LOGGING 0
-#define STUBDOM_CONSOLE_SAVE 1
-#define STUBDOM_CONSOLE_RESTORE 2
-#define STUBDOM_CONSOLE_SERIAL 3
-#define STUBDOM_SPECIAL_CONSOLES 3
-#define LIBXL_LINUX_STUBDOM_MEM 128
-#define TAP_DEVICE_SUFFIX "-emu"
-#define DOMID_XS_PATH "domid"
-#define PVSHIM_BASENAME "xen-shim"
-#define PVSHIM_CMDLINE "pv-shim console=xen,pv"
-
-/* Size macros. */
-#define __AC(X,Y) (X##Y)
-#define _AC(X,Y) __AC(X,Y)
-#define MB(_mb) (_AC(_mb, ULL) << 20)
-#define GB(_gb) (_AC(_gb, ULL) << 30)
-
-#define DIV_ROUNDUP(n, d) (((n) + (d) - 1) / (d))
-
-#define MASK_EXTR(v, m) (((v) & (m)) / ((m) & -(m)))
-#define MASK_INSR(v, m) (((v) * ((m) & -(m))) & (m))
-
-#define LIBXL__LOGGING_ENABLED
-
-#ifdef LIBXL__LOGGING_ENABLED
-#define LIBXL__LOG(ctx, loglevel, _f, _a...) libxl__log(ctx, loglevel, -1, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a)
-#define LIBXL__LOG_ERRNO(ctx, loglevel, _f, _a...) libxl__log(ctx, loglevel, errno, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a)
-#define LIBXL__LOG_ERRNOVAL(ctx, loglevel, errnoval, _f, _a...) libxl__log(ctx, loglevel, errnoval, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a)
-
-/* Same log functions as above, but with _d being a domain id. */
-#define LIBXL__LOGD(ctx, loglevel, _d, _f, _a...) libxl__log(ctx, loglevel, -1, __FILE__, __LINE__, __func__, _d, _f, ##_a)
-#define LIBXL__LOGD_ERRNO(ctx, loglevel, _d, _f, _a...) libxl__log(ctx, loglevel, errno, __FILE__, __LINE__, __func__, _d, _f, ##_a)
-#define LIBXL__LOGD_ERRNOVAL(ctx, loglevel, errnoval, _d, _f, _a...) libxl__log(ctx, loglevel, errnoval, __FILE__, __LINE__, __func__, _d, _f, ##_a)
-#else
-#define LIBXL__LOG(ctx, loglevel, _f, _a...)
-#define LIBXL__LOG_ERRNO(ctx, loglevel, _f, _a...)
-#define LIBXL__LOG_ERRNOVAL(ctx, loglevel, errnoval, _f, _a...)
-
-#define LIBXLD__LOG(ctx, loglevel, _d, _f, _a...)
-#define LIBXLD__LOG_ERRNO(ctx, loglevel, _d, _f, _a...)
-#define LIBXLD__LOG_ERRNOVAL(ctx, loglevel, errnoval, _d, _f, _a...)
-#endif
- /* all of these macros preserve errno (saving and restoring) */
-
-/*
- * A macro to help retain the first failure in "do as much as you can"
- * situations. Note the hard-coded use of the variable name `rc`.
- */
-#define ACCUMULATE_RC(rc_acc) ((rc_acc) = (rc_acc) ?: rc)
-
-/* Convert pfn to physical address space. */
-#define pfn_to_paddr(x) ((uint64_t)(x) << XC_PAGE_SHIFT)
-
-/* logging */
-_hidden void libxl__logv(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
- const char *file /* may be 0 */, int line /* ignored if !file */,
- const char *func /* may be 0 */,
- uint32_t domid /* may be INVALID_DOMID */,
- const char *fmt, va_list al)
- __attribute__((format(printf,8,0)));
-
-_hidden void libxl__log(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
- const char *file /* may be 0 */, int line /* ignored if !file */,
- const char *func /* may be 0 */,
- uint32_t domid /* may be INVALID_DOMID */,
- const char *fmt, ...)
- __attribute__((format(printf,8,9)));
-
- /* these functions preserve errno (saving and restoring) */
-
-typedef struct libxl__gc libxl__gc;
-typedef struct libxl__egc libxl__egc;
-typedef struct libxl__ao libxl__ao;
-typedef struct libxl__aop_occurred libxl__aop_occurred;
-typedef struct libxl__osevent_hook_nexus libxl__osevent_hook_nexus;
-typedef struct libxl__osevent_hook_nexi libxl__osevent_hook_nexi;
-typedef struct libxl__device_type libxl__device_type;
-typedef struct libxl__json_object libxl__json_object;
-typedef struct libxl__carefd libxl__carefd;
-typedef struct libxl__ev_slowlock libxl__ev_slowlock;
-typedef struct libxl__dm_resume_state libxl__dm_resume_state;
-typedef struct libxl__ao_device libxl__ao_device;
-typedef struct libxl__multidev libxl__multidev;
-typedef struct libxl__ev_immediate libxl__ev_immediate;
-
-typedef struct libxl__domain_create_state libxl__domain_create_state;
-typedef void libxl__domain_create_cb(struct libxl__egc *egc,
- libxl__domain_create_state *dcs,
- int rc, uint32_t domid);
-
-typedef struct libxl__colo_device_nic libxl__colo_device_nic;
-typedef struct libxl__colo_qdisk libxl__colo_qdisk;
-typedef struct libxl__colo_proxy_state libxl__colo_proxy_state;
-typedef struct libxl__colo_save_state libxl__colo_save_state;
-typedef struct libxl__colo_restore_state libxl__colo_restore_state;
-
-_hidden void libxl__alloc_failed(libxl_ctx *, const char *func,
- size_t nmemb, size_t size) __attribute__((noreturn));
- /* func, size and nmemb are used only in the log message.
- * You may pass size==0 if size and nmemb are not meaningful
- * and should not be printed. */
-
-typedef struct libxl__ev_fd libxl__ev_fd;
-typedef void libxl__ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev,
- int fd, short events, short revents);
- /* Note that revents may contain POLLERR or POLLHUP regardless of
- * events; otherwise revents contains only bits in events. Contrary
- * to the documentation for poll(2), POLLERR and POLLHUP can occur
- * even if only POLLIN was set in events. (POLLNVAL is a fatal
- * error and will cause libxl event machinery to fail an assertion.)
- *
- * It is not permitted to listen for the same or overlapping events
- * on the same fd using multiple different libxl__ev_fd's.
- *
- * Note that (depending on the underlying event loop implementation)
- * it is possible that a the fd callback system is `level triggered'
- * or `event triggered'. That is, the callback may be called only
- * once for each transition from not ready to ready. So the
- * callback must generally contain a loop which exhausts the fd,
- * rather than relying on being called again if the fd is still
- * ready.
- *
- * (Spurious wakeups, and spurious bits set in revents, are
- * suppressed by the libxl event core.)
- */
-struct libxl__ev_fd {
- /* caller should include this in their own struct */
- /* read-only for caller, who may read only when registered: */
- int fd;
- short events;
- libxl__ev_fd_callback *func;
- /* remainder is private for libxl__ev_fd... */
- LIBXL_LIST_ENTRY(libxl__ev_fd) entry;
- libxl__osevent_hook_nexus *nexus;
-};
-
-
-typedef struct libxl__ao_abortable libxl__ao_abortable;
-typedef void libxl__ao_abortable_callback(libxl__egc *egc,
- libxl__ao_abortable *ao_abortable, int rc /* ABORTED */);
-
-struct libxl__ao_abortable {
- /* caller must fill this in and it must remain valid */
- libxl__ao *ao;
- libxl__ao_abortable_callback *callback;
- /* remainder is private for abort machinery */
- bool registered;
- LIBXL_LIST_ENTRY(libxl__ao_abortable) entry;
- /*
- * For nested aos:
- * Semantically, abort affects the whole tree of aos,
- * not just the parent.
- * libxl__ao_abortable.ao refers to the child, so
- * that the child callback sees the right ao. (After all,
- * it was code dealing with the child that set .ao.)
- * But, the abortable is recorded on the "abortables" list
- * for the ultimate root ao, so that every possible child
- * abort occurs as a result of the abort of the parent.
- * We set ao->aborting only in the root.
- */
-};
-
-_hidden int libxl__ao_abortable_register(libxl__ao_abortable*);
-_hidden void libxl__ao_abortable_deregister(libxl__ao_abortable*);
-
-static inline void libxl__ao_abortable_init
- (libxl__ao_abortable *c) { c->registered = 0; }
-static inline bool libxl__ao_abortable_isregistered
- (const libxl__ao_abortable *c) { return c->registered; }
-
-int libxl__ao_aborting(libxl__ao *ao); /* -> 0 or ERROR_ABORTED */
-
-
-typedef struct libxl__ev_time libxl__ev_time;
-typedef void libxl__ev_time_callback(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc); /* TIMEDOUT or ABORTED */
-struct libxl__ev_time {
- /* caller should include this in their own struct */
- /* read-only for caller, who may read only when registered: */
- libxl__ev_time_callback *func;
- /* remainder is private for libxl__ev_time... */
- int infinite; /* not registered in list or with app if infinite */
- LIBXL_TAILQ_ENTRY(libxl__ev_time) entry;
- struct timeval abs;
- libxl__osevent_hook_nexus *nexus;
- libxl__ao_abortable abrt;
-};
-
-typedef struct libxl__ev_xswatch libxl__ev_xswatch;
-typedef void libxl__ev_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch*,
- const char *watch_path, const char *event_path);
-struct libxl__ev_xswatch {
- /* caller should include this in their own struct */
- /* read-only for caller, who may read only when registered: */
- char *path;
- libxl__ev_xswatch_callback *callback;
- /* remainder is private for libxl__ev_xswatch... */
- int slotnum; /* registered iff slotnum >= 0 */
- uint32_t counterval;
-};
-
-typedef struct libxl__ev_evtchn libxl__ev_evtchn;
-typedef void libxl__ev_evtchn_callback(libxl__egc *egc, libxl__ev_evtchn*);
-struct libxl__ev_evtchn {
- /* caller must fill these in, and they must all remain valid */
- libxl__ev_evtchn_callback *callback;
- int port;
- /* remainder is private for libxl__ev_evtchn_... */
- bool waiting;
- LIBXL_LIST_ENTRY(libxl__ev_evtchn) entry;
-};
-
-/*
- * An entry in the watch_slots table is either:
- * 1. an entry in the free list, ie NULL or pointer to next free list entry
- * 2. an pointer to a libxl__ev_xswatch
- *
- * But we don't want to use unions or type-punning because the
- * compiler might "prove" that our code is wrong and misoptimise it.
- *
- * The rules say that all struct pointers have identical
- * representation and alignment requirements (C99+TC1+TC2 6.2.5p26) so
- * what we do is simply declare our array as containing only the free
- * list pointers, and explicitly convert from and to our actual
- * xswatch pointers when we store and retrieve them.
- */
-typedef struct libxl__ev_watch_slot {
- LIBXL_SLIST_ENTRY(struct libxl__ev_watch_slot) empty;
-} libxl__ev_watch_slot;
-
-_hidden libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc,
- int slotnum);
-
-
-typedef struct libxl__ev_child libxl__ev_child;
-typedef void libxl__ev_child_callback(libxl__egc *egc, libxl__ev_child*,
- pid_t pid, int status);
-struct libxl__ev_child {
- /* caller should include this in their own struct */
- /* read-only for caller: */
- pid_t pid; /* -1 means unused ("unregistered", ie Idle) */
- libxl__ev_child_callback *callback;
- /* remainder is private for libxl__ev_... */
- LIBXL_LIST_ENTRY(struct libxl__ev_child) entry;
-};
-
-/* libxl__ev_immediate
- *
- * Allow to call a non-reentrant callback.
- *
- * `callback' will be called immediately as a new event.
- */
-struct libxl__ev_immediate {
- /* filled by user */
- void (*callback)(libxl__egc *, libxl__ev_immediate *);
- /* private to libxl__ev_immediate */
- LIBXL_STAILQ_ENTRY(libxl__ev_immediate) entry;
-};
-void libxl__ev_immediate_register(libxl__egc *, libxl__ev_immediate *);
-
-/*
- * Lock for device hotplug, qmp_lock.
- *
- * libxl__ev_slowlock implement a lock that is outside of CTX_LOCK in the
- * lock hierarchy. It can be used when one want to make QMP calls to QEMU,
- * which may take a significant amount time.
- * It is to be acquired by an ao event callback.
- *
- * If libxl__ev_devlock is needed, it should be acquired while every
- * libxl__ev_qmp are Idle for the current domain.
- *
- * It is to be acquired when adding/removing devices or making changes
- * to them when this is a slow operation and json_lock isn't appropriate.
- *
- * Possible states of libxl__ev_slowlock:
- * Undefined
- * Might contain anything.
- * Idle
- * Struct contents are defined enough to pass to any
- * libxl__ev_slowlock_* function.
- * The struct does not contain references to any allocated private
- * resources so can be thrown away.
- * Active
- * Waiting to get a lock.
- * Needs to wait until the callback is called.
- * LockAcquired
- * libxl__ev_slowlock_unlock will need to be called to release the lock
- * and the resources of libxl__ev_slowlock.
- *
- * libxl__ev_*lock_init: Undefined/Idle -> Idle
- * libxl__ev_slowlock_lock: Idle -> Active
- * May call callback synchronously.
- * libxl__ev_slowlock_unlock: LockAcquired/Idle -> Idle
- * libxl__ev_slowlock_dispose: Idle/Active/LockAcquired -> Idle
- * The callback will not be called anymore.
- * callback: When called: Active -> LockAcquired (on error: Idle)
- * The callback is only called once.
- */
-struct libxl__ev_slowlock {
- /* filled by user */
- libxl__ao *ao;
- libxl_domid domid;
- void (*callback)(libxl__egc *, libxl__ev_slowlock *, int rc);
- /* private to libxl__ev_slowlock* */
- libxl__ev_child child;
- const char *userdata_userid;
- char *path; /* path of the lock file itself */
- int fd;
- bool held;
-};
-_hidden void libxl__ev_devlock_init(libxl__ev_slowlock *);
-_hidden void libxl__ev_qmplock_init(libxl__ev_slowlock *);
-_hidden void libxl__ev_slowlock_lock(libxl__egc *, libxl__ev_slowlock *);
-_hidden void libxl__ev_slowlock_unlock(libxl__gc *, libxl__ev_slowlock *);
-_hidden void libxl__ev_slowlock_dispose(libxl__gc *, libxl__ev_slowlock *);
-
-/*
- * QMP asynchronous calls
- *
- * This facility allows a command to be sent to QEMU, and the response
- * to be handed to a callback function.
- *
- * Commands can be submited one after an other with the same
- * connection (e.g. the result from the "add-fd" command need to be
- * use in a follow-up command before disconnecting from QMP). A
- * libxl__ev_qmp can be reused when the callback is been called in
- * order to use the same connection.
- *
- * Only one connection at a time can be made to one QEMU, so avoid
- * keeping a libxl__ev_qmp Connected for to long and call
- * libxl__ev_qmp_dispose as soon as it is not needed anymore.
- *
- * Possible states of a libxl__ev_qmp:
- * Undefined
- * Might contain anything.
- * Idle
- * Struct contents are defined enough to pass to any
- * libxl__ev_qmp_* function.
- * The struct does not contain references to any allocated private
- * resources so can be thrown away.
- * Active
- * Currently waiting for the callback to be called.
- * _dispose must be called to reclaim resources.
- * Connected
- * Struct contain allocated ressources.
- * Calling _send() with this same ev will use the same QMP connection.
- * _dispose() must be called to reclaim resources.
- *
- * libxl__ev_qmp_init: Undefined/Idle -> Idle
- *
- * libxl__ev_qmp_send: Idle/Connected -> Active (on error: Idle)
- * Sends a command to QEMU.
- * callback will be called when a response is received or when an
- * error as occured.
- * callback isn't called synchronously.
- *
- * libxl__ev_qmp_dispose: Connected/Active/Idle -> Idle
- *
- * callback: When called: Active -> Connected (on error: Idle/Connected)
- * When called, ev is Connected and can be reused or disposed of.
- * On error, the callback is called with response == NULL and the
- * error code in rc. The new state of ev depending on the value of rc:
- * - rc == ERROR_QMP_*: This is an error associated with the cmd to
- * run, ev is Connected.
- * - otherwise: An other error happend, ev is now Idle.
- * The callback is only called once.
- */
-typedef struct libxl__ev_qmp libxl__ev_qmp;
-typedef void libxl__ev_qmp_callback(libxl__egc *egc, libxl__ev_qmp *ev,
- const libxl__json_object *response,
- int rc);
-
-_hidden void libxl__ev_qmp_init(libxl__ev_qmp *ev);
-_hidden int libxl__ev_qmp_send(libxl__egc *egc, libxl__ev_qmp *ev,
- const char *cmd, libxl__json_object *args);
-_hidden void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev);
-
-typedef enum {
- /* initial state */
- qmp_state_disconnected = 1,
- /* waiting for lock */
- qmp_state_waiting_lock,
- /* connected to QMP socket, waiting for greeting message */
- qmp_state_connecting,
- /* qmp_capabilities command sent, waiting for reply */
- qmp_state_capability_negotiation,
- /* sending user's cmd and waiting for reply */
- qmp_state_waiting_reply,
- /* ready to send commands */
- qmp_state_connected,
-} libxl__qmp_state;
-
-struct libxl__ev_qmp {
- /* caller should include this in their own struct */
- /* caller must fill these in, and they must all remain valid */
- libxl__ao *ao;
- libxl_domid domid;
- libxl__ev_qmp_callback *callback;
- int payload_fd; /* set to send a fd with the command, -1 otherwise */
-
- /* read-only when Connected
- * and not to be accessed by the caller otherwise */
- struct {
- int major;
- int minor;
- int micro;
- } qemu_version;
-
- /*
- * remaining fields are private to libxl_ev_qmp_*
- */
-
- libxl__carefd *cfd;
- libxl__ev_fd efd;
- libxl__qmp_state state;
- libxl__ev_slowlock lock;
- libxl__ev_immediate ei;
- int rc;
- int id;
- int next_id; /* next id to use */
- /* receive buffer */
- char *rx_buf;
- size_t rx_buf_size; /* current allocated size */
- size_t rx_buf_used; /* actual data in the buffer */
- /* sending buffer */
- char *tx_buf;
- size_t tx_buf_len; /* tx_buf size */
- size_t tx_buf_off; /* already sent */
- /* The message to send when ready */
- char *msg;
- int msg_id;
-};
-
-/* QMP parameters helpers */
-
-_hidden void libxl__qmp_param_add_string(libxl__gc *gc,
- libxl__json_object **param,
- const char *name, const char *s);
-_hidden void libxl__qmp_param_add_bool(libxl__gc *gc,
- libxl__json_object **param,
- const char *name, bool b);
-_hidden void libxl__qmp_param_add_integer(libxl__gc *gc,
- libxl__json_object **param,
- const char *name, const int i);
-#define QMP_PARAMETERS_SPRINTF(args, name, format, ...) \
- libxl__qmp_param_add_string(gc, args, name, \
- GCSPRINTF(format, __VA_ARGS__))
-
-
-/*
- * evgen structures, which are the state we use for generating
- * events for the caller.
- *
- * In general in each case there's an internal and an external
- * version of the _evdisable_FOO function; the internal one is
- * used during cleanup.
- */
-struct libxl__evgen_domain_death {
- uint32_t domid;
- unsigned shutdown_reported:1, death_reported:1;
- LIBXL_TAILQ_ENTRY(libxl_evgen_domain_death) entry;
- /* on list .death_reported ? CTX->death_list : CTX->death_reported */
- libxl_ev_user user;
-};
-_hidden void
-libxl__evdisable_domain_death(libxl__gc*, libxl_evgen_domain_death*);
-
-struct libxl__evgen_disk_eject {
- libxl__ev_xswatch watch;
- uint32_t domid;
- LIBXL_LIST_ENTRY(libxl_evgen_disk_eject) entry;
- libxl_ev_user user;
- char *vdev, *be_ptr_path;
-};
-_hidden void
-libxl__evdisable_disk_eject(libxl__gc*, libxl_evgen_disk_eject*);
-
-typedef struct libxl__poller libxl__poller;
-struct libxl__poller {
- /*
- * These are used to allow other threads to wake up a thread which
- * may be stuck in poll, because whatever it was waiting for
- * hadn't happened yet. Threads which generate events will write
- * a byte to each pipe. A thread which is waiting will empty its
- * own pipe, and put its poller on the pollers_event list, before
- * releasing the ctx lock and going into poll; when it comes out
- * of poll it will take the poller off the pollers_event list.
- *
- * A thread which is waiting for completion of a synchronous ao
- * will allocate a poller and record it in the ao, so that other
- * threads can wake it up.
- *
- * When a thread is done with a poller it should put it onto
- * pollers_idle, where it can be reused later.
- *
- * The "poller_app" is never idle, but is sometimes on
- * pollers_event.
- */
- LIBXL_LIST_ENTRY(libxl__poller) entry;
-
- struct pollfd *fd_polls;
- int fd_polls_allocd;
-
- int fd_rindices_allocd;
- int (*fd_rindices)[3]; /* see libxl_event.c:beforepoll_internal */
-
- int wakeup_pipe[2]; /* 0 means no fd allocated */
- bool pipe_nonempty;
-
- /*
- * We also use the poller to record whether any fds have been
- * deregistered since we entered poll. Each poller which is not
- * idle is on the list pollers_active. fds_deregistered is
- * cleared by beforepoll, and tested by afterpoll. Whenever an fd
- * event is deregistered, we set the fds_deregistered of all non-idle
- * pollers. So afterpoll can tell whether any POLLNVAL is
- * plausibly due to an fd being closed and reopened.
- *
- * Additionally, we record whether any fd or time event sources
- * have been registered. This is necessary because sometimes we
- * need to wake up the only libxl thread stuck in
- * eventloop_iteration so that it will pick up new fds or earlier
- * timeouts. osevents_added is cleared by beforepoll, and set by
- * fd or timeout event registration. When we are about to leave
- * libxl (strictly, when we are about to give up an egc), we check
- * whether there are any pollers. If there are, then at least one
- * of them must have osevents_added clear. If not, we wake up the
- * first one on the list. Any entry on pollers_active constitutes
- * a promise to also make this check, so the baton will never be
- * dropped.
- */
- LIBXL_LIST_ENTRY(libxl__poller) active_entry;
- bool fds_deregistered;
- bool osevents_added;
-};
-
-struct libxl__gc {
- /* mini-GC */
- int alloc_maxsize; /* -1 means this is the dummy non-gc gc */
- void **alloc_ptrs;
- libxl_ctx *owner;
-};
-
-struct libxl__ctx {
- xentoollog_logger *lg;
- xc_interface *xch;
- struct xs_handle *xsh;
- libxl__gc nogc_gc;
-
- const libxl_event_hooks *event_hooks;
- void *event_hooks_user;
-
- pthread_mutex_t lock; /* protects data structures hanging off the ctx */
- /* Always use libxl__ctx_lock and _unlock (or the convenience
- * macors CTX_LOCK and CTX_UNLOCK) to manipulate this.
- *
- * You may acquire this mutex recursively if it is convenient to
- * do so. You may not acquire this lock at the same time as any
- * other lock. If you need to call application code outside
- * libxl (ie, a callback) with this lock held then it is
- * necessaray to impose restrictions on the caller to maintain a
- * proper lock hierarchy, and these restrictions must then be
- * documented in the libxl public interface.
- */
-
- LIBXL_TAILQ_HEAD(libxl__event_list, libxl_event) occurred;
-
- int osevent_in_hook;
- const libxl_osevent_hooks *osevent_hooks;
- void *osevent_user;
- /* See the comment for OSEVENT_HOOK_INTERN in libxl_event.c
- * for restrictions on the use of the osevent fields. */
-
- libxl__poller *poller_app; /* libxl_osevent_beforepoll and _afterpoll */
- LIBXL_LIST_HEAD(, libxl__poller) pollers_event, pollers_idle;
- LIBXL_LIST_HEAD(, libxl__poller) pollers_active;
-
- LIBXL_SLIST_HEAD(libxl__osevent_hook_nexi, libxl__osevent_hook_nexus)
- hook_fd_nexi_idle, hook_timeout_nexi_idle;
- LIBXL_LIST_HEAD(, libxl__ev_fd) efds;
- LIBXL_TAILQ_HEAD(, libxl__ev_time) etimes;
-
- libxl__ev_watch_slot *watch_slots;
- int watch_nslots, nwatches;
- LIBXL_SLIST_HEAD(, libxl__ev_watch_slot) watch_freeslots;
- uint32_t watch_counter; /* helps disambiguate slot reuse */
- libxl__ev_fd watch_efd;
-
- xenevtchn_handle *xce; /* waiting must be done only with libxl__ev_evtchn* */
- LIBXL_LIST_HEAD(, libxl__ev_evtchn) evtchns_waiting;
- libxl__ev_fd evtchn_efd;
-
- LIBXL_LIST_HEAD(, libxl__ao) aos_inprogress;
-
- LIBXL_TAILQ_HEAD(libxl__evgen_domain_death_list, libxl_evgen_domain_death)
- death_list /* sorted by domid */,
- death_reported;
- libxl__ev_xswatch death_watch;
-
- LIBXL_LIST_HEAD(, libxl_evgen_disk_eject) disk_eject_evgens;
-
- const libxl_childproc_hooks *childproc_hooks;
- void *childproc_user;
- int sigchld_selfpipe[2]; /* [0]==-1 means handler not installed */
- libxl__ev_fd sigchld_selfpipe_efd;
- LIBXL_LIST_HEAD(, libxl__ev_child) children;
- bool sigchld_user_registered;
- LIBXL_LIST_ENTRY(libxl_ctx) sigchld_users_entry;
-
- libxl_version_info version_info;
-
- bool libxl_domain_need_memory_0x041200_called,
- libxl_domain_need_memory_called;
-};
-
-/*
- * libxl__device is a transparent structure that doesn't contain private fields
- * or external memory references, and as such can be copied by assignment.
- */
-typedef struct {
- uint32_t backend_devid;
- uint32_t backend_domid;
- uint32_t devid;
- uint32_t domid;
- libxl__device_kind backend_kind;
- libxl__device_kind kind;
-} libxl__device;
-
-/* Used to know if backend of given device is QEMU */
-#define QEMU_BACKEND(dev) (\
- (dev)->backend_kind == LIBXL__DEVICE_KIND_QDISK || \
- (dev)->backend_kind == LIBXL__DEVICE_KIND_VFB || \
- (dev)->backend_kind == LIBXL__DEVICE_KIND_QUSB || \
- (dev)->backend_kind == LIBXL__DEVICE_KIND_9PFS || \
- (dev)->backend_kind == LIBXL__DEVICE_KIND_VKBD)
-
-#define XC_PCI_BDF "0x%x, 0x%x, 0x%x, 0x%x"
-#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
-#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
-#define PCI_FUNC(devfn) ((devfn) & 0x07)
-#define AUTO_PHP_SLOT 0x100
-
-#define PROC_PCI_NUM_RESOURCES 7
-#define PCI_BAR_IO 0x01
-
-#define PRINTF_ATTRIBUTE(x, y) __attribute__((format(printf, x, y)))
-
-struct libxl__egc {
- /* For event-generating functions only.
- * The egc and its gc may be accessed only on the creating thread. */
- struct libxl__gc gc;
- struct libxl__event_list occurred_for_callback;
- LIBXL_TAILQ_HEAD(, libxl__ao) aos_for_callback;
- LIBXL_TAILQ_HEAD(, libxl__aop_occurred) aops_for_callback;
- LIBXL_STAILQ_HEAD(, libxl__ev_immediate) ev_immediates;
-};
-
-struct libxl__aop_occurred {
- /*
- * An aop belongs to, and may be accessed only on, the thread
- * which created it. It normally lives in that thread's egc.
- *
- * While an aop exists, it corresponds to one refcount in
- * ao->progress_reports_outstanding, preventing ao destruction.
- */
- LIBXL_TAILQ_ENTRY(libxl__aop_occurred) entry;
- libxl__ao *ao;
- libxl_event *ev;
- const libxl_asyncprogress_how *how;
-};
-
-#define LIBXL__AO_MAGIC 0xA0FACE00ul
-#define LIBXL__AO_MAGIC_DESTROYED 0xA0DEAD00ul
-
-struct libxl__ao {
- /*
- * An ao and its gc may be accessed only with the ctx lock held.
- *
- * Special exception: If an ao has been added to
- * egc->aos_for_callback, the thread owning the egc may remove the
- * ao from that list and make the callback without holding the
- * lock.
- *
- * Corresponding restriction: An ao may be added only to one
- * egc->aos_for_callback, once; rc and how must already have been
- * set and may not be subsequently modified. (This restriction is
- * easily and obviously met since the ao is queued for callback
- * only in libxl__ao_complete.)
- */
- uint32_t magic;
- unsigned constructing:1, in_initiator:1, complete:1, notified:1,
- aborting:1;
- int manip_refcnt;
- libxl__ao *nested_root;
- int nested_progeny;
- int progress_reports_outstanding;
- int rc;
- LIBXL_LIST_HEAD(, libxl__ao_abortable) abortables;
- LIBXL_LIST_ENTRY(libxl__ao) inprogress_entry;
- libxl__gc gc;
- libxl_asyncop_how how;
- libxl__poller *poller;
- uint32_t domid;
- LIBXL_TAILQ_ENTRY(libxl__ao) entry_for_callback;
- int outstanding_killed_child;
-};
-
-#define LIBXL_INIT_GC(gc,ctx) do{ \
- (gc).alloc_maxsize = 0; \
- (gc).alloc_ptrs = 0; \
- (gc).owner = (ctx); \
- } while(0)
- /* NB, also, a gc struct ctx->nogc_gc is initialised in libxl_ctx_alloc */
-
-static inline libxl_ctx *libxl__gc_owner(libxl__gc *gc)
-{
- return gc->owner;
-}
-
-static inline int libxl__gc_is_real(const libxl__gc *gc)
-{
- return gc->alloc_maxsize >= 0;
-}
-
-/*
- * Memory allocation tracking/helpers
- *
- * See comment "libxl memory management" in libxl.h for a description
- * of the framework which these calls belong to.
- *
- * These functions deal with memory allocations of type (a) and (d) in
- * that description.
- *
- * All pointers returned by these functions are registered for garbage
- * collection on exit from the outermost libxl callframe.
- *
- * However, where the argument is stated to be "gc_opt", &ctx->nogc_gc
- * may be passed instead, in which case no garbage collection will
- * occur; the pointer must later be freed with free(). (Passing NULL
- * for gc_opt is not permitted.) This is for memory allocations of
- * types (b) and (c). The convenience macro NOGC should be used where
- * possible.
- *
- * NOGC (and ctx->nogc_gc) may ONLY be used with functions which
- * explicitly declare that it's OK. Use with nonconsenting functions
- * may result in leaks of those functions' internal allocations on the
- * psuedo-gc.
- */
-/* register ptr in gc for free on exit from outermost libxl callframe. */
-
-#define NN(...) __attribute__((nonnull(__VA_ARGS__)))
-#define NN1 __attribute__((nonnull(1)))
- /* It used to be legal to pass NULL for gc_opt. Get the compiler to
- * warn about this if any slip through. */
-
-_hidden void libxl__ptr_add(libxl__gc *gc_opt, void *ptr /* may be NULL */) NN1;
-/* if this is the outermost libxl callframe then free all pointers in @gc */
-_hidden void libxl__free_all(libxl__gc *gc);
-/* allocate @size bytes. (a gc'd malloc(3)) */
-_hidden void *libxl__malloc(libxl__gc *gc_opt, size_t size) NN1;
-/* allocate and zero @size. (similar to a gc'd malloc(3)+memzero()) */
-_hidden void *libxl__zalloc(libxl__gc *gc_opt, size_t size) NN1;
-/* allocate and zero memory for an array of @nmemb members of @size each.
- * (similar to a gc'd calloc(3)). */
-_hidden void *libxl__calloc(libxl__gc *gc_opt, size_t nmemb, size_t size) NN1;
-/* change the size of the memory block pointed to by @ptr to @new_size bytes.
- * unlike other allocation functions here any additional space between the
- * oldsize and @new_size is not initialised (similar to a gc'd realloc(3)).
- * if @ptr is non-NULL and @gc_opt is not nogc_gc then @ptr must have been
- * registered with @gc_opt previously. */
-_hidden void *libxl__realloc(libxl__gc *gc_opt, void *ptr, size_t new_size) NN1;
-/* print @fmt into an allocated string large enoughto contain the result.
- * (similar to gc'd asprintf(3)). */
-_hidden char *libxl__sprintf(libxl__gc *gc_opt, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3) NN1;
-_hidden char *libxl__vsprintf(libxl__gc *gc, const char *format, va_list ap) PRINTF_ATTRIBUTE(2, 0);
-/* duplicate the string @c (similar to a gc'd strdup(3)). */
-_hidden char *libxl__strdup(libxl__gc *gc_opt,
- const char *c /* may be NULL */) NN1;
-/* duplicate at most @n bytes of string @c (similar to a gc'd strndup(3)). */
-_hidden char *libxl__strndup(libxl__gc *gc_opt,
- const char *c /* may be NULL */,
- size_t n) NN1;
-/* strip the last path component from @s and return as a newly allocated
- * string. (similar to a gc'd dirname(3)). */
-_hidden char *libxl__dirname(libxl__gc *gc_opt, const char *s) NN1;
-
-/* Make a pipe and set both ends nonblocking. On error, nothing
- * is left open and both fds[]==-1, and a message is logged.
- * Useful for self-pipes. */
-_hidden int libxl__pipe_nonblock(libxl_ctx *ctx, int fds[2]);
-/* Closes the pipe fd(s). Either or both of fds[] may be -1 meaning
- * `not open'. Ignores any errors. Sets fds[] to -1. */
-_hidden void libxl__pipe_close(int fds[2]);
-
-/* Change the flags for the file description associated with fd to
- * (flags & mask) | val.
- * If r_oldflags != NULL then sets *r_oldflags to the original set of
- * flags.
- */
-_hidden int libxl__fd_flags_modify_save(libxl__gc *gc, int fd,
- int mask, int val, int *r_oldflags);
-/* Restores the flags for the file description associated with fd to
- * to the previous value (returned by libxl__fd_flags_modify_save)
- */
-_hidden int libxl__fd_flags_restore(libxl__gc *gc, int fd, int old_flags);
-
-/* Each of these logs errors and returns a libxl error code.
- * They do not mind if path is already removed.
- * For _file, path must not be a directory; for _directory it must be. */
-_hidden int libxl__remove_file(libxl__gc *gc, const char *path);
-_hidden int libxl__remove_directory(libxl__gc *gc, const char *path);
-_hidden int libxl__remove_file_or_directory(libxl__gc *gc, const char *path);
-
-
-_hidden char **libxl__xs_kvs_of_flexarray(libxl__gc *gc, flexarray_t *array);
-
-/* treats kvs as pairs of keys and values and writes each to dir. */
-_hidden int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t,
- const char *dir, char **kvs);
-/* as writev but also sets the permissions on each path */
-_hidden int libxl__xs_writev_perms(libxl__gc *gc, xs_transaction_t t,
- const char *dir, char *kvs[],
- struct xs_permissions *perms,
- unsigned int num_perms);
-/* _atonce creates a transaction and writes all keys at once */
-_hidden int libxl__xs_writev_atonce(libxl__gc *gc,
- const char *dir, char **kvs);
- /* Each fn returns 0 on success.
- * On error: returns -1, sets errno (no logging) */
-
-_hidden char *libxl__xs_get_dompath(libxl__gc *gc, uint32_t domid);
- /* On error: logs, returns NULL, sets errno. */
-
-_hidden char *libxl__xs_read(libxl__gc *gc, xs_transaction_t t,
- const char *path);
-_hidden char **libxl__xs_directory(libxl__gc *gc, xs_transaction_t t,
- const char *path, unsigned int *nb);
- /* On error: returns NULL, sets errno (no logging) */
-_hidden char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t domid);
-
-_hidden int libxl__backendpath_parse_domid(libxl__gc *gc, const char *be_path,
- libxl_domid *domid_out);
-
-/*----- "checked" xenstore access functions -----*/
-/* Each of these functions will check that it succeeded; if it
- * fails it logs and returns ERROR_FAIL.
- */
-
-int libxl__xs_vprintf(libxl__gc *gc, xs_transaction_t t,
- const char *path, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(4, 0);
-int libxl__xs_printf(libxl__gc *gc, xs_transaction_t t,
- const char *path, const char *fmt, ...) PRINTF_ATTRIBUTE(4, 5);
-
-/* On success, path will exist and will have an empty value */
-int libxl__xs_mknod(libxl__gc *gc, xs_transaction_t t,
- const char *path, struct xs_permissions *perms,
- unsigned int num_perms);
-
-/* On success, *result_out came from the gc.
- * On error, *result_out is undefined.
- * ENOENT is regarded as error.
- */
-int libxl__xs_read_mandatory(libxl__gc *gc, xs_transaction_t t,
- const char *path, const char **result_out);
-
-/* On success, *result_out came from the gc.
- * On error, *result_out is undefined.
- * ENOENT counts as success but sets *result_out=0
- */
-int libxl__xs_read_checked(libxl__gc *gc, xs_transaction_t t,
- const char *path, const char **result_out);
-
-/* Does not include a trailing null.
- * May usefully be combined with GCSPRINTF if the format string
- * behaviour of libxl__xs_printf is desirable. */
-int libxl__xs_write_checked(libxl__gc *gc, xs_transaction_t t,
- const char *path, const char *string);
-
-/* ENOENT is not an error (even if the parent directories don't exist) */
-int libxl__xs_rm_checked(libxl__gc *gc, xs_transaction_t t, const char *path);
-
-/* Transaction functions, best used together.
- * The caller should initialise *t to 0 (XBT_NULL) before calling start.
- * Each function leaves *t!=0 iff the transaction needs cleaning up.
- *
- * libxl__xs_transaction_commit returns:
- * <0 failure - a libxl error code
- * +1 commit conflict; transaction has been destroyed and caller
- * must go round again (call _start again and retry)
- * 0 committed successfully
- *
- * The intended usage pattern looks like this:
- * int some_function()
- * {
- * int rc;
- * xs_transaction_t t = 0;
- * // other initialisations
- *
- * // do whatever you need to do before the xenstore stuff
- * // errors? set rc and goto out.
- *
- * for (;;) {
- * rc = libxl__xs_transaction_start(gc, &t);
- * if (rc) goto out;
- *
- * // do your work here, including all xenstore reads and writes
- * // libxl__xs_*_checked are useful; pass them t.
- * // errors? set rc and goto out.
- *
- * rc = libxl__xs_transaction_commit(gc, &t);
- * if (!rc) break;
- * if (rc<0) goto out;
- * }
- *
- * // now the xenstore transaction succeeded
- * // do whatever else you need to do
- * // errors? set rc and goto out.
- *
- * return something;
- *
- * out:
- * // other cleanups
- * libxl__xs_transaction_abort(gc, &t);
- * // other cleanups
- * return rc;
- * }
- *
- * Formally the states of *t are:
- *
- * name value of *t description
- * Idle 0 no transaction exists
- * Ready non-0 ready for work, nothing done yet
- * Busy non-0 writes have been made but we are not finished
- * Uncommitted non-0 writes have been made and should be committed
- *
- * libxl__xs_transaction_start: Idle -> Ready (on error: Idle)
- *
- * The transaction goes from Ready to Busy, and from Busy to
- * Uncommitted, by the use of xenstore read and write operations
- * (libxl__xs_..., xs_...) made by libxl__xs_transaction's caller.
- *
- * libxl__xs_transaction_commit: Ready/Uncommitted -> Idle
- * on success (returns 0): xenstore has been updated
- * on error (<0) or conflict (+1): updates discarded
- *
- * libxl__xs_transaction_abort: Any -> Idle (any updates discarded)
- */
-int libxl__xs_transaction_start(libxl__gc *gc, xs_transaction_t *t);
-int libxl__xs_transaction_commit(libxl__gc *gc, xs_transaction_t *t);
-void libxl__xs_transaction_abort(libxl__gc *gc, xs_transaction_t *t);
-
-
-
-/*
- * This is a recursive delete, from top to bottom. What this function does
- * is remove empty folders that contained the deleted entry.
- *
- * It mimics xenstore-rm -t behaviour.
- */
-_hidden int libxl__xs_path_cleanup(libxl__gc *gc, xs_transaction_t t,
- const char *user_path);
-
-/*
- * Event generation functions provided by the libxl event core to the
- * rest of libxl. Implemented in terms of _beforepoll/_afterpoll
- * and/or the fd registration machinery, as provided by the
- * application.
- *
- * Semantics are similar to those of the fd and timeout registration
- * functions provided to libxl_osevent_register_hooks.
- *
- * Non-0 returns from libxl__ev_{modify,deregister} have already been
- * logged by the core and should be returned unmodified to libxl's
- * caller; NB that they may be valid libxl error codes but they may
- * also be positive numbers supplied by the caller.
- *
- * In each case, there is a libxl__ev_FOO structure which can be in
- * one of three states:
- *
- * Undefined - Might contain anything. All-bits-zero is
- * an undefined state.
- *
- * Idle - Struct contents are defined enough to pass to any
- * libxl__ev_FOO function but not registered and
- * callback will not be called. The struct does not
- * contain references to any allocated resources so
- * can be thrown away.
- *
- * Active - Request for events has been registered and events
- * may be generated. _deregister must be called to
- * reclaim resources.
- *
- * These functions are provided for each kind of event KIND:
- *
- * int libxl__ev_KIND_register(libxl__gc *gc, libxl__ev_KIND *GEN,
- * libxl__ev_KIND_callback *FUNC,
- * DETAILS);
- * On entry *GEN must be in state Undefined or Idle.
- * Returns a libxl error code; on error return *GEN is Idle.
- * On successful return *GEN is Active and FUNC wil be
- * called by the event machinery in future. FUNC will
- * not be called from within the call to _register.
- * FUNC will be called with the context locked (with CTX_LOCK).
- *
- * void libxl__ev_KIND_deregister(libxl__gc *gc, libxl__ev_KIND *GEN_upd);
- * On entry *GEN must be in state Active or Idle.
- * On return it is Idle. (Idempotent.)
- *
- * void libxl__ev_KIND_init(libxl__ev_KIND *GEN);
- * Provided for initialising an Undefined KIND.
- * On entry *GEN must be in state Idle or Undefined.
- * On return it is Idle. (Idempotent.)
- *
- * int libxl__ev_KIND_isregistered(const libxl__ev_KIND *GEN);
- * On entry *GEN must be Idle or Active.
- * Returns nonzero if it is Active, zero otherwise.
- * Cannot fail.
- *
- * int libxl__ev_KIND_modify(libxl__gc*, libxl__ev_KIND *GEN,
- * DETAILS);
- * Only provided for some kinds of generator.
- * On entry *GEN must be Active and on return, whether successful
- * or not, it will be Active.
- * Returns a libxl error code; on error the modification
- * is not effective.
- *
- * All of these functions are fully threadsafe and may be called by
- * general code in libxl even from within event callback FUNCs.
- * The ctx will be locked on entry to each FUNC and FUNC should not
- * unlock it.
- *
- * Callers of libxl__ev_KIND_register must ensure that the
- * registration is undone, with _deregister, in libxl_ctx_free.
- * This means that normally each kind of libxl__evgen (ie each
- * application-requested event source) needs to be on a list so that
- * it can be automatically deregistered as promised in libxl_event.h.
- */
-
-
-_hidden int libxl__ev_fd_register(libxl__gc*, libxl__ev_fd *ev_out,
- libxl__ev_fd_callback*,
- int fd, short events /* as for poll(2) */);
-_hidden int libxl__ev_fd_modify(libxl__gc*, libxl__ev_fd *ev,
- short events);
-_hidden void libxl__ev_fd_deregister(libxl__gc*, libxl__ev_fd *ev);
-static inline void libxl__ev_fd_init(libxl__ev_fd *efd)
- { efd->fd = -1; }
-static inline int libxl__ev_fd_isregistered(const libxl__ev_fd *efd)
- { return efd->fd >= 0; }
-
-_hidden int libxl__ev_time_register_rel(libxl__ao*, libxl__ev_time *ev_out,
- libxl__ev_time_callback*,
- int milliseconds /* as for poll(2) */);
-_hidden int libxl__ev_time_register_abs(libxl__ao*, libxl__ev_time *ev_out,
- libxl__ev_time_callback*,
- struct timeval);
-_hidden int libxl__ev_time_modify_rel(libxl__gc*, libxl__ev_time *ev,
- int milliseconds /* as for poll(2) */);
-_hidden int libxl__ev_time_modify_abs(libxl__gc*, libxl__ev_time *ev,
- struct timeval);
-_hidden void libxl__ev_time_deregister(libxl__gc*, libxl__ev_time *ev);
-static inline void libxl__ev_time_init(libxl__ev_time *ev)
- { ev->func = 0; libxl__ao_abortable_init(&ev->abrt); }
-static inline int libxl__ev_time_isregistered(const libxl__ev_time *ev)
- { return !!ev->func; }
-
-
-_hidden int libxl__ev_xswatch_register(libxl__gc*, libxl__ev_xswatch *xsw_out,
- libxl__ev_xswatch_callback*,
- const char *path /* copied */);
-_hidden void libxl__ev_xswatch_deregister(libxl__gc *gc, libxl__ev_xswatch*);
-
-static inline void libxl__ev_xswatch_init(libxl__ev_xswatch *xswatch_out)
- { xswatch_out->slotnum = -1; }
-static inline int libxl__ev_xswatch_isregistered(const libxl__ev_xswatch *xw)
- { return xw->slotnum >= 0; }
-
-
-/*
- * The evtchn facility is one-shot per call to libxl__ev_evtchn_wait.
- * You should:
- * Use libxl__ctx_evtchn_init to make sure CTX->xce is valid;
- * Call some suitable xc bind function on (or to obtain) the port;
- * Then call libxl__ev_evtchn_wait.
- *
- * When the event is signaled then the callback will be made, once.
- * Then you must call libxl__ev_evtchn_wait again, if desired.
- *
- * You must NOT call xenevtchn_unmask. wait will do that for you.
- *
- * Calling libxl__ev_evtchn_cancel will arrange for libxl to disregard
- * future occurrences of event. Both libxl__ev_evtchn_wait and
- * libxl__ev_evtchn_cancel are idempotent.
- *
- * (Note of course that an event channel becomes signaled when it is
- * first bound, so you will get one call to libxl__ev_evtchn_wait
- * "right away"; unless you have won a very fast race, the condition
- * you were waiting for won't exist yet so when you check for it
- * you'll find you need to call wait again.)
- *
- * You must not wait on the same port twice at once (that is, with
- * two separate libxl__ev_evtchn's).
- */
-_hidden int libxl__ev_evtchn_wait(libxl__gc*, libxl__ev_evtchn *evev);
-_hidden void libxl__ev_evtchn_cancel(libxl__gc *gc, libxl__ev_evtchn *evev);
-
-static inline void libxl__ev_evtchn_init(libxl__ev_evtchn *evev)
- { evev->waiting = 0; }
-static inline bool libxl__ev_evtchn_iswaiting(const libxl__ev_evtchn *evev)
- { return evev->waiting; }
-
-_hidden int libxl__ctx_evtchn_init(libxl__gc *gc); /* for libxl_ctx_alloc */
-
-/*
- * For making subprocesses. This is the only permitted mechanism for
- * code in libxl to do so.
- *
- * In the parent, returns the pid, filling in childw_out.
- * In the child, returns 0.
- * If it fails, returns a libxl error (all of which are -ve).
- *
- * The child should go on to exec (or exit) soon. The child may not
- * make any further calls to libxl infrastructure, except for memory
- * allocation and logging. If the child needs to use xenstore it
- * must open its own xs handle and use it directly, rather than via
- * the libxl event machinery.
- *
- * The parent may signal the child but it must not reap it. That will
- * be done by the event machinery.
- *
- * The child death event will generate exactly one event callback; until
- * then the childw is Active and may not be reused.
- *
- * libxl__ev_child_kill_deregister: Active -> Idle
- * This will transfer ownership of the child process death event from
- * `ch' to `ao', thus deregister the callback.
- * The `ao' completion will wait until the child have been reaped by the
- * event machinery.
- */
-_hidden pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *childw_out,
- libxl__ev_child_callback *death);
-static inline void libxl__ev_child_init(libxl__ev_child *childw_out)
- { childw_out->pid = -1; }
-static inline int libxl__ev_child_inuse(const libxl__ev_child *childw_out)
- { return childw_out->pid >= 0; }
-_hidden void libxl__ev_child_kill_deregister(libxl__ao *ao,
- libxl__ev_child *ch,
- int sig);
-
-/* Useable (only) in the child to once more make the ctx useable for
- * xenstore operations. logs failure in the form "what: <error
- * message>". */
-_hidden int libxl__ev_child_xenstore_reopen(libxl__gc *gc, const char *what);
-
-
-/*
- * Other event-handling support provided by the libxl event core to
- * the rest of libxl.
- */
-
-_hidden void libxl__event_occurred(libxl__egc*, libxl_event *event);
- /* Arranges to notify the application that the event has occurred.
- * event should be suitable for passing to libxl_event_free. */
-
-_hidden libxl_event *libxl__event_new(libxl__egc*, libxl_event_type,
- uint32_t domid,
- libxl_ev_user for_user);
- /* Convenience function.
- * Allocates a new libxl_event, fills in domid and type.
- * Cannot fail. */
-
-#define NEW_EVENT(egc, type, domid, user) \
- libxl__event_new((egc), LIBXL_EVENT_TYPE_##type, (domid), (user))
- /* Convenience macro. */
-
-/*
- * In general, call this via the macro LIBXL__EVENT_DISASTER.
- *
- * Event-generating functions, or ao machinery, may call this if they
- * might have wanted to generate an event (either an internal one ie a
- * libxl__ev_FOO_callback or an application event), but are prevented
- * from doing so due to eg lack of memory.
- *
- * NB that this function may return and the caller isn't supposed to
- * then crash, although it may fail (and henceforth leave things in a
- * state where many or all calls fail).
- */
-_hidden void libxl__event_disaster(libxl__gc*, const char *msg, int errnoval,
- libxl_event_type type /* may be 0 */,
- const char *file, int line,
- const char *func);
-#define LIBXL__EVENT_DISASTER(gc, msg, errnoval, type) \
- libxl__event_disaster(gc, msg, errnoval, type, __FILE__,__LINE__,__func__)
-
-
-/* Fills in, or disposes of, the resources held by, a poller whose
- * space the caller has allocated. ctx must be locked. */
-_hidden int libxl__poller_init(libxl__gc *gc, libxl__poller *p);
-_hidden void libxl__poller_dispose(libxl__poller *p);
-
-/* Obtain a fresh poller from malloc or the idle list, and put it
- * away again afterwards. _get can fail, returning NULL.
- * ctx must be locked. */
-_hidden libxl__poller *libxl__poller_get(libxl__gc *gc);
-_hidden void libxl__poller_put(libxl_ctx*, libxl__poller *p /* may be NULL */);
-
-/* Notifies whoever is polling using p that they should wake up.
- * ctx must be locked. */
-_hidden void libxl__poller_wakeup(libxl__gc *egc, libxl__poller *p);
-
-/* Internal to fork and child reaping machinery */
-extern const libxl_childproc_hooks libxl__childproc_default_hooks;
-int libxl__sigchld_needed(libxl__gc*); /* non-reentrant idempotent, logs errs */
-void libxl__sigchld_notneeded(libxl__gc*); /* non-reentrant idempotent */
-void libxl__sigchld_check_stale_handler(void);
-int libxl__self_pipe_wakeup(int fd); /* returns 0 or -1 setting errno */
-int libxl__self_pipe_eatall(int fd); /* returns 0 or -1 setting errno */
-
-
-_hidden int libxl__atfork_init(libxl_ctx *ctx);
-
-
-/* File references */
-typedef struct {
- /*
- * Path is always set if the file reference is valid. However if
- * mapped is true then the actual file may already be unlinked.
- */
- const char * path;
- int mapped;
- void * data;
- size_t size;
-} libxl__file_reference;
-_hidden int libxl__file_reference_map(libxl__file_reference *f);
-_hidden int libxl__file_reference_unmap(libxl__file_reference *f);
-
-/* from xl_dom */
-_hidden libxl_domain_type libxl__domain_type(libxl__gc *gc, uint32_t domid);
-_hidden int libxl__domain_cpupool(libxl__gc *gc, uint32_t domid);
-_hidden libxl_scheduler libxl__domain_scheduler(libxl__gc *gc, uint32_t domid);
-_hidden int libxl__sched_set_params(libxl__gc *gc, uint32_t domid,
- libxl_domain_sched_params *scparams);
-_hidden int libxl__grant_vga_iomem_permission(libxl__gc *gc, const uint32_t domid,
- libxl_domain_config *const d_config);
-
-typedef struct {
- uint32_t store_port;
- uint32_t store_domid;
- unsigned long store_mfn;
-
- uint32_t console_port;
- uint32_t console_domid;
- unsigned long console_mfn;
- char *console_tty;
-
- char *saved_state;
- int dm_monitor_fd;
-
- libxl__file_reference pv_kernel;
- libxl__file_reference pv_ramdisk;
- const char *shim_path;
- const char *shim_cmdline;
- const char *pv_cmdline;
-
- /*
- * dm_runas: If set, pass qemu the `-runas` parameter with this
- * string as an argument
- * dm_kill_uid: If set, the devicemodel should be killed by
- * destroying all processes with this uid.
- */
- char *dm_runas, *dm_kill_uid;
-
- xen_vmemrange_t *vmemranges;
- uint32_t num_vmemranges;
-
- xen_pfn_t vuart_gfn;
- evtchn_port_t vuart_port;
-
- /* ARM only to deal with broken firmware */
- uint32_t clock_frequency;
-
- /* Whether this domain is being migrated/restored, or booting fresh. Only
- * applicable to the primary domain, not support domains (e.g. stub QEMU). */
- bool restore;
-} libxl__domain_build_state;
-
-_hidden void libxl__domain_build_state_init(libxl__domain_build_state *s);
-_hidden void libxl__domain_build_state_dispose(libxl__domain_build_state *s);
-
-_hidden int libxl__build_pre(libxl__gc *gc, uint32_t domid,
- libxl_domain_config * const d_config,
- libxl__domain_build_state *state);
-_hidden int libxl__build_post(libxl__gc *gc, uint32_t domid,
- libxl_domain_build_info *info, libxl__domain_build_state *state,
- char **vms_ents, char **local_ents);
-
-_hidden int libxl__build_pv(libxl__gc *gc, uint32_t domid,
- libxl_domain_config *const d_config, libxl__domain_build_state *state);
-_hidden int libxl__build_hvm(libxl__gc *gc, uint32_t domid,
- libxl_domain_config *d_config,
- libxl__domain_build_state *state);
-
-_hidden int libxl__qemu_traditional_cmd(libxl__gc *gc, uint32_t domid,
- const char *cmd);
-_hidden int libxl__domain_rename(libxl__gc *gc, uint32_t domid,
- const char *old_name, const char *new_name,
- xs_transaction_t trans);
-
-/* Deprecated, use libxl__dm_resume instead. */
-_hidden int libxl__domain_resume_device_model_deprecated(libxl__gc *gc, uint32_t domid);
-
-_hidden const char *libxl__userdata_path(libxl__gc *gc, uint32_t domid,
- const char *userdata_userid,
- const char *wh);
-_hidden void libxl__userdata_destroyall(libxl__gc *gc, uint32_t domid);
-/* Caller must hold userdata store lock before calling
- * libxl__userdata_{retrieve,store}
- * See libxl__{un,}lock_domain_userdata.
- */
-_hidden int libxl__userdata_retrieve(libxl__gc *gc, uint32_t domid,
- const char *userdata_userid,
- uint8_t **data_r, int *datalen_r);
-_hidden int libxl__userdata_store(libxl__gc *gc, uint32_t domid,
- const char *userdata_userid,
- const uint8_t *data, int datalen);
-
-/* Deprecated, use libxl__domain_resume instead */
-_hidden int libxl__domain_resume_deprecated(libxl__gc *gc, uint32_t domid,
- int suspend_cancel);
-/* Deprecated, use libxl__domain_unpause instead */
-_hidden int libxl__domain_unpause_deprecated(libxl__gc *,
- libxl_domid domid);
-
-/* Call libxl__dm_resume_init() and fill the first few fields,
- * then call one of libxl__domain_resume / libxl__domain_unpause
- * or directly libxl__dm_resume if only the device model needs to be
- * "resumed". */
-struct libxl__dm_resume_state {
- /* caller must fill these in, and they must all remain valid */
- libxl__ao *ao;
- libxl_domid domid;
- void (*callback)(libxl__egc *, libxl__dm_resume_state *, int rc);
-
- /* private to libxl__domain_resume and libxl__domain_unpause */
- void (*dm_resumed_callback)(libxl__egc *,
- libxl__dm_resume_state *, int rc);
- /* private to libxl__domain_resume */
- bool suspend_cancel;
-
- /* private to libxl__dm_resume */
- libxl__ev_qmp qmp;
- libxl__ev_time time;
- libxl__ev_xswatch watch;
-};
-_hidden void libxl__dm_resume(libxl__egc *egc,
- libxl__dm_resume_state *dmrs);
-_hidden void libxl__domain_resume(libxl__egc *egc,
- libxl__dm_resume_state *dmrs,
- bool suspend_cancel);
-_hidden void libxl__domain_unpause(libxl__egc *,
- libxl__dm_resume_state *dmrs);
-
-/* returns 0 or 1, or a libxl error code */
-_hidden int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid);
-
-_hidden const char *libxl__domain_pvcontrol_xspath(libxl__gc*, uint32_t domid);
-_hidden char * libxl__domain_pvcontrol_read(libxl__gc *gc,
- xs_transaction_t t, uint32_t domid);
-
-/* from xl_device */
-_hidden char *libxl__device_disk_string_of_backend(libxl_disk_backend backend);
-_hidden char *libxl__device_disk_string_of_format(libxl_disk_format format);
-_hidden const char *libxl__qemu_disk_format_string(libxl_disk_format format);
-_hidden int libxl__device_disk_set_backend(libxl__gc*, libxl_device_disk*);
-
-_hidden int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor);
-_hidden int libxl__device_disk_dev_number(const char *virtpath,
- int *pdisk, int *ppartition);
-_hidden char *libxl__devid_to_vdev(libxl__gc *gc, int devid);
-
-_hidden int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
- libxl__device_console *console,
- libxl__domain_build_state *state,
- libxl__device *device);
-_hidden int libxl__device_vuart_add(libxl__gc *gc, uint32_t domid,
- libxl__device_console *console,
- libxl__domain_build_state *state);
-
-/* Returns 1 if device exists, 0 if not, ERROR_* (<0) on error. */
-_hidden int libxl__device_exists(libxl__gc *gc, xs_transaction_t t,
- libxl__device *device);
-_hidden int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t,
- libxl__device *device, char **bents, char **fents, char **ro_fents);
-_hidden char *libxl__domain_device_frontend_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
- libxl__device_kind device_kind);
-_hidden char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device);
-_hidden char *libxl__domain_device_backend_path(libxl__gc *gc, uint32_t backend_domid,
- uint32_t domid, uint32_t devid,
- libxl__device_kind device_kind);
-_hidden char *libxl__device_libxl_path(libxl__gc *gc, libxl__device *device);
-_hidden char *libxl__domain_device_libxl_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
- libxl__device_kind device_kind);
-_hidden int libxl__parse_backend_path(libxl__gc *gc, const char *path,
- libxl__device *dev);
-_hidden int libxl__console_tty_path(libxl__gc *gc, uint32_t domid, int cons_num,
- libxl_console_type type, char **tty_path);
-_hidden int libxl__device_destroy(libxl__gc *gc, libxl__device *dev);
-_hidden int libxl__wait_for_backend(libxl__gc *gc, const char *be_path,
- const char *state);
-_hidden int libxl__nic_type(libxl__gc *gc, libxl__device *dev,
- libxl_nic_type *nictype);
-_hidden int libxl__init_console_from_channel(libxl__gc *gc,
- libxl__device_console *console,
- int dev_num,
- libxl_device_channel *channel);
-_hidden int libxl__device_nextid(libxl__gc *gc, uint32_t domid,
- libxl__device_kind device);
-_hidden int libxl__resolve_domid(libxl__gc *gc, const char *name,
- uint32_t *domid);
-
-/*
- * For each aggregate type which can be used as an input we provide:
- *
- * int libxl__<type>_setdefault(gc, <type> *p):
- *
- * Idempotently sets any members of "p" which is currently set to
- * a special value indicating that the defaults should be used
- * (per libxl_<type>_init) to a specific value.
- *
- * All libxl API functions are expected to have arranged for this
- * to be called before using any values within these structures.
- */
-_hidden int libxl__domain_config_setdefault(libxl__gc *gc,
- libxl_domain_config *d_config,
- uint32_t domid /* logging only */);
-_hidden int libxl__domain_create_info_setdefault(libxl__gc *gc,
- libxl_domain_create_info *c_info,
- const libxl_physinfo *info);
-_hidden int libxl__domain_build_info_setdefault(libxl__gc *gc,
- libxl_domain_build_info *b_info);
-_hidden void libxl__rdm_setdefault(libxl__gc *gc,
- libxl_domain_build_info *b_info);
-
-_hidden int libxl__domain_need_memory_calculate(libxl__gc *gc,
- libxl_domain_build_info *b_info,
- uint64_t *need_memkb);
-
-_hidden const char *libxl__device_nic_devname(libxl__gc *gc,
- uint32_t domid,
- uint32_t devid,
- libxl_nic_type type);
-
-_hidden int libxl__get_domid(libxl__gc *gc, uint32_t *domid);
-
-/*----- xswait: wait for a xenstore node to be suitable -----*/
-
-typedef struct libxl__xswait_state libxl__xswait_state;
-
-/*
- * rc describes the circumstances of this callback:
- *
- * rc==0
- *
- * The xenstore path (may have) changed. It has been read for
- * you. The result is in data (allocated from the ao gc).
- * data may be NULL, which means that the xenstore read gave
- * ENOENT.
- *
- * If you are satisfied, you MUST call libxl__xswait_stop.
- * Otherwise, xswait will continue waiting and watching and
- * will call you back later.
- *
- * rc==ERROR_TIMEDOUT, rc==ERROR_ABORTED
- *
- * The specified timeout was reached.
- * This has NOT been logged (except to the debug log).
- * xswait will not continue (but calling libxl__xswait_stop is OK).
- *
- * rc!=0, !=ERROR_TIMEDOUT, !=ERROR_ABORTED
- *
- * Some other error occurred.
- * This HAS been logged.
- * xswait will not continue (but calling libxl__xswait_stop is OK).
- *
- * xswait.path may start with with '@', in which case no read is done
- * and the callback will always get data==0.
- */
-typedef void libxl__xswait_callback(libxl__egc *egc,
- libxl__xswait_state *xswa, int rc, const char *data);
-
-struct libxl__xswait_state {
- /* caller must fill these in, and they must all remain valid */
- libxl__ao *ao;
- const char *what; /* for error msgs: noun phrase, what we're waiting for */
- const char *path;
- int timeout_ms; /* as for poll(2) */
- libxl__xswait_callback *callback;
- /* remaining fields are private to xswait */
- libxl__ev_time time_ev;
- libxl__ev_xswatch watch_ev;
-};
-
-void libxl__xswait_init(libxl__xswait_state*);
-void libxl__xswait_stop(libxl__gc*, libxl__xswait_state*); /*idempotent*/
-bool libxl__xswait_inuse(const libxl__xswait_state *ss);
-
-int libxl__xswait_start(libxl__gc*, libxl__xswait_state*);
-
-/*
- * libxl__ev_devstate - waits a given time for a device to
- * reach a given state. Follows the libxl_ev_* conventions.
- * Will generate only one event, and after that is automatically
- * cancelled.
- */
-typedef struct libxl__ev_devstate libxl__ev_devstate;
-typedef void libxl__ev_devstate_callback(libxl__egc *egc, libxl__ev_devstate*,
- int rc);
- /* rc will be 0, ERROR_TIMEDOUT, ERROR_ABORTED, ERROR_INVAL
- * (meaning path was removed), or ERROR_FAIL if other stuff went
- * wrong (in which latter case, logged) */
-
-struct libxl__ev_devstate {
- /* read-only for caller, who may read only when waiting: */
- int wanted;
- libxl__ev_devstate_callback *callback;
- /* as for the remainder, read-only public parts may also be
- * read by the caller (notably, watch.path), but only when waiting: */
- libxl__xswait_state w;
-};
-
-static inline void libxl__ev_devstate_init(libxl__ev_devstate *ds)
-{
- libxl__xswait_init(&ds->w);
-}
-
-static inline void libxl__ev_devstate_cancel(libxl__gc *gc,
- libxl__ev_devstate *ds)
-{
- libxl__xswait_stop(gc,&ds->w);
-}
-
-_hidden int libxl__ev_devstate_wait(libxl__ao *ao, libxl__ev_devstate *ds,
- libxl__ev_devstate_callback cb,
- const char *state_path,
- int state, int milliseconds);
-
-/*
- * libxl__ev_domaindeathcheck_register - arranges to call back (once)
- * if the domain is destroyed. If the domain dies, we log a message
- * of the form "<what>: <explanation of the situation, including the domid>".
- */
-
-typedef struct libxl__domaindeathcheck libxl__domaindeathcheck;
-typedef void libxl___domaindeathcheck_callback(libxl__egc *egc,
- libxl__domaindeathcheck*,
- int rc /* DESTROYED or ABORTED */);
-
-struct libxl__domaindeathcheck {
- /* must be filled in by caller, and remain valid: */
- const char *what;
- uint32_t domid;
- libxl___domaindeathcheck_callback *callback;
- /* private */
- libxl__ao_abortable abrt;
- libxl__ev_xswatch watch;
-};
-
-_hidden int libxl__domaindeathcheck_start(libxl__ao *ao,
- libxl__domaindeathcheck *dc);
-
-void libxl__domaindeathcheck_init(libxl__domaindeathcheck *dc);
-void libxl__domaindeathcheck_stop(libxl__gc *gc, libxl__domaindeathcheck *dc);
-
-
-/*
- * libxl__try_phy_backend - Check if there's support for the passed
- * type of file using the PHY backend
- * st_mode: mode_t of the file, as returned by stat function
- *
- * Returns 1 on success, and 0 if not suitable for phy backend.
- */
-_hidden int libxl__try_phy_backend(mode_t st_mode);
-
-
-_hidden char *libxl__devid_to_localdev(libxl__gc *gc, int devid);
-
-_hidden int libxl__pci_numdevs(libxl__gc *gc);
-_hidden int libxl__pci_topology_init(libxl__gc *gc,
- physdev_pci_device_t *devs,
- int num_devs);
-
-/* from libxl_pci */
-
-_hidden void libxl__device_pci_add(libxl__egc *egc, uint32_t domid,
- libxl_device_pci *pcidev, bool starting,
- libxl__ao_device *aodev);
-_hidden void libxl__device_pci_destroy_all(libxl__egc *egc, uint32_t domid,
- libxl__multidev *);
-_hidden int libxl__device_pci_setdefault(libxl__gc *gc, uint32_t domid,
- libxl_device_pci *pci, bool hotplug);
-_hidden bool libxl__is_igd_vga_passthru(libxl__gc *gc,
- const libxl_domain_config *d_config);
-
-/* from libxl_dtdev */
-
-_hidden int libxl__device_dt_add(libxl__gc *gc, uint32_t domid,
- const libxl_device_dtdev *dtdev);
-
-/*
- *----- spawn -----
- *
- * Higher-level double-fork and separate detach eg as for device models
- *
- * Each libxl__spawn_state is in one of these states
- * Undefined, Idle, Attached, Detaching
- */
-
-typedef struct libxl__obsolete_spawn_starting libxl__spawn_starting;
-/* this type is never defined, so no objects of this type exist
- * fixme-ao This should go away completely. */
-
-typedef struct libxl__spawn_state libxl__spawn_state;
-
-/* Clears out a spawn state; idempotent. */
-_hidden void libxl__spawn_init(libxl__spawn_state*);
-
-/*
- * libxl__spawn_spawn - Create a new process which will become daemonic
- * Forks twice, to allow the child to detach entirely from the parent.
- *
- * We call the two generated processes the "middle child" (result of
- * the first fork) and the "inner child" (result of the second fork
- * which takes place in the middle child).
- *
- * The inner child must soon exit or exec. It must also soon exit or
- * notify the parent of its successful startup by writing to the
- * xenstore path xspath OR via other means that the parent will have
- * to set up.
- *
- * The user (in the parent) will be called back (confirm_cb) every
- * time that xenstore path is modified.
- *
- * In both children, the ctx is not fully usable: gc and logging
- * operations are OK, but operations on Xen and xenstore are not.
- * (The restrictions are the same as those which apply to children
- * made with libxl__ev_child_fork.)
- *
- * midproc_cb will be called in the middle child, with the pid of the
- * inner child; this could for example record the pid. midproc_cb
- * should be fast, and should return. It will be called (reentrantly)
- * within libxl__spawn_init.
- *
- * failure_cb will be called in the parent on failure of the
- * intermediate or final child; an error message will have been
- * logged.
- *
- * confirm_cb, failure_cb and detached_cb will not be called
- * reentrantly from within libxl__spawn_spawn.
- *
- * what: string describing the spawned process, used for logging
- *
- * Logs errors. A copy of "what" is taken.
- * Return values:
- * < 0 error, *spawn is now Idle and need not be detached
- * +1 caller is the parent, *spawn is Attached and must be detached
- * 0 caller is now the inner child, should probably call libxl__exec
- *
- * The spawn state must be Undefined or Idle on entry.
- */
-_hidden int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *spawn);
-
-/*
- * libxl__spawn_request_detach - Detaches the daemonic child.
- *
- * Works by killing the intermediate process from spawn_spawn.
- * After this function returns, failures of either child are no
- * longer reported via failure_cb.
- *
- * This is not synchronous: there will be a further callback when
- * the detach is complete.
- *
- * If called before the inner child has been created, this may prevent
- * it from running at all. Thus this should be called only when the
- * inner child has notified that it is ready. Normally it will be
- * called from within confirm_cb.
- *
- * Logs errors.
- *
- * The spawn state must be Attached entry and will be Detaching
- * on return.
- */
-_hidden void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state*);
-
-/*
- * libxl__spawn_initiate_failure - Propagate failure from the caller to the
- * callee.
- *
- * Works by killing the intermediate process from spawn_spawn.
- * After this function returns, a failure will be reported.
- *
- * This is not synchronous: there will be a further callback when
- * the detach is complete.
- *
- * Caller must have logged a failure reason.
- *
- * The spawn state must be Attached on entry and will remain Attached. It
- * is possible for a spawn to fail for multiple reasons, for example
- * call(s) to libxl__spawn_initiate_failure and also for some other reason.
- * In that case the first rc value from any source will take precedence.
- */
-_hidden void libxl__spawn_initiate_failure(libxl__egc *egc,
- libxl__spawn_state *ss, int rc);
-
-/*
- * If successful, this should return 0.
- *
- * Otherwise it should return a signal number, which will be
- * sent to the inner child; the overall spawn will then fail.
- */
-typedef int /* signal number */
-libxl__spawn_midproc_cb(libxl__gc*, libxl__spawn_state*, pid_t inner);
-
-/*
- * Called if the spawn failed. The reason will have been logged.
- * The spawn state will be Idle on entry to the callback (and
- * it may be reused immediately if desired).
- */
-typedef void libxl__spawn_failure_cb(libxl__egc*, libxl__spawn_state*,
- int rc);
-
-/*
- * Called when the xspath watch triggers. xspath will have been read
- * and the result placed in xsdata; if that failed because the key
- * didn't exist, xspath==0. (If it failed for some other reason,
- * the spawn machinery calls failure_cb instead.)
- *
- * If the child has indicated its successful startup, or a failure
- * has occurred, this should call libxl__spawn_detach.
- *
- * If the child is still starting up, should simply return, doing
- * nothing.
- *
- * The spawn state will be Active on entry to the callback; there
- * are no restrictions on the state on return; it may even have
- * been detached and reused.
- */
-typedef void libxl__spawn_confirm_cb(libxl__egc*, libxl__spawn_state*,
- const char *xsdata);
-
-/*
- * Called when the detach (requested by libxl__spawn_initiate_detach) has
- * completed. On entry to the callback the spawn state is Idle.
- */
-typedef void libxl__spawn_detached_cb(libxl__egc*, libxl__spawn_state*);
-
-struct libxl__spawn_state {
- /* must be filled in by user and remain valid */
- libxl__ao *ao;
- const char *what;
- const char *xspath;
- const char *pidpath; /* only used by libxl__spawn_midproc_record_pid */
- int timeout_ms; /* -1 means forever */
- libxl__spawn_midproc_cb *midproc_cb;
- libxl__spawn_failure_cb *failure_cb;
- libxl__spawn_confirm_cb *confirm_cb;
- libxl__spawn_detached_cb *detached_cb;
-
- /* remaining fields are private to libxl_spawn_... */
- int detaching; /* we are in Detaching */
- int rc; /* might be non-0 whenever we are not Idle */
- libxl__ev_child mid; /* always in use whenever we are not Idle */
- libxl__xswait_state xswait;
-};
-
-static inline int libxl__spawn_inuse(const libxl__spawn_state *ss)
- { return libxl__ev_child_inuse(&ss->mid); }
-
-/*
- * libxl_spawner_record_pid - Record given pid in xenstore
- *
- * This function can be passed directly as an intermediate_hook to
- * libxl__spawn_spawn. On failure, returns the value SIGTERM.
- */
-_hidden int libxl__spawn_record_pid(libxl__gc*, libxl__spawn_state*,
- pid_t innerchild);
-
-/*
- * libxl__xenstore_child_wait_deprecated - Wait for daemonic child IPC
- *
- * This is a NOT function for waiting for ordinary child processes.
- * If you want to run (fork/exec/wait) subprocesses from libxl:
- * - Make your libxl entrypoint use the ao machinery
- * - Use libxl__ev_child_fork, and use the callback programming style
- *
- * This function is intended for interprocess communication with a
- * service process. If the service process does not respond quickly,
- * the whole caller may be blocked. Therefore this function is
- * deprecated. This function is currently used only by
- * libxl__wait_for_device_model_deprecated.
- *
- * gc: allocation pool
- * domid: guest to work with
- * timeout: how many seconds to wait for the state to appear
- * what: string describing the spawned process
- * path: path to the state file in xenstore
- * state: expected string to wait for in path (optional)
- * spawning: malloc'd pointer to libxl__spawn_starting (optional)
- * check_callback: (optional)
- * check_callback_userdata: data to pass to the callback function
- *
- * Returns 0 on success, and < 0 on error.
- *
- * This function waits the given timeout for the given path to appear
- * in xenstore, and optionally for state in path.
- * If path appears and state matches, check_callback is called.
- * If check_callback returns > 0, waiting for path or state continues.
- * Otherwise libxl__xenstore_child_wait_deprecated returns.
- */
-_hidden int libxl__xenstore_child_wait_deprecated(libxl__gc *gc,
- uint32_t domid,
- uint32_t timeout, char *what,
- char *path, char *state,
- libxl__spawn_starting *spawning,
- int (*check_callback)(libxl__gc *gc,
- uint32_t domid,
- const char *state,
- void *userdata),
- void *check_callback_userdata);
-
-
- /* low-level stuff, for synchronous subprocesses etc. */
-
-/*
- * env should be passed using the following format,
- *
- * env[0]: name of env variable
- * env[1]: value of env variable
- * env[n]: ...
- *
- * So it efectively becomes something like:
- * export env[n]=env[n+1]
- * (where n%2 = 0)
- *
- * The last entry of the array always has to be NULL.
- *
- * stdinfd, stdoutfd, stderrfd will be dup2'd onto the corresponding
- * fd in the child, if they are not -1. The original copy of the
- * descriptor will be closed in the child (unless it's 0, 1 or 2
- * ie the source descriptor is itself stdin, stdout or stderr).
- *
- * Logs errors, never returns.
- */
-_hidden void libxl__exec(libxl__gc *gc, int stdinfd, int stdoutfd,
- int stderrfd, const char *arg0, char *const args[],
- char *const env[]) __attribute__((noreturn));
-
-/* from xl_create */
-
- /* on entry, libxl_domid_valid_guest(domid) must be false;
- * on exit (even error exit), domid may be valid and refer to a domain */
-_hidden int libxl__domain_make(libxl__gc *gc,
- libxl_domain_config *d_config,
- libxl__domain_build_state *state,
- uint32_t *domid, bool soft_reset);
-
-_hidden int libxl__domain_build(libxl__gc *gc,
- libxl_domain_config *d_config,
- uint32_t domid,
- libxl__domain_build_state *state);
-
-/* for device model creation */
-_hidden const char *libxl__domain_device_model(libxl__gc *gc,
- const libxl_domain_build_info *info);
-_hidden int libxl__need_xenpv_qemu(libxl__gc *gc,
- libxl_domain_config *d_config);
-_hidden bool libxl__query_qemu_backend(libxl__gc *gc,
- uint32_t domid,
- uint32_t backend_id,
- const char *type,
- bool def);
-_hidden int libxl__dm_active(libxl__gc *gc, uint32_t domid);
-_hidden int libxl__dm_check_start(libxl__gc *gc,
- libxl_domain_config *d_config,
- uint32_t domid);
-
-/*
- * This function will fix reserved device memory conflict
- * according to user's configuration.
- */
-_hidden int libxl__domain_device_construct_rdm(libxl__gc *gc,
- libxl_domain_config *d_config,
- uint64_t rdm_mem_guard,
- struct xc_dom_image *dom);
-
-/*
- * This function will cause the whole libxl process to hang
- * if the device model does not respond. It is deprecated.
- *
- * Instead of calling this function:
- * - Make your libxl entrypoint use the ao machinery
- * - Use libxl__ev_xswatch_register, and use the callback programming
- * style
- */
-_hidden int libxl__wait_for_device_model_deprecated(libxl__gc *gc,
- uint32_t domid, char *state,
- libxl__spawn_starting *spawning
- /* NULL allowed */,
- int (*check_callback)(libxl__gc *gc,
- uint32_t domid,
- const char *state,
- void *userdata),
- void *check_callback_userdata);
-
-_hidden const libxl_vnc_info *libxl__dm_vnc(const libxl_domain_config *g_cfg);
-
-_hidden char *libxl__abs_path(libxl__gc *gc, const char *s, const char *path);
-
-#define LIBXL__LOG_DEBUG XTL_DEBUG
-#define LIBXL__LOG_VERBOSE XTL_VERBOSE
-#define LIBXL__LOG_INFO XTL_INFO
-#define LIBXL__LOG_WARNING XTL_WARN
-#define LIBXL__LOG_ERROR XTL_ERROR
-
-_hidden char *libxl__domid_to_name(libxl__gc *gc, uint32_t domid);
-_hidden char *libxl__cpupoolid_to_name(libxl__gc *gc, uint32_t poolid);
-
-_hidden int libxl__enum_from_string(const libxl_enum_string_table *t,
- const char *s, int *e) NN(2);
-
-_hidden yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str);
-
-_hidden yajl_gen_status libxl__string_gen_json(yajl_gen hand, const char *p);
-
-typedef yajl_gen_status (*libxl__gen_json_callback)(yajl_gen hand, void *);
-_hidden char *libxl__object_to_json(libxl_ctx *ctx, const char *type,
- libxl__gen_json_callback gen, void *p);
-
-_hidden void libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool retore,
- libxl_domain_build_info *info);
-
-/* Calls poll() again - useful to check whether a signaled condition
- * is still true. Cannot fail. Returns currently-true revents. */
-_hidden short libxl__fd_poll_recheck(libxl__egc *egc, int fd, short events);
-
-_hidden char *libxl__uuid2string(libxl__gc *gc, const libxl_uuid uuid);
-
-struct libxl__xen_console_reader {
- char *buffer;
- unsigned int size;
- unsigned int count;
- unsigned int clear;
- unsigned int incremental;
- unsigned int index;
-};
-
-/* parse the string @s as a sequence of 6 colon separated bytes in to @mac */
-_hidden int libxl__parse_mac(const char *s, libxl_mac mac);
-/* compare mac address @a and @b. 0 if the same, -ve if a<b and +ve if a>b */
-_hidden int libxl__compare_macs(libxl_mac *a, libxl_mac *b);
-/* return true if mac address is all zero (the default value) */
-_hidden int libxl__mac_is_default(libxl_mac *mac);
-/* init a recursive mutex */
-_hidden int libxl__init_recursive_mutex(libxl_ctx *ctx, pthread_mutex_t *lock);
-
-_hidden int libxl__gettimeofday(libxl__gc *gc, struct timeval *now_r);
-
-#define STRINGIFY(x) #x
-#define TOSTRING(x) STRINGIFY(x)
-
-/* from libxl_qmp */
-typedef struct libxl__qmp_handler libxl__qmp_handler;
-
-/* Initialise and connect to the QMP socket.
- * Return an handler or NULL if there is an error
- */
-_hidden libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc,
- uint32_t domid);
-/* Resume QEMU. */
-_hidden int libxl__qmp_resume(libxl__gc *gc, int domid);
-/* Load current QEMU state from file. */
-_hidden int libxl__qmp_restore(libxl__gc *gc, int domid, const char *filename);
-/* Start NBD server */
-_hidden int libxl__qmp_nbd_server_start(libxl__gc *gc, int domid,
- const char *host, const char *port);
-/* Add a disk to NBD server */
-_hidden int libxl__qmp_nbd_server_add(libxl__gc *gc, int domid,
- const char *disk);
-/* Start replication */
-_hidden int libxl__qmp_start_replication(libxl__gc *gc, int domid,
- bool primary);
-/* Get replication error that occurs when the vm is running */
-_hidden int libxl__qmp_query_xen_replication_status(libxl__gc *gc, int domid);
-/* Do checkpoint */
-_hidden int libxl__qmp_colo_do_checkpoint(libxl__gc *gc, int domid);
-/* Stop replication */
-_hidden int libxl__qmp_stop_replication(libxl__gc *gc, int domid,
- bool primary);
-/* Stop NBD server */
-_hidden int libxl__qmp_nbd_server_stop(libxl__gc *gc, int domid);
-/* Add or remove a child to/from quorum */
-_hidden int libxl__qmp_x_blockdev_change(libxl__gc *gc, int domid,
- const char *parant,
- const char *child, const char *node);
-/* run a hmp command in qmp mode */
-_hidden int libxl__qmp_hmp(libxl__gc *gc, int domid, const char *command_line,
- char **out);
-/* close and free the QMP handler */
-_hidden void libxl__qmp_close(libxl__qmp_handler *qmp);
-/* remove the socket file, if the file has already been removed,
- * nothing happen */
-_hidden void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid);
-
-/* `data' should contain a byte to send.
- * When dealing with a non-blocking fd, it returns
- * ERROR_NOT_READY on EWOULDBLOCK
- * logs on other failures. */
-int libxl__sendmsg_fds(libxl__gc *gc, int carrier,
- const char data,
- int nfds, const int fds[], const char *what);
-
-/* Insists on receiving exactly nfds and datalen. On failure, logs
- * and leaves *fds untouched. */
-int libxl__recvmsg_fds(libxl__gc *gc, int carrier,
- void *databuf, size_t datalen,
- int nfds, int fds[], const char *what);
-
-/* from libxl_json */
-#include <yajl/yajl_gen.h>
-
-_hidden yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str);
-_hidden yajl_gen_status libxl__yajl_gen_enum(yajl_gen hand, const char *str);
-
-typedef enum {
- JSON_NULL = (1 << 0),
- JSON_BOOL = (1 << 1),
- JSON_INTEGER = (1 << 2),
- JSON_DOUBLE = (1 << 3),
- /* number is store in string, it's too big to be a long long or a double */
- JSON_NUMBER = (1 << 4),
- JSON_STRING = (1 << 5),
- JSON_MAP = (1 << 6),
- JSON_ARRAY = (1 << 7),
- JSON_ANY = 255 /* this is a mask of all values above, adjust as needed */
-} libxl__json_node_type;
-
-struct libxl__json_object {
- libxl__json_node_type type;
- union {
- bool b;
- long long i;
- double d;
- char *string;
- /* List of libxl__json_object */
- flexarray_t *array;
- /* List of libxl__json_map_node */
- flexarray_t *map;
- } u;
- struct libxl__json_object *parent;
-};
-
-typedef int (*libxl__json_parse_callback)(libxl__gc *gc,
- libxl__json_object *o,
- void *p);
-_hidden int libxl__object_from_json(libxl_ctx *ctx, const char *type,
- libxl__json_parse_callback parse,
- void *p,
- const char *s);
-
-typedef struct {
- char *map_key;
- libxl__json_object *obj;
-} libxl__json_map_node;
-
-static inline bool libxl__json_object_is_null(const libxl__json_object *o)
-{
- return o != NULL && o->type == JSON_NULL;
-}
-static inline bool libxl__json_object_is_bool(const libxl__json_object *o)
-{
- return o != NULL && o->type == JSON_BOOL;
-}
-static inline bool libxl__json_object_is_string(const libxl__json_object *o)
-{
- return o != NULL && o->type == JSON_STRING;
-}
-static inline bool libxl__json_object_is_integer(const libxl__json_object *o)
-{
- return o != NULL && o->type == JSON_INTEGER;
-}
-static inline bool libxl__json_object_is_double(const libxl__json_object *o)
-{
- return o != NULL && o->type == JSON_DOUBLE;
-}
-static inline bool libxl__json_object_is_number(const libxl__json_object *o)
-{
- return o != NULL && o->type == JSON_NUMBER;
-}
-static inline bool libxl__json_object_is_map(const libxl__json_object *o)
-{
- return o != NULL && o->type == JSON_MAP;
-}
-static inline bool libxl__json_object_is_array(const libxl__json_object *o)
-{
- return o != NULL && o->type == JSON_ARRAY;
-}
-
-/*
- * `o` may be NULL for all libxl__json_object_get_* functions.
- */
-
-static inline bool libxl__json_object_get_bool(const libxl__json_object *o)
-{
- if (libxl__json_object_is_bool(o))
- return o->u.b;
- else
- return false;
-}
-static inline
-const char *libxl__json_object_get_string(const libxl__json_object *o)
-{
- if (libxl__json_object_is_string(o))
- return o->u.string;
- else
- return NULL;
-}
-static inline
-const char *libxl__json_object_get_number(const libxl__json_object *o)
-{
- if (libxl__json_object_is_number(o))
- return o->u.string;
- else
- return NULL;
-}
-static inline
-flexarray_t *libxl__json_object_get_map(const libxl__json_object *o)
-{
- if (libxl__json_object_is_map(o))
- return o->u.map;
- else
- return NULL;
-}
-static inline
-flexarray_t *libxl__json_object_get_array(const libxl__json_object *o)
-{
- if (libxl__json_object_is_array(o))
- return o->u.array;
- else
- return NULL;
-}
-static inline long long libxl__json_object_get_integer(const libxl__json_object *o)
-{
- if (libxl__json_object_is_integer(o))
- return o->u.i;
- else
- return -1;
-}
-
-/*
- * `o` may be NULL for the following libxl__json_*_get functions.
- */
-
-_hidden libxl__json_object *libxl__json_array_get(const libxl__json_object *o,
- int i);
-_hidden
-libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o,
- int i);
-_hidden const libxl__json_object *libxl__json_map_get(const char *key,
- const libxl__json_object *o,
- libxl__json_node_type expected_type);
-
-/*
- * NOGC can be used with those json_object functions, but the
- * libxl__json_object* will need to be freed with libxl__json_object_free.
- */
-_hidden libxl__json_object *libxl__json_object_alloc(libxl__gc *gc_opt,
- libxl__json_node_type type);
-_hidden yajl_status libxl__json_object_to_yajl_gen(libxl__gc *gc_opt,
- yajl_gen hand,
- const libxl__json_object *param);
-_hidden void libxl__json_object_free(libxl__gc *gc_opt,
- libxl__json_object *obj);
-
-_hidden libxl__json_object *libxl__json_parse(libxl__gc *gc_opt, const char *s);
-
-/* `args` may be NULL */
-_hidden char *libxl__json_object_to_json(libxl__gc *gc,
- const libxl__json_object *args);
-/* Always return a valid string, but invalid json on error. */
-#define JSON(o) \
- (libxl__json_object_to_json(gc, (o)) ? : "<invalid-json-object>")
-
- /* Based on /local/domain/$domid/dm-version xenstore key
- * default is qemu xen traditional */
-_hidden int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid);
- /* Return the system-wide default device model */
-_hidden libxl_device_model_version libxl__default_device_model(libxl__gc *gc);
-
-static inline
-bool libxl__stubdomain_is_linux_running(libxl__gc *gc, uint32_t domid)
-{
- /* same logic as in libxl__stubdomain_is_linux */
- return libxl__device_model_version_running(gc, domid)
- == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
-}
-
-static inline
-bool libxl__stubdomain_is_linux(libxl_domain_build_info *b_info)
-{
- /* right now qemu-tranditional implies MiniOS stubdomain and qemu-xen
- * implies Linux stubdomain */
- return libxl_defbool_val(b_info->device_model_stubdomain) &&
- b_info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
-}
-
-#define DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, fmt, _a...) \
- libxl__sprintf(gc, "/local/domain/%u/device-model/%u" fmt, dm_domid, \
- domid, ##_a)
-
-/*
- * Calling context and GC for event-generating functions:
- *
- * These are for use by parts of libxl which directly or indirectly
- * call libxl__event_occurred. These contain a gc but also a list of
- * deferred events.
- *
- * You should never need to initialise an egc unless you are part of
- * the event machinery itself. Otherwise you will always be given an
- * egc if you need one. Even functions which generate specific kinds
- * of events don't need to - rather, they will be passed an egc into
- * their own callback function and should just use the one they're
- * given.
- *
- * Functions using LIBXL_INIT_EGC may *not* generally be called from
- * within libxl, because libxl__egc_cleanup may call back into the
- * application. This should be enforced by declaring all such
- * functions in libxl.h or libxl_event.h with
- * LIBXL_EXTERNAL_CALLERS_ONLY. You should in any case not find it
- * necessary to call egc-creators from within libxl.
- *
- * The callbacks must all take place with the ctx unlocked because
- * the application is entitled to reenter libxl from them. This
- * would be bad not because the lock is not recursive (it is) but
- * because the application might make blocking libxl calls which
- * would hold the lock unreasonably long.
- *
- * For the same reason libxl__egc_cleanup (or EGC_FREE) must be called
- * with the ctx *unlocked*. So the right pattern has the EGC_...
- * macro calls on the outside of the CTX_... ones.
- */
-
-/* useful for all functions which take an egc: */
-
-#define EGC_GC \
- libxl__gc *const gc __attribute__((unused)) = &egc->gc
-
-/* egc initialisation and destruction: */
-
-#define LIBXL_INIT_EGC(egc,ctx) do{ \
- LIBXL_INIT_GC((egc).gc,ctx); \
- LIBXL_TAILQ_INIT(&(egc).occurred_for_callback); \
- LIBXL_TAILQ_INIT(&(egc).aos_for_callback); \
- LIBXL_TAILQ_INIT(&(egc).aops_for_callback); \
- LIBXL_STAILQ_INIT(&(egc).ev_immediates); \
- } while(0)
-
-_hidden void libxl__egc_ao_cleanup_1_baton(libxl__gc *gc);
- /* Passes the baton for added osevents. See comment for
- * osevents_added in struct libxl__poller. */
-_hidden void libxl__egc_cleanup_2_ul_cb_gc(libxl__egc *egc);
- /* Frees memory allocated within this egc's gc, and and report all
- * occurred events via callback, if applicable. May reenter the
- * application; see restrictions above. The ctx must be UNLOCKED. */
-
-/* convenience macros: */
-
-#define EGC_INIT(ctx) \
- libxl__egc egc[1]; LIBXL_INIT_EGC(egc[0],ctx); \
- EGC_GC
-
-#define CTX_UNLOCK_EGC_FREE do{ \
- libxl__egc_ao_cleanup_1_baton(&egc->gc); \
- CTX_UNLOCK; \
- libxl__egc_cleanup_2_ul_cb_gc(egc); \
- }while(0)
-
-
-/*
- * Machinery for asynchronous operations ("ao")
- *
- * All "slow" functions (see below for the exact definition) need to
- * use the asynchronous operation ("ao") machinery. The function
- * should take a parameter const libxl_asyncop_how *ao_how and must
- * start with a call to AO_CREATE or equivalent. These functions MAY
- * NOT be called from inside libxl (regardless of what is passed for
- * ao_how), because they can cause reentrancy hazards due to
- * callbacks.
- *
- * For the same reason functions taking an ao_how may make themselves
- * an egc with EGC_INIT (and they will generally want to, to be able
- * to immediately complete an ao during its setup).
- *
- *
- * "Slow" functions includes any that might block on a guest or an
- * external script. More broadly, it includes any operations which
- * are sufficiently slow that an application might reasonably want to
- * initiate them, and then carry on doing something else, while the
- * operation completes. That is, a "fast" function must be fast
- * enough that we do not mind blocking all other management operations
- * on the same host while it completes.
- *
- * There are certain primitive functions which make a libxl operation
- * necessarily "slow" for API reasons. These are:
- * - awaiting xenstore watches (although read-modify-write xenstore
- * transactions are OK for fast functions)
- * - spawning subprocesses
- * - anything with a timeout
- *
- *
- * Lifecycle of an ao:
- *
- * - Created by libxl__ao_create (or the AO_CREATE convenience macro).
- *
- * - After creation, can be used by code which implements
- * the operation as follows:
- * - the ao's gc, for allocating memory for the lifetime
- * of the operation (possibly with the help of the AO_GC
- * macro to introduce the gc into scope)
- * - the ao itself may be passed about to sub-functions
- * so that they can stash it away etc.
- * - in particular, the ao pointer must be stashed in some
- * per-operation structure which is also passed as a user
- * pointer to the internal event generation request routines
- * libxl__evgen_FOO, so that at some point a CALLBACK will be
- * made when the operation is complete.
- * - if the operation provides progress reports, the aop_how(s)
- * must be copied into the per-operation structure using
- * libxl__ao_progress_gethow.
- *
- * - If the initiation is unsuccessful, the initiating function must
- * call libxl__ao_create_fail before unlocking and returning whatever
- * error code is appropriate (AO_CREATE_FAIL macro).
- *
- * If initiation is successful:
- *
- * - The initiating function must run libxl__ao_inprogress right
- * before unlocking and returning, and return whatever it returns.
- * This is best achieved with the AO_INPROGRESS macro.
- *
- * - If the operation supports progress reports, it may generate
- * suitable events with NEW_EVENT and report them with
- * libxl__ao_progress_report (with the ctx locked).
- *
- * - Eventually, some callback function, whose callback has been
- * requested directly or indirectly, should call libxl__ao_complete
- * (with the ctx locked, as it will generally already be in any
- * event callback function). This must happen exactly once for each
- * ao, as the last that happens with that ao.
- *
- * - However, it is permissible for the initiating function to call
- * libxl__ao_inprogress and/or libxl__ao_complete (directly or
- * indirectly), before it uses AO_INPROGRESS to return. (The ao
- * infrastructure will arrange to defer destruction of the ao, etc.,
- * until the proper time.) An initiating function should do this
- * if it takes a codepath which completes synchronously.
- *
- * - Conversely it is forbidden to call libxl__ao_complete in the
- * initiating function _after_ AO_INPROGRESS, because
- * libxl__ao_complete requires the ctx to be locked.
- *
- * - Note that during callback functions, two gcs are available:
- * - The one in egc, whose lifetime is only this callback
- * - The one in ao, whose lifetime is the asynchronous operation
- * Usually a callback function should use CONTAINER_OF to obtain its
- * own state structure, containing a pointer to the ao. It should
- * then obtain the ao and use the ao's gc; this is most easily done
- * using the convenience macro STATE_AO_GC.
- */
-
-#define AO_CREATE(ctx, domid, ao_how) \
- libxl__ctx_lock(ctx); \
- libxl__ao *ao = libxl__ao_create(ctx, domid, ao_how, \
- __FILE__, __LINE__, __func__); \
- if (!ao) { libxl__ctx_unlock(ctx); return ERROR_NOMEM; } \
- libxl__egc egc[1]; LIBXL_INIT_EGC(egc[0],ctx); \
- AO_GC;
-
-#define AO_INPROGRESS ({ \
- libxl_ctx *ao__ctx = libxl__gc_owner(&ao->gc); \
- /* __ao_inprogress will do egc..1_baton if needed */ \
- CTX_UNLOCK; \
- libxl__egc_cleanup_2_ul_cb_gc(egc); \
- CTX_LOCK; \
- int ao__rc = libxl__ao_inprogress(ao, \
- __FILE__, __LINE__, __func__); \
- libxl__ctx_unlock(ao__ctx); /* gc is now invalid */ \
- (ao__rc); \
- })
-
-#define AO_CREATE_FAIL(rc) ({ \
- libxl_ctx *ao__ctx = libxl__gc_owner(&ao->gc); \
- assert(rc); \
- libxl__ao_create_fail(ao); \
- libxl__egc_ao_cleanup_1_baton(&egc->gc); \
- libxl__ctx_unlock(ao__ctx); /* gc is now invalid */ \
- libxl__egc_cleanup_2_ul_cb_gc(egc); \
- (rc); \
- })
-
-
-/*
- * Given, in scope,
- * libxl__ao *ao;
- * produces, in scope,
- * libxl__gc *gc;
- */
-#define AO_GC \
- libxl__gc *const gc __attribute__((unused)) = &ao->gc
-
-/*
- * void STATE_AO_GC(libxl__ao *ao_spec);
- * // Produces, in scope:
- * libxl__ao *ao; // set from ao_spec
- * libxl__gc *gc;
- */
-#define STATE_AO_GC(op_ao) \
- libxl__ao *const ao = (op_ao); \
- libxl__gc *const gc __attribute__((unused)) = libxl__ao_inprogress_gc(ao)
-
-
-/* All of these MUST be called with the ctx locked.
- * libxl__ao_inprogress MUST be called with the ctx locked exactly once. */
-_hidden libxl__ao *libxl__ao_create(libxl_ctx*, uint32_t domid,
- const libxl_asyncop_how*,
- const char *file, int line, const char *func);
-_hidden int libxl__ao_inprogress(libxl__ao *ao,
- const char *file, int line, const char *func); /* temporarily unlocks */
-_hidden void libxl__ao_create_fail(libxl__ao *ao);
-_hidden void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc);
-_hidden libxl__gc *libxl__ao_inprogress_gc(libxl__ao *ao);
-
-/* Can be called at any time. Use is essential for any aop user. */
-_hidden void libxl__ao_progress_gethow(libxl_asyncprogress_how *in_state,
- const libxl_asyncprogress_how *from_app);
-
-/* Must be called with the ctx locked. Will fill in ev->for_user,
- * so caller need not do that. */
-_hidden void libxl__ao_progress_report(libxl__egc *egc, libxl__ao *ao,
- const libxl_asyncprogress_how *how, libxl_event *ev /* consumed */);
-
-/* For use by ao machinery ONLY */
-_hidden void libxl__ao__destroy(libxl_ctx*, libxl__ao *ao);
-_hidden void libxl__ao_complete_check_progress_reports(libxl__egc*, libxl__ao*);
-
-
-/*
- * Short-lived sub-ao, aka "nested ao".
- *
- * Some asynchronous operations are very long-running. Generally,
- * since an ao has a gc, any allocations made in that ao will live
- * until the ao is completed. When this is not desirable, these
- * functions may be used to manage a "sub-ao".
- *
- * The returned sub-ao is suitable for passing to gc-related functions
- * and macros such as libxl__ao_inprogress_gc, AO_GC, and STATE_AO_GC.
- *
- * It MUST NOT be used with AO_INPROGRESS, AO_CREATE_FAIL,
- * libxl__ao_complete, libxl__ao_progress_report, and so on.
- *
- * The caller must ensure that all of the sub-ao's are freed before
- * the parent is. Multiple levels of nesting are OK (although
- * hopefully they won't be necessary).
- */
-
-_hidden libxl__ao *libxl__nested_ao_create(libxl__ao *parent); /* cannot fail */
-_hidden void libxl__nested_ao_free(libxl__ao *child);
-
-
-/*
- * File descriptors and CLOEXEC
- */
-
-/*
- * For libxl functions which create file descriptors, at least one
- * of the following must be true:
- * (a) libxl does not care if copies of this open-file are inherited
- * by random children and might remain open indefinitely
- * (b) libxl must take extra care for the fd (the actual descriptor,
- * not the open-file) as below. We call this a "carefd".
- *
- * The rules for opening a carefd are:
- * (i) Before bringing any carefds into existence,
- * libxl code must call libxl__carefd_begin.
- * (ii) Then for each carefd brought into existence,
- * libxl code must call libxl__carefd_record
- * and remember the libxl__carefd_record*.
- * (iii) Then it must call libxl__carefd_unlock.
- * (iv) When in a child process the fd is to be passed across
- * exec by libxl, the libxl code must unset FD_CLOEXEC
- * on the fd eg by using libxl_fd_set_cloexec.
- * (v) Later, when the fd is to be closed in the same process,
- * libxl code must not call close. Instead, it must call
- * libxl__carefd_close.
- * Steps (ii) and (iii) can be combined by calling the convenience
- * function libxl__carefd_opened.
- */
-/* libxl__carefd_begin and _unlock (or _opened) must be called always
- * in pairs. They may be called with the CTX lock held. In between
- * _begin and _unlock, the following are prohibited:
- * - anything which might block
- * - any callbacks to the application
- * - nested calls to libxl__carefd_begin
- * - fork (libxl__fork)
- * In general nothing should be done before _unlock that could be done
- * afterwards.
- */
-
-_hidden void libxl__carefd_begin(void);
-_hidden void libxl__carefd_unlock(void);
-
-/* fd may be -1, in which case this returns a dummy libxl__fd_record
- * on which it _carefd_close is a no-op. Cannot fail. */
-_hidden libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd);
-
-/* Combines _record and _unlock in a single call. If fd==-1,
- * still does the unlock, but returns 0. */
-_hidden libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd);
-
-/* Works just like close(2). You may pass NULL, in which case it's
- * a successful no-op. */
-_hidden int libxl__carefd_close(libxl__carefd*);
-
-/* You may pass NULL in which case the answer is -1. */
-_hidden int libxl__carefd_fd(const libxl__carefd*);
-
-/* common paths */
-_hidden const char *libxl__private_bindir_path(void);
-_hidden const char *libxl__xenfirmwaredir_path(void);
-_hidden const char *libxl__xen_config_dir_path(void);
-_hidden const char *libxl__xen_script_dir_path(void);
-_hidden const char *libxl__lock_dir_path(void);
-_hidden const char *libxl__run_dir_path(void);
-_hidden const char *libxl__seabios_path(void);
-_hidden const char *libxl__ovmf_path(void);
-_hidden const char *libxl__ipxe_path(void);
-
-/*----- subprocess execution with timeout -----*/
-
-typedef struct libxl__async_exec_state libxl__async_exec_state;
-
-typedef void libxl__async_exec_callback(libxl__egc *egc,
- libxl__async_exec_state *aes, int rc, int status);
-/*
- * Meaning of status and rc:
- * rc==0, status==0 all went well
- * rc==0, status!=0 everything OK except child exited nonzero (logged)
- * rc!=0 something else went wrong (status is real
- * exit status; maybe reflecting SIGKILL, and
- * therefore not very interesting, if aes code
- * killed the child). Logged unless ABORTED.
- */
-
-struct libxl__async_exec_state {
- /* caller must fill these in */
- libxl__ao *ao;
- const char *what; /* for error msgs, what we're executing */
- int timeout_ms;
- libxl__async_exec_callback *callback;
- /* caller must fill in; as for libxl__exec */
- int stdfds[3];
- char **args; /* execution arguments */
- char **env; /* execution environment */
-
- /* private */
- libxl__ev_time time;
- libxl__ev_child child;
- int rc;
-};
-
-void libxl__async_exec_init(libxl__async_exec_state *aes);
-int libxl__async_exec_start(libxl__async_exec_state *aes);
-bool libxl__async_exec_inuse(const libxl__async_exec_state *aes);
-
-_hidden void libxl__kill(libxl__gc *gc, pid_t pid, int sig, const char *what);
-/* kill SIGHUP a pid stored in xenstore */
-_hidden int libxl__kill_xs_path(libxl__gc *gc, const char *xs_path_pid,
- const char *what);
-
-/*----- device addition/removal -----*/
-
-typedef void libxl__device_callback(libxl__egc*, libxl__ao_device*);
-
-/* This functions sets the necessary libxl__ao_device struct values to use
- * safely inside functions. It marks the operation as "active"
- * since we need to be sure that all device status structs are set
- * to active before start queueing events, or we might call
- * ao_complete before all devices had finished
- *
- * libxl__initiate_device_{remove/addition} should not be called without
- * calling libxl__prepare_ao_device first, since it initializes the private
- * fields of the struct libxl__ao_device to what this functions expect.
- *
- * Once _prepare has been called on a libxl__ao_device, it is safe to just
- * discard this struct, there's no need to call any destroy function.
- * _prepare can also be called multiple times with the same libxl__ao_device.
- *
- * But if any of the fields `backend_ds', `timeout', `xswait', `qmp' is
- * used by a caller of _prepare, the caller will have to arrange to clean
- * or dispose of them.
- */
-_hidden void libxl__prepare_ao_device(libxl__ao *ao, libxl__ao_device *aodev);
-
-/* generic callback for devices that only need to set ao_complete */
-_hidden void device_addrm_aocomplete(libxl__egc *egc, libxl__ao_device *aodev);
-
-typedef struct {
- enum {
- LIBXL__FORCE_AUTO, /* Re-execute with FORCE_ON if op times out */
- LIBXL__FORCE_ON,
- LIBXL__FORCE_OFF,
- } flag;
-} libxl__force;
-
-struct libxl__ao_device {
- /* filled in by user */
- libxl__ao *ao;
- libxl__device_action action;
- libxl__device *dev;
- libxl__force force;
- libxl__device_callback *callback;
- /* return value, zeroed by user on entry, is valid on callback */
- int rc;
- /* private for multidev */
- int active;
- libxl__multidev *multidev; /* reference to the containing multidev */
- /* private for add/remove implementation */
- libxl__ev_devstate backend_ds;
- /* Bodge for Qemu devices */
- libxl__ev_time timeout;
- /* xenstore watch for backend path of driver domains */
- libxl__xswait_state xswait;
- int num_exec;
- /* for calling hotplug scripts */
- libxl__async_exec_state aes;
- /* If we need to update JSON config */
- bool update_json;
- /* for asynchronous execution of synchronous-only syscalls etc. */
- libxl__ev_child child;
- libxl__ev_qmp qmp;
- /* 'device_config' can be used to to pass to callbacks a pointer of one
- * of the type 'libxl_device_$type' corresponding to the device been
- * hotplug. 'device_type' should have the corresponding
- * 'libxl__$type_devtype'. */
- void *device_config;
- const libxl__device_type *device_type;
-};
-
-/*
- * Multiple devices "multidev" handling.
- *
- * Firstly, you should
- * libxl__multidev_begin
- * multidev->callback = ...
- * Then zero or more times
- * libxl__multidev_prepare
- * libxl__initiate_device_{remove/addition}
- * (or some other thing which will eventually call
- * aodev->callback or libxl__multidev_one_callback)
- * Finally, once
- * libxl__multidev_prepared
- * which will result (perhaps reentrantly) in one call to
- * multidev->callback().
- */
-
-/* Starts preparing to add/remove a bunch of devices. */
-_hidden void libxl__multidev_begin(libxl__ao *ao, libxl__multidev*);
-
-/* Prepares to add/remove one of many devices.
- * Calls libxl__prepare_ao_device on libxl__ao_device argument provided and
- * also sets the aodev->callback (to libxl__multidev_one_callback)
- * The user should not mess with aodev->callback.
- */
-_hidden void libxl__multidev_prepare_with_aodev(libxl__multidev*,
- libxl__ao_device*);
-
-/* A wrapper function around libxl__multidev_prepare_with_aodev.
- * Allocates a libxl__ao_device and prepares it for addition/removal.
- * Returns the newly allocated libxl__ao_dev.
- */
-_hidden libxl__ao_device *libxl__multidev_prepare(libxl__multidev*);
-
-/* Indicates to multidev that this one device has been processed.
- * Normally the multidev user does not need to touch this function, as
- * multidev_prepare will name it in aodev->callback. However, if you
- * want to do something more complicated you can set aodev->callback
- * yourself to something else, so long as you eventually call
- * libxl__multidev_one_callback.
- */
-_hidden void libxl__multidev_one_callback(libxl__egc *egc,
- libxl__ao_device *aodev);
-
-/* Notifies the multidev machinery that we have now finished preparing
- * and initiating devices. multidev->callback may then be called as
- * soon as there are no prepared but not completed operations
- * outstanding, perhaps reentrantly. If rc!=0 (error should have been
- * logged) multidev->callback will get a non-zero rc.
- * callback may be set by the user at any point before prepared. */
-_hidden void libxl__multidev_prepared(libxl__egc*, libxl__multidev*, int rc);
-
-typedef void libxl__devices_callback(libxl__egc*, libxl__multidev*, int rc);
-struct libxl__multidev {
- /* set by user: */
- libxl__devices_callback *callback;
- /* for private use by libxl__...ao_devices... machinery: */
- libxl__ao *ao;
- libxl__ao_device **array;
- int used, allocd;
- libxl__ao_device *preparation;
-};
-
-/*
- * Algorithm for handling device removal (including domain
- * destruction). This is somewhat subtle because we may already have
- * killed the domain and caused the death of qemu.
- *
- * In current versions of qemu there is no mechanism for ensuring that
- * the resources used by its devices (both emulated and any PV devices
- * provided by qemu) are freed (eg, fds closed) before it shuts down,
- * and no confirmation from a terminating qemu back to the toolstack.
- *
- * This will need to be fixed in future Xen versions. In the meantime
- * (Xen 4.2) we implement a bodge.
- *
- * WE WANT TO UNPLUG WE WANT TO SHUT DOWN OR DESTROY
- * | |
- * | LIBXL SENDS SIGHUP TO QEMU
- * | .....................|........................
- * | : XEN 4.3+ PLANNED | :
- * | : QEMU TEARS DOWN ALL DEVICES :
- * | : FREES RESOURCES (closing fds) :
- * | : SETS PV BACKENDS TO STATE 5, :
- * | : waits for PV frontends to shut down :
- * | : SETS PV BACKENDS TO STATE 6 :
- * | : | :
- * | : QEMU NOTIFIES TOOLSTACK (via :
- * | : xenstore) that it is exiting :
- * | : QEMU EXITS (parent may be init) :
- * | : | :
- * | : TOOLSTACK WAITS FOR QEMU :
- * | : notices qemu has finished :
- * | :....................|.......................:
- * | .--------------------'
- * V V
- * for each device
- * we want to unplug/remove
- * ..................|...........................................
- * : V XEN 4.2 RACY BODGE :
- * : device is provided by qemu :
- * : | `-----------. :
- * : something| V :
- * : else, eg| domain (that is domain for which :
- * : blkback| this PV device is the backend, :
- * : | which might be the stub dm) :
- * : | is still alive? :
- * : | | | :
- * : | |alive |dead :
- * : |<-----------------' | :
- * : | hopefully qemu is | :
- * : | still running | :
- * :............|................. | :
- * ,----->| : we may be racing :
- * | backend state? : with qemu's death :
- * ^ | | : | :
- * xenstore| |other |6 : WAIT 2.0s :
- * conflict| | | : TIMEOUT :
- * | WRITE B.E. | : | :
- * | STATE:=5 | : hopefully qemu has :
- * `---' | | : gone by now and :
- * |ok | : freed its resources :
- * | | : | :
- * WAIT FOR | : SET B.E. :
- * STATE==6 | : STATE:=6 :
- * / | | :..........|...................:
- * timeout/ ok| | |
- * / | | |
- * | RUN HOTPLUG <-'<----------------'
- * | SCRIPT
- * | |
- * `---> NUKE
- * BACKEND
- * |
- * DONE.
- */
-
-/*
- * As of Xen 4.5 we maintain various information, including hotplug
- * device information, in JSON files, so that we can use this JSON
- * file as a template to reconstruct domain configuration.
- *
- * In essense there are now two views of device state, one is the
- * primary config (xenstore or QEMU), the other is JSON file.
- *
- * Here we maintain one invariant: every device in the primary config
- * must have an entry in JSON file.
- *
- * All device hotplug routines should comply to following pattern:
- * lock json config (json_lock)
- * read json config
- * update in-memory json config with new entry, replacing
- * any stale entry
- * for loop -- xs transaction
- * open xs transaction
- * check device existence, bail if it exists
- * write in-memory json config to disk
- * commit xs transaction
- * end for loop
- * unlock json config
- *
- * Or in case QEMU is the primary config, this pattern can be use:
- * qmp_lock (libxl__ev_devlock_init)
- * lock json config (json_lock)
- * read json config
- * update in-memory json config with new entry, replacing
- * any stale entry
- * unlock json config
- * apply new config to primary config
- * lock json config (json_lock)
- * read json config
- * update in-memory json config with new entry, replacing
- * any stale entry
- * write in-memory json config to disk
- * unlock json config
- * unlock qmp_lock
- * (CTX_LOCK can be acquired and released several time while holding the
- * qmp_lock)
- *
- * Device removal routines are not touched.
- *
- * Here is the proof that we always maintain that invariant and we
- * don't leak files during interaction of hotplug thread and other
- * threads / processes.
- *
- * # Safe against parallel add
- *
- * When another thread / process tries to add same device, it's
- * blocked by json_lock. The loser of two threads will bail at
- * existence check, so that we don't overwrite anything.
- *
- * # Safe against domain destruction
- *
- * If the thread / process trying to destroy domain loses the race, it's
- * blocked by json_lock. If the hotplug thread is loser, it bails at
- * acquiring lock because lock acquisition function checks existence of
- * the domain.
- *
- * # Safe against parallel removal
- *
- * When another thread / process tries to remove a device, it's _NOT_
- * blocked by json_lock, but xenstore transaction can help maintain
- * invariant. The removal threads either a) sees that device in
- * xenstore, b) doesn't see that device in xenstore.
- *
- * In a), it sees that device in xenstore. At that point hotplug is
- * already finished (both JSON and xenstore changes committed). So that
- * device can be safely removed. JSON entry is left untouched and
- * becomes stale, but this is a valid state -- next time when a
- * device with same identifier gets added, the stale entry gets
- * overwritten.
- *
- * In b), it doesn't see that device in xenstore, but it will commence
- * anyway. Eventually a forcibly removal is initiated, which will forcely
- * remove xenstore entry.
- *
- * If hotplug threads creates xenstore entry (therefore JSON entry as
- * well) before force removal, that xenstore entry is removed. We're
- * left with JSON stale entry but not xenstore entry, which is a valid
- * state.
- *
- * If hotplug thread has not created xenstore entry when the removal
- * is committed, we're obviously safe. Hotplug thread will add in
- * xenstore entry afterwards. We have both JSON and xenstore entry,
- * it's a valid state.
- */
-
-/* Waits for the passed device to reach state XenbusStateInitWait.
- * This is not really useful by itself, but is important when executing
- * hotplug scripts, since we need to be sure the device is in the correct
- * state before executing them.
- *
- * Once finished, aodev->callback will be executed.
- */
-_hidden void libxl__wait_device_connection(libxl__egc*,
- libxl__ao_device *aodev);
-
-/* Arranges that dev will be removed to the guest, and the
- * hotplug scripts will be executed (if necessary). When
- * this is done (or an error happens), the callback in
- * aodev->callback will be called.
- *
- * The libxl__ao_device passed to this function should be
- * prepared using libxl__prepare_ao_device prior to calling
- * this function.
- *
- * Once finished, aodev->callback will be executed.
- */
-_hidden void libxl__initiate_device_generic_remove(libxl__egc *egc,
- libxl__ao_device *aodev);
-
-_hidden void libxl__initiate_device_usbctrl_remove(libxl__egc *egc,
- libxl__ao_device *aodev);
-
-/*
- * libxl__get_hotplug_script_info returns the args and env that should
- * be passed to the hotplug script for the requested device.
- *
- * Since a device might not need to execute any hotplug script, this function
- * can return the following values:
- * < 0: Error
- * 0: No need to execute hotplug script
- * 1: Execute hotplug script
- *
- * The last parameter, "num_exec" refeers to the number of times hotplug
- * scripts have been called for this device.
- *
- * The main body of libxl will, for each device, keep calling
- * libxl__get_hotplug_script_info, with incrementing values of
- * num_exec, and executing the resulting script accordingly,
- * until libxl__get_hotplug_script_info returns<=0.
- */
-_hidden int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
- char ***args, char ***env,
- libxl__device_action action,
- int num_exec);
-
-/*----- local disk attach: attach a disk locally to run the bootloader -----*/
-
-typedef struct libxl__disk_local_state libxl__disk_local_state;
-typedef void libxl__disk_local_state_callback(libxl__egc*,
- libxl__disk_local_state*,
- int rc);
-
-/* A libxl__disk_local_state may be in the following states:
- * Undefined, Idle, Attaching, Attached, Detaching.
- */
-struct libxl__disk_local_state {
- /* filled by the user */
- libxl__ao *ao;
- const libxl_device_disk *in_disk;
- libxl_device_disk disk;
- const char *blkdev_start;
- libxl__disk_local_state_callback *callback;
- /* filled by libxl__device_disk_local_initiate_attach */
- char *diskpath;
- /* private for implementation of local detach */
- libxl__ao_device aodev;
- int rc;
-};
-
-/*
- * Prepares a dls for use.
- * State Undefined -> Idle
- */
-static inline void libxl__device_disk_local_init(libxl__disk_local_state *dls)
-{
- dls->rc = 0;
-}
-
-/*
- * See if we can find a way to access a disk locally
- */
-_hidden char * libxl__device_disk_find_local_path(libxl__gc *gc,
- libxl_domid guest_domid,
- const libxl_device_disk *disk,
- bool qdisk_direct);
-
-
-/* Make a disk available in this (the control) domain. Always calls
- * dls->callback when finished.
- * State Idle -> Attaching
- *
- * The state of dls on entry to the callback depends on the value
- * of rc passed to the callback:
- * rc == 0: Attached if rc == 0
- * rc != 0: Idle
- */
-_hidden void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
- libxl__disk_local_state *dls);
-
-/* Disconnects a disk device form the control domain. If the passed
- * dls is not attached (or has already been detached),
- * libxl__device_disk_local_initiate_detach will just call the callback
- * directly.
- * State Idle/Attached -> Detaching
- *
- * The state of dls on entry to the callback is Idle.
- */
-_hidden void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
- libxl__disk_local_state *dls);
-
-/*----- datacopier: copies data from one fd to another -----*/
-
-typedef struct libxl__datacopier_state libxl__datacopier_state;
-typedef struct libxl__datacopier_buf libxl__datacopier_buf;
-
-/* onwrite==1 means problem happened when writing
- * rc==FAIL errnoval >0 we had a write error, logged
- * onwrite==0 means problem happened when reading
- * rc==0 errnoval==0 we got eof and all data was written
- * rc==FAIL errnoval >0 we had a read error, logged
- * onwrite==-1 means some other internal problem
- * rc==FAIL errnoval==EIO some other internal failure, logged
- * rc==ABORTED errnoval==0 abort requested, not logged
- * If we get POLLHUP, we call callback_pollhup with
- * rc==FAIL errnoval==-1 POLLHUP signalled
- * or if callback_pollhup==0 this is treated as eof (if POLLIN|POLLHUP
- * on the reading fd) or an internal failure (otherwise), as above.
- * In all cases copier is killed before calling this callback */
-typedef void libxl__datacopier_callback(libxl__egc *egc,
- libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
-
-struct libxl__datacopier_buf {
- /* private to datacopier */
- LIBXL_TAILQ_ENTRY(libxl__datacopier_buf) entry;
- int used;
- char buf[1000];
-};
-
-struct libxl__datacopier_state {
- /* caller must fill these in, and they must all remain valid */
- libxl__ao *ao;
- int readfd, writefd;
- ssize_t maxsz;
- ssize_t bytes_to_read; /* set to -1 to read until EOF */
- const char *copywhat, *readwhat, *writewhat; /* for error msgs */
- FILE *log; /* gets a copy of everything */
- libxl__datacopier_callback *callback;
- libxl__datacopier_callback *callback_pollhup;
- void *readbuf; /* Set this to read data into it without writing to an
- fd. The buffer should be at least as large as the
- bytes_to_read parameter, which should not be -1. */
- /* remaining fields are private to datacopier */
- libxl__ao_abortable abrt;
- libxl__ev_fd toread, towrite;
- ssize_t used;
- LIBXL_TAILQ_HEAD(libxl__datacopier_bufs, libxl__datacopier_buf) bufs;
-};
-
-_hidden void libxl__datacopier_init(libxl__datacopier_state *dc);
-_hidden void libxl__datacopier_kill(libxl__datacopier_state *dc);
-_hidden int libxl__datacopier_start(libxl__datacopier_state *dc);
-
-/* Inserts literal data into the output stream. The data is copied.
- * May safely be used only immediately after libxl__datacopier_start
- * (before the ctx is unlocked). But may be called multiple times.
- * NB exceeding maxsz will fail an assertion! */
-_hidden void libxl__datacopier_prefixdata(libxl__egc*, libxl__datacopier_state*,
- const void *data, size_t len);
-
-/*----- Save/restore helper (used by creation and suspend) -----*/
-
-typedef struct libxl__srm_save_callbacks {
- libxl__srm_save_autogen_callbacks a;
-} libxl__srm_save_callbacks;
-
-typedef struct libxl__srm_restore_callbacks {
- libxl__srm_restore_autogen_callbacks a;
-} libxl__srm_restore_callbacks;
-
-/* a pointer to this struct is also passed as "user" to the
- * save callout helper callback functions */
-typedef struct libxl__save_helper_state {
- /* public, caller of run_helper initialises */
- libxl__ao *ao;
- uint32_t domid;
- union {
- libxl__srm_save_callbacks save;
- libxl__srm_restore_callbacks restore;
- } callbacks;
- int (*recv_callback)(const unsigned char *msg, uint32_t len, void *user);
- void (*completion_callback)(libxl__egc *egc, void *caller_state,
- int rc, int retval, int errnoval);
- void *caller_state;
- int need_results; /* set to 0 or 1 by caller of run_helper;
- * if set to 1 then the ultimate caller's
- * results function must set it to 0 */
- /* private */
- int rc;
- int completed; /* retval/errnoval valid iff completed */
- int retval, errnoval; /* from xc_domain_save / xc_domain_restore */
- libxl__ao_abortable abrt;
- libxl__carefd *pipes[2]; /* 0 = helper's stdin, 1 = helper's stdout */
- libxl__ev_fd readable;
- libxl__ev_child child;
- const char *stdin_what, *stdout_what;
-
- libxl__egc *egc; /* valid only for duration of each event callback;
- * is here in this struct for the benefit of the
- * marshalling and xc callback functions */
-} libxl__save_helper_state;
-
-/*----- checkpoint device related state structure -----*/
-/*
- * The abstract checkpoint device layer exposes a common
- * set of API to [external] libxl for manipulating devices attached to
- * a guest protected by Remus/COLO. The device layer also exposes a set of
- * [internal] interfaces that every device type must implement.
- *
- * The following API are exposed to libxl:
- *
- * One-time configuration operations:
- * +libxl__checkpoint_devices_setup
- * > Enable output buffering for NICs, setup disk replication, etc.
- * +libxl__checkpoint_devices_teardown
- * > Disable output buffering and disk replication; teardown any
- * associated external setups like qdiscs for NICs.
- *
- * Operations executed every checkpoint (in order of invocation):
- * +libxl__checkpoint_devices_postsuspend
- * +libxl__checkpoint_devices_preresume
- * +libxl__checkpoint_devices_commit
- *
- * Each device type needs to implement the interfaces specified in
- * the libxl__checkpoint_device_instance_ops if it wishes to support Remus/COLO.
- *
- * The high-level control flow through the checkpoint device layer is shown
- * below:
- *
- * xl remus
- * |-> libxl_domain_remus_start
- * |-> libxl__checkpoint_devices_setup
- * |-> Per-checkpoint libxl__checkpoint_devices_[postsuspend,preresume,commit]
- * ...
- * |-> On backup failure, network error or other internal errors:
- * libxl__checkpoint_devices_teardown
- */
-
-typedef struct libxl__checkpoint_device libxl__checkpoint_device;
-typedef struct libxl__checkpoint_devices_state libxl__checkpoint_devices_state;
-typedef struct libxl__checkpoint_device_instance_ops libxl__checkpoint_device_instance_ops;
-
-/*
- * Interfaces to be implemented by every device subkind that wishes to
- * support Remus/COLO. Functions must be implemented unless otherwise
- * stated. Many of these functions are asynchronous. They call
- * dev->aodev.callback when done. The actual implementations may be
- * synchronous and call dev->aodev.callback directly (as the last
- * thing they do).
- */
-struct libxl__checkpoint_device_instance_ops {
- /* the device kind this ops belongs to... */
- libxl__device_kind kind;
-
- /*
- * Checkpoint operations. May be NULL, meaning the op is not
- * implemented and the caller should treat them as a no-op (and do
- * nothing when checkpointing).
- * Asynchronous.
- */
-
- void (*postsuspend)(libxl__egc *egc, libxl__checkpoint_device *dev);
- void (*preresume)(libxl__egc *egc, libxl__checkpoint_device *dev);
- void (*commit)(libxl__egc *egc, libxl__checkpoint_device *dev);
-
- /*
- * setup() and teardown() are refer to the actual checkpoint device.
- * Asynchronous.
- * teardown is called even if setup fails.
- */
- /*
- * setup() should first determines whether the subkind matches the specific
- * device. If matched, the device will then be managed with this set of
- * subkind operations.
- * Yields 0 if the device successfully set up.
- * CHECKPOINT_DEVOPS_DOES_NOT_MATCH if the ops does not match the device.
- * any other rc indicates failure.
- */
- void (*setup)(libxl__egc *egc, libxl__checkpoint_device *dev);
- void (*teardown)(libxl__egc *egc, libxl__checkpoint_device *dev);
-};
-
-int init_subkind_nic(libxl__checkpoint_devices_state *cds);
-void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds);
-int init_subkind_drbd_disk(libxl__checkpoint_devices_state *cds);
-void cleanup_subkind_drbd_disk(libxl__checkpoint_devices_state *cds);
-
-typedef void libxl__checkpoint_callback(libxl__egc *,
- libxl__checkpoint_devices_state *,
- int rc);
-
-/*
- * State associated with a checkpoint invocation, including parameters
- * passed to the checkpoint abstract device layer by the remus
- * save/restore machinery.
- */
-struct libxl__checkpoint_devices_state {
- /*-- must be set by caller of libxl__checkpoint_device_(setup|teardown) --*/
-
- libxl__ao *ao;
- uint32_t domid;
- libxl__checkpoint_callback *callback;
- void *concrete_data;
- int device_kind_flags;
- /* The ops must be pointer array, and the last ops must be NULL. */
- const libxl__checkpoint_device_instance_ops **ops;
-
- /*----- private for abstract layer only -----*/
-
- int num_devices;
- /*
- * this array is allocated before setup the checkpoint devices by the
- * checkpoint abstract layer.
- * devs may be NULL, means there's no checkpoint devices that has been
- * set up.
- * the size of this array is 'num_devices', which is the total number
- * of libxl nic devices and disk devices(num_nics + num_disks).
- */
- libxl__checkpoint_device **devs;
-
- libxl_device_nic *nics;
- int num_nics;
- libxl_device_disk *disks;
- int num_disks;
-
- libxl__multidev multidev;
-};
-
-/*
- * Information about a single device being handled by remus.
- * Allocated by the checkpoint abstract layer.
- */
-struct libxl__checkpoint_device {
- /*----- shared between abstract and concrete layers -----*/
- /*
- * if this is true, that means the subkind ops match the device
- */
- bool matched;
-
- /*----- set by checkpoint device abstruct layer -----*/
- /* libxl__device_* which this checkpoint device related to */
- const void *backend_dev;
- libxl__device_kind kind;
- libxl__checkpoint_devices_state *cds;
- libxl__ao_device aodev;
-
- /*----- private for abstract layer only -----*/
-
- /*
- * Control and state variables for the asynchronous callback
- * based loops which iterate over device subkinds, and over
- * individual devices.
- */
- int ops_index;
- const libxl__checkpoint_device_instance_ops *ops;
-
- /*----- private for concrete (device-specific) layer -----*/
-
- /* concrete device's private data */
- void *concrete_data;
-};
-
-/* the following 5 APIs are async ops, call cds->callback when done */
-_hidden void libxl__checkpoint_devices_setup(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds);
-_hidden void libxl__checkpoint_devices_teardown(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds);
-_hidden void libxl__checkpoint_devices_postsuspend(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds);
-_hidden void libxl__checkpoint_devices_preresume(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds);
-_hidden void libxl__checkpoint_devices_commit(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds);
-
-/*----- Remus related state structure -----*/
-typedef struct libxl__remus_state libxl__remus_state;
-struct libxl__remus_state {
- /* private */
- libxl__ev_time checkpoint_timeout; /* used for Remus checkpoint */
- int interval; /* checkpoint interval */
-
- /*----- private for concrete (device-specific) layer only -----*/
- /* private for nic device subkind ops */
- char *netbufscript;
- struct nl_sock *nlsock;
- struct nl_cache *qdisc_cache;
-
- /* private for drbd disk subkind ops */
- char *drbd_probe_script;
-};
-_hidden int libxl__netbuffer_enabled(libxl__gc *gc);
-
-/*----- Legacy conversion helper -----*/
-typedef struct libxl__conversion_helper_state libxl__conversion_helper_state;
-
-struct libxl__conversion_helper_state {
- /* Public - Must be filled by caller unless noted. */
- libxl__ao *ao;
- int legacy_fd; /* fd to read the legacy stream from. */
- bool hvm; /* pv or hvm domain? */
- libxl__carefd *v2_carefd; /* Filled by successful call to
- * libxl__convert_legacy_stream(). Caller
- * assumes ownership of the fd. */
- void (*completion_callback)(
- libxl__egc *egc, libxl__conversion_helper_state *chs, int rc);
- /* private */
- int rc;
- libxl__ao_abortable abrt;
- libxl__ev_child child;
-};
-
-_hidden void libxl__conversion_helper_init
- (libxl__conversion_helper_state *chs);
-_hidden int libxl__convert_legacy_stream(libxl__egc *egc,
- libxl__conversion_helper_state *chs);
-_hidden void libxl__conversion_helper_abort(libxl__egc *egc,
- libxl__conversion_helper_state *chs, int rc);
-static inline bool libxl__conversion_helper_inuse
- (const libxl__conversion_helper_state *chs)
-{ return libxl__ev_child_inuse(&chs->child); }
-
-/* State for reading a libxl migration v2 stream */
-typedef struct libxl__stream_read_state libxl__stream_read_state;
-
-typedef struct libxl__sr_record_buf {
- /* private to stream read helper */
- LIBXL_STAILQ_ENTRY(struct libxl__sr_record_buf) entry;
- libxl__sr_rec_hdr hdr;
- void *body; /* iff hdr.length != 0 */
-} libxl__sr_record_buf;
-
-struct libxl__stream_read_state {
- /* filled by the user */
- libxl__ao *ao;
- libxl__domain_create_state *dcs;
- int fd;
- bool legacy;
- bool back_channel;
- void (*completion_callback)(libxl__egc *egc,
- libxl__stream_read_state *srs,
- int rc);
- void (*checkpoint_callback)(libxl__egc *egc,
- libxl__stream_read_state *srs,
- int rc);
- /* Private */
- int rc;
- bool running;
- bool in_checkpoint;
- bool sync_teardown; /* Only used to coordinate shutdown on error path. */
- bool in_checkpoint_state;
- libxl__save_helper_state shs;
- libxl__conversion_helper_state chs;
-
- /* Main stream-reading data. */
- libxl__datacopier_state dc; /* Only used when reading a record */
- libxl__sr_hdr hdr;
- LIBXL_STAILQ_HEAD(, libxl__sr_record_buf) record_queue; /* NOGC */
- enum {
- SRS_PHASE_NORMAL,
- SRS_PHASE_BUFFERING,
- SRS_PHASE_UNBUFFERING,
- } phase;
- bool recursion_guard;
-
- /* Only used while actively reading a record from the stream. */
- libxl__sr_record_buf *incoming_record; /* NOGC */
-
- /* Both only used when processing an EMULATOR record. */
- libxl__datacopier_state emu_dc;
- libxl__carefd *emu_carefd;
-};
-
-_hidden void libxl__stream_read_init(libxl__stream_read_state *stream);
-_hidden void libxl__stream_read_start(libxl__egc *egc,
- libxl__stream_read_state *stream);
-_hidden void libxl__stream_read_start_checkpoint(libxl__egc *egc,
- libxl__stream_read_state *stream);
-_hidden void libxl__stream_read_checkpoint_state(libxl__egc *egc,
- libxl__stream_read_state *stream);
-_hidden void libxl__stream_read_abort(libxl__egc *egc,
- libxl__stream_read_state *stream, int rc);
-static inline bool
-libxl__stream_read_inuse(const libxl__stream_read_state *stream)
-{
- return stream->running;
-}
-
-#include "libxl_colo.h"
-
-/*----- Domain suspend (save) state structure -----*/
-/*
- * "suspend" refers to quiescing the VM, so pausing qemu, making a
- * remote_shutdown(SHUTDOWN_suspend) hypercall etc.
- *
- * "save" refers to the actions involved in actually shuffling the
- * state of the VM, so xc_domain_save() etc.
- */
-
-typedef struct libxl__domain_suspend_state libxl__domain_suspend_state;
-typedef struct libxl__domain_save_state libxl__domain_save_state;
-
-typedef void libxl__domain_save_cb(libxl__egc*,
- libxl__domain_save_state*, int rc);
-typedef void libxl__save_device_model_cb(libxl__egc*,
- libxl__domain_save_state*, int rc);
-
-/* State for writing a libxl migration v2 stream */
-typedef struct libxl__stream_write_state libxl__stream_write_state;
-typedef void (*sws_record_done_cb)(libxl__egc *egc,
- libxl__stream_write_state *sws);
-struct libxl__stream_write_state {
- /* filled by the user */
- libxl__ao *ao;
- libxl__domain_save_state *dss;
- int fd;
- bool back_channel;
- void (*completion_callback)(libxl__egc *egc,
- libxl__stream_write_state *sws,
- int rc);
- void (*checkpoint_callback)(libxl__egc *egc,
- libxl__stream_write_state *sws,
- int rc);
- /* Private */
- int rc;
- bool running;
- bool in_checkpoint;
- bool sync_teardown; /* Only used to coordinate shutdown on error path. */
- bool in_checkpoint_state;
- libxl__save_helper_state shs;
-
- /* Main stream-writing data. */
- libxl__datacopier_state dc;
- sws_record_done_cb record_done_callback;
-
- /* Cache device model version. */
- libxl_device_model_version device_model_version;
-
- /* Only used when constructing EMULATOR records. */
- libxl__datacopier_state emu_dc;
- libxl__carefd *emu_carefd;
- libxl__sr_rec_hdr emu_rec_hdr;
- libxl__sr_emulator_hdr emu_sub_hdr;
- void *emu_body;
-};
-
-_hidden void libxl__stream_write_init(libxl__stream_write_state *stream);
-_hidden void libxl__stream_write_start(libxl__egc *egc,
- libxl__stream_write_state *stream);
-_hidden void
-libxl__stream_write_start_checkpoint(libxl__egc *egc,
- libxl__stream_write_state *stream);
-_hidden void
-libxl__stream_write_checkpoint_state(libxl__egc *egc,
- libxl__stream_write_state *stream,
- libxl_sr_checkpoint_state *srcs);
-_hidden void libxl__stream_write_abort(libxl__egc *egc,
- libxl__stream_write_state *stream,
- int rc);
-static inline bool
-libxl__stream_write_inuse(const libxl__stream_write_state *stream)
-{
- return stream->running;
-}
-
-typedef struct libxl__logdirty_switch {
- /* Set by caller of libxl__domain_common_switch_qemu_logdirty */
- libxl__ao *ao;
- void (*callback)(libxl__egc *egc, struct libxl__logdirty_switch *lds,
- int rc);
-
- const char *cmd;
- const char *cmd_path;
- const char *ret_path;
- libxl__ev_xswatch watch;
- libxl__ev_time timeout;
- libxl__ev_qmp qmp;
-} libxl__logdirty_switch;
-
-_hidden void libxl__logdirty_init(libxl__logdirty_switch *lds);
-
-struct libxl__domain_suspend_state {
- /* set by caller of libxl__domain_suspend_init */
- libxl__ao *ao;
- uint32_t domid;
- bool live;
-
- /* private */
- libxl_domain_type type;
-
- libxl__ev_evtchn guest_evtchn;
- int guest_evtchn_lockfd;
- int guest_responded;
-
- libxl__xswait_state pvcontrol;
- libxl__ev_xswatch guest_watch;
- libxl__ev_time guest_timeout;
- libxl__ev_qmp qmp;
-
- const char *dm_savefile;
- void (*callback_device_model_done)(libxl__egc*,
- struct libxl__domain_suspend_state*, int rc);
- void (*callback_common_done)(libxl__egc*,
- struct libxl__domain_suspend_state*, int ok);
-};
-int libxl__domain_suspend_init(libxl__egc *egc,
- libxl__domain_suspend_state *dsps,
- libxl_domain_type type);
-
-/* calls dsps->callback_device_model_done when done
- * may synchronously calls this callback */
-_hidden void libxl__qmp_suspend_save(libxl__egc *egc,
- libxl__domain_suspend_state *dsps);
-
-struct libxl__domain_save_state {
- /* set by caller of libxl__domain_save */
- libxl__ao *ao;
- libxl__domain_save_cb *callback;
-
- uint32_t domid;
- int fd;
- int fdfl; /* original flags on fd */
- int recv_fd;
- libxl_domain_type type;
- int live;
- int debug;
- int checkpointed_stream;
- const libxl_domain_remus_info *remus;
- /* private */
- int rc;
- int xcflags;
- libxl__domain_suspend_state dsps;
- union {
- /* for Remus */
- libxl__remus_state rs;
- /* for COLO */
- libxl__colo_save_state css;
- };
- libxl__checkpoint_devices_state cds;
- libxl__stream_write_state sws;
- libxl__logdirty_switch logdirty;
-};
-
-
-/*----- openpty -----*/
-
-/*
- * opens count (>0) ptys like count calls to openpty, and then
- * calls back. On entry, all op[].master and op[].slave must be
- * 0. On callback, either rc==0 and master and slave are non-0,
- * or rc is a libxl error and they are both 0. If libxl__openpty
- * returns non-0 no callback will happen and everything is left
- * cleaned up.
- */
-
-typedef struct libxl__openpty_state libxl__openpty_state;
-typedef struct libxl__openpty_result libxl__openpty_result;
-typedef void libxl__openpty_callback(libxl__egc *egc, libxl__openpty_state *op);
-
-struct libxl__openpty_state {
- /* caller must fill these in, and they must all remain valid */
- libxl__ao *ao;
- libxl__openpty_callback *callback;
- int count;
- libxl__openpty_result *results; /* actual size is count, out parameter */
- /* public, result, caller may only read in callback */
- int rc;
- /* private for implementation */
- libxl__ev_child child;
-};
-
-struct libxl__openpty_result {
- libxl__carefd *master, *slave;
-};
-
-int libxl__openptys(libxl__openpty_state *op,
- struct termios *termp,
- struct winsize *winp);
-
-
-/*----- bootloader -----*/
-
-typedef struct libxl__bootloader_state libxl__bootloader_state;
-typedef void libxl__run_bootloader_callback(libxl__egc*,
- libxl__bootloader_state*, int rc);
-typedef void libxl__bootloader_console_callback(libxl__egc*,
- libxl__bootloader_state*);
-
-struct libxl__bootloader_state {
- /* caller must fill these in, and they must all remain valid */
- libxl__ao *ao;
- libxl__run_bootloader_callback *callback;
- libxl__bootloader_console_callback *console_available;
- const libxl_domain_build_info *info;
- libxl_device_disk *disk;
- /* Should be zeroed by caller on entry. Will be filled in by
- * bootloader machinery; represents the local attachment of the
- * disk for the benefit of the bootloader. Must be detached by
- * the caller using libxl__device_disk_local_initiate_detach.
- * (This is safe to do after ->callback() has happened since
- * the domain's kernel and initramfs will have been copied
- * out of the guest's disk into a temporary directory, mapped
- * as file references, and deleted. */
- libxl__disk_local_state dls;
- uint32_t domid;
- /* outputs:
- * - caller must initialise kernel and ramdisk to point to file
- * references, these will be updated and mapped;
- * - caller must initialise cmdline to NULL, it will be updated with a
- * string allocated from the gc;
- */
- libxl__file_reference *kernel, *ramdisk;
- const char *cmdline;
- /* private to libxl__run_bootloader */
- char *outputpath, *outputdir, *logfile;
- libxl__openpty_state openpty;
- libxl__openpty_result ptys[2]; /* [0] is for bootloader */
- libxl__ev_child child;
- libxl__domaindeathcheck deathcheck;
- int nargs, argsspace;
- const char **args;
- libxl__datacopier_state keystrokes, display;
- int rc, got_pollhup;
-};
-
-_hidden void libxl__bootloader_init(libxl__bootloader_state *bl);
-
-/* Will definitely call st->callback, perhaps reentrantly.
- * If callback is passed rc==0, will have updated st->info appropriately */
-_hidden void libxl__bootloader_run(libxl__egc*, libxl__bootloader_state *st);
-
-/*----- Generic Device Handling -----*/
-#define LIBXL_DEFINE_DEVICE_ADD(type) \
- int libxl_device_##type##_add(libxl_ctx *ctx, \
- uint32_t domid, libxl_device_##type *type, \
- const libxl_asyncop_how *ao_how) \
- { \
- AO_CREATE(ctx, domid, ao_how); \
- libxl__ao_device *aodev; \
- \
- GCNEW(aodev); \
- libxl__prepare_ao_device(ao, aodev); \
- aodev->action = LIBXL__DEVICE_ACTION_ADD; \
- aodev->callback = device_addrm_aocomplete; \
- aodev->update_json = true; \
- libxl__device_##type##_add(egc, domid, type, aodev); \
- \
- return AO_INPROGRESS; \
- }
-
-#define LIBXL_DEFINE_DEVICES_ADD(type) \
- void libxl__add_##type##s(libxl__egc *egc, libxl__ao *ao, uint32_t domid, \
- libxl_domain_config *d_config, \
- libxl__multidev *multidev) \
- { \
- AO_GC; \
- int i; \
- for (i = 0; i < d_config->num_##type##s; i++) { \
- libxl__ao_device *aodev = libxl__multidev_prepare(multidev); \
- libxl__device_##type##_add(egc, domid, &d_config->type##s[i], \
- aodev); \
- } \
- }
-
-#define LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, remtype, removedestroy, f) \
- int libxl_device_##type##_##removedestroy(libxl_ctx *ctx, \
- uint32_t domid, libxl_device_##type *type, \
- const libxl_asyncop_how *ao_how) \
- { \
- AO_CREATE(ctx, domid, ao_how); \
- libxl__device *device; \
- libxl__ao_device *aodev; \
- int rc; \
- \
- GCNEW(device); \
- rc = libxl__device_from_##type(gc, domid, type, device); \
- if (rc != 0) goto out; \
- \
- GCNEW(aodev); \
- libxl__prepare_ao_device(ao, aodev); \
- aodev->action = LIBXL__DEVICE_ACTION_REMOVE; \
- aodev->dev = device; \
- aodev->callback = device_addrm_aocomplete; \
- aodev->force.flag = f; \
- libxl__initiate_device_##remtype##_remove(egc, aodev); \
- \
- out: \
- if (rc) return AO_CREATE_FAIL(rc); \
- return AO_INPROGRESS; \
- }
-
-#define LIBXL_DEFINE_UPDATE_DEVID(name) \
- int libxl__device_##name##_update_devid(libxl__gc *gc, \
- uint32_t domid, \
- libxl_device_##name *type) \
- { \
- if (type->devid == -1) \
- type->devid = libxl__device_nextid(gc, domid, \
- libxl__##name##_devtype.type); \
- if (type->devid < 0) \
- return ERROR_FAIL; \
- return 0; \
- }
-
-#define LIBXL_DEFINE_DEVICE_FROM_TYPE(name) \
- int libxl__device_from_##name(libxl__gc *gc, uint32_t domid, \
- libxl_device_##name *type, \
- libxl__device *device) \
- { \
- device->backend_devid = type->devid; \
- device->backend_domid = type->backend_domid; \
- device->backend_kind = libxl__##name##_devtype.type; \
- device->devid = type->devid; \
- device->domid = domid; \
- device->kind = libxl__##name##_devtype.type; \
- \
- return 0; \
- }
-
-#define LIBXL_DEFINE_DEVID_TO_DEVICE(name) \
- int libxl_devid_to_device_##name(libxl_ctx *ctx, uint32_t domid, \
- int devid, \
- libxl_device_##name *type) \
- { \
- GC_INIT(ctx); \
- \
- char *device_path; \
- const char *tmp; \
- int rc; \
- \
- libxl_device_##name##_init(type); \
- \
- device_path = GCSPRINTF("%s/device/%s/%d", \
- libxl__xs_libxl_path(gc, domid), \
- libxl__device_kind_to_string( \
- libxl__##name##_devtype.type), \
- devid); \
- \
- if (libxl__xs_read_mandatory(gc, XBT_NULL, device_path, &tmp)) {\
- rc = ERROR_NOTFOUND; goto out; \
- } \
- \
- if (libxl__##name##_devtype.from_xenstore) { \
- rc = libxl__##name##_devtype.from_xenstore(gc, device_path, \
- devid, type); \
- if (rc) goto out; \
- } \
- \
- rc = 0; \
- \
- out: \
- \
- GC_FREE; \
- return rc; \
- }
-
-
-#define LIBXL_DEFINE_DEVICE_REMOVE(type) \
- LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, remove, \
- LIBXL__FORCE_AUTO) \
- LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, destroy, \
- LIBXL__FORCE_ON)
-
-#define LIBXL_DEFINE_DEVICE_REMOVE_CUSTOM(type) \
- LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, type, remove, \
- LIBXL__FORCE_AUTO) \
- LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, type, destroy, \
- LIBXL__FORCE_ON)
-
-#define LIBXL_DEFINE_DEVICE_SAFE_REMOVE(type) \
- LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, safe_remove, \
- LIBXL__FORCE_OFF)
-
-#define LIBXL_DEFINE_DEVICE_LIST(type) \
- libxl_device_##type *libxl_device_##type##_list(libxl_ctx *ctx, \
- uint32_t domid, \
- int *num) \
- { \
- libxl_device_##type *r; \
- GC_INIT(ctx); \
- r = libxl__device_list(gc, &libxl__##type##_devtype, \
- domid, num); \
- GC_FREE; \
- return r; \
- } \
- \
- void libxl_device_##type##_list_free(libxl_device_##type *list, \
- int num) \
- { \
- libxl__device_list_free(&libxl__##type##_devtype, list, num); \
- }
-
-typedef void (*device_add_fn_t)(libxl__egc *, libxl__ao *, uint32_t,
- libxl_domain_config *, libxl__multidev *);
-typedef int (*device_set_default_fn_t)(libxl__gc *, uint32_t, void *, bool);
-typedef int (*device_to_device_fn_t)(libxl__gc *, uint32_t, void *,
- libxl__device *);
-typedef void (*device_init_fn_t)(void *);
-typedef void (*device_copy_fn_t)(libxl_ctx *, void *dst, const void *src);
-typedef void (*device_dispose_fn_t)(void *);
-typedef int (*device_compare_fn_t)(const void *, const void *);
-typedef void (*device_merge_fn_t)(libxl_ctx *, void *, void *);
-typedef int (*device_dm_needed_fn_t)(void *, unsigned);
-typedef void (*device_update_config_fn_t)(libxl__gc *, void *, void *);
-typedef int (*device_update_devid_fn_t)(libxl__gc *, uint32_t, void *);
-typedef int (*device_get_num_fn_t)(libxl__gc *, const char *, unsigned int *);
-typedef int (*device_from_xenstore_fn_t)(libxl__gc *, const char *,
- libxl_devid, void *);
-typedef int (*device_set_xenstore_config_fn_t)(libxl__gc *, uint32_t, void *,
- flexarray_t *, flexarray_t *,
- flexarray_t *);
-
-struct libxl__device_type {
- libxl__device_kind type;
- int skip_attach; /* Skip entry in domcreate_attach_devices() if 1 */
- int ptr_offset; /* Offset of device array ptr in libxl_domain_config */
- int num_offset; /* Offset of # of devices in libxl_domain_config */
- int dev_elem_size; /* Size of one device element in array */
- device_add_fn_t add;
- device_set_default_fn_t set_default;
- device_to_device_fn_t to_device;
- device_init_fn_t init;
- device_copy_fn_t copy;
- device_dispose_fn_t dispose;
- device_compare_fn_t compare;
- device_merge_fn_t merge;
- device_dm_needed_fn_t dm_needed;
- device_update_config_fn_t update_config;
- device_update_devid_fn_t update_devid;
- device_get_num_fn_t get_num;
- device_from_xenstore_fn_t from_xenstore;
- device_set_xenstore_config_fn_t set_xenstore_config;
-};
-
-#define DEFINE_DEVICE_TYPE_STRUCT_X(name, sname, kind, ...) \
- const libxl__device_type libxl__ ## name ## _devtype = { \
- .type = LIBXL__DEVICE_KIND_ ## kind, \
- .ptr_offset = offsetof(libxl_domain_config, name ## s), \
- .num_offset = offsetof(libxl_domain_config, num_ ## name ## s), \
- .dev_elem_size = sizeof(libxl_device_ ## sname), \
- .add = libxl__add_ ## name ## s, \
- .set_default = (device_set_default_fn_t) \
- libxl__device_ ## sname ## _setdefault, \
- .to_device = (device_to_device_fn_t)libxl__device_from_ ## name, \
- .init = (device_init_fn_t)libxl_device_ ## sname ## _init, \
- .copy = (device_copy_fn_t)libxl_device_ ## sname ## _copy, \
- .dispose = (device_dispose_fn_t) \
- libxl_device_ ## sname ## _dispose, \
- .compare = (device_compare_fn_t) \
- libxl_device_ ## sname ## _compare, \
- .update_devid = (device_update_devid_fn_t) \
- libxl__device_ ## sname ## _update_devid, \
- __VA_ARGS__ \
- }
-
-#define DEFINE_DEVICE_TYPE_STRUCT(name, kind, ...) \
- DEFINE_DEVICE_TYPE_STRUCT_X(name, name, kind, __VA_ARGS__)
-
-static inline void **libxl__device_type_get_ptr(
- const libxl__device_type *dt, const libxl_domain_config *d_config)
-{
- return (void **)((void *)d_config + dt->ptr_offset);
-}
-
-static inline void *libxl__device_type_get_elem(
- const libxl__device_type *dt, const libxl_domain_config *d_config,
- int e)
-{
- return *libxl__device_type_get_ptr(dt, d_config) + dt->dev_elem_size * e;
-}
-
-static inline int *libxl__device_type_get_num(
- const libxl__device_type *dt, const libxl_domain_config *d_config)
-{
- return (int *)((void *)d_config + dt->num_offset);
-}
-
-extern const libxl__device_type libxl__vfb_devtype;
-extern const libxl__device_type libxl__vkb_devtype;
-extern const libxl__device_type libxl__disk_devtype;
-extern const libxl__device_type libxl__nic_devtype;
-extern const libxl__device_type libxl__vtpm_devtype;
-extern const libxl__device_type libxl__usbctrl_devtype;
-extern const libxl__device_type libxl__usbdev_devtype;
-extern const libxl__device_type libxl__pcidev_devtype;
-extern const libxl__device_type libxl__vdispl_devtype;
-extern const libxl__device_type libxl__p9_devtype;
-extern const libxl__device_type libxl__pvcallsif_devtype;
-extern const libxl__device_type libxl__vsnd_devtype;
-
-extern const libxl__device_type *device_type_tbl[];
-
-/*----- Domain destruction -----*/
-
-/* Domain destruction has been split into two functions:
- *
- * libxl__domain_destroy is the main destroy function, which detects
- * stubdoms and calls libxl__destroy_domid on the domain and its
- * stubdom if present, creating a different libxl__destroy_domid_state
- * for each one of them.
- *
- * libxl__destroy_domid actually destroys the domain, but it
- * doesn't check for stubdomains, since that would involve
- * recursion, which we want to avoid.
- */
-
-typedef struct libxl__domain_destroy_state libxl__domain_destroy_state;
-typedef struct libxl__destroy_domid_state libxl__destroy_domid_state;
-typedef struct libxl__destroy_devicemodel_state libxl__destroy_devicemodel_state;
-typedef struct libxl__devices_remove_state libxl__devices_remove_state;
-
-typedef void libxl__domain_destroy_cb(libxl__egc *egc,
- libxl__domain_destroy_state *dds,
- int rc);
-
-typedef void libxl__domid_destroy_cb(libxl__egc *egc,
- libxl__destroy_domid_state *dis,
- int rc);
-
-typedef void libxl__devicemodel_destroy_cb(libxl__egc *egc,
- libxl__destroy_devicemodel_state *ddms,
- int rc);
-
-typedef void libxl__devices_remove_callback(libxl__egc *egc,
- libxl__devices_remove_state *drs,
- int rc);
-
-struct libxl__devices_remove_state {
- /* filled in by user */
- libxl__ao *ao;
- uint32_t domid;
- libxl__devices_remove_callback *callback;
- libxl__force force; /* libxl_device_TYPE_destroy rather than _remove */
- /* private */
- libxl__multidev multidev;
- int num_devices;
-};
-
-struct libxl__destroy_devicemodel_state {
- /* filled in by user */
- libxl__ao *ao;
- uint32_t domid;
- libxl__devicemodel_destroy_cb *callback; /* May be called re-entrantly */
- /* private to implementation */
- libxl__ev_child destroyer;
- int rc; /* Accumulated return value for the destroy operation */
-};
-
-struct libxl__destroy_domid_state {
- /* filled in by user */
- libxl__ao *ao;
- uint32_t domid;
- libxl__domid_destroy_cb *callback;
- /* private to implementation */
- libxl__devices_remove_state drs;
- libxl__destroy_devicemodel_state ddms;
- libxl__ev_child destroyer;
- bool soft_reset;
- libxl__multidev multidev;
-};
-
-struct libxl__domain_destroy_state {
- /* filled by the user */
- libxl__ao *ao;
- uint32_t domid;
- libxl__domain_destroy_cb *callback;
- /* Private */
- int rc;
- uint32_t stubdomid;
- libxl__destroy_domid_state stubdom;
- int stubdom_finished;
- libxl__destroy_domid_state domain;
- int domain_finished;
- bool soft_reset;
-};
-
-/*
- * Entry point for domain destruction
- * This function checks for stubdom presence and then calls
- * libxl__destroy_domid on the passed domain and its stubdom if found.
- */
-_hidden void libxl__domain_destroy(libxl__egc *egc,
- libxl__domain_destroy_state *dds);
-
-/* Used to destroy a domain with the passed id (it doesn't check for stubs) */
-_hidden void libxl__destroy_domid(libxl__egc *egc,
- libxl__destroy_domid_state *dis);
-
-/* Used to detroy the device model */
-_hidden void libxl__destroy_device_model(libxl__egc *egc,
- libxl__destroy_devicemodel_state *ddms);
-
-/* Entry point for devices destruction */
-_hidden void libxl__devices_destroy(libxl__egc *egc,
- libxl__devices_remove_state *drs);
-
-/* Helper function to add a bunch of disks. This should be used when
- * the caller is inside an async op. "multidev" will NOT be prepared by
- * this function, so the caller must make sure to call
- * libxl__multidev_begin before calling this function.
- *
- * The "callback" will be called for each device, and the user is responsible
- * for calling libxl__ao_device_check_last on the callback.
- */
-_hidden void libxl__add_disks(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
- libxl_domain_config *d_config,
- libxl__multidev *multidev);
-
-_hidden void libxl__add_nics(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
- libxl_domain_config *d_config,
- libxl__multidev *multidev);
-
-/*----- device model creation -----*/
-
-/* First layer; wraps libxl__spawn_spawn. */
-
-typedef struct libxl__dm_spawn_state libxl__dm_spawn_state;
-
-typedef void libxl__dm_spawn_cb(libxl__egc *egc, libxl__dm_spawn_state*,
- int rc /* if !0, error was logged */);
-
-/* Call dmss_init and dmss_dispose to initialise and dispose of
- * libxl__dm_spawn_state */
-struct libxl__dm_spawn_state {
- /* mixed - spawn.ao must be initialised by user; rest is private: */
- libxl__spawn_state spawn;
- libxl__ev_qmp qmp;
- libxl__ev_time timeout;
- libxl__dm_resume_state dmrs;
- /* filled in by user, must remain valid: */
- uint32_t guest_domid; /* domain being served */
- libxl_domain_config *guest_config;
- libxl__domain_build_state *build_state; /* relates to guest_domid */
- libxl__dm_spawn_cb *callback;
-};
-
-_hidden void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state*);
-
-/*
- * Called after forking but before executing the local devicemodel.
- */
-_hidden int libxl__local_dm_preexec_restrict(libxl__gc *gc);
-
-/* Stubdom device models. */
-
-typedef struct {
- /* Mixed - user must fill in public parts EXCEPT callback,
- * which may be undefined on entry. (See above for details) */
- libxl__dm_spawn_state dm; /* the stub domain device model */
- /* filled in by user, must remain valid: */
- libxl__dm_spawn_cb *callback; /* called as callback(,&sdss->dm,) */
- /* private to libxl__spawn_stub_dm: */
- libxl_domain_config dm_config;
- libxl__domain_build_state dm_state;
- libxl__dm_spawn_state pvqemu;
- libxl__destroy_domid_state dis;
- libxl__multidev multidev;
- libxl__xswait_state xswait;
- libxl__spawn_state qmp_proxy_spawn;
-} libxl__stub_dm_spawn_state;
-
-_hidden void libxl__spawn_stub_dm(libxl__egc *egc, libxl__stub_dm_spawn_state*);
-
-_hidden char *libxl__stub_dm_name(libxl__gc *gc, const char * guest_name);
-
-/* Qdisk backend launch helpers */
-
-_hidden void libxl__spawn_qdisk_backend(libxl__egc *egc,
- libxl__dm_spawn_state *dmss);
-_hidden int libxl__destroy_qdisk_backend(libxl__gc *gc, uint32_t domid);
-
-/*----- Domain creation -----*/
-
-
-struct libxl__domain_create_state {
- /* filled in by user */
- libxl__ao *ao;
- libxl_domain_config *guest_config;
- libxl_domain_config guest_config_saved; /* vanilla config */
- int restore_fd, libxc_fd;
- int restore_fdfl; /* original flags of restore_fd */
- int send_back_fd;
- libxl_domain_restore_params restore_params;
- uint32_t domid;
- bool soft_reset;
- libxl__domain_create_cb *callback;
- libxl_asyncprogress_how aop_console_how;
- /* private to domain_create */
- int guest_domid;
- int device_type_idx;
- const char *colo_proxy_script;
- libxl__domain_build_state build_state;
- libxl__colo_restore_state crs;
- libxl__checkpoint_devices_state cds;
- libxl__bootloader_state bl;
- libxl__stub_dm_spawn_state sdss;
- /* If we're not doing stubdom, we use only sdss.dm,
- * for the non-stubdom device model. */
- libxl__stream_read_state srs;
- /* necessary if the domain creation failed and we have to destroy it */
- libxl__domain_destroy_state dds;
- libxl__multidev multidev;
- libxl__xswait_state console_xswait;
-};
-
-_hidden int libxl__device_nic_set_devids(libxl__gc *gc,
- libxl_domain_config *d_config,
- uint32_t domid);
-
-/*----- Domain suspend (save) functions -----*/
-
-/* calls dss->callback when done */
-_hidden void libxl__domain_save(libxl__egc *egc,
- libxl__domain_save_state *dss);
-
-
-/* calls libxl__xc_domain_suspend_done when done */
-_hidden void libxl__xc_domain_save(libxl__egc *egc,
- libxl__domain_save_state *dss,
- libxl__save_helper_state *shs);
-/* If rc==0 then retval is the return value from xc_domain_save
- * and errnoval is the errno value it provided.
- * If rc!=0, retval and errnoval are undefined. */
-_hidden void libxl__xc_domain_save_done(libxl__egc*, void *dss_void,
- int rc, int retval, int errnoval);
-
-/* Used by asynchronous callbacks: ie ones which xc regards as
- * returning a value, but which we want to handle asynchronously.
- * Such functions' actual callback function return void in libxl
- * When they are ready to indicate completion, they call this. */
-void libxl__xc_domain_saverestore_async_callback_done(libxl__egc *egc,
- libxl__save_helper_state *shs, int return_value);
-
-
-_hidden void libxl__domain_suspend_common_switch_qemu_logdirty
- (uint32_t domid, unsigned int enable, void *data);
-_hidden void libxl__domain_common_switch_qemu_logdirty(libxl__egc *egc,
- int domid, unsigned enable,
- libxl__logdirty_switch *lds);
-_hidden int libxl__save_emulator_xenstore_data(libxl__domain_save_state *dss,
- char **buf, uint32_t *len);
-_hidden int libxl__restore_emulator_xenstore_data
- (libxl__domain_create_state *dcs, const char *ptr, uint32_t size);
-
-
-/* calls libxl__xc_domain_restore_done when done */
-_hidden void libxl__xc_domain_restore(libxl__egc *egc,
- libxl__domain_create_state *dcs,
- libxl__save_helper_state *shs);
-/* If rc==0 then retval is the return value from xc_domain_save
- * and errnoval is the errno value it provided.
- * If rc!=0, retval and errnoval are undefined. */
-_hidden void libxl__xc_domain_restore_done(libxl__egc *egc, void *dcs_void,
- int rc, int retval, int errnoval);
-
-_hidden void libxl__save_helper_init(libxl__save_helper_state *shs);
-_hidden void libxl__save_helper_abort(libxl__egc *egc,
- libxl__save_helper_state *shs);
-
-static inline bool libxl__save_helper_inuse(const libxl__save_helper_state *shs)
-{
- return libxl__ev_child_inuse(&shs->child);
-}
-
-/* Each time the dm needs to be saved, we must call suspend and then save
- * calls dsps->callback_device_model_done when done */
-_hidden void libxl__domain_suspend_device_model(libxl__egc *egc,
- libxl__domain_suspend_state *dsps);
-
-_hidden const char *libxl__device_model_savefile(libxl__gc *gc, uint32_t domid);
-
-/* calls dsps->callback_common_done when done */
-_hidden void libxl__domain_suspend(libxl__egc *egc,
- libxl__domain_suspend_state *dsps);
-/* used by libxc to suspend the guest during migration */
-_hidden void libxl__domain_suspend_callback(void *data);
-
-/* Remus setup and teardown */
-_hidden void libxl__remus_setup(libxl__egc *egc,
- libxl__remus_state *rs);
-_hidden void libxl__remus_teardown(libxl__egc *egc,
- libxl__remus_state *rs,
- int rc);
-_hidden void libxl__remus_restore_setup(libxl__egc *egc,
- libxl__domain_create_state *dcs);
-
-_hidden char *libxl__domid_history_path(libxl__gc *gc,
- const char *suffix);
-
-/*
- * Convenience macros.
- */
-
-#define FILLZERO LIBXL_FILLZERO
-
-
-/*
- * All of these assume (or define)
- * libxl__gc *gc;
- * as a local variable.
- */
-
-#define GC_INIT(ctx) libxl__gc gc[1]; LIBXL_INIT_GC(gc[0],ctx)
-#define GC_FREE libxl__free_all(gc)
-#define CTX libxl__gc_owner(gc)
-#define NOGC (&CTX->nogc_gc) /* pass only to consenting functions */
-
-/* Allocation macros all of which use the gc. */
-
-#define ARRAY_SIZE_OK(ptr, nmemb) ((nmemb) < INT_MAX / (sizeof(*(ptr)) * 2))
-
-/*
- * Expression statement <type> *GCNEW(<type> *var);
- * Uses libxl__gc *gc;
- *
- * Allocates a new object of type <type> from the gc and zeroes it
- * with memset. Sets var to point to the new object or zero (setting
- * errno). Returns the new value of var.
- */
-#define GCNEW(var) \
- (((var) = libxl__zalloc((gc),sizeof(*(var)))))
-
-/*
- * Expression statement <type> *GCNEW_ARRAY(<type> *var, ssize_t nmemb);
- * Uses libxl__gc *gc;
- *
- * Like GCNEW but allocates an array of nmemb elements, as if from
- * calloc. Does check for integer overflow due to large nmemb. If
- * nmemb is 0 may succeed by returning 0.
- */
-#define GCNEW_ARRAY(var, nmemb) \
- ((var) = libxl__calloc((gc), (nmemb), sizeof(*(var))))
-
-/*
- * Expression statement <type> *GCREALLOC_ARRAY(<type> *var, size_t nmemb);
- * Uses libxl__gc *gc;
- *
- * Reallocates the array var to be of size nmemb elements. Updates
- * var and returns the new value of var. Does check for integer
- * overflow due to large nmemb.
- *
- * Do not pass nmemb==0. old may be 0 on entry.
- */
-#define GCREALLOC_ARRAY(var, nmemb) \
- (assert(nmemb > 0), \
- assert(ARRAY_SIZE_OK((var), (nmemb))), \
- (var) = libxl__realloc((gc), (var), (nmemb)*sizeof(*(var))))
-
-
-/*
- * Expression char *GCSPRINTF(const char *fmt, ...);
- * Uses libxl__gc *gc;
- *
- * Trivial convenience wrapper for libxl__sprintf.
- */
-#define GCSPRINTF(fmt, ...) (libxl__sprintf((gc), (fmt), __VA_ARGS__))
-
-
-/*
- * Expression statements
- * void LOG(<xtl_level_suffix>, const char *fmt, ...);
- * void LOGE(<xtl_level_suffix>, const char *fmt, ...);
- * void LOGEV(<xtl_level_suffix>, int errnoval, const char *fmt, ...);
- *
- * void LOGD(<xtl_level_suffix>, uint32_t domid, const char *fmt, ...);
- * void LOGED(<xtl_level_suffix>, uint32_t domid, const char *fmt, ...);
- * void LOGEVD(<xtl_level_suffix>, int errnoval, uint32_t domid, const char *fmt, ...);
- * Use
- * libxl__gc *gc;
- *
- * Trivial convenience wrappers for LIBXL__LOG, LIBXL__LOG_ERRNO,
- * LIBXL__LOG_ERRNOVAL, LIBXL__LOGD, LIBXL__LOGD_ERRNO and
- * LIBXL__LOGD_ERRNOVAL respectively (and thus for libxl__log).
- *
- * XTL_<xtl_level_suffix> should exist and be an xentoollog.h log level
- * So <xtl_level_suffix> should be one of
- * DEBUG VERBOSE DETAIL PROGRESS INFO NOTICE WARN ERROR ERROR CRITICAL
- * Of these, most of libxl uses
- * DEBUG INFO WARN ERROR
- *
- * The LOG*D family will preprend the log message with a string formatted
- * as follows: 'Domain %PRIu32:'. This should help better automatic sorting
- * of log messages per domain.
- */
-#define LOG(l,f, ...) LIBXL__LOG(CTX,XTL_##l,(f),##__VA_ARGS__)
-#define LOGE(l,f, ...) LIBXL__LOG_ERRNO(CTX,XTL_##l,(f),##__VA_ARGS__)
-#define LOGEV(l,e,f, ...) LIBXL__LOG_ERRNOVAL(CTX,XTL_##l,(e),(f),##__VA_ARGS__)
-
-#define LOGD(l,d,f, ...) LIBXL__LOGD(CTX,XTL_##l,(d),(f),##__VA_ARGS__)
-#define LOGED(l,d,f, ...) LIBXL__LOGD_ERRNO(CTX,XTL_##l,(d),(f),##__VA_ARGS__)
-#define LOGEVD(l,e,d,f, ...) LIBXL__LOGD_ERRNOVAL(CTX,XTL_##l,(e),(d),(f),##__VA_ARGS__)
-
-
-/* Locking functions. See comment for "lock" member of libxl__ctx. */
-
-static inline void libxl__ctx_lock(libxl_ctx *ctx) {
- int r = pthread_mutex_lock(&ctx->lock);
- assert(!r);
-}
-
-static inline void libxl__ctx_unlock(libxl_ctx *ctx) {
- int r = pthread_mutex_unlock(&ctx->lock);
- assert(!r);
-}
-
-#define CTX_LOCK (libxl__ctx_lock(CTX))
-#define CTX_UNLOCK (libxl__ctx_unlock(CTX))
-
-/*
- * Automatic NUMA placement
- *
- * These functions and data structures deal with the initial placement of a
- * domain onto the host NUMA nodes.
- *
- * The key concept here is the one of "NUMA placement candidate", which is
- * basically a set of nodes whose characteristics have been successfully
- * checked against some specific requirements. More precisely, a candidate
- * is the nodemap associated with one of the possible subset of the host
- * NUMA nodes providing a certain amount of free memory, or a given number
- * of cpus, or even both (depending in what the caller wants). For
- * convenience of use, some of this information are stored within the
- * candidate itself, instead of always being dynamically computed. A single
- * node can be valid placement candidate, as well as it is possible for a
- * candidate to contain all the nodes of the host. The fewer nodes there
- * are in a candidate, the better performance a domain placed onto it
- * should get (at least from a NUMA point of view). For instance, looking
- * for a numa candidates with 2GB of free memory means we want the subsets
- * of the host NUMA nodes with, cumulatively, at least 2GB of free memory.
- * This condition can be satisfied by just one particular node, or it may
- * require more nodes, depending on the characteristics of the host, on how
- * many domains have been created already, on how big they are, etc.
- *
- * The intended usage is as follows:
- * 1. first of all, call libxl__get_numa_candidates(), and specify the
- * proper constraints to it (e.g., the amount of memory a domain need
- * as the minimum amount of free memory for the candidates). If a
- * candidate comparison function is provided, the candidate with fewer
- * nodes that is found to be best according to what such fucntion says
- * is returned. If no comparison function is passed, the very first
- * candidate is.
- * 2. The chosen candidate's nodemap should be utilized for computing the
- * actual affinity of the domain which, given the current NUMA support
- * in the hypervisor, is what determines the placement of the domain's
- * vcpus and memory.
- */
-
-typedef struct {
- int nr_cpus, nr_nodes;
- int nr_vcpus;
- uint64_t free_memkb;
- libxl_bitmap nodemap;
-} libxl__numa_candidate;
-
-/* Signature for the comparison function between two candidates */
-typedef int (*libxl__numa_candidate_cmpf)(const libxl__numa_candidate *c1,
- const libxl__numa_candidate *c2);
-
-/*
- * This looks for the best NUMA placement candidate satisfying some
- * specific conditions. If min_nodes and/or max_nodes are not 0, their
- * value is used to determine the minimum and maximum number of nodes the
- * candidate can have. If they are 0, it means the candidate can contain
- * from 1 node (min_nodes=0) to the total number of nodes of the host
- * (max_ndoes=0). If min_free_memkb and/or min_cpus are not 0, the caller
- * only wants candidates with at least the amount of free memory and the
- * number of cpus they specify, respectively. If they are 0, the
- * candidates' free memory and/or number of cpus won't be checked at all.
- *
- * Candidates are compared among each others by calling numa_cmpf(), which
- * is where the heuristics for determining which candidate is the best
- * one is actually implemented. The only bit of it that is hardcoded in
- * this function is the fact that candidates with fewer nodes are always
- * preferrable.
- *
- * If at least one suitable candidate is found, it is returned in cndt_out,
- * cndt_found is set to one, and the function returns successfully. On the
- * other hand, if not even one single candidate can be found, the function
- * still returns successfully but cndt_found will be zero.
- *
- * Finally, suitable_cpumap is useful for telling that only the cpus in that
- * mask should be considered when generating placement candidates (for
- * example because of cpupools).
- *
- * It is up to the function to properly allocate cndt_out (by calling
- * libxl__numa_candidate_alloc()), while it is the caller that should init
- * (libxl__numa_candidate_init()) and free (libxl__numa_candidate_dispose())
- * it.
- */
-_hidden int libxl__get_numa_candidate(libxl__gc *gc,
- uint64_t min_free_memkb, int min_cpus,
- int min_nodes, int max_nodes,
- const libxl_bitmap *suitable_cpumap,
- libxl__numa_candidate_cmpf numa_cmpf,
- libxl__numa_candidate *cndt_out,
- int *cndt_found);
-
-/* Initialization, allocation and deallocation for placement candidates */
-static inline void libxl__numa_candidate_init(libxl__numa_candidate *cndt)
-{
- cndt->free_memkb = 0;
- cndt->nr_cpus = cndt->nr_nodes = cndt->nr_vcpus = 0;
- libxl_bitmap_init(&cndt->nodemap);
-}
-
-static inline int libxl__numa_candidate_alloc(libxl__gc *gc,
- libxl__numa_candidate *cndt)
-{
- return libxl_node_bitmap_alloc(CTX, &cndt->nodemap, 0);
-}
-static inline void libxl__numa_candidate_dispose(libxl__numa_candidate *cndt)
-{
- libxl_bitmap_dispose(&cndt->nodemap);
-}
-
-/* Retrieve (in nodemap) the node map associated to placement candidate cndt */
-static inline
-void libxl__numa_candidate_get_nodemap(libxl__gc *gc,
- const libxl__numa_candidate *cndt,
- libxl_bitmap *nodemap)
-{
- libxl_bitmap_copy(CTX, nodemap, &cndt->nodemap);
-}
-/* Set the node map of placement candidate cndt to match nodemap */
-static inline
-void libxl__numa_candidate_put_nodemap(libxl__gc *gc,
- libxl__numa_candidate *cndt,
- const libxl_bitmap *nodemap)
-{
- libxl_bitmap_copy(CTX, &cndt->nodemap, nodemap);
-}
-
-/* Check if vNUMA config is valid. Returns 0 if valid,
- * ERROR_VNUMA_CONFIG_INVALID otherwise.
- */
-int libxl__vnuma_config_check(libxl__gc *gc,
- const libxl_domain_build_info *b_info,
- const libxl__domain_build_state *state);
-int libxl__vnuma_build_vmemrange_pv_generic(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_build_info *b_info,
- libxl__domain_build_state *state);
-int libxl__vnuma_build_vmemrange_pv(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_build_info *b_info,
- libxl__domain_build_state *state);
-int libxl__vnuma_build_vmemrange_hvm(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_build_info *b_info,
- libxl__domain_build_state *state,
- struct xc_dom_image *dom);
-bool libxl__vnuma_configured(const libxl_domain_build_info *b_info);
-
-_hidden int libxl__ms_vm_genid_set(libxl__gc *gc, uint32_t domid,
- const libxl_ms_vm_genid *id);
-
-
-/* Som handy macros for defbool type. */
-#define LIBXL__DEFBOOL_DEFAULT (0)
-#define LIBXL__DEFBOOL_FALSE (-1)
-#define LIBXL__DEFBOOL_TRUE (1)
-#define LIBXL__DEFBOOL_STR_DEFAULT "<default>"
-#define LIBXL__DEFBOOL_STR_FALSE "False"
-#define LIBXL__DEFBOOL_STR_TRUE "True"
-static inline int libxl__defbool_is_default(libxl_defbool *db)
-{
- return !db->val;
-}
-
-/*
- * Inserts "elm_new" into the sorted list "head".
- *
- * "elm_search" must be a loop search variable of the same type as
- * "elm_new". "new_after_search_p" must be an expression which is
- * true iff the element "elm_new" sorts after the element
- * "elm_search".
- *
- * "search_body" can be empty, or some declaration(s) and statement(s)
- * needed for "new_after_search_p".
- */
-#define LIBXL_TAILQ_INSERT_SORTED(head, entry, elm_new, elm_search, \
- search_body, new_after_search_p) \
- do { \
- for ((elm_search) = LIBXL_TAILQ_FIRST((head)); \
- (elm_search); \
- (elm_search) = LIBXL_TAILQ_NEXT((elm_search), entry)) { \
- search_body; \
- if (!(new_after_search_p)) \
- break; \
- } \
- /* now elm_search is either the element before which we want \
- * to place elm_new, or NULL meaning we want to put elm_new at \
- * the end */ \
- if ((elm_search)) \
- LIBXL_TAILQ_INSERT_BEFORE((elm_search), (elm_new), entry); \
- else \
- LIBXL_TAILQ_INSERT_TAIL((head), (elm_new), entry); \
- } while(0)
-
-
-/*
- * int CTYPE(ISFOO, char c);
- * int CTYPE(toupper, char c);
- * int CTYPE(tolower, char c);
- *
- * This is necessary because passing a simple char to a ctype.h
- * is forbidden. ctype.h macros take ints derived from _unsigned_ chars.
- *
- * If you have a char which might be EOF then you should already have
- * it in an int representing an unsigned char, and you can use the
- * <ctype.h> macros directly. This generally happens only with values
- * from fgetc et al.
- *
- * For any value known to be a character (eg, anything that came from
- * a char[]), use CTYPE.
- */
-#define CTYPE(isfoo,c) (isfoo((unsigned char)(c)))
-
-int libxl__defbool_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_defbool *p);
-int libxl__bool_parse_json(libxl__gc *gc, const libxl__json_object *o,
- bool *p);
-int libxl__mac_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_mac *p);
-int libxl__bitmap_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_bitmap *p);
-int libxl__uuid_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_uuid *p);
-int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
- const libxl__json_object *o,
- libxl_cpuid_policy_list *p);
-int libxl__string_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_string_list *p);
-int libxl__key_value_list_parse_json(libxl__gc *gc,
- const libxl__json_object *o,
- libxl_key_value_list *p);
-int libxl__hwcap_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_hwcap *p);
-int libxl__ms_vm_genid_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_ms_vm_genid *p);
-int libxl__int_parse_json(libxl__gc *gc, const libxl__json_object *o,
- void *p);
-int libxl__uint8_parse_json(libxl__gc *gc, const libxl__json_object *o,
- void *p);
-int libxl__uint16_parse_json(libxl__gc *gc, const libxl__json_object *o,
- void *p);
-int libxl__uint32_parse_json(libxl__gc *gc, const libxl__json_object *o,
- void *p);
-int libxl__uint64_parse_json(libxl__gc *gc, const libxl__json_object *o,
- void *p);
-int libxl__string_parse_json(libxl__gc *gc, const libxl__json_object *o,
- char **p);
-
-int libxl__random_bytes(libxl__gc *gc, uint8_t *buf, size_t len);
-
-#include "_libxl_types_private.h"
-#include "_libxl_types_internal_private.h"
-
-/* This always return false, there's no "default value" for hw cap */
-static inline int libxl__hwcap_is_default(libxl_hwcap *hwcap)
-{
- return 0;
-}
-
-static inline int libxl__string_list_is_empty(libxl_string_list *psl)
-{
- return !libxl_string_list_length(psl);
-}
-
-static inline int libxl__key_value_list_is_empty(libxl_key_value_list *pkvl)
-{
- return !libxl_key_value_list_length(pkvl);
-}
-
-int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl);
-
-/* Portability note: a proper flock(2) implementation is required */
-typedef struct {
- libxl__carefd *carefd;
- char *path; /* path of the lock file itself */
-} libxl__flock;
-/* The CTX_LOCK must be held around uses of this lock */
-
-libxl__flock *libxl__lock_file(libxl__gc *gc, const char *filename);
-void libxl__unlock_file(libxl__flock *lock);
-
-libxl__flock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid);
-libxl__flock *libxl__lock_domid_history(libxl__gc *gc);
-
-/*
- * Retrieve / store domain configuration from / to libxl private
- * data store. The registry entry in libxl private data store
- * is "libxl-json".
- * Caller must hold user data lock.
- *
- * Other names used for this lock throughout the libxl code are json_lock,
- * libxl__domain_userdata_lock, "libxl-json", data store lock.
- *
- * See the comment for libxl__ao_device, and "Algorithm for handling device
- * removal", for information about using the libxl-json lock / json_lock.
- */
-int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid,
- libxl_domain_config *d_config);
-int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid,
- libxl_domain_config *d_config);
-
-/* ------ Things related to updating domain configurations ----- */
-void libxl__update_domain_configuration(libxl__gc *gc,
- libxl_domain_config *dst,
- const libxl_domain_config *src);
-
-/* Target memory in xenstore is different from what user has
- * asked for. The difference is video_memkb + (possible) fudge.
- * See libxl_set_memory_target.
- */
-static inline
-uint64_t libxl__get_targetmem_fudge(libxl__gc *gc,
- const libxl_domain_build_info *info)
-{
- int64_t mem_target_fudge = (info->type == LIBXL_DOMAIN_TYPE_HVM &&
- info->max_memkb > info->target_memkb)
- ? LIBXL_MAXMEM_CONSTANT : 0;
-
- return info->video_memkb + mem_target_fudge;
-}
-
-int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
- uint64_t *out_target_memkb,
- uint64_t *out_max_memkb);
-void libxl__xcinfo2xlinfo(libxl_ctx *ctx,
- const xc_domaininfo_t *xcinfo,
- libxl_dominfo *xlinfo);
-
-/* Macros used to compare device identifier. Returns true if the two
- * devices have same identifier. */
-#define COMPARE_DEVID(a, b) ((a)->devid == (b)->devid)
-#define COMPARE_DISK(a, b) (!strcmp((a)->vdev, (b)->vdev))
-#define COMPARE_PCI(a, b) ((a)->func == (b)->func && \
- (a)->bus == (b)->bus && \
- (a)->dev == (b)->dev)
-#define COMPARE_USB(a, b) ((a)->ctrl == (b)->ctrl && \
- (a)->port == (b)->port)
-#define COMPARE_USBCTRL(a, b) ((a)->devid == (b)->devid)
-
-/* This function copies X bytes from source to destination bitmap,
- * where X is the smaller of the two sizes.
- *
- * If destination's size is larger than source, the extra bytes are
- * untouched.
- *
- * XXX This is introduced to fix a regression for 4.5. It shall
- * be revisited in 4.6 time frame.
- */
-void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr,
- const libxl_bitmap *sptr);
-
-int libxl__count_physical_sockets(libxl__gc *gc, int *sockets);
-
-_hidden int libxl__read_sysfs_file_contents(libxl__gc *gc,
- const char *filename,
- void **data_r,
- int *datalen_r);
-
-#define LIBXL_QEMU_USER_PREFIX "xen-qemuuser"
-#define LIBXL_QEMU_USER_SHARED LIBXL_QEMU_USER_PREFIX"-shared"
-#define LIBXL_QEMU_USER_RANGE_BASE LIBXL_QEMU_USER_PREFIX"-range-base"
-#define LIBXL_QEMU_USER_REAPER LIBXL_QEMU_USER_PREFIX"-reaper"
-
-static inline bool libxl__acpi_defbool_val(const libxl_domain_build_info *b_info)
-{
- return libxl_defbool_val(b_info->acpi) &&
- libxl_defbool_val(b_info->u.hvm.acpi);
-}
-
-/*
- * Add a device in libxl_domain_config structure
- *
- * If there is already a device with the same identifier in d_config,
- * that entry is updated.
- *
- * parameters:
- * d_config: pointer to template domain config
- * dt: type of `dev'
- * dev: the device that is to be added / removed / updated
- * (a copy of `dev' will be made)
- */
-void device_add_domain_config(libxl__gc *gc, libxl_domain_config *d_config,
- const libxl__device_type *dt,
- const void *dev);
-
-void libxl__device_add_async(libxl__egc *egc, uint32_t domid,
- const libxl__device_type *dt, void *type,
- libxl__ao_device *aodev);
-int libxl__device_add(libxl__gc *gc, uint32_t domid,
- const libxl__device_type *dt, void *type);
-
-/* Caller is responsible for freeing the memory by calling
- * libxl__device_list_free
- */
-void* libxl__device_list(libxl__gc *gc, const libxl__device_type *dt,
- uint32_t domid, int *num);
-void libxl__device_list_free(const libxl__device_type *dt,
- void *list, int num);
-
-static inline bool libxl__timer_mode_is_default(libxl_timer_mode *tm)
-{
- return *tm == LIBXL_TIMER_MODE_DEFAULT;
-}
-
-static inline bool libxl__string_is_default(char **s)
-{
- return *s == NULL;
-}
-
-_hidden int libxl__prepare_sockaddr_un(libxl__gc *gc, struct sockaddr_un *un,
- const char *path, const char *what);
-static inline const char *libxl__qemu_qmp_path(libxl__gc *gc, int domid)
-{
- return GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
-}
-
-/* Send control commands over xenstore and wait for an Ack. */
-_hidden int libxl__domain_pvcontrol(libxl__egc *egc,
- libxl__xswait_state *pvcontrol,
- domid_t domid, const char *cmd);
-
-/*
- * Maximum number of seconds after desctruction then a domid remains
- * 'recent'. Recent domids are not allowed to be re-used. This can be
- * overidden, for debugging purposes, by the environment variable of the
- * same name.
- */
-#define LIBXL_DOMID_REUSE_TIMEOUT 60
-
-/* Check whether a domid is recent */
-int libxl__is_domid_recent(libxl__gc *gc, uint32_t domid, bool *recent);
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2011 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <math.h>
-
-#include <yajl/yajl_parse.h>
-#include <yajl/yajl_gen.h>
-
-#include "libxl_internal.h"
-
-/* #define DEBUG_ANSWER */
-
-typedef struct libxl__yajl_ctx {
- libxl__gc *gc;
- yajl_handle hand;
- libxl__json_object *head;
- libxl__json_object *current;
-#ifdef DEBUG_ANSWER
- yajl_gen g;
-#endif
-} libxl__yajl_ctx;
-
-#ifdef DEBUG_ANSWER
-#if YAJL_VERSION < 20000
-# define DEBUG_GEN_ALLOC(ctx) \
- if ((ctx)->g == NULL) { \
- yajl_gen_config conf = { 1, " " }; \
- (ctx)->g = yajl_gen_alloc(&conf, NULL); \
- }
-#else /* YAJL2 */
-# define DEBUG_GEN_ALLOC(ctx) \
- if ((ctx)->g == NULL) { \
- (ctx)->g = yajl_gen_alloc(NULL); \
- yajl_gen_config((ctx)->g, yajl_gen_beautify, 1); \
- yajl_gen_config((ctx)->g, yajl_gen_indent_string, " "); \
- }
-#endif
-# define DEBUG_GEN_FREE(ctx) \
- if ((ctx)->g) yajl_gen_free((ctx)->g)
-# define DEBUG_GEN(ctx, type) yajl_gen_##type(ctx->g)
-# define DEBUG_GEN_VALUE(ctx, type, value) yajl_gen_##type(ctx->g, value)
-# define DEBUG_GEN_STRING(ctx, str, n) yajl_gen_string(ctx->g, str, n)
-# define DEBUG_GEN_NUMBER(ctx, str, n) yajl_gen_number(ctx->g, str, n)
-# define DEBUG_GEN_REPORT(yajl_ctx) \
- do { \
- const unsigned char *buf = NULL; \
- size_t len = 0; \
- yajl_gen_get_buf((yajl_ctx)->g, &buf, &len); \
- LIBXL__LOG(libxl__gc_owner((yajl_ctx)->gc), XTL_DEBUG, \
- "response: %s\n", buf); \
- yajl_gen_free((yajl_ctx)->g); \
- (yajl_ctx)->g = NULL; \
- } while (0)
-#else
-# define DEBUG_GEN_ALLOC(ctx) ((void)0)
-# define DEBUG_GEN_FREE(ctx) ((void)0)
-# define DEBUG_GEN(ctx, type) ((void)0)
-# define DEBUG_GEN_VALUE(ctx, type, value) ((void)0)
-# define DEBUG_GEN_STRING(ctx, value, length) ((void)0)
-# define DEBUG_GEN_NUMBER(ctx, value, length) ((void)0)
-# define DEBUG_GEN_REPORT(ctx) ((void)0)
-#endif
-
-/*
- * YAJL Helper
- */
-
-yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str)
-{
- return yajl_gen_string(hand, (const unsigned char *)str, strlen(str));
-}
-
-yajl_gen_status libxl__yajl_gen_enum(yajl_gen hand, const char *str)
-{
- if (str)
- return libxl__yajl_gen_asciiz(hand, str);
- else
- return yajl_gen_null(hand);
-}
-
-/*
- * YAJL generators for builtin libxl types.
- */
-yajl_gen_status libxl_defbool_gen_json(yajl_gen hand,
- libxl_defbool *db)
-{
- return libxl__yajl_gen_asciiz(hand, libxl_defbool_to_string(*db));
-}
-
-int libxl__defbool_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_defbool *p)
-{
- const char *s;
-
- if (!libxl__json_object_is_string(o))
- return ERROR_FAIL;
-
- s = libxl__json_object_get_string(o);
-
- if (!strncmp(s, LIBXL__DEFBOOL_STR_DEFAULT,
- strlen(LIBXL__DEFBOOL_STR_DEFAULT)))
- p->val = LIBXL__DEFBOOL_DEFAULT;
- else if (!strncmp(s, LIBXL__DEFBOOL_STR_TRUE,
- strlen(LIBXL__DEFBOOL_STR_TRUE)))
- p->val = LIBXL__DEFBOOL_TRUE;
- else if (!strncmp(s, LIBXL__DEFBOOL_STR_FALSE,
- strlen(LIBXL__DEFBOOL_STR_FALSE)))
- p->val = LIBXL__DEFBOOL_FALSE;
- else
- return ERROR_FAIL;
-
- return 0;
-}
-
-int libxl__bool_parse_json(libxl__gc *gc, const libxl__json_object *o,
- bool *p)
-{
- if (!libxl__json_object_is_bool(o))
- return ERROR_FAIL;
-
- *p = libxl__json_object_get_bool(o);
-
- return 0;
-}
-
-yajl_gen_status libxl_uuid_gen_json(yajl_gen hand,
- libxl_uuid *uuid)
-{
- char buf[LIBXL_UUID_FMTLEN+1];
- snprintf(buf, sizeof(buf), LIBXL_UUID_FMT, LIBXL_UUID_BYTES((*uuid)));
- return yajl_gen_string(hand, (const unsigned char *)buf, LIBXL_UUID_FMTLEN);
-}
-
-int libxl__uuid_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_uuid *p)
-{
- if (!libxl__json_object_is_string(o))
- return ERROR_FAIL;
-
- return libxl_uuid_from_string(p, o->u.string);
-}
-
-yajl_gen_status libxl_bitmap_gen_json(yajl_gen hand,
- libxl_bitmap *bitmap)
-{
- yajl_gen_status s;
- int i;
-
- s = yajl_gen_array_open(hand);
- if (s != yajl_gen_status_ok) goto out;
-
- libxl_for_each_bit(i, *bitmap) {
- if (libxl_bitmap_test(bitmap, i)) {
- s = yajl_gen_integer(hand, i);
- if (s != yajl_gen_status_ok) goto out;
- }
- }
- s = yajl_gen_array_close(hand);
-out:
- return s;
-}
-
-int libxl__bitmap_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_bitmap *p)
-{
- int i;
- int size;
- const libxl__json_object *t;
- flexarray_t *array;
-
- if (!libxl__json_object_is_array(o))
- return ERROR_FAIL;
-
- array = libxl__json_object_get_array(o);
- if (!array->count) {
- libxl_bitmap_init(p);
- return 0;
- }
-
- t = libxl__json_array_get(o, array->count - 1);
- if (!libxl__json_object_is_integer(t))
- return ERROR_FAIL;
- size = libxl__json_object_get_integer(t) + 1;
-
- libxl_bitmap_alloc(CTX, p, size);
-
- for (i = 0; (t = libxl__json_array_get(o, i)); i++) {
- if (!libxl__json_object_is_integer(t))
- return ERROR_FAIL;
-
- libxl_bitmap_set(p, libxl__json_object_get_integer(t));
- }
-
- return 0;
-}
-
-yajl_gen_status libxl_key_value_list_gen_json(yajl_gen hand,
- libxl_key_value_list *pkvl)
-{
- libxl_key_value_list kvl = *pkvl;
- yajl_gen_status s;
- int i;
-
- s = yajl_gen_map_open(hand);
- if (s != yajl_gen_status_ok) goto out;
-
- if (!kvl) goto empty;
-
- for (i = 0; kvl[i] != NULL; i += 2) {
- s = libxl__yajl_gen_asciiz(hand, kvl[i]);
- if (s != yajl_gen_status_ok) goto out;
- if (kvl[i + 1])
- s = libxl__yajl_gen_asciiz(hand, kvl[i+1]);
- else
- s = yajl_gen_null(hand);
- if (s != yajl_gen_status_ok) goto out;
- }
-empty:
- s = yajl_gen_map_close(hand);
-out:
- return s;
-}
-
-int libxl__key_value_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_key_value_list *p)
-{
- libxl__json_map_node *node = NULL;
- flexarray_t *maps = NULL;
- int i, size;
- libxl_key_value_list kvl;
-
- if (!libxl__json_object_is_map(o))
- return ERROR_FAIL;
-
- maps = libxl__json_object_get_map(o);
- size = maps->count * 2;
- kvl = *p = libxl__calloc(NOGC, size+1, sizeof(char *));
-
- for (i = 0; i < maps->count; i++) {
- int idx = i * 2;
- if (flexarray_get(maps, i, (void**)&node) != 0)
- return ERROR_FAIL;
-
- if (!libxl__json_object_is_string(node->obj) &&
- !libxl__json_object_is_null(node->obj))
- return ERROR_FAIL;
-
- kvl[idx] = libxl__strdup(NOGC, node->map_key);
- if (libxl__json_object_is_string(node->obj))
- kvl[idx+1] =
- libxl__strdup(NOGC, libxl__json_object_get_string(node->obj));
- else
- kvl[idx+1] = NULL;
- }
-
- return 0;
-}
-
-yajl_gen_status libxl_string_list_gen_json(yajl_gen hand, libxl_string_list *pl)
-{
- libxl_string_list l = *pl;
- yajl_gen_status s;
- int i;
-
- s = yajl_gen_array_open(hand);
- if (s != yajl_gen_status_ok) goto out;
-
- if (!l) goto empty;
-
- for (i = 0; l[i] != NULL; i++) {
- s = libxl__yajl_gen_asciiz(hand, l[i]);
- if (s != yajl_gen_status_ok) goto out;
- }
-empty:
- s = yajl_gen_array_close(hand);
-out:
- return s;
-}
-
-int libxl__string_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_string_list *p)
-{
- const libxl__json_object *t;
- libxl_string_list l;
- flexarray_t *array = NULL;
- int i, size;
-
- if (!libxl__json_object_is_array(o))
- return ERROR_FAIL;
-
- array = libxl__json_object_get_array(o);
- size = array->count;
-
- if (size == 0) {
- *p = NULL;
- return 0;
- }
-
- /* need one extra slot as sentinel */
- l = *p = libxl__calloc(NOGC, size + 1, sizeof(char *));
-
- for (i = 0; (t = libxl__json_array_get(o, i)); i++) {
- if (!libxl__json_object_is_string(t))
- return ERROR_FAIL;
-
- l[i] = libxl__strdup(NOGC, libxl__json_object_get_string(t));
- }
-
- return 0;
-}
-
-yajl_gen_status libxl_mac_gen_json(yajl_gen hand, libxl_mac *mac)
-{
- char buf[LIBXL_MAC_FMTLEN+1];
- snprintf(buf, sizeof(buf), LIBXL_MAC_FMT, LIBXL_MAC_BYTES((*mac)));
- return yajl_gen_string(hand, (const unsigned char *)buf, LIBXL_MAC_FMTLEN);
-}
-
-int libxl__mac_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_mac *p)
-{
- if (!libxl__json_object_is_string(o))
- return ERROR_FAIL;
-
- return libxl__parse_mac(libxl__json_object_get_string(o), *p);
-}
-
-yajl_gen_status libxl_hwcap_gen_json(yajl_gen hand,
- libxl_hwcap *p)
-{
- yajl_gen_status s;
- int i;
-
- s = yajl_gen_array_open(hand);
- if (s != yajl_gen_status_ok) goto out;
-
- for(i=0; i<4; i++) {
- s = yajl_gen_integer(hand, (*p)[i]);
- if (s != yajl_gen_status_ok) goto out;
- }
- s = yajl_gen_array_close(hand);
-out:
- return s;
-}
-
-int libxl__hwcap_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_hwcap *p)
-{
- int i;
-
- if (!libxl__json_object_is_array(o))
- return ERROR_FAIL;
-
- for (i = 0; i<4; i++) {
- const libxl__json_object *t;
-
- t = libxl__json_array_get(o, i);
- if (!t || !libxl__json_object_is_integer(t))
- return ERROR_FAIL;
-
- (*p)[i] = libxl__json_object_get_integer(t);
- }
-
- return 0;
-}
-
-yajl_gen_status libxl_ms_vm_genid_gen_json(yajl_gen hand, libxl_ms_vm_genid *p)
-{
- yajl_gen_status s;
- int i;
-
- s = yajl_gen_array_open(hand);
- if (s != yajl_gen_status_ok)
- return s;
-
- for (i = 0; i < LIBXL_MS_VM_GENID_LEN; i++) {
- s = yajl_gen_integer(hand, p->bytes[i]);
- if (s != yajl_gen_status_ok)
- return s;
- }
-
- return yajl_gen_array_close(hand);
-}
-
-int libxl__ms_vm_genid_parse_json(libxl__gc *gc, const libxl__json_object *o,
- libxl_ms_vm_genid *p)
-{
- unsigned int i;
-
- if (!libxl__json_object_is_array(o))
- return ERROR_FAIL;
-
- for (i = 0; i < LIBXL_MS_VM_GENID_LEN; i++) {
- const libxl__json_object *t;
-
- t = libxl__json_array_get(o, i);
- if (!t || !libxl__json_object_is_integer(t))
- return ERROR_FAIL;
-
- p->bytes[i] = libxl__json_object_get_integer(t);
- }
-
- return 0;
-}
-
-yajl_gen_status libxl__string_gen_json(yajl_gen hand,
- const char *p)
-{
- if (p)
- return libxl__yajl_gen_asciiz(hand, p);
- else
- return yajl_gen_null(hand);
-}
-
-int libxl__string_parse_json(libxl__gc *gc, const libxl__json_object *o,
- char **p)
-{
- if (!libxl__json_object_is_string(o) && !libxl__json_object_is_null(o))
- return ERROR_FAIL;
-
- if (libxl__json_object_is_null(o))
- *p = NULL;
- else
- *p = libxl__strdup(NOGC, libxl__json_object_get_string(o));
-
- return 0;
-}
-
-/*
- * libxl__json_object helper functions
- */
-
-libxl__json_object *libxl__json_object_alloc(libxl__gc *gc,
- libxl__json_node_type type)
-{
- libxl__json_object *obj;
-
- obj = libxl__zalloc(gc, sizeof(*obj));
-
- obj->type = type;
-
- if (type == JSON_MAP || type == JSON_ARRAY) {
- flexarray_t *array = flexarray_make(gc, 1, 1);
- if (type == JSON_MAP)
- obj->u.map = array;
- else
- obj->u.array = array;
- }
-
- return obj;
-}
-
-static int libxl__json_object_append_to(libxl__gc *gc,
- libxl__json_object *obj,
- libxl__yajl_ctx *ctx)
-{
- libxl__json_object *dst = ctx->current;
-
- if (dst) {
- switch (dst->type) {
- case JSON_MAP: {
- libxl__json_map_node *last;
-
- if (dst->u.map->count == 0) {
- LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR,
- "Try to add a value to an empty map (with no key)");
- return ERROR_FAIL;
- }
- flexarray_get(dst->u.map, dst->u.map->count - 1, (void**)&last);
- last->obj = obj;
- break;
- }
- case JSON_ARRAY:
- flexarray_append(dst->u.array, obj);
- break;
- default:
- LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR,
- "Try append an object is not a map/array (%i)",
- dst->type);
- return ERROR_FAIL;
- }
- }
-
- obj->parent = dst;
-
- if (libxl__json_object_is_map(obj) || libxl__json_object_is_array(obj))
- ctx->current = obj;
- if (ctx->head == NULL)
- ctx->head = obj;
-
- return 0;
-}
-
-void libxl__json_object_free(libxl__gc *gc, libxl__json_object *obj)
-{
- int idx = 0;
-
- if (obj == NULL)
- return;
- switch (obj->type) {
- case JSON_STRING:
- case JSON_NUMBER:
- free(obj->u.string);
- break;
- case JSON_MAP: {
- libxl__json_map_node *node = NULL;
-
- for (idx = 0; idx < obj->u.map->count; idx++) {
- if (flexarray_get(obj->u.map, idx, (void**)&node) != 0)
- break;
- libxl__json_object_free(gc, node->obj);
- free(node->map_key);
- free(node);
- node = NULL;
- }
- flexarray_free(obj->u.map);
- break;
- }
- case JSON_ARRAY: {
- libxl__json_object *node = NULL;
-
- for (idx = 0; idx < obj->u.array->count; idx++) {
- if (flexarray_get(obj->u.array, idx, (void**)&node) != 0)
- break;
- libxl__json_object_free(gc, node);
- node = NULL;
- }
- flexarray_free(obj->u.array);
- break;
- }
- default:
- break;
- }
- free(obj);
-}
-
-libxl__json_object *libxl__json_array_get(const libxl__json_object *o, int i)
-{
- flexarray_t *array = NULL;
- libxl__json_object *obj = NULL;
-
- if ((array = libxl__json_object_get_array(o)) == NULL) {
- return NULL;
- }
-
- if (i >= array->count)
- return NULL;
-
- if (flexarray_get(array, i, (void**)&obj) != 0)
- return NULL;
-
- return obj;
-}
-
-libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o,
- int i)
-{
- flexarray_t *array = NULL;
- libxl__json_map_node *obj = NULL;
-
- if ((array = libxl__json_object_get_map(o)) == NULL) {
- return NULL;
- }
-
- if (i >= array->count)
- return NULL;
-
- if (flexarray_get(array, i, (void**)&obj) != 0)
- return NULL;
-
- return obj;
-}
-
-const libxl__json_object *libxl__json_map_get(const char *key,
- const libxl__json_object *o,
- libxl__json_node_type expected_type)
-{
- flexarray_t *maps = NULL;
- int idx = 0;
-
- if (libxl__json_object_is_map(o)) {
- libxl__json_map_node *node = NULL;
-
- maps = o->u.map;
- for (idx = 0; idx < maps->count; idx++) {
- if (flexarray_get(maps, idx, (void**)&node) != 0)
- return NULL;
- if (strcmp(key, node->map_key) == 0) {
- if (expected_type == JSON_ANY
- || (node->obj && (node->obj->type & expected_type))) {
- return node->obj;
- } else {
- return NULL;
- }
- }
- }
- }
- return NULL;
-}
-
-yajl_status libxl__json_object_to_yajl_gen(libxl__gc *gc,
- yajl_gen hand,
- const libxl__json_object *obj)
-{
- int idx = 0;
- yajl_status rc;
-
-#define CONVERT_YAJL_GEN_TO_STATUS(gen) \
- ((gen) == yajl_gen_status_ok ? yajl_status_ok : yajl_status_error)
-
- switch (obj->type) {
- case JSON_NULL:
- return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_null(hand));
- case JSON_BOOL:
- return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_bool(hand, obj->u.b));
- case JSON_INTEGER:
- return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_integer(hand, obj->u.i));
- case JSON_DOUBLE:
- return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_double(hand, obj->u.d));
- case JSON_NUMBER:
- return CONVERT_YAJL_GEN_TO_STATUS(
- yajl_gen_number(hand, obj->u.string, strlen(obj->u.string)));
- case JSON_STRING:
- return CONVERT_YAJL_GEN_TO_STATUS(
- libxl__yajl_gen_asciiz(hand, obj->u.string));
- case JSON_MAP: {
- libxl__json_map_node *node = NULL;
-
- rc = CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_map_open(hand));
- if (rc != yajl_status_ok)
- return rc;
- for (idx = 0; idx < obj->u.map->count; idx++) {
- if (flexarray_get(obj->u.map, idx, (void**)&node) != 0)
- break;
-
- rc = CONVERT_YAJL_GEN_TO_STATUS(
- libxl__yajl_gen_asciiz(hand, node->map_key));
- if (rc != yajl_status_ok)
- return rc;
- rc = libxl__json_object_to_yajl_gen(gc, hand, node->obj);
- if (rc != yajl_status_ok)
- return rc;
- }
- return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_map_close(hand));
- }
- case JSON_ARRAY: {
- libxl__json_object *node = NULL;
-
- rc = CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_array_open(hand));
- if (rc != yajl_status_ok)
- return rc;
- for (idx = 0; idx < obj->u.array->count; idx++) {
- if (flexarray_get(obj->u.array, idx, (void**)&node) != 0)
- break;
- rc = libxl__json_object_to_yajl_gen(gc, hand, node);
- if (rc != yajl_status_ok)
- return rc;
- }
- return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_array_close(hand));
- }
- case JSON_ANY:
- /* JSON_ANY is not a valid value for obj->type. */
- ;
- }
- abort();
-#undef CONVERT_YAJL_GEN_TO_STATUS
-}
-
-
-/*
- * JSON callbacks
- */
-
-static int json_callback_null(void *opaque)
-{
- libxl__yajl_ctx *ctx = opaque;
- libxl__json_object *obj;
-
- DEBUG_GEN(ctx, null);
-
- obj = libxl__json_object_alloc(ctx->gc, JSON_NULL);
-
- if (libxl__json_object_append_to(ctx->gc, obj, ctx))
- return 0;
-
- return 1;
-}
-
-static int json_callback_boolean(void *opaque, int boolean)
-{
- libxl__yajl_ctx *ctx = opaque;
- libxl__json_object *obj;
-
- DEBUG_GEN_VALUE(ctx, bool, boolean);
-
- obj = libxl__json_object_alloc(ctx->gc, JSON_BOOL);
- obj->u.b = boolean;
-
- if (libxl__json_object_append_to(ctx->gc, obj, ctx))
- return 0;
-
- return 1;
-}
-
-static bool is_decimal(const char *s, unsigned len)
-{
- const char *end = s + len;
- for (; s < end; s++) {
- if (*s == '.')
- return true;
- }
- return false;
-}
-
-static int json_callback_number(void *opaque, const char *s, libxl_yajl_length len)
-{
- libxl__yajl_ctx *ctx = opaque;
- libxl__json_object *obj = NULL;
- char *t = NULL;
-
- DEBUG_GEN_NUMBER(ctx, s, len);
-
- if (is_decimal(s, len)) {
- double d = strtod(s, NULL);
-
- if ((d == HUGE_VALF || d == HUGE_VALL) && errno == ERANGE) {
- goto error;
- }
-
- obj = libxl__json_object_alloc(ctx->gc, JSON_DOUBLE);
- obj->u.d = d;
- } else {
- long long i = strtoll(s, NULL, 10);
-
- if ((i == LLONG_MIN || i == LLONG_MAX) && errno == ERANGE) {
- goto error;
- }
-
- obj = libxl__json_object_alloc(ctx->gc, JSON_INTEGER);
- obj->u.i = i;
- }
- goto out;
-
-error:
- /* If the conversion fail, we just store the original string. */
- obj = libxl__json_object_alloc(ctx->gc, JSON_NUMBER);
-
- t = libxl__zalloc(ctx->gc, len + 1);
- strncpy(t, s, len);
- t[len] = 0;
-
- obj->u.string = t;
-
-out:
- if (libxl__json_object_append_to(ctx->gc, obj, ctx))
- return 0;
-
- return 1;
-}
-
-static int json_callback_string(void *opaque, const unsigned char *str,
- libxl_yajl_length len)
-{
- libxl__yajl_ctx *ctx = opaque;
- char *t = NULL;
- libxl__json_object *obj = NULL;
-
- t = libxl__zalloc(ctx->gc, len + 1);
-
- DEBUG_GEN_STRING(ctx, str, len);
-
- strncpy(t, (const char *) str, len);
- t[len] = 0;
-
- obj = libxl__json_object_alloc(ctx->gc, JSON_STRING);
- obj->u.string = t;
-
- if (libxl__json_object_append_to(ctx->gc, obj, ctx))
- return 0;
-
- return 1;
-}
-
-static int json_callback_map_key(void *opaque, const unsigned char *str,
- libxl_yajl_length len)
-{
- libxl__yajl_ctx *ctx = opaque;
- char *t = NULL;
- libxl__json_object *obj = ctx->current;
- libxl__gc *gc = ctx->gc;
-
- t = libxl__zalloc(gc, len + 1);
-
- DEBUG_GEN_STRING(ctx, str, len);
-
- strncpy(t, (const char *) str, len);
- t[len] = 0;
-
- if (libxl__json_object_is_map(obj)) {
- libxl__json_map_node *node;
-
- GCNEW(node);
- node->map_key = t;
- node->obj = NULL;
-
- flexarray_append(obj->u.map, node);
- } else {
- LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
- "Current json object is not a map");
- return 0;
- }
-
- return 1;
-}
-
-static int json_callback_start_map(void *opaque)
-{
- libxl__yajl_ctx *ctx = opaque;
- libxl__json_object *obj = NULL;
-
- DEBUG_GEN(ctx, map_open);
-
- obj = libxl__json_object_alloc(ctx->gc, JSON_MAP);
-
- if (libxl__json_object_append_to(ctx->gc, obj, ctx))
- return 0;
-
- return 1;
-}
-
-static int json_callback_end_map(void *opaque)
-{
- libxl__yajl_ctx *ctx = opaque;
-
- DEBUG_GEN(ctx, map_close);
-
- if (ctx->current) {
- ctx->current = ctx->current->parent;
- } else {
- LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
- "No current libxl__json_object, cannot use his parent.");
- return 0;
- }
-
- return 1;
-}
-
-static int json_callback_start_array(void *opaque)
-{
- libxl__yajl_ctx *ctx = opaque;
- libxl__json_object *obj = NULL;
-
- DEBUG_GEN(ctx, array_open);
-
- obj = libxl__json_object_alloc(ctx->gc, JSON_ARRAY);
-
- if (libxl__json_object_append_to(ctx->gc, obj, ctx))
- return 0;
-
- return 1;
-}
-
-static int json_callback_end_array(void *opaque)
-{
- libxl__yajl_ctx *ctx = opaque;
-
- DEBUG_GEN(ctx, array_close);
-
- if (ctx->current) {
- ctx->current = ctx->current->parent;
- } else {
- LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
- "No current libxl__json_object, cannot use his parent.");
- return 0;
- }
-
- return 1;
-}
-
-static yajl_callbacks callbacks = {
- json_callback_null,
- json_callback_boolean,
- NULL,
- NULL,
- json_callback_number,
- json_callback_string,
- json_callback_start_map,
- json_callback_map_key,
- json_callback_end_map,
- json_callback_start_array,
- json_callback_end_array
-};
-
-static void yajl_ctx_free(libxl__yajl_ctx *yajl_ctx)
-{
- if (yajl_ctx->hand) {
- yajl_free(yajl_ctx->hand);
- yajl_ctx->hand = NULL;
- }
- DEBUG_GEN_FREE(yajl_ctx);
-}
-
-libxl__json_object *libxl__json_parse(libxl__gc *gc, const char *s)
-{
- yajl_status status;
- libxl__yajl_ctx yajl_ctx;
- libxl__json_object *o = NULL;
- unsigned char *str = NULL;
-
- memset(&yajl_ctx, 0, sizeof (yajl_ctx));
- yajl_ctx.gc = gc;
-
- DEBUG_GEN_ALLOC(&yajl_ctx);
-
- if (yajl_ctx.hand == NULL) {
- yajl_ctx.hand = libxl__yajl_alloc(&callbacks, NULL, &yajl_ctx);
- }
- status = yajl_parse(yajl_ctx.hand, (const unsigned char *)s, strlen(s));
- if (status != yajl_status_ok)
- goto out;
-
- status = yajl_complete_parse(yajl_ctx.hand);
- if (status != yajl_status_ok)
- goto out;
-
- o = yajl_ctx.head;
-
- DEBUG_GEN_REPORT(&yajl_ctx);
-
- yajl_ctx.head = NULL;
-
- yajl_ctx_free(&yajl_ctx);
- return o;
-
-out:
- str = yajl_get_error(yajl_ctx.hand, 1, (const unsigned char*)s, strlen(s));
-
- LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR, "yajl error: %s", str);
- yajl_free_error(yajl_ctx.hand, str);
- yajl_ctx_free(&yajl_ctx);
- return NULL;
-}
-
-static const char *yajl_gen_status_to_string(yajl_gen_status s)
-{
- switch (s) {
- case yajl_gen_status_ok: abort();
- case yajl_gen_keys_must_be_strings:
- return "keys must be strings";
- case yajl_max_depth_exceeded:
- return "max depth exceeded";
- case yajl_gen_in_error_state:
- return "in error state";
- case yajl_gen_generation_complete:
- return "generation complete";
- case yajl_gen_invalid_number:
- return "invalid number";
-#if 0 /* This is in the docs but not implemented in the version I am running. */
- case yajl_gen_no_buf:
- return "no buffer";
- case yajl_gen_invalid_string:
- return "invalid string";
-#endif
- default:
- return "unknown error";
- }
-}
-
-char *libxl__object_to_json(libxl_ctx *ctx, const char *type,
- libxl__gen_json_callback gen, void *p)
-{
- const unsigned char *buf;
- char *ret = NULL;
- libxl_yajl_length len = 0;
- yajl_gen_status s;
- yajl_gen hand;
-
- hand = libxl_yajl_gen_alloc(NULL);
- if (!hand)
- return NULL;
-
- s = gen(hand, p);
- if (s != yajl_gen_status_ok)
- goto out;
-
- s = yajl_gen_get_buf(hand, &buf, &len);
- if (s != yajl_gen_status_ok)
- goto out;
- ret = strdup((const char *)buf);
-
-out:
- yajl_gen_free(hand);
-
- if (s != yajl_gen_status_ok) {
- LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
- "unable to convert %s to JSON representation. "
- "YAJL error code %d: %s", type,
- s, yajl_gen_status_to_string(s));
- } else if (!ret) {
- LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
- "unable to allocate space for to JSON representation of %s",
- type);
- }
-
- return ret;
-}
-
-char *libxl__json_object_to_json(libxl__gc *gc,
- const libxl__json_object *args)
-{
- const unsigned char *buf;
- libxl_yajl_length len;
- yajl_gen_status s;
- yajl_gen hand;
- char *ret = NULL;
- int rc;
-
- if (!args)
- return NULL;
-
- hand = libxl_yajl_gen_alloc(NULL);
- if (!hand)
- return NULL;
-
- rc = libxl__json_object_to_yajl_gen(gc, hand, args);
- if (rc)
- goto out;
-
- s = yajl_gen_get_buf(hand, &buf, &len);
- if (s)
- goto out;
-
- ret = libxl__strndup(gc, (const char *)buf, len);
-
-out:
- yajl_gen_free(hand);
- return ret;
-}
-
-yajl_gen_status libxl__uint64_gen_json(yajl_gen hand, uint64_t val)
-{
- char *num;
- int len;
- yajl_gen_status s;
-
-
- len = asprintf(&num, "%"PRIu64, val);
- if (len == -1) {
- s = yajl_gen_in_error_state;
- goto out;
- }
-
- s = yajl_gen_number(hand, num, len);
-
- free(num);
-
-out:
- return s;
-}
-
-int libxl__object_from_json(libxl_ctx *ctx, const char *type,
- libxl__json_parse_callback parse,
- void *p, const char *s)
-{
- GC_INIT(ctx);
- libxl__json_object *o;
- int rc;
-
- o = libxl__json_parse(gc, s);
- if (!o) {
- LOG(ERROR,
- "unable to generate libxl__json_object from JSON representation of %s.",
- type);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = parse(gc, o, p);
- if (rc) {
- LOG(ERROR, "unable to convert libxl__json_object to %s. (rc=%d)", type, rc);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl__int_parse_json(libxl__gc *gc, const libxl__json_object *o,
- void *p)
-{
- long long i;
-
- if (!libxl__json_object_is_integer(o))
- return ERROR_FAIL;
-
- i = libxl__json_object_get_integer(o);
-
- if (i > INT_MAX || i < INT_MIN)
- return ERROR_FAIL;
-
- *((int *)p) = i;
-
- return 0;
-}
-
-/* Macro to generate:
- * libxl__uint8_parse_json
- * libxl__uint16_parse_json
- * libxl__uint32_parse_json
- */
-#define PARSE_UINT(width) \
- int libxl__uint ## width ## _parse_json(libxl__gc *gc, \
- const libxl__json_object *o,\
- void *p) \
- { \
- long long i; \
- \
- if (!libxl__json_object_is_integer(o)) \
- return ERROR_FAIL; \
- \
- i = libxl__json_object_get_integer(o); \
- \
- if (i < 0 || i > UINT ## width ## _MAX) \
- return ERROR_FAIL; \
- \
- *((uint ## width ## _t *)p) = i; \
- \
- return 0; \
- }
-
-PARSE_UINT(8);
-PARSE_UINT(16);
-PARSE_UINT(32);
-
-int libxl__uint64_parse_json(libxl__gc *gc, const libxl__json_object *o,
- void *p)
-{
- if (!libxl__json_object_is_integer(o) &&
- !libxl__json_object_is_number(o))
- return ERROR_FAIL;
-
- if (libxl__json_object_is_integer(o)) {
- long long i = libxl__json_object_get_integer(o);
-
- if (i < 0)
- return ERROR_FAIL;
-
- *((uint64_t *)p) = i;
- } else {
- const char *s;
- unsigned long long i;
- int saved_errno = errno;
-
- s = libxl__json_object_get_number(o);
-
- errno = 0;
- i = strtoull(s, NULL, 10);
-
- if (i == ULLONG_MAX && errno == ERANGE)
- return ERROR_FAIL;
-
- errno = saved_errno;
- *((uint64_t *)p) = i;
- }
-
- return 0;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2011 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#ifndef LIBXL_JSON_H
-#define LIBXL_JSON_H
-
-#include <yajl/yajl_gen.h>
-#include <yajl/yajl_parse.h>
-
-#ifdef HAVE_YAJL_YAJL_VERSION_H
-# include <yajl/yajl_version.h>
-#endif
-
-yajl_gen_status libxl__uint64_gen_json(yajl_gen hand, uint64_t val);
-yajl_gen_status libxl_defbool_gen_json(yajl_gen hand, libxl_defbool *p);
-yajl_gen_status libxl_uuid_gen_json(yajl_gen hand, libxl_uuid *p);
-yajl_gen_status libxl_mac_gen_json(yajl_gen hand, libxl_mac *p);
-yajl_gen_status libxl_bitmap_gen_json(yajl_gen hand, libxl_bitmap *p);
-yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
- libxl_cpuid_policy_list *p);
-yajl_gen_status libxl_string_list_gen_json(yajl_gen hand, libxl_string_list *p);
-yajl_gen_status libxl_key_value_list_gen_json(yajl_gen hand,
- libxl_key_value_list *p);
-yajl_gen_status libxl_hwcap_gen_json(yajl_gen hand, libxl_hwcap *p);
-yajl_gen_status libxl_ms_vm_genid_gen_json(yajl_gen hand, libxl_ms_vm_genid *p);
-
-#include <_libxl_types_json.h>
-
-/* YAJL version check */
-#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
-# define HAVE_YAJL_V2 1
-#endif
-
-#ifdef HAVE_YAJL_V2
-
-typedef size_t libxl_yajl_length;
-
-static inline yajl_handle libxl__yajl_alloc(const yajl_callbacks *callbacks,
- yajl_alloc_funcs *allocFuncs,
- void *ctx)
-{
- yajl_handle hand = yajl_alloc(callbacks, allocFuncs, ctx);
- if (hand)
- yajl_config(hand, yajl_allow_trailing_garbage, 1);
- return hand;
-}
-
-static inline yajl_gen libxl_yajl_gen_alloc(const yajl_alloc_funcs *allocFuncs)
-{
- yajl_gen g;
- g = yajl_gen_alloc(allocFuncs);
- if (g)
- yajl_gen_config(g, yajl_gen_beautify, 1);
- return g;
-}
-
-#else /* !HAVE_YAJL_V2 */
-
-#define yajl_complete_parse yajl_parse_complete
-
-typedef unsigned int libxl_yajl_length;
-
-static inline yajl_handle libxl__yajl_alloc(const yajl_callbacks *callbacks,
- const yajl_alloc_funcs *allocFuncs,
- void *ctx)
-{
- yajl_parser_config cfg = {
- .allowComments = 1,
- .checkUTF8 = 1,
- };
- return yajl_alloc(callbacks, &cfg, allocFuncs, ctx);
-}
-
-static inline yajl_gen libxl_yajl_gen_alloc(const yajl_alloc_funcs *allocFuncs)
-{
- yajl_gen_config conf = { 1, " " };
- return yajl_gen_alloc(&conf, allocFuncs);
-}
-
-#endif /* !HAVE_YAJL_V2 */
-
-yajl_gen_status libxl_domain_config_gen_json(yajl_gen hand,
- libxl_domain_config *p);
-
-#endif /* LIBXL_JSON_H */
+++ /dev/null
-/*
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- *
- * This file is part of libxl, and was originally taken from libfdt.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * Additionally, this particular file is dual licensed. That is,
- * alternatively, at your option:
- *
- * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
- *
- * Note that this applies only to this file, and other files with a
- * similar notice. Also, note that when the same code is distributed
- * along with the rest of libxl, you must comply with the terms of the
- * LGPLv2.1 for the whole of libxl including this file.
- *
- * The intent is to permit, in particular, upstream libfdt to
- * incorporate improvements to this file within upstream libfdt. At
- * the time of writing, upstream libfdt is dual licensed: 2-clause BSD
- * (as above) and GPLv2-or-later. The 2-clause BSD licence is
- * compatible with both GPLv2-or-later and LGPLv2.1-only; this permits
- * copying in both directions, and the optional licence upgrade to a
- * copyleft licence by libdft upstream or the Xen Project,
- * respectively.
- */
-
-#include <libfdt.h>
-
-#include "libxl_libfdt_compat.h"
-
-#ifndef HAVE_FDT_FIRST_SUBNODE
-_hidden int fdt_first_subnode(const void *fdt, int offset)
-{
- int depth = 0;
-
- offset = fdt_next_node(fdt, offset, &depth);
- if (offset < 0 || depth != 1)
- return -FDT_ERR_NOTFOUND;
-
- return offset;
-}
-#endif
-
-#ifndef HAVE_FDT_NEXT_SUBNODE
-_hidden int fdt_next_subnode(const void *fdt, int offset)
-{
- int depth = 1;
-
- /*
- * With respect to the parent, the depth of the next subnode will be
- * the same as the last.
- */
- do {
- offset = fdt_next_node(fdt, offset, &depth);
- if (offset < 0 || depth < 1)
- return -FDT_ERR_NOTFOUND;
- } while (depth > 1);
-
- return offset;
-}
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- *
- * This file is part of libxl, and was originally taken from libfdt.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * Additionally, this particular file is dual licensed. That is,
- * alternatively, at your option:
- *
- * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
- *
- * Note that this applies only to this file, and other files with a
- * similar notice. Also, note that when the same code is distributed
- * along with the rest of libxl, you must comply with the terms of the
- * LGPLv2.1 for the whole of libxl including this file.
- *
- * The intent is to permit, in particular, upstream libfdt to
- * incorporate improvements to this file within upstream libfdt. At
- * the time of writing, upstream libfdt is dual licensed: 2-clause BSD
- * (as above) and GPLv2-or-later. The 2-clause BSD licence is
- * compatible with both GPLv2-or-later and LGPLv2.1-only; this permits
- * copying in both directions, and the optional licence upgrade to a
- * copyleft licence by libdft upstream or the Xen Project,
- * respectively.
- */
-
-#ifndef LIBXL_LIBFDT_COMPAT_H
-#define LIBXL_LIBFDT_COMPAT_H
-
-#include "libxl_internal.h"
-#include <libfdt.h>
-
-#if !HAVE_DECL_FDT_FIRST_SUBNODE
-_hidden int fdt_first_subnode(const void *fdt, int offset);
-#endif
-
-#if !HAVE_DECL_FDT_NEXT_SUBNODE
-_hidden int fdt_next_subnode(const void *fdt, int offset);
-#endif
-
-#if !HAVE_DECL_FDT_PROPERTY_U32
-static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val)
-{
- uint32_t tmp = cpu_to_fdt32(val);
- return fdt_property(fdt, name, &tmp, sizeof(tmp));
-}
-#endif
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2011
- * Author Roger Pau Monne <roger.pau@entel.upc.edu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <sys/resource.h>
-#include "libxl_internal.h"
-
-
-/* Workarounds for Linux-specific lacks can go here: */
-
-#ifndef CLONE_NEWIPC /* Available as of Linux 2.6.19 / glibc 2.8 */
-# define CLONE_NEWIPC 0x08000000
-#endif
-
-
-int libxl__try_phy_backend(mode_t st_mode)
-{
- if (S_ISBLK(st_mode) || S_ISREG(st_mode)) {
- return 1;
- }
-
- return 0;
-}
-
-char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
-{
- return libxl__devid_to_vdev(gc, devid);
-}
-
-/* Hotplug scripts helpers */
-
-static char **get_hotplug_env(libxl__gc *gc,
- char *script, libxl__device *dev)
-{
- const char *type = libxl__device_kind_to_string(dev->backend_kind);
- char *be_path = libxl__device_backend_path(gc, dev);
- char **env;
- int nr = 0;
-
- const int arraysize = 15;
- GCNEW_ARRAY(env, arraysize);
- env[nr++] = "script";
- env[nr++] = script;
- env[nr++] = "XENBUS_TYPE";
- env[nr++] = (char *) type;
- env[nr++] = "XENBUS_PATH";
- env[nr++] = GCSPRINTF("backend/%s/%u/%d", type, dev->domid, dev->devid);
- env[nr++] = "XENBUS_BASE_PATH";
- env[nr++] = "backend";
- if (dev->backend_kind == LIBXL__DEVICE_KIND_VIF) {
- libxl_nic_type nictype;
- char *gatewaydev;
-
- gatewaydev = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/%s", be_path, "gatewaydev"));
- env[nr++] = "netdev";
- env[nr++] = gatewaydev ? : "";
-
- if (libxl__nic_type(gc, dev, &nictype)) {
- LOGD(ERROR, dev->domid, "unable to get nictype");
- return NULL;
- }
- switch (nictype) {
- case LIBXL_NIC_TYPE_VIF_IOEMU:
- env[nr++] = "INTERFACE";
- env[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
- dev->devid,
- LIBXL_NIC_TYPE_VIF_IOEMU);
- /*
- * We need to fall through because for PV_IOEMU nic types we need
- * to execute both the vif and the tap hotplug script, and we
- * don't know which one we are executing in this call, so provide
- * both env variables.
- */
- case LIBXL_NIC_TYPE_VIF:
- env[nr++] = "vif";
- env[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
- dev->devid,
- LIBXL_NIC_TYPE_VIF);
- break;
- default:
- return NULL;
- }
- }
-
- env[nr++] = NULL;
- assert(nr <= arraysize);
-
- return env;
-}
-
-/* Hotplug scripts caller functions */
-
-static int libxl__hotplug_nic(libxl__gc *gc, libxl__device *dev,
- char ***args, char ***env,
- libxl__device_action action, int num_exec)
-{
- char *be_path = libxl__device_backend_path(gc, dev);
- char *script;
- int nr = 0, rc = 0;
- libxl_nic_type nictype;
-
- script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path,
- "script"));
- if (!script) {
- LOGED(ERROR, dev->domid,
- "unable to read script from %s", be_path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__nic_type(gc, dev, &nictype);
- if (rc) {
- LOGD(ERROR, dev->domid, "error when fetching nic type");
- rc = ERROR_FAIL;
- goto out;
- }
- if (nictype == LIBXL_NIC_TYPE_VIF && num_exec != 0) {
- rc = 0;
- goto out;
- }
-
- *env = get_hotplug_env(gc, script, dev);
- if (!*env) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- const int arraysize = 4;
- GCNEW_ARRAY(*args, arraysize);
- (*args)[nr++] = script;
-
- if (nictype == LIBXL_NIC_TYPE_VIF_IOEMU && num_exec) {
- (*args)[nr++] = (char *) libxl__device_action_to_string(action);
- (*args)[nr++] = "type_if=tap";
- (*args)[nr++] = NULL;
- } else {
- (*args)[nr++] = action == LIBXL__DEVICE_ACTION_ADD ? "online" :
- "offline";
- (*args)[nr++] = "type_if=vif";
- (*args)[nr++] = NULL;
- }
- assert(nr == arraysize);
- rc = 1;
-
-out:
- return rc;
-}
-
-static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev,
- char ***args, char ***env,
- libxl__device_action action)
-{
- char *be_path = libxl__device_backend_path(gc, dev);
- char *script;
- int nr = 0, rc = 0;
-
- script = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/%s", be_path, "script"));
- if (!script) {
- LOGEVD(ERROR, errno, dev->domid,
- "unable to read script from %s", be_path);
- rc = ERROR_FAIL;
- goto error;
- }
-
- *env = get_hotplug_env(gc, script, dev);
- if (!*env) {
- LOGD(ERROR, dev->domid, "Failed to get hotplug environment");
- rc = ERROR_FAIL;
- goto error;
- }
-
- const int arraysize = 3;
- GCNEW_ARRAY(*args, arraysize);
- (*args)[nr++] = script;
- (*args)[nr++] = (char *) libxl__device_action_to_string(action);
- (*args)[nr++] = NULL;
- assert(nr == arraysize);
-
- LOGD(DEBUG, dev->domid, "Args and environment ready");
- rc = 1;
-
-error:
- return rc;
-}
-
-int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
- char ***args, char ***env,
- libxl__device_action action,
- int num_exec)
-{
- int rc;
-
- switch (dev->backend_kind) {
- case LIBXL__DEVICE_KIND_VBD:
- if (num_exec != 0) {
- LOGD(DEBUG, dev->domid,
- "num_exec %d, not running hotplug scripts", num_exec);
- rc = 0;
- goto out;
- }
- rc = libxl__hotplug_disk(gc, dev, args, env, action);
- break;
- case LIBXL__DEVICE_KIND_VIF:
- /*
- * If domain has a stubdom we don't have to execute hotplug scripts
- * for emulated interfaces
- */
- if ((num_exec > 1) ||
- (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
- LOGD(DEBUG, dev->domid,
- "num_exec %d, not running hotplug scripts", num_exec);
- rc = 0;
- goto out;
- }
- rc = libxl__hotplug_nic(gc, dev, args, env, action, num_exec);
- break;
- default:
- /* No need to execute any hotplug scripts */
- LOGD(DEBUG, dev->domid,
- "backend_kind %d, no need to execute scripts", dev->backend_kind);
- rc = 0;
- break;
- }
-
-out:
- return rc;
-}
-
-libxl_device_model_version libxl__default_device_model(libxl__gc *gc)
-{
- return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
-}
-
-int libxl__pci_numdevs(libxl__gc *gc)
-{
- DIR *dir;
- struct dirent *entry;
- int num_devs = 0;
-
- dir = opendir("/sys/bus/pci/devices");
- if (!dir) {
- LOGE(ERROR, "Cannot open /sys/bus/pci/devices");
- return ERROR_FAIL;
- }
-
- while ((entry = readdir(dir))) {
- if (entry->d_name[0] == '.')
- continue;
- num_devs++;
- }
- closedir(dir);
-
- return num_devs;
-}
-
-int libxl__pci_topology_init(libxl__gc *gc,
- physdev_pci_device_t *devs,
- int num_devs)
-{
-
- DIR *dir;
- struct dirent *entry;
- int i, err = 0;
-
- dir = opendir("/sys/bus/pci/devices");
- if (!dir) {
- LOGE(ERROR, "Cannot open /sys/bus/pci/devices");
- return ERROR_FAIL;
- }
-
- i = 0;
- while ((entry = readdir(dir))) {
- unsigned int dom, bus, dev, func;
-
- if (entry->d_name[0] == '.')
- continue;
-
- if (i == num_devs) {
- LOG(ERROR, "Too many devices");
- err = ERROR_FAIL;
- errno = -ENOSPC;
- goto out;
- }
-
- if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4) {
- LOGE(ERROR, "Error processing /sys/bus/pci/devices");
- err = ERROR_FAIL;
- goto out;
- }
-
- devs[i].seg = dom;
- devs[i].bus = bus;
- devs[i].devfn = ((dev & 0x1f) << 3) | (func & 7);
-
- i++;
- }
-
- out:
- closedir(dir);
-
- return err;
-}
-
-static struct {
- int resource;
- rlim_t limit;
-} rlimits[] = {
-#define RLIMIT_ENTRY(r, l) \
- { .resource = r, .limit = l }
- /* Big enough for log files, not big enough for a DoS */
- RLIMIT_ENTRY(RLIMIT_FSIZE, 256*1024),
-
- /* Shouldn't need any of these */
- RLIMIT_ENTRY(RLIMIT_CORE, 0),
- RLIMIT_ENTRY(RLIMIT_MSGQUEUE, 0),
- RLIMIT_ENTRY(RLIMIT_LOCKS, 0),
- RLIMIT_ENTRY(RLIMIT_MEMLOCK, 0),
-
- /* End-of-list marker */
- RLIMIT_ENTRY(RLIMIT_NLIMITS, 0),
-#undef RLIMIT_ENTRY
-};
-
-int libxl__local_dm_preexec_restrict(libxl__gc *gc)
-{
- int r;
- unsigned i;
-
- /* Unshare mount and IPC namespaces. These are unused by QEMU. */
- r = unshare(CLONE_NEWNS | CLONE_NEWIPC);
- if (r) {
- LOGE(ERROR, "libxl: unshare Mount and IPC namespace failed");
- return ERROR_FAIL;
- }
-
- /* Set various "easy" rlimits */
- for (i = 0; rlimits[i].resource != RLIMIT_NLIMITS; i++) {
- struct rlimit rlim;
-
- rlim.rlim_cur = rlim.rlim_max = rlimits[i].limit;
-
- r = setrlimit(rlimits[i].resource, &rlim);
- if (r < 0) {
- LOGE(ERROR, "Setting rlimit %d to %llu failed\n",
- rlimits[i].resource,
- (unsigned long long)rlimits[i].limit);
- return ERROR_FAIL;
- }
- }
-
- return 0;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-
-/*
- * Set the maximum memory size of the domain in the hypervisor. There is no
- * change of the current memory size involved. The specified memory size can
- * even be above the configured maxmem size of the domain, but the related
- * Xenstore entry memory/static-max isn't modified!
- */
-int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t max_memkb)
-{
- GC_INIT(ctx);
- char *mem, *endptr;
- uint64_t memorykb, size;
- char *dompath = libxl__xs_get_dompath(gc, domid);
- int rc = 1;
- libxl__flock *lock = NULL;
- libxl_domain_config d_config;
-
- libxl_domain_config_init(&d_config);
-
- CTX_LOCK;
-
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- mem = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target", dompath));
- if (!mem) {
- LOGED(ERROR, domid, "Cannot get memory info from %s/memory/target",
- dompath);
- goto out;
- }
- memorykb = strtoull(mem, &endptr, 10);
- if (*endptr != '\0') {
- LOGED(ERROR, domid, "Invalid memory %s from %s/memory/target\n",
- mem, dompath);
- goto out;
- }
-
- if (max_memkb < memorykb) {
- LOGED(ERROR, domid,
- "memory_static_max must be greater than or or equal to memory_dynamic_max");
- goto out;
- }
-
- rc = libxl__get_domain_configuration(gc, domid, &d_config);
- if (rc < 0) {
- LOGE(ERROR, "unable to retrieve domain configuration");
- goto out;
- }
-
- rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size);
- if (rc < 0) {
- LOGE(ERROR, "Couldn't get arch extra constant memory size");
- goto out;
- }
-
- rc = xc_domain_setmaxmem(ctx->xch, domid, max_memkb + size);
- if (rc != 0) {
- LOGED(ERROR, domid,
- "xc_domain_setmaxmem domid=%d memkb=%"PRIu64" failed ""rc=%d\n",
- domid, max_memkb + size, rc);
- goto out;
- }
-
- rc = 0;
-out:
- libxl_domain_config_dispose(&d_config);
- if (lock) libxl__unlock_file(lock);
- CTX_UNLOCK;
- GC_FREE;
- return rc;
-}
-
-static int libxl__fill_dom0_memory_info(libxl__gc *gc, uint64_t *target_memkb,
- uint64_t *max_memkb)
-{
- int rc;
- libxl_dominfo info;
- libxl_physinfo physinfo;
- char *target = NULL, *staticmax = NULL, *endptr = NULL;
- char *target_path = "/local/domain/0/memory/target";
- char *max_path = "/local/domain/0/memory/static-max";
- xs_transaction_t t;
- libxl_ctx *ctx = libxl__gc_owner(gc);
-
- libxl_dominfo_init(&info);
-
-retry_transaction:
- t = xs_transaction_start(ctx->xsh);
-
- target = libxl__xs_read(gc, t, target_path);
- staticmax = libxl__xs_read(gc, t, max_path);
- if (target && staticmax) {
- rc = 0;
- goto out;
- }
-
- if (target) {
- *target_memkb = strtoull(target, &endptr, 10);
- if (*endptr != '\0') {
- LOGED(ERROR, 0, "Invalid memory target %s from %s\n", target,
- target_path);
- rc = ERROR_FAIL;
- goto out;
- }
- }
-
- if (staticmax) {
- *max_memkb = strtoull(staticmax, &endptr, 10);
- if (*endptr != '\0') {
- LOGED(ERROR, 0, "Invalid memory static-max %s from %s\n",
- staticmax,
- max_path);
- rc = ERROR_FAIL;
- goto out;
- }
- }
-
- libxl_dominfo_dispose(&info);
- libxl_dominfo_init(&info);
- rc = libxl_domain_info(ctx, &info, 0);
- if (rc < 0)
- goto out;
-
- rc = libxl_get_physinfo(ctx, &physinfo);
- if (rc < 0)
- goto out;
-
- if (target == NULL) {
- libxl__xs_printf(gc, t, target_path, "%"PRIu64, info.current_memkb);
- *target_memkb = info.current_memkb;
- }
- if (staticmax == NULL) {
- libxl__xs_printf(gc, t, max_path, "%"PRIu64, info.max_memkb);
- *max_memkb = info.max_memkb;
- }
-
- rc = 0;
-
-out:
- if (!xs_transaction_end(ctx->xsh, t, 0)) {
- if (errno == EAGAIN)
- goto retry_transaction;
- else
- rc = ERROR_FAIL;
- }
-
- libxl_dominfo_dispose(&info);
- return rc;
-}
-
-int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid,
- int64_t target_memkb, int relative, int enforce)
-{
- GC_INIT(ctx);
- int rc, r, lrc, abort_transaction = 0;
- uint64_t memorykb, size;
- uint64_t videoram = 0;
- uint64_t current_target_memkb = 0, new_target_memkb = 0;
- uint64_t current_max_memkb = 0;
- char *memmax, *endptr, *videoram_s = NULL, *target = NULL;
- char *dompath = libxl__xs_get_dompath(gc, domid);
- xc_domaininfo_t info;
- libxl_dominfo ptr;
- char *uuid;
- xs_transaction_t t;
- libxl__flock *lock;
- libxl_domain_config d_config;
-
- libxl_domain_config_init(&d_config);
-
- CTX_LOCK;
-
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out_no_transaction;
- }
-
- rc = libxl__get_domain_configuration(gc, domid, &d_config);
- if (rc < 0) {
- LOGE(ERROR, "unable to retrieve domain configuration");
- goto out_no_transaction;
- }
-
- rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size);
- if (rc < 0) {
- LOGE(ERROR, "Couldn't get arch extra constant memory size");
- goto out_no_transaction;
- }
-
-retry_transaction:
- t = xs_transaction_start(ctx->xsh);
-
- target = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/target", dompath));
- if (!target && !domid) {
- if (!xs_transaction_end(ctx->xsh, t, 1)) {
- rc = ERROR_FAIL;
- goto out_no_transaction;
- }
- lrc = libxl__fill_dom0_memory_info(gc, ¤t_target_memkb,
- ¤t_max_memkb);
- if (lrc < 0) { rc = ERROR_FAIL; goto out_no_transaction; }
- goto retry_transaction;
- } else if (!target) {
- LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
- dompath);
- abort_transaction = 1;
- rc = ERROR_FAIL;
- goto out;
- } else {
- current_target_memkb = strtoull(target, &endptr, 10);
- if (*endptr != '\0') {
- LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
- target, dompath);
- abort_transaction = 1;
- rc = ERROR_FAIL;
- goto out;
- }
- }
- memmax = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/static-max", dompath));
- if (!memmax) {
- LOGED(ERROR, domid, "Cannot get memory info from %s/memory/static-max",
- dompath);
- abort_transaction = 1;
- rc = ERROR_FAIL;
- goto out;
- }
- memorykb = strtoull(memmax, &endptr, 10);
- if (*endptr != '\0') {
- LOGED(ERROR, domid, "Invalid max memory %s from %s/memory/static-max\n",
- memmax, dompath);
- abort_transaction = 1;
- rc = ERROR_FAIL;
- goto out;
- }
-
- videoram_s = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/videoram",
- dompath));
- videoram = videoram_s ? atoi(videoram_s) : 0;
-
- if (relative) {
- if (target_memkb < 0 && llabs(target_memkb) > current_target_memkb)
- new_target_memkb = 0;
- else
- new_target_memkb = current_target_memkb + target_memkb;
- } else
- new_target_memkb = target_memkb - videoram;
- if (new_target_memkb > memorykb) {
- LOGD(ERROR, domid,
- "memory_dynamic_max must be less than or equal to"
- " memory_static_max\n");
- abort_transaction = 1;
- rc = ERROR_INVAL;
- goto out;
- }
-
- if (!domid && new_target_memkb < LIBXL_MIN_DOM0_MEM) {
- LOGD(ERROR, domid,
- "New target %"PRIu64" for dom0 is below the minimum threshold",
- new_target_memkb);
- abort_transaction = 1;
- rc = ERROR_INVAL;
- goto out;
- }
-
- if (enforce) {
- memorykb = new_target_memkb + videoram;
- r = xc_domain_setmaxmem(ctx->xch, domid, memorykb + size);
- if (r != 0) {
- LOGED(ERROR, domid,
- "xc_domain_setmaxmem memkb=%"PRIu64" failed ""rc=%d\n",
- memorykb + size,
- r);
- abort_transaction = 1;
- rc = ERROR_FAIL;
- goto out;
- }
- }
-
- if (d_config.c_info.type != LIBXL_DOMAIN_TYPE_PV) {
- r = xc_domain_set_pod_target(ctx->xch, domid,
- (new_target_memkb + size) / 4, NULL, NULL, NULL);
- if (r != 0) {
- LOGED(ERROR, domid,
- "xc_domain_set_pod_target memkb=%"PRIu64" failed rc=%d\n",
- (new_target_memkb + size) / 4,
- r);
- abort_transaction = 1;
- rc = ERROR_FAIL;
- goto out;
- }
- }
-
- libxl__xs_printf(gc, t, GCSPRINTF("%s/memory/target", dompath),
- "%"PRIu64, new_target_memkb);
-
- r = xc_domain_getinfolist(ctx->xch, domid, 1, &info);
- if (r != 1 || info.domain != domid) {
- abort_transaction = 1;
- rc = ERROR_FAIL;
- goto out;
- }
-
- libxl_dominfo_init(&ptr);
- libxl__xcinfo2xlinfo(ctx, &info, &ptr);
- uuid = libxl__uuid2string(gc, ptr.uuid);
- libxl__xs_printf(gc, t, GCSPRINTF("/vm/%s/memory", uuid),
- "%"PRIu64, new_target_memkb / 1024);
- libxl_dominfo_dispose(&ptr);
-
- rc = 0;
-out:
- if (!xs_transaction_end(ctx->xsh, t, abort_transaction)
- && !abort_transaction)
- if (errno == EAGAIN)
- goto retry_transaction;
-
-out_no_transaction:
- libxl_domain_config_dispose(&d_config);
- if (lock) libxl__unlock_file(lock);
- CTX_UNLOCK;
- GC_FREE;
- return rc;
-}
-
-/* out_target_memkb and out_max_memkb can be NULL */
-int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
- uint64_t *out_target_memkb,
- uint64_t *out_max_memkb)
-{
- int rc;
- char *target = NULL, *static_max = NULL, *endptr = NULL;
- char *dompath = libxl__xs_get_dompath(gc, domid);
- uint64_t target_memkb, max_memkb;
-
- target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target",
- dompath));
- static_max = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/memory/static-max", dompath));
-
- rc = ERROR_FAIL;
- if ((!target || !static_max) && !domid) {
- rc = libxl__fill_dom0_memory_info(gc, &target_memkb,
- &max_memkb);
- if (rc < 0)
- goto out;
- } else if (!target) {
- LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
- dompath);
- goto out;
- } else if (!static_max) {
- LOGED(ERROR, domid,
- "Cannot get target memory info from %s/memory/static-max",
- dompath);
- goto out;
- } else {
- target_memkb = strtoull(target, &endptr, 10);
- if (*endptr != '\0') {
- LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
- target, dompath);
- goto out;
- }
- max_memkb = strtoull(static_max, &endptr, 10);
- if (*endptr != '\0') {
- LOGED(ERROR, domid,
- "Invalid memory target %s from %s/memory/static-max\n",
- static_max,
- dompath);
- goto out;
- }
-
- }
-
- if (out_target_memkb)
- *out_target_memkb = target_memkb;
-
- if (out_max_memkb)
- *out_max_memkb = max_memkb;
-
- rc = 0;
-
-out:
- return rc;
-}
-
-static int libxl__memkb_64to32(libxl_ctx *ctx, int rc,
- uint64_t val64, uint32_t *ptr32)
-{
- GC_INIT(ctx);
-
- if (rc)
- goto out;
-
- *ptr32 = val64;
- if (*ptr32 == val64)
- goto out;
-
- LOGE(ERROR, "memory size %"PRIu64" too large for 32 bit value\n", val64);
- rc = ERROR_FAIL;
-
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid,
- uint64_t *out_target)
-{
- GC_INIT(ctx);
- int rc;
-
- rc = libxl__get_memory_target(gc, domid, out_target, NULL);
-
- GC_FREE;
- return rc;
-}
-
-int libxl_get_memory_target_0x040700(
- libxl_ctx *ctx, uint32_t domid, uint32_t *out_target)
-{
- uint64_t my_out_target;
- int rc;
-
- rc = libxl_get_memory_target(ctx, domid, &my_out_target);
- return libxl__memkb_64to32(ctx, rc, my_out_target, out_target);
-}
-
-int libxl__domain_need_memory_calculate(libxl__gc *gc,
- libxl_domain_build_info *b_info,
- uint64_t *need_memkb)
-{
- int rc;
-
- *need_memkb = b_info->target_memkb;
- *need_memkb += b_info->shadow_memkb + b_info->iommu_memkb;
-
- switch (b_info->type) {
- case LIBXL_DOMAIN_TYPE_PVH:
- case LIBXL_DOMAIN_TYPE_HVM:
- *need_memkb += LIBXL_HVM_EXTRA_MEMORY;
- if (libxl_defbool_val(b_info->device_model_stubdomain)) {
- *need_memkb += b_info->stubdomain_memkb;
- *need_memkb += b_info->video_memkb;
- }
- break;
- case LIBXL_DOMAIN_TYPE_PV:
- *need_memkb += LIBXL_PV_EXTRA_MEMORY;
- break;
- default:
- rc = ERROR_INVAL;
- goto out;
- }
- if (*need_memkb % (2 * 1024))
- *need_memkb += (2 * 1024) - (*need_memkb % (2 * 1024));
- rc = 0;
-out:
- return rc;
-}
-
-int libxl_domain_need_memory(libxl_ctx *ctx,
- libxl_domain_config *d_config,
- uint32_t domid_for_logging,
- uint64_t *need_memkb)
-{
- GC_INIT(ctx);
- int rc;
-
- ctx->libxl_domain_need_memory_called = 1;
-
- rc = libxl__domain_config_setdefault(gc,
- d_config,
- domid_for_logging);
- if (rc) goto out;
-
- rc = libxl__domain_need_memory_calculate(gc,
- &d_config->b_info,
- need_memkb);
- if (rc) goto out;
-
- rc = 0;
- out:
- GC_FREE;
- return rc;
-}
-
-int libxl_domain_need_memory_0x041200(libxl_ctx *ctx,
- const libxl_domain_build_info *b_info_in,
- uint64_t *need_memkb)
-{
- GC_INIT(ctx);
- int rc;
-
- ctx->libxl_domain_need_memory_0x041200_called = 1;
-
- libxl_domain_build_info b_info[1];
- libxl_domain_build_info_init(b_info);
- libxl_domain_build_info_copy(ctx, b_info, b_info_in);
-
- rc = libxl__domain_build_info_setdefault(gc, b_info);
- if (rc) goto out;
-
- rc = libxl__domain_need_memory_calculate(gc,
- b_info,
- need_memkb);
- if (rc) goto out;
-
- rc = 0;
- out:
- libxl_domain_build_info_dispose(b_info);
- GC_FREE;
- return rc;
-}
-
-int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
- const libxl_domain_build_info *b_info_in,
- uint32_t *need_memkb)
-{
- uint64_t my_need_memkb;
- int rc;
-
- rc = libxl_domain_need_memory_0x041200(ctx, b_info_in, &my_need_memkb);
- return libxl__memkb_64to32(ctx, rc, my_need_memkb, need_memkb);
-}
-
-int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb)
-{
- int rc = 0;
- libxl_physinfo info;
- GC_INIT(ctx);
-
- rc = libxl_get_physinfo(ctx, &info);
- if (rc < 0)
- goto out;
-
- *memkb = (info.free_pages + info.scrub_pages) * 4;
-
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
-{
- uint64_t my_memkb;
- int rc;
-
- rc = libxl_get_free_memory(ctx, &my_memkb);
- return libxl__memkb_64to32(ctx, rc, my_memkb, memkb);
-}
-
-int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid,
- uint64_t memory_kb, int wait_secs)
-{
- int rc = 0;
- libxl_physinfo info;
- GC_INIT(ctx);
-
- while (wait_secs > 0) {
- rc = libxl_get_physinfo(ctx, &info);
- if (rc < 0)
- goto out;
- if (info.free_pages * 4 >= memory_kb) {
- rc = 0;
- goto out;
- }
- wait_secs--;
- sleep(1);
- }
- rc = ERROR_NOMEM;
-
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs)
-{
- int rc = 0;
- uint64_t target_memkb = 0;
- uint64_t current_memkb, prev_memkb;
- libxl_dominfo info;
-
- rc = libxl_get_memory_target(ctx, domid, &target_memkb);
- if (rc < 0)
- return rc;
-
- libxl_dominfo_init(&info);
- prev_memkb = UINT64_MAX;
-
- do {
- sleep(2);
-
- libxl_dominfo_dispose(&info);
- libxl_dominfo_init(&info);
- rc = libxl_domain_info(ctx, &info, domid);
- if (rc < 0)
- goto out;
-
- current_memkb = info.current_memkb + info.outstanding_memkb;
-
- if (current_memkb > prev_memkb)
- {
- rc = ERROR_FAIL;
- goto out;
- }
- else if (current_memkb == prev_memkb)
- wait_secs -= 2;
- /* if current_memkb < prev_memkb loop for free as progress has
- * been made */
-
- prev_memkb = current_memkb;
- } while (wait_secs > 0 && current_memkb > target_memkb);
-
- if (current_memkb <= target_memkb)
- rc = 0;
- else
- rc = ERROR_FAIL;
-
-out:
- libxl_dominfo_dispose(&info);
- return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2011
- * Author Roger Pau Monne <roger.pau@entel.upc.edu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-int libxl__try_phy_backend(mode_t st_mode)
-{
- if (S_ISREG(st_mode) || S_ISBLK(st_mode))
- return 1;
-
- return 0;
-}
-
-char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
-{
- /* TODO */
- return NULL;
-}
-
-/* Hotplug scripts caller functions */
-static int libxl__hotplug(libxl__gc *gc, libxl__device *dev, char ***args,
- libxl__device_action action)
-{
- char *be_path = libxl__device_backend_path(gc, dev);
- char *script;
- int nr = 0, rc = 0, arraysize = 4;
-
- script = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/%s", be_path, "script"));
- if (!script) {
- LOGEVD(ERROR, errno, dev->domid,
- "unable to read script from %s", be_path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- GCNEW_ARRAY(*args, arraysize);
- (*args)[nr++] = script;
- (*args)[nr++] = be_path;
- (*args)[nr++] = GCSPRINTF("%d", action == LIBXL__DEVICE_ACTION_ADD ?
- XenbusStateInitWait : XenbusStateClosed);
- (*args)[nr++] = NULL;
- assert(nr == arraysize);
-
-out:
- return rc;
-}
-
-int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
- char ***args, char ***env,
- libxl__device_action action,
- int num_exec)
-{
- int rc;
-
- switch (dev->backend_kind) {
- case LIBXL__DEVICE_KIND_VBD:
- if (num_exec != 0) {
- LOGD(DEBUG, dev->domid,
- "num_exec %d, not running hotplug scripts", num_exec);
- rc = 0;
- goto out;
- }
- rc = libxl__hotplug(gc, dev, args, action);
- if (!rc) rc = 1;
- break;
- case LIBXL__DEVICE_KIND_VIF:
- /*
- * If domain has a stubdom we don't have to execute hotplug scripts
- * for emulated interfaces
- *
- * NetBSD let QEMU call a script to plug emulated nic, so
- * only test if num_exec == 0 in that case.
- */
- if ((num_exec != 0) ||
- (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
- LOGD(DEBUG, dev->domid,
- "num_exec %d, not running hotplug scripts", num_exec);
- rc = 0;
- goto out;
- }
- rc = libxl__hotplug(gc, dev, args, action);
- if (!rc) rc = 1;
- break;
- default:
- /* If no need to execute any hotplug scripts,
- * call the callback manually
- */
- rc = 0;
- break;
- }
-
-out:
- return rc;
-}
-
-libxl_device_model_version libxl__default_device_model(libxl__gc *gc)
-{
- return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
-}
-
-int libxl__pci_numdevs(libxl__gc *gc)
-{
- return ERROR_NI;
-}
-
-int libxl__pci_topology_init(libxl__gc *gc,
- physdev_pci_device_t *devs,
- int num_devs)
-{
- return ERROR_NI;
-}
-
-int libxl__local_dm_preexec_restrict(libxl__gc *gc)
-{
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) 2014
- * Author Shriram Rajagopalan <rshriram@cs.ubc.ca>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#include <netlink/cache.h>
-#include <netlink/socket.h>
-#include <netlink/attr.h>
-#include <netlink/route/link.h>
-#include <netlink/route/route.h>
-#include <netlink/route/qdisc.h>
-#include <netlink/route/qdisc/plug.h>
-
-typedef struct libxl__remus_device_nic {
- int devid;
-
- const char *vif;
- const char *ifb;
- struct rtnl_qdisc *qdisc;
-} libxl__remus_device_nic;
-
-int libxl__netbuffer_enabled(libxl__gc *gc)
-{
- return 1;
-}
-
-int init_subkind_nic(libxl__checkpoint_devices_state *cds)
-{
- int rc, ret;
- libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
- libxl__remus_state *rs = cds->concrete_data;
-
- STATE_AO_GC(cds->ao);
-
- rs->nlsock = nl_socket_alloc();
- if (!rs->nlsock) {
- LOGD(ERROR, dss->domid, "cannot allocate nl socket");
- rc = ERROR_FAIL;
- goto out;
- }
-
- ret = nl_connect(rs->nlsock, NETLINK_ROUTE);
- if (ret) {
- LOGD(ERROR, dss->domid, "failed to open netlink socket: %s",
- nl_geterror(ret));
- rc = ERROR_FAIL;
- goto out;
- }
-
- /* get list of all qdiscs installed on network devs. */
- ret = rtnl_qdisc_alloc_cache(rs->nlsock, &rs->qdisc_cache);
- if (ret) {
- LOGD(ERROR, dss->domid, "failed to allocate qdisc cache: %s",
- nl_geterror(ret));
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (dss->remus->netbufscript) {
- rs->netbufscript = libxl__strdup(gc, dss->remus->netbufscript);
- } else {
- rs->netbufscript = GCSPRINTF("%s/remus-netbuf-setup",
- libxl__xen_script_dir_path());
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds)
-{
- libxl__remus_state *rs = cds->concrete_data;
-
- STATE_AO_GC(cds->ao);
-
- /* free qdisc cache */
- if (rs->qdisc_cache) {
- nl_cache_clear(rs->qdisc_cache);
- nl_cache_free(rs->qdisc_cache);
- rs->qdisc_cache = NULL;
- }
-
- /* close & free nlsock */
- if (rs->nlsock) {
- nl_close(rs->nlsock);
- nl_socket_free(rs->nlsock);
- rs->nlsock = NULL;
- }
-}
-
-/*----- setup() and teardown() -----*/
-
-/* helper functions */
-
-/*
- * If the device has a vifname, then use that instead of
- * the vifX.Y format.
- * it must ONLY be used for remus because if driver domains
- * were in use it would constitute a security vulnerability.
- */
-static const char *get_vifname(libxl__checkpoint_device *dev,
- const libxl_device_nic *nic)
-{
- const char *vifname = NULL;
- const char *path;
- int rc;
-
- STATE_AO_GC(dev->cds->ao);
-
- /* Convenience aliases */
- const uint32_t domid = dev->cds->domid;
-
- path = GCSPRINTF("%s/vifname",
- libxl__domain_device_backend_path(gc, 0, domid,
- nic->devid, LIBXL__DEVICE_KIND_VIF));
-
- rc = libxl__xs_read_checked(gc, XBT_NULL, path, &vifname);
- if (!rc && !vifname) {
- vifname = libxl__device_nic_devname(gc, domid,
- nic->devid,
- nic->nictype);
- }
-
- return vifname;
-}
-
-static void free_qdisc(libxl__remus_device_nic *remus_nic)
-{
- if (remus_nic->qdisc == NULL)
- return;
-
- nl_object_put((struct nl_object *)(remus_nic->qdisc));
- remus_nic->qdisc = NULL;
-}
-
-static int init_qdisc(libxl__checkpoint_devices_state *cds,
- libxl__remus_device_nic *remus_nic)
-{
- int rc, ret, ifindex;
- struct rtnl_link *ifb = NULL;
- struct rtnl_qdisc *qdisc = NULL;
- libxl__remus_state *rs = cds->concrete_data;
-
- STATE_AO_GC(cds->ao);
-
- /* Now that we have brought up REMUS_IFB device with plug qdisc for
- * this vif, so we need to refill the qdisc cache.
- */
- ret = nl_cache_refill(rs->nlsock, rs->qdisc_cache);
- if (ret) {
- LOGD(ERROR, cds->domid,
- "cannot refill qdisc cache: %s", nl_geterror(ret));
- rc = ERROR_FAIL;
- goto out;
- }
-
- /* get a handle to the REMUS_IFB interface */
- ret = rtnl_link_get_kernel(rs->nlsock, 0, remus_nic->ifb, &ifb);
- if (ret) {
- LOGD(ERROR, cds->domid,
- "cannot obtain handle for %s: %s", remus_nic->ifb,
- nl_geterror(ret));
- rc = ERROR_FAIL;
- goto out;
- }
-
- ifindex = rtnl_link_get_ifindex(ifb);
- if (!ifindex) {
- LOGD(ERROR, cds->domid,
- "interface %s has no index", remus_nic->ifb);
- rc = ERROR_FAIL;
- goto out;
- }
-
- /* Get a reference to the root qdisc installed on the REMUS_IFB, by
- * querying the qdisc list we obtained earlier. The netbufscript
- * sets up the plug qdisc as the root qdisc, so we don't have to
- * search the entire qdisc tree on the REMUS_IFB dev.
-
- * There is no need to explicitly free this qdisc as its just a
- * reference from the qdisc cache we allocated earlier.
- */
- qdisc = rtnl_qdisc_get_by_parent(rs->qdisc_cache, ifindex, TC_H_ROOT);
- if (qdisc) {
- const char *tc_kind = rtnl_tc_get_kind(TC_CAST(qdisc));
- /* Sanity check: Ensure that the root qdisc is a plug qdisc. */
- if (!tc_kind || strcmp(tc_kind, "plug")) {
- LOGD(ERROR, cds->domid,
- "plug qdisc is not installed on %s", remus_nic->ifb);
- rc = ERROR_FAIL;
- goto out;
- }
- remus_nic->qdisc = qdisc;
- } else {
- LOGD(ERROR, cds->domid,
- "Cannot get qdisc handle from ifb %s", remus_nic->ifb);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-
-out:
- if (ifb)
- rtnl_link_put(ifb);
-
- if (rc && qdisc)
- nl_object_put((struct nl_object *)qdisc);
-
- return rc;
-}
-
-/* callbacks */
-
-static void netbuf_setup_script_cb(libxl__egc *egc,
- libxl__async_exec_state *aes,
- int rc, int status);
-static void netbuf_teardown_script_cb(libxl__egc *egc,
- libxl__async_exec_state *aes,
- int rc, int status);
-
-/*
- * the script needs the following env & args
- * $vifname
- * $XENBUS_PATH (/libxl/<domid>/remus/netbuf/<devid>/)
- * $REMUS_IFB (for teardown)
- * setup/teardown as command line arg.
- */
-static void setup_async_exec(libxl__checkpoint_device *dev, char *op)
-{
- int arraysize, nr = 0;
- char **env = NULL, **args = NULL;
- libxl__remus_device_nic *remus_nic = dev->concrete_data;
- libxl__checkpoint_devices_state *cds = dev->cds;
- libxl__async_exec_state *aes = &dev->aodev.aes;
- libxl__remus_state *rs = cds->concrete_data;
-
- STATE_AO_GC(cds->ao);
-
- /* Convenience aliases */
- char *const script = libxl__strdup(gc, rs->netbufscript);
- const uint32_t domid = cds->domid;
- const int dev_id = remus_nic->devid;
- const char *const vif = remus_nic->vif;
- const char *const ifb = remus_nic->ifb;
-
- arraysize = 7;
- GCNEW_ARRAY(env, arraysize);
- env[nr++] = "vifname";
- env[nr++] = libxl__strdup(gc, vif);
- env[nr++] = "XENBUS_PATH";
- env[nr++] = GCSPRINTF("%s/remus/netbuf/%d",
- libxl__xs_libxl_path(gc, domid), dev_id);
- if (!strcmp(op, "teardown") && ifb) {
- env[nr++] = "REMUS_IFB";
- env[nr++] = libxl__strdup(gc, ifb);
- }
- env[nr++] = NULL;
- assert(nr <= arraysize);
-
- arraysize = 3; nr = 0;
- GCNEW_ARRAY(args, arraysize);
- args[nr++] = script;
- args[nr++] = op;
- args[nr++] = NULL;
- assert(nr == arraysize);
-
- aes->ao = dev->cds->ao;
- aes->what = GCSPRINTF("%s %s", args[0], args[1]);
- aes->env = env;
- aes->args = args;
- aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
- aes->stdfds[0] = -1;
- aes->stdfds[1] = -1;
- aes->stdfds[2] = -1;
-
- if (!strcmp(op, "teardown"))
- aes->callback = netbuf_teardown_script_cb;
- else
- aes->callback = netbuf_setup_script_cb;
-}
-
-/* setup() and teardown() */
-
-static void nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
- int rc;
- libxl__remus_device_nic *remus_nic;
- const libxl_device_nic *nic = dev->backend_dev;
-
- STATE_AO_GC(dev->cds->ao);
-
- /*
- * thers's no subkind of nic devices, so nic ops is always matched
- * with nic devices
- */
- dev->matched = true;
-
- GCNEW(remus_nic);
- dev->concrete_data = remus_nic;
- remus_nic->devid = nic->devid;
- remus_nic->vif = get_vifname(dev, nic);
- if (!remus_nic->vif) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- setup_async_exec(dev, "setup");
- rc = libxl__async_exec_start(&dev->aodev.aes);
- if (rc)
- goto out;
-
- return;
-
-out:
- dev->aodev.rc = rc;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-/*
- * In return, the script writes the name of REMUS_IFB device (during setup)
- * to be used for output buffering into XENBUS_PATH/ifb
- */
-static void netbuf_setup_script_cb(libxl__egc *egc,
- libxl__async_exec_state *aes,
- int rc, int status)
-{
- libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
- libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
- libxl__remus_device_nic *remus_nic = dev->concrete_data;
- libxl__checkpoint_devices_state *cds = dev->cds;
- libxl__remus_state *rs = cds->concrete_data;
- const char *out_path_base, *hotplug_error = NULL;
-
- STATE_AO_GC(cds->ao);
-
- /* Convenience aliases */
- const uint32_t domid = cds->domid;
- const int devid = remus_nic->devid;
- const char *const vif = remus_nic->vif;
- const char **const ifb = &remus_nic->ifb;
-
- if (status && !rc)
- rc = ERROR_FAIL;
- if (rc)
- goto out;
-
- /*
- * we need to get ifb first because it's needed for teardown
- */
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/remus/netbuf/%d/ifb",
- libxl__xs_libxl_path(gc, domid),
- devid),
- ifb);
- if (rc)
- goto out;
-
- if (!(*ifb)) {
- LOGD(ERROR, domid, "Cannot get ifb dev name for domain %u dev %s",
- domid, vif);
- rc = ERROR_FAIL;
- goto out;
- }
-
- out_path_base = GCSPRINTF("%s/remus/netbuf/%d",
- libxl__xs_libxl_path(gc, domid), devid);
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/hotplug-error", out_path_base),
- &hotplug_error);
- if (rc)
- goto out;
-
- if (hotplug_error) {
- LOGD(ERROR, domid, "netbuf script %s setup failed for vif %s: %s",
- rs->netbufscript, vif, hotplug_error);
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (status) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- LOGD(DEBUG, domid, "%s will buffer packets from vif %s", *ifb, vif);
- rc = init_qdisc(cds, remus_nic);
-
-out:
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-static void nic_teardown(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
- int rc;
- STATE_AO_GC(dev->cds->ao);
-
- setup_async_exec(dev, "teardown");
-
- rc = libxl__async_exec_start(&dev->aodev.aes);
- if (rc)
- goto out;
-
- return;
-
-out:
- dev->aodev.rc = rc;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-static void netbuf_teardown_script_cb(libxl__egc *egc,
- libxl__async_exec_state *aes,
- int rc, int status)
-{
- libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
- libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
- libxl__remus_device_nic *remus_nic = dev->concrete_data;
-
- if (status && !rc)
- rc = ERROR_FAIL;
-
- free_qdisc(remus_nic);
-
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-/*----- checkpointing APIs -----*/
-
-/* The value of buffer_op, not the value passed to kernel */
-enum {
- tc_buffer_start,
- tc_buffer_release
-};
-
-/* API implementations */
-
-static int remus_netbuf_op(libxl__remus_device_nic *remus_nic,
- libxl__checkpoint_devices_state *cds,
- int buffer_op)
-{
- int rc, ret;
- libxl__remus_state *rs = cds->concrete_data;
-
- STATE_AO_GC(cds->ao);
-
- if (buffer_op == tc_buffer_start)
- ret = rtnl_qdisc_plug_buffer(remus_nic->qdisc);
- else
- ret = rtnl_qdisc_plug_release_one(remus_nic->qdisc);
-
- if (ret) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- ret = rtnl_qdisc_add(rs->nlsock, remus_nic->qdisc, NLM_F_REQUEST);
- if (ret) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-
-out:
- if (rc)
- LOGD(ERROR, cds-> domid, "Remus: cannot do netbuf op %s on %s:%s",
- ((buffer_op == tc_buffer_start) ?
- "start_new_epoch" : "release_prev_epoch"),
- remus_nic->ifb, nl_geterror(ret));
- return rc;
-}
-
-static void nic_postsuspend(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
- int rc;
- libxl__remus_device_nic *remus_nic = dev->concrete_data;
-
- STATE_AO_GC(dev->cds->ao);
-
- rc = remus_netbuf_op(remus_nic, dev->cds, tc_buffer_start);
-
- dev->aodev.rc = rc;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-static void nic_commit(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
- int rc;
- libxl__remus_device_nic *remus_nic = dev->concrete_data;
-
- STATE_AO_GC(dev->cds->ao);
-
- rc = remus_netbuf_op(remus_nic, dev->cds, tc_buffer_release);
-
- dev->aodev.rc = rc;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-const libxl__checkpoint_device_instance_ops remus_device_nic = {
- .kind = LIBXL__DEVICE_KIND_VIF,
- .setup = nic_setup,
- .teardown = nic_teardown,
- .postsuspend = nic_postsuspend,
- .commit = nic_commit,
-};
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2016 SUSE Linux GmbH
- * Author Juergen Gross <jgross@suse.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-int libxl_mac_to_device_nic(libxl_ctx *ctx, uint32_t domid,
- const char *mac, libxl_device_nic *nic)
-{
- GC_INIT(ctx);
- libxl_device_nic *nics;
- int nb, rc, i;
- libxl_mac mac_n;
-
- rc = libxl__parse_mac(mac, mac_n);
- if (rc)
- return rc;
-
- nics = libxl__device_list(gc, &libxl__nic_devtype, domid, &nb);
- if (!nics)
- return ERROR_FAIL;
-
- memset(nic, 0, sizeof (libxl_device_nic));
-
- rc = ERROR_INVAL;
- for (i = 0; i < nb; ++i) {
- if (!libxl__compare_macs(&mac_n, &nics[i].mac)) {
- *nic = nics[i];
- rc = 0;
- i++; /* Do not dispose this NIC on exit path */
- break;
- }
- libxl_device_nic_dispose(&nics[i]);
- }
-
- for (; i<nb; i++)
- libxl_device_nic_dispose(&nics[i]);
-
- free(nics);
- return rc;
-}
-
-#define LIBXL_DEVICE_NIC_MTU_DEFAULT 1500
-
-static int libxl__device_nic_setdefault(libxl__gc *gc, uint32_t domid,
- libxl_device_nic *nic, bool hotplug)
-{
- int rc;
-
- if (!nic->mtu)
- nic->mtu = LIBXL_DEVICE_NIC_MTU_DEFAULT;
- if (!nic->model) {
- nic->model = strdup("rtl8139");
- if (!nic->model) return ERROR_NOMEM;
- }
- if (libxl__mac_is_default(&nic->mac)) {
- const uint8_t *r;
- libxl_uuid uuid;
-
- libxl_uuid_generate(&uuid);
- r = libxl_uuid_bytearray(&uuid);
-
- nic->mac[0] = 0x00;
- nic->mac[1] = 0x16;
- nic->mac[2] = 0x3e;
- nic->mac[3] = r[0] & 0x7f;
- nic->mac[4] = r[1];
- nic->mac[5] = r[2];
- }
- if (!nic->bridge) {
- nic->bridge = strdup("xenbr0");
- if (!nic->bridge) return ERROR_NOMEM;
- }
- if ( !nic->script && asprintf(&nic->script, "%s/vif-bridge",
- libxl__xen_script_dir_path()) < 0 )
- return ERROR_FAIL;
-
- rc = libxl__resolve_domid(gc, nic->backend_domname, &nic->backend_domid);
- if (rc < 0) return rc;
-
- switch (libxl__domain_type(gc, domid)) {
- case LIBXL_DOMAIN_TYPE_HVM:
- if (!nic->nictype) {
- if (hotplug)
- nic->nictype = LIBXL_NIC_TYPE_VIF;
- else
- nic->nictype = LIBXL_NIC_TYPE_VIF_IOEMU;
- }
- break;
- case LIBXL_DOMAIN_TYPE_PVH:
- case LIBXL_DOMAIN_TYPE_PV:
- if (nic->nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
- LOGD(ERROR, domid,
- "trying to create PV or PVH guest with an emulated interface");
- return ERROR_INVAL;
- }
- nic->nictype = LIBXL_NIC_TYPE_VIF;
- break;
- case LIBXL_DOMAIN_TYPE_INVALID:
- return ERROR_FAIL;
- default:
- abort();
- }
-
- return rc;
-}
-
-static void libxl__update_config_nic(libxl__gc *gc, libxl_device_nic *dst,
- const libxl_device_nic *src)
-{
- dst->devid = src->devid;
- dst->nictype = src->nictype;
- libxl_mac_copy(CTX, &dst->mac, &src->mac);
-}
-
-static int libxl__set_xenstore_nic(libxl__gc *gc, uint32_t domid,
- libxl_device_nic *nic,
- flexarray_t *back, flexarray_t *front,
- flexarray_t *ro_front)
-{
- flexarray_grow(back, 2);
-
- if (nic->script)
- flexarray_append_pair(back, "script",
- libxl__abs_path(gc, nic->script,
- libxl__xen_script_dir_path()));
-
- if (nic->ifname) {
- flexarray_append(back, "vifname");
- flexarray_append(back, nic->ifname);
- }
-
- if (nic->coloft_forwarddev) {
- flexarray_append(back, "forwarddev");
- flexarray_append(back, nic->coloft_forwarddev);
- }
-
-#define MAYBE_ADD_COLO_ARGS(arg) ({ \
- if (nic->colo_##arg) { \
- flexarray_append(back, "colo_"#arg); \
- flexarray_append(back, nic->colo_##arg); \
- } \
-})
-
- MAYBE_ADD_COLO_ARGS(sock_mirror_id);
- MAYBE_ADD_COLO_ARGS(sock_mirror_ip);
- MAYBE_ADD_COLO_ARGS(sock_mirror_port);
- MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_id);
- MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_ip);
- MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_port);
- MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_id);
- MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_ip);
- MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_port);
- MAYBE_ADD_COLO_ARGS(sock_compare_notify_id);
- MAYBE_ADD_COLO_ARGS(sock_compare_notify_ip);
- MAYBE_ADD_COLO_ARGS(sock_compare_notify_port);
- MAYBE_ADD_COLO_ARGS(sock_redirector0_id);
- MAYBE_ADD_COLO_ARGS(sock_redirector0_ip);
- MAYBE_ADD_COLO_ARGS(sock_redirector0_port);
- MAYBE_ADD_COLO_ARGS(sock_redirector1_id);
- MAYBE_ADD_COLO_ARGS(sock_redirector1_ip);
- MAYBE_ADD_COLO_ARGS(sock_redirector1_port);
- MAYBE_ADD_COLO_ARGS(sock_redirector2_id);
- MAYBE_ADD_COLO_ARGS(sock_redirector2_ip);
- MAYBE_ADD_COLO_ARGS(sock_redirector2_port);
- MAYBE_ADD_COLO_ARGS(filter_mirror_queue);
- MAYBE_ADD_COLO_ARGS(filter_mirror_outdev);
- MAYBE_ADD_COLO_ARGS(filter_redirector0_queue);
- MAYBE_ADD_COLO_ARGS(filter_redirector0_indev);
- MAYBE_ADD_COLO_ARGS(filter_redirector0_outdev);
- MAYBE_ADD_COLO_ARGS(filter_redirector1_queue);
- MAYBE_ADD_COLO_ARGS(filter_redirector1_indev);
- MAYBE_ADD_COLO_ARGS(filter_redirector1_outdev);
- MAYBE_ADD_COLO_ARGS(compare_pri_in);
- MAYBE_ADD_COLO_ARGS(compare_sec_in);
- MAYBE_ADD_COLO_ARGS(compare_out);
- MAYBE_ADD_COLO_ARGS(compare_notify_dev);
-
- MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_id);
- MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_ip);
- MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_port);
- MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_id);
- MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_ip);
- MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_port);
- MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_queue);
- MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_indev);
- MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_outdev);
- MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_queue);
- MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_indev);
- MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_outdev);
- MAYBE_ADD_COLO_ARGS(filter_sec_rewriter0_queue);
- MAYBE_ADD_COLO_ARGS(checkpoint_host);
- MAYBE_ADD_COLO_ARGS(checkpoint_port);
-
-#undef MAYBE_ADD_COLO_ARGS
-
- flexarray_append(back, "mac");
- flexarray_append(back,GCSPRINTF(LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
- if (nic->ip) {
- flexarray_append(back, "ip");
- flexarray_append(back, libxl__strdup(gc, nic->ip));
- }
- if (nic->gatewaydev) {
- flexarray_append(back, "gatewaydev");
- flexarray_append(back, libxl__strdup(gc, nic->gatewaydev));
- }
-
- if (nic->rate_interval_usecs > 0) {
- flexarray_append(back, "rate");
- flexarray_append(back, GCSPRINTF("%"PRIu64",%"PRIu32"",
- nic->rate_bytes_per_interval,
- nic->rate_interval_usecs));
- }
-
- if (nic->mtu != LIBXL_DEVICE_NIC_MTU_DEFAULT) {
- flexarray_append(back, "mtu");
- flexarray_append(back, GCSPRINTF("%u", nic->mtu));
- }
-
- flexarray_append(back, "bridge");
- flexarray_append(back, libxl__strdup(gc, nic->bridge));
- flexarray_append(back, "handle");
- flexarray_append(back, GCSPRINTF("%d", nic->devid));
- flexarray_append(back, "type");
- flexarray_append(back, libxl__strdup(gc,
- libxl_nic_type_to_string(nic->nictype)));
-
- flexarray_append(front, "handle");
- flexarray_append(front, GCSPRINTF("%d", nic->devid));
- flexarray_append(front, "mac");
- flexarray_append(front, GCSPRINTF(
- LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
-
- flexarray_append(ro_front, "mtu");
- flexarray_append(ro_front, GCSPRINTF("%u", nic->mtu));
-
- return 0;
-}
-
-static void libxl__device_nic_add(libxl__egc *egc, uint32_t domid,
- libxl_device_nic *nic,
- libxl__ao_device *aodev)
-{
- libxl__device_add_async(egc, domid, &libxl__nic_devtype, nic, aodev);
-}
-
-static int libxl__nic_from_xenstore(libxl__gc *gc, const char *libxl_path,
- libxl_devid devid, libxl_device_nic *nic)
-{
- const char *tmp;
- int rc;
-
- libxl_device_nic_init(nic);
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/handle", libxl_path), &tmp);
- if (rc) goto out;
- if (tmp)
- nic->devid = atoi(tmp);
- else
- nic->devid = 0;
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path), &tmp);
- if (rc) goto out;
-
- if (!tmp) {
- LOG(ERROR, "nic %s does not exist (no backend path)", libxl_path);
- rc = ERROR_FAIL;
- goto out;
- }
- rc = libxl__backendpath_parse_domid(gc, tmp, &nic->backend_domid);
- if (rc) goto out;
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/mtu", libxl_path), &tmp);
- if (rc) goto out;
- if (tmp) {
- char *endptr;
-
- nic->mtu = strtol(tmp, &endptr, 10);
- if (*endptr != '\0') {
- rc = ERROR_INVAL;
- goto out;
- }
- } else {
- nic->mtu = LIBXL_DEVICE_NIC_MTU_DEFAULT;
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/mac", libxl_path), &tmp);
- if (rc) goto out;
- if (tmp) {
- rc = libxl__parse_mac(tmp, nic->mac);
- if (rc) goto out;
- } else {
- memset(nic->mac, 0, sizeof(nic->mac));
- }
-
- rc = libxl__xs_read_checked(NOGC, XBT_NULL,
- GCSPRINTF("%s/ip", libxl_path),
- (const char **)(&nic->ip));
- if (rc) goto out;
- rc = libxl__xs_read_checked(NOGC, XBT_NULL,
- GCSPRINTF("%s/bridge", libxl_path),
- (const char **)(&nic->bridge));
- if (rc) goto out;
- rc = libxl__xs_read_checked(NOGC, XBT_NULL,
- GCSPRINTF("%s/script", libxl_path),
- (const char **)(&nic->script));
- if (rc) goto out;
- rc = libxl__xs_read_checked(NOGC, XBT_NULL,
- GCSPRINTF("%s/forwarddev", libxl_path),
- (const char **)(&nic->coloft_forwarddev));
- if (rc) goto out;
-
-#define CHECK_COLO_ARGS(arg) ({ \
- rc = libxl__xs_read_checked(NOGC, XBT_NULL, \
- GCSPRINTF("%s/colo_"#arg, libxl_path), \
- (const char **)(&nic->colo_##arg)); \
- if (rc) goto out; \
-})
-
- CHECK_COLO_ARGS(sock_mirror_id);
- CHECK_COLO_ARGS(sock_mirror_ip);
- CHECK_COLO_ARGS(sock_mirror_port);
- CHECK_COLO_ARGS(sock_compare_pri_in_id);
- CHECK_COLO_ARGS(sock_compare_pri_in_ip);
- CHECK_COLO_ARGS(sock_compare_pri_in_port);
- CHECK_COLO_ARGS(sock_compare_sec_in_id);
- CHECK_COLO_ARGS(sock_compare_sec_in_ip);
- CHECK_COLO_ARGS(sock_compare_sec_in_port);
- CHECK_COLO_ARGS(sock_compare_notify_id);
- CHECK_COLO_ARGS(sock_compare_notify_ip);
- CHECK_COLO_ARGS(sock_compare_notify_port);
- CHECK_COLO_ARGS(sock_redirector0_id);
- CHECK_COLO_ARGS(sock_redirector0_ip);
- CHECK_COLO_ARGS(sock_redirector0_port);
- CHECK_COLO_ARGS(sock_redirector1_id);
- CHECK_COLO_ARGS(sock_redirector1_ip);
- CHECK_COLO_ARGS(sock_redirector1_port);
- CHECK_COLO_ARGS(sock_redirector2_id);
- CHECK_COLO_ARGS(sock_redirector2_ip);
- CHECK_COLO_ARGS(sock_redirector2_port);
- CHECK_COLO_ARGS(filter_mirror_queue);
- CHECK_COLO_ARGS(filter_mirror_outdev);
- CHECK_COLO_ARGS(filter_redirector0_queue);
- CHECK_COLO_ARGS(filter_redirector0_indev);
- CHECK_COLO_ARGS(filter_redirector0_outdev);
- CHECK_COLO_ARGS(filter_redirector1_queue);
- CHECK_COLO_ARGS(filter_redirector1_indev);
- CHECK_COLO_ARGS(filter_redirector1_outdev);
- CHECK_COLO_ARGS(compare_pri_in);
- CHECK_COLO_ARGS(compare_sec_in);
- CHECK_COLO_ARGS(compare_out);
- CHECK_COLO_ARGS(compare_notify_dev);
- CHECK_COLO_ARGS(sock_sec_redirector0_id);
- CHECK_COLO_ARGS(sock_sec_redirector0_ip);
- CHECK_COLO_ARGS(sock_sec_redirector0_port);
- CHECK_COLO_ARGS(sock_sec_redirector1_id);
- CHECK_COLO_ARGS(sock_sec_redirector1_ip);
- CHECK_COLO_ARGS(sock_sec_redirector1_port);
- CHECK_COLO_ARGS(filter_sec_redirector0_queue);
- CHECK_COLO_ARGS(filter_sec_redirector0_indev);
- CHECK_COLO_ARGS(filter_sec_redirector0_outdev);
- CHECK_COLO_ARGS(filter_sec_redirector1_queue);
- CHECK_COLO_ARGS(filter_sec_redirector1_indev);
- CHECK_COLO_ARGS(filter_sec_redirector1_outdev);
- CHECK_COLO_ARGS(filter_sec_rewriter0_queue);
- CHECK_COLO_ARGS(checkpoint_host);
- CHECK_COLO_ARGS(checkpoint_port);
-
-#undef CHECK_COLO_ARGS
-
- /* vif_ioemu nics use the same xenstore entries as vif interfaces */
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/type", libxl_path), &tmp);
- if (rc) goto out;
- if (tmp) {
- rc = libxl_nic_type_from_string(tmp, &nic->nictype);
- if (rc) goto out;
- } else {
- nic->nictype = LIBXL_NIC_TYPE_VIF;
- }
- nic->model = NULL; /* XXX Only for TYPE_IOEMU */
- nic->ifname = NULL; /* XXX Only for TYPE_IOEMU */
-
- rc = 0;
- out:
- return rc;
-}
-
-libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx, uint32_t domid, int *num)
-{
- libxl_device_nic *r;
-
- GC_INIT(ctx);
-
- r = libxl__device_list(gc, &libxl__nic_devtype, domid, num);
-
- GC_FREE;
-
- return r;
-}
-
-void libxl_device_nic_list_free(libxl_device_nic* list, int num)
-{
- libxl__device_list_free(&libxl__nic_devtype, list, num);
-}
-
-int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_nic *nic,
- libxl_nicinfo *nicinfo)
-{
- GC_INIT(ctx);
- char *nicpath, *libxl_path;
- char *val;
- int rc;
-
- nicinfo->devid = nic->devid;
-
- nicpath = libxl__domain_device_frontend_path(gc, domid, nicinfo->devid,
- LIBXL__DEVICE_KIND_VIF);
- libxl_path = libxl__domain_device_libxl_path(gc, domid, nicinfo->devid,
- LIBXL__DEVICE_KIND_VIF);
- nicinfo->backend = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path), NULL);
- if (!nicinfo->backend) {
- GC_FREE;
- return ERROR_FAIL;
- }
- rc = libxl__backendpath_parse_domid(gc, nicinfo->backend,
- &nicinfo->backend_id);
- if (rc) goto out;
-
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", nicpath));
- nicinfo->state = val ? strtoul(val, NULL, 10) : -1;
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", nicpath));
- nicinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tx-ring-ref", nicpath));
- nicinfo->rref_tx = val ? strtoul(val, NULL, 10) : -1;
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/rx-ring-ref", nicpath));
- nicinfo->rref_rx = val ? strtoul(val, NULL, 10) : -1;
- nicinfo->frontend = libxl__strdup(NOGC, nicpath);
- nicinfo->frontend_id = domid;
-
- rc = 0;
- out:
- GC_FREE;
- return rc;
-}
-
-const char *libxl__device_nic_devname(libxl__gc *gc,
- uint32_t domid,
- uint32_t devid,
- libxl_nic_type type)
-{
- switch (type) {
- case LIBXL_NIC_TYPE_VIF:
- return GCSPRINTF(NETBACK_NIC_NAME, domid, devid);
- case LIBXL_NIC_TYPE_VIF_IOEMU:
- return GCSPRINTF(NETBACK_NIC_NAME TAP_DEVICE_SUFFIX, domid, devid);
- default:
- abort();
- }
-}
-
-static int libxl_device_nic_compare(const libxl_device_nic *d1,
- const libxl_device_nic *d2)
-{
- return COMPARE_DEVID(d1, d2);
-}
-
-static void libxl_device_nic_update_config(libxl__gc *gc, void *d, void *s)
-{
- libxl__update_config_nic(gc, d, s);
-}
-
-int libxl__device_nic_set_devids(libxl__gc *gc, libxl_domain_config *d_config,
- uint32_t domid)
-{
- int ret = 0;
- int i;
- size_t last_devid = -1;
-
- for (i = 0; i < d_config->num_nics; i++) {
- /* We have to init the nic here, because we still haven't
- * called libxl_device_nic_add when domcreate_launch_dm gets called,
- * but qemu needs the nic information to be complete.
- */
- ret = libxl__device_nic_setdefault(gc, domid, &d_config->nics[i],
- false);
- if (ret) {
- LOGD(ERROR, domid, "Unable to set nic defaults for nic %d", i);
- goto out;
- }
-
- if (d_config->nics[i].devid > last_devid)
- last_devid = d_config->nics[i].devid;
- }
- for (i = 0; i < d_config->num_nics; i++) {
- if (d_config->nics[i].devid < 0)
- d_config->nics[i].devid = ++last_devid;
- }
-
-out:
- return ret;
-}
-
-static LIBXL_DEFINE_UPDATE_DEVID(nic)
-static LIBXL_DEFINE_DEVICE_FROM_TYPE(nic)
-
-LIBXL_DEFINE_DEVID_TO_DEVICE(nic)
-LIBXL_DEFINE_DEVICE_ADD(nic)
-LIBXL_DEFINE_DEVICES_ADD(nic)
-LIBXL_DEFINE_DEVICE_REMOVE(nic)
-
-DEFINE_DEVICE_TYPE_STRUCT(nic, VIF,
- .update_config = libxl_device_nic_update_config,
- .from_xenstore = (device_from_xenstore_fn_t)libxl__nic_from_xenstore,
- .set_xenstore_config = (device_set_xenstore_config_fn_t)
- libxl__set_xenstore_nic,
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2016
- * Author Wei Liu <wei.liu2@citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-void libxl__colo_restore_setup(libxl__egc *egc,
- libxl__colo_restore_state *crs)
-{
- STATE_AO_GC(crs->ao);
-
- LOGD(ERROR, crs->domid, "COLO is not supported");
-
- crs->callback(egc, crs, ERROR_FAIL);
-}
-
-void libxl__colo_restore_teardown(libxl__egc *egc, void *dcs_void,
- int ret, int retval, int errnoval)
-{
- /* Shouldn't be here because setup already failed */
- abort();
-}
-
-void libxl__colo_save_setup(libxl__egc *egc, libxl__colo_save_state *css)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
- STATE_AO_GC(dss->ao);
-
- LOGD(ERROR, dss->domid, "COLO is not supported");
-
- dss->callback(egc, dss, ERROR_FAIL);
-}
-
-void libxl__colo_save_teardown(libxl__egc *egc,
- libxl__colo_save_state *css,
- int rc)
-{
- /* Shouldn't be here because setup already failed */
- abort();
-}
-
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2015 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-void libxl__conversion_helper_init(libxl__conversion_helper_state *chs)
-{
- libxl__ev_child_init(&chs->child);
-}
-
-int libxl__convert_legacy_stream(libxl__egc *egc,
- libxl__conversion_helper_state *chs)
-{
- return ERROR_FAIL;
-}
-
-void libxl__conversion_helper_abort(libxl__egc *egc,
- libxl__conversion_helper_state *chs,
- int rc)
-{
- /* no op */
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl)
-{
- return 1;
-}
-
-void libxl_cpuid_dispose(libxl_cpuid_policy_list *p_cpuid_list)
-{
-}
-
-int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str)
-{
- return 0;
-}
-
-int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid,
- const char* str)
-{
- return 0;
-}
-
-void libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool restore,
- libxl_domain_build_info *info)
-{
-}
-
-yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
- libxl_cpuid_policy_list *pcpuid)
-{
- return 0;
-}
-
-int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
- const libxl__json_object *o,
- libxl_cpuid_policy_list *p)
-{
- return 0;
-}
-
-void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
- libxl_cpuid_policy_list *dst,
- const libxl_cpuid_policy_list *src)
-{
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2014
- * Author Shriram Rajagopalan <rshriram@cs.ubc.ca>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-int libxl__netbuffer_enabled(libxl__gc *gc)
-{
- return 0;
-}
-
-int init_subkind_nic(libxl__checkpoint_devices_state *cds)
-{
- return 0;
-}
-
-void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds)
-{
- return;
-}
-
-static void nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
- STATE_AO_GC(dev->cds->ao);
-
- dev->aodev.rc = ERROR_FAIL;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-const libxl__checkpoint_device_instance_ops remus_device_nic = {
- .kind = LIBXL__DEVICE_KIND_VIF,
- .setup = nic_setup,
-};
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2012 Citrix Ltd.
- * Author Dario Faggioli <dario.faggioli@citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <glob.h>
-
-#include "libxl_internal.h"
-
-/*
- * What follows are helpers for generating all the k-combinations
- * without repetitions of a set S with n elements in it. Formally
- * speaking, they are subsets of k distinct elements of S and, if
- * S is n elements big, the number of k-combinations is equal to the
- * binomial coefficient C(n k)=n!/(k! * (n - k)!).
- *
- * The various subset are generated one after the other by calling
- * comb_init() first, and, after that, comb_next()
- * C(n k)-1 times. An iterator is used to store the current status
- * of the whole generation operation (i.e., basically, the last
- * combination that has been generated). As soon as all combinations
- * have been generated, comb_next() will start returning 0 instead of
- * 1. The same instance of the iterator and the same values for
- * n and k _must_ be used for each call (if that doesn't happen, the
- * result is unspecified).
- *
- * The algorithm is a well known one (see, for example, D. Knuth's "The
- * Art of Computer Programming - Volume 4, Fascicle 3" and it produces
- * the combinations in such a way that they (well, more precisely, their
- * indexes it the array/map representing the set) come with lexicographic
- * ordering.
- *
- * For example, with n = 5 and k = 3, calling comb_init()
- * will generate { 0, 1, 2 }, while subsequent valid calls to
- * comb_next() will produce the following:
- * { { 0, 1, 3 }, { 0, 1, 4 },
- * { 0, 2, 3 }, { 0, 2, 4 }, { 0, 3, 4 },
- * { 1, 2, 3 }, { 1, 2, 4 }, { 1, 3, 4 },
- * { 2, 3, 4 } }
- *
- * This is used by the automatic NUMA placement logic below.
- */
-typedef int* comb_iter_t;
-
-static int comb_init(libxl__gc *gc, comb_iter_t *it, int n, int k)
-{
- comb_iter_t new_iter;
- int i;
-
- if (n < k)
- return 0;
-
- /* First set is always { 0, 1, 2, ..., k-1 } */
- GCNEW_ARRAY(new_iter, k);
- for (i = 0; i < k; i++)
- new_iter[i] = i;
-
- *it = new_iter;
- return 1;
-}
-
-static int comb_next(comb_iter_t it, int n, int k)
-{
- int i;
-
- /*
- * The idea here is to find the leftmost element from where
- * we should start incrementing the indexes of the iterator.
- * This means looking for the highest index that can be increased
- * while still producing value smaller than n-1. In the example
- * above, when dealing with { 0, 1, 4 }, such an element is the
- * second one, as the third is already equal to 4 (which actually
- * is n-1).
- * Once we found from where to start, we increment that element
- * and override the right-hand rest of the iterator with its
- * successors, thus achieving lexicographic ordering.
- *
- * Regarding the termination of the generation process, when we
- * manage in bringing n-k at the very first position of the iterator,
- * we know that is the last valid combination ( { 2, 3, 4 }, with
- * n - k = 5 - 2 = 2, in the example above), and thus we start
- * returning 0 as soon as we cross that border.
- */
- for (i = k - 1; it[i] == n - k + i; i--) {
- if (i <= 0)
- return 0;
- }
- for (it[i]++, i++; i < k; i++)
- it[i] = it[i - 1] + 1;
- return 1;
-}
-
-/* NUMA automatic placement (see libxl_internal.h for details) */
-
-/*
- * This function turns a k-combination iterator into a node map,
- * given another map, telling us which nodes should be considered.
- *
- * This means the bits that are set in suitable_nodemap and that
- * corresponds to the indexes of the given combination are the ones
- * that will be set in nodemap.
- *
- * For example, given a fully set suitable_nodemap, if the iterator
- * represents the combination { 0, 2, 4}, nodmeap will have bits #0,
- * #2 and #4 set.
- * On the other hand, if, say, suitable_nodemap=01011011, the same
- * iterator will cause bits #1, #4 and #7 of nodemap to be set.
- */
-static void comb_get_nodemap(comb_iter_t it, libxl_bitmap *suitable_nodemap,
- libxl_bitmap *nodemap, int k)
-{
- int i, m = 0, n = 0;
-
- libxl_bitmap_set_none(nodemap);
- libxl_for_each_set_bit(i, *suitable_nodemap) {
- /* Check wether the n-th set bit of suitable_nodemap
- * matches with the m-th element of the iterator (and,
- * only if it does, advance to the next one) */
- if (m < k && n == it[m]) {
- libxl_bitmap_set(nodemap, i);
- m++;
- }
- n++;
- }
-}
-
-/* Retrieve the number of cpus that the nodes that are part of the nodemap
- * span and are also set in suitable_cpumap. */
-static int nodemap_to_nr_cpus(libxl_cputopology *tinfo, int nr_cpus,
- const libxl_bitmap *suitable_cpumap,
- const libxl_bitmap *nodemap)
-{
- int i, nodes_cpus = 0;
-
- for (i = 0; i < nr_cpus; i++) {
- if (libxl_bitmap_test(suitable_cpumap, i) &&
- libxl_bitmap_test(nodemap, tinfo[i].node))
- nodes_cpus++;
- }
- return nodes_cpus;
-}
-
-/* Retrieve the amount of free memory within the nodemap */
-static uint32_t nodemap_to_free_memkb(libxl_numainfo *ninfo,
- libxl_bitmap *nodemap)
-{
- uint32_t free_memkb = 0;
- int i;
-
- libxl_for_each_set_bit(i, *nodemap)
- free_memkb += ninfo[i].free / 1024;
-
- return free_memkb;
-}
-
-/* Retrieve the number of vcpus able to run on the nodes in nodemap */
-static int nodemap_to_nr_vcpus(libxl__gc *gc, int vcpus_on_node[],
- const libxl_bitmap *nodemap)
-{
- int i, nr_vcpus = 0;
-
- libxl_for_each_set_bit(i, *nodemap)
- nr_vcpus += vcpus_on_node[i];
-
- return nr_vcpus;
-}
-
-/* Number of vcpus able to run on the cpus of the various nodes
- * (reported by filling the array vcpus_on_node[]). */
-static int nr_vcpus_on_nodes(libxl__gc *gc, libxl_cputopology *tinfo,
- size_t tinfo_elements,
- const libxl_bitmap *suitable_cpumap,
- int vcpus_on_node[])
-{
- libxl_dominfo *dinfo = NULL;
- libxl_bitmap dom_nodemap, nodes_counted;
- int nr_doms, nr_cpus;
- int i, j, k;
-
- dinfo = libxl_list_domain(CTX, &nr_doms);
- if (dinfo == NULL)
- return ERROR_FAIL;
-
- if (libxl_node_bitmap_alloc(CTX, &nodes_counted, 0) < 0) {
- libxl_dominfo_list_free(dinfo, nr_doms);
- return ERROR_FAIL;
- }
-
- if (libxl_node_bitmap_alloc(CTX, &dom_nodemap, 0) < 0) {
- libxl_bitmap_dispose(&nodes_counted);
- libxl_dominfo_list_free(dinfo, nr_doms);
- return ERROR_FAIL;
- }
-
- for (i = 0; i < nr_doms; i++) {
- libxl_vcpuinfo *vinfo = NULL;
- int nr_dom_vcpus = 0;
- libxl_cpupoolinfo cpupool_info;
- int cpupool;
-
- libxl_cpupoolinfo_init(&cpupool_info);
-
- cpupool = libxl__domain_cpupool(gc, dinfo[i].domid);
- if (cpupool < 0)
- goto next;
- if (libxl_cpupool_info(CTX, &cpupool_info, cpupool))
- goto next;
-
- vinfo = libxl_list_vcpu(CTX, dinfo[i].domid, &nr_dom_vcpus, &nr_cpus);
- if (vinfo == NULL)
- goto next;
-
- /* Retrieve the domain's node-affinity map */
- libxl_domain_get_nodeaffinity(CTX, dinfo[i].domid, &dom_nodemap);
-
- for (j = 0; j < nr_dom_vcpus; j++) {
- /*
- * For each vcpu of each domain, it must have both vcpu-affinity
- * and node-affinity to (a pcpu belonging to) a certain node to
- * cause an increment in the corresponding element of the array.
- *
- * Note that we also need to check whether the cpu actually
- * belongs to the domain's cpupool (the cpupool of the domain
- * being checked). In fact, it could be that the vcpu has affinity
- * with cpus in suitable_cpumask, but that are not in its own
- * cpupool, and we don't want to consider those!
- */
- libxl_bitmap_set_none(&nodes_counted);
- libxl_for_each_set_bit(k, vinfo[j].cpumap) {
- if (k >= tinfo_elements)
- break;
- int node = tinfo[k].node;
-
- if (libxl_bitmap_test(suitable_cpumap, k) &&
- libxl_bitmap_test(&cpupool_info.cpumap, k) &&
- libxl_bitmap_test(&dom_nodemap, node) &&
- !libxl_bitmap_test(&nodes_counted, node)) {
- libxl_bitmap_set(&nodes_counted, node);
- vcpus_on_node[node]++;
- }
- }
- }
-
- next:
- libxl_cpupoolinfo_dispose(&cpupool_info);
- libxl_vcpuinfo_list_free(vinfo, nr_dom_vcpus);
- }
-
- libxl_bitmap_dispose(&dom_nodemap);
- libxl_bitmap_dispose(&nodes_counted);
- libxl_dominfo_list_free(dinfo, nr_doms);
- return 0;
-}
-
-/*
- * This function tries to figure out if the host has a consistent number
- * of cpus along all its NUMA nodes. In fact, if that is the case, we can
- * calculate the minimum number of nodes needed for a domain by just
- * dividing its total number of vcpus by this value computed here.
- * However, we are not allowed to assume that all the nodes have the
- * same number of cpus. Therefore, in case discrepancies among different
- * nodes are found, this function just returns 0, for the caller to know
- * it shouldn't rely on this 'optimization', and sort out things in some
- * other way (by doing something basic, like starting trying with
- * candidates with just one node).
- */
-static int count_cpus_per_node(libxl_cputopology *tinfo, int nr_cpus,
- int nr_nodes)
-{
- int cpus_per_node = 0;
- int j, i;
-
- /* This makes sense iff # of PCPUs is the same for all nodes */
- for (j = 0; j < nr_nodes; j++) {
- int curr_cpus = 0;
-
- for (i = 0; i < nr_cpus; i++) {
- if (tinfo[i].node == j)
- curr_cpus++;
- }
- /* So, if the above does not hold, turn the whole thing off! */
- cpus_per_node = cpus_per_node == 0 ? curr_cpus : cpus_per_node;
- if (cpus_per_node != curr_cpus)
- return 0;
- }
- return cpus_per_node;
-}
-
-/*
- * Looks for the placement candidates that satisfyies some specific
- * conditions and return the best one according to the provided
- * comparison function.
- */
-int libxl__get_numa_candidate(libxl__gc *gc,
- uint64_t min_free_memkb, int min_cpus,
- int min_nodes, int max_nodes,
- const libxl_bitmap *suitable_cpumap,
- libxl__numa_candidate_cmpf numa_cmpf,
- libxl__numa_candidate *cndt_out,
- int *cndt_found)
-{
- libxl__numa_candidate new_cndt;
- libxl_cputopology *tinfo = NULL;
- libxl_numainfo *ninfo = NULL;
- int nr_nodes = 0, nr_suit_nodes, nr_cpus = 0;
- libxl_bitmap suitable_nodemap, nodemap;
- int *vcpus_on_node, rc = 0;
-
- libxl_bitmap_init(&nodemap);
- libxl_bitmap_init(&suitable_nodemap);
- libxl__numa_candidate_init(&new_cndt);
-
- /* Get platform info and prepare the map for testing the combinations */
- ninfo = libxl_get_numainfo(CTX, &nr_nodes);
- if (ninfo == NULL)
- return ERROR_FAIL;
-
- if (nr_nodes <= 1) {
- *cndt_found = 0;
- goto out;
- }
-
- GCNEW_ARRAY(vcpus_on_node, nr_nodes);
-
- /*
- * The good thing about this solution is that it is based on heuristics
- * (implemented in numa_cmpf() ), but we at least can evaluate it on
- * all the possible placement candidates. That can happen because the
- * number of nodes present in current NUMA systems is quite small.
- * In fact, even if a sum of binomials is involved, if the system has
- * up to 16 nodes it "only" takes 65535 steps. This is fine, as the
- * number of nodes the biggest NUMA systems provide at the time of this
- * writing is 8 (and it will probably continue to be so for a while).
- * However, computanional complexity would explode on systems bigger
- * than that, and it's really important we avoid trying to run this
- * on monsters with 32, 64 or more nodes (if they ever pop into being).
- * Therefore, here it comes a safety catch that disables the algorithm
- * for the cases when it wouldn't work well.
- */
- if (nr_nodes > 16) {
- /* Log we did nothing and return 0, as no real error occurred */
- LOG(WARN, "System has %d NUMA nodes, which is too big for the "
- "placement algorithm to work effectively: skipping it. "
- "Consider manually pinning the vCPUs and/or looking at "
- "cpupools for manually partitioning the system.",
- nr_nodes);
- *cndt_found = 0;
- goto out;
- }
-
- tinfo = libxl_get_cpu_topology(CTX, &nr_cpus);
- if (tinfo == NULL) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl_node_bitmap_alloc(CTX, &nodemap, 0);
- if (rc)
- goto out;
- rc = libxl__numa_candidate_alloc(gc, &new_cndt);
- if (rc)
- goto out;
-
- /* Allocate and prepare the map of the node that can be utilized for
- * placement, basing on the map of suitable cpus. */
- rc = libxl_node_bitmap_alloc(CTX, &suitable_nodemap, 0);
- if (rc)
- goto out;
- rc = libxl_cpumap_to_nodemap(CTX, suitable_cpumap, &suitable_nodemap);
- if (rc)
- goto out;
-
- /*
- * Later on, we will try to figure out how many vcpus are runnable on
- * each candidate (as a part of choosing the best one of them). That
- * requires going through all the vcpus of all the domains and check
- * their affinities. So, instead of doing that for each candidate,
- * let's count here the number of vcpus runnable on each node, so that
- * all we have to do later is summing up the right elements of the
- * vcpus_on_node array.
- */
- rc = nr_vcpus_on_nodes(gc, tinfo, nr_cpus, suitable_cpumap, vcpus_on_node);
- if (rc)
- goto out;
-
- /*
- * If the minimum number of NUMA nodes is not explicitly specified
- * (i.e., min_nodes == 0), we try to figure out a sensible number of nodes
- * from where to start generating candidates, if possible (or just start
- * from 1 otherwise). The maximum number of nodes should not exceed the
- * number of existent NUMA nodes on the host, or the candidate generation
- * won't work properly.
- */
- if (!min_nodes) {
- int cpus_per_node;
-
- cpus_per_node = count_cpus_per_node(tinfo, nr_cpus, nr_nodes);
- if (cpus_per_node == 0)
- min_nodes = 1;
- else
- min_nodes = (min_cpus + cpus_per_node - 1) / cpus_per_node;
- }
- /* We also need to be sure we do not exceed the number of
- * nodes we are allowed to use. */
- nr_suit_nodes = libxl_bitmap_count_set(&suitable_nodemap);
-
- if (min_nodes > nr_suit_nodes)
- min_nodes = nr_suit_nodes;
- if (!max_nodes || max_nodes > nr_suit_nodes)
- max_nodes = nr_suit_nodes;
- if (min_nodes > max_nodes) {
- LOG(ERROR, "Inconsistent minimum or maximum number of guest nodes");
- rc = ERROR_INVAL;
- goto out;
- }
-
- /* This is up to the caller to be disposed */
- rc = libxl__numa_candidate_alloc(gc, cndt_out);
- if (rc)
- goto out;
-
- /*
- * Consider all the combinations with sizes in [min_nodes, max_nodes]
- * (see comb_init() and comb_next()). Note that, since the fewer the
- * number of nodes the better, it is guaranteed that any candidate
- * found during the i-eth step will be better than any other one we
- * could find during the (i+1)-eth and all the subsequent steps (they
- * all will have more nodes). It's thus pointless to keep going if
- * we already found something.
- */
- *cndt_found = 0;
- while (min_nodes <= max_nodes && *cndt_found == 0) {
- comb_iter_t comb_iter;
- int comb_ok;
-
- /*
- * And here it is. Each step of this cycle generates a combination of
- * nodes as big as min_nodes mandates. Each of these combinations is
- * checked against the constraints provided by the caller (namely,
- * amount of free memory and number of cpus) and it can concur to
- * become our best placement iff it passes the check.
- */
- for (comb_ok = comb_init(gc, &comb_iter, nr_suit_nodes, min_nodes);
- comb_ok;
- comb_ok = comb_next(comb_iter, nr_suit_nodes, min_nodes)) {
- uint64_t nodes_free_memkb;
- int nodes_cpus;
-
- /* Get the nodemap for the combination, only considering
- * suitable nodes. */
- comb_get_nodemap(comb_iter, &suitable_nodemap,
- &nodemap, min_nodes);
-
- /* If there is not enough memory in this combination, skip it
- * and go generating the next one... */
- nodes_free_memkb = nodemap_to_free_memkb(ninfo, &nodemap);
- if (min_free_memkb && nodes_free_memkb < min_free_memkb)
- continue;
-
- /* And the same applies if this combination is short in cpus */
- nodes_cpus = nodemap_to_nr_cpus(tinfo, nr_cpus, suitable_cpumap,
- &nodemap);
- if (min_cpus && nodes_cpus < min_cpus)
- continue;
-
- /*
- * Conditions are met, we can compare this candidate with the
- * current best one (if any).
- */
- libxl__numa_candidate_put_nodemap(gc, &new_cndt, &nodemap);
- new_cndt.nr_vcpus = nodemap_to_nr_vcpus(gc, vcpus_on_node,
- &nodemap);
- new_cndt.free_memkb = nodes_free_memkb;
- new_cndt.nr_nodes = libxl_bitmap_count_set(&nodemap);
- new_cndt.nr_cpus = nodes_cpus;
-
- /*
- * Check if the new candidate we is better the what we found up
- * to now by means of the comparison function. If no comparison
- * function is provided, just return as soon as we find our first
- * candidate.
- */
- if (*cndt_found == 0 || numa_cmpf(&new_cndt, cndt_out) < 0) {
- *cndt_found = 1;
-
- LOG(DEBUG, "New best NUMA placement candidate found: "
- "nr_nodes=%d, nr_cpus=%d, nr_vcpus=%d, "
- "free_memkb=%"PRIu64"", new_cndt.nr_nodes,
- new_cndt.nr_cpus, new_cndt.nr_vcpus,
- new_cndt.free_memkb / 1024);
-
- libxl__numa_candidate_put_nodemap(gc, cndt_out, &nodemap);
- cndt_out->nr_vcpus = new_cndt.nr_vcpus;
- cndt_out->free_memkb = new_cndt.free_memkb;
- cndt_out->nr_nodes = new_cndt.nr_nodes;
- cndt_out->nr_cpus = new_cndt.nr_cpus;
-
- if (numa_cmpf == NULL)
- break;
- }
- }
- min_nodes++;
- }
-
- if (*cndt_found == 0)
- LOG(NOTICE, "NUMA placement failed, performance might be affected");
-
- out:
- libxl_bitmap_dispose(&nodemap);
- libxl_bitmap_dispose(&suitable_nodemap);
- libxl__numa_candidate_dispose(&new_cndt);
- libxl_numainfo_list_free(ninfo, nr_nodes);
- libxl_cputopology_list_free(tinfo, nr_cpus);
- return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-/*
- * This header must be included first, before any system headers,
- * so that _GNU_SOURCE takes effect properly.
- */
-
-#ifndef LIBXL_OSDEP
-#define LIBXL_OSDEP
-
-#define _GNU_SOURCE
-
-#if defined(__NetBSD__)
-#define SYSFS_USB_DEV "/sys/bus/usb/devices"
-#define SYSFS_USBBACK_DRIVER "/kern/xen/usb"
-#define SYSFS_PCI_DEV "/sys/bus/pci/devices"
-#define SYSFS_PCIBACK_DRIVER "/kern/xen/pci"
-#define NETBACK_NIC_NAME "xvif%ui%d"
-#include <util.h>
-#include <uuid.h>
-#elif defined(__OpenBSD__)
-#include <util.h>
-#elif defined(__linux__)
-#define SYSFS_USB_DEV "/sys/bus/usb/devices"
-#define SYSFS_USBBACK_DRIVER "/sys/bus/usb/drivers/usbback"
-#define SYSFS_PCI_DEV "/sys/bus/pci/devices"
-#define SYSFS_PCIBACK_DRIVER "/sys/bus/pci/drivers/pciback"
-#define NETBACK_NIC_NAME "vif%u.%d"
-#include <sys/sysmacros.h>
-#include <pty.h>
-#include <uuid/uuid.h>
-#elif defined(__sun__)
-#include <stropts.h>
-#elif defined(__FreeBSD__)
-#define SYSFS_USB_DEV "/dev/null"
-#define SYSFS_USBBACK_DRIVER "/dev/null"
-#define SYSFS_PCI_DEV "/dev/null"
-#define SYSFS_PCIBACK_DRIVER "/dev/null"
-#define NETBACK_NIC_NAME "xnb%u.%d"
-#include <libutil.h>
-#include <sys/endian.h>
-#include <uuid.h>
-/*
- * FreeBSD doesn't have ENODATA errno ATM, so privcmd always translates
- * ENODATA into ENOENT.
- */
-#ifndef ENODATA
-#define ENODATA ENOENT
-#endif
-#endif
-
-#ifndef SYSFS_USBBACK_DRIVER
-#error define SYSFS_USBBACK_DRIVER for your platform
-#endif
-#ifndef SYSFS_USB_DEV
-#error define SYSFS_USB_DEV for your platform
-#endif
-
-#ifndef SYSFS_PCIBACK_DRIVER
-#error define SYSFS_PCIBACK_DRIVER for your platform
-#endif
-#ifndef SYSFS_PCI_DEV
-#error define SYSFS_PCI_DEV for your platform
-#endif
-
-#ifdef NEED_OWN_ASPRINTF
-#include <stdarg.h>
-
-int asprintf(char **buffer, char *fmt, ...);
-int vasprintf(char **buffer, const char *fmt, va_list ap);
-#endif /*NEED_OWN_ASPRINTF*/
-
-#ifndef htobe32 /* glibc < 2.9 */
-# include <byteswap.h>
-
-# if __BYTE_ORDER == __LITTLE_ENDIAN
-# define htobe16(x) __bswap_16(x)
-# define htole16(x) (x)
-# define be16toh(x) __bswap_16(x)
-# define le16toh(x) (x)
-
-# define htobe32(x) __bswap_32(x)
-# define htole32(x) (x)
-# define be32toh(x) __bswap_32(x)
-# define le32toh(x) (x)
-
-# define htobe64(x) __bswap_64(x)
-# define htole64(x) (x)
-# define be64toh(x) __bswap_64(x)
-# define le64toh(x) (x)
-# else
-# define htobe16(x) (x)
-# define htole16(x) __bswap_16(x)
-# define be16toh(x) (x)
-# define le16toh(x) __bswap_16(x)
-
-# define htobe32(x) (x)
-# define htole32(x) __bswap_32(x)
-# define be32toh(x) (x)
-# define le32toh(x) __bswap_32(x)
-
-# define htobe64(x) (x)
-# define htole64(x) __bswap_64(x)
-# define be64toh(x) (x)
-# define le64toh(x) __bswap_64(x)
-# endif
-#endif
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2010 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-#include "libxl_internal.h"
-
-const char *libxl__private_bindir_path(void)
-{
- return LIBEXEC_BIN;
-}
-
-const char *libxl__xenfirmwaredir_path(void)
-{
- return XENFIRMWAREDIR;
-}
-
-const char *libxl__xen_script_dir_path(void)
-{
- return XEN_SCRIPT_DIR;
-}
-
-const char *libxl__run_dir_path(void)
-{
- return XEN_RUN_DIR;
-}
-
-const char *libxl__seabios_path(void)
-{
-#ifdef SEABIOS_PATH
- return SEABIOS_PATH;
-#else
- return NULL;
-#endif
-}
-
-const char *libxl__ovmf_path(void)
-{
-#ifdef OVMF_PATH
- return OVMF_PATH;
-#else
- return NULL;
-#endif
-}
-
-const char *libxl__ipxe_path(void)
-{
-#ifdef IPXE_PATH
- return IPXE_PATH;
-#else
- return NULL;
-#endif
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#define PCI_BDF "%04x:%02x:%02x.%01x"
-#define PCI_BDF_SHORT "%02x:%02x.%01x"
-#define PCI_BDF_VDEVFN "%04x:%02x:%02x.%01x@%02x"
-#define PCI_OPTIONS "msitranslate=%d,power_mgmt=%d"
-#define PCI_BDF_XSPATH "%04x-%02x-%02x-%01x"
-#define PCI_PT_QDEV_ID "pci-pt-%02x_%02x.%01x"
-
-static unsigned int pcidev_encode_bdf(libxl_device_pci *pcidev)
-{
- unsigned int value;
-
- value = pcidev->domain << 16;
- value |= (pcidev->bus & 0xff) << 8;
- value |= (pcidev->dev & 0x1f) << 3;
- value |= (pcidev->func & 0x7);
-
- return value;
-}
-
-static void pcidev_struct_fill(libxl_device_pci *pcidev, unsigned int domain,
- unsigned int bus, unsigned int dev,
- unsigned int func, unsigned int vdevfn)
-{
- pcidev->domain = domain;
- pcidev->bus = bus;
- pcidev->dev = dev;
- pcidev->func = func;
- pcidev->vdevfn = vdevfn;
-}
-
-static void libxl_create_pci_backend_device(libxl__gc *gc,
- flexarray_t *back,
- int num,
- const libxl_device_pci *pcidev)
-{
- flexarray_append(back, GCSPRINTF("key-%d", num));
- flexarray_append(back, GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
- flexarray_append(back, GCSPRINTF("dev-%d", num));
- flexarray_append(back, GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
- if (pcidev->vdevfn)
- flexarray_append_pair(back, GCSPRINTF("vdevfn-%d", num), GCSPRINTF("%x", pcidev->vdevfn));
- flexarray_append(back, GCSPRINTF("opts-%d", num));
- flexarray_append(back,
- GCSPRINTF("msitranslate=%d,power_mgmt=%d,permissive=%d",
- pcidev->msitranslate, pcidev->power_mgmt,
- pcidev->permissive));
- flexarray_append_pair(back, GCSPRINTF("state-%d", num), GCSPRINTF("%d", XenbusStateInitialising));
-}
-
-static void libxl__device_from_pcidev(libxl__gc *gc, uint32_t domid,
- const libxl_device_pci *pcidev,
- libxl__device *device)
-{
- device->backend_devid = 0;
- device->backend_domid = 0;
- device->backend_kind = LIBXL__DEVICE_KIND_PCI;
- device->devid = 0;
- device->domid = domid;
- device->kind = LIBXL__DEVICE_KIND_PCI;
-}
-
-static int libxl__create_pci_backend(libxl__gc *gc, uint32_t domid,
- const libxl_device_pci *pcidev,
- int num)
-{
- flexarray_t *front = NULL;
- flexarray_t *back = NULL;
- libxl__device device;
- int i;
-
- front = flexarray_make(gc, 16, 1);
- back = flexarray_make(gc, 16, 1);
-
- LOGD(DEBUG, domid, "Creating pci backend");
-
- /* add pci device */
- libxl__device_from_pcidev(gc, domid, pcidev, &device);
-
- flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
- flexarray_append_pair(back, "online", "1");
- flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateInitialising));
- flexarray_append_pair(back, "domain", libxl__domid_to_name(gc, domid));
-
- for (i = 0; i < num; i++, pcidev++)
- libxl_create_pci_backend_device(gc, back, i, pcidev);
-
- flexarray_append_pair(back, "num_devs", GCSPRINTF("%d", num));
- flexarray_append_pair(front, "backend-id", GCSPRINTF("%d", 0));
- flexarray_append_pair(front, "state", GCSPRINTF("%d", XenbusStateInitialising));
-
- return libxl__device_generic_add(gc, XBT_NULL, &device,
- libxl__xs_kvs_of_flexarray(gc, back),
- libxl__xs_kvs_of_flexarray(gc, front),
- NULL);
-}
-
-static int libxl__device_pci_add_xenstore(libxl__gc *gc,
- uint32_t domid,
- const libxl_device_pci *pcidev,
- bool starting)
-{
- flexarray_t *back;
- char *num_devs, *be_path;
- int num = 0;
- xs_transaction_t t = XBT_NULL;
- int rc;
- libxl_domain_config d_config;
- libxl__flock *lock = NULL;
- bool is_stubdomain = libxl_is_stubdom(CTX, domid, NULL);
-
- /* Stubdomain doesn't have own config. */
- if (!is_stubdomain)
- libxl_domain_config_init(&d_config);
-
- be_path = libxl__domain_device_backend_path(gc, 0, domid, 0,
- LIBXL__DEVICE_KIND_PCI);
- num_devs = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/num_devs", be_path));
- if (!num_devs)
- return libxl__create_pci_backend(gc, domid, pcidev, 1);
-
- libxl_domain_type domtype = libxl__domain_type(gc, domid);
- if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
- return ERROR_FAIL;
-
- if (!starting && domtype == LIBXL_DOMAIN_TYPE_PV) {
- if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0)
- return ERROR_FAIL;
- }
-
- back = flexarray_make(gc, 16, 1);
-
- LOGD(DEBUG, domid, "Adding new pci device to xenstore");
- num = atoi(num_devs);
- libxl_create_pci_backend_device(gc, back, num, pcidev);
- flexarray_append_pair(back, "num_devs", GCSPRINTF("%d", num + 1));
- if (!starting)
- flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateReconfiguring));
-
- /*
- * Stubdomin config is derived from its target domain, it doesn't have
- * its own file.
- */
- if (!is_stubdomain) {
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- rc = libxl__get_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
-
- device_add_domain_config(gc, &d_config, &libxl__pcidev_devtype,
- pcidev);
-
- rc = libxl__dm_check_start(gc, &d_config, domid);
- if (rc) goto out;
- }
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
-
- if (lock) {
- rc = libxl__set_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
- }
-
- libxl__xs_writev(gc, t, be_path, libxl__xs_kvs_of_flexarray(gc, back));
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc < 0) goto out;
- }
-
-out:
- libxl__xs_transaction_abort(gc, &t);
- if (lock) libxl__unlock_file(lock);
- if (!is_stubdomain)
- libxl_domain_config_dispose(&d_config);
- return rc;
-}
-
-static int libxl__device_pci_remove_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char *be_path, *num_devs_path, *num_devs, *xsdev, *tmp, *tmppath;
- int num, i, j;
- xs_transaction_t t;
-
- be_path = libxl__domain_device_backend_path(gc, 0, domid, 0,
- LIBXL__DEVICE_KIND_PCI);
- num_devs_path = GCSPRINTF("%s/num_devs", be_path);
- num_devs = libxl__xs_read(gc, XBT_NULL, num_devs_path);
- if (!num_devs)
- return ERROR_INVAL;
- num = atoi(num_devs);
-
- libxl_domain_type domtype = libxl__domain_type(gc, domid);
- if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
- return ERROR_FAIL;
-
- if (domtype == LIBXL_DOMAIN_TYPE_PV) {
- if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0) {
- LOGD(DEBUG, domid, "pci backend at %s is not ready", be_path);
- return ERROR_FAIL;
- }
- }
-
- for (i = 0; i < num; i++) {
- unsigned int domain = 0, bus = 0, dev = 0, func = 0;
- xsdev = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/dev-%d", be_path, i));
- sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func);
- if (domain == pcidev->domain && bus == pcidev->bus &&
- pcidev->dev == dev && pcidev->func == func) {
- break;
- }
- }
- if (i == num) {
- LOGD(ERROR, domid, "Couldn't find the device on xenstore");
- return ERROR_INVAL;
- }
-
-retry_transaction:
- t = xs_transaction_start(ctx->xsh);
- xs_write(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, i), GCSPRINTF("%d", XenbusStateClosing), 1);
- xs_write(ctx->xsh, t, GCSPRINTF("%s/state", be_path), GCSPRINTF("%d", XenbusStateReconfiguring), 1);
- if (!xs_transaction_end(ctx->xsh, t, 0))
- if (errno == EAGAIN)
- goto retry_transaction;
-
- if (domtype == LIBXL_DOMAIN_TYPE_PV) {
- if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0) {
- LOGD(DEBUG, domid, "pci backend at %s is not ready", be_path);
- return ERROR_FAIL;
- }
- }
-
-retry_transaction2:
- t = xs_transaction_start(ctx->xsh);
- xs_rm(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, i));
- xs_rm(ctx->xsh, t, GCSPRINTF("%s/key-%d", be_path, i));
- xs_rm(ctx->xsh, t, GCSPRINTF("%s/dev-%d", be_path, i));
- xs_rm(ctx->xsh, t, GCSPRINTF("%s/vdev-%d", be_path, i));
- xs_rm(ctx->xsh, t, GCSPRINTF("%s/opts-%d", be_path, i));
- xs_rm(ctx->xsh, t, GCSPRINTF("%s/vdevfn-%d", be_path, i));
- libxl__xs_printf(gc, t, num_devs_path, "%d", num - 1);
- for (j = i + 1; j < num; j++) {
- tmppath = GCSPRINTF("%s/state-%d", be_path, j);
- tmp = libxl__xs_read(gc, t, tmppath);
- xs_write(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, j - 1), tmp, strlen(tmp));
- xs_rm(ctx->xsh, t, tmppath);
- tmppath = GCSPRINTF("%s/dev-%d", be_path, j);
- tmp = libxl__xs_read(gc, t, tmppath);
- xs_write(ctx->xsh, t, GCSPRINTF("%s/dev-%d", be_path, j - 1), tmp, strlen(tmp));
- xs_rm(ctx->xsh, t, tmppath);
- tmppath = GCSPRINTF("%s/key-%d", be_path, j);
- tmp = libxl__xs_read(gc, t, tmppath);
- xs_write(ctx->xsh, t, GCSPRINTF("%s/key-%d", be_path, j - 1), tmp, strlen(tmp));
- xs_rm(ctx->xsh, t, tmppath);
- tmppath = GCSPRINTF("%s/vdev-%d", be_path, j);
- tmp = libxl__xs_read(gc, t, tmppath);
- if (tmp) {
- xs_write(ctx->xsh, t, GCSPRINTF("%s/vdev-%d", be_path, j - 1), tmp, strlen(tmp));
- xs_rm(ctx->xsh, t, tmppath);
- }
- tmppath = GCSPRINTF("%s/opts-%d", be_path, j);
- tmp = libxl__xs_read(gc, t, tmppath);
- if (tmp) {
- xs_write(ctx->xsh, t, GCSPRINTF("%s/opts-%d", be_path, j - 1), tmp, strlen(tmp));
- xs_rm(ctx->xsh, t, tmppath);
- }
- tmppath = GCSPRINTF("%s/vdevfn-%d", be_path, j);
- tmp = libxl__xs_read(gc, t, tmppath);
- if (tmp) {
- xs_write(ctx->xsh, t, GCSPRINTF("%s/vdevfn-%d", be_path, j - 1), tmp, strlen(tmp));
- xs_rm(ctx->xsh, t, tmppath);
- }
- }
- if (!xs_transaction_end(ctx->xsh, t, 0))
- if (errno == EAGAIN)
- goto retry_transaction2;
-
- if (num == 1) {
- libxl__device dev;
- if (libxl__parse_backend_path(gc, be_path, &dev) != 0)
- return ERROR_FAIL;
-
- dev.domid = domid;
- dev.kind = LIBXL__DEVICE_KIND_PCI;
- dev.devid = 0;
-
- libxl__device_destroy(gc, &dev);
- return 0;
- }
-
- return 0;
-}
-
-static int get_all_assigned_devices(libxl__gc *gc, libxl_device_pci **list, int *num)
-{
- char **domlist;
- unsigned int nd = 0, i;
-
- *list = NULL;
- *num = 0;
-
- domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &nd);
- for(i = 0; i < nd; i++) {
- char *path, *num_devs;
-
- path = GCSPRINTF("/local/domain/0/backend/%s/%s/0/num_devs",
- libxl__device_kind_to_string(LIBXL__DEVICE_KIND_PCI),
- domlist[i]);
- num_devs = libxl__xs_read(gc, XBT_NULL, path);
- if ( num_devs ) {
- int ndev = atoi(num_devs), j;
- char *devpath, *bdf;
-
- for(j = 0; j < ndev; j++) {
- devpath = GCSPRINTF("/local/domain/0/backend/%s/%s/0/dev-%u",
- libxl__device_kind_to_string(LIBXL__DEVICE_KIND_PCI),
- domlist[i], j);
- bdf = libxl__xs_read(gc, XBT_NULL, devpath);
- if ( bdf ) {
- unsigned dom, bus, dev, func;
- if ( sscanf(bdf, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
- continue;
-
- *list = realloc(*list, sizeof(libxl_device_pci) * ((*num) + 1));
- if (*list == NULL)
- return ERROR_NOMEM;
- pcidev_struct_fill(*list + *num, dom, bus, dev, func, 0);
- (*num)++;
- }
- }
- }
- }
- libxl__ptr_add(gc, *list);
-
- return 0;
-}
-
-static int is_pcidev_in_array(libxl_device_pci *assigned, int num_assigned,
- int dom, int bus, int dev, int func)
-{
- int i;
-
- for(i = 0; i < num_assigned; i++) {
- if ( assigned[i].domain != dom )
- continue;
- if ( assigned[i].bus != bus )
- continue;
- if ( assigned[i].dev != dev )
- continue;
- if ( assigned[i].func != func )
- continue;
- return 1;
- }
-
- return 0;
-}
-
-/* Write the standard BDF into the sysfs path given by sysfs_path. */
-static int sysfs_write_bdf(libxl__gc *gc, const char * sysfs_path,
- libxl_device_pci *pcidev)
-{
- int rc, fd;
- char *buf;
-
- fd = open(sysfs_path, O_WRONLY);
- if (fd < 0) {
- LOGE(ERROR, "Couldn't open %s", sysfs_path);
- return ERROR_FAIL;
- }
-
- buf = GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus,
- pcidev->dev, pcidev->func);
- rc = write(fd, buf, strlen(buf));
- /* Annoying to have two if's, but we need the errno */
- if (rc < 0)
- LOGE(ERROR, "write to %s returned %d", sysfs_path, rc);
- close(fd);
-
- if (rc < 0)
- return ERROR_FAIL;
-
- return 0;
-}
-
-libxl_device_pci *libxl_device_pci_assignable_list(libxl_ctx *ctx, int *num)
-{
- GC_INIT(ctx);
- libxl_device_pci *pcidevs = NULL, *new, *assigned;
- struct dirent *de;
- DIR *dir;
- int r, num_assigned;
-
- *num = 0;
-
- r = get_all_assigned_devices(gc, &assigned, &num_assigned);
- if (r) goto out;
-
- dir = opendir(SYSFS_PCIBACK_DRIVER);
- if (NULL == dir) {
- if (errno == ENOENT) {
- LOG(ERROR, "Looks like pciback driver not loaded");
- } else {
- LOGE(ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER);
- }
- goto out;
- }
-
- while((de = readdir(dir))) {
- unsigned dom, bus, dev, func;
- if (sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4)
- continue;
-
- if (is_pcidev_in_array(assigned, num_assigned, dom, bus, dev, func))
- continue;
-
- new = realloc(pcidevs, ((*num) + 1) * sizeof(*new));
- if (NULL == new)
- continue;
-
- pcidevs = new;
- new = pcidevs + *num;
-
- memset(new, 0, sizeof(*new));
- pcidev_struct_fill(new, dom, bus, dev, func, 0);
- (*num)++;
- }
-
- closedir(dir);
-out:
- GC_FREE;
- return pcidevs;
-}
-
-/* Unbind device from its current driver, if any. If driver_path is non-NULL,
- * store the path to the original driver in it. */
-static int sysfs_dev_unbind(libxl__gc *gc, libxl_device_pci *pcidev,
- char **driver_path)
-{
- char * spath, *dp = NULL;
- struct stat st;
-
- spath = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/driver",
- pcidev->domain,
- pcidev->bus,
- pcidev->dev,
- pcidev->func);
- if ( !lstat(spath, &st) ) {
- /* Find the canonical path to the driver. */
- dp = libxl__zalloc(gc, PATH_MAX);
- dp = realpath(spath, dp);
- if ( !dp ) {
- LOGE(ERROR, "realpath() failed");
- return -1;
- }
-
- LOG(DEBUG, "Driver re-plug path: %s", dp);
-
- /* Unbind from the old driver */
- spath = GCSPRINTF("%s/unbind", dp);
- if ( sysfs_write_bdf(gc, spath, pcidev) < 0 ) {
- LOGE(ERROR, "Couldn't unbind device");
- return -1;
- }
- }
-
- if ( driver_path )
- *driver_path = dp;
-
- return 0;
-}
-
-static uint16_t sysfs_dev_get_vendor(libxl__gc *gc, libxl_device_pci *pcidev)
-{
- char *pci_device_vendor_path =
- GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/vendor",
- pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
- uint16_t read_items;
- uint16_t pci_device_vendor;
-
- FILE *f = fopen(pci_device_vendor_path, "r");
- if (!f) {
- LOGE(ERROR,
- "pci device "PCI_BDF" does not have vendor attribute",
- pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
- return 0xffff;
- }
- read_items = fscanf(f, "0x%hx\n", &pci_device_vendor);
- fclose(f);
- if (read_items != 1) {
- LOGE(ERROR,
- "cannot read vendor of pci device "PCI_BDF,
- pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
- return 0xffff;
- }
-
- return pci_device_vendor;
-}
-
-static uint16_t sysfs_dev_get_device(libxl__gc *gc, libxl_device_pci *pcidev)
-{
- char *pci_device_device_path =
- GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/device",
- pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
- uint16_t read_items;
- uint16_t pci_device_device;
-
- FILE *f = fopen(pci_device_device_path, "r");
- if (!f) {
- LOGE(ERROR,
- "pci device "PCI_BDF" does not have device attribute",
- pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
- return 0xffff;
- }
- read_items = fscanf(f, "0x%hx\n", &pci_device_device);
- fclose(f);
- if (read_items != 1) {
- LOGE(ERROR,
- "cannot read device of pci device "PCI_BDF,
- pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
- return 0xffff;
- }
-
- return pci_device_device;
-}
-
-static int sysfs_dev_get_class(libxl__gc *gc, libxl_device_pci *pcidev,
- unsigned long *class)
-{
- char *pci_device_class_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/class",
- pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
- int read_items, ret = 0;
-
- FILE *f = fopen(pci_device_class_path, "r");
- if (!f) {
- LOGE(ERROR,
- "pci device "PCI_BDF" does not have class attribute",
- pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
- ret = ERROR_FAIL;
- goto out;
- }
- read_items = fscanf(f, "0x%lx\n", class);
- fclose(f);
- if (read_items != 1) {
- LOGE(ERROR,
- "cannot read class of pci device "PCI_BDF,
- pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
- ret = ERROR_FAIL;
- }
-
-out:
- return ret;
-}
-
-/*
- * Some devices may need some ways to work well. Here like IGD,
- * we have to pass a specific option to qemu.
- */
-bool libxl__is_igd_vga_passthru(libxl__gc *gc,
- const libxl_domain_config *d_config)
-{
- unsigned int i;
- uint16_t pt_vendor, pt_device;
- unsigned long class;
-
- for (i = 0 ; i < d_config->num_pcidevs ; i++) {
- libxl_device_pci *pcidev = &d_config->pcidevs[i];
- pt_vendor = sysfs_dev_get_vendor(gc, pcidev);
- pt_device = sysfs_dev_get_device(gc, pcidev);
-
- if (pt_vendor == 0xffff || pt_device == 0xffff ||
- pt_vendor != 0x8086)
- continue;
-
- if (sysfs_dev_get_class(gc, pcidev, &class))
- continue;
- if (class == 0x030000)
- return true;
- }
-
- return false;
-}
-
-/*
- * A brief comment about slots. I don't know what slots are for; however,
- * I have by experimentation determined:
- * - Before a device can be bound to pciback, its BDF must first be listed
- * in pciback/slots
- * - The way to get the BDF listed there is to write BDF to
- * pciback/new_slot
- * - Writing the same BDF to pciback/new_slot is not idempotent; it results
- * in two entries of the BDF in pciback/slots
- * It's not clear whether having two entries in pciback/slots is a problem
- * or not. Just to be safe, this code does the conservative thing, and
- * first checks to see if there is a slot, adding one only if one does not
- * already exist.
- */
-
-/* Scan through /sys/.../pciback/slots looking for pcidev's BDF */
-static int pciback_dev_has_slot(libxl__gc *gc, libxl_device_pci *pcidev)
-{
- FILE *f;
- int rc = 0;
- unsigned dom, bus, dev, func;
-
- f = fopen(SYSFS_PCIBACK_DRIVER"/slots", "r");
-
- if (f == NULL) {
- LOGE(ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER"/slots");
- return ERROR_FAIL;
- }
-
- while(fscanf(f, "%x:%x:%x.%d\n", &dom, &bus, &dev, &func)==4) {
- if(dom == pcidev->domain
- && bus == pcidev->bus
- && dev == pcidev->dev
- && func == pcidev->func) {
- rc = 1;
- goto out;
- }
- }
-out:
- fclose(f);
- return rc;
-}
-
-static int pciback_dev_is_assigned(libxl__gc *gc, libxl_device_pci *pcidev)
-{
- char * spath;
- int rc;
- struct stat st;
-
- if ( access(SYSFS_PCIBACK_DRIVER, F_OK) < 0 ) {
- if ( errno == ENOENT ) {
- LOG(ERROR, "Looks like pciback driver is not loaded");
- } else {
- LOGE(ERROR, "Can't access "SYSFS_PCIBACK_DRIVER);
- }
- return -1;
- }
-
- spath = GCSPRINTF(SYSFS_PCIBACK_DRIVER"/"PCI_BDF,
- pcidev->domain, pcidev->bus,
- pcidev->dev, pcidev->func);
- rc = lstat(spath, &st);
-
- if( rc == 0 )
- return 1;
- if ( rc < 0 && errno == ENOENT )
- return 0;
- LOGE(ERROR, "Accessing %s", spath);
- return -1;
-}
-
-static int pciback_dev_assign(libxl__gc *gc, libxl_device_pci *pcidev)
-{
- int rc;
-
- if ( (rc=pciback_dev_has_slot(gc, pcidev)) < 0 ) {
- LOGE(ERROR, "Error checking for pciback slot");
- return ERROR_FAIL;
- } else if (rc == 0) {
- if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/new_slot",
- pcidev) < 0 ) {
- LOGE(ERROR, "Couldn't bind device to pciback!");
- return ERROR_FAIL;
- }
- }
-
- if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/bind", pcidev) < 0 ) {
- LOGE(ERROR, "Couldn't bind device to pciback!");
- return ERROR_FAIL;
- }
- return 0;
-}
-
-static int pciback_dev_unassign(libxl__gc *gc, libxl_device_pci *pcidev)
-{
- /* Remove from pciback */
- if ( sysfs_dev_unbind(gc, pcidev, NULL) < 0 ) {
- LOG(ERROR, "Couldn't unbind device!");
- return ERROR_FAIL;
- }
-
- /* Remove slot if necessary */
- if ( pciback_dev_has_slot(gc, pcidev) > 0 ) {
- if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/remove_slot",
- pcidev) < 0 ) {
- LOGE(ERROR, "Couldn't remove pciback slot");
- return ERROR_FAIL;
- }
- }
- return 0;
-}
-
-#define PCIBACK_INFO_PATH "/libxl/pciback"
-
-static void pci_assignable_driver_path_write(libxl__gc *gc,
- libxl_device_pci *pcidev,
- char *driver_path)
-{
- char *path;
-
- path = GCSPRINTF(PCIBACK_INFO_PATH"/"PCI_BDF_XSPATH"/driver_path",
- pcidev->domain,
- pcidev->bus,
- pcidev->dev,
- pcidev->func);
- if ( libxl__xs_printf(gc, XBT_NULL, path, "%s", driver_path) < 0 ) {
- LOGE(WARN, "Write of %s to node %s failed.", driver_path, path);
- }
-}
-
-static char * pci_assignable_driver_path_read(libxl__gc *gc,
- libxl_device_pci *pcidev)
-{
- return libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF(
- PCIBACK_INFO_PATH "/" PCI_BDF_XSPATH "/driver_path",
- pcidev->domain,
- pcidev->bus,
- pcidev->dev,
- pcidev->func));
-}
-
-static void pci_assignable_driver_path_remove(libxl__gc *gc,
- libxl_device_pci *pcidev)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
-
- /* Remove the xenstore entry */
- xs_rm(ctx->xsh, XBT_NULL,
- GCSPRINTF(PCIBACK_INFO_PATH "/" PCI_BDF_XSPATH,
- pcidev->domain,
- pcidev->bus,
- pcidev->dev,
- pcidev->func) );
-}
-
-static int libxl__device_pci_assignable_add(libxl__gc *gc,
- libxl_device_pci *pcidev,
- int rebind)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- unsigned dom, bus, dev, func;
- char *spath, *driver_path = NULL;
- int rc;
- struct stat st;
-
- /* Local copy for convenience */
- dom = pcidev->domain;
- bus = pcidev->bus;
- dev = pcidev->dev;
- func = pcidev->func;
-
- /* See if the device exists */
- spath = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF, dom, bus, dev, func);
- if ( lstat(spath, &st) ) {
- LOGE(ERROR, "Couldn't lstat %s", spath);
- return ERROR_FAIL;
- }
-
- /* Check to see if it's already assigned to pciback */
- rc = pciback_dev_is_assigned(gc, pcidev);
- if ( rc < 0 ) {
- return ERROR_FAIL;
- }
- if ( rc ) {
- LOG(WARN, PCI_BDF" already assigned to pciback", dom, bus, dev, func);
- goto quarantine;
- }
-
- /* Check to see if there's already a driver that we need to unbind from */
- if ( sysfs_dev_unbind(gc, pcidev, &driver_path ) ) {
- LOG(ERROR, "Couldn't unbind "PCI_BDF" from driver",
- dom, bus, dev, func);
- return ERROR_FAIL;
- }
-
- /* Store driver_path for rebinding to dom0 */
- if ( rebind ) {
- if ( driver_path ) {
- pci_assignable_driver_path_write(gc, pcidev, driver_path);
- } else if ( (driver_path =
- pci_assignable_driver_path_read(gc, pcidev)) != NULL ) {
- LOG(INFO, PCI_BDF" not bound to a driver, will be rebound to %s",
- dom, bus, dev, func, driver_path);
- } else {
- LOG(WARN, PCI_BDF" not bound to a driver, will not be rebound.",
- dom, bus, dev, func);
- }
- } else {
- pci_assignable_driver_path_remove(gc, pcidev);
- }
-
- if ( pciback_dev_assign(gc, pcidev) ) {
- LOG(ERROR, "Couldn't bind device to pciback!");
- return ERROR_FAIL;
- }
-
-quarantine:
- /*
- * DOMID_IO is just a sentinel domain, without any actual mappings,
- * so always pass XEN_DOMCTL_DEV_RDM_RELAXED to avoid assignment being
- * unnecessarily denied.
- */
- rc = xc_assign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev),
- XEN_DOMCTL_DEV_RDM_RELAXED);
- if ( rc < 0 ) {
- LOG(ERROR, "failed to quarantine "PCI_BDF, dom, bus, dev, func);
- return ERROR_FAIL;
- }
-
- return 0;
-}
-
-static int libxl__device_pci_assignable_remove(libxl__gc *gc,
- libxl_device_pci *pcidev,
- int rebind)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- int rc;
- char *driver_path;
-
- /* De-quarantine */
- rc = xc_deassign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev));
- if ( rc < 0 ) {
- LOG(ERROR, "failed to de-quarantine "PCI_BDF, pcidev->domain, pcidev->bus,
- pcidev->dev, pcidev->func);
- return ERROR_FAIL;
- }
-
- /* Unbind from pciback */
- if ( (rc=pciback_dev_is_assigned(gc, pcidev)) < 0 ) {
- return ERROR_FAIL;
- } else if ( rc ) {
- pciback_dev_unassign(gc, pcidev);
- } else {
- LOG(WARN, "Not bound to pciback");
- }
-
- /* Rebind if necessary */
- driver_path = pci_assignable_driver_path_read(gc, pcidev);
-
- if ( driver_path ) {
- if ( rebind ) {
- LOG(INFO, "Rebinding to driver at %s", driver_path);
-
- if ( sysfs_write_bdf(gc,
- GCSPRINTF("%s/bind", driver_path),
- pcidev) < 0 ) {
- LOGE(ERROR, "Couldn't bind device to %s", driver_path);
- return -1;
- }
-
- pci_assignable_driver_path_remove(gc, pcidev);
- }
- } else {
- if ( rebind ) {
- LOG(WARN,
- "Couldn't find path for original driver; not rebinding");
- }
- }
-
- return 0;
-}
-
-int libxl_device_pci_assignable_add(libxl_ctx *ctx, libxl_device_pci *pcidev,
- int rebind)
-{
- GC_INIT(ctx);
- int rc;
-
- rc = libxl__device_pci_assignable_add(gc, pcidev, rebind);
-
- GC_FREE;
- return rc;
-}
-
-
-int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci *pcidev,
- int rebind)
-{
- GC_INIT(ctx);
- int rc;
-
- rc = libxl__device_pci_assignable_remove(gc, pcidev, rebind);
-
- GC_FREE;
- return rc;
-}
-
-/*
- * This function checks that all functions of a device are bound to pciback
- * driver. It also initialises a bit-mask of which function numbers are present
- * on that device.
-*/
-static int pci_multifunction_check(libxl__gc *gc, libxl_device_pci *pcidev, unsigned int *func_mask)
-{
- struct dirent *de;
- DIR *dir;
-
- *func_mask = 0;
-
- dir = opendir(SYSFS_PCI_DEV);
- if ( NULL == dir ) {
- LOGE(ERROR, "Couldn't open %s", SYSFS_PCI_DEV);
- return -1;
- }
-
- while( (de = readdir(dir)) ) {
- unsigned dom, bus, dev, func;
- struct stat st;
- char *path;
-
- if ( sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
- continue;
- if ( pcidev->domain != dom )
- continue;
- if ( pcidev->bus != bus )
- continue;
- if ( pcidev->dev != dev )
- continue;
-
- path = GCSPRINTF("%s/" PCI_BDF, SYSFS_PCIBACK_DRIVER, dom, bus, dev, func);
- if ( lstat(path, &st) ) {
- if ( errno == ENOENT )
- LOG(ERROR, PCI_BDF " is not assigned to pciback driver",
- dom, bus, dev, func);
- else
- LOGE(ERROR, "Couldn't lstat %s", path);
- closedir(dir);
- return -1;
- }
- (*func_mask) |= (1 << func);
- }
-
- closedir(dir);
- return 0;
-}
-
-static int pci_ins_check(libxl__gc *gc, uint32_t domid, const char *state, void *priv)
-{
- char *orig_state = priv;
-
- if ( !strcmp(state, "pci-insert-failed") )
- return -1;
- if ( !strcmp(state, "pci-inserted") )
- return 0;
- if ( !strcmp(state, orig_state) )
- return 1;
-
- return 1;
-}
-
-static int qemu_pci_add_xenstore(libxl__gc *gc, uint32_t domid,
- libxl_device_pci *pcidev)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- int rc = 0;
- char *path;
- char *state, *vdevfn;
- uint32_t dm_domid;
-
- dm_domid = libxl_get_stubdom_id(CTX, domid);
- path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
- state = libxl__xs_read(gc, XBT_NULL, path);
- path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter");
- if (pcidev->vdevfn) {
- libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF_VDEVFN","PCI_OPTIONS,
- pcidev->domain, pcidev->bus, pcidev->dev,
- pcidev->func, pcidev->vdevfn, pcidev->msitranslate,
- pcidev->power_mgmt);
- } else {
- libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF","PCI_OPTIONS,
- pcidev->domain, pcidev->bus, pcidev->dev,
- pcidev->func, pcidev->msitranslate, pcidev->power_mgmt);
- }
-
- libxl__qemu_traditional_cmd(gc, domid, "pci-ins");
- rc = libxl__wait_for_device_model_deprecated(gc, domid, NULL, NULL,
- pci_ins_check, state);
- path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter");
- vdevfn = libxl__xs_read(gc, XBT_NULL, path);
- path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
- if ( rc < 0 )
- LOGD(ERROR, domid, "qemu refused to add device: %s", vdevfn);
- else if ( sscanf(vdevfn, "0x%x", &pcidev->vdevfn) != 1 ) {
- LOGD(ERROR, domid, "wrong format for the vdevfn: '%s'", vdevfn);
- rc = -1;
- }
- xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
-
- return rc;
-}
-
-static int check_qemu_running(libxl__gc *gc,
- libxl_domid domid,
- libxl__xswait_state *xswa,
- int rc,
- const char *state)
-{
- if (rc) {
- if (rc == ERROR_TIMEDOUT) {
- LOGD(ERROR, domid, "%s not ready", xswa->what);
- }
- goto out;
- }
-
- if (!state || strcmp(state, "running"))
- return ERROR_NOT_READY;
-
-out:
- libxl__xswait_stop(gc, xswa);
- return rc;
-}
-
-typedef struct pci_add_state {
- /* filled by user of do_pci_add */
- libxl__ao_device *aodev;
- libxl_domid domid;
- bool starting;
- void (*callback)(libxl__egc *, struct pci_add_state *, int rc);
-
- /* private to device_pci_add_stubdom_wait */
- libxl__ev_devstate pciback_ds;
-
- /* private to do_pci_add */
- libxl__xswait_state xswait;
- libxl__ev_qmp qmp;
- libxl__ev_time timeout;
- libxl_device_pci *pcidev;
- int pci_domid;
-} pci_add_state;
-
-static void pci_add_qemu_trad_watch_state_cb(libxl__egc *egc,
- libxl__xswait_state *xswa, int rc, const char *state);
-static void pci_add_qmp_device_add(libxl__egc *, pci_add_state *);
-static void pci_add_qmp_device_add_cb(libxl__egc *,
- libxl__ev_qmp *, const libxl__json_object *, int rc);
-static void pci_add_qmp_query_pci_cb(libxl__egc *,
- libxl__ev_qmp *, const libxl__json_object *, int rc);
-static void pci_add_timeout(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs, int rc);
-static void pci_add_dm_done(libxl__egc *,
- pci_add_state *, int rc);
-
-static void do_pci_add(libxl__egc *egc,
- libxl_domid domid,
- libxl_device_pci *pcidev,
- pci_add_state *pas)
-{
- STATE_AO_GC(pas->aodev->ao);
- libxl_domain_type type = libxl__domain_type(gc, domid);
- int rc;
-
- /* init pci_add_state */
- libxl__xswait_init(&pas->xswait);
- libxl__ev_qmp_init(&pas->qmp);
- pas->pcidev = pcidev;
- pas->pci_domid = domid;
- libxl__ev_time_init(&pas->timeout);
-
- if (type == LIBXL_DOMAIN_TYPE_INVALID) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (type == LIBXL_DOMAIN_TYPE_HVM) {
- switch (libxl__device_model_version_running(gc, domid)) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- pas->xswait.ao = ao;
- pas->xswait.what = "Device Model";
- pas->xswait.path = DEVICE_MODEL_XS_PATH(gc,
- libxl_get_stubdom_id(CTX, domid), domid, "/state");
- pas->xswait.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
- pas->xswait.callback = pci_add_qemu_trad_watch_state_cb;
- rc = libxl__xswait_start(gc, &pas->xswait);
- if (rc) goto out;
- return;
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- pci_add_qmp_device_add(egc, pas); /* must be last */
- return;
- default:
- rc = ERROR_INVAL;
- break;
- }
- }
-
- rc = 0;
-
-out:
- pci_add_dm_done(egc, pas, rc); /* must be last */
-}
-
-static void pci_add_qemu_trad_watch_state_cb(libxl__egc *egc,
- libxl__xswait_state *xswa,
- int rc,
- const char *state)
-{
- pci_add_state *pas = CONTAINER_OF(xswa, *pas, xswait);
- STATE_AO_GC(pas->aodev->ao);
-
- /* Convenience aliases */
- libxl_domid domid = pas->domid;
- libxl_device_pci *pcidev = pas->pcidev;
-
- rc = check_qemu_running(gc, domid, xswa, rc, state);
- if (rc == ERROR_NOT_READY)
- return;
- if (rc)
- goto out;
-
- rc = qemu_pci_add_xenstore(gc, domid, pcidev);
-out:
- pci_add_dm_done(egc, pas, rc); /* must be last */
-}
-
-static void pci_add_qmp_device_add(libxl__egc *egc, pci_add_state *pas)
-{
- STATE_AO_GC(pas->aodev->ao);
- libxl__json_object *args = NULL;
- int rc;
-
- /* Convenience aliases */
- libxl_domid domid = pas->domid;
- libxl_device_pci *pcidev = pas->pcidev;
- libxl__ev_qmp *const qmp = &pas->qmp;
-
- rc = libxl__ev_time_register_rel(ao, &pas->timeout,
- pci_add_timeout,
- LIBXL_QMP_CMD_TIMEOUT * 1000);
- if (rc) goto out;
-
- libxl__qmp_param_add_string(gc, &args, "driver",
- "xen-pci-passthrough");
- QMP_PARAMETERS_SPRINTF(&args, "id", PCI_PT_QDEV_ID,
- pcidev->bus, pcidev->dev, pcidev->func);
- QMP_PARAMETERS_SPRINTF(&args, "hostaddr",
- "%04x:%02x:%02x.%01x", pcidev->domain,
- pcidev->bus, pcidev->dev, pcidev->func);
- if (pcidev->vdevfn) {
- QMP_PARAMETERS_SPRINTF(&args, "addr", "%x.%x",
- PCI_SLOT(pcidev->vdevfn),
- PCI_FUNC(pcidev->vdevfn));
- }
- /*
- * Version of QEMU prior to the XSA-131 fix did not support
- * this property and were effectively always in permissive
- * mode. The fix for XSA-131 switched the default to be
- * restricted by default and added the permissive property.
- *
- * Therefore in order to support both old and new QEMU we only
- * set the permissive flag if it is true. Users of older QEMU
- * have no reason to set the flag so this is ok.
- */
- if (pcidev->permissive)
- libxl__qmp_param_add_bool(gc, &args, "permissive", true);
-
- qmp->ao = pas->aodev->ao;
- qmp->domid = domid;
- qmp->payload_fd = -1;
- qmp->callback = pci_add_qmp_device_add_cb;
- rc = libxl__ev_qmp_send(egc, qmp, "device_add", args);
- if (rc) goto out;
- return;
-
-out:
- pci_add_dm_done(egc, pas, rc); /* must be last */
-}
-
-static void pci_add_qmp_device_add_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *response,
- int rc)
-{
- EGC_GC;
- pci_add_state *pas = CONTAINER_OF(qmp, *pas, qmp);
-
- if (rc) goto out;
-
- qmp->callback = pci_add_qmp_query_pci_cb;
- rc = libxl__ev_qmp_send(egc, qmp, "query-pci", NULL);
- if (rc) goto out;
- return;
-
-out:
- pci_add_dm_done(egc, pas, rc); /* must be last */
-}
-
-static void pci_add_qmp_query_pci_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *response,
- int rc)
-{
- EGC_GC;
- pci_add_state *pas = CONTAINER_OF(qmp, *pas, qmp);
- const libxl__json_object *bus = NULL;
- char *asked_id;
- int i, j;
- const libxl__json_object *devices = NULL;
- const libxl__json_object *device = NULL;
- const libxl__json_object *o = NULL;
- const char *id = NULL;
- int dev_slot, dev_func;
-
- /* Convenience aliases */
- libxl_device_pci *pcidev = pas->pcidev;
-
- if (rc) goto out;
-
- /* `query-pci' returns:
- * [
- * {'bus': 'int',
- * 'devices': [
- * {'bus': 'int', 'slot': 'int', 'function': 'int',
- * 'class_info': 'PciDeviceClass', 'id': 'PciDeviceId',
- * '*irq': 'int', 'qdev_id': 'str',
- * '*pci_bridge': 'PciBridgeInfo',
- * 'regions': ['PciMemoryRegion']
- * }
- * ]
- * }
- * ]
- * (See qemu.git/qapi/ for the struct that aren't detailed here)
- */
-
- asked_id = GCSPRINTF(PCI_PT_QDEV_ID,
- pcidev->bus, pcidev->dev, pcidev->func);
-
- for (i = 0; (bus = libxl__json_array_get(response, i)); i++) {
- devices = libxl__json_map_get("devices", bus, JSON_ARRAY);
- if (!devices) {
- rc = ERROR_QEMU_API;
- goto out;
- }
-
- for (j = 0; (device = libxl__json_array_get(devices, j)); j++) {
- o = libxl__json_map_get("qdev_id", device, JSON_STRING);
- if (!o) {
- rc = ERROR_QEMU_API;
- goto out;
- }
- id = libxl__json_object_get_string(o);
- if (!id || strcmp(asked_id, id))
- continue;
-
- o = libxl__json_map_get("slot", device, JSON_INTEGER);
- if (!o) {
- rc = ERROR_QEMU_API;
- goto out;
- }
- dev_slot = libxl__json_object_get_integer(o);
- o = libxl__json_map_get("function", device, JSON_INTEGER);
- if (!o) {
- rc = ERROR_QEMU_API;
- goto out;
- }
- dev_func = libxl__json_object_get_integer(o);
-
- pcidev->vdevfn = PCI_DEVFN(dev_slot, dev_func);
-
- rc = 0;
- goto out;
- }
- }
-
- rc = ERROR_FAIL;
- LOGD(ERROR, qmp->domid,
- "PCI device id '%s' wasn't found in QEMU's 'query-pci' response.",
- asked_id);
-
-out:
- if (rc == ERROR_QEMU_API) {
- LOGD(ERROR, qmp->domid,
- "Unexpected response to QMP cmd 'query-pci', received:\n%s",
- JSON(response));
- }
- pci_add_dm_done(egc, pas, rc); /* must be last */
-}
-
-static void pci_add_timeout(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- pci_add_state *pas = CONTAINER_OF(ev, *pas, timeout);
-
- pci_add_dm_done(egc, pas, rc);
-}
-
-static void pci_add_dm_done(libxl__egc *egc,
- pci_add_state *pas,
- int rc)
-{
- STATE_AO_GC(pas->aodev->ao);
- libxl_ctx *ctx = libxl__gc_owner(gc);
- libxl_domid domid = pas->pci_domid;
- char *sysfs_path;
- FILE *f;
- unsigned long long start, end, flags, size;
- int irq, i;
- int r;
- uint32_t flag = XEN_DOMCTL_DEV_RDM_RELAXED;
- uint32_t domainid = domid;
- bool isstubdom = libxl_is_stubdom(ctx, domid, &domainid);
-
- /* Convenience aliases */
- bool starting = pas->starting;
- libxl_device_pci *pcidev = pas->pcidev;
- bool hvm = libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM;
-
- libxl__ev_qmp_dispose(gc, &pas->qmp);
-
- if (rc) goto out;
-
- /* stubdomain is always running by now, even at create time */
- if (isstubdom)
- starting = false;
-
- sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
- pcidev->bus, pcidev->dev, pcidev->func);
- f = fopen(sysfs_path, "r");
- start = end = flags = size = 0;
- irq = 0;
-
- if (f == NULL) {
- LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
- rc = ERROR_FAIL;
- goto out;
- }
- for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
- if (fscanf(f, "0x%llx 0x%llx 0x%llx\n", &start, &end, &flags) != 3)
- continue;
- size = end - start + 1;
- if (start) {
- if (flags & PCI_BAR_IO) {
- r = xc_domain_ioport_permission(ctx->xch, domid, start, size, 1);
- if (r < 0) {
- LOGED(ERROR, domainid,
- "xc_domain_ioport_permission 0x%llx/0x%llx (error %d)",
- start, size, r);
- fclose(f);
- rc = ERROR_FAIL;
- goto out;
- }
- } else {
- r = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
- (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 1);
- if (r < 0) {
- LOGED(ERROR, domainid,
- "xc_domain_iomem_permission 0x%llx/0x%llx (error %d)",
- start, size, r);
- fclose(f);
- rc = ERROR_FAIL;
- goto out;
- }
- }
- }
- }
- fclose(f);
- sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
- pcidev->bus, pcidev->dev, pcidev->func);
- f = fopen(sysfs_path, "r");
- if (f == NULL) {
- LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
- goto out_no_irq;
- }
- if ((fscanf(f, "%u", &irq) == 1) && irq) {
- r = xc_physdev_map_pirq(ctx->xch, domid, irq, &irq);
- if (r < 0) {
- LOGED(ERROR, domainid, "xc_physdev_map_pirq irq=%d (error=%d)",
- irq, r);
- fclose(f);
- rc = ERROR_FAIL;
- goto out;
- }
- r = xc_domain_irq_permission(ctx->xch, domid, irq, 1);
- if (r < 0) {
- LOGED(ERROR, domainid,
- "xc_domain_irq_permission irq=%d (error=%d)", irq, r);
- fclose(f);
- rc = ERROR_FAIL;
- goto out;
- }
- }
- fclose(f);
-
- /* Don't restrict writes to the PCI config space from this VM */
- if (pcidev->permissive) {
- if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/permissive",
- pcidev) < 0 ) {
- LOGD(ERROR, domainid, "Setting permissive for device");
- rc = ERROR_FAIL;
- goto out;
- }
- }
-
-out_no_irq:
- if (!isstubdom) {
- if (pcidev->rdm_policy == LIBXL_RDM_RESERVE_POLICY_STRICT) {
- flag &= ~XEN_DOMCTL_DEV_RDM_RELAXED;
- } else if (pcidev->rdm_policy != LIBXL_RDM_RESERVE_POLICY_RELAXED) {
- LOGED(ERROR, domainid, "unknown rdm check flag.");
- rc = ERROR_FAIL;
- goto out;
- }
- r = xc_assign_device(ctx->xch, domid, pcidev_encode_bdf(pcidev), flag);
- if (r < 0 && (hvm || errno != ENOSYS)) {
- LOGED(ERROR, domainid, "xc_assign_device failed");
- rc = ERROR_FAIL;
- goto out;
- }
- }
-
- if (!starting && !libxl_get_stubdom_id(CTX, domid))
- rc = libxl__device_pci_add_xenstore(gc, domid, pcidev, starting);
- else
- rc = 0;
-out:
- libxl__ev_time_deregister(gc, &pas->timeout);
- pas->callback(egc, pas, rc);
-}
-
-static int libxl__device_pci_reset(libxl__gc *gc, unsigned int domain, unsigned int bus,
- unsigned int dev, unsigned int func)
-{
- char *reset;
- int fd, rc;
-
- reset = GCSPRINTF("%s/do_flr", SYSFS_PCIBACK_DRIVER);
- fd = open(reset, O_WRONLY);
- if (fd >= 0) {
- char *buf = GCSPRINTF(PCI_BDF, domain, bus, dev, func);
- rc = write(fd, buf, strlen(buf));
- if (rc < 0)
- LOGD(ERROR, domain, "write to %s returned %d", reset, rc);
- close(fd);
- return rc < 0 ? rc : 0;
- }
- if (errno != ENOENT)
- LOGED(ERROR, domain, "Failed to access pciback path %s", reset);
- reset = GCSPRINTF("%s/"PCI_BDF"/reset", SYSFS_PCI_DEV, domain, bus, dev, func);
- fd = open(reset, O_WRONLY);
- if (fd >= 0) {
- rc = write(fd, "1", 1);
- if (rc < 0)
- LOGED(ERROR, domain, "write to %s returned %d", reset, rc);
- close(fd);
- return rc < 0 ? rc : 0;
- }
- if (errno == ENOENT) {
- LOGD(ERROR, domain,
- "The kernel doesn't support reset from sysfs for PCI device "PCI_BDF,
- domain, bus, dev, func);
- } else {
- LOGED(ERROR, domain, "Failed to access reset path %s", reset);
- }
- return -1;
-}
-
-int libxl__device_pci_setdefault(libxl__gc *gc, uint32_t domid,
- libxl_device_pci *pci, bool hotplug)
-{
- /* We'd like to force reserve rdm specific to a device by default.*/
- if (pci->rdm_policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
- pci->rdm_policy = LIBXL_RDM_RESERVE_POLICY_STRICT;
- return 0;
-}
-
-int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid,
- libxl_device_pci *pcidev,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- libxl__ao_device *aodev;
-
- GCNEW(aodev);
- libxl__prepare_ao_device(ao, aodev);
- aodev->action = LIBXL__DEVICE_ACTION_ADD;
- aodev->callback = device_addrm_aocomplete;
- aodev->update_json = true;
- libxl__device_pci_add(egc, domid, pcidev, false, aodev);
- return AO_INPROGRESS;
-}
-
-static int libxl_pcidev_assignable(libxl_ctx *ctx, libxl_device_pci *pcidev)
-{
- libxl_device_pci *pcidevs;
- int num, i;
-
- pcidevs = libxl_device_pci_assignable_list(ctx, &num);
- for (i = 0; i < num; i++) {
- if (pcidevs[i].domain == pcidev->domain &&
- pcidevs[i].bus == pcidev->bus &&
- pcidevs[i].dev == pcidev->dev &&
- pcidevs[i].func == pcidev->func)
- break;
- }
- free(pcidevs);
- return i != num;
-}
-
-static void device_pci_add_stubdom_wait(libxl__egc *egc,
- pci_add_state *pas, int rc);
-static void device_pci_add_stubdom_ready(libxl__egc *egc,
- libxl__ev_devstate *ds, int rc);
-static void device_pci_add_stubdom_done(libxl__egc *egc,
- pci_add_state *, int rc);
-static void device_pci_add_done(libxl__egc *egc,
- pci_add_state *, int rc);
-
-void libxl__device_pci_add(libxl__egc *egc, uint32_t domid,
- libxl_device_pci *pcidev, bool starting,
- libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- libxl_ctx *ctx = libxl__gc_owner(gc);
- libxl_device_pci *assigned;
- int num_assigned, rc;
- int stubdomid = 0;
- pci_add_state *pas;
-
- /* Store *pcidev to be used by callbacks */
- aodev->device_config = pcidev;
- aodev->device_type = &libxl__pcidev_devtype;
-
- GCNEW(pas);
- pas->aodev = aodev;
- pas->domid = domid;
- pas->starting = starting;
- pas->callback = device_pci_add_stubdom_done;
-
- if (libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) {
- rc = xc_test_assign_device(ctx->xch, domid, pcidev_encode_bdf(pcidev));
- if (rc) {
- LOGD(ERROR, domid,
- "PCI device %04x:%02x:%02x.%u %s?",
- pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func,
- errno == EOPNOTSUPP ? "cannot be assigned - no IOMMU"
- : "already assigned to a different guest");
- goto out;
- }
- }
-
- rc = libxl__device_pci_setdefault(gc, domid, pcidev, !starting);
- if (rc) goto out;
-
- if (pcidev->seize && !pciback_dev_is_assigned(gc, pcidev)) {
- rc = libxl__device_pci_assignable_add(gc, pcidev, 1);
- if ( rc )
- goto out;
- }
-
- if (!libxl_pcidev_assignable(ctx, pcidev)) {
- LOGD(ERROR, domid, "PCI device %x:%x:%x.%x is not assignable",
- pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = get_all_assigned_devices(gc, &assigned, &num_assigned);
- if ( rc ) {
- LOGD(ERROR, domid,
- "cannot determine if device is assigned, refusing to continue");
- goto out;
- }
- if ( is_pcidev_in_array(assigned, num_assigned, pcidev->domain,
- pcidev->bus, pcidev->dev, pcidev->func) ) {
- LOGD(ERROR, domid, "PCI device already attached to a domain");
- rc = ERROR_FAIL;
- goto out;
- }
-
- libxl__device_pci_reset(gc, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-
- stubdomid = libxl_get_stubdom_id(ctx, domid);
- if (stubdomid != 0) {
- libxl_device_pci *pcidev_s;
-
- GCNEW(pcidev_s);
- libxl_device_pci_init(pcidev_s);
- libxl_device_pci_copy(CTX, pcidev_s, pcidev);
- pas->callback = device_pci_add_stubdom_wait;
-
- do_pci_add(egc, stubdomid, pcidev_s, pas); /* must be last */
- return;
- }
-
- device_pci_add_stubdom_done(egc, pas, 0); /* must be last */
- return;
-
-out:
- device_pci_add_done(egc, pas, rc); /* must be last */
-}
-
-static void device_pci_add_stubdom_wait(libxl__egc *egc,
- pci_add_state *pas,
- int rc)
-{
- libxl__ao_device *aodev = pas->aodev;
- STATE_AO_GC(aodev->ao);
- int stubdomid = libxl_get_stubdom_id(CTX, pas->domid);
- char *state_path;
-
- if (rc) goto out;
-
- /* Wait for the device actually being connected, otherwise device model
- * running there will fail to find the device. */
- state_path = GCSPRINTF("%s/state",
- libxl__domain_device_backend_path(gc, 0, stubdomid, 0,
- LIBXL__DEVICE_KIND_PCI));
- rc = libxl__ev_devstate_wait(ao, &pas->pciback_ds,
- device_pci_add_stubdom_ready,
- state_path, XenbusStateConnected,
- LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000);
- if (rc) goto out;
- return;
-out:
- device_pci_add_done(egc, pas, rc); /* must be last */
-}
-
-static void device_pci_add_stubdom_ready(libxl__egc *egc,
- libxl__ev_devstate *ds,
- int rc)
-{
- pci_add_state *pas = CONTAINER_OF(ds, *pas, pciback_ds);
-
- device_pci_add_stubdom_done(egc, pas, rc); /* must be last */
-}
-
-static void device_pci_add_stubdom_done(libxl__egc *egc,
- pci_add_state *pas,
- int rc)
-{
- STATE_AO_GC(pas->aodev->ao);
- unsigned int orig_vdev, pfunc_mask;
- int i;
-
- /* Convenience aliases */
- libxl__ao_device *aodev = pas->aodev;
- libxl_domid domid = pas->domid;
- libxl_device_pci *pcidev = aodev->device_config;
-
- if (rc) goto out;
-
- orig_vdev = pcidev->vdevfn & ~7U;
-
- if ( pcidev->vfunc_mask == LIBXL_PCI_FUNC_ALL ) {
- if ( !(pcidev->vdevfn >> 3) ) {
- LOGD(ERROR, domid, "Must specify a v-slot for multi-function devices");
- rc = ERROR_INVAL;
- goto out;
- }
- if ( pci_multifunction_check(gc, pcidev, &pfunc_mask) ) {
- rc = ERROR_FAIL;
- goto out;
- }
- pcidev->vfunc_mask &= pfunc_mask;
- /* so now vfunc_mask == pfunc_mask */
- }else{
- pfunc_mask = (1 << pcidev->func);
- }
-
- for(rc = 0, i = 7; i >= 0; --i) {
- if ( (1 << i) & pfunc_mask ) {
- if ( pcidev->vfunc_mask == pfunc_mask ) {
- pcidev->func = i;
- pcidev->vdevfn = orig_vdev | i;
- }else{
- /* if not passing through multiple devices in a block make
- * sure that virtual function number 0 is always used otherwise
- * guest won't see the device
- */
- pcidev->vdevfn = orig_vdev;
- }
- pas->callback = device_pci_add_done;
- do_pci_add(egc, domid, pcidev, pas); /* must be last */
- return;
- }
- }
-
-out:
- device_pci_add_done(egc, pas, rc);
-}
-
-static void device_pci_add_done(libxl__egc *egc,
- pci_add_state *pas,
- int rc)
-{
- EGC_GC;
- libxl__ao_device *aodev = pas->aodev;
- libxl_domid domid = pas->domid;
- libxl_device_pci *pcidev = aodev->device_config;
-
- if (rc) {
- LOGD(ERROR, domid,
- "libxl__device_pci_add failed for "
- "PCI device %x:%x:%x.%x (rc %d)",
- pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func,
- rc);
- }
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-typedef struct {
- libxl__multidev multidev;
- libxl__ao_device *outer_aodev;
- libxl_domain_config *d_config;
- libxl_domid domid;
-} add_pcidevs_state;
-
-static void add_pcidevs_done(libxl__egc *, libxl__multidev *, int rc);
-
-static void libxl__add_pcidevs(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
- libxl_domain_config *d_config,
- libxl__multidev *multidev)
-{
- AO_GC;
- add_pcidevs_state *apds;
- int i;
-
- /* We need to start a new multidev in order to be able to execute
- * libxl__create_pci_backend only once. */
-
- GCNEW(apds);
- apds->outer_aodev = libxl__multidev_prepare(multidev);
- apds->d_config = d_config;
- apds->domid = domid;
- apds->multidev.callback = add_pcidevs_done;
- libxl__multidev_begin(ao, &apds->multidev);
-
- for (i = 0; i < d_config->num_pcidevs; i++) {
- libxl__ao_device *aodev = libxl__multidev_prepare(&apds->multidev);
- libxl__device_pci_add(egc, domid, &d_config->pcidevs[i],
- true, aodev);
- }
-
- libxl__multidev_prepared(egc, &apds->multidev, 0);
-}
-
-static void add_pcidevs_done(libxl__egc *egc, libxl__multidev *multidev,
- int rc)
-{
- EGC_GC;
- add_pcidevs_state *apds = CONTAINER_OF(multidev, *apds, multidev);
-
- /* Convenience aliases */
- libxl_domain_config *d_config = apds->d_config;
- libxl_domid domid = apds->domid;
- libxl__ao_device *aodev = apds->outer_aodev;
-
- if (rc) goto out;
-
- if (d_config->num_pcidevs > 0 && !libxl_get_stubdom_id(CTX, domid)) {
- rc = libxl__create_pci_backend(gc, domid, d_config->pcidevs,
- d_config->num_pcidevs);
- if (rc < 0) {
- LOGD(ERROR, domid, "libxl_create_pci_backend failed: %d", rc);
- goto out;
- }
- }
-
-out:
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-static int qemu_pci_remove_xenstore(libxl__gc *gc, uint32_t domid,
- libxl_device_pci *pcidev, int force)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char *state;
- char *path;
- uint32_t dm_domid;
-
- dm_domid = libxl_get_stubdom_id(CTX, domid);
-
- path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
- state = libxl__xs_read(gc, XBT_NULL, path);
- path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter");
- libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF, pcidev->domain,
- pcidev->bus, pcidev->dev, pcidev->func);
-
- /* Remove all functions at once atomically by only signalling
- * device-model for function 0 */
- if ( !force && (pcidev->vdevfn & 0x7) == 0 ) {
- libxl__qemu_traditional_cmd(gc, domid, "pci-rem");
- if (libxl__wait_for_device_model_deprecated(gc, domid, "pci-removed",
- NULL, NULL, NULL) < 0) {
- LOGD(ERROR, domid, "Device Model didn't respond in time");
- /* This depends on guest operating system acknowledging the
- * SCI, if it doesn't respond in time then we may wish to
- * force the removal.
- */
- return ERROR_FAIL;
- }
- }
- path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
- xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
-
- return 0;
-}
-
-typedef struct pci_remove_state {
- libxl__ao_device *aodev;
- libxl_domid domid;
- libxl_device_pci *pcidev;
- bool force;
- bool hvm;
- unsigned int orig_vdev;
- unsigned int pfunc_mask;
- int next_func;
- libxl__ao_device stubdom_aodev;
- libxl__xswait_state xswait;
- libxl__ev_qmp qmp;
- libxl__ev_time timeout;
- libxl__ev_time retry_timer;
-} pci_remove_state;
-
-static void libxl__device_pci_remove_common(libxl__egc *egc,
- uint32_t domid, libxl_device_pci *pcidev, bool force,
- libxl__ao_device *aodev);
-static void device_pci_remove_common_next(libxl__egc *egc,
- pci_remove_state *prs, int rc);
-
-static void pci_remove_qemu_trad_watch_state_cb(libxl__egc *egc,
- libxl__xswait_state *xswa, int rc, const char *state);
-static void pci_remove_qmp_device_del(libxl__egc *egc,
- pci_remove_state *prs);
-static void pci_remove_qmp_device_del_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
-static void pci_remove_qmp_retry_timer_cb(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void pci_remove_qmp_query_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
-static void pci_remove_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void pci_remove_detatched(libxl__egc *egc,
- pci_remove_state *prs, int rc);
-static void pci_remove_stubdom_done(libxl__egc *egc,
- libxl__ao_device *aodev);
-static void pci_remove_done(libxl__egc *egc,
- pci_remove_state *prs, int rc);
-
-static void do_pci_remove(libxl__egc *egc, uint32_t domid,
- libxl_device_pci *pcidev, int force,
- pci_remove_state *prs)
-{
- STATE_AO_GC(prs->aodev->ao);
- libxl_ctx *ctx = libxl__gc_owner(gc);
- libxl_device_pci *assigned;
- libxl_domain_type type = libxl__domain_type(gc, domid);
- int rc, num;
- uint32_t domainid = domid;
-
- assigned = libxl_device_pci_list(ctx, domid, &num);
- if (assigned == NULL) {
- rc = ERROR_FAIL;
- goto out_fail;
- }
- libxl__ptr_add(gc, assigned);
-
- rc = ERROR_INVAL;
- if ( !is_pcidev_in_array(assigned, num, pcidev->domain,
- pcidev->bus, pcidev->dev, pcidev->func) ) {
- LOGD(ERROR, domainid, "PCI device not attached to this domain");
- goto out_fail;
- }
-
- rc = ERROR_FAIL;
- if (type == LIBXL_DOMAIN_TYPE_HVM) {
- prs->hvm = true;
- switch (libxl__device_model_version_running(gc, domid)) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- prs->xswait.ao = ao;
- prs->xswait.what = "Device Model";
- prs->xswait.path = DEVICE_MODEL_XS_PATH(gc,
- libxl_get_stubdom_id(CTX, domid), domid, "/state");
- prs->xswait.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
- prs->xswait.callback = pci_remove_qemu_trad_watch_state_cb;
- rc = libxl__xswait_start(gc, &prs->xswait);
- if (rc) goto out_fail;
- return;
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- pci_remove_qmp_device_del(egc, prs); /* must be last */
- return;
- default:
- rc = ERROR_INVAL;
- goto out_fail;
- }
- } else {
- assert(type == LIBXL_DOMAIN_TYPE_PV);
-
- char *sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
- pcidev->bus, pcidev->dev, pcidev->func);
- FILE *f = fopen(sysfs_path, "r");
- unsigned int start = 0, end = 0, flags = 0, size = 0;
- int irq = 0;
- int i;
-
- if (f == NULL) {
- LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
- goto skip1;
- }
- for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
- if (fscanf(f, "0x%x 0x%x 0x%x\n", &start, &end, &flags) != 3)
- continue;
- size = end - start + 1;
- if (start) {
- if (flags & PCI_BAR_IO) {
- rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 0);
- if (rc < 0)
- LOGED(ERROR, domainid,
- "xc_domain_ioport_permission error 0x%x/0x%x",
- start,
- size);
- } else {
- rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
- (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 0);
- if (rc < 0)
- LOGED(ERROR, domainid,
- "xc_domain_iomem_permission error 0x%x/0x%x",
- start,
- size);
- }
- }
- }
- fclose(f);
-skip1:
- sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
- pcidev->bus, pcidev->dev, pcidev->func);
- f = fopen(sysfs_path, "r");
- if (f == NULL) {
- LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
- goto skip_irq;
- }
- if ((fscanf(f, "%u", &irq) == 1) && irq) {
- rc = xc_physdev_unmap_pirq(ctx->xch, domid, irq);
- if (rc < 0) {
- LOGED(ERROR, domainid, "xc_physdev_unmap_pirq irq=%d", irq);
- }
- rc = xc_domain_irq_permission(ctx->xch, domid, irq, 0);
- if (rc < 0) {
- LOGED(ERROR, domainid, "xc_domain_irq_permission irq=%d", irq);
- }
- }
- fclose(f);
- }
-skip_irq:
- rc = 0;
-out_fail:
- pci_remove_detatched(egc, prs, rc); /* must be last */
-}
-
-static void pci_remove_qemu_trad_watch_state_cb(libxl__egc *egc,
- libxl__xswait_state *xswa,
- int rc,
- const char *state)
-{
- pci_remove_state *prs = CONTAINER_OF(xswa, *prs, xswait);
- STATE_AO_GC(prs->aodev->ao);
-
- /* Convenience aliases */
- libxl_domid domid = prs->domid;
- libxl_device_pci *const pcidev = prs->pcidev;
-
- rc = check_qemu_running(gc, domid, xswa, rc, state);
- if (rc == ERROR_NOT_READY)
- return;
- if (rc)
- goto out;
-
- rc = qemu_pci_remove_xenstore(gc, domid, pcidev, prs->force);
-
-out:
- pci_remove_detatched(egc, prs, rc);
-}
-
-static void pci_remove_qmp_device_del(libxl__egc *egc,
- pci_remove_state *prs)
-{
- STATE_AO_GC(prs->aodev->ao);
- libxl__json_object *args = NULL;
- int rc;
-
- /* Convenience aliases */
- libxl_device_pci *const pcidev = prs->pcidev;
-
- rc = libxl__ev_time_register_rel(ao, &prs->timeout,
- pci_remove_timeout,
- LIBXL_QMP_CMD_TIMEOUT * 1000);
- if (rc) goto out;
-
- QMP_PARAMETERS_SPRINTF(&args, "id", PCI_PT_QDEV_ID,
- pcidev->bus, pcidev->dev, pcidev->func);
- prs->qmp.callback = pci_remove_qmp_device_del_cb;
- rc = libxl__ev_qmp_send(egc, &prs->qmp, "device_del", args);
- if (rc) goto out;
- return;
-
-out:
- pci_remove_detatched(egc, prs, rc);
-}
-
-static void pci_remove_qmp_device_del_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *response,
- int rc)
-{
- EGC_GC;
- pci_remove_state *prs = CONTAINER_OF(qmp, *prs, qmp);
-
- if (rc) goto out;
-
- /* Now that the command is sent, we want to wait until QEMU has
- * confirmed that the device is removed. */
- /* TODO: Instead of using a poll loop { ev_timer ; query-pci }, it
- * could be possible to listen to events sent by QEMU via QMP in order
- * to wait for the passthrough pci-device to be removed from QEMU. */
- pci_remove_qmp_retry_timer_cb(egc, &prs->retry_timer, NULL,
- ERROR_TIMEDOUT);
- return;
-
-out:
- pci_remove_detatched(egc, prs, rc);
-}
-
-static void pci_remove_qmp_retry_timer_cb(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- EGC_GC;
- pci_remove_state *prs = CONTAINER_OF(ev, *prs, retry_timer);
-
- prs->qmp.callback = pci_remove_qmp_query_cb;
- rc = libxl__ev_qmp_send(egc, &prs->qmp, "query-pci", NULL);
- if (rc) goto out;
- return;
-
-out:
- pci_remove_detatched(egc, prs, rc);
-}
-
-static void pci_remove_qmp_query_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *response,
- int rc)
-{
- EGC_GC;
- pci_remove_state *prs = CONTAINER_OF(qmp, *prs, qmp);
- const libxl__json_object *bus = NULL;
- const char *asked_id;
- int i, j;
-
- /* Convenience aliases */
- libxl__ao *const ao = prs->aodev->ao;
- libxl_device_pci *const pcidev = prs->pcidev;
-
- if (rc) goto out;
-
- libxl__ev_qmp_dispose(gc, qmp);
-
- asked_id = GCSPRINTF(PCI_PT_QDEV_ID,
- pcidev->bus, pcidev->dev, pcidev->func);
-
- /* query-pci response:
- * [{ 'devices': [ 'qdev_id': 'str', ... ], ... }]
- * */
-
- for (i = 0; (bus = libxl__json_array_get(response, i)); i++) {
- const libxl__json_object *devices = NULL;
- const libxl__json_object *device = NULL;
- const libxl__json_object *o = NULL;
- const char *id = NULL;
-
- devices = libxl__json_map_get("devices", bus, JSON_ARRAY);
- if (!devices) {
- rc = ERROR_QEMU_API;
- goto out;
- }
-
- for (j = 0; (device = libxl__json_array_get(devices, j)); j++) {
- o = libxl__json_map_get("qdev_id", device, JSON_STRING);
- if (!o) {
- rc = ERROR_QEMU_API;
- goto out;
- }
- id = libxl__json_object_get_string(o);
-
- if (id && !strcmp(asked_id, id)) {
- /* Device still in QEMU, need to wait longuer. */
- rc = libxl__ev_time_register_rel(ao, &prs->retry_timer,
- pci_remove_qmp_retry_timer_cb, 1000);
- if (rc) goto out;
- return;
- }
- }
- }
-
-out:
- pci_remove_detatched(egc, prs, rc); /* must be last */
-}
-
-static void pci_remove_timeout(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- EGC_GC;
- pci_remove_state *prs = CONTAINER_OF(ev, *prs, timeout);
-
- /* Convenience aliases */
- libxl_device_pci *const pcidev = prs->pcidev;
-
- LOGD(WARN, prs->domid, "timed out waiting for DM to remove "
- PCI_PT_QDEV_ID, pcidev->bus, pcidev->dev, pcidev->func);
-
- /* If we timed out, we might still want to keep destroying the device
- * (when force==true), so let the next function decide what to do on
- * error */
- pci_remove_detatched(egc, prs, rc);
-}
-
-static void pci_remove_detatched(libxl__egc *egc,
- pci_remove_state *prs,
- int rc)
-{
- STATE_AO_GC(prs->aodev->ao);
- int stubdomid = 0;
- uint32_t domainid = prs->domid;
- bool isstubdom;
-
- /* Convenience aliases */
- libxl_device_pci *const pcidev = prs->pcidev;
- libxl_domid domid = prs->domid;
-
- /* Cleaning QMP states ASAP */
- libxl__ev_qmp_dispose(gc, &prs->qmp);
- libxl__ev_time_deregister(gc, &prs->timeout);
- libxl__ev_time_deregister(gc, &prs->retry_timer);
-
- if (rc && !prs->force)
- goto out;
-
- isstubdom = libxl_is_stubdom(CTX, domid, &domainid);
-
- /* don't do multiple resets while some functions are still passed through */
- if ( (pcidev->vdevfn & 0x7) == 0 ) {
- libxl__device_pci_reset(gc, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
- }
-
- if (!isstubdom) {
- rc = xc_deassign_device(CTX->xch, domid, pcidev_encode_bdf(pcidev));
- if (rc < 0 && (prs->hvm || errno != ENOSYS))
- LOGED(ERROR, domainid, "xc_deassign_device failed");
- }
-
- stubdomid = libxl_get_stubdom_id(CTX, domid);
- if (stubdomid != 0) {
- libxl_device_pci *pcidev_s;
- libxl__ao_device *const stubdom_aodev = &prs->stubdom_aodev;
-
- GCNEW(pcidev_s);
- libxl_device_pci_init(pcidev_s);
- libxl_device_pci_copy(CTX, pcidev_s, pcidev);
-
- libxl__prepare_ao_device(ao, stubdom_aodev);
- stubdom_aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
- stubdom_aodev->callback = pci_remove_stubdom_done;
- stubdom_aodev->update_json = prs->aodev->update_json;
- libxl__device_pci_remove_common(egc, stubdomid, pcidev_s,
- prs->force, stubdom_aodev);
- return;
- }
-
- rc = 0;
-out:
- pci_remove_done(egc, prs, rc);
-}
-
-static void pci_remove_stubdom_done(libxl__egc *egc,
- libxl__ao_device *aodev)
-{
- pci_remove_state *prs = CONTAINER_OF(aodev, *prs, stubdom_aodev);
-
- pci_remove_done(egc, prs, 0);
-}
-
-static void pci_remove_done(libxl__egc *egc,
- pci_remove_state *prs,
- int rc)
-{
- EGC_GC;
-
- if (rc) goto out;
-
- libxl__device_pci_remove_xenstore(gc, prs->domid, prs->pcidev);
-out:
- device_pci_remove_common_next(egc, prs, rc);
-}
-
-static void libxl__device_pci_remove_common(libxl__egc *egc,
- uint32_t domid,
- libxl_device_pci *pcidev,
- bool force,
- libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- int rc;
- pci_remove_state *prs;
-
- GCNEW(prs);
- prs->aodev = aodev;
- prs->domid = domid;
- prs->pcidev = pcidev;
- prs->force = force;
- libxl__xswait_init(&prs->xswait);
- libxl__ev_qmp_init(&prs->qmp);
- prs->qmp.ao = prs->aodev->ao;
- prs->qmp.domid = prs->domid;
- prs->qmp.payload_fd = -1;
- libxl__ev_time_init(&prs->timeout);
- libxl__ev_time_init(&prs->retry_timer);
-
- prs->orig_vdev = pcidev->vdevfn & ~7U;
-
- if ( pcidev->vfunc_mask == LIBXL_PCI_FUNC_ALL ) {
- if ( pci_multifunction_check(gc, pcidev, &prs->pfunc_mask) ) {
- rc = ERROR_FAIL;
- goto out;
- }
- pcidev->vfunc_mask &= prs->pfunc_mask;
- }else{
- prs->pfunc_mask = (1 << pcidev->func);
- }
-
- rc = 0;
- prs->next_func = 7;
-out:
- device_pci_remove_common_next(egc, prs, rc);
-}
-
-static void device_pci_remove_common_next(libxl__egc *egc,
- pci_remove_state *prs,
- int rc)
-{
- EGC_GC;
-
- /* Convenience aliases */
- libxl_domid domid = prs->domid;
- libxl_device_pci *const pcidev = prs->pcidev;
- libxl__ao_device *const aodev = prs->aodev;
- const unsigned int pfunc_mask = prs->pfunc_mask;
- const unsigned int orig_vdev = prs->orig_vdev;
-
- if (rc) goto out;
-
- while (prs->next_func >= 0) {
- const int i = prs->next_func;
- prs->next_func--;
- if ( (1 << i) & pfunc_mask ) {
- if ( pcidev->vfunc_mask == pfunc_mask ) {
- pcidev->func = i;
- pcidev->vdevfn = orig_vdev | i;
- }else{
- pcidev->vdevfn = orig_vdev;
- }
- do_pci_remove(egc, domid, pcidev, prs->force, prs);
- return;
- }
- }
-
- rc = 0;
-out:
- libxl__ev_qmp_dispose(gc, &prs->qmp);
- libxl__xswait_stop(gc, &prs->xswait);
- libxl__ev_time_deregister(gc, &prs->timeout);
- libxl__ev_time_deregister(gc, &prs->retry_timer);
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_pci *pcidev,
- const libxl_asyncop_how *ao_how)
-
-{
- AO_CREATE(ctx, domid, ao_how);
- libxl__ao_device *aodev;
-
- GCNEW(aodev);
- libxl__prepare_ao_device(ao, aodev);
- aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
- aodev->callback = device_addrm_aocomplete;
- aodev->update_json = true;
- libxl__device_pci_remove_common(egc, domid, pcidev, false, aodev);
- return AO_INPROGRESS;
-}
-
-int libxl_device_pci_destroy(libxl_ctx *ctx, uint32_t domid,
- libxl_device_pci *pcidev,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- libxl__ao_device *aodev;
-
- GCNEW(aodev);
- libxl__prepare_ao_device(ao, aodev);
- aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
- aodev->callback = device_addrm_aocomplete;
- aodev->update_json = true;
- libxl__device_pci_remove_common(egc, domid, pcidev, true, aodev);
- return AO_INPROGRESS;
-}
-
-static int libxl__device_pci_from_xs_be(libxl__gc *gc,
- const char *be_path,
- libxl_devid nr, void *data)
-{
- char *s;
- unsigned int domain = 0, bus = 0, dev = 0, func = 0, vdevfn = 0;
- libxl_device_pci *pci = data;
-
- s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/dev-%d", be_path, nr));
- sscanf(s, PCI_BDF, &domain, &bus, &dev, &func);
-
- s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vdevfn-%d", be_path, nr));
- if (s)
- vdevfn = strtol(s, (char **) NULL, 16);
-
- pcidev_struct_fill(pci, domain, bus, dev, func, vdevfn);
-
- s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/opts-%d", be_path, nr));
- if (s) {
- char *saveptr;
- char *p = strtok_r(s, ",=", &saveptr);
- do {
- while (*p == ' ')
- p++;
- if (!strcmp(p, "msitranslate")) {
- p = strtok_r(NULL, ",=", &saveptr);
- pci->msitranslate = atoi(p);
- } else if (!strcmp(p, "power_mgmt")) {
- p = strtok_r(NULL, ",=", &saveptr);
- pci->power_mgmt = atoi(p);
- } else if (!strcmp(p, "permissive")) {
- p = strtok_r(NULL, ",=", &saveptr);
- pci->permissive = atoi(p);
- }
- } while ((p = strtok_r(NULL, ",=", &saveptr)) != NULL);
- }
-
- return 0;
-}
-
-static int libxl__device_pci_get_num(libxl__gc *gc, const char *be_path,
- unsigned int *num)
-{
- char *num_devs;
- int rc = 0;
-
- num_devs = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/num_devs", be_path));
- if (!num_devs)
- rc = ERROR_FAIL;
- else
- *num = atoi(num_devs);
-
- return rc;
-}
-
-libxl_device_pci *libxl_device_pci_list(libxl_ctx *ctx, uint32_t domid, int *num)
-{
- GC_INIT(ctx);
- char *be_path;
- unsigned int n, i;
- libxl_device_pci *pcidevs = NULL;
-
- *num = 0;
-
- be_path = libxl__domain_device_backend_path(gc, 0, domid, 0,
- LIBXL__DEVICE_KIND_PCI);
- if (libxl__device_pci_get_num(gc, be_path, &n))
- goto out;
-
- pcidevs = calloc(n, sizeof(libxl_device_pci));
-
- for (i = 0; i < n; i++)
- libxl__device_pci_from_xs_be(gc, be_path, i, pcidevs + i);
-
- *num = n;
-out:
- GC_FREE;
- return pcidevs;
-}
-
-void libxl__device_pci_destroy_all(libxl__egc *egc, uint32_t domid,
- libxl__multidev *multidev)
-{
- STATE_AO_GC(multidev->ao);
- libxl_device_pci *pcidevs;
- int num, i;
-
- pcidevs = libxl_device_pci_list(CTX, domid, &num);
- if ( pcidevs == NULL )
- return;
- libxl__ptr_add(gc, pcidevs);
-
- for (i = 0; i < num; i++) {
- /* Force remove on shutdown since, on HVM, qemu will not always
- * respond to SCI interrupt because the guest kernel has shut down the
- * devices by the time we even get here!
- */
- libxl__ao_device *aodev = libxl__multidev_prepare(multidev);
- libxl__device_pci_remove_common(egc, domid, pcidevs + i, true,
- aodev);
- }
-}
-
-int libxl__grant_vga_iomem_permission(libxl__gc *gc, const uint32_t domid,
- libxl_domain_config *const d_config)
-{
- int i, ret;
-
- if (!libxl_defbool_val(d_config->b_info.u.hvm.gfx_passthru))
- return 0;
-
- for (i = 0 ; i < d_config->num_pcidevs ; i++) {
- uint64_t vga_iomem_start = 0xa0000 >> XC_PAGE_SHIFT;
- uint32_t stubdom_domid;
- libxl_device_pci *pcidev = &d_config->pcidevs[i];
- unsigned long pci_device_class;
-
- if (sysfs_dev_get_class(gc, pcidev, &pci_device_class))
- continue;
- if (pci_device_class != 0x030000) /* VGA class */
- continue;
-
- stubdom_domid = libxl_get_stubdom_id(CTX, domid);
- ret = xc_domain_iomem_permission(CTX->xch, stubdom_domid,
- vga_iomem_start, 0x20, 1);
- if (ret < 0) {
- LOGED(ERROR, domid,
- "failed to give stubdom%d access to iomem range "
- "%"PRIx64"-%"PRIx64" for VGA passthru",
- stubdom_domid,
- vga_iomem_start, (vga_iomem_start + 0x20 - 1));
- return ret;
- }
- ret = xc_domain_iomem_permission(CTX->xch, domid,
- vga_iomem_start, 0x20, 1);
- if (ret < 0) {
- LOGED(ERROR, domid,
- "failed to give dom%d access to iomem range "
- "%"PRIx64"-%"PRIx64" for VGA passthru",
- domid, vga_iomem_start, (vga_iomem_start + 0x20 - 1));
- return ret;
- }
- break;
- }
-
- return 0;
-}
-
-static int libxl_device_pci_compare(const libxl_device_pci *d1,
- const libxl_device_pci *d2)
-{
- return COMPARE_PCI(d1, d2);
-}
-
-#define libxl__device_pci_update_devid NULL
-
-DEFINE_DEVICE_TYPE_STRUCT_X(pcidev, pci, PCI,
- .get_num = libxl__device_pci_get_num,
- .from_xenstore = libxl__device_pci_from_xs_be,
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2014 Intel Corporation
- * Author Dongxiao Xu <dongxiao.xu@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-#include "libxl_internal.h"
-
-#include <xen-tools/libs.h>
-
-#define IA32_QM_CTR_ERROR_MASK (0x3ul << 62)
-
-static void libxl__psr_log_err_msg(libxl__gc *gc, int err)
-{
- char *msg;
-
- switch (err) {
- case ENOSYS:
- case EOPNOTSUPP:
- msg = "unsupported operation";
- break;
- case ESRCH:
- msg = "invalid domain ID";
- break;
- case ENOTSOCK:
- msg = "socket is not supported";
- break;
- case EFAULT:
- msg = "failed to exchange data with Xen";
- break;
- default:
- msg = "unknown error";
- break;
- }
-
- LOGE(ERROR, "%s", msg);
-}
-
-static void libxl__psr_cmt_log_err_msg(libxl__gc *gc, int err)
-{
- char *msg;
-
- switch (err) {
- case ENODEV:
- msg = "CMT is not supported in this system";
- break;
- case EEXIST:
- msg = "CMT is already attached to this domain";
- break;
- case ENOENT:
- msg = "CMT is not attached to this domain";
- break;
- case EOVERFLOW:
- msg = "no free RMID available";
- break;
- default:
- libxl__psr_log_err_msg(gc, err);
- return;
- }
-
- LOGE(ERROR, "%s", msg);
-}
-
-static void libxl__psr_alloc_log_err_msg(libxl__gc *gc,
- int err,
- libxl_psr_type type)
-{
- /*
- * Index is 'libxl_psr_type' so we set two 'CDP' to correspond to
- * DATA and CODE.
- */
- const char * const feat_name[] = {
- [LIBXL_PSR_CBM_TYPE_UNKNOWN] = "UNKNOWN",
- [LIBXL_PSR_CBM_TYPE_L3_CBM] = "L3 CAT",
- [LIBXL_PSR_CBM_TYPE_L3_CBM_CODE...LIBXL_PSR_CBM_TYPE_L3_CBM_DATA] =
- "CDP",
- [LIBXL_PSR_CBM_TYPE_L2_CBM] = "L2 CAT",
- [LIBXL_PSR_CBM_TYPE_MBA_THRTL] = "MBA",
- };
- char *msg;
-
- switch (err) {
- case ENODEV:
- msg = "is not supported in this system";
- break;
- case ENOENT:
- msg = "is not enabled on the socket";
- break;
- case EOVERFLOW:
- msg = "no free COS available";
- break;
- case EEXIST:
- msg = "The same CBM is already set to this domain";
- break;
- case ENXIO:
- msg = "Unable to set code or data CBM when CDP is disabled";
- break;
- case EINVAL:
- msg = "Invalid input or some internal values are not expected";
- break;
- case ERANGE:
- msg = "Socket number is wrong";
- break;
- case ENOSPC:
- msg = "Value array exceeds the range";
- break;
-
- default:
- libxl__psr_log_err_msg(gc, err);
- return;
- }
-
- LOG(ERROR, "%s: %s", feat_name[type], msg);
-}
-
-static int libxl__pick_socket_cpu(libxl__gc *gc, uint32_t socketid)
-{
- int i, nr_cpus;
- libxl_cputopology *topology;
- int cpu = ERROR_FAIL;
-
- topology = libxl_get_cpu_topology(CTX, &nr_cpus);
- if (!topology)
- return ERROR_FAIL;
-
- for (i = 0; i < nr_cpus; i++)
- if (topology[i].socket == socketid) {
- cpu = i;
- break;
- }
-
- libxl_cputopology_list_free(topology, nr_cpus);
- return cpu;
-}
-
-int libxl_psr_cmt_attach(libxl_ctx *ctx, uint32_t domid)
-{
- GC_INIT(ctx);
- int rc;
-
- rc = xc_psr_cmt_attach(ctx->xch, domid);
- if (rc < 0) {
- libxl__psr_cmt_log_err_msg(gc, errno);
- rc = ERROR_FAIL;
- }
-
- GC_FREE;
- return rc;
-}
-
-int libxl_psr_cmt_detach(libxl_ctx *ctx, uint32_t domid)
-{
- GC_INIT(ctx);
- int rc;
-
- rc = xc_psr_cmt_detach(ctx->xch, domid);
- if (rc < 0) {
- libxl__psr_cmt_log_err_msg(gc, errno);
- rc = ERROR_FAIL;
- }
-
- GC_FREE;
- return rc;
-}
-
-int libxl_psr_cmt_domain_attached(libxl_ctx *ctx, uint32_t domid)
-{
- int rc;
- uint32_t rmid;
-
- rc = xc_psr_cmt_get_domain_rmid(ctx->xch, domid, &rmid);
- if (rc < 0)
- return 0;
-
- return !!rmid;
-}
-
-int libxl_psr_cmt_enabled(libxl_ctx *ctx)
-{
- return xc_psr_cmt_enabled(ctx->xch);
-}
-
-int libxl_psr_cmt_get_total_rmid(libxl_ctx *ctx, uint32_t *total_rmid)
-{
- GC_INIT(ctx);
- int rc;
-
- rc = xc_psr_cmt_get_total_rmid(ctx->xch, total_rmid);
- if (rc < 0) {
- libxl__psr_cmt_log_err_msg(gc, errno);
- rc = ERROR_FAIL;
- }
-
- GC_FREE;
- return rc;
-}
-
-int libxl_psr_cmt_get_l3_cache_size(libxl_ctx *ctx,
- uint32_t socketid,
- uint32_t *l3_cache_size)
-{
- GC_INIT(ctx);
-
- int rc;
- int cpu = libxl__pick_socket_cpu(gc, socketid);
-
- if (cpu < 0) {
- LOGE(ERROR, "failed to get socket cpu");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = xc_psr_cmt_get_l3_cache_size(ctx->xch, cpu, l3_cache_size);
- if (rc < 0) {
- libxl__psr_cmt_log_err_msg(gc, errno);
- rc = ERROR_FAIL;
- }
-
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl_psr_cmt_type_supported(libxl_ctx *ctx, libxl_psr_cmt_type type)
-{
- GC_INIT(ctx);
- uint32_t event_mask;
- int rc;
-
- rc = xc_psr_cmt_get_l3_event_mask(ctx->xch, &event_mask);
- if (rc < 0) {
- libxl__psr_cmt_log_err_msg(gc, errno);
- rc = 0;
- } else {
- rc = event_mask & (1 << (type - 1));
- }
-
- GC_FREE;
- return rc;
-}
-
-int libxl_psr_cmt_get_sample(libxl_ctx *ctx,
- uint32_t domid,
- libxl_psr_cmt_type type,
- uint64_t scope,
- uint64_t *sample_r,
- uint64_t *tsc_r)
-{
- GC_INIT(ctx);
- unsigned int rmid;
- uint32_t upscaling_factor;
- uint64_t monitor_data;
- int cpu, rc;
-
- rc = xc_psr_cmt_get_domain_rmid(ctx->xch, domid, &rmid);
- if (rc < 0 || rmid == 0) {
- LOGED(ERROR, domid, "fail to get the domain rmid, "
- "or domain is not attached with platform QoS monitoring service");
- rc = ERROR_FAIL;
- goto out;
- }
-
- cpu = libxl__pick_socket_cpu(gc, scope);
- if (cpu < 0) {
- LOGED(ERROR, domid, "failed to get socket cpu");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = xc_psr_cmt_get_data(ctx->xch, rmid, cpu, type - 1,
- &monitor_data, tsc_r);
- if (rc < 0) {
- LOGED(ERROR, domid, "failed to get monitoring data");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = xc_psr_cmt_get_l3_upscaling_factor(ctx->xch, &upscaling_factor);
- if (rc < 0) {
- LOGED(ERROR, domid, "failed to get L3 upscaling factor");
- rc = ERROR_FAIL;
- goto out;
- }
-
- *sample_r = monitor_data * upscaling_factor;
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl_psr_cmt_get_cache_occupancy(libxl_ctx *ctx,
- uint32_t domid,
- uint32_t socketid,
- uint32_t *l3_cache_occupancy)
-{
- uint64_t data;
- int rc;
-
- rc = libxl_psr_cmt_get_sample(ctx, domid,
- LIBXL_PSR_CMT_TYPE_CACHE_OCCUPANCY,
- socketid, &data, NULL);
- if (rc < 0)
- goto out;
-
- *l3_cache_occupancy = data / 1024;
-out:
- return rc;
-}
-
-static inline xc_psr_type libxl__psr_type_to_libxc_psr_type(
- libxl_psr_type type)
-{
- BUILD_BUG_ON(sizeof(libxl_psr_type) != sizeof(xc_psr_type));
- return (xc_psr_type)type;
-}
-
-int libxl_psr_cat_set_cbm(libxl_ctx *ctx, uint32_t domid,
- libxl_psr_cbm_type type, libxl_bitmap *target_map,
- uint64_t cbm)
-{
- return libxl_psr_set_val(ctx, domid, type, target_map, cbm);
-}
-
-int libxl_psr_cat_get_cbm(libxl_ctx *ctx, uint32_t domid,
- libxl_psr_cbm_type type, uint32_t target,
- uint64_t *cbm_r)
-{
- return libxl_psr_get_val(ctx, domid, type, target, cbm_r);
-}
-
-static xc_psr_feat_type libxl__feat_type_to_libxc_feat_type(
- libxl_psr_feat_type type, unsigned int lvl)
-{
- xc_psr_feat_type xc_type;
-
- switch (type) {
- case LIBXL_PSR_FEAT_TYPE_CAT:
- assert(lvl == 3 || lvl == 2);
-
- if (lvl == 3)
- xc_type = XC_PSR_CAT_L3;
- if (lvl == 2)
- xc_type = XC_PSR_CAT_L2;
- break;
- case LIBXL_PSR_FEAT_TYPE_MBA:
- xc_type = XC_PSR_MBA;
- break;
- default:
- /* Could not happen */
- assert(0);
- }
-
- return xc_type;
-}
-
-static void libxl__hw_info_to_libxl_cat_info(
- libxl_psr_feat_type type, libxl_psr_hw_info *hw_info,
- libxl_psr_cat_info *cat_info)
-{
- assert(type == LIBXL_PSR_FEAT_TYPE_CAT);
-
- cat_info->id = hw_info->id;
- cat_info->cos_max = hw_info->u.cat.cos_max;
- cat_info->cbm_len = hw_info->u.cat.cbm_len;
- cat_info->cdp_enabled = hw_info->u.cat.cdp_enabled;
-}
-
-int libxl_psr_cat_get_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
- unsigned int *nr, unsigned int lvl)
-{
- GC_INIT(ctx);
- int rc;
- unsigned int i;
- libxl_psr_hw_info *hw_info;
- libxl_psr_cat_info *ptr;
-
- rc = libxl_psr_get_hw_info(ctx, LIBXL_PSR_FEAT_TYPE_CAT, lvl, nr, &hw_info);
- if (rc)
- goto out;
-
- ptr = libxl__malloc(NOGC, *nr * sizeof(libxl_psr_cat_info));
-
- for (i = 0; i < *nr; i++)
- libxl__hw_info_to_libxl_cat_info(LIBXL_PSR_FEAT_TYPE_CAT,
- &hw_info[i],
- &ptr[i]);
-
- *info = ptr;
- libxl_psr_hw_info_list_free(hw_info, *nr);
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl_psr_cat_get_l3_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
- int *nr)
-{
- int rc;
- unsigned int num;
-
- rc = libxl_psr_cat_get_info(ctx, info, &num, 3);
- if (!rc)
- *nr = num;
-
- return rc;
-}
-
-void libxl_psr_cat_info_list_free(libxl_psr_cat_info *list, int nr)
-{
- int i;
-
- for (i = 0; i < nr; i++)
- libxl_psr_cat_info_dispose(&list[i]);
- free(list);
-}
-
-int libxl_psr_set_val(libxl_ctx *ctx, uint32_t domid,
- libxl_psr_type type, libxl_bitmap *target_map,
- uint64_t val)
-{
- GC_INIT(ctx);
- int rc, socketid, nr_sockets;
- xc_psr_type xc_type = libxl__psr_type_to_libxc_psr_type(type);
-
- rc = libxl__count_physical_sockets(gc, &nr_sockets);
- if (rc) {
- LOG(ERROR, "failed to get system socket count");
- goto out;
- }
-
- libxl_for_each_set_bit(socketid, *target_map) {
- if (socketid >= nr_sockets)
- break;
-
- if (xc_psr_set_domain_data(ctx->xch, domid, xc_type,
- socketid, val)) {
- libxl__psr_alloc_log_err_msg(gc, errno, type);
- rc = ERROR_FAIL;
- }
- }
-
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl_psr_get_val(libxl_ctx *ctx, uint32_t domid,
- libxl_psr_type type, unsigned int target,
- uint64_t *val)
-{
- GC_INIT(ctx);
- int rc = 0;
- xc_psr_type xc_type = libxl__psr_type_to_libxc_psr_type(type);
-
- if (xc_psr_get_domain_data(ctx->xch, domid, xc_type,
- target, val)) {
- libxl__psr_alloc_log_err_msg(gc, errno, type);
- rc = ERROR_FAIL;
- }
-
- GC_FREE;
- return rc;
-}
-
-static void libxl__xc_hw_info_to_libxl_hw_info(
- libxl_psr_feat_type type, xc_psr_hw_info *xc_info,
- libxl_psr_hw_info *xl_info)
-{
- switch (type) {
- case LIBXL_PSR_FEAT_TYPE_CAT:
- xl_info->u.cat.cos_max = xc_info->cat.cos_max;
- xl_info->u.cat.cbm_len = xc_info->cat.cbm_len;
- xl_info->u.cat.cdp_enabled = xc_info->cat.cdp_enabled;
- break;
- case LIBXL_PSR_FEAT_TYPE_MBA:
- xl_info->u.mba.cos_max = xc_info->mba.cos_max;
- xl_info->u.mba.thrtl_max = xc_info->mba.thrtl_max;
- xl_info->u.mba.linear = xc_info->mba.linear;
- break;
- default:
- assert(0);
- }
-}
-
-int libxl_psr_get_hw_info(libxl_ctx *ctx, libxl_psr_feat_type type,
- unsigned int lvl, unsigned int *nr,
- libxl_psr_hw_info **info)
-{
- GC_INIT(ctx);
- int rc, nr_sockets;
- unsigned int i = 0, socketid;
- libxl_bitmap socketmap;
- libxl_psr_hw_info *ptr;
- xc_psr_feat_type xc_type;
- xc_psr_hw_info hw_info;
-
- libxl_bitmap_init(&socketmap);
-
- xc_type = libxl__feat_type_to_libxc_feat_type(type, lvl);
-
- rc = libxl__count_physical_sockets(gc, &nr_sockets);
- if (rc) {
- LOG(ERROR, "failed to get system socket count");
- goto out;
- }
-
- libxl_socket_bitmap_alloc(ctx, &socketmap, nr_sockets);
- rc = libxl_get_online_socketmap(ctx, &socketmap);
- if (rc) {
- LOGE(ERROR, "failed to get available sockets");
- goto out;
- }
-
- ptr = libxl__malloc(NOGC, nr_sockets * sizeof(libxl_psr_hw_info));
-
- libxl_for_each_set_bit(socketid, socketmap) {
- ptr[i].id = socketid;
- if (xc_psr_get_hw_info(ctx->xch, socketid, xc_type, &hw_info)) {
- rc = ERROR_FAIL;
- free(ptr);
- goto out;
- }
-
- libxl__xc_hw_info_to_libxl_hw_info(type, &hw_info, &ptr[i]);
-
- i++;
- }
-
- *info = ptr;
- *nr = i;
-out:
- libxl_bitmap_dispose(&socketmap);
- GC_FREE;
- return rc;
-}
-
-void libxl_psr_hw_info_list_free(libxl_psr_hw_info *list, unsigned int nr)
-{
- unsigned int i;
-
- for (i = 0; i < nr; i++)
- libxl_psr_hw_info_dispose(&list[i]);
- free(list);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2018 Aporeto
- * Author Stefano Stabellini <stefano@aporeto.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-static int libxl__device_pvcallsif_setdefault(libxl__gc *gc, uint32_t domid,
- libxl_device_pvcallsif *pvcallsif,
- bool hotplug)
-{
- return libxl__resolve_domid(gc, pvcallsif->backend_domname,
- &pvcallsif->backend_domid);
-}
-
-static LIBXL_DEFINE_UPDATE_DEVID(pvcallsif)
-static LIBXL_DEFINE_DEVICE_FROM_TYPE(pvcallsif)
-
-#define libxl__add_pvcallsifs NULL
-#define libxl_device_pvcallsif_list NULL
-#define libxl_device_pvcallsif_compare NULL
-
-LIBXL_DEFINE_DEVICE_REMOVE(pvcallsif)
-
-DEFINE_DEVICE_TYPE_STRUCT(pvcallsif, PVCALLS);
+++ /dev/null
-/*
- * Copyright (C) 2011 Citrix Ltd.
- * Author Anthony PERARD <anthony.perard@citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-/*
- * This file implement a client for QMP (QEMU Monitor Protocol). For the
- * Specification, see in the QEMU repository.
- *
- * WARNING - Do not trust QEMU when writing codes for new commands or when
- * improving the client code.
- */
-
-/*
- * Logic used to send command to QEMU
- *
- * qmp_open():
- * Will open a socket and connect to QEMU.
- *
- * qmp_next():
- * Will read data sent by QEMU and then call qmp_handle_response() once a
- * complete QMP message is received.
- * The function return on timeout/error or once every data received as been
- * processed.
- *
- * qmp_handle_response()
- * This process json messages received from QEMU and update different list and
- * may call callback function.
- * `libxl__qmp_handler.wait_for_id` is reset once a message with this ID is
- * processed.
- * `libxl__qmp_handler.callback_list`: list with ID of command sent and
- * optional assotiated callback function. The return value of a callback is
- * set in context.
- *
- * qmp_send():
- * Simply prepare a QMP command and send it to QEMU.
- * It also add a `struct callback_id_pair` on the
- * `libxl__qmp_handler.callback_list` via qmp_send_prepare().
- *
- * qmp_synchronous_send():
- * This function calls qmp_send(), then wait for QEMU to reply to the command.
- * The wait is done by calling qmp_next() over and over again until either
- * there is a response for the command or there is an error.
- *
- * An ID can be set for each QMP command, this is set into
- * `libxl__qmp_handler.wait_for_id`. qmp_next will check every response's ID
- * again this field and change the value of the field once the ID is found.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <sys/un.h>
-
-#include <yajl/yajl_gen.h>
-
-#include "_libxl_list.h"
-#include "libxl_internal.h"
-
-/* #define DEBUG_RECEIVED */
-
-#ifdef DEBUG_RECEIVED
-# define DEBUG_REPORT_RECEIVED(dom, buf, len) \
- LOGD(DEBUG, dom, "received: '%.*s'", len, buf)
-#else
-# define DEBUG_REPORT_RECEIVED(dom, buf, len) ((void)0)
-#endif
-
-#ifdef DEBUG_QMP_CLIENT
-# define LOG_QMP(f, ...) LOGD(DEBUG, ev->domid, f, ##__VA_ARGS__)
-#else
-# define LOG_QMP(f, ...)
-#endif
-
-/*
- * QMP types & constant
- */
-
-#define QMP_RECEIVE_BUFFER_SIZE 4096
-#define QMP_MAX_SIZE_RX_BUF MB(1)
-
-/*
- * qmp_callback_t is call whenever a message from QMP contain the "id"
- * associated with the callback.
- * "tree" contain the JSON tree that is in "return" of a QMP message. If QMP
- * sent an error message, "tree" will be NULL.
- */
-typedef int (*qmp_callback_t)(libxl__qmp_handler *qmp,
- const libxl__json_object *tree,
- void *opaque);
-
-typedef struct qmp_request_context {
- int rc;
-} qmp_request_context;
-
-typedef struct callback_id_pair {
- int id;
- qmp_callback_t callback;
- void *opaque;
- qmp_request_context *context;
- LIBXL_STAILQ_ENTRY(struct callback_id_pair) next;
-} callback_id_pair;
-
-struct libxl__qmp_handler {
- int qmp_fd;
- bool connected;
- time_t timeout;
- /* wait_for_id will be used by the synchronous send function */
- int wait_for_id;
-
- char buffer[QMP_RECEIVE_BUFFER_SIZE + 1];
-
- libxl_ctx *ctx;
- uint32_t domid;
-
- int last_id_used;
- LIBXL_STAILQ_HEAD(callback_list, callback_id_pair) callback_list;
- struct {
- int major;
- int minor;
- int micro;
- } version;
-};
-
-static int qmp_send(libxl__qmp_handler *qmp,
- const char *cmd, libxl__json_object *args,
- qmp_callback_t callback, void *opaque,
- qmp_request_context *context);
-
-static const int QMP_SOCKET_CONNECT_TIMEOUT = 5;
-
-/*
- * QMP callbacks functions
- */
-
-static int qmp_capabilities_callback(libxl__qmp_handler *qmp,
- const libxl__json_object *o, void *unused)
-{
- qmp->connected = true;
-
- return 0;
-}
-
-/*
- * QMP commands
- */
-
-static int enable_qmp_capabilities(libxl__qmp_handler *qmp)
-{
- return qmp_send(qmp, "qmp_capabilities", NULL,
- qmp_capabilities_callback, NULL, NULL);
-}
-
-/*
- * Helpers
- */
-
-static libxl__qmp_message_type qmp_response_type(const libxl__json_object *o)
-{
- libxl__qmp_message_type type;
- libxl__json_map_node *node = NULL;
- int i = 0;
-
- for (i = 0; (node = libxl__json_map_node_get(o, i)); i++) {
- if (libxl__qmp_message_type_from_string(node->map_key, &type) == 0)
- return type;
- }
-
- return LIBXL__QMP_MESSAGE_TYPE_INVALID;
-}
-
-static callback_id_pair *qmp_get_callback_from_id(libxl__qmp_handler *qmp,
- const libxl__json_object *o)
-{
- const libxl__json_object *id_object = libxl__json_map_get("id", o,
- JSON_INTEGER);
- int id = -1;
- callback_id_pair *pp = NULL;
-
- if (id_object) {
- id = libxl__json_object_get_integer(id_object);
-
- LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) {
- if (pp->id == id) {
- return pp;
- }
- }
- }
- return NULL;
-}
-
-static void qmp_handle_error_response(libxl__gc *gc, libxl__qmp_handler *qmp,
- const libxl__json_object *resp)
-{
- callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
-
- resp = libxl__json_map_get("error", resp, JSON_MAP);
- resp = libxl__json_map_get("desc", resp, JSON_STRING);
-
- if (pp) {
- if (pp->callback) {
- int rc = pp->callback(qmp, NULL, pp->opaque);
- if (pp->context) {
- pp->context->rc = rc;
- }
- }
- if (pp->id == qmp->wait_for_id) {
- /* tell that the id have been processed */
- qmp->wait_for_id = 0;
- }
- LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair, next);
- free(pp);
- }
-
- LOGD(ERROR, qmp->domid, "received an error message from QMP server: %s",
- libxl__json_object_get_string(resp));
-}
-
-static int qmp_handle_response(libxl__gc *gc, libxl__qmp_handler *qmp,
- const libxl__json_object *resp)
-{
- libxl__qmp_message_type type = LIBXL__QMP_MESSAGE_TYPE_INVALID;
-
- type = qmp_response_type(resp);
- LOGD(DEBUG, qmp->domid, "message type: %s", libxl__qmp_message_type_to_string(type));
-
- switch (type) {
- case LIBXL__QMP_MESSAGE_TYPE_QMP: {
- const libxl__json_object *o;
- o = libxl__json_map_get("QMP", resp, JSON_MAP);
- o = libxl__json_map_get("version", o, JSON_MAP);
- o = libxl__json_map_get("qemu", o, JSON_MAP);
- qmp->version.major = libxl__json_object_get_integer(
- libxl__json_map_get("major", o, JSON_INTEGER));
- qmp->version.minor = libxl__json_object_get_integer(
- libxl__json_map_get("minor", o, JSON_INTEGER));
- qmp->version.micro = libxl__json_object_get_integer(
- libxl__json_map_get("micro", o, JSON_INTEGER));
- LOGD(DEBUG, qmp->domid, "QEMU version: %d.%d.%d",
- qmp->version.major, qmp->version.minor, qmp->version.micro);
- /* On the greeting message from the server, enable QMP capabilities */
- return enable_qmp_capabilities(qmp);
- }
- case LIBXL__QMP_MESSAGE_TYPE_RETURN: {
- callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
-
- if (pp) {
- if (pp->callback) {
- int rc = pp->callback(qmp,
- libxl__json_map_get("return", resp, JSON_ANY),
- pp->opaque);
- if (pp->context) {
- pp->context->rc = rc;
- }
- }
- if (pp->id == qmp->wait_for_id) {
- /* tell that the id have been processed */
- qmp->wait_for_id = 0;
- }
- LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair,
- next);
- free(pp);
- }
- return 0;
- }
- case LIBXL__QMP_MESSAGE_TYPE_ERROR:
- qmp_handle_error_response(gc, qmp, resp);
- return -1;
- case LIBXL__QMP_MESSAGE_TYPE_EVENT:
- return 0;
- case LIBXL__QMP_MESSAGE_TYPE_INVALID:
- return -1;
- }
- return 0;
-}
-
-/*
- * return values:
- * < 0 if qemu's version < asked version
- * = 0 if qemu's version == asked version
- * > 0 if qemu's version > asked version
- */
-static int qmp_ev_qemu_compare_version(libxl__ev_qmp *ev, int major,
- int minor, int micro)
-{
-#define CHECK_VERSION(level) do { \
- if (ev->qemu_version.level > (level)) return +1; \
- if (ev->qemu_version.level < (level)) return -1; \
-} while (0)
-
- CHECK_VERSION(major);
- CHECK_VERSION(minor);
- CHECK_VERSION(micro);
-
-#undef CHECK_VERSION
-
- return 0;
-}
-
-/*
- * Handler functions
- */
-
-static libxl__qmp_handler *qmp_init_handler(libxl__gc *gc, uint32_t domid)
-{
- libxl__qmp_handler *qmp = NULL;
-
- qmp = calloc(1, sizeof (libxl__qmp_handler));
- if (qmp == NULL) {
- LOGED(ERROR, domid, "Failed to allocate qmp_handler");
- return NULL;
- }
- qmp->ctx = CTX;
- qmp->domid = domid;
- qmp->timeout = 5;
-
- LIBXL_STAILQ_INIT(&qmp->callback_list);
-
- return qmp;
-}
-
-static int qmp_open(libxl__qmp_handler *qmp, const char *qmp_socket_path,
- int timeout)
-{
- GC_INIT(qmp->ctx);
- int ret = -1;
- int i = 0;
- struct sockaddr_un addr;
-
- qmp->qmp_fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (qmp->qmp_fd < 0) {
- goto out;
- }
- ret = libxl_fd_set_nonblock(qmp->ctx, qmp->qmp_fd, 1);
- if (ret) {
- ret = -1;
- goto out;
- }
- ret = libxl_fd_set_cloexec(qmp->ctx, qmp->qmp_fd, 1);
- if (ret) {
- ret = -1;
- goto out;
- }
-
- ret = libxl__prepare_sockaddr_un(gc, &addr, qmp_socket_path, "QMP socket");
- if (ret)
- goto out;
-
- do {
- ret = connect(qmp->qmp_fd, (struct sockaddr *) &addr, sizeof(addr));
- if (ret == 0)
- break;
- if (errno == ENOENT || errno == ECONNREFUSED) {
- /* ENOENT : Socket may not have shown up yet
- * ECONNREFUSED : Leftover socket hasn't been removed yet */
- continue;
- }
- ret = -1;
- goto out;
- } while ((++i / 5 <= timeout) && (usleep(200 * 1000) <= 0));
-
-out:
- if (ret == -1 && qmp->qmp_fd > -1) close(qmp->qmp_fd);
-
- GC_FREE;
- return ret;
-}
-
-static void qmp_close(libxl__qmp_handler *qmp)
-{
- callback_id_pair *pp = NULL;
- callback_id_pair *tmp = NULL;
-
- close(qmp->qmp_fd);
- LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) {
- free(tmp);
- tmp = pp;
- }
- free(tmp);
-}
-
-static int qmp_next(libxl__gc *gc, libxl__qmp_handler *qmp)
-{
- ssize_t rd;
- char *s = NULL;
- char *s_end = NULL;
-
- char *incomplete = NULL;
- size_t incomplete_size = 0;
- int rc = 0;
-
- do {
- fd_set rfds;
- int ret = 0;
- struct timeval timeout = {
- .tv_sec = qmp->timeout,
- .tv_usec = 0,
- };
-
- FD_ZERO(&rfds);
- FD_SET(qmp->qmp_fd, &rfds);
-
- ret = select(qmp->qmp_fd + 1, &rfds, NULL, NULL, &timeout);
- if (ret == 0) {
- LOGD(ERROR, qmp->domid, "timeout");
- return -1;
- } else if (ret < 0) {
- if (errno == EINTR)
- continue;
- LOGED(ERROR, qmp->domid, "Select error");
- return -1;
- }
-
- rd = read(qmp->qmp_fd, qmp->buffer, QMP_RECEIVE_BUFFER_SIZE);
- if (rd == 0) {
- LOGD(ERROR, qmp->domid, "Unexpected end of socket");
- return -1;
- } else if (rd < 0) {
- LOGED(ERROR, qmp->domid, "Socket read error");
- return rd;
- }
- qmp->buffer[rd] = '\0';
-
- DEBUG_REPORT_RECEIVED(qmp->domid, qmp->buffer, (int)rd);
-
- if (incomplete) {
- size_t current_pos = s - incomplete;
- incomplete = libxl__realloc(gc, incomplete,
- incomplete_size + rd + 1);
- strncat(incomplete + incomplete_size, qmp->buffer, rd);
- s = incomplete + current_pos;
- incomplete_size += rd;
- s_end = incomplete + incomplete_size;
- } else {
- incomplete = libxl__strndup(gc, qmp->buffer, rd);
- incomplete_size = rd;
- s = incomplete;
- s_end = s + rd;
- rd = 0;
- }
-
- do {
- char *end = NULL;
-
- end = strstr(s, "\r\n");
- if (end) {
- libxl__json_object *o = NULL;
-
- *end = '\0';
-
- o = libxl__json_parse(gc, s);
-
- if (o) {
- rc = qmp_handle_response(gc, qmp, o);
- } else {
- LOGD(ERROR, qmp->domid, "Parse error of : %s", s);
- return -1;
- }
-
- s = end + 2;
- } else {
- break;
- }
- } while (s < s_end);
- } while (s < s_end);
-
- return rc;
-}
-
-static char *qmp_prepare_cmd(libxl__gc *gc, const char *cmd,
- const libxl__json_object *args,
- int id)
-{
- yajl_gen hand = NULL;
- /* memory for 'buf' is owned by 'hand' */
- const unsigned char *buf;
- libxl_yajl_length len;
- yajl_gen_status s;
- char *ret = NULL;
-
- hand = libxl_yajl_gen_alloc(NULL);
-
- if (!hand) {
- return NULL;
- }
-
-#if HAVE_YAJL_V2
- /* Disable beautify for data sent to QEMU */
- yajl_gen_config(hand, yajl_gen_beautify, 0);
-#endif
-
- yajl_gen_map_open(hand);
- libxl__yajl_gen_asciiz(hand, "execute");
- libxl__yajl_gen_asciiz(hand, cmd);
- libxl__yajl_gen_asciiz(hand, "id");
- yajl_gen_integer(hand, id);
- if (args) {
- libxl__yajl_gen_asciiz(hand, "arguments");
- libxl__json_object_to_yajl_gen(gc, hand, args);
- }
- yajl_gen_map_close(hand);
-
- s = yajl_gen_get_buf(hand, &buf, &len);
-
- if (s != yajl_gen_status_ok)
- goto out;
-
- ret = libxl__sprintf(gc, "%*.*s\r\n", (int)len, (int)len, buf);
-
-out:
- yajl_gen_free(hand);
- return ret;
-}
-
-static char *qmp_send_prepare(libxl__gc *gc, libxl__qmp_handler *qmp,
- const char *cmd, libxl__json_object *args,
- qmp_callback_t callback, void *opaque,
- qmp_request_context *context)
-{
- char *buf;
- callback_id_pair *elm;
-
- buf = qmp_prepare_cmd(gc, cmd, args, ++qmp->last_id_used);
-
- if (!buf) {
- LOGD(ERROR, qmp->domid, "Failed to generate a qmp command");
- goto out;
- }
-
- elm = malloc(sizeof (callback_id_pair));
- if (elm == NULL) {
- LOGED(ERROR, qmp->domid, "Failed to allocate a QMP callback");
- goto out;
- }
- elm->id = qmp->last_id_used;
- elm->callback = callback;
- elm->opaque = opaque;
- elm->context = context;
- LIBXL_STAILQ_INSERT_TAIL(&qmp->callback_list, elm, next);
-
- LOGD(DEBUG, qmp->domid, "next qmp command: '%s'", buf);
-
-out:
- return buf;
-}
-
-static int qmp_send(libxl__qmp_handler *qmp,
- const char *cmd, libxl__json_object *args,
- qmp_callback_t callback, void *opaque,
- qmp_request_context *context)
-{
- char *buf = NULL;
- int rc = -1;
- GC_INIT(qmp->ctx);
-
- buf = qmp_send_prepare(gc, qmp, cmd, args, callback, opaque, context);
-
- if (buf == NULL) {
- goto out;
- }
-
- if (libxl_write_exactly(qmp->ctx, qmp->qmp_fd, buf, strlen(buf),
- "QMP command", "QMP socket"))
- goto out;
-
- rc = qmp->last_id_used;
-out:
- GC_FREE;
- return rc;
-}
-
-static int qmp_synchronous_send(libxl__qmp_handler *qmp, const char *cmd,
- libxl__json_object *args,
- qmp_callback_t callback, void *opaque,
- int ask_timeout)
-{
- int id = 0;
- int ret = 0;
- GC_INIT(qmp->ctx);
- qmp_request_context context = { .rc = 0 };
-
- id = qmp_send(qmp, cmd, args, callback, opaque, &context);
- if (id <= 0) {
- return ERROR_FAIL;
- }
- qmp->wait_for_id = id;
-
- while (qmp->wait_for_id == id) {
- if ((ret = qmp_next(gc, qmp)) < 0) {
- break;
- }
- }
-
- if (qmp->wait_for_id != id && ret == 0) {
- ret = context.rc;
- }
-
- GC_FREE;
-
- return ret;
-}
-
-static void qmp_free_handler(libxl__qmp_handler *qmp)
-{
- free(qmp);
-}
-
-/*
- * QMP Parameters Helpers
- */
-static void qmp_parameters_common_add(libxl__gc *gc,
- libxl__json_object **param,
- const char *name,
- libxl__json_object *obj)
-{
- libxl__json_map_node *arg = NULL;
-
- if (!*param) {
- *param = libxl__json_object_alloc(gc, JSON_MAP);
- }
-
- GCNEW(arg);
-
- arg->map_key = libxl__strdup(gc, name);
- arg->obj = obj;
-
- flexarray_append((*param)->u.map, arg);
-}
-
-void libxl__qmp_param_add_string(libxl__gc *gc,
- libxl__json_object **param,
- const char *name, const char *argument)
-{
- libxl__json_object *obj;
-
- obj = libxl__json_object_alloc(gc, JSON_STRING);
- obj->u.string = libxl__strdup(gc, argument);
-
- qmp_parameters_common_add(gc, param, name, obj);
-}
-
-void libxl__qmp_param_add_bool(libxl__gc *gc,
- libxl__json_object **param,
- const char *name, bool b)
-{
- libxl__json_object *obj;
-
- obj = libxl__json_object_alloc(gc, JSON_BOOL);
- obj->u.b = b;
- qmp_parameters_common_add(gc, param, name, obj);
-}
-
-void libxl__qmp_param_add_integer(libxl__gc *gc,
- libxl__json_object **param,
- const char *name, const int i)
-{
- libxl__json_object *obj;
-
- obj = libxl__json_object_alloc(gc, JSON_INTEGER);
- obj->u.i = i;
-
- qmp_parameters_common_add(gc, param, name, obj);
-}
-
-/*
- * API
- */
-
-libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc, uint32_t domid)
-{
- int ret = 0;
- libxl__qmp_handler *qmp = NULL;
- char *qmp_socket;
-
- qmp = qmp_init_handler(gc, domid);
- if (!qmp) return NULL;
-
- qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
- if ((ret = qmp_open(qmp, qmp_socket, QMP_SOCKET_CONNECT_TIMEOUT)) < 0) {
- LOGED(ERROR, domid, "Connection error");
- qmp_free_handler(qmp);
- return NULL;
- }
-
- LOGD(DEBUG, domid, "connected to %s", qmp_socket);
-
- /* Wait for the response to qmp_capabilities */
- while (!qmp->connected) {
- if ((ret = qmp_next(gc, qmp)) < 0) {
- break;
- }
- }
-
- if (!qmp->connected) {
- LOGD(ERROR, domid, "Failed to connect to QMP");
- libxl__qmp_close(qmp);
- return NULL;
- }
- return qmp;
-}
-
-void libxl__qmp_close(libxl__qmp_handler *qmp)
-{
- if (!qmp)
- return;
- qmp_close(qmp);
- qmp_free_handler(qmp);
-}
-
-void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid)
-{
- char *qmp_socket;
-
- qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
- if (unlink(qmp_socket) == -1) {
- if (errno != ENOENT) {
- LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket);
- }
- }
-
- qmp_socket = GCSPRINTF("%s/qmp-libxenstat-%d", libxl__run_dir_path(), domid);
- if (unlink(qmp_socket) == -1) {
- if (errno != ENOENT) {
- LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket);
- }
- }
-}
-
-static int qmp_run_command(libxl__gc *gc, int domid,
- const char *cmd, libxl__json_object *args,
- qmp_callback_t callback, void *opaque)
-{
- libxl__qmp_handler *qmp = NULL;
- int rc = 0;
-
- qmp = libxl__qmp_initialize(gc, domid);
- if (!qmp)
- return ERROR_FAIL;
-
- rc = qmp_synchronous_send(qmp, cmd, args, callback, opaque, qmp->timeout);
-
- libxl__qmp_close(qmp);
- return rc;
-}
-
-int libxl__qmp_restore(libxl__gc *gc, int domid, const char *state_file)
-{
- libxl__json_object *args = NULL;
-
- libxl__qmp_param_add_string(gc, &args, "filename", state_file);
-
- return qmp_run_command(gc, domid, "xen-load-devices-state", args,
- NULL, NULL);
-}
-
-int libxl__qmp_resume(libxl__gc *gc, int domid)
-{
- return qmp_run_command(gc, domid, "cont", NULL, NULL, NULL);
-}
-
-int libxl__qmp_nbd_server_start(libxl__gc *gc, int domid,
- const char *host, const char *port)
-{
- libxl__json_object *args = NULL;
- libxl__json_object *addr = NULL;
- libxl__json_object *data = NULL;
-
- /* 'addr': {
- * 'type': 'inet',
- * 'data': {
- * 'host': '$nbd_host',
- * 'port': '$nbd_port'
- * }
- * }
- */
- libxl__qmp_param_add_string(gc, &data, "host", host);
- libxl__qmp_param_add_string(gc, &data, "port", port);
-
- libxl__qmp_param_add_string(gc, &addr, "type", "inet");
- qmp_parameters_common_add(gc, &addr, "data", data);
-
- qmp_parameters_common_add(gc, &args, "addr", addr);
-
- return qmp_run_command(gc, domid, "nbd-server-start", args, NULL, NULL);
-}
-
-int libxl__qmp_nbd_server_add(libxl__gc *gc, int domid, const char *disk)
-{
- libxl__json_object *args = NULL;
-
- libxl__qmp_param_add_string(gc, &args, "device", disk);
- libxl__qmp_param_add_bool(gc, &args, "writable", true);
-
- return qmp_run_command(gc, domid, "nbd-server-add", args, NULL, NULL);
-}
-
-int libxl__qmp_start_replication(libxl__gc *gc, int domid, bool primary)
-{
- libxl__json_object *args = NULL;
-
- libxl__qmp_param_add_bool(gc, &args, "enable", true);
- libxl__qmp_param_add_bool(gc, &args, "primary", primary);
-
- return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL);
-}
-
-int libxl__qmp_query_xen_replication_status(libxl__gc *gc, int domid)
-{
- return qmp_run_command(gc, domid, "query-xen-replication-status", NULL,
- NULL, NULL);
-}
-
-int libxl__qmp_colo_do_checkpoint(libxl__gc *gc, int domid)
-{
- return qmp_run_command(gc, domid, "xen-colo-do-checkpoint",
- NULL, NULL, NULL);
-}
-
-int libxl__qmp_stop_replication(libxl__gc *gc, int domid, bool primary)
-{
- libxl__json_object *args = NULL;
-
- libxl__qmp_param_add_bool(gc, &args, "enable", false);
- libxl__qmp_param_add_bool(gc, &args, "primary", primary);
-
- return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL);
-}
-
-int libxl__qmp_nbd_server_stop(libxl__gc *gc, int domid)
-{
- return qmp_run_command(gc, domid, "nbd-server-stop", NULL, NULL, NULL);
-}
-
-int libxl__qmp_x_blockdev_change(libxl__gc *gc, int domid, const char *parent,
- const char *child, const char *node)
-{
- libxl__json_object *args = NULL;
-
- libxl__qmp_param_add_string(gc, &args, "parent", parent);
- if (child)
- libxl__qmp_param_add_string(gc, &args, "child", child);
- if (node)
- libxl__qmp_param_add_string(gc, &args, "node", node);
-
- return qmp_run_command(gc, domid, "x-blockdev-change", args, NULL, NULL);
-}
-
-static int hmp_callback(libxl__qmp_handler *qmp,
- const libxl__json_object *response,
- void *opaque)
-{
- char **output = opaque;
- GC_INIT(qmp->ctx);
- int rc;
-
- rc = 0;
- if (!output)
- goto out;
-
- *output = NULL;
-
- if (libxl__json_object_is_string(response)) {
- *output = libxl__strdup(NOGC, libxl__json_object_get_string(response));
- goto out;
- }
-
- LOG(ERROR, "Response has unexpected format");
- rc = ERROR_FAIL;
-
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl__qmp_hmp(libxl__gc *gc, int domid, const char *command_line,
- char **output)
-{
- libxl__json_object *args = NULL;
-
- libxl__qmp_param_add_string(gc, &args, "command-line", command_line);
-
- return qmp_run_command(gc, domid, "human-monitor-command", args,
- hmp_callback, output);
-}
-
-
-typedef struct {
- libxl__ev_qmp qmp;
- char **output; /* user pointer */
-} qemu_monitor_command_state;
-
-static void qemu_monitor_command_done(libxl__egc *, libxl__ev_qmp *,
- const libxl__json_object *response,
- int rc);
-
-int libxl_qemu_monitor_command(libxl_ctx *ctx, uint32_t domid,
- const char *command_line, char **output,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- qemu_monitor_command_state *qmcs;
- libxl__json_object *args = NULL;
- int rc;
-
- if (!output) {
- rc = ERROR_INVAL;
- goto out;
- }
-
- GCNEW(qmcs);
- libxl__ev_qmp_init(&qmcs->qmp);
- qmcs->qmp.ao = ao;
- qmcs->qmp.domid = domid;
- qmcs->qmp.payload_fd = -1;
- qmcs->qmp.callback = qemu_monitor_command_done;
- qmcs->output = output;
- libxl__qmp_param_add_string(gc, &args, "command-line", command_line);
- rc = libxl__ev_qmp_send(egc, &qmcs->qmp, "human-monitor-command", args);
-out:
- if (rc) return AO_CREATE_FAIL(rc);
- return AO_INPROGRESS;
-}
-
-static void qemu_monitor_command_done(libxl__egc *egc, libxl__ev_qmp *qmp,
- const libxl__json_object *response,
- int rc)
-{
- STATE_AO_GC(qmp->ao);
- qemu_monitor_command_state *qmcs = CONTAINER_OF(qmp, *qmcs, qmp);
-
- if (rc) goto out;
-
- if (!libxl__json_object_is_string(response)) {
- rc = ERROR_QEMU_API;
- LOGD(ERROR, qmp->domid, "Response has unexpected format");
- goto out;
- }
-
- *(qmcs->output) =
- libxl__strdup(NOGC, libxl__json_object_get_string(response));
- rc = 0;
-
-out:
- libxl__ev_qmp_dispose(gc, qmp);
- libxl__ao_complete(egc, ao, rc);
-}
-
-/*
- * Functions using libxl__ev_qmp
- */
-
-static void dm_stopped(libxl__egc *egc, libxl__ev_qmp *ev,
- const libxl__json_object *response, int rc);
-static void dm_state_fd_ready(libxl__egc *egc, libxl__ev_qmp *ev,
- const libxl__json_object *response, int rc);
-static void dm_state_save_to_fdset(libxl__egc *egc, libxl__ev_qmp *ev, int fdset);
-static void dm_state_saved(libxl__egc *egc, libxl__ev_qmp *ev,
- const libxl__json_object *response, int rc);
-
-/* calls dsps->callback_device_model_done when done */
-void libxl__qmp_suspend_save(libxl__egc *egc,
- libxl__domain_suspend_state *dsps)
-{
- EGC_GC;
- int rc;
- libxl__ev_qmp *ev = &dsps->qmp;
-
- ev->ao = dsps->ao;
- ev->domid = dsps->domid;
- ev->callback = dm_stopped;
- ev->payload_fd = -1;
-
- rc = libxl__ev_qmp_send(egc, ev, "stop", NULL);
- if (rc)
- goto error;
-
- return;
-
-error:
- dsps->callback_device_model_done(egc, dsps, rc);
-}
-
-static void dm_stopped(libxl__egc *egc, libxl__ev_qmp *ev,
- const libxl__json_object *response, int rc)
-{
- EGC_GC;
- libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
- const char *const filename = dsps->dm_savefile;
- uint32_t dm_domid = libxl_get_stubdom_id(CTX, dsps->domid);
-
- if (rc)
- goto error;
-
- if (dm_domid) {
- /* see Linux stubdom interface in docs/stubdom.txt */
- dm_state_save_to_fdset(egc, ev, 1);
- return;
- }
-
- ev->payload_fd = open(filename, O_WRONLY | O_CREAT, 0600);
- if (ev->payload_fd < 0) {
- LOGED(ERROR, ev->domid,
- "Failed to open file %s for QEMU", filename);
- rc = ERROR_FAIL;
- goto error;
- }
-
- ev->callback = dm_state_fd_ready;
- rc = libxl__ev_qmp_send(egc, ev, "add-fd", NULL);
- if (rc)
- goto error;
-
- return;
-
-error:
- if (ev->payload_fd >= 0) {
- close(ev->payload_fd);
- libxl__remove_file(gc, filename);
- ev->payload_fd = -1;
- }
- dsps->callback_device_model_done(egc, dsps, rc);
-}
-
-static void dm_state_fd_ready(libxl__egc *egc, libxl__ev_qmp *ev,
- const libxl__json_object *response, int rc)
-{
- EGC_GC;
- int fdset;
- const libxl__json_object *o;
- libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
-
- close(ev->payload_fd);
- ev->payload_fd = -1;
-
- if (rc)
- goto error;
-
- o = libxl__json_map_get("fdset-id", response, JSON_INTEGER);
- if (!o) {
- rc = ERROR_QEMU_API;
- goto error;
- }
- fdset = libxl__json_object_get_integer(o);
- dm_state_save_to_fdset(egc, ev, fdset);
- return;
-
-error:
- assert(rc);
- libxl__remove_file(gc, dsps->dm_savefile);
- dsps->callback_device_model_done(egc, dsps, rc);
-}
-
-static void dm_state_save_to_fdset(libxl__egc *egc, libxl__ev_qmp *ev, int fdset)
-{
- EGC_GC;
- int rc;
- libxl__json_object *args = NULL;
- libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
-
- ev->callback = dm_state_saved;
-
- /* The `live` parameter was added to QEMU 2.11. It signals QEMU that
- * the save operation is for a live migration rather than for taking a
- * snapshot. */
- if (qmp_ev_qemu_compare_version(ev, 2, 11, 0) >= 0)
- libxl__qmp_param_add_bool(gc, &args, "live", dsps->live);
- QMP_PARAMETERS_SPRINTF(&args, "filename", "/dev/fdset/%d", fdset);
- rc = libxl__ev_qmp_send(egc, ev, "xen-save-devices-state", args);
- if (rc)
- goto error;
-
- return;
-
-error:
- assert(rc);
- if (!libxl_get_stubdom_id(CTX, dsps->domid))
- libxl__remove_file(gc, dsps->dm_savefile);
- dsps->callback_device_model_done(egc, dsps, rc);
-}
-
-static void dm_state_saved(libxl__egc *egc, libxl__ev_qmp *ev,
- const libxl__json_object *response, int rc)
-{
- EGC_GC;
- libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
-
- if (rc)
- libxl__remove_file(gc, dsps->dm_savefile);
-
- dsps->callback_device_model_done(egc, dsps, rc);
-}
-
-
-/* ------------ Implementation of libxl__ev_qmp ---------------- */
-
-/*
- * Possible internal state compared to qmp_state:
- *
- * qmp_state External cfd efd id rx_buf* tx_buf* msg* lock
- * disconnected Idle NULL Idle reset free free free Idle
- * waiting_lock Active open Idle reset used free set Active
- * connecting Active open IN reset used free set Acquired
- * cap.neg Active open IN|OUT sent used cap_neg set Acquired
- * cap.neg Active open IN sent used free set Acquired
- * connected Connected open IN any used free free Acquired
- * waiting_reply Active open IN|OUT sent used free set Acquired
- * waiting_reply Active open IN|OUT sent used user's free Acquired
- * waiting_reply Active open IN sent used free free Acquired
- * broken[1] none[2] any Active any any any any any
- *
- * [1] When an internal function return an error, it can leave ev_qmp in a
- * `broken` state but only if the caller is another internal function.
- * That `broken` needs to be cleaned up, e.i. transitionned to the
- * `disconnected` state, before the control of ev_qmp is released outsides
- * of ev_qmp implementation.
- *
- * [2] This internal state should not be visible externally, see [1].
- *
- * Possible buffers states:
- * - receiving buffer:
- * free used
- * rx_buf NULL NULL or allocated
- * rx_buf_size 0 allocation size of `rx_buf`
- * rx_buf_used 0 <= rx_buf_size, actual data in the buffer
- * - transmitting buffer:
- * free used
- * tx_buf NULL contains data
- * tx_buf_len 0 size of data
- * tx_buf_off 0 <= tx_buf_len, data already sent
- * - queued user command:
- * free set
- * msg NULL contains data
- * msg_id 0 id assoctiated with the command in `msg`
- *
- * - Allowed internal state transition:
- * disconnected -> waiting_lock
- * waiting_lock -> connecting
- * connection -> capability_negotiation
- * capability_negotiation/connected -> waiting_reply
- * waiting_reply -> connected
- * any -> broken
- * broken -> disconnected
- * any -> disconnected
- *
- * The QEMU Machine Protocol (QMP) specification can be found in the QEMU
- * repository:
- * https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/interop/qmp-spec.txt
- */
-
-/* prototypes */
-
-static void qmp_ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd,
- int fd, short events, short revents);
-static int qmp_ev_callback_writable(libxl__gc *gc,
- libxl__ev_qmp *ev, int fd);
-static int qmp_ev_callback_readable(libxl__egc *egc,
- libxl__ev_qmp *ev, int fd);
-static int qmp_ev_get_next_msg(libxl__egc *egc, libxl__ev_qmp *ev,
- libxl__json_object **o_r);
-static int qmp_ev_handle_message(libxl__egc *egc,
- libxl__ev_qmp *ev,
- const libxl__json_object *resp);
-
-/* helpers */
-
-static void qmp_ev_ensure_reading_writing(libxl__gc *gc, libxl__ev_qmp *ev)
- /* Update the state of `efd` to match the permited state
- * on entry: !disconnected */
-{
- short events = POLLIN;
-
- if (ev->state == qmp_state_waiting_lock)
- /* We can't modify the efd yet, as it isn't registered. */
- return;
-
- if (ev->tx_buf)
- events |= POLLOUT;
- else if ((ev->state == qmp_state_waiting_reply) && ev->msg)
- events |= POLLOUT;
-
- libxl__ev_fd_modify(gc, &ev->efd, events);
-}
-
-static void qmp_ev_set_state(libxl__gc *gc, libxl__ev_qmp *ev,
- libxl__qmp_state new_state)
- /* on entry: !broken and !disconnected */
-{
- switch (new_state) {
- case qmp_state_disconnected:
- break;
- case qmp_state_waiting_lock:
- assert(ev->state == qmp_state_disconnected);
- break;
- case qmp_state_connecting:
- assert(ev->state == qmp_state_waiting_lock);
- break;
- case qmp_state_capability_negotiation:
- assert(ev->state == qmp_state_connecting);
- break;
- case qmp_state_waiting_reply:
- assert(ev->state == qmp_state_capability_negotiation ||
- ev->state == qmp_state_connected);
- break;
- case qmp_state_connected:
- assert(ev->state == qmp_state_waiting_reply);
- break;
- }
-
- ev->state = new_state;
-
- qmp_ev_ensure_reading_writing(gc, ev);
-}
-
-static void qmp_ev_tx_buf_clear(libxl__ev_qmp *ev)
-{
- ev->tx_buf = NULL;
- ev->tx_buf_len = 0;
- ev->tx_buf_off = 0;
-}
-
-static int qmp_error_class_to_libxl_error_code(libxl__gc *gc,
- const char *eclass)
-{
- const libxl_enum_string_table *t = libxl_error_string_table;
- const char skip[] = "QMP_";
- const size_t skipl = sizeof(skip) - 1;
-
- /* compare "QMP_GENERIC_ERROR" from libxl_error to "GenericError"
- * generated by the QMP server */
-
- for (; t->s; t++) {
- const char *s = eclass;
- const char *se = t->s;
- if (strncasecmp(t->s, skip, skipl))
- continue;
- se += skipl;
- while (*s && *se) {
- /* skip underscores */
- if (*se == '_') {
- se++;
- continue;
- }
- if (tolower(*s) != tolower(*se))
- break;
- s++, se++;
- }
- if (!*s && !*se)
- return t->v;
- }
-
- LOG(ERROR, "Unknown QMP error class '%s'", eclass);
- return ERROR_UNKNOWN_QMP_ERROR;
-}
-
-/* Setup connection */
-
-static void qmp_ev_lock_aquired(libxl__egc *, libxl__ev_slowlock *,
- int rc);
-static void lock_error_callback(libxl__egc *, libxl__ev_immediate *);
-
-static int qmp_ev_connect(libxl__egc *egc, libxl__ev_qmp *ev)
- /* disconnected -> waiting_lock/connecting but with `msg` free
- * on error: broken */
-{
- EGC_GC;
- int fd;
- int rc;
-
- /* Convenience aliases */
- libxl__ev_slowlock *lock = &ev->lock;
-
- assert(ev->state == qmp_state_disconnected);
-
- libxl__carefd_begin();
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
- ev->cfd = libxl__carefd_opened(CTX, fd);
- if (!ev->cfd) {
- LOGED(ERROR, ev->domid, "socket() failed");
- rc = ERROR_FAIL;
- goto out;
- }
- rc = libxl_fd_set_nonblock(CTX, libxl__carefd_fd(ev->cfd), 1);
- if (rc)
- goto out;
-
- qmp_ev_set_state(gc, ev, qmp_state_waiting_lock);
-
- lock->ao = ev->ao;
- lock->domid = ev->domid;
- lock->callback = qmp_ev_lock_aquired;
- libxl__ev_slowlock_lock(egc, &ev->lock);
-
- return 0;
-
-out:
- return rc;
-}
-
-static void qmp_ev_lock_aquired(libxl__egc *egc, libxl__ev_slowlock *lock,
- int rc)
- /* waiting_lock (with `lock' Acquired) -> connecting
- * on error: broken */
-{
- libxl__ev_qmp *ev = CONTAINER_OF(lock, *ev, lock);
- EGC_GC;
- const char *qmp_socket_path;
- struct sockaddr_un un;
- int r;
-
- if (rc) goto out;
-
- qmp_socket_path = libxl__qemu_qmp_path(gc, ev->domid);
-
- LOGD(DEBUG, ev->domid, "Connecting to %s", qmp_socket_path);
-
- rc = libxl__prepare_sockaddr_un(gc, &un, qmp_socket_path,
- "QMP socket");
- if (rc)
- goto out;
-
- r = connect(libxl__carefd_fd(ev->cfd),
- (struct sockaddr *) &un, sizeof(un));
- if (r && errno != EINPROGRESS) {
- LOGED(ERROR, ev->domid, "Failed to connect to QMP socket %s",
- qmp_socket_path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__ev_fd_register(gc, &ev->efd, qmp_ev_fd_callback,
- libxl__carefd_fd(ev->cfd), POLLIN);
- if (rc)
- goto out;
-
- qmp_ev_set_state(gc, ev, qmp_state_connecting);
-
- return;
-
-out:
- /* An error occurred and we need to let the caller know. At this
- * point, we can only do so via the callback. Unfortunately, the
- * callback of libxl__ev_slowlock_lock() might be called synchronously,
- * but libxl__ev_qmp_send() promise that it will not call the callback
- * synchronously. So we have to arrange to call the callback
- * asynchronously. */
- ev->rc = rc;
- ev->ei.callback = lock_error_callback;
- libxl__ev_immediate_register(egc, &ev->ei);
-}
-
-static void lock_error_callback(libxl__egc *egc, libxl__ev_immediate *ei)
- /* broken -> disconnected */
-{
- EGC_GC;
- libxl__ev_qmp *ev = CONTAINER_OF(ei, *ev, ei);
-
- int rc = ev->rc;
-
- /* On error, deallocate all private resources */
- libxl__ev_qmp_dispose(gc, ev);
-
- /* And tell libxl__ev_qmp user about the error */
- ev->callback(egc, ev, NULL, rc); /* must be last */
-}
-
-/* QMP FD callbacks */
-
-static void qmp_ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd,
- int fd, short events, short revents)
- /* On entry, ev_fd is (of course) Active. The ev_qmp may be in any
- * state where this is permitted. qmp_ev_fd_callback will do the work
- * necessary to make progress, depending on the current state, and make
- * the appropriate state transitions and callbacks. */
-{
- libxl__ev_qmp *ev = CONTAINER_OF(ev_fd, *ev, efd);
- STATE_AO_GC(ev->ao);
- int rc;
-
- if (revents & (POLLHUP|POLLERR)) {
- int r;
- int error_val = 0;
- socklen_t opt_len = sizeof(error_val);
-
- r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error_val, &opt_len);
- if (r)
- LOGED(ERROR, ev->domid, "getsockopt failed");
- if (!r && error_val) {
- errno = error_val;
- LOGED(ERROR, ev->domid, "error on QMP socket");
- } else {
- LOGD(ERROR, ev->domid,
- "received POLLHUP|POLLERR from QMP socket");
- }
- rc = ERROR_PROTOCOL_ERROR_QMP;
- goto error;
- }
-
- if (revents & ~(POLLIN|POLLOUT)) {
- LOGD(ERROR, ev->domid,
- "unexpected poll event 0x%x on QMP socket (expected POLLIN "
- "and/or POLLOUT)",
- revents);
- rc = ERROR_FAIL;
- goto error;
- }
-
- if (revents & POLLOUT) {
- rc = qmp_ev_callback_writable(gc, ev, fd);
- if (rc)
- goto error;
- }
-
- if (revents & POLLIN) {
- rc = qmp_ev_callback_readable(egc, ev, fd);
- if (rc < 0)
- goto error;
- if (rc == 1) {
- /* user callback has been called */
- return;
- }
- }
-
- return;
-
-error:
- assert(rc);
-
- LOGD(ERROR, ev->domid,
- "Error happened with the QMP connection to QEMU");
-
- /* On error, deallocate all private ressources */
- libxl__ev_qmp_dispose(gc, ev);
-
- /* And tell libxl__ev_qmp user about the error */
- ev->callback(egc, ev, NULL, rc); /* must be last */
-}
-
-static int qmp_ev_callback_writable(libxl__gc *gc,
- libxl__ev_qmp *ev, int fd)
- /* on entry: !disconnected
- * on return, one of these state transition:
- * waiting_reply (with msg set) -> waiting_reply (with msg free)
- * tx_buf set -> same state or tx_buf free
- * on error: broken */
-{
- int rc;
- ssize_t r;
-
- if (ev->state == qmp_state_waiting_reply) {
- if (ev->msg) {
- assert(!ev->tx_buf);
- ev->tx_buf = ev->msg;
- ev->tx_buf_len = strlen(ev->msg);
- ev->tx_buf_off = 0;
- ev->id = ev->msg_id;
- ev->msg = NULL;
- ev->msg_id = 0;
- }
- }
-
- assert(ev->tx_buf);
-
- LOG_QMP("sending: '%.*s'", (int)ev->tx_buf_len, ev->tx_buf);
-
- /*
- * We will send a file descriptor associated with a command on the
- * first byte of this command.
- */
- if (ev->state == qmp_state_waiting_reply &&
- ev->payload_fd >= 0 &&
- ev->tx_buf_off == 0) {
-
- rc = libxl__sendmsg_fds(gc, fd, ev->tx_buf[ev->tx_buf_off],
- 1, &ev->payload_fd, "QMP socket");
- /* Check for EWOULDBLOCK, and return to try again later */
- if (rc == ERROR_NOT_READY)
- return 0;
- if (rc)
- return rc;
- ev->tx_buf_off++;
- }
-
- while (ev->tx_buf_off < ev->tx_buf_len) {
- ssize_t max_write = ev->tx_buf_len - ev->tx_buf_off;
- r = write(fd, ev->tx_buf + ev->tx_buf_off, max_write);
- if (r < 0) {
- if (errno == EINTR)
- continue;
- if (errno == EWOULDBLOCK)
- break;
- LOGED(ERROR, ev->domid, "failed to write to QMP socket");
- return ERROR_FAIL;
- }
- assert(r > 0 && r <= max_write);
- ev->tx_buf_off += r;
- }
-
- if (ev->tx_buf_off == ev->tx_buf_len)
- qmp_ev_tx_buf_clear(ev);
-
- qmp_ev_ensure_reading_writing(gc, ev);
-
- return 0;
-}
-
-static int qmp_ev_callback_readable(libxl__egc *egc,
- libxl__ev_qmp *ev, int fd)
- /*
- * Return values:
- * < 0 libxl error code
- * 0 success
- * 1 success, but a user callback has been called,
- * `ev` should not be used anymore.
- *
- * This function will update the rx buffer and possibly update
- * ev->state:
- * connecting -> capability_negotiation
- * capability_negotiation -> waiting_reply
- * waiting_reply -> connected
- * on error: broken
- */
-{
- STATE_AO_GC(ev->ao);
- int rc;
- ssize_t r;
-
- while (1) {
- while (1) {
- libxl__json_object *o = NULL;
-
- /* parse rx buffer to find one json object */
- rc = qmp_ev_get_next_msg(egc, ev, &o);
- if (rc == ERROR_NOTFOUND)
- break;
- else if (rc)
- return rc;
-
- /* Must be last and return when the user callback is called */
- rc = qmp_ev_handle_message(egc, ev, o);
- if (rc)
- /* returns both rc values -ERROR_* and 1 */
- return rc;
- }
-
- /* Check if the buffer still have space, or increase size */
- if (ev->rx_buf_size - ev->rx_buf_used < QMP_RECEIVE_BUFFER_SIZE) {
- size_t newsize = ev->rx_buf_size * 2 + QMP_RECEIVE_BUFFER_SIZE;
-
- if (newsize > QMP_MAX_SIZE_RX_BUF) {
- LOGD(ERROR, ev->domid,
- "QMP receive buffer is too big (%zu > %lld)",
- newsize, QMP_MAX_SIZE_RX_BUF);
- return ERROR_BUFFERFULL;
- }
- ev->rx_buf_size = newsize;
- ev->rx_buf = libxl__realloc(gc, ev->rx_buf, ev->rx_buf_size);
- }
-
- r = read(fd, ev->rx_buf + ev->rx_buf_used,
- ev->rx_buf_size - ev->rx_buf_used);
- if (r < 0) {
- if (errno == EINTR)
- continue;
- if (errno == EWOULDBLOCK)
- break;
- LOGED(ERROR, ev->domid, "error reading QMP socket");
- return ERROR_FAIL;
- }
-
- if (r == 0) {
- LOGD(ERROR, ev->domid, "Unexpected EOF on QMP socket");
- return ERROR_PROTOCOL_ERROR_QMP;
- }
-
- LOG_QMP("received %ldB: '%.*s'", r,
- (int)r, ev->rx_buf + ev->rx_buf_used);
-
- ev->rx_buf_used += r;
- assert(ev->rx_buf_used <= ev->rx_buf_size);
- }
-
- return 0;
-}
-
-/* Handle messages received from QMP server */
-
-static int qmp_ev_get_next_msg(libxl__egc *egc, libxl__ev_qmp *ev,
- libxl__json_object **o_r)
- /* Find a JSON object and store it in o_r.
- * return ERROR_NOTFOUND if no object is found.
- *
- * !disconnected -> same state (with rx buffer updated)
- */
-{
- STATE_AO_GC(ev->ao);
- size_t len;
- char *end = NULL;
- const char eom[] = "\r\n";
- const size_t eoml = sizeof(eom) - 1;
- libxl__json_object *o = NULL;
-
- if (!ev->rx_buf_used)
- return ERROR_NOTFOUND;
-
- /* Search for the end of a QMP message: "\r\n" */
- end = memmem(ev->rx_buf, ev->rx_buf_used, eom, eoml);
- if (!end)
- return ERROR_NOTFOUND;
- len = (end - ev->rx_buf) + eoml;
-
- LOG_QMP("parsing %luB: '%.*s'", len, (int)len, ev->rx_buf);
-
- /* Replace \r by \0 so that libxl__json_parse can use strlen */
- ev->rx_buf[len - eoml] = '\0';
- o = libxl__json_parse(gc, ev->rx_buf);
-
- if (!o) {
- LOGD(ERROR, ev->domid, "Parse error");
- return ERROR_PROTOCOL_ERROR_QMP;
- }
-
- ev->rx_buf_used -= len;
- memmove(ev->rx_buf, ev->rx_buf + len, ev->rx_buf_used);
-
- LOG_QMP("JSON object received: %s", JSON(o));
-
- *o_r = o;
-
- return 0;
-}
-
-static int qmp_ev_parse_error_messages(libxl__egc *egc,
- libxl__ev_qmp *ev,
- const libxl__json_object *resp);
-
-static int qmp_ev_handle_message(libxl__egc *egc,
- libxl__ev_qmp *ev,
- const libxl__json_object *resp)
- /*
- * This function will handle every messages sent by the QMP server.
- * Return values:
- * < 0 libxl error code
- * 0 success
- * 1 success, but a user callback has been called,
- * `ev` should not be used anymore.
- *
- * Possible state changes:
- * connecting -> capability_negotiation
- * capability_negotiation -> waiting_reply
- * waiting_reply -> waiting_reply/connected
- *
- * on error: broken
- */
-{
- STATE_AO_GC(ev->ao);
- int id;
- char *buf;
- int rc = 0;
- const libxl__json_object *o;
- const libxl__json_object *response;
- libxl__qmp_message_type type = qmp_response_type(resp);
-
- switch (type) {
- case LIBXL__QMP_MESSAGE_TYPE_QMP:
- /* greeting message */
-
- if (ev->state != qmp_state_connecting) {
- LOGD(ERROR, ev->domid,
- "Unexpected greeting message received");
- return ERROR_PROTOCOL_ERROR_QMP;
- }
-
- /*
- * Store advertised QEMU version
- * { "QMP": { "version": {
- * "qemu": { "major": int, "minor": int, "micro": int } } } }
- */
- o = libxl__json_map_get("QMP", resp, JSON_MAP);
- o = libxl__json_map_get("version", o, JSON_MAP);
- o = libxl__json_map_get("qemu", o, JSON_MAP);
-#define GRAB_VERSION(level) do { \
- ev->qemu_version.level = libxl__json_object_get_integer( \
- libxl__json_map_get(#level, o, JSON_INTEGER)); \
- } while (0)
- GRAB_VERSION(major);
- GRAB_VERSION(minor);
- GRAB_VERSION(micro);
-#undef GRAB_VERSION
- LOGD(DEBUG, ev->domid, "QEMU version: %d.%d.%d",
- ev->qemu_version.major,
- ev->qemu_version.minor,
- ev->qemu_version.micro);
-
- /* Prepare next message to send */
- assert(!ev->tx_buf);
- ev->id = ev->next_id++;
- buf = qmp_prepare_cmd(gc, "qmp_capabilities", NULL, ev->id);
- if (!buf) {
- LOGD(ERROR, ev->domid,
- "Failed to generate qmp_capabilities command");
- return ERROR_FAIL;
- }
- ev->tx_buf = buf;
- ev->tx_buf_len = strlen(buf);
- ev->tx_buf_off = 0;
- qmp_ev_set_state(gc, ev, qmp_state_capability_negotiation);
-
- return 0;
-
- case LIBXL__QMP_MESSAGE_TYPE_RETURN:
- case LIBXL__QMP_MESSAGE_TYPE_ERROR:
- /*
- * Reply to a command (success/error) or server error
- *
- * In this cases, we are parsing two possibles responses:
- * - success:
- * { "return": json-value, "id": int }
- * - error:
- * { "error": { "class": string, "desc": string }, "id": int }
- */
-
- o = libxl__json_map_get("id", resp, JSON_INTEGER);
- if (!o) {
- /*
- * If "id" isn't present, an error occur on the server before
- * it has read the "id" provided by libxl.
- *
- * We deliberately squash all errors into
- * ERROR_PROTOCOL_ERROR_QMP as qmp_ev_parse_error_messages may
- * also return ERROR_QMP_* but those are reserved for errors
- * return by the caller's command.
- */
- qmp_ev_parse_error_messages(egc, ev, resp);
- return ERROR_PROTOCOL_ERROR_QMP;
- }
-
- id = libxl__json_object_get_integer(o);
-
- if (id != ev->id) {
- LOGD(ERROR, ev->domid,
- "Message from QEMU with unexpected id %d: %s",
- id, JSON(resp));
- return ERROR_PROTOCOL_ERROR_QMP;
- }
-
- switch (ev->state) {
- case qmp_state_capability_negotiation:
- if (type != LIBXL__QMP_MESSAGE_TYPE_RETURN) {
- LOGD(ERROR, ev->domid,
- "Error during capability negotiation: %s",
- JSON(resp));
- return ERROR_PROTOCOL_ERROR_QMP;
- }
- qmp_ev_set_state(gc, ev, qmp_state_waiting_reply);
- return 0;
- case qmp_state_waiting_reply:
- if (type == LIBXL__QMP_MESSAGE_TYPE_RETURN) {
- response = libxl__json_map_get("return", resp, JSON_ANY);
- rc = 0;
- } else {
- /* error message */
- response = NULL;
- rc = qmp_ev_parse_error_messages(egc, ev, resp);
- }
- qmp_ev_set_state(gc, ev, qmp_state_connected);
- ev->callback(egc, ev, response, rc); /* must be last */
- return 1;
- default:
- LOGD(ERROR, ev->domid, "Unexpected message: %s", JSON(resp));
- return ERROR_PROTOCOL_ERROR_QMP;
- }
- return 0;
-
- case LIBXL__QMP_MESSAGE_TYPE_EVENT:
- /* Events are ignored */
- return 0;
-
- case LIBXL__QMP_MESSAGE_TYPE_INVALID:
- LOGD(ERROR, ev->domid, "Unexpected message received: %s",
- JSON(resp));
- return ERROR_PROTOCOL_ERROR_QMP;
-
- default:
- abort();
- }
-
- return 0;
-}
-
-static int qmp_ev_parse_error_messages(libxl__egc *egc,
- libxl__ev_qmp *ev,
- const libxl__json_object *resp)
- /* no state change */
-{
- STATE_AO_GC(ev->ao);
- int rc;
- const char *s;
- const libxl__json_object *o;
- const libxl__json_object *err;
-
- /*
- * { "error": { "class": string, "desc": string } }
- */
-
- err = libxl__json_map_get("error", resp, JSON_MAP);
-
- o = libxl__json_map_get("class", err, JSON_STRING);
- if (!o) {
- LOGD(ERROR, ev->domid,
- "Protocol error: missing 'class' member in error message");
- return ERROR_PROTOCOL_ERROR_QMP;
- }
- s = libxl__json_object_get_string(o);
- if (s)
- rc = qmp_error_class_to_libxl_error_code(gc, s);
- else
- rc = ERROR_PROTOCOL_ERROR_QMP;
-
- o = libxl__json_map_get("desc", err, JSON_STRING);
- if (!o) {
- LOGD(ERROR, ev->domid,
- "Protocol error: missing 'desc' member in error message");
- return ERROR_PROTOCOL_ERROR_QMP;
- }
- s = libxl__json_object_get_string(o);
- if (s)
- LOGD(ERROR, ev->domid, "%s", s);
- else
- LOGD(ERROR, ev->domid, "Received unexpected error: %s",
- JSON(resp));
- return rc;
-}
-
-/*
- * libxl__ev_qmp_*
- */
-
-void libxl__ev_qmp_init(libxl__ev_qmp *ev)
- /* disconnected -> disconnected */
-{
- /* Start with an message ID that is obviously generated by libxl
- * "xlq\0" */
- ev->next_id = 0x786c7100;
-
- ev->cfd = NULL;
- libxl__ev_fd_init(&ev->efd);
- ev->state = qmp_state_disconnected;
- ev->id = 0;
-
- ev->rx_buf = NULL;
- ev->rx_buf_size = ev->rx_buf_used = 0;
- qmp_ev_tx_buf_clear(ev);
-
- ev->msg = NULL;
- ev->msg_id = 0;
-
- ev->qemu_version.major = -1;
- ev->qemu_version.minor = -1;
- ev->qemu_version.micro = -1;
-
- libxl__ev_qmplock_init(&ev->lock);
- ev->rc = 0;
-}
-
-int libxl__ev_qmp_send(libxl__egc *egc, libxl__ev_qmp *ev,
- const char *cmd, libxl__json_object *args)
- /* disconnected -> waiting_lock/connecting
- * connected -> waiting_reply (with msg set)
- * on error: disconnected */
-{
- STATE_AO_GC(ev->ao);
- int rc;
-
- LOGD(DEBUG, ev->domid, " ev %p, cmd '%s'", ev, cmd);
-
- assert(ev->state == qmp_state_disconnected ||
- ev->state == qmp_state_connected);
- assert(cmd);
-
- /* Connect to QEMU if not already connected */
- if (ev->state == qmp_state_disconnected) {
- rc = qmp_ev_connect(egc, ev);
- if (rc)
- goto error;
- }
-
- /* Prepare user command */
- ev->msg_id = ev->next_id++;
- ev->msg = qmp_prepare_cmd(gc, cmd, args, ev->msg_id);
- if (!ev->msg) {
- LOGD(ERROR, ev->domid, "Failed to generate caller's command %s",
- cmd);
- rc = ERROR_FAIL;
- goto error;
- }
- if (ev->state == qmp_state_connected) {
- qmp_ev_set_state(gc, ev, qmp_state_waiting_reply);
- }
-
- return 0;
-
-error:
- libxl__ev_qmp_dispose(gc, ev);
- return rc;
-}
-
-void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev)
- /* * -> disconnected */
-{
- LOGD(DEBUG, ev->domid, " ev %p", ev);
-
- libxl__ev_fd_deregister(gc, &ev->efd);
- libxl__carefd_close(ev->cfd);
- libxl__ev_slowlock_dispose(gc, &ev->lock);
-
- libxl__ev_qmp_init(ev);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Yang Hongyang <hongyang.yang@easystack.cn>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-extern const libxl__checkpoint_device_instance_ops remus_device_nic;
-extern const libxl__checkpoint_device_instance_ops remus_device_drbd_disk;
-static const libxl__checkpoint_device_instance_ops *remus_ops[] = {
- &remus_device_nic,
- &remus_device_drbd_disk,
- NULL,
-};
-
-/*----- helper functions -----*/
-
-static int init_device_subkind(libxl__checkpoint_devices_state *cds)
-{
- /* init device subkind-specific state in the libxl ctx */
- int rc;
- STATE_AO_GC(cds->ao);
-
- if (libxl__netbuffer_enabled(gc)) {
- rc = init_subkind_nic(cds);
- if (rc) goto out;
- }
-
- rc = init_subkind_drbd_disk(cds);
- if (rc) goto out;
-
- rc = 0;
-out:
- return rc;
-}
-
-static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds)
-{
- /* cleanup device subkind-specific state in the libxl ctx */
- STATE_AO_GC(cds->ao);
-
- if (libxl__netbuffer_enabled(gc))
- cleanup_subkind_nic(cds);
-
- cleanup_subkind_drbd_disk(cds);
-}
-
-/*-------------------- Remus setup and teardown ---------------------*/
-
-static void remus_setup_done(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds, int rc);
-static void remus_setup_failed(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds, int rc);
-static void remus_checkpoint_stream_written(
- libxl__egc *egc, libxl__stream_write_state *sws, int rc);
-static void libxl__remus_domain_suspend_callback(void *data);
-static void libxl__remus_domain_resume_callback(void *data);
-static void libxl__remus_domain_save_checkpoint_callback(void *data);
-
-void libxl__remus_setup(libxl__egc *egc, libxl__remus_state *rs)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(rs, *dss, rs);
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *const cds = &dss->cds;
- const libxl_domain_remus_info *const info = dss->remus;
- libxl__srm_save_autogen_callbacks *const callbacks =
- &dss->sws.shs.callbacks.save.a;
-
- STATE_AO_GC(dss->ao);
-
- if (libxl_defbool_val(info->netbuf)) {
- if (!libxl__netbuffer_enabled(gc)) {
- LOGD(ERROR, dss->domid,
- "Remus: No support for network buffering");
- goto out;
- }
- cds->device_kind_flags |= (1 << LIBXL__DEVICE_KIND_VIF);
- }
-
- if (libxl_defbool_val(info->diskbuf))
- cds->device_kind_flags |= (1 << LIBXL__DEVICE_KIND_VBD);
-
- cds->ao = ao;
- cds->domid = dss->domid;
- cds->callback = remus_setup_done;
- cds->ops = remus_ops;
- cds->concrete_data = rs;
- rs->interval = info->interval;
-
- if (init_device_subkind(cds)) {
- LOGD(ERROR, dss->domid,
- "Remus: failed to init device subkind");
- goto out;
- }
-
- dss->sws.checkpoint_callback = remus_checkpoint_stream_written;
-
- callbacks->suspend = libxl__remus_domain_suspend_callback;
- callbacks->postcopy = libxl__remus_domain_resume_callback;
- callbacks->checkpoint = libxl__remus_domain_save_checkpoint_callback;
-
- libxl__checkpoint_devices_setup(egc, cds);
- return;
-
-out:
- dss->callback(egc, dss, ERROR_FAIL);
-}
-
-static void remus_setup_done(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds, int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
- STATE_AO_GC(dss->ao);
-
- if (!rc) {
- libxl__domain_save(egc, dss);
- return;
- }
-
- LOGD(ERROR, dss->domid, "Remus: failed to setup device, rc %d", rc);
- cds->callback = remus_setup_failed;
- libxl__checkpoint_devices_teardown(egc, cds);
-}
-
-static void remus_setup_failed(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds, int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
- STATE_AO_GC(dss->ao);
-
- if (rc)
- LOGD(ERROR, dss->domid,
- "Remus: failed to teardown device after setup failed, rc %d", rc);
-
- cleanup_device_subkind(cds);
-
- dss->callback(egc, dss, rc);
-}
-
-static void remus_teardown_done(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-void libxl__remus_teardown(libxl__egc *egc,
- libxl__remus_state *rs,
- int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(rs, *dss, rs);
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *const cds = &dss->cds;
-
- EGC_GC;
-
- LOGD(WARN, dss->domid, "Remus: Domain suspend terminated with rc %d,"
- " teardown Remus devices...", rc);
- cds->callback = remus_teardown_done;
- libxl__checkpoint_devices_teardown(egc, cds);
-}
-
-static void remus_teardown_done(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
- STATE_AO_GC(dss->ao);
-
- if (rc)
- LOGD(ERROR, dss->domid, "Remus: failed to teardown device,"
- " rc %d", rc);
-
- cleanup_device_subkind(cds);
-
- dss->callback(egc, dss, rc);
-}
-
-/*---------------------- remus callbacks (save) -----------------------*/
-
-static void remus_domain_suspend_callback_common_done(libxl__egc *egc,
- libxl__domain_suspend_state *dsps, int ok);
-static void remus_devices_postsuspend_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-static void remus_devices_preresume_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-
-static void libxl__remus_domain_suspend_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__egc *egc = shs->egc;
- libxl__domain_save_state *dss = shs->caller_state;
- libxl__domain_suspend_state *dsps = &dss->dsps;
-
- dsps->callback_common_done = remus_domain_suspend_callback_common_done;
- libxl__domain_suspend(egc, dsps);
-}
-
-static void remus_domain_suspend_callback_common_done(libxl__egc *egc,
- libxl__domain_suspend_state *dsps, int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
-
- if (rc)
- goto out;
-
- libxl__checkpoint_devices_state *const cds = &dss->cds;
- cds->callback = remus_devices_postsuspend_cb;
- libxl__checkpoint_devices_postsuspend(egc, cds);
- return;
-
-out:
- dss->rc = rc;
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-static void remus_devices_postsuspend_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
-
- if (rc)
- goto out;
-
- rc = 0;
-
-out:
- if (rc)
- dss->rc = rc;
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-static void libxl__remus_domain_resume_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__egc *egc = shs->egc;
- libxl__domain_save_state *dss = shs->caller_state;
- STATE_AO_GC(dss->ao);
-
- libxl__checkpoint_devices_state *const cds = &dss->cds;
- cds->callback = remus_devices_preresume_cb;
- libxl__checkpoint_devices_preresume(egc, cds);
-}
-
-static void remus_devices_preresume_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
- STATE_AO_GC(dss->ao);
-
- if (rc)
- goto out;
-
- /* Resumes the domain and the device model */
- rc = libxl__domain_resume_deprecated(gc, dss->domid, /* Fast Suspend */1);
- if (rc)
- goto out;
-
- rc = 0;
-
-out:
- if (rc)
- dss->rc = rc;
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-/*----- remus asynchronous checkpoint callback -----*/
-
-static void remus_devices_commit_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc);
-static void remus_next_checkpoint(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc);
-
-static void libxl__remus_domain_save_checkpoint_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__domain_save_state *dss = shs->caller_state;
- libxl__egc *egc = shs->egc;
- STATE_AO_GC(dss->ao);
-
- libxl__stream_write_start_checkpoint(egc, &dss->sws);
-}
-
-static void remus_checkpoint_stream_written(
- libxl__egc *egc, libxl__stream_write_state *sws, int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(sws, *dss, sws);
-
- /* Convenience aliases */
- libxl__checkpoint_devices_state *const cds = &dss->cds;
-
- STATE_AO_GC(dss->ao);
-
- if (rc) {
- LOGD(ERROR, dss->domid, "Failed to save device model."
- " Terminating Remus..");
- goto out;
- }
-
- cds->callback = remus_devices_commit_cb;
- libxl__checkpoint_devices_commit(egc, cds);
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-static void remus_devices_commit_cb(libxl__egc *egc,
- libxl__checkpoint_devices_state *cds,
- int rc)
-{
- libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
-
- STATE_AO_GC(dss->ao);
-
- if (rc) {
- LOGD(ERROR, dss->domid, "Failed to do device commit op."
- " Terminating Remus..");
- goto out;
- }
-
- /*
- * At this point, we have successfully checkpointed the guest and
- * committed it at the backup. We'll come back after the checkpoint
- * interval to checkpoint the guest again. Until then, let the guest
- * continue execution.
- */
-
- /* Set checkpoint interval timeout */
- rc = libxl__ev_time_register_rel(ao, &dss->rs.checkpoint_timeout,
- remus_next_checkpoint,
- dss->rs.interval);
-
- if (rc)
- goto out;
-
- return;
-
-out:
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-static void remus_next_checkpoint(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- libxl__domain_save_state *dss =
- CONTAINER_OF(ev, *dss, rs.checkpoint_timeout);
-
- STATE_AO_GC(dss->ao);
-
- if (rc == ERROR_TIMEDOUT) /* As intended */
- rc = 0;
-
- /*
- * Time to checkpoint the guest again. We return 1 to libxc
- * (xc_domain_save.c). in order to continue executing the infinite loop
- * (suspend, checkpoint, resume) in xc_domain_save().
- */
-
- if (rc)
- dss->rc = rc;
-
- libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-/*---------------------- remus callbacks (restore) -----------------------*/
-
-/*----- remus asynchronous checkpoint callback -----*/
-
-static void remus_checkpoint_stream_done(
- libxl__egc *egc, libxl__stream_read_state *srs, int rc);
-
-static void libxl__remus_domain_restore_checkpoint_callback(void *data)
-{
- libxl__save_helper_state *shs = data;
- libxl__domain_create_state *dcs = shs->caller_state;
- libxl__egc *egc = shs->egc;
- STATE_AO_GC(dcs->ao);
-
- libxl__stream_read_start_checkpoint(egc, &dcs->srs);
-}
-
-static void remus_checkpoint_stream_done(
- libxl__egc *egc, libxl__stream_read_state *stream, int rc)
-{
- libxl__xc_domain_saverestore_async_callback_done(egc, &stream->shs, rc);
-}
-
-void libxl__remus_restore_setup(libxl__egc *egc,
- libxl__domain_create_state *dcs)
-{
- /* Convenience aliases */
- libxl__srm_restore_autogen_callbacks *const callbacks =
- &dcs->srs.shs.callbacks.restore.a;
-
- callbacks->checkpoint = libxl__remus_domain_restore_checkpoint_callback;
- dcs->srs.checkpoint_callback = remus_checkpoint_stream_done;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2014 FUJITSU LIMITED
- * Author Lai Jiangshan <laijs@cn.fujitsu.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*** drbd implementation ***/
-const int DRBD_SEND_CHECKPOINT = 20;
-const int DRBD_WAIT_CHECKPOINT_ACK = 30;
-
-typedef struct libxl__remus_drbd_disk {
- int ctl_fd;
- int ackwait;
-} libxl__remus_drbd_disk;
-
-int init_subkind_drbd_disk(libxl__checkpoint_devices_state *cds)
-{
- libxl__remus_state *rs = cds->concrete_data;
- STATE_AO_GC(cds->ao);
-
- rs->drbd_probe_script = GCSPRINTF("%s/block-drbd-probe",
- libxl__xen_script_dir_path());
-
- return 0;
-}
-
-void cleanup_subkind_drbd_disk(libxl__checkpoint_devices_state *cds)
-{
- return;
-}
-
-/*----- match(), setup() and teardown() -----*/
-
-/* callbacks */
-static void match_async_exec_cb(libxl__egc *egc,
- libxl__async_exec_state *aes,
- int rc, int status);
-
-/* implementations */
-
-static void match_async_exec(libxl__egc *egc, libxl__checkpoint_device *dev);
-
-static void drbd_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
- STATE_AO_GC(dev->cds->ao);
-
- match_async_exec(egc, dev);
-}
-
-static void match_async_exec(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
- int arraysize, nr = 0, rc;
- const libxl_device_disk *disk = dev->backend_dev;
- libxl__async_exec_state *aes = &dev->aodev.aes;
- libxl__remus_state *rs = dev->cds->concrete_data;
- STATE_AO_GC(dev->cds->ao);
-
- /* setup env & args */
- arraysize = 1;
- GCNEW_ARRAY(aes->env, arraysize);
- aes->env[nr++] = NULL;
- assert(nr <= arraysize);
-
- arraysize = 3;
- nr = 0;
- GCNEW_ARRAY(aes->args, arraysize);
- aes->args[nr++] = rs->drbd_probe_script;
- aes->args[nr++] = disk->pdev_path;
- aes->args[nr++] = NULL;
- assert(nr <= arraysize);
-
- aes->ao = dev->cds->ao;
- aes->what = GCSPRINTF("%s %s", aes->args[0], aes->args[1]);
- aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
- aes->callback = match_async_exec_cb;
- aes->stdfds[0] = -1;
- aes->stdfds[1] = -1;
- aes->stdfds[2] = -1;
-
- rc = libxl__async_exec_start(aes);
- if (rc)
- goto out;
-
- return;
-
-out:
- dev->aodev.rc = rc;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-static void match_async_exec_cb(libxl__egc *egc,
- libxl__async_exec_state *aes,
- int rc, int status)
-{
- libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
- libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
- libxl__remus_drbd_disk *drbd_disk;
- const libxl_device_disk *disk = dev->backend_dev;
-
- STATE_AO_GC(aodev->ao);
-
- if (rc)
- goto out;
-
- if (status) {
- rc = ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH;
- /* BUG: seems to assume that any exit status means `no match' */
- /* BUG: exit status will have been logged as an error */
- goto out;
- }
-
- /* ops matched */
- dev->matched = true;
-
- GCNEW(drbd_disk);
- dev->concrete_data = drbd_disk;
- drbd_disk->ackwait = 0;
- drbd_disk->ctl_fd = open(disk->pdev_path, O_RDONLY);
- if (drbd_disk->ctl_fd < 0) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-
-out:
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-static void drbd_teardown(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
- libxl__remus_drbd_disk *drbd_disk = dev->concrete_data;
- STATE_AO_GC(dev->cds->ao);
-
- close(drbd_disk->ctl_fd);
- dev->aodev.rc = 0;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-/*----- checkpointing APIs -----*/
-
-/* callbacks */
-static void checkpoint_async_call_done(libxl__egc *egc,
- libxl__ev_child *child,
- pid_t pid, int status);
-
-/* API implementations */
-
-/* this op will not wait and block, so implement as sync op */
-static void drbd_postsuspend(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
- STATE_AO_GC(dev->cds->ao);
-
- libxl__remus_drbd_disk *rdd = dev->concrete_data;
-
- if (!rdd->ackwait) {
- if (ioctl(rdd->ctl_fd, DRBD_SEND_CHECKPOINT, 0) <= 0)
- rdd->ackwait = 1;
- }
-
- dev->aodev.rc = 0;
- dev->aodev.callback(egc, &dev->aodev);
-}
-
-
-static void drbd_preresume_async(libxl__checkpoint_device *dev);
-
-static void drbd_preresume(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
- ASYNC_CALL(egc, dev->cds->ao, &dev->aodev.child, dev,
- drbd_preresume_async,
- checkpoint_async_call_done);
-}
-
-static void drbd_preresume_async(libxl__checkpoint_device *dev)
-{
- libxl__remus_drbd_disk *rdd = dev->concrete_data;
- int ackwait = rdd->ackwait;
-
- if (ackwait) {
- ioctl(rdd->ctl_fd, DRBD_WAIT_CHECKPOINT_ACK, 0);
- ackwait = 0;
- }
-
- _exit(ackwait);
-}
-
-static void checkpoint_async_call_done(libxl__egc *egc,
- libxl__ev_child *child,
- pid_t pid, int status)
-{
- int rc;
- libxl__ao_device *aodev = CONTAINER_OF(child, *aodev, child);
- libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
- libxl__remus_drbd_disk *rdd = dev->concrete_data;
-
- STATE_AO_GC(aodev->ao);
-
- if (!WIFEXITED(status)) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- rdd->ackwait = WEXITSTATUS(status);
- rc = 0;
-
-out:
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-const libxl__checkpoint_device_instance_ops remus_device_drbd_disk = {
- .kind = LIBXL__DEVICE_KIND_VBD,
- .setup = drbd_setup,
- .teardown = drbd_teardown,
- .postsuspend = drbd_postsuspend,
- .preresume = drbd_preresume,
-};
+++ /dev/null
-/*
- * Copyright (C) 2012 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-/* stream_fd is as from the caller (eventually, the application).
- * It may be 0, 1 or 2, in which case we need to dup it elsewhere.
- * The actual fd value is not included in the supplied argnums; rather
- * it will be automatically supplied by run_helper as the 2nd argument.
- *
- * preserve_fds are fds that the caller is intending to pass to the
- * helper so which need cloexec clearing. They may not be 0, 1 or 2.
- * An entry may be -1 in which case it will be ignored.
- */
-static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs,
- const char *mode_arg,
- int stream_fd, int back_channel_fd,
- const int *preserve_fds, int num_preserve_fds,
- const unsigned long *argnums, int num_argnums);
-
-static void helper_failed(libxl__egc*, libxl__save_helper_state *shs, int rc);
-static void helper_stop(libxl__egc *egc, libxl__ao_abortable*, int rc);
-static void helper_stdout_readable(libxl__egc *egc, libxl__ev_fd *ev,
- int fd, short events, short revents);
-static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
- pid_t pid, int status);
-static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs);
-
-/*----- entrypoints -----*/
-
-void libxl__xc_domain_restore(libxl__egc *egc, libxl__domain_create_state *dcs,
- libxl__save_helper_state *shs)
-{
- STATE_AO_GC(dcs->ao);
-
- /* Convenience aliases */
- const uint32_t domid = dcs->guest_domid;
- const int restore_fd = dcs->libxc_fd;
- const int send_back_fd = dcs->send_back_fd;
- libxl__domain_build_state *const state = &dcs->build_state;
-
- unsigned cbflags =
- libxl__srm_callout_enumcallbacks_restore(&shs->callbacks.restore.a);
-
- const unsigned long argnums[] = {
- domid,
- state->store_port,
- state->store_domid, state->console_port,
- state->console_domid,
- cbflags, dcs->restore_params.checkpointed_stream,
- };
-
- shs->ao = ao;
- shs->domid = domid;
- shs->recv_callback = libxl__srm_callout_received_restore;
- if (dcs->restore_params.checkpointed_stream ==
- LIBXL_CHECKPOINTED_STREAM_COLO)
- shs->completion_callback = libxl__colo_restore_teardown;
- else
- shs->completion_callback = libxl__xc_domain_restore_done;
- shs->caller_state = dcs;
- shs->need_results = 1;
-
- run_helper(egc, shs, "--restore-domain", restore_fd, send_back_fd, 0, 0,
- argnums, ARRAY_SIZE(argnums));
-}
-
-void libxl__xc_domain_save(libxl__egc *egc, libxl__domain_save_state *dss,
- libxl__save_helper_state *shs)
-{
- STATE_AO_GC(dss->ao);
-
- unsigned cbflags =
- libxl__srm_callout_enumcallbacks_save(&shs->callbacks.save.a);
-
- const unsigned long argnums[] = {
- dss->domid, dss->xcflags, cbflags,
- dss->checkpointed_stream,
- };
-
- shs->ao = ao;
- shs->domid = dss->domid;
- shs->recv_callback = libxl__srm_callout_received_save;
- shs->completion_callback = libxl__xc_domain_save_done;
- shs->caller_state = dss;
- shs->need_results = 0;
-
- run_helper(egc, shs, "--save-domain", dss->fd, dss->recv_fd,
- NULL, 0,
- argnums, ARRAY_SIZE(argnums));
- return;
-}
-
-
-void libxl__xc_domain_saverestore_async_callback_done(libxl__egc *egc,
- libxl__save_helper_state *shs, int return_value)
-{
- shs->egc = egc;
- libxl__srm_callout_sendreply(return_value, shs);
- shs->egc = 0;
-}
-
-void libxl__save_helper_init(libxl__save_helper_state *shs)
-{
- libxl__ao_abortable_init(&shs->abrt);
- libxl__ev_fd_init(&shs->readable);
- libxl__ev_child_init(&shs->child);
-}
-
-/*----- helper execution -----*/
-
-/* This function can not fail. */
-static int dup_cloexec(libxl__gc *gc, int fd, const char *what)
-{
- int dup_fd = fd;
-
- if (fd <= 2) {
- dup_fd = dup(fd);
- if (dup_fd < 0) {
- LOGE(ERROR,"dup %s", what);
- exit(-1);
- }
- }
- libxl_fd_set_cloexec(CTX, dup_fd, 0);
-
- return dup_fd;
-}
-
-/*
- * Both save and restore share four parameters:
- * 1) Path to libxl-save-helper.
- * 2) --[restore|save]-domain.
- * 3) stream file descriptor.
- * 4) back channel file descriptor.
- * n) save/restore specific parameters.
- * 5) A \0 at the end.
- */
-#define HELPER_NR_ARGS 5
-static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs,
- const char *mode_arg,
- int stream_fd, int back_channel_fd,
- const int *preserve_fds, int num_preserve_fds,
- const unsigned long *argnums, int num_argnums)
-{
- STATE_AO_GC(shs->ao);
- const char *args[HELPER_NR_ARGS + num_argnums];
- const char **arg = args;
- int i, rc;
-
- /* Resources we must free */
- libxl__carefd *childs_pipes[2] = { 0,0 };
-
- /* Convenience aliases */
- const uint32_t domid = shs->domid;
-
- shs->rc = 0;
- shs->completed = 0;
- shs->pipes[0] = shs->pipes[1] = 0;
- libxl__save_helper_init(shs);
-
- shs->abrt.ao = shs->ao;
- shs->abrt.callback = helper_stop;
- rc = libxl__ao_abortable_register(&shs->abrt);
- if (rc) goto out;
-
- shs->stdin_what = GCSPRINTF("domain %"PRIu32" save/restore helper"
- " stdin pipe", domid);
- shs->stdout_what = GCSPRINTF("domain %"PRIu32" save/restore helper"
- " stdout pipe", domid);
-
- *arg++ = getenv("LIBXL_SAVE_HELPER") ?: LIBEXEC_BIN "/" "libxl-save-helper";
- *arg++ = mode_arg;
- const char **stream_fd_arg = arg++;
- const char **back_channel_fd_arg = arg++;
- for (i=0; i<num_argnums; i++)
- *arg++ = GCSPRINTF("%lu", argnums[i]);
- *arg++ = 0;
- assert(arg == args + ARRAY_SIZE(args));
-
- libxl__carefd_begin();
- int childfd;
- for (childfd=0; childfd<2; childfd++) {
- /* Setting up the pipe for the child's fd childfd */
- int fds[2];
- if (libxl_pipe(CTX,fds)) {
- rc = ERROR_FAIL;
- libxl__carefd_unlock();
- goto out;
- }
- int childs_end = childfd==0 ? 0 /*read*/ : 1 /*write*/;
- int our_end = childfd==0 ? 1 /*write*/ : 0 /*read*/;
- childs_pipes[childfd] = libxl__carefd_record(CTX, fds[childs_end]);
- shs->pipes[childfd] = libxl__carefd_record(CTX, fds[our_end]);
- }
- libxl__carefd_unlock();
-
- pid_t pid = libxl__ev_child_fork(gc, &shs->child, helper_exited);
- if (!pid) {
- stream_fd = dup_cloexec(gc, stream_fd, "migration stream fd");
- *stream_fd_arg = GCSPRINTF("%d", stream_fd);
-
- if (back_channel_fd >= 0)
- back_channel_fd = dup_cloexec(gc, back_channel_fd,
- "migration back channel fd");
- *back_channel_fd_arg = GCSPRINTF("%d", back_channel_fd);
-
- for (i=0; i<num_preserve_fds; i++)
- if (preserve_fds[i] >= 0) {
- assert(preserve_fds[i] > 2);
- libxl_fd_set_cloexec(CTX, preserve_fds[i], 0);
- }
-
- libxl__exec(gc,
- libxl__carefd_fd(childs_pipes[0]),
- libxl__carefd_fd(childs_pipes[1]),
- -1,
- args[0], (char**)args, 0);
- }
-
- libxl__carefd_close(childs_pipes[0]);
- libxl__carefd_close(childs_pipes[1]);
-
- rc = libxl__ev_fd_register(gc, &shs->readable, helper_stdout_readable,
- libxl__carefd_fd(shs->pipes[1]), POLLIN|POLLPRI);
- if (rc) goto out;
- return;
-
- out:
- libxl__carefd_close(childs_pipes[0]);
- libxl__carefd_close(childs_pipes[1]);
- helper_failed(egc, shs, rc);;
-}
-
-static void helper_failed(libxl__egc *egc, libxl__save_helper_state *shs,
- int rc)
-{
- STATE_AO_GC(shs->ao);
-
- if (!shs->rc)
- shs->rc = rc;
-
- libxl__ev_fd_deregister(gc, &shs->readable);
-
- if (!libxl__save_helper_inuse(shs)) {
- helper_done(egc, shs);
- return;
- }
-
- libxl__kill(gc, shs->child.pid, SIGKILL, "save/restore helper");
-}
-
-static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
-{
- libxl__save_helper_state *shs = CONTAINER_OF(abrt, *shs, abrt);
- STATE_AO_GC(shs->ao);
-
- if (!libxl__save_helper_inuse(shs)) {
- helper_failed(egc, shs, rc);
- return;
- }
-
- if (!shs->rc)
- shs->rc = rc;
-
- libxl__kill(gc, shs->child.pid, SIGTERM, "save/restore helper");
-}
-
-void libxl__save_helper_abort(libxl__egc *egc,
- libxl__save_helper_state *shs)
-{
- helper_stop(egc, &shs->abrt, ERROR_FAIL);
-}
-
-static void helper_stdout_readable(libxl__egc *egc, libxl__ev_fd *ev,
- int fd, short events, short revents)
-{
- libxl__save_helper_state *shs = CONTAINER_OF(ev, *shs, readable);
- STATE_AO_GC(shs->ao);
- int rc, errnoval;
-
- if (revents & (POLLERR|POLLPRI)) {
- LOGD(ERROR, shs->domid, "%s signaled POLLERR|POLLPRI (%#x)",
- shs->stdout_what, revents);
- rc = ERROR_FAIL;
- out:
- /* this is here because otherwise we bypass the decl of msg[] */
- helper_failed(egc, shs, rc);
- return;
- }
-
- uint16_t msglen;
- errnoval = libxl_read_exactly(CTX, fd, &msglen, sizeof(msglen),
- shs->stdout_what, "ipc msg header");
- if (errnoval) { rc = ERROR_FAIL; goto out; }
-
- unsigned char msg[msglen];
- errnoval = libxl_read_exactly(CTX, fd, msg, msglen,
- shs->stdout_what, "ipc msg body");
- if (errnoval) { rc = ERROR_FAIL; goto out; }
-
- shs->egc = egc;
- shs->recv_callback(msg, msglen, shs);
- shs->egc = 0;
- return;
-}
-
-static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
- pid_t pid, int status)
-{
- libxl__save_helper_state *shs = CONTAINER_OF(ch, *shs, child);
- STATE_AO_GC(shs->ao);
-
- /* Convenience aliases */
- const uint32_t domid = shs->domid;
-
- const char *what =
- GCSPRINTF("domain %"PRIu32" save/restore helper", domid);
-
- if (status) {
- libxl_report_child_exitstatus(CTX, XTL_ERROR, what, pid, status);
- if (!shs->rc)
- shs->rc = ERROR_FAIL;
- }
-
- if (shs->need_results) {
- if (!shs->rc) {
- LOGD(ERROR,shs->domid,"%s exited without providing results",what);
- shs->rc = ERROR_FAIL;
- }
- }
-
- if (!shs->completed) {
- if (!shs->rc) {
- LOGD(ERROR,shs->domid,"%s exited without signaling completion",what);
- shs->rc = ERROR_FAIL;
- }
- }
-
- helper_done(egc, shs);
- return;
-}
-
-static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs)
-{
- STATE_AO_GC(shs->ao);
-
- libxl__ao_abortable_deregister(&shs->abrt);
- libxl__ev_fd_deregister(gc, &shs->readable);
- libxl__carefd_close(shs->pipes[0]); shs->pipes[0] = 0;
- libxl__carefd_close(shs->pipes[1]); shs->pipes[1] = 0;
- assert(!libxl__save_helper_inuse(shs));
-
- shs->egc = egc;
- shs->completion_callback(egc, shs->caller_state,
- shs->rc, shs->retval, shs->errnoval);
- shs->egc = 0;
-}
-
-/*----- generic helpers for the autogenerated code -----*/
-
-const libxl__srm_save_autogen_callbacks*
-libxl__srm_callout_get_callbacks_save(void *user)
-{
- libxl__save_helper_state *shs = user;
- return &shs->callbacks.save.a;
-}
-
-const libxl__srm_restore_autogen_callbacks*
-libxl__srm_callout_get_callbacks_restore(void *user)
-{
- libxl__save_helper_state *shs = user;
- return &shs->callbacks.restore.a;
-}
-
-void libxl__srm_callout_sendreply(int r, void *user)
-{
- libxl__save_helper_state *shs = user;
- libxl__egc *egc = shs->egc;
- STATE_AO_GC(shs->ao);
- int errnoval;
-
- errnoval = libxl_write_exactly(CTX, libxl__carefd_fd(shs->pipes[0]),
- &r, sizeof(r), shs->stdin_what,
- "callback return value");
- if (errnoval)
- helper_failed(egc, shs, ERROR_FAIL);
-}
-
-void libxl__srm_callout_callback_log(uint32_t level, uint32_t errnoval,
- const char *context, const char *formatted, void *user)
-{
- libxl__save_helper_state *shs = user;
- STATE_AO_GC(shs->ao);
- xtl_log(CTX->lg, level, errnoval, context, "%s", formatted);
-}
-
-void libxl__srm_callout_callback_progress(const char *context,
- const char *doing_what, unsigned long done,
- unsigned long total, void *user)
-{
- libxl__save_helper_state *shs = user;
- STATE_AO_GC(shs->ao);
- xtl_progress(CTX->lg, context, doing_what, done, total);
-}
-
-int libxl__srm_callout_callback_complete(int retval, int errnoval,
- void *user)
-{
- libxl__save_helper_state *shs = user;
- STATE_AO_GC(shs->ao);
-
- shs->completed = 1;
- shs->retval = retval;
- shs->errnoval = errnoval;
- libxl__ev_fd_deregister(gc, &shs->readable);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) 2012 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-/*
- * The libxl-save-helper utility speaks a protocol to its caller for
- * the callbacks. The protocol is as follows.
- *
- * The helper talks on stdin and stdout, in binary in machine
- * endianness. The helper speaks first, and only when it has a
- * callback to make. It writes a 16-bit number being the message
- * length, and then the message body.
- *
- * Each message starts with a 16-bit number indicating which of the
- * messages it is, and then some arguments in a binary marshalled form.
- * If the callback does not need a reply (it returns void), the helper
- * just continues. Otherwise the helper waits for its caller to send a
- * single int which is to be the return value from the callback.
- *
- * Where feasible the stubs and callbacks have prototypes identical to
- * those required by xc_domain_save and xc_domain_restore, so that the
- * autogenerated functions can be used/provided directly.
- *
- * The actual messages are in the array @msgs in libxl_save_msgs_gen.pl
- */
-
-#include "libxl_osdeps.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <assert.h>
-#include <inttypes.h>
-#include <fcntl.h>
-#include <signal.h>
-
-#include "libxl.h"
-#include "libxl_utils.h"
-
-#include "xenctrl.h"
-#include "xenguest.h"
-#include "_libxl_save_msgs_helper.h"
-
-/*----- logger -----*/
-
-__attribute__((format(printf, 5, 0)))
-static void tellparent_vmessage(xentoollog_logger *logger_in,
- xentoollog_level level,
- int errnoval,
- const char *context,
- const char *format,
- va_list al)
-{
- char *formatted;
- int r = vasprintf(&formatted, format, al);
- if (r < 0) { perror("memory allocation failed during logging"); exit(-1); }
- helper_stub_log(level, errnoval, context, formatted, 0);
- free(formatted);
-}
-
-static void tellparent_progress(struct xentoollog_logger *logger_in,
- const char *context,
- const char *doing_what, int percent,
- unsigned long done, unsigned long total)
-{
- helper_stub_progress(context, doing_what, done, total, 0);
-}
-
-static void tellparent_destroy(struct xentoollog_logger *logger_in)
-{
- abort();
-}
-
-/*----- globals -----*/
-
-static const char *program = "libxl-save-helper";
-static xentoollog_logger logger = {
- tellparent_vmessage,
- tellparent_progress,
- tellparent_destroy,
-};
-static xc_interface *xch;
-static int io_fd;
-
-/*----- error handling -----*/
-
-static void fail(int errnoval, const char *fmt, ...)
- __attribute__((noreturn,format(printf,2,3)));
-static void fail(int errnoval, const char *fmt, ...)
-{
- va_list al;
- va_start(al,fmt);
- xtl_logv(&logger,XTL_ERROR,errnoval,program,fmt,al);
- exit(-1);
-}
-
-static int read_exactly(int fd, void *buf, size_t len)
-/* returns 0 if we get eof, even if we got it midway through; 1 if ok */
-{
- while (len) {
- ssize_t r = read(fd, buf, len);
- if (r<=0) return r;
- assert(r <= len);
- len -= r;
- buf = (char*)buf + r;
- }
- return 1;
-}
-
-static void *xmalloc(size_t sz)
-{
- if (!sz) return 0;
- void *r = malloc(sz);
- if (!r) { perror("memory allocation failed"); exit(-1); }
- return r;
-}
-
-/*----- signal handling -----*/
-
-static int unwriteable_fd;
-
-static void save_signal_handler(int num)
-{
- /*
- * We want to be able to interrupt save. But the code in libxc
- * which does the actual saving is straight-through, and we need
- * to execute its error path to put the guest back to sanity.
- *
- * So what we do is this: when we get the signal, we dup2
- * the result of open("/dev/null",O_RDONLY) onto the output fd.
- *
- * This is guaranteed to 1. interrupt libxc's write (causing it to
- * return short, or maybe EINTR); 2. make the next write give
- * EBADF, so that: 3. at latest, libxc will notice when it next
- * tries to write data and will then go into its cleanup path.
- *
- * We make no effort here to sanitise the resulting errors.
- * That's libxl's job.
- */
- int esave = errno;
-
- int r = dup2(unwriteable_fd, io_fd);
- if (r != io_fd)
- /* we can't write an xtl message because we might end up
- * interleaving on our control stream; we can't use stdio
- * because it's not async-signal-safe */
- abort();
-
- errno = esave;
-}
-
-static void setup_signals(void (*handler)(int))
-{
- struct sigaction sa;
- sigset_t spmask;
- int r;
-
- unwriteable_fd = open("/dev/null",O_RDONLY);
- if (unwriteable_fd < 0) fail(errno,"open /dev/null for reading");
-
- LIBXL_FILLZERO(sa);
- sa.sa_handler = handler;
- sigemptyset(&sa.sa_mask);
- r = sigaction(SIGTERM, &sa, 0);
- if (r) fail(errno,"sigaction SIGTERM failed");
-
- sigemptyset(&spmask);
- sigaddset(&spmask,SIGTERM);
- r = sigprocmask(SIG_UNBLOCK,&spmask,0);
- if (r) fail(errno,"sigprocmask unblock SIGTERM failed");
-}
-
-/*----- helper functions called by autogenerated stubs -----*/
-
-unsigned char * helper_allocbuf(int len, void *user)
-{
- return xmalloc(len);
-}
-
-static void transmit(const unsigned char *msg, int len, void *user)
-{
- while (len) {
- int r = write(1, msg, len);
- if (r<0) { perror("write"); exit(-1); }
- assert(r >= 0);
- assert(r <= len);
- len -= r;
- msg += r;
- }
-}
-
-void helper_transmitmsg(unsigned char *msg_freed, int len_in, void *user)
-{
- assert(len_in < 64*1024);
- uint16_t len = len_in;
- transmit((const void*)&len, sizeof(len), user);
- transmit(msg_freed, len, user);
- free(msg_freed);
-}
-
-int helper_getreply(void *user)
-{
- int v;
- int r = read_exactly(0, &v, sizeof(v));
- if (r<=0) exit(-2);
- return v;
-}
-
-/*----- other callbacks -----*/
-
-static void startup(const char *op) {
- xtl_log(&logger,XTL_DEBUG,0,program,"starting %s",op);
-
- xch = xc_interface_open(&logger,&logger,0);
- if (!xch) fail(errno,"xc_interface_open failed");
-}
-
-static void complete(int retval) {
- int errnoval = retval ? errno : 0; /* suppress irrelevant errnos */
- xtl_log(&logger,XTL_DEBUG,errnoval,program,"complete r=%d",retval);
- helper_stub_complete(retval,errnoval,0);
- xc_interface_close(xch);
- exit(0);
-}
-
-int main(int argc, char **argv)
-{
- int r;
- int send_back_fd, recv_fd;
-
-#define NEXTARG (++argv, assert(*argv), *argv)
-
- const char *mode = *++argv;
- assert(mode);
-
- if (!strcmp(mode,"--save-domain")) {
- static struct save_callbacks cb;
-
- io_fd = atoi(NEXTARG);
- recv_fd = atoi(NEXTARG);
- uint32_t dom = strtoul(NEXTARG,0,10);
- uint32_t flags = strtoul(NEXTARG,0,10);
- unsigned cbflags = strtoul(NEXTARG,0,10);
- xc_stream_type_t stream_type = strtoul(NEXTARG,0,10);
- assert(!*++argv);
-
- helper_setcallbacks_save(&cb, cbflags);
-
- startup("save");
- setup_signals(save_signal_handler);
-
- r = xc_domain_save(xch, io_fd, dom, flags, &cb, stream_type, recv_fd);
- complete(r);
-
- } else if (!strcmp(mode,"--restore-domain")) {
- static struct restore_callbacks cb;
-
- io_fd = atoi(NEXTARG);
- send_back_fd = atoi(NEXTARG);
- uint32_t dom = strtoul(NEXTARG,0,10);
- unsigned store_evtchn = strtoul(NEXTARG,0,10);
- domid_t store_domid = strtoul(NEXTARG,0,10);
- unsigned console_evtchn = strtoul(NEXTARG,0,10);
- domid_t console_domid = strtoul(NEXTARG,0,10);
- unsigned cbflags = strtoul(NEXTARG,0,10);
- xc_stream_type_t stream_type = strtoul(NEXTARG,0,10);
- assert(!*++argv);
-
- helper_setcallbacks_restore(&cb, cbflags);
-
- unsigned long store_mfn = 0;
- unsigned long console_mfn = 0;
-
- startup("restore");
- setup_signals(SIG_DFL);
-
- r = xc_domain_restore(xch, io_fd, dom, store_evtchn, &store_mfn,
- store_domid, console_evtchn, &console_mfn,
- console_domid, stream_type, &cb, send_back_fd);
- helper_stub_restore_results(store_mfn,console_mfn,0);
- complete(r);
-
- } else {
- assert(!"unexpected mode argument");
- }
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-#!/usr/bin/perl -w
-
-use warnings;
-use strict;
-use POSIX;
-
-our $debug = 0; # produce copious debugging output at run-time?
-
-our @msgs = (
- # flags:
- # s - applicable to save
- # r - applicable to restore
- # c - function pointer in callbacks struct rather than fixed function
- # x - function pointer is in struct {save,restore}_callbacks
- # and its null-ness needs to be passed through to the helper's xc
- # W - needs a return value; callback is synchronous
- # A - needs a return value; callback is asynchronous
- [ 'sr', "log", [qw(uint32_t level
- uint32_t errnoval
- STRING context
- STRING formatted)] ],
- [ 'sr', "progress", [qw(STRING context
- STRING doing_what),
- 'unsigned long', 'done',
- 'unsigned long', 'total'] ],
- [ 'srcxA', "suspend", [] ],
- [ 'srcxA', "postcopy", [] ],
- [ 'srcxA', "checkpoint", [] ],
- [ 'srcxA', "wait_checkpoint", [] ],
- [ 'scxA', "switch_qemu_logdirty", [qw(uint32_t domid
- unsigned enable)] ],
- [ 'rcxW', "static_data_done", [qw(unsigned missing)] ],
- [ 'rcx', "restore_results", ['xen_pfn_t', 'store_gfn',
- 'xen_pfn_t', 'console_gfn'] ],
- [ 'srW', "complete", [qw(int retval
- int errnoval)] ],
-);
-
-#----------------------------------------
-
-our %cbs;
-our %func;
-our %func_ah;
-our @outfuncs;
-our %out_decls;
-our %out_body;
-our $msgnum = 0;
-
-die unless @ARGV==1;
-die if $ARGV[0] =~ m/^-/;
-
-our ($intendedout) = @ARGV;
-
-$intendedout =~ m/([a-z]+)\.([ch])$/ or die;
-my ($want_ah, $ch) = ($1, $2);
-
-my $declprefix = '';
-
-foreach my $ah (qw(callout helper)) {
- $out_body{$ah} .=
- <<END_BOTH.($ah eq 'callout' ? <<END_CALLOUT : <<END_HELPER);
-#include "libxl_osdeps.h"
-
-#include <assert.h>
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-END_BOTH
-
-#include "libxl_internal.h"
-
-END_CALLOUT
-
-#include <xenctrl.h>
-#include <xenguest.h>
-#include "_libxl_save_msgs_${ah}.h"
-
-END_HELPER
-}
-
-die $want_ah unless defined $out_body{$want_ah};
-
-sub f_decl ($$$$) {
- my ($name, $ah, $c_rtype, $c_decl) = @_;
- $out_decls{$name} = "${declprefix}$c_rtype $name$c_decl;\n";
- $func{$name} = "$c_rtype $name$c_decl\n{\n" . ($func{$name} || '');
- $func_ah{$name} = $ah;
-}
-
-sub f_more ($$) {
- my ($name, $addbody) = @_;
- $func{$name} ||= '';
- $func{$name} .= $addbody;
- push @outfuncs, $name;
-}
-
-our $libxl = "libxl__srm";
-our $callback = "${libxl}_callout_callback";
-our $receiveds = "${libxl}_callout_received";
-our $sendreply = "${libxl}_callout_sendreply";
-our $getcallbacks = "${libxl}_callout_get_callbacks";
-our $enumcallbacks = "${libxl}_callout_enumcallbacks";
-sub cbtype ($) { "${libxl}_".$_[0]."_autogen_callbacks"; };
-
-f_decl($sendreply, 'callout', 'void', "(int r, void *user)");
-
-our $helper = "helper";
-our $encode = "${helper}_stub";
-our $allocbuf = "${helper}_allocbuf";
-our $transmit = "${helper}_transmitmsg";
-our $getreply = "${helper}_getreply";
-our $setcallbacks = "${helper}_setcallbacks";
-
-f_decl($allocbuf, 'helper', 'unsigned char *', '(int len, void *user)');
-f_decl($transmit, 'helper', 'void',
- '(unsigned char *msg_freed, int len, void *user)');
-f_decl($getreply, 'helper', 'int', '(void *user)');
-
-sub typeid ($) { my ($t) = @_; $t =~ s/\W/_/; return $t; };
-
-$out_body{'callout'} .= <<END;
-static int bytes_get(const unsigned char **msg,
- const unsigned char *const endmsg,
- void *result, int rlen)
-{
- if (endmsg - *msg < rlen) return 0;
- memcpy(result, *msg, rlen);
- *msg += rlen;
- return 1;
-}
-
-END
-$out_body{'helper'} .= <<END;
-static void bytes_put(unsigned char *const buf, int *len,
- const void *value, int vlen)
-{
- assert(vlen < INT_MAX/2 - *len);
- if (buf)
- memcpy(buf + *len, value, vlen);
- *len += vlen;
-}
-
-END
-
-foreach my $simpletype (qw(int uint16_t uint32_t unsigned), 'unsigned long', 'xen_pfn_t') {
- my $typeid = typeid($simpletype);
- $out_body{'callout'} .= <<END;
-static int ${typeid}_get(const unsigned char **msg,
- const unsigned char *const endmsg,
- $simpletype *result)
-{
- return bytes_get(msg, endmsg, result, sizeof(*result));
-}
-
-END
- $out_body{'helper'} .= <<END;
-static void ${typeid}_put(unsigned char *const buf, int *len,
- const $simpletype value)
-{
- bytes_put(buf, len, &value, sizeof(value));
-}
-
-END
-}
-
-$out_body{'callout'} .= <<END;
-static int BLOCK_get(const unsigned char **msg,
- const unsigned char *const endmsg,
- const uint8_t **result, uint32_t *result_size)
-{
- if (!uint32_t_get(msg, endmsg, result_size)) return 0;
- if (endmsg - *msg < *result_size) return 0;
- *result = (const void*)*msg;
- *msg += *result_size;
- return 1;
-}
-
-static int STRING_get(const unsigned char **msg,
- const unsigned char *const endmsg,
- const char **result)
-{
- const uint8_t *data;
- uint32_t datalen;
- if (!BLOCK_get(msg, endmsg, &data, &datalen)) return 0;
- if (datalen == 0) return 0;
- if (data[datalen-1] != '\\0') return 0;
- *result = (const void*)data;
- return 1;
-}
-
-END
-$out_body{'helper'} .= <<END;
-static void BLOCK_put(unsigned char *const buf,
- int *len,
- const uint8_t *bytes, uint32_t size)
-{
- uint32_t_put(buf, len, size);
- bytes_put(buf, len, bytes, size);
-}
-
-static void STRING_put(unsigned char *const buf,
- int *len,
- const char *string)
-{
- size_t slen = strlen(string);
- assert(slen < INT_MAX / 4);
- assert(slen < (uint32_t)0x40000000);
- BLOCK_put(buf, len, (const void*)string, slen+1);
-}
-
-END
-
-foreach my $sr (qw(save restore)) {
- f_decl("${getcallbacks}_${sr}", 'callout',
- "const ".cbtype($sr)." *",
- "(void *data)");
-
- f_decl("${receiveds}_${sr}", 'callout', 'int',
- "(const unsigned char *msg, uint32_t len, void *user)");
-
- f_decl("${enumcallbacks}_${sr}", 'callout', 'unsigned',
- "(const ".cbtype($sr)." *cbs)");
- f_more("${enumcallbacks}_${sr}", " unsigned cbflags = 0;\n");
-
- f_decl("${setcallbacks}_${sr}", 'helper', 'void',
- "(struct ${sr}_callbacks *cbs, unsigned cbflags)");
-
- f_more("${receiveds}_${sr}",
- <<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS);
- const unsigned char *const endmsg = msg + len;
- uint16_t mtype;
- if (!uint16_t_get(&msg, endmsg, &mtype)) return 0;
-END_ALWAYS
- fprintf(stderr,"libxl callout receiver: got len=%u mtype=%u\\n",len,mtype);
-END_DEBUG
- switch (mtype) {
-
-END_ALWAYS
-
- $cbs{$sr} = "typedef struct ".cbtype($sr)." {\n";
-}
-
-foreach my $msginfo (@msgs) {
- my ($flags, $name, $args) = @$msginfo;
- $msgnum++;
-
- my $f_more_sr = sub {
- my ($contents_spec, $fnamebase) = @_;
- $fnamebase ||= "${receiveds}";
- foreach my $sr (qw(save restore)) {
- $sr =~ m/^./;
- next unless $flags =~ m/$&/;
- my $contents = (!ref $contents_spec) ? $contents_spec :
- $contents_spec->($sr);
- f_more("${fnamebase}_${sr}", $contents);
- }
- };
-
- $f_more_sr->(" case $msgnum: { /* $name */\n");
- if ($flags =~ m/W/) {
- $f_more_sr->(" int r;\n");
- }
-
- my $c_rtype_helper = $flags =~ m/[WA]/ ? 'int' : 'void';
- my $c_rtype_callout = $flags =~ m/W/ ? 'int' : 'void';
- my $c_decl = '(';
- my $c_callback_args = '';
-
- f_more("${encode}_$name",
- <<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS);
- unsigned char *buf = 0;
- int len = 0, allocd = 0;
-
-END_ALWAYS
- fprintf(stderr,"libxl-save-helper: encoding $name\\n");
-END_DEBUG
- for (;;) {
- uint16_t_put(buf, &len, $msgnum /* $name */);
-END_ALWAYS
-
- my @args = @$args;
- my $c_recv = '';
- my ($argtype, $arg);
- while (($argtype, $arg, @args) = @args) {
- my $typeid = typeid($argtype);
- my $c_args = "$arg";
- my $c_get_args = "&$arg";
- if ($argtype eq 'STRING') {
- $c_decl .= "const char *$arg, ";
- $f_more_sr->(" const char *$arg;\n");
- } elsif ($argtype eq 'BLOCK') {
- $c_decl .= "const uint8_t *$arg, uint32_t ${arg}_size, ";
- $c_args .= ", ${arg}_size";
- $c_get_args .= ", &${arg}_size";
- $f_more_sr->(" const uint8_t *$arg;\n".
- " uint32_t ${arg}_size;\n");
- } else {
- $c_decl .= "$argtype $arg, ";
- $f_more_sr->(" $argtype $arg;\n");
- }
- $c_callback_args .= "$c_args, ";
- $c_recv.=
- " if (!${typeid}_get(&msg, endmsg, $c_get_args)) return 0;\n";
- f_more("${encode}_$name", " ${typeid}_put(buf, &len, $c_args);\n");
- }
- $f_more_sr->($c_recv);
- $c_decl .= "void *user)";
- $c_callback_args .= "user";
-
- $f_more_sr->(" if (msg != endmsg) return 0;\n");
-
- my $c_callback;
- if ($flags !~ m/c/) {
- $c_callback = "${callback}_$name";
- } else {
- $f_more_sr->(sub {
- my ($sr) = @_;
- $cbs{$sr} .= " $c_rtype_callout (*${name})$c_decl;\n";
- return
- " const ".cbtype($sr)." *const cbs =\n".
- " ${getcallbacks}_${sr}(user);\n";
- });
- $c_callback = "cbs->${name}";
- }
- my $c_make_callback = "$c_callback($c_callback_args)";
- if ($flags !~ m/W/) {
- $f_more_sr->(" $c_make_callback;\n");
- } else {
- $f_more_sr->(" r = $c_make_callback;\n".
- " $sendreply(r, user);\n");
- f_decl($sendreply, 'callout', 'void', '(int r, void *user)');
- }
- if ($flags =~ m/x/) {
- my $c_v = "(1u<<$msgnum)";
- my $c_cb = "cbs->$name";
- $f_more_sr->(" if ($c_cb) cbflags |= $c_v;\n", $enumcallbacks);
- $f_more_sr->(" if (cbflags & $c_v) $c_cb = ${encode}_${name};\n",
- $setcallbacks);
- }
- $f_more_sr->(" return 1;\n }\n\n");
- f_decl("${callback}_$name", 'callout', $c_rtype_callout, $c_decl);
- f_decl("${encode}_$name", 'helper', $c_rtype_helper, $c_decl);
- f_more("${encode}_$name",
-" if (buf) break;
- buf = ${helper}_allocbuf(len, user);
- assert(buf);
- allocd = len;
- len = 0;
- }
- assert(len == allocd);
- ${transmit}(buf, len, user);
-");
- if ($flags =~ m/[WA]/) {
- f_more("${encode}_$name",
- (<<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS));
- int r = ${helper}_getreply(user);
-END_ALWAYS
- fprintf(stderr,"libxl-save-helper: $name got reply %d\\n",r);
-END_DEBUG
- return r;
-END_ALWAYS
- }
-}
-
-print "/* AUTOGENERATED by $0 DO NOT EDIT */\n\n" or die $!;
-
-foreach my $sr (qw(save restore)) {
- f_more("${enumcallbacks}_${sr}",
- " return cbflags;\n");
- f_more("${receiveds}_${sr}",
- " default:\n".
- " return 0;\n".
- " }\n");
- $cbs{$sr} .= "} ".cbtype($sr).";\n\n";
- if ($ch eq 'h') {
- print $cbs{$sr} or die $!;
- print "struct ${sr}_callbacks;\n";
- }
-}
-
-if ($ch eq 'c') {
- foreach my $name (@outfuncs) {
- next unless defined $func{$name};
- $func{$name} .= "}\n\n";
- $out_body{$func_ah{$name}} .= $func{$name};
- delete $func{$name};
- }
- print $out_body{$want_ah} or die $!;
-} else {
- foreach my $name (sort keys %out_decls) {
- next unless $func_ah{$name} eq $want_ah;
- print $out_decls{$name} or die $!;
- }
-}
-
-close STDOUT or die $!;
+++ /dev/null
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-static int libxl__set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid,
- uint32_t vcpuid,
- const libxl_bitmap *cpumap_hard,
- const libxl_bitmap *cpumap_soft,
- unsigned flags)
-{
- GC_INIT(ctx);
- libxl_bitmap hard, soft;
- int rc;
-
- libxl_bitmap_init(&hard);
- libxl_bitmap_init(&soft);
-
- if (!cpumap_hard && !cpumap_soft && !flags) {
- rc = ERROR_INVAL;
- goto out;
- }
-
- /*
- * Xen wants writable hard and/or soft cpumaps, to put back in them
- * the effective hard and/or soft affinity that will be used.
- */
- if (cpumap_hard) {
- rc = libxl_cpu_bitmap_alloc(ctx, &hard, 0);
- if (rc)
- goto out;
-
- libxl__bitmap_copy_best_effort(gc, &hard, cpumap_hard);
- flags |= XEN_VCPUAFFINITY_HARD;
- }
- if (cpumap_soft) {
- rc = libxl_cpu_bitmap_alloc(ctx, &soft, 0);
- if (rc)
- goto out;
-
- libxl__bitmap_copy_best_effort(gc, &soft, cpumap_soft);
- flags |= XEN_VCPUAFFINITY_SOFT;
- }
-
- if (xc_vcpu_setaffinity(ctx->xch, domid, vcpuid,
- cpumap_hard ? hard.map : NULL,
- cpumap_soft ? soft.map : NULL,
- flags)) {
- LOGED(ERROR, domid, "Setting vcpu affinity");
- rc = ERROR_FAIL;
- goto out;
- }
-
- /*
- * Let's check the results. Hard affinity will never be empty, but it
- * is possible that Xen will use something different from what we asked
- * for various reasons. If that's the case, report it.
- */
- if (cpumap_hard &&
- !libxl_bitmap_equal(cpumap_hard, &hard, 0))
- LOGD(DEBUG, domid, "New hard affinity for vcpu %d has unreachable cpus", vcpuid);
- /*
- * Soft affinity can both be different from what asked and empty. Check
- * for (and report) both.
- */
- if (cpumap_soft) {
- if (!libxl_bitmap_equal(cpumap_soft, &soft, 0))
- LOGD(DEBUG, domid, "New soft affinity for vcpu %d has unreachable cpus",
- vcpuid);
- if (libxl_bitmap_is_empty(&soft))
- LOGD(WARN, domid, "All cpus in soft affinity of vcpu %d are unreachable."
- " Only hard affinity will be considered for scheduling",
- vcpuid);
- }
-
- rc = 0;
- out:
- libxl_bitmap_dispose(&hard);
- libxl_bitmap_dispose(&soft);
- GC_FREE;
- return rc;
-}
-
-int libxl_set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid,
- const libxl_bitmap *cpumap_hard,
- const libxl_bitmap *cpumap_soft)
-{
- return libxl__set_vcpuaffinity(ctx, domid, vcpuid, cpumap_hard,
- cpumap_soft, 0);
-}
-
-int libxl_set_vcpuaffinity_force(libxl_ctx *ctx, uint32_t domid,
- uint32_t vcpuid,
- const libxl_bitmap *cpumap_hard,
- const libxl_bitmap *cpumap_soft)
-{
- return libxl__set_vcpuaffinity(ctx, domid, vcpuid, cpumap_hard,
- cpumap_soft, XEN_VCPUAFFINITY_FORCE);
-}
-
-int libxl_set_vcpuaffinity_all(libxl_ctx *ctx, uint32_t domid,
- unsigned int max_vcpus,
- const libxl_bitmap *cpumap_hard,
- const libxl_bitmap *cpumap_soft)
-{
- GC_INIT(ctx);
- int i, rc = 0;
-
- for (i = 0; i < max_vcpus; i++) {
- if (libxl_set_vcpuaffinity(ctx, domid, i, cpumap_hard, cpumap_soft)) {
- LOGD(WARN, domid, "Failed to set affinity for %d", i);
- rc = ERROR_FAIL;
- }
- }
-
- GC_FREE;
- return rc;
-}
-
-int libxl_domain_set_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
- libxl_bitmap *nodemap)
-{
- GC_INIT(ctx);
- if (xc_domain_node_setaffinity(ctx->xch, domid, nodemap->map)) {
- LOGED(ERROR, domid, "Setting node affinity");
- GC_FREE;
- return ERROR_FAIL;
- }
- GC_FREE;
- return 0;
-}
-
-int libxl_domain_get_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
- libxl_bitmap *nodemap)
-{
- GC_INIT(ctx);
- if (xc_domain_node_getaffinity(ctx->xch, domid, nodemap->map)) {
- LOGED(ERROR, domid, "Getting node affinity");
- GC_FREE;
- return ERROR_FAIL;
- }
- GC_FREE;
- return 0;
-}
-
-int libxl_get_scheduler(libxl_ctx *ctx)
-{
- int r, sched;
-
- GC_INIT(ctx);
- r = xc_sched_id(ctx->xch, &sched);
- if (r != 0) {
- LOGE(ERROR, "getting current scheduler id");
- sched = ERROR_FAIL;
- }
- GC_FREE;
- return sched;
-}
-
-static int sched_arinc653_domain_set(libxl__gc *gc, uint32_t domid,
- const libxl_domain_sched_params *scinfo)
-{
- /* Currently, the ARINC 653 scheduler does not take any domain-specific
- configuration, so we simply return success. */
- return 0;
-}
-
-static int sched_null_domain_set(libxl__gc *gc, uint32_t domid,
- const libxl_domain_sched_params *scinfo)
-{
- /* There aren't any domain-specific parameters to be set. */
- return 0;
-}
-
-static int sched_null_domain_get(libxl__gc *gc, uint32_t domid,
- libxl_domain_sched_params *scinfo)
-{
- /* There aren't any domain-specific parameters to return. */
- return 0;
-}
-
-static int sched_credit_domain_get(libxl__gc *gc, uint32_t domid,
- libxl_domain_sched_params *scinfo)
-{
- struct xen_domctl_sched_credit sdom;
- int rc;
-
- rc = xc_sched_credit_domain_get(CTX->xch, domid, &sdom);
- if (rc != 0) {
- LOGED(ERROR, domid, "Getting domain sched credit");
- return ERROR_FAIL;
- }
-
- libxl_domain_sched_params_init(scinfo);
- scinfo->sched = LIBXL_SCHEDULER_CREDIT;
- scinfo->weight = sdom.weight;
- scinfo->cap = sdom.cap;
-
- return 0;
-}
-
-static int sched_credit_domain_set(libxl__gc *gc, uint32_t domid,
- const libxl_domain_sched_params *scinfo)
-{
- struct xen_domctl_sched_credit sdom;
- xc_domaininfo_t domaininfo;
- int rc;
-
- rc = xc_domain_getinfolist(CTX->xch, domid, 1, &domaininfo);
- if (rc < 0) {
- LOGED(ERROR, domid, "Getting domain info list");
- return ERROR_FAIL;
- }
- if (rc != 1 || domaininfo.domain != domid)
- return ERROR_INVAL;
-
- rc = xc_sched_credit_domain_get(CTX->xch, domid, &sdom);
- if (rc != 0) {
- LOGED(ERROR, domid, "Getting domain sched credit");
- return ERROR_FAIL;
- }
-
- if (scinfo->weight != LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT) {
- if (scinfo->weight < 1 || scinfo->weight > 65535) {
- LOGD(ERROR, domid, "Cpu weight out of range, "
- "valid values are within range from 1 to 65535");
- return ERROR_INVAL;
- }
- sdom.weight = scinfo->weight;
- }
-
- if (scinfo->cap != LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT) {
- if (scinfo->cap < 0
- || scinfo->cap > (domaininfo.max_vcpu_id + 1) * 100) {
- LOGD(ERROR, domid, "Cpu cap out of range, "
- "valid range is from 0 to %d for specified number of vcpus",
- ((domaininfo.max_vcpu_id + 1) * 100));
- return ERROR_INVAL;
- }
- sdom.cap = scinfo->cap;
- }
-
- rc = xc_sched_credit_domain_set(CTX->xch, domid, &sdom);
- if ( rc < 0 ) {
- LOGED(ERROR, domid, "Setting domain sched credit");
- return ERROR_FAIL;
- }
-
- return 0;
-}
-
-static int sched_ratelimit_check(libxl__gc *gc, int ratelimit)
-{
- if (ratelimit != 0 &&
- (ratelimit < XEN_SYSCTL_SCHED_RATELIMIT_MIN ||
- ratelimit > XEN_SYSCTL_SCHED_RATELIMIT_MAX)) {
- LOG(ERROR, "Ratelimit out of range, valid range is from %d to %d",
- XEN_SYSCTL_SCHED_RATELIMIT_MIN, XEN_SYSCTL_SCHED_RATELIMIT_MAX);
- return ERROR_INVAL;
- }
-
- return 0;
-}
-
-int libxl_sched_credit_params_get(libxl_ctx *ctx, uint32_t poolid,
- libxl_sched_credit_params *scinfo)
-{
- struct xen_sysctl_credit_schedule sparam;
- int r, rc;
- GC_INIT(ctx);
-
- r = xc_sched_credit_params_get(ctx->xch, poolid, &sparam);
- if (r < 0) {
- LOGE(ERROR, "getting Credit scheduler parameters");
- rc = ERROR_FAIL;
- goto out;
- }
-
- scinfo->tslice_ms = sparam.tslice_ms;
- scinfo->ratelimit_us = sparam.ratelimit_us;
- scinfo->vcpu_migr_delay_us = sparam.vcpu_migr_delay_us;
-
- rc = 0;
- out:
- GC_FREE;
- return rc;
-}
-
-int libxl_sched_credit_params_set(libxl_ctx *ctx, uint32_t poolid,
- libxl_sched_credit_params *scinfo)
-{
- struct xen_sysctl_credit_schedule sparam;
- int r, rc;
- GC_INIT(ctx);
-
- if (scinfo->tslice_ms < XEN_SYSCTL_CSCHED_TSLICE_MIN
- || scinfo->tslice_ms > XEN_SYSCTL_CSCHED_TSLICE_MAX) {
- LOG(ERROR, "Time slice out of range, valid range is from %d to %d",
- XEN_SYSCTL_CSCHED_TSLICE_MIN, XEN_SYSCTL_CSCHED_TSLICE_MAX);
- rc = ERROR_INVAL;
- goto out;
- }
- rc = sched_ratelimit_check(gc, scinfo->ratelimit_us);
- if (rc) {
- goto out;
- }
- if (scinfo->ratelimit_us > scinfo->tslice_ms*1000) {
- LOG(ERROR, "Ratelimit cannot be greater than timeslice");
- rc = ERROR_INVAL;
- goto out;
- }
- if (scinfo->vcpu_migr_delay_us > XEN_SYSCTL_CSCHED_MGR_DLY_MAX_US) {
- LOG(ERROR, "vcpu migration delay should be >= 0 and <= %dus",
- XEN_SYSCTL_CSCHED_MGR_DLY_MAX_US);
- rc = ERROR_INVAL;
- goto out;
- }
-
- sparam.tslice_ms = scinfo->tslice_ms;
- sparam.ratelimit_us = scinfo->ratelimit_us;
- sparam.vcpu_migr_delay_us = scinfo->vcpu_migr_delay_us;
-
- r = xc_sched_credit_params_set(ctx->xch, poolid, &sparam);
- if ( r < 0 ) {
- LOGE(ERROR, "Setting Credit scheduler parameters");
- rc = ERROR_FAIL;
- goto out;
- }
-
- scinfo->tslice_ms = sparam.tslice_ms;
- scinfo->ratelimit_us = sparam.ratelimit_us;
- scinfo->vcpu_migr_delay_us = sparam.vcpu_migr_delay_us;
-
- rc = 0;
- out:
- GC_FREE;
- return rc;
-}
-
-int libxl_sched_credit2_params_get(libxl_ctx *ctx, uint32_t poolid,
- libxl_sched_credit2_params *scinfo)
-{
- struct xen_sysctl_credit2_schedule sparam;
- int r, rc;
- GC_INIT(ctx);
-
- r = xc_sched_credit2_params_get(ctx->xch, poolid, &sparam);
- if (r < 0) {
- LOGE(ERROR, "getting Credit2 scheduler parameters");
- rc = ERROR_FAIL;
- goto out;
- }
-
- scinfo->ratelimit_us = sparam.ratelimit_us;
-
- rc = 0;
- out:
- GC_FREE;
- return rc;
-}
-
-int libxl_sched_credit2_params_set(libxl_ctx *ctx, uint32_t poolid,
- libxl_sched_credit2_params *scinfo)
-{
- struct xen_sysctl_credit2_schedule sparam;
- int r, rc;
- GC_INIT(ctx);
-
- rc = sched_ratelimit_check(gc, scinfo->ratelimit_us);
- if (rc) goto out;
-
- sparam.ratelimit_us = scinfo->ratelimit_us;
-
- r = xc_sched_credit2_params_set(ctx->xch, poolid, &sparam);
- if (r < 0) {
- LOGE(ERROR, "Setting Credit2 scheduler parameters");
- rc = ERROR_FAIL;
- goto out;
- }
-
- scinfo->ratelimit_us = sparam.ratelimit_us;
-
- rc = 0;
- out:
- GC_FREE;
- return rc;
-}
-
-static int sched_credit2_domain_get(libxl__gc *gc, uint32_t domid,
- libxl_domain_sched_params *scinfo)
-{
- struct xen_domctl_sched_credit2 sdom;
- int rc;
-
- rc = xc_sched_credit2_domain_get(CTX->xch, domid, &sdom);
- if (rc != 0) {
- LOGED(ERROR, domid, "Getting domain sched credit2");
- return ERROR_FAIL;
- }
-
- libxl_domain_sched_params_init(scinfo);
- scinfo->sched = LIBXL_SCHEDULER_CREDIT2;
- scinfo->weight = sdom.weight;
- scinfo->cap = sdom.cap;
-
- return 0;
-}
-
-static int sched_credit2_domain_set(libxl__gc *gc, uint32_t domid,
- const libxl_domain_sched_params *scinfo)
-{
- struct xen_domctl_sched_credit2 sdom;
- xc_domaininfo_t info;
- int rc;
-
- rc = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
- if (rc < 0) {
- LOGED(ERROR, domid, "Getting domain info");
- return ERROR_FAIL;
- }
- if (rc != 1 || info.domain != domid)
- return ERROR_INVAL;
-
- rc = xc_sched_credit2_domain_get(CTX->xch, domid, &sdom);
- if (rc != 0) {
- LOGED(ERROR, domid, "Getting domain sched credit2");
- return ERROR_FAIL;
- }
-
- if (scinfo->weight != LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT) {
- if (scinfo->weight < 1 || scinfo->weight > 65535) {
- LOGD(ERROR, domid, "Cpu weight out of range, "
- "valid values are within range from 1 to 65535");
- return ERROR_INVAL;
- }
- sdom.weight = scinfo->weight;
- }
-
- if (scinfo->cap != LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT) {
- if (scinfo->cap < 0
- || scinfo->cap > (info.max_vcpu_id + 1) * 100) {
- LOGD(ERROR, domid, "Cpu cap out of range, "
- "valid range is from 0 to %d for specified number of vcpus",
- ((info.max_vcpu_id + 1) * 100));
- return ERROR_INVAL;
- }
- sdom.cap = scinfo->cap;
- }
-
- rc = xc_sched_credit2_domain_set(CTX->xch, domid, &sdom);
- if ( rc < 0 ) {
- LOGED(ERROR, domid, "Setting domain sched credit2");
- return ERROR_FAIL;
- }
-
- return 0;
-}
-
-static int sched_rtds_validate_params(libxl__gc *gc, int period, int budget)
-{
- int rc;
-
- if (period < 1) {
- LOG(ERROR, "Invalid VCPU period of %d (it should be >= 1)", period);
- rc = ERROR_INVAL;
- goto out;
- }
-
- if (budget < 1) {
- LOG(ERROR, "Invalid VCPU budget of %d (it should be >= 1)", budget);
- rc = ERROR_INVAL;
- goto out;
- }
-
- if (budget > period) {
- LOG(ERROR, "VCPU budget must be smaller than or equal to period, "
- "but %d > %d", budget, period);
- rc = ERROR_INVAL;
- goto out;
- }
- rc = 0;
-out:
- return rc;
-}
-
-/* Get the RTDS scheduling parameters of vcpu(s) */
-static int sched_rtds_vcpu_get(libxl__gc *gc, uint32_t domid,
- libxl_vcpu_sched_params *scinfo)
-{
- uint32_t num_vcpus;
- int i, r, rc;
- xc_dominfo_t info;
- struct xen_domctl_schedparam_vcpu *vcpus;
-
- r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
- if (r < 0) {
- LOGED(ERROR, domid, "Getting domain info");
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (scinfo->num_vcpus <= 0) {
- rc = ERROR_INVAL;
- goto out;
- } else {
- num_vcpus = scinfo->num_vcpus;
- GCNEW_ARRAY(vcpus, num_vcpus);
- for (i = 0; i < num_vcpus; i++) {
- if (scinfo->vcpus[i].vcpuid < 0 ||
- scinfo->vcpus[i].vcpuid > info.max_vcpu_id) {
- LOGD(ERROR, domid, "VCPU index is out of range, "
- "valid values are within range from 0 to %d",
- info.max_vcpu_id);
- rc = ERROR_INVAL;
- goto out;
- }
- vcpus[i].vcpuid = scinfo->vcpus[i].vcpuid;
- }
- }
-
- r = xc_sched_rtds_vcpu_get(CTX->xch, domid, vcpus, num_vcpus);
- if (r != 0) {
- LOGED(ERROR, domid, "Getting vcpu sched rtds");
- rc = ERROR_FAIL;
- goto out;
- }
- scinfo->sched = LIBXL_SCHEDULER_RTDS;
- for (i = 0; i < num_vcpus; i++) {
- scinfo->vcpus[i].period = vcpus[i].u.rtds.period;
- scinfo->vcpus[i].budget = vcpus[i].u.rtds.budget;
- scinfo->vcpus[i].extratime =
- !!(vcpus[i].u.rtds.flags & XEN_DOMCTL_SCHEDRT_extra);
- scinfo->vcpus[i].vcpuid = vcpus[i].vcpuid;
- }
- rc = 0;
-out:
- return rc;
-}
-
-/* Get the RTDS scheduling parameters of all vcpus of a domain */
-static int sched_rtds_vcpu_get_all(libxl__gc *gc, uint32_t domid,
- libxl_vcpu_sched_params *scinfo)
-{
- uint32_t num_vcpus;
- int i, r, rc;
- xc_dominfo_t info;
- struct xen_domctl_schedparam_vcpu *vcpus;
-
- r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
- if (r < 0) {
- LOGED(ERROR, domid, "Getting domain info");
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (scinfo->num_vcpus > 0) {
- rc = ERROR_INVAL;
- goto out;
- } else {
- num_vcpus = info.max_vcpu_id + 1;
- GCNEW_ARRAY(vcpus, num_vcpus);
- for (i = 0; i < num_vcpus; i++)
- vcpus[i].vcpuid = i;
- }
-
- r = xc_sched_rtds_vcpu_get(CTX->xch, domid, vcpus, num_vcpus);
- if (r != 0) {
- LOGED(ERROR, domid, "Getting vcpu sched rtds");
- rc = ERROR_FAIL;
- goto out;
- }
- scinfo->sched = LIBXL_SCHEDULER_RTDS;
- scinfo->num_vcpus = num_vcpus;
- scinfo->vcpus = libxl__calloc(NOGC, num_vcpus,
- sizeof(libxl_sched_params));
-
- for (i = 0; i < num_vcpus; i++) {
- scinfo->vcpus[i].period = vcpus[i].u.rtds.period;
- scinfo->vcpus[i].budget = vcpus[i].u.rtds.budget;
- scinfo->vcpus[i].extratime =
- !!(vcpus[i].u.rtds.flags & XEN_DOMCTL_SCHEDRT_extra);
- scinfo->vcpus[i].vcpuid = vcpus[i].vcpuid;
- }
- rc = 0;
-out:
- return rc;
-}
-
-/* Set the RTDS scheduling parameters of vcpu(s) */
-static int sched_rtds_vcpu_set(libxl__gc *gc, uint32_t domid,
- const libxl_vcpu_sched_params *scinfo)
-{
- int r, rc;
- int i;
- uint16_t max_vcpuid;
- xc_dominfo_t info;
- struct xen_domctl_schedparam_vcpu *vcpus;
-
- r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
- if (r < 0) {
- LOGED(ERROR, domid, "Getting domain info");
- rc = ERROR_FAIL;
- goto out;
- }
- max_vcpuid = info.max_vcpu_id;
-
- if (scinfo->num_vcpus <= 0) {
- rc = ERROR_INVAL;
- goto out;
- }
- for (i = 0; i < scinfo->num_vcpus; i++) {
- if (scinfo->vcpus[i].vcpuid < 0 ||
- scinfo->vcpus[i].vcpuid > max_vcpuid) {
- LOGD(ERROR, domid, "Invalid VCPU %d: valid range is [0, %d]",
- scinfo->vcpus[i].vcpuid, max_vcpuid);
- rc = ERROR_INVAL;
- goto out;
- }
- rc = sched_rtds_validate_params(gc, scinfo->vcpus[i].period,
- scinfo->vcpus[i].budget);
- if (rc) {
- rc = ERROR_INVAL;
- goto out;
- }
- }
- GCNEW_ARRAY(vcpus, scinfo->num_vcpus);
- for (i = 0; i < scinfo->num_vcpus; i++) {
- vcpus[i].vcpuid = scinfo->vcpus[i].vcpuid;
- vcpus[i].u.rtds.period = scinfo->vcpus[i].period;
- vcpus[i].u.rtds.budget = scinfo->vcpus[i].budget;
- if (scinfo->vcpus[i].extratime)
- vcpus[i].u.rtds.flags |= XEN_DOMCTL_SCHEDRT_extra;
- else
- vcpus[i].u.rtds.flags &= ~XEN_DOMCTL_SCHEDRT_extra;
- }
-
- r = xc_sched_rtds_vcpu_set(CTX->xch, domid,
- vcpus, scinfo->num_vcpus);
- if (r != 0) {
- LOGED(ERROR, domid, "Setting vcpu sched rtds");
- rc = ERROR_FAIL;
- goto out;
- }
- rc = 0;
-out:
- return rc;
-}
-
-/* Set the RTDS scheduling parameters of all vcpus of a domain */
-static int sched_rtds_vcpu_set_all(libxl__gc *gc, uint32_t domid,
- const libxl_vcpu_sched_params *scinfo)
-{
- int r, rc;
- int i;
- uint16_t max_vcpuid;
- xc_dominfo_t info;
- struct xen_domctl_schedparam_vcpu *vcpus;
- uint32_t num_vcpus;
-
- r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
- if (r < 0) {
- LOGED(ERROR, domid, "Getting domain info");
- rc = ERROR_FAIL;
- goto out;
- }
- max_vcpuid = info.max_vcpu_id;
-
- if (scinfo->num_vcpus != 1) {
- rc = ERROR_INVAL;
- goto out;
- }
- if (sched_rtds_validate_params(gc, scinfo->vcpus[0].period,
- scinfo->vcpus[0].budget)) {
- rc = ERROR_INVAL;
- goto out;
- }
- num_vcpus = max_vcpuid + 1;
- GCNEW_ARRAY(vcpus, num_vcpus);
- for (i = 0; i < num_vcpus; i++) {
- vcpus[i].vcpuid = i;
- vcpus[i].u.rtds.period = scinfo->vcpus[0].period;
- vcpus[i].u.rtds.budget = scinfo->vcpus[0].budget;
- if (scinfo->vcpus[0].extratime)
- vcpus[i].u.rtds.flags |= XEN_DOMCTL_SCHEDRT_extra;
- else
- vcpus[i].u.rtds.flags &= ~XEN_DOMCTL_SCHEDRT_extra;
- }
-
- r = xc_sched_rtds_vcpu_set(CTX->xch, domid,
- vcpus, num_vcpus);
- if (r != 0) {
- LOGED(ERROR, domid, "Setting vcpu sched rtds");
- rc = ERROR_FAIL;
- goto out;
- }
- rc = 0;
-out:
- return rc;
-}
-
-static int sched_rtds_domain_get(libxl__gc *gc, uint32_t domid,
- libxl_domain_sched_params *scinfo)
-{
- struct xen_domctl_sched_rtds sdom;
- int rc;
-
- rc = xc_sched_rtds_domain_get(CTX->xch, domid, &sdom);
- if (rc != 0) {
- LOGED(ERROR, domid, "Getting domain sched rtds");
- return ERROR_FAIL;
- }
-
- libxl_domain_sched_params_init(scinfo);
-
- scinfo->sched = LIBXL_SCHEDULER_RTDS;
- scinfo->period = sdom.period;
- scinfo->budget = sdom.budget;
-
- return 0;
-}
-
-static int sched_rtds_domain_set(libxl__gc *gc, uint32_t domid,
- const libxl_domain_sched_params *scinfo)
-{
- struct xen_domctl_sched_rtds sdom;
- int rc;
-
- rc = xc_sched_rtds_domain_get(CTX->xch, domid, &sdom);
- if (rc != 0) {
- LOGED(ERROR, domid, "Getting domain sched rtds");
- return ERROR_FAIL;
- }
- if (scinfo->period != LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT)
- sdom.period = scinfo->period;
- if (scinfo->budget != LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT)
- sdom.budget = scinfo->budget;
- /* Set extratime by default */
- if (scinfo->extratime)
- sdom.flags |= XEN_DOMCTL_SCHEDRT_extra;
- else
- sdom.flags &= ~XEN_DOMCTL_SCHEDRT_extra;
- if (sched_rtds_validate_params(gc, sdom.period, sdom.budget))
- return ERROR_INVAL;
-
- rc = xc_sched_rtds_domain_set(CTX->xch, domid, &sdom);
- if (rc < 0) {
- LOGED(ERROR, domid, "Setting domain sched rtds");
- return ERROR_FAIL;
- }
-
- return 0;
-}
-
-int libxl_domain_sched_params_set(libxl_ctx *ctx, uint32_t domid,
- const libxl_domain_sched_params *scinfo)
-{
- GC_INIT(ctx);
- libxl_scheduler sched = scinfo->sched;
- int ret;
-
- if (sched == LIBXL_SCHEDULER_UNKNOWN)
- sched = libxl__domain_scheduler(gc, domid);
-
- switch (sched) {
- case LIBXL_SCHEDULER_SEDF:
- LOGD(ERROR, domid, "SEDF scheduler no longer available");
- ret=ERROR_FEATURE_REMOVED;
- break;
- case LIBXL_SCHEDULER_CREDIT:
- ret=sched_credit_domain_set(gc, domid, scinfo);
- break;
- case LIBXL_SCHEDULER_CREDIT2:
- ret=sched_credit2_domain_set(gc, domid, scinfo);
- break;
- case LIBXL_SCHEDULER_ARINC653:
- ret=sched_arinc653_domain_set(gc, domid, scinfo);
- break;
- case LIBXL_SCHEDULER_RTDS:
- ret=sched_rtds_domain_set(gc, domid, scinfo);
- break;
- case LIBXL_SCHEDULER_NULL:
- ret=sched_null_domain_set(gc, domid, scinfo);
- break;
- default:
- LOGD(ERROR, domid, "Unknown scheduler");
- ret=ERROR_INVAL;
- break;
- }
-
- GC_FREE;
- return ret;
-}
-
-int libxl_vcpu_sched_params_set(libxl_ctx *ctx, uint32_t domid,
- const libxl_vcpu_sched_params *scinfo)
-{
- GC_INIT(ctx);
- libxl_scheduler sched = scinfo->sched;
- int rc;
-
- if (sched == LIBXL_SCHEDULER_UNKNOWN)
- sched = libxl__domain_scheduler(gc, domid);
-
- switch (sched) {
- case LIBXL_SCHEDULER_SEDF:
- LOGD(ERROR, domid, "SEDF scheduler no longer available");
- rc = ERROR_FEATURE_REMOVED;
- break;
- case LIBXL_SCHEDULER_CREDIT:
- case LIBXL_SCHEDULER_CREDIT2:
- case LIBXL_SCHEDULER_ARINC653:
- case LIBXL_SCHEDULER_NULL:
- LOGD(ERROR, domid, "per-VCPU parameter setting not supported for this scheduler");
- rc = ERROR_INVAL;
- break;
- case LIBXL_SCHEDULER_RTDS:
- rc = sched_rtds_vcpu_set(gc, domid, scinfo);
- break;
- default:
- LOGD(ERROR, domid, "Unknown scheduler");
- rc = ERROR_INVAL;
- break;
- }
-
- GC_FREE;
- return rc;
-}
-
-int libxl_vcpu_sched_params_set_all(libxl_ctx *ctx, uint32_t domid,
- const libxl_vcpu_sched_params *scinfo)
-{
- GC_INIT(ctx);
- libxl_scheduler sched = scinfo->sched;
- int rc;
-
- if (sched == LIBXL_SCHEDULER_UNKNOWN)
- sched = libxl__domain_scheduler(gc, domid);
-
- switch (sched) {
- case LIBXL_SCHEDULER_SEDF:
- LOGD(ERROR, domid, "SEDF scheduler no longer available");
- rc = ERROR_FEATURE_REMOVED;
- break;
- case LIBXL_SCHEDULER_CREDIT:
- case LIBXL_SCHEDULER_CREDIT2:
- case LIBXL_SCHEDULER_ARINC653:
- case LIBXL_SCHEDULER_NULL:
- LOGD(ERROR, domid, "per-VCPU parameter setting not supported for this scheduler");
- rc = ERROR_INVAL;
- break;
- case LIBXL_SCHEDULER_RTDS:
- rc = sched_rtds_vcpu_set_all(gc, domid, scinfo);
- break;
- default:
- LOGD(ERROR, domid, "Unknown scheduler");
- rc = ERROR_INVAL;
- break;
- }
-
- GC_FREE;
- return rc;
-}
-
-int libxl_domain_sched_params_get(libxl_ctx *ctx, uint32_t domid,
- libxl_domain_sched_params *scinfo)
-{
- GC_INIT(ctx);
- int ret;
-
- libxl_domain_sched_params_init(scinfo);
-
- scinfo->sched = libxl__domain_scheduler(gc, domid);
-
- switch (scinfo->sched) {
- case LIBXL_SCHEDULER_SEDF:
- LOGD(ERROR, domid, "SEDF scheduler no longer available");
- ret=ERROR_FEATURE_REMOVED;
- break;
- case LIBXL_SCHEDULER_CREDIT:
- ret=sched_credit_domain_get(gc, domid, scinfo);
- break;
- case LIBXL_SCHEDULER_CREDIT2:
- ret=sched_credit2_domain_get(gc, domid, scinfo);
- break;
- case LIBXL_SCHEDULER_RTDS:
- ret=sched_rtds_domain_get(gc, domid, scinfo);
- break;
- case LIBXL_SCHEDULER_NULL:
- ret=sched_null_domain_get(gc, domid, scinfo);
- break;
- default:
- LOGD(ERROR, domid, "Unknown scheduler");
- ret=ERROR_INVAL;
- break;
- }
-
- GC_FREE;
- return ret;
-}
-
-int libxl_vcpu_sched_params_get(libxl_ctx *ctx, uint32_t domid,
- libxl_vcpu_sched_params *scinfo)
-{
- GC_INIT(ctx);
- int rc;
-
- scinfo->sched = libxl__domain_scheduler(gc, domid);
-
- switch (scinfo->sched) {
- case LIBXL_SCHEDULER_SEDF:
- LOGD(ERROR, domid, "SEDF scheduler is no longer available");
- rc = ERROR_FEATURE_REMOVED;
- break;
- case LIBXL_SCHEDULER_CREDIT:
- case LIBXL_SCHEDULER_CREDIT2:
- case LIBXL_SCHEDULER_ARINC653:
- case LIBXL_SCHEDULER_NULL:
- LOGD(ERROR, domid, "per-VCPU parameter getting not supported for this scheduler");
- rc = ERROR_INVAL;
- break;
- case LIBXL_SCHEDULER_RTDS:
- rc = sched_rtds_vcpu_get(gc, domid, scinfo);
- break;
- default:
- LOGD(ERROR, domid, "Unknown scheduler");
- rc = ERROR_INVAL;
- break;
- }
-
- GC_FREE;
- return rc;
-}
-
-int libxl_vcpu_sched_params_get_all(libxl_ctx *ctx, uint32_t domid,
- libxl_vcpu_sched_params *scinfo)
-{
- GC_INIT(ctx);
- int rc;
-
- scinfo->sched = libxl__domain_scheduler(gc, domid);
-
- switch (scinfo->sched) {
- case LIBXL_SCHEDULER_SEDF:
- LOGD(ERROR, domid, "SEDF scheduler is no longer available");
- rc = ERROR_FEATURE_REMOVED;
- break;
- case LIBXL_SCHEDULER_CREDIT:
- case LIBXL_SCHEDULER_CREDIT2:
- case LIBXL_SCHEDULER_ARINC653:
- case LIBXL_SCHEDULER_NULL:
- LOGD(ERROR, domid, "per-VCPU parameter getting not supported for this scheduler");
- rc = ERROR_INVAL;
- break;
- case LIBXL_SCHEDULER_RTDS:
- rc = sched_rtds_vcpu_get_all(gc, domid, scinfo);
- break;
- default:
- LOGD(ERROR, domid, "Unknown scheduler");
- rc = ERROR_INVAL;
- break;
- }
-
- GC_FREE;
- return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-#ifndef LIBXL__SR_STREAM_FORMAT_H
-#define LIBXL__SR_STREAM_FORMAT_H
-
-/*
- * C structures for the Migration v2 stream format.
- * See docs/specs/libxl-migration-stream.pandoc
- */
-
-#include <stdint.h>
-
-typedef struct libxl__sr_hdr
-{
- uint64_t ident;
- uint32_t version;
- uint32_t options;
-} libxl__sr_hdr;
-
-#define RESTORE_STREAM_IDENT 0x4c6962786c466d74UL
-#define RESTORE_STREAM_VERSION 0x00000002U
-
-#define RESTORE_OPT_BIG_ENDIAN (1u << 0)
-#define RESTORE_OPT_LEGACY (1u << 1)
-
-
-typedef struct libxl__sr_rec_hdr
-{
- uint32_t type;
- uint32_t length;
-} libxl__sr_rec_hdr;
-
-/* All records must be aligned up to an 8 octet boundary */
-#define REC_ALIGN_ORDER 3U
-
-#define REC_TYPE_END 0x00000000U
-#define REC_TYPE_LIBXC_CONTEXT 0x00000001U
-#define REC_TYPE_EMULATOR_XENSTORE_DATA 0x00000002U
-#define REC_TYPE_EMULATOR_CONTEXT 0x00000003U
-#define REC_TYPE_CHECKPOINT_END 0x00000004U
-#define REC_TYPE_CHECKPOINT_STATE 0x00000005U
-
-typedef struct libxl__sr_emulator_hdr
-{
- uint32_t id;
- uint32_t index;
-} libxl__sr_emulator_hdr;
-
-#define EMULATOR_UNKNOWN 0x00000000U
-#define EMULATOR_QEMU_TRADITIONAL 0x00000001U
-#define EMULATOR_QEMU_UPSTREAM 0x00000002U
-
-typedef struct libxl_sr_checkpoint_state
-{
- uint32_t id;
-} libxl_sr_checkpoint_state;
-
-#define CHECKPOINT_NEW 0x00000000U
-#define CHECKPOINT_SVM_SUSPENDED 0x00000001U
-#define CHECKPOINT_SVM_READY 0x00000002U
-#define CHECKPOINT_SVM_RESUMED 0x00000003U
-
-#endif /* LIBXL__SR_STREAM_FORMAT_H */
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2015 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*
- * Infrastructure for reading and acting on the contents of a libxl
- * migration stream. There are a lot of moving parts here.
- *
- * The logic revolves around two actions; reading another record from
- * the stream, and processing the records. The stream_continue()
- * function is responsible for choosing the next action to perform.
- *
- * The exact order of reading and processing is controlled by 'phase'.
- * All complete records are held in the record_queue before being
- * processed, and all records will be processed in queue order.
- *
- * Internal states:
- * running phase in_ record incoming
- * checkpoint _queue _record
- *
- * Undefined undef undef undef undef undef
- * Idle false undef false 0 0
- * Active true NORMAL false 0/1 0/partial
- * Active true BUFFERING true any 0/partial
- * Active true UNBUFFERING true any 0
- *
- * While reading data from the stream, 'dc' is active and a callback
- * is expected. Most actions in process_record() start a callback of
- * their own. Those which don't return out and stream_continue() sets
- * up the next action.
- *
- * PHASE_NORMAL:
- * This phase is used for regular migration or resume from file.
- * Records are read one at time and immediately processed. (The
- * record queue will not contain more than a single record.)
- *
- * PHASE_BUFFERING:
- * This phase is used in checkpointed streams, when libxc signals
- * the presence of a checkpoint in the stream. Records are read and
- * buffered until a CHECKPOINT_END record has been read.
- *
- * PHASE_UNBUFFERING:
- * Once a CHECKPOINT_END record has been read, all buffered records
- * are processed.
- *
- * Note:
- * Record buffers are not allocated from a GC; they are allocated
- * and tracked manually. This is to avoid OOM with Remus where the
- * AO lives for the lifetime of the process. Per-checkpoint AO's
- * might be an avenue to explore.
- *
- * Entry points from outside:
- * - libxl__stream_read_init()
- * - Initialises state. Must be called once before _start()
- * - libxl__stream_read_start()
- * - Starts reading records from the stream, and acting on them.
- * - libxl__stream_read_start_checkpoint()
- * - Starts buffering records at a checkpoint. Must be called on
- * a running stream.
- *
- * There are several chains of event:
- *
- * 1) Starting a stream follows:
- * - libxl__stream_read_start()
- * - stream_header_done()
- * - stream_continue()
- *
- * 2) Reading a record follows:
- * - stream_continue()
- * - record_header_done()
- * - record_body_done()
- * - stream_continue()
- *
- * 3) Processing a record had several chains to follow, depending on
- * the record in question.
- * 3a) "Simple" record:
- * - process_record()
- * - stream_continue()
- * 3b) LIBXC record:
- * - process_record()
- * - libxl__xc_domain_restore()
- * - libxl__xc_domain_restore_done()
- * - stream_continue()
- * 3c) EMULATOR record:
- * - process_record()
- * - stream_write_emulator()
- * - stream_write_emulator_done()
- * - stream_continue()
- *
- * Depending on the contents of the stream, there are likely to be several
- * parallel tasks being managed. check_all_finished() is used to join all
- * tasks in both success and error cases.
- *
- * Failover for remus
- * - We buffer all records until a CHECKPOINT_END record is received
- * - We will consume the buffered records when a CHECKPOINT_END record
- * is received
- * - If we find some internal error, then rc or retval is not 0 in
- * libxl__xc_domain_restore_done(). In this case, we don't resume the
- * guest
- * - If we need to do failover from primary, then rc and retval are both
- * 0 in libxl__xc_domain_restore_done(). In this case, the buffered
- * state will be dropped, because we haven't received a CHECKPOINT_END
- * record, and therefore the buffered state is inconsistent. In
- * libxl__xc_domain_restore_done(), we just complete the stream and
- * stream->completion_callback() will be called to resume the guest
- *
- * For back channel stream:
- * - libxl__stream_read_start()
- * - Set up the stream to running state
- *
- * - libxl__stream_read_continue()
- * - Set up reading the next record from a started stream.
- * Add some codes to process_record() to handle the record.
- * Then call stream->checkpoint_callback() to return.
- */
-
-/* Success/error/cleanup handling. */
-static void stream_complete(libxl__egc *egc,
- libxl__stream_read_state *stream, int rc);
-static void checkpoint_done(libxl__egc *egc,
- libxl__stream_read_state *stream, int rc);
-static void stream_done(libxl__egc *egc,
- libxl__stream_read_state *stream, int rc);
-static void conversion_done(libxl__egc *egc,
- libxl__conversion_helper_state *chs, int rc);
-static void check_all_finished(libxl__egc *egc,
- libxl__stream_read_state *stream, int rc);
-
-/* Event chain for first iteration, from _start(). */
-static void stream_header_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval);
-static void stream_continue(libxl__egc *egc,
- libxl__stream_read_state *stream);
-static void setup_read_record(libxl__egc *egc,
- libxl__stream_read_state *stream);
-static void record_header_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval);
-static void record_body_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval);
-static bool process_record(libxl__egc *egc,
- libxl__stream_read_state *stream);
-
-/* Event chain for processing an emulator blob. */
-static void write_emulator_blob(libxl__egc *egc,
- libxl__stream_read_state *stream,
- libxl__sr_record_buf *rec);
-static void write_emulator_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval);
-
-/* Handlers for checkpoint state mini-loop */
-static void checkpoint_state_done(libxl__egc *egc,
- libxl__stream_read_state *stream, int rc);
-
-/*----- Helpers -----*/
-
-/* Helper to set up reading some data from the stream. */
-static int setup_read(libxl__stream_read_state *stream,
- const char *what, void *ptr, size_t nr_bytes,
- libxl__datacopier_callback cb)
-{
- libxl__datacopier_state *dc = &stream->dc;
-
- dc->readwhat = what;
- dc->readbuf = ptr;
- dc->bytes_to_read = nr_bytes;
- dc->used = 0;
- dc->callback = cb;
-
- return libxl__datacopier_start(dc);
-}
-
-static void free_record(libxl__sr_record_buf *rec)
-{
- if (rec) {
- free(rec->body);
- free(rec);
- }
-}
-
-/*----- Entrypoints -----*/
-
-void libxl__stream_read_init(libxl__stream_read_state *stream)
-{
- assert(stream->ao);
-
- stream->shs.ao = stream->ao;
- libxl__save_helper_init(&stream->shs);
-
- stream->chs.ao = stream->ao;
- libxl__conversion_helper_init(&stream->chs);
-
- stream->rc = 0;
- stream->running = false;
- stream->in_checkpoint = false;
- stream->sync_teardown = false;
- FILLZERO(stream->dc);
- FILLZERO(stream->hdr);
- LIBXL_STAILQ_INIT(&stream->record_queue);
- stream->phase = SRS_PHASE_NORMAL;
- stream->recursion_guard = false;
- stream->incoming_record = NULL;
- FILLZERO(stream->emu_dc);
- stream->emu_carefd = NULL;
-}
-
-void libxl__stream_read_start(libxl__egc *egc,
- libxl__stream_read_state *stream)
-{
- libxl__datacopier_state *dc = &stream->dc;
- STATE_AO_GC(stream->ao);
- int rc = 0;
-
- libxl__stream_read_init(stream);
-
- stream->running = true;
- stream->phase = SRS_PHASE_NORMAL;
-
- if (stream->legacy) {
- /*
- * Convert the legacy stream.
- *
- * This results in a fork()/exec() of conversion helper script. It is
- * passed the exiting stream->fd as an input, and returns the
- * transformed stream via a new pipe. The fd of this new pipe then
- * replaces stream->fd, to make the rest of the stream read code
- * agnostic to whether legacy conversion is happening or not.
- */
- libxl__conversion_helper_state *chs = &stream->chs;
-
- chs->legacy_fd = stream->fd;
- chs->hvm =
- (stream->dcs->guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM);
- chs->completion_callback = conversion_done;
-
- rc = libxl__convert_legacy_stream(egc, &stream->chs);
-
- if (rc) {
- LOG(ERROR, "Failed to start the legacy stream conversion helper");
- goto err;
- }
-
- /* There should be no interaction of COLO backchannels and legacy
- * stream conversion. */
- assert(!stream->back_channel);
-
- /* Confirm *dc is still zeroed out, while we shuffle stream->fd. */
- assert(dc->ao == NULL);
- assert(stream->chs.v2_carefd);
- stream->fd = libxl__carefd_fd(stream->chs.v2_carefd);
- stream->dcs->libxc_fd = stream->fd;
- }
- /* stream->fd is now a v2 stream. */
-
- dc->ao = stream->ao;
- dc->copywhat = "restore v2 stream";
- dc->readfd = stream->fd;
- dc->writefd = -1;
-
- if (stream->back_channel)
- return;
-
- /* Start reading the stream header. */
- rc = setup_read(stream, "stream header",
- &stream->hdr, sizeof(stream->hdr),
- stream_header_done);
- if (rc)
- goto err;
-
- assert(!rc);
- return;
-
- err:
- assert(rc);
- stream_complete(egc, stream, rc);
-}
-
-void libxl__stream_read_start_checkpoint(libxl__egc *egc,
- libxl__stream_read_state *stream)
-{
- assert(stream->running);
- assert(!stream->in_checkpoint);
-
- stream->in_checkpoint = true;
- stream->phase = SRS_PHASE_BUFFERING;
-
- /*
- * Libxc has handed control of the fd to us. Start reading some
- * libxl records out of it.
- */
- stream_continue(egc, stream);
-}
-
-void libxl__stream_read_abort(libxl__egc *egc,
- libxl__stream_read_state *stream, int rc)
-{
- assert(rc);
-
- if (stream->running)
- stream_complete(egc, stream, rc);
-}
-
-/*----- Event logic -----*/
-
-static void stream_header_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval)
-{
- libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc);
- libxl__sr_hdr *hdr = &stream->hdr;
- STATE_AO_GC(dc->ao);
-
- if (rc)
- goto err;
-
- hdr->ident = be64toh(hdr->ident);
- hdr->version = be32toh(hdr->version);
- hdr->options = be32toh(hdr->options);
-
- if (hdr->ident != RESTORE_STREAM_IDENT) {
- rc = ERROR_FAIL;
- LOG(ERROR,
- "Invalid ident: expected 0x%016"PRIx64", got 0x%016"PRIx64,
- RESTORE_STREAM_IDENT, hdr->ident);
- goto err;
- }
- if (hdr->version != RESTORE_STREAM_VERSION) {
- rc = ERROR_FAIL;
- LOG(ERROR, "Unexpected Version: expected %"PRIu32", got %"PRIu32,
- RESTORE_STREAM_VERSION, hdr->version);
- goto err;
- }
- if (hdr->options & RESTORE_OPT_BIG_ENDIAN) {
- rc = ERROR_FAIL;
- LOG(ERROR, "Unable to handle big endian streams");
- goto err;
- }
-
- LOG(DEBUG, "Stream v%"PRIu32"%s", hdr->version,
- hdr->options & RESTORE_OPT_LEGACY ? " (from legacy)" : "");
-
- stream_continue(egc, stream);
- return;
-
- err:
- assert(rc);
- stream_complete(egc, stream, rc);
-}
-
-static void stream_continue(libxl__egc *egc,
- libxl__stream_read_state *stream)
-{
- STATE_AO_GC(stream->ao);
-
- /*
- * Must not mutually recurse with process_record().
- *
- * For records whose processing function is synchronous
- * (e.g. TOOLSTACK), process_record() does not start another async
- * operation, and a further operation should be started.
- *
- * A naive solution, which would function in general, would be for
- * process_record() to call stream_continue(). However, this
- * would allow the content of the stream to cause mutual
- * recursion, and possibly for us to fall off our stack.
- *
- * Instead, process_record() indicates with its return value
- * whether a further operation needs to start, and the
- * recursion_guard is in place to catch any code paths which get
- * this wrong.
- */
- assert(stream->recursion_guard == false);
- stream->recursion_guard = true;
-
- switch (stream->phase) {
- case SRS_PHASE_NORMAL:
- /*
- * Normal phase (regular migration or restore from file):
- *
- * logically:
- * do { read_record(); process_record(); } while ( not END );
- *
- * Alternate between reading a record from the stream, and
- * processing the record. There should never be two records
- * in the queue.
- */
- if (LIBXL_STAILQ_EMPTY(&stream->record_queue))
- setup_read_record(egc, stream);
- else {
- if (process_record(egc, stream))
- setup_read_record(egc, stream);
-
- /*
- * process_record() had better have consumed the one and
- * only record in the queue.
- */
- assert(LIBXL_STAILQ_EMPTY(&stream->record_queue));
- }
- break;
-
- case SRS_PHASE_BUFFERING: {
- /*
- * Buffering phase (checkpointed streams only):
- *
- * logically:
- * do { read_record(); } while ( not CHECKPOINT_END );
- *
- * Read and buffer all records from the stream until a
- * CHECKPOINT_END record is encountered. We need to peek at
- * the tail to spot the CHECKPOINT_END record, and switch to
- * the unbuffering phase.
- */
- libxl__sr_record_buf *rec = LIBXL_STAILQ_LAST(
- &stream->record_queue, libxl__sr_record_buf, entry);
-
- assert(stream->in_checkpoint);
-
- if (!rec || (rec->hdr.type != REC_TYPE_CHECKPOINT_END)) {
- setup_read_record(egc, stream);
- break;
- }
-
- /*
- * There are now some number of buffered records, with a
- * CHECKPOINT_END at the end. Start processing them all.
- */
- stream->phase = SRS_PHASE_UNBUFFERING;
- }
- /* FALLTHROUGH */
- case SRS_PHASE_UNBUFFERING:
- /*
- * Unbuffering phase (checkpointed streams only):
- *
- * logically:
- * do { process_record(); } while ( not CHECKPOINT_END );
- *
- * Process all records collected during the buffering phase.
- */
- assert(stream->in_checkpoint);
-
- while (process_record(egc, stream))
- ; /*
- * Nothing! process_record() helpfully tells us if no specific
- * futher actions have been set up, in which case we want to go
- * ahead and process the next record.
- */
- break;
-
- default:
- abort();
- }
-
- assert(stream->recursion_guard == true);
- stream->recursion_guard = false;
-}
-
-static void setup_read_record(libxl__egc *egc,
- libxl__stream_read_state *stream)
-{
- libxl__sr_record_buf *rec = NULL;
- STATE_AO_GC(stream->ao);
- int rc;
-
- assert(stream->incoming_record == NULL);
- stream->incoming_record = rec = libxl__zalloc(NOGC, sizeof(*rec));
-
- rc = setup_read(stream, "record header",
- &rec->hdr, sizeof(rec->hdr),
- record_header_done);
- if (rc)
- goto err;
- return;
-
- err:
- assert(rc);
- stream_complete(egc, stream, rc);
-}
-
-static void record_header_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval)
-{
- libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc);
- libxl__sr_record_buf *rec = stream->incoming_record;
- STATE_AO_GC(dc->ao);
-
- if (rc)
- goto err;
-
- /* No body? All done. */
- if (rec->hdr.length == 0) {
- record_body_done(egc, dc, 0, 0, 0);
- return;
- }
-
- size_t bytes_to_read = ROUNDUP(rec->hdr.length, REC_ALIGN_ORDER);
- rec->body = libxl__malloc(NOGC, bytes_to_read);
-
- rc = setup_read(stream, "record body",
- rec->body, bytes_to_read,
- record_body_done);
- if (rc)
- goto err;
- return;
-
- err:
- assert(rc);
- stream_complete(egc, stream, rc);
-}
-
-static void record_body_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval)
-{
- libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc);
- libxl__sr_record_buf *rec = stream->incoming_record;
- STATE_AO_GC(dc->ao);
-
- if (rc)
- goto err;
-
- LIBXL_STAILQ_INSERT_TAIL(&stream->record_queue, rec, entry);
- stream->incoming_record = NULL;
-
- stream_continue(egc, stream);
- return;
-
- err:
- assert(rc);
- stream_complete(egc, stream, rc);
-}
-
-/*
- * Returns a boolean indicating whether a further action should be set
- * up by the caller. This is needed to prevent mutual recursion with
- * stream_continue().
- *
- * It is a bug for this function to ever call stream_continue() or
- * setup_read_record().
- */
-static bool process_record(libxl__egc *egc,
- libxl__stream_read_state *stream)
-{
- STATE_AO_GC(stream->ao);
- libxl__domain_create_state *dcs = stream->dcs;
- libxl__sr_record_buf *rec;
- libxl_sr_checkpoint_state *srcs;
- bool further_action_needed = false;
- int rc = 0;
-
- /* Pop a record from the head of the queue. */
- assert(!LIBXL_STAILQ_EMPTY(&stream->record_queue));
- rec = LIBXL_STAILQ_FIRST(&stream->record_queue);
- LIBXL_STAILQ_REMOVE_HEAD(&stream->record_queue, entry);
-
- LOG(DEBUG, "Record: %u, length %u", rec->hdr.type, rec->hdr.length);
-
- switch (rec->hdr.type) {
-
- case REC_TYPE_END:
- stream_complete(egc, stream, 0);
- break;
-
- case REC_TYPE_LIBXC_CONTEXT:
- libxl__xc_domain_restore(egc, dcs, &stream->shs);
- break;
-
- case REC_TYPE_EMULATOR_XENSTORE_DATA:
- if (dcs->guest_config->b_info.type != LIBXL_DOMAIN_TYPE_HVM) {
- rc = ERROR_FAIL;
- LOG(ERROR,
- "Received a xenstore emulator record when none was expected");
- goto err;
- }
-
- if (rec->hdr.length < sizeof(libxl__sr_emulator_hdr)) {
- rc = ERROR_FAIL;
- LOG(ERROR,
- "Emulator xenstore data record too short to contain header");
- goto err;
- }
-
- rc = libxl__restore_emulator_xenstore_data(dcs,
- rec->body + sizeof(libxl__sr_emulator_hdr),
- rec->hdr.length - sizeof(libxl__sr_emulator_hdr));
- if (rc)
- goto err;
-
- /*
- * libxl__restore_emulator_xenstore_data() is a synchronous function.
- * Request that our caller queues another action for us.
- */
- further_action_needed = true;
- break;
-
- case REC_TYPE_EMULATOR_CONTEXT:
- if (dcs->guest_config->b_info.type != LIBXL_DOMAIN_TYPE_HVM) {
- rc = ERROR_FAIL;
- LOG(ERROR,
- "Received an emulator context record when none was expected");
- goto err;
- }
-
- write_emulator_blob(egc, stream, rec);
- break;
-
- case REC_TYPE_CHECKPOINT_END:
- if (!stream->in_checkpoint) {
- LOG(ERROR, "Unexpected CHECKPOINT_END record in stream");
- rc = ERROR_FAIL;
- goto err;
- }
- checkpoint_done(egc, stream, 0);
- break;
-
- case REC_TYPE_CHECKPOINT_STATE:
- if (!stream->in_checkpoint_state) {
- LOG(ERROR, "Unexpected CHECKPOINT_STATE record in stream");
- rc = ERROR_FAIL;
- goto err;
- }
-
- srcs = rec->body;
- checkpoint_state_done(egc, stream, srcs->id);
- break;
-
- default:
- LOG(ERROR, "Unrecognised record 0x%08x", rec->hdr.type);
- rc = ERROR_FAIL;
- goto err;
- }
-
- assert(!rc);
- free_record(rec);
- return further_action_needed;
-
- err:
- assert(rc);
- free_record(rec);
- stream_complete(egc, stream, rc);
- return false;
-}
-
-static void write_emulator_blob(libxl__egc *egc,
- libxl__stream_read_state *stream,
- libxl__sr_record_buf *rec)
-{
- libxl__domain_create_state *dcs = stream->dcs;
- libxl__datacopier_state *dc = &stream->emu_dc;
- libxl__sr_emulator_hdr *emu_hdr;
- STATE_AO_GC(stream->ao);
- char path[256];
- int rc = 0, writefd;
-
- if (rec->hdr.length < sizeof(*emu_hdr)) {
- rc = ERROR_FAIL;
- LOG(ERROR, "Emulator record too short to contain header");
- goto err;
- }
- emu_hdr = rec->body;
-
- sprintf(path, LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", dcs->guest_domid);
-
- assert(stream->emu_carefd == NULL);
- libxl__carefd_begin();
- writefd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
- stream->emu_carefd = libxl__carefd_opened(CTX, writefd);
-
- if (writefd == -1) {
- rc = ERROR_FAIL;
- LOGE(ERROR, "unable to open %s", path);
- goto err;
- }
-
- FILLZERO(*dc);
- dc->ao = stream->ao;
- dc->writewhat = "qemu save file";
- dc->copywhat = "restore v2 stream";
- dc->writefd = writefd;
- dc->readfd = -1;
- dc->maxsz = -1;
- dc->callback = write_emulator_done;
-
- rc = libxl__datacopier_start(dc);
- if (rc)
- goto err;
-
- libxl__datacopier_prefixdata(egc, dc,
- rec->body + sizeof(*emu_hdr),
- rec->hdr.length - sizeof(*emu_hdr));
- return;
-
- err:
- assert(rc);
- stream_complete(egc, stream, rc);
-}
-
-static void write_emulator_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval)
-{
- libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, emu_dc);
- STATE_AO_GC(dc->ao);
-
- libxl__carefd_close(stream->emu_carefd);
- stream->emu_carefd = NULL;
-
- if (rc)
- goto err;
-
- stream_continue(egc, stream);
- return;
-
- err:
- assert(rc);
- stream_complete(egc, stream, rc);
-}
-
-/*----- Success/error/cleanup handling. -----*/
-
-static void stream_complete(libxl__egc *egc,
- libxl__stream_read_state *stream, int rc)
-{
- assert(stream->running);
-
- if (stream->in_checkpoint) {
- assert(rc);
-
- /*
- * If an error is encountered while in a checkpoint, pass it
- * back to libxc. The failure will come back around to us via
- * libxl__xc_domain_restore_done()
- */
- checkpoint_done(egc, stream, rc);
- return;
- }
-
- if (stream->in_checkpoint_state) {
- assert(rc);
-
- /*
- * If an error is encountered while in a checkpoint, pass it
- * back to libxc. The failure will come back around to us via
- * 1. normal stream
- * libxl__xc_domain_restore_done()
- * 2. back_channel stream
- * libxl__stream_read_abort()
- */
- checkpoint_state_done(egc, stream, rc);
- return;
- }
-
- stream_done(egc, stream, rc);
-}
-
-static void checkpoint_done(libxl__egc *egc,
- libxl__stream_read_state *stream, int rc)
-{
- int ret;
-
- assert(stream->in_checkpoint);
-
- if (rc == 0)
- ret = XGR_CHECKPOINT_SUCCESS;
- else if (stream->phase == SRS_PHASE_BUFFERING)
- ret = XGR_CHECKPOINT_FAILOVER;
- else
- ret = XGR_CHECKPOINT_ERROR;
-
- stream->checkpoint_callback(egc, stream, ret);
-
- stream->in_checkpoint = false;
- stream->phase = SRS_PHASE_NORMAL;
-}
-
-static void stream_done(libxl__egc *egc,
- libxl__stream_read_state *stream, int rc)
-{
- libxl__sr_record_buf *rec, *trec;
-
- assert(stream->running);
- assert(!stream->in_checkpoint);
- assert(!stream->in_checkpoint_state);
- stream->running = false;
-
- if (stream->incoming_record)
- free_record(stream->incoming_record);
-
- if (stream->emu_carefd)
- libxl__carefd_close(stream->emu_carefd);
-
- /* If we started a conversion helper, we took ownership of its carefd. */
- if (stream->chs.v2_carefd)
- libxl__carefd_close(stream->chs.v2_carefd);
-
- /* The record queue had better be empty if the stream believes
- * itself to have been successful. */
- assert(LIBXL_STAILQ_EMPTY(&stream->record_queue) || stream->rc);
-
- LIBXL_STAILQ_FOREACH_SAFE(rec, &stream->record_queue, entry, trec)
- free_record(rec);
-
- if (!stream->back_channel) {
- /*
- * 1. In stream_done(), stream->running is set to false, so
- * the stream itself is not in use.
- * 2. Read stream is a back channel stream, this means it is
- * only used by primary(save side) to read records sent by
- * secondary(restore side), so it doesn't have restore helper.
- * 3. Back channel stream doesn't support legacy stream, so
- * there is no conversion helper.
- * So we don't need invoke check_all_finished here
- */
- check_all_finished(egc, stream, rc);
- }
-}
-
-void libxl__xc_domain_restore_done(libxl__egc *egc, void *dcs_void,
- int rc, int retval, int errnoval)
-{
- libxl__domain_create_state *dcs = dcs_void;
- libxl__stream_read_state *stream = &dcs->srs;
- STATE_AO_GC(dcs->ao);
-
- /* convenience aliases */
- const int checkpointed_stream = dcs->restore_params.checkpointed_stream;
-
- if (rc)
- goto err;
-
- if (retval) {
- LOGEV(ERROR, errnoval, "restoring domain");
- rc = ERROR_FAIL;
- goto err;
- }
-
- err:
- check_all_finished(egc, stream, rc);
-
- /*
- * This function is the callback associated with the save helper
- * task, not the stream task. We do not know whether the stream is
- * alive, and check_all_finished() may have torn it down around us.
- * If the stream is not still alive, we must not continue any work.
- */
- if (libxl__stream_read_inuse(stream)) {
- switch (checkpointed_stream) {
- case LIBXL_CHECKPOINTED_STREAM_COLO:
- if (stream->completion_callback) {
- /*
- * restore, just build the secondary vm, don't close
- * the stream
- */
- stream->completion_callback(egc, stream, 0);
- } else {
- /* failover, just close the stream */
- stream_complete(egc, stream, 0);
- }
- break;
- case LIBXL_CHECKPOINTED_STREAM_REMUS:
- /*
- * Failover from primary. Domain state is currently at a
- * consistent checkpoint, complete the stream, and call
- * stream->completion_callback() to resume the guest.
- */
- stream_complete(egc, stream, 0);
- break;
- case LIBXL_CHECKPOINTED_STREAM_NONE:
- /*
- * Libxc has indicated that it is done with the stream.
- * Resume reading libxl records from it.
- */
- stream_continue(egc, stream);
- break;
- }
- }
-}
-
-static void conversion_done(libxl__egc *egc,
- libxl__conversion_helper_state *chs, int rc)
-{
- libxl__stream_read_state *stream = CONTAINER_OF(chs, *stream, chs);
-
- check_all_finished(egc, stream, rc);
-}
-
-static void check_all_finished(libxl__egc *egc,
- libxl__stream_read_state *stream, int rc)
-{
- STATE_AO_GC(stream->ao);
-
- /*
- * In the case of a failure, the _abort()'s below might cancel
- * synchronously on top of us, or asynchronously at a later point.
- *
- * We must avoid the situation where all _abort() cancel
- * synchronously and the completion_callback() gets called twice;
- * once by the first error and once by the final stacked abort(),
- * both of whom will find that all of the tasks have stopped.
- *
- * To avoid this problem, any stacked re-entry into this function is
- * ineligible to fire the completion callback. The outermost
- * instance will take care of completing, once the stack has
- * unwound.
- */
- if (stream->sync_teardown)
- return;
-
- if (!stream->rc && rc) {
- /* First reported failure. Tear everything down. */
- stream->rc = rc;
- stream->sync_teardown = true;
-
- libxl__stream_read_abort(egc, stream, rc);
- libxl__save_helper_abort(egc, &stream->shs);
- libxl__conversion_helper_abort(egc, &stream->chs, rc);
-
- stream->sync_teardown = false;
- }
-
- /* Don't fire the callback until all our parallel tasks have stopped. */
- if (libxl__stream_read_inuse(stream) ||
- libxl__save_helper_inuse(&stream->shs) ||
- libxl__conversion_helper_inuse(&stream->chs))
- return;
-
- if (stream->completion_callback)
- /* back channel stream doesn't have completion_callback() */
- stream->completion_callback(egc, stream, stream->rc);
-}
-
-/*----- Checkpoint state handlers -----*/
-
-void libxl__stream_read_checkpoint_state(libxl__egc *egc,
- libxl__stream_read_state *stream)
-{
- assert(stream->running);
- assert(!stream->in_checkpoint);
- assert(!stream->in_checkpoint_state);
- stream->in_checkpoint_state = true;
-
- setup_read_record(egc, stream);
-}
-
-static void checkpoint_state_done(libxl__egc *egc,
- libxl__stream_read_state *stream, int rc)
-{
- assert(stream->in_checkpoint_state);
- stream->in_checkpoint_state = false;
- stream->checkpoint_callback(egc, stream, rc);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2015 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*
- * Infrastructure for writing a domain to a libxl migration v2 stream.
- *
- * Entry points from outside:
- * - libxl__stream_write_start()
- * - Start writing a stream from the start.
- * - libxl__stream_write_start_checkpoint()
- * - Write the records which form a checkpoint into a stream.
- *
- * In normal operation, there are two tasks running at once; this
- * stream processing, and the libxl-save-helper. check_all_finished()
- * is used to join all the tasks in both success and error cases.
- *
- * Nomenclature for event callbacks:
- * - $FOO_done(): Completion callback for $FOO
- * - write_$FOO(): Set up the datacopier to write a $FOO
- * - $BAR_header(): A $BAR record header only
- * - $BAR_record(): A complete $BAR record with header and content
- *
- * The main loop for a plain VM writes:
- * - Stream header
- * - Libxc record
- * - (optional) Emulator xenstore record
- * - if (hvm)
- * - Emulator context record
- * - End record
- *
- * For checkpointed stream, there is a second loop which is triggered by a
- * save-helper checkpoint callback. It writes:
- * - (optional) Emulator xenstore record
- * - if (hvm)
- * - Emulator context record
- * - Checkpoint end record
- *
- * For back channel stream:
- * - libxl__stream_write_start()
- * - Set up the stream to running state
- *
- * - Use libxl__stream_write_checkpoint_state to write the record. When the
- * record is written out, call stream->checkpoint_callback() to return.
- */
-
-/* Success/error/cleanup handling. */
-static void stream_success(libxl__egc *egc,
- libxl__stream_write_state *stream);
-static void stream_complete(libxl__egc *egc,
- libxl__stream_write_state *stream, int rc);
-static void stream_done(libxl__egc *egc,
- libxl__stream_write_state *stream, int rc);
-static void checkpoint_done(libxl__egc *egc,
- libxl__stream_write_state *stream,
- int rc);
-static void check_all_finished(libxl__egc *egc,
- libxl__stream_write_state *stream, int rc);
-
-/* Event chain for a plain VM. */
-static void stream_header_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval);
-static void libxc_header_done(libxl__egc *egc,
- libxl__stream_write_state *stream);
-/* libxl__xc_domain_save_done() lives here, event-order wise. */
-static void write_emulator_xenstore_record(libxl__egc *egc,
- libxl__stream_write_state *stream);
-static void emulator_xenstore_record_done(libxl__egc *egc,
- libxl__stream_write_state *stream);
-static void write_emulator_context_record(libxl__egc *egc,
- libxl__stream_write_state *stream);
-static void emulator_context_read_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval);
-static void emulator_context_record_done(libxl__egc *egc,
- libxl__stream_write_state *stream);
-static void write_end_record(libxl__egc *egc,
- libxl__stream_write_state *stream);
-
-/* Event chain unique to checkpointed streams. */
-static void write_checkpoint_end_record(libxl__egc *egc,
- libxl__stream_write_state *stream);
-static void checkpoint_end_record_done(libxl__egc *egc,
- libxl__stream_write_state *stream);
-
-/* checkpoint state */
-static void write_checkpoint_state_done(libxl__egc *egc,
- libxl__stream_write_state *stream);
-static void checkpoint_state_done(libxl__egc *egc,
- libxl__stream_write_state *stream, int rc);
-
-/*----- Helpers -----*/
-
-static void write_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval);
-
-/* Generic helper to set up writing some data to the stream. */
-static void setup_generic_write(libxl__egc *egc,
- libxl__stream_write_state *stream,
- const char *what,
- libxl__sr_rec_hdr *hdr,
- libxl__sr_emulator_hdr *emu_hdr,
- void *body,
- sws_record_done_cb cb)
-{
- static const uint8_t zero_padding[1U << REC_ALIGN_ORDER] = { 0 };
-
- libxl__datacopier_state *dc = &stream->dc;
- int rc;
-
- assert(stream->record_done_callback == NULL);
-
- dc->writewhat = what;
- dc->used = 0;
- dc->callback = write_done;
- rc = libxl__datacopier_start(dc);
-
- if (rc) {
- stream_complete(egc, stream, rc);
- return;
- }
-
- size_t padsz = ROUNDUP(hdr->length, REC_ALIGN_ORDER) - hdr->length;
- uint32_t length = hdr->length;
-
- /* Insert header */
- libxl__datacopier_prefixdata(egc, dc, hdr, sizeof(*hdr));
-
- /* Optional emulator sub-header */
- if (emu_hdr) {
- assert(length >= sizeof(*emu_hdr));
- libxl__datacopier_prefixdata(egc, dc, emu_hdr, sizeof(*emu_hdr));
- length -= sizeof(*emu_hdr);
- }
-
- /* Optional body */
- if (body)
- libxl__datacopier_prefixdata(egc, dc, body, length);
-
- /* Any required padding */
- if (padsz > 0)
- libxl__datacopier_prefixdata(egc, dc,
- zero_padding, padsz);
- stream->record_done_callback = cb;
-}
-
-/* Helper to set up writing a regular record to the stream. */
-static void setup_write(libxl__egc *egc,
- libxl__stream_write_state *stream,
- const char *what,
- libxl__sr_rec_hdr *hdr,
- void *body,
- sws_record_done_cb cb)
-{
- setup_generic_write(egc, stream, what, hdr, NULL, body, cb);
-}
-
-/* Helper to set up writing a record with an emulator prefix to the stream. */
-static void setup_emulator_write(libxl__egc *egc,
- libxl__stream_write_state *stream,
- const char *what,
- libxl__sr_rec_hdr *hdr,
- libxl__sr_emulator_hdr *emu_hdr,
- void *body,
- sws_record_done_cb cb)
-{
- assert(stream->emu_sub_hdr.id != EMULATOR_UNKNOWN);
- setup_generic_write(egc, stream, what, hdr, emu_hdr, body, cb);
-}
-
-
-static void write_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval)
-{
- libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, dc);
- STATE_AO_GC(stream->ao);
- sws_record_done_cb cb = stream->record_done_callback;
-
- stream->record_done_callback = NULL;
-
- if (onwrite || errnoval)
- stream_complete(egc, stream, rc ?: ERROR_FAIL);
- else
- cb(egc, stream);
-}
-
-/*----- Entrypoints -----*/
-
-void libxl__stream_write_init(libxl__stream_write_state *stream)
-{
- assert(stream->ao);
-
- stream->shs.ao = stream->ao;
- libxl__save_helper_init(&stream->shs);
-
- stream->rc = 0;
- stream->running = false;
- stream->in_checkpoint = false;
- stream->sync_teardown = false;
- FILLZERO(stream->dc);
- stream->record_done_callback = NULL;
- FILLZERO(stream->emu_dc);
- stream->emu_carefd = NULL;
- FILLZERO(stream->emu_rec_hdr);
- FILLZERO(stream->emu_sub_hdr);
- stream->emu_body = NULL;
- stream->device_model_version = LIBXL_DEVICE_MODEL_VERSION_UNKNOWN;
-}
-
-void libxl__stream_write_start(libxl__egc *egc,
- libxl__stream_write_state *stream)
-{
- libxl__datacopier_state *dc = &stream->dc;
- libxl__domain_save_state *dss = stream->dss;
- STATE_AO_GC(stream->ao);
- struct libxl__sr_hdr hdr;
- int rc = 0;
-
- libxl__stream_write_init(stream);
-
- stream->running = true;
-
- dc->ao = ao;
- dc->readfd = -1;
- dc->writewhat = "stream header";
- dc->copywhat = "save v2 stream";
- dc->writefd = stream->fd;
- dc->maxsz = -1;
- dc->callback = stream_header_done;
-
- if (stream->back_channel)
- return;
-
- if (dss->type == LIBXL_DOMAIN_TYPE_HVM) {
- stream->device_model_version =
- libxl__device_model_version_running(gc, dss->domid);
- switch (stream->device_model_version) {
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
- stream->emu_sub_hdr.id = EMULATOR_QEMU_TRADITIONAL;
- break;
-
- case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
- stream->emu_sub_hdr.id = EMULATOR_QEMU_UPSTREAM;
- break;
-
- default:
- rc = ERROR_FAIL;
- LOGD(ERROR, dss->domid, "Unknown emulator for HVM domain");
- goto err;
- }
- stream->emu_sub_hdr.index = 0;
- }
-
- rc = libxl__datacopier_start(dc);
- if (rc)
- goto err;
-
- FILLZERO(hdr);
- hdr.ident = htobe64(RESTORE_STREAM_IDENT);
- hdr.version = htobe32(RESTORE_STREAM_VERSION);
- hdr.options = htobe32(0);
-
- libxl__datacopier_prefixdata(egc, dc, &hdr, sizeof(hdr));
- return;
-
- err:
- assert(rc);
- stream_complete(egc, stream, rc);
-}
-
-void libxl__stream_write_start_checkpoint(libxl__egc *egc,
- libxl__stream_write_state *stream)
-{
- assert(stream->running);
- assert(!stream->in_checkpoint);
- assert(!stream->back_channel);
- stream->in_checkpoint = true;
-
- write_emulator_xenstore_record(egc, stream);
-}
-
-void libxl__stream_write_abort(libxl__egc *egc,
- libxl__stream_write_state *stream, int rc)
-{
- assert(rc);
-
- if (stream->running)
- stream_complete(egc, stream, rc);
-}
-
-/*----- Event logic -----*/
-
-static void stream_header_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval)
-{
- libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, dc);
- STATE_AO_GC(stream->ao);
- struct libxl__sr_rec_hdr rec;
-
- if (rc || errnoval) {
- stream_complete(egc, stream, rc ?: ERROR_FAIL);
- return;
- }
-
- FILLZERO(rec);
- rec.type = REC_TYPE_LIBXC_CONTEXT;
-
- setup_write(egc, stream, "libxc header",
- &rec, NULL, libxc_header_done);
-}
-
-static void libxc_header_done(libxl__egc *egc,
- libxl__stream_write_state *stream)
-{
- libxl__xc_domain_save(egc, stream->dss, &stream->shs);
-}
-
-void libxl__xc_domain_save_done(libxl__egc *egc, void *dss_void,
- int rc, int retval, int errnoval)
-{
- libxl__domain_save_state *dss = dss_void;
- libxl__stream_write_state *stream = &dss->sws;
- STATE_AO_GC(dss->ao);
-
- if (rc)
- goto err;
-
- if (retval) {
- LOGEVD(ERROR, errnoval, dss->domid, "saving domain: %s",
- dss->dsps.guest_responded ?
- "domain responded to suspend request" :
- "domain did not respond to suspend request");
- if (!dss->dsps.guest_responded)
- rc = ERROR_GUEST_TIMEDOUT;
- else if (dss->rc)
- rc = dss->rc;
- else
- rc = ERROR_FAIL;
- goto err;
- }
-
- err:
- check_all_finished(egc, stream, rc);
-
- /*
- * This function is the callback associated with the save helper
- * task, not the stream task. We do not know whether the stream is
- * alive, and check_all_finished() may have torn it down around us.
- * If the stream is not still alive, we must not continue any work.
- */
- if (libxl__stream_write_inuse(stream)) {
- if (dss->checkpointed_stream != LIBXL_CHECKPOINTED_STREAM_NONE)
- /*
- * For remus, if libxl__xc_domain_save_done() completes,
- * there was an error sending data to the secondary.
- * Resume the primary ASAP. The caller doesn't care of the
- * return value (Please refer to libxl__remus_teardown())
- */
- stream_complete(egc, stream, 0);
- else
- write_emulator_xenstore_record(egc, stream);
- }
-}
-
-static void write_emulator_xenstore_record(libxl__egc *egc,
- libxl__stream_write_state *stream)
-{
- libxl__domain_save_state *dss = stream->dss;
- STATE_AO_GC(stream->ao);
- struct libxl__sr_rec_hdr rec;
- int rc;
- char *buf = NULL;
- uint32_t len = 0;
-
- if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
- emulator_xenstore_record_done(egc, stream);
- return;
- }
-
- rc = libxl__save_emulator_xenstore_data(dss, &buf, &len);
- if (rc)
- goto err;
-
- /* No record? - All done. */
- if (len == 0) {
- emulator_xenstore_record_done(egc, stream);
- return;
- }
-
- FILLZERO(rec);
- rec.type = REC_TYPE_EMULATOR_XENSTORE_DATA;
- rec.length = len + sizeof(stream->emu_sub_hdr);
-
- setup_emulator_write(egc, stream, "emulator xenstore record",
- &rec, &stream->emu_sub_hdr, buf,
- emulator_xenstore_record_done);
- return;
-
- err:
- assert(rc);
- stream_complete(egc, stream, rc);
-}
-
-static void emulator_xenstore_record_done(libxl__egc *egc,
- libxl__stream_write_state *stream)
-{
- libxl__domain_save_state *dss = stream->dss;
-
- if (dss->type == LIBXL_DOMAIN_TYPE_HVM)
- write_emulator_context_record(egc, stream);
- else {
- if (stream->in_checkpoint)
- write_checkpoint_end_record(egc, stream);
- else
- write_end_record(egc, stream);
- }
-}
-
-static void write_emulator_context_record(libxl__egc *egc,
- libxl__stream_write_state *stream)
-{
- libxl__domain_save_state *dss = stream->dss;
- libxl__datacopier_state *dc = &stream->emu_dc;
- STATE_AO_GC(stream->ao);
- struct libxl__sr_rec_hdr *rec = &stream->emu_rec_hdr;
- struct stat st;
- int rc;
-
- if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
- emulator_context_record_done(egc, stream);
- return;
- }
-
- /* Convenience aliases */
- const char *const filename = dss->dsps.dm_savefile;
-
- libxl__carefd_begin();
- int readfd = open(filename, O_RDONLY);
- stream->emu_carefd = libxl__carefd_opened(CTX, readfd);
- if (readfd == -1) {
- rc = ERROR_FAIL;
- LOGED(ERROR, dss->domid, "unable to open %s", filename);
- goto err;
- }
-
- if (fstat(readfd, &st)) {
- rc = ERROR_FAIL;
- LOGED(ERROR, dss->domid, "unable to fstat %s", filename);
- goto err;
- }
-
- if (!S_ISREG(st.st_mode)) {
- rc = ERROR_FAIL;
- LOGD(ERROR, dss->domid, "%s is not a plain file!", filename);
- goto err;
- }
-
- rec->type = REC_TYPE_EMULATOR_CONTEXT;
- rec->length = st.st_size + sizeof(stream->emu_sub_hdr);
- stream->emu_body = libxl__malloc(NOGC, st.st_size);
-
- FILLZERO(*dc);
- dc->ao = stream->ao;
- dc->readwhat = "qemu save file";
- dc->copywhat = "save v2 stream";
- dc->readfd = readfd;
- dc->writefd = -1;
- dc->maxsz = -1;
- dc->readbuf = stream->emu_body;
- dc->bytes_to_read = st.st_size;
- dc->callback = emulator_context_read_done;
-
- rc = libxl__datacopier_start(dc);
- if (rc)
- goto err;
-
- return;
-
- err:
- assert(rc);
- stream_complete(egc, stream, rc);
-}
-
-static void emulator_context_read_done(libxl__egc *egc,
- libxl__datacopier_state *dc,
- int rc, int onwrite, int errnoval)
-{
- libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, emu_dc);
- STATE_AO_GC(stream->ao);
-
- if (rc || onwrite || errnoval) {
- stream_complete(egc, stream, rc ?: ERROR_FAIL);
- return;
- }
-
- libxl__carefd_close(stream->emu_carefd);
- stream->emu_carefd = NULL;
-
- setup_emulator_write(egc, stream, "emulator record",
- &stream->emu_rec_hdr,
- &stream->emu_sub_hdr,
- stream->emu_body,
- emulator_context_record_done);
-}
-
-static void emulator_context_record_done(libxl__egc *egc,
- libxl__stream_write_state *stream)
-{
- free(stream->emu_body);
- stream->emu_body = NULL;
-
- if (stream->in_checkpoint)
- write_checkpoint_end_record(egc, stream);
- else
- write_end_record(egc, stream);
-}
-
-static void write_end_record(libxl__egc *egc,
- libxl__stream_write_state *stream)
-{
- struct libxl__sr_rec_hdr rec;
-
- FILLZERO(rec);
- rec.type = REC_TYPE_END;
-
- setup_write(egc, stream, "end record",
- &rec, NULL, stream_success);
-}
-
-static void write_checkpoint_end_record(libxl__egc *egc,
- libxl__stream_write_state *stream)
-{
- struct libxl__sr_rec_hdr rec;
-
- FILLZERO(rec);
- rec.type = REC_TYPE_CHECKPOINT_END;
-
- setup_write(egc, stream, "checkpoint end record",
- &rec, NULL, checkpoint_end_record_done);
-}
-
-static void checkpoint_end_record_done(libxl__egc *egc,
- libxl__stream_write_state *stream)
-{
- checkpoint_done(egc, stream, 0);
-}
-
-/*----- Success/error/cleanup handling. -----*/
-
-static void stream_success(libxl__egc *egc, libxl__stream_write_state *stream)
-{
- stream_complete(egc, stream, 0);
-}
-
-static void stream_complete(libxl__egc *egc,
- libxl__stream_write_state *stream, int rc)
-{
- assert(stream->running);
-
- if (stream->in_checkpoint) {
- assert(rc);
-
- /*
- * If an error is encountered while in a checkpoint, pass it
- * back to libxc. The failure will come back around to us via
- * libxl__xc_domain_save_done()
- */
- checkpoint_done(egc, stream, rc);
- return;
- }
-
- if (stream->in_checkpoint_state) {
- assert(rc);
-
- /*
- * If an error is encountered while in a checkpoint, pass it
- * back to libxc. The failure will come back around to us via
- * 1. normal stream
- * libxl__xc_domain_save_done()
- * 2. back_channel stream
- * libxl__stream_write_abort()
- */
- checkpoint_state_done(egc, stream, rc);
- return;
- }
-
- stream_done(egc, stream, rc);
-}
-
-static void stream_done(libxl__egc *egc,
- libxl__stream_write_state *stream, int rc)
-{
- assert(stream->running);
- assert(!stream->in_checkpoint_state);
- stream->running = false;
-
- if (stream->emu_carefd)
- libxl__carefd_close(stream->emu_carefd);
- free(stream->emu_body);
-
- if (!stream->back_channel) {
- /*
- * 1. In stream_done(), stream->running is set to false, so
- * the stream itself is not in use.
- * 2. Write stream is a back channel stream, this means it
- * is only used by secondary(restore side) to send records
- * back, so it doesn't have save helper.
- * So we don't need invoke check_all_finished here
- */
- check_all_finished(egc, stream, rc);
- }
-}
-
-static void checkpoint_done(libxl__egc *egc,
- libxl__stream_write_state *stream,
- int rc)
-{
- assert(stream->in_checkpoint);
-
- stream->in_checkpoint = false;
- stream->checkpoint_callback(egc, stream, rc);
-}
-
-static void check_all_finished(libxl__egc *egc,
- libxl__stream_write_state *stream,
- int rc)
-{
- STATE_AO_GC(stream->ao);
-
- /*
- * In the case of a failure, the _abort()'s below might cancel
- * synchronously on top of us, or asynchronously at a later point.
- *
- * We must avoid the situation where all _abort() cancel
- * synchronously and the completion_callback() gets called twice;
- * once by the first error and once by the final stacked abort(),
- * both of whom will find that all of the tasks have stopped.
- *
- * To avoid this problem, any stacked re-entry into this function is
- * ineligible to fire the completion callback. The outermost
- * instance will take care of completing, once the stack has
- * unwound.
- */
- if (stream->sync_teardown)
- return;
-
- if (!stream->rc && rc) {
- /* First reported failure. Tear everything down. */
- stream->rc = rc;
- stream->sync_teardown = true;
-
- libxl__stream_write_abort(egc, stream, rc);
- libxl__save_helper_abort(egc, &stream->shs);
-
- stream->sync_teardown = false;
- }
-
- /* Don't fire the callback until all our parallel tasks have stopped. */
- if (libxl__stream_write_inuse(stream) ||
- libxl__save_helper_inuse(&stream->shs))
- return;
-
- if (stream->completion_callback)
- /* back channel stream doesn't have completion_callback() */
- stream->completion_callback(egc, stream, stream->rc);
-}
-
-/*----- checkpoint state -----*/
-
-void libxl__stream_write_checkpoint_state(libxl__egc *egc,
- libxl__stream_write_state *stream,
- libxl_sr_checkpoint_state *srcs)
-{
- struct libxl__sr_rec_hdr rec;
-
- assert(stream->running);
- assert(!stream->in_checkpoint);
- assert(!stream->in_checkpoint_state);
- stream->in_checkpoint_state = true;
-
- FILLZERO(rec);
- rec.type = REC_TYPE_CHECKPOINT_STATE;
- rec.length = sizeof(*srcs);
-
- setup_write(egc, stream, "checkpoint state", &rec,
- srcs, write_checkpoint_state_done);
-}
-
-static void write_checkpoint_state_done(libxl__egc *egc,
- libxl__stream_write_state *stream)
-{
- checkpoint_state_done(egc, stream, 0);
-}
-
-static void checkpoint_state_done(libxl__egc *egc,
- libxl__stream_write_state *stream, int rc)
-{
- assert(stream->in_checkpoint_state);
- stream->in_checkpoint_state = false;
- stream->checkpoint_callback(egc, stream, rc);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * fdevent test helpr for the libxl event system
- */
-
-#include "libxl_internal.h"
-
-#include "libxl_test_fdevent.h"
-
-typedef struct {
- libxl__ao *ao;
- libxl__ev_fd fd;
- libxl__ao_abortable abrt;
-} libxl__test_fdevent;
-
-static void fdevent_complete(libxl__egc *egc, libxl__test_fdevent *tfe,
- int rc);
-
-static void tfe_init(libxl__test_fdevent *tfe, libxl__ao *ao)
-{
- tfe->ao = ao;
- libxl__ev_fd_init(&tfe->fd);
- libxl__ao_abortable_init(&tfe->abrt);
-}
-
-static void tfe_cleanup(libxl__gc *gc, libxl__test_fdevent *tfe)
-{
- libxl__ev_fd_deregister(gc, &tfe->fd);
- libxl__ao_abortable_deregister(&tfe->abrt);
-}
-
-static void tfe_fd_cb(libxl__egc *egc, libxl__ev_fd *ev,
- int fd, short events, short revents)
-{
- libxl__test_fdevent *tfe = CONTAINER_OF(ev,*tfe,fd);
- STATE_AO_GC(tfe->ao);
- fdevent_complete(egc, tfe, 0);
-}
-
-static void tfe_abrt_cb(libxl__egc *egc, libxl__ao_abortable *abrt,
- int rc)
-{
- libxl__test_fdevent *tfe = CONTAINER_OF(abrt,*tfe,abrt);
- STATE_AO_GC(tfe->ao);
- fdevent_complete(egc, tfe, rc);
-}
-
-static void fdevent_complete(libxl__egc *egc, libxl__test_fdevent *tfe,
- int rc)
-{
- STATE_AO_GC(tfe->ao);
- tfe_cleanup(gc, tfe);
- libxl__ao_complete(egc, ao, rc);
-}
-
-int libxl_test_fdevent(libxl_ctx *ctx, int fd, short events,
- libxl_asyncop_how *ao_how)
-{
- int rc;
- libxl__test_fdevent *tfe;
-
- AO_CREATE(ctx, 0, ao_how);
- GCNEW(tfe);
-
- tfe_init(tfe, ao);
-
- rc = libxl__ev_fd_register(gc, &tfe->fd, tfe_fd_cb, fd, events);
- if (rc) goto out;
-
- tfe->abrt.ao = ao;
- tfe->abrt.callback = tfe_abrt_cb;
- rc = libxl__ao_abortable_register(&tfe->abrt);
- if (rc) goto out;
-
- return AO_INPROGRESS;
-
- out:
- tfe_cleanup(gc, tfe);
- return AO_CREATE_FAIL(rc);
-}
+++ /dev/null
-#ifndef TEST_FDEVENT_H
-#define TEST_FDEVENT_H
-
-#include <pthread.h>
-
-int libxl_test_fdevent(libxl_ctx *ctx, int fd, short events,
- libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-/* This operation waits for one of the poll events to occur on fd, and
- * then completes successfully. (Or, it can be aborted.) */
-
-#endif /*TEST_FDEVENT_H*/
+++ /dev/null
-/*
- * timedereg test case for the libxl event system
- *
- * To run this test:
- * ./test_timedereg
- * Success:
- * program takes a few seconds, prints some debugging output and exits 0
- * Failure:
- * crash
- *
- * set up [0]-group timeouts 0 1 2
- * wait for timeout 1 to occur
- * deregister 0 and 2. 1 is supposed to be deregistered already
- * register [1]-group 0 1 2
- * deregister 1 (should be a no-op)
- * wait for [1]-group 0 1 2 in turn
- * on final callback assert that all have been deregistered
- */
-
-#include "libxl_internal.h"
-
-#include "libxl_test_timedereg.h"
-
-#define NTIMES 3
-static const int ms[2][NTIMES] = { { 2000,1000,2000 }, { 1000,2000,3000 } };
-static libxl__ev_time et[2][NTIMES];
-static libxl__ao *tao;
-static int seq;
-
-static void occurs(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs, int rc);
-
-static void regs(libxl__ao *ao, int j)
-{
- AO_GC;
- int rc, i;
- LOG(DEBUG,"regs(%d)", j);
- for (i=0; i<NTIMES; i++) {
- rc = libxl__ev_time_register_rel(ao, &et[j][i], occurs, ms[j][i]);
- assert(!rc);
- }
-}
-
-int libxl_test_timedereg(libxl_ctx *ctx, libxl_asyncop_how *ao_how)
-{
- int i;
- AO_CREATE(ctx, 0, ao_how);
-
- tao = ao;
-
- for (i=0; i<NTIMES; i++) {
- libxl__ev_time_init(&et[0][i]);
- libxl__ev_time_init(&et[1][i]);
- }
-
- regs(ao, 0);
-
- return AO_INPROGRESS;
-}
-
-static void occurs(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs, int rc)
-{
- EGC_GC;
- int i;
-
- int off = ev - &et[0][0];
- LOG(DEBUG,"occurs[%d][%d] seq=%d rc=%d", off/NTIMES, off%NTIMES, seq, rc);
-
- assert(rc == ERROR_TIMEDOUT);
-
- switch (seq) {
- case 0:
- assert(ev == &et[0][1]);
- libxl__ev_time_deregister(gc, &et[0][0]);
- libxl__ev_time_deregister(gc, &et[0][2]);
- regs(tao, 1);
- libxl__ev_time_deregister(gc, &et[0][1]);
- break;
-
- case 1:
- case 2:
- assert(ev == &et[1][seq-1]);
- break;
-
- case 3:
- assert(ev == &et[1][2]);
- for (i=0; i<NTIMES; i++) {
- assert(!libxl__ev_time_isregistered(&et[0][i]));
- assert(!libxl__ev_time_isregistered(&et[1][i]));
- }
- libxl__ao_complete(egc, tao, 0);
- return;
-
- default:
- abort();
- }
-
- seq++;
-}
+++ /dev/null
-#ifndef TEST_TIMEDEREG_H
-#define TEST_TIMEDEREG_H
-
-#include <pthread.h>
-
-int libxl_test_timedereg(libxl_ctx *ctx, libxl_asyncop_how *ao_how)
- LIBXL_EXTERNAL_CALLERS_ONLY;
-
-#endif /*TEST_TIMEDEREG_H*/
+++ /dev/null
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-/* TMEM is gone. Leave some stubs here. */
-
-char *libxl_tmem_list(libxl_ctx *ctx, uint32_t domid, int use_long)
-{
- GC_INIT(ctx);
- LOGED(ERROR, domid, "Can not get tmem list");
- GC_FREE;
- return NULL;
-}
-
-int libxl_tmem_freeze(libxl_ctx *ctx, uint32_t domid)
-{
- GC_INIT(ctx);
- LOGED(ERROR, domid, "Can not freeze tmem pools");
- GC_FREE;
- return ERROR_FAIL;
-}
-
-int libxl_tmem_thaw(libxl_ctx *ctx, uint32_t domid)
-{
- GC_INIT(ctx);
- LOGED(ERROR, domid, "Can not thaw tmem pools");
- GC_FREE;
- return ERROR_FAIL;
-}
-
-int libxl_tmem_set(libxl_ctx *ctx, uint32_t domid, char* name, uint32_t set)
-{
- GC_INIT(ctx);
- LOGED(ERROR, domid, "Can not set tmem %s", name);
- GC_FREE;
- return ERROR_FAIL;
-}
-
-int libxl_tmem_shared_auth(libxl_ctx *ctx, uint32_t domid,
- char* uuid, int auth)
-{
- GC_INIT(ctx);
- LOGED(ERROR, domid, "Can not set tmem shared auth");
- GC_FREE;
- return ERROR_FAIL;
-}
-
-int libxl_tmem_freeable(libxl_ctx *ctx)
-{
- GC_INIT(ctx);
- LOGE(ERROR, "Can not get tmem freeable memory");
- GC_FREE;
- return ERROR_FAIL;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-# -*- python -*-
-#
-# Builtin libxl types
-#
-
-namespace("libxl_")
-
-libxl_defbool = Builtin("defbool", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, copy_fn=None,
- check_default_fn="libxl__defbool_is_default")
-libxl_domid = Builtin("domid", json_gen_fn = "yajl_gen_integer", json_parse_fn = "libxl__uint32_parse_json",
- json_parse_type = "JSON_INTEGER", autogenerate_json = False, copy_fn=None)
-libxl_devid = Builtin("devid", json_gen_fn = "yajl_gen_integer", json_parse_fn = "libxl__int_parse_json",
- json_parse_type = "JSON_INTEGER", autogenerate_json = False, signed = True, init_val="-1",
- copy_fn=None)
-libxl_uuid = Builtin("uuid", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, check_default_fn="libxl_uuid_is_nil",
- copy_fn="libxl_uuid_copy")
-libxl_mac = Builtin("mac", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, check_default_fn="libxl__mac_is_default",
- copy_fn="libxl_mac_copy")
-libxl_bitmap = Builtin("bitmap", json_parse_type="JSON_ARRAY", dispose_fn="libxl_bitmap_dispose", passby=PASS_BY_REFERENCE,
- check_default_fn="libxl_bitmap_is_empty", copy_fn="libxl_bitmap_copy_alloc")
-libxl_cpuid_policy_list = Builtin("cpuid_policy_list", dispose_fn="libxl_cpuid_dispose", passby=PASS_BY_REFERENCE,
- json_parse_type="JSON_ARRAY", check_default_fn="libxl__cpuid_policy_is_empty",
- copy_fn="libxl_cpuid_policy_list_copy")
-
-libxl_string_list = Builtin("string_list", dispose_fn="libxl_string_list_dispose", passby=PASS_BY_REFERENCE,
- json_parse_type="JSON_ARRAY", check_default_fn="libxl__string_list_is_empty",
- copy_fn="libxl_string_list_copy")
-libxl_key_value_list = Builtin("key_value_list", dispose_fn="libxl_key_value_list_dispose", passby=PASS_BY_REFERENCE,
- json_parse_type="JSON_MAP", check_default_fn="libxl__key_value_list_is_empty",
- copy_fn="libxl_key_value_list_copy")
-libxl_hwcap = Builtin("hwcap", passby=PASS_BY_REFERENCE, json_parse_type="JSON_ARRAY",
- check_default_fn="libxl__hwcap_is_default", copy_fn="libxl_hwcap_copy")
-libxl_ms_vm_genid = Builtin("ms_vm_genid", passby=PASS_BY_REFERENCE, check_default_fn="libxl_ms_vm_genid_is_zero",
- copy_fn="libxl_ms_vm_genid_copy")
-
-#
-# Specific integer types
-#
-
-MemKB = UInt(64, init_val = "LIBXL_MEMKB_DEFAULT", json_gen_fn = "libxl__uint64_gen_json")
-
-#
-# Constants / Enumerations
-#
-
-libxl_error = Enumeration("error", [
- (-1, "NONSPECIFIC"),
- (-2, "VERSION"),
- (-3, "FAIL"),
- (-4, "NI"),
- (-5, "NOMEM"),
- (-6, "INVAL"),
- (-7, "BADFAIL"),
- (-8, "GUEST_TIMEDOUT"),
- (-9, "TIMEDOUT"),
- (-10, "NOPARAVIRT"),
- (-11, "NOT_READY"),
- (-12, "OSEVENT_REG_FAIL"),
- (-13, "BUFFERFULL"),
- (-14, "UNKNOWN_CHILD"),
- (-15, "LOCK_FAIL"),
- (-16, "JSON_CONFIG_EMPTY"),
- (-17, "DEVICE_EXISTS"),
- (-18, "CHECKPOINT_DEVOPS_DOES_NOT_MATCH"),
- (-19, "CHECKPOINT_DEVICE_NOT_SUPPORTED"),
- (-20, "VNUMA_CONFIG_INVALID"),
- (-21, "DOMAIN_NOTFOUND"),
- (-22, "ABORTED"),
- (-23, "NOTFOUND"),
- (-24, "DOMAIN_DESTROYED"), # Target domain ceased to exist during op
- (-25, "FEATURE_REMOVED"), # For functionality that has been removed
- (-26, "PROTOCOL_ERROR_QMP"),
- (-27, "UNKNOWN_QMP_ERROR"),
- (-28, "QMP_GENERIC_ERROR"), # unspecified qmp error
- (-29, "QMP_COMMAND_NOT_FOUND"), # the requested command has not been found
- (-30, "QMP_DEVICE_NOT_ACTIVE"), # a device has failed to be become active
- (-31, "QMP_DEVICE_NOT_FOUND"), # the requested device has not been found
- (-32, "QEMU_API"), # QEMU's replies don't contains expected members
- ], value_namespace = "")
-
-libxl_domain_type = Enumeration("domain_type", [
- (-1, "INVALID"),
- (1, "HVM"),
- (2, "PV"),
- (3, "PVH"),
- ], init_val = "LIBXL_DOMAIN_TYPE_INVALID")
-
-libxl_rdm_reserve_strategy = Enumeration("rdm_reserve_strategy", [
- (0, "ignore"),
- (1, "host"),
- ])
-
-libxl_rdm_reserve_policy = Enumeration("rdm_reserve_policy", [
- (-1, "invalid"),
- (0, "strict"),
- (1, "relaxed"),
- ], init_val = "LIBXL_RDM_RESERVE_POLICY_INVALID")
-
-libxl_channel_connection = Enumeration("channel_connection", [
- (0, "UNKNOWN"),
- (1, "PTY"),
- (2, "SOCKET"), # a listening Unix domain socket
- ])
-
-libxl_device_model_version = Enumeration("device_model_version", [
- (0, "UNKNOWN"),
- (1, "QEMU_XEN_TRADITIONAL"), # Historical qemu-xen device model (qemu-dm)
- (2, "QEMU_XEN"), # Upstream based qemu-xen device model
- ])
-
-libxl_console_type = Enumeration("console_type", [
- (0, "UNKNOWN"),
- (1, "SERIAL"),
- (2, "PV"),
- (3, "VUART"),
- ])
-
-libxl_disk_format = Enumeration("disk_format", [
- (0, "UNKNOWN"),
- (1, "QCOW"),
- (2, "QCOW2"),
- (3, "VHD"),
- (4, "RAW"),
- (5, "EMPTY"),
- (6, "QED"),
- ])
-
-libxl_disk_backend = Enumeration("disk_backend", [
- (0, "UNKNOWN"),
- (1, "PHY"),
- (2, "TAP"),
- (3, "QDISK"),
- ])
-
-libxl_nic_type = Enumeration("nic_type", [
- (0, "UNKNOWN"),
- (1, "VIF_IOEMU"),
- (2, "VIF"),
- ])
-
-libxl_action_on_shutdown = Enumeration("action_on_shutdown", [
- (1, "DESTROY"),
-
- (2, "RESTART"),
- (3, "RESTART_RENAME"),
-
- (4, "PRESERVE"),
-
- (5, "COREDUMP_DESTROY"),
- (6, "COREDUMP_RESTART"),
-
- (7, "SOFT_RESET"),
- ], init_val = "LIBXL_ACTION_ON_SHUTDOWN_DESTROY")
-
-libxl_trigger = Enumeration("trigger", [
- (0, "UNKNOWN"),
- (1, "POWER"),
- (2, "SLEEP"),
- (3, "NMI"),
- (4, "INIT"),
- (5, "RESET"),
- (6, "S3RESUME"),
- ])
-
-libxl_tsc_mode = Enumeration("tsc_mode", [
- (0, "default"),
- (1, "always_emulate"),
- (2, "native"),
- (3, "native_paravirt"),
- ])
-
-libxl_gfx_passthru_kind = Enumeration("gfx_passthru_kind", [
- (0, "default"),
- (1, "igd"),
- ])
-
-# Consistent with the values defined for HVM_PARAM_TIMER_MODE.
-libxl_timer_mode = Enumeration("timer_mode", [
- (-1, "unknown"),
- (0, "delay_for_missed_ticks"),
- (1, "no_delay_for_missed_ticks"),
- (2, "no_missed_ticks_pending"),
- (3, "one_missed_tick_pending"),
- ], init_val = "LIBXL_TIMER_MODE_DEFAULT",
- check_default_fn = "libxl__timer_mode_is_default")
-
-libxl_bios_type = Enumeration("bios_type", [
- (0, "unknown"),
- (1, "rombios"),
- (2, "seabios"),
- (3, "ovmf"),
- ])
-
-# Consistent with values defined in domctl.h
-# Except unknown which we have made up
-libxl_scheduler = Enumeration("scheduler", [
- (0, "unknown"),
- (4, "sedf"),
- (5, "credit"),
- (6, "credit2"),
- (7, "arinc653"),
- (8, "rtds"),
- (9, "null"),
- ])
-
-# Consistent with SHUTDOWN_* in sched.h (apart from UNKNOWN)
-libxl_shutdown_reason = Enumeration("shutdown_reason", [
- (-1, "unknown"),
- (0, "poweroff"),
- (1, "reboot"),
- (2, "suspend"),
- (3, "crash"),
- (4, "watchdog"),
- (5, "soft_reset"),
- ], init_val = "LIBXL_SHUTDOWN_REASON_UNKNOWN")
-
-libxl_vga_interface_type = Enumeration("vga_interface_type", [
- (0, "UNKNOWN"),
- (1, "CIRRUS"),
- (2, "STD"),
- (3, "NONE"),
- (4, "QXL"),
- ], init_val = "LIBXL_VGA_INTERFACE_TYPE_UNKNOWN")
-
-libxl_vendor_device = Enumeration("vendor_device", [
- (0, "NONE"),
- (1, "XENSERVER"),
- ])
-
-libxl_viridian_enlightenment = Enumeration("viridian_enlightenment", [
- (0, "base"),
- (1, "freq"),
- (2, "time_ref_count"),
- (3, "reference_tsc"),
- (4, "hcall_remote_tlb_flush"),
- (5, "apic_assist"),
- (6, "crash_ctl"),
- (7, "synic"),
- (8, "stimer"),
- (9, "hcall_ipi"),
- ])
-
-libxl_hdtype = Enumeration("hdtype", [
- (1, "IDE"),
- (2, "AHCI"),
- ], init_val = "LIBXL_HDTYPE_IDE")
-
-# Consistent with the values defined for migration_stream.
-libxl_checkpointed_stream = Enumeration("checkpointed_stream", [
- (0, "NONE"),
- (1, "REMUS"),
- (2, "COLO"),
- ])
-
-libxl_vuart_type = Enumeration("vuart_type", [
- (0, "unknown"),
- (1, "sbsa_uart"),
- ])
-
-libxl_vkb_backend = Enumeration("vkb_backend", [
- (0, "UNKNOWN"),
- (1, "QEMU"),
- (2, "LINUX")
- ])
-
-libxl_passthrough = Enumeration("passthrough", [
- (0, "default"),
- (1, "disabled"),
- (2, "enabled"), # becomes {sync,share}_pt once defaults are evaluated
- (3, "sync_pt"),
- (4, "share_pt"),
- ])
-
-#
-# Complex libxl types
-#
-
-libxl_ioport_range = Struct("ioport_range", [
- ("first", uint32),
- ("number", uint32),
- ])
-
-libxl_iomem_range = Struct("iomem_range", [
- # start host frame number to be mapped to the guest
- ("start", uint64),
- # number of frames to be mapped
- ("number", uint64),
- # guest frame number used as a start for the mapping
- ("gfn", uint64, {'init_val': "LIBXL_INVALID_GFN"}),
- ])
-
-libxl_vga_interface_info = Struct("vga_interface_info", [
- ("kind", libxl_vga_interface_type),
- ])
-
-libxl_vnc_info = Struct("vnc_info", [
- ("enable", libxl_defbool),
- # "address:port" that should be listened on
- ("listen", string),
- ("passwd", string),
- ("display", integer),
- # If set then try to find an unused port
- ("findunused", libxl_defbool),
- ])
-
-libxl_spice_info = Struct("spice_info", [
- ("enable", libxl_defbool),
- # At least one of spice port or spicetls_post must be given
- ("port", integer),
- ("tls_port", integer),
- # Interface to bind to
- ("host", string),
- # enable client connection with no password
- ("disable_ticketing", libxl_defbool),
- ("passwd", string),
- ("agent_mouse", libxl_defbool),
- ("vdagent", libxl_defbool),
- ("clipboard_sharing", libxl_defbool),
- ("usbredirection", integer),
- ("image_compression", string),
- ("streaming_video", string),
- ])
-
-libxl_sdl_info = Struct("sdl_info", [
- ("enable", libxl_defbool),
- ("opengl", libxl_defbool),
- ("display", string),
- ("xauthority", string),
- ])
-
-libxl_dominfo = Struct("dominfo",[
- ("uuid", libxl_uuid),
- ("domid", libxl_domid),
- ("ssidref", uint32),
- ("ssid_label", string),
- ("running", bool),
- ("blocked", bool),
- ("paused", bool),
- ("shutdown", bool),
- ("dying", bool),
- ("never_stop", bool),
-
- # Valid iff ->shutdown is true.
- #
- # Otherwise set to a value guaranteed not to clash with any valid
- # LIBXL_SHUTDOWN_REASON_* constant.
- ("shutdown_reason", libxl_shutdown_reason),
- ("outstanding_memkb", MemKB),
- ("current_memkb", MemKB),
- ("shared_memkb", MemKB),
- ("paged_memkb", MemKB),
- ("max_memkb", MemKB),
- ("cpu_time", uint64),
- ("vcpu_max_id", uint32),
- ("vcpu_online", uint32),
- ("cpupool", uint32),
- ("domain_type", libxl_domain_type),
- ], dir=DIR_OUT)
-
-libxl_cpupoolinfo = Struct("cpupoolinfo", [
- ("poolid", uint32),
- ("pool_name", string),
- ("sched", libxl_scheduler),
- ("n_dom", uint32),
- ("cpumap", libxl_bitmap)
- ], dir=DIR_OUT)
-
-libxl_channelinfo = Struct("channelinfo", [
- ("backend", string),
- ("backend_id", uint32),
- ("frontend", string),
- ("frontend_id", uint32),
- ("devid", libxl_devid),
- ("state", integer),
- ("evtch", integer),
- ("rref", integer),
- ("u", KeyedUnion(None, libxl_channel_connection, "connection",
- [("unknown", None),
- ("pty", Struct(None, [("path", string),])),
- ("socket", None),
- ])),
- ], dir=DIR_OUT)
-
-libxl_vminfo = Struct("vminfo", [
- ("uuid", libxl_uuid),
- ("domid", libxl_domid),
- ], dir=DIR_OUT)
-
-libxl_version_info = Struct("version_info", [
- ("xen_version_major", integer),
- ("xen_version_minor", integer),
- ("xen_version_extra", string),
- ("compiler", string),
- ("compile_by", string),
- ("compile_domain", string),
- ("compile_date", string),
- ("capabilities", string),
- ("changeset", string),
- ("virt_start", uint64),
- ("pagesize", integer),
- ("commandline", string),
- ("build_id", string),
- ], dir=DIR_OUT)
-
-libxl_domain_create_info = Struct("domain_create_info",[
- ("type", libxl_domain_type),
- ("hap", libxl_defbool),
- ("oos", libxl_defbool),
- ("ssidref", uint32),
- ("ssid_label", string),
- ("name", string),
- ("domid", libxl_domid),
- ("uuid", libxl_uuid),
- ("xsdata", libxl_key_value_list),
- ("platformdata", libxl_key_value_list),
- ("poolid", uint32),
- ("pool_name", string),
- ("run_hotplug_scripts",libxl_defbool),
- ("driver_domain",libxl_defbool),
- ("passthrough", libxl_passthrough),
- ("xend_suspend_evtchn_compat",libxl_defbool),
- ], dir=DIR_IN)
-
-libxl_domain_restore_params = Struct("domain_restore_params", [
- ("checkpointed_stream", integer),
- ("stream_version", uint32, {'init_val': '1'}),
- ("colo_proxy_script", string),
- ("userspace_colo_proxy", libxl_defbool),
- ])
-
-libxl_sched_params = Struct("sched_params",[
- ("vcpuid", integer, {'init_val': 'LIBXL_SCHED_PARAM_VCPU_INDEX_DEFAULT'}),
- ("weight", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT'}),
- ("cap", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT'}),
- ("period", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT'}),
- ("extratime", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT'}),
- ("budget", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT'}),
- ])
-
-libxl_vcpu_sched_params = Struct("vcpu_sched_params",[
- ("sched", libxl_scheduler),
- ("vcpus", Array(libxl_sched_params, "num_vcpus")),
- ])
-
-libxl_domain_sched_params = Struct("domain_sched_params",[
- ("sched", libxl_scheduler),
- ("weight", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT'}),
- ("cap", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT'}),
- ("period", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT'}),
- ("budget", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT'}),
- ("extratime", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT'}),
-
- # The following three parameters ('slice' and 'latency') are deprecated,
- # and will have no effect if used, since the SEDF scheduler has been removed.
- # Note that 'period' and 'extratime' was an SDF parameter too, but it is still effective
- # as they are now used (together with 'budget') by the RTDS scheduler.
- ("slice", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_SLICE_DEFAULT'}),
- ("latency", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_LATENCY_DEFAULT'}),
- ])
-
-libxl_vnode_info = Struct("vnode_info", [
- ("memkb", MemKB),
- ("distances", Array(uint32, "num_distances")), # distances from this node to other nodes
- ("pnode", uint32), # physical node of this node
- ("vcpus", libxl_bitmap), # vcpus in this node
- ])
-
-libxl_gic_version = Enumeration("gic_version", [
- (0, "DEFAULT"),
- (0x20, "v2"),
- (0x30, "v3")
- ], init_val = "LIBXL_GIC_VERSION_DEFAULT")
-
-libxl_tee_type = Enumeration("tee_type", [
- (0, "none"),
- (1, "optee")
- ], init_val = "LIBXL_TEE_TYPE_NONE")
-
-libxl_rdm_reserve = Struct("rdm_reserve", [
- ("strategy", libxl_rdm_reserve_strategy),
- ("policy", libxl_rdm_reserve_policy),
- ])
-
-# Consistent with the values defined for HVM_PARAM_ALTP2M
-libxl_altp2m_mode = Enumeration("altp2m_mode", [
- (0, "disabled"),
- (1, "mixed"),
- (2, "external"),
- (3, "limited"),
- ], init_val = "LIBXL_ALTP2M_MODE_DISABLED")
-
-libxl_domain_build_info = Struct("domain_build_info",[
- ("max_vcpus", integer),
- ("avail_vcpus", libxl_bitmap),
- ("cpumap", libxl_bitmap),
- ("nodemap", libxl_bitmap),
- ("vcpu_hard_affinity", Array(libxl_bitmap, "num_vcpu_hard_affinity")),
- ("vcpu_soft_affinity", Array(libxl_bitmap, "num_vcpu_soft_affinity")),
- ("numa_placement", libxl_defbool),
- ("tsc_mode", libxl_tsc_mode),
- ("max_memkb", MemKB),
- ("target_memkb", MemKB),
- ("video_memkb", MemKB),
- ("shadow_memkb", MemKB),
- ("iommu_memkb", MemKB),
- ("rtc_timeoffset", uint32),
- ("exec_ssidref", uint32),
- ("exec_ssid_label", string),
- ("localtime", libxl_defbool),
- ("disable_migrate", libxl_defbool),
- ("cpuid", libxl_cpuid_policy_list),
- ("blkdev_start", string),
-
- ("vnuma_nodes", Array(libxl_vnode_info, "num_vnuma_nodes")),
-
- ("max_grant_frames", uint32, {'init_val': 'LIBXL_MAX_GRANT_DEFAULT'}),
- ("max_maptrack_frames", uint32, {'init_val': 'LIBXL_MAX_GRANT_DEFAULT'}),
-
- ("device_model_version", libxl_device_model_version),
- ("device_model_stubdomain", libxl_defbool),
- ("stubdomain_memkb", MemKB),
- ("stubdomain_kernel", string),
- ("stubdomain_ramdisk", string),
- # if you set device_model you must set device_model_version too
- ("device_model", string),
- ("device_model_ssidref", uint32),
- ("device_model_ssid_label", string),
- ("device_model_user", string),
-
- # extra parameters pass directly to qemu, NULL terminated
- ("extra", libxl_string_list),
- # extra parameters pass directly to qemu for PV guest, NULL terminated
- ("extra_pv", libxl_string_list),
- # extra parameters pass directly to qemu for HVM guest, NULL terminated
- ("extra_hvm", libxl_string_list),
- # parameters for all type of scheduler
- ("sched_params", libxl_domain_sched_params),
-
- ("ioports", Array(libxl_ioport_range, "num_ioports")),
- ("irqs", Array(uint32, "num_irqs")),
- ("iomem", Array(libxl_iomem_range, "num_iomem")),
- ("claim_mode", libxl_defbool),
- ("event_channels", uint32),
- ("kernel", string),
- ("cmdline", string),
- ("ramdisk", string),
- # Given the complexity of verifying the validity of a device tree,
- # libxl doesn't do any security check on it. It's the responsibility
- # of the caller to provide only trusted device tree.
- # Note that the partial device tree should avoid to use the phandle
- # 65000 which is reserved by the toolstack.
- ("device_tree", string),
- ("acpi", libxl_defbool),
- ("bootloader", string),
- ("bootloader_args", libxl_string_list),
- ("timer_mode", libxl_timer_mode),
- ("nested_hvm", libxl_defbool),
- ("apic", libxl_defbool),
- ("dm_restrict", libxl_defbool),
- ("tee", libxl_tee_type),
- ("u", KeyedUnion(None, libxl_domain_type, "type",
- [("hvm", Struct(None, [("firmware", string),
- ("bios", libxl_bios_type),
- ("pae", libxl_defbool),
- ("apic", libxl_defbool, {'deprecated_by': 'apic'}),
- # The following acpi field is deprecated.
- # Please use the unified acpi field above
- # which works for both x86 and ARM.
- ("acpi", libxl_defbool),
- ("acpi_s3", libxl_defbool),
- ("acpi_s4", libxl_defbool),
- ("acpi_laptop_slate",libxl_defbool),
- ("nx", libxl_defbool),
- ("viridian", libxl_defbool),
- ("viridian_enable", libxl_bitmap),
- ("viridian_disable", libxl_bitmap),
- ("timeoffset", string),
- ("hpet", libxl_defbool),
- ("vpt_align", libxl_defbool),
- ("mmio_hole_memkb", MemKB),
- ("timer_mode", libxl_timer_mode, {'deprecated_by': 'timer_mode'}),
- ("nested_hvm", libxl_defbool, {'deprecated_by': 'nested_hvm'}),
- # The u.hvm.altp2m field is used solely
- # for x86 HVM guests and is maintained
- # for legacy purposes.
- ("altp2m", libxl_defbool),
- ("system_firmware", string),
- ("smbios_firmware", string),
- ("acpi_firmware", string),
- ("hdtype", libxl_hdtype),
- ("nographic", libxl_defbool),
- ("vga", libxl_vga_interface_info),
- ("vnc", libxl_vnc_info),
- # keyboard layout, default is en-us keyboard
- ("keymap", string),
- ("sdl", libxl_sdl_info),
- ("spice", libxl_spice_info),
-
- ("gfx_passthru", libxl_defbool),
- ("gfx_passthru_kind", libxl_gfx_passthru_kind),
-
- ("serial", string),
- ("boot", string),
- ("usb", libxl_defbool),
- ("usbversion", integer),
- # usbdevice:
- # - "tablet" for absolute mouse,
- # - "mouse" for PS/2 protocol relative mouse
- ("usbdevice", string),
- ("vkb_device", libxl_defbool),
- ("soundhw", string),
- ("xen_platform_pci", libxl_defbool),
- ("usbdevice_list", libxl_string_list),
- ("vendor_device", libxl_vendor_device),
- # See libxl_ms_vm_genid_generate()
- ("ms_vm_genid", libxl_ms_vm_genid),
- ("serial_list", libxl_string_list),
- ("rdm", libxl_rdm_reserve),
- ("rdm_mem_boundary_memkb", MemKB),
- ("mca_caps", uint64),
- ])),
- ("pv", Struct(None, [("kernel", string, {'deprecated_by': 'kernel'}),
- ("slack_memkb", MemKB),
- ("bootloader", string, {'deprecated_by': 'bootloader'}),
- ("bootloader_args", libxl_string_list, {'deprecated_by': 'bootloader_args'}),
- ("cmdline", string, {'deprecated_by': 'cmdline'}),
- ("ramdisk", string, {'deprecated_by': 'ramdisk'}),
- ("features", string, {'const': True}),
- # Use host's E820 for PCI passthrough.
- ("e820_host", libxl_defbool),
- ])),
- ("pvh", Struct(None, [("pvshim", libxl_defbool),
- ("pvshim_path", string),
- ("pvshim_cmdline", string),
- ("pvshim_extra", string), # eg "loglvl=all guest_loglvl=all apic_verbosity=debug e820-verbose"
- ])),
- ("invalid", None),
- ], keyvar_init_val = "LIBXL_DOMAIN_TYPE_INVALID")),
-
-
- ("arch_arm", Struct(None, [("gic_version", libxl_gic_version),
- ("vuart", libxl_vuart_type),
- ])),
- # Alternate p2m is not bound to any architecture or guest type, as it is
- # supported by x86 HVM and ARM support is planned.
- ("altp2m", libxl_altp2m_mode),
-
- ], dir=DIR_IN,
- copy_deprecated_fn="libxl__domain_build_info_copy_deprecated",
-)
-
-libxl_device_vfb = Struct("device_vfb", [
- ("backend_domid", libxl_domid),
- ("backend_domname",string),
- ("devid", libxl_devid),
- ("vnc", libxl_vnc_info),
- ("sdl", libxl_sdl_info),
- # set keyboard layout, default is en-us keyboard
- ("keymap", string),
- ])
-
-libxl_device_vkb = Struct("device_vkb", [
- ("backend_domid", libxl_domid),
- ("backend_domname", string),
- ("devid", libxl_devid),
- ("backend_type", libxl_vkb_backend),
- ("unique_id", string),
- ("feature_disable_keyboard", bool),
- ("feature_disable_pointer", bool),
- ("feature_abs_pointer", bool),
- ("feature_raw_pointer", bool),
- ("feature_multi_touch", bool),
- ("width", uint32),
- ("height", uint32),
- ("multi_touch_width", uint32),
- ("multi_touch_height", uint32),
- ("multi_touch_num_contacts", uint32)
- ])
-
-libxl_device_disk = Struct("device_disk", [
- ("backend_domid", libxl_domid),
- ("backend_domname", string),
- ("pdev_path", string),
- ("vdev", string),
- ("backend", libxl_disk_backend),
- ("format", libxl_disk_format),
- ("script", string),
- ("removable", integer),
- ("readwrite", integer),
- ("is_cdrom", integer),
- ("direct_io_safe", bool),
- ("discard_enable", libxl_defbool),
- # Note that the COLO configuration settings should be considered unstable.
- # They may change incompatibly in future versions of Xen.
- ("colo_enable", libxl_defbool),
- ("colo_restore_enable", libxl_defbool),
- ("colo_host", string),
- ("colo_port", integer),
- ("colo_export", string),
- ("active_disk", string),
- ("hidden_disk", string)
- ])
-
-libxl_device_nic = Struct("device_nic", [
- ("backend_domid", libxl_domid),
- ("backend_domname", string),
- ("devid", libxl_devid),
- ("mtu", integer),
- ("model", string),
- ("mac", libxl_mac),
- ("ip", string),
- ("bridge", string),
- ("ifname", string),
- ("script", string),
- ("nictype", libxl_nic_type),
- ("rate_bytes_per_interval", uint64),
- ("rate_interval_usecs", uint32),
- ("gatewaydev", string),
- # Note that the COLO configuration settings should be considered unstable.
- # They may change incompatibly in future versions of Xen.
- ("coloft_forwarddev", string),
- ("colo_sock_mirror_id", string),
- ("colo_sock_mirror_ip", string),
- ("colo_sock_mirror_port", string),
- ("colo_sock_compare_pri_in_id", string),
- ("colo_sock_compare_pri_in_ip", string),
- ("colo_sock_compare_pri_in_port", string),
- ("colo_sock_compare_sec_in_id", string),
- ("colo_sock_compare_sec_in_ip", string),
- ("colo_sock_compare_sec_in_port", string),
- ("colo_sock_compare_notify_id", string),
- ("colo_sock_compare_notify_ip", string),
- ("colo_sock_compare_notify_port", string),
- ("colo_sock_redirector0_id", string),
- ("colo_sock_redirector0_ip", string),
- ("colo_sock_redirector0_port", string),
- ("colo_sock_redirector1_id", string),
- ("colo_sock_redirector1_ip", string),
- ("colo_sock_redirector1_port", string),
- ("colo_sock_redirector2_id", string),
- ("colo_sock_redirector2_ip", string),
- ("colo_sock_redirector2_port", string),
- ("colo_filter_mirror_queue", string),
- ("colo_filter_mirror_outdev", string),
- ("colo_filter_redirector0_queue", string),
- ("colo_filter_redirector0_indev", string),
- ("colo_filter_redirector0_outdev", string),
- ("colo_filter_redirector1_queue", string),
- ("colo_filter_redirector1_indev", string),
- ("colo_filter_redirector1_outdev", string),
- ("colo_compare_pri_in", string),
- ("colo_compare_sec_in", string),
- ("colo_compare_out", string),
- ("colo_compare_notify_dev", string),
- ("colo_sock_sec_redirector0_id", string),
- ("colo_sock_sec_redirector0_ip", string),
- ("colo_sock_sec_redirector0_port", string),
- ("colo_sock_sec_redirector1_id", string),
- ("colo_sock_sec_redirector1_ip", string),
- ("colo_sock_sec_redirector1_port", string),
- ("colo_filter_sec_redirector0_queue", string),
- ("colo_filter_sec_redirector0_indev", string),
- ("colo_filter_sec_redirector0_outdev", string),
- ("colo_filter_sec_redirector1_queue", string),
- ("colo_filter_sec_redirector1_indev", string),
- ("colo_filter_sec_redirector1_outdev", string),
- ("colo_filter_sec_rewriter0_queue", string),
- ("colo_checkpoint_host", string),
- ("colo_checkpoint_port", string)
- ])
-
-libxl_device_pci = Struct("device_pci", [
- ("func", uint8),
- ("dev", uint8),
- ("bus", uint8),
- ("domain", integer),
- ("vdevfn", uint32),
- ("vfunc_mask", uint32),
- ("msitranslate", bool),
- ("power_mgmt", bool),
- ("permissive", bool),
- ("seize", bool),
- ("rdm_policy", libxl_rdm_reserve_policy),
- ])
-
-libxl_device_rdm = Struct("device_rdm", [
- ("start", uint64),
- ("size", uint64),
- ("policy", libxl_rdm_reserve_policy),
- ])
-
-libxl_usbctrl_type = Enumeration("usbctrl_type", [
- (0, "AUTO"),
- (1, "PV"),
- (2, "DEVICEMODEL"),
- (3, "QUSB"),
- ])
-
-libxl_usbdev_type = Enumeration("usbdev_type", [
- (1, "hostdev"),
- ])
-
-libxl_device_usbctrl = Struct("device_usbctrl", [
- ("type", libxl_usbctrl_type),
- ("devid", libxl_devid),
- ("version", integer),
- ("ports", integer),
- ("backend_domid", libxl_domid),
- ("backend_domname", string),
- ])
-
-libxl_device_usbdev = Struct("device_usbdev", [
- ("ctrl", libxl_devid),
- ("port", integer),
- ("u", KeyedUnion(None, libxl_usbdev_type, "type",
- [("hostdev", Struct(None, [
- ("hostbus", uint8),
- ("hostaddr", uint8)])),
- ])),
- ])
-
-libxl_device_dtdev = Struct("device_dtdev", [
- ("path", string),
- ])
-
-libxl_device_vtpm = Struct("device_vtpm", [
- ("backend_domid", libxl_domid),
- ("backend_domname", string),
- ("devid", libxl_devid),
- ("uuid", libxl_uuid),
-])
-
-libxl_device_p9 = Struct("device_p9", [
- ("backend_domid", libxl_domid),
- ("backend_domname", string),
- ("tag", string),
- ("path", string),
- ("security_model", string),
- ("devid", libxl_devid),
-])
-
-libxl_device_pvcallsif = Struct("device_pvcallsif", [
- ("backend_domid", libxl_domid),
- ("backend_domname", string),
- ("devid", libxl_devid),
-])
-
-libxl_device_channel = Struct("device_channel", [
- ("backend_domid", libxl_domid),
- ("backend_domname", string),
- ("devid", libxl_devid),
- ("name", string),
- ("u", KeyedUnion(None, libxl_channel_connection, "connection",
- [("unknown", None),
- ("pty", None),
- ("socket", Struct(None, [("path", string)])),
- ])),
-])
-
-libxl_connector_param = Struct("connector_param", [
- ("unique_id", string),
- ("width", uint32),
- ("height", uint32)
- ])
-
-libxl_device_vdispl = Struct("device_vdispl", [
- ("backend_domid", libxl_domid),
- ("backend_domname", string),
- ("devid", libxl_devid),
- ("be_alloc", bool),
- ("connectors", Array(libxl_connector_param, "num_connectors"))
- ])
-
-libxl_vsnd_pcm_format = Enumeration("vsnd_pcm_format", [
- (1, "S8"),
- (2, "U8"),
- (3, "S16_LE"),
- (4, "S16_BE"),
- (5, "U16_LE"),
- (6, "U16_BE"),
- (7, "S24_LE"),
- (8, "S24_BE"),
- (9, "U24_LE"),
- (10, "U24_BE"),
- (11, "S32_LE"),
- (12, "S32_BE"),
- (13, "U32_LE"),
- (14, "U32_BE"),
- (15, "F32_LE"),
- (16, "F32_BE"),
- (17, "F64_LE"),
- (18, "F64_BE"),
- (19, "IEC958_SUBFRAME_LE"),
- (20, "IEC958_SUBFRAME_BE"),
- (21, "MU_LAW"),
- (22, "A_LAW"),
- (23, "IMA_ADPCM"),
- (24, "MPEG"),
- (25, "GSM")
- ])
-
-libxl_vsnd_params = Struct("vsnd_params", [
- ("sample_rates", Array(uint32, "num_sample_rates")),
- ("sample_formats", Array(libxl_vsnd_pcm_format, "num_sample_formats")),
- ("channels_min", uint32),
- ("channels_max", uint32),
- ("buffer_size", uint32)
- ])
-
-libxl_vsnd_stream_type = Enumeration("vsnd_stream_type", [
- (1, "P"),
- (2, "C")
- ])
-
-libxl_vsnd_stream = Struct("vsnd_stream", [
- ("unique_id", string),
- ("type", libxl_vsnd_stream_type),
- ("params", libxl_vsnd_params)
- ])
-
-libxl_vsnd_pcm = Struct("vsnd_pcm", [
- ("name", string),
- ("params", libxl_vsnd_params),
- ("streams", Array(libxl_vsnd_stream, "num_vsnd_streams"))
- ])
-
-libxl_device_vsnd = Struct("device_vsnd", [
- ("backend_domid", libxl_domid),
- ("backend_domname", string),
- ("devid", libxl_devid),
- ("short_name", string),
- ("long_name", string),
- ("params", libxl_vsnd_params),
- ("pcms", Array(libxl_vsnd_pcm, "num_vsnd_pcms"))
- ])
-
-libxl_domain_config = Struct("domain_config", [
- ("c_info", libxl_domain_create_info),
- ("b_info", libxl_domain_build_info),
-
- ("disks", Array(libxl_device_disk, "num_disks")),
- ("nics", Array(libxl_device_nic, "num_nics")),
- ("pcidevs", Array(libxl_device_pci, "num_pcidevs")),
- ("rdms", Array(libxl_device_rdm, "num_rdms")),
- ("dtdevs", Array(libxl_device_dtdev, "num_dtdevs")),
- ("vfbs", Array(libxl_device_vfb, "num_vfbs")),
- ("vkbs", Array(libxl_device_vkb, "num_vkbs")),
- ("vtpms", Array(libxl_device_vtpm, "num_vtpms")),
- ("p9s", Array(libxl_device_p9, "num_p9s")),
- ("pvcallsifs", Array(libxl_device_pvcallsif, "num_pvcallsifs")),
- ("vdispls", Array(libxl_device_vdispl, "num_vdispls")),
- ("vsnds", Array(libxl_device_vsnd, "num_vsnds")),
- # a channel manifests as a console with a name,
- # see docs/misc/channels.txt
- ("channels", Array(libxl_device_channel, "num_channels")),
- ("usbctrls", Array(libxl_device_usbctrl, "num_usbctrls")),
- ("usbdevs", Array(libxl_device_usbdev, "num_usbdevs")),
-
- ("on_poweroff", libxl_action_on_shutdown),
- ("on_reboot", libxl_action_on_shutdown),
- ("on_watchdog", libxl_action_on_shutdown),
- ("on_crash", libxl_action_on_shutdown),
- ("on_soft_reset", libxl_action_on_shutdown),
- ], dir=DIR_IN)
-
-libxl_diskinfo = Struct("diskinfo", [
- ("backend", string),
- ("backend_id", uint32),
- ("frontend", string),
- ("frontend_id", uint32),
- ("devid", libxl_devid),
- ("state", integer),
- ("evtch", integer),
- ("rref", integer),
- ], dir=DIR_OUT)
-
-libxl_nicinfo = Struct("nicinfo", [
- ("backend", string),
- ("backend_id", uint32),
- ("frontend", string),
- ("frontend_id", uint32),
- ("devid", libxl_devid),
- ("state", integer),
- ("evtch", integer),
- ("rref_tx", integer),
- ("rref_rx", integer),
- ], dir=DIR_OUT)
-
-libxl_vtpminfo = Struct("vtpminfo", [
- ("backend", string),
- ("backend_id", uint32),
- ("frontend", string),
- ("frontend_id", uint32),
- ("devid", libxl_devid),
- ("state", integer),
- ("evtch", integer),
- ("rref", integer),
- ("uuid", libxl_uuid),
- ], dir=DIR_OUT)
-
-libxl_usbctrlinfo = Struct("usbctrlinfo", [
- ("type", libxl_usbctrl_type),
- ("devid", libxl_devid),
- ("version", integer),
- ("ports", integer),
- ("backend", string),
- ("backend_id", uint32),
- ("frontend", string),
- ("frontend_id", uint32),
- ("state", integer),
- ("evtch", integer),
- ("ref_urb", integer),
- ("ref_conn", integer),
- ], dir=DIR_OUT)
-
-libxl_vcpuinfo = Struct("vcpuinfo", [
- ("vcpuid", uint32),
- ("cpu", uint32),
- ("online", bool),
- ("blocked", bool),
- ("running", bool),
- ("vcpu_time", uint64), # total vcpu time ran (ns)
- ("cpumap", libxl_bitmap), # current hard cpu affinity
- ("cpumap_soft", libxl_bitmap), # current soft cpu affinity
- ], dir=DIR_OUT)
-
-libxl_physinfo = Struct("physinfo", [
- ("threads_per_core", uint32),
- ("cores_per_socket", uint32),
-
- ("max_cpu_id", uint32),
- ("nr_cpus", uint32),
- ("cpu_khz", uint32),
-
- ("total_pages", uint64),
- ("free_pages", uint64),
- ("scrub_pages", uint64),
- ("outstanding_pages", uint64),
- ("sharing_freed_pages", uint64),
- ("sharing_used_frames", uint64),
- ("max_possible_mfn", uint64),
-
- ("nr_nodes", uint32),
- ("hw_cap", libxl_hwcap),
-
- ("cap_hvm", bool),
- ("cap_pv", bool),
- ("cap_hvm_directio", bool), # No longer HVM specific
- ("cap_hap", bool),
- ("cap_shadow", bool),
- ("cap_iommu_hap_pt_share", bool),
- ], dir=DIR_OUT)
-
-libxl_connectorinfo = Struct("connectorinfo", [
- ("unique_id", string),
- ("width", uint32),
- ("height", uint32),
- ("req_evtch", integer),
- ("req_rref", integer),
- ("evt_evtch", integer),
- ("evt_rref", integer),
- ], dir=DIR_OUT)
-
-libxl_vdisplinfo = Struct("vdisplinfo", [
- ("backend", string),
- ("backend_id", uint32),
- ("frontend", string),
- ("frontend_id", uint32),
- ("devid", libxl_devid),
- ("state", integer),
- ("be_alloc", bool),
- ("connectors", Array(libxl_connectorinfo, "num_connectors"))
- ], dir=DIR_OUT)
-
-libxl_streaminfo = Struct("streaminfo", [
- ("req_evtch", integer),
- ("req_rref", integer)
- ])
-
-libxl_pcminfo = Struct("pcminfo", [
- ("streams", Array(libxl_streaminfo, "num_vsnd_streams"))
- ])
-
-libxl_vsndinfo = Struct("vsndinfo", [
- ("backend", string),
- ("backend_id", uint32),
- ("frontend", string),
- ("frontend_id", uint32),
- ("devid", libxl_devid),
- ("state", integer),
- ("pcms", Array(libxl_pcminfo, "num_vsnd_pcms"))
- ])
-
-libxl_vkbinfo = Struct("vkbinfo", [
- ("backend", string),
- ("backend_id", uint32),
- ("frontend", string),
- ("frontend_id", uint32),
- ("devid", libxl_devid),
- ("state", integer),
- ("evtch", integer),
- ("rref", integer)
- ], dir=DIR_OUT)
-
-# NUMA node characteristics: size and free are how much memory it has, and how
-# much of it is free, respectively. dists is an array of distances from this
-# node to each other node.
-libxl_numainfo = Struct("numainfo", [
- ("size", uint64),
- ("free", uint64),
- ("dists", Array(uint32, "num_dists")),
- ], dir=DIR_OUT)
-
-libxl_cputopology = Struct("cputopology", [
- ("core", uint32),
- ("socket", uint32),
- ("node", uint32),
- ], dir=DIR_OUT)
-
-libxl_pcitopology = Struct("pcitopology", [
- ("seg", uint16),
- ("bus", uint8),
- ("devfn", uint8),
- ("node", uint32),
- ], dir=DIR_OUT)
-
-libxl_sched_credit_params = Struct("sched_credit_params", [
- ("tslice_ms", integer),
- ("ratelimit_us", integer),
- ("vcpu_migr_delay_us", integer),
- ], dispose_fn=None)
-
-libxl_sched_credit2_params = Struct("sched_credit2_params", [
- ("ratelimit_us", integer),
- ], dispose_fn=None)
-
-libxl_domain_remus_info = Struct("domain_remus_info",[
- ("interval", integer),
- ("allow_unsafe", libxl_defbool),
- ("blackhole", libxl_defbool),
- ("compression", libxl_defbool),
- ("netbuf", libxl_defbool),
- ("netbufscript", string),
- ("diskbuf", libxl_defbool),
- ("colo", libxl_defbool),
- ("userspace_colo_proxy", libxl_defbool)
- ])
-
-libxl_event_type = Enumeration("event_type", [
- (1, "DOMAIN_SHUTDOWN"),
- (2, "DOMAIN_DEATH"),
- (3, "DISK_EJECT"),
- (4, "OPERATION_COMPLETE"),
- (5, "DOMAIN_CREATE_CONSOLE_AVAILABLE"),
- ])
-
-libxl_ev_user = UInt(64)
-
-libxl_ev_link = Builtin("ev_link", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, private=True)
-
-libxl_event = Struct("event",[
- ("link", libxl_ev_link),
- # for use by libxl; caller may use this once the event has been
- # returned by libxl_event_{check,wait}
- ("domid", libxl_domid),
- ("domuuid", libxl_uuid),
- ("for_user", libxl_ev_user),
- ("u", KeyedUnion(None, libxl_event_type, "type",
- [("domain_shutdown", Struct(None, [
- ("shutdown_reason", uint8),
- ])),
- ("domain_death", None),
- ("disk_eject", Struct(None, [
- ("vdev", string),
- ("disk", libxl_device_disk),
- ])),
- ("operation_complete", Struct(None, [
- ("rc", integer),
- ])),
- ("domain_create_console_available", None),
- ]))])
-
-libxl_psr_cmt_type = Enumeration("psr_cmt_type", [
- (1, "CACHE_OCCUPANCY"),
- (2, "TOTAL_MEM_COUNT"),
- (3, "LOCAL_MEM_COUNT"),
- ])
-
-libxl_psr_cbm_type = Enumeration("psr_cbm_type", [
- (0, "UNKNOWN"),
- (1, "L3_CBM"),
- (2, "L3_CBM_CODE"),
- (3, "L3_CBM_DATA"),
- (4, "L2_CBM"),
- (5, "MBA_THRTL"),
- ])
-
-libxl_psr_cat_info = Struct("psr_cat_info", [
- ("id", uint32),
- ("cos_max", uint32),
- ("cbm_len", uint32),
- ("cdp_enabled", bool),
- ])
-
-libxl_psr_feat_type = Enumeration("psr_feat_type", [
- (1, "CAT"),
- (2, "MBA"),
- ])
-
-libxl_psr_hw_info = Struct("psr_hw_info", [
- ("id", uint32),
- ("u", KeyedUnion(None, libxl_psr_feat_type, "type",
- [("cat", Struct(None, [
- ("cos_max", uint32),
- ("cbm_len", uint32),
- ("cdp_enabled", bool),
- ])),
- ("mba", Struct(None, [
- ("cos_max", uint32),
- ("thrtl_max", uint32),
- ("linear", bool),
- ])),
- ]))
- ], dir=DIR_OUT)
+++ /dev/null
-namespace("libxl__")
-hidden(True)
-
-libxl_domid = Builtin("domid", namespace="libxl_", json_gen_fn = "yajl_gen_integer",
- json_parse_fn = "libxl__uint32_parse_json", json_parse_type = "JSON_INTEGER",
- autogenerate_json = False, copy_fn = None)
-
-libxl__qmp_message_type = Enumeration("qmp_message_type", [
- (1, "QMP"),
- (2, "return"),
- (3, "error"),
- (4, "event"),
- (5, "invalid"),
- ])
-
-# Consider adding to QEMU_BACKEND in libxl_internal.h
-libxl__device_kind = Enumeration("device_kind", [
- (0, "NONE"),
- (1, "VIF"),
- (2, "VBD"),
- (3, "QDISK"),
- (4, "PCI"),
- (5, "VFB"),
- (6, "VKBD"),
- (7, "CONSOLE"),
- (8, "VTPM"),
- (9, "VUSB"),
- (10, "QUSB"),
- (11, "9PFS"),
- (12, "VDISPL"),
- (13, "VUART"),
- (14, "PVCALLS"),
- (15, "VSND"),
- (16, "VINPUT"),
- ])
-
-libxl__console_backend = Enumeration("console_backend", [
- (1, "XENCONSOLED"),
- (2, "IOEMU"),
- ])
-
-libxl__device_console = Struct("device_console", [
- ("backend_domid", libxl_domid),
- ("devid", integer),
- ("consback", libxl__console_backend),
- ("output", string),
- # A regular console has no name.
- # A console with a name is called a 'channel', see docs/misc/channels.txt
- ("name", string),
- ("connection", string),
- ("path", string),
- ])
-
-libxl__device_action = Enumeration("device_action", [
- (1, "ADD"),
- (2, "REMOVE"),
- ])
+++ /dev/null
-/*
- * Copyright (C) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany.
- * Author Chunyan Liu <cyliu@suse.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-#include <inttypes.h>
-#include <xen/io/usbif.h>
-
-#define USBBACK_INFO_PATH "/libxl/usbback"
-
-#define USBHUB_CLASS_CODE 9
-
-static int usbback_is_loaded(libxl__gc *gc)
-{
- int r;
- struct stat st;
-
- r = lstat(SYSFS_USBBACK_DRIVER, &st);
-
- if (r == 0)
- return 1;
- if (r < 0 && errno == ENOENT)
- return 0;
- LOGE(ERROR, "Accessing %s", SYSFS_USBBACK_DRIVER);
- return ERROR_FAIL;
-}
-
-static int libxl__device_usbctrl_setdefault(libxl__gc *gc, uint32_t domid,
- libxl_device_usbctrl *usbctrl,
- bool hotplug)
-{
- int rc;
- libxl_domain_type domtype = libxl__domain_type(gc, domid);
-
- if (usbctrl->type == LIBXL_USBCTRL_TYPE_AUTO) {
- if (domtype != LIBXL_DOMAIN_TYPE_HVM) {
- rc = usbback_is_loaded(gc);
- if (rc < 0)
- goto out;
- usbctrl->type = rc ? LIBXL_USBCTRL_TYPE_PV
- : LIBXL_USBCTRL_TYPE_QUSB;
- } else {
- /* FIXME: See if we can detect PV frontend */
- usbctrl->type = LIBXL_USBCTRL_TYPE_DEVICEMODEL;
- }
- }
-
- switch (usbctrl->type) {
- case LIBXL_USBCTRL_TYPE_PV:
- case LIBXL_USBCTRL_TYPE_QUSB:
- if (!usbctrl->version)
- usbctrl->version = 2;
- if (usbctrl->version < 1 || usbctrl->version > 2) {
- LOG(ERROR,
- "USB version for paravirtualized devices must be 1 or 2");
- rc = ERROR_INVAL;
- goto out;
- }
- if (!usbctrl->ports)
- usbctrl->ports = 8;
- if (usbctrl->ports < 1 || usbctrl->ports > USBIF_MAX_PORTNR) {
- LOG(ERROR, "Number of ports for USB controller is limited to %u",
- USBIF_MAX_PORTNR);
- rc = ERROR_INVAL;
- goto out;
- }
- break;
- case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
- if (!usbctrl->version)
- usbctrl->version = 2;
- switch (usbctrl->version) {
- case 1:
- /* uhci controller in qemu has fixed number of ports. */
- if (usbctrl->ports && usbctrl->ports != 2) {
- LOG(ERROR,
- "Number of ports for USB controller of version 1 is always 2");
- rc = ERROR_INVAL;
- goto out;
- }
- usbctrl->ports = 2;
- break;
- case 2:
- /* ehci controller in qemu has fixed number of ports. */
- if (usbctrl->ports && usbctrl->ports != 6) {
- LOG(ERROR,
- "Number of ports for USB controller of version 2 is always 6");
- rc = ERROR_INVAL;
- goto out;
- }
- usbctrl->ports = 6;
- break;
- case 3:
- if (!usbctrl->ports)
- usbctrl->ports = 8;
- /* xhci controller in qemu supports up to 15 ports. */
- if (usbctrl->ports > 15) {
- LOG(ERROR,
- "Number of ports for USB controller of version 3 is limited to 15");
- rc = ERROR_INVAL;
- goto out;
- }
- break;
- default:
- LOG(ERROR, "Illegal USB version");
- rc = ERROR_INVAL;
- goto out;
- }
- break;
- default:
- break;
- }
-
- rc = libxl__resolve_domid(gc, usbctrl->backend_domname,
- &usbctrl->backend_domid);
-
-out:
- return rc;
-}
-
-static int libxl__device_from_usbctrl(libxl__gc *gc, uint32_t domid,
- libxl_device_usbctrl *usbctrl,
- libxl__device *device)
-{
- device->backend_devid = usbctrl->devid;
- device->backend_domid = usbctrl->backend_domid;
- switch (usbctrl->type) {
- case LIBXL_USBCTRL_TYPE_PV:
- device->backend_kind = LIBXL__DEVICE_KIND_VUSB;
- break;
- case LIBXL_USBCTRL_TYPE_QUSB:
- device->backend_kind = LIBXL__DEVICE_KIND_QUSB;
- break;
- case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
- device->backend_kind = LIBXL__DEVICE_KIND_NONE;
- break;
- default:
- abort(); /* can't really happen. */
- }
- device->devid = usbctrl->devid;
- device->domid = domid;
- device->kind = LIBXL__DEVICE_KIND_VUSB;
-
- return 0;
-}
-
-static const char *vusb_be_from_xs_libxl_type(libxl__gc *gc,
- const char *libxl_path,
- libxl_usbctrl_type type)
-{
- const char *be_path = NULL, *tmp;
- int r;
-
- if (type == LIBXL_USBCTRL_TYPE_AUTO) {
- r = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/type", libxl_path), &tmp);
- if (r || libxl_usbctrl_type_from_string(tmp, &type))
- goto out;
- }
-
- if (type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
- be_path = libxl_path;
- goto out;
- }
-
- r = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path),
- &be_path);
- if (r)
- be_path = NULL;
-
-out:
- return be_path;
-}
-
-/* Add usbctrl information to xenstore.
- *
- * Adding a usb controller will add a new 'qusb' or 'vusb' device in xenstore,
- * and add corresponding frontend, backend information to it. According to
- * "update_json", decide whether to update json config file.
- */
-static int libxl__device_usbctrl_add_xenstore(libxl__gc *gc, uint32_t domid,
- libxl_device_usbctrl *usbctrl,
- bool update_json)
-{
- libxl__device *device;
- flexarray_t *front = NULL;
- flexarray_t *back;
- xs_transaction_t t = XBT_NULL;
- int i, rc;
- libxl_domain_config d_config;
- libxl_device_usbctrl usbctrl_saved;
- libxl__flock *lock = NULL;
-
- libxl_domain_config_init(&d_config);
- libxl_device_usbctrl_init(&usbctrl_saved);
- libxl_device_usbctrl_copy(CTX, &usbctrl_saved, usbctrl);
-
- GCNEW(device);
- rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device);
- if (rc) goto out;
-
- back = flexarray_make(gc, 12, 1);
-
- if (device->backend_kind != LIBXL__DEVICE_KIND_NONE) {
- front = flexarray_make(gc, 4, 1);
-
- flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
- flexarray_append_pair(back, "online", "1");
- flexarray_append_pair(back, "state",
- GCSPRINTF("%d", XenbusStateInitialising));
- flexarray_append_pair(front, "backend-id",
- GCSPRINTF("%d", usbctrl->backend_domid));
- flexarray_append_pair(front, "state",
- GCSPRINTF("%d", XenbusStateInitialising));
- }
-
- flexarray_append_pair(back, "type",
- (char *)libxl_usbctrl_type_to_string(usbctrl->type));
- flexarray_append_pair(back, "usb-ver", GCSPRINTF("%d", usbctrl->version));
- flexarray_append_pair(back, "num-ports", GCSPRINTF("%d", usbctrl->ports));
- flexarray_append_pair(back, "port", "");
- for (i = 0; i < usbctrl->ports; i++)
- flexarray_append_pair(back, GCSPRINTF("port/%d", i + 1), "");
-
- if (update_json) {
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- rc = libxl__get_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
-
- device_add_domain_config(gc, &d_config, &libxl__usbctrl_devtype,
- &usbctrl_saved);
-
- rc = libxl__dm_check_start(gc, &d_config, domid);
- if (rc) goto out;
-
- if (usbctrl->type == LIBXL_USBCTRL_TYPE_QUSB) {
- if (!libxl__query_qemu_backend(gc, domid, usbctrl->backend_domid,
- "qusb", false)) {
- LOGD(ERROR, domid, "backend type not supported by device model");
- rc = ERROR_FAIL;
- goto out;
- }
- }
- }
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
-
- rc = libxl__device_exists(gc, t, device);
- if (rc < 0) goto out;
- if (rc == 1) {
- /* already exists in xenstore */
- LOGD(ERROR, domid, "device already exists in xenstore");
- rc = ERROR_DEVICE_EXISTS;
- goto out;
- }
-
- if (update_json) {
- rc = libxl__set_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
- }
-
- libxl__device_generic_add(gc, t, device,
- libxl__xs_kvs_of_flexarray(gc, back),
- libxl__xs_kvs_of_flexarray(gc, front),
- NULL);
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc < 0) goto out;
- }
-
-out:
- libxl__xs_transaction_abort(gc, &t);
- if (lock) libxl__unlock_file(lock);
- libxl_device_usbctrl_dispose(&usbctrl_saved);
- libxl_domain_config_dispose(&d_config);
- return rc;
-}
-
-static const char *vusb_be_from_xs_libxl(libxl__gc *gc, const char *libxl_path)
-{
- return vusb_be_from_xs_libxl_type(gc, libxl_path, LIBXL_USBCTRL_TYPE_AUTO);
-}
-
-static void libxl__device_usbctrl_del_xenstore(libxl__gc *gc, uint32_t domid,
- libxl_device_usbctrl *usbctrl)
-{
- const char *libxl_path, *be_path;
- xs_transaction_t t = XBT_NULL;
- int rc;
-
- libxl_path = libxl__domain_device_libxl_path(gc, domid, usbctrl->devid,
- LIBXL__DEVICE_KIND_VUSB);
- be_path = vusb_be_from_xs_libxl_type(gc, libxl_path, usbctrl->type);
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
-
- libxl__xs_path_cleanup(gc, t, be_path);
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc < 0) goto out;
- }
-
- return;
-
-out:
- libxl__xs_transaction_abort(gc, &t);
-}
-
-static char *pvusb_get_device_type(libxl_usbctrl_type type)
-{
- switch (type) {
- case LIBXL_USBCTRL_TYPE_PV:
- return "vusb";
- case LIBXL_USBCTRL_TYPE_QUSB:
- return "qusb";
- default:
- return NULL;
- }
-}
-
-/* Send qmp commands to create a usb controller in qemu.
- *
- * Depending on the speed (usbctrl->version) we create:
- * - piix3-usb-uhci (version=1), always 2 ports
- * - usb-ehci (version=2), always 6 ports
- * - nec-usb-xhci (version=3), up to 15 ports
- */
-static int libxl__device_usbctrl_add_hvm(libxl__egc *egc, libxl__ev_qmp *qmp,
- libxl_device_usbctrl *usbctrl)
-{
- EGC_GC;
- libxl__json_object *qmp_args = NULL;
-
- switch (usbctrl->version) {
- case 1:
- libxl__qmp_param_add_string(gc, &qmp_args,
- "driver", "piix3-usb-uhci");
- break;
- case 2:
- libxl__qmp_param_add_string(gc, &qmp_args,
- "driver", "usb-ehci");
- break;
- case 3:
- libxl__qmp_param_add_string(gc, &qmp_args,
- "driver", "nec-usb-xhci");
- libxl__qmp_param_add_string(gc, &qmp_args, "p2",
- GCSPRINTF("%d", usbctrl->ports));
- libxl__qmp_param_add_string(gc, &qmp_args, "p3",
- GCSPRINTF("%d", usbctrl->ports));
- break;
- default:
- abort(); /* Should not be possible. */
- }
-
- libxl__qmp_param_add_string(gc, &qmp_args, "id",
- GCSPRINTF("xenusb-%d", usbctrl->devid));
-
- return libxl__ev_qmp_send(egc, qmp, "device_add", qmp_args);
-}
-
-/* Send qmp commands to delete a usb controller in qemu. */
-static int libxl__device_usbctrl_del_hvm(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- int devid)
-{
- EGC_GC;
- libxl__json_object *qmp_args = NULL;
-
- libxl__qmp_param_add_string(gc, &qmp_args,
- "id", GCSPRINTF("xenusb-%d", devid));
-
- return libxl__ev_qmp_send(egc, qmp, "device_del", qmp_args);
-}
-
-/* Send qmp commands to create a usb device in qemu. */
-static int libxl__device_usbdev_add_hvm(libxl__egc *egc, libxl__ev_qmp *qmp,
- libxl_device_usbdev *usbdev)
-{
- EGC_GC;
- libxl__json_object *qmp_args = NULL;
-
- libxl__qmp_param_add_string(gc, &qmp_args, "id",
- GCSPRINTF("xenusb-%d-%d", usbdev->u.hostdev.hostbus,
- usbdev->u.hostdev.hostaddr));
- libxl__qmp_param_add_string(gc, &qmp_args, "driver", "usb-host");
- libxl__qmp_param_add_string(gc, &qmp_args, "bus",
- GCSPRINTF("xenusb-%d.0", usbdev->ctrl));
- libxl__qmp_param_add_string(gc, &qmp_args, "port",
- GCSPRINTF("%d", usbdev->port));
- libxl__qmp_param_add_string(gc, &qmp_args, "hostbus",
- GCSPRINTF("%d", usbdev->u.hostdev.hostbus));
- libxl__qmp_param_add_string(gc, &qmp_args, "hostaddr",
- GCSPRINTF("%d", usbdev->u.hostdev.hostaddr));
-
- return libxl__ev_qmp_send(egc, qmp, "device_add", qmp_args);
-}
-
-/* Send qmp commands to delete a usb device in qemu. */
-static int libxl__device_usbdev_del_hvm(libxl__egc *egc, libxl__ev_qmp *qmp,
- libxl_device_usbdev *usbdev)
-{
- EGC_GC;
- libxl__json_object *qmp_args = NULL;
-
- libxl__qmp_param_add_string(gc, &qmp_args, "id",
- GCSPRINTF("xenusb-%d-%d", usbdev->u.hostdev.hostbus,
- usbdev->u.hostdev.hostaddr));
-
- return libxl__ev_qmp_send(egc, qmp, "device_del", qmp_args);
-}
-
-static LIBXL_DEFINE_UPDATE_DEVID(usbctrl)
-
-static void device_usbctrl_add_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void device_usbctrl_add_qmp_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *r, int rc);
-static void device_usbctrl_add_done(libxl__egc *egc,
- libxl__ao_device *aodev, int rc);
-
-/* AO operation to add a usb controller.
- *
- * Generally, it does:
- * 1) fill in necessary usb controler information with default value
- * 2) write usb controller frontend/backend info to xenstore, update json
- * config file if necessary.
- * 3) wait for device connection. PVUSB frontend and backend driver will
- * probe xenstore paths and build connection between frontend and backend.
- *
- * Before calling this function, aodev should be properly filled:
- * aodev->ao, aodev->callback, aodev->update_json, ...
- */
-static void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid,
- libxl_device_usbctrl *usbctrl,
- libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- libxl__device *device;
- int rc;
-
- /* Store *usbctrl to be used by callbacks */
- aodev->device_config = usbctrl;
- aodev->device_type = &libxl__usbctrl_devtype;
-
- rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl,
- aodev->update_json);
- if (rc < 0) goto out;
-
- rc = libxl__device_usbctrl_update_devid(gc, domid, usbctrl);
- if (rc) goto out;
-
- rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl,
- aodev->update_json);
- if (rc) goto out;
-
- GCNEW(device);
- rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device);
- if (rc) goto outrm;
- aodev->dev = device;
-
- if (device->backend_kind == LIBXL__DEVICE_KIND_NONE) {
- libxl__ev_qmp *const qmp = &aodev->qmp;
-
- rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
- device_usbctrl_add_timeout,
- LIBXL_QMP_CMD_TIMEOUT * 1000);
- if (rc) goto outrm;
-
- qmp->ao = ao;
- qmp->domid = domid;
- qmp->payload_fd = -1;
- qmp->callback = device_usbctrl_add_qmp_cb;
- rc = libxl__device_usbctrl_add_hvm(egc, qmp, usbctrl);
- if (rc) goto outrm;
- return;
- }
-
- aodev->action = LIBXL__DEVICE_ACTION_ADD;
- libxl__wait_device_connection(egc, aodev);
- return;
-
-outrm:
- libxl__device_usbctrl_del_xenstore(gc, domid, usbctrl);
-out:
- device_usbctrl_add_done(egc, aodev, rc);
-}
-
-static void device_usbctrl_add_timeout(libxl__egc *egc, libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- EGC_GC;
- libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
-
- if (rc == ERROR_TIMEDOUT)
- LOGD(ERROR, aodev->dev->domid, "Adding usbctrl to QEMU timed out");
- device_usbctrl_add_qmp_cb(egc, &aodev->qmp, NULL, rc);
-}
-
-static void device_usbctrl_add_qmp_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *r,
- int rc)
-{
- EGC_GC;
- libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
- libxl_device_usbctrl *const usbctrl = aodev->device_config;
-
- if (rc)
- libxl__device_usbctrl_del_xenstore(gc, aodev->dev->domid, usbctrl);
-
- device_usbctrl_add_done(egc, aodev, rc);
-}
-
-static void device_usbctrl_add_done(libxl__egc *egc,
- libxl__ao_device *aodev,
- int rc)
-{
- EGC_GC;
- libxl__ev_qmp_dispose(gc, &aodev->qmp);
- libxl__ev_time_deregister(gc, &aodev->timeout);
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-LIBXL_DEFINE_DEVICE_ADD(usbctrl)
-static LIBXL_DEFINE_DEVICES_ADD(usbctrl)
-LIBXL_DEFINE_DEVICE_REMOVE_CUSTOM(usbctrl)
-
-static int libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc, uint32_t domid,
- libxl_devid usbctrl,
- libxl_device_usbdev **usbdevs,
- int *num);
-
-static void libxl__device_usbdev_remove(libxl__egc *egc,
- uint32_t domid, libxl_device_usbdev *usbdev, libxl__ao_device *aodev);
-
-static void device_usbctrl_usbdevs_removed(libxl__egc *,
- libxl__multidev *, int rc);
-static void device_usbctrl_remove_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void device_usbctrl_remove_qmp_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *resp, int rc);
-static void device_usbctrl_remove_done(libxl__egc *egc,
- libxl__ao_device *, int rc);
-
-typedef struct {
- libxl__multidev multidev;
- libxl__ao_device *aodev;
-} usbctrl_remove_state;
-
-/* AO function to remove a usb controller.
- *
- * Generally, it does:
- * 1) check if the usb controller exists or not
- * 2) remove all usb devices under controller
- * 3) remove usb controller information from xenstore
- *
- * Before calling this function, aodev should be properly filled:
- * aodev->ao, aodev->dev, aodev->callback, ...
- */
-void libxl__initiate_device_usbctrl_remove(libxl__egc *egc,
- libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- libxl_device_usbdev *usbdevs = NULL;
- int num_usbdev = 0;
- int i, rc;
- uint32_t domid = aodev->dev->domid;
- int usbctrl_devid = aodev->dev->devid;
- libxl_device_usbctrl *usbctrl;
- usbctrl_remove_state *ucrs;
-
- GCNEW(ucrs);
- ucrs->aodev = aodev;
- ucrs->multidev.callback = device_usbctrl_usbdevs_removed;
- libxl__multidev_begin(ao, &ucrs->multidev);
-
- GCNEW(usbctrl);
- libxl_device_usbctrl_init(usbctrl);
- rc = libxl_devid_to_device_usbctrl(CTX, domid, usbctrl_devid,
- usbctrl);
- if (rc) goto out;
-
- /* Store *usbctrl to be used by callbacks */
- aodev->device_config = usbctrl;
- aodev->device_type = &libxl__usbctrl_devtype;
-
- /* Remove usb devices first */
- rc = libxl__device_usbdev_list_for_usbctrl(gc, domid, usbctrl_devid,
- &usbdevs, &num_usbdev);
- if (rc) goto out;
-
- for (i = 0; i < num_usbdev; i++) {
- libxl__ao_device *usbdev_aodev =
- libxl__multidev_prepare(&ucrs->multidev);
- usbdev_aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
- libxl__device_usbdev_remove(egc, domid, &usbdevs[i], usbdev_aodev);
- }
-
-out:
- libxl__multidev_prepared(egc, &ucrs->multidev, rc); /* must be last */
-}
-
-static void device_usbctrl_usbdevs_removed(libxl__egc *egc,
- libxl__multidev *multidev,
- int rc)
-{
- usbctrl_remove_state *ucrs =
- CONTAINER_OF(multidev, *ucrs, multidev);
- libxl__ao_device *aodev = ucrs->aodev;
- STATE_AO_GC(aodev->ao);
- libxl_device_usbctrl *const usbctrl = aodev->device_config;
-
- if (rc) goto out;
-
- /* Remove usbctrl */
- if (usbctrl->type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
- libxl__ev_qmp *const qmp = &aodev->qmp;
-
- rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
- device_usbctrl_remove_timeout,
- LIBXL_QMP_CMD_TIMEOUT * 1000);
- if (rc) goto out;
-
- qmp->ao = ao;
- qmp->domid = aodev->dev->domid;
- qmp->callback = device_usbctrl_remove_qmp_cb;
- qmp->payload_fd = -1;
- rc = libxl__device_usbctrl_del_hvm(egc, qmp, aodev->dev->devid);
- if (rc) goto out;
- return;
- }
-
- libxl_device_usbctrl_dispose(usbctrl);
-
- /* Remove usbctrl */
- libxl__initiate_device_generic_remove(egc, aodev); /* must be last */
- return;
-out:
- device_usbctrl_remove_done(egc, aodev, rc); /* must be last */
-}
-
-static void device_usbctrl_remove_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
-{
- EGC_GC;
- libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
-
- if (rc == ERROR_TIMEDOUT)
- LOGD(ERROR, aodev->dev->domid,
- "Removing usbctrl from QEMU timed out");
- device_usbctrl_remove_qmp_cb(egc, &aodev->qmp, NULL, rc);
-}
-
-static void device_usbctrl_remove_qmp_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *resp,
- int rc)
-{
- EGC_GC;
- libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
- libxl_device_usbctrl *const usbctrl = aodev->device_config;
-
- if (!rc)
- libxl__device_usbctrl_del_xenstore(gc, aodev->dev->domid, usbctrl);
-
- device_usbctrl_remove_done(egc, aodev, rc);
-}
-
-static void device_usbctrl_remove_done(libxl__egc *egc,
- libxl__ao_device *aodev,
- int rc)
-{
- EGC_GC;
- libxl_device_usbctrl *const usbctrl = aodev->device_config;
-
- libxl_device_usbctrl_dispose(usbctrl);
- libxl__ev_qmp_dispose(gc, &aodev->qmp);
- libxl__ev_time_deregister(gc, &aodev->timeout);
-
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-static int libxl__usbctrl_from_xenstore(libxl__gc *gc,
- const char *libxl_path,
- libxl_devid devid,
- libxl_device_usbctrl *usbctrl_r)
-{
- int rc;
- const char *tmp;
- const char *be_path;
-
-#define READ_SUBPATH(path, subpath) ({ \
- rc = libxl__xs_read_checked(gc, XBT_NULL, \
- GCSPRINTF("%s/" subpath, path), \
- &tmp); \
- if (rc) goto out; \
- (char *)tmp; \
- })
-
-#define READ_SUBPATH_INT(path, subpath) ({ \
- rc = libxl__xs_read_checked(gc, XBT_NULL, \
- GCSPRINTF("%s/" subpath, path), \
- &tmp); \
- if (rc) goto out; \
- tmp ? atoi(tmp) : -1; \
- })
-
- usbctrl_r->devid = devid;
- libxl_usbctrl_type_from_string(READ_SUBPATH(libxl_path, "type"),
- &usbctrl_r->type);
- if (usbctrl_r->type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
- be_path = libxl_path;
- rc = libxl__get_domid(gc, &usbctrl_r->backend_domid);
- } else {
- be_path = READ_SUBPATH(libxl_path, "backend");
- if (!be_path) goto out;
- rc = libxl__backendpath_parse_domid(gc, be_path,
- &usbctrl_r->backend_domid);
- }
- if (rc) goto out;
- usbctrl_r->version = READ_SUBPATH_INT(be_path, "usb-ver");
- usbctrl_r->ports = READ_SUBPATH_INT(be_path, "num-ports");
-
-#undef READ_SUBPATH
-#undef READ_SUBPATH_INT
-out:
- if (rc)
- libxl_device_usbctrl_dispose(usbctrl_r);
- return rc;
-}
-
-int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_usbctrl *usbctrl,
- libxl_usbctrlinfo *usbctrlinfo)
-{
- GC_INIT(ctx);
- const char *fe_path, *be_path, *tmp;
- const char *libxl_path;
- int rc;
-
- usbctrlinfo->devid = usbctrl->devid;
-
-#define READ_SUBPATH(path, subpath) ({ \
- rc = libxl__xs_read_mandatory(gc, XBT_NULL, \
- GCSPRINTF("%s/" subpath, path), \
- &tmp); \
- if (rc) goto out; \
- (char *)tmp; \
- })
-
-#define READ_SUBPATH_INT(path, subpath) ({ \
- rc = libxl__xs_read_checked(gc, XBT_NULL, \
- GCSPRINTF("%s/" subpath, path), \
- &tmp); \
- if (rc) goto out; \
- tmp ? atoi(tmp) : -1; \
- })
-
- libxl_path = libxl__domain_device_libxl_path(gc, domid, usbctrl->devid,
- LIBXL__DEVICE_KIND_VUSB);
- libxl_usbctrl_type_from_string(READ_SUBPATH(libxl_path, "type"),
- &usbctrlinfo->type);
-
- if (usbctrlinfo->type != LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
- fe_path = libxl__domain_device_frontend_path(gc, domid, usbctrl->devid,
- LIBXL__DEVICE_KIND_VUSB);
- be_path = READ_SUBPATH(libxl_path, "backend");
- usbctrlinfo->backend = libxl__strdup(NOGC, be_path);
- rc = libxl__backendpath_parse_domid(gc, be_path,
- &usbctrlinfo->backend_id);
- if (rc) goto out;
- usbctrlinfo->state = READ_SUBPATH_INT(fe_path, "state");
- usbctrlinfo->evtch = READ_SUBPATH_INT(fe_path, "event-channel");
- usbctrlinfo->ref_urb = READ_SUBPATH_INT(fe_path, "urb-ring-ref");
- usbctrlinfo->ref_conn = READ_SUBPATH_INT(fe_path, "urb-ring-ref");
- usbctrlinfo->frontend = libxl__strdup(NOGC, fe_path);
- usbctrlinfo->frontend_id = domid;
- usbctrlinfo->ports = READ_SUBPATH_INT(be_path, "num-ports");
- usbctrlinfo->version = READ_SUBPATH_INT(be_path, "usb-ver");
- } else {
- usbctrlinfo->ports = READ_SUBPATH_INT(libxl_path, "num-ports");
- usbctrlinfo->version = READ_SUBPATH_INT(libxl_path, "usb-ver");
- rc = libxl__get_domid(gc, &usbctrlinfo->backend_id);
- if (rc) goto out;
- }
-
-#undef READ_SUBPATH
-#undef READ_SUBPATH_INT
-
- rc = 0;
-
-out:
- GC_FREE;
- return rc;
-}
-
-
-static char *usbdev_busaddr_to_busid(libxl__gc *gc, int bus, int addr)
-{
- DIR *dir;
- char *busid = NULL;
- struct dirent *de;
-
- /* invalid hostbus or hostaddr */
- if (bus < 1 || addr < 1)
- return NULL;
-
- dir = opendir(SYSFS_USB_DEV);
- if (!dir) {
- LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV);
- return NULL;
- }
-
- for (;;) {
- char *filename;
- void *buf;
- int busnum = -1;
- int devnum = -1;
-
- errno = 0;
- de = readdir(dir);
- if (!de && errno) {
- LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV);
- break;
- }
- if (!de)
- break;
-
- if (!strcmp(de->d_name, ".") ||
- !strcmp(de->d_name, ".."))
- continue;
-
- filename = GCSPRINTF(SYSFS_USB_DEV "/%s/devnum", de->d_name);
- if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
- devnum = atoi(buf);
-
- filename = GCSPRINTF(SYSFS_USB_DEV "/%s/busnum", de->d_name);
- if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
- busnum = atoi(buf);
-
- if (bus == busnum && addr == devnum) {
- busid = libxl__strdup(gc, de->d_name);
- break;
- }
- }
-
- closedir(dir);
- return busid;
-}
-
-static int usbdev_busaddr_from_busid(libxl__gc *gc, const char *busid,
- uint8_t *bus, uint8_t *addr)
-{
- char *filename;
- void *buf;
-
- filename = GCSPRINTF(SYSFS_USB_DEV "/%s/busnum", busid);
- if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
- *bus = atoi(buf);
- else
- return ERROR_FAIL;
-
- filename = GCSPRINTF(SYSFS_USB_DEV "/%s/devnum", busid);
- if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
- *addr = atoi(buf);
- else
- return ERROR_FAIL;
-
- return 0;
-}
-
-static int get_assigned_devices(libxl__gc *gc,
- libxl_device_usbdev **list, int *num)
-{
- char **domlist;
- unsigned int ndom = 0;
- int i, j, k;
- int rc;
-
- *list = NULL;
- *num = 0;
-
- domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &ndom);
- for (i = 0; i < ndom; i++) {
- char *libxl_vusbs_path;
- char **usbctrls;
- unsigned int nc = 0;
- uint32_t domid = atoi(domlist[i]);
-
- libxl_vusbs_path = GCSPRINTF("%s/device/%s",
- libxl__xs_libxl_path(gc, domid),
- libxl__device_kind_to_string(LIBXL__DEVICE_KIND_VUSB));
- usbctrls = libxl__xs_directory(gc, XBT_NULL,
- libxl_vusbs_path, &nc);
-
- for (j = 0; j < nc; j++) {
- libxl_device_usbdev *tmp = NULL;
- int nd = 0;
-
- rc = libxl__device_usbdev_list_for_usbctrl(gc, domid,
- atoi(usbctrls[j]),
- &tmp, &nd);
- if (rc) goto out;
-
- if (!nd) continue;
-
- GCREALLOC_ARRAY(*list, *num + nd);
- for (k = 0; k < nd; k++) {
- libxl_device_usbdev_copy(CTX, *list + *num, tmp + k);
- (*num)++;
- }
- }
- }
-
- return 0;
-
-out:
- LOG(ERROR, "fail to get assigned devices");
- return rc;
-}
-
-static bool is_usbdev_in_array(libxl_device_usbdev *usbdevs, int num,
- libxl_device_usbdev *usbdev)
-{
- int i;
-
- for (i = 0; i < num; i++) {
- if (usbdevs[i].u.hostdev.hostbus == usbdev->u.hostdev.hostbus &&
- usbdevs[i].u.hostdev.hostaddr == usbdev->u.hostdev.hostaddr)
- return true;
- }
-
- return false;
-}
-
-/* check if USB device type is assignable */
-static bool is_usbdev_assignable(libxl__gc *gc, libxl_device_usbdev *usbdev)
-{
- int classcode;
- char *filename;
- void *buf = NULL;
- char *busid = NULL;
-
- busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
- usbdev->u.hostdev.hostaddr);
- if (!busid) return false;
-
- filename = GCSPRINTF(SYSFS_USB_DEV "/%s/bDeviceClass", busid);
- if (libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
- return false;
-
- classcode = atoi(buf);
- return classcode != USBHUB_CLASS_CODE;
-}
-
-/* get usb devices under certain usb controller */
-static int
-libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc,
- uint32_t domid,
- libxl_devid usbctrl,
- libxl_device_usbdev **usbdevs,
- int *num)
-{
- const char *libxl_path, *be_path, *num_devs;
- int n, i, rc;
-
- *usbdevs = NULL;
- *num = 0;
-
- libxl_path = libxl__domain_device_libxl_path(gc, domid, usbctrl,
- LIBXL__DEVICE_KIND_VUSB);
-
- be_path = vusb_be_from_xs_libxl(gc, libxl_path);
- if (!be_path) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/num-ports", be_path),
- &num_devs);
- if (rc) goto out;
-
- n = num_devs ? atoi(num_devs) : 0;
-
- for (i = 0; i < n; i++) {
- const char *busid;
- libxl_device_usbdev *usbdev;
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/port/%d", be_path, i + 1),
- &busid);
- if (rc) goto out;
-
- if (busid && strcmp(busid, "")) {
- GCREALLOC_ARRAY(*usbdevs, *num + 1);
- usbdev = *usbdevs + *num;
- (*num)++;
- libxl_device_usbdev_init(usbdev);
- usbdev->ctrl = usbctrl;
- usbdev->port = i + 1;
- usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
- rc = usbdev_busaddr_from_busid(gc, busid,
- &usbdev->u.hostdev.hostbus,
- &usbdev->u.hostdev.hostaddr);
- if (rc) goto out;
- }
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-/* get all usb devices of the domain */
-libxl_device_usbdev *
-libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num)
-{
- GC_INIT(ctx);
- libxl_device_usbdev *usbdevs = NULL;
- const char *libxl_vusbs_path;
- char **usbctrls;
- unsigned int nc = 0;
- int i, j;
-
- *num = 0;
-
- libxl_vusbs_path = GCSPRINTF("%s/device/%s",
- libxl__xs_libxl_path(gc, domid),
- libxl__device_kind_to_string(
- LIBXL__DEVICE_KIND_VUSB));
- usbctrls = libxl__xs_directory(gc, XBT_NULL, libxl_vusbs_path, &nc);
-
- for (i = 0; i < nc; i++) {
- int rc, nd = 0;
- libxl_device_usbdev *tmp = NULL;
-
- rc = libxl__device_usbdev_list_for_usbctrl(gc, domid,
- atoi(usbctrls[i]),
- &tmp, &nd);
- if (rc || !nd) continue;
-
- usbdevs = libxl__realloc(NOGC, usbdevs,
- sizeof(*usbdevs) * (*num + nd));
- for (j = 0; j < nd; j++) {
- libxl_device_usbdev_copy(ctx, usbdevs + *num, tmp + j);
- (*num)++;
- }
- }
-
- GC_FREE;
- return usbdevs;
-}
-
-static char *vusb_get_port_path(libxl__gc *gc, uint32_t domid,
- libxl_usbctrl_type type, int ctrl, int port)
-{
- char *path;
-
- if (type == LIBXL_USBCTRL_TYPE_DEVICEMODEL)
- path = GCSPRINTF("%s/device/%s", libxl__xs_libxl_path(gc, domid),
- libxl__device_kind_to_string(LIBXL__DEVICE_KIND_VUSB));
- else
- path = GCSPRINTF("%s/backend/%s/%d",
- libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID),
- pvusb_get_device_type(type), domid);
-
- return GCSPRINTF("%s/%d/port/%d", path, ctrl, port);
-}
-
-/* find first unused controller:port and give that to usb device */
-static int
-libxl__device_usbdev_set_default_usbctrl(libxl__gc *gc, uint32_t domid,
- libxl_device_usbdev *usbdev)
-{
- libxl_device_usbctrl *usbctrls = NULL;
- int numctrl = 0;
- int i, j, rc;
-
- usbctrls = libxl_device_usbctrl_list(CTX, domid, &numctrl);
- if (!numctrl || !usbctrls) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- for (i = 0; i < numctrl; i++) {
- for (j = 0; j < usbctrls[i].ports; j++) {
- const char *path, *tmp;
-
- path = vusb_get_port_path(gc, domid, usbctrls[i].type,
- usbctrls[i].devid, j + 1);
- rc = libxl__xs_read_checked(gc, XBT_NULL, path, &tmp);
- if (rc) goto out;
-
- if (tmp && !strcmp(tmp, "")) {
- usbdev->ctrl = usbctrls[i].devid;
- usbdev->port = j + 1;
- rc = 0;
- goto out;
- }
- }
- }
-
- /* no available controller:port */
- rc = ERROR_FAIL;
-
-out:
- libxl_device_usbctrl_list_free(usbctrls, numctrl);
- return rc;
-}
-
-/* Fill in usb information with default value.
- *
- * Generally, it does:
- * 1) if "controller" is not specified:
- * - if "port" is not specified, try to find an available controller:port,
- * if found, use that; otherwise, create a new controller, use this
- * controller and its first port
- * - if "port" is specified, report error.
- * 2) if "controller" is specified, but port is not specified:
- * try to find an available port under this controller, if found, use
- * that, otherwise, report error.
- * 3) if both "controller" and "port" are specified:
- * check the controller:port is available, if not, report error.
- */
-static int libxl__device_usbdev_setdefault(libxl__gc *gc,
- uint32_t domid,
- libxl_device_usbdev *usbdev,
- bool update_json)
-{
- int rc;
-
- if (!usbdev->type)
- usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
-
- if (usbdev->ctrl == -1) {
- if (usbdev->port) {
- LOGD(ERROR, domid,
- "USB controller must be specified if you specify port");
- return ERROR_INVAL;
- }
-
- rc = libxl__device_usbdev_set_default_usbctrl(gc, domid, usbdev);
- /* If no existing controller to host this usb device, add a new one */
- if (rc) {
- libxl_device_usbctrl *usbctrl;
-
- GCNEW(usbctrl);
- libxl_device_usbctrl_init(usbctrl);
- rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl,
- update_json);
- if (rc < 0) goto out;
-
- rc = libxl__device_usbctrl_update_devid(gc, domid, usbctrl);
- if (rc) goto out;
-
- rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl,
- update_json);
- if (rc) goto out;
-
- usbdev->ctrl = usbctrl->devid;
- usbdev->port = 1;
- }
- } else {
- /* A controller was specified; look it up */
- const char *libxl_path, *be_path, *tmp;
-
- libxl_path = libxl__domain_device_libxl_path(gc, domid, usbdev->ctrl,
- LIBXL__DEVICE_KIND_VUSB);
-
- be_path = vusb_be_from_xs_libxl(gc, libxl_path);
- if (!be_path) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (usbdev->port) {
- /* A specific port was requested; see if it's available */
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/port/%d",
- be_path, usbdev->port),
- &tmp);
- if (rc) goto out;
-
- if (tmp && strcmp(tmp, "")) {
- LOGD(ERROR, domid, "The controller port isn't available");
- rc = ERROR_FAIL;
- goto out;
- }
- } else {
- /* No port was requested. Choose free port. */
- int i, ports;
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/num-ports", be_path), &tmp);
- if (rc) goto out;
-
- ports = tmp ? atoi(tmp) : 0;
-
- for (i = 0; i < ports; i++) {
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/port/%d", be_path, i + 1),
- &tmp);
- if (rc) goto out;
-
- if (tmp && !strcmp(tmp, "")) {
- usbdev->port = i + 1;
- break;
- }
- }
-
- if (!usbdev->port) {
- LOGD(ERROR, domid, "No available port under specified controller");
- rc = ERROR_FAIL;
- goto out;
- }
- }
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-/* Add usb information to xenstore
- *
- * Adding a usb device won't create new 'qusb'/'vusb' device, but only write
- * the device busid to the controller:port in xenstore.
- */
-static int libxl__device_usbdev_add_xenstore(libxl__gc *gc, uint32_t domid,
- libxl_device_usbdev *usbdev,
- libxl_usbctrl_type type,
- bool update_json)
-{
- char *be_path, *busid;
- int rc;
- xs_transaction_t t = XBT_NULL;
- libxl_domain_config d_config;
- libxl_device_usbdev usbdev_saved;
- libxl__flock *lock = NULL;
-
- libxl_domain_config_init(&d_config);
- libxl_device_usbdev_init(&usbdev_saved);
- libxl_device_usbdev_copy(CTX, &usbdev_saved, usbdev);
-
- busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
- usbdev->u.hostdev.hostaddr);
- if (!busid) {
- LOGD(DEBUG, domid, "Fail to get busid of usb device");
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (update_json) {
- lock = libxl__lock_domain_userdata(gc, domid);
- if (!lock) {
- rc = ERROR_LOCK_FAIL;
- goto out;
- }
-
- rc = libxl__get_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
-
- device_add_domain_config(gc, &d_config, &libxl__usbdev_devtype,
- &usbdev_saved);
-
- rc = libxl__dm_check_start(gc, &d_config, domid);
- if (rc) goto out;
- }
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
-
- if (update_json) {
- rc = libxl__set_domain_configuration(gc, domid, &d_config);
- if (rc) goto out;
- }
-
- be_path = vusb_get_port_path(gc, domid, type, usbdev->ctrl,
- usbdev->port);
-
- LOGD(DEBUG, domid, "Adding usb device %s to xenstore: controller %d, port %d",
- busid, usbdev->ctrl, usbdev->port);
-
- rc = libxl__xs_write_checked(gc, t, be_path, busid);
- if (rc) goto out;
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc < 0) goto out;
- }
-
- rc = 0;
-
-out:
- if (lock) libxl__unlock_file(lock);
- libxl_device_usbdev_dispose(&usbdev_saved);
- libxl_domain_config_dispose(&d_config);
- return rc;
-}
-
-static int libxl__device_usbdev_remove_xenstore(libxl__gc *gc, uint32_t domid,
- libxl_device_usbdev *usbdev,
- libxl_usbctrl_type type)
-{
- char *be_path;
-
- be_path = vusb_get_port_path(gc, domid, type, usbdev->ctrl, usbdev->port);
-
- LOGD(DEBUG, domid, "Removing usb device from xenstore: controller %d, port %d",
- usbdev->ctrl, usbdev->port);
-
- return libxl__xs_write_checked(gc, XBT_NULL, be_path, "");
-}
-
-static char *usbdev_busid_from_ctrlport(libxl__gc *gc, uint32_t domid,
- libxl_device_usbdev *usbdev,
- libxl_usbctrl_type type)
-{
- return libxl__xs_read(gc, XBT_NULL,
- vusb_get_port_path(gc, domid, type, usbdev->ctrl,
- usbdev->port));
-}
-
-/* get original driver path of usb interface, stored in @drvpath */
-static int usbintf_get_drvpath(libxl__gc *gc, const char *intf, char **drvpath)
-{
- char *spath, *dp = NULL;
-
- spath = GCSPRINTF(SYSFS_USB_DEV "/%s/driver", intf);
-
- /* Find the canonical path to the driver. */
- dp = libxl__zalloc(gc, PATH_MAX);
- dp = realpath(spath, dp);
- if (!dp && errno != ENOENT) {
- LOGE(ERROR, "get realpath failed: '%s'", spath);
- return ERROR_FAIL;
- }
-
- *drvpath = dp;
-
- return 0;
-}
-
-static int unbind_usbintf(libxl__gc *gc, const char *intf)
-{
- char *path;
- int fd = -1;
- int rc;
-
- path = GCSPRINTF(SYSFS_USB_DEV "/%s/driver/unbind", intf);
-
- fd = open(path, O_WRONLY);
- if (fd < 0) {
- LOGE(ERROR, "open file failed: '%s'", path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (libxl_write_exactly(CTX, fd, intf, strlen(intf), path, intf)) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-
-out:
- if (fd >= 0) close(fd);
- return rc;
-}
-
-static int bind_usbintf(libxl__gc *gc, const char *intf, const char *drvpath)
-{
- char *bind_path, *intf_path;
- struct stat st;
- int fd = -1;
- int rc, r;
-
- intf_path = GCSPRINTF("%s/%s", drvpath, intf);
-
- /* check through lstat, if intf already exists under drvpath,
- * it's already bound, return directly; if it doesn't exist,
- * continue to do bind work; otherwise, return error.
- */
- r = lstat(intf_path, &st);
- if (r == 0)
- return 0;
- if (r < 0 && errno != ENOENT)
- return ERROR_FAIL;
-
- bind_path = GCSPRINTF("%s/bind", drvpath);
-
- fd = open(bind_path, O_WRONLY);
- if (fd < 0) {
- LOGE(ERROR, "open file failed: '%s'", bind_path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- if (libxl_write_exactly(CTX, fd, intf, strlen(intf), bind_path, intf)) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-
-out:
- if (fd >= 0) close(fd);
- return rc;
-}
-
-/* Is usb interface bound to usbback? */
-static int usbintf_is_assigned(libxl__gc *gc, char *intf)
-{
- char *spath;
- int r;
- struct stat st;
-
- spath = GCSPRINTF(SYSFS_USBBACK_DRIVER "/%s", intf);
- r = lstat(spath, &st);
-
- if (r == 0)
- return 1;
- if (r < 0 && errno == ENOENT)
- return 0;
- LOGE(ERROR, "Accessing %s", spath);
- return -1;
-}
-
-static int usbdev_get_all_interfaces(libxl__gc *gc, const char *busid,
- char ***intfs, int *num)
-{
- DIR *dir;
- char *buf;
- struct dirent *de;
- int rc;
-
- *intfs = NULL;
- *num = 0;
-
- buf = GCSPRINTF("%s:", busid);
-
- dir = opendir(SYSFS_USB_DEV);
- if (!dir) {
- LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV);
- return ERROR_FAIL;
- }
-
- for (;;) {
- errno = 0;
- de = readdir(dir);
-
- if (!de && errno) {
- LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV);
- rc = ERROR_FAIL;
- goto out;
- }
- if (!de)
- break;
-
- if (!strcmp(de->d_name, ".") ||
- !strcmp(de->d_name, ".."))
- continue;
-
- if (!strncmp(de->d_name, buf, strlen(buf))) {
- GCREALLOC_ARRAY(*intfs, *num + 1);
- (*intfs)[*num] = libxl__strdup(gc, de->d_name);
- (*num)++;
- }
- }
-
- rc = 0;
-
-out:
- closedir(dir);
- return rc;
-}
-
-/* Encode usb interface so that it could be written to xenstore as a key.
- *
- * Since xenstore key cannot include '.' or ':', we'll change '.' to '_',
- * change ':' to '@'. For example, 3-1:2.1 will be encoded to 3-1@2_1.
- * This will be used to save original driver of USB device to xenstore.
- */
-static char *usb_interface_xenstore_encode(libxl__gc *gc, const char *busid)
-{
- char *str = libxl__strdup(gc, busid);
- int i, len = strlen(str);
-
- for (i = 0; i < len; i++) {
- if (str[i] == '.') str[i] = '_';
- if (str[i] == ':') str[i] = '@';
- }
- return str;
-}
-
-/* Unbind USB device from "usbback" driver.
- *
- * If there are many interfaces under USB device, check each interface,
- * unbind from "usbback" driver.
- */
-static int usbback_dev_unassign(libxl__gc *gc, const char *busid)
-{
- char **intfs = NULL;
- int i, num = 0;
- int rc;
-
- rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
- if (rc) goto out;
-
- for (i = 0; i < num; i++) {
- char *intf = intfs[i];
-
- /* check if the USB interface is already bound to "usbback" */
- if (usbintf_is_assigned(gc, intf) > 0) {
- /* unbind interface from usbback driver */
- rc = unbind_usbintf(gc, intf);
- if (rc) {
- LOGE(ERROR, "Couldn't unbind %s from usbback", intf);
- goto out;
- }
- }
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-/* rebind USB device to original driver.
- *
- * If there are many interfaces under USB device, for reach interface,
- * read driver_path from xenstore (if there is) and rebind to its
- * original driver, then remove driver_path information from xenstore.
- */
-static int usbdev_rebind(libxl__gc *gc, const char *busid)
-{
- char **intfs = NULL;
- char *usbdev_encode = NULL;
- char *path = NULL;
- int i, num = 0;
- int rc;
-
- rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
- if (rc) goto out;
-
- usbdev_encode = usb_interface_xenstore_encode(gc, busid);
-
- for (i = 0; i < num; i++) {
- char *intf = intfs[i];
- char *usbintf_encode = NULL;
- const char *drvpath;
-
- /* rebind USB interface to its originial driver */
- usbintf_encode = usb_interface_xenstore_encode(gc, intf);
- path = GCSPRINTF(USBBACK_INFO_PATH "/%s/%s/driver_path",
- usbdev_encode, usbintf_encode);
- rc = libxl__xs_read_checked(gc, XBT_NULL, path, &drvpath);
- if (rc) goto out;
-
- if (drvpath) {
- rc = bind_usbintf(gc, intf, drvpath);
- if (rc) {
- LOGE(ERROR, "Couldn't rebind %s to %s", intf, drvpath);
- goto out;
- }
- }
- }
-
-out:
- path = GCSPRINTF(USBBACK_INFO_PATH "/%s", usbdev_encode);
- libxl__xs_rm_checked(gc, XBT_NULL, path);
- return rc;
-}
-
-
-/* Bind USB device to "usbback" driver.
- *
- * If there are many interfaces under USB device, check each interface,
- * unbind from original driver and bind to "usbback" driver.
- */
-static int usbback_dev_assign(libxl__gc *gc, const char *busid)
-{
- char **intfs = NULL;
- int num = 0, i;
- int rc;
- char *usbdev_encode = NULL;
-
- rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
- if (rc) return rc;
-
- usbdev_encode = usb_interface_xenstore_encode(gc, busid);
-
- for (i = 0; i < num; i++) {
- char *intf = intfs[i];
- char *drvpath = NULL;
-
- /* already assigned to usbback */
- if (usbintf_is_assigned(gc, intf) > 0)
- continue;
-
- rc = usbintf_get_drvpath(gc, intf, &drvpath);
- if (rc) goto out;
-
- if (drvpath) {
- /* write driver path to xenstore for later rebinding */
- char *usbintf_encode = NULL;
- char *path;
-
- usbintf_encode = usb_interface_xenstore_encode(gc, intf);
- path = GCSPRINTF(USBBACK_INFO_PATH "/%s/%s/driver_path",
- usbdev_encode, usbintf_encode);
- rc = libxl__xs_write_checked(gc, XBT_NULL, path, drvpath);
- if (rc) goto out;
-
- /* unbind interface from original driver */
- rc = unbind_usbintf(gc, intf);
- if (rc) goto out;
- }
-
- /* bind interface to usbback */
- rc = bind_usbintf(gc, intf, SYSFS_USBBACK_DRIVER);
- if (rc) {
- LOG(ERROR, "Couldn't bind %s to %s", intf, SYSFS_USBBACK_DRIVER);
- goto out;
- }
- }
-
- return 0;
-
-out:
- /* some interfaces might be bound to usbback, unbind it and
- * rebind it to its original driver
- */
- usbback_dev_unassign(gc, busid);
- usbdev_rebind(gc, busid);
- return rc;
-}
-
-static void device_usbdev_add_qmp_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *r, int rc);
-static void device_usbdev_add_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void device_usbdev_add_done(libxl__egc *egc,
- libxl__ao_device *aodev, int rc);
-
-/* AO operation to add a usb device.
- *
- * Generally, it does:
- * 1) check if the usb device type is assignable
- * 2) check if the usb device is already assigned to a domain
- * 3) add 'busid' of the usb device to xenstore contoller/port/.
- * (PVUSB driver watches the xenstore changes and will detect that.)
- * 4) unbind usb device from original driver and bind to usbback.
- * If usb device has many interfaces, then:
- * - unbind each interface from its original driver and bind to usbback.
- * - store the original driver to xenstore for later rebinding when
- * detaching the device.
- *
- * Before calling this function, aodev should be properly filled:
- * aodev->ao, aodev->callback, aodev->update_json, ...
- */
-static void libxl__device_usbdev_add(libxl__egc *egc, uint32_t domid,
- libxl_device_usbdev *usbdev,
- libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- int rc;
- libxl_device_usbdev *assigned;
- int num_assigned;
- libxl_device_usbctrl usbctrl;
- char *busid;
- bool has_callback = false;
-
- libxl_device_usbctrl_init(&usbctrl);
-
- /* Store *usbdev to be used by callbacks */
- aodev->device_config = usbdev;
- aodev->device_type = &libxl__usbdev_devtype;
-
- /* Currently only support adding USB device from Dom0 backend.
- * So, if USB controller is specified, check its backend domain,
- * if it's not Dom0, report error.
- */
- if (usbdev->ctrl != -1) {
- rc = libxl_devid_to_device_usbctrl(CTX, domid, usbdev->ctrl,
- &usbctrl);
- if (rc) goto out;
-
- if (usbctrl.backend_domid != LIBXL_TOOLSTACK_DOMID) {
- LOGD(ERROR, domid,
- "Don't support adding USB device from non-Dom0 backend");
- rc = ERROR_INVAL;
- goto out;
- }
- libxl_device_usbctrl_dispose(&usbctrl);
- }
-
- /* check usb device is assignable type */
- if (!is_usbdev_assignable(gc, usbdev)) {
- LOGD(ERROR, domid, "USB device is not assignable.");
- rc = ERROR_FAIL;
- goto out;
- }
-
- /* check usb device is already assigned */
- rc = get_assigned_devices(gc, &assigned, &num_assigned);
- if (rc) {
- LOGD(ERROR, domid, "cannot determine if device is assigned,"
- " refusing to continue");
- goto out;
- }
-
- if (is_usbdev_in_array(assigned, num_assigned, usbdev)) {
- LOGD(ERROR, domid, "USB device already attached to a domain");
- rc = ERROR_INVAL;
- goto out;
- }
-
- /* fill default values, e.g, if usbdev->ctrl and usbdev->port
- * not specified, choose available controller:port and fill in. */
- rc = libxl__device_usbdev_setdefault(gc, domid, usbdev,
- aodev->update_json);
- if (rc) goto out;
-
- rc = libxl_devid_to_device_usbctrl(CTX, domid, usbdev->ctrl, &usbctrl);
- if (rc) goto out;
-
- /* do actual adding usb device operation */
- switch (usbctrl.type) {
- case LIBXL_USBCTRL_TYPE_PV:
- busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
- usbdev->u.hostdev.hostaddr);
- if (!busid) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
- LIBXL_USBCTRL_TYPE_PV,
- aodev->update_json);
- if (rc) goto out;
-
- rc = usbback_dev_assign(gc, busid);
- if (rc) {
- libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
- LIBXL_USBCTRL_TYPE_PV);
- goto out;
- }
- break;
- case LIBXL_USBCTRL_TYPE_QUSB:
- rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
- LIBXL_USBCTRL_TYPE_QUSB,
- aodev->update_json);
- if (rc) goto out;
-
- break;
- case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
- rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
- LIBXL_USBCTRL_TYPE_DEVICEMODEL,
- aodev->update_json);
- if (rc) goto out;
-
- rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
- device_usbdev_add_timeout,
- LIBXL_QMP_CMD_TIMEOUT * 1000);
- if (rc) goto out;
-
- aodev->qmp.ao = ao;
- aodev->qmp.domid = domid;
- aodev->qmp.callback = device_usbdev_add_qmp_cb;
- aodev->qmp.payload_fd = -1;
- rc = libxl__device_usbdev_add_hvm(egc, &aodev->qmp, usbdev);
- if (rc) {
- libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
- LIBXL_USBCTRL_TYPE_DEVICEMODEL);
- goto out;
- }
- has_callback = true;
- break;
- default:
- LOGD(ERROR, domid, "Unsupported usb controller type");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-
-out:
- libxl_device_usbctrl_dispose(&usbctrl);
- /* Only call _done if no callback have been setup */
- if (!has_callback)
- device_usbdev_add_done(egc, aodev, rc); /* must be last */
-}
-
-static void device_usbdev_add_timeout(libxl__egc *egc,
- libxl__ev_time *ev,
- const struct timeval *requested_abs,
- int rc)
-{
- EGC_GC;
- libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
-
- if (rc == ERROR_TIMEDOUT)
- LOGD(ERROR, aodev->qmp.domid,
- "Adding usbdev to QEMU timed out");
- device_usbdev_add_qmp_cb(egc, &aodev->qmp, NULL, rc);
-}
-
-static void device_usbdev_add_qmp_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *r,
- int rc)
-{
- EGC_GC;
- libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
- libxl_device_usbdev *const usbdev = aodev->device_config;
-
- if (rc)
- libxl__device_usbdev_remove_xenstore(gc, qmp->domid,
- usbdev, LIBXL_USBCTRL_TYPE_DEVICEMODEL);
- device_usbdev_add_done(egc, aodev, rc); /* must be last */
-}
-
-static void device_usbdev_add_done(libxl__egc *egc,
- libxl__ao_device *aodev,
- int rc)
-{
- EGC_GC;
-
- libxl__ev_time_deregister(gc, &aodev->timeout);
- libxl__ev_qmp_dispose(gc, &aodev->qmp);
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-LIBXL_DEFINE_DEVICE_ADD(usbdev)
-static LIBXL_DEFINE_DEVICES_ADD(usbdev)
-
-static void device_usbdev_remove_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void device_usbdev_remove_qmp_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp, const libxl__json_object *r, int rc);
-static void device_usbdev_remove_done(libxl__egc *egc,
- libxl__ao_device *aodev, int rc);
-
-/* Operation to remove usb device.
- *
- * Generally, it does:
- * 1) check if the usb device is assigned to the domain
- * 2) remove the usb device from xenstore controller/port.
- * 3) unbind usb device from usbback and rebind to its original driver.
- * If usb device has many interfaces, do it to each interface.
- *
- * Before calling this function, aodev should be properly filled:
- * aodev->ao, aodev->callback, ...
- */
-static void libxl__device_usbdev_remove(libxl__egc *egc, uint32_t domid,
- libxl_device_usbdev *usbdev,
- libxl__ao_device *aodev)
-{
- STATE_AO_GC(aodev->ao);
- int rc;
- char *busid;
- libxl_device_usbctrl usbctrl;
- bool has_callback = false;
-
- /* Store *usbdev to be used by callbacks */
- aodev->device_config = usbdev;
- aodev->device_type = &libxl__usbdev_devtype;
-
- libxl_device_usbctrl_init(&usbctrl);
-
- if (usbdev->ctrl < 0 || usbdev->port < 1) {
- LOGD(ERROR, domid, "Invalid USB device");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl_devid_to_device_usbctrl(CTX, domid, usbdev->ctrl, &usbctrl);
- if (rc) goto out;
-
- if (usbctrl.backend_domid != LIBXL_TOOLSTACK_DOMID) {
- LOGD(ERROR, domid,
- "Don't support removing USB device from non-Dom0 backend");
- rc = ERROR_INVAL;
- goto out;
- }
-
- /* do actual removing usb device operation */
- switch (usbctrl.type) {
- case LIBXL_USBCTRL_TYPE_PV:
- busid = usbdev_busid_from_ctrlport(gc, domid, usbdev, usbctrl.type);
- if (!busid) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- /* Things are done in order of:
- * unbind USB device from usbback,
- * remove USB device from xenstore,
- * rebind USB device to original driver.
- * It is to balance simplicity with robustness in case of failure:
- * - We unbind all interfaces before rebinding any interfaces, so
- * that we never get into a situation where some interfaces are
- * assigned to usbback and some are assigned to the original drivers.
- * - We also unbind the interfaces before removing the pvusb xenstore
- * nodes, so that if the unbind fails in the middle, the device still
- * shows up in xl usb-list, and the user can re-try removing it.
- */
- rc = usbback_dev_unassign(gc, busid);
- if (rc) {
- LOGD(ERROR, domid, "Error removing device from guest."
- " Try running usbdev-detach again.");
- goto out;
- }
-
- rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
- LIBXL_USBCTRL_TYPE_PV);
- if (rc) {
- LOGD(ERROR, domid, "Error removing device from guest."
- " Try running usbdev-detach again.");
- goto out;
- }
-
- rc = usbdev_rebind(gc, busid);
- if (rc) {
- LOGD(ERROR, domid, "USB device removed from guest, but couldn't"
- " re-bind to domain 0. Try removing and re-inserting"
- " the USB device or reloading the driver modules.");
- goto out;
- }
-
- break;
- case LIBXL_USBCTRL_TYPE_QUSB:
- rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
- LIBXL_USBCTRL_TYPE_QUSB);
- if (rc) goto out;
-
- break;
- case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
- rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
- LIBXL_USBCTRL_TYPE_DEVICEMODEL);
- if (rc) goto out;
-
- rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
- device_usbdev_remove_timeout,
- LIBXL_QMP_CMD_TIMEOUT * 1000);
- if (rc) goto out;
-
- aodev->qmp.ao = ao;
- aodev->qmp.domid = domid;
- aodev->qmp.callback = device_usbdev_remove_qmp_cb;
- aodev->qmp.payload_fd = -1;
- rc = libxl__device_usbdev_del_hvm(egc, &aodev->qmp, usbdev);
- if (rc) {
- libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
- LIBXL_USBCTRL_TYPE_DEVICEMODEL,
- false);
- goto out;
- }
- has_callback = true;
- break;
- default:
- LOGD(ERROR, domid, "Unsupported usb controller type");
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = 0;
-
-out:
- libxl_device_usbctrl_dispose(&usbctrl);
- /* Only call _done if no callback have been setup */
- if (!has_callback)
- device_usbdev_remove_done(egc, aodev, rc); /* must be last */
-}
-
-static void device_usbdev_remove_timeout(libxl__egc *egc,
- libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
-{
- EGC_GC;
- libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
-
- if (rc == ERROR_TIMEDOUT)
- LOGD(ERROR, aodev->qmp.domid,
- "Removing usbdev from QEMU timed out");
- device_usbdev_remove_qmp_cb(egc, &aodev->qmp, NULL, rc);
-}
-
-static void device_usbdev_remove_qmp_cb(libxl__egc *egc,
- libxl__ev_qmp *qmp,
- const libxl__json_object *r,
- int rc)
-{
- EGC_GC;
- libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
- libxl_device_usbdev *const usbdev = aodev->device_config;
-
- if (rc) {
- libxl__device_usbdev_add_xenstore(gc, qmp->domid, usbdev,
- LIBXL_USBCTRL_TYPE_DEVICEMODEL,
- false);
- }
-
- device_usbdev_remove_done(egc, aodev, rc); /* must be last */
-}
-
-static void device_usbdev_remove_done(libxl__egc *egc,
- libxl__ao_device *aodev,
- int rc)
-{
- EGC_GC;
-
- libxl__ev_time_deregister(gc, &aodev->timeout);
- libxl__ev_qmp_dispose(gc, &aodev->qmp);
- aodev->rc = rc;
- aodev->callback(egc, aodev);
-}
-
-int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid,
- libxl_device_usbdev *usbdev,
- const libxl_asyncop_how *ao_how)
-
-{
- AO_CREATE(ctx, domid, ao_how);
- libxl__ao_device *aodev;
-
- GCNEW(aodev);
- libxl__prepare_ao_device(ao, aodev);
- aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
- aodev->callback = device_addrm_aocomplete;
- libxl__device_usbdev_remove(egc, domid, usbdev, aodev);
-
- return AO_INPROGRESS;
-}
-
-int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx,
- uint32_t domid,
- int ctrl,
- int port,
- libxl_device_usbdev *usbdev)
-{
- GC_INIT(ctx);
- const char *libxl_path, *be_path, *busid;
- int rc;
-
- libxl_path = libxl__domain_device_libxl_path(gc, domid, ctrl,
- LIBXL__DEVICE_KIND_VUSB);
- be_path = vusb_be_from_xs_libxl(gc, libxl_path);
- if (!be_path) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/port/%d", be_path, port),
- &busid);
- if (rc) goto out;
-
- if (!busid || !strcmp(busid, "")) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- usbdev->ctrl = ctrl;
- usbdev->port = port;
- usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
- rc = usbdev_busaddr_from_busid(gc, busid,
- &usbdev->u.hostdev.hostbus,
- &usbdev->u.hostdev.hostaddr);
-
-out:
- GC_FREE;
- return rc;
-}
-
-static int libxl_device_usbctrl_compare(const libxl_device_usbctrl *d1,
- const libxl_device_usbctrl *d2)
-{
- return COMPARE_USBCTRL(d1, d2);
-}
-
-static int libxl_device_usbctrl_dm_needed(void *e, unsigned domid)
-{
- libxl_device_usbctrl *elem = e;
-
- return elem->type == LIBXL_USBCTRL_TYPE_QUSB &&
- elem->backend_domid == domid;
-}
-
-static int libxl_device_usbdev_compare(const libxl_device_usbdev *d1,
- const libxl_device_usbdev *d2)
-{
- return COMPARE_USB(d1, d2);
-}
-
-void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr)
-{
- int i;
-
- for (i = 0; i < nr; i++)
- libxl_device_usbdev_dispose(&list[i]);
- free(list);
-}
-
-#define libxl__device_usbctrl_update_devid NULL
-
-LIBXL_DEFINE_DEVID_TO_DEVICE(usbctrl)
-LIBXL_DEFINE_DEVICE_LIST(usbctrl)
-DEFINE_DEVICE_TYPE_STRUCT(usbctrl, VUSB,
- .from_xenstore = (device_from_xenstore_fn_t)libxl__usbctrl_from_xenstore,
- .dm_needed = libxl_device_usbctrl_dm_needed
-);
-
-#define libxl__device_from_usbdev NULL
-#define libxl__device_usbdev_update_devid NULL
-
-DEFINE_DEVICE_TYPE_STRUCT(usbdev, VUSB);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <ctype.h>
-
-#include "libxl_internal.h"
-#include "_paths.h"
-
-#ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
-const
-#endif
-char *libxl_basename(const char *name)
-{
- const char *filename;
- if (name == NULL)
- return strdup(".");
- if (name[0] == '\0')
- return strdup(".");
-
- filename = strrchr(name, '/');
- if (filename)
- return strdup(filename+1);
- return strdup(name);
-}
-
-unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus)
-{
- /* 256 pages (1MB) per vcpu,
- plus 1 page per MiB of RAM for the P2M map,
- plus 1 page per MiB of RAM to shadow the resident processes.
- This is higher than the minimum that Xen would allocate if no value
- were given (but the Xen minimum is for safety, not performance).
- */
- return 4 * (256 * smp_cpus + 2 * (maxmem_kb / 1024));
-}
-
-char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid)
-{
- unsigned int len;
- char path[strlen("/local/domain") + 12];
- char *s;
-
- snprintf(path, sizeof(path), "/local/domain/%d/name", domid);
- s = xs_read(ctx->xsh, XBT_NULL, path, &len);
- return s;
-}
-
-char *libxl__domid_to_name(libxl__gc *gc, uint32_t domid)
-{
- char *s = libxl_domid_to_name(CTX, domid);
- libxl__ptr_add(gc, s);
- return s;
-}
-
-int libxl_name_to_domid(libxl_ctx *ctx, const char *name,
- uint32_t *domid)
-{
- int i, nb_domains;
- char *domname;
- libxl_dominfo *dominfo;
- int ret = ERROR_INVAL;
-
- dominfo = libxl_list_domain(ctx, &nb_domains);
- if (!dominfo)
- return ERROR_NOMEM;
-
- for (i = 0; i < nb_domains; i++) {
- domname = libxl_domid_to_name(ctx, dominfo[i].domid);
- if (!domname)
- continue;
- if (strcmp(domname, name) == 0) {
- *domid = dominfo[i].domid;
- ret = 0;
- free(domname);
- break;
- }
- free(domname);
- }
- libxl_dominfo_list_free(dominfo, nb_domains);
- return ret;
-}
-
-int libxl_domain_qualifier_to_domid(libxl_ctx *ctx, const char *name,
- uint32_t *domid)
-{
- int i, rv;
- for (i=0; name[i]; i++) {
- if (!CTYPE(isdigit, name[i])) {
- goto nondigit_found;
- }
- }
- *domid = strtoul(name, NULL, 10);
- return 0;
-
- nondigit_found:
- /* this could also check for uuids */
- rv = libxl_name_to_domid(ctx, name, domid);
- return rv;
-}
-
-static int qualifier_to_id(const char *p, uint32_t *id_r)
-{
- int i, alldigit;
-
- alldigit = 1;
- for (i = 0; p[i]; i++) {
- if (!isdigit((uint8_t)p[i])) {
- alldigit = 0;
- break;
- }
- }
-
- if (i > 0 && alldigit) {
- *id_r = strtoul(p, NULL, 10);
- return 0;
- } else {
- /* check here if it's a uuid and do proper conversion */
- }
- return 1;
-}
-
-int libxl_cpupool_qualifier_to_cpupoolid(libxl_ctx *ctx, const char *p,
- uint32_t *poolid_r,
- int *was_name_r)
-{
- int was_name;
-
- was_name = qualifier_to_id(p, poolid_r);
- if (was_name_r) *was_name_r = was_name;
- return was_name ? libxl_name_to_cpupoolid(ctx, p, poolid_r) : 0;
-}
-
-char *libxl_cpupoolid_to_name(libxl_ctx *ctx, uint32_t poolid)
-{
- unsigned int len;
- char path[strlen("/local/pool") + 12];
- char *s;
-
- snprintf(path, sizeof(path), "/local/pool/%d/name", poolid);
- s = xs_read(ctx->xsh, XBT_NULL, path, &len);
- if (!s && (poolid == 0))
- return strdup("Pool-0");
- return s;
-}
-
-/* This is a bit horrid but without xs_exists it seems like the only way. */
-int libxl_cpupoolid_is_valid(libxl_ctx *ctx, uint32_t poolid)
-{
- int ret;
- char *s = libxl_cpupoolid_to_name(ctx, poolid);
-
- ret = (s != NULL);
- free(s);
- return ret;
-}
-
-char *libxl__cpupoolid_to_name(libxl__gc *gc, uint32_t poolid)
-{
- char *s = libxl_cpupoolid_to_name(CTX, poolid);
- libxl__ptr_add(gc, s);
- return s;
-}
-
-int libxl_name_to_cpupoolid(libxl_ctx *ctx, const char *name,
- uint32_t *poolid)
-{
- int i, nb_pools;
- char *poolname;
- libxl_cpupoolinfo *poolinfo;
- int ret = ERROR_INVAL;
-
- poolinfo = libxl_list_cpupool(ctx, &nb_pools);
- if (!poolinfo)
- return ERROR_NOMEM;
-
- for (i = 0; i < nb_pools; i++) {
- if (ret && ((poolname = libxl_cpupoolid_to_name(ctx,
- poolinfo[i].poolid)) != NULL)) {
- if (strcmp(poolname, name) == 0) {
- *poolid = poolinfo[i].poolid;
- ret = 0;
- }
- free(poolname);
- }
- }
- libxl_cpupoolinfo_list_free(poolinfo, nb_pools);
- return ret;
-}
-
-int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid)
-{
- GC_INIT(ctx);
- char * stubdom_id_s;
- int ret;
-
- stubdom_id_s = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/image/device-model-domid",
- libxl__xs_get_dompath(gc, guest_domid)));
- if (stubdom_id_s)
- ret = atoi(stubdom_id_s);
- else
- ret = 0;
- GC_FREE;
- return ret;
-}
-
-int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid)
-{
- GC_INIT(ctx);
- char *target, *endptr;
- uint32_t value;
- int ret = 0;
-
- target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/target",
- libxl__xs_get_dompath(gc, domid)));
- if (!target)
- goto out;
- value = strtol(target, &endptr, 10);
- if (*endptr != '\0')
- goto out;
- if (target_domid)
- *target_domid = value;
- ret = 1;
-out:
- GC_FREE;
- return ret;
-}
-
-static int logrename(libxl__gc *gc, const char *old, const char *new)
-{
- int r;
-
- r = rename(old, new);
- if (r) {
- if (errno == ENOENT) return 0; /* ok */
-
- LOGE(ERROR, "failed to rotate logfile - "
- "could not rename %s to %s", old, new);
- return ERROR_FAIL;
- }
- return 0;
-}
-
-int libxl_create_logfile(libxl_ctx *ctx, const char *name, char **full_name)
-{
- GC_INIT(ctx);
- struct stat stat_buf;
- char *logfile, *logfile_new;
- int i, rc;
-
- logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log", name);
- if (stat(logfile, &stat_buf) == 0) {
- /* file exists, rotate */
- logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log.10", name);
- unlink(logfile);
- for (i = 9; i > 0; i--) {
- logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log.%d", name, i);
- logfile_new = GCSPRINTF(XEN_LOG_DIR "/%s.log.%d", name, i + 1);
- rc = logrename(gc, logfile, logfile_new);
- if (rc)
- goto out;
- }
- logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log", name);
- logfile_new = GCSPRINTF(XEN_LOG_DIR "/%s.log.1", name);
-
- rc = logrename(gc, logfile, logfile_new);
- if (rc)
- goto out;
- } else {
- if (errno != ENOENT)
- LOGE(WARN, "problem checking existence of logfile %s, "
- "which might have needed to be rotated",
- name);
- }
- *full_name = strdup(logfile);
- rc = 0;
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl_string_to_backend(libxl_ctx *ctx, char *s, libxl_disk_backend *backend)
-{
- char *p;
- int rc = 0;
-
- if (!strcmp(s, "phy")) {
- *backend = LIBXL_DISK_BACKEND_PHY;
- } else if (!strcmp(s, "file")) {
- *backend = LIBXL_DISK_BACKEND_TAP;
- } else if (!strcmp(s, "qdisk")) {
- *backend = LIBXL_DISK_BACKEND_QDISK;
- } else if (!strcmp(s, "tap")) {
- p = strchr(s, ':');
- if (!p) {
- rc = ERROR_INVAL;
- goto out;
- }
- p++;
- if (!strcmp(p, "vhd")) {
- *backend = LIBXL_DISK_BACKEND_TAP;
- } else if (!strcmp(p, "qcow")) {
- *backend = LIBXL_DISK_BACKEND_QDISK;
- } else if (!strcmp(p, "qcow2")) {
- *backend = LIBXL_DISK_BACKEND_QDISK;
- } else if (!strcmp(p, "qed")) {
- *backend = LIBXL_DISK_BACKEND_QDISK;
- }
- }
-out:
- return rc;
-}
-
-int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
- void **data_r, int *datalen_r) {
- GC_INIT(ctx);
- FILE *f = 0;
- uint8_t *data = 0;
- int datalen = 0;
- int e;
- struct stat stab;
- ssize_t rs;
-
- f = fopen(filename, "r");
- if (!f) {
- if (errno == ENOENT) return ENOENT;
- LOGE(ERROR, "failed to open %s", filename);
- goto xe;
- }
-
- if (fstat(fileno(f), &stab)) {
- LOGE(ERROR, "failed to fstat %s", filename);
- goto xe;
- }
-
- if (!S_ISREG(stab.st_mode)) {
- LOGE(ERROR, "%s is not a plain file", filename);
- errno = ENOTTY;
- goto xe;
- }
-
- if (stab.st_size > INT_MAX) {
- LOG(ERROR, "file %s is far too large", filename);
- errno = EFBIG;
- goto xe;
- }
-
- datalen = stab.st_size;
-
- if (stab.st_size && data_r) {
- data = malloc(datalen);
- if (!data) goto xe;
-
- rs = fread(data, 1, datalen, f);
- if (rs != datalen) {
- if (ferror(f))
- LOGE(ERROR, "failed to read %s", filename);
- else if (feof(f))
- LOG(ERROR, "%s changed size while we were reading it",
- filename);
- else
- abort();
- goto xe;
- }
- }
-
- if (fclose(f)) {
- f = 0;
- LOGE(ERROR, "failed to close %s", filename);
- goto xe;
- }
-
- if (data_r) *data_r = data;
- if (datalen_r) *datalen_r = datalen;
-
- GC_FREE;
- return 0;
-
- xe:
- GC_FREE;
- e = errno;
- assert(e != ENOENT);
- if (f) fclose(f);
- free(data);
- return e;
-}
-
-int libxl__read_sysfs_file_contents(libxl__gc *gc, const char *filename,
- void **data_r, int *datalen_r)
-{
- FILE *f = 0;
- uint8_t *data = 0;
- int datalen = 0;
- int e;
- struct stat stab;
- ssize_t rs;
-
- f = fopen(filename, "r");
- if (!f) {
- if (errno == ENOENT) return ENOENT;
- LOGE(ERROR, "failed to open %s", filename);
- goto xe;
- }
-
- if (fstat(fileno(f), &stab)) {
- LOGE(ERROR, "failed to fstat %s", filename);
- goto xe;
- }
-
- if (!S_ISREG(stab.st_mode)) {
- LOGE(ERROR, "%s is not a plain file", filename);
- errno = ENOTTY;
- goto xe;
- }
-
- if (stab.st_size > INT_MAX) {
- LOG(ERROR, "file %s is far too large", filename);
- errno = EFBIG;
- goto xe;
- }
-
- datalen = stab.st_size;
-
- if (stab.st_size && data_r) {
- data = libxl__malloc(gc, datalen);
-
- /* For sysfs file, datalen is always PAGE_SIZE. 'read'
- * will return the number of bytes of the actual content,
- * rs <= datalen is expected.
- */
- rs = fread(data, 1, datalen, f);
- if (rs < datalen) {
- if (ferror(f)) {
- LOGE(ERROR, "failed to read %s", filename);
- goto xe;
- }
-
- datalen = rs;
- data = libxl__realloc(gc, data, datalen);
- }
- }
-
- if (fclose(f)) {
- f = 0;
- LOGE(ERROR, "failed to close %s", filename);
- goto xe;
- }
-
- if (data_r) *data_r = data;
- if (datalen_r) *datalen_r = datalen;
-
- return 0;
-
- xe:
- e = errno;
- assert(e != ENOENT);
- if (f) fclose(f);
- return e;
-}
-
-
-#define READ_WRITE_EXACTLY(rw, zero_is_eof, constdata) \
- \
- int libxl_##rw##_exactly(libxl_ctx *ctx, int fd, \
- constdata void *data, ssize_t sz, \
- const char *source, const char *what) { \
- ssize_t got; \
- GC_INIT(ctx); \
- \
- while (sz > 0) { \
- got = rw(fd, data, sz); \
- if (got == -1) { \
- if (errno == EINTR) continue; \
- if (!ctx) { GC_FREE; return errno; } \
- LOGE(ERROR, "failed to "#rw" %s%s%s", \
- what ? what : "", what ? " from " : "", source); \
- GC_FREE; \
- return errno; \
- } \
- if (got == 0) { \
- if (!ctx) { GC_FREE; return EPROTO; } \
- LOG(ERROR, zero_is_eof \
- ? "file/stream truncated reading %s%s%s" \
- : "file/stream write returned 0! writing %s%s%s", \
- what ? what : "", what ? " from " : "", source); \
- GC_FREE; \
- return EPROTO; \
- } \
- sz -= got; \
- data = (char*)data + got; \
- } \
- GC_FREE; \
- return 0; \
- }
-
-READ_WRITE_EXACTLY(read, 1, /* */)
-READ_WRITE_EXACTLY(write, 0, const)
-
-int libxl__remove_file(libxl__gc *gc, const char *path)
-{
- for (;;) {
- int r = unlink(path);
- if (!r) return 0;
- if (errno == ENOENT) return 0;
- if (errno == EINTR) continue;
- LOGE(ERROR, "failed to remove file %s", path);
- return ERROR_FAIL;
- }
-}
-
-int libxl__remove_file_or_directory(libxl__gc *gc, const char *path)
-{
- for (;;) {
- int r = rmdir(path);
- if (!r) return 0;
- if (errno == ENOENT) return 0;
- if (errno == ENOTEMPTY) return libxl__remove_directory(gc, path);
- if (errno == ENOTDIR) return libxl__remove_file(gc, path);
- if (errno == EINTR) continue;
- LOGE(ERROR, "failed to remove %s", path);
- return ERROR_FAIL;
- }
-}
-
-int libxl__remove_directory(libxl__gc *gc, const char *dirpath)
-{
- int rc = 0;
- DIR *d = 0;
-
- d = opendir(dirpath);
- if (!d) {
- if (errno == ENOENT)
- goto out;
-
- LOGE(ERROR, "failed to opendir %s for removal", dirpath);
- rc = ERROR_FAIL;
- goto out;
- }
-
- struct dirent *de;
-
- for (;;) {
- errno = 0;
- de = readdir(d);
- if (!de && errno) {
- LOGE(ERROR, "failed to readdir %s for removal", dirpath);
- rc = ERROR_FAIL;
- break;
- }
- if (!de)
- break;
-
- if (!strcmp(de->d_name, ".") ||
- !strcmp(de->d_name, ".."))
- continue;
-
- const char *subpath = GCSPRINTF("%s/%s", dirpath, de->d_name);
- if (libxl__remove_file_or_directory(gc, subpath))
- rc = ERROR_FAIL;
- }
-
- for (;;) {
- int r = rmdir(dirpath);
- if (!r) break;
- if (errno == ENOENT) goto out;
- if (errno == EINTR) continue;
- LOGE(ERROR, "failed to remove emptied directory %s", dirpath);
- rc = ERROR_FAIL;
- }
-
- out:
- if (d) closedir(d);
-
- return rc;
-}
-
-int libxl_pipe(libxl_ctx *ctx, int pipes[2])
-{
- GC_INIT(ctx);
- int ret = 0;
- if (pipe(pipes) < 0) {
- LOG(ERROR, "Failed to create a pipe");
- ret = -1;
- }
- GC_FREE;
- return ret;
-}
-
-int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits)
-{
- GC_INIT(ctx);
- int sz;
-
- sz = (n_bits + 7) / 8;
- bitmap->map = libxl__calloc(NOGC, sizeof(*bitmap->map), sz);
- bitmap->size = sz;
-
- GC_FREE;
- return 0;
-}
-
-void libxl_bitmap_init(libxl_bitmap *map)
-{
- memset(map, '\0', sizeof(*map));
-}
-
-void libxl_bitmap_dispose(libxl_bitmap *map)
-{
- if (!map)
- return;
-
- free(map->map);
- map->map = NULL;
- map->size = 0;
-}
-
-void libxl_bitmap_copy(libxl_ctx *ctx, libxl_bitmap *dptr,
- const libxl_bitmap *sptr)
-{
- int sz;
-
- assert(dptr->size == sptr->size);
- sz = dptr->size = sptr->size;
- memcpy(dptr->map, sptr->map, sz * sizeof(*dptr->map));
-}
-
-/* This function copies X bytes from source to destination bitmap,
- * where X is the smaller of the two sizes.
- *
- * If destination's size is larger than source, the extra bytes are
- * untouched.
- */
-void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr,
- const libxl_bitmap *sptr)
-{
- int sz;
-
- sz = dptr->size < sptr->size ? dptr->size : sptr->size;
- memcpy(dptr->map, sptr->map, sz * sizeof(*dptr->map));
-}
-
-void libxl_bitmap_copy_alloc(libxl_ctx *ctx,
- libxl_bitmap *dptr,
- const libxl_bitmap *sptr)
-{
- GC_INIT(ctx);
-
- dptr->map = libxl__calloc(NOGC, sptr->size, sizeof(*sptr->map));
- dptr->size = sptr->size;
- memcpy(dptr->map, sptr->map, sptr->size * sizeof(*sptr->map));
-
- GC_FREE;
-}
-
-int libxl_bitmap_is_full(const libxl_bitmap *bitmap)
-{
- int i;
-
- for (i = 0; i < bitmap->size; i++)
- if (bitmap->map[i] != (uint8_t)-1)
- return 0;
- return 1;
-}
-
-int libxl_bitmap_is_empty(const libxl_bitmap *bitmap)
-{
- int i;
-
- for (i = 0; i < bitmap->size; i++)
- if (bitmap->map[i])
- return 0;
- return 1;
-}
-
-int libxl_bitmap_test(const libxl_bitmap *bitmap, int bit)
-{
- if (bit >= bitmap->size * 8)
- return 0;
- return (bitmap->map[bit / 8] & (1 << (bit & 7))) ? 1 : 0;
-}
-
-void libxl_bitmap_set(libxl_bitmap *bitmap, int bit)
-{
- if (bit >= bitmap->size * 8)
- return;
- bitmap->map[bit / 8] |= 1 << (bit & 7);
-}
-
-void libxl_bitmap_reset(libxl_bitmap *bitmap, int bit)
-{
- if (bit >= bitmap->size * 8)
- return;
- bitmap->map[bit / 8] &= ~(1 << (bit & 7));
-}
-
-int libxl_bitmap_or(libxl_ctx *ctx, libxl_bitmap *or_map,
- const libxl_bitmap *map1, const libxl_bitmap *map2)
-{
- GC_INIT(ctx);
- int rc;
- uint32_t i;
- const libxl_bitmap *large_map;
- const libxl_bitmap *small_map;
-
- if (map1->size > map2->size) {
- large_map = map1;
- small_map = map2;
- } else {
- large_map = map2;
- small_map = map1;
- }
-
- rc = libxl_bitmap_alloc(ctx, or_map, large_map->size * 8);
- if (rc)
- goto out;
-
- /*
- * If bitmaps aren't the same size, their union (logical or) will
- * be size of larger bit map. Any bit past the end of the
- * smaller bit map, will match the larger one.
- */
- for (i = 0; i < small_map->size; i++)
- or_map->map[i] = (small_map->map[i] | large_map->map[i]);
-
- for (i = small_map->size; i < large_map->size; i++)
- or_map->map[i] = large_map->map[i];
-
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl_bitmap_and(libxl_ctx *ctx, libxl_bitmap *and_map,
- const libxl_bitmap *map1, const libxl_bitmap *map2)
-{
- GC_INIT(ctx);
- int rc;
- uint32_t i;
- const libxl_bitmap *large_map;
- const libxl_bitmap *small_map;
-
- if (map1->size > map2->size) {
- large_map = map1;
- small_map = map2;
- } else {
- large_map = map2;
- small_map = map1;
- }
-
- rc = libxl_bitmap_alloc(ctx, and_map, small_map->size * 8);
- if (rc)
- goto out;
-
- /*
- * If bitmaps aren't same size, their 'and' will be size of
- * smaller bit map
- */
- for (i = 0; i < and_map->size; i++)
- and_map->map[i] = (large_map->map[i] & small_map->map[i]);
-
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl_bitmap_count_set(const libxl_bitmap *bitmap)
-{
- int i, nr_set_bits = 0;
- libxl_for_each_set_bit(i, *bitmap)
- nr_set_bits++;
-
- return nr_set_bits;
-}
-
-/* NB. caller is responsible for freeing the memory */
-char *libxl_bitmap_to_hex_string(libxl_ctx *ctx, const libxl_bitmap *bitmap)
-{
- GC_INIT(ctx);
- int i = bitmap->size;
- char *p = libxl__zalloc(NOGC, bitmap->size * 2 + 3);
- char *q = p;
- strncpy(p, "0x", 3);
- p += 2;
- while(--i >= 0) {
- sprintf(p, "%02x", bitmap->map[i]);
- p += 2;
- }
- *p = '\0';
- GC_FREE;
- return q;
-}
-
-int libxl_cpu_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *cpumap, int max_cpus)
-{
- GC_INIT(ctx);
- int rc = 0;
-
- if (max_cpus < 0) {
- rc = ERROR_INVAL;
- LOG(ERROR, "invalid number of cpus provided");
- goto out;
- }
- if (max_cpus == 0)
- max_cpus = libxl_get_max_cpus(ctx);
- if (max_cpus < 0) {
- LOG(ERROR, "failed to retrieve the maximum number of cpus");
- rc = max_cpus;
- goto out;
- }
- /* This can't fail: no need to check and log */
- libxl_bitmap_alloc(ctx, cpumap, max_cpus);
-
- out:
- GC_FREE;
- return rc;
-}
-
-int libxl_node_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *nodemap,
- int max_nodes)
-{
- GC_INIT(ctx);
- int rc = 0;
-
- if (max_nodes < 0) {
- rc = ERROR_INVAL;
- LOG(ERROR, "invalid number of nodes provided");
- goto out;
- }
-
- if (max_nodes == 0)
- max_nodes = libxl_get_max_nodes(ctx);
- if (max_nodes < 0) {
- LOG(ERROR, "failed to retrieve the maximum number of nodes");
- rc = max_nodes;
- goto out;
- }
- /* This can't fail: no need to check and log */
- libxl_bitmap_alloc(ctx, nodemap, max_nodes);
-
- out:
- GC_FREE;
- return rc;
-}
-
-int libxl__count_physical_sockets(libxl__gc *gc, int *sockets)
-{
- int rc;
- libxl_physinfo info;
-
- libxl_physinfo_init(&info);
-
- rc = libxl_get_physinfo(CTX, &info);
- if (rc)
- return rc;
-
- *sockets = info.nr_cpus / info.threads_per_core
- / info.cores_per_socket;
-
- libxl_physinfo_dispose(&info);
- return 0;
-}
-
-int libxl_socket_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *socketmap,
- int max_sockets)
-{
- GC_INIT(ctx);
- int rc = 0;
-
- if (max_sockets < 0) {
- rc = ERROR_INVAL;
- LOG(ERROR, "invalid number of sockets provided");
- goto out;
- }
-
- if (max_sockets == 0) {
- rc = libxl__count_physical_sockets(gc, &max_sockets);
- if (rc) {
- LOGE(ERROR, "failed to get system socket count");
- goto out;
- }
- }
- /* This can't fail: no need to check and log */
- libxl_bitmap_alloc(ctx, socketmap, max_sockets);
-
- out:
- GC_FREE;
- return rc;
-
-}
-
-int libxl_get_online_socketmap(libxl_ctx *ctx, libxl_bitmap *socketmap)
-{
- libxl_cputopology *tinfo = NULL;
- int nr_cpus = 0, i, rc = 0;
-
- tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
- if (tinfo == NULL) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- libxl_bitmap_set_none(socketmap);
- for (i = 0; i < nr_cpus; i++)
- if (tinfo[i].socket != XEN_INVALID_SOCKET_ID
- && !libxl_bitmap_test(socketmap, tinfo[i].socket))
- libxl_bitmap_set(socketmap, tinfo[i].socket);
-
- out:
- libxl_cputopology_list_free(tinfo, nr_cpus);
- return rc;
-}
-
-int libxl_nodemap_to_cpumap(libxl_ctx *ctx,
- const libxl_bitmap *nodemap,
- libxl_bitmap *cpumap)
-{
- libxl_cputopology *tinfo = NULL;
- int nr_cpus = 0, i, rc = 0;
-
- tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
- if (tinfo == NULL) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- libxl_bitmap_set_none(cpumap);
- for (i = 0; i < nr_cpus; i++) {
- if (libxl_bitmap_test(nodemap, tinfo[i].node))
- libxl_bitmap_set(cpumap, i);
- }
- out:
- libxl_cputopology_list_free(tinfo, nr_cpus);
- return rc;
-}
-
-int libxl_node_to_cpumap(libxl_ctx *ctx, int node,
- libxl_bitmap *cpumap)
-{
- libxl_bitmap nodemap;
- int rc = 0;
-
- libxl_bitmap_init(&nodemap);
-
- rc = libxl_node_bitmap_alloc(ctx, &nodemap, 0);
- if (rc)
- goto out;
-
- libxl_bitmap_set_none(&nodemap);
- libxl_bitmap_set(&nodemap, node);
-
- rc = libxl_nodemap_to_cpumap(ctx, &nodemap, cpumap);
-
- out:
- libxl_bitmap_dispose(&nodemap);
- return rc;
-}
-
-int libxl_cpumap_to_nodemap(libxl_ctx *ctx,
- const libxl_bitmap *cpumap,
- libxl_bitmap *nodemap)
-{
- libxl_cputopology *tinfo = NULL;
- int nr_cpus = 0, i, rc = 0;
-
- tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
- if (tinfo == NULL) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- libxl_bitmap_set_none(nodemap);
- libxl_for_each_set_bit(i, *cpumap) {
- if (i >= nr_cpus)
- break;
- libxl_bitmap_set(nodemap, tinfo[i].node);
- }
- out:
- libxl_cputopology_list_free(tinfo, nr_cpus);
- return rc;
-}
-
-int libxl_get_max_cpus(libxl_ctx *ctx)
-{
- int max_cpus = xc_get_max_cpus(ctx->xch);
-
- return max_cpus < 0 ? ERROR_FAIL : max_cpus;
-}
-
-int libxl_get_online_cpus(libxl_ctx *ctx)
-{
- int online_cpus = xc_get_online_cpus(ctx->xch);
-
- return online_cpus < 0 ? ERROR_FAIL : online_cpus;
-}
-
-int libxl_get_max_nodes(libxl_ctx *ctx)
-{
- int max_nodes = xc_get_max_nodes(ctx->xch);
-
- return max_nodes < 0 ? ERROR_FAIL : max_nodes;
-}
-
-int libxl__enum_from_string(const libxl_enum_string_table *t,
- const char *s, int *e)
-{
- if (!t) return ERROR_INVAL;
-
- for( ; t->s; t++) {
- if (!strcasecmp(t->s, s)) {
- *e = t->v;
- return 0;
- }
- }
- return ERROR_FAIL;
-}
-
-void libxl_cputopology_list_free(libxl_cputopology *list, int nr)
-{
- int i;
- for (i = 0; i < nr; i++)
- libxl_cputopology_dispose(&list[i]);
- free(list);
-}
-
-void libxl_pcitopology_list_free(libxl_pcitopology *list, int nr)
-{
- int i;
- for (i = 0; i < nr; i++)
- libxl_pcitopology_dispose(&list[i]);
- free(list);
-}
-
-void libxl_numainfo_list_free(libxl_numainfo *list, int nr)
-{
- int i;
- for (i = 0; i < nr; i++)
- libxl_numainfo_dispose(&list[i]);
- free(list);
-}
-
-void libxl_vcpuinfo_list_free(libxl_vcpuinfo *list, int nr)
-{
- int i;
- for (i = 0; i < nr; i++)
- libxl_vcpuinfo_dispose(&list[i]);
- free(list);
-}
-
-int libxl__sendmsg_fds(libxl__gc *gc, int carrier,
- const char data,
- int nfds, const int fds[], const char *what) {
- struct msghdr msg = { 0 };
- struct cmsghdr *cmsg;
- size_t spaceneeded = nfds * sizeof(fds[0]);
- char control[CMSG_SPACE(spaceneeded)];
- const size_t datalen = 1;
- struct iovec iov;
- int r;
-
- iov.iov_base = (void*)&data;
- iov.iov_len = datalen;
-
- /* compose the message */
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
-
- /* attach open fd */
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(spaceneeded);
- memcpy(CMSG_DATA(cmsg), fds, spaceneeded);
-
- msg.msg_controllen = cmsg->cmsg_len;
-
- while (1) {
- r = sendmsg(carrier, &msg, 0);
- if (r < 0) {
- if (errno == EINTR)
- continue;
- if (errno == EWOULDBLOCK) {
- return ERROR_NOT_READY;
- }
- LOGE(ERROR, "failed to send fd-carrying message (%s)", what);
- return ERROR_FAIL;
- }
- if (r != datalen) {
- LOG(ERROR, "sendmsg have written %d instead of %zu",
- r, datalen);
- return ERROR_FAIL;
- }
- break;
- };
-
- return 0;
-}
-
-int libxl__recvmsg_fds(libxl__gc *gc, int carrier,
- void *databuf, size_t datalen,
- int nfds, int fds[], const char *what)
-{
- struct msghdr msg = { 0 };
- struct cmsghdr *cmsg;
- size_t spaceneeded = nfds * sizeof(fds[0]);
- char control[CMSG_SPACE(spaceneeded)];
- struct iovec iov;
- int r;
-
- iov.iov_base = databuf;
- iov.iov_len = datalen;
-
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
-
- for (;;) {
- r = recvmsg(carrier, &msg, 0);
- if (r < 0) {
- if (errno == EINTR) continue;
- if (errno == EWOULDBLOCK) return -1;
- LOGE(ERROR,"recvmsg failed (%s)", what);
- return ERROR_FAIL;
- }
- if (r == 0) {
- LOG(ERROR,"recvmsg got EOF (%s)", what);
- return ERROR_FAIL;
- }
- cmsg = CMSG_FIRSTHDR(&msg);
- if (cmsg->cmsg_len <= CMSG_LEN(0)) {
- LOG(ERROR,"recvmsg got no control msg"
- " when expecting fds (%s)", what);
- return ERROR_FAIL;
- }
- if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
- LOG(ERROR, "recvmsg got unexpected"
- " cmsg_level %d (!=%d) or _type %d (!=%d) (%s)",
- cmsg->cmsg_level, SOL_SOCKET,
- cmsg->cmsg_type, SCM_RIGHTS,
- what);
- return ERROR_FAIL;
- }
- if (cmsg->cmsg_len != CMSG_LEN(spaceneeded) ||
- msg.msg_controllen != cmsg->cmsg_len) {
- LOG(ERROR, "recvmsg got unexpected"
- " number of fds or extra control data"
- " (%ld bytes' worth, expected %ld) (%s)",
- (long)CMSG_LEN(spaceneeded), (long)cmsg->cmsg_len,
- what);
- int i, fd;
- unsigned char *p;
- for (i=0, p=CMSG_DATA(cmsg);
- CMSG_SPACE(i * sizeof(fds[0]));
- i++, i+=sizeof(fd)) {
- memcpy(&fd, p, sizeof(fd));
- close(fd);
- }
- return ERROR_FAIL;
- }
- memcpy(fds, CMSG_DATA(cmsg), spaceneeded);
- return 0;
- }
-}
-
-void libxl_dominfo_list_free(libxl_dominfo *list, int nr)
-{
- int i;
- for (i = 0; i < nr; i++)
- libxl_dominfo_dispose(&list[i]);
- free(list);
-}
-
-void libxl_vminfo_list_free(libxl_vminfo *list, int nr)
-{
- int i;
- for (i = 0; i < nr; i++)
- libxl_vminfo_dispose(&list[i]);
- free(list);
-}
-
-void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nr)
-{
- int i;
- for (i = 0; i < nr; i++)
- libxl_cpupoolinfo_dispose(&list[i]);
- free(list);
-}
-
-int libxl_domid_valid_guest(uint32_t domid)
-{
- /* returns 1 if the value _could_ be a valid guest domid, 0 otherwise
- * does not check whether the domain actually exists */
- return domid > 0 && domid < DOMID_FIRST_RESERVED;
-}
-
-void libxl_string_copy(libxl_ctx *ctx, char **dst, char * const*src)
-{
- GC_INIT(ctx);
-
- if (*src)
- *dst = libxl__strdup(NOGC, *src);
- else
- *dst = NULL;
-
- GC_FREE;
-}
-
-/*
- * Fill @buf with @len random bytes.
- */
-int libxl__random_bytes(libxl__gc *gc, uint8_t *buf, size_t len)
-{
- static const char *dev = "/dev/urandom";
- int fd;
- int ret;
-
- fd = open(dev, O_RDONLY);
- if (fd < 0) {
- LOGE(ERROR, "failed to open \"%s\"", dev);
- return ERROR_FAIL;
- }
- ret = libxl_fd_set_cloexec(CTX, fd, 1);
- if (ret) {
- close(fd);
- return ERROR_FAIL;
- }
-
- ret = libxl_read_exactly(CTX, fd, buf, len, dev, NULL);
-
- close(fd);
-
- return ret;
-}
-
-int libxl__prepare_sockaddr_un(libxl__gc *gc,
- struct sockaddr_un *un, const char *path,
- const char *what)
-{
- if (sizeof(un->sun_path) - 1 <= strlen(path)) {
- LOG(ERROR, "UNIX socket path '%s' is too long for %s", path, what);
- LOG(DEBUG, "Path must be less than %zu bytes", sizeof(un->sun_path) - 1);
- return ERROR_INVAL;
- }
- memset(un, 0, sizeof(struct sockaddr_un));
- un->sun_family = AF_UNIX;
- strncpy(un->sun_path, path, sizeof(un->sun_path) - 1);
- return 0;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#ifndef LIBXL_UTILS_H
-#define LIBXL_UTILS_H
-
-#include "libxl.h"
-
-#ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
-const
-#endif
-char *libxl_basename(const char *name); /* returns string from strdup */
-
-unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus);
- /* deprecated; see LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONFIG in libxl.h */
-int libxl_name_to_domid(libxl_ctx *ctx, const char *name, uint32_t *domid);
-int libxl_domain_qualifier_to_domid(libxl_ctx *ctx, const char *name, uint32_t *domid);
-char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid);
-int libxl_cpupool_qualifier_to_cpupoolid(libxl_ctx *ctx, const char *p,
- uint32_t *poolid_r,
- int *was_name_r);
-int libxl_name_to_cpupoolid(libxl_ctx *ctx, const char *name, uint32_t *poolid);
-char *libxl_cpupoolid_to_name(libxl_ctx *ctx, uint32_t poolid);
-int libxl_cpupoolid_is_valid(libxl_ctx *ctx, uint32_t poolid);
-int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid);
-int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid);
-int libxl_create_logfile(libxl_ctx *ctx, const char *name, char **full_name);
-int libxl_string_to_backend(libxl_ctx *ctx, char *s, libxl_disk_backend *backend);
-
-int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
- void **data_r, int *datalen_r);
- /* Reads the contents of the plain file filename into a mallocd
- * buffer. Returns 0 or errno. Any errors other than ENOENT are logged.
- * If the file is empty, *data_r and *datalen_r are set to 0.
- * On error, *data_r and *datalen_r are unchanged.
- * data_r and/or datalen_r may be 0.
- */
-
-int libxl_read_exactly(libxl_ctx *ctx, int fd, void *data, ssize_t sz,
- const char *filename, const char *what);
-int libxl_write_exactly(libxl_ctx *ctx, int fd, const void *data,
- ssize_t sz, const char *filename, const char *what);
- /* Returns 0 or errno. If file is truncated on reading, returns
- * EPROTO and you have no way to tell how much was read. Errors are
- * logged using filename (which is only used for logging) and what
- * (which may be 0). */
-
-int libxl_pipe(libxl_ctx *ctx, int pipes[2]);
- /* Just like pipe(2), but log errors. */
-
-void libxl_report_child_exitstatus(libxl_ctx *ctx, xentoollog_level,
- const char *what, pid_t pid, int status);
- /* treats all exit statuses as errors; if that's not what you want,
- * check status yourself first */
-
-int libxl_mac_to_device_nic(libxl_ctx *ctx, uint32_t domid,
- const char *mac, libxl_device_nic *nic);
-int libxl_devid_to_device_nic(libxl_ctx *ctx, uint32_t domid, int devid,
- libxl_device_nic *nic);
-
-int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid, const char *vdev,
- libxl_device_disk *disk);
-
-int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
- libxl_uuid *uuid, libxl_device_vtpm *vtpm);
-int libxl_devid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
- int devid, libxl_device_vtpm *vtpm);
-int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, uint32_t domid,
- int devid, libxl_device_usbctrl *usbctrl);
-
-int libxl_devid_to_device_vkb(libxl_ctx *ctx, uint32_t domid,
- int devid, libxl_device_vkb *vkb);
-
-int libxl_devid_to_device_vdispl(libxl_ctx *ctx, uint32_t domid,
- int devid, libxl_device_vdispl *vdispl);
-
-int libxl_devid_to_device_vsnd(libxl_ctx *ctx, uint32_t domid,
- int devid, libxl_device_vsnd *vsnd);
-
-int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx, uint32_t domid,
- int ctrl, int port,
- libxl_device_usbdev *usbdev);
-
-int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits);
- /* Allocated bimap is from malloc, libxl_bitmap_dispose() to be
- * called by the application when done. */
-void libxl_bitmap_copy_alloc(libxl_ctx *ctx, libxl_bitmap *dptr,
- const libxl_bitmap *sptr);
-void libxl_bitmap_copy(libxl_ctx *ctx, libxl_bitmap *dptr,
- const libxl_bitmap *sptr);
-int libxl_bitmap_is_full(const libxl_bitmap *bitmap);
-int libxl_bitmap_is_empty(const libxl_bitmap *bitmap);
-int libxl_bitmap_test(const libxl_bitmap *bitmap, int bit);
-void libxl_bitmap_set(libxl_bitmap *bitmap, int bit);
-void libxl_bitmap_reset(libxl_bitmap *bitmap, int bit);
-int libxl_bitmap_count_set(const libxl_bitmap *bitmap);
-int libxl_bitmap_or(libxl_ctx *ctx, libxl_bitmap *or_map,
- const libxl_bitmap *map1,
- const libxl_bitmap *map2);
-int libxl_bitmap_and(libxl_ctx *ctx, libxl_bitmap *and_map,
- const libxl_bitmap *map1,
- const libxl_bitmap *map2);
-char *libxl_bitmap_to_hex_string(libxl_ctx *ctx, const libxl_bitmap *bitmap);
-static inline void libxl_bitmap_set_any(libxl_bitmap *bitmap)
-{
- memset(bitmap->map, -1, bitmap->size);
-}
-static inline void libxl_bitmap_set_none(libxl_bitmap *bitmap)
-{
- memset(bitmap->map, 0, bitmap->size);
-}
-static inline int libxl_bitmap_cpu_valid(libxl_bitmap *bitmap, int bit)
-{
- return bit >= 0 && bit < (bitmap->size * 8);
-}
-#define libxl_for_each_bit(var, map) for (var = 0; var < (map).size * 8; var++)
-#define libxl_for_each_set_bit(v, m) for (v = 0; v < (m).size * 8; v++) \
- if (libxl_bitmap_test(&(m), v))
-
-/*
- * Compares two bitmaps bit by bit, up to nr_bits or, if nr_bits is 0, up
- * to the size of the largest bitmap. If sizes does not match, bits past the
- * of a bitmap are considered as being 0, which matches with the semantic and
- * implementation of libxl_bitmap_test I think().
- *
- * So, basically, [0,1,0] and [0,1] are considered equal, while [0,1,1] and
- * [0,1] are different.
- */
-static inline int libxl_bitmap_equal(const libxl_bitmap *ba,
- const libxl_bitmap *bb,
- int nr_bits)
-{
- int i;
-
- if (nr_bits == 0)
- nr_bits = ba->size > bb->size ? ba->size * 8 : bb->size * 8;
-
- for (i = 0; i < nr_bits; i++) {
- if (libxl_bitmap_test(ba, i) != libxl_bitmap_test(bb, i))
- return 0;
- }
- return 1;
-}
-
-int libxl_cpu_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *cpumap, int max_cpus);
-int libxl_node_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *nodemap,
- int max_nodes);
-int libxl_socket_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *socketmap,
- int max_sockets);
-/* Fill socketmap with the CPU topology information on the system. */
-int libxl_get_online_socketmap(libxl_ctx *ctx, libxl_bitmap *socketmap);
-
-/* Populate cpumap with the cpus spanned by the nodes in nodemap */
-int libxl_nodemap_to_cpumap(libxl_ctx *ctx,
- const libxl_bitmap *nodemap,
- libxl_bitmap *cpumap);
-/* Populate cpumap with the cpus spanned by node */
-int libxl_node_to_cpumap(libxl_ctx *ctx, int node,
- libxl_bitmap *cpumap);
-/* Populate nodemap with the nodes of the cpus in cpumap */
-int libxl_cpumap_to_nodemap(libxl_ctx *ctx,
- const libxl_bitmap *cpumap,
- libxl_bitmap *nodemap);
-
- static inline uint32_t libxl__sizekb_to_mb(uint32_t s) {
- return (s + 1023) / 1024;
-}
-
-void libxl_string_copy(libxl_ctx *ctx, char **dst, char * const*src);
-
-
-#define LIBXL_FILLZERO(object) (memset(&(object), 0, sizeof((object))))
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2008,2010 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#if defined(__linux__)
-
-int libxl_uuid_is_nil(const libxl_uuid *uuid)
-{
- return uuid_is_null(uuid->uuid);
-}
-
-void libxl_uuid_generate(libxl_uuid *uuid)
-{
- uuid_generate(uuid->uuid);
-}
-
-int libxl_uuid_from_string(libxl_uuid *uuid, const char *in)
-{
- return uuid_parse(in, uuid->uuid);
-}
-
-void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst,
- const libxl_uuid *src)
-{
- uuid_copy(dst->uuid, src->uuid);
-}
-
-void libxl_uuid_clear(libxl_uuid *uuid)
-{
- uuid_clear(uuid->uuid);
-}
-
-int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2)
-{
- return uuid_compare(uuid1->uuid, uuid2->uuid);
-}
-
-const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid)
-{
- return uuid->uuid;
-}
-
-uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid)
-{
- return uuid->uuid;
-}
-
-#elif defined(__FreeBSD__) || defined(__NetBSD__)
-
-int libxl_uuid_is_nil(const libxl_uuid *uuid)
-{
- uint32_t status;
- uuid_t nat_uuid;
-
- uuid_dec_be(uuid->uuid, &nat_uuid);
-
- return uuid_is_nil(&nat_uuid, &status);
-}
-
-void libxl_uuid_generate(libxl_uuid *uuid)
-{
- uint32_t status;
- uuid_t nat_uuid;
-
- uuid_create(&nat_uuid, &status);
- assert(status == uuid_s_ok);
-
- uuid_enc_be(uuid->uuid, &nat_uuid);
-}
-
-#ifdef __FreeBSD__
-int libxl_uuid_from_string(libxl_uuid *uuid, const char *in)
-{
- uint32_t status;
- uuid_t nat_uuid;
-
- uuid_from_string(in, &nat_uuid, &status);
- if (status != uuid_s_ok)
- return ERROR_FAIL;
- uuid_enc_be(uuid->uuid, &nat_uuid);
-
- return 0;
-}
-#else
-#define LIBXL__UUID_PTRS(uuid) &uuid[0], &uuid[1], &uuid[2], &uuid[3], \
- &uuid[4], &uuid[5], &uuid[6], &uuid[7], \
- &uuid[8], &uuid[9], &uuid[10],&uuid[11], \
- &uuid[12],&uuid[13],&uuid[14],&uuid[15]
-int libxl_uuid_from_string(libxl_uuid *uuid, const char *in)
-{
- if ( sscanf(in, LIBXL_UUID_FMT, LIBXL__UUID_PTRS(uuid->uuid)) != sizeof(uuid->uuid) )
- return -1;
- return 0;
-}
-#undef LIBXL__UUID_PTRS
-#endif
-
-void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst,
- const libxl_uuid *src)
-{
- memcpy(&dst->uuid, &src->uuid, sizeof(dst->uuid));
-}
-
-void libxl_uuid_clear(libxl_uuid *uuid)
-{
- memset(&uuid->uuid, 0, sizeof(uuid->uuid));
-}
-
-#ifdef __FreeBSD__
-int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2)
-{
- uuid_t nat_uuid1, nat_uuid2;
-
- uuid_dec_be(uuid1->uuid, &nat_uuid1);
- uuid_dec_be(uuid2->uuid, &nat_uuid2);
-
- return uuid_compare(&nat_uuid1, &nat_uuid2, NULL);
-}
-#else
-int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2)
-{
- return memcmp(uuid1->uuid, uuid2->uuid, sizeof(uuid1->uuid));
-}
-#endif
-
-const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid)
-{
-
- return uuid->uuid;
-}
-
-uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid)
-{
-
- return uuid->uuid;
-}
-#else
-
-#error "Please update libxl_uuid.c for your OS"
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2008,2010 Citrix Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#ifndef __LIBXL_UUID_H__
-#define __LIBXL_UUID_H__
-
-#define LIBXL_UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
-#define LIBXL_UUID_FMTLEN ((2*16)+4) /* 16 hex bytes plus 4 hypens */
-#define LIBXL__UUID_BYTES(uuid) uuid[0], uuid[1], uuid[2], uuid[3], \
- uuid[4], uuid[5], uuid[6], uuid[7], \
- uuid[8], uuid[9], uuid[10], uuid[11], \
- uuid[12], uuid[13], uuid[14], uuid[15]
-#define LIBXL_UUID_BYTES(arg) LIBXL__UUID_BYTES((arg).uuid)
-
-typedef struct {
- /* UUID as an octet stream in big-endian byte-order. */
- unsigned char uuid[16];
-} libxl_uuid;
-
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040700
-#if defined(__linux__)
-
-#include <uuid/uuid.h>
-#include <stdint.h>
-
-#elif defined(__FreeBSD__) || defined(__NetBSD__)
-
-#include <uuid.h>
-#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-
-#else
-
-#error "Please update libxl_uuid.h for your OS"
-
-#endif
-#endif
-
-int libxl_uuid_is_nil(const libxl_uuid *uuid);
-void libxl_uuid_generate(libxl_uuid *uuid);
-int libxl_uuid_from_string(libxl_uuid *uuid, const char *in);
-void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst,
- const libxl_uuid *src);
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040500
-static inline void libxl_uuid_copy_0x040400(libxl_uuid *dst,
- const libxl_uuid *src)
-{
- libxl_uuid_copy(NULL, dst, src);
-}
-#define libxl_uuid_copy libxl_uuid_copy_0x040400
-#endif
-
-void libxl_uuid_clear(libxl_uuid *uuid);
-int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2);
-const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid);
-uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid);
-
-#endif /* __LIBXL_UUID_H__ */
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2016 EPAM Systems Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_internal.h"
-
-#include <xen/io/displif.h>
-
-static int libxl__device_vdispl_setdefault(libxl__gc *gc, uint32_t domid,
- libxl_device_vdispl *vdispl,
- bool hotplug)
-{
- return libxl__resolve_domid(gc, vdispl->backend_domname,
- &vdispl->backend_domid);
-}
-
-static int libxl__vdispl_from_xenstore(libxl__gc *gc, const char *libxl_path,
- libxl_devid devid,
- libxl_device_vdispl *vdispl)
-{
- const char *be_path;
- int rc;
-
- vdispl->devid = devid;
- rc = libxl__xs_read_mandatory(gc, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path),
- &be_path);
- if (rc) return rc;
-
- return libxl__backendpath_parse_domid(gc, be_path, &vdispl->backend_domid);
-}
-
-static void libxl__update_config_vdispl(libxl__gc *gc,
- libxl_device_vdispl *dst,
- libxl_device_vdispl *src)
-{
- dst->devid = src->devid;
- dst->be_alloc = src->be_alloc;
-}
-
-static int libxl_device_vdispl_compare(const libxl_device_vdispl *d1,
- const libxl_device_vdispl *d2)
-{
- return COMPARE_DEVID(d1, d2);
-}
-
-static void libxl__device_vdispl_add(libxl__egc *egc, uint32_t domid,
- libxl_device_vdispl *vdispl,
- libxl__ao_device *aodev)
-{
- libxl__device_add_async(egc, domid, &libxl__vdispl_devtype, vdispl, aodev);
-}
-
-static int libxl__set_xenstore_vdispl(libxl__gc *gc, uint32_t domid,
- libxl_device_vdispl *vdispl,
- flexarray_t *back, flexarray_t *front,
- flexarray_t *ro_front)
-{
- int i;
-
- flexarray_append_pair(ro_front, XENDISPL_FIELD_BE_ALLOC,
- GCSPRINTF("%d", vdispl->be_alloc));
-
- for (i = 0; i < vdispl->num_connectors; i++) {
- flexarray_append_pair(ro_front, GCSPRINTF("%d/"XENDISPL_FIELD_RESOLUTION, i),
- GCSPRINTF("%d"XENDISPL_RESOLUTION_SEPARATOR"%d", vdispl->connectors[i].width,
- vdispl->connectors[i].height));
- flexarray_append_pair(ro_front, GCSPRINTF("%d/"XENDISPL_FIELD_UNIQUE_ID, i),
- vdispl->connectors[i].unique_id);
- }
-
- return 0;
-}
-
-static int libxl__device_vdispl_getconnectors(libxl_ctx *ctx,
- const char *path,
- libxl_vdisplinfo *info)
-{
- GC_INIT(ctx);
- char *connector = NULL;
- char *connector_path;
- int i, rc;
-
- info->num_connectors = 0;
-
- connector_path = GCSPRINTF("%s/%d", path, info->num_connectors);
-
- while ((connector = xs_read(ctx->xsh, XBT_NULL, connector_path, NULL)) !=
- NULL) {
- free(connector);
- connector_path = GCSPRINTF("%s/%d", path, ++info->num_connectors);
- }
-
- info->connectors = libxl__calloc(NOGC, info->num_connectors,
- sizeof(*info->connectors));
-
- for (i = 0; i < info->num_connectors; i++) {
- char *value;
- char *value_path;
-
- value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_UNIQUE_ID, path, i);
- info->connectors[i].unique_id = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
- if (info->connectors[i].unique_id == NULL) { rc = ERROR_FAIL; goto out; }
-
- value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_RESOLUTION, path, i);
- value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
- if (value == NULL) { rc = ERROR_FAIL; goto out; }
-
- rc = sscanf(value, "%u"XENDISPL_RESOLUTION_SEPARATOR"%u", &info->connectors[i].width,
- &info->connectors[i].height);
- free(value);
-
- if (rc != 2) {
- rc = ERROR_FAIL; goto out;
- }
-
- value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_REQ_RING_REF, path, i);
- value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
- info->connectors[i].req_rref = value ? strtoul(value, NULL, 10) : -1;
- free(value);
-
- value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_REQ_CHANNEL, path, i);
- value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
- info->connectors[i].req_evtch = value ? strtoul(value, NULL, 10) : -1;
- free(value);
-
- value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_EVT_RING_REF, path, i);
- value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
- info->connectors[i].evt_rref = value ? strtoul(value, NULL, 10) : -1;
- free(value);
-
- value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_EVT_CHANNEL, path, i);
- value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
- info->connectors[i].evt_evtch = value ? strtoul(value, NULL, 10) : -1;
- free(value);
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-int libxl_device_vdispl_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_vdispl *vdispl,
- libxl_vdisplinfo *info)
-{
- GC_INIT(ctx);
- char *libxl_path, *devpath;
- char *val;
- int rc;
-
- libxl_vdisplinfo_init(info);
- info->devid = vdispl->devid;
-
- devpath = libxl__domain_device_frontend_path(gc, domid, info->devid,
- LIBXL__DEVICE_KIND_VDISPL);
- libxl_path = libxl__domain_device_libxl_path(gc, domid, info->devid,
- LIBXL__DEVICE_KIND_VDISPL);
-
- info->backend = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path),
- NULL);
- if (!info->backend) { rc = ERROR_FAIL; goto out; }
-
- rc = libxl__backendpath_parse_domid(gc, info->backend, &info->backend_id);
- if (rc) goto out;
-
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", devpath));
- info->state = val ? strtoul(val, NULL, 10) : -1;
-
- info->frontend = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/frontend", libxl_path),
- NULL);
- info->frontend_id = domid;
-
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/"XENDISPL_FIELD_BE_ALLOC, devpath));
- info->be_alloc = val ? strtoul(val, NULL, 10) : 0;
-
- rc = libxl__device_vdispl_getconnectors(ctx, devpath, info);
- if (rc) goto out;
-
- rc = 0;
-
-out:
- GC_FREE;
- return rc;
-}
-
-static LIBXL_DEFINE_DEVICE_FROM_TYPE(vdispl)
-static LIBXL_DEFINE_UPDATE_DEVID(vdispl)
-static LIBXL_DEFINE_DEVICES_ADD(vdispl)
-
-LIBXL_DEFINE_DEVID_TO_DEVICE(vdispl)
-LIBXL_DEFINE_DEVICE_ADD(vdispl)
-LIBXL_DEFINE_DEVICE_REMOVE(vdispl)
-LIBXL_DEFINE_DEVICE_LIST(vdispl)
-
-DEFINE_DEVICE_TYPE_STRUCT(vdispl, VDISPL,
- .update_config = (device_update_config_fn_t)libxl__update_config_vdispl,
- .from_xenstore = (device_from_xenstore_fn_t)libxl__vdispl_from_xenstore,
- .set_xenstore_config = (device_set_xenstore_config_fn_t)
- libxl__set_xenstore_vdispl
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2016 EPAM Systems Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_internal.h"
-
-#include <xen/io/kbdif.h>
-
-static int libxl__device_vkb_setdefault(libxl__gc *gc, uint32_t domid,
- libxl_device_vkb *vkb, bool hotplug)
-{
- if (vkb->backend_type == LIBXL_VKB_BACKEND_UNKNOWN) {
- vkb->backend_type = LIBXL_VKB_BACKEND_QEMU;
- }
-
- return libxl__resolve_domid(gc, vkb->backend_domname, &vkb->backend_domid);
-}
-
-static int libxl__device_vkb_dm_needed(void *e, uint32_t domid)
-{
- libxl_device_vkb *elem = e;
-
- return elem->backend_type == LIBXL_VKB_BACKEND_QEMU;
-}
-
-static int libxl__set_xenstore_vkb(libxl__gc *gc, uint32_t domid,
- libxl_device_vkb *vkb,
- flexarray_t *back, flexarray_t *front,
- flexarray_t *ro_front)
-{
- if (vkb->unique_id) {
- flexarray_append_pair(back, XENKBD_FIELD_UNIQUE_ID, vkb->unique_id);
- }
-
- if (vkb->feature_disable_keyboard) {
- flexarray_append_pair(back, XENKBD_FIELD_FEAT_DSBL_KEYBRD,
- GCSPRINTF("%u", vkb->feature_disable_keyboard));
- }
-
- if (vkb->feature_disable_pointer) {
- flexarray_append_pair(back, XENKBD_FIELD_FEAT_DSBL_POINTER,
- GCSPRINTF("%u", vkb->feature_disable_pointer));
- }
-
- if (vkb->feature_abs_pointer) {
- flexarray_append_pair(back, XENKBD_FIELD_FEAT_ABS_POINTER,
- GCSPRINTF("%u", vkb->feature_abs_pointer));
- }
-
- if (vkb->feature_raw_pointer) {
- flexarray_append_pair(back, XENKBD_FIELD_FEAT_RAW_POINTER,
- GCSPRINTF("%u", vkb->feature_raw_pointer));
- }
-
- if (vkb->feature_multi_touch) {
- flexarray_append_pair(back, XENKBD_FIELD_FEAT_MTOUCH,
- GCSPRINTF("%u", vkb->feature_multi_touch));
- flexarray_append_pair(back, XENKBD_FIELD_MT_WIDTH,
- GCSPRINTF("%u", vkb->multi_touch_width));
- flexarray_append_pair(back, XENKBD_FIELD_MT_HEIGHT,
- GCSPRINTF("%u", vkb->multi_touch_height));
- flexarray_append_pair(back, XENKBD_FIELD_MT_NUM_CONTACTS,
- GCSPRINTF("%u", vkb->multi_touch_num_contacts));
- }
-
- if (vkb->width) {
- flexarray_append_pair(back, XENKBD_FIELD_WIDTH,
- GCSPRINTF("%u", vkb->width));
- }
-
- if (vkb->height) {
- flexarray_append_pair(back, XENKBD_FIELD_HEIGHT,
- GCSPRINTF("%u", vkb->height));
- }
-
- return 0;
-}
-
-static int libxl__vkb_from_xenstore(libxl__gc *gc, const char *libxl_path,
- libxl_devid devid,
- libxl_device_vkb *vkb)
-{
- const char *be_path, *fe_path, *tmp;
- libxl__device dev;
- int rc;
-
- vkb->devid = devid;
-
- rc = libxl__xs_read_mandatory(gc, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path),
- &be_path);
- if (rc) goto out;
-
- rc = libxl__xs_read_mandatory(gc, XBT_NULL,
- GCSPRINTF("%s/frontend", libxl_path),
- &fe_path);
- if (rc) goto out;
-
- rc = libxl__backendpath_parse_domid(gc, be_path, &vkb->backend_domid);
- if (rc) goto out;
-
- rc = libxl__parse_backend_path(gc, be_path, &dev);
- if (rc) goto out;
-
- vkb->backend_type = dev.backend_kind == LIBXL__DEVICE_KIND_VINPUT ?
- LIBXL_VKB_BACKEND_LINUX : LIBXL_VKB_BACKEND_QEMU;
-
- vkb->unique_id = xs_read(CTX->xsh, XBT_NULL, GCSPRINTF("%s/"XENKBD_FIELD_UNIQUE_ID, be_path), NULL);
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENKBD_FIELD_FEAT_DSBL_KEYBRD,
- be_path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- vkb->feature_disable_keyboard = strtoul(tmp, NULL, 0);
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENKBD_FIELD_FEAT_DSBL_POINTER,
- be_path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- vkb->feature_disable_pointer = strtoul(tmp, NULL, 0);
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENKBD_FIELD_FEAT_ABS_POINTER,
- be_path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- vkb->feature_abs_pointer = strtoul(tmp, NULL, 0);
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENKBD_FIELD_FEAT_RAW_POINTER,
- be_path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- vkb->feature_raw_pointer = strtoul(tmp, NULL, 0);
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENKBD_FIELD_FEAT_MTOUCH,
- be_path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- vkb->feature_multi_touch = strtoul(tmp, NULL, 0);
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENKBD_FIELD_MT_WIDTH,
- be_path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- vkb->multi_touch_width = strtoul(tmp, NULL, 0);
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENKBD_FIELD_MT_HEIGHT,
- be_path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- vkb->multi_touch_height = strtoul(tmp, NULL, 0);
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENKBD_FIELD_MT_NUM_CONTACTS,
- be_path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- vkb->multi_touch_num_contacts = strtoul(tmp, NULL, 0);
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENKBD_FIELD_WIDTH,
- be_path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- vkb->width = strtoul(tmp, NULL, 0);
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENKBD_FIELD_HEIGHT,
- be_path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- vkb->height = strtoul(tmp, NULL, 0);
- }
-
- rc = 0;
-
-out:
-
- return rc;
-}
-
-static int libxl__device_from_vkb(libxl__gc *gc, uint32_t domid,
- libxl_device_vkb *type, libxl__device *device)
-{
- device->backend_devid = type->devid;
- device->backend_domid = type->backend_domid;
- device->backend_kind = type->backend_type == LIBXL_VKB_BACKEND_LINUX ?
- LIBXL__DEVICE_KIND_VINPUT : LIBXL__DEVICE_KIND_VKBD;
- device->devid = type->devid;
- device->domid = domid;
- device->kind = LIBXL__DEVICE_KIND_VKBD;
-
- return 0;
-}
-
-int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb,
- const libxl_asyncop_how *ao_how)
-{
- AO_CREATE(ctx, domid, ao_how);
- int rc;
-
- rc = libxl__device_add(gc, domid, &libxl__vkb_devtype, vkb);
- if (rc) {
- LOGD(ERROR, domid, "Unable to add vkb device");
- goto out;
- }
-
-out:
- libxl__ao_complete(egc, ao, rc);
- return AO_INPROGRESS;
-}
-
-int libxl_devid_to_device_vkb(libxl_ctx *ctx, uint32_t domid,
- int devid, libxl_device_vkb *vkb)
-{
- GC_INIT(ctx);
-
- libxl_device_vkb *vkbs = NULL;
- int n, i;
- int rc;
-
- libxl_device_vkb_init(vkb);
-
- vkbs = libxl__device_list(gc, &libxl__vkb_devtype, domid, &n);
-
- if (!vkbs) { rc = ERROR_NOTFOUND; goto out; }
-
- for (i = 0; i < n; ++i) {
- if (devid == vkbs[i].devid) {
- libxl_device_vkb_copy(ctx, vkb, &vkbs[i]);
- rc = 0;
- goto out;
- }
- }
-
- rc = ERROR_NOTFOUND;
-
-out:
-
- if (vkbs)
- libxl__device_list_free(&libxl__vkb_devtype, vkbs, n);
-
- GC_FREE;
- return rc;
-}
-
-int libxl_device_vkb_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_vkb *vkb,
- libxl_vkbinfo *info)
-{
- GC_INIT(ctx);
- char *libxl_path, *dompath, *devpath;
- char *val;
- int rc;
-
- libxl_vkbinfo_init(info);
- dompath = libxl__xs_get_dompath(gc, domid);
- info->devid = vkb->devid;
-
- devpath = libxl__domain_device_frontend_path(gc, domid, info->devid,
- LIBXL__DEVICE_KIND_VKBD);
- libxl_path = libxl__domain_device_libxl_path(gc, domid, info->devid,
- LIBXL__DEVICE_KIND_VKBD);
-
- info->backend = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path),
- NULL);
- if (!info->backend) { rc = ERROR_FAIL; goto out; }
-
- rc = libxl__backendpath_parse_domid(gc, info->backend, &info->backend_id);
- if (rc) goto out;
-
- val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", devpath));
- info->state = val ? strtoul(val, NULL, 10) : -1;
-
- info->frontend = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/frontend", libxl_path),
- NULL);
- info->frontend_id = domid;
-
- val = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/"XENKBD_FIELD_EVT_CHANNEL, devpath));
- info->evtch = val ? strtoul(val, NULL, 10) : -1;
-
- val = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/"XENKBD_FIELD_RING_GREF, devpath));
- info->rref = val ? strtoul(val, NULL, 10) : -1;
-
- rc = 0;
-
-out:
- GC_FREE;
- return rc;
-}
-
-static LIBXL_DEFINE_UPDATE_DEVID(vkb)
-
-#define libxl__add_vkbs NULL
-#define libxl_device_vkb_compare NULL
-
-LIBXL_DEFINE_DEVICE_LIST(vkb)
-LIBXL_DEFINE_DEVICE_REMOVE(vkb)
-
-DEFINE_DEVICE_TYPE_STRUCT(vkb, VKBD,
- .skip_attach = 1,
- .dm_needed = libxl__device_vkb_dm_needed,
- .set_xenstore_config = (device_set_xenstore_config_fn_t)
- libxl__set_xenstore_vkb,
- .from_xenstore = (device_from_xenstore_fn_t)libxl__vkb_from_xenstore
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2014 Citrix Ltd.
- * Author Wei Liu <wei.liu2@citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-#include "libxl_osdeps.h" /* must come before any other headers */
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-#include <stdlib.h>
-
-#include <xenctrl_dom.h>
-
-bool libxl__vnuma_configured(const libxl_domain_build_info *b_info)
-{
- return b_info->num_vnuma_nodes != 0;
-}
-
-/* Sort vmemranges in ascending order with "start" */
-static int compare_vmemrange(const void *a, const void *b)
-{
- const xen_vmemrange_t *x = a, *y = b;
- if (x->start < y->start)
- return -1;
- if (x->start > y->start)
- return 1;
- return 0;
-}
-
-/* Check if a vcpu has an hard (or soft) affinity set in such
- * a way that it does not match the pnode to which the vcpu itself
- * is assigned to.
- */
-static int check_vnuma_affinity(libxl__gc *gc,
- unsigned int vcpu,
- unsigned int pnode,
- unsigned int num_affinity,
- const libxl_bitmap *affinity,
- const char *kind)
-{
- libxl_bitmap nodemap;
- int rc = 0;
-
- libxl_bitmap_init(&nodemap);
-
- rc = libxl_node_bitmap_alloc(CTX, &nodemap, 0);
- if (rc) {
- LOG(ERROR, "Can't allocate nodemap");
- goto out;
- }
-
- rc = libxl_cpumap_to_nodemap(CTX, affinity, &nodemap);
- if (rc) {
- LOG(ERROR, "Can't convert Vcpu %d affinity to nodemap", vcpu);
- goto out;
- }
-
- if (libxl_bitmap_count_set(&nodemap) != 1 ||
- !libxl_bitmap_test(&nodemap, pnode))
- LOG(WARN, "Vcpu %d %s affinity and vnuma info mismatch", vcpu, kind);
-
-out:
- libxl_bitmap_dispose(&nodemap);
- return rc;
-}
-
-/* Check if vNUMA configuration is valid:
- * 1. all pnodes inside vnode_to_pnode array are valid
- * 2. each vcpu belongs to one and only one vnode
- * 3. each vmemrange is valid and doesn't overlap with any other
- * 4. local distance cannot be larger than remote distance
- *
- * Check also, if any hard or soft affinity is specified, whether
- * they match with the vNUMA related bits (namely vcpus to vnodes
- * mappings and vnodes to pnodes association). If that does not
- * hold, however, just print a warning, as that has "only"
- * performance implications.
- */
-int libxl__vnuma_config_check(libxl__gc *gc,
- const libxl_domain_build_info *b_info,
- const libxl__domain_build_state *state)
-{
- int nr_nodes = 0, rc = ERROR_VNUMA_CONFIG_INVALID;
- unsigned int i, j;
- libxl_numainfo *ninfo = NULL;
- uint64_t total_memkb = 0;
- libxl_bitmap cpumap;
- libxl_vnode_info *v;
-
- libxl_bitmap_init(&cpumap);
-
- /* Check pnode specified is valid */
- ninfo = libxl_get_numainfo(CTX, &nr_nodes);
- if (!ninfo) {
- LOG(ERROR, "libxl_get_numainfo failed");
- goto out;
- }
-
- for (i = 0; i < b_info->num_vnuma_nodes; i++) {
- uint32_t pnode;
-
- v = &b_info->vnuma_nodes[i];
- pnode = v->pnode;
-
- /* The pnode specified is not valid? */
- if (pnode >= nr_nodes) {
- LOG(ERROR, "Invalid pnode %"PRIu32" specified", pnode);
- goto out;
- }
-
- total_memkb += v->memkb;
- }
-
- if (total_memkb != b_info->max_memkb) {
- LOG(ERROR, "Amount of memory mismatch (0x%"PRIx64" != 0x%"PRIx64")",
- total_memkb, b_info->max_memkb);
- goto out;
- }
-
- /* Check vcpu mapping */
- libxl_cpu_bitmap_alloc(CTX, &cpumap, b_info->max_vcpus);
- for (i = 0; i < b_info->num_vnuma_nodes; i++) {
- v = &b_info->vnuma_nodes[i];
- libxl_for_each_set_bit(j, v->vcpus) {
- if (!libxl_bitmap_test(&cpumap, j))
- libxl_bitmap_set(&cpumap, j);
- else {
- LOG(ERROR, "Vcpu %d assigned more than once", j);
- goto out;
- }
- }
- }
-
- for (i = 0; i < b_info->max_vcpus; i++) {
- if (!libxl_bitmap_test(&cpumap, i)) {
- LOG(ERROR, "Vcpu %d is not assigned to any vnode", i);
- goto out;
- }
- }
-
- /* Check whether vcpu affinity (if any) matches vnuma configuration */
- for (i = 0; i < b_info->num_vnuma_nodes; i++) {
- v = &b_info->vnuma_nodes[i];
- libxl_for_each_set_bit(j, v->vcpus) {
- if (b_info->num_vcpu_hard_affinity > j)
- check_vnuma_affinity(gc, j, v->pnode,
- b_info->num_vcpu_hard_affinity,
- &b_info->vcpu_hard_affinity[j],
- "hard");
- if (b_info->num_vcpu_soft_affinity > j)
- check_vnuma_affinity(gc, j, v->pnode,
- b_info->num_vcpu_soft_affinity,
- &b_info->vcpu_soft_affinity[j],
- "soft");
- }
- }
-
- /* Check vmemranges */
- qsort(state->vmemranges, state->num_vmemranges, sizeof(xen_vmemrange_t),
- compare_vmemrange);
-
- for (i = 0; i < state->num_vmemranges; i++) {
- if (state->vmemranges[i].end < state->vmemranges[i].start) {
- LOG(ERROR, "Vmemrange end < start");
- goto out;
- }
- }
-
- for (i = 0; i < state->num_vmemranges - 1; i++) {
- if (state->vmemranges[i].end > state->vmemranges[i+1].start) {
- LOG(ERROR,
- "Vmemranges overlapped, 0x%"PRIx64"-0x%"PRIx64", 0x%"PRIx64"-0x%"PRIx64,
- state->vmemranges[i].start, state->vmemranges[i].end,
- state->vmemranges[i+1].start, state->vmemranges[i+1].end);
- goto out;
- }
- }
-
- /* Check vdistances */
- for (i = 0; i < b_info->num_vnuma_nodes; i++) {
- uint32_t local_distance, remote_distance;
-
- v = &b_info->vnuma_nodes[i];
- local_distance = v->distances[i];
-
- for (j = 0; j < v->num_distances; j++) {
- if (i == j) continue;
- remote_distance = v->distances[j];
- if (local_distance > remote_distance) {
- LOG(ERROR,
- "Distance from %u to %u smaller than %u's local distance",
- i, j, i);
- goto out;
- }
- }
- }
-
- rc = 0;
-out:
- libxl_numainfo_list_free(ninfo, nr_nodes);
- libxl_bitmap_dispose(&cpumap);
- return rc;
-}
-
-int libxl__vnuma_build_vmemrange_pv_generic(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_build_info *b_info,
- libxl__domain_build_state *state)
-{
- int i;
- uint64_t next;
- xen_vmemrange_t *v = NULL;
-
- /* Generate one vmemrange for each virtual node. */
- GCREALLOC_ARRAY(v, b_info->num_vnuma_nodes);
- next = 0;
- for (i = 0; i < b_info->num_vnuma_nodes; i++) {
- libxl_vnode_info *p = &b_info->vnuma_nodes[i];
-
- v[i].start = next;
- v[i].end = next + (p->memkb << 10);
- v[i].flags = 0;
- v[i].nid = i;
-
- next = v[i].end;
- }
-
- state->vmemranges = v;
- state->num_vmemranges = i;
-
- return 0;
-}
-
-/* Build vmemranges for PV guest */
-int libxl__vnuma_build_vmemrange_pv(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_build_info *b_info,
- libxl__domain_build_state *state)
-{
- assert(state->vmemranges == NULL);
- return libxl__arch_vnuma_build_vmemrange(gc, domid, b_info, state);
-}
-
-/* Build vmemranges for HVM guest */
-int libxl__vnuma_build_vmemrange_hvm(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_build_info *b_info,
- libxl__domain_build_state *state,
- struct xc_dom_image *dom)
-{
- uint64_t hole_start, hole_end, next;
- int nid, nr_vmemrange;
- xen_vmemrange_t *vmemranges;
- int rc;
-
- /* Derive vmemranges from vnode size and memory hole.
- *
- * Guest physical address space layout:
- * [0, hole_start) [hole_start, hole_end) [hole_end, highmem_end)
- */
- hole_start = dom->lowmem_end < dom->mmio_start ?
- dom->lowmem_end : dom->mmio_start;
- hole_end = (dom->mmio_start + dom->mmio_size) > (1ULL << 32) ?
- (dom->mmio_start + dom->mmio_size) : (1ULL << 32);
-
- assert(state->vmemranges == NULL);
-
- next = 0;
- nr_vmemrange = 0;
- vmemranges = NULL;
- for (nid = 0; nid < b_info->num_vnuma_nodes; nid++) {
- libxl_vnode_info *p = &b_info->vnuma_nodes[nid];
- uint64_t remaining_bytes = p->memkb << 10;
-
- /* Consider video ram belongs to vnode 0 */
- if (nid == 0) {
- if (p->memkb < b_info->video_memkb) {
- LOGD(ERROR, domid, "vnode 0 too small to contain video ram");
- rc = ERROR_INVAL;
- goto out;
- }
- remaining_bytes -= (b_info->video_memkb << 10);
- }
-
- while (remaining_bytes > 0) {
- uint64_t count = remaining_bytes;
-
- if (next >= hole_start && next < hole_end)
- next = hole_end;
- if ((next < hole_start) && (next + remaining_bytes >= hole_start))
- count = hole_start - next;
-
- GCREALLOC_ARRAY(vmemranges, nr_vmemrange+1);
- vmemranges[nr_vmemrange].start = next;
- vmemranges[nr_vmemrange].end = next + count;
- vmemranges[nr_vmemrange].flags = 0;
- vmemranges[nr_vmemrange].nid = nid;
-
- nr_vmemrange++;
- remaining_bytes -= count;
- next += count;
- }
- }
-
- state->vmemranges = vmemranges;
- state->num_vmemranges = nr_vmemrange;
-
- rc = 0;
-out:
- return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2016 EPAM Systems Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_internal.h"
-
-#include <xen/io/sndif.h>
-
-static int libxl__device_vsnd_setdefault(libxl__gc *gc, uint32_t domid,
- libxl_device_vsnd *vsnd,
- bool hotplug)
-{
- return libxl__resolve_domid(gc, vsnd->backend_domname,
- &vsnd->backend_domid);
-}
-
-static int libxl__device_from_vsnd(libxl__gc *gc, uint32_t domid,
- libxl_device_vsnd *vsnd,
- libxl__device *device)
-{
- device->backend_devid = vsnd->devid;
- device->backend_domid = vsnd->backend_domid;
- device->backend_kind = LIBXL__DEVICE_KIND_VSND;
- device->devid = vsnd->devid;
- device->domid = domid;
- device->kind = LIBXL__DEVICE_KIND_VSND;
-
- return 0;
-}
-
-static int libxl__sample_rates_from_string(libxl__gc *gc, const char *str,
- libxl_vsnd_params *params)
-{
- char *tmp = libxl__strdup(gc, str);
-
- params->num_sample_rates = 0;
- params->sample_rates = NULL;
-
- char *p = strtok(tmp, " ,");
-
- while (p != NULL) {
- params->sample_rates = libxl__realloc(NOGC, params->sample_rates,
- sizeof(*params->sample_rates) *
- (params->num_sample_rates + 1));
- params->sample_rates[params->num_sample_rates++] = strtoul(p, NULL, 0);
- p = strtok(NULL, " ,");
- }
-
- return 0;
-}
-
-static int libxl__sample_formats_from_string(libxl__gc *gc, const char *str,
- libxl_vsnd_params *params)
-{
- int rc;
- char *tmp = libxl__strdup(gc, str);
-
- params->num_sample_formats = 0;
- params->sample_formats = NULL;
-
- char *p = strtok(tmp, " ,");
-
- while (p != NULL) {
- params->sample_formats = libxl__realloc(NOGC, params->sample_formats,
- sizeof(*params->sample_formats) *
- (params->num_sample_formats + 1));
-
- libxl_vsnd_pcm_format format;
-
- rc = libxl_vsnd_pcm_format_from_string(p, &format);
- if (rc) goto out;
-
- params->sample_formats[params->num_sample_formats++] = format;
- p = strtok(NULL, " ,");
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-static int libxl__params_from_xenstore(libxl__gc *gc, const char *path,
- libxl_vsnd_params *params)
-{
- const char *tmp;
- int rc;
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENSND_FIELD_SAMPLE_RATES,
- path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- rc = libxl__sample_rates_from_string(gc, tmp, params);
- if (rc) goto out;
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENSND_FIELD_SAMPLE_FORMATS,
- path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- rc = libxl__sample_formats_from_string(gc, tmp, params);
- if (rc) goto out;
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENSND_FIELD_CHANNELS_MIN,
- path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- params->channels_min = strtoul(tmp, NULL, 0);
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENSND_FIELD_CHANNELS_MAX,
- path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- params->channels_max = strtoul(tmp, NULL, 0);
- }
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENSND_FIELD_BUFFER_SIZE,
- path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- params->buffer_size = strtoul(tmp, NULL, 0);
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-static int libxl__stream_from_xenstore(libxl__gc *gc, const char *path,
- libxl_vsnd_stream *stream)
-{
- const char *tmp;
- int rc;
-
- stream->unique_id = xs_read(CTX->xsh, XBT_NULL,
- GCSPRINTF("%s/"XENSND_FIELD_STREAM_UNIQUE_ID,
- path), NULL);
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/"XENSND_FIELD_TYPE,
- path), &tmp);
- if (rc) goto out;
-
- if (tmp) {
- libxl_vsnd_stream_type type;
-
- rc = libxl_vsnd_stream_type_from_string(tmp, &type);
- if (rc) goto out;
-
- stream->type = type;
- }
-
- rc = libxl__params_from_xenstore(gc, path, &stream->params);
- if (rc) goto out;
-
- rc = 0;
-
-out:
- return rc;
-}
-
-static int libxl__pcm_from_xenstore(libxl__gc *gc, const char *path,
- libxl_vsnd_pcm *pcm)
-{
- const char *tmp;
- int rc;
-
- pcm->name = xs_read(CTX->xsh, XBT_NULL,
- GCSPRINTF("%s/"XENSND_FIELD_DEVICE_NAME, path), NULL);
-
- rc = libxl__params_from_xenstore(gc, path, &pcm->params);
- if (rc) goto out;
-
- pcm->streams = NULL;
- pcm->num_vsnd_streams = 0;
-
- do {
- char *stream_path = GCSPRINTF("%s/%d", path, pcm->num_vsnd_streams);
-
- rc = libxl__xs_read_checked(gc, XBT_NULL, stream_path, &tmp);
- if (rc) goto out;
-
- if (tmp) {
- pcm->streams = libxl__realloc(NOGC, pcm->streams,
- sizeof(*pcm->streams) *
- (++pcm->num_vsnd_streams));
-
- libxl_vsnd_stream_init(&pcm->streams[pcm->num_vsnd_streams - 1]);
-
- rc = libxl__stream_from_xenstore(gc, stream_path,
- &pcm->streams[pcm->num_vsnd_streams
- - 1]);
- if (rc) goto out;
- }
- } while (tmp);
-
- rc = 0;
-
-out:
- return rc;
-}
-
-static int libxl__vsnd_from_xenstore(libxl__gc *gc, const char *libxl_path,
- libxl_devid devid,
- libxl_device_vsnd *vsnd)
-{
- const char *tmp;
- const char *fe_path;
- int rc;
-
- vsnd->devid = devid;
- rc = libxl__xs_read_mandatory(gc, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path),
- &tmp);
- if (rc) goto out;
-
- rc = libxl__backendpath_parse_domid(gc, tmp, &vsnd->backend_domid);
- if (rc) goto out;
-
- rc = libxl__xs_read_mandatory(gc, XBT_NULL,
- GCSPRINTF("%s/frontend", libxl_path),
- &fe_path);
- if (rc) goto out;
-
- vsnd->short_name = xs_read(CTX->xsh, XBT_NULL,
- GCSPRINTF("%s/"XENSND_FIELD_VCARD_SHORT_NAME,
- fe_path), NULL);
-
- vsnd->long_name = xs_read(CTX->xsh, XBT_NULL,
- GCSPRINTF("%s/"XENSND_FIELD_VCARD_LONG_NAME,
- fe_path), NULL);
-
- rc = libxl__params_from_xenstore(gc, fe_path, &vsnd->params);
- if (rc) goto out;
-
- vsnd->pcms = NULL;
- vsnd->num_vsnd_pcms = 0;
-
- do {
- char *pcm_path = GCSPRINTF("%s/%d", fe_path, vsnd->num_vsnd_pcms);
-
- rc = libxl__xs_read_checked(gc, XBT_NULL, pcm_path, &tmp);
- if (rc) goto out;
-
- if (tmp) {
- vsnd->pcms = libxl__realloc(NOGC, vsnd->pcms, sizeof(*vsnd->pcms) *
- (++vsnd->num_vsnd_pcms));
-
- libxl_vsnd_pcm_init(&vsnd->pcms[vsnd->num_vsnd_pcms - 1]);
-
- rc = libxl__pcm_from_xenstore(gc, pcm_path,
- &vsnd->pcms[vsnd->num_vsnd_pcms - 1]);
- if (rc) goto out;
- }
- } while (tmp);
-
- rc = 0;
-
-out:
- return rc;
-}
-
-static void libxl__update_config_vsnd(libxl__gc *gc,
- libxl_device_vsnd *dst,
- libxl_device_vsnd *src)
-{
- dst->devid = src->devid;
-}
-
-static int libxl_device_vsnd_compare(const libxl_device_vsnd *d1,
- const libxl_device_vsnd *d2)
-{
- return COMPARE_DEVID(d1, d2);
-}
-
-static void libxl__device_vsnd_add(libxl__egc *egc, uint32_t domid,
- libxl_device_vsnd *vsnd,
- libxl__ao_device *aodev)
-{
- libxl__device_add_async(egc, domid, &libxl__vsnd_devtype, vsnd, aodev);
-}
-
-static unsigned int libxl__rates_to_str_vsnd(char *str, uint32_t *sample_rates,
- int num_sample_rates)
-{
- unsigned int len;
- int i;
-
- len = 0;
-
- if (num_sample_rates == 0) goto out;
-
- for (i = 0; i < num_sample_rates - 1; i++) {
- if (str) {
- len += sprintf(&str[len], "%u,", sample_rates[i]);
- } else {
- len += snprintf(NULL, 0, "%u,", sample_rates[i]);
- }
- }
-
- if (str) {
- len += sprintf(&str[len], "%u", sample_rates[i]);
- } else {
- len += snprintf(NULL, 0, "%u", sample_rates[i]);
- }
-
-out:
- return len;
-}
-
-static unsigned int libxl__formats_to_str_vsnd(char *str,
- libxl_vsnd_pcm_format *sample_formats,
- int num_sample_formats)
-{
- unsigned int len;
- int i;
-
- len = 0;
-
- if (num_sample_formats == 0) goto out;
-
- for (i = 0; i < num_sample_formats - 1; i++) {
- if (str) {
- len += sprintf(&str[len], "%s,",
- libxl_vsnd_pcm_format_to_string(sample_formats[i]));
- } else {
- len += snprintf(NULL, 0, "%s,",
- libxl_vsnd_pcm_format_to_string(sample_formats[i]));
- }
- }
-
- if (str) {
- len += sprintf(&str[len], "%s",
- libxl_vsnd_pcm_format_to_string(sample_formats[i]));
- } else {
- len += snprintf(NULL, 0, "%s",
- libxl_vsnd_pcm_format_to_string(sample_formats[i]));
- }
-
-out:
- return len;
-}
-
-static int libxl__set_params_vsnd(libxl__gc *gc, char *path,
- libxl_vsnd_params *params, flexarray_t *front)
-{
- char *buffer;
- int len;
- int rc;
-
- if (params->sample_rates) {
- /* calculate required string size */
- len = libxl__rates_to_str_vsnd(NULL, params->sample_rates,
- params->num_sample_rates);
-
- if (len) {
- buffer = libxl__malloc(gc, len + 1);
-
- libxl__rates_to_str_vsnd(buffer, params->sample_rates,
- params->num_sample_rates);
- rc = flexarray_append_pair(front,
- GCSPRINTF("%s"XENSND_FIELD_SAMPLE_RATES,
- path), buffer);
- if (rc) goto out;
- }
- }
-
- if (params->sample_formats) {
- /* calculate required string size */
- len = libxl__formats_to_str_vsnd(NULL, params->sample_formats,
- params->num_sample_formats);
-
- if (len) {
- buffer = libxl__malloc(gc, len + 1);
-
- libxl__formats_to_str_vsnd(buffer, params->sample_formats,
- params->num_sample_formats);
- rc = flexarray_append_pair(front,
- GCSPRINTF("%s"XENSND_FIELD_SAMPLE_FORMATS,
- path), buffer);
- if (rc) goto out;
- }
- }
-
- if (params->channels_min) {
- rc = flexarray_append_pair(front,
- GCSPRINTF("%s"XENSND_FIELD_CHANNELS_MIN, path),
- GCSPRINTF("%u", params->channels_min));
- if (rc) goto out;
- }
-
- if (params->channels_max) {
- rc = flexarray_append_pair(front,
- GCSPRINTF("%s"XENSND_FIELD_CHANNELS_MAX, path),
- GCSPRINTF("%u", params->channels_max));
- if (rc) goto out;
- }
-
- if (params->buffer_size) {
- rc = flexarray_append_pair(front,
- GCSPRINTF("%s"XENSND_FIELD_BUFFER_SIZE, path),
- GCSPRINTF("%u", params->buffer_size));
- if (rc) goto out;
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-static int libxl__set_streams_vsnd(libxl__gc *gc, char *path,
- libxl_vsnd_stream *streams,
- int num_streams, flexarray_t *front)
-{
- int i;
- int rc;
-
- for (i = 0; i < num_streams; i++) {
- rc = flexarray_append_pair(front,
- GCSPRINTF("%s%d/"XENSND_FIELD_STREAM_UNIQUE_ID, path, i),
- streams[i].unique_id);
- if (rc) goto out;
-
- const char *type = libxl_vsnd_stream_type_to_string(streams[i].type);
-
- if (type) {
- rc = flexarray_append_pair(front,
- GCSPRINTF("%s%d/"XENSND_FIELD_TYPE, path, i),
- (char *)type);
- if (rc) goto out;
- }
-
- rc = libxl__set_params_vsnd(gc, GCSPRINTF("%s%d/", path, i),
- &streams[i].params, front);
- if (rc) goto out;
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-static int libxl__set_pcms_vsnd(libxl__gc *gc, libxl_vsnd_pcm *pcms,
- int num_pcms, flexarray_t *front)
-{
- int i;
- int rc;
-
- for (i = 0; i < num_pcms; i++) {
- if (pcms[i].name) {
- rc = flexarray_append_pair(front,
- GCSPRINTF("%d/"XENSND_FIELD_DEVICE_NAME, i),
- pcms[i].name);
- if (rc) goto out;
- }
-
- char *path = GCSPRINTF("%d/", i);
-
- rc = libxl__set_params_vsnd(gc, path, &pcms[i].params, front);
- if (rc) goto out;
-
- rc = libxl__set_streams_vsnd(gc, path, pcms[i].streams,
- pcms[i].num_vsnd_streams, front);
- if (rc) goto out;
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-static int libxl__set_xenstore_vsnd(libxl__gc *gc, uint32_t domid,
- libxl_device_vsnd *vsnd,
- flexarray_t *back, flexarray_t *front,
- flexarray_t *ro_front)
-{
- int rc;
-
- if (vsnd->long_name) {
- rc = flexarray_append_pair(front, XENSND_FIELD_VCARD_LONG_NAME,
- vsnd->long_name);
- if (rc) goto out;
- }
-
- if (vsnd->short_name) {
- rc = flexarray_append_pair(front, XENSND_FIELD_VCARD_SHORT_NAME,
- vsnd->short_name);
- if (rc) goto out;
- }
-
- rc = libxl__set_params_vsnd(gc, "", &vsnd->params, front);
- if (rc) goto out;
-
- rc = libxl__set_pcms_vsnd(gc, vsnd->pcms, vsnd->num_vsnd_pcms, front);
- if (rc) goto out;
-
- rc = 0;
-
-out:
- return rc;
-}
-
-static int libxl__device_stream_getinfo(libxl__gc *gc, const char *path,
- libxl_vsnd_pcm* pcm,
- libxl_pcminfo *info)
-{
- const char *tmp;
- int i;
- int rc;
-
- info->num_vsnd_streams = pcm->num_vsnd_streams;
- info->streams = libxl__malloc(NOGC, sizeof(*info->streams) * info->num_vsnd_streams);
-
- for (i = 0; i < info->num_vsnd_streams; i++)
- {
- libxl_streaminfo_init(&info->streams[i]);
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/%d/"XENSND_FIELD_RING_REF,
- path, i), &tmp);
- if (rc) goto out;
-
- info->streams[i].req_rref = tmp ? strtoul(tmp, NULL, 10) : -1;
-
- rc = libxl__xs_read_checked(gc, XBT_NULL,
- GCSPRINTF("%s/%d/"XENSND_FIELD_EVT_CHNL,
- path, i), &tmp);
- if (rc) goto out;
-
- info->streams[i].req_evtch = tmp ? strtoul(tmp, NULL, 10) : -1;
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-static int libxl__device_pcm_getinfo(libxl__gc *gc, const char *path,
- const libxl_device_vsnd *vsnd,
- libxl_vsndinfo *info)
-{
- int i;
- int rc;
-
- info->num_vsnd_pcms = vsnd->num_vsnd_pcms;
- info->pcms = libxl__malloc(NOGC, sizeof(*info->pcms) * info->num_vsnd_pcms);
-
- for (i = 0; i < info->num_vsnd_pcms; i++)
- {
- libxl_pcminfo_init(&info->pcms[i]);
-
- rc = libxl__device_stream_getinfo(gc, GCSPRINTF("%s/%d", path, i),
- &vsnd->pcms[i], &info->pcms[i]);
- if (rc) goto out;
- }
-
- rc = 0;
-
-out:
- return rc;
-}
-
-int libxl_device_vsnd_getinfo(libxl_ctx *ctx, uint32_t domid,
- const libxl_device_vsnd *vsnd,
- libxl_vsndinfo *info)
-{
- GC_INIT(ctx);
- char *libxl_path, *dompath, *devpath;
- const char *val;
- int rc;
-
- libxl_vsndinfo_init(info);
- dompath = libxl__xs_get_dompath(gc, domid);
- info->devid = vsnd->devid;
-
- devpath = libxl__domain_device_frontend_path(gc, domid, info->devid,
- LIBXL__DEVICE_KIND_VSND);
- libxl_path = libxl__domain_device_libxl_path(gc, domid, info->devid,
- LIBXL__DEVICE_KIND_VSND);
-
- info->backend = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path), NULL);
-
- rc = libxl__backendpath_parse_domid(gc, info->backend, &info->backend_id);
- if (rc) goto out;
-
- val = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/state", devpath), NULL);
-
- info->state = val ? strtoul(val, NULL, 10) : -1;
-
- info->frontend = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/frontend", libxl_path), NULL);
-
- info->frontend_id = domid;
-
- rc = libxl__device_pcm_getinfo(gc, devpath, vsnd, info);
- if (rc) goto out;
-
- rc = 0;
-
-out:
- GC_FREE;
- return rc;
-}
-
-int libxl_devid_to_device_vsnd(libxl_ctx *ctx, uint32_t domid,
- int devid, libxl_device_vsnd *vsnd)
-{
- GC_INIT(ctx);
-
- libxl_device_vsnd *vsnds = NULL;
- int n, i;
- int rc;
-
- libxl_device_vsnd_init(vsnd);
-
- vsnds = libxl__device_list(gc, &libxl__vsnd_devtype, domid, &n);
-
- if (!vsnds) { rc = ERROR_NOTFOUND; goto out; }
-
- for (i = 0; i < n; ++i) {
- if (devid == vsnds[i].devid) {
- libxl_device_vsnd_copy(ctx, vsnd, &vsnds[i]);
- rc = 0;
- goto out;
- }
- }
-
- rc = ERROR_NOTFOUND;
-
-out:
- if (vsnds)
- libxl__device_list_free(&libxl__vsnd_devtype, vsnds, n);
-
- GC_FREE;
- return rc;
-}
-
-static LIBXL_DEFINE_UPDATE_DEVID(vsnd)
-static LIBXL_DEFINE_DEVICES_ADD(vsnd)
-
-LIBXL_DEFINE_DEVICE_ADD(vsnd)
-LIBXL_DEFINE_DEVICE_REMOVE(vsnd)
-LIBXL_DEFINE_DEVICE_LIST(vsnd)
-
-DEFINE_DEVICE_TYPE_STRUCT(vsnd, VSND,
- .update_config = (device_update_config_fn_t) libxl__update_config_vsnd,
- .from_xenstore = (device_from_xenstore_fn_t) libxl__vsnd_from_xenstore,
- .set_xenstore_config = (device_set_xenstore_config_fn_t)
- libxl__set_xenstore_vsnd
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2016 SUSE Linux GmbH
- * Author Juergen Gross <jgross@suse.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-static int libxl__device_vtpm_setdefault(libxl__gc *gc, uint32_t domid,
- libxl_device_vtpm *vtpm, bool hotplug)
-{
- int rc;
- if (libxl_uuid_is_nil(&vtpm->uuid)) {
- libxl_uuid_generate(&vtpm->uuid);
- }
- rc = libxl__resolve_domid(gc, vtpm->backend_domname, &vtpm->backend_domid);
- return rc;
-}
-
-static void libxl__update_config_vtpm(libxl__gc *gc, libxl_device_vtpm *dst,
- libxl_device_vtpm *src)
-{
- dst->devid = src->devid;
- libxl_uuid_copy(CTX, &dst->uuid, &src->uuid);
-}
-
-static int libxl__set_xenstore_vtpm(libxl__gc *gc, uint32_t domid,
- libxl_device_vtpm *vtpm,
- flexarray_t *back, flexarray_t *front,
- flexarray_t *ro_front)
-{
- flexarray_append_pair(back, "handle", GCSPRINTF("%d", vtpm->devid));
- flexarray_append_pair(back, "uuid",
- GCSPRINTF(LIBXL_UUID_FMT,
- LIBXL_UUID_BYTES(vtpm->uuid)));
- flexarray_append_pair(back, "resume", "False");
-
- flexarray_append_pair(front, "handle", GCSPRINTF("%d", vtpm->devid));
-
- return 0;
-}
-
-static void libxl__device_vtpm_add(libxl__egc *egc, uint32_t domid,
- libxl_device_vtpm *vtpm,
- libxl__ao_device *aodev)
-{
- libxl__device_add_async(egc, domid, &libxl__vtpm_devtype, vtpm, aodev);
-}
-
-static int libxl__vtpm_from_xenstore(libxl__gc *gc, const char *libxl_path,
- libxl_devid devid,
- libxl_device_vtpm *vtpm)
-{
- int rc;
- const char *be_path;
- char *uuid;
-
- vtpm->devid = devid;
-
- rc = libxl__xs_read_mandatory(gc, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path),
- &be_path);
- if (rc) return rc;
-
- rc = libxl__backendpath_parse_domid(gc, be_path, &vtpm->backend_domid);
- if (rc) return rc;
-
- uuid = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/uuid", be_path));
- if (uuid) {
- if(libxl_uuid_from_string(&(vtpm->uuid), uuid)) {
- LOGD(ERROR, vtpm->backend_domid, "%s/uuid is a malformed uuid?? "
- "(%s) Probably a bug!!\n", be_path, uuid);
- return ERROR_FAIL;
- }
- }
-
- return 0;
-}
-
-int libxl_device_vtpm_getinfo(libxl_ctx *ctx,
- uint32_t domid,
- const libxl_device_vtpm *vtpm,
- libxl_vtpminfo *vtpminfo)
-{
- GC_INIT(ctx);
- char *libxl_path, *vtpmpath;
- char *val;
- int rc = 0;
-
- libxl_vtpminfo_init(vtpminfo);
- vtpminfo->devid = vtpm->devid;
-
- vtpmpath = libxl__domain_device_frontend_path(gc, domid, vtpminfo->devid,
- LIBXL__DEVICE_KIND_VTPM);
- libxl_path = libxl__domain_device_libxl_path(gc, domid, vtpminfo->devid,
- LIBXL__DEVICE_KIND_VTPM);
- vtpminfo->backend = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/backend", libxl_path), NULL);
- if (!vtpminfo->backend) {
- goto err;
- }
-
- rc = libxl__backendpath_parse_domid(gc, vtpminfo->backend,
- &vtpminfo->backend_id);
- if (rc) goto exit;
-
- val = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/state", vtpmpath));
- vtpminfo->state = val ? strtoul(val, NULL, 10) : -1;
-
- val = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/event-channel", vtpmpath));
- vtpminfo->evtch = val ? strtoul(val, NULL, 10) : -1;
-
- val = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/ring-ref", vtpmpath));
- vtpminfo->rref = val ? strtoul(val, NULL, 10) : -1;
-
- vtpminfo->frontend = xs_read(ctx->xsh, XBT_NULL,
- GCSPRINTF("%s/frontend", libxl_path), NULL);
- vtpminfo->frontend_id = domid;
-
- val = libxl__xs_read(gc, XBT_NULL,
- GCSPRINTF("%s/uuid", libxl_path));
- if(val == NULL) {
- LOGD(ERROR, domid, "%s/uuid does not exist!", vtpminfo->backend);
- goto err;
- }
- if(libxl_uuid_from_string(&(vtpminfo->uuid), val)) {
- LOGD(ERROR, domid,
- "%s/uuid is a malformed uuid?? (%s) Probably a bug!\n",
- vtpminfo->backend, val);
- goto err;
- }
-
- goto exit;
-err:
- rc = ERROR_FAIL;
-exit:
- GC_FREE;
- return rc;
-}
-
-int libxl_devid_to_device_vtpm(libxl_ctx *ctx,
- uint32_t domid,
- int devid,
- libxl_device_vtpm *vtpm)
-{
- GC_INIT(ctx);
- libxl_device_vtpm *vtpms;
- int nb, i;
- int rc;
-
- vtpms = libxl__device_list(gc, &libxl__vtpm_devtype, domid, &nb);
- if (!vtpms)
- return ERROR_FAIL;
-
- libxl_device_vtpm_init(vtpm);
- rc = 1;
- for (i = 0; i < nb; ++i) {
- if(devid == vtpms[i].devid) {
- vtpm->backend_domid = vtpms[i].backend_domid;
- vtpm->devid = vtpms[i].devid;
- libxl_uuid_copy(ctx, &vtpm->uuid, &vtpms[i].uuid);
- rc = 0;
- break;
- }
- }
-
- libxl__device_list_free(&libxl__vtpm_devtype, vtpms, nb);
- GC_FREE;
- return rc;
-}
-
-static int libxl_device_vtpm_compare(const libxl_device_vtpm *d1,
- const libxl_device_vtpm *d2)
-{
- return COMPARE_DEVID(d1, d2);
-}
-
-int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
- libxl_uuid* uuid, libxl_device_vtpm *vtpm)
-{
- GC_INIT(ctx);
- libxl_device_vtpm *vtpms;
- int nb, i;
- int rc;
-
- vtpms = libxl__device_list(gc, &libxl__vtpm_devtype, domid, &nb);
- if (!vtpms)
- return ERROR_FAIL;
-
- memset(vtpm, 0, sizeof (libxl_device_vtpm));
- rc = 1;
- for (i = 0; i < nb; ++i) {
- if(!libxl_uuid_compare(uuid, &vtpms[i].uuid)) {
- vtpm->backend_domid = vtpms[i].backend_domid;
- vtpm->devid = vtpms[i].devid;
- libxl_uuid_copy(ctx, &vtpm->uuid, &vtpms[i].uuid);
- rc = 0;
- break;
- }
- }
-
- libxl__device_list_free(&libxl__vtpm_devtype, vtpms, nb);
- GC_FREE;
- return rc;
-}
-
-static void libxl_device_vtpm_update_config(libxl__gc *gc, void *d, void *s)
-{
- libxl__update_config_vtpm(gc, d, s);
-}
-
-static LIBXL_DEFINE_UPDATE_DEVID(vtpm)
-static LIBXL_DEFINE_DEVICE_FROM_TYPE(vtpm)
-static LIBXL_DEFINE_DEVICES_ADD(vtpm)
-
-LIBXL_DEFINE_DEVICE_ADD(vtpm)
-LIBXL_DEFINE_DEVICE_REMOVE(vtpm)
-LIBXL_DEFINE_DEVICE_LIST(vtpm)
-
-DEFINE_DEVICE_TYPE_STRUCT(vtpm, VTPM,
- .update_config = libxl_device_vtpm_update_config,
- .from_xenstore = (device_from_xenstore_fn_t)libxl__vtpm_from_xenstore,
- .set_xenstore_config = (device_set_xenstore_config_fn_t)
- libxl__set_xenstore_vtpm,
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
-
+++ /dev/null
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-
-#include <xenctrl_dom.h>
-
-int libxl__arch_domain_prepare_config(libxl__gc *gc,
- libxl_domain_config *d_config,
- struct xen_domctl_createdomain *config)
-{
- switch(d_config->c_info.type) {
- case LIBXL_DOMAIN_TYPE_HVM:
- config->arch.emulation_flags = (XEN_X86_EMU_ALL & ~XEN_X86_EMU_VPCI);
- break;
- case LIBXL_DOMAIN_TYPE_PVH:
- config->arch.emulation_flags = XEN_X86_EMU_LAPIC;
- break;
- case LIBXL_DOMAIN_TYPE_PV:
- config->arch.emulation_flags = 0;
- break;
- default:
- abort();
- }
-
- return 0;
-}
-
-int libxl__arch_domain_save_config(libxl__gc *gc,
- libxl_domain_config *d_config,
- libxl__domain_build_state *state,
- const struct xen_domctl_createdomain *config)
-{
- return 0;
-}
-
-static const char *e820_names(int type)
-{
- switch (type) {
- case E820_RAM: return "RAM";
- case E820_RESERVED: return "Reserved";
- case E820_ACPI: return "ACPI";
- case E820_NVS: return "ACPI NVS";
- case E820_UNUSABLE: return "Unusable";
- default: break;
- }
- return "Unknown";
-}
-
-static int e820_sanitize(libxl__gc *gc, struct e820entry src[],
- uint32_t *nr_entries,
- unsigned long map_limitkb,
- unsigned long balloon_kb)
-{
- uint64_t delta_kb = 0, start = 0, start_kb = 0, last = 0, ram_end;
- uint32_t i, idx = 0, nr;
- struct e820entry e820[E820MAX];
-
- if (!src || !map_limitkb || !nr_entries)
- return ERROR_INVAL;
-
- nr = *nr_entries;
- if (!nr)
- return ERROR_INVAL;
-
- if (nr > E820MAX)
- return ERROR_NOMEM;
-
- /* Weed out anything under 1MB */
- for (i = 0; i < nr; i++) {
- if (src[i].addr > 0x100000)
- continue;
-
- src[i].type = 0;
- src[i].size = 0;
- src[i].addr = -1ULL;
- }
-
- /* Find the lowest and highest entry in E820, skipping over
- * undesired entries. */
- start = -1ULL;
- last = 0;
- for (i = 0; i < nr; i++) {
- if ((src[i].type == E820_RAM) ||
- (src[i].type == E820_UNUSABLE) ||
- (src[i].type == 0))
- continue;
-
- start = src[i].addr < start ? src[i].addr : start;
- last = src[i].addr + src[i].size > last ?
- src[i].addr + src[i].size > last : last;
- }
- if (start > 1024)
- start_kb = start >> 10;
-
- /* Add the memory RAM region for the guest */
- e820[idx].addr = 0;
- e820[idx].size = (uint64_t)map_limitkb << 10;
- e820[idx].type = E820_RAM;
-
- /* .. and trim if neccessary */
- if (start_kb && map_limitkb > start_kb) {
- delta_kb = map_limitkb - start_kb;
- if (delta_kb)
- e820[idx].size -= (uint64_t)(delta_kb << 10);
- }
- /* Note: We don't touch balloon_kb here. Will add it at the end. */
- ram_end = e820[idx].addr + e820[idx].size;
- idx ++;
-
- LOG(DEBUG, "Memory: %"PRIu64"kB End of RAM: " \
- "0x%"PRIx64" (PFN) Delta: %"PRIu64"kB, PCI start: %"PRIu64"kB " \
- "(0x%"PRIx64" PFN), Balloon %"PRIu64"kB\n", (uint64_t)map_limitkb,
- ram_end >> 12, delta_kb, start_kb ,start >> 12,
- (uint64_t)balloon_kb);
-
-
- /* This whole code below is to guard against if the Intel IGD is passed into
- * the guest. If we don't pass in IGD, this whole code can be ignored.
- *
- * The reason for this code is that Intel boxes fill their E820 with
- * E820_RAM amongst E820_RESERVED and we can't just ditch those E820_RAM.
- * That is b/c any "gaps" in the E820 is considered PCI I/O space by
- * Linux and it would be utilized by the Intel IGD as I/O space while
- * in reality it was an RAM region.
- *
- * What this means is that we have to walk the E820 and for any region
- * that is RAM and below 4GB and above ram_end, needs to change its type
- * to E820_UNUSED. We also need to move some of the E820_RAM regions if
- * the overlap with ram_end. */
- for (i = 0; i < nr; i++) {
- uint64_t end = src[i].addr + src[i].size;
-
- /* We don't care about E820_UNUSABLE, but we need to
- * change the type to zero b/c the loop after this
- * sticks E820_UNUSABLE on the guest's E820 but ignores
- * the ones with type zero. */
- if ((src[i].type == E820_UNUSABLE) ||
- /* Any region that is within the "RAM region" can
- * be safely ditched. */
- (end < ram_end)) {
- src[i].type = 0;
- continue;
- }
-
- /* Look only at RAM regions. */
- if (src[i].type != E820_RAM)
- continue;
-
- /* We only care about RAM regions below 4GB. */
- if (src[i].addr >= (1ULL<<32))
- continue;
-
- /* E820_RAM overlaps with our RAM region. Move it */
- if (src[i].addr < ram_end) {
- uint64_t delta;
-
- src[i].type = E820_UNUSABLE;
- delta = ram_end - src[i].addr;
- /* The end < ram_end should weed this out */
- if (src[i].size < delta)
- src[i].type = 0;
- else {
- src[i].size -= delta;
- src[i].addr = ram_end;
- }
- if (src[i].addr + src[i].size != end) {
- /* We messed up somewhere */
- src[i].type = 0;
- LOGE(ERROR, "Computed E820 wrongly. Continuing on.");
- }
- }
- /* Lastly, convert the RAM to UNSUABLE. Look in the Linux kernel
- at git commit 2f14ddc3a7146ea4cd5a3d1ecd993f85f2e4f948
- "xen/setup: Inhibit resource API from using System RAM E820
- gaps as PCI mem gaps" for full explanation. */
- if (end > ram_end)
- src[i].type = E820_UNUSABLE;
- }
-
- /* Check if there is a region between ram_end and start. */
- if (start > ram_end) {
- int add_unusable = 1;
- for (i = 0; i < nr && add_unusable; i++) {
- if (src[i].type != E820_UNUSABLE)
- continue;
- if (ram_end != src[i].addr)
- continue;
- if (start != src[i].addr + src[i].size) {
- /* there is one, adjust it */
- src[i].size = start - src[i].addr;
- }
- add_unusable = 0;
- }
- /* .. and if not present, add it in. This is to guard against
- the Linux guest assuming that the gap between the end of
- RAM region and the start of the E820_[ACPI,NVS,RESERVED]
- is PCI I/O space. Which it certainly is _not_. */
- if (add_unusable) {
- e820[idx].type = E820_UNUSABLE;
- e820[idx].addr = ram_end;
- e820[idx].size = start - ram_end;
- idx++;
- }
- }
- /* Almost done: copy them over, ignoring the undesireable ones */
- for (i = 0; i < nr; i++) {
- if ((src[i].type == E820_RAM) ||
- (src[i].type == 0))
- continue;
-
- e820[idx].type = src[i].type;
- e820[idx].addr = src[i].addr;
- e820[idx].size = src[i].size;
- idx++;
- }
- /* At this point we have the mapped RAM + E820 entries from src. */
- if (balloon_kb || delta_kb) {
- /* and if we truncated the RAM region, then add it to the end. */
- e820[idx].type = E820_RAM;
- e820[idx].addr = (uint64_t)(1ULL << 32) > last ?
- (uint64_t)(1ULL << 32) : last;
- /* also add the balloon memory to the end. */
- e820[idx].size = (uint64_t)(delta_kb << 10) +
- (uint64_t)(balloon_kb << 10);
- idx++;
-
- }
- nr = idx;
-
- for (i = 0; i < nr; i++) {
- LOG(DEBUG, ":\t[%"PRIx64" -> %"PRIx64"] %s", e820[i].addr >> 12,
- (e820[i].addr + e820[i].size) >> 12, e820_names(e820[i].type));
- }
-
- /* Done: copy the sanitized version. */
- *nr_entries = nr;
- memcpy(src, e820, nr * sizeof(struct e820entry));
- return 0;
-}
-
-static int e820_host_sanitize(libxl__gc *gc,
- libxl_domain_build_info *b_info,
- struct e820entry map[],
- uint32_t *nr)
-{
- int rc;
-
- rc = xc_get_machine_memory_map(CTX->xch, map, *nr);
- if (rc < 0)
- return ERROR_FAIL;
-
- *nr = rc;
-
- rc = e820_sanitize(gc, map, nr, b_info->target_memkb,
- (b_info->max_memkb - b_info->target_memkb) +
- b_info->u.pv.slack_memkb);
- return rc;
-}
-
-static int libxl__e820_alloc(libxl__gc *gc, uint32_t domid,
- libxl_domain_config *d_config)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- int rc;
- uint32_t nr;
- struct e820entry map[E820MAX];
- libxl_domain_build_info *b_info;
-
- if (d_config == NULL || d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV)
- return ERROR_INVAL;
-
- b_info = &d_config->b_info;
- if (!libxl_defbool_val(b_info->u.pv.e820_host))
- return ERROR_INVAL;
-
- nr = E820MAX;
- rc = e820_host_sanitize(gc, b_info, map, &nr);
- if (rc)
- return ERROR_FAIL;
-
- rc = xc_domain_set_memory_map(ctx->xch, domid, map, nr);
-
- if (rc < 0)
- return ERROR_FAIL;
-
- return 0;
-}
-
-static unsigned long timer_mode(const libxl_domain_build_info *info)
-{
- const libxl_timer_mode mode = info->timer_mode;
- assert(mode >= LIBXL_TIMER_MODE_DELAY_FOR_MISSED_TICKS &&
- mode <= LIBXL_TIMER_MODE_ONE_MISSED_TICK_PENDING);
- return ((unsigned long)mode);
-}
-
-static int hvm_set_viridian_features(libxl__gc *gc, uint32_t domid,
- const libxl_domain_build_info *info)
-{
- libxl_bitmap enlightenments;
- libxl_viridian_enlightenment v;
- uint64_t mask = 0;
-
- libxl_bitmap_init(&enlightenments);
- libxl_bitmap_alloc(CTX, &enlightenments,
- LIBXL_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE_WIDTH);
-
- if (libxl_defbool_val(info->u.hvm.viridian)) {
- /* Enable defaults */
- libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE);
- libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_FREQ);
- libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT);
- libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST);
- libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_CRASH_CTL);
- }
-
- libxl_for_each_set_bit(v, info->u.hvm.viridian_enable) {
- if (libxl_bitmap_test(&info->u.hvm.viridian_disable, v)) {
- LOG(ERROR, "%s group both enabled and disabled",
- libxl_viridian_enlightenment_to_string(v));
- goto err;
- }
- if (libxl_viridian_enlightenment_to_string(v)) /* check validity */
- libxl_bitmap_set(&enlightenments, v);
- }
-
- libxl_for_each_set_bit(v, info->u.hvm.viridian_disable)
- if (libxl_viridian_enlightenment_to_string(v)) /* check validity */
- libxl_bitmap_reset(&enlightenments, v);
-
- /* The base set is a pre-requisite for all others */
- if (!libxl_bitmap_is_empty(&enlightenments) &&
- !libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE)) {
- LOG(ERROR, "base group not enabled");
- goto err;
- }
-
- libxl_for_each_set_bit(v, enlightenments)
- LOG(DETAIL, "%s group enabled", libxl_viridian_enlightenment_to_string(v));
-
- if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE)) {
- mask |= HVMPV_base_freq;
-
- if (!libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_FREQ))
- mask |= HVMPV_no_freq;
- }
-
- if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT))
- mask |= HVMPV_time_ref_count;
-
- if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_REFERENCE_TSC))
- mask |= HVMPV_reference_tsc;
-
- if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_HCALL_REMOTE_TLB_FLUSH))
- mask |= HVMPV_hcall_remote_tlb_flush;
-
- if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST))
- mask |= HVMPV_apic_assist;
-
- if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_CRASH_CTL))
- mask |= HVMPV_crash_ctl;
-
- if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_SYNIC))
- mask |= HVMPV_synic;
-
- if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_STIMER))
- mask |= HVMPV_time_ref_count | HVMPV_synic | HVMPV_stimer;
-
- if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_HCALL_IPI))
- mask |= HVMPV_hcall_ipi;
-
- if (mask != 0 &&
- xc_hvm_param_set(CTX->xch,
- domid,
- HVM_PARAM_VIRIDIAN,
- mask) != 0) {
- LOGE(ERROR, "Couldn't set viridian feature mask (0x%"PRIx64")", mask);
- goto err;
- }
-
- libxl_bitmap_dispose(&enlightenments);
- return 0;
-
-err:
- libxl_bitmap_dispose(&enlightenments);
- return ERROR_FAIL;
-}
-
-static int hvm_set_conf_params(libxl__gc *gc, uint32_t domid,
- const libxl_domain_build_info *info)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- xc_interface *xch = ctx->xch;
- int ret = ERROR_FAIL;
- unsigned int altp2m = info->altp2m;
-
- switch(info->type) {
- case LIBXL_DOMAIN_TYPE_HVM:
- /* The config parameter "altp2m" replaces the parameter "altp2mhvm". For
- * legacy reasons, both parameters are accepted on x86 HVM guests.
- *
- * If the legacy field info->u.hvm.altp2m is set, activate altp2m.
- * Otherwise set altp2m based on the field info->altp2m. */
- if (info->altp2m == LIBXL_ALTP2M_MODE_DISABLED &&
- libxl_defbool_val(info->u.hvm.altp2m))
- altp2m = libxl_defbool_val(info->u.hvm.altp2m);
-
- if (xc_hvm_param_set(xch, domid, HVM_PARAM_HPET_ENABLED,
- libxl_defbool_val(info->u.hvm.hpet))) {
- LOG(ERROR, "Couldn't set HVM_PARAM_HPET_ENABLED");
- goto out;
- }
- if (xc_hvm_param_set(xch, domid, HVM_PARAM_VPT_ALIGN,
- libxl_defbool_val(info->u.hvm.vpt_align))) {
- LOG(ERROR, "Couldn't set HVM_PARAM_VPT_ALIGN");
- goto out;
- }
- if (info->u.hvm.mca_caps &&
- xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_MCA_CAP,
- info->u.hvm.mca_caps)) {
- LOG(ERROR, "Couldn't set HVM_PARAM_MCA_CAP");
- goto out;
- }
-
- /* Fallthrough */
- case LIBXL_DOMAIN_TYPE_PVH:
- if (xc_hvm_param_set(xch, domid, HVM_PARAM_TIMER_MODE,
- timer_mode(info))) {
- LOG(ERROR, "Couldn't set HVM_PARAM_TIMER_MODE");
- goto out;
- }
- if (xc_hvm_param_set(xch, domid, HVM_PARAM_NESTEDHVM,
- libxl_defbool_val(info->nested_hvm))) {
- LOG(ERROR, "Couldn't set HVM_PARAM_NESTEDHVM");
- goto out;
- }
- if (xc_hvm_param_set(xch, domid, HVM_PARAM_ALTP2M, altp2m)) {
- LOG(ERROR, "Couldn't set HVM_PARAM_ALTP2M");
- goto out;
- }
- break;
-
- default:
- abort();
- }
-
- ret = 0;
-
- out:
- return ret;
-}
-
-int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
- uint32_t domid)
-{
- const libxl_domain_build_info *info = &d_config->b_info;
- int ret = 0;
- int tsc_mode;
- uint32_t rtc_timeoffset;
- libxl_ctx *ctx = libxl__gc_owner(gc);
-
- if (info->type != LIBXL_DOMAIN_TYPE_PV &&
- (ret = hvm_set_conf_params(gc, domid, info)) != 0)
- goto out;
-
- if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
- (ret = hvm_set_viridian_features(gc, domid, info)) != 0)
- goto out;
-
- if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PV)
- xc_domain_set_memmap_limit(ctx->xch, domid,
- (d_config->b_info.max_memkb +
- d_config->b_info.u.pv.slack_memkb));
-
- switch (d_config->b_info.tsc_mode) {
- case LIBXL_TSC_MODE_DEFAULT:
- tsc_mode = 0;
- break;
- case LIBXL_TSC_MODE_ALWAYS_EMULATE:
- tsc_mode = 1;
- break;
- case LIBXL_TSC_MODE_NATIVE:
- tsc_mode = 2;
- break;
- case LIBXL_TSC_MODE_NATIVE_PARAVIRT:
- LOGD(ERROR, domid, "TSC Mode native_paravirt (a.k.a PVRDTSCP) has been removed");
- ret = ERROR_FEATURE_REMOVED;
- goto out;
- default:
- abort();
- }
-
- if (xc_domain_set_tsc_info(ctx->xch, domid, tsc_mode, 0, 0, 0)) {
- LOGE(ERROR, "xc_domain_set_tsc_info() failed");
- ret = ERROR_FAIL;
- goto out;
- }
-
- rtc_timeoffset = d_config->b_info.rtc_timeoffset;
- if (libxl_defbool_val(d_config->b_info.localtime)) {
- time_t t;
- struct tm *tm, result;
-
- t = time(NULL);
- tm = localtime_r(&t, &result);
-
- if (!tm) {
- LOGED(ERROR, domid, "Failed to call localtime_r");
- ret = ERROR_FAIL;
- goto out;
- }
-
- rtc_timeoffset += tm->tm_gmtoff;
- }
-
- if (rtc_timeoffset)
- xc_domain_set_time_offset(ctx->xch, domid, rtc_timeoffset);
-
- if (d_config->b_info.type != LIBXL_DOMAIN_TYPE_PV) {
- unsigned long shadow = DIV_ROUNDUP(d_config->b_info.shadow_memkb,
- 1024);
- xc_shadow_control(ctx->xch, domid, XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION,
- NULL, 0, &shadow, 0, NULL);
- }
-
- if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_PV &&
- libxl_defbool_val(d_config->b_info.u.pv.e820_host)) {
- ret = libxl__e820_alloc(gc, domid, d_config);
- if (ret) {
- LOGED(ERROR, domid, "Failed while collecting E820 with: %d (errno:%d)\n",
- ret, errno);
- }
- }
-
-out:
- return ret;
-}
-
-int libxl__arch_extra_memory(libxl__gc *gc,
- const libxl_domain_build_info *info,
- uint64_t *out)
-{
- *out = LIBXL_MAXMEM_CONSTANT;
-
- return 0;
-}
-
-int libxl__arch_domain_init_hw_description(libxl__gc *gc,
- libxl_domain_build_info *info,
- libxl__domain_build_state *state,
- struct xc_dom_image *dom)
-{
- return 0;
-}
-
-int libxl__arch_build_dom_finish(libxl__gc *gc,
- libxl_domain_build_info *info,
- struct xc_dom_image *dom,
- libxl__domain_build_state *state)
-{
- return 0;
-}
-
-/* Return 0 on success, ERROR_* on failure. */
-int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_build_info *b_info,
- libxl__domain_build_state *state)
-{
- int nid, nr_vmemrange, rc;
- uint32_t nr_e820, e820_count;
- struct e820entry map[E820MAX];
- xen_vmemrange_t *vmemranges;
- unsigned int array_size;
-
- /* If e820_host is not set, call the generic function */
- if (!(b_info->type == LIBXL_DOMAIN_TYPE_PV &&
- libxl_defbool_val(b_info->u.pv.e820_host)))
- return libxl__vnuma_build_vmemrange_pv_generic(gc, domid, b_info,
- state);
-
- assert(state->vmemranges == NULL);
-
- nr_e820 = E820MAX;
- rc = e820_host_sanitize(gc, b_info, map, &nr_e820);
- if (rc) goto out;
-
- e820_count = 0;
- nr_vmemrange = 0;
- vmemranges = NULL;
- array_size = 0;
- for (nid = 0; nid < b_info->num_vnuma_nodes; nid++) {
- libxl_vnode_info *p = &b_info->vnuma_nodes[nid];
- uint64_t remaining_bytes = (p->memkb << 10), bytes;
-
- while (remaining_bytes > 0) {
- if (e820_count >= nr_e820) {
- rc = ERROR_NOMEM;
- goto out;
- }
-
- /* Skip non RAM region */
- if (map[e820_count].type != E820_RAM) {
- e820_count++;
- continue;
- }
-
- if (nr_vmemrange >= array_size) {
- array_size += 32;
- GCREALLOC_ARRAY(vmemranges, array_size);
- }
-
- bytes = map[e820_count].size >= remaining_bytes ?
- remaining_bytes : map[e820_count].size;
-
- vmemranges[nr_vmemrange].start = map[e820_count].addr;
- vmemranges[nr_vmemrange].end = map[e820_count].addr + bytes;
-
- if (map[e820_count].size >= remaining_bytes) {
- map[e820_count].addr += bytes;
- map[e820_count].size -= bytes;
- } else {
- e820_count++;
- }
-
- remaining_bytes -= bytes;
-
- vmemranges[nr_vmemrange].flags = 0;
- vmemranges[nr_vmemrange].nid = nid;
- nr_vmemrange++;
- }
- }
-
- state->vmemranges = vmemranges;
- state->num_vmemranges = nr_vmemrange;
-
- rc = 0;
-out:
- return rc;
-}
-
-int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq)
-{
- int ret;
-
- ret = xc_physdev_map_pirq(CTX->xch, domid, irq, &irq);
- if (ret)
- return ret;
-
- ret = xc_domain_irq_permission(CTX->xch, domid, irq, 1);
-
- return ret;
-}
-
-/*
- * Here we're just trying to set these kinds of e820 mappings:
- *
- * #1. Low memory region
- *
- * Low RAM starts at least from 1M to make sure all standard regions
- * of the PC memory map, like BIOS, VGA memory-mapped I/O and vgabios,
- * have enough space.
- * Note: Those stuffs below 1M are still constructed with multiple
- * e820 entries by hvmloader. At this point we don't change anything.
- *
- * #2. RDM region if it exists
- *
- * #3. High memory region if it exists
- *
- * Note: these regions are not overlapping since we already check
- * to adjust them. Please refer to libxl__domain_device_construct_rdm().
- */
-#define GUEST_LOW_MEM_START_DEFAULT 0x100000
-static int domain_construct_memmap(libxl__gc *gc,
- libxl_domain_config *d_config,
- uint32_t domid,
- struct xc_dom_image *dom)
-{
- int rc = 0;
- unsigned int nr = 0, i;
- /* We always own at least one lowmem entry. */
- unsigned int e820_entries = 1;
- struct e820entry *e820 = NULL;
- uint64_t highmem_size =
- dom->highmem_end ? dom->highmem_end - (1ull << 32) : 0;
- uint32_t lowmem_start = dom->device_model ? GUEST_LOW_MEM_START_DEFAULT : 0;
- unsigned page_size = XC_DOM_PAGE_SIZE(dom);
-
- /* Add all rdm entries. */
- for (i = 0; i < d_config->num_rdms; i++)
- if (d_config->rdms[i].policy != LIBXL_RDM_RESERVE_POLICY_INVALID)
- e820_entries++;
-
- /* Add the HVM special pages to PVH memmap as RESERVED. */
- if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PVH)
- e820_entries++;
-
- /* If we should have a highmem range. */
- if (highmem_size)
- e820_entries++;
-
- for (i = 0; i < MAX_ACPI_MODULES; i++)
- if (dom->acpi_modules[i].length)
- e820_entries++;
-
- if (e820_entries >= E820MAX) {
- LOGD(ERROR, domid, "Ooops! Too many entries in the memory map!");
- rc = ERROR_INVAL;
- goto out;
- }
-
- e820 = libxl__malloc(gc, sizeof(struct e820entry) * e820_entries);
-
- /* Low memory */
- e820[nr].addr = lowmem_start;
- e820[nr].size = dom->lowmem_end - lowmem_start;
- e820[nr].type = E820_RAM;
- nr++;
-
- /* RDM mapping */
- for (i = 0; i < d_config->num_rdms; i++) {
- if (d_config->rdms[i].policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
- continue;
-
- e820[nr].addr = d_config->rdms[i].start;
- e820[nr].size = d_config->rdms[i].size;
- e820[nr].type = E820_RESERVED;
- nr++;
- }
-
- /* HVM special pages */
- if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PVH) {
- e820[nr].addr = (X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES)
- << XC_PAGE_SHIFT;
- e820[nr].size = X86_HVM_NR_SPECIAL_PAGES << XC_PAGE_SHIFT;
- e820[nr].type = E820_RESERVED;
- nr++;
- }
-
- for (i = 0; i < MAX_ACPI_MODULES; i++) {
- if (dom->acpi_modules[i].length) {
- e820[nr].addr = dom->acpi_modules[i].guest_addr_out & ~(page_size - 1);
- e820[nr].size = dom->acpi_modules[i].length +
- (dom->acpi_modules[i].guest_addr_out & (page_size - 1));
- e820[nr].type = E820_ACPI;
- nr++;
- }
- }
-
- /* High memory */
- if (highmem_size) {
- e820[nr].addr = ((uint64_t)1 << 32);
- e820[nr].size = highmem_size;
- e820[nr].type = E820_RAM;
- }
-
- if (xc_domain_set_memory_map(CTX->xch, domid, e820, e820_entries) != 0) {
- rc = ERROR_FAIL;
- goto out;
- }
-
- dom->e820 = e820;
- dom->e820_entries = e820_entries;
-
-out:
- return rc;
-}
-
-int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_config *d_config,
- struct xc_dom_image *dom)
-{
- libxl_domain_build_info *const info = &d_config->b_info;
- int rc;
-
- if (info->type == LIBXL_DOMAIN_TYPE_PV)
- return 0;
-
- if (info->type == LIBXL_DOMAIN_TYPE_PVH) {
- rc = libxl__dom_load_acpi(gc, info, dom);
- if (rc != 0) {
- LOGE(ERROR, "libxl_dom_load_acpi failed");
- return rc;
- }
- }
-
- rc = domain_construct_memmap(gc, d_config, domid, dom);
- if (rc != 0)
- LOGE(ERROR, "setting domain memory map failed");
-
- return rc;
-}
-
-void libxl__arch_domain_create_info_setdefault(libxl__gc *gc,
- libxl_domain_create_info *c_info)
-{
-}
-
-void libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
- libxl_domain_build_info *b_info)
-{
- libxl_defbool_setdefault(&b_info->acpi, true);
-}
-
-int libxl__arch_passthrough_mode_setdefault(libxl__gc *gc,
- uint32_t domid,
- libxl_domain_config *d_config,
- const libxl_physinfo *physinfo)
-{
- int rc;
- libxl_domain_create_info *const c_info = &d_config->c_info;
-
- if (c_info->passthrough != LIBXL_PASSTHROUGH_DISABLED &&
- c_info->type == LIBXL_DOMAIN_TYPE_PVH) {
- LOGD(ERROR, domid,
- "passthrough not yet supported for x86 PVH guests\n");
- rc = ERROR_INVAL;
- goto out;
- }
-
- const char *whynot_pt_share =
- c_info->type == LIBXL_DOMAIN_TYPE_PV ? "not valid for PV domain" :
- !physinfo->cap_iommu_hap_pt_share ? "not supported on this platform" :
- !libxl_defbool_val(d_config->c_info.hap) ?"only valid for HAP guests":
- NULL;
-
- if (c_info->passthrough == LIBXL_PASSTHROUGH_ENABLED) {
- c_info->passthrough = whynot_pt_share
- ? LIBXL_PASSTHROUGH_SYNC_PT : LIBXL_PASSTHROUGH_SHARE_PT;
- }
-
- if (c_info->passthrough == LIBXL_PASSTHROUGH_SHARE_PT && whynot_pt_share) {
- LOGD(ERROR, domid,
- "passthrough=\"share_pt\" %s\n",
- whynot_pt_share);
- rc = ERROR_INVAL;
- goto out;
- }
-
- rc = 0;
- out:
- return rc;
-}
-
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
- */
-
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-#include <xen/hvm/hvm_info_table.h>
-#include <xen/hvm/e820.h>
-#include "libacpi/libacpi.h"
-
-#include <xenctrl_dom.h>
-
- /* Number of pages holding ACPI tables */
-#define NUM_ACPI_PAGES 16
-
-struct libxl_acpi_ctxt {
- struct acpi_ctxt c;
-
- unsigned int page_size;
- unsigned int page_shift;
-
- /* Memory allocator */
- unsigned long alloc_base_paddr;
- unsigned long alloc_base_vaddr;
- unsigned long alloc_currp;
- unsigned long alloc_end;
-};
-
-extern const unsigned char dsdt_pvh[];
-extern const unsigned int dsdt_pvh_len;
-
-/* Assumes contiguous physical space */
-static unsigned long virt_to_phys(struct acpi_ctxt *ctxt, void *v)
-{
- struct libxl_acpi_ctxt *libxl_ctxt =
- CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c);
-
- return (((unsigned long)v - libxl_ctxt->alloc_base_vaddr) +
- libxl_ctxt->alloc_base_paddr);
-}
-
-static void *mem_alloc(struct acpi_ctxt *ctxt,
- uint32_t size, uint32_t align)
-{
- struct libxl_acpi_ctxt *libxl_ctxt =
- CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c);
- unsigned long s, e;
-
- /* Align to at least 16 bytes. */
- if (align < 16)
- align = 16;
-
- s = (libxl_ctxt->alloc_currp + align) & ~((unsigned long)align - 1);
- e = s + size - 1;
-
- /* TODO: Reallocate memory */
- if ((e < s) || (e >= libxl_ctxt->alloc_end))
- return NULL;
-
- while (libxl_ctxt->alloc_currp >> libxl_ctxt->page_shift !=
- e >> libxl_ctxt->page_shift)
- libxl_ctxt->alloc_currp += libxl_ctxt->page_size;
-
- libxl_ctxt->alloc_currp = e;
-
- return (void *)s;
-}
-
-static void acpi_mem_free(struct acpi_ctxt *ctxt,
- void *v, uint32_t size)
-{
-}
-
-static uint32_t acpi_lapic_id(unsigned cpu)
-{
- return cpu * 2;
-}
-
-static int init_acpi_config(libxl__gc *gc,
- struct xc_dom_image *dom,
- const libxl_domain_build_info *b_info,
- struct acpi_config *config)
-{
- xc_interface *xch = dom->xch;
- uint32_t domid = dom->guest_domid;
- xc_dominfo_t info;
- struct hvm_info_table *hvminfo;
- int i, r, rc;
-
- config->dsdt_anycpu = config->dsdt_15cpu = dsdt_pvh;
- config->dsdt_anycpu_len = config->dsdt_15cpu_len = dsdt_pvh_len;
-
- r = xc_domain_getinfo(xch, domid, 1, &info);
- if (r < 0) {
- LOG(ERROR, "getdomaininfo failed (rc=%d)", r);
- rc = ERROR_FAIL;
- goto out;
- }
-
- hvminfo = libxl__zalloc(gc, sizeof(*hvminfo));
-
- hvminfo->apic_mode = libxl_defbool_val(b_info->apic);
-
- if (dom->nr_vnodes) {
- unsigned int *vcpu_to_vnode, *vdistance;
- struct xen_vmemrange *vmemrange;
- struct acpi_numa *numa = &config->numa;
-
- r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes,
- &numa->nr_vmemranges,
- &hvminfo->nr_vcpus, NULL, NULL, NULL);
- if (r) {
- LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r);
- rc = ERROR_FAIL;
- goto out;
- }
-
- vmemrange = libxl__zalloc(gc, dom->nr_vmemranges * sizeof(*vmemrange));
- vdistance = libxl__zalloc(gc, dom->nr_vnodes * sizeof(*vdistance));
- vcpu_to_vnode = libxl__zalloc(gc, hvminfo->nr_vcpus *
- sizeof(*vcpu_to_vnode));
- r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes,
- &numa->nr_vmemranges, &hvminfo->nr_vcpus,
- vmemrange, vdistance, vcpu_to_vnode);
- if (r) {
- LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r);
- rc = ERROR_FAIL;
- goto out;
- }
- numa->vmemrange = vmemrange;
- numa->vdistance = vdistance;
- numa->vcpu_to_vnode = vcpu_to_vnode;
- } else {
- hvminfo->nr_vcpus = info.max_vcpu_id + 1;
- }
-
- for (i = 0; i < hvminfo->nr_vcpus; i++)
- hvminfo->vcpu_online[i / 8] |= 1 << (i & 7);
-
- config->hvminfo = hvminfo;
-
- config->lapic_base_address = LAPIC_BASE_ADDRESS;
- config->lapic_id = acpi_lapic_id;
- config->acpi_revision = 5;
-
- rc = 0;
-out:
- return rc;
-}
-
-int libxl__dom_load_acpi(libxl__gc *gc,
- const libxl_domain_build_info *b_info,
- struct xc_dom_image *dom)
-{
- struct acpi_config config = {0};
- struct libxl_acpi_ctxt libxl_ctxt;
- int rc = 0, acpi_pages_num;
- void *acpi_pages;
- unsigned long page_mask;
-
- if (b_info->type != LIBXL_DOMAIN_TYPE_PVH)
- goto out;
-
- libxl_ctxt.page_size = XC_DOM_PAGE_SIZE(dom);
- libxl_ctxt.page_shift = XC_DOM_PAGE_SHIFT(dom);
- page_mask = (1UL << libxl_ctxt.page_shift) - 1;
-
- libxl_ctxt.c.mem_ops.alloc = mem_alloc;
- libxl_ctxt.c.mem_ops.v2p = virt_to_phys;
- libxl_ctxt.c.mem_ops.free = acpi_mem_free;
-
- rc = init_acpi_config(gc, dom, b_info, &config);
- if (rc) {
- LOG(ERROR, "init_acpi_config failed (rc=%d)", rc);
- goto out;
- }
-
- config.rsdp = (unsigned long)libxl__malloc(gc, libxl_ctxt.page_size);
- config.infop = (unsigned long)libxl__malloc(gc, libxl_ctxt.page_size);
- /* Pages to hold ACPI tables */
- acpi_pages = libxl__malloc(gc, (NUM_ACPI_PAGES + 1) *
- libxl_ctxt.page_size);
-
- /*
- * Set up allocator memory.
- * Start next to acpi_info page to avoid fracturing e820.
- */
- libxl_ctxt.alloc_base_paddr = ACPI_INFO_PHYSICAL_ADDRESS +
- libxl_ctxt.page_size;
- libxl_ctxt.alloc_base_vaddr = libxl_ctxt.alloc_currp =
- (unsigned long)acpi_pages;
- libxl_ctxt.alloc_end = (unsigned long)acpi_pages +
- (NUM_ACPI_PAGES * libxl_ctxt.page_size);
-
- /* Build the tables. */
- rc = acpi_build_tables(&libxl_ctxt.c, &config);
- if (rc) {
- LOG(ERROR, "acpi_build_tables failed with %d", rc);
- goto out;
- }
-
- /* Calculate how many pages are needed for the tables. */
- acpi_pages_num =
- ((libxl_ctxt.alloc_currp - (unsigned long)acpi_pages)
- >> libxl_ctxt.page_shift) +
- ((libxl_ctxt.alloc_currp & page_mask) ? 1 : 0);
-
- dom->acpi_modules[0].data = (void *)config.rsdp;
- dom->acpi_modules[0].length = 64;
- /*
- * Some Linux versions cannot properly process hvm_start_info.rsdp_paddr
- * and so we need to put RSDP in location that can be discovered by ACPI's
- * standard search method, in R-O BIOS memory (we chose last 64 bytes)
- */
- if (strcmp(dom->parms.guest_os, "linux") ||
- elf_xen_feature_get(XENFEAT_linux_rsdp_unrestricted,
- dom->parms.f_supported))
- dom->acpi_modules[0].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS +
- (1 + acpi_pages_num) * libxl_ctxt.page_size;
- else
- dom->acpi_modules[0].guest_addr_out = 0x100000 - 64;
-
- dom->acpi_modules[1].data = (void *)config.infop;
- dom->acpi_modules[1].length = 4096;
- dom->acpi_modules[1].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS;
-
- dom->acpi_modules[2].data = acpi_pages;
- dom->acpi_modules[2].length = acpi_pages_num << libxl_ctxt.page_shift;
- dom->acpi_modules[2].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS +
- libxl_ctxt.page_size;
-
-out:
- return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
- */
-
-#ifndef LIBXL_X86_ACPI_H
-#define LIBXL_X86_ACPI_H
-
-#include "libxl_internal.h"
-
-#define ASSERT(x) assert(x)
-
-static inline int test_bit(unsigned int b, const void *p)
-{
- return !!(((const uint8_t *)p)[b>>3] & (1u<<(b&7)));
-}
-
-#endif /* LIBXL_X_86_ACPI_H */
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-char **libxl__xs_kvs_of_flexarray(libxl__gc *gc, flexarray_t *array)
-{
- char **kvs;
- int i, length;
-
- if (!array)
- return NULL;
-
- length = array->count;
- if (!length)
- return NULL;
-
- kvs = libxl__calloc(gc, length + 2, sizeof(char *));
- if (kvs) {
- for (i = 0; i < length; i += 2) {
- void *ptr;
-
- flexarray_get(array, i, &ptr);
- kvs[i] = (char *) ptr;
- flexarray_get(array, i + 1, &ptr);
- kvs[i + 1] = (char *) ptr;
- }
- kvs[i] = NULL;
- kvs[i + 1] = NULL;
- }
- return kvs;
-}
-
-int libxl__xs_writev_perms(libxl__gc *gc, xs_transaction_t t,
- const char *dir, char *kvs[],
- struct xs_permissions *perms,
- unsigned int num_perms)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char *path;
- int i;
-
- if (!kvs)
- return 0;
-
- for (i = 0; kvs[i] != NULL; i += 2) {
- path = GCSPRINTF("%s/%s", dir, kvs[i]);
- if (path && kvs[i + 1]) {
- int length = strlen(kvs[i + 1]);
- xs_write(ctx->xsh, t, path, kvs[i + 1], length);
- if (perms)
- xs_set_permissions(ctx->xsh, t, path, perms, num_perms);
- }
- }
- return 0;
-}
-
-int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t,
- const char *dir, char *kvs[])
-{
- return libxl__xs_writev_perms(gc, t, dir, kvs, NULL, 0);
-}
-
-int libxl__xs_writev_atonce(libxl__gc *gc,
- const char *dir, char *kvs[])
-{
- int rc;
- xs_transaction_t t = XBT_NULL;
-
- for (;;) {
- rc = libxl__xs_transaction_start(gc, &t);
- if (rc) goto out;
-
- rc = libxl__xs_writev(gc, t, dir, kvs);
- if (rc) goto out;
-
- rc = libxl__xs_transaction_commit(gc, &t);
- if (!rc) break;
- if (rc<0) goto out;
- }
-
-out:
- libxl__xs_transaction_abort(gc, &t);
-
- return rc;
-
-}
-
-int libxl__xs_vprintf(libxl__gc *gc, xs_transaction_t t,
- const char *path, const char *fmt, va_list ap)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char *s;
- bool ok;
-
- s = libxl__vsprintf(gc, fmt, ap);
-
- ok = xs_write(ctx->xsh, t, path, s, strlen(s));
- if (!ok) {
- LOGE(ERROR, "xenstore write failed: `%s' = `%s'", path, s);
- return ERROR_FAIL;
- }
-
- return 0;
-}
-
-int libxl__xs_printf(libxl__gc *gc, xs_transaction_t t,
- const char *path, const char *fmt, ...)
-{
- va_list ap;
- int rc;
-
- va_start(ap, fmt);
- rc = libxl__xs_vprintf(gc, t, path, fmt, ap);
- va_end(ap);
-
- return rc;
-}
-
-char * libxl__xs_read(libxl__gc *gc, xs_transaction_t t, const char *path)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char *ptr;
-
- ptr = xs_read(ctx->xsh, t, path, NULL);
- libxl__ptr_add(gc, ptr);
- return ptr;
-}
-
-char *libxl__xs_get_dompath(libxl__gc *gc, uint32_t domid)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char *s = xs_get_domain_path(ctx->xsh, domid);
- if (!s) {
- LOGED(ERROR, domid, "Failed to get dompath");
- return NULL;
- }
- libxl__ptr_add(gc, s);
- return s;
-}
-
-char **libxl__xs_directory(libxl__gc *gc, xs_transaction_t t,
- const char *path, unsigned int *nb)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- char **ret = NULL;
- ret = xs_directory(ctx->xsh, t, path, nb);
- libxl__ptr_add(gc, ret);
- return ret;
-}
-
-int libxl__xs_mknod(libxl__gc *gc, xs_transaction_t t,
- const char *path, struct xs_permissions *perms,
- unsigned int num_perms)
-{
- libxl_ctx *ctx = libxl__gc_owner(gc);
- bool ok;
-
- ok = xs_write(ctx->xsh, t, path, "", 0);
- if (!ok) {
- LOGE(ERROR, "xenstore write failed: `%s' = ''", path);
- return ERROR_FAIL;
- }
-
- ok = xs_set_permissions(ctx->xsh, t, path, perms, num_perms);
- if (!ok) {
- LOGE(ERROR, "xenstore set permissions failed on `%s'", path);
- return ERROR_FAIL;
- }
-
- return 0;
-}
-
-char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t domid)
-{
- char *s = GCSPRINTF("/libxl/%i", domid);
- if (!s)
- LOGD(ERROR, domid, "cannot allocate create paths");
- return s;
-}
-
-int libxl__xs_read_mandatory(libxl__gc *gc, xs_transaction_t t,
- const char *path, const char **result_out)
-{
- char *result = libxl__xs_read(gc, t, path);
- if (!result) {
- LOGE(ERROR, "xenstore read failed: `%s'", path);
- return ERROR_FAIL;
- }
- *result_out = result;
- return 0;
-}
-
-int libxl__xs_read_checked(libxl__gc *gc, xs_transaction_t t,
- const char *path, const char **result_out)
-{
- char *result = libxl__xs_read(gc, t, path);
- if (!result && errno != ENOENT) {
- LOGE(ERROR, "xenstore read failed: `%s'", path);
- return ERROR_FAIL;
- }
- *result_out = result;
- return 0;
-}
-
-int libxl__xs_write_checked(libxl__gc *gc, xs_transaction_t t,
- const char *path, const char *string)
-{
- size_t length = strlen(string);
- if (!xs_write(CTX->xsh, t, path, string, length)) {
- LOGE(ERROR, "xenstore write failed: `%s' = `%s'", path, string);
- return ERROR_FAIL;
- }
- return 0;
-}
-
-int libxl__xs_rm_checked(libxl__gc *gc, xs_transaction_t t, const char *path)
-{
- if (!xs_rm(CTX->xsh, t, path)) {
- if (errno == ENOENT)
- return 0;
-
- LOGE(ERROR, "xenstore rm failed: `%s'", path);
- return ERROR_FAIL;
- }
- return 0;
-}
-
-int libxl__xs_transaction_start(libxl__gc *gc, xs_transaction_t *t)
-{
- assert(!*t);
- *t = xs_transaction_start(CTX->xsh);
- if (!*t) {
- LOGE(ERROR, "could not create xenstore transaction");
- return ERROR_FAIL;
- }
- return 0;
-}
-
-int libxl__xs_transaction_commit(libxl__gc *gc, xs_transaction_t *t)
-{
- assert(*t);
-
- if (!xs_transaction_end(CTX->xsh, *t, 0)) {
- *t = 0;
- if (errno == EAGAIN)
- return +1;
-
- LOGE(ERROR, "could not commit xenstore transaction");
- return ERROR_FAIL;
- }
-
- *t = 0;
- return 0;
-}
-
-void libxl__xs_transaction_abort(libxl__gc *gc, xs_transaction_t *t)
-{
- if (!*t)
- return;
-
- if (!xs_transaction_end(CTX->xsh, *t, 1))
- LOGE(ERROR, "could not abort xenstore transaction");
-
- *t = 0;
-}
-
-int libxl__xs_path_cleanup(libxl__gc *gc, xs_transaction_t t,
- const char *user_path)
-{
- unsigned int nb = 0;
- char *path, *last, *val;
- int rc;
-
- /* A path and transaction must be provided by the caller */
- assert(user_path && t);
-
- path = libxl__strdup(gc, user_path);
- if (!xs_rm(CTX->xsh, t, path)) {
- if (errno != ENOENT)
- LOGE(DEBUG, "unable to remove path %s", path);
- rc = ERROR_FAIL;
- goto out;
- }
-
- for (last = strrchr(path, '/'); last != NULL; last = strrchr(path, '/')) {
- *last = '\0';
-
- if (!strlen(path)) break;
-
- val = libxl__xs_read(gc, t, path);
- if (!val || strlen(val) != 0) break;
-
- if (!libxl__xs_directory(gc, t, path, &nb) || nb != 0) break;
-
- if (!xs_rm(CTX->xsh, t, path)) {
- if (errno != ENOENT)
- LOGE(DEBUG, "unable to remove path %s", path);
- rc = ERROR_FAIL;
- goto out;
- }
- }
- rc = 0;
-
-out:
- return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2009 Citrix Ltd.
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- */
-
-#include "libxl_osdeps.h"
-
-#include <unistd.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <sys/time.h>
-#include <stdlib.h>
-
-#ifdef NEED_OWN_ASPRINTF
-
-int vasprintf(char **buffer, const char *fmt, va_list ap)
-{
- int size = 0;
- int nchars;
-
- *buffer = 0;
-
- nchars = vsnprintf(*buffer, 0, fmt, ap);
-
- if (nchars >= size)
- {
- char *tmpbuff;
- /* Reallocate buffer now that we know how much space is needed. */
- size = nchars+1;
- tmpbuff = (char*)realloc(*buffer, size);
-
-
- if (tmpbuff == NULL) { /* we need to free it*/
- free(*buffer);
- return -1;
- }
-
- *buffer=tmpbuff;
- /* Try again. */
- nchars = vsnprintf(*buffer, size, fmt, ap);
- }
-
- if (nchars < 0) return nchars;
- return size;
-}
-
-int asprintf(char **buffer, char *fmt, ...)
-{
- int status;
- va_list ap;
-
- va_start (ap, fmt);
- status = vasprintf (buffer, fmt, ap);
- va_end (ap);
- return status;
-}
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-#include "test_common.h"
-
-libxl_ctx *ctx;
-
-void test_common_setup(int level)
-{
- xentoollog_logger_stdiostream *logger_s
- = xtl_createlogger_stdiostream(stderr, level, 0);
- assert(logger_s);
-
- xentoollog_logger *logger = (xentoollog_logger*)logger_s;
-
- int rc = libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, logger);
- assert(!rc);
-}
-
-struct timeval now;
-
-void test_common_get_now(void)
-{
- int r = gettimeofday(&now, 0); assert(!r);
-}
-
-int poll_nfds, poll_nfds_allocd;
-struct pollfd *poll_fds;
-int poll_timeout;
-
-void test_common_beforepoll(void)
-{
- for (;;) {
- test_common_get_now();
-
- poll_timeout = -1;
- poll_nfds = poll_nfds_allocd;
- int rc = libxl_osevent_beforepoll(ctx, &poll_nfds, poll_fds,
- &poll_timeout, now);
- if (!rc) return;
- assert(rc == ERROR_BUFFERFULL);
-
- assert(poll_nfds > poll_nfds_allocd);
- poll_fds = realloc(poll_fds, poll_nfds * sizeof(poll_fds[0]));
- assert(poll_fds);
- poll_nfds_allocd = poll_nfds;
- }
-}
-
-void test_common_dopoll(void) {
- errno = 0;
- int r = poll(poll_fds, poll_nfds, poll_timeout);
- fprintf(stderr, "poll: r=%d errno=%s\n", r, strerror(errno));
-}
-
-void test_common_afterpoll(void)
-{
- test_common_get_now();
- libxl_osevent_afterpoll(ctx, poll_nfds, poll_fds, now);
-}
+++ /dev/null
-#ifndef TEST_COMMON_H
-#define TEST_COMMON_H
-
-#include "libxl.h"
-
-#include <assert.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-void test_common_setup(int level);
-
-extern libxl_ctx *ctx;
-
-void test_common_get_now(void);
-
-extern struct timeval now;
-
-void test_common_beforepoll(void);
-void test_common_dopoll(void);
-void test_common_afterpoll(void);
-
-extern int poll_nfds, poll_nfds_allocd;
-extern struct pollfd *poll_fds;
-extern int poll_timeout;
-
-#endif /*TEST_COMMON_H*/
+++ /dev/null
-#include "test_common.h"
-#include "libxl_test_fdevent.h"
-
-int main(int argc, char **argv) {
- int rc, i;
- libxl_asyncop_how how;
- libxl_event *event;
-
- test_common_setup(XTL_DEBUG);
-
- how.callback = NULL;
- how.u.for_event = 1;
-
- int fd = open("/dev/null", O_RDONLY);
- assert(fd > 0);
-
- rc = libxl_test_fdevent(ctx, fd, POLLIN, &how);
- assert(!rc);
-
- test_common_beforepoll();
-
- rc = libxl_ao_abort(ctx, &how);
- assert(!rc);
-
- rc = libxl_event_check(ctx, &event, LIBXL_EVENTMASK_ALL, 0,0);
- assert(!rc);
- assert(event);
- assert(event->for_user == how.u.for_event);
- assert(event->type == LIBXL_EVENT_TYPE_OPERATION_COMPLETE);
- assert(event->u.operation_complete.rc == ERROR_ABORTED);
-
- close(fd);
-
- test_common_dopoll();
-
- for (i=0; i<poll_nfds; i++) {
- if (poll_fds[i].fd == fd && (poll_fds[i].revents & POLLNVAL)) {
- fprintf(stderr, "POLLNVAL on fd=%d in slot i=%d as expected\n",
- fd, i);
- goto found;
- }
- }
- abort();
- found:;
-
- int fd2 = open("/dev/null", O_RDONLY);
- assert(fd2 == fd);
-
- how.u.for_event++;
- rc = libxl_test_fdevent(ctx, fd, POLLIN, &how);
- assert(!rc);
-
- test_common_afterpoll();
-
- fprintf(stderr, "complete\n");
-}
+++ /dev/null
-#include "test_common.h"
-#include "libxl_test_timedereg.h"
-
-int main(int argc, char **argv) {
- int rc;
-
- test_common_setup(XTL_DEBUG);
-
- rc = libxl_test_timedereg(ctx, 0);
- assert(!rc);
-}
< xenlight.mli.in > xenlight.mli.tmp
$(Q)mv xenlight.mli.tmp xenlight.mli
-_libxl_types.ml.in _libxl_types.mli.in _libxl_types.inc: genwrap.py $(XEN_ROOT)/tools/libxl/libxl_types.idl \
- $(XEN_ROOT)/tools/libxl/idl.py
- PYTHONPATH=$(XEN_ROOT)/tools/libxl $(PYTHON) genwrap.py \
- $(XEN_ROOT)/tools/libxl/libxl_types.idl \
+_libxl_types.ml.in _libxl_types.mli.in _libxl_types.inc: genwrap.py $(XEN_ROOT)/tools/libs/light/libxl_types.idl \
+ $(XEN_ROOT)/tools/libs/light/idl.py
+ PYTHONPATH=$(XEN_ROOT)/tools/libs/light $(PYTHON) genwrap.py \
+ $(XEN_ROOT)/tools/libs/light/libxl_types.idl \
_libxl_types.mli.in _libxl_types.ml.in _libxl_types.inc
libs: $(LIBS)