/* * xen_internal.c: direct access to Xen hypervisor level * * Copyright (C) 2005 Red Hat, Inc. * * See COPYING.LIB for the License of this software * * Daniel Veillard */ #include #include /* required for uint8_t, uint32_t, etc ... */ #include #include #include #include #include #include #include #include /* required for dom0_getdomaininfo_t */ #include #include #include #include /* #ifndef __LINUX_PUBLIC_PRIVCMD_H__ */ typedef struct old_hypercall_struct { unsigned long op; unsigned long arg[5]; } old_hypercall_t; #define XEN_OLD_IOCTL_HYPERCALL_CMD \ _IOC(_IOC_NONE, 'P', 0, sizeof(old_hypercall_t)) typedef struct privcmd_hypercall hypercall_t; #define XEN_IOCTL_HYPERCALL_CMD IOCTL_PRIVCMD_HYPERCALL static int xen_ioctl_hypercall_cmd = 0; static int old_hypervisor = 0; static int initialized = 0; static int hv_version = 0; #include "internal.h" #include "driver.h" #include "xen_internal.h" #define XEN_HYPERVISOR_SOCKET "/proc/xen/privcmd" static const char * xenHypervisorGetType(virConnectPtr conn); static unsigned long xenHypervisorGetMaxMemory(virDomainPtr domain); static int xenHypervisorInit(void); #ifndef XEN_RO static virDriver xenHypervisorDriver = { VIR_DRV_XEN_HYPERVISOR, "Xen", (DOM0_INTERFACE_VERSION >> 24) * 1000000 + ((DOM0_INTERFACE_VERSION >> 16) & 0xFF) * 1000 + (DOM0_INTERFACE_VERSION & 0xFFFF), xenHypervisorInit, /* init */ xenHypervisorOpen, /* open */ xenHypervisorClose, /* close */ xenHypervisorGetType, /* type */ xenHypervisorGetVersion, /* version */ NULL, /* nodeGetInfo */ xenHypervisorListDomains, /* listDomains */ xenHypervisorNumOfDomains, /* numOfDomains */ NULL, /* domainCreateLinux */ NULL, /* domainLookupByID */ NULL, /* domainLookupByUUID */ NULL, /* domainLookupByName */ xenHypervisorPauseDomain, /* domainSuspend */ xenHypervisorResumeDomain, /* domainResume */ NULL, /* domainShutdown */ NULL, /* domainReboot */ xenHypervisorDestroyDomain, /* domainDestroy */ NULL, /* domainFree */ NULL, /* domainGetName */ NULL, /* domainGetID */ NULL, /* domainGetUUID */ NULL, /* domainGetOSType */ xenHypervisorGetMaxMemory, /* domainGetMaxMemory */ xenHypervisorSetMaxMemory, /* domainSetMaxMemory */ NULL, /* domainSetMemory */ xenHypervisorGetDomainInfo, /* domainGetInfo */ NULL, /* domainSave */ NULL /* domainRestore */ }; #endif /* !XEN_RO */ /** * virXenError: * @conn: the connection if available * @error: the error number * @info: extra information string * * Handle an error at the xend daemon interface */ static void virXenError(virErrorNumber error, const char *info, int value) { const char *errmsg; if (error == VIR_ERR_OK) return; errmsg = __virErrorMsg(error, info); __virRaiseError(NULL, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR, errmsg, info, NULL, value, 0, errmsg, info, value); } /** * xenHypervisorInit: * * Initialize the hypervisor layer. Try to detect the kind of interface * used i.e. pre or post changeset 10277 */ int xenHypervisorInit(void) { int fd, ret, cmd; hypercall_t hc; old_hypercall_t old_hc; if (initialized) { if (old_hypervisor == -1) return(-1); return(0); } initialized = 1; ret = open(XEN_HYPERVISOR_SOCKET, O_RDWR); if (ret < 0) { old_hypervisor = -1; return (-1); } fd = ret; hc.op = __HYPERVISOR_xen_version; hc.arg[0] = (unsigned long) XENVER_version; hc.arg[1] = 0; cmd = IOCTL_PRIVCMD_HYPERCALL; ret = ioctl(fd, cmd, (unsigned long) &hc); if ((ret != -1) && (ret != 0)) { /* fprintf(stderr, "Using new hypervisor call: %X\n", ret); */ hv_version = ret; xen_ioctl_hypercall_cmd = cmd; old_hypervisor = 0; goto done; } old_hc.op = __HYPERVISOR_xen_version; old_hc.arg[0] = (unsigned long) XENVER_version; old_hc.arg[1] = 0; cmd = _IOC(_IOC_NONE, 'P', 0, sizeof(old_hypercall_t)); ret = ioctl(fd, cmd, (unsigned long) &old_hc); if ((ret != -1) && (ret != 0)) { /* fprintf(stderr, "Using old hypervisor call: %X\n", ret); */ hv_version = ret; xen_ioctl_hypercall_cmd = cmd; old_hypervisor = 1; goto done; } old_hypervisor = -1; virXenError(VIR_ERR_XEN_CALL, " ioctl ", IOCTL_PRIVCMD_HYPERCALL); close(fd); return(-1); done: close(fd); return(0); } #ifndef XEN_RO /** * xenHypervisorRegister: * * Registers the xenHypervisor driver */ void xenHypervisorRegister(void) { if (initialized == 0) xenHypervisorInit(); virRegisterDriver(&xenHypervisorDriver); } #endif /* !XEN_RO */ /** * 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, const char *name, int flags) { int ret; if (initialized == 0) xenHypervisorInit(); if ((name != NULL) && (strcasecmp(name, "xen"))) return(-1); conn->handle = -1; ret = open(XEN_HYPERVISOR_SOCKET, O_RDWR); if (ret < 0) { if (!(flags & VIR_DRV_OPEN_QUIET)) virXenError(VIR_ERR_NO_XEN, XEN_HYPERVISOR_SOCKET, 0); return (-1); } conn->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; if ((conn == NULL) || (conn->handle < 0)) return (-1); ret = close(conn->handle); if (ret < 0) return (-1); return (0); } /** * xenHypervisorDoOldOp: * @handle: the handle to the Xen hypervisor * @op: pointer to the hyperviros operation structure * * Do an hypervisor operation though the old interface, * this leads to an hypervisor call through ioctl. * * Returns 0 in case of success and -1 in case of error. */ static int xenHypervisorDoOldOp(int handle, dom0_op_t * op) { int ret; old_hypercall_t hc; memset(&hc, 0, sizeof(hc)); op->interface_version = hv_version << 8; hc.op = __HYPERVISOR_dom0_op; hc.arg[0] = (unsigned long) op; if (mlock(op, sizeof(dom0_op_t)) < 0) { virXenError(VIR_ERR_XEN_CALL, " locking", sizeof(dom0_op_t)); return (-1); } ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); if (ret < 0) { virXenError(VIR_ERR_XEN_CALL, " ioctl ", xen_ioctl_hypercall_cmd); } if (munlock(op, sizeof(dom0_op_t)) < 0) { virXenError(VIR_ERR_XEN_CALL, " releasing", sizeof(dom0_op_t)); ret = -1; } if (ret < 0) return (-1); return (0); } /** * xenHypervisorDoOp: * @handle: the handle to the Xen hypervisor * @op: pointer to the hyperviros operation structure * * Do an hypervisor operation, this leads to an hypervisor call through ioctl. * * Returns 0 in case of success and -1 in case of error. */ static int xenHypervisorDoOp(int handle, dom0_op_t * op) { int ret; hypercall_t hc; if (old_hypervisor) return(xenHypervisorDoOldOp(handle, op)); memset(&hc, 0, sizeof(hc)); op->interface_version = DOM0_INTERFACE_VERSION; hc.op = __HYPERVISOR_dom0_op; hc.arg[0] = (unsigned long) op; if (mlock(op, sizeof(dom0_op_t)) < 0) { virXenError(VIR_ERR_XEN_CALL, " locking", sizeof(dom0_op_t)); return (-1); } ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); if (ret < 0) { virXenError(VIR_ERR_XEN_CALL, " ioctl ", xen_ioctl_hypercall_cmd); } if (munlock(op, sizeof(dom0_op_t)) < 0) { virXenError(VIR_ERR_XEN_CALL, " releasing", sizeof(dom0_op_t)); ret = -1; } if (ret < 0) return (-1); return (0); } /** * xenHypervisorGetType: * @conn: pointer to the Xen Hypervisor block * * Get the version level of the Hypervisor running. * * Returns -1 in case of error, 0 otherwise. if the version can't be * extracted by lack of capacities returns 0 and @hvVer is 0, otherwise * @hvVer value is major * 1,000,000 + minor * 1,000 + release */ static const char * xenHypervisorGetType(virConnectPtr conn) { if (!VIR_IS_CONNECT(conn)) { virXenError(VIR_ERR_INVALID_CONN, __FUNCTION__, 0); return (NULL); } return("Xen"); } /** * 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, unsigned long *hvVer) { if ((conn == NULL) || (conn->handle < 0) || (hvVer == NULL)) return (-1); *hvVer = (hv_version >> 16) * 1000000 + (hv_version & 0xFFFF) * 1000; return(0); } /** * xenHypervisorNumOfDomains: * @conn: pointer to the connection block * * Provides the number of active domains. * * Returns the number of domain found or -1 in case of error */ int xenHypervisorNumOfDomains(virConnectPtr conn) { dom0_op_t op; dom0_getdomaininfo_t *dominfos; int ret, nbids; static int last_maxids = 2; int maxids = last_maxids; if ((conn == NULL) || (conn->handle < 0)) return (-1); retry: dominfos = malloc(maxids * sizeof(dom0_getdomaininfo_t)); if (dominfos == NULL) { virXenError(VIR_ERR_NO_MEMORY, "failed to allocate %d domain info", maxids); return(-1); } memset(dominfos, 0, sizeof(dom0_getdomaininfo_t) * maxids); if (mlock(dominfos, sizeof(dom0_getdomaininfo_t) * maxids) < 0) { virXenError(VIR_ERR_XEN_CALL, " locking", sizeof(dom0_getdomaininfo_t) * maxids); free(dominfos); return (-1); } op.cmd = DOM0_GETDOMAININFOLIST; op.u.getdomaininfolist.first_domain = (domid_t) 0; op.u.getdomaininfolist.max_domains = maxids; op.u.getdomaininfolist.buffer = dominfos; op.u.getdomaininfolist.num_domains = maxids; ret = xenHypervisorDoOp(conn->handle, &op); if (munlock(dominfos, sizeof(dom0_getdomaininfo_t) * maxids) < 0) { virXenError(VIR_ERR_XEN_CALL, " release", sizeof(dom0_getdomaininfo_t) * maxids); ret = -1; } free(dominfos); if (ret < 0) return (-1); nbids = op.u.getdomaininfolist.num_domains; if (nbids == maxids) { last_maxids *= 2; maxids *= 2; goto retry; } if ((nbids < 0) || (nbids > maxids)) return(-1); return(nbids); } /** * xenHypervisorListDomains: * @conn: pointer to the connection block * @ids: array to collect the list of IDs of active domains * @maxids: size of @ids * * Collect the list of active domains, and store their ID in @maxids * * Returns the number of domain found or -1 in case of error */ int xenHypervisorListDomains(virConnectPtr conn, int *ids, int maxids) { dom0_op_t op; dom0_getdomaininfo_t *dominfos; int ret, nbids, i; if ((conn == NULL) || (conn->handle < 0) || (ids == NULL) || (maxids < 1)) return (-1); dominfos = malloc(maxids * sizeof(dom0_getdomaininfo_t)); if (dominfos == NULL) { virXenError(VIR_ERR_NO_MEMORY, "failed to allocate %d domain info", maxids); return(-1); } memset(dominfos, 0, sizeof(dom0_getdomaininfo_t) * maxids); memset(ids, 0, maxids * sizeof(int)); if (mlock(dominfos, sizeof(dom0_getdomaininfo_t) * maxids) < 0) { virXenError(VIR_ERR_XEN_CALL, " locking", sizeof(dom0_getdomaininfo_t) * maxids); free(dominfos); return (-1); } op.cmd = DOM0_GETDOMAININFOLIST; op.u.getdomaininfolist.first_domain = (domid_t) 0; op.u.getdomaininfolist.max_domains = maxids; op.u.getdomaininfolist.buffer = dominfos; op.u.getdomaininfolist.num_domains = maxids; ret = xenHypervisorDoOp(conn->handle, &op); if (munlock(dominfos, sizeof(dom0_getdomaininfo_t) * maxids) < 0) { virXenError(VIR_ERR_XEN_CALL, " release", sizeof(dom0_getdomaininfo_t) * maxids); ret = -1; } if (ret < 0) { free(dominfos); return (-1); } nbids = op.u.getdomaininfolist.num_domains; if ((nbids < 0) || (nbids > maxids)) { free(dominfos); return(-1); } for (i = 0;i < nbids;i++) { ids[i] = dominfos[i].domain; } free(dominfos); return (nbids); } /** * xenHypervisorGetDomMaxMemory: * @conn: connection data * @id: domain id * * 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 xenHypervisorGetDomMaxMemory(virConnectPtr conn, int id) { dom0_op_t op; dom0_getdomaininfo_t dominfo; int ret; if ((conn == NULL) || (conn->handle < 0)) return (0); memset(&dominfo, 0, sizeof(dom0_getdomaininfo_t)); if (mlock(&dominfo, sizeof(dom0_getdomaininfo_t)) < 0) { virXenError(VIR_ERR_XEN_CALL, " locking", sizeof(dom0_getdomaininfo_t)); return (0); } op.cmd = DOM0_GETDOMAININFOLIST; op.u.getdomaininfolist.first_domain = (domid_t) id; op.u.getdomaininfolist.max_domains = 1; op.u.getdomaininfolist.buffer = &dominfo; op.u.getdomaininfolist.num_domains = 1; dominfo.domain = id; ret = xenHypervisorDoOp(conn->handle, &op); if (munlock(&dominfo, sizeof(dom0_getdomaininfo_t)) < 0) { virXenError(VIR_ERR_XEN_CALL, " release", sizeof(dom0_getdomaininfo_t)); ret = -1; } if (ret < 0) return (0); return((unsigned long) dominfo.max_pages * 4); } /** * xenHypervisorGetMaxMemory: * @domain: a domain object or NULL * * Retrieve the maximum amount of physical memory allocated to a * domain. If domain is NULL, then this get the amount of memory reserved * to Domain0 i.e. the domain where the application runs. * * Returns the memory size in kilobytes or 0 in case of error. */ static unsigned long xenHypervisorGetMaxMemory(virDomainPtr domain) { if ((domain == NULL) || (domain->conn == NULL) || (domain->conn->handle < 0)) return (0); return(xenHypervisorGetDomMaxMemory(domain->conn, domain->handle)); } /** * xenHypervisorGetDomInfo: * @conn: connection data * @id: the domain ID * @info: the place where information should be stored * * Do an 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) { dom0_op_t op; dom0_getdomaininfo_t dominfo; int ret; if ((conn == NULL) || (conn->handle < 0) || (info == NULL)) return (-1); memset(info, 0, sizeof(virDomainInfo)); memset(&dominfo, 0, sizeof(dom0_getdomaininfo_t)); if (mlock(&dominfo, sizeof(dom0_getdomaininfo_t)) < 0) { virXenError(VIR_ERR_XEN_CALL, " locking", sizeof(dom0_getdomaininfo_t)); return (-1); } op.cmd = DOM0_GETDOMAININFOLIST; op.u.getdomaininfolist.first_domain = (domid_t) id; op.u.getdomaininfolist.max_domains = 1; op.u.getdomaininfolist.buffer = &dominfo; op.u.getdomaininfolist.num_domains = 1; dominfo.domain = id; ret = xenHypervisorDoOp(conn->handle, &op); if (munlock(&dominfo, sizeof(dom0_getdomaininfo_t)) < 0) { virXenError(VIR_ERR_XEN_CALL, " release", sizeof(dom0_getdomaininfo_t)); ret = -1; } if (ret < 0) return (-1); switch (dominfo.flags & 0xFF) { case DOMFLAGS_DYING: info->state = VIR_DOMAIN_SHUTDOWN; break; case DOMFLAGS_SHUTDOWN: 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_NONE; } /* * the API brings back the cpu time in nanoseconds, * convert to microseconds, same thing convert to * kilobytes from page counts */ info->cpuTime = dominfo.cpu_time; info->memory = dominfo.tot_pages * 4; info->maxMem = dominfo.max_pages * 4; info->nrVirtCpu = dominfo.nr_online_vcpus; return (0); } /** * xenHypervisorGetDomainInfo: * @domain: pointer to the domain block * @info: the place where information should be stored * * Do an hypervisor call to get the related set of domain information. * * Returns 0 in case of success, -1 in case of error. */ int xenHypervisorGetDomainInfo(virDomainPtr domain, virDomainInfoPtr info) { if ((domain == NULL) || (domain->conn == NULL) || (domain->conn->handle < 0) || (info == NULL) || (domain->handle < 0)) return (-1); return(xenHypervisorGetDomInfo(domain->conn, domain->handle, info)); } /** * xenHypervisorPauseDomain: * @domain: pointer to the domain block * * Do an hypervisor call to pause the given domain * * Returns 0 in case of success, -1 in case of error. */ int xenHypervisorPauseDomain(virDomainPtr domain) { dom0_op_t op; int ret; if ((domain == NULL) || (domain->conn == NULL) || (domain->conn->handle < 0)) return (-1); op.cmd = DOM0_PAUSEDOMAIN; op.u.pausedomain.domain = (domid_t) domain->handle; ret = xenHypervisorDoOp(domain->conn->handle, &op); if (ret < 0) return (-1); return (0); } /** * xenHypervisorResumeDomain: * @domain: pointer to the domain block * * Do an hypervisor call to resume the given domain * * Returns 0 in case of success, -1 in case of error. */ int xenHypervisorResumeDomain(virDomainPtr domain) { dom0_op_t op; int ret; if ((domain == NULL) || (domain->conn == NULL) || (domain->conn->handle < 0)) return (-1); op.cmd = DOM0_UNPAUSEDOMAIN; op.u.unpausedomain.domain = (domid_t) domain->handle; ret = xenHypervisorDoOp(domain->conn->handle, &op); if (ret < 0) return (-1); return (0); } /** * xenHypervisorDestroyDomain: * @domain: pointer to the domain block * * Do an hypervisor call to destroy the given domain * * Returns 0 in case of success, -1 in case of error. */ int xenHypervisorDestroyDomain(virDomainPtr domain) { dom0_op_t op; int ret; if ((domain == NULL) || (domain->conn == NULL) || (domain->conn->handle < 0)) return (-1); op.cmd = DOM0_DESTROYDOMAIN; op.u.destroydomain.domain = (domid_t) domain->handle; ret = xenHypervisorDoOp(domain->conn->handle, &op); if (ret < 0) return (-1); return (0); } /** * xenHypervisorSetMaxMemory: * @domain: pointer to the domain block * @memory: the max memory size in kilobytes. * * Do an hypervisor call to change the maximum amount of memory used * * Returns 0 in case of success, -1 in case of error. */ int xenHypervisorSetMaxMemory(virDomainPtr domain, unsigned long memory) { dom0_op_t op; int ret; if ((domain == NULL) || (domain->conn == NULL) || (domain->conn->handle < 0)) return (-1); op.cmd = DOM0_SETDOMAINMAXMEM; op.u.setdomainmaxmem.domain = (domid_t) domain->handle; op.u.setdomainmaxmem.max_memkb = memory; ret = xenHypervisorDoOp(domain->conn->handle, &op); if (ret < 0) return (-1); return (0); } /** * xenHypervisorCheckID: * @domain: pointer to the domain block * @info: the place where information should be stored * * Do an hypervisor call to verify the domain ID is valid * * Returns 0 in case of success, -1 in case of error. */ int xenHypervisorCheckID(virConnectPtr conn, int id) { dom0_op_t op; dom0_getdomaininfo_t dominfo; int ret; if ((conn->handle < 0) || (id < 0)) return (-1); memset(&dominfo, 0, sizeof(dom0_getdomaininfo_t)); if (mlock(&dominfo, sizeof(dom0_getdomaininfo_t)) < 0) { virXenError(VIR_ERR_XEN_CALL, " locking", sizeof(dom0_getdomaininfo_t)); return (-1); } op.cmd = DOM0_GETDOMAININFOLIST; op.u.getdomaininfolist.first_domain = (domid_t) id; op.u.getdomaininfolist.max_domains = 1; op.u.getdomaininfolist.buffer = &dominfo; op.u.getdomaininfolist.num_domains = 1; dominfo.domain = id; ret = xenHypervisorDoOp(conn->handle, &op); if (munlock(&dominfo, sizeof(dom0_getdomaininfo_t)) < 0) { virXenError(VIR_ERR_XEN_CALL, " release", sizeof(dom0_getdomaininfo_t)); ret = -1; } if (ret < 0) return (-1); return (0); }