2012-05-03 18:01:05 +04:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/mman.h>
|
2012-05-29 20:11:00 +04:00
|
|
|
#include <stdlib.h>
|
2013-11-14 19:19:49 +04:00
|
|
|
#include <fcntl.h>
|
2012-05-03 18:01:05 +04:00
|
|
|
|
2013-11-06 17:21:13 +04:00
|
|
|
#include "pid.h"
|
2012-05-03 18:01:05 +04:00
|
|
|
#include "shmem.h"
|
|
|
|
#include "image.h"
|
2014-03-14 10:16:55 +04:00
|
|
|
#include "cr_options.h"
|
2013-03-15 17:54:27 +04:00
|
|
|
#include "page-pipe.h"
|
|
|
|
#include "page-xfer.h"
|
2013-11-02 01:06:31 +04:00
|
|
|
#include "rst-malloc.h"
|
2013-11-05 12:33:03 +04:00
|
|
|
#include "vma.h"
|
|
|
|
|
2012-07-19 12:43:36 +04:00
|
|
|
#include "protobuf.h"
|
2013-03-12 21:00:05 +04:00
|
|
|
#include "protobuf/pagemap.pb-c.h"
|
2012-07-19 12:43:36 +04:00
|
|
|
|
2013-11-02 01:06:31 +04:00
|
|
|
unsigned long nr_shmems;
|
|
|
|
unsigned int rst_shmems;
|
2012-05-03 18:01:05 +04:00
|
|
|
|
|
|
|
void show_saved_shmems(void)
|
|
|
|
{
|
|
|
|
int i;
|
2013-11-02 01:06:31 +04:00
|
|
|
struct shmem_info *si;
|
2012-05-03 18:01:05 +04:00
|
|
|
|
|
|
|
pr_info("\tSaved shmems:\n");
|
2013-11-02 01:06:31 +04:00
|
|
|
si = rst_mem_remap_ptr(rst_shmems, RM_SHREMAP);
|
|
|
|
for (i = 0; i < nr_shmems; i++, si++)
|
2012-05-03 18:01:05 +04:00
|
|
|
pr_info("\t\tstart: 0x%016lx shmid: 0x%lx pid: %d\n",
|
2013-11-02 01:06:31 +04:00
|
|
|
si->start, si->shmid, si->pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct shmem_info *find_shmem_by_id(unsigned long id)
|
|
|
|
{
|
|
|
|
struct shmem_info *si;
|
|
|
|
|
|
|
|
si = rst_mem_remap_ptr(rst_shmems, RM_SHREMAP);
|
|
|
|
return find_shmem(si, nr_shmems, id);
|
2012-05-03 18:01:05 +04:00
|
|
|
}
|
|
|
|
|
2014-02-03 15:12:08 +04:00
|
|
|
int collect_shmem(int pid, VmaEntry *vi)
|
2012-05-03 18:01:05 +04:00
|
|
|
{
|
|
|
|
unsigned long size = vi->pgoff + vi->end - vi->start;
|
|
|
|
struct shmem_info *si;
|
|
|
|
|
2013-11-02 01:06:31 +04:00
|
|
|
si = find_shmem_by_id(vi->shmid);
|
2012-05-03 18:01:05 +04:00
|
|
|
if (si) {
|
|
|
|
|
|
|
|
if (si->size < size)
|
|
|
|
si->size = size;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only the shared mapping with a lowest
|
|
|
|
* pid will be created in real, other processes
|
|
|
|
* will wait until the kernel propagate this mapping
|
|
|
|
* into /proc
|
|
|
|
*/
|
2013-08-29 08:07:15 +04:00
|
|
|
if (!pid_rst_prio(pid, si->pid))
|
2012-05-03 18:01:05 +04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
si->pid = pid;
|
|
|
|
si->start = vi->start;
|
|
|
|
si->end = vi->end;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-02 01:06:31 +04:00
|
|
|
si = rst_mem_alloc(sizeof(struct shmem_info), RM_SHREMAP);
|
|
|
|
if (!si)
|
2012-05-03 18:01:05 +04:00
|
|
|
return -1;
|
|
|
|
|
2013-04-16 21:52:15 +04:00
|
|
|
pr_info("Add new shmem 0x%"PRIx64" (0x0160x%"PRIx64"-0x0160x%"PRIx64")\n",
|
2012-05-03 18:01:05 +04:00
|
|
|
vi->shmid, vi->start, vi->end);
|
|
|
|
|
|
|
|
si->start = vi->start;
|
|
|
|
si->end = vi->end;
|
|
|
|
si->shmid = vi->shmid;
|
|
|
|
si->pid = pid;
|
|
|
|
si->size = size;
|
|
|
|
si->fd = -1;
|
|
|
|
|
2013-11-02 01:06:31 +04:00
|
|
|
nr_shmems++;
|
2012-05-03 18:01:05 +04:00
|
|
|
futex_init(&si->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int shmem_wait_and_open(int pid, struct shmem_info *si)
|
|
|
|
{
|
|
|
|
char path[128];
|
|
|
|
int ret;
|
|
|
|
|
2012-06-20 12:16:00 +04:00
|
|
|
snprintf(path, sizeof(path), "/proc/%d/map_files/%lx-%lx",
|
2012-05-03 18:01:05 +04:00
|
|
|
si->pid, si->start, si->end);
|
|
|
|
|
2012-05-02 14:42:00 +04:00
|
|
|
pr_info("Waiting for [%s] to appear\n", path);
|
2012-05-03 18:01:05 +04:00
|
|
|
futex_wait_until(&si->lock, 1);
|
|
|
|
|
2012-05-02 14:42:00 +04:00
|
|
|
pr_info("Opening shmem [%s] \n", path);
|
2012-06-19 15:53:00 +04:00
|
|
|
ret = open_proc_rw(si->pid, "map_files/%lx-%lx", si->start, si->end);
|
2012-05-03 18:01:05 +04:00
|
|
|
if (ret < 0)
|
|
|
|
pr_perror(" %d: Can't stat shmem at %s",
|
|
|
|
si->pid, path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int restore_shmem_content(void *addr, struct shmem_info *si)
|
|
|
|
{
|
2014-03-14 10:16:55 +04:00
|
|
|
int ret = 0;
|
|
|
|
struct page_read pr;
|
|
|
|
unsigned long off_real;
|
|
|
|
|
|
|
|
ret = open_page_read(si->shmid, &pr, opts.auto_dedup ? O_RDWR : O_RSTR, true);
|
|
|
|
if (ret)
|
|
|
|
goto err_unmap;
|
2012-05-03 18:01:05 +04:00
|
|
|
|
|
|
|
while (1) {
|
2013-03-27 13:12:02 +04:00
|
|
|
unsigned long vaddr;
|
|
|
|
unsigned nr_pages;
|
2014-03-14 10:16:55 +04:00
|
|
|
struct iovec iov;
|
2013-03-12 21:00:05 +04:00
|
|
|
|
2014-03-14 10:16:55 +04:00
|
|
|
ret = pr.get_pagemap(&pr, &iov);
|
|
|
|
if (ret <= 0)
|
|
|
|
break;
|
2013-03-27 13:12:02 +04:00
|
|
|
|
2014-03-14 10:16:55 +04:00
|
|
|
vaddr = (unsigned long)iov.iov_base;
|
|
|
|
nr_pages = iov.iov_len / PAGE_SIZE;
|
2012-05-03 18:01:05 +04:00
|
|
|
|
2013-03-27 13:12:02 +04:00
|
|
|
if (vaddr + nr_pages * PAGE_SIZE > si->size)
|
2012-05-03 18:01:05 +04:00
|
|
|
break;
|
|
|
|
|
2014-03-14 10:16:55 +04:00
|
|
|
off_real = lseek(pr.fd_pg, 0, SEEK_CUR);
|
|
|
|
|
|
|
|
ret = read(pr.fd_pg, addr + vaddr, nr_pages * PAGE_SIZE);
|
2013-03-27 13:12:02 +04:00
|
|
|
if (ret != nr_pages * PAGE_SIZE) {
|
2013-03-12 21:00:05 +04:00
|
|
|
ret = -1;
|
2012-05-03 18:01:05 +04:00
|
|
|
break;
|
2013-03-12 21:00:05 +04:00
|
|
|
}
|
|
|
|
|
2014-03-14 10:16:55 +04:00
|
|
|
if (opts.auto_dedup) {
|
|
|
|
ret = punch_hole(&pr, off_real, nr_pages * PAGE_SIZE, false);
|
|
|
|
if (ret == -1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pr.put_pagemap)
|
|
|
|
pr.put_pagemap(&pr);
|
2012-05-03 18:01:05 +04:00
|
|
|
}
|
|
|
|
|
2014-03-14 10:16:55 +04:00
|
|
|
pr.close(&pr);
|
2012-05-03 18:01:05 +04:00
|
|
|
return ret;
|
2013-03-12 21:00:05 +04:00
|
|
|
err_unmap:
|
|
|
|
munmap(addr, si->size);
|
|
|
|
return -1;
|
2012-05-03 18:01:05 +04:00
|
|
|
}
|
|
|
|
|
2012-07-19 12:43:36 +04:00
|
|
|
int get_shmem_fd(int pid, VmaEntry *vi)
|
2012-05-03 18:01:05 +04:00
|
|
|
{
|
|
|
|
struct shmem_info *si;
|
|
|
|
void *addr;
|
|
|
|
int f;
|
|
|
|
|
2013-11-02 01:06:31 +04:00
|
|
|
si = find_shmem_by_id(vi->shmid);
|
2013-01-16 19:20:08 +04:00
|
|
|
pr_info("Search for 0x%016"PRIx64" shmem 0x%"PRIx64" %p/%d\n", vi->start, vi->shmid, si, si ? si->pid : -1);
|
2012-05-03 18:01:05 +04:00
|
|
|
if (!si) {
|
2013-01-16 19:20:08 +04:00
|
|
|
pr_err("Can't find my shmem 0x%016"PRIx64"\n", vi->start);
|
2012-05-03 18:01:05 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (si->pid != pid)
|
|
|
|
return shmem_wait_and_open(pid, si);
|
|
|
|
|
|
|
|
if (si->fd != -1)
|
|
|
|
return dup(si->fd);
|
|
|
|
|
2012-08-11 21:48:17 +04:00
|
|
|
/*
|
|
|
|
* The following hack solves problems:
|
2012-05-03 18:01:05 +04:00
|
|
|
* vi->pgoff may be not zero in a target process.
|
|
|
|
* This mapping may be mapped more then once.
|
|
|
|
* The restorer doesn't have snprintf.
|
|
|
|
* Here is a good place to restore content
|
|
|
|
*/
|
|
|
|
addr = mmap(NULL, si->size,
|
|
|
|
PROT_WRITE | PROT_READ,
|
|
|
|
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
|
|
|
if (addr == MAP_FAILED) {
|
2013-01-16 19:20:08 +04:00
|
|
|
pr_err("Can't mmap shmid=0x%"PRIx64" size=%ld\n",
|
2012-05-03 18:01:05 +04:00
|
|
|
vi->shmid, si->size);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (restore_shmem_content(addr, si) < 0) {
|
|
|
|
pr_err("Can't restore shmem content\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
f = open_proc_rw(getpid(), "map_files/%lx-%lx",
|
|
|
|
(unsigned long) addr,
|
|
|
|
(unsigned long) addr + si->size);
|
|
|
|
munmap(addr, si->size);
|
|
|
|
if (f < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
si->fd = f;
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2012-08-11 22:19:34 +04:00
|
|
|
struct shmem_info_dump {
|
2012-05-03 18:07:01 +04:00
|
|
|
unsigned long size;
|
|
|
|
unsigned long shmid;
|
|
|
|
unsigned long start;
|
|
|
|
unsigned long end;
|
|
|
|
int pid;
|
2012-08-17 00:20:58 +04:00
|
|
|
|
|
|
|
struct shmem_info_dump *next;
|
2012-05-03 18:07:01 +04:00
|
|
|
};
|
|
|
|
|
2012-08-17 00:20:58 +04:00
|
|
|
#define SHMEM_HASH_SIZE 32
|
|
|
|
static struct shmem_info_dump *shmems_hash[SHMEM_HASH_SIZE];
|
2012-05-03 18:07:01 +04:00
|
|
|
|
2012-08-17 00:20:58 +04:00
|
|
|
static struct shmem_info_dump *shmem_find(struct shmem_info_dump **chain,
|
|
|
|
unsigned long shmid)
|
2012-05-03 18:07:01 +04:00
|
|
|
{
|
2012-08-17 00:20:58 +04:00
|
|
|
struct shmem_info_dump *sh;
|
2012-05-03 18:07:01 +04:00
|
|
|
|
2012-08-17 00:20:58 +04:00
|
|
|
for (sh = *chain; sh; sh = sh->next)
|
|
|
|
if (sh->shmid == shmid)
|
|
|
|
return sh;
|
2012-05-03 18:07:01 +04:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-07-19 12:43:36 +04:00
|
|
|
int add_shmem_area(pid_t pid, VmaEntry *vma)
|
2012-05-03 18:07:01 +04:00
|
|
|
{
|
2012-08-17 00:20:58 +04:00
|
|
|
struct shmem_info_dump *si, **chain;
|
2012-05-03 18:07:01 +04:00
|
|
|
unsigned long size = vma->pgoff + (vma->end - vma->start);
|
|
|
|
|
2012-08-17 00:20:58 +04:00
|
|
|
chain = &shmems_hash[vma->shmid % SHMEM_HASH_SIZE];
|
|
|
|
si = shmem_find(chain, vma->shmid);
|
2012-05-03 18:07:01 +04:00
|
|
|
if (si) {
|
|
|
|
if (si->size < size)
|
|
|
|
si->size = size;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-17 00:20:58 +04:00
|
|
|
si = xmalloc(sizeof(*si));
|
|
|
|
if (!si)
|
2012-05-03 18:07:01 +04:00
|
|
|
return -1;
|
|
|
|
|
2012-08-17 00:20:58 +04:00
|
|
|
si->next = *chain;
|
|
|
|
*chain = si;
|
|
|
|
|
2012-05-03 18:07:01 +04:00
|
|
|
si->size = size;
|
|
|
|
si->pid = pid;
|
|
|
|
si->start = vma->start;
|
|
|
|
si->end = vma->end;
|
|
|
|
si->shmid = vma->shmid;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-02-14 16:44:28 +04:00
|
|
|
static int dump_pages(struct page_pipe *pp, struct page_xfer *xfer, void *addr)
|
|
|
|
{
|
|
|
|
struct page_pipe_buf *ppb;
|
|
|
|
|
|
|
|
list_for_each_entry(ppb, &pp->bufs, l)
|
|
|
|
if (vmsplice(ppb->p[1], ppb->iov, ppb->nr_segs,
|
|
|
|
SPLICE_F_GIFT | SPLICE_F_NONBLOCK) !=
|
|
|
|
ppb->pages_in * PAGE_SIZE) {
|
|
|
|
pr_perror("Can't get shmem into page-pipe");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return page_xfer_dump_pages(xfer, pp, (unsigned long)addr);
|
|
|
|
}
|
|
|
|
|
2013-03-15 17:33:19 +04:00
|
|
|
static int dump_one_shmem(struct shmem_info_dump *si)
|
2012-05-03 18:07:01 +04:00
|
|
|
{
|
2013-03-15 17:54:27 +04:00
|
|
|
struct iovec *iovs;
|
|
|
|
struct page_pipe *pp;
|
|
|
|
struct page_xfer xfer;
|
|
|
|
int err, ret = -1, fd;
|
2012-05-03 18:07:01 +04:00
|
|
|
unsigned char *map = NULL;
|
|
|
|
void *addr = NULL;
|
|
|
|
unsigned long pfn, nrpages;
|
|
|
|
|
2013-05-16 16:20:17 +04:00
|
|
|
pr_info("Dumping shared memory %ld\n", si->shmid);
|
2013-03-12 21:00:05 +04:00
|
|
|
|
2013-03-15 17:33:19 +04:00
|
|
|
nrpages = (si->size + PAGE_SIZE - 1) / PAGE_SIZE;
|
|
|
|
map = xmalloc(nrpages * sizeof(*map));
|
|
|
|
if (!map)
|
|
|
|
goto err;
|
2012-05-03 18:07:01 +04:00
|
|
|
|
2013-03-15 17:33:19 +04:00
|
|
|
fd = open_proc(si->pid, "map_files/%lx-%lx", si->start, si->end);
|
|
|
|
if (fd < 0)
|
|
|
|
goto err;
|
2012-05-03 18:07:01 +04:00
|
|
|
|
2013-03-15 17:33:19 +04:00
|
|
|
addr = mmap(NULL, si->size, PROT_READ, MAP_SHARED, fd, 0);
|
|
|
|
close(fd);
|
|
|
|
if (addr == MAP_FAILED) {
|
|
|
|
pr_err("Can't map shmem 0x%lx (0x%lx-0x%lx)\n",
|
|
|
|
si->shmid, si->start, si->end);
|
|
|
|
goto err;
|
|
|
|
}
|
2012-05-03 18:07:01 +04:00
|
|
|
|
2013-03-15 17:33:19 +04:00
|
|
|
/*
|
|
|
|
* We can't use pagemap here, because this vma is
|
|
|
|
* not mapped to us at all, but mincore reports the
|
|
|
|
* pagecache status of a file, which is correct in
|
|
|
|
* this case.
|
|
|
|
*/
|
2012-05-03 18:07:01 +04:00
|
|
|
|
2013-03-15 17:33:19 +04:00
|
|
|
err = mincore(addr, si->size, map);
|
|
|
|
if (err)
|
|
|
|
goto err_unmap;
|
2012-05-03 18:07:01 +04:00
|
|
|
|
2013-03-15 18:19:59 +04:00
|
|
|
iovs = xmalloc(((nrpages + 1) / 2) * sizeof(struct iovec));
|
2013-03-15 17:54:27 +04:00
|
|
|
if (!iovs)
|
2013-03-15 17:33:19 +04:00
|
|
|
goto err_unmap;
|
2013-03-12 21:00:05 +04:00
|
|
|
|
2014-02-14 16:44:28 +04:00
|
|
|
pp = create_page_pipe((nrpages + 1) / 2, iovs, true);
|
2013-03-15 17:54:27 +04:00
|
|
|
if (!pp)
|
|
|
|
goto err_iovs;
|
2012-05-03 18:07:01 +04:00
|
|
|
|
2014-02-14 16:44:28 +04:00
|
|
|
err = open_page_xfer(&xfer, CR_FD_SHMEM_PAGEMAP, si->shmid);
|
|
|
|
if (err)
|
|
|
|
goto err_pp;
|
|
|
|
|
2013-03-15 17:33:19 +04:00
|
|
|
for (pfn = 0; pfn < nrpages; pfn++) {
|
2013-03-15 17:54:27 +04:00
|
|
|
if (!(map[pfn] & PAGE_RSS))
|
|
|
|
continue;
|
2014-02-14 16:44:28 +04:00
|
|
|
again:
|
|
|
|
ret = page_pipe_add_page(pp, (unsigned long)addr + pfn * PAGE_SIZE);
|
|
|
|
if (ret == -EAGAIN) {
|
|
|
|
ret = dump_pages(pp, &xfer, addr);
|
|
|
|
if (ret)
|
|
|
|
goto err_xfer;
|
|
|
|
page_pipe_reinit(pp);
|
|
|
|
goto again;
|
|
|
|
} else if (ret)
|
|
|
|
goto err_xfer;
|
2013-03-15 17:54:27 +04:00
|
|
|
}
|
|
|
|
|
2014-02-14 16:44:28 +04:00
|
|
|
ret = dump_pages(pp, &xfer, addr);
|
2013-03-15 17:33:19 +04:00
|
|
|
|
2014-02-14 16:44:28 +04:00
|
|
|
err_xfer:
|
2013-03-15 17:54:27 +04:00
|
|
|
xfer.close(&xfer);
|
|
|
|
err_pp:
|
|
|
|
destroy_page_pipe(pp);
|
|
|
|
err_iovs:
|
|
|
|
xfree(iovs);
|
2012-05-03 18:07:01 +04:00
|
|
|
err_unmap:
|
|
|
|
munmap(addr, si->size);
|
|
|
|
err:
|
|
|
|
xfree(map);
|
2013-03-15 17:39:09 +04:00
|
|
|
return ret;
|
2012-05-03 18:07:01 +04:00
|
|
|
}
|
2013-03-15 17:33:19 +04:00
|
|
|
|
|
|
|
#define for_each_shmem_dump(_i, _si) \
|
|
|
|
for (i = 0; i < SHMEM_HASH_SIZE; i++) \
|
|
|
|
for (si = shmems_hash[i]; si; si = si->next)
|
|
|
|
|
|
|
|
int cr_dump_shmem(void)
|
|
|
|
{
|
|
|
|
int ret = 0, i;
|
|
|
|
struct shmem_info_dump *si;
|
|
|
|
|
|
|
|
for_each_shmem_dump (i, si) {
|
|
|
|
ret = dump_one_shmem(si);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|