/* * xen_hypervisor.c: direct access to Xen hypervisor level * * Copyright (C) 2005-2014 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Daniel Veillard */ #include #include #include /* required for uint8_t, uint32_t, etc ... */ #include #include #include #include #include #include #include #include #include #include #ifdef __sun # include # include # ifndef PRIV_XVM_CONTROL # define PRIV_XVM_CONTROL ((const char *)"xvm_control") # endif #endif /* __sun */ /* required for dom0_getdomaininfo_t */ #include #include #ifdef HAVE_XEN_LINUX_PRIVCMD_H # include #else # ifdef HAVE_XEN_SYS_PRIVCMD_H # include # endif #endif /* required for shutdown flags */ #include #include "virerror.h" #include "virlog.h" #include "datatypes.h" #include "driver.h" #include "xen_driver.h" #include "xen_hypervisor.h" #include "xs_internal.h" #include "virstats.h" #include "block_stats.h" #include "xend_internal.h" #include "virbuffer.h" #include "capabilities.h" #include "viralloc.h" #include "virthread.h" #include "virfile.h" #include "virnodesuspend.h" #include "virtypedparam.h" #include "virendian.h" #include "virstring.h" #define VIR_FROM_THIS VIR_FROM_XEN VIR_LOG_INIT("xen.xen_hypervisor"); /* * so far there is 2 versions of the structures usable for doing * hypervisor calls. */ /* the old one */ typedef struct v0_hypercall_struct { unsigned long op; unsigned long arg[5]; } v0_hypercall_t; #ifdef __linux__ # define XEN_V0_IOCTL_HYPERCALL_CMD \ _IOC(_IOC_NONE, 'P', 0, sizeof(v0_hypercall_t)) /* the new one */ typedef struct v1_hypercall_struct { uint64_t op; uint64_t arg[5]; } v1_hypercall_t; # define XEN_V1_IOCTL_HYPERCALL_CMD \ _IOC(_IOC_NONE, 'P', 0, sizeof(v1_hypercall_t)) typedef v1_hypercall_t hypercall_t; #elif defined(__sun) typedef privcmd_hypercall_t hypercall_t; #else # error "unsupported platform" #endif #ifndef __HYPERVISOR_sysctl # define __HYPERVISOR_sysctl 35 #endif #ifndef __HYPERVISOR_domctl # define __HYPERVISOR_domctl 36 #endif #ifdef WITH_RHEL5_API # define SYS_IFACE_MIN_VERS_NUMA 3 #else # define SYS_IFACE_MIN_VERS_NUMA 4 #endif static int xen_ioctl_hypercall_cmd; static struct xenHypervisorVersions hv_versions = { .hv = 0, .hypervisor = 2, .sys_interface = -1, .dom_interface = -1, }; static int kb_per_pages; /* Regular expressions used by xenHypervisorGetCapabilities, and * compiled once by xenHypervisorInit. Note that these are POSIX.2 * extended regular expressions (regex(7)). */ static const char *flags_hvm_re = "^flags[[:blank:]]+:.* (vmx|svm)[[:space:]]"; static regex_t flags_hvm_rec; static const char *flags_pae_re = "^flags[[:blank:]]+:.* pae[[:space:]]"; static regex_t flags_pae_rec; static const char *xen_cap_re = "(xen|hvm)-[[:digit:]]+\\.[[:digit:]]+-(x86_32|x86_64|ia64|powerpc64)(p|be)?"; static regex_t xen_cap_rec; /* * The content of the structures for a getdomaininfolist system hypercall */ #ifndef DOMFLAGS_DYING # define DOMFLAGS_DYING (1<<0) /* Domain is scheduled to die. */ # define DOMFLAGS_HVM (1<<1) /* Domain is HVM */ # define DOMFLAGS_SHUTDOWN (1<<2) /* The guest OS has shut down. */ # define DOMFLAGS_PAUSED (1<<3) /* Currently paused by control software. */ # define DOMFLAGS_BLOCKED (1<<4) /* Currently blocked pending an event. */ # define DOMFLAGS_RUNNING (1<<5) /* Domain is currently running. */ # define DOMFLAGS_CPUMASK 255 /* CPU to which this domain is bound. */ # define DOMFLAGS_CPUSHIFT 8 # define DOMFLAGS_SHUTDOWNMASK 255 /* DOMFLAGS_SHUTDOWN guest-supplied code. */ # define DOMFLAGS_SHUTDOWNSHIFT 16 #endif /* * These flags explain why a system is in the state of "shutdown". Normally, * They are defined in xen/sched.h */ #ifndef SHUTDOWN_poweroff # define SHUTDOWN_poweroff 0 /* Domain exited normally. Clean up and kill. */ # define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */ # define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */ # define SHUTDOWN_crash 3 /* Tell controller we've crashed. */ #endif #define XEN_V0_OP_GETDOMAININFOLIST 38 #define XEN_V1_OP_GETDOMAININFOLIST 38 #define XEN_V2_OP_GETDOMAININFOLIST 6 struct xen_v0_getdomaininfo { domid_t domain; /* the domain number */ uint32_t flags; /* flags, see before */ uint64_t tot_pages; /* total number of pages used */ uint64_t max_pages; /* maximum number of pages allowed */ unsigned long shared_info_frame; /* MFN of shared_info struct */ uint64_t cpu_time; /* CPU time used */ uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ uint32_t ssidref; xen_domain_handle_t handle; }; typedef struct xen_v0_getdomaininfo xen_v0_getdomaininfo; struct xen_v2_getdomaininfo { domid_t domain; /* the domain number */ uint32_t flags; /* flags, see before */ uint64_t tot_pages; /* total number of pages used */ uint64_t max_pages; /* maximum number of pages allowed */ uint64_t shared_info_frame; /* MFN of shared_info struct */ uint64_t cpu_time; /* CPU time used */ uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ uint32_t ssidref; xen_domain_handle_t handle; }; typedef struct xen_v2_getdomaininfo xen_v2_getdomaininfo; /* As of Hypervisor Call v2, DomCtl v5 we are now 8-byte aligned even on 32-bit archs when dealing with uint64_t */ #define ALIGN_64 __attribute__((aligned(8))) struct xen_v2d5_getdomaininfo { domid_t domain; /* the domain number */ uint32_t flags; /* flags, see before */ uint64_t tot_pages ALIGN_64; /* total number of pages used */ uint64_t max_pages ALIGN_64; /* maximum number of pages allowed */ uint64_t shared_info_frame ALIGN_64; /* MFN of shared_info struct */ uint64_t cpu_time ALIGN_64; /* CPU time used */ uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ uint32_t ssidref; xen_domain_handle_t handle; }; typedef struct xen_v2d5_getdomaininfo xen_v2d5_getdomaininfo; struct xen_v2d6_getdomaininfo { domid_t domain; /* the domain number */ uint32_t flags; /* flags, see before */ uint64_t tot_pages ALIGN_64; /* total number of pages used */ uint64_t max_pages ALIGN_64; /* maximum number of pages allowed */ uint64_t shr_pages ALIGN_64; /* number of shared pages */ uint64_t shared_info_frame ALIGN_64; /* MFN of shared_info struct */ uint64_t cpu_time ALIGN_64; /* CPU time used */ uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ uint32_t ssidref; xen_domain_handle_t handle; }; typedef struct xen_v2d6_getdomaininfo xen_v2d6_getdomaininfo; struct xen_v2d7_getdomaininfo { domid_t domain; /* the domain number */ uint32_t flags; /* flags, see before */ uint64_t tot_pages ALIGN_64; /* total number of pages used */ uint64_t max_pages ALIGN_64; /* maximum number of pages allowed */ uint64_t shr_pages ALIGN_64; /* number of shared pages */ uint64_t shared_info_frame ALIGN_64; /* MFN of shared_info struct */ uint64_t cpu_time ALIGN_64; /* CPU time used */ uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ uint32_t ssidref; xen_domain_handle_t handle; uint32_t cpupool; }; typedef struct xen_v2d7_getdomaininfo xen_v2d7_getdomaininfo; struct xen_v2d8_getdomaininfo { domid_t domain; /* the domain number */ uint32_t flags; /* flags, see before */ uint64_t tot_pages ALIGN_64; /* total number of pages used */ uint64_t max_pages ALIGN_64; /* maximum number of pages allowed */ uint64_t shr_pages ALIGN_64; /* number of shared pages */ uint64_t paged_pages ALIGN_64; /* number of paged pages */ uint64_t shared_info_frame ALIGN_64; /* MFN of shared_info struct */ uint64_t cpu_time ALIGN_64; /* CPU time used */ uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ uint32_t ssidref; xen_domain_handle_t handle; uint32_t cpupool; }; typedef struct xen_v2d8_getdomaininfo xen_v2d8_getdomaininfo; struct xen_v2d9_getdomaininfo { domid_t domain; /* the domain number */ uint32_t flags; /* flags, see before */ uint64_t tot_pages ALIGN_64; /* total number of pages used */ uint64_t max_pages ALIGN_64; /* maximum number of pages allowed */ uint64_t outstanding_pages ALIGN_64; uint64_t shr_pages ALIGN_64; /* number of shared pages */ uint64_t paged_pages ALIGN_64; /* number of paged pages */ uint64_t shared_info_frame ALIGN_64; /* MFN of shared_info struct */ uint64_t cpu_time ALIGN_64; /* CPU time used */ uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ uint32_t ssidref; xen_domain_handle_t handle; uint32_t cpupool; }; typedef struct xen_v2d9_getdomaininfo xen_v2d9_getdomaininfo; union xen_getdomaininfo { struct xen_v0_getdomaininfo v0; struct xen_v2_getdomaininfo v2; struct xen_v2d5_getdomaininfo v2d5; struct xen_v2d6_getdomaininfo v2d6; struct xen_v2d7_getdomaininfo v2d7; struct xen_v2d8_getdomaininfo v2d8; struct xen_v2d9_getdomaininfo v2d9; }; typedef union xen_getdomaininfo xen_getdomaininfo; union xen_getdomaininfolist { struct xen_v0_getdomaininfo *v0; struct xen_v2_getdomaininfo *v2; struct xen_v2d5_getdomaininfo *v2d5; struct xen_v2d6_getdomaininfo *v2d6; struct xen_v2d7_getdomaininfo *v2d7; struct xen_v2d8_getdomaininfo *v2d8; struct xen_v2d8_getdomaininfo *v2d9; }; typedef union xen_getdomaininfolist xen_getdomaininfolist; struct xen_v2_getschedulerid { uint32_t sched_id; /* Get Scheduler ID from Xen */ }; typedef struct xen_v2_getschedulerid xen_v2_getschedulerid; union xen_getschedulerid { struct xen_v2_getschedulerid *v2; }; typedef union xen_getschedulerid xen_getschedulerid; struct xen_v2s4_availheap { uint32_t min_bitwidth; /* Smallest address width (zero if don't care). */ uint32_t max_bitwidth; /* Largest address width (zero if don't care). */ int32_t node; /* NUMA node (-1 for sum across all nodes). */ uint64_t avail_bytes; /* Bytes available in the specified region. */ }; typedef struct xen_v2s4_availheap xen_v2s4_availheap; struct xen_v2s5_availheap { uint32_t min_bitwidth; /* Smallest address width (zero if don't care). */ uint32_t max_bitwidth; /* Largest address width (zero if don't care). */ int32_t node; /* NUMA node (-1 for sum across all nodes). */ uint64_t avail_bytes ALIGN_64; /* Bytes available in the specified region. */ }; typedef struct xen_v2s5_availheap xen_v2s5_availheap; #define XEN_GETDOMAININFOLIST_ALLOC(domlist, size) \ (hv_versions.hypervisor < 2 ? \ (VIR_ALLOC_N(domlist.v0, (size)) == 0) : \ (hv_versions.dom_interface >= 9 ? \ (VIR_ALLOC_N(domlist.v2d9, (size)) == 0) : \ (hv_versions.dom_interface == 8 ? \ (VIR_ALLOC_N(domlist.v2d8, (size)) == 0) : \ (hv_versions.dom_interface == 7 ? \ (VIR_ALLOC_N(domlist.v2d7, (size)) == 0) : \ (hv_versions.dom_interface == 6 ? \ (VIR_ALLOC_N(domlist.v2d6, (size)) == 0) : \ (hv_versions.dom_interface == 5 ? \ (VIR_ALLOC_N(domlist.v2d5, (size)) == 0) : \ (VIR_ALLOC_N(domlist.v2, (size)) == 0))))))) #define XEN_GETDOMAININFOLIST_FREE(domlist) \ (hv_versions.hypervisor < 2 ? \ VIR_FREE(domlist.v0) : \ (hv_versions.dom_interface >= 9 ? \ VIR_FREE(domlist.v2d9) : \ (hv_versions.dom_interface == 8 ? \ VIR_FREE(domlist.v2d8) : \ (hv_versions.dom_interface == 7 ? \ VIR_FREE(domlist.v2d7) : \ (hv_versions.dom_interface == 6 ? \ VIR_FREE(domlist.v2d6) : \ (hv_versions.dom_interface == 5 ? \ VIR_FREE(domlist.v2d5) : \ VIR_FREE(domlist.v2))))))) #define XEN_GETDOMAININFOLIST_CLEAR(domlist, size) \ (hv_versions.hypervisor < 2 ? \ memset(domlist.v0, 0, sizeof(*domlist.v0) * size) : \ (hv_versions.dom_interface >= 9 ? \ memset(domlist.v2d9, 0, sizeof(*domlist.v2d9) * size) : \ (hv_versions.dom_interface == 8 ? \ memset(domlist.v2d8, 0, sizeof(*domlist.v2d8) * size) : \ (hv_versions.dom_interface == 7 ? \ memset(domlist.v2d7, 0, sizeof(*domlist.v2d7) * size) : \ (hv_versions.dom_interface == 6 ? \ memset(domlist.v2d6, 0, sizeof(*domlist.v2d6) * size) : \ (hv_versions.dom_interface == 5 ? \ memset(domlist.v2d5, 0, sizeof(*domlist.v2d5) * size) : \ memset(domlist.v2, 0, sizeof(*domlist.v2) * size))))))) #define XEN_GETDOMAININFOLIST_DOMAIN(domlist, n) \ (hv_versions.hypervisor < 2 ? \ domlist.v0[n].domain : \ (hv_versions.dom_interface >= 9 ? \ domlist.v2d9[n].domain : \ (hv_versions.dom_interface == 8 ? \ domlist.v2d8[n].domain : \ (hv_versions.dom_interface == 7 ? \ domlist.v2d7[n].domain : \ (hv_versions.dom_interface == 6 ? \ domlist.v2d6[n].domain : \ (hv_versions.dom_interface == 5 ? \ domlist.v2d5[n].domain : \ domlist.v2[n].domain)))))) #define XEN_GETDOMAININFOLIST_UUID(domlist, n) \ (hv_versions.hypervisor < 2 ? \ domlist.v0[n].handle : \ (hv_versions.dom_interface >= 9 ? \ domlist.v2d9[n].handle : \ (hv_versions.dom_interface == 8 ? \ domlist.v2d8[n].handle : \ (hv_versions.dom_interface == 7 ? \ domlist.v2d7[n].handle : \ (hv_versions.dom_interface == 6 ? \ domlist.v2d6[n].handle : \ (hv_versions.dom_interface == 5 ? \ domlist.v2d5[n].handle : \ domlist.v2[n].handle)))))) #define XEN_GETDOMAININFOLIST_DATA(domlist) \ (hv_versions.hypervisor < 2 ? \ (void*)(domlist->v0) : \ (hv_versions.dom_interface >= 9 ? \ (void*)(domlist->v2d9) : \ (hv_versions.dom_interface == 8 ? \ (void*)(domlist->v2d8) : \ (hv_versions.dom_interface == 7 ? \ (void*)(domlist->v2d7) : \ (hv_versions.dom_interface == 6 ? \ (void*)(domlist->v2d6) : \ (hv_versions.dom_interface == 5 ? \ (void*)(domlist->v2d5) : \ (void*)(domlist->v2))))))) #define XEN_GETDOMAININFO_SIZE \ (hv_versions.hypervisor < 2 ? \ sizeof(xen_v0_getdomaininfo) : \ (hv_versions.dom_interface >= 9 ? \ sizeof(xen_v2d9_getdomaininfo) : \ (hv_versions.dom_interface == 8 ? \ sizeof(xen_v2d8_getdomaininfo) : \ (hv_versions.dom_interface == 7 ? \ sizeof(xen_v2d7_getdomaininfo) : \ (hv_versions.dom_interface == 6 ? \ sizeof(xen_v2d6_getdomaininfo) : \ (hv_versions.dom_interface == 5 ? \ sizeof(xen_v2d5_getdomaininfo) : \ sizeof(xen_v2_getdomaininfo))))))) #define XEN_GETDOMAININFO_CLEAR(dominfo) \ (hv_versions.hypervisor < 2 ? \ memset(&(dominfo.v0), 0, sizeof(xen_v0_getdomaininfo)) : \ (hv_versions.dom_interface >= 9 ? \ memset(&(dominfo.v2d9), 0, sizeof(xen_v2d9_getdomaininfo)) : \ (hv_versions.dom_interface == 8 ? \ memset(&(dominfo.v2d8), 0, sizeof(xen_v2d8_getdomaininfo)) : \ (hv_versions.dom_interface == 7 ? \ memset(&(dominfo.v2d7), 0, sizeof(xen_v2d7_getdomaininfo)) : \ (hv_versions.dom_interface == 6 ? \ memset(&(dominfo.v2d6), 0, sizeof(xen_v2d6_getdomaininfo)) : \ (hv_versions.dom_interface == 5 ? \ memset(&(dominfo.v2d5), 0, sizeof(xen_v2d5_getdomaininfo)) : \ memset(&(dominfo.v2), 0, sizeof(xen_v2_getdomaininfo)))))))) #define XEN_GETDOMAININFO_DOMAIN(dominfo) \ (hv_versions.hypervisor < 2 ? \ dominfo.v0.domain : \ (hv_versions.dom_interface >= 9 ? \ dominfo.v2d9.domain : \ (hv_versions.dom_interface == 8 ? \ dominfo.v2d8.domain : \ (hv_versions.dom_interface == 7 ? \ dominfo.v2d7.domain : \ (hv_versions.dom_interface == 6 ? \ dominfo.v2d6.domain : \ (hv_versions.dom_interface == 5 ? \ dominfo.v2d5.domain : \ dominfo.v2.domain)))))) #define XEN_GETDOMAININFO_CPUTIME(dominfo) \ (hv_versions.hypervisor < 2 ? \ dominfo.v0.cpu_time : \ (hv_versions.dom_interface >= 9 ? \ dominfo.v2d9.cpu_time : \ (hv_versions.dom_interface == 8 ? \ dominfo.v2d8.cpu_time : \ (hv_versions.dom_interface == 7 ? \ dominfo.v2d7.cpu_time : \ (hv_versions.dom_interface == 6 ? \ dominfo.v2d6.cpu_time : \ (hv_versions.dom_interface == 5 ? \ dominfo.v2d5.cpu_time : \ dominfo.v2.cpu_time)))))) #define XEN_GETDOMAININFO_CPUCOUNT(dominfo) \ (hv_versions.hypervisor < 2 ? \ dominfo.v0.nr_online_vcpus : \ (hv_versions.dom_interface >= 9 ? \ dominfo.v2d9.nr_online_vcpus : \ (hv_versions.dom_interface == 8 ? \ dominfo.v2d8.nr_online_vcpus : \ (hv_versions.dom_interface == 7 ? \ dominfo.v2d7.nr_online_vcpus : \ (hv_versions.dom_interface == 6 ? \ dominfo.v2d6.nr_online_vcpus : \ (hv_versions.dom_interface == 5 ? \ dominfo.v2d5.nr_online_vcpus : \ dominfo.v2.nr_online_vcpus)))))) #define XEN_GETDOMAININFO_MAXCPUID(dominfo) \ (hv_versions.hypervisor < 2 ? \ dominfo.v0.max_vcpu_id : \ (hv_versions.dom_interface >= 9 ? \ dominfo.v2d9.max_vcpu_id : \ (hv_versions.dom_interface == 8 ? \ dominfo.v2d8.max_vcpu_id : \ (hv_versions.dom_interface == 7 ? \ dominfo.v2d7.max_vcpu_id : \ (hv_versions.dom_interface == 6 ? \ dominfo.v2d6.max_vcpu_id : \ (hv_versions.dom_interface == 5 ? \ dominfo.v2d5.max_vcpu_id : \ dominfo.v2.max_vcpu_id)))))) #define XEN_GETDOMAININFO_FLAGS(dominfo) \ (hv_versions.hypervisor < 2 ? \ dominfo.v0.flags : \ (hv_versions.dom_interface >= 9 ? \ dominfo.v2d9.flags : \ (hv_versions.dom_interface == 8 ? \ dominfo.v2d8.flags : \ (hv_versions.dom_interface == 7 ? \ dominfo.v2d7.flags : \ (hv_versions.dom_interface == 6 ? \ dominfo.v2d6.flags : \ (hv_versions.dom_interface == 5 ? \ dominfo.v2d5.flags : \ dominfo.v2.flags)))))) #define XEN_GETDOMAININFO_TOT_PAGES(dominfo) \ (hv_versions.hypervisor < 2 ? \ dominfo.v0.tot_pages : \ (hv_versions.dom_interface >= 9 ? \ dominfo.v2d9.tot_pages : \ (hv_versions.dom_interface == 8 ? \ dominfo.v2d8.tot_pages : \ (hv_versions.dom_interface == 7 ? \ dominfo.v2d7.tot_pages : \ (hv_versions.dom_interface == 6 ? \ dominfo.v2d6.tot_pages : \ (hv_versions.dom_interface == 5 ? \ dominfo.v2d5.tot_pages : \ dominfo.v2.tot_pages)))))) #define XEN_GETDOMAININFO_MAX_PAGES(dominfo) \ (hv_versions.hypervisor < 2 ? \ dominfo.v0.max_pages : \ (hv_versions.dom_interface >= 9 ? \ dominfo.v2d9.max_pages : \ (hv_versions.dom_interface == 8 ? \ dominfo.v2d8.max_pages : \ (hv_versions.dom_interface == 7 ? \ dominfo.v2d7.max_pages : \ (hv_versions.dom_interface == 6 ? \ dominfo.v2d6.max_pages : \ (hv_versions.dom_interface == 5 ? \ dominfo.v2d5.max_pages : \ dominfo.v2.max_pages)))))) #define XEN_GETDOMAININFO_UUID(dominfo) \ (hv_versions.hypervisor < 2 ? \ dominfo.v0.handle : \ (hv_versions.dom_interface >= 9 ? \ dominfo.v2d9.handle : \ (hv_versions.dom_interface == 8 ? \ dominfo.v2d8.handle : \ (hv_versions.dom_interface == 7 ? \ dominfo.v2d7.handle : \ (hv_versions.dom_interface == 6 ? \ dominfo.v2d6.handle : \ (hv_versions.dom_interface == 5 ? \ dominfo.v2d5.handle : \ dominfo.v2.handle)))))) static int lock_pages(void *addr, size_t len) { #ifdef __linux__ if (mlock(addr, len) < 0) { virReportSystemError(errno, _("Unable to lock %zu bytes of memory"), len); return -1; } return 0; #elif defined(__sun) return 0; #endif } static int unlock_pages(void *addr, size_t len) { #ifdef __linux__ if (munlock(addr, len) < 0) { virReportSystemError(errno, _("Unable to unlock %zu bytes of memory"), len); return -1; } return 0; #elif defined(__sun) return 0; #endif } struct xen_v0_getdomaininfolistop { domid_t first_domain; uint32_t max_domains; struct xen_v0_getdomaininfo *buffer; uint32_t num_domains; }; typedef struct xen_v0_getdomaininfolistop xen_v0_getdomaininfolistop; struct xen_v2_getdomaininfolistop { domid_t first_domain; uint32_t max_domains; struct xen_v2_getdomaininfo *buffer; uint32_t num_domains; }; typedef struct xen_v2_getdomaininfolistop xen_v2_getdomaininfolistop; /* As of HV version 2, sysctl version 3 the *buffer pointer is 64-bit aligned */ struct xen_v2s3_getdomaininfolistop { domid_t first_domain; uint32_t max_domains; #ifdef __BIG_ENDIAN__ struct { int __pad[(sizeof(long long) - sizeof(struct xen_v2d5_getdomaininfo *)) / sizeof(int)]; struct xen_v2d5_getdomaininfo *v; } buffer; #else union { struct xen_v2d5_getdomaininfo *v; uint64_t pad ALIGN_64; } buffer; #endif uint32_t num_domains; }; typedef struct xen_v2s3_getdomaininfolistop xen_v2s3_getdomaininfolistop; struct xen_v0_domainop { domid_t domain; }; typedef struct xen_v0_domainop xen_v0_domainop; /* * The information for a pausedomain system hypercall */ #define XEN_V0_OP_PAUSEDOMAIN 10 #define XEN_V1_OP_PAUSEDOMAIN 10 #define XEN_V2_OP_PAUSEDOMAIN 3 /* * The information for an unpausedomain system hypercall */ #define XEN_V0_OP_UNPAUSEDOMAIN 11 #define XEN_V1_OP_UNPAUSEDOMAIN 11 #define XEN_V2_OP_UNPAUSEDOMAIN 4 /* * The information for a setmaxmem system hypercall */ #define XEN_V0_OP_SETMAXMEM 28 #define XEN_V1_OP_SETMAXMEM 28 #define XEN_V2_OP_SETMAXMEM 11 struct xen_v0_setmaxmem { domid_t domain; uint64_t maxmem; }; typedef struct xen_v0_setmaxmem xen_v0_setmaxmem; typedef struct xen_v0_setmaxmem xen_v1_setmaxmem; struct xen_v2_setmaxmem { uint64_t maxmem; }; typedef struct xen_v2_setmaxmem xen_v2_setmaxmem; struct xen_v2d5_setmaxmem { uint64_t maxmem ALIGN_64; }; typedef struct xen_v2d5_setmaxmem xen_v2d5_setmaxmem; /* * The information for a setvcpumap system hypercall * Note that between 1 and 2 the limitation to 64 physical CPU was lifted * hence the difference in structures */ #define XEN_V0_OP_SETVCPUMAP 20 #define XEN_V1_OP_SETVCPUMAP 20 #define XEN_V2_OP_SETVCPUMAP 9 struct xen_v0_setvcpumap { domid_t domain; uint32_t vcpu; cpumap_t cpumap; }; typedef struct xen_v0_setvcpumap xen_v0_setvcpumap; typedef struct xen_v0_setvcpumap xen_v1_setvcpumap; struct xen_v2_cpumap { uint8_t *bitmap; uint32_t nr_cpus; }; struct xen_v2_setvcpumap { uint32_t vcpu; struct xen_v2_cpumap cpumap; }; typedef struct xen_v2_setvcpumap xen_v2_setvcpumap; /* HV version 2, Dom version 5 requires 64-bit alignment */ struct xen_v2d5_cpumap { #ifdef __BIG_ENDIAN__ struct { int __pad[(sizeof(long long) - sizeof(uint8_t *)) / sizeof(int)]; uint8_t *v; } bitmap; #else union { uint8_t *v; uint64_t pad ALIGN_64; } bitmap; #endif uint32_t nr_cpus; }; struct xen_v2d5_setvcpumap { uint32_t vcpu; struct xen_v2d5_cpumap cpumap; }; typedef struct xen_v2d5_setvcpumap xen_v2d5_setvcpumap; /* * The information for a vcpuinfo system hypercall */ #define XEN_V0_OP_GETVCPUINFO 43 #define XEN_V1_OP_GETVCPUINFO 43 #define XEN_V2_OP_GETVCPUINFO 14 struct xen_v0_vcpuinfo { domid_t domain; /* owner's domain */ uint32_t vcpu; /* the vcpu number */ uint8_t online; /* seen as on line */ uint8_t blocked; /* blocked on event */ uint8_t running; /* scheduled on CPU */ uint64_t cpu_time; /* nanosecond of CPU used */ uint32_t cpu; /* current mapping */ cpumap_t cpumap; /* deprecated in V2 */ }; typedef struct xen_v0_vcpuinfo xen_v0_vcpuinfo; typedef struct xen_v0_vcpuinfo xen_v1_vcpuinfo; struct xen_v2_vcpuinfo { uint32_t vcpu; /* the vcpu number */ uint8_t online; /* seen as on line */ uint8_t blocked; /* blocked on event */ uint8_t running; /* scheduled on CPU */ uint64_t cpu_time; /* nanosecond of CPU used */ uint32_t cpu; /* current mapping */ }; typedef struct xen_v2_vcpuinfo xen_v2_vcpuinfo; struct xen_v2d5_vcpuinfo { uint32_t vcpu; /* the vcpu number */ uint8_t online; /* seen as on line */ uint8_t blocked; /* blocked on event */ uint8_t running; /* scheduled on CPU */ uint64_t cpu_time ALIGN_64; /* nanosecond of CPU used */ uint32_t cpu; /* current mapping */ }; typedef struct xen_v2d5_vcpuinfo xen_v2d5_vcpuinfo; /* * from V2 the pinning of a vcpu is read with a separate call */ #define XEN_V2_OP_GETVCPUMAP 25 typedef struct xen_v2_setvcpumap xen_v2_getvcpumap; typedef struct xen_v2d5_setvcpumap xen_v2d5_getvcpumap; /* * from V2 we get the scheduler information */ #define XEN_V2_OP_GETSCHEDULERID 4 /* * from V2 we get the available heap information */ #define XEN_V2_OP_GETAVAILHEAP 9 /* * from V2 we get the scheduler parameter */ #define XEN_V2_OP_SCHEDULER 16 /* Scheduler types. */ #define XEN_SCHEDULER_SEDF 4 #define XEN_SCHEDULER_CREDIT 5 /* get/set scheduler parameters */ #define XEN_DOMCTL_SCHEDOP_putinfo 0 #define XEN_DOMCTL_SCHEDOP_getinfo 1 struct xen_v2_setschedinfo { uint32_t sched_id; uint32_t cmd; union { struct xen_domctl_sched_sedf { uint64_t period ALIGN_64; uint64_t slice ALIGN_64; uint64_t latency ALIGN_64; uint32_t extratime; uint32_t weight; } sedf; struct xen_domctl_sched_credit { uint16_t weight; uint16_t cap; } credit; } u; }; typedef struct xen_v2_setschedinfo xen_v2_setschedinfo; typedef struct xen_v2_setschedinfo xen_v2_getschedinfo; /* * The hypercall operation structures also have changed on * changeset 86d26e6ec89b */ /* the old structure */ struct xen_op_v0 { uint32_t cmd; uint32_t interface_version; union { xen_v0_getdomaininfolistop getdomaininfolist; xen_v0_domainop domain; xen_v0_setmaxmem setmaxmem; xen_v0_setvcpumap setvcpumap; xen_v0_vcpuinfo getvcpuinfo; uint8_t padding[128]; } u; }; typedef struct xen_op_v0 xen_op_v0; typedef struct xen_op_v0 xen_op_v1; /* the new structure for systems operations */ struct xen_op_v2_sys { uint32_t cmd; uint32_t interface_version; union { xen_v2_getdomaininfolistop getdomaininfolist; xen_v2s3_getdomaininfolistop getdomaininfolists3; xen_v2_getschedulerid getschedulerid; xen_v2s4_availheap availheap; xen_v2s5_availheap availheap5; uint8_t padding[128]; } u; }; typedef struct xen_op_v2_sys xen_op_v2_sys; /* the new structure for domains operation */ struct xen_op_v2_dom { uint32_t cmd; uint32_t interface_version; domid_t domain; union { xen_v2_setmaxmem setmaxmem; xen_v2d5_setmaxmem setmaxmemd5; xen_v2_setvcpumap setvcpumap; xen_v2d5_setvcpumap setvcpumapd5; xen_v2_vcpuinfo getvcpuinfo; xen_v2d5_vcpuinfo getvcpuinfod5; xen_v2_getvcpumap getvcpumap; xen_v2d5_getvcpumap getvcpumapd5; xen_v2_setschedinfo setschedinfo; xen_v2_getschedinfo getschedinfo; uint8_t padding[128]; } u; }; typedef struct xen_op_v2_dom xen_op_v2_dom; #ifdef __linux__ # define XEN_HYPERVISOR_SOCKET "/proc/xen/privcmd" # define HYPERVISOR_CAPABILITIES "/sys/hypervisor/properties/capabilities" #elif defined(__sun) # define XEN_HYPERVISOR_SOCKET "/dev/xen/privcmd" #else # error "unsupported platform" #endif /** * xenHypervisorDoV0Op: * @handle: the handle to the Xen hypervisor * @op: pointer to the hypervisor operation structure * * Do a hypervisor operation though the old interface, * this leads to a hypervisor call through ioctl. * * Returns 0 in case of success and -1 in case of error. */ static int xenHypervisorDoV0Op(int handle, xen_op_v0 * op) { int ret; v0_hypercall_t hc; memset(&hc, 0, sizeof(hc)); op->interface_version = hv_versions.hv << 8; hc.op = __HYPERVISOR_dom0_op; hc.arg[0] = (unsigned long) op; if (lock_pages(op, sizeof(dom0_op_t)) < 0) return -1; ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); if (ret < 0) { virReportSystemError(errno, _("Unable to issue hypervisor ioctl %d"), xen_ioctl_hypercall_cmd); } if (unlock_pages(op, sizeof(dom0_op_t)) < 0) ret = -1; if (ret < 0) return -1; return 0; } /** * xenHypervisorDoV1Op: * @handle: the handle to the Xen hypervisor * @op: pointer to the hypervisor operation structure * * Do a hypervisor v1 operation, this leads to a hypervisor call through * ioctl. * * Returns 0 in case of success and -1 in case of error. */ static int xenHypervisorDoV1Op(int handle, xen_op_v1* op) { int ret; hypercall_t hc; memset(&hc, 0, sizeof(hc)); op->interface_version = DOM0_INTERFACE_VERSION; hc.op = __HYPERVISOR_dom0_op; hc.arg[0] = (unsigned long) op; if (lock_pages(op, sizeof(dom0_op_t)) < 0) return -1; ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); if (ret < 0) { virReportSystemError(errno, _("Unable to issue hypervisor ioctl %d"), xen_ioctl_hypercall_cmd); } if (unlock_pages(op, sizeof(dom0_op_t)) < 0) ret = -1; if (ret < 0) return -1; return 0; } /** * xenHypervisorDoV2Sys: * @handle: the handle to the Xen hypervisor * @op: pointer to the hypervisor operation structure * * Do a hypervisor v2 system operation, this leads to a hypervisor * call through ioctl. * * Returns 0 in case of success and -1 in case of error. */ static int xenHypervisorDoV2Sys(int handle, xen_op_v2_sys* op) { int ret; hypercall_t hc; memset(&hc, 0, sizeof(hc)); op->interface_version = hv_versions.sys_interface; hc.op = __HYPERVISOR_sysctl; hc.arg[0] = (unsigned long) op; if (lock_pages(op, sizeof(dom0_op_t)) < 0) return -1; ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); if (ret < 0) { virReportSystemError(errno, _("Unable to issue hypervisor ioctl %d"), xen_ioctl_hypercall_cmd); } if (unlock_pages(op, sizeof(dom0_op_t)) < 0) ret = -1; if (ret < 0) return -1; return 0; } /** * xenHypervisorDoV2Dom: * @handle: the handle to the Xen hypervisor * @op: pointer to the hypervisor domain operation structure * * Do a hypervisor v2 domain operation, this leads to a hypervisor * call through ioctl. * * Returns 0 in case of success and -1 in case of error. */ static int xenHypervisorDoV2Dom(int handle, xen_op_v2_dom* op) { int ret; hypercall_t hc; memset(&hc, 0, sizeof(hc)); op->interface_version = hv_versions.dom_interface; hc.op = __HYPERVISOR_domctl; hc.arg[0] = (unsigned long) op; if (lock_pages(op, sizeof(dom0_op_t)) < 0) return -1; ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); if (ret < 0) { virReportSystemError(errno, _("Unable to issue hypervisor ioctl %d"), xen_ioctl_hypercall_cmd); } if (unlock_pages(op, sizeof(dom0_op_t)) < 0) ret = -1; if (ret < 0) return -1; return 0; } /** * virXen_getdomaininfolist: * @handle: the hypervisor handle * @first_domain: first domain in the range * @maxids: maximum number of domains to list * @dominfos: output structures * * Do a low level hypercall to list existing domains information * * Returns the number of domains or -1 in case of failure */ static int virXen_getdomaininfolist(int handle, int first_domain, int maxids, xen_getdomaininfolist *dominfos) { int ret = -1; if (lock_pages(XEN_GETDOMAININFOLIST_DATA(dominfos), XEN_GETDOMAININFO_SIZE * maxids) < 0) return -1; if (hv_versions.hypervisor > 1) { xen_op_v2_sys op; memset(&op, 0, sizeof(op)); op.cmd = XEN_V2_OP_GETDOMAININFOLIST; if (hv_versions.sys_interface < 3) { op.u.getdomaininfolist.first_domain = (domid_t) first_domain; op.u.getdomaininfolist.max_domains = maxids; op.u.getdomaininfolist.buffer = dominfos->v2; op.u.getdomaininfolist.num_domains = maxids; } else { op.u.getdomaininfolists3.first_domain = (domid_t) first_domain; op.u.getdomaininfolists3.max_domains = maxids; op.u.getdomaininfolists3.buffer.v = dominfos->v2d5; op.u.getdomaininfolists3.num_domains = maxids; } ret = xenHypervisorDoV2Sys(handle, &op); if (ret == 0) { if (hv_versions.sys_interface < 3) ret = op.u.getdomaininfolist.num_domains; else ret = op.u.getdomaininfolists3.num_domains; } } else if (hv_versions.hypervisor == 1) { xen_op_v1 op; memset(&op, 0, sizeof(op)); op.cmd = XEN_V1_OP_GETDOMAININFOLIST; op.u.getdomaininfolist.first_domain = (domid_t) first_domain; op.u.getdomaininfolist.max_domains = maxids; op.u.getdomaininfolist.buffer = dominfos->v0; op.u.getdomaininfolist.num_domains = maxids; ret = xenHypervisorDoV1Op(handle, &op); if (ret == 0) ret = op.u.getdomaininfolist.num_domains; } else if (hv_versions.hypervisor == 0) { xen_op_v0 op; memset(&op, 0, sizeof(op)); op.cmd = XEN_V0_OP_GETDOMAININFOLIST; op.u.getdomaininfolist.first_domain = (domid_t) first_domain; op.u.getdomaininfolist.max_domains = maxids; op.u.getdomaininfolist.buffer = dominfos->v0; op.u.getdomaininfolist.num_domains = maxids; ret = xenHypervisorDoV0Op(handle, &op); if (ret == 0) ret = op.u.getdomaininfolist.num_domains; } if (unlock_pages(XEN_GETDOMAININFOLIST_DATA(dominfos), XEN_GETDOMAININFO_SIZE * maxids) < 0) ret = -1; return ret; } static int virXen_getdomaininfo(int handle, int first_domain, xen_getdomaininfo *dominfo) { xen_getdomaininfolist dominfos; if (hv_versions.hypervisor < 2) { dominfos.v0 = &(dominfo->v0); } else { dominfos.v2 = &(dominfo->v2); } return virXen_getdomaininfolist(handle, first_domain, 1, &dominfos); } /** * xenHypervisorGetSchedulerType: * @conn: the hypervisor connection * @nparams:give a number of scheduler parameters. * * Do a low level hypercall to get scheduler type * * Returns scheduler name or NULL in case of failure */ char * xenHypervisorGetSchedulerType(virConnectPtr conn, int *nparams) { char *schedulertype = NULL; xenUnifiedPrivatePtr priv = conn->privateData; /* * Support only hv_versions.dom_interface >=5 * (Xen3.1.0 or later) * TODO: check on Xen 3.0.3 */ if (hv_versions.dom_interface < 5) { virReportError(VIR_ERR_NO_XEN, "%s", _("unsupported in dom interface < 5")); return NULL; } if (hv_versions.hypervisor > 1) { xen_op_v2_sys op; int ret; memset(&op, 0, sizeof(op)); op.cmd = XEN_V2_OP_GETSCHEDULERID; ret = xenHypervisorDoV2Sys(priv->handle, &op); if (ret < 0) return NULL; switch (op.u.getschedulerid.sched_id) { case XEN_SCHEDULER_SEDF: ignore_value(VIR_STRDUP(schedulertype, "sedf")); if (nparams) *nparams = XEN_SCHED_SEDF_NPARAM; break; case XEN_SCHEDULER_CREDIT: ignore_value(VIR_STRDUP(schedulertype, "credit")); if (nparams) *nparams = XEN_SCHED_CRED_NPARAM; break; default: break; } } return schedulertype; } /** * xenHypervisorGetSchedulerParameters: * @conn: the hypervisor connection * @def: domain configuration * @params: pointer to scheduler parameters. * This memory area should be allocated before calling. * @nparams: this parameter must be at least as large as * the given number of scheduler parameters. * from xenHypervisorGetSchedulerType(). * * Do a low level hypercall to get scheduler parameters * * Returns 0 or -1 in case of failure */ int xenHypervisorGetSchedulerParameters(virConnectPtr conn, virDomainDefPtr def, virTypedParameterPtr params, int *nparams) { xenUnifiedPrivatePtr priv = conn->privateData; /* * Support only hv_versions.dom_interface >=5 * (Xen3.1.0 or later) * TODO: check on Xen 3.0.3 */ if (hv_versions.dom_interface < 5) { virReportError(VIR_ERR_NO_XEN, "%s", _("unsupported in dom interface < 5")); return -1; } if (hv_versions.hypervisor > 1) { xen_op_v2_sys op_sys; xen_op_v2_dom op_dom; int ret; memset(&op_sys, 0, sizeof(op_sys)); op_sys.cmd = XEN_V2_OP_GETSCHEDULERID; ret = xenHypervisorDoV2Sys(priv->handle, &op_sys); if (ret < 0) return -1; switch (op_sys.u.getschedulerid.sched_id) { case XEN_SCHEDULER_SEDF: if (*nparams < XEN_SCHED_SEDF_NPARAM) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("Invalid parameter count")); return -1; } /* TODO: Implement for Xen/SEDF */ TODO return -1; case XEN_SCHEDULER_CREDIT: memset(&op_dom, 0, sizeof(op_dom)); op_dom.cmd = XEN_V2_OP_SCHEDULER; op_dom.domain = (domid_t) def->id; op_dom.u.getschedinfo.sched_id = XEN_SCHEDULER_CREDIT; op_dom.u.getschedinfo.cmd = XEN_DOMCTL_SCHEDOP_getinfo; ret = xenHypervisorDoV2Dom(priv->handle, &op_dom); if (ret < 0) return -1; if (virTypedParameterAssign(¶ms[0], VIR_DOMAIN_SCHEDULER_WEIGHT, VIR_TYPED_PARAM_UINT, op_dom.u.getschedinfo.u.credit.weight) < 0) return -1; if (*nparams > 1 && virTypedParameterAssign(¶ms[1], VIR_DOMAIN_SCHEDULER_CAP, VIR_TYPED_PARAM_UINT, op_dom.u.getschedinfo.u.credit.cap) < 0) return -1; if (*nparams > XEN_SCHED_CRED_NPARAM) *nparams = XEN_SCHED_CRED_NPARAM; break; default: virReportError(VIR_ERR_INVALID_ARG, _("Unknown scheduler %d"), op_sys.u.getschedulerid.sched_id); return -1; } } return 0; } /** * xenHypervisorSetSchedulerParameters: * @conn: the hypervisor connection * @def: domain configuration * @nparams:give a number of scheduler setting parameters . * * Do a low level hypercall to set scheduler parameters * * Returns 0 or -1 in case of failure */ int xenHypervisorSetSchedulerParameters(virConnectPtr conn, virDomainDefPtr def, virTypedParameterPtr params, int nparams) { size_t i; unsigned int val; xenUnifiedPrivatePtr priv = conn->privateData; char buf[256]; if (nparams == 0) { /* nothing to do, exit early */ return 0; } if (virTypedParamsValidate(params, nparams, VIR_DOMAIN_SCHEDULER_WEIGHT, VIR_TYPED_PARAM_UINT, VIR_DOMAIN_SCHEDULER_CAP, VIR_TYPED_PARAM_UINT, NULL) < 0) return -1; /* * Support only hv_versions.dom_interface >=5 * (Xen3.1.0 or later) * TODO: check on Xen 3.0.3 */ if (hv_versions.dom_interface < 5) { virReportError(VIR_ERR_NO_XEN, "%s", _("unsupported in dom interface < 5")); return -1; } if (hv_versions.hypervisor > 1) { xen_op_v2_sys op_sys; xen_op_v2_dom op_dom; int ret; memset(&op_sys, 0, sizeof(op_sys)); op_sys.cmd = XEN_V2_OP_GETSCHEDULERID; ret = xenHypervisorDoV2Sys(priv->handle, &op_sys); if (ret == -1) return -1; switch (op_sys.u.getschedulerid.sched_id) { case XEN_SCHEDULER_SEDF: /* TODO: Implement for Xen/SEDF */ TODO return -1; case XEN_SCHEDULER_CREDIT: { memset(&op_dom, 0, sizeof(op_dom)); op_dom.cmd = XEN_V2_OP_SCHEDULER; op_dom.domain = (domid_t) def->id; op_dom.u.getschedinfo.sched_id = XEN_SCHEDULER_CREDIT; op_dom.u.getschedinfo.cmd = XEN_DOMCTL_SCHEDOP_putinfo; /* * credit scheduler parameters * following values do not change the parameters */ op_dom.u.getschedinfo.u.credit.weight = 0; op_dom.u.getschedinfo.u.credit.cap = (uint16_t)~0U; for (i = 0; i < nparams; i++) { memset(&buf, 0, sizeof(buf)); if (STREQ(params[i].field, VIR_DOMAIN_SCHEDULER_WEIGHT)) { val = params[i].value.ui; if ((val < 1) || (val > USHRT_MAX)) { virReportError(VIR_ERR_INVALID_ARG, _("Credit scheduler weight parameter (%d) " "is out of range (1-65535)"), val); return -1; } op_dom.u.getschedinfo.u.credit.weight = val; } else if (STREQ(params[i].field, VIR_DOMAIN_SCHEDULER_CAP)) { val = params[i].value.ui; if (val >= USHRT_MAX) { virReportError(VIR_ERR_INVALID_ARG, _("Credit scheduler cap parameter (%d) is " "out of range (0-65534)"), val); return -1; } op_dom.u.getschedinfo.u.credit.cap = val; } } ret = xenHypervisorDoV2Dom(priv->handle, &op_dom); if (ret < 0) return -1; break; } default: virReportError(VIR_ERR_INVALID_ARG, _("Unknown scheduler %d"), op_sys.u.getschedulerid.sched_id); return -1; } } return 0; } int xenHypervisorDomainBlockStats(virConnectPtr conn, virDomainDefPtr def, const char *path, virDomainBlockStatsPtr stats) { #ifdef __linux__ xenUnifiedPrivatePtr priv = conn->privateData; int ret; xenUnifiedLock(priv); /* Need to lock because it hits the xenstore handle :-( */ ret = xenLinuxDomainBlockStats(priv, def, path, stats); xenUnifiedUnlock(priv); return ret; #else virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("block statistics not supported on this platform")); return -1; #endif } /* Paths have the form vif. (this interface checks that * is the real domain ID and returns an error if not). * * In future we may allow you to query bridge stats (virbrX or * xenbrX), but that will probably be through a separate * virNetwork interface, as yet not decided. */ int xenHypervisorDomainInterfaceStats(virDomainDefPtr def, const char *path, virDomainInterfaceStatsPtr stats) { #ifdef __linux__ int rqdomid, device; /* Verify that the vif requested is one belonging to the current * domain. */ if (sscanf(path, "vif%d.%d", &rqdomid, &device) != 2) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("invalid path, should be vif..")); return -1; } if (rqdomid != def->id) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("invalid path, vif should match this domain ID")); return -1; } return virNetInterfaceStats(path, stats); #else virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("/proc/net/dev: Interface not found")); return -1; #endif } /** * virXen_setmaxmem: * @handle: the hypervisor handle * @id: the domain id * @memory: the amount of memory in kilobytes * * Do a low level hypercall to change the max memory amount * * Returns 0 or -1 in case of failure */ static int virXen_setmaxmem(int handle, int id, unsigned long memory) { int ret = -1; if (hv_versions.hypervisor > 1) { xen_op_v2_dom op; memset(&op, 0, sizeof(op)); op.cmd = XEN_V2_OP_SETMAXMEM; op.domain = (domid_t) id; if (hv_versions.dom_interface < 5) op.u.setmaxmem.maxmem = memory; else op.u.setmaxmemd5.maxmem = memory; ret = xenHypervisorDoV2Dom(handle, &op); } else if (hv_versions.hypervisor == 1) { xen_op_v1 op; memset(&op, 0, sizeof(op)); op.cmd = XEN_V1_OP_SETMAXMEM; op.u.setmaxmem.domain = (domid_t) id; op.u.setmaxmem.maxmem = memory; ret = xenHypervisorDoV1Op(handle, &op); } else if (hv_versions.hypervisor == 0) { xen_op_v0 op; memset(&op, 0, sizeof(op)); op.cmd = XEN_V0_OP_SETMAXMEM; op.u.setmaxmem.domain = (domid_t) id; op.u.setmaxmem.maxmem = memory; ret = xenHypervisorDoV0Op(handle, &op); } return ret; } /** * virXen_setvcpumap: * @handle: the hypervisor handle * @id: the domain id * @vcpu: the vcpu to map * @cpumap: the bitmap for this vcpu * @maplen: the size of the bitmap in bytes * * Do a low level hypercall to change the pinning for vcpu * * Returns 0 or -1 in case of failure */ static int virXen_setvcpumap(int handle, int id, unsigned int vcpu, unsigned char * cpumap, int maplen) { int ret = -1; unsigned char *new = NULL; unsigned char *bitmap = NULL; uint32_t nr_cpus; if (hv_versions.hypervisor > 1) { xen_op_v2_dom op; if (lock_pages(cpumap, maplen) < 0) return -1; memset(&op, 0, sizeof(op)); op.cmd = XEN_V2_OP_SETVCPUMAP; op.domain = (domid_t) id; /* The allocated memory to cpumap must be 'sizeof(uint64_t)' byte * * for Xen, and also nr_cpus must be 'sizeof(uint64_t) * 8' */ if (maplen < 8) { if (VIR_ALLOC_N(new, sizeof(uint64_t)) < 0) return -1; memcpy(new, cpumap, maplen); bitmap = new; nr_cpus = sizeof(uint64_t) * 8; } else { bitmap = cpumap; nr_cpus = maplen * 8; } if (hv_versions.dom_interface < 5) { op.u.setvcpumap.vcpu = vcpu; op.u.setvcpumap.cpumap.bitmap = bitmap; op.u.setvcpumap.cpumap.nr_cpus = nr_cpus; } else { op.u.setvcpumapd5.vcpu = vcpu; op.u.setvcpumapd5.cpumap.bitmap.v = bitmap; op.u.setvcpumapd5.cpumap.nr_cpus = nr_cpus; } ret = xenHypervisorDoV2Dom(handle, &op); VIR_FREE(new); if (unlock_pages(cpumap, maplen) < 0) ret = -1; } else { cpumap_t xen_cpumap; /* limited to 64 CPUs in old hypervisors */ char buf[8] = ""; if (maplen > sizeof(cpumap_t) || sizeof(cpumap_t) != sizeof(uint64_t)) return -1; /* Supply trailing 0s if user's input array was short */ memcpy(buf, cpumap, maplen); xen_cpumap = virReadBufInt64LE(buf); if (hv_versions.hypervisor == 1) { xen_op_v1 op; memset(&op, 0, sizeof(op)); op.cmd = XEN_V1_OP_SETVCPUMAP; op.u.setvcpumap.domain = (domid_t) id; op.u.setvcpumap.vcpu = vcpu; op.u.setvcpumap.cpumap = xen_cpumap; ret = xenHypervisorDoV1Op(handle, &op); } else if (hv_versions.hypervisor == 0) { xen_op_v0 op; memset(&op, 0, sizeof(op)); op.cmd = XEN_V0_OP_SETVCPUMAP; op.u.setvcpumap.domain = (domid_t) id; op.u.setvcpumap.vcpu = vcpu; op.u.setvcpumap.cpumap = xen_cpumap; ret = xenHypervisorDoV0Op(handle, &op); } } return ret; } /** * virXen_getvcpusinfo: * @handle: the hypervisor handle * @id: the domain id * @vcpu: the vcpu to map * @cpumap: the bitmap for this vcpu * @maplen: the size of the bitmap in bytes * * Do a low level hypercall to change the pinning for vcpu * * Returns 0 or -1 in case of failure */ static int virXen_getvcpusinfo(int handle, int id, unsigned int vcpu, virVcpuInfoPtr ipt, unsigned char *cpumap, int maplen) { int ret = -1; if (hv_versions.hypervisor > 1) { xen_op_v2_dom op; memset(&op, 0, sizeof(op)); op.cmd = XEN_V2_OP_GETVCPUINFO; op.domain = (domid_t) id; if (hv_versions.dom_interface < 5) op.u.getvcpuinfo.vcpu = (uint16_t) vcpu; else op.u.getvcpuinfod5.vcpu = (uint16_t) vcpu; ret = xenHypervisorDoV2Dom(handle, &op); if (ret < 0) return -1; ipt->number = vcpu; if (hv_versions.dom_interface < 5) { if (op.u.getvcpuinfo.online) { if (op.u.getvcpuinfo.running) ipt->state = VIR_VCPU_RUNNING; if (op.u.getvcpuinfo.blocked) ipt->state = VIR_VCPU_BLOCKED; } else { ipt->state = VIR_VCPU_OFFLINE; } ipt->cpuTime = op.u.getvcpuinfo.cpu_time; ipt->cpu = op.u.getvcpuinfo.online ? (int)op.u.getvcpuinfo.cpu : -1; } else { if (op.u.getvcpuinfod5.online) { if (op.u.getvcpuinfod5.running) ipt->state = VIR_VCPU_RUNNING; if (op.u.getvcpuinfod5.blocked) ipt->state = VIR_VCPU_BLOCKED; } else { ipt->state = VIR_VCPU_OFFLINE; } ipt->cpuTime = op.u.getvcpuinfod5.cpu_time; ipt->cpu = op.u.getvcpuinfod5.online ? (int)op.u.getvcpuinfod5.cpu : -1; } if ((cpumap != NULL) && (maplen > 0)) { if (lock_pages(cpumap, maplen) < 0) return -1; memset(cpumap, 0, maplen); memset(&op, 0, sizeof(op)); op.cmd = XEN_V2_OP_GETVCPUMAP; op.domain = (domid_t) id; if (hv_versions.dom_interface < 5) { op.u.getvcpumap.vcpu = vcpu; op.u.getvcpumap.cpumap.bitmap = cpumap; op.u.getvcpumap.cpumap.nr_cpus = maplen * 8; } else { op.u.getvcpumapd5.vcpu = vcpu; op.u.getvcpumapd5.cpumap.bitmap.v = cpumap; op.u.getvcpumapd5.cpumap.nr_cpus = maplen * 8; } ret = xenHypervisorDoV2Dom(handle, &op); if (unlock_pages(cpumap, maplen) < 0) ret = -1; } } else { int mapl = maplen; int cpu; if (maplen > (int)sizeof(cpumap_t)) mapl = (int)sizeof(cpumap_t); if (hv_versions.hypervisor == 1) { xen_op_v1 op; memset(&op, 0, sizeof(op)); op.cmd = XEN_V1_OP_GETVCPUINFO; op.u.getvcpuinfo.domain = (domid_t) id; op.u.getvcpuinfo.vcpu = vcpu; ret = xenHypervisorDoV1Op(handle, &op); if (ret < 0) return -1; ipt->number = vcpu; if (op.u.getvcpuinfo.online) { if (op.u.getvcpuinfo.running) ipt->state = VIR_VCPU_RUNNING; if (op.u.getvcpuinfo.blocked) ipt->state = VIR_VCPU_BLOCKED; } else ipt->state = VIR_VCPU_OFFLINE; ipt->cpuTime = op.u.getvcpuinfo.cpu_time; ipt->cpu = op.u.getvcpuinfo.online ? (int)op.u.getvcpuinfo.cpu : -1; if ((cpumap != NULL) && (maplen > 0)) { for (cpu = 0; cpu < (mapl * 8); cpu++) { if (op.u.getvcpuinfo.cpumap & ((uint64_t)1<number = vcpu; if (op.u.getvcpuinfo.online) { if (op.u.getvcpuinfo.running) ipt->state = VIR_VCPU_RUNNING; if (op.u.getvcpuinfo.blocked) ipt->state = VIR_VCPU_BLOCKED; } else ipt->state = VIR_VCPU_OFFLINE; ipt->cpuTime = op.u.getvcpuinfo.cpu_time; ipt->cpu = op.u.getvcpuinfo.online ? (int)op.u.getvcpuinfo.cpu : -1; if ((cpumap != NULL) && (maplen > 0)) { for (cpu = 0; cpu < (mapl * 8); cpu++) { if (op.u.getvcpuinfo.cpumap & ((uint64_t)1< xen-unstable c/s 21118:28e5409e3fb3 * domctl version 7 -> xen-unstable c/s 21212:de94884a669c * domctl version 8 -> xen-unstable c/s 23874:651aed73b39c */ hv_versions.sys_interface = 8; /* XEN_SYSCTL_INTERFACE_VERSION */ if (virXen_getdomaininfo(fd, 0, &info) == 1) { hv_versions.dom_interface = 7; /* XEN_DOMCTL_INTERFACE_VERSION */ if (virXen_getvcpusinfo(fd, 0, 0, ipt, NULL, 0) == 0) { VIR_DEBUG("Using hypervisor call v2, sys ver8 dom ver7"); goto done; } hv_versions.dom_interface = 8; /* XEN_DOMCTL_INTERFACE_VERSION */ if (virXen_getvcpusinfo(fd, 0, 0, ipt, NULL, 0) == 0) { VIR_DEBUG("Using hypervisor call v2, sys ver8 dom ver8"); goto done; } } /* Xen 4.2 * sysctl version 9 -> xen-unstable c/s 24102:dc8e55c90604 * domctl version 8 -> unchanged from Xen 4.1 */ hv_versions.sys_interface = 9; /* XEN_SYSCTL_INTERFACE_VERSION */ if (virXen_getdomaininfo(fd, 0, &info) == 1) { hv_versions.dom_interface = 8; /* XEN_DOMCTL_INTERFACE_VERSION */ if (virXen_getvcpusinfo(fd, 0, 0, ipt, NULL, 0) == 0) { VIR_DEBUG("Using hypervisor call v2, sys ver9 dom ver8"); goto done; } } /* Xen 4.3 * sysctl version 10 -> xen-unstable commit bec8f17e * domctl version 9 -> xen-unstable commit 65c9792d */ hv_versions.sys_interface = 10; /* XEN_SYSCTL_INTERFACE_VERSION */ if (virXen_getdomaininfo(fd, 0, &info) == 1) { hv_versions.dom_interface = 9; /* XEN_DOMCTL_INTERFACE_VERSION */ if (virXen_getvcpusinfo(fd, 0, 0, ipt, NULL, 0) == 0) { VIR_DEBUG("Using hypervisor call v2, sys ver10 dom ver9"); goto done; } } hv_versions.hypervisor = 1; hv_versions.sys_interface = -1; if (virXen_getdomaininfo(fd, 0, &info) == 1) { VIR_DEBUG("Using hypervisor call v1"); goto done; } /* * we failed to make the getdomaininfolist hypercall */ hv_versions.hypervisor = -1; virReportSystemError(errno, _("Unable to issue hypervisor ioctl %lu"), (unsigned long)IOCTL_PRIVCMD_HYPERCALL); VIR_DEBUG("Failed to find any Xen hypervisor method"); VIR_FORCE_CLOSE(fd); VIR_FREE(ipt); return -1; done: VIR_FORCE_CLOSE(fd); VIR_FREE(ipt); return 0; } static int xenHypervisorOnceInit(void) { return xenHypervisorInit(NULL); } VIR_ONCE_GLOBAL_INIT(xenHypervisor) /** * xenHypervisorOpen: * @conn: pointer to the connection block * @name: URL for the target, NULL for local * @flags: combination of virDrvOpenFlag(s) * * Connects to the Xen hypervisor. * * Returns 0 or -1 in case of error. */ int xenHypervisorOpen(virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED, unsigned int flags) { int ret; xenUnifiedPrivatePtr priv = conn->privateData; virCheckFlags(VIR_CONNECT_RO, -1); if (xenHypervisorInitialize() < 0) return -1; priv->handle = -1; ret = open(XEN_HYPERVISOR_SOCKET, O_RDWR); if (ret < 0) { virReportError(VIR_ERR_NO_XEN, "%s", XEN_HYPERVISOR_SOCKET); return -1; } priv->handle = ret; return 0; } /** * xenHypervisorClose: * @conn: pointer to the connection block * * Close the connection to the Xen hypervisor. * * Returns 0 in case of success or -1 in case of error. */ int xenHypervisorClose(virConnectPtr conn) { int ret; xenUnifiedPrivatePtr priv = conn->privateData; ret = VIR_CLOSE(priv->handle); if (ret < 0) return -1; return 0; } /** * xenHypervisorGetVersion: * @conn: pointer to the connection block * @hvVer: where to store the version * * Call the hypervisor to extracts his own internal API version * * Returns 0 in case of success, -1 in case of error */ int xenHypervisorGetVersion(virConnectPtr conn ATTRIBUTE_UNUSED, unsigned long *hvVer) { *hvVer = (hv_versions.hv >> 16) * 1000000 + (hv_versions.hv & 0xFFFF) * 1000; return 0; } struct guest_arch { virArch arch; int hvm; int pae; int nonpae; int ia64_be; }; static virCapsPtr xenHypervisorBuildCapabilities(virConnectPtr conn, virArch hostarch, int host_pae, const char *hvm_type, struct guest_arch *guest_archs, int nr_guest_archs) { virCapsPtr caps; size_t i; int hv_major = hv_versions.hv >> 16; int hv_minor = hv_versions.hv & 0xFFFF; if ((caps = virCapabilitiesNew(hostarch, true, true)) == NULL) goto no_memory; if (hvm_type && STRNEQ(hvm_type, "") && virCapabilitiesAddHostFeature(caps, hvm_type) < 0) goto no_memory; if (host_pae && virCapabilitiesAddHostFeature(caps, "pae") < 0) goto no_memory; if (virCapabilitiesAddHostMigrateTransport(caps, "xenmigr") < 0) goto no_memory; if (hv_versions.sys_interface >= SYS_IFACE_MIN_VERS_NUMA && conn != NULL) { if (xenDaemonNodeGetTopology(conn, caps) != 0) { virObjectUnref(caps); return NULL; } } for (i = 0; i < nr_guest_archs; ++i) { virCapsGuestPtr guest; char const *const xen_machines[] = {guest_archs[i].hvm ? "xenfv" : "xenpv"}; virCapsGuestMachinePtr *machines; if ((machines = virCapabilitiesAllocMachines(xen_machines, 1)) == NULL) goto no_memory; if ((guest = virCapabilitiesAddGuest(caps, guest_archs[i].hvm ? "hvm" : "xen", guest_archs[i].arch, (hostarch == VIR_ARCH_X86_64 ? "/usr/lib64/xen/bin/qemu-dm" : "/usr/lib/xen/bin/qemu-dm"), (guest_archs[i].hvm ? "/usr/lib/xen/boot/hvmloader" : NULL), 1, machines)) == NULL) { virCapabilitiesFreeMachines(machines, 1); goto no_memory; } machines = NULL; if (virCapabilitiesAddGuestDomain(guest, "xen", NULL, NULL, 0, NULL) == NULL) goto no_memory; if (guest_archs[i].pae && virCapabilitiesAddGuestFeature(guest, "pae", true, false) == NULL) goto no_memory; if (guest_archs[i].nonpae && virCapabilitiesAddGuestFeature(guest, "nonpae", true, false) == NULL) goto no_memory; if (guest_archs[i].ia64_be && virCapabilitiesAddGuestFeature(guest, "ia64_be", true, false) == NULL) goto no_memory; if (guest_archs[i].hvm) { if (virCapabilitiesAddGuestFeature(guest, "acpi", true, true) == NULL) goto no_memory; /* In Xen 3.1.0, APIC is always on and can't be toggled */ if (virCapabilitiesAddGuestFeature(guest, "apic", true, !(hv_major > 3 && hv_minor > 0)) == NULL) goto no_memory; /* Xen 3.3.x and beyond supports enabling/disabling * hardware assisted paging. Default is off. */ if ((hv_major == 3 && hv_minor >= 3) || (hv_major > 3)) if (virCapabilitiesAddGuestFeature(guest, "hap", false, true) == NULL) goto no_memory; /* Xen 3.4.x and beyond supports the Viridian (Hyper-V) * enlightenment interface. Default is off. */ if ((hv_major == 3 && hv_minor >= 4) || (hv_major > 3)) if (virCapabilitiesAddGuestFeature(guest, "viridian", false, true) == NULL) goto no_memory; } } return caps; no_memory: virObjectUnref(caps); return NULL; } #ifdef __sun static int get_cpu_flags(virConnectPtr conn, const char **hvm, int *pae, int *longmode) { struct { uint32_t r_eax, r_ebx, r_ecx, r_edx; } regs; char tmpbuf[20]; int ret = 0; int fd; /* returns -1, errno 22 if in 32-bit mode */ *longmode = (sysinfo(SI_ARCHITECTURE_64, tmpbuf, sizeof(tmpbuf)) != -1); if ((fd = open("/dev/cpu/self/cpuid", O_RDONLY)) == -1 || pread(fd, ®s, sizeof(regs), 0) != sizeof(regs)) { virReportSystemError(errno, "%s", _("could not read CPU flags")); goto out; } *pae = 0; *hvm = ""; if (STREQLEN((const char *)®s.r_ebx, "AuthcAMDenti", 12)) { if (pread(fd, ®s, sizeof(regs), 0x80000001) == sizeof(regs)) { /* Read secure virtual machine bit (bit 2 of ECX feature ID) */ if ((regs.r_ecx >> 2) & 1) *hvm = "svm"; if ((regs.r_edx >> 6) & 1) *pae = 1; } } else if (STREQLEN((const char *)®s.r_ebx, "GenuntelineI", 12)) { if (pread(fd, ®s, sizeof(regs), 0x00000001) == sizeof(regs)) { /* Read VMXE feature bit (bit 5 of ECX feature ID) */ if ((regs.r_ecx >> 5) & 1) *hvm = "vmx"; if ((regs.r_edx >> 6) & 1) *pae = 1; } } ret = 1; out: VIR_FORCE_CLOSE(fd); return ret; } static virCapsPtr xenHypervisorMakeCapabilitiesSunOS(virConnectPtr conn) { struct guest_arch guest_arches[32]; size_t i = 0; virCapsPtr caps = NULL; int pae, longmode; const char *hvm; if (!get_cpu_flags(conn, &hvm, &pae, &longmode)) return NULL; guest_arches[i].arch = VIR_ARCH_I686; guest_arches[i].hvm = 0; guest_arches[i].pae = pae; guest_arches[i].nonpae = !pae; guest_arches[i].ia64_be = 0; i++; if (longmode) { guest_arches[i].arch = VIR_ARCH_X86_64; guest_arches[i].hvm = 0; guest_arches[i].pae = 0; guest_arches[i].nonpae = 0; guest_arches[i].ia64_be = 0; i++; } if (hvm[0] != '\0') { guest_arches[i].arch = VIR_ARCH_I686; guest_arches[i].hvm = 1; guest_arches[i].pae = pae; guest_arches[i].nonpae = 1; guest_arches[i].ia64_be = 0; i++; if (longmode) { guest_arches[i].arch = VIR_ARCH_X86_64; guest_arches[i].hvm = 1; guest_arches[i].pae = 0; guest_arches[i].nonpae = 0; guest_arches[i].ia64_be = 0; i++; } } caps = xenHypervisorBuildCapabilities(conn, virArchFromHost(), pae, hvm, guest_arches, i); return caps; } #endif /* __sun */ /** * xenHypervisorMakeCapabilitiesInternal: * @conn: pointer to the connection block * @cpuinfo: file handle containing /proc/cpuinfo data, or NULL * @capabilities: file handle containing /sys/hypervisor/properties/capabilities data, or NULL * * Return the capabilities of this hypervisor. */ virCapsPtr xenHypervisorMakeCapabilitiesInternal(virConnectPtr conn, virArch hostarch, FILE *cpuinfo, FILE *capabilities) { char line[1024], *str, *token; regmatch_t subs[4]; char *saveptr = NULL; size_t i; char hvm_type[4] = ""; /* "vmx" or "svm" (or "" if not in CPU). */ int host_pae = 0; struct guest_arch guest_archs[32]; int nr_guest_archs = 0; virCapsPtr caps = NULL; memset(guest_archs, 0, sizeof(guest_archs)); /* /proc/cpuinfo: flags: Intel calls HVM "vmx", AMD calls it "svm". * It's not clear if this will work on IA64, let alone other * architectures and non-Linux. (XXX) */ if (cpuinfo) { while (fgets(line, sizeof(line), cpuinfo)) { if (regexec(&flags_hvm_rec, line, sizeof(subs)/sizeof(regmatch_t), subs, 0) == 0 && subs[0].rm_so != -1) { if (virStrncpy(hvm_type, &line[subs[1].rm_so], subs[1].rm_eo-subs[1].rm_so, sizeof(hvm_type)) == NULL) goto no_memory; } else if (regexec(&flags_pae_rec, line, 0, NULL, 0) == 0) { host_pae = 1; } } } /* Most of the useful info is in /sys/hypervisor/properties/capabilities * which is documented in the code in xen-unstable.hg/xen/arch/.../setup.c. * * It is a space-separated list of supported guest architectures. * * For x86: * TYP-VER-ARCH[p] * ^ ^ ^ ^ * | | | +-- PAE supported * | | +------- x86_32 or x86_64 * | +----------- the version of Xen, eg. "3.0" * +--------------- "xen" or "hvm" for para or full virt respectively * * For PPC this file appears to be always empty (?) * * For IA64: * TYP-VER-ARCH[be] * ^ ^ ^ ^ * | | | +-- Big-endian supported * | | +------- always "ia64" * | +----------- the version of Xen, eg. "3.0" * +--------------- "xen" or "hvm" for para or full virt respectively */ /* Expecting one line in this file - ignore any more. */ if ((capabilities) && (fgets(line, sizeof(line), capabilities))) { /* Split the line into tokens. strtok_r is OK here because we "own" * this buffer. Parse out the features from each token. */ for (str = line, nr_guest_archs = 0; nr_guest_archs < sizeof(guest_archs) / sizeof(guest_archs[0]) && (token = strtok_r(str, " ", &saveptr)) != NULL; str = NULL) { if (regexec(&xen_cap_rec, token, sizeof(subs) / sizeof(subs[0]), subs, 0) == 0) { int hvm = STRPREFIX(&token[subs[1].rm_so], "hvm"); int pae = 0, nonpae = 0, ia64_be = 0; virArch arch; if (STRPREFIX(&token[subs[2].rm_so], "x86_32")) { arch = VIR_ARCH_I686; if (subs[3].rm_so != -1 && STRPREFIX(&token[subs[3].rm_so], "p")) pae = 1; else nonpae = 1; } else if (STRPREFIX(&token[subs[2].rm_so], "x86_64")) { arch = VIR_ARCH_X86_64; } else if (STRPREFIX(&token[subs[2].rm_so], "ia64")) { arch = VIR_ARCH_ITANIUM; if (subs[3].rm_so != -1 && STRPREFIX(&token[subs[3].rm_so], "be")) ia64_be = 1; } else if (STRPREFIX(&token[subs[2].rm_so], "powerpc64")) { arch = VIR_ARCH_PPC64; } else { /* XXX surely no other Xen archs exist. Arrrrrrrrrm */ continue; } /* Search for existing matching (model,hvm) tuple */ for (i = 0; i < nr_guest_archs; i++) { if (guest_archs[i].arch == arch && guest_archs[i].hvm == hvm) { break; } } /* Too many arch flavours - highly unlikely ! */ if (i >= ARRAY_CARDINALITY(guest_archs)) continue; /* Didn't find a match, so create a new one */ if (i == nr_guest_archs) nr_guest_archs++; guest_archs[i].arch = arch; guest_archs[i].hvm = hvm; /* Careful not to overwrite a previous positive setting with a negative one here - some archs can do both pae & non-pae, but Xen reports separately capabilities so we're merging archs */ if (pae) guest_archs[i].pae = pae; if (nonpae) guest_archs[i].nonpae = nonpae; if (ia64_be) guest_archs[i].ia64_be = ia64_be; } } } if ((caps = xenHypervisorBuildCapabilities(conn, hostarch, host_pae, hvm_type, guest_archs, nr_guest_archs)) == NULL) goto no_memory; return caps; no_memory: virObjectUnref(caps); return NULL; } /** * xenHypervisorMakeCapabilities: * * Return the capabilities of this hypervisor. */ virCapsPtr xenHypervisorMakeCapabilities(virConnectPtr conn) { #ifdef __sun return xenHypervisorMakeCapabilitiesSunOS(conn); #else virCapsPtr caps = NULL; FILE *cpuinfo, *capabilities; cpuinfo = fopen("/proc/cpuinfo", "r"); if (cpuinfo == NULL) { if (errno != ENOENT) { virReportSystemError(errno, _("cannot read file %s"), "/proc/cpuinfo"); return NULL; } } capabilities = fopen("/sys/hypervisor/properties/capabilities", "r"); if (capabilities == NULL) { if (errno != ENOENT) { VIR_FORCE_FCLOSE(cpuinfo); virReportSystemError(errno, _("cannot read file %s"), "/sys/hypervisor/properties/capabilities"); return NULL; } } caps = xenHypervisorMakeCapabilitiesInternal(conn, virArchFromHost(), cpuinfo, capabilities); if (caps == NULL) goto cleanup; if (virNodeSuspendGetTargetMask(&caps->host.powerMgmt) < 0) VIR_WARN("Failed to get host power management capabilities"); cleanup: VIR_FORCE_FCLOSE(cpuinfo); VIR_FORCE_FCLOSE(capabilities); return caps; #endif /* __sun */ } /** * xenHypervisorGetCapabilities: * @conn: pointer to the connection block * * Return the capabilities of this hypervisor. */ char * xenHypervisorGetCapabilities(virConnectPtr conn) { xenUnifiedPrivatePtr priv = conn->privateData; return virCapabilitiesFormatXML(priv->caps); } char * xenHypervisorDomainGetOSType(virConnectPtr conn, virDomainDefPtr def) { xenUnifiedPrivatePtr priv = conn->privateData; xen_getdomaininfo dominfo; char *ostype = NULL; /* HV's earlier than 3.1.0 don't include the HVM flags in guests status*/ if (hv_versions.hypervisor < 2 || hv_versions.dom_interface < 4) { return xenDaemonDomainGetOSType(conn, def); } XEN_GETDOMAININFO_CLEAR(dominfo); if (virXen_getdomaininfo(priv->handle, def->id, &dominfo) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot get domain details")); return NULL; } if (XEN_GETDOMAININFO_DOMAIN(dominfo) != def->id) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot get domain details")); return NULL; } ignore_value(VIR_STRDUP(ostype, XEN_GETDOMAININFO_FLAGS(dominfo) & DOMFLAGS_HVM ? "hvm" : "linux")); return ostype; } int xenHypervisorHasDomain(virConnectPtr conn, int id) { xenUnifiedPrivatePtr priv = conn->privateData; xen_getdomaininfo dominfo; XEN_GETDOMAININFO_CLEAR(dominfo); if (virXen_getdomaininfo(priv->handle, id, &dominfo) < 0) return 0; if (XEN_GETDOMAININFO_DOMAIN(dominfo) != id) return 0; return 1; } virDomainDefPtr xenHypervisorLookupDomainByID(virConnectPtr conn, int id) { xenUnifiedPrivatePtr priv = conn->privateData; xen_getdomaininfo dominfo; virDomainDefPtr ret; char *name; XEN_GETDOMAININFO_CLEAR(dominfo); if (virXen_getdomaininfo(priv->handle, id, &dominfo) < 0) return NULL; if (XEN_GETDOMAININFO_DOMAIN(dominfo) != id) return NULL; xenUnifiedLock(priv); name = xenStoreDomainGetName(conn, id); xenUnifiedUnlock(priv); if (!name) return NULL; ret = virDomainDefNew(name, XEN_GETDOMAININFO_UUID(dominfo), id); VIR_FREE(name); return ret; } virDomainDefPtr xenHypervisorLookupDomainByUUID(virConnectPtr conn, const unsigned char *uuid) { xen_getdomaininfolist dominfos; xenUnifiedPrivatePtr priv = conn->privateData; virDomainDefPtr ret; char *name; int maxids = 100, nids, id; size_t i; retry: if (!(XEN_GETDOMAININFOLIST_ALLOC(dominfos, maxids))) { virReportOOMError(); return NULL; } XEN_GETDOMAININFOLIST_CLEAR(dominfos, maxids); nids = virXen_getdomaininfolist(priv->handle, 0, maxids, &dominfos); if (nids < 0) { XEN_GETDOMAININFOLIST_FREE(dominfos); return NULL; } /* Can't possibly have more than 65,000 concurrent guests * so limit how many times we try, to avoid increasing * without bound & thus allocating all of system memory ! * XXX I'll regret this comment in a few years time ;-) */ if (nids == maxids) { XEN_GETDOMAININFOLIST_FREE(dominfos); if (maxids < 65000) { maxids *= 2; goto retry; } return NULL; } id = -1; for (i = 0; i < nids; i++) { if (memcmp(XEN_GETDOMAININFOLIST_UUID(dominfos, i), uuid, VIR_UUID_BUFLEN) == 0) { id = XEN_GETDOMAININFOLIST_DOMAIN(dominfos, i); break; } } XEN_GETDOMAININFOLIST_FREE(dominfos); if (id == -1) return NULL; xenUnifiedLock(priv); name = xenStoreDomainGetName(conn, id); xenUnifiedUnlock(priv); if (!name) return NULL; ret = virDomainDefNew(name, uuid, id); if (ret) ret->id = id; VIR_FREE(name); return ret; } /** * xenHypervisorGetMaxVcpus: * * Returns the maximum of CPU defined by Xen. */ int xenHypervisorGetMaxVcpus(virConnectPtr conn ATTRIBUTE_UNUSED, const char *type ATTRIBUTE_UNUSED) { return MAX_VIRT_CPUS; } /** * xenHypervisorDomMaxMemory: * @dom: domain * * Retrieve the maximum amount of physical memory allocated to a * domain. * * Returns the memory size in kilobytes or 0 in case of error. */ unsigned long xenHypervisorGetMaxMemory(virConnectPtr conn, virDomainDefPtr def) { xenUnifiedPrivatePtr priv = conn->privateData; xen_getdomaininfo dominfo; int ret; if (kb_per_pages == 0) { kb_per_pages = virGetSystemPageSizeKB(); if (kb_per_pages <= 0) kb_per_pages = 4; } XEN_GETDOMAININFO_CLEAR(dominfo); ret = virXen_getdomaininfo(priv->handle, def->id, &dominfo); if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != def->id)) return 0; return (unsigned long) XEN_GETDOMAININFO_MAX_PAGES(dominfo) * kb_per_pages; } /** * xenHypervisorGetDomInfo: * @conn: connection data * @id: the domain ID * @info: the place where information should be stored * * Do a hypervisor call to get the related set of domain information. * * Returns 0 in case of success, -1 in case of error. */ int xenHypervisorGetDomInfo(virConnectPtr conn, int id, virDomainInfoPtr info) { xenUnifiedPrivatePtr priv = conn->privateData; xen_getdomaininfo dominfo; int ret; uint32_t domain_flags, domain_state, domain_shutdown_cause; if (kb_per_pages == 0) { kb_per_pages = virGetSystemPageSizeKB(); if (kb_per_pages <= 0) kb_per_pages = 4; } memset(info, 0, sizeof(virDomainInfo)); XEN_GETDOMAININFO_CLEAR(dominfo); ret = virXen_getdomaininfo(priv->handle, id, &dominfo); if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != id)) return -1; domain_flags = XEN_GETDOMAININFO_FLAGS(dominfo); domain_flags &= ~DOMFLAGS_HVM; /* Mask out HVM flags */ domain_state = domain_flags & 0xFF; /* Mask out high bits */ switch (domain_state) { case DOMFLAGS_DYING: info->state = VIR_DOMAIN_SHUTDOWN; break; case DOMFLAGS_SHUTDOWN: /* The domain is shutdown. Determine the cause. */ domain_shutdown_cause = domain_flags >> DOMFLAGS_SHUTDOWNSHIFT; switch (domain_shutdown_cause) { case SHUTDOWN_crash: info->state = VIR_DOMAIN_CRASHED; break; default: info->state = VIR_DOMAIN_SHUTOFF; } break; case DOMFLAGS_PAUSED: info->state = VIR_DOMAIN_PAUSED; break; case DOMFLAGS_BLOCKED: info->state = VIR_DOMAIN_BLOCKED; break; case DOMFLAGS_RUNNING: info->state = VIR_DOMAIN_RUNNING; break; default: info->state = VIR_DOMAIN_NOSTATE; } /* * the API brings back the cpu time in nanoseconds, * convert to microseconds, same thing convert to * kilobytes from page counts */ info->cpuTime = XEN_GETDOMAININFO_CPUTIME(dominfo); info->memory = XEN_GETDOMAININFO_TOT_PAGES(dominfo) * kb_per_pages; info->maxMem = XEN_GETDOMAININFO_MAX_PAGES(dominfo); if (info->maxMem != UINT_MAX) info->maxMem *= kb_per_pages; info->nrVirtCpu = XEN_GETDOMAININFO_CPUCOUNT(dominfo); return 0; } /** * xenHypervisorGetDomainInfo: * @domain: pointer to the domain block * @info: the place where information should be stored * * Do a hypervisor call to get the related set of domain information. * * Returns 0 in case of success, -1 in case of error. */ int xenHypervisorGetDomainInfo(virConnectPtr conn, virDomainDefPtr def, virDomainInfoPtr info) { return xenHypervisorGetDomInfo(conn, def->id, info); } /** * xenHypervisorGetDomainState: * @domain: pointer to the domain block * @state: returned state of the domain * @reason: returned reason for the state * * Do a hypervisor call to get the related set of domain information. * * Returns 0 in case of success, -1 in case of error. */ int xenHypervisorGetDomainState(virConnectPtr conn, virDomainDefPtr def, int *state, int *reason) { virDomainInfo info; if (xenHypervisorGetDomInfo(conn, def->id, &info) < 0) return -1; *state = info.state; if (reason) *reason = 0; return 0; } /** * xenHypervisorNodeGetCellsFreeMemory: * @conn: pointer to the hypervisor connection * @freeMems: pointer to the array of unsigned long long * @startCell: index of first cell to return freeMems info on. * @maxCells: Maximum number of cells for which freeMems information can * be returned. * * This call returns the amount of free memory in one or more NUMA cells. * The @freeMems array must be allocated by the caller and will be filled * with the amount of free memory in kilobytes for each cell requested, * starting with startCell (in freeMems[0]), up to either * (startCell + maxCells), or the number of additional cells in the node, * whichever is smaller. * * Returns the number of entries filled in freeMems, or -1 in case of error. */ int xenHypervisorNodeGetCellsFreeMemory(virConnectPtr conn, unsigned long long *freeMems, int startCell, int maxCells) { xen_op_v2_sys op_sys; size_t i; int cell; int ret; xenUnifiedPrivatePtr priv = conn->privateData; if (priv->nbNodeCells < 0) { virReportError(VIR_ERR_XEN_CALL, "%s", _("cannot determine actual number of cells")); return -1; } if ((maxCells < 1) || (startCell >= priv->nbNodeCells)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("invalid argument")); return -1; } /* * Support only hv_versions.sys_interface >=4 */ if (hv_versions.sys_interface < SYS_IFACE_MIN_VERS_NUMA) { virReportError(VIR_ERR_XEN_CALL, "%s", _("unsupported in sys interface < 4")); return -1; } memset(&op_sys, 0, sizeof(op_sys)); op_sys.cmd = XEN_V2_OP_GETAVAILHEAP; for (cell = startCell, i = 0; cell < priv->nbNodeCells && i < maxCells; cell++, i++) { if (hv_versions.sys_interface >= 5) op_sys.u.availheap5.node = cell; else op_sys.u.availheap.node = cell; ret = xenHypervisorDoV2Sys(priv->handle, &op_sys); if (ret < 0) return -1; if (hv_versions.sys_interface >= 5) freeMems[i] = op_sys.u.availheap5.avail_bytes; else freeMems[i] = op_sys.u.availheap.avail_bytes; } return i; } /** * xenHypervisorSetMaxMemory: * @domain: pointer to the domain block * @memory: the max memory size in kilobytes. * * Do a hypervisor call to change the maximum amount of memory used * * Returns 0 in case of success, -1 in case of error. */ int xenHypervisorSetMaxMemory(virConnectPtr conn, virDomainDefPtr def, unsigned long memory) { int ret; xenUnifiedPrivatePtr priv = conn->privateData; ret = virXen_setmaxmem(priv->handle, def->id, memory); if (ret < 0) return -1; return 0; } /** * xenHypervisorPinVcpu: * @domain: pointer to domain object * @vcpu: virtual CPU number * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) * @maplen: length of cpumap in bytes * * Dynamically change the real CPUs which can be allocated to a virtual CPU. * * Returns 0 in case of success, -1 in case of failure. */ int xenHypervisorPinVcpu(virConnectPtr conn, virDomainDefPtr def, unsigned int vcpu, unsigned char *cpumap, int maplen) { int ret; xenUnifiedPrivatePtr priv = conn->privateData; ret = virXen_setvcpumap(priv->handle, def->id, vcpu, cpumap, maplen); if (ret < 0) return -1; return 0; } /** * virDomainGetVcpus: * @domain: pointer to domain object, or NULL for Domain0 * @info: pointer to an array of virVcpuInfo structures (OUT) * @maxinfo: number of structures in info array * @cpumaps: pointer to a bit map of real CPUs for all vcpus of this domain (in 8-bit bytes) (OUT) * If cpumaps is NULL, then no cpumap information is returned by the API. * It's assumed there is cpumap in cpumaps array. * The memory allocated to cpumaps must be (maxinfo * maplen) bytes * (ie: calloc(maxinfo, maplen)). * One cpumap inside cpumaps has the format described in virDomainPinVcpu() API. * @maplen: number of bytes in one cpumap, from 1 up to size of CPU map in * underlying virtualization system (Xen...). * * Extract information about virtual CPUs of domain, store it in info array * and also in cpumaps if this pointer isn't NULL. * * Returns the number of info filled in case of success, -1 in case of failure. */ int xenHypervisorGetVcpus(virConnectPtr conn, virDomainDefPtr def, virVcpuInfoPtr info, int maxinfo, unsigned char *cpumaps, int maplen) { xen_getdomaininfo dominfo; int ret; xenUnifiedPrivatePtr priv = conn->privateData; virVcpuInfoPtr ipt; int nbinfo; size_t i; if (sizeof(cpumap_t) & 7) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid cpumap_t size")); return -1; } /* first get the number of virtual CPUs in this domain */ XEN_GETDOMAININFO_CLEAR(dominfo); ret = virXen_getdomaininfo(priv->handle, def->id, &dominfo); if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != def->id)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot get domain details")); return -1; } nbinfo = XEN_GETDOMAININFO_CPUCOUNT(dominfo) + 1; if (nbinfo > maxinfo) nbinfo = maxinfo; if (cpumaps != NULL) memset(cpumaps, 0, maxinfo * maplen); for (i = 0, ipt = info; i < nbinfo; i++, ipt++) { if ((cpumaps != NULL) && (i < maxinfo)) { ret = virXen_getvcpusinfo(priv->handle, def->id, i, ipt, (unsigned char *)VIR_GET_CPUMAP(cpumaps, maplen, i), maplen); if (ret < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot get VCPUs info")); return -1; } } else { ret = virXen_getvcpusinfo(priv->handle, def->id, i, ipt, NULL, 0); if (ret < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot get VCPUs info")); return -1; } } } return nbinfo; } /** * xenHypervisorGetVcpuMax: * * Returns the maximum number of virtual CPUs supported for * the guest VM. If the guest is inactive, this is the maximum * of CPU defined by Xen. If the guest is running this reflect * the maximum number of virtual CPUs the guest was booted with. */ int xenHypervisorGetVcpuMax(virConnectPtr conn, virDomainDefPtr def) { xen_getdomaininfo dominfo; int ret; int maxcpu; xenUnifiedPrivatePtr priv = conn->privateData; /* inactive domain */ if (def->id < 0) { maxcpu = MAX_VIRT_CPUS; } else { XEN_GETDOMAININFO_CLEAR(dominfo); ret = virXen_getdomaininfo(priv->handle, def->id, &dominfo); if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != def->id)) return -1; maxcpu = XEN_GETDOMAININFO_MAXCPUID(dominfo) + 1; } return maxcpu; } /** * xenHavePrivilege() * * Return true if the current process should be able to connect to Xen. */ int xenHavePrivilege(void) { #ifdef __sun return priv_ineffect(PRIV_XVM_CONTROL); #else return access(XEN_HYPERVISOR_SOCKET, R_OK) == 0; #endif }