xen_internal.c 14.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * 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 <veillard@redhat.com>
 */

#include <stdio.h>
#include <string.h>
13
/* required for uint8_t, uint32_t, etc ... */
14 15 16 17 18 19 20 21
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

22 23 24
#include <stdint.h>

/* required for dom0_getdomaininfo_t */
25
#include <xen/dom0_ops.h>
26
#include <xen/version.h>
27 28 29
#include <xen/xen.h>

#ifndef __LINUX_PUBLIC_PRIVCMD_H__
30
typedef struct hypercall_struct {
31 32 33 34 35 36 37
    unsigned long op;
    unsigned long arg[5];
} hypercall_t;
#endif


#include "internal.h"
38
#include "driver.h"
39 40 41 42
#include "xen_internal.h"

#define XEN_HYPERVISOR_SOCKET "/proc/xen/privcmd"

43 44
static const char * xenHypervisorGetType(virConnectPtr conn);

45 46
static virDriver xenHypervisorDriver = {
    "Xen",
47 48 49
    (DOM0_INTERFACE_VERSION >> 24) * 1000000 +
    ((DOM0_INTERFACE_VERSION >> 16) & 0xFF) * 1000 +
    (DOM0_INTERFACE_VERSION & 0xFFFF),
50 51 52
    NULL, /* init */
    xenHypervisorOpen, /* open */
    xenHypervisorClose, /* close */
53
    xenHypervisorGetType, /* type */
54
    xenHypervisorGetVersion, /* version */
55
    NULL, /* nodeGetInfo */
56 57
    xenHypervisorListDomains, /* listDomains */
    xenHypervisorNumOfDomains, /* numOfDomains */
58 59 60 61 62 63 64
    NULL, /* domainCreateLinux */
    NULL, /* domainLookupByID */
    NULL, /* domainLookupByUUID */
    NULL, /* domainLookupByName */
    xenHypervisorPauseDomain, /* domainSuspend */
    xenHypervisorResumeDomain, /* domainResume */
    NULL, /* domainShutdown */
65
    NULL, /* domainReboot */
66 67 68 69 70 71 72 73
    xenHypervisorDestroyDomain, /* domainDestroy */
    NULL, /* domainFree */
    NULL, /* domainGetName */
    NULL, /* domainGetID */
    NULL, /* domainGetUUID */
    NULL, /* domainGetOSType */
    NULL, /* domainGetMaxMemory */
    xenHypervisorSetMaxMemory, /* domainSetMaxMemory */
74
    NULL, /* domainSetMemory */
75 76 77 78 79
    xenHypervisorGetDomainInfo, /* domainGetInfo */
    NULL, /* domainSave */
    NULL /* domainRestore */
};

80 81 82 83 84 85 86 87 88 89
/**
 * xenHypervisorRegister:
 *
 * Registers the xenHypervisor driver
 */
void xenHypervisorRegister(void)
{
    virRegisterDriver(&xenHypervisorDriver);
}

90 91 92 93 94 95 96 97 98
/**
 * virXenError:
 * @conn: the connection if available
 * @error: the error number
 * @info: extra information string
 *
 * Handle an error at the xend daemon interface
 */
static void
99 100
virXenError(virErrorNumber error, const char *info, int value)
{
101
    const char *errmsg;
102

103 104 105 106 107
    if (error == VIR_ERR_OK)
        return;

    errmsg = __virErrorMsg(error, info);
    __virRaiseError(NULL, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR,
108
                    errmsg, info, NULL, value, 0, errmsg, info, value);
109 110
}

111 112
/**
 * xenHypervisorOpen:
113 114 115
 * @conn: pointer to the connection block
 * @name: URL for the target, NULL for local
 * @flags: combination of virDrvOpenFlag(s)
116 117 118
 *
 * Connects to the Xen hypervisor.
 *
119
 * Returns 0 or -1 in case of error.
120
 */
121
int
122
xenHypervisorOpen(virConnectPtr conn, const char *name, int flags)
123
{
124 125
    int ret;

126
    if ((name != NULL) && (strcasecmp(name, "xen")))
127 128 129 130
        return(-1);

    conn->handle = -1;

131
    ret = open(XEN_HYPERVISOR_SOCKET, O_RDWR);
132
    if (ret < 0) {
133
        if (!(flags & VIR_DRV_OPEN_QUIET))
134 135
            virXenError(VIR_ERR_NO_XEN, XEN_HYPERVISOR_SOCKET, 0);
        return (-1);
136
    }
137
    conn->handle = ret;
138

139
    return(0);
140 141 142 143
}

