Beispiel #1
0
int64_t qemu_get_clock_ns(QEMUClock *clock)
{
    switch(clock->type) {
    case QEMU_CLOCK_REALTIME:
        return get_clock();
    default:
    case QEMU_CLOCK_VIRTUAL:
        return cpu_get_clock();
    case QEMU_CLOCK_HOST:
        return get_clock_realtime();
    }
}
Beispiel #2
0
static QEMUClock *qemu_new_clock(int type)
{
    QEMUClock *clock;

    clock = qemu_mallocz(sizeof(QEMUClock));
    clock->type = type;
    clock->enabled = 1;
    notifier_list_init(&clock->reset_notifiers);
    /* required to detect & report backward jumps */
    if (type == QEMU_CLOCK_HOST) {
        clock->last = get_clock_realtime();
    }
    return clock;
}
Beispiel #3
0
static void tlb_dyn_init(CPUArchState *env)
{
    int i;

    for (i = 0; i < NB_MMU_MODES; i++) {
        CPUTLBDesc *desc = &env->tlb_d[i];
        size_t n_entries = 1 << CPU_TLB_DYN_DEFAULT_BITS;

        tlb_window_reset(&desc->window, get_clock_realtime(), 0);
        desc->n_used_entries = 0;
        env->tlb_mask[i] = (n_entries - 1) << CPU_TLB_ENTRY_BITS;
        env->tlb_table[i] = g_new(CPUTLBEntry, n_entries);
        env->iotlb[i] = g_new(CPUIOTLBEntry, n_entries);
    }
}
Beispiel #4
0
int64_t qemu_get_clock(QEMUClock *clock)
{
    switch(clock->type) {
    case QEMU_CLOCK_REALTIME:
        return get_clock() / 1000000;
    default:
    case QEMU_CLOCK_VIRTUAL:
        if (use_icount) {
            return cpu_get_icount();
        } else {
            return cpu_get_clock();
        }
    case QEMU_CLOCK_HOST:
        return get_clock_realtime();
    }
}
Beispiel #5
0
static int64_t get_clock(void)
{
#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \
	|| defined(__DragonFly__) || defined(__FreeBSD_kernel__)
    if (use_rt_clock) {
        struct timespec ts;
        clock_gettime(CLOCK_MONOTONIC, &ts);
        return ts.tv_sec * 1000000000LL + ts.tv_nsec;
    } else
#endif
    {
        /* XXX: using gettimeofday leads to problems if the date
           changes, so it should be avoided. */
        return get_clock_realtime();
    }
}
Beispiel #6
0
int64_t qemu_get_clock(QEMUClock *clock)
{
    switch(clock->type) {
    case QEMU_CLOCK_REALTIME:
        return get_clock() / 1000000;
    default:
    case QEMU_CLOCK_VIRTUAL:
#ifdef MARSS_QEMU
        if (in_simulation) {
            return cpu_get_sim_clock();
        }
#endif
        if (use_icount) {
            return cpu_get_icount();
        } else {
            return cpu_get_clock();
        }
    case QEMU_CLOCK_HOST:
        return get_clock_realtime();
    }
}
Beispiel #7
0
int64_t qemu_get_clock_ns(QEMUClock *clock)
{
    int64_t now, last;

    switch(clock->type) {
    case QEMU_CLOCK_REALTIME:
        return get_clock();
    default:
    case QEMU_CLOCK_VIRTUAL:
        if (use_icount) {
            return cpu_get_icount();
        } else {
            return cpu_get_clock();
        }
    case QEMU_CLOCK_HOST:
        now = get_clock_realtime();
        last = clock->last;
        clock->last = now;
        if (now < last) {
            notifier_list_notify(&clock->reset_notifiers, &now);
        }
        return now;
    }
}
Beispiel #8
0
/**
 * tlb_mmu_resize_locked() - perform TLB resize bookkeeping; resize if necessary
 * @env: CPU that owns the TLB
 * @mmu_idx: MMU index of the TLB
 *
 * Called with tlb_lock_held.
 *
 * We have two main constraints when resizing a TLB: (1) we only resize it
 * on a TLB flush (otherwise we'd have to take a perf hit by either rehashing
 * the array or unnecessarily flushing it), which means we do not control how
 * frequently the resizing can occur; (2) we don't have access to the guest's
 * future scheduling decisions, and therefore have to decide the magnitude of
 * the resize based on past observations.
 *
 * In general, a memory-hungry process can benefit greatly from an appropriately
 * sized TLB, since a guest TLB miss is very expensive. This doesn't mean that
 * we just have to make the TLB as large as possible; while an oversized TLB
 * results in minimal TLB miss rates, it also takes longer to be flushed
 * (flushes can be _very_ frequent), and the reduced locality can also hurt
 * performance.
 *
 * To achieve near-optimal performance for all kinds of workloads, we:
 *
 * 1. Aggressively increase the size of the TLB when the use rate of the
 * TLB being flushed is high, since it is likely that in the near future this
 * memory-hungry process will execute again, and its memory hungriness will
 * probably be similar.
 *
 * 2. Slowly reduce the size of the TLB as the use rate declines over a
 * reasonably large time window. The rationale is that if in such a time window
 * we have not observed a high TLB use rate, it is likely that we won't observe
 * it in the near future. In that case, once a time window expires we downsize
 * the TLB to match the maximum use rate observed in the window.
 *
 * 3. Try to keep the maximum use rate in a time window in the 30-70% range,
 * since in that range performance is likely near-optimal. Recall that the TLB
 * is direct mapped, so we want the use rate to be low (or at least not too
 * high), since otherwise we are likely to have a significant amount of
 * conflict misses.
 */
