2
0

xen-mapcache.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. * Copyright (C) 2011 Citrix Ltd.
  3. *
  4. * This work is licensed under the terms of the GNU GPL, version 2. See
  5. * the COPYING file in the top-level directory.
  6. *
  7. */
  8. #include "config.h"
  9. #include <sys/resource.h>
  10. #include "hw/xen_backend.h"
  11. #include "blockdev.h"
  12. #include "bitmap.h"
  13. #include <xen/hvm/params.h>
  14. #include <sys/mman.h>
  15. #include "xen-mapcache.h"
  16. #include "trace.h"
  17. //#define MAPCACHE_DEBUG
  18. #ifdef MAPCACHE_DEBUG
  19. # define DPRINTF(fmt, ...) do { \
  20. fprintf(stderr, "xen_mapcache: " fmt, ## __VA_ARGS__); \
  21. } while (0)
  22. #else
  23. # define DPRINTF(fmt, ...) do { } while (0)
  24. #endif
  25. #if defined(__i386__)
  26. # define MCACHE_BUCKET_SHIFT 16
  27. # define MCACHE_MAX_SIZE (1UL<<31) /* 2GB Cap */
  28. #elif defined(__x86_64__)
  29. # define MCACHE_BUCKET_SHIFT 20
  30. # define MCACHE_MAX_SIZE (1UL<<35) /* 32GB Cap */
  31. #endif
  32. #define MCACHE_BUCKET_SIZE (1UL << MCACHE_BUCKET_SHIFT)
  33. /* This is the size of the virtual address space reserve to QEMU that will not
  34. * be use by MapCache.
  35. * From empirical tests I observed that qemu use 75MB more than the
  36. * max_mcache_size.
  37. */
  38. #define NON_MCACHE_MEMORY_SIZE (80 * 1024 * 1024)
  39. #define mapcache_lock() ((void)0)
  40. #define mapcache_unlock() ((void)0)
  41. typedef struct MapCacheEntry {
  42. target_phys_addr_t paddr_index;
  43. uint8_t *vaddr_base;
  44. unsigned long *valid_mapping;
  45. uint8_t lock;
  46. target_phys_addr_t size;
  47. struct MapCacheEntry *next;
  48. } MapCacheEntry;
  49. typedef struct MapCacheRev {
  50. uint8_t *vaddr_req;
  51. target_phys_addr_t paddr_index;
  52. target_phys_addr_t size;
  53. QTAILQ_ENTRY(MapCacheRev) next;
  54. } MapCacheRev;
  55. typedef struct MapCache {
  56. MapCacheEntry *entry;
  57. unsigned long nr_buckets;
  58. QTAILQ_HEAD(map_cache_head, MapCacheRev) locked_entries;
  59. /* For most cases (>99.9%), the page address is the same. */
  60. target_phys_addr_t last_address_index;
  61. uint8_t *last_address_vaddr;
  62. unsigned long max_mcache_size;
  63. unsigned int mcache_bucket_shift;
  64. } MapCache;
  65. static MapCache *mapcache;
  66. static inline int test_bits(int nr, int size, const unsigned long *addr)
  67. {
  68. unsigned long res = find_next_zero_bit(addr, size + nr, nr);
  69. if (res >= nr + size)
  70. return 1;
  71. else
  72. return 0;
  73. }
  74. void xen_map_cache_init(void)
  75. {
  76. unsigned long size;
  77. struct rlimit rlimit_as;
  78. mapcache = g_malloc0(sizeof (MapCache));
  79. QTAILQ_INIT(&mapcache->locked_entries);
  80. mapcache->last_address_index = -1;
  81. if (geteuid() == 0) {
  82. rlimit_as.rlim_cur = RLIM_INFINITY;
  83. rlimit_as.rlim_max = RLIM_INFINITY;
  84. mapcache->max_mcache_size = MCACHE_MAX_SIZE;
  85. } else {
  86. getrlimit(RLIMIT_AS, &rlimit_as);
  87. rlimit_as.rlim_cur = rlimit_as.rlim_max;
  88. if (rlimit_as.rlim_max != RLIM_INFINITY) {
  89. fprintf(stderr, "Warning: QEMU's maximum size of virtual"
  90. " memory is not infinity.\n");
  91. }
  92. if (rlimit_as.rlim_max < MCACHE_MAX_SIZE + NON_MCACHE_MEMORY_SIZE) {
  93. mapcache->max_mcache_size = rlimit_as.rlim_max -
  94. NON_MCACHE_MEMORY_SIZE;
  95. } else {
  96. mapcache->max_mcache_size = MCACHE_MAX_SIZE;
  97. }
  98. }
  99. setrlimit(RLIMIT_AS, &rlimit_as);
  100. mapcache->nr_buckets =
  101. (((mapcache->max_mcache_size >> XC_PAGE_SHIFT) +
  102. (1UL << (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)) - 1) >>
  103. (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT));
  104. size = mapcache->nr_buckets * sizeof (MapCacheEntry);
  105. size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1);
  106. DPRINTF("%s, nr_buckets = %lx size %lu\n", __func__,
  107. mapcache->nr_buckets, size);
  108. mapcache->entry = g_malloc0(size);
  109. }
  110. static void xen_remap_bucket(MapCacheEntry *entry,
  111. target_phys_addr_t size,
  112. target_phys_addr_t address_index)
  113. {
  114. uint8_t *vaddr_base;
  115. xen_pfn_t *pfns;
  116. int *err;
  117. unsigned int i;
  118. target_phys_addr_t nb_pfn = size >> XC_PAGE_SHIFT;
  119. trace_xen_remap_bucket(address_index);
  120. pfns = g_malloc0(nb_pfn * sizeof (xen_pfn_t));
  121. err = g_malloc0(nb_pfn * sizeof (int));
  122. if (entry->vaddr_base != NULL) {
  123. if (munmap(entry->vaddr_base, entry->size) != 0) {
  124. perror("unmap fails");
  125. exit(-1);
  126. }
  127. }
  128. if (entry->valid_mapping != NULL) {
  129. g_free(entry->valid_mapping);
  130. entry->valid_mapping = NULL;
  131. }
  132. for (i = 0; i < nb_pfn; i++) {
  133. pfns[i] = (address_index << (MCACHE_BUCKET_SHIFT-XC_PAGE_SHIFT)) + i;
  134. }
  135. vaddr_base = xc_map_foreign_bulk(xen_xc, xen_domid, PROT_READ|PROT_WRITE,
  136. pfns, err, nb_pfn);
  137. if (vaddr_base == NULL) {
  138. perror("xc_map_foreign_bulk");
  139. exit(-1);
  140. }
  141. entry->vaddr_base = vaddr_base;
  142. entry->paddr_index = address_index;
  143. entry->size = size;
  144. entry->valid_mapping = (unsigned long *) g_malloc0(sizeof(unsigned long) *
  145. BITS_TO_LONGS(size >> XC_PAGE_SHIFT));
  146. bitmap_zero(entry->valid_mapping, nb_pfn);
  147. for (i = 0; i < nb_pfn; i++) {
  148. if (!err[i]) {
  149. bitmap_set(entry->valid_mapping, i, 1);
  150. }
  151. }
  152. g_free(pfns);
  153. g_free(err);
  154. }
  155. uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size,
  156. uint8_t lock)
  157. {
  158. MapCacheEntry *entry, *pentry = NULL;
  159. target_phys_addr_t address_index = phys_addr >> MCACHE_BUCKET_SHIFT;
  160. target_phys_addr_t address_offset = phys_addr & (MCACHE_BUCKET_SIZE - 1);
  161. target_phys_addr_t __size = size;
  162. trace_xen_map_cache(phys_addr);
  163. if (address_index == mapcache->last_address_index && !lock && !__size) {
  164. trace_xen_map_cache_return(mapcache->last_address_vaddr + address_offset);
  165. return mapcache->last_address_vaddr + address_offset;
  166. }
  167. /* size is always a multiple of MCACHE_BUCKET_SIZE */
  168. if ((address_offset + (__size % MCACHE_BUCKET_SIZE)) > MCACHE_BUCKET_SIZE)
  169. __size += MCACHE_BUCKET_SIZE;
  170. if (__size % MCACHE_BUCKET_SIZE)
  171. __size += MCACHE_BUCKET_SIZE - (__size % MCACHE_BUCKET_SIZE);
  172. if (!__size)
  173. __size = MCACHE_BUCKET_SIZE;
  174. entry = &mapcache->entry[address_index % mapcache->nr_buckets];
  175. while (entry && entry->lock && entry->vaddr_base &&
  176. (entry->paddr_index != address_index || entry->size != __size ||
  177. !test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT,
  178. entry->valid_mapping))) {
  179. pentry = entry;
  180. entry = entry->next;
  181. }
  182. if (!entry) {
  183. entry = g_malloc0(sizeof (MapCacheEntry));
  184. pentry->next = entry;
  185. xen_remap_bucket(entry, __size, address_index);
  186. } else if (!entry->lock) {
  187. if (!entry->vaddr_base || entry->paddr_index != address_index ||
  188. entry->size != __size ||
  189. !test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT,
  190. entry->valid_mapping)) {
  191. xen_remap_bucket(entry, __size, address_index);
  192. }
  193. }
  194. if(!test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT,
  195. entry->valid_mapping)) {
  196. mapcache->last_address_index = -1;
  197. trace_xen_map_cache_return(NULL);
  198. return NULL;
  199. }
  200. mapcache->last_address_index = address_index;
  201. mapcache->last_address_vaddr = entry->vaddr_base;
  202. if (lock) {
  203. MapCacheRev *reventry = g_malloc0(sizeof(MapCacheRev));
  204. entry->lock++;
  205. reventry->vaddr_req = mapcache->last_address_vaddr + address_offset;
  206. reventry->paddr_index = mapcache->last_address_index;
  207. reventry->size = entry->size;
  208. QTAILQ_INSERT_HEAD(&mapcache->locked_entries, reventry, next);
  209. }
  210. trace_xen_map_cache_return(mapcache->last_address_vaddr + address_offset);
  211. return mapcache->last_address_vaddr + address_offset;
  212. }
  213. ram_addr_t xen_ram_addr_from_mapcache(void *ptr)
  214. {
  215. MapCacheEntry *entry = NULL;
  216. MapCacheRev *reventry;
  217. target_phys_addr_t paddr_index;
  218. target_phys_addr_t size;
  219. int found = 0;
  220. QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
  221. if (reventry->vaddr_req == ptr) {
  222. paddr_index = reventry->paddr_index;
  223. size = reventry->size;
  224. found = 1;
  225. break;
  226. }
  227. }
  228. if (!found) {
  229. fprintf(stderr, "%s, could not find %p\n", __func__, ptr);
  230. QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
  231. DPRINTF(" "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index,
  232. reventry->vaddr_req);
  233. }
  234. abort();
  235. return 0;
  236. }
  237. entry = &mapcache->entry[paddr_index % mapcache->nr_buckets];
  238. while (entry && (entry->paddr_index != paddr_index || entry->size != size)) {
  239. entry = entry->next;
  240. }
  241. if (!entry) {
  242. DPRINTF("Trying to find address %p that is not in the mapcache!\n", ptr);
  243. return 0;
  244. }
  245. return (reventry->paddr_index << MCACHE_BUCKET_SHIFT) +
  246. ((unsigned long) ptr - (unsigned long) entry->vaddr_base);
  247. }
  248. void xen_invalidate_map_cache_entry(uint8_t *buffer)
  249. {
  250. MapCacheEntry *entry = NULL, *pentry = NULL;
  251. MapCacheRev *reventry;
  252. target_phys_addr_t paddr_index;
  253. target_phys_addr_t size;
  254. int found = 0;
  255. if (mapcache->last_address_vaddr == buffer) {
  256. mapcache->last_address_index = -1;
  257. }
  258. QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
  259. if (reventry->vaddr_req == buffer) {
  260. paddr_index = reventry->paddr_index;
  261. size = reventry->size;
  262. found = 1;
  263. break;
  264. }
  265. }
  266. if (!found) {
  267. DPRINTF("%s, could not find %p\n", __func__, buffer);
  268. QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
  269. DPRINTF(" "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index, reventry->vaddr_req);
  270. }
  271. return;
  272. }
  273. QTAILQ_REMOVE(&mapcache->locked_entries, reventry, next);
  274. g_free(reventry);
  275. entry = &mapcache->entry[paddr_index % mapcache->nr_buckets];
  276. while (entry && (entry->paddr_index != paddr_index || entry->size != size)) {
  277. pentry = entry;
  278. entry = entry->next;
  279. }
  280. if (!entry) {
  281. DPRINTF("Trying to unmap address %p that is not in the mapcache!\n", buffer);
  282. return;
  283. }
  284. entry->lock--;
  285. if (entry->lock > 0 || pentry == NULL) {
  286. return;
  287. }
  288. pentry->next = entry->next;
  289. if (munmap(entry->vaddr_base, entry->size) != 0) {
  290. perror("unmap fails");
  291. exit(-1);
  292. }
  293. g_free(entry->valid_mapping);
  294. g_free(entry);
  295. }
  296. void xen_invalidate_map_cache(void)
  297. {
  298. unsigned long i;
  299. MapCacheRev *reventry;
  300. /* Flush pending AIO before destroying the mapcache */
  301. qemu_aio_flush();
  302. QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
  303. DPRINTF("There should be no locked mappings at this time, "
  304. "but "TARGET_FMT_plx" -> %p is present\n",
  305. reventry->paddr_index, reventry->vaddr_req);
  306. }
  307. mapcache_lock();
  308. for (i = 0; i < mapcache->nr_buckets; i++) {
  309. MapCacheEntry *entry = &mapcache->entry[i];
  310. if (entry->vaddr_base == NULL) {
  311. continue;
  312. }
  313. if (munmap(entry->vaddr_base, entry->size) != 0) {
  314. perror("unmap fails");
  315. exit(-1);
  316. }
  317. entry->paddr_index = 0;
  318. entry->vaddr_base = NULL;
  319. entry->size = 0;
  320. g_free(entry->valid_mapping);
  321. entry->valid_mapping = NULL;
  322. }
  323. mapcache->last_address_index = -1;
  324. mapcache->last_address_vaddr = NULL;
  325. mapcache_unlock();
  326. }