/**
 * xenHypervisorClose:
144
 * @conn: pointer to the connection block
145 146 147 148 149
 *
 * Close the connection to the Xen hypervisor.
 *
 * Returns 0 in case of success or -1 in case of error.
 */
150
int
151
xenHypervisorClose(virConnectPtr conn)
152
{
153 154
    int ret;

155
    if ((conn == NULL) || (conn->handle < 0))
156
        return (-1);
157

158
    ret = close(conn->handle);
159
    if (ret < 0)
160 161
        return (-1);
    return (0);
162 163 164 165 166 167 168 169 170 171 172 173
}

/**
 * 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
174 175
xenHypervisorDoOp(int handle, dom0_op_t * op)
{
176
    int ret;
177
    unsigned int cmd;
178 179 180 181
    hypercall_t hc;

    op->interface_version = DOM0_INTERFACE_VERSION;
    hc.op = __HYPERVISOR_dom0_op;
182
    hc.arg[0] = (unsigned long) op;
183

184 185
    if (mlock(op, sizeof(dom0_op_t)) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " locking", sizeof(dom0_op_t));
186
        return (-1);
187
    }
188

189 190
    cmd = _IOC(_IOC_NONE, 'P', 0, sizeof(hc));
    ret = ioctl(handle, cmd, (unsigned long) &hc);
191 192 193
    if (ret < 0) {
        virXenError(VIR_ERR_XEN_CALL, " ioctl ", cmd);
    }
194

195 196
    if (munlock(op, sizeof(dom0_op_t)) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " releasing", sizeof(dom0_op_t));
197
        ret = -1;
198
    }
199 200

    if (ret < 0)
201 202 203
        return (-1);

    return (0);
204 205
}

206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
/**
 * 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");
}

226 227
/**
 * xenHypervisorGetVersion:
228 229
 * @conn: pointer to the connection block
 * @hvVer: where to store the version
230 231 232
 *
 * Call the hypervisor to extracts his own internal API version
 *
233
 * Returns 0 in case of success, -1 in case of error
234
 */