static void tlb_mmu_resize_locked(CPUArchState *env, int mmu_idx)
{
    CPUTLBDesc *desc = &env->tlb_d[mmu_idx];
    size_t old_size = tlb_n_entries(env, mmu_idx);
    size_t rate;
    size_t new_size = old_size;
    int64_t now = get_clock_realtime();
    int64_t window_len_ms = 100;
    int64_t window_len_ns = window_len_ms * 1000 * 1000;
    bool window_expired = now > desc->window.begin_ns + window_len_ns;

    if (desc->n_used_entries > desc->window.max_entries) {
        desc->window.max_entries = desc->n_used_entries;
    }
    rate = desc->window.max_entries * 100 / old_size;

    if (rate > 70) {
        new_size = MIN(old_size << 1, 1 << CPU_TLB_DYN_MAX_BITS);
    } else if (rate < 30 && window_expired) {
        size_t ceil = pow2ceil(desc->window.max_entries);
        size_t expected_rate = desc->window.max_entries * 100 / ceil;

        /*
         * Avoid undersizing when the max number of entries seen is just below
         * a pow2. For instance, if max_entries == 1025, the expected use rate
         * would be 1025/2048==50%. However, if max_entries == 1023, we'd get
         * 1023/1024==99.9% use rate, so we'd likely end up doubling the size
         * later. Thus, make sure that the expected use rate remains below 70%.
         * (and since we double the size, that means the lowest rate we'd
         * expect to get is 35%, which is still in the 30-70% range where
         * we consider that the size is appropriate.)
         */
        if (expected_rate > 70) {
            ceil *= 2;
        }
        new_size = MAX(ceil, 1 << CPU_TLB_DYN_MIN_BITS);
    }

    if (new_size == old_size) {
        if (window_expired) {
            tlb_window_reset(&desc->window, now, desc->n_used_entries);
        }
        return;
    }

    g_free(env->tlb_table[mmu_idx]);
    g_free(env->iotlb[mmu_idx]);

    tlb_window_reset(&desc->window, now, 0);
    /* desc->n_used_entries is cleared by the caller */
    env->tlb_mask[mmu_idx] = (new_size - 1) << CPU_TLB_ENTRY_BITS;
    env->tlb_table[mmu_idx] = g_try_new(CPUTLBEntry, new_size);
    env->iotlb[mmu_idx] = g_try_new(CPUIOTLBEntry, new_size);
    /*
     * If the allocations fail, try smaller sizes. We just freed some
     * memory, so going back to half of new_size has a good chance of working.
     * Increased memory pressure elsewhere in the system might cause the
     * allocations to fail though, so we progressively reduce the allocation
     * size, aborting if we cannot even allocate the smallest TLB we support.
     */
    while (env->tlb_table[mmu_idx] == NULL || env->iotlb[mmu_idx] == NULL) {
        if (new_size == (1 << CPU_TLB_DYN_MIN_BITS)) {
            error_report("%s: %s", __func__, strerror(errno));
            abort();
        }
        new_size = MAX(new_size >> 1, 1 << CPU_TLB_DYN_MIN_BITS);
        env->tlb_mask[mmu_idx] = (new_size - 1) << CPU_TLB_ENTRY_BITS;

        g_free(env->tlb_table[mmu_idx]);
        g_free(env->iotlb[mmu_idx]);
        env->tlb_table[mmu_idx] = g_try_new(CPUTLBEntry, new_size);
        env->iotlb[mmu_idx] = g_try_new(CPUIOTLBEntry, new_size);
    }
}