void virEventPollUpdateTimeout(int timer, int frequency) { unsigned long long now; int i; PROBE(EVENT_POLL_UPDATE_TIMEOUT, "timer=%d frequency=%d", timer, frequency); if (timer <= 0) { VIR_WARN("Ignoring invalid update timer %d", timer); return; } if (virTimeMillisNow(&now) < 0) { return; } virMutexLock(&eventLoop.lock); for (i = 0 ; i < eventLoop.timeoutsCount ; i++) { if (eventLoop.timeouts[i].timer == timer) { eventLoop.timeouts[i].frequency = frequency; eventLoop.timeouts[i].expiresAt = frequency >= 0 ? frequency + now : 0; virEventPollInterruptLocked(); break; } } virMutexUnlock(&eventLoop.lock); }
/* Iterates over all registered timeouts and determine which * will be the first to expire. * @timeout: filled with expiry time of soonest timer, or -1 if * no timeout is pending * returns: 0 on success, -1 on error */ static int virEventPollCalculateTimeout(int *timeout) { unsigned long long then = 0; int i; EVENT_DEBUG("Calculate expiry of %zu timers", eventLoop.timeoutsCount); /* Figure out if we need a timeout */ for (i = 0 ; i < eventLoop.timeoutsCount ; i++) { if (eventLoop.timeouts[i].frequency < 0) continue; EVENT_DEBUG("Got a timeout scheduled for %llu", eventLoop.timeouts[i].expiresAt); if (then == 0 || eventLoop.timeouts[i].expiresAt < then) then = eventLoop.timeouts[i].expiresAt; } /* Calculate how long we should wait for a timeout if needed */ if (then > 0) { unsigned long long now; if (virTimeMillisNow(&now) < 0) return -1; *timeout = then - now; if (*timeout < 0) *timeout = 0; } else { *timeout = -1; } EVENT_DEBUG("Timeout at %llu due in %d ms", then, *timeout); return 0; }
/** * qemuAgentGuestSync: * @mon: Monitor * * Send guest-sync with unique ID * and wait for reply. If we get one, check if * received ID is equal to given. * * Returns: 0 on success, * -1 otherwise */ static int qemuAgentGuestSync(qemuAgentPtr mon) { int ret = -1; int send_ret; unsigned long long id, id_ret; qemuAgentMessage sync_msg; memset(&sync_msg, 0, sizeof(sync_msg)); if (virTimeMillisNow(&id) < 0) return -1; if (virAsprintf(&sync_msg.txBuffer, "{\"execute\":\"guest-sync\", " "\"arguments\":{\"id\":%llu}}", id) < 0) { virReportOOMError(); return -1; } sync_msg.txLength = strlen(sync_msg.txBuffer); VIR_DEBUG("Sending guest-sync command with ID: %llu", id); send_ret = qemuAgentSend(mon, &sync_msg, true); VIR_DEBUG("qemuAgentSend returned: %d", send_ret); if (send_ret < 0) { /* error reported */ goto cleanup; } if (!sync_msg.rxObject) { qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing monitor reply object")); goto cleanup; } if (virJSONValueObjectGetNumberUlong(sync_msg.rxObject, "return", &id_ret) < 0) { qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Malformed return value")); goto cleanup; } VIR_DEBUG("Guest returned ID: %llu", id_ret); if (id_ret != id) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("Guest agent returned ID: %llu instead of %llu"), id_ret, id); goto cleanup; } ret = 0; cleanup: virJSONValueFree(sync_msg.rxObject); VIR_FREE(sync_msg.txBuffer); return ret; }
/** * qemuAgentSend: * @mon: Monitor * @msg: Message * @seconds: number of seconds to wait for the result, it can be either * -2, -1, 0 or positive. * * Send @msg to agent @mon. If @seconds is equal to * VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK(-2), this function will block forever * waiting for the result. The value of * VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT(-1) means use default timeout value * and VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT(0) makes this this function return * immediately without waiting. Any positive value means the number of seconds * to wait for the result. * * Returns: 0 on success, * -2 on timeout, * -1 otherwise */ static int qemuAgentSend(qemuAgentPtr mon, qemuAgentMessagePtr msg, int seconds) { int ret = -1; unsigned long long then = 0; /* Check whether qemu quit unexpectedly */ if (mon->lastError.code != VIR_ERR_OK) { VIR_DEBUG("Attempt to send command while error is set %s", NULLSTR(mon->lastError.message)); virSetError(&mon->lastError); return -1; } if (seconds > VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) { unsigned long long now; if (virTimeMillisNow(&now) < 0) return -1; if (seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT) seconds = QEMU_AGENT_WAIT_TIME; then = now + seconds * 1000ull; } mon->msg = msg; qemuAgentUpdateWatch(mon); while (!mon->msg->finished) { if ((then && virCondWaitUntil(&mon->notify, &mon->parent.lock, then) < 0) || (!then && virCondWait(&mon->notify, &mon->parent.lock) < 0)) { if (errno == ETIMEDOUT) { virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s", _("Guest agent not available for now")); ret = -2; } else { virReportSystemError(errno, "%s", _("Unable to wait on agent monitor " "condition")); } goto cleanup; } } if (mon->lastError.code != VIR_ERR_OK) { VIR_DEBUG("Send command resulted in error %s", NULLSTR(mon->lastError.message)); virSetError(&mon->lastError); goto cleanup; } ret = 0; cleanup: mon->msg = NULL; qemuAgentUpdateWatch(mon); return ret; }
/** * virTimeFieldsNowRaw: * @fields: filled with current time fields * * Retrieves the current time, in broken-down field format. * The time is always in UTC. * * Returns 0 on success, -1 on error with errno reported */ int virTimeFieldsNow(struct tm *fields) { unsigned long long now; if (virTimeMillisNow(&now) < 0) return -1; return virTimeFieldsThen(now, fields); }
/** * virTimeBackOffStart: * @var: Timeout variable (with type virTimeBackOffVar). * @first: Initial time to wait (milliseconds). * @timeout: Timeout (milliseconds). * * Initialize the timeout variable @var and start the timer running. * * Returns 0 on success, -1 on error and raises a libvirt error. */ int virTimeBackOffStart(virTimeBackOffVar *var, unsigned long long first, unsigned long long timeout) { if (virTimeMillisNow(&var->start_t) < 0) return -1; var->next = first; var->limit_t = var->start_t + timeout; return 0; }
/** * qemuAgentSend: * @mon: Monitor * @msg: Message * @timeout: use timeout? * * Send @msg to agent @mon. * Wait max QEMU_AGENT_WAIT_TIME for agent * to reply. * * Returns: 0 on success, * -2 on timeout, * -1 otherwise */ static int qemuAgentSend(qemuAgentPtr mon, qemuAgentMessagePtr msg, bool timeout) { int ret = -1; unsigned long long now, then = 0; /* Check whether qemu quit unexpectedly */ if (mon->lastError.code != VIR_ERR_OK) { VIR_DEBUG("Attempt to send command while error is set %s", NULLSTR(mon->lastError.message)); virSetError(&mon->lastError); return -1; } if (timeout) { if (virTimeMillisNow(&now) < 0) return -1; then = now + QEMU_AGENT_WAIT_TIME; } mon->msg = msg; qemuAgentUpdateWatch(mon); while (!mon->msg->finished) { if ((timeout && virCondWaitUntil(&mon->notify, &mon->lock, then) < 0) || (!timeout && virCondWait(&mon->notify, &mon->lock) < 0)) { if (errno == ETIMEDOUT) { qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Guest agent not available for now")); ret = -2; } else { qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unable to wait on monitor condition")); } goto cleanup; } } if (mon->lastError.code != VIR_ERR_OK) { VIR_DEBUG("Send command resulted in error %s", NULLSTR(mon->lastError.message)); virSetError(&mon->lastError); goto cleanup; } ret = 0; cleanup: mon->msg = NULL; qemuAgentUpdateWatch(mon); return ret; }
/* * obj must be locked before calling, libxlDriverPrivatePtr must NOT be locked * * This must be called by anything that will change the VM state * in any way * * Upon successful return, the object will have its ref count increased, * successful calls must be followed by EndJob eventually */ int libxlDomainObjBeginJob(libxlDriverPrivatePtr driver ATTRIBUTE_UNUSED, virDomainObjPtr obj, enum libxlDomainJob job) { libxlDomainObjPrivatePtr priv = obj->privateData; unsigned long long now; unsigned long long then; if (virTimeMillisNow(&now) < 0) return -1; then = now + LIBXL_JOB_WAIT_TIME; virObjectRef(obj); while (priv->job.active) { VIR_DEBUG("Wait normal job condition for starting job: %s", libxlDomainJobTypeToString(job)); if (virCondWaitUntil(&priv->job.cond, &obj->parent.lock, then) < 0) goto error; } libxlDomainObjResetJob(priv); VIR_DEBUG("Starting job: %s", libxlDomainJobTypeToString(job)); priv->job.active = job; priv->job.owner = virThreadSelfID(); priv->job.started = now; priv->job.current->type = VIR_DOMAIN_JOB_UNBOUNDED; return 0; error: VIR_WARN("Cannot start job (%s) for domain %s;" " current job is (%s) owned by (%d)", libxlDomainJobTypeToString(job), obj->def->name, libxlDomainJobTypeToString(priv->job.active), priv->job.owner); if (errno == ETIMEDOUT) virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s", _("cannot acquire state change lock")); else virReportSystemError(errno, "%s", _("cannot acquire job mutex")); virObjectUnref(obj); return -1; }
/* * Register a callback for a timer event * NB, it *must* be safe to call this from within a callback * For this reason we only ever append to existing list. */ int virEventPollAddTimeout(int frequency, virEventTimeoutCallback cb, void *opaque, virFreeCallback ff) { unsigned long long now; int ret; if (virTimeMillisNow(&now) < 0) { return -1; } virMutexLock(&eventLoop.lock); if (eventLoop.timeoutsCount == eventLoop.timeoutsAlloc) { EVENT_DEBUG("Used %zu timeout slots, adding at least %d more", eventLoop.timeoutsAlloc, EVENT_ALLOC_EXTENT); if (VIR_RESIZE_N(eventLoop.timeouts, eventLoop.timeoutsAlloc, eventLoop.timeoutsCount, EVENT_ALLOC_EXTENT) < 0) { virMutexUnlock(&eventLoop.lock); return -1; } } eventLoop.timeouts[eventLoop.timeoutsCount].timer = nextTimer++; eventLoop.timeouts[eventLoop.timeoutsCount].frequency = frequency; eventLoop.timeouts[eventLoop.timeoutsCount].cb = cb; eventLoop.timeouts[eventLoop.timeoutsCount].ff = ff; eventLoop.timeouts[eventLoop.timeoutsCount].opaque = opaque; eventLoop.timeouts[eventLoop.timeoutsCount].deleted = 0; eventLoop.timeouts[eventLoop.timeoutsCount].expiresAt = frequency >= 0 ? frequency + now : 0; eventLoop.timeoutsCount++; ret = nextTimer-1; virEventPollInterruptLocked(); PROBE(EVENT_POLL_ADD_TIMEOUT, "timer=%d frequency=%d cb=%p opaque=%p ff=%p", ret, frequency, cb, opaque, ff); virMutexUnlock(&eventLoop.lock); return ret; }
int libxlDomainJobUpdateTime(struct libxlDomainJobObj *job) { virDomainJobInfoPtr jobInfo = job->current; unsigned long long now; if (!job->started) return 0; if (virTimeMillisNow(&now) < 0) return -1; if (now < job->started) { job->started = 0; return 0; } jobInfo->timeElapsed = now - job->started; return 0; }
/* * Iterate over all timers and determine if any have expired. * Invoke the user supplied callback for each timer whose * expiry time is met, and schedule the next timeout. Does * not try to 'catch up' on time if the actual expiry time * was later than the requested time. * * This method must cope with new timers being registered * by a callback, and must skip any timers marked as deleted. * * Returns 0 upon success, -1 if an error occurred */ static int virEventPollDispatchTimeouts(void) { unsigned long long now; int i; /* Save this now - it may be changed during dispatch */ int ntimeouts = eventLoop.timeoutsCount; VIR_DEBUG("Dispatch %d", ntimeouts); if (virTimeMillisNow(&now) < 0) return -1; for (i = 0 ; i < ntimeouts ; i++) { if (eventLoop.timeouts[i].deleted || eventLoop.timeouts[i].frequency < 0) continue; /* Add 20ms fuzz so we don't pointlessly spin doing * <10ms sleeps, particularly on kernels with low HZ * it is fine that a timer expires 20ms earlier than * requested */ if (eventLoop.timeouts[i].expiresAt <= (now+20)) { virEventTimeoutCallback cb = eventLoop.timeouts[i].cb; int timer = eventLoop.timeouts[i].timer; void *opaque = eventLoop.timeouts[i].opaque; eventLoop.timeouts[i].expiresAt = now + eventLoop.timeouts[i].frequency; PROBE(EVENT_POLL_DISPATCH_TIMEOUT, "timer=%d", timer); virMutexUnlock(&eventLoop.lock); (cb)(timer, opaque); virMutexLock(&eventLoop.lock); } } return 0; }
void virEventPollUpdateTimeout(int timer, int frequency) { unsigned long long now; int i; bool found = false; PROBE(EVENT_POLL_UPDATE_TIMEOUT, "timer=%d frequency=%d", timer, frequency); if (timer <= 0) { VIR_WARN("Ignoring invalid update timer %d", timer); return; } if (virTimeMillisNow(&now) < 0) { return; } virMutexLock(&eventLoop.lock); for (i = 0; i < eventLoop.timeoutsCount; i++) { if (eventLoop.timeouts[i].timer == timer) { eventLoop.timeouts[i].frequency = frequency; eventLoop.timeouts[i].expiresAt = frequency >= 0 ? frequency + now : 0; VIR_DEBUG("Set timer freq=%d expires=%llu", frequency, eventLoop.timeouts[i].expiresAt); virEventPollInterruptLocked(); found = true; break; } } virMutexUnlock(&eventLoop.lock); if (!found) VIR_WARN("Got update for non-existent timer %d", timer); }