[Qemu-devel] [PULL 04/25] exec: introduce MemoryRegionCache - Paolo Bonzini

struct MemoryRegionCache (QEMU)

In QEMU, MemoryRegionCache is a data structure that optimizes memory access for virtual devices by caching translation results.

Introduce a new data structure to cache the result of address_space_translate() without forcing usage of a host address like address_space_map() does.

我的理解是, address_space_map() 函数看起来好像只是 j

目前好像只在 virtio 中用到了,因为只在 virtio 中看到了相关的代码。请看 virtio_init_region_cache()

MemoryRegion 是描述一片内存区,MemoryRegionCache 是真的要用的内存,Hypervisor 根据需要动态申请,后面简称 MRC。

为甚不用 RAMBlock 了呢?

struct MemoryRegionCache {
    // 真实 hold 着这片区域的内存,也就是这片区域的虚拟地址(HVA)
    void *ptr;
    // [] linear address translation?
    // 这片区域表示的地址空间的起始地址
    // 可以看 address_space_map 来印证这一点
    hwaddr xlat;
    hwaddr len;
    // address space 对应的 flatview
    FlatView *fv;
    MemoryRegionSection mrs;
    bool is_write;
};

address_space_cache_init() QEMU

初始化一个 MemoryRegionCache^。

传入的参数包含一个在 AddressSpace 中的 (addr, len),我们要做的就是把这片地址映射到 MemoryRegionCache 中去。

int64_t address_space_cache_init(MemoryRegionCache *cache,
                             AddressSpace *as,
                             hwaddr addr,
                             hwaddr len,
                             bool is_write)
{
    AddressSpaceDispatch *d;
    hwaddr l;
    MemoryRegion *mr;
    Int128 diff;

    //...
    l = len;
    cache->fv = address_space_get_flatview(as);
    d = flatview_to_dispatch(cache->fv);
    cache->mrs = *address_space_translate_internal(d, addr, &cache->xlat, &l, true);

    /*
     * cache->xlat is now relative to cache->mrs.mr, not to the section itself.
     * Take that into account to compute how many bytes are there between
     * cache->xlat and the end of the section.
     */
    diff = int128_sub(cache->mrs.size, int128_make64(cache->xlat - cache->mrs.offset_within_region));
    l = int128_get64(int128_min(diff, int128_make64(l)));

    mr = cache->mrs.mr;
    memory_region_ref(mr);
    if (memory_access_is_direct(mr, is_write)) {
        /* We don't care about the memory attributes here as we're only
         * doing this if we found actual RAM, which behaves the same
         * regardless of attributes; so UNSPECIFIED is fine.
         */
        // 得到 addr(所在 AddressSpace 里的偏移)对应的 xlat(所在 MR 里的偏移)
        // 并缓存到 cache 里
        l = flatview_extend_translation(cache->fv, addr, len, mr,
                                        cache->xlat, l, is_write,
                                        MEMTXATTRS_UNSPECIFIED);
        // 还是从 RAMBlock 里取 ptr。
        // addr 是 cache->xlat,其实就是 offset,加上 ram_block->host
        // 就变成 ptr 了。
        cache->ptr = qemu_ram_ptr_length(mr->ram_block, cache->xlat, &l, true);
    } else {
        cache->ptr = NULL;
    }

    cache->len = l;
    cache->is_write = is_write;
    return l;
}

address_space_write_cached() QEMU

Write to a cached RAM region.

/**
 * address_space_write_cached: write to a cached RAM region
 *
 * @cache: Cached region to be addressed
 * @addr: address relative to the base of the RAM region
 * @buf: buffer with the data transferred
 * @len: length of the data transferred
 */
static inline MemTxResult
address_space_write_cached(MemoryRegionCache *cache, hwaddr addr,
                           const void *buf, hwaddr len)
{
    assert(addr < cache->len && len <= cache->len - addr);
    // 如果之前 cache 已经缓存过了
    if (likely(cache->ptr)) {
        memcpy(cache->ptr + addr, buf, len);
        return MEMTX_OK;
    } else {
        return address_space_write_cached_slow(cache, addr, buf, len);
    }
}