xref: /openbmc/qemu/hw/xen/xen-mapcache.c (revision b771b026d8495feea8c3b7aee43ee2084102e3b4)
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