1e7218dd5SVikram Garhwal /* 2e7218dd5SVikram Garhwal * Copyright (C) 2011 Citrix Ltd. 3e7218dd5SVikram Garhwal * 4e7218dd5SVikram Garhwal * This work is licensed under the terms of the GNU GPL, version 2. See 5e7218dd5SVikram Garhwal * the COPYING file in the top-level directory. 6e7218dd5SVikram Garhwal * 7e7218dd5SVikram Garhwal * Contributions after 2012-01-13 are licensed under the terms of the 8e7218dd5SVikram Garhwal * GNU GPL, version 2 or (at your option) any later version. 9e7218dd5SVikram Garhwal */ 10e7218dd5SVikram Garhwal 11e7218dd5SVikram Garhwal #include "qemu/osdep.h" 12e7218dd5SVikram Garhwal #include "qemu/units.h" 13e7218dd5SVikram Garhwal #include "qemu/error-report.h" 14e7218dd5SVikram Garhwal 15e7218dd5SVikram Garhwal #include <sys/resource.h> 16e7218dd5SVikram Garhwal 17e7218dd5SVikram Garhwal #include "hw/xen/xen_native.h" 18e7218dd5SVikram Garhwal #include "qemu/bitmap.h" 19e7218dd5SVikram Garhwal 20e7218dd5SVikram Garhwal #include "sysemu/runstate.h" 21e7218dd5SVikram Garhwal #include "sysemu/xen-mapcache.h" 22e7218dd5SVikram Garhwal #include "trace.h" 23e7218dd5SVikram Garhwal 24e7218dd5SVikram Garhwal 25e7218dd5SVikram Garhwal #if HOST_LONG_BITS == 32 26e7218dd5SVikram Garhwal # define MCACHE_MAX_SIZE (1UL<<31) /* 2GB Cap */ 27e7218dd5SVikram Garhwal #else 28e7218dd5SVikram Garhwal # define MCACHE_MAX_SIZE (1UL<<35) /* 32GB Cap */ 29e7218dd5SVikram Garhwal #endif 30e7218dd5SVikram Garhwal 31e7218dd5SVikram Garhwal /* This is the size of the virtual address space reserve to QEMU that will not 32e7218dd5SVikram Garhwal * be use by MapCache. 33e7218dd5SVikram Garhwal * From empirical tests I observed that qemu use 75MB more than the 34e7218dd5SVikram Garhwal * max_mcache_size. 35e7218dd5SVikram Garhwal */ 36e7218dd5SVikram Garhwal #define NON_MCACHE_MEMORY_SIZE (80 * MiB) 37e7218dd5SVikram Garhwal 38e7218dd5SVikram Garhwal typedef struct MapCacheEntry { 39e7218dd5SVikram Garhwal hwaddr paddr_index; 40e7218dd5SVikram Garhwal uint8_t *vaddr_base; 41e7218dd5SVikram Garhwal unsigned long *valid_mapping; 42e7218dd5SVikram Garhwal uint32_t lock; 43e7218dd5SVikram Garhwal #define XEN_MAPCACHE_ENTRY_DUMMY (1 << 0) 44e7218dd5SVikram Garhwal uint8_t flags; 45e7218dd5SVikram Garhwal hwaddr size; 46e7218dd5SVikram Garhwal struct MapCacheEntry *next; 47e7218dd5SVikram Garhwal } MapCacheEntry; 48e7218dd5SVikram Garhwal 49e7218dd5SVikram Garhwal typedef struct MapCacheRev { 50e7218dd5SVikram Garhwal uint8_t *vaddr_req; 51e7218dd5SVikram Garhwal hwaddr paddr_index; 52e7218dd5SVikram Garhwal hwaddr size; 53e7218dd5SVikram Garhwal QTAILQ_ENTRY(MapCacheRev) next; 54e7218dd5SVikram Garhwal bool dma; 55e7218dd5SVikram Garhwal } MapCacheRev; 56e7218dd5SVikram Garhwal 57e7218dd5SVikram Garhwal typedef struct MapCache { 58e7218dd5SVikram Garhwal MapCacheEntry *entry; 59e7218dd5SVikram Garhwal unsigned long nr_buckets; 60e7218dd5SVikram Garhwal QTAILQ_HEAD(, MapCacheRev) locked_entries; 61e7218dd5SVikram Garhwal 62e7218dd5SVikram Garhwal /* For most cases (>99.9%), the page address is the same. */ 63e7218dd5SVikram Garhwal MapCacheEntry *last_entry; 64e7218dd5SVikram Garhwal unsigned long max_mcache_size; 65*b771b026SEdgar E. Iglesias unsigned int bucket_shift; 66*b771b026SEdgar E. Iglesias unsigned long bucket_size; 67e7218dd5SVikram Garhwal 68e7218dd5SVikram Garhwal phys_offset_to_gaddr_t phys_offset_to_gaddr; 69e7218dd5SVikram Garhwal QemuMutex lock; 70e7218dd5SVikram Garhwal void *opaque; 71e7218dd5SVikram Garhwal } MapCache; 72e7218dd5SVikram Garhwal 73e7218dd5SVikram Garhwal static MapCache *mapcache; 74e7218dd5SVikram Garhwal 75efb0c6caSEdgar E. Iglesias static inline void mapcache_lock(MapCache *mc) 76e7218dd5SVikram Garhwal { 77efb0c6caSEdgar E. Iglesias qemu_mutex_lock(&mc->lock); 78e7218dd5SVikram Garhwal } 79e7218dd5SVikram Garhwal 80efb0c6caSEdgar E. Iglesias static inline void mapcache_unlock(MapCache *mc) 81e7218dd5SVikram Garhwal { 82efb0c6caSEdgar E. Iglesias qemu_mutex_unlock(&mc->lock); 83e7218dd5SVikram Garhwal } 84e7218dd5SVikram Garhwal 85e7218dd5SVikram Garhwal static inline int test_bits(int nr, int size, const unsigned long *addr) 86e7218dd5SVikram Garhwal { 87e7218dd5SVikram Garhwal unsigned long res = find_next_zero_bit(addr, size + nr, nr); 88e7218dd5SVikram Garhwal if (res >= nr + size) 89e7218dd5SVikram Garhwal return 1; 90e7218dd5SVikram Garhwal else 91e7218dd5SVikram Garhwal return 0; 92e7218dd5SVikram Garhwal } 93e7218dd5SVikram Garhwal 94886e5adeSEdgar E. Iglesias static MapCache *xen_map_cache_init_single(phys_offset_to_gaddr_t f, 95886e5adeSEdgar E. Iglesias void *opaque, 96*b771b026SEdgar E. Iglesias unsigned int bucket_shift, 97886e5adeSEdgar E. Iglesias unsigned long max_size) 98e7218dd5SVikram Garhwal { 99e7218dd5SVikram Garhwal unsigned long size; 100886e5adeSEdgar E. Iglesias MapCache *mc; 101886e5adeSEdgar E. Iglesias 102*b771b026SEdgar E. Iglesias assert(bucket_shift >= XC_PAGE_SHIFT); 103*b771b026SEdgar E. Iglesias 104886e5adeSEdgar E. Iglesias mc = g_new0(MapCache, 1); 105886e5adeSEdgar E. Iglesias 106886e5adeSEdgar E. Iglesias mc->phys_offset_to_gaddr = f; 107886e5adeSEdgar E. Iglesias mc->opaque = opaque; 108886e5adeSEdgar E. Iglesias qemu_mutex_init(&mc->lock); 109886e5adeSEdgar E. Iglesias 110886e5adeSEdgar E. Iglesias QTAILQ_INIT(&mc->locked_entries); 111886e5adeSEdgar E. Iglesias 112*b771b026SEdgar E. Iglesias mc->bucket_shift = bucket_shift; 113*b771b026SEdgar E. Iglesias mc->bucket_size = 1UL << bucket_shift; 114886e5adeSEdgar E. Iglesias mc->max_mcache_size = max_size; 115886e5adeSEdgar E. Iglesias 116886e5adeSEdgar E. Iglesias mc->nr_buckets = 117886e5adeSEdgar E. Iglesias (((mc->max_mcache_size >> XC_PAGE_SHIFT) + 118*b771b026SEdgar E. Iglesias (1UL << (bucket_shift - XC_PAGE_SHIFT)) - 1) >> 119*b771b026SEdgar E. Iglesias (bucket_shift - XC_PAGE_SHIFT)); 120886e5adeSEdgar E. Iglesias 121886e5adeSEdgar E. Iglesias size = mc->nr_buckets * sizeof(MapCacheEntry); 122886e5adeSEdgar E. Iglesias size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1); 123886e5adeSEdgar E. Iglesias trace_xen_map_cache_init(mc->nr_buckets, size); 124886e5adeSEdgar E. Iglesias mc->entry = g_malloc0(size); 125886e5adeSEdgar E. Iglesias return mc; 126886e5adeSEdgar E. Iglesias } 127886e5adeSEdgar E. Iglesias 128886e5adeSEdgar E. Iglesias void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) 129886e5adeSEdgar E. Iglesias { 130e7218dd5SVikram Garhwal struct rlimit rlimit_as; 131886e5adeSEdgar E. Iglesias unsigned long max_mcache_size; 132*b771b026SEdgar E. Iglesias unsigned int bucket_shift; 133*b771b026SEdgar E. Iglesias 134*b771b026SEdgar E. Iglesias if (HOST_LONG_BITS == 32) { 135*b771b026SEdgar E. Iglesias bucket_shift = 16; 136*b771b026SEdgar E. Iglesias } else { 137*b771b026SEdgar E. Iglesias bucket_shift = 20; 138*b771b026SEdgar E. Iglesias } 139e7218dd5SVikram Garhwal 140e7218dd5SVikram Garhwal if (geteuid() == 0) { 141e7218dd5SVikram Garhwal rlimit_as.rlim_cur = RLIM_INFINITY; 142e7218dd5SVikram Garhwal rlimit_as.rlim_max = RLIM_INFINITY; 143886e5adeSEdgar E. Iglesias max_mcache_size = MCACHE_MAX_SIZE; 144e7218dd5SVikram Garhwal } else { 145e7218dd5SVikram Garhwal getrlimit(RLIMIT_AS, &rlimit_as); 146e7218dd5SVikram Garhwal rlimit_as.rlim_cur = rlimit_as.rlim_max; 147e7218dd5SVikram Garhwal 148e7218dd5SVikram Garhwal if (rlimit_as.rlim_max != RLIM_INFINITY) { 149e7218dd5SVikram Garhwal warn_report("QEMU's maximum size of virtual" 150e7218dd5SVikram Garhwal " memory is not infinity"); 151e7218dd5SVikram Garhwal } 152e7218dd5SVikram Garhwal if (rlimit_as.rlim_max < MCACHE_MAX_SIZE + NON_MCACHE_MEMORY_SIZE) { 153886e5adeSEdgar E. Iglesias max_mcache_size = rlimit_as.rlim_max - NON_MCACHE_MEMORY_SIZE; 154e7218dd5SVikram Garhwal } else { 155886e5adeSEdgar E. Iglesias max_mcache_size = MCACHE_MAX_SIZE; 156e7218dd5SVikram Garhwal } 157e7218dd5SVikram Garhwal } 158e7218dd5SVikram Garhwal 159*b771b026SEdgar E. Iglesias mapcache = xen_map_cache_init_single(f, opaque, 160*b771b026SEdgar E. Iglesias bucket_shift, 161*b771b026SEdgar E. Iglesias max_mcache_size); 162e7218dd5SVikram Garhwal setrlimit(RLIMIT_AS, &rlimit_as); 163e7218dd5SVikram Garhwal } 164e7218dd5SVikram Garhwal 1659b1f33faSEdgar E. Iglesias static void xen_remap_bucket(MapCache *mc, 1669b1f33faSEdgar E. Iglesias MapCacheEntry *entry, 167e7218dd5SVikram Garhwal void *vaddr, 168e7218dd5SVikram Garhwal hwaddr size, 169e7218dd5SVikram Garhwal hwaddr address_index, 170e7218dd5SVikram Garhwal bool dummy) 171e7218dd5SVikram Garhwal { 172e7218dd5SVikram Garhwal uint8_t *vaddr_base; 173e7218dd5SVikram Garhwal xen_pfn_t *pfns; 174e7218dd5SVikram Garhwal int *err; 175e7218dd5SVikram Garhwal unsigned int i; 176e7218dd5SVikram Garhwal hwaddr nb_pfn = size >> XC_PAGE_SHIFT; 177e7218dd5SVikram Garhwal 178e7218dd5SVikram Garhwal trace_xen_remap_bucket(address_index); 179e7218dd5SVikram Garhwal 180e7218dd5SVikram Garhwal pfns = g_new0(xen_pfn_t, nb_pfn); 181e7218dd5SVikram Garhwal err = g_new0(int, nb_pfn); 182e7218dd5SVikram Garhwal 183e7218dd5SVikram Garhwal if (entry->vaddr_base != NULL) { 184e7218dd5SVikram Garhwal if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { 185e7218dd5SVikram Garhwal ram_block_notify_remove(entry->vaddr_base, entry->size, 186e7218dd5SVikram Garhwal entry->size); 187e7218dd5SVikram Garhwal } 188e7218dd5SVikram Garhwal 189e7218dd5SVikram Garhwal /* 190e7218dd5SVikram Garhwal * If an entry is being replaced by another mapping and we're using 191e7218dd5SVikram Garhwal * MAP_FIXED flag for it - there is possibility of a race for vaddr 192e7218dd5SVikram Garhwal * address with another thread doing an mmap call itself 193e7218dd5SVikram Garhwal * (see man 2 mmap). To avoid that we skip explicit unmapping here 194e7218dd5SVikram Garhwal * and allow the kernel to destroy the previous mappings by replacing 195e7218dd5SVikram Garhwal * them in mmap call later. 196e7218dd5SVikram Garhwal * 197e7218dd5SVikram Garhwal * Non-identical replacements are not allowed therefore. 198e7218dd5SVikram Garhwal */ 199e7218dd5SVikram Garhwal assert(!vaddr || (entry->vaddr_base == vaddr && entry->size == size)); 200e7218dd5SVikram Garhwal 201e7218dd5SVikram Garhwal if (!vaddr && munmap(entry->vaddr_base, entry->size) != 0) { 202e7218dd5SVikram Garhwal perror("unmap fails"); 203e7218dd5SVikram Garhwal exit(-1); 204e7218dd5SVikram Garhwal } 205e7218dd5SVikram Garhwal } 206e7218dd5SVikram Garhwal g_free(entry->valid_mapping); 207e7218dd5SVikram Garhwal entry->valid_mapping = NULL; 208e7218dd5SVikram Garhwal 209e7218dd5SVikram Garhwal for (i = 0; i < nb_pfn; i++) { 210*b771b026SEdgar E. Iglesias pfns[i] = (address_index << (mc->bucket_shift - XC_PAGE_SHIFT)) + i; 211e7218dd5SVikram Garhwal } 212e7218dd5SVikram Garhwal 213e7218dd5SVikram Garhwal /* 214e7218dd5SVikram Garhwal * If the caller has requested the mapping at a specific address use 215e7218dd5SVikram Garhwal * MAP_FIXED to make sure it's honored. 216e7218dd5SVikram Garhwal */ 217e7218dd5SVikram Garhwal if (!dummy) { 218e7218dd5SVikram Garhwal vaddr_base = xenforeignmemory_map2(xen_fmem, xen_domid, vaddr, 219e7218dd5SVikram Garhwal PROT_READ | PROT_WRITE, 220e7218dd5SVikram Garhwal vaddr ? MAP_FIXED : 0, 221e7218dd5SVikram Garhwal nb_pfn, pfns, err); 222e7218dd5SVikram Garhwal if (vaddr_base == NULL) { 223e7218dd5SVikram Garhwal perror("xenforeignmemory_map2"); 224e7218dd5SVikram Garhwal exit(-1); 225e7218dd5SVikram Garhwal } 226e7218dd5SVikram Garhwal } else { 227e7218dd5SVikram Garhwal /* 228e7218dd5SVikram Garhwal * We create dummy mappings where we are unable to create a foreign 229e7218dd5SVikram Garhwal * mapping immediately due to certain circumstances (i.e. on resume now) 230e7218dd5SVikram Garhwal */ 231e7218dd5SVikram Garhwal vaddr_base = mmap(vaddr, size, PROT_READ | PROT_WRITE, 232e7218dd5SVikram Garhwal MAP_ANON | MAP_SHARED | (vaddr ? MAP_FIXED : 0), 233e7218dd5SVikram Garhwal -1, 0); 234e7218dd5SVikram Garhwal if (vaddr_base == MAP_FAILED) { 235e7218dd5SVikram Garhwal perror("mmap"); 236e7218dd5SVikram Garhwal exit(-1); 237e7218dd5SVikram Garhwal } 238e7218dd5SVikram Garhwal } 239e7218dd5SVikram Garhwal 240e7218dd5SVikram Garhwal if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { 241e7218dd5SVikram Garhwal ram_block_notify_add(vaddr_base, size, size); 242e7218dd5SVikram Garhwal } 243e7218dd5SVikram Garhwal 244e7218dd5SVikram Garhwal entry->vaddr_base = vaddr_base; 245e7218dd5SVikram Garhwal entry->paddr_index = address_index; 246e7218dd5SVikram Garhwal entry->size = size; 247e7218dd5SVikram Garhwal entry->valid_mapping = g_new0(unsigned long, 248e7218dd5SVikram Garhwal BITS_TO_LONGS(size >> XC_PAGE_SHIFT)); 249e7218dd5SVikram Garhwal 250e7218dd5SVikram Garhwal if (dummy) { 251e7218dd5SVikram Garhwal entry->flags |= XEN_MAPCACHE_ENTRY_DUMMY; 252e7218dd5SVikram Garhwal } else { 253e7218dd5SVikram Garhwal entry->flags &= ~(XEN_MAPCACHE_ENTRY_DUMMY); 254e7218dd5SVikram Garhwal } 255e7218dd5SVikram Garhwal 256e7218dd5SVikram Garhwal bitmap_zero(entry->valid_mapping, nb_pfn); 257e7218dd5SVikram Garhwal for (i = 0; i < nb_pfn; i++) { 258e7218dd5SVikram Garhwal if (!err[i]) { 259e7218dd5SVikram Garhwal bitmap_set(entry->valid_mapping, i, 1); 260e7218dd5SVikram Garhwal } 261e7218dd5SVikram Garhwal } 262e7218dd5SVikram Garhwal 263e7218dd5SVikram Garhwal g_free(pfns); 264e7218dd5SVikram Garhwal g_free(err); 265e7218dd5SVikram Garhwal } 266e7218dd5SVikram Garhwal 267eda3a8cdSEdgar E. Iglesias static uint8_t *xen_map_cache_unlocked(MapCache *mc, 268eda3a8cdSEdgar E. Iglesias hwaddr phys_addr, hwaddr size, 2695a5585f4SEdgar E. Iglesias uint8_t lock, bool dma, bool is_write) 270e7218dd5SVikram Garhwal { 271e7218dd5SVikram Garhwal MapCacheEntry *entry, *pentry = NULL, 272e7218dd5SVikram Garhwal *free_entry = NULL, *free_pentry = NULL; 273e7218dd5SVikram Garhwal hwaddr address_index; 274e7218dd5SVikram Garhwal hwaddr address_offset; 275e7218dd5SVikram Garhwal hwaddr cache_size = size; 276e7218dd5SVikram Garhwal hwaddr test_bit_size; 277e7218dd5SVikram Garhwal bool translated G_GNUC_UNUSED = false; 278e7218dd5SVikram Garhwal bool dummy = false; 279e7218dd5SVikram Garhwal 280e7218dd5SVikram Garhwal tryagain: 281*b771b026SEdgar E. Iglesias address_index = phys_addr >> mc->bucket_shift; 282*b771b026SEdgar E. Iglesias address_offset = phys_addr & (mc->bucket_size - 1); 283e7218dd5SVikram Garhwal 284e7218dd5SVikram Garhwal trace_xen_map_cache(phys_addr); 285e7218dd5SVikram Garhwal 286e7218dd5SVikram Garhwal /* test_bit_size is always a multiple of XC_PAGE_SIZE */ 287e7218dd5SVikram Garhwal if (size) { 288e7218dd5SVikram Garhwal test_bit_size = size + (phys_addr & (XC_PAGE_SIZE - 1)); 289e7218dd5SVikram Garhwal 290e7218dd5SVikram Garhwal if (test_bit_size % XC_PAGE_SIZE) { 291e7218dd5SVikram Garhwal test_bit_size += XC_PAGE_SIZE - (test_bit_size % XC_PAGE_SIZE); 292e7218dd5SVikram Garhwal } 293e7218dd5SVikram Garhwal } else { 294e7218dd5SVikram Garhwal test_bit_size = XC_PAGE_SIZE; 295e7218dd5SVikram Garhwal } 296e7218dd5SVikram Garhwal 297eda3a8cdSEdgar E. Iglesias if (mc->last_entry != NULL && 298eda3a8cdSEdgar E. Iglesias mc->last_entry->paddr_index == address_index && 299e7218dd5SVikram Garhwal !lock && !size && 300e7218dd5SVikram Garhwal test_bits(address_offset >> XC_PAGE_SHIFT, 301e7218dd5SVikram Garhwal test_bit_size >> XC_PAGE_SHIFT, 302eda3a8cdSEdgar E. Iglesias mc->last_entry->valid_mapping)) { 3030402b968SManos Pitsidianakis trace_xen_map_cache_return( 304eda3a8cdSEdgar E. Iglesias mc->last_entry->vaddr_base + address_offset 3050402b968SManos Pitsidianakis ); 306eda3a8cdSEdgar E. Iglesias return mc->last_entry->vaddr_base + address_offset; 307e7218dd5SVikram Garhwal } 308e7218dd5SVikram Garhwal 309*b771b026SEdgar E. Iglesias /* size is always a multiple of mc->bucket_size */ 310e7218dd5SVikram Garhwal if (size) { 311e7218dd5SVikram Garhwal cache_size = size + address_offset; 312*b771b026SEdgar E. Iglesias if (cache_size % mc->bucket_size) { 313*b771b026SEdgar E. Iglesias cache_size += mc->bucket_size - (cache_size % mc->bucket_size); 314e7218dd5SVikram Garhwal } 315e7218dd5SVikram Garhwal } else { 316*b771b026SEdgar E. Iglesias cache_size = mc->bucket_size; 317e7218dd5SVikram Garhwal } 318e7218dd5SVikram Garhwal 319eda3a8cdSEdgar E. Iglesias entry = &mc->entry[address_index % mc->nr_buckets]; 320e7218dd5SVikram Garhwal 321e7218dd5SVikram Garhwal while (entry && (lock || entry->lock) && entry->vaddr_base && 322e7218dd5SVikram Garhwal (entry->paddr_index != address_index || entry->size != cache_size || 323e7218dd5SVikram Garhwal !test_bits(address_offset >> XC_PAGE_SHIFT, 324e7218dd5SVikram Garhwal test_bit_size >> XC_PAGE_SHIFT, 325e7218dd5SVikram Garhwal entry->valid_mapping))) { 326e7218dd5SVikram Garhwal if (!free_entry && !entry->lock) { 327e7218dd5SVikram Garhwal free_entry = entry; 328e7218dd5SVikram Garhwal free_pentry = pentry; 329e7218dd5SVikram Garhwal } 330e7218dd5SVikram Garhwal pentry = entry; 331e7218dd5SVikram Garhwal entry = entry->next; 332e7218dd5SVikram Garhwal } 333e7218dd5SVikram Garhwal if (!entry && free_entry) { 334e7218dd5SVikram Garhwal entry = free_entry; 335e7218dd5SVikram Garhwal pentry = free_pentry; 336e7218dd5SVikram Garhwal } 337e7218dd5SVikram Garhwal if (!entry) { 338e7218dd5SVikram Garhwal entry = g_new0(MapCacheEntry, 1); 339e7218dd5SVikram Garhwal pentry->next = entry; 3409b1f33faSEdgar E. Iglesias xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy); 341e7218dd5SVikram Garhwal } else if (!entry->lock) { 342e7218dd5SVikram Garhwal if (!entry->vaddr_base || entry->paddr_index != address_index || 343e7218dd5SVikram Garhwal entry->size != cache_size || 344e7218dd5SVikram Garhwal !test_bits(address_offset >> XC_PAGE_SHIFT, 345e7218dd5SVikram Garhwal test_bit_size >> XC_PAGE_SHIFT, 346e7218dd5SVikram Garhwal entry->valid_mapping)) { 3479b1f33faSEdgar E. Iglesias xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy); 348e7218dd5SVikram Garhwal } 349e7218dd5SVikram Garhwal } 350e7218dd5SVikram Garhwal 351e7218dd5SVikram Garhwal if(!test_bits(address_offset >> XC_PAGE_SHIFT, 352e7218dd5SVikram Garhwal test_bit_size >> XC_PAGE_SHIFT, 353e7218dd5SVikram Garhwal entry->valid_mapping)) { 354eda3a8cdSEdgar E. Iglesias mc->last_entry = NULL; 355e7218dd5SVikram Garhwal #ifdef XEN_COMPAT_PHYSMAP 356eda3a8cdSEdgar E. Iglesias if (!translated && mc->phys_offset_to_gaddr) { 357eda3a8cdSEdgar E. Iglesias phys_addr = mc->phys_offset_to_gaddr(phys_addr, size); 358e7218dd5SVikram Garhwal translated = true; 359e7218dd5SVikram Garhwal goto tryagain; 360e7218dd5SVikram Garhwal } 361e7218dd5SVikram Garhwal #endif 362e7218dd5SVikram Garhwal if (!dummy && runstate_check(RUN_STATE_INMIGRATE)) { 363e7218dd5SVikram Garhwal dummy = true; 364e7218dd5SVikram Garhwal goto tryagain; 365e7218dd5SVikram Garhwal } 366e7218dd5SVikram Garhwal trace_xen_map_cache_return(NULL); 367e7218dd5SVikram Garhwal return NULL; 368e7218dd5SVikram Garhwal } 369e7218dd5SVikram Garhwal 370eda3a8cdSEdgar E. Iglesias mc->last_entry = entry; 371e7218dd5SVikram Garhwal if (lock) { 372e7218dd5SVikram Garhwal MapCacheRev *reventry = g_new0(MapCacheRev, 1); 373e7218dd5SVikram Garhwal entry->lock++; 374e7218dd5SVikram Garhwal if (entry->lock == 0) { 375d0e16850SManos Pitsidianakis error_report("mapcache entry lock overflow: "HWADDR_FMT_plx" -> %p", 376e7218dd5SVikram Garhwal entry->paddr_index, entry->vaddr_base); 377e7218dd5SVikram Garhwal abort(); 378e7218dd5SVikram Garhwal } 379e7218dd5SVikram Garhwal reventry->dma = dma; 380eda3a8cdSEdgar E. Iglesias reventry->vaddr_req = mc->last_entry->vaddr_base + address_offset; 381eda3a8cdSEdgar E. Iglesias reventry->paddr_index = mc->last_entry->paddr_index; 382e7218dd5SVikram Garhwal reventry->size = entry->size; 383eda3a8cdSEdgar E. Iglesias QTAILQ_INSERT_HEAD(&mc->locked_entries, reventry, next); 384e7218dd5SVikram Garhwal } 385e7218dd5SVikram Garhwal 3860402b968SManos Pitsidianakis trace_xen_map_cache_return( 387eda3a8cdSEdgar E. Iglesias mc->last_entry->vaddr_base + address_offset 3880402b968SManos Pitsidianakis ); 389eda3a8cdSEdgar E. Iglesias return mc->last_entry->vaddr_base + address_offset; 390e7218dd5SVikram Garhwal } 391e7218dd5SVikram Garhwal 3925a5585f4SEdgar E. Iglesias uint8_t *xen_map_cache(MemoryRegion *mr, 3935a5585f4SEdgar E. Iglesias hwaddr phys_addr, hwaddr size, 3945a5585f4SEdgar E. Iglesias uint8_t lock, bool dma, 3955a5585f4SEdgar E. Iglesias bool is_write) 396e7218dd5SVikram Garhwal { 397e7218dd5SVikram Garhwal uint8_t *p; 398e7218dd5SVikram Garhwal 399efb0c6caSEdgar E. Iglesias mapcache_lock(mapcache); 4005a5585f4SEdgar E. Iglesias p = xen_map_cache_unlocked(mapcache, phys_addr, size, lock, dma, is_write); 401efb0c6caSEdgar E. Iglesias mapcache_unlock(mapcache); 402e7218dd5SVikram Garhwal return p; 403e7218dd5SVikram Garhwal } 404e7218dd5SVikram Garhwal 4059b005553SEdgar E. Iglesias static ram_addr_t xen_ram_addr_from_mapcache_single(MapCache *mc, void *ptr) 406e7218dd5SVikram Garhwal { 407e7218dd5SVikram Garhwal MapCacheEntry *entry = NULL; 408e7218dd5SVikram Garhwal MapCacheRev *reventry; 409e7218dd5SVikram Garhwal hwaddr paddr_index; 410e7218dd5SVikram Garhwal hwaddr size; 411e7218dd5SVikram Garhwal ram_addr_t raddr; 412e7218dd5SVikram Garhwal int found = 0; 413e7218dd5SVikram Garhwal 4149b005553SEdgar E. Iglesias mapcache_lock(mc); 4159b005553SEdgar E. Iglesias QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { 416e7218dd5SVikram Garhwal if (reventry->vaddr_req == ptr) { 417e7218dd5SVikram Garhwal paddr_index = reventry->paddr_index; 418e7218dd5SVikram Garhwal size = reventry->size; 419e7218dd5SVikram Garhwal found = 1; 420e7218dd5SVikram Garhwal break; 421e7218dd5SVikram Garhwal } 422e7218dd5SVikram Garhwal } 423e7218dd5SVikram Garhwal if (!found) { 4240402b968SManos Pitsidianakis trace_xen_ram_addr_from_mapcache_not_found(ptr); 4259b005553SEdgar E. Iglesias mapcache_unlock(mc); 426337265dbSJuergen Gross return RAM_ADDR_INVALID; 427e7218dd5SVikram Garhwal } 428e7218dd5SVikram Garhwal 4299b005553SEdgar E. Iglesias entry = &mc->entry[paddr_index % mc->nr_buckets]; 430e7218dd5SVikram Garhwal while (entry && (entry->paddr_index != paddr_index || entry->size != size)) { 431e7218dd5SVikram Garhwal entry = entry->next; 432e7218dd5SVikram Garhwal } 433e7218dd5SVikram Garhwal if (!entry) { 4340402b968SManos Pitsidianakis trace_xen_ram_addr_from_mapcache_not_in_cache(ptr); 435337265dbSJuergen Gross raddr = RAM_ADDR_INVALID; 436e7218dd5SVikram Garhwal } else { 437*b771b026SEdgar E. Iglesias raddr = (reventry->paddr_index << mc->bucket_shift) + 438e7218dd5SVikram Garhwal ((unsigned long) ptr - (unsigned long) entry->vaddr_base); 439e7218dd5SVikram Garhwal } 4409b005553SEdgar E. Iglesias mapcache_unlock(mc); 441e7218dd5SVikram Garhwal return raddr; 442e7218dd5SVikram Garhwal } 443e7218dd5SVikram Garhwal 4449b005553SEdgar E. Iglesias ram_addr_t xen_ram_addr_from_mapcache(void *ptr) 4459b005553SEdgar E. Iglesias { 4469b005553SEdgar E. Iglesias return xen_ram_addr_from_mapcache_single(mapcache, ptr); 4479b005553SEdgar E. Iglesias } 4489b005553SEdgar E. Iglesias 44987b5a05aSEdgar E. Iglesias static void xen_invalidate_map_cache_entry_unlocked(MapCache *mc, 45087b5a05aSEdgar E. Iglesias uint8_t *buffer) 451e7218dd5SVikram Garhwal { 452e7218dd5SVikram Garhwal MapCacheEntry *entry = NULL, *pentry = NULL; 453e7218dd5SVikram Garhwal MapCacheRev *reventry; 454e7218dd5SVikram Garhwal hwaddr paddr_index; 455e7218dd5SVikram Garhwal hwaddr size; 456e7218dd5SVikram Garhwal int found = 0; 457e7218dd5SVikram Garhwal 45887b5a05aSEdgar E. Iglesias QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { 459e7218dd5SVikram Garhwal if (reventry->vaddr_req == buffer) { 460e7218dd5SVikram Garhwal paddr_index = reventry->paddr_index; 461e7218dd5SVikram Garhwal size = reventry->size; 462e7218dd5SVikram Garhwal found = 1; 463e7218dd5SVikram Garhwal break; 464e7218dd5SVikram Garhwal } 465e7218dd5SVikram Garhwal } 466e7218dd5SVikram Garhwal if (!found) { 4670402b968SManos Pitsidianakis trace_xen_invalidate_map_cache_entry_unlocked_not_found(buffer); 46887b5a05aSEdgar E. Iglesias QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { 4690402b968SManos Pitsidianakis trace_xen_invalidate_map_cache_entry_unlocked_found( 4700402b968SManos Pitsidianakis reventry->paddr_index, 4710402b968SManos Pitsidianakis reventry->vaddr_req 4720402b968SManos Pitsidianakis ); 473e7218dd5SVikram Garhwal } 474e7218dd5SVikram Garhwal return; 475e7218dd5SVikram Garhwal } 47687b5a05aSEdgar E. Iglesias QTAILQ_REMOVE(&mc->locked_entries, reventry, next); 477e7218dd5SVikram Garhwal g_free(reventry); 478e7218dd5SVikram Garhwal 47987b5a05aSEdgar E. Iglesias if (mc->last_entry != NULL && 48087b5a05aSEdgar E. Iglesias mc->last_entry->paddr_index == paddr_index) { 48187b5a05aSEdgar E. Iglesias mc->last_entry = NULL; 482e7218dd5SVikram Garhwal } 483e7218dd5SVikram Garhwal 48487b5a05aSEdgar E. Iglesias entry = &mc->entry[paddr_index % mc->nr_buckets]; 485e7218dd5SVikram Garhwal while (entry && (entry->paddr_index != paddr_index || entry->size != size)) { 486e7218dd5SVikram Garhwal pentry = entry; 487e7218dd5SVikram Garhwal entry = entry->next; 488e7218dd5SVikram Garhwal } 489e7218dd5SVikram Garhwal if (!entry) { 4900402b968SManos Pitsidianakis trace_xen_invalidate_map_cache_entry_unlocked_miss(buffer); 491e7218dd5SVikram Garhwal return; 492e7218dd5SVikram Garhwal } 493e7218dd5SVikram Garhwal entry->lock--; 494e7218dd5SVikram Garhwal if (entry->lock > 0 || pentry == NULL) { 495e7218dd5SVikram Garhwal return; 496e7218dd5SVikram Garhwal } 497e7218dd5SVikram Garhwal 498e7218dd5SVikram Garhwal pentry->next = entry->next; 499e7218dd5SVikram Garhwal ram_block_notify_remove(entry->vaddr_base, entry->size, entry->size); 500e7218dd5SVikram Garhwal if (munmap(entry->vaddr_base, entry->size) != 0) { 501e7218dd5SVikram Garhwal perror("unmap fails"); 502e7218dd5SVikram Garhwal exit(-1); 503e7218dd5SVikram Garhwal } 504e7218dd5SVikram Garhwal g_free(entry->valid_mapping); 505e7218dd5SVikram Garhwal g_free(entry); 506e7218dd5SVikram Garhwal } 507e7218dd5SVikram Garhwal 5089253d830SPeng Fan typedef struct XenMapCacheData { 5099253d830SPeng Fan Coroutine *co; 5109253d830SPeng Fan uint8_t *buffer; 5119253d830SPeng Fan } XenMapCacheData; 5129253d830SPeng Fan 5139253d830SPeng Fan static void xen_invalidate_map_cache_entry_bh(void *opaque) 514e7218dd5SVikram Garhwal { 5159253d830SPeng Fan XenMapCacheData *data = opaque; 5169253d830SPeng Fan 517efb0c6caSEdgar E. Iglesias mapcache_lock(mapcache); 51887b5a05aSEdgar E. Iglesias xen_invalidate_map_cache_entry_unlocked(mapcache, data->buffer); 519efb0c6caSEdgar E. Iglesias mapcache_unlock(mapcache); 5209253d830SPeng Fan 5219253d830SPeng Fan aio_co_wake(data->co); 5229253d830SPeng Fan } 5239253d830SPeng Fan 5249253d830SPeng Fan void coroutine_mixed_fn xen_invalidate_map_cache_entry(uint8_t *buffer) 5259253d830SPeng Fan { 5269253d830SPeng Fan if (qemu_in_coroutine()) { 5279253d830SPeng Fan XenMapCacheData data = { 5289253d830SPeng Fan .co = qemu_coroutine_self(), 5299253d830SPeng Fan .buffer = buffer, 5309253d830SPeng Fan }; 5319253d830SPeng Fan aio_bh_schedule_oneshot(qemu_get_current_aio_context(), 5329253d830SPeng Fan xen_invalidate_map_cache_entry_bh, &data); 5339253d830SPeng Fan qemu_coroutine_yield(); 5349253d830SPeng Fan } else { 535efb0c6caSEdgar E. Iglesias mapcache_lock(mapcache); 53687b5a05aSEdgar E. Iglesias xen_invalidate_map_cache_entry_unlocked(mapcache, buffer); 537efb0c6caSEdgar E. Iglesias mapcache_unlock(mapcache); 538e7218dd5SVikram Garhwal } 5399253d830SPeng Fan } 540e7218dd5SVikram Garhwal 541946b4c9bSEdgar E. Iglesias static void xen_invalidate_map_cache_single(MapCache *mc) 542e7218dd5SVikram Garhwal { 543e7218dd5SVikram Garhwal unsigned long i; 544e7218dd5SVikram Garhwal MapCacheRev *reventry; 545e7218dd5SVikram Garhwal 546946b4c9bSEdgar E. Iglesias mapcache_lock(mc); 547e7218dd5SVikram Garhwal 548946b4c9bSEdgar E. Iglesias QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { 549e7218dd5SVikram Garhwal if (!reventry->dma) { 550e7218dd5SVikram Garhwal continue; 551e7218dd5SVikram Garhwal } 5520402b968SManos Pitsidianakis trace_xen_invalidate_map_cache(reventry->paddr_index, 5530402b968SManos Pitsidianakis reventry->vaddr_req); 554e7218dd5SVikram Garhwal } 555e7218dd5SVikram Garhwal 556946b4c9bSEdgar E. Iglesias for (i = 0; i < mc->nr_buckets; i++) { 557946b4c9bSEdgar E. Iglesias MapCacheEntry *entry = &mc->entry[i]; 558e7218dd5SVikram Garhwal 559e7218dd5SVikram Garhwal if (entry->vaddr_base == NULL) { 560e7218dd5SVikram Garhwal continue; 561e7218dd5SVikram Garhwal } 562e7218dd5SVikram Garhwal if (entry->lock > 0) { 563e7218dd5SVikram Garhwal continue; 564e7218dd5SVikram Garhwal } 565e7218dd5SVikram Garhwal 566e7218dd5SVikram Garhwal if (munmap(entry->vaddr_base, entry->size) != 0) { 567e7218dd5SVikram Garhwal perror("unmap fails"); 568e7218dd5SVikram Garhwal exit(-1); 569e7218dd5SVikram Garhwal } 570e7218dd5SVikram Garhwal 571e7218dd5SVikram Garhwal entry->paddr_index = 0; 572e7218dd5SVikram Garhwal entry->vaddr_base = NULL; 573e7218dd5SVikram Garhwal entry->size = 0; 574e7218dd5SVikram Garhwal g_free(entry->valid_mapping); 575e7218dd5SVikram Garhwal entry->valid_mapping = NULL; 576e7218dd5SVikram Garhwal } 577e7218dd5SVikram Garhwal 578946b4c9bSEdgar E. Iglesias mc->last_entry = NULL; 579e7218dd5SVikram Garhwal 580946b4c9bSEdgar E. Iglesias mapcache_unlock(mc); 581946b4c9bSEdgar E. Iglesias } 582946b4c9bSEdgar E. Iglesias 583946b4c9bSEdgar E. Iglesias void xen_invalidate_map_cache(void) 584946b4c9bSEdgar E. Iglesias { 585946b4c9bSEdgar E. Iglesias /* Flush pending AIO before destroying the mapcache */ 586946b4c9bSEdgar E. Iglesias bdrv_drain_all(); 587946b4c9bSEdgar E. Iglesias 588946b4c9bSEdgar E. Iglesias xen_invalidate_map_cache_single(mapcache); 589e7218dd5SVikram Garhwal } 590e7218dd5SVikram Garhwal 5918be27f50SEdgar E. Iglesias static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc, 5928be27f50SEdgar E. Iglesias hwaddr old_phys_addr, 593e7218dd5SVikram Garhwal hwaddr new_phys_addr, 594e7218dd5SVikram Garhwal hwaddr size) 595e7218dd5SVikram Garhwal { 596e7218dd5SVikram Garhwal MapCacheEntry *entry; 597e7218dd5SVikram Garhwal hwaddr address_index, address_offset; 598e7218dd5SVikram Garhwal hwaddr test_bit_size, cache_size = size; 599e7218dd5SVikram Garhwal 600*b771b026SEdgar E. Iglesias address_index = old_phys_addr >> mc->bucket_shift; 601*b771b026SEdgar E. Iglesias address_offset = old_phys_addr & (mc->bucket_size - 1); 602e7218dd5SVikram Garhwal 603e7218dd5SVikram Garhwal assert(size); 604e7218dd5SVikram Garhwal /* test_bit_size is always a multiple of XC_PAGE_SIZE */ 605e7218dd5SVikram Garhwal test_bit_size = size + (old_phys_addr & (XC_PAGE_SIZE - 1)); 606e7218dd5SVikram Garhwal if (test_bit_size % XC_PAGE_SIZE) { 607e7218dd5SVikram Garhwal test_bit_size += XC_PAGE_SIZE - (test_bit_size % XC_PAGE_SIZE); 608e7218dd5SVikram Garhwal } 609e7218dd5SVikram Garhwal cache_size = size + address_offset; 610*b771b026SEdgar E. Iglesias if (cache_size % mc->bucket_size) { 611*b771b026SEdgar E. Iglesias cache_size += mc->bucket_size - (cache_size % mc->bucket_size); 612e7218dd5SVikram Garhwal } 613e7218dd5SVikram Garhwal 6148be27f50SEdgar E. Iglesias entry = &mc->entry[address_index % mc->nr_buckets]; 615e7218dd5SVikram Garhwal while (entry && !(entry->paddr_index == address_index && 616e7218dd5SVikram Garhwal entry->size == cache_size)) { 617e7218dd5SVikram Garhwal entry = entry->next; 618e7218dd5SVikram Garhwal } 619e7218dd5SVikram Garhwal if (!entry) { 6200402b968SManos Pitsidianakis trace_xen_replace_cache_entry_unlocked(old_phys_addr); 621e7218dd5SVikram Garhwal return NULL; 622e7218dd5SVikram Garhwal } 623e7218dd5SVikram Garhwal 624*b771b026SEdgar E. Iglesias address_index = new_phys_addr >> mc->bucket_shift; 625*b771b026SEdgar E. Iglesias address_offset = new_phys_addr & (mc->bucket_size - 1); 626e7218dd5SVikram Garhwal 6270402b968SManos Pitsidianakis trace_xen_replace_cache_entry_dummy(old_phys_addr, new_phys_addr); 628e7218dd5SVikram Garhwal 6298be27f50SEdgar E. Iglesias xen_remap_bucket(mc, entry, entry->vaddr_base, 630e7218dd5SVikram Garhwal cache_size, address_index, false); 631e7218dd5SVikram Garhwal if (!test_bits(address_offset >> XC_PAGE_SHIFT, 632e7218dd5SVikram Garhwal test_bit_size >> XC_PAGE_SHIFT, 633e7218dd5SVikram Garhwal entry->valid_mapping)) { 6340402b968SManos Pitsidianakis trace_xen_replace_cache_entry_unlocked_could_not_update_entry( 6350402b968SManos Pitsidianakis old_phys_addr 6360402b968SManos Pitsidianakis ); 637e7218dd5SVikram Garhwal return NULL; 638e7218dd5SVikram Garhwal } 639e7218dd5SVikram Garhwal 640e7218dd5SVikram Garhwal return entry->vaddr_base + address_offset; 641e7218dd5SVikram Garhwal } 642e7218dd5SVikram Garhwal 643e7218dd5SVikram Garhwal uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, 644e7218dd5SVikram Garhwal hwaddr new_phys_addr, 645e7218dd5SVikram Garhwal hwaddr size) 646e7218dd5SVikram Garhwal { 647e7218dd5SVikram Garhwal uint8_t *p; 648e7218dd5SVikram Garhwal 649efb0c6caSEdgar E. Iglesias mapcache_lock(mapcache); 6508be27f50SEdgar E. Iglesias p = xen_replace_cache_entry_unlocked(mapcache, old_phys_addr, 6518be27f50SEdgar E. Iglesias new_phys_addr, size); 652efb0c6caSEdgar E. Iglesias mapcache_unlock(mapcache); 653e7218dd5SVikram Garhwal return p; 654e7218dd5SVikram Garhwal } 655