I've been doing some testing with current xen-unstable (ie what will very shortly be 3.0.5) and came across a whole bunch of things which needed fixing - some expected, others not expected. The attached patch addresses the following issues: - Many of the hypercalls have their structs changed so that int64_t or 'foo *' members are always 64-bit aligned even on 32-bit platforms. This is part of the work to allow 32-bit Dom0/DomU to work on 64-bit hypervisor. For the int64_t types I had to annotate with __attribute__((aligned(8))). This did not work for pointer data types, so I for those I had to do a more complex hack with union { foo *v; int64_t pad __attribute__((aligned(8))) } This matches what is done in the public (BSD licensed) Xen HV header files. We already had ways to deal with v0 vs v2 hypercalls structs. This change is still techincally v2, but just a minor revision of the domctl or sysctl interfaces. Thus I have named the extra structs v2d5 or v2s3 to indicated hypercall version 2, domctl version 5 or hypercall version 2, sysctl version 3 respectively. - The 'flags' field in the getdomaininfo hypercall now has an extra flag defined '(1<<1)' which was previously not used, is now used to indicate that the guest is HVM. Thus when fetching domain state, we have to mask out that flag, otherwise we'll never match the correct paused/running/ blocked/etc states. - In the xenHypervisorNumOfDomains method, under certain scenarios we will re-try the hypercall, allocating a bigger memory buffer. Well due to the ABI alignment changes we hit that scenario everytime, and ended up allocating a multi-GB buffer :-) The fixed structs sort this out, but as a preventative measure for any future HV changes the patch will break out of the loop at the 10,000 guest mark to avoid allocating GB of memory. - The unified Xen driver broke the GetVCPUs method - it was mistakenly checking for return value == 0, instead of > 0. Trivial fix. - The method to open the XenD connection was calling xenDaemonGetVersion to test if the connection succeeded. But then also calling the xend_detect_config_version which does pretty much same thing. So I removed the former, and now we only do the latter as a 'ping' test when opening. This removes 1 HTTP GET which is worthwhile performance boost given how horrifically slow XenD is. - The HVM SEXPR for configuring the VNC / SDL graphics is no longere part of the (image) block. it now matches the PVFB graphics config and is an explicit (vfb) block within the (devices) block. So if xend_config_format >= 4 we use the new style config - this is assuming upstream XenD is patched to increment xend_config_format from 3 to 4 - I send a patch & am confident it will be applied very shortly. - The QEMU device model allows a user to specify multiple devices for the boot order, eg 'andc' to indicated 'floppy', 'network' 'cdrom', 'disk'. We assumed it was a single letter only. I now serialize this into multiple <boot dev='XXX'/> elements, ordered according to priority. The XML -> SEXPR conversion allows the same. I've tested all this on a 32-bit Dom0 running on 32-bit HV, and 64-bit HV, but not tested a 64-bit Dom0 on 64-bit HV. I'm pretty sure it'll work,but if anyone is runnning 64-on-64 please test this patch. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
Index: xen_internal.c =================================================================== RCS file: /data/cvs/libvirt/src/xen_internal.c,v retrieving revision 1.70 diff -u -p -r1.70 xen_internal.c --- xen_internal.c 4 Apr 2007 14:19:49 -0000 1.70 +++ xen_internal.c 12 Apr 2007 00:52:26 -0000 @@ -93,6 +93,7 @@ static regex_t xen_cap_rec; */ #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. */ @@ -146,87 +147,136 @@ struct xen_v2_getdomaininfo { }; 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; /* falgs, 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; + union xen_getdomaininfo { struct xen_v0_getdomaininfo v0; struct xen_v2_getdomaininfo v2; + struct xen_v2d5_getdomaininfo v2d5; }; typedef union xen_getdomaininfo xen_getdomaininfo; union xen_getdomaininfolist { struct xen_v0_getdomaininfo *v0; struct xen_v2_getdomaininfo *v2; + struct xen_v2d5_getdomaininfo *v2d5; }; typedef union xen_getdomaininfolist xen_getdomaininfolist; #define XEN_GETDOMAININFOLIST_ALLOC(domlist, size) \ (hypervisor_version < 2 ? \ ((domlist.v0 = malloc(sizeof(xen_v0_getdomaininfo)*(size))) != NULL) : \ - ((domlist.v2 = malloc(sizeof(xen_v2_getdomaininfo)*(size))) != NULL)) - -#define XEN_GETDOMAININFOLIST_FREE(domlist) \ - (hypervisor_version < 2 ? \ - free(domlist.v0) : \ - free(domlist.v2)) - -#define XEN_GETDOMAININFOLIST_CLEAR(domlist, size) \ - (hypervisor_version < 2 ? \ - memset(domlist.v0, 0, sizeof(xen_v0_getdomaininfo) * size) : \ - memset(domlist.v2, 0, sizeof(xen_v2_getdomaininfo) * size)) + (dom_interface_version < 5 ? \ + ((domlist.v2 = malloc(sizeof(xen_v2_getdomaininfo)*(size))) != NULL) : \ + ((domlist.v2d5 = malloc(sizeof(xen_v2d5_getdomaininfo)*(size))) != NULL))) + +#define XEN_GETDOMAININFOLIST_FREE(domlist) \ + (hypervisor_version < 2 ? \ + free(domlist.v0) : \ + (dom_interface_version < 5 ? \ + free(domlist.v2) : \ + free(domlist.v2d5))) + +#define XEN_GETDOMAININFOLIST_CLEAR(domlist, size) \ + (hypervisor_version < 2 ? \ + memset(domlist.v0, 0, sizeof(xen_v0_getdomaininfo) * size) : \ + (dom_interface_version < 5 ? \ + memset(domlist.v2, 0, sizeof(xen_v2_getdomaininfo) * size) : \ + memset(domlist.v2d5, 0, sizeof(xen_v2d5_getdomaininfo) * size))) #define XEN_GETDOMAININFOLIST_DOMAIN(domlist, n) \ (hypervisor_version < 2 ? \ domlist.v0[n].domain : \ - domlist.v2[n].domain) - -#define XEN_GETDOMAININFOLIST_DATA(domlist) \ - (hypervisor_version < 2 ? \ - (void*)(domlist->v0) : \ - (void*)(domlist->v2)) - -#define XEN_GETDOMAININFO_SIZE \ - (hypervisor_version < 2 ? \ - sizeof(xen_v0_getdomaininfo) : \ - sizeof(xen_v2_getdomaininfo)) - -#define XEN_GETDOMAININFO_CLEAR(dominfo) \ - (hypervisor_version < 2 ? \ - memset(&(dominfo.v0), 0, sizeof(xen_v0_getdomaininfo)) : \ - memset(&(dominfo.v2), 0, sizeof(xen_v2_getdomaininfo))) + (dom_interface_version < 5 ? \ + domlist.v2[n].domain : \ + domlist.v2d5[n].domain)) + +#define XEN_GETDOMAININFOLIST_DATA(domlist) \ + (hypervisor_version < 2 ? \ + (void*)(domlist->v0) : \ + (dom_interface_version < 5 ? \ + (void*)(domlist->v2) : \ + (void*)(domlist->v2d5))) + +#define XEN_GETDOMAININFO_SIZE \ + (hypervisor_version < 2 ? \ + sizeof(xen_v0_getdomaininfo) : \ + (dom_interface_version < 5 ? \ + sizeof(xen_v2_getdomaininfo) : \ + sizeof(xen_v2d5_getdomaininfo))) + +#define XEN_GETDOMAININFO_CLEAR(dominfo) \ + (hypervisor_version < 2 ? \ + memset(&(dominfo.v0), 0, sizeof(xen_v0_getdomaininfo)) : \ + (dom_interface_version < 5 ? \ + memset(&(dominfo.v2), 0, sizeof(xen_v2_getdomaininfo)) : \ + memset(&(dominfo.v2d5), 0, sizeof(xen_v2d5_getdomaininfo)))) #define XEN_GETDOMAININFO_DOMAIN(dominfo) \ (hypervisor_version < 2 ? \ dominfo.v0.domain : \ - dominfo.v2.domain) + (dom_interface_version < 5 ? \ + dominfo.v2.domain : \ + dominfo.v2d5.domain)) #define XEN_GETDOMAININFO_CPUTIME(dominfo) \ (hypervisor_version < 2 ? \ dominfo.v0.cpu_time : \ - dominfo.v2.cpu_time) + (dom_interface_version < 5 ? \ + dominfo.v2.cpu_time : \ + dominfo.v2d5.cpu_time)) #define XEN_GETDOMAININFO_CPUCOUNT(dominfo) \ (hypervisor_version < 2 ? \ dominfo.v0.nr_online_vcpus : \ - dominfo.v2.nr_online_vcpus) + (dom_interface_version < 5 ? \ + dominfo.v2.nr_online_vcpus : \ + dominfo.v2d5.nr_online_vcpus)) #define XEN_GETDOMAININFO_MAXCPUID(dominfo) \ (hypervisor_version < 2 ? \ dominfo.v0.max_vcpu_id : \ - dominfo.v2.max_vcpu_id) + (dom_interface_version < 5 ? \ + dominfo.v2.max_vcpu_id : \ + dominfo.v2d5.max_vcpu_id)) #define XEN_GETDOMAININFO_FLAGS(dominfo) \ (hypervisor_version < 2 ? \ dominfo.v0.flags : \ - dominfo.v2.flags) + (dom_interface_version < 5 ? \ + dominfo.v2.flags : \ + dominfo.v2d5.flags)) #define XEN_GETDOMAININFO_TOT_PAGES(dominfo) \ (hypervisor_version < 2 ? \ dominfo.v0.tot_pages : \ - dominfo.v2.tot_pages) + (dom_interface_version < 5 ? \ + dominfo.v2.tot_pages : \ + dominfo.v2d5.tot_pages)) #define XEN_GETDOMAININFO_MAX_PAGES(dominfo) \ (hypervisor_version < 2 ? \ dominfo.v0.max_pages : \ - dominfo.v2.max_pages) + (dom_interface_version < 5 ? \ + dominfo.v2.max_pages : \ + dominfo.v2d5.max_pages)) @@ -247,6 +297,18 @@ struct xen_v2_getdomaininfolistop { }; 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; + union { + struct xen_v2d5_getdomaininfo *v; + uint64_t pad ALIGN_64; + } buffer; + uint32_t num_domains; +}; +typedef struct xen_v2s3_getdomaininfolistop xen_v2s3_getdomaininfolistop; + struct xen_v0_domainop { @@ -294,6 +356,11 @@ struct xen_v2_setmaxmem { }; 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 informations for an setmaxvcpu system hypercall */ @@ -340,6 +407,20 @@ struct xen_v2_setvcpumap { }; typedef struct xen_v2_setvcpumap xen_v2_setvcpumap; +/* HV version 2, Dom version 5 requires 64-bit alignment */ +struct xen_v2d5_cpumap { + union { + uint8_t *v; + uint64_t pad ALIGN_64; + } bitmap; + 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 informations for an vcpuinfo system hypercall */ @@ -370,11 +451,22 @@ struct xen_v2_vcpuinfo { }; 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; /* * The hypercall operation structures also have changed on @@ -402,7 +494,8 @@ struct xen_op_v2_sys { uint32_t cmd; uint32_t interface_version; union { - xen_v2_getdomaininfolistop getdomaininfolist; + xen_v2_getdomaininfolistop getdomaininfolist; + xen_v2s3_getdomaininfolistop getdomaininfolists3; uint8_t padding[128]; } u; }; @@ -415,10 +508,14 @@ struct xen_op_v2_dom { domid_t domain; union { xen_v2_setmaxmem setmaxmem; + xen_v2d5_setmaxmem setmaxmemd5; xen_v2_setmaxvcpu setmaxvcpu; xen_v2_setvcpumap setvcpumap; + xen_v2d5_setvcpumap setvcpumapd5; xen_v2_vcpuinfo getvcpuinfo; + xen_v2d5_vcpuinfo getvcpuinfod5; xen_v2_getvcpumap getvcpumap; + xen_v2d5_getvcpumap getvcpumapd5; uint8_t padding[128]; } u; }; @@ -726,13 +823,26 @@ virXen_getdomaininfolist(int handle, int memset(&op, 0, sizeof(op)); op.cmd = XEN_V2_OP_GETDOMAININFOLIST; - 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; + + if (sys_interface_version < 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) - ret = op.u.getdomaininfolist.num_domains; + + if (ret == 0) { + if (sys_interface_version < 3) + ret = op.u.getdomaininfolist.num_domains; + else + ret = op.u.getdomaininfolists3.num_domains; + } } else if (hypervisor_version == 1) { xen_op_v1 op; @@ -921,7 +1031,10 @@ virXen_setmaxmem(int handle, int id, uns memset(&op, 0, sizeof(op)); op.cmd = XEN_V2_OP_SETMAXMEM; op.domain = (domid_t) id; - op.u.setmaxmem.maxmem = memory; + if (dom_interface_version < 5) + op.u.setmaxmem.maxmem = memory; + else + op.u.setmaxmemd5.maxmem = memory; ret = xenHypervisorDoV2Dom(handle, &op); } else if (hypervisor_version == 1) { xen_op_v1 op; @@ -1014,10 +1127,17 @@ virXen_setvcpumap(int handle, int id, un memset(&op, 0, sizeof(op)); op.cmd = XEN_V2_OP_SETVCPUMAP; op.domain = (domid_t) id; - op.u.setvcpumap.vcpu = vcpu; - op.u.setvcpumap.cpumap.bitmap = cpumap; - op.u.setvcpumap.cpumap.nr_cpus = maplen * 8; + if (dom_interface_version < 5) { + op.u.setvcpumap.vcpu = vcpu; + op.u.setvcpumap.cpumap.bitmap = cpumap; + op.u.setvcpumap.cpumap.nr_cpus = maplen * 8; + } else { + op.u.setvcpumapd5.vcpu = vcpu; + op.u.setvcpumapd5.cpumap.bitmap.v = cpumap; + op.u.setvcpumapd5.cpumap.nr_cpus = maplen * 8; + } ret = xenHypervisorDoV2Dom(handle, &op); + if (munlock(cpumap, maplen) < 0) { virXenError(VIR_ERR_XEN_CALL, " release", maplen); ret = -1; @@ -1082,29 +1202,56 @@ virXen_getvcpusinfo(int handle, int id, memset(&op, 0, sizeof(op)); op.cmd = XEN_V2_OP_GETVCPUINFO; op.domain = (domid_t) id; - op.u.getvcpuinfo.vcpu = (uint16_t) vcpu; + if (dom_interface_version < 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 (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 (dom_interface_version < 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 (mlock(cpumap, maplen) < 0) { virXenError(VIR_ERR_XEN_CALL, " locking", maplen); return (-1); } + memset(cpumap, 0, maplen); memset(&op, 0, sizeof(op)); op.cmd = XEN_V2_OP_GETVCPUMAP; op.domain = (domid_t) id; - op.u.setvcpumap.vcpu = vcpu; - op.u.setvcpumap.cpumap.bitmap = cpumap; - op.u.setvcpumap.cpumap.nr_cpus = maplen * 8; + if (dom_interface_version < 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 (munlock(cpumap, maplen) < 0) { virXenError(VIR_ERR_XEN_CALL, " release", maplen); @@ -1802,10 +1949,18 @@ xenHypervisorNumOfDomains(virConnectPtr return (-1); nbids = ret; + /* Can't possibly have more than 10,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 (nbids == maxids) { - last_maxids *= 2; - maxids *= 2; - goto retry; + if (maxids < 10000) { + last_maxids *= 2; + maxids *= 2; + goto retry; + } + nbids = -1; } if ((nbids < 0) || (nbids > maxids)) return(-1); @@ -1994,7 +2149,8 @@ xenHypervisorGetDomInfo(virConnectPtr co return (-1); domain_flags = XEN_GETDOMAININFO_FLAGS(dominfo); - domain_state = domain_flags & 0xFF; + 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; Index: xen_unified.c =================================================================== RCS file: /data/cvs/libvirt/src/xen_unified.c,v retrieving revision 1.3 diff -u -p -r1.3 xen_unified.c --- xen_unified.c 10 Apr 2007 13:00:26 -0000 1.3 +++ xen_unified.c 12 Apr 2007 00:52:26 -0000 @@ -546,13 +546,14 @@ xenUnifiedDomainGetVcpus (virDomainPtr d virVcpuInfoPtr info, int maxinfo, unsigned char *cpumaps, int maplen) { - int i; + int i, ret; for (i = 0; i < nb_drivers; ++i) - if (drivers[i]->domainGetVcpus && - drivers[i]->domainGetVcpus (dom, info, maxinfo, cpumaps, maplen) == 0) - return 0; - + if (drivers[i]->domainGetVcpus) { + ret = drivers[i]->domainGetVcpus (dom, info, maxinfo, cpumaps, maplen); + if (ret > 0) + return ret; + } return -1; } Index: xend_internal.c =================================================================== RCS file: /data/cvs/libvirt/src/xend_internal.c,v retrieving revision 1.107 diff -u -p -r1.107 xend_internal.c --- xend_internal.c 11 Apr 2007 16:06:30 -0000 1.107 +++ xend_internal.c 12 Apr 2007 00:52:28 -0000 @@ -1151,7 +1151,7 @@ xend_detect_config_version(virConnectPtr priv->xendConfigVersion = 1; } sexpr_free(root); - return priv->xendConfigVersion; + return (0); } /** @@ -1251,13 +1251,14 @@ xend_log(virConnectPtr xend, char *buffe * @node: the root of the parsed S-Expression * @buf: output buffer object * @hvm: true or 1 if no contains HVM S-Expression + * @bootloader: true or 1 if a bootloader is defined * * Parse the xend sexp for description of os and append it to buf. * * Returns 0 in case of success and -1 in case of error */ static int -xend_parse_sexp_desc_os(virConnectPtr xend, struct sexpr *node, virBufferPtr buf, int hvm) +xend_parse_sexp_desc_os(virConnectPtr xend, struct sexpr *node, virBufferPtr buf, int hvm, int bootloader) { const char *tmp; @@ -1269,37 +1270,44 @@ xend_parse_sexp_desc_os(virConnectPtr xe if (hvm) { virBufferVSprintf(buf, " <type>hvm</type>\n"); tmp = sexpr_node(node, "domain/image/hvm/kernel"); - if (tmp == NULL) { + if (tmp == NULL && !bootloader) { virXendError(xend, VIR_ERR_INTERNAL_ERROR, - _("domain information incomplete, missing kernel")); + _("domain information incomplete, missing kernel & bootloader")); return(-1); - } - virBufferVSprintf(buf, " <loader>%s</loader>\n", tmp); + } + if (tmp) + virBufferVSprintf(buf, " <loader>%s</loader>\n", tmp); tmp = sexpr_node(node, "domain/image/hvm/boot"); if ((tmp != NULL) && (tmp[0] != 0)) { - if (tmp[0] == 'a') - /* XXX no way to deal with boot from 2nd floppy */ - virBufferAdd(buf, " <boot dev='fd'/>\n", 21 ); - else if (tmp[0] == 'c') - /* - * Don't know what to put here. Say the vm has been given 3 - * disks - hda, hdb, hdc. How does one identify the boot disk? - * We're going to assume that first disk is the boot disk since - * this is most common practice - */ - virBufferAdd(buf, " <boot dev='hd'/>\n", 21 ); - else if (strcmp(tmp, "d") == 0) - virBufferAdd(buf, " <boot dev='cdrom'/>\n", 24 ); + while (*tmp) { + if (*tmp == 'a') + /* XXX no way to deal with boot from 2nd floppy */ + virBufferAdd(buf, " <boot dev='fd'/>\n", 21 ); + else if (*tmp == 'c') + /* + * Don't know what to put here. Say the vm has been given 3 + * disks - hda, hdb, hdc. How does one identify the boot disk? + * We're going to assume that first disk is the boot disk since + * this is most common practice + */ + virBufferAdd(buf, " <boot dev='hd'/>\n", 21 ); + else if (*tmp == 'd') + virBufferAdd(buf, " <boot dev='cdrom'/>\n", 24 ); + else if (*tmp == 'n') + virBufferAdd(buf, " <boot dev='network'/>\n", 26 ); + tmp++; + } } } else { virBufferVSprintf(buf, " <type>linux</type>\n"); tmp = sexpr_node(node, "domain/image/linux/kernel"); - if (tmp == NULL) { + if (tmp == NULL && !bootloader) { virXendError(xend, VIR_ERR_INTERNAL_ERROR, - _("domain information incomplete, missing kernel")); + _("domain information incomplete, missing kernel & bootloader")); return(-1); - } - virBufferVSprintf(buf, " <kernel>%s</kernel>\n", tmp); + } + if (tmp) + virBufferVSprintf(buf, " <kernel>%s</kernel>\n", tmp); tmp = sexpr_node(node, "domain/image/linux/ramdisk"); if ((tmp != NULL) && (tmp[0] != 0)) virBufferVSprintf(buf, " <initrd>%s</initrd>\n", tmp); @@ -1334,7 +1342,7 @@ xend_parse_sexp_desc(virConnectPtr conn, const char *tmp; char *tty; virBuffer buf; - int hvm = 0; + int hvm = 0, bootloader = 0; int domid = -1; int max_mem, cur_mem; @@ -1385,12 +1393,14 @@ xend_parse_sexp_desc(virConnectPtr conn, virBufferVSprintf(&buf, " <uuid>%s</uuid>\n", compact); } tmp = sexpr_node(root, "domain/bootloader"); - if (tmp != NULL) + if (tmp != NULL) { + bootloader = 1; virBufferVSprintf(&buf, " <bootloader>%s</bootloader>\n", tmp); + } if (sexpr_lookup(root, "domain/image")) { hvm = sexpr_lookup(root, "domain/image/hvm") ? 1 : 0; - xend_parse_sexp_desc_os(conn, root, &buf, hvm); + xend_parse_sexp_desc_os(conn, root, &buf, hvm, bootloader); } max_mem = (int) (sexpr_u64(root, "domain/maxmem") << 10); @@ -1612,9 +1622,9 @@ xend_parse_sexp_desc(virConnectPtr conn, tmp2); virBufferAdd(&buf, " </interface>\n", 17); - } else if (!hvm && - sexpr_lookup(node, "device/vfb")) { - /* New style graphics config for PV guests only in 3.0.4 */ + } else if (sexpr_lookup(node, "device/vfb")) { + /* New style graphics config for PV guests in >= 3.0.4, + * or for HVM guests in >= 3.0.5 */ tmp = sexpr_node(node, "device/vfb/type"); if (tmp && !strcmp(tmp, "sdl")) { @@ -1663,7 +1673,7 @@ xend_parse_sexp_desc(virConnectPtr conn, } } - /* Graphics device (HVM, or old (pre-3.0.4) style PV vnc config) */ + /* Graphics device (HVM <= 3.0.4, or PV <= 3.0.4) vnc config */ tmp = sexpr_fmt_node(root, "domain/image/%s/vnc", hvm ? "hvm" : "linux"); if (tmp != NULL) { if (tmp[0] == '1') { @@ -1901,7 +1911,6 @@ xenDaemonOpen(virConnectPtr conn, const { xmlURIPtr uri = NULL; int ret; - unsigned long version; /* If the name is just "xen" (it might originally have been NULL, see * xenUnifiedOpen) then try default paths and methods to get to the @@ -1914,8 +1923,8 @@ xenDaemonOpen(virConnectPtr conn, const ret = xenDaemonOpen_unix(conn, "/var/lib/xend/xend-socket"); if (ret < 0) goto try_http; - ret = xenDaemonGetVersion(conn, &version); - if (ret == 0) + ret = xend_detect_config_version(conn); + if (ret != -1) goto done; try_http: @@ -1925,8 +1934,8 @@ xenDaemonOpen(virConnectPtr conn, const ret = xenDaemonOpen_tcp(conn, "localhost", 8000); if (ret < 0) goto failed; - ret = xenDaemonGetVersion(conn, &version); - if (ret < 0) + ret = xend_detect_config_version(conn); + if (ret == -1) goto failed; } else { /* @@ -1950,15 +1959,15 @@ xenDaemonOpen(virConnectPtr conn, const if (ret < 0) goto failed; - ret = xenDaemonGetVersion(conn, &version); - if (ret < 0) + ret = xend_detect_config_version(conn); + if (ret == -1) goto failed; } else if (!strcasecmp(uri->scheme, "http")) { ret = xenDaemonOpen_tcp(conn, uri->server, uri->port); if (ret < 0) goto failed; - ret = xenDaemonGetVersion(conn, &version); - if (ret < 0) + ret = xend_detect_config_version(conn); + if (ret == -1) goto failed; } else { if (!(flags & VIR_DRV_OPEN_QUIET)) @@ -1967,17 +1976,7 @@ xenDaemonOpen(virConnectPtr conn, const } } - done: - /* The XenD config version is used to determine - * which APIs / features to activate. Lookup & cache - * it now to avoid repeated HTTP calls - */ - if (xend_detect_config_version(conn) < 0) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "cannot determine xend config version"); - goto failed; - } - + done: if (uri != NULL) xmlFreeURI(uri); return(ret); @@ -2962,7 +2961,7 @@ xenDaemonCreateLinux(virConnectPtr conn, return (NULL); } - + printf("%s\n", sexpr); ret = xenDaemonDomainCreateLinux(conn, sexpr); free(sexpr); if (ret != 0) { Index: xml.c =================================================================== RCS file: /data/cvs/libvirt/src/xml.c,v retrieving revision 1.71 diff -u -p -r1.71 xml.c --- xml.c 11 Apr 2007 16:06:30 -0000 1.71 +++ xml.c 12 Apr 2007 00:52:29 -0000 @@ -591,7 +591,8 @@ virDomainParseXMLOSDescHVM(virConnectPtr xmlNodePtr cur, txt; xmlChar *type = NULL; xmlChar *loader = NULL; - xmlChar *boot_dev = NULL; + char bootorder[5]; + int nbootorder = 0; int res; char *str; @@ -610,13 +611,32 @@ virDomainParseXMLOSDescHVM(virConnectPtr if ((txt != NULL) && (txt->type == XML_TEXT_NODE) && (txt->next == NULL)) loader = txt->content; - } else if ((boot_dev == NULL) && - (xmlStrEqual(cur->name, BAD_CAST "boot"))) { - boot_dev = xmlGetProp(cur, BAD_CAST "dev"); + } else if ((xmlStrEqual(cur->name, BAD_CAST "boot"))) { + xmlChar *boot_dev = xmlGetProp(cur, BAD_CAST "dev"); + if (nbootorder == ((sizeof(bootorder)/sizeof(bootorder[0]))-1)) { + virXMLError(conn, VIR_ERR_XML_ERROR, "too many boot devices", 0); + return (-1); + } + if (xmlStrEqual(boot_dev, BAD_CAST "fd")) { + bootorder[nbootorder++] = 'a'; + } else if (xmlStrEqual(boot_dev, BAD_CAST "cdrom")) { + bootorder[nbootorder++] = 'd'; + } else if (xmlStrEqual(boot_dev, BAD_CAST "network")) { + bootorder[nbootorder++] = 'n'; + } else if (xmlStrEqual(boot_dev, BAD_CAST "hd")) { + bootorder[nbootorder++] = 'c'; + } else { + xmlFree(boot_dev); + /* Any other type of boot dev is unsupported right now */ + virXMLError(conn, VIR_ERR_XML_ERROR, NULL, 0); + return (-1); + } + xmlFree(boot_dev); } } cur = cur->next; } + bootorder[nbootorder] = '\0'; if ((type == NULL) || (!xmlStrEqual(type, BAD_CAST "hvm"))) { /* VIR_ERR_OS_TYPE */ virXMLError(conn, VIR_ERR_OS_TYPE, (const char *) type, 0); @@ -641,73 +661,58 @@ virDomainParseXMLOSDescHVM(virConnectPtr virBufferVSprintf(buf, "(vcpus %d)", vcpus); - if (boot_dev) { - if (xmlStrEqual(boot_dev, BAD_CAST "fd")) { - virBufferVSprintf(buf, "(boot a)" /*, (const char *) boot_dev*/); - } else if (xmlStrEqual(boot_dev, BAD_CAST "cdrom")) { - virBufferVSprintf(buf, "(boot d)" /*, (const char *) boot_dev*/); - } else if (xmlStrEqual(boot_dev, BAD_CAST "hd")) { - virBufferVSprintf(buf, "(boot c)" /*, (const char *) boot_dev*/); - } else { - /* Any other type of boot dev is unsupported right now */ - virXMLError(conn, VIR_ERR_XML_ERROR, NULL, 0); - } + if (nbootorder) + virBufferVSprintf(buf, "(boot %s)", bootorder); - /* get the 1st floppy device file */ - cur = virXPathNode( - "/domain/devices/disk[@device='floppy' and target/@dev='fda']/source", - ctxt); + /* get the 1st floppy device file */ + cur = virXPathNode("/domain/devices/disk[@device='floppy' and target/@dev='fda']/source", + ctxt); if (cur != NULL) { - xmlChar *fdfile; - - fdfile = xmlGetProp(cur, BAD_CAST "file"); + xmlChar *fdfile; + fdfile = xmlGetProp(cur, BAD_CAST "file"); if (fdfile != NULL) { - virBufferVSprintf(buf, "(fda '%s')", fdfile); - free(fdfile); + virBufferVSprintf(buf, "(fda '%s')", fdfile); + free(fdfile); } - } + } - /* get the 2nd floppy device file */ - cur = virXPathNode( - "/domain/devices/disk[@device='floppy' and target/@dev='fdb']/source", - ctxt); + /* get the 2nd floppy device file */ + cur = virXPathNode("/domain/devices/disk[@device='floppy' and target/@dev='fdb']/source", + ctxt); if (cur != NULL) { - xmlChar *fdfile; - - fdfile = xmlGetProp(cur, BAD_CAST "file"); + xmlChar *fdfile; + fdfile = xmlGetProp(cur, BAD_CAST "file"); if (fdfile != NULL) { - virBufferVSprintf(buf, "(fdb '%s')", fdfile); - free(fdfile); + virBufferVSprintf(buf, "(fdb '%s')", fdfile); + free(fdfile); } - } + } - /* get the cdrom device file */ - /* Only XenD <= 3.0.2 wants cdrom config here */ - if (xendConfigVersion == 1) { - cur = virXPathNode( - "/domain/devices/disk[@device='cdrom' and target/@dev='hdc']/source", + /* get the cdrom device file */ + /* Only XenD <= 3.0.2 wants cdrom config here */ + if (xendConfigVersion == 1) { + cur = virXPathNode("/domain/devices/disk[@device='cdrom' and target/@dev='hdc']/source", ctxt); if (cur != NULL) { - xmlChar *cdfile; + xmlChar *cdfile; - cdfile = xmlGetProp(cur, BAD_CAST "file"); - if (cdfile != NULL) { - virBufferVSprintf(buf, "(cdrom '%s')", - (const char *)cdfile); - xmlFree(cdfile); - } + cdfile = xmlGetProp(cur, BAD_CAST "file"); + if (cdfile != NULL) { + virBufferVSprintf(buf, "(cdrom '%s')", + (const char *)cdfile); + xmlFree(cdfile); } } - - if (virXPathNode("/domain/features/acpi", ctxt) != NULL) - virBufferAdd(buf, "(acpi 1)", 8); - if (virXPathNode("/domain/features/apic", ctxt) != NULL) - virBufferAdd(buf, "(apic 1)", 8); - if (virXPathNode("/domain/features/pae", ctxt) != NULL) - virBufferAdd(buf, "(pae 1)", 7); } + if (virXPathNode("/domain/features/acpi", ctxt) != NULL) + virBufferAdd(buf, "(acpi 1)", 8); + if (virXPathNode("/domain/features/apic", ctxt) != NULL) + virBufferAdd(buf, "(apic 1)", 8); + if (virXPathNode("/domain/features/pae", ctxt) != NULL) + virBufferAdd(buf, "(pae 1)", 7); + res = virXPathBoolean("count(domain/devices/console) > 0", ctxt); if (res < 0) { virXMLError(conn, VIR_ERR_XML_ERROR, NULL, 0); @@ -717,25 +722,24 @@ virDomainParseXMLOSDescHVM(virConnectPtr virBufferAdd(buf, "(serial pty)", 12); } - /* Is a graphics device specified? */ - cur = virXPathNode("/domain/devices/graphics[1]", ctxt); - if (cur != NULL) { - res = virDomainParseXMLGraphicsDescImage(conn, cur, buf, - xendConfigVersion); - if (res != 0) { - goto error; + /* HVM graphics for xen <= 3.0.5 */ + if (xendConfigVersion < 4) { + /* Is a graphics device specified? */ + cur = virXPathNode("/domain/devices/graphics[1]", ctxt); + if (cur != NULL) { + res = virDomainParseXMLGraphicsDescImage(conn, cur, buf, + xendConfigVersion); + if (res != 0) { + goto error; + } } } virBufferAdd(buf, "))", 2); - if (boot_dev) - xmlFree(boot_dev); - return (0); + error: - if (boot_dev) - xmlFree(boot_dev); return(-1); } @@ -821,13 +825,12 @@ virDomainParseXMLOSDescPV(virConnectPtr if (cmdline != NULL) virBufferVSprintf(buf, "(args '%s')", (const char *) cmdline); - /* Is a graphics device specified? */ - /* Old style config before merge of PVFB */ + /* PV graphics for xen <= 3.0.4 */ if (xendConfigVersion < 3) { cur = virXPathNode("/domain/devices/graphics[1]", ctxt); if (cur != NULL) { res = virDomainParseXMLGraphicsDescImage(conn, cur, buf, - xendConfigVersion); + xendConfigVersion); if (res != 0) { goto error; } @@ -1326,7 +1329,7 @@ virDomainParseXMLDesc(virConnectPtr conn goto error; } } - free(nodes); + free(nodes); } nb_nodes = virXPathNodeSet("/domain/devices/interface", ctxt, &nodes); @@ -1340,21 +1343,23 @@ virDomainParseXMLDesc(virConnectPtr conn } virBufferAdd(&buf, ")", 1); } - free(nodes); + free(nodes); } - /* New style PVFB config - 3.0.4 merge */ - if (xendConfigVersion >= 3 && !hvm) { + /* New style PV graphics config xen >= 3.0.4, + * or HVM graphics config xen >= 3.0.5 */ + if ((xendConfigVersion >= 3 && !hvm) || + (xendConfigVersion >= 4 && hvm)) { nb_nodes = virXPathNodeSet("/domain/devices/graphics", ctxt, &nodes); - if (nb_nodes > 0) { + if (nb_nodes > 0) { for (i = 0; i < nb_nodes; i++) { res = virDomainParseXMLGraphicsDescVFB(conn, nodes[i], &buf); if (res != 0) { - free(nodes); + free(nodes); goto error; } } - free(nodes); + free(nodes); } }