Branch data Line data Source code
1 : : #include <unistd.h>
2 : : #include <fcntl.h>
3 : :
4 : : #include "pagemap-cache.h"
5 : : #include "compiler.h"
6 : : #include "xmalloc.h"
7 : : #include "util.h"
8 : : #include "log.h"
9 : : #include "vma.h"
10 : :
11 : : #undef LOG_PREFIX
12 : : #define LOG_PREFIX "pagemap-cache: "
13 : :
14 : : /* To carry up to 2M of physical memory */
15 : : #define PMC_SHIFT (21)
16 : : #define PMC_SIZE (1ul << PMC_SHIFT)
17 : : #define PMC_MASK (~(PMC_SIZE - 1))
18 : : #define PMC_SIZE_GAP (PMC_SIZE / 4)
19 : :
20 : : #define PAGEMAP_LEN(addr) (PAGE_PFN(addr) * sizeof(u64))
21 : :
22 : : static inline void pmc_reset(pmc_t *pmc)
23 : : {
24 : : memzero(pmc, sizeof(*pmc));
25 : 7328 : pmc->fd = -1;
26 : : }
27 : :
28 : : static inline void pmc_zap(pmc_t *pmc)
29 : : {
30 : 0 : pmc->start = pmc->end = 0;
31 : : }
32 : :
33 : 3664 : int pmc_init(pmc_t *pmc, pid_t pid, struct list_head *vma_head, size_t size)
34 : : {
35 : 3664 : size_t map_size = max(size, (size_t)PMC_SIZE);
36 : : pmc_reset(pmc);
37 : :
38 [ - + ]: 3664 : BUG_ON(!vma_head);
39 : :
40 : 3664 : pmc->pid = pid;
41 [ - + ]: 3664 : pmc->fd = open_proc(pid, "pagemap");
42 : 3664 : pmc->map_len = PAGEMAP_LEN(map_size);
43 [ - + ]: 3664 : pmc->map = xmalloc(pmc->map_len);
44 : 3664 : pmc->vma_head = vma_head;
45 : :
46 [ + - ][ - + ]: 3664 : if (!pmc->map || pmc->fd < 0) {
47 : 0 : pr_err("Failed to init pagemap for %d\n", pid);
48 : 0 : pmc_fini(pmc);
49 : 0 : return -1;
50 : : }
51 : :
52 : 3664 : pr_debug("created for pid %d (takes %zu bytes)\n", pid, pmc->map_len);
53 : :
54 : 3664 : return 0;
55 : : }
56 : :
57 : : static inline u64 *__pmc_get_map(pmc_t *pmc, unsigned long addr)
58 : : {
59 : 151400 : return &pmc->map[PAGE_PFN(addr - pmc->start)];
60 : : }
61 : :
62 : 89551 : static int pmc_fill_cache(pmc_t *pmc, struct vma_area *vma)
63 : : {
64 : 89551 : unsigned long low = vma->e->start & PMC_MASK;
65 : 89551 : unsigned long high = low + PMC_SIZE;
66 : 89551 : size_t len = vma_area_len(vma);
67 : : size_t size_map;
68 : :
69 [ - + ]: 89551 : if (high > TASK_SIZE)
70 : : high = TASK_SIZE;
71 : :
72 : 89551 : pmc->start = vma->e->start;
73 : 89551 : pmc->end = vma->e->end;
74 : :
75 : 89551 : pr_debug("filling VMA %lx-%lx (%zuK) [l:%lx h:%lx]\n",
76 : : (long)vma->e->start, (long)vma->e->end, len >> 10, low, high);
77 : :
78 : : /*
79 : : * If we meet a small VMA, lets try to fit 2M cache
80 : : * window at least 75% full, otherwise left as a plain
81 : : * "one vma at a time" read. Note the VMAs in cache must
82 : : * fit in solid manner, iow -- either the whole vma fits
83 : : * the cache window, either plain read is used.
84 : : *
85 : : * The benefit (apart redusing the number of read() calls)
86 : : * is to walk page tables less.
87 : : */
88 [ + + ][ + + ]: 89551 : if (len < PMC_SIZE && (vma->e->start - low) < PMC_SIZE_GAP) {
89 : : size_t size_cov = len;
90 : : size_t nr_vmas = 1;
91 : :
92 : 23229 : pr_debug("\t%16lx-%-16lx nr:%-5zu cov:%zu\n",
93 : : (long)vma->e->start, (long)vma->e->end, nr_vmas, size_cov);
94 : :
95 [ + - ]: 85162 : list_for_each_entry_continue(vma, pmc->vma_head, list) {
96 [ + + ][ + + ]: 85162 : if (vma->e->start > high || vma->e->end > high)
97 : : break;
98 : :
99 [ - + ]: 61933 : BUG_ON(vma->e->start < low);
100 : 61933 : size_cov += vma_area_len(vma);
101 : 61933 : nr_vmas++;
102 : :
103 : 61933 : pr_debug("\t%16lx-%-16lx nr:%-5zu cov:%zu\n",
104 : : (long)vma->e->start, (long)vma->e->end, nr_vmas, size_cov);
105 : : }
106 : :
107 [ + + ]: 23229 : if (nr_vmas > 1) {
108 : : /*
109 : : * Note we don't touch low bound since it's set
110 : : * to first VMA start already and not updating it
111 : : * allows us to save a couple of code bytes.
112 : : */
113 : 16229 : pmc->end = high;
114 : 16229 : pr_debug("\tcache mode [l:%lx h:%lx]\n", pmc->start, pmc->end);
115 : : } else
116 : 7000 : pr_debug("\tsimple mode [l:%lx h:%lx]\n", pmc->start, pmc->end);
117 : : }
118 : :
119 : 89551 : size_map = PAGEMAP_LEN(pmc->end - pmc->start);
120 [ - + ]: 89551 : BUG_ON(pmc->map_len < size_map);
121 : :
122 [ - + ]: 89551 : if (pread(pmc->fd, pmc->map, size_map, PAGEMAP_PFN_OFF(pmc->start)) != size_map) {
123 : : pmc_zap(pmc);
124 : 0 : pr_perror("Can't read %d's pagemap file", pmc->pid);
125 : : return -1;
126 : : }
127 : :
128 : : return 0;
129 : : }
130 : :
131 : 151400 : u64 *pmc_get_map(pmc_t *pmc, struct vma_area *vma)
132 : : {
133 : : /* Hit */
134 [ + - ][ + + ]: 151400 : if (likely(pmc->start <= vma->e->start && pmc->end >= vma->e->end))
135 : 61849 : return __pmc_get_map(pmc, vma->e->start);
136 : :
137 : : /* Miss, refill the cache */
138 [ - + ]: 89551 : if (pmc_fill_cache(pmc, vma)) {
139 : 0 : pr_err("Failed to fill cache for %d (%lx-%lx)\n",
140 : : pmc->pid, (long)vma->e->start, (long)vma->e->end);
141 : 0 : return NULL;
142 : : }
143 : :
144 : : /* Hit for sure */
145 : 89551 : return __pmc_get_map(pmc, vma->e->start);
146 : : }
147 : :
148 : 3664 : void pmc_fini(pmc_t *pmc)
149 : : {
150 : 3664 : close_safe(&pmc->fd);
151 [ + - ]: 3664 : xfree(pmc->map);
152 : : pmc_reset(pmc);
153 : 3664 : }
|