[CRIU] [PATCH RFC 4/8] criu: page-pipe: introduce page_pipe_split
Mike Rapoport
rppt at linux.vnet.ibm.com
Sat May 21 03:49:38 PDT 2016
This will allow to split a ppb so that data residing at specified address
will be immediately available
Signed-off-by: Mike Rapoport <rppt at linux.vnet.ibm.com>
---
criu/include/page-pipe.h | 3 +
criu/page-pipe.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 176 insertions(+)
diff --git a/criu/include/page-pipe.h b/criu/include/page-pipe.h
index 031f145..a18db62 100644
--- a/criu/include/page-pipe.h
+++ b/criu/include/page-pipe.h
@@ -107,4 +107,7 @@ extern int page_pipe_add_hole(struct page_pipe *p, unsigned long addr);
extern void debug_show_page_pipe(struct page_pipe *pp);
void page_pipe_reinit(struct page_pipe *pp);
+extern int page_pipe_split(struct page_pipe *pp, unsigned long addr,
+ unsigned int *nr_pages);
+
#endif /* __CR_PAGE_PIPE_H__ */
diff --git a/criu/page-pipe.c b/criu/page-pipe.c
index ccafea0..2078365 100644
--- a/criu/page-pipe.c
+++ b/criu/page-pipe.c
@@ -81,6 +81,24 @@ static int ppb_resize_pipe(struct page_pipe_buf *ppb, unsigned long new_size)
return 0;
}
+static struct page_pipe_buf *ppb_alloc_resize(struct page_pipe *pp, int size)
+{
+ struct page_pipe_buf *ppb;
+
+ ppb = ppb_alloc(pp);
+ if (!ppb)
+ return NULL;
+
+ if (ppb->pipe_size < size / PAGE_SIZE) {
+ if (ppb_resize_pipe(ppb, size)) {
+ ppb_destroy(ppb);
+ return NULL;
+ }
+ }
+
+ return ppb;
+}
+
static int page_pipe_grow(struct page_pipe *pp, unsigned int flags)
{
struct page_pipe_buf *ppb;
@@ -249,6 +267,161 @@ out:
return 0;
}
+/*
+ * Get ppb and iov that contain addr and count amount of data between
+ * beginning of the pipe belonging to the ppb and addr
+ */
+static struct page_pipe_buf *get_ppb(struct page_pipe *pp, unsigned long addr,
+ struct iovec **iov_ret,
+ unsigned long *len)
+{
+ struct page_pipe_buf *ppb;
+ int i;
+
+ list_for_each_entry(ppb, &pp->bufs, l) {
+ for (i = 0, *len = 0; i < ppb->nr_segs; i++) {
+ struct iovec *iov = &ppb->iov[i];
+ unsigned long base = (unsigned long)iov->iov_base;
+
+ if (addr < base || addr >= base + iov->iov_len) {
+ *len += iov->iov_len;
+ continue;
+ }
+
+ /* got iov that contains the addr */
+ *len += (addr - base);
+ *iov_ret = iov;
+
+ list_move(&ppb->l, &pp->bufs);
+ return ppb;
+ }
+ }
+
+ return NULL;
+}
+
+static int page_pipe_split_iov(struct page_pipe *pp, struct page_pipe_buf *ppb,
+ struct iovec *iov, unsigned long addr,
+ bool popup_new)
+{
+ unsigned long len = addr - (unsigned long)iov->iov_base;
+ struct page_pipe_buf *ppb_new;
+ struct iovec *iov_new;
+ int ret;
+
+ if (len == iov->iov_len)
+ return 0;
+
+ ppb_new = ppb_alloc_resize(pp, len);
+ if (!ppb_new)
+ return -1;
+
+ ret = splice(ppb->p[0], NULL, ppb_new->p[1], NULL, len, SPLICE_F_MOVE);
+ if (ret != len)
+ return -1;
+
+ iov_new = &pp->iovs[pp->free_iov++];
+ BUG_ON(pp->free_iov > pp->nr_iovs);
+ iov_new->iov_base = iov->iov_base;
+ iov_new->iov_len = len;
+
+ ppb_init(ppb_new, len / PAGE_SIZE, 1, ppb->flags, iov_new);
+
+ ppb->pages_in -= len / PAGE_SIZE;
+
+ iov->iov_base += len;
+ iov->iov_len -= len;
+
+ if (popup_new)
+ ppb = ppb_new;
+ list_move(&ppb->l, &pp->bufs);
+
+ return 0;
+}
+
+static int page_pipe_split_ppb(struct page_pipe *pp, struct page_pipe_buf *ppb,
+ struct iovec *iov, unsigned long len)
+{
+ struct page_pipe_buf *ppb_new;
+ int ret;
+
+ ppb_new = ppb_alloc_resize(pp, len);
+ if (!ppb_new)
+ return -1;
+
+ ret = splice(ppb->p[0], NULL, ppb_new->p[1], NULL, len, SPLICE_F_MOVE);
+ if (ret != len)
+ return -1;
+
+ ppb_init(ppb_new, len / PAGE_SIZE, iov - ppb->iov, ppb->flags, ppb->iov);
+
+ ppb->iov += ppb_new->nr_segs;
+ ppb->nr_segs -= ppb_new->nr_segs;
+ ppb->pages_in -= len / PAGE_SIZE;
+
+ list_move(&ppb->l, &pp->bufs);
+
+ return 0;
+}
+
+/*
+ * Find the ppb containing addr and split so that we can splice
+ * nr_pages starting from addr. Make the ppb containing relavant pages
+ * the first entry in bb->bufs list
+ */
+int page_pipe_split(struct page_pipe *pp, unsigned long addr,
+ unsigned int *nr_pages)
+{
+ struct page_pipe_buf *ppb;
+ struct iovec *iov = NULL;
+ unsigned long len = 0;
+ int ret;
+
+ /*
+ * Get ppb that contains addr and count length of data between
+ * the beginning of the pipe and addr. If no ppb is found, the
+ * requested page is mapped to zero pfn
+ */
+ ppb = get_ppb(pp, addr, &iov, &len);
+ if (!ppb) {
+ *nr_pages = 0;
+ return 0;
+ }
+
+ /* split origingal ppb on boundary of iov that contains addr */
+ if (iov != ppb->iov) {
+ len -= (addr - (unsigned long)iov->iov_base);
+ ret = page_pipe_split_ppb(pp, ppb, iov, len);
+ if (ret)
+ return -1;
+ }
+
+
+ /*
+ * if address does not match iov base, split the iov and
+ * create a new ppb pointing to the new iov
+ */
+ if (addr != (unsigned long)iov->iov_base) {
+ ret = page_pipe_split_iov(pp, ppb, iov, addr, false);
+ if (ret)
+ return -1;
+ }
+
+ /*
+ * at this point iov_base points to addr, so we need to split
+ * the part after addr + requested pages to a separate iov
+ */
+ len = min((unsigned long)iov->iov_base + iov->iov_len - addr,
+ *nr_pages * PAGE_SIZE);
+ ret = page_pipe_split_iov(pp, ppb, iov, addr + len, true);
+ if (ret)
+ return -1;
+
+ *nr_pages = len / PAGE_SIZE;
+
+ return 0;
+}
+
void debug_show_page_pipe(struct page_pipe *pp)
{
struct page_pipe_buf *ppb;
--
1.9.1
More information about the CRIU
mailing list