235 236
int
xenHypervisorGetVersion(virConnectPtr conn, unsigned long *hvVer)
237
{
238 239 240 241
    int ret;
    unsigned int cmd;
    hypercall_t hc;

242 243 244 245
    if ((conn == NULL) || (conn->handle < 0) || (hvVer == NULL))
        return (-1);
    *hvVer = 0;

246
    hc.op = __HYPERVISOR_xen_version;
247
    hc.arg[0] = (unsigned long) XENVER_version;
248 249 250
    hc.arg[1] = 0;

    cmd = _IOC(_IOC_NONE, 'P', 0, sizeof(hc));
251
    ret = ioctl(conn->handle, cmd, (unsigned long) &hc);
252

253 254
    if (ret < 0) {
        virXenError(VIR_ERR_XEN_CALL, " getting version ", XENVER_version);
255
        return (-1);
256
    }
257 258
    *hvVer = (ret >> 16) * 1000000 + (ret & 0xFFFF) * 1000;
    return(0);
259 260
}

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
/**
 * 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);
}

399 400
/**
 * xenHypervisorGetDomainInfo:
401
 * @domain: pointer to the domain block
402 403 404 405 406 407 408
 * @info: the place where informations should be stored
 *
 * Do an hypervisor call to get the related set of domain informations.
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
409
xenHypervisorGetDomainInfo(virDomainPtr domain, virDomainInfoPtr info)
410
{
411
    dom0_op_t op;
412
    dom0_getdomaininfo_t dominfo;
413 414
    int ret;

415 416
    if ((domain == NULL) || (domain->conn == NULL) ||
        (domain->conn->handle < 0) || (info == NULL))
417
        return (-1);
418

419 420
    memset(info, 0, sizeof(virDomainInfo));
    memset(&dominfo, 0, sizeof(dom0_getdomaininfo_t));
421

422
    if (mlock(&dominfo, sizeof(dom0_getdomaininfo_t)) < 0) {
423 424 425
        virXenError(VIR_ERR_XEN_CALL, " locking",
                    sizeof(dom0_getdomaininfo_t));
        return (-1);
426
    }
427 428

    op.cmd = DOM0_GETDOMAININFOLIST;
429
    op.u.getdomaininfolist.first_domain = (domid_t) domain->handle;
430
    op.u.getdomaininfolist.max_domains = 1;
431
    op.u.getdomaininfolist.buffer = &dominfo;
432
    op.u.getdomaininfolist.num_domains = 1;
433
    dominfo.domain = domain->handle;
434

435
    ret = xenHypervisorDoOp(domain->conn->handle, &op);
436

437
    if (munlock(&dominfo, sizeof(dom0_getdomaininfo_t)) < 0) {
438 439
        virXenError(VIR_ERR_XEN_CALL, " release",
                    sizeof(dom0_getdomaininfo_t));
440
        ret = -1;
441
    }
442

443
    if (ret < 0)
444
        return (-1);
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474

    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;
475
    return (0);
476 477
}

478 479
/**
 * xenHypervisorPauseDomain:
480
 * @domain: pointer to the domain block
481 482 483 484 485 486
 *
 * Do an hypervisor call to pause the given domain
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
487
xenHypervisorPauseDomain(virDomainPtr domain)
488
{
489 490 491
    dom0_op_t op;
    int ret;

492 493 494 495
    if ((domain == NULL) || (domain->conn == NULL) ||
        (domain->conn->handle < 0))
        return (-1);

496
    op.cmd = DOM0_PAUSEDOMAIN;
497
    op.u.pausedomain.domain = (domid_t) domain->handle;
498

499
    ret = xenHypervisorDoOp(domain->conn->handle, &op);
500 501

    if (ret < 0)
502 503
        return (-1);
    return (0);
504 505 506 507
}

/**
 * xenHypervisorResumeDomain:
508
 * @domain: pointer to the domain block
509 510 511 512 513 514
 *
 * Do an hypervisor call to resume the given domain
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
515
xenHypervisorResumeDomain(virDomainPtr domain)
516
{
517 518 519
    dom0_op_t op;
    int ret;

520 521 522 523
    if ((domain == NULL) || (domain->conn == NULL) ||
        (domain->conn->handle < 0))
        return (-1);

524
    op.cmd = DOM0_UNPAUSEDOMAIN;
525
    op.u.unpausedomain.domain = (domid_t) domain->handle;
526

527
    ret = xenHypervisorDoOp(domain->conn->handle, &op);
528 529

    if (ret < 0)
530 531
        return (-1);
    return (0);
532 533 534 535
}

/**
 * xenHypervisorDestroyDomain:
536
 * @domain: pointer to the domain block
537 538 539 540 541 542
 *
 * Do an hypervisor call to destroy the given domain
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
543
xenHypervisorDestroyDomain(virDomainPtr domain)
544
{
545 546 547
    dom0_op_t op;
    int ret;

548 549 550 551
    if ((domain == NULL) || (domain->conn == NULL) ||
        (domain->conn->handle < 0))
        return (-1);

552
    op.cmd = DOM0_DESTROYDOMAIN;
553
    op.u.destroydomain.domain = (domid_t) domain->handle;
554

555
    ret = xenHypervisorDoOp(domain->conn->handle, &op);
556 557

    if (ret < 0)
558 559
        return (-1);
    return (0);
560 561
}

562 563
/**
 * xenHypervisorSetMaxMemory:
564
 * @domain: pointer to the domain block
565 566 567 568 569 570 571
 * @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
572
xenHypervisorSetMaxMemory(virDomainPtr domain, unsigned long memory)
573
{
574 575 576
    dom0_op_t op;
    int ret;

577 578 579 580
    if ((domain == NULL) || (domain->conn == NULL) ||
        (domain->conn->handle < 0))
        return (-1);

581
    op.cmd = DOM0_SETDOMAINMAXMEM;
582
    op.u.setdomainmaxmem.domain = (domid_t) domain->handle;
583 584
    op.u.setdomainmaxmem.max_memkb = memory;

585
    ret = xenHypervisorDoOp(domain->conn->handle, &op);
586 587

    if (ret < 0)
588 589
        return (-1);
    return (0);
590
}