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