xen_internal.c 13.6 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 45 46 47 48 49
static virDriver xenHypervisorDriver = {
    "Xen",
    NULL, /* init */
    xenHypervisorOpen, /* open */
    xenHypervisorClose, /* close */
    NULL, /* type */
    xenHypervisorGetVersion, /* version */
50
    NULL, /* nodeGetInfo */
51 52
    xenHypervisorListDomains, /* listDomains */
    xenHypervisorNumOfDomains, /* numOfDomains */
53 54 55 56 57 58 59
    NULL, /* domainCreateLinux */
    NULL, /* domainLookupByID */
    NULL, /* domainLookupByUUID */
    NULL, /* domainLookupByName */
    xenHypervisorPauseDomain, /* domainSuspend */
    xenHypervisorResumeDomain, /* domainResume */
    NULL, /* domainShutdown */
60
    NULL, /* domainReboot */
61 62 63 64 65 66 67 68
    xenHypervisorDestroyDomain, /* domainDestroy */
    NULL, /* domainFree */
    NULL, /* domainGetName */
    NULL, /* domainGetID */
    NULL, /* domainGetUUID */
    NULL, /* domainGetOSType */
    NULL, /* domainGetMaxMemory */
    xenHypervisorSetMaxMemory, /* domainSetMaxMemory */
69
    NULL, /* domainSetMemory */
70 71 72 73 74
    xenHypervisorGetDomainInfo, /* domainGetInfo */
    NULL, /* domainSave */
    NULL /* domainRestore */
};

75 76 77 78 79 80 81 82 83 84
/**
 * xenHypervisorRegister:
 *
 * Registers the xenHypervisor driver
 */
void xenHypervisorRegister(void)
{
    virRegisterDriver(&xenHypervisorDriver);
}

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

98 99 100 101 102
    if (error == VIR_ERR_OK)
        return;

    errmsg = __virErrorMsg(error, info);
    __virRaiseError(NULL, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR,
103
                    errmsg, info, NULL, value, 0, errmsg, info, value);
104 105
}

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

121 122 123 124 125
    if ((name != NULL) && (strcmp(name, "xen")))
        return(-1);

    conn->handle = -1;

126
    ret = open(XEN_HYPERVISOR_SOCKET, O_RDWR);
127
    if (ret < 0) {
128
        if (!(flags & VIR_DRV_OPEN_QUIET))
129 130
            virXenError(VIR_ERR_NO_XEN, XEN_HYPERVISOR_SOCKET, 0);
        return (-1);
131
    }
132
    conn->handle = ret;
133

134
    return(0);
135 136 137 138
}

/**
 * xenHypervisorClose:
139
 * @conn: pointer to the connection block
140 141 142 143 144
 *
 * Close the connection to the Xen hypervisor.
 *
 * Returns 0 in case of success or -1 in case of error.
 */
145
int
146
xenHypervisorClose(virConnectPtr conn)
147
{
148 149
    int ret;

150
    if ((conn == NULL) || (conn->handle < 0))
151
        return (-1);
152

153
    ret = close(conn->handle);
154
    if (ret < 0)
155 156
        return (-1);
    return (0);
157 158 159 160 161 162 163 164 165 166 167 168
}

/**
 * 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
169 170
xenHypervisorDoOp(int handle, dom0_op_t * op)
{
171
    int ret;
172
    unsigned int cmd;
173 174 175 176
    hypercall_t hc;

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

179 180
    if (mlock(op, sizeof(dom0_op_t)) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " locking", sizeof(dom0_op_t));
181
        return (-1);
182
    }
183

184 185
    cmd = _IOC(_IOC_NONE, 'P', 0, sizeof(hc));
    ret = ioctl(handle, cmd, (unsigned long) &hc);
186 187 188
    if (ret < 0) {
        virXenError(VIR_ERR_XEN_CALL, " ioctl ", cmd);
    }
189

190 191
    if (munlock(op, sizeof(dom0_op_t)) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " releasing", sizeof(dom0_op_t));
192
        ret = -1;
193
    }
194 195

    if (ret < 0)
196 197 198
        return (-1);

    return (0);
199 200
}

201 202
/**
 * xenHypervisorGetVersion:
203 204
 * @conn: pointer to the connection block
 * @hvVer: where to store the version
205 206 207
 *
 * Call the hypervisor to extracts his own internal API version
 *
208
 * Returns 0 in case of success, -1 in case of error
209
 */
210 211
int
xenHypervisorGetVersion(virConnectPtr conn, unsigned long *hvVer)
212
{
213 214 215 216
    int ret;
    unsigned int cmd;
    hypercall_t hc;

217 218 219 220
    if ((conn == NULL) || (conn->handle < 0) || (hvVer == NULL))
        return (-1);
    *hvVer = 0;

221
    hc.op = __HYPERVISOR_xen_version;
222
    hc.arg[0] = (unsigned long) XENVER_version;
223 224 225
    hc.arg[1] = 0;

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

228 229
    if (ret < 0) {
        virXenError(VIR_ERR_XEN_CALL, " getting version ", XENVER_version);
230
        return (-1);
231
    }
232 233
    *hvVer = (ret >> 16) * 1000000 + (ret & 0xFFFF) * 1000;
    return(0);
234 235
}

