Branch data Line data Source code
1 : : #include <unistd.h>
2 : : #include <sys/mman.h>
3 : : #include <stdlib.h>
4 : : #include <fcntl.h>
5 : :
6 : : #include "pid.h"
7 : : #include "shmem.h"
8 : : #include "image.h"
9 : : #include "cr_options.h"
10 : : #include "page-pipe.h"
11 : : #include "page-xfer.h"
12 : : #include "rst-malloc.h"
13 : : #include "vma.h"
14 : :
15 : : #include "protobuf.h"
16 : : #include "protobuf/pagemap.pb-c.h"
17 : :
18 : : unsigned long nr_shmems;
19 : : unsigned int rst_shmems;
20 : :
21 : 356 : void show_saved_shmems(void)
22 : : {
23 : : int i;
24 : : struct shmem_info *si;
25 : :
26 : 356 : pr_info("\tSaved shmems:\n");
27 : 356 : si = rst_mem_remap_ptr(rst_shmems, RM_SHREMAP);
28 [ + + ]: 388 : for (i = 0; i < nr_shmems; i++, si++)
29 : 32 : pr_info("\t\tstart: 0x%016lx shmid: 0x%lx pid: %d\n",
30 : : si->start, si->shmid, si->pid);
31 : 356 : }
32 : :
33 : :
34 : 112 : static struct shmem_info *find_shmem_by_id(unsigned long id)
35 : : {
36 : : struct shmem_info *si;
37 : :
38 : 112 : si = rst_mem_remap_ptr(rst_shmems, RM_SHREMAP);
39 : 112 : return find_shmem(si, nr_shmems, id);
40 : : }
41 : :
42 : 76 : int collect_shmem(int pid, VmaEntry *vi)
43 : : {
44 : 76 : unsigned long size = vi->pgoff + vi->end - vi->start;
45 : : struct shmem_info *si;
46 : :
47 : 76 : si = find_shmem_by_id(vi->shmid);
48 [ + + ]: 76 : if (si) {
49 : :
50 [ + + ]: 44 : if (si->size < size)
51 : 8 : si->size = size;
52 : :
53 : : /*
54 : : * Only the shared mapping with a lowest
55 : : * pid will be created in real, other processes
56 : : * will wait until the kernel propagate this mapping
57 : : * into /proc
58 : : */
59 [ - + ]: 44 : if (!pid_rst_prio(pid, si->pid))
60 : : return 0;
61 : :
62 : 0 : si->pid = pid;
63 : 0 : si->start = vi->start;
64 : 0 : si->end = vi->end;
65 : :
66 : 0 : return 0;
67 : : }
68 : :
69 : 32 : si = rst_mem_alloc(sizeof(struct shmem_info), RM_SHREMAP);
70 [ + - ]: 32 : if (!si)
71 : : return -1;
72 : :
73 : 32 : pr_info("Add new shmem 0x%"PRIx64" (0x0160x%"PRIx64"-0x0160x%"PRIx64")\n",
74 : : vi->shmid, vi->start, vi->end);
75 : :
76 : 32 : si->start = vi->start;
77 : 32 : si->end = vi->end;
78 : 32 : si->shmid = vi->shmid;
79 : 32 : si->pid = pid;
80 : 32 : si->size = size;
81 : 32 : si->fd = -1;
82 : :
83 : 32 : nr_shmems++;
84 : : futex_init(&si->lock);
85 : :
86 : 32 : return 0;
87 : : }
88 : :
89 : 24 : static int shmem_wait_and_open(int pid, struct shmem_info *si)
90 : : {
91 : : char path[128];
92 : : int ret;
93 : :
94 : 12 : snprintf(path, sizeof(path), "/proc/%d/map_files/%lx-%lx",
95 : : si->pid, si->start, si->end);
96 : :
97 : 12 : pr_info("Waiting for [%s] to appear\n", path);
98 : 12 : futex_wait_until(&si->lock, 1);
99 : :
100 : 12 : pr_info("Opening shmem [%s] \n", path);
101 [ - + ]: 12 : ret = open_proc_rw(si->pid, "map_files/%lx-%lx", si->start, si->end);
102 [ - + ]: 12 : if (ret < 0)
103 : 0 : pr_perror(" %d: Can't stat shmem at %s",
104 : : si->pid, path);
105 : 12 : return ret;
106 : : }
107 : :
108 : 20 : static int restore_shmem_content(void *addr, struct shmem_info *si)
109 : : {
110 : : int ret = 0;
111 : : struct page_read pr;
112 : : unsigned long off_real;
113 : :
114 [ - + ]: 20 : ret = open_page_read(si->shmid, &pr, opts.auto_dedup ? O_RDWR : O_RSTR, true);
115 [ + - ]: 20 : if (ret)
116 : : goto err_unmap;
117 : :
118 : : while (1) {
119 : : unsigned long vaddr;
120 : : unsigned nr_pages;
121 : : struct iovec iov;
122 : :
123 : 38 : ret = pr.get_pagemap(&pr, &iov);
124 [ + + ]: 38 : if (ret <= 0)
125 : : break;
126 : :
127 : 18 : vaddr = (unsigned long)iov.iov_base;
128 : 18 : nr_pages = iov.iov_len / PAGE_SIZE;
129 : :
130 [ + - ]: 18 : if (vaddr + nr_pages * PAGE_SIZE > si->size)
131 : : break;
132 : :
133 : 18 : off_real = lseek(pr.fd_pg, 0, SEEK_CUR);
134 : :
135 : 36 : ret = read(pr.fd_pg, addr + vaddr, nr_pages * PAGE_SIZE);
136 [ + - ]: 18 : if (ret != nr_pages * PAGE_SIZE) {
137 : : ret = -1;
138 : : break;
139 : : }
140 : :
141 [ + - ]: 18 : if (opts.auto_dedup) {
142 : 18 : ret = punch_hole(&pr, off_real, nr_pages * PAGE_SIZE, false);
143 [ + - ]: 18 : if (ret == -1) {
144 : : break;
145 : : }
146 : : }
147 : :
148 [ + - ]: 18 : if (pr.put_pagemap)
149 : 18 : pr.put_pagemap(&pr);
150 : : }
151 : :
152 : 20 : pr.close(&pr);
153 : : return ret;
154 : : err_unmap:
155 : 0 : munmap(addr, si->size);
156 : : return -1;
157 : : }
158 : :
159 : 36 : int get_shmem_fd(int pid, VmaEntry *vi)
160 : : {
161 : 20 : struct shmem_info *si;
162 : : void *addr;
163 : : int f;
164 : :
165 : 36 : si = find_shmem_by_id(vi->shmid);
166 [ + - ]: 36 : pr_info("Search for 0x%016"PRIx64" shmem 0x%"PRIx64" %p/%d\n", vi->start, vi->shmid, si, si ? si->pid : -1);
167 [ - + ]: 36 : if (!si) {
168 : 0 : pr_err("Can't find my shmem 0x%016"PRIx64"\n", vi->start);
169 : 0 : return -1;
170 : : }
171 : :
172 [ + + ]: 36 : if (si->pid != pid)
173 : 12 : return shmem_wait_and_open(pid, si);
174 : :
175 [ + + ]: 24 : if (si->fd != -1)
176 : 4 : return dup(si->fd);
177 : :
178 : : /*
179 : : * The following hack solves problems:
180 : : * vi->pgoff may be not zero in a target process.
181 : : * This mapping may be mapped more then once.
182 : : * The restorer doesn't have snprintf.
183 : : * Here is a good place to restore content
184 : : */
185 : 20 : addr = mmap(NULL, si->size,
186 : : PROT_WRITE | PROT_READ,
187 : : MAP_SHARED | MAP_ANONYMOUS, -1, 0);
188 [ - + ]: 20 : if (addr == MAP_FAILED) {
189 : 0 : pr_err("Can't mmap shmid=0x%"PRIx64" size=%ld\n",
190 : : vi->shmid, si->size);
191 : 0 : return -1;
192 : : }
193 : :
194 [ - + ]: 20 : if (restore_shmem_content(addr, si) < 0) {
195 : 0 : pr_err("Can't restore shmem content\n");
196 : 0 : return -1;
197 : : }
198 : :
199 [ - + ]: 20 : f = open_proc_rw(getpid(), "map_files/%lx-%lx",
200 : : (unsigned long) addr,
201 : : (unsigned long) addr + si->size);
202 : 20 : munmap(addr, si->size);
203 [ + - ]: 20 : if (f < 0)
204 : : return -1;
205 : :
206 : 20 : si->fd = f;
207 : 20 : return f;
208 : : }
209 : :
210 : : struct shmem_info_dump {
211 : : unsigned long size;
212 : : unsigned long shmid;
213 : : unsigned long start;
214 : : unsigned long end;
215 : : int pid;
216 : :
217 : : struct shmem_info_dump *next;
218 : : };
219 : :
220 : : #define SHMEM_HASH_SIZE 32
221 : : static struct shmem_info_dump *shmems_hash[SHMEM_HASH_SIZE];
222 : :
223 : : static struct shmem_info_dump *shmem_find(struct shmem_info_dump **chain,
224 : : unsigned long shmid)
225 : : {
226 : : struct shmem_info_dump *sh;
227 : :
228 [ + + ]: 96 : for (sh = *chain; sh; sh = sh->next)
229 [ - + ]: 54 : if (sh->shmid == shmid)
230 : : return sh;
231 : :
232 : : return NULL;
233 : : }
234 : :
235 : 96 : int add_shmem_area(pid_t pid, VmaEntry *vma)
236 : : {
237 : 96 : struct shmem_info_dump *si, **chain;
238 : 96 : unsigned long size = vma->pgoff + (vma->end - vma->start);
239 : :
240 : 96 : chain = &shmems_hash[vma->shmid % SHMEM_HASH_SIZE];
241 : : si = shmem_find(chain, vma->shmid);
242 [ + + ]: 96 : if (si) {
243 [ + + ]: 54 : if (si->size < size)
244 : 8 : si->size = size;
245 : : return 0;
246 : : }
247 : :
248 [ - + ]: 42 : si = xmalloc(sizeof(*si));
249 [ + - ]: 42 : if (!si)
250 : : return -1;
251 : :
252 : 42 : si->next = *chain;
253 : 42 : *chain = si;
254 : :
255 : 42 : si->size = size;
256 : 42 : si->pid = pid;
257 : 42 : si->start = vma->start;
258 : 42 : si->end = vma->end;
259 : 42 : si->shmid = vma->shmid;
260 : :
261 : 42 : return 0;
262 : : }
263 : :
264 : 42 : static int dump_pages(struct page_pipe *pp, struct page_xfer *xfer, void *addr)
265 : : {
266 : : struct page_pipe_buf *ppb;
267 : :
268 [ + + ]: 84 : list_for_each_entry(ppb, &pp->bufs, l)
269 [ - + ]: 42 : if (vmsplice(ppb->p[1], ppb->iov, ppb->nr_segs,
270 : 42 : SPLICE_F_GIFT | SPLICE_F_NONBLOCK) !=
271 : 42 : ppb->pages_in * PAGE_SIZE) {
272 : 0 : pr_perror("Can't get shmem into page-pipe");
273 : 0 : return -1;
274 : : }
275 : :
276 : 42 : return page_xfer_dump_pages(xfer, pp, (unsigned long)addr);
277 : : }
278 : :
279 : 42 : static int dump_one_shmem(struct shmem_info_dump *si)
280 : : {
281 : : struct iovec *iovs;
282 : : struct page_pipe *pp;
283 : : struct page_xfer xfer;
284 : : int err, ret = -1, fd;
285 : : unsigned char *map = NULL;
286 : : void *addr = NULL;
287 : : unsigned long pfn, nrpages;
288 : :
289 : 42 : pr_info("Dumping shared memory %ld\n", si->shmid);
290 : :
291 : 42 : nrpages = (si->size + PAGE_SIZE - 1) / PAGE_SIZE;
292 [ - + ]: 42 : map = xmalloc(nrpages * sizeof(*map));
293 [ + - ]: 42 : if (!map)
294 : : goto err;
295 : :
296 [ - + ]: 42 : fd = open_proc(si->pid, "map_files/%lx-%lx", si->start, si->end);
297 [ + - ]: 42 : if (fd < 0)
298 : : goto err;
299 : :
300 : 42 : addr = mmap(NULL, si->size, PROT_READ, MAP_SHARED, fd, 0);
301 : 42 : close(fd);
302 [ - + ]: 42 : if (addr == MAP_FAILED) {
303 : 0 : pr_err("Can't map shmem 0x%lx (0x%lx-0x%lx)\n",
304 : : si->shmid, si->start, si->end);
305 : 0 : goto err;
306 : : }
307 : :
308 : : /*
309 : : * We can't use pagemap here, because this vma is
310 : : * not mapped to us at all, but mincore reports the
311 : : * pagecache status of a file, which is correct in
312 : : * this case.
313 : : */
314 : :
315 : 42 : err = mincore(addr, si->size, map);
316 [ + - ]: 42 : if (err)
317 : : goto err_unmap;
318 : :
319 [ - + ]: 42 : iovs = xmalloc(((nrpages + 1) / 2) * sizeof(struct iovec));
320 [ + - ]: 42 : if (!iovs)
321 : : goto err_unmap;
322 : :
323 : 42 : pp = create_page_pipe((nrpages + 1) / 2, iovs, true);
324 [ + - ]: 42 : if (!pp)
325 : : goto err_iovs;
326 : :
327 : 42 : err = open_page_xfer(&xfer, CR_FD_SHMEM_PAGEMAP, si->shmid);
328 [ + - ]: 42 : if (err)
329 : : goto err_pp;
330 : :
331 [ + + ]: 1048672 : for (pfn = 0; pfn < nrpages; pfn++) {
332 [ + + ]: 1048630 : if (!(map[pfn] & PAGE_RSS))
333 : 1048584 : continue;
334 : : again:
335 : 46 : ret = page_pipe_add_page(pp, (unsigned long)addr + pfn * PAGE_SIZE);
336 [ - + ]: 46 : if (ret == -EAGAIN) {
337 : 0 : ret = dump_pages(pp, &xfer, addr);
338 [ # # ]: 0 : if (ret)
339 : : goto err_xfer;
340 : 0 : page_pipe_reinit(pp);
341 : 0 : goto again;
342 [ + - ]: 46 : } else if (ret)
343 : : goto err_xfer;
344 : : }
345 : :
346 : 42 : ret = dump_pages(pp, &xfer, addr);
347 : :
348 : : err_xfer:
349 : 42 : xfer.close(&xfer);
350 : : err_pp:
351 : 42 : destroy_page_pipe(pp);
352 : : err_iovs:
353 [ + - ]: 42 : xfree(iovs);
354 : : err_unmap:
355 : 42 : munmap(addr, si->size);
356 : : err:
357 [ + - ]: 42 : xfree(map);
358 : 42 : return ret;
359 : : }
360 : :
361 : : #define for_each_shmem_dump(_i, _si) \
362 : : for (i = 0; i < SHMEM_HASH_SIZE; i++) \
363 : : for (si = shmems_hash[i]; si; si = si->next)
364 : :
365 : 448 : int cr_dump_shmem(void)
366 : : {
367 : : int ret = 0, i;
368 : : struct shmem_info_dump *si;
369 : :
370 [ + + ][ + + ]: 14826 : for_each_shmem_dump (i, si) {
371 : 42 : ret = dump_one_shmem(si);
372 [ + - ]: 42 : if (ret)
373 : : break;
374 : : }
375 : :
376 : 448 : return ret;
377 : : }
|