From: Juergen Gross Date: Wed, 23 Sep 2020 04:57:20 +0000 (+0200) Subject: tools/libxl: move libxenlight to tools/libs/light X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=41aea82de2b581c61482aeddab151ecf3b1bca25;p=people%2Fsstabellini%2Fxen-unstable.git%2F.git tools/libxl: move libxenlight to tools/libs/light Carve out all libxenlight related sources and move them to tools/libs/light in order to use the generic library build environment. The closely related sources for libxl-save-helper and the libxl test environment are being moved, too. Signed-off-by: Juergen Gross Acked-by: Wei Liu --- diff --git a/.gitignore b/.gitignore index 5e8c47e2db..f30550255f 100644 --- a/.gitignore +++ b/.gitignore @@ -128,6 +128,22 @@ tools/libs/guest/xc_core.h 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 @@ -216,16 +232,8 @@ tools/include/xen/* 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 @@ -380,13 +388,6 @@ tools/include/xen-foreign/arm64.h 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 diff --git a/tools/Rules.mk b/tools/Rules.mk index a71abb2e4f..a68dbb9de8 100644 --- a/tools/Rules.mk +++ b/tools/Rules.mk @@ -15,9 +15,7 @@ XEN_INCLUDE = $(XEN_ROOT)/tools/include 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) @@ -107,6 +105,8 @@ ifeq ($(CONFIG_Linux),y) LDLIBS_libxenstore += -ldl endif +CFLAGS_libxenlight += $(CFLAGS_libxenctrl) + ifeq ($(debug),y) # Disable optimizations CFLAGS += -O0 -fno-omit-frame-pointer @@ -116,11 +116,6 @@ else 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) diff --git a/tools/configure b/tools/configure index edcdcf4f73..8a708e9baa 100755 --- a/tools/configure +++ b/tools/configure @@ -585,7 +585,7 @@ PACKAGE_STRING='Xen Hypervisor Tools 4.15' 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 diff --git a/tools/configure.ac b/tools/configure.ac index 6614a4f130..ee8ba5ff24 100644 --- a/tools/configure.ac +++ b/tools/configure.ac @@ -4,7 +4,7 @@ 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 diff --git a/tools/golang/xenlight/Makefile b/tools/golang/xenlight/Makefile index b17095e64b..fd8e4893db 100644 --- a/tools/golang/xenlight/Makefile +++ b/tools/golang/xenlight/Makefile @@ -8,7 +8,7 @@ GOXL_INSTALL_DIR = $(GOCODE_DIR)/src/$(XEN_GOCODE_URL)/xenlight/ GO ?= go -LIBXL_SRC_DIR = ../../libxl +LIBXL_SRC_DIR = $(XEN_ROOT)/tools/libs/light .PHONY: all all: build diff --git a/tools/libs/Makefile b/tools/libs/Makefile index e8fcd59214..c41455c604 100644 --- a/tools/libs/Makefile +++ b/tools/libs/Makefile @@ -15,6 +15,7 @@ SUBDIRS-y += hypfs SUBDIRS-y += store SUBDIRS-y += stat SUBDIRS-$(CONFIG_Linux) += vchan +SUBDIRS-y += light ifeq ($(CONFIG_RUMP),y) SUBDIRS-y := toolcore diff --git a/tools/libs/light/CODING_STYLE b/tools/libs/light/CODING_STYLE new file mode 100644 index 0000000000..3d572f6925 --- /dev/null +++ b/tools/libs/light/CODING_STYLE @@ -0,0 +1,330 @@ +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. + diff --git a/tools/libs/light/Makefile b/tools/libs/light/Makefile new file mode 100644 index 0000000000..f58a3214e5 --- /dev/null +++ b/tools/libs/light/Makefile @@ -0,0 +1,277 @@ +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 diff --git a/tools/libs/light/check-libxl-api-rules b/tools/libs/light/check-libxl-api-rules new file mode 100755 index 0000000000..18ff39ca5f --- /dev/null +++ b/tools/libs/light/check-libxl-api-rules @@ -0,0 +1,23 @@ +#!/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"; + } +} diff --git a/tools/libs/light/flexarray.c b/tools/libs/light/flexarray.c new file mode 100644 index 0000000000..fe40e762e4 --- /dev/null +++ b/tools/libs/light/flexarray.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * + * 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 + +/* + * 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: + */ diff --git a/tools/libs/light/flexarray.h b/tools/libs/light/flexarray.h new file mode 100644 index 0000000000..a1e86475c4 --- /dev/null +++ b/tools/libs/light/flexarray.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * + * 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: + */ diff --git a/tools/libs/light/gentest.py b/tools/libs/light/gentest.py new file mode 100644 index 0000000000..1cc7eebc82 --- /dev/null +++ b/tools/libs/light/gentest.py @@ -0,0 +1,374 @@ +#!/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 ", 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 +#include +#include +#include + +#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; isize = 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< 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; +} +""") diff --git a/tools/libs/light/gentypes.py b/tools/libs/light/gentypes.py new file mode 100644 index 0000000000..9a45e45acc --- /dev/null +++ b/tools/libs/light/gentypes.py @@ -0,0 +1,797 @@ +#!/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
", 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 +#include +#include + +#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() diff --git a/tools/libs/light/idl.py b/tools/libs/light/idl.py new file mode 100644 index 0000000000..d7367503b4 --- /dev/null +++ b/tools/libs/light/idl.py @@ -0,0 +1,377 @@ +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) diff --git a/tools/libs/light/idl.txt b/tools/libs/light/idl.txt new file mode 100644 index 0000000000..7440fb3b76 --- /dev/null +++ b/tools/libs/light/idl.txt @@ -0,0 +1,214 @@ +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_t types. + + The 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 diff --git a/tools/libs/light/include/libxl.h b/tools/libs/light/include/libxl.h new file mode 100644 index 0000000000..1ea5b4f446 --- /dev/null +++ b/tools/libs/light/include/libxl.h @@ -0,0 +1,2732 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * + * 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_ 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__init( *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__init_( *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__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__to_json(instance *p) + * + * Generates a JSON object from "p" in the form of a NULL terminated + * string. + * + * libxl__from_json(const char *json) + * int libxl__from_json(const char *json) + * + * Parses "json" and returns: + * + * an int value, if is enumeration type. The value is the enum value + * representing the respective string in "json". + * + * an instance of , if 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 +#include +#include +#include +#include +#include +#include /* for pid_t */ + +#include + +typedef struct libxl__ctx libxl_ctx; + +#include +#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_ data structure + * which is defined via the IDL. In addition some devices have an + * additional data type libxl_device__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__list(ctx, domid, nr): + * + * Returns an array of libxl_device_ length nr representing + * the devices attached to the specified domain. + * + * libxl_device__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__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__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__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__destroy() + * semantics apply. + * + * libxl_device__safe_remove(ctx, domid, device): + * + * This has the same semantics as libxl_device__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 , there will be a set of functions + * and types for each . For example, for =usb, there + * may be ctrl (controller) and dev (device), with ctrl being + * level 0. + * + * libxl_device__ 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 + * 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, . 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_ 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__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__list will list all devices of + * at 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__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 + +/* + * 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: + */ diff --git a/tools/libs/light/include/libxl_event.h b/tools/libs/light/include/libxl_event.h new file mode 100644 index 0000000000..8d0aa6417e --- /dev/null +++ b/tools/libs/light/include/libxl_event.h @@ -0,0 +1,632 @@ +/* + * Copyright (C) 2011 Citrix Ltd. + * Author Ian Jackson + * + * 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 +#include +#include + +/*======================================================================*/ + +/* + * 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..] suitably for poll(2), updates *timeout_upd if needed, + * and returns ok. + * + * If space was insufficient, fds[0..] 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: + */ diff --git a/tools/libs/light/include/libxl_json.h b/tools/libs/light/include/libxl_json.h new file mode 100644 index 0000000000..260783bfde --- /dev/null +++ b/tools/libs/light/include/libxl_json.h @@ -0,0 +1,96 @@ +/* + * 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 +#include + +#ifdef HAVE_YAJL_YAJL_VERSION_H +# include +#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 */ diff --git a/tools/libs/light/include/libxl_utils.h b/tools/libs/light/include/libxl_utils.h new file mode 100644 index 0000000000..46918aea84 --- /dev/null +++ b/tools/libs/light/include/libxl_utils.h @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Stefano Stabellini + * + * 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: + */ diff --git a/tools/libs/light/include/libxl_uuid.h b/tools/libs/light/include/libxl_uuid.h new file mode 100644 index 0000000000..17c8b9736e --- /dev/null +++ b/tools/libs/light/include/libxl_uuid.h @@ -0,0 +1,80 @@ +/* + * 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 +#include + +#elif defined(__FreeBSD__) || defined(__NetBSD__) + +#include +#include +#include +#include +#include +#include + +#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: + */ diff --git a/tools/libs/light/libxl.c b/tools/libs/light/libxl.c new file mode 100644 index 0000000000..621acc88f3 --- /dev/null +++ b/tools/libs/light/libxl.c @@ -0,0 +1,831 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_9pfs.c b/tools/libs/light/libxl_9pfs.c new file mode 100644 index 0000000000..e5c41e9a25 --- /dev/null +++ b/tools/libs/light/libxl_9pfs.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 Aporeto + * Author Stefano Stabellini + * + * 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, +); diff --git a/tools/libs/light/libxl_aoutils.c b/tools/libs/light/libxl_aoutils.c new file mode 100644 index 0000000000..c4c095a5ba --- /dev/null +++ b/tools/libs/light/libxl_aoutils.c @@ -0,0 +1,667 @@ +/* + * 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; icount; 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; iresults[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; iresults[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: + */ diff --git a/tools/libs/light/libxl_arch.h b/tools/libs/light/libxl_arch.h new file mode 100644 index 0000000000..6a91775b9e --- /dev/null +++ b/tools/libs/light/libxl_arch.h @@ -0,0 +1,96 @@ +/* + * 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 diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c new file mode 100644 index 0000000000..975a4d730a --- /dev/null +++ b/tools/libs/light/libxl_arm.c @@ -0,0 +1,1230 @@ +#include "libxl_internal.h" +#include "libxl_arch.h" +#include "libxl_libfdt_compat.h" +#include "libxl_arm.h" + +#include +#include +#include +#include +#include + +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: + */ diff --git a/tools/libs/light/libxl_arm.h b/tools/libs/light/libxl_arm.h new file mode 100644 index 0000000000..52c2ab5e3a --- /dev/null +++ b/tools/libs/light/libxl_arm.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Linaro Ltd. + * + * Author: Shannon Zhao + * + * 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 + +_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: + */ diff --git a/tools/libs/light/libxl_arm_acpi.c b/tools/libs/light/libxl_arm_acpi.c new file mode 100644 index 0000000000..ba874c3d32 --- /dev/null +++ b/tools/libs/light/libxl_arm_acpi.c @@ -0,0 +1,413 @@ +/* + * ARM DomU ACPI generation + * + * Copyright (C) 2016 Linaro Ltd. + * + * Author: Shannon Zhao + * + * 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 + +/* 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 +#include + +#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 + +_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: + */ diff --git a/tools/libs/light/libxl_arm_no_acpi.c b/tools/libs/light/libxl_arm_no_acpi.c new file mode 100644 index 0000000000..5dde0cddee --- /dev/null +++ b/tools/libs/light/libxl_arm_no_acpi.c @@ -0,0 +1,40 @@ +/* + * ARM DomU ACPI generation + * + * Copyright (C) 2016 Linaro Ltd. + * + * Author: Shannon Zhao + * + * 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: + */ diff --git a/tools/libs/light/libxl_bootloader.c b/tools/libs/light/libxl_bootloader.c new file mode 100644 index 0000000000..18e9ebd714 --- /dev/null +++ b/tools/libs/light/libxl_bootloader.c @@ -0,0 +1,680 @@ +/* + * Copyright (C) 2010 Citrix Ltd. + * Author Ian Campbell + * + * 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 +#ifdef HAVE_UTMP_H +#include +#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: + */ diff --git a/tools/libs/light/libxl_checkpoint_device.c b/tools/libs/light/libxl_checkpoint_device.c new file mode 100644 index 0000000000..f6395dc672 --- /dev/null +++ b/tools/libs/light/libxl_checkpoint_device.c @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2014 FUJITSU LIMITED + * Author: Yang Hongyang + * + * 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); +} diff --git a/tools/libs/light/libxl_colo.h b/tools/libs/light/libxl_colo.h new file mode 100644 index 0000000000..6c01b554a1 --- /dev/null +++ b/tools/libs/light/libxl_colo.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2016 FUJITSU LIMITED + * Author: Wen Congyang + * + * 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 diff --git a/tools/libs/light/libxl_colo_nic.c b/tools/libs/light/libxl_colo_nic.c new file mode 100644 index 0000000000..bf5c48f0b4 --- /dev/null +++ b/tools/libs/light/libxl_colo_nic.c @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2016 FUJITSU LIMITED + * Author: Wen Congyang + * + * 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, +}; diff --git a/tools/libs/light/libxl_colo_proxy.c b/tools/libs/light/libxl_colo_proxy.c new file mode 100644 index 0000000000..5475f7ea32 --- /dev/null +++ b/tools/libs/light/libxl_colo_proxy.c @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2016 FUJITSU LIMITED + * Author: Yang Hongyang + * + * 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 +#include +#include +#include + +/* 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; +} diff --git a/tools/libs/light/libxl_colo_qdisk.c b/tools/libs/light/libxl_colo_qdisk.c new file mode 100644 index 0000000000..4c017cabe6 --- /dev/null +++ b/tools/libs/light/libxl_colo_qdisk.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2016 FUJITSU LIMITED + * Author: Wen Congyang + * + * 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, +}; diff --git a/tools/libs/light/libxl_colo_restore.c b/tools/libs/light/libxl_colo_restore.c new file mode 100644 index 0000000000..aa365670fb --- /dev/null +++ b/tools/libs/light/libxl_colo_restore.c @@ -0,0 +1,1093 @@ +/* + * Copyright (C) 2016 FUJITSU LIMITED + * Author: Wen Congyang + * Yang Hongyang + * + * 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); +} diff --git a/tools/libs/light/libxl_colo_save.c b/tools/libs/light/libxl_colo_save.c new file mode 100644 index 0000000000..b47f038f6e --- /dev/null +++ b/tools/libs/light/libxl_colo_save.c @@ -0,0 +1,714 @@ +/* + * Copyright (C) 2016 FUJITSU LIMITED + * Author: Wen Congyang + * Yang Hongyang + * + * 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); +} diff --git a/tools/libs/light/libxl_console.c b/tools/libs/light/libxl_console.c new file mode 100644 index 0000000000..047d23d7ae --- /dev/null +++ b/tools/libs/light/libxl_console.c @@ -0,0 +1,802 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_convert_callout.c b/tools/libs/light/libxl_convert_callout.c new file mode 100644 index 0000000000..5e5678b896 --- /dev/null +++ b/tools/libs/light/libxl_convert_callout.c @@ -0,0 +1,173 @@ +/* + * 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); +} diff --git a/tools/libs/light/libxl_cpuid.c b/tools/libs/light/libxl_cpuid.c new file mode 100644 index 0000000000..08e85dcffb --- /dev/null +++ b/tools/libs/light/libxl_cpuid.c @@ -0,0 +1,628 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_cpupool.c b/tools/libs/light/libxl_cpupool.c new file mode 100644 index 0000000000..85b06882db --- /dev/null +++ b/tools/libs/light/libxl_cpupool.c @@ -0,0 +1,452 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_create.c b/tools/libs/light/libxl_create.c new file mode 100644 index 0000000000..1031b75159 --- /dev/null +++ b/tools/libs/light/libxl_create.c @@ -0,0 +1,2310 @@ +/* + * Copyright (C) 2010 Citrix Ltd. + * Author Vincent Hanquez + * Author Stefano Stabellini + * Author Gianni Tedesco + * + * 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 +#include +#include +#include + +#include + +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: + */ diff --git a/tools/libs/light/libxl_device.c b/tools/libs/light/libxl_device.c new file mode 100644 index 0000000000..e081faf9a9 --- /dev/null +++ b/tools/libs/light/libxl_device.c @@ -0,0 +1,2090 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * Author Stefano Stabellini + * + * 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//backend/// */ + 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<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/ */ + 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: + */ diff --git a/tools/libs/light/libxl_disk.c b/tools/libs/light/libxl_disk.c new file mode 100644 index 0000000000..de183e0fb0 --- /dev/null +++ b/tools/libs/light/libxl_disk.c @@ -0,0 +1,1390 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_dm.c b/tools/libs/light/libxl_dm.c new file mode 100644 index 0000000000..fec4e0fbe5 --- /dev/null +++ b/tools/libs/light/libxl_dm.c @@ -0,0 +1,3795 @@ +/* + * Copyright (C) 2010 Citrix Ltd. + * Author Vincent Hanquez + * Author Stefano Stabellini + * Author Gianni Tedesco + * + * 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 +#include +#include +#include +#include + +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- + * + * 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; inics[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: + */ diff --git a/tools/libs/light/libxl_dom.c b/tools/libs/light/libxl_dom.c new file mode 100644 index 0000000000..597a6826d1 --- /dev/null +++ b/tools/libs/light/libxl_dom.c @@ -0,0 +1,1469 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * + * 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 + +#include "libxl_internal.h" +#include "libxl_arch.h" + +#include +#include +#include +#include + +#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= 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: + */ diff --git a/tools/libs/light/libxl_dom_save.c b/tools/libs/light/libxl_dom_save.c new file mode 100644 index 0000000000..32e3cb5a13 --- /dev/null +++ b/tools/libs/light/libxl_dom_save.c @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * + * 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 + +/*========================= 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 : ""); + 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: + */ diff --git a/tools/libs/light/libxl_dom_suspend.c b/tools/libs/light/libxl_dom_suspend.c new file mode 100644 index 0000000000..25d1571895 --- /dev/null +++ b/tools/libs/light/libxl_dom_suspend.c @@ -0,0 +1,677 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * + * 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: + */ diff --git a/tools/libs/light/libxl_domain.c b/tools/libs/light/libxl_domain.c new file mode 100644 index 0000000000..5d4ec90711 --- /dev/null +++ b/tools/libs/light/libxl_domain.c @@ -0,0 +1,2462 @@ +/* + * 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//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: + */ diff --git a/tools/libs/light/libxl_event.c b/tools/libs/light/libxl_event.c new file mode 100644 index 0000000000..7c5387e94f --- /dev/null +++ b/tools/libs/light/libxl_event.c @@ -0,0 +1,2467 @@ +/* + * 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 + +#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: + */ diff --git a/tools/libs/light/libxl_exec.c b/tools/libs/light/libxl_exec.c new file mode 100644 index 0000000000..47c9c8f1ba --- /dev/null +++ b/tools/libs/light/libxl_exec.c @@ -0,0 +1,473 @@ + +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * Author Stefano Stabellini + * + * 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: + */ diff --git a/tools/libs/light/libxl_flask.c b/tools/libs/light/libxl_flask.c new file mode 100644 index 0000000000..38347a31a3 --- /dev/null +++ b/tools/libs/light/libxl_flask.c @@ -0,0 +1,79 @@ +/* + * Author: Machon Gregory, + * + * 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: + */ diff --git a/tools/libs/light/libxl_fork.c b/tools/libs/light/libxl_fork.c new file mode 100644 index 0000000000..9a4709b9a4 --- /dev/null +++ b/tools/libs/light/libxl_fork.c @@ -0,0 +1,734 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_freebsd.c b/tools/libs/light/libxl_freebsd.c new file mode 100644 index 0000000000..f7ef4a8910 --- /dev/null +++ b/tools/libs/light/libxl_freebsd.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2014 + * Author Roger Pau Monne + * + * 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; +} diff --git a/tools/libs/light/libxl_genid.c b/tools/libs/light/libxl_genid.c new file mode 100644 index 0000000000..7f52356c60 --- /dev/null +++ b/tools/libs/light/libxl_genid.c @@ -0,0 +1,117 @@ +/* + * 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 +#include + +/* + * 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; +} diff --git a/tools/libs/light/libxl_internal.c b/tools/libs/light/libxl_internal.c new file mode 100644 index 0000000000..d93a75533f --- /dev/null +++ b/tools/libs/light/libxl_internal.c @@ -0,0 +1,806 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * + * 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: + */ diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_internal.h new file mode 100644 index 0000000000..1fcf85c3e2 --- /dev/null +++ b/tools/libs/light/libxl_internal.h @@ -0,0 +1,4859 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * Author Stefano Stabellini + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#define XC_WANT_COMPAT_MAP_FOREIGN_API +#include +#include +#include +#include + +#include + +#include "xentoollog.h" + +#include + +#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: ". */ +_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___setdefault(gc, *p): + * + * Idempotently sets any members of "p" which is currently set to + * a special value indicating that the defaults should be used + * (per libxl__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 ": ". + */ + +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 ab */ +_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 + +_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)) ? : "") + + /* 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 *GCNEW( *var); + * Uses libxl__gc *gc; + * + * Allocates a new object of 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 *GCNEW_ARRAY( *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 *GCREALLOC_ARRAY( *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(, const char *fmt, ...); + * void LOGE(, const char *fmt, ...); + * void LOGEV(, int errnoval, const char *fmt, ...); + * + * void LOGD(, uint32_t domid, const char *fmt, ...); + * void LOGED(, uint32_t domid, const char *fmt, ...); + * void LOGEVD(, 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_ should exist and be an xentoollog.h log level + * So 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 "" +#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 + * 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: + */ diff --git a/tools/libs/light/libxl_json.c b/tools/libs/light/libxl_json.c new file mode 100644 index 0000000000..9b8ef2cab9 --- /dev/null +++ b/tools/libs/light/libxl_json.c @@ -0,0 +1,1191 @@ +/* + * 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 + +#include +#include + +#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: + */ diff --git a/tools/libs/light/libxl_libfdt_compat.c b/tools/libs/light/libxl_libfdt_compat.c new file mode 100644 index 0000000000..02b8f7425e --- /dev/null +++ b/tools/libs/light/libxl_libfdt_compat.c @@ -0,0 +1,94 @@ +/* + * 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 + +#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 diff --git a/tools/libs/light/libxl_libfdt_compat.h b/tools/libs/light/libxl_libfdt_compat.h new file mode 100644 index 0000000000..23230b51b1 --- /dev/null +++ b/tools/libs/light/libxl_libfdt_compat.h @@ -0,0 +1,90 @@ +/* + * 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 + +#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: + */ diff --git a/tools/libs/light/libxl_linux.c b/tools/libs/light/libxl_linux.c new file mode 100644 index 0000000000..873b0271af --- /dev/null +++ b/tools/libs/light/libxl_linux.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2011 + * Author Roger Pau Monne + * + * 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 +#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: + */ diff --git a/tools/libs/light/libxl_mem.c b/tools/libs/light/libxl_mem.c new file mode 100644 index 0000000000..e52a9624ea --- /dev/null +++ b/tools/libs/light/libxl_mem.c @@ -0,0 +1,651 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_netbsd.c b/tools/libs/light/libxl_netbsd.c new file mode 100644 index 0000000000..e66a393d7f --- /dev/null +++ b/tools/libs/light/libxl_netbsd.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2011 + * Author Roger Pau Monne + * + * 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; +} diff --git a/tools/libs/light/libxl_netbuffer.c b/tools/libs/light/libxl_netbuffer.c new file mode 100644 index 0000000000..4b21914407 --- /dev/null +++ b/tools/libs/light/libxl_netbuffer.c @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2014 + * Author Shriram Rajagopalan + * + * 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 +#include +#include +#include +#include +#include +#include + +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//remus/netbuf//) + * $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: + */ diff --git a/tools/libs/light/libxl_nic.c b/tools/libs/light/libxl_nic.c new file mode 100644 index 0000000000..0e5d120ae9 --- /dev/null +++ b/tools/libs/light/libxl_nic.c @@ -0,0 +1,544 @@ +/* + * Copyright (C) 2016 SUSE Linux GmbH + * Author Juergen Gross + * + * 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 (; imtu) + 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: + */ diff --git a/tools/libs/light/libxl_no_colo.c b/tools/libs/light/libxl_no_colo.c new file mode 100644 index 0000000000..2e1315ca0f --- /dev/null +++ b/tools/libs/light/libxl_no_colo.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 + * Author Wei Liu + * + * 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: + */ diff --git a/tools/libs/light/libxl_no_convert_callout.c b/tools/libs/light/libxl_no_convert_callout.c new file mode 100644 index 0000000000..6ba4d92e08 --- /dev/null +++ b/tools/libs/light/libxl_no_convert_callout.c @@ -0,0 +1,35 @@ +/* + * 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 */ +} diff --git a/tools/libs/light/libxl_nocpuid.c b/tools/libs/light/libxl_nocpuid.c new file mode 100644 index 0000000000..f47336565b --- /dev/null +++ b/tools/libs/light/libxl_nocpuid.c @@ -0,0 +1,67 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_nonetbuffer.c b/tools/libs/light/libxl_nonetbuffer.c new file mode 100644 index 0000000000..4b6815211d --- /dev/null +++ b/tools/libs/light/libxl_nonetbuffer.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 + * Author Shriram Rajagopalan + * + * 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: + */ diff --git a/tools/libs/light/libxl_numa.c b/tools/libs/light/libxl_numa.c new file mode 100644 index 0000000000..a8a75f89e9 --- /dev/null +++ b/tools/libs/light/libxl_numa.c @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2012 Citrix Ltd. + * Author Dario Faggioli + * + * 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 + +#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: + */ diff --git a/tools/libs/light/libxl_osdeps.h b/tools/libs/light/libxl_osdeps.h new file mode 100644 index 0000000000..de1d24ecae --- /dev/null +++ b/tools/libs/light/libxl_osdeps.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Stefano Stabellini + * + * 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 +#include +#elif defined(__OpenBSD__) +#include +#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 +#include +#include +#elif defined(__sun__) +#include +#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 +#include +#include +/* + * 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 + +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 + +# 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: + */ diff --git a/tools/libs/light/libxl_paths.c b/tools/libs/light/libxl_paths.c new file mode 100644 index 0000000000..3f6a33628e --- /dev/null +++ b/tools/libs/light/libxl_paths.c @@ -0,0 +1,71 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_pci.c b/tools/libs/light/libxl_pci.c new file mode 100644 index 0000000000..bc5843b137 --- /dev/null +++ b/tools/libs/light/libxl_pci.c @@ -0,0 +1,2508 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * Author Stefano Stabellini + * + * 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: + */ diff --git a/tools/libs/light/libxl_psr.c b/tools/libs/light/libxl_psr.c new file mode 100644 index 0000000000..9ced7d1715 --- /dev/null +++ b/tools/libs/light/libxl_psr.c @@ -0,0 +1,562 @@ +/* + * Copyright (C) 2014 Intel Corporation + * Author Dongxiao Xu + * + * 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 + +#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: + */ diff --git a/tools/libs/light/libxl_pvcalls.c b/tools/libs/light/libxl_pvcalls.c new file mode 100644 index 0000000000..870318e716 --- /dev/null +++ b/tools/libs/light/libxl_pvcalls.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 Aporeto + * Author Stefano Stabellini + * + * 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); diff --git a/tools/libs/light/libxl_qmp.c b/tools/libs/light/libxl_qmp.c new file mode 100644 index 0000000000..c394000ea9 --- /dev/null +++ b/tools/libs/light/libxl_qmp.c @@ -0,0 +1,1934 @@ +/* + * Copyright (C) 2011 Citrix Ltd. + * Author Anthony PERARD + * + * 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 + +#include + +#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: + */ diff --git a/tools/libs/light/libxl_remus.c b/tools/libs/light/libxl_remus.c new file mode 100644 index 0000000000..6338a1bae5 --- /dev/null +++ b/tools/libs/light/libxl_remus.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * Yang Hongyang + * + * 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: + */ diff --git a/tools/libs/light/libxl_remus_disk_drbd.c b/tools/libs/light/libxl_remus_disk_drbd.c new file mode 100644 index 0000000000..d08e470bfa --- /dev/null +++ b/tools/libs/light/libxl_remus_disk_drbd.c @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2014 FUJITSU LIMITED + * Author Lai Jiangshan + * + * 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, +}; diff --git a/tools/libs/light/libxl_save_callout.c b/tools/libs/light/libxl_save_callout.c new file mode 100644 index 0000000000..0b11495f9b --- /dev/null +++ b/tools/libs/light/libxl_save_callout.c @@ -0,0 +1,430 @@ +/* + * 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; ipipes[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= 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; +} diff --git a/tools/libs/light/libxl_save_helper.c b/tools/libs/light/libxl_save_helper.c new file mode 100644 index 0000000000..65dff389bf --- /dev/null +++ b/tools/libs/light/libxl_save_helper.c @@ -0,0 +1,303 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#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: + */ diff --git a/tools/libs/light/libxl_save_msgs_gen.pl b/tools/libs/light/libxl_save_msgs_gen.pl new file mode 100755 index 0000000000..5bfbd4fd10 --- /dev/null +++ b/tools/libs/light/libxl_save_msgs_gen.pl @@ -0,0 +1,396 @@ +#!/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} .= + < +#include +#include +#include +END_BOTH + +#include "libxl_internal.h" + +END_CALLOUT + +#include +#include +#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'} .= <($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", + <(" 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", + (<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: + */ diff --git a/tools/libs/light/libxl_sr_stream_format.h b/tools/libs/light/libxl_sr_stream_format.h new file mode 100644 index 0000000000..75f5190886 --- /dev/null +++ b/tools/libs/light/libxl_sr_stream_format.h @@ -0,0 +1,69 @@ +#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 + +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: + */ diff --git a/tools/libs/light/libxl_stream_read.c b/tools/libs/light/libxl_stream_read.c new file mode 100644 index 0000000000..514f6d9f89 --- /dev/null +++ b/tools/libs/light/libxl_stream_read.c @@ -0,0 +1,977 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_stream_write.c b/tools/libs/light/libxl_stream_write.c new file mode 100644 index 0000000000..634f3240d1 --- /dev/null +++ b/tools/libs/light/libxl_stream_write.c @@ -0,0 +1,727 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_test_fdevent.c b/tools/libs/light/libxl_test_fdevent.c new file mode 100644 index 0000000000..2d875d995f --- /dev/null +++ b/tools/libs/light/libxl_test_fdevent.c @@ -0,0 +1,79 @@ +/* + * 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); +} diff --git a/tools/libs/light/libxl_test_fdevent.h b/tools/libs/light/libxl_test_fdevent.h new file mode 100644 index 0000000000..82a307e75b --- /dev/null +++ b/tools/libs/light/libxl_test_fdevent.h @@ -0,0 +1,12 @@ +#ifndef TEST_FDEVENT_H +#define TEST_FDEVENT_H + +#include + +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*/ diff --git a/tools/libs/light/libxl_test_timedereg.c b/tools/libs/light/libxl_test_timedereg.c new file mode 100644 index 0000000000..a567db6839 --- /dev/null +++ b/tools/libs/light/libxl_test_timedereg.c @@ -0,0 +1,100 @@ +/* + * 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 + +int libxl_test_timedereg(libxl_ctx *ctx, libxl_asyncop_how *ao_how) + LIBXL_EXTERNAL_CALLERS_ONLY; + +#endif /*TEST_TIMEDEREG_H*/ diff --git a/tools/libs/light/libxl_tmem.c b/tools/libs/light/libxl_tmem.c new file mode 100644 index 0000000000..a553b39738 --- /dev/null +++ b/tools/libs/light/libxl_tmem.c @@ -0,0 +1,76 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl new file mode 100644 index 0000000000..9d3f05f399 --- /dev/null +++ b/tools/libs/light/libxl_types.idl @@ -0,0 +1,1224 @@ +# -*- 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) diff --git a/tools/libs/light/libxl_types_internal.idl b/tools/libs/light/libxl_types_internal.idl new file mode 100644 index 0000000000..3593e21dbb --- /dev/null +++ b/tools/libs/light/libxl_types_internal.idl @@ -0,0 +1,57 @@ +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"), + ]) diff --git a/tools/libs/light/libxl_usb.c b/tools/libs/light/libxl_usb.c new file mode 100644 index 0000000000..171bb04439 --- /dev/null +++ b/tools/libs/light/libxl_usb.c @@ -0,0 +1,2158 @@ +/* + * Copyright (C) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany. + * Author Chunyan Liu + * + * 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 +#include + +#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: + */ diff --git a/tools/libs/light/libxl_utils.c b/tools/libs/light/libxl_utils.c new file mode 100644 index 0000000000..b039143b8a --- /dev/null +++ b/tools/libs/light/libxl_utils.c @@ -0,0 +1,1272 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Stefano Stabellini + * + * 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 + +#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: + */ diff --git a/tools/libs/light/libxl_uuid.c b/tools/libs/light/libxl_uuid.c new file mode 100644 index 0000000000..dadb79bad8 --- /dev/null +++ b/tools/libs/light/libxl_uuid.c @@ -0,0 +1,163 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_vdispl.c b/tools/libs/light/libxl_vdispl.c new file mode 100644 index 0000000000..8ddc8940e9 --- /dev/null +++ b/tools/libs/light/libxl_vdispl.c @@ -0,0 +1,222 @@ +/* + * 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 + +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: + */ diff --git a/tools/libs/light/libxl_vkb.c b/tools/libs/light/libxl_vkb.c new file mode 100644 index 0000000000..4c44a813c1 --- /dev/null +++ b/tools/libs/light/libxl_vkb.c @@ -0,0 +1,353 @@ +/* + * 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 + +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: + */ diff --git a/tools/libs/light/libxl_vnuma.c b/tools/libs/light/libxl_vnuma.c new file mode 100644 index 0000000000..c2e144ceae --- /dev/null +++ b/tools/libs/light/libxl_vnuma.c @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2014 Citrix Ltd. + * Author Wei Liu + * + * 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 + +#include + +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: + */ diff --git a/tools/libs/light/libxl_vsnd.c b/tools/libs/light/libxl_vsnd.c new file mode 100644 index 0000000000..0bc5f6dbb1 --- /dev/null +++ b/tools/libs/light/libxl_vsnd.c @@ -0,0 +1,686 @@ +/* + * 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 + +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: + */ diff --git a/tools/libs/light/libxl_vtpm.c b/tools/libs/light/libxl_vtpm.c new file mode 100644 index 0000000000..dd00b267bb --- /dev/null +++ b/tools/libs/light/libxl_vtpm.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2016 SUSE Linux GmbH + * Author Juergen Gross + * + * 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: + */ + diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c new file mode 100644 index 0000000000..7d95506e00 --- /dev/null +++ b/tools/libs/light/libxl_x86.c @@ -0,0 +1,852 @@ +#include "libxl_internal.h" +#include "libxl_arch.h" + +#include + +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: + */ diff --git a/tools/libs/light/libxl_x86_acpi.c b/tools/libs/light/libxl_x86_acpi.c new file mode 100644 index 0000000000..3df86c7be5 --- /dev/null +++ b/tools/libs/light/libxl_x86_acpi.c @@ -0,0 +1,252 @@ +/* + * 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 +#include +#include "libacpi/libacpi.h" + +#include + + /* 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: + */ diff --git a/tools/libs/light/libxl_x86_acpi.h b/tools/libs/light/libxl_x86_acpi.h new file mode 100644 index 0000000000..d404637176 --- /dev/null +++ b/tools/libs/light/libxl_x86_acpi.h @@ -0,0 +1,35 @@ +/* + * 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: + */ diff --git a/tools/libs/light/libxl_xshelp.c b/tools/libs/light/libxl_xshelp.c new file mode 100644 index 0000000000..751cd942d9 --- /dev/null +++ b/tools/libs/light/libxl_xshelp.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Vincent Hanquez + * + * 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: + */ diff --git a/tools/libs/light/osdeps.c b/tools/libs/light/osdeps.c new file mode 100644 index 0000000000..0e0b447117 --- /dev/null +++ b/tools/libs/light/osdeps.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009 Citrix Ltd. + * Author Stefano Stabellini + * + * 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 +#include +#include +#include +#include + +#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: + */ diff --git a/tools/libs/light/test_common.c b/tools/libs/light/test_common.c new file mode 100644 index 0000000000..c6bbbabf2d --- /dev/null +++ b/tools/libs/light/test_common.c @@ -0,0 +1,57 @@ +#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); +} diff --git a/tools/libs/light/test_common.h b/tools/libs/light/test_common.h new file mode 100644 index 0000000000..10c71669c8 --- /dev/null +++ b/tools/libs/light/test_common.h @@ -0,0 +1,29 @@ +#ifndef TEST_COMMON_H +#define TEST_COMMON_H + +#include "libxl.h" + +#include +#include +#include +#include +#include +#include + +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*/ diff --git a/tools/libs/light/test_fdderegrace.c b/tools/libs/light/test_fdderegrace.c new file mode 100644 index 0000000000..f57965f709 --- /dev/null +++ b/tools/libs/light/test_fdderegrace.c @@ -0,0 +1,56 @@ +#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$@.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 diff --git a/tools/libxl/check-libxl-api-rules b/tools/libxl/check-libxl-api-rules deleted file mode 100755 index 18ff39ca5f..0000000000 --- a/tools/libxl/check-libxl-api-rules +++ /dev/null @@ -1,23 +0,0 @@ -#!/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"; - } -} diff --git a/tools/libxl/flexarray.c b/tools/libxl/flexarray.c deleted file mode 100644 index fe40e762e4..0000000000 --- a/tools/libxl/flexarray.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * - * 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 - -/* - * 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: - */ diff --git a/tools/libxl/flexarray.h b/tools/libxl/flexarray.h deleted file mode 100644 index a1e86475c4..0000000000 --- a/tools/libxl/flexarray.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * - * 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: - */ diff --git a/tools/libxl/gentest.py b/tools/libxl/gentest.py deleted file mode 100644 index 1cc7eebc82..0000000000 --- a/tools/libxl/gentest.py +++ /dev/null @@ -1,374 +0,0 @@ -#!/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 ", 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 -#include -#include -#include - -#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; isize = 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< 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; -} -""") diff --git a/tools/libxl/gentypes.py b/tools/libxl/gentypes.py deleted file mode 100644 index 9a45e45acc..0000000000 --- a/tools/libxl/gentypes.py +++ /dev/null @@ -1,797 +0,0 @@ -#!/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
", 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 -#include -#include - -#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() diff --git a/tools/libxl/idl.py b/tools/libxl/idl.py deleted file mode 100644 index d7367503b4..0000000000 --- a/tools/libxl/idl.py +++ /dev/null @@ -1,377 +0,0 @@ -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) diff --git a/tools/libxl/idl.txt b/tools/libxl/idl.txt deleted file mode 100644 index 7440fb3b76..0000000000 --- a/tools/libxl/idl.txt +++ /dev/null @@ -1,214 +0,0 @@ -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_t types. - - The 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 diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c deleted file mode 100644 index 621acc88f3..0000000000 --- a/tools/libxl/libxl.c +++ /dev/null @@ -1,831 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h deleted file mode 100644 index 1ea5b4f446..0000000000 --- a/tools/libxl/libxl.h +++ /dev/null @@ -1,2732 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * - * 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_ 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__init( *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__init_( *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__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__to_json(instance *p) - * - * Generates a JSON object from "p" in the form of a NULL terminated - * string. - * - * libxl__from_json(const char *json) - * int libxl__from_json(const char *json) - * - * Parses "json" and returns: - * - * an int value, if is enumeration type. The value is the enum value - * representing the respective string in "json". - * - * an instance of , if 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 -#include -#include -#include -#include -#include -#include /* for pid_t */ - -#include - -typedef struct libxl__ctx libxl_ctx; - -#include -#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_ data structure - * which is defined via the IDL. In addition some devices have an - * additional data type libxl_device__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__list(ctx, domid, nr): - * - * Returns an array of libxl_device_ length nr representing - * the devices attached to the specified domain. - * - * libxl_device__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__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__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__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__destroy() - * semantics apply. - * - * libxl_device__safe_remove(ctx, domid, device): - * - * This has the same semantics as libxl_device__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 , there will be a set of functions - * and types for each . For example, for =usb, there - * may be ctrl (controller) and dev (device), with ctrl being - * level 0. - * - * libxl_device__ 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 - * 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, . 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_ 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__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__list will list all devices of - * at 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__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 - -/* - * 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: - */ diff --git a/tools/libxl/libxl_9pfs.c b/tools/libxl/libxl_9pfs.c deleted file mode 100644 index e5c41e9a25..0000000000 --- a/tools/libxl/libxl_9pfs.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2017 Aporeto - * Author Stefano Stabellini - * - * 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, -); diff --git a/tools/libxl/libxl_aoutils.c b/tools/libxl/libxl_aoutils.c deleted file mode 100644 index c4c095a5ba..0000000000 --- a/tools/libxl/libxl_aoutils.c +++ /dev/null @@ -1,667 +0,0 @@ -/* - * 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; icount; 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; iresults[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; iresults[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: - */ diff --git a/tools/libxl/libxl_arch.h b/tools/libxl/libxl_arch.h deleted file mode 100644 index 6a91775b9e..0000000000 --- a/tools/libxl/libxl_arch.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 diff --git a/tools/libxl/libxl_arm.c b/tools/libxl/libxl_arm.c deleted file mode 100644 index 975a4d730a..0000000000 --- a/tools/libxl/libxl_arm.c +++ /dev/null @@ -1,1230 +0,0 @@ -#include "libxl_internal.h" -#include "libxl_arch.h" -#include "libxl_libfdt_compat.h" -#include "libxl_arm.h" - -#include -#include -#include -#include -#include - -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: - */ diff --git a/tools/libxl/libxl_arm.h b/tools/libxl/libxl_arm.h deleted file mode 100644 index 52c2ab5e3a..0000000000 --- a/tools/libxl/libxl_arm.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2016 Linaro Ltd. - * - * Author: Shannon Zhao - * - * 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 - -_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: - */ diff --git a/tools/libxl/libxl_arm_acpi.c b/tools/libxl/libxl_arm_acpi.c deleted file mode 100644 index ba874c3d32..0000000000 --- a/tools/libxl/libxl_arm_acpi.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * ARM DomU ACPI generation - * - * Copyright (C) 2016 Linaro Ltd. - * - * Author: Shannon Zhao - * - * 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 - -/* 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 -#include - -#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 - -_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: - */ diff --git a/tools/libxl/libxl_arm_no_acpi.c b/tools/libxl/libxl_arm_no_acpi.c deleted file mode 100644 index 5dde0cddee..0000000000 --- a/tools/libxl/libxl_arm_no_acpi.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * ARM DomU ACPI generation - * - * Copyright (C) 2016 Linaro Ltd. - * - * Author: Shannon Zhao - * - * 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: - */ diff --git a/tools/libxl/libxl_bootloader.c b/tools/libxl/libxl_bootloader.c deleted file mode 100644 index 18e9ebd714..0000000000 --- a/tools/libxl/libxl_bootloader.c +++ /dev/null @@ -1,680 +0,0 @@ -/* - * Copyright (C) 2010 Citrix Ltd. - * Author Ian Campbell - * - * 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 -#ifdef HAVE_UTMP_H -#include -#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: - */ diff --git a/tools/libxl/libxl_checkpoint_device.c b/tools/libxl/libxl_checkpoint_device.c deleted file mode 100644 index f6395dc672..0000000000 --- a/tools/libxl/libxl_checkpoint_device.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2014 FUJITSU LIMITED - * Author: Yang Hongyang - * - * 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); -} diff --git a/tools/libxl/libxl_colo.h b/tools/libxl/libxl_colo.h deleted file mode 100644 index 6c01b554a1..0000000000 --- a/tools/libxl/libxl_colo.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2016 FUJITSU LIMITED - * Author: Wen Congyang - * - * 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 diff --git a/tools/libxl/libxl_colo_nic.c b/tools/libxl/libxl_colo_nic.c deleted file mode 100644 index bf5c48f0b4..0000000000 --- a/tools/libxl/libxl_colo_nic.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (C) 2016 FUJITSU LIMITED - * Author: Wen Congyang - * - * 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, -}; diff --git a/tools/libxl/libxl_colo_proxy.c b/tools/libxl/libxl_colo_proxy.c deleted file mode 100644 index 5475f7ea32..0000000000 --- a/tools/libxl/libxl_colo_proxy.c +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright (C) 2016 FUJITSU LIMITED - * Author: Yang Hongyang - * - * 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 -#include -#include -#include - -/* 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; -} diff --git a/tools/libxl/libxl_colo_qdisk.c b/tools/libxl/libxl_colo_qdisk.c deleted file mode 100644 index 4c017cabe6..0000000000 --- a/tools/libxl/libxl_colo_qdisk.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2016 FUJITSU LIMITED - * Author: Wen Congyang - * - * 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, -}; diff --git a/tools/libxl/libxl_colo_restore.c b/tools/libxl/libxl_colo_restore.c deleted file mode 100644 index aa365670fb..0000000000 --- a/tools/libxl/libxl_colo_restore.c +++ /dev/null @@ -1,1093 +0,0 @@ -/* - * Copyright (C) 2016 FUJITSU LIMITED - * Author: Wen Congyang - * Yang Hongyang - * - * 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); -} diff --git a/tools/libxl/libxl_colo_save.c b/tools/libxl/libxl_colo_save.c deleted file mode 100644 index b47f038f6e..0000000000 --- a/tools/libxl/libxl_colo_save.c +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Copyright (C) 2016 FUJITSU LIMITED - * Author: Wen Congyang - * Yang Hongyang - * - * 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); -} diff --git a/tools/libxl/libxl_console.c b/tools/libxl/libxl_console.c deleted file mode 100644 index 047d23d7ae..0000000000 --- a/tools/libxl/libxl_console.c +++ /dev/null @@ -1,802 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_convert_callout.c b/tools/libxl/libxl_convert_callout.c deleted file mode 100644 index 5e5678b896..0000000000 --- a/tools/libxl/libxl_convert_callout.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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); -} diff --git a/tools/libxl/libxl_cpuid.c b/tools/libxl/libxl_cpuid.c deleted file mode 100644 index 08e85dcffb..0000000000 --- a/tools/libxl/libxl_cpuid.c +++ /dev/null @@ -1,628 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_cpupool.c b/tools/libxl/libxl_cpupool.c deleted file mode 100644 index 85b06882db..0000000000 --- a/tools/libxl/libxl_cpupool.c +++ /dev/null @@ -1,452 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c deleted file mode 100644 index 1031b75159..0000000000 --- a/tools/libxl/libxl_create.c +++ /dev/null @@ -1,2310 +0,0 @@ -/* - * Copyright (C) 2010 Citrix Ltd. - * Author Vincent Hanquez - * Author Stefano Stabellini - * Author Gianni Tedesco - * - * 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 -#include -#include -#include - -#include - -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: - */ diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c deleted file mode 100644 index e081faf9a9..0000000000 --- a/tools/libxl/libxl_device.c +++ /dev/null @@ -1,2090 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * Author Stefano Stabellini - * - * 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//backend/// */ - 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<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/ */ - 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: - */ diff --git a/tools/libxl/libxl_disk.c b/tools/libxl/libxl_disk.c deleted file mode 100644 index de183e0fb0..0000000000 --- a/tools/libxl/libxl_disk.c +++ /dev/null @@ -1,1390 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_dm.c b/tools/libxl/libxl_dm.c deleted file mode 100644 index fec4e0fbe5..0000000000 --- a/tools/libxl/libxl_dm.c +++ /dev/null @@ -1,3795 +0,0 @@ -/* - * Copyright (C) 2010 Citrix Ltd. - * Author Vincent Hanquez - * Author Stefano Stabellini - * Author Gianni Tedesco - * - * 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 -#include -#include -#include -#include - -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- - * - * 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; inics[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: - */ diff --git a/tools/libxl/libxl_dom.c b/tools/libxl/libxl_dom.c deleted file mode 100644 index 597a6826d1..0000000000 --- a/tools/libxl/libxl_dom.c +++ /dev/null @@ -1,1469 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * - * 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 - -#include "libxl_internal.h" -#include "libxl_arch.h" - -#include -#include -#include -#include - -#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= 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: - */ diff --git a/tools/libxl/libxl_dom_save.c b/tools/libxl/libxl_dom_save.c deleted file mode 100644 index 32e3cb5a13..0000000000 --- a/tools/libxl/libxl_dom_save.c +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * - * 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 - -/*========================= 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 : ""); - 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: - */ diff --git a/tools/libxl/libxl_dom_suspend.c b/tools/libxl/libxl_dom_suspend.c deleted file mode 100644 index 25d1571895..0000000000 --- a/tools/libxl/libxl_dom_suspend.c +++ /dev/null @@ -1,677 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * - * 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: - */ diff --git a/tools/libxl/libxl_domain.c b/tools/libxl/libxl_domain.c deleted file mode 100644 index 5d4ec90711..0000000000 --- a/tools/libxl/libxl_domain.c +++ /dev/null @@ -1,2462 +0,0 @@ -/* - * 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//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: - */ diff --git a/tools/libxl/libxl_event.c b/tools/libxl/libxl_event.c deleted file mode 100644 index 7c5387e94f..0000000000 --- a/tools/libxl/libxl_event.c +++ /dev/null @@ -1,2467 +0,0 @@ -/* - * 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 - -#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: - */ diff --git a/tools/libxl/libxl_event.h b/tools/libxl/libxl_event.h deleted file mode 100644 index 8d0aa6417e..0000000000 --- a/tools/libxl/libxl_event.h +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Copyright (C) 2011 Citrix Ltd. - * Author Ian Jackson - * - * 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 -#include -#include - -/*======================================================================*/ - -/* - * 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..] suitably for poll(2), updates *timeout_upd if needed, - * and returns ok. - * - * If space was insufficient, fds[0..] 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: - */ diff --git a/tools/libxl/libxl_exec.c b/tools/libxl/libxl_exec.c deleted file mode 100644 index 47c9c8f1ba..0000000000 --- a/tools/libxl/libxl_exec.c +++ /dev/null @@ -1,473 +0,0 @@ - -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * Author Stefano Stabellini - * - * 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: - */ diff --git a/tools/libxl/libxl_flask.c b/tools/libxl/libxl_flask.c deleted file mode 100644 index 38347a31a3..0000000000 --- a/tools/libxl/libxl_flask.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Author: Machon Gregory, - * - * 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: - */ diff --git a/tools/libxl/libxl_fork.c b/tools/libxl/libxl_fork.c deleted file mode 100644 index 9a4709b9a4..0000000000 --- a/tools/libxl/libxl_fork.c +++ /dev/null @@ -1,734 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_freebsd.c b/tools/libxl/libxl_freebsd.c deleted file mode 100644 index f7ef4a8910..0000000000 --- a/tools/libxl/libxl_freebsd.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2014 - * Author Roger Pau Monne - * - * 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; -} diff --git a/tools/libxl/libxl_genid.c b/tools/libxl/libxl_genid.c deleted file mode 100644 index 7f52356c60..0000000000 --- a/tools/libxl/libxl_genid.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 -#include - -/* - * 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; -} diff --git a/tools/libxl/libxl_internal.c b/tools/libxl/libxl_internal.c deleted file mode 100644 index d93a75533f..0000000000 --- a/tools/libxl/libxl_internal.c +++ /dev/null @@ -1,806 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * - * 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: - */ diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h deleted file mode 100644 index 1fcf85c3e2..0000000000 --- a/tools/libxl/libxl_internal.h +++ /dev/null @@ -1,4859 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * Author Stefano Stabellini - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#define XC_WANT_COMPAT_MAP_FOREIGN_API -#include -#include -#include -#include - -#include - -#include "xentoollog.h" - -#include - -#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: ". */ -_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___setdefault(gc, *p): - * - * Idempotently sets any members of "p" which is currently set to - * a special value indicating that the defaults should be used - * (per libxl__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 ": ". - */ - -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 ab */ -_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 - -_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)) ? : "") - - /* 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 *GCNEW( *var); - * Uses libxl__gc *gc; - * - * Allocates a new object of 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 *GCNEW_ARRAY( *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 *GCREALLOC_ARRAY( *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(, const char *fmt, ...); - * void LOGE(, const char *fmt, ...); - * void LOGEV(, int errnoval, const char *fmt, ...); - * - * void LOGD(, uint32_t domid, const char *fmt, ...); - * void LOGED(, uint32_t domid, const char *fmt, ...); - * void LOGEVD(, 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_ should exist and be an xentoollog.h log level - * So 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 "" -#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 - * 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: - */ diff --git a/tools/libxl/libxl_json.c b/tools/libxl/libxl_json.c deleted file mode 100644 index 9b8ef2cab9..0000000000 --- a/tools/libxl/libxl_json.c +++ /dev/null @@ -1,1191 +0,0 @@ -/* - * 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 - -#include -#include - -#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: - */ diff --git a/tools/libxl/libxl_json.h b/tools/libxl/libxl_json.h deleted file mode 100644 index 260783bfde..0000000000 --- a/tools/libxl/libxl_json.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 -#include - -#ifdef HAVE_YAJL_YAJL_VERSION_H -# include -#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 */ diff --git a/tools/libxl/libxl_libfdt_compat.c b/tools/libxl/libxl_libfdt_compat.c deleted file mode 100644 index 02b8f7425e..0000000000 --- a/tools/libxl/libxl_libfdt_compat.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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 - -#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 diff --git a/tools/libxl/libxl_libfdt_compat.h b/tools/libxl/libxl_libfdt_compat.h deleted file mode 100644 index 23230b51b1..0000000000 --- a/tools/libxl/libxl_libfdt_compat.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 - -#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: - */ diff --git a/tools/libxl/libxl_linux.c b/tools/libxl/libxl_linux.c deleted file mode 100644 index 873b0271af..0000000000 --- a/tools/libxl/libxl_linux.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright (C) 2011 - * Author Roger Pau Monne - * - * 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 -#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: - */ diff --git a/tools/libxl/libxl_mem.c b/tools/libxl/libxl_mem.c deleted file mode 100644 index e52a9624ea..0000000000 --- a/tools/libxl/libxl_mem.c +++ /dev/null @@ -1,651 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_netbsd.c b/tools/libxl/libxl_netbsd.c deleted file mode 100644 index e66a393d7f..0000000000 --- a/tools/libxl/libxl_netbsd.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2011 - * Author Roger Pau Monne - * - * 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; -} diff --git a/tools/libxl/libxl_netbuffer.c b/tools/libxl/libxl_netbuffer.c deleted file mode 100644 index 4b21914407..0000000000 --- a/tools/libxl/libxl_netbuffer.c +++ /dev/null @@ -1,532 +0,0 @@ -/* - * Copyright (C) 2014 - * Author Shriram Rajagopalan - * - * 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 -#include -#include -#include -#include -#include -#include - -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//remus/netbuf//) - * $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: - */ diff --git a/tools/libxl/libxl_nic.c b/tools/libxl/libxl_nic.c deleted file mode 100644 index 0e5d120ae9..0000000000 --- a/tools/libxl/libxl_nic.c +++ /dev/null @@ -1,544 +0,0 @@ -/* - * Copyright (C) 2016 SUSE Linux GmbH - * Author Juergen Gross - * - * 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 (; imtu) - 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: - */ diff --git a/tools/libxl/libxl_no_colo.c b/tools/libxl/libxl_no_colo.c deleted file mode 100644 index 2e1315ca0f..0000000000 --- a/tools/libxl/libxl_no_colo.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2016 - * Author Wei Liu - * - * 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: - */ diff --git a/tools/libxl/libxl_no_convert_callout.c b/tools/libxl/libxl_no_convert_callout.c deleted file mode 100644 index 6ba4d92e08..0000000000 --- a/tools/libxl/libxl_no_convert_callout.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 */ -} diff --git a/tools/libxl/libxl_nocpuid.c b/tools/libxl/libxl_nocpuid.c deleted file mode 100644 index f47336565b..0000000000 --- a/tools/libxl/libxl_nocpuid.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_nonetbuffer.c b/tools/libxl/libxl_nonetbuffer.c deleted file mode 100644 index 4b6815211d..0000000000 --- a/tools/libxl/libxl_nonetbuffer.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2014 - * Author Shriram Rajagopalan - * - * 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: - */ diff --git a/tools/libxl/libxl_numa.c b/tools/libxl/libxl_numa.c deleted file mode 100644 index a8a75f89e9..0000000000 --- a/tools/libxl/libxl_numa.c +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright (C) 2012 Citrix Ltd. - * Author Dario Faggioli - * - * 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 - -#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: - */ diff --git a/tools/libxl/libxl_osdeps.h b/tools/libxl/libxl_osdeps.h deleted file mode 100644 index de1d24ecae..0000000000 --- a/tools/libxl/libxl_osdeps.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Stefano Stabellini - * - * 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 -#include -#elif defined(__OpenBSD__) -#include -#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 -#include -#include -#elif defined(__sun__) -#include -#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 -#include -#include -/* - * 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 - -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 - -# 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: - */ diff --git a/tools/libxl/libxl_paths.c b/tools/libxl/libxl_paths.c deleted file mode 100644 index 3f6a33628e..0000000000 --- a/tools/libxl/libxl_paths.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_pci.c b/tools/libxl/libxl_pci.c deleted file mode 100644 index bc5843b137..0000000000 --- a/tools/libxl/libxl_pci.c +++ /dev/null @@ -1,2508 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * Author Stefano Stabellini - * - * 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: - */ diff --git a/tools/libxl/libxl_psr.c b/tools/libxl/libxl_psr.c deleted file mode 100644 index 9ced7d1715..0000000000 --- a/tools/libxl/libxl_psr.c +++ /dev/null @@ -1,562 +0,0 @@ -/* - * Copyright (C) 2014 Intel Corporation - * Author Dongxiao Xu - * - * 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 - -#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: - */ diff --git a/tools/libxl/libxl_pvcalls.c b/tools/libxl/libxl_pvcalls.c deleted file mode 100644 index 870318e716..0000000000 --- a/tools/libxl/libxl_pvcalls.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2018 Aporeto - * Author Stefano Stabellini - * - * 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); diff --git a/tools/libxl/libxl_qmp.c b/tools/libxl/libxl_qmp.c deleted file mode 100644 index c394000ea9..0000000000 --- a/tools/libxl/libxl_qmp.c +++ /dev/null @@ -1,1934 +0,0 @@ -/* - * Copyright (C) 2011 Citrix Ltd. - * Author Anthony PERARD - * - * 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 - -#include - -#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: - */ diff --git a/tools/libxl/libxl_remus.c b/tools/libxl/libxl_remus.c deleted file mode 100644 index 6338a1bae5..0000000000 --- a/tools/libxl/libxl_remus.c +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * Yang Hongyang - * - * 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: - */ diff --git a/tools/libxl/libxl_remus_disk_drbd.c b/tools/libxl/libxl_remus_disk_drbd.c deleted file mode 100644 index d08e470bfa..0000000000 --- a/tools/libxl/libxl_remus_disk_drbd.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2014 FUJITSU LIMITED - * Author Lai Jiangshan - * - * 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, -}; diff --git a/tools/libxl/libxl_save_callout.c b/tools/libxl/libxl_save_callout.c deleted file mode 100644 index 0b11495f9b..0000000000 --- a/tools/libxl/libxl_save_callout.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * 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; ipipes[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= 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; -} diff --git a/tools/libxl/libxl_save_helper.c b/tools/libxl/libxl_save_helper.c deleted file mode 100644 index 65dff389bf..0000000000 --- a/tools/libxl/libxl_save_helper.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#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: - */ diff --git a/tools/libxl/libxl_save_msgs_gen.pl b/tools/libxl/libxl_save_msgs_gen.pl deleted file mode 100755 index 5bfbd4fd10..0000000000 --- a/tools/libxl/libxl_save_msgs_gen.pl +++ /dev/null @@ -1,396 +0,0 @@ -#!/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} .= - < -#include -#include -#include -END_BOTH - -#include "libxl_internal.h" - -END_CALLOUT - -#include -#include -#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'} .= <($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", - <(" 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", - (<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: - */ diff --git a/tools/libxl/libxl_sr_stream_format.h b/tools/libxl/libxl_sr_stream_format.h deleted file mode 100644 index 75f5190886..0000000000 --- a/tools/libxl/libxl_sr_stream_format.h +++ /dev/null @@ -1,69 +0,0 @@ -#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 - -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: - */ diff --git a/tools/libxl/libxl_stream_read.c b/tools/libxl/libxl_stream_read.c deleted file mode 100644 index 514f6d9f89..0000000000 --- a/tools/libxl/libxl_stream_read.c +++ /dev/null @@ -1,977 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_stream_write.c b/tools/libxl/libxl_stream_write.c deleted file mode 100644 index 634f3240d1..0000000000 --- a/tools/libxl/libxl_stream_write.c +++ /dev/null @@ -1,727 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_test_fdevent.c b/tools/libxl/libxl_test_fdevent.c deleted file mode 100644 index 2d875d995f..0000000000 --- a/tools/libxl/libxl_test_fdevent.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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); -} diff --git a/tools/libxl/libxl_test_fdevent.h b/tools/libxl/libxl_test_fdevent.h deleted file mode 100644 index 82a307e75b..0000000000 --- a/tools/libxl/libxl_test_fdevent.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef TEST_FDEVENT_H -#define TEST_FDEVENT_H - -#include - -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*/ diff --git a/tools/libxl/libxl_test_timedereg.c b/tools/libxl/libxl_test_timedereg.c deleted file mode 100644 index a567db6839..0000000000 --- a/tools/libxl/libxl_test_timedereg.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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 - -int libxl_test_timedereg(libxl_ctx *ctx, libxl_asyncop_how *ao_how) - LIBXL_EXTERNAL_CALLERS_ONLY; - -#endif /*TEST_TIMEDEREG_H*/ diff --git a/tools/libxl/libxl_tmem.c b/tools/libxl/libxl_tmem.c deleted file mode 100644 index a553b39738..0000000000 --- a/tools/libxl/libxl_tmem.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl deleted file mode 100644 index 9d3f05f399..0000000000 --- a/tools/libxl/libxl_types.idl +++ /dev/null @@ -1,1224 +0,0 @@ -# -*- 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) diff --git a/tools/libxl/libxl_types_internal.idl b/tools/libxl/libxl_types_internal.idl deleted file mode 100644 index 3593e21dbb..0000000000 --- a/tools/libxl/libxl_types_internal.idl +++ /dev/null @@ -1,57 +0,0 @@ -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"), - ]) diff --git a/tools/libxl/libxl_usb.c b/tools/libxl/libxl_usb.c deleted file mode 100644 index 171bb04439..0000000000 --- a/tools/libxl/libxl_usb.c +++ /dev/null @@ -1,2158 +0,0 @@ -/* - * Copyright (C) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany. - * Author Chunyan Liu - * - * 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 -#include - -#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: - */ diff --git a/tools/libxl/libxl_utils.c b/tools/libxl/libxl_utils.c deleted file mode 100644 index b039143b8a..0000000000 --- a/tools/libxl/libxl_utils.c +++ /dev/null @@ -1,1272 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Stefano Stabellini - * - * 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 - -#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: - */ diff --git a/tools/libxl/libxl_utils.h b/tools/libxl/libxl_utils.h deleted file mode 100644 index 46918aea84..0000000000 --- a/tools/libxl/libxl_utils.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Stefano Stabellini - * - * 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: - */ diff --git a/tools/libxl/libxl_uuid.c b/tools/libxl/libxl_uuid.c deleted file mode 100644 index dadb79bad8..0000000000 --- a/tools/libxl/libxl_uuid.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_uuid.h b/tools/libxl/libxl_uuid.h deleted file mode 100644 index 17c8b9736e..0000000000 --- a/tools/libxl/libxl_uuid.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 -#include - -#elif defined(__FreeBSD__) || defined(__NetBSD__) - -#include -#include -#include -#include -#include -#include - -#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: - */ diff --git a/tools/libxl/libxl_vdispl.c b/tools/libxl/libxl_vdispl.c deleted file mode 100644 index 8ddc8940e9..0000000000 --- a/tools/libxl/libxl_vdispl.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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 - -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: - */ diff --git a/tools/libxl/libxl_vkb.c b/tools/libxl/libxl_vkb.c deleted file mode 100644 index 4c44a813c1..0000000000 --- a/tools/libxl/libxl_vkb.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * 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 - -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: - */ diff --git a/tools/libxl/libxl_vnuma.c b/tools/libxl/libxl_vnuma.c deleted file mode 100644 index c2e144ceae..0000000000 --- a/tools/libxl/libxl_vnuma.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (C) 2014 Citrix Ltd. - * Author Wei Liu - * - * 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 - -#include - -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: - */ diff --git a/tools/libxl/libxl_vsnd.c b/tools/libxl/libxl_vsnd.c deleted file mode 100644 index 0bc5f6dbb1..0000000000 --- a/tools/libxl/libxl_vsnd.c +++ /dev/null @@ -1,686 +0,0 @@ -/* - * 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 - -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: - */ diff --git a/tools/libxl/libxl_vtpm.c b/tools/libxl/libxl_vtpm.c deleted file mode 100644 index dd00b267bb..0000000000 --- a/tools/libxl/libxl_vtpm.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (C) 2016 SUSE Linux GmbH - * Author Juergen Gross - * - * 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: - */ - diff --git a/tools/libxl/libxl_x86.c b/tools/libxl/libxl_x86.c deleted file mode 100644 index 7d95506e00..0000000000 --- a/tools/libxl/libxl_x86.c +++ /dev/null @@ -1,852 +0,0 @@ -#include "libxl_internal.h" -#include "libxl_arch.h" - -#include - -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: - */ diff --git a/tools/libxl/libxl_x86_acpi.c b/tools/libxl/libxl_x86_acpi.c deleted file mode 100644 index 3df86c7be5..0000000000 --- a/tools/libxl/libxl_x86_acpi.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 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 -#include -#include "libacpi/libacpi.h" - -#include - - /* 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: - */ diff --git a/tools/libxl/libxl_x86_acpi.h b/tools/libxl/libxl_x86_acpi.h deleted file mode 100644 index d404637176..0000000000 --- a/tools/libxl/libxl_x86_acpi.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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: - */ diff --git a/tools/libxl/libxl_xshelp.c b/tools/libxl/libxl_xshelp.c deleted file mode 100644 index 751cd942d9..0000000000 --- a/tools/libxl/libxl_xshelp.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Vincent Hanquez - * - * 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: - */ diff --git a/tools/libxl/osdeps.c b/tools/libxl/osdeps.c deleted file mode 100644 index 0e0b447117..0000000000 --- a/tools/libxl/osdeps.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2009 Citrix Ltd. - * Author Stefano Stabellini - * - * 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 -#include -#include -#include -#include - -#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: - */ diff --git a/tools/libxl/test_common.c b/tools/libxl/test_common.c deleted file mode 100644 index c6bbbabf2d..0000000000 --- a/tools/libxl/test_common.c +++ /dev/null @@ -1,57 +0,0 @@ -#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); -} diff --git a/tools/libxl/test_common.h b/tools/libxl/test_common.h deleted file mode 100644 index 10c71669c8..0000000000 --- a/tools/libxl/test_common.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef TEST_COMMON_H -#define TEST_COMMON_H - -#include "libxl.h" - -#include -#include -#include -#include -#include -#include - -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*/ diff --git a/tools/libxl/test_fdderegrace.c b/tools/libxl/test_fdderegrace.c deleted file mode 100644 index f57965f709..0000000000 --- a/tools/libxl/test_fdderegrace.c +++ /dev/null @@ -1,56 +0,0 @@ -#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 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)