236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 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
/**
 * 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);
}

374 375
/**
 * xenHypervisorGetDomainInfo:
376
 * @domain: pointer to the domain block
377 378 379 380 381 382 383
 * @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
384
xenHypervisorGetDomainInfo(virDomainPtr domain, virDomainInfoPtr info)
385
{
386
    dom0_op_t op;
387
    dom0_getdomaininfo_t dominfo;
388 389
    int ret;

390 391
    if ((domain == NULL) || (domain->conn == NULL) ||
        (domain->conn->handle < 0) || (info == NULL))
392
        return (-1);
393

394 395
    memset(info, 0, sizeof(virDomainInfo));
    memset(&dominfo, 0, sizeof(dom0_getdomaininfo_t));
396

397
    if (mlock(&dominfo, sizeof(dom0_getdomaininfo_t)) < 0) {
398 399 400
        virXenError(VIR_ERR_XEN_CALL, " locking",
                    sizeof(dom0_getdomaininfo_t));
        return (-1);
401
    }
402 403

    op.cmd = DOM0_GETDOMAININFOLIST;
404
    op.u.getdomaininfolist.first_domain = (domid_t) domain->handle;
405
    op.u.getdomaininfolist.max_domains = 1;
406
    op.u.getdomaininfolist.buffer = &dominfo;
407
    op.u.getdomaininfolist.num_domains = 1;
408
    dominfo.domain = domain->handle;
409

410
    ret = xenHypervisorDoOp(domain->conn->handle, &op);
411

412
    if (munlock(&dominfo, sizeof(dom0_getdomaininfo_t)) < 0) {
413 414
        virXenError(VIR_ERR_XEN_CALL, " release",
                    sizeof(dom0_getdomaininfo_t));
415
        ret = -1;
416
    }
417

418
    if (ret < 0)
419
        return (-1);
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449

    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;
450
    return (0);
451 452
}

453 454
/**
 * xenHypervisorPauseDomain:
455
 * @domain: pointer to the domain block
456 457 458 459 460 461
 *
 * Do an hypervisor call to pause the given domain
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
462
xenHypervisorPauseDomain(virDomainPtr domain)
463
{
464 465 466
    dom0_op_t op;
    int ret;

467 468 469 470
    if ((domain == NULL) || (domain->conn == NULL) ||
        (domain->conn->handle < 0))
        return (-1);

471
    op.cmd = DOM0_PAUSEDOMAIN;
472
    op.u.pausedomain.domain = (domid_t) domain->handle;
473

474
    ret = xenHypervisorDoOp(domain->conn->handle, &op);
475 476

    if (ret < 0)
477 478
        return (-1);
    return (0);
479 480 481 482
}

/**
 * xenHypervisorResumeDomain:
483
 * @domain: pointer to the domain block
484 485 486 487 488 489
 *
 * Do an hypervisor call to resume the given domain
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
490
xenHypervisorResumeDomain(virDomainPtr domain)
491
{
492 493 494
    dom0_op_t op;
    int ret;

495 496 497 498
    if ((domain == NULL) || (domain->conn == NULL) ||
        (domain->conn->handle < 0))
        return (-1);

499
    op.cmd = DOM0_UNPAUSEDOMAIN;
500
    op.u.unpausedomain.domain = (domid_t) domain->handle;
501

502
    ret = xenHypervisorDoOp(domain->conn->handle, &op);
503 504

    if (ret < 0)
505 506
        return (-1);
    return (0);
507 508 509 510
}

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

523 524 525 526
    if ((domain == NULL) || (domain->conn == NULL) ||
        (domain->conn->handle < 0))
        return (-1);

527
    op.cmd = DOM0_DESTROYDOMAIN;
528
    op.u.destroydomain.domain = (domid_t) domain->handle;
529

530
    ret = xenHypervisorDoOp(domain->conn->handle, &op);
531 532

    if (ret < 0)
533 534
        return (-1);
    return (0);
535 536
}

537 538
/**
 * xenHypervisorSetMaxMemory:
539
 * @domain: pointer to the domain block
540 541 542 543 544 545 546
 * @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
547
xenHypervisorSetMaxMemory(virDomainPtr domain, unsigned long memory)
548
{
549 550 551
    dom0_op_t op;
    int ret;

552 553 554 555
    if ((domain == NULL) || (domain->conn == NULL) ||
        (domain->conn->handle < 0))
        return (-1);

556
    op.cmd = DOM0_SETDOMAINMAXMEM;
557
    op.u.setdomainmaxmem.domain = (domid_t) domain->handle;
558 559
    op.u.setdomainmaxmem.max_memkb = memory;

560
    ret = xenHypervisorDoOp(domain->conn->handle, &op);
561 562

    if (ret < 0)
563 564
        return (-1);
    return (0);
565
}