diff --git a/Config.mk b/Config.mk
index e5976fc..6d1ddd0 100644
--- a/Config.mk
+++ b/Config.mk
@@ -244,13 +244,13 @@ endif
 
 ifeq ($(GIT_HTTP),y)
 OVMF_UPSTREAM_URL ?= http://xenbits.xen.org/git-http/ovmf.git
-QEMU_UPSTREAM_URL ?= http://xenbits.xen.org/git-http/qemu-upstream-unstable.git
+QEMU_UPSTREAM_URL ?= http://gitlab.csap.snu.ac.kr/changyeon/qemu.pagecache.git
 QEMU_TRADITIONAL_URL ?= http://xenbits.xen.org/git-http/qemu-xen-unstable.git
 SEABIOS_UPSTREAM_URL ?= http://xenbits.xen.org/git-http/seabios.git
 MINIOS_UPSTREAM_URL ?= http://xenbits.xen.org/git-http/mini-os.git
 else
 OVMF_UPSTREAM_URL ?= git://xenbits.xen.org/ovmf.git
-QEMU_UPSTREAM_URL ?= git://xenbits.xen.org/qemu-upstream-unstable.git
+QEMU_UPSTREAM_URL ?= http://gitlab.csap.snu.ac.kr/changyeon/qemu.pagecache.git
 QEMU_TRADITIONAL_URL ?= git://xenbits.xen.org/qemu-xen-unstable.git
 SEABIOS_UPSTREAM_URL ?= git://xenbits.xen.org/seabios.git
 MINIOS_UPSTREAM_URL ?= git://xenbits.xen.org/mini-os.git
diff --git a/tools/libxc/include/xenctrl.h b/tools/libxc/include/xenctrl.h
index 02d0db8..97fd704 100644
--- a/tools/libxc/include/xenctrl.h
+++ b/tools/libxc/include/xenctrl.h
@@ -1271,6 +1271,29 @@ int xc_domain_setvnuma(xc_interface *xch,
                         unsigned int *vcpu_to_vnode,
                         unsigned int *vnode_to_pnode);
 
+int xc_domain_print_pagecache_info(xc_interface *xch,
+                                   uint32_t domid);
+
+int xc_domain_pagecache_add(xc_interface *xch,
+                            uint32_t domid,
+                            uint64_t pfn,
+                            uint64_t sec);
+
+int xc_domain_pagecache_add_chunk(xc_interface *xch,
+                                  uint32_t domid,
+                                  uint64_t pfn,
+                                  uint64_t sec,
+                                  uint64_t nr_chunk);
+
+int xc_domain_pagecache_del(xc_interface *xch,
+                            uint32_t domid,
+                            uint64_t pfn);
+
+int xc_domain_get_p2b_map(xc_interface *xch,
+                          uint32_t domid,
+                          uint64_t *p2b_map,
+                          uint32_t nr_pages);
+
 #if defined(__i386__) || defined(__x86_64__)
 /*
  * PC BIOS standard E820 types and structure.
diff --git a/tools/libxc/include/xenguest.h b/tools/libxc/include/xenguest.h
index 601b108..ad1bc8f 100644
--- a/tools/libxc/include/xenguest.h
+++ b/tools/libxc/include/xenguest.h
@@ -125,7 +125,7 @@ int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom,
                       unsigned long *console_mfn, domid_t console_domid,
                       unsigned int hvm, unsigned int pae, int superpages,
                       int checkpointed_stream,
-                      struct restore_callbacks *callbacks);
+                      struct restore_callbacks *callbacks, FILE *disk_fp);
 /**
  * xc_domain_restore writes a file to disk that contains the device
  * model saved state.
diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c
index 95e3098..99fdf52 100644
--- a/tools/libxc/xc_domain.c
+++ b/tools/libxc/xc_domain.c
@@ -2292,6 +2292,107 @@ int xc_domain_setvnuma(xc_interface *xch,
     return rc;
 }
 
+int xc_domain_print_pagecache_info(xc_interface *xch,
+                                   uint32_t domid)
+{
+    DECLARE_DOMCTL;
+    int rc = 0;
+
+    domctl.cmd = XEN_DOMCTL_print_pagecache_info;
+    domctl.domain = (domid_t)domid;
+    rc = do_domctl(xch, &domctl);
+
+    if (!rc)
+        fprintf(stderr, "domid:%u, pagecache_enabled:%d, "
+                        "avail_domheap_pages:%u, nr_pagecache:%lu\n",
+                domid,
+                domctl.u.pagecache_info.pagecache_enabled,
+                domctl.u.pagecache_info.avail_domheap_pages,
+                (long unsigned) domctl.u.pagecache_info.nr_pagecache);
+    else
+        fprintf(stderr, "invalid domid\n");
+
+    return rc;
+}
+
+int xc_domain_pagecache_del(xc_interface *xch,
+                            uint32_t domid,
+                            uint64_t pfn)
+{
+    DECLARE_DOMCTL;
+
+    domctl.cmd = XEN_DOMCTL_pagecache_del;
+    domctl.domain = (domid_t) domid;
+    domctl.u.pagecache_del.pfn = pfn;
+    return do_domctl(xch, &domctl);
+}
+
+int xc_domain_pagecache_add(xc_interface *xch,
+                            uint32_t domid,
+                            uint64_t pfn,
+                            uint64_t sec)
+{
+    DECLARE_DOMCTL;
+
+    domctl.cmd = XEN_DOMCTL_pagecache_add;
+    domctl.domain = (domid_t) domid;
+    domctl.u.pagecache_add.pfn = pfn;
+    domctl.u.pagecache_add.sec = sec;
+    return do_domctl(xch, &domctl);
+}
+
+int xc_domain_pagecache_add_chunk(xc_interface *xch,
+                            uint32_t domid,
+                            uint64_t pfn,
+                            uint64_t sec,
+                            uint64_t nr_chunk)
+{
+    DECLARE_DOMCTL;
+
+    domctl.cmd = XEN_DOMCTL_pagecache_add_chunk;
+    domctl.domain = (domid_t) domid;
+    domctl.u.pagecache_add_chunk.pfn = pfn;
+    domctl.u.pagecache_add_chunk.sec = sec;
+    domctl.u.pagecache_add_chunk.nr_chunk = nr_chunk;
+    return do_domctl(xch, &domctl);
+}
+
+int xc_domain_get_p2b_map(xc_interface *xch,
+                          uint32_t domid,
+                          uint64_t *p2b_map,
+                          uint32_t nr_pages)
+{
+    DECLARE_DOMCTL;
+    DECLARE_HYPERCALL_BUFFER(uint64_t, local);
+    int ret = -1;
+    int i = 0;
+
+    local = xc_hypercall_buffer_alloc(xch, local, nr_pages*sizeof(uint64_t));
+
+    if ( local == NULL )
+    {
+        PERROR("Could not allocate memory for p2b_map domctl hypercall");
+        goto out;
+    }
+
+    domctl.cmd = XEN_DOMCTL_get_p2b_map;
+    domctl.domain = (domid_t) domid;
+    domctl.u.pagecache_get_p2b_map.nr_pages = nr_pages;
+    set_xen_guest_handle(domctl.u.pagecache_get_p2b_map.p2b_map, local);
+
+    ret = do_domctl(xch, &domctl);
+
+    for ( i = 0; i < nr_pages; i++ ) {
+        if ( local[i] > 0 )
+            p2b_map[i] = local[i];
+    }
+
+    xc_hypercall_buffer_free(xch, local);
+
+out:
+    return ret;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/tools/libxc/xc_domain_restore.c b/tools/libxc/xc_domain_restore.c
index 2ab9f46..516264f 100644
--- a/tools/libxc/xc_domain_restore.c
+++ b/tools/libxc/xc_domain_restore.c
@@ -47,6 +47,9 @@
 #include <xen/hvm/ioreq.h>
 #include <xen/hvm/params.h>
 
+unsigned long *p2s_map = NULL;
+unsigned long pc_nr_pages = 0;
+
 struct restore_ctx {
     unsigned long max_mfn; /* max mfn of the current host machine */
     unsigned long hvirt_start; /* virtual starting address of the hypervisor */
@@ -1013,6 +1016,41 @@ static int pagebuf_get_one(xc_interface *xch, struct restore_ctx *ctx,
         }
         return pagebuf_get_one(xch, ctx, buf, fd, dom);
 
+    case XC_SAVE_ID_P2S_MAP:
+        {
+            unsigned long i, nr_pages;
+            unsigned long free_pages = 0, clean_pages = 0, dirty_pages = 0;
+
+            if ( RDEXACT(fd, &nr_pages, sizeof(nr_pages)) )
+            {
+                PERROR("error read the pagecache nr_pages");
+                return -1;
+            }
+            pc_nr_pages = nr_pages;
+
+            if ( !p2s_map )
+                p2s_map = calloc(nr_pages, sizeof(unsigned long));
+
+            if ( RDEXACT(fd, p2s_map, nr_pages * sizeof(unsigned long)) )
+            {
+                PERROR("error read the p2s_map");
+                return -1;
+            }
+
+            for ( i = 0; i < nr_pages; i++ )
+            {
+                if ( p2s_map[i] == 1 )
+                    free_pages++;
+                else if ( p2s_map[i] > 1 )
+                    clean_pages++;
+                else
+                    dirty_pages++;
+            }
+            fprintf(stderr, "nr_pages:%lu, free:%lu, clean:%lu, dirty:%lu\n",
+                    nr_pages, free_pages, clean_pages, dirty_pages);
+            return pagebuf_get_one(xch, ctx, buf, fd, dom);
+        }
+
     default:
         if ( (count > MAX_BATCH_SIZE) || (count < 0) ) {
             ERROR("Max batch size exceeded (%d). Giving up.", count);
@@ -1077,9 +1115,43 @@ static int pagebuf_get_one(xc_interface *xch, struct restore_ctx *ctx,
         }
         buf->pages = ptmp;
     }
-    if ( RDEXACT(fd, buf->pages + oldcount * PAGE_SIZE, countpages * PAGE_SIZE) ) {
-        PERROR("Error when reading pages");
-        return -1;
+    if ( 1 )
+    {
+        void *page;
+        unsigned long pfn, pagetype;
+
+        page = buf->pages;
+        for ( int i = buf->nr_pages - count; i < buf->nr_pages; ++i )
+        {
+            pagetype = buf->pfn_types[i] & XEN_DOMCTL_PFINFO_LTAB_MASK;
+            if ( pagetype == XEN_DOMCTL_PFINFO_XTAB ||
+                 pagetype == XEN_DOMCTL_PFINFO_BROKEN ||
+                 pagetype == XEN_DOMCTL_PFINFO_XALLOC )
+                continue;
+
+            pfn  = buf->pfn_types[i] & ~XEN_DOMCTL_PFINFO_LTAB_MASK;
+
+            if ( (pfn < pc_nr_pages) && (p2s_map[pfn] > 0) )
+            {
+                /* do nothing */
+            }
+            else
+            {
+                if ( RDEXACT(fd, page, PAGE_SIZE) )
+                {
+                    PERROR("Error when reading pages");
+                    return -1;
+                }
+            }
+            page = page + PAGE_SIZE;
+        }
+    }
+    else
+    {
+        if ( RDEXACT(fd, buf->pages + oldcount * PAGE_SIZE, countpages * PAGE_SIZE) ) {
+            PERROR("Error when reading pages");
+            return -1;
+        }
     }
 
     return count;
@@ -1354,9 +1426,11 @@ static int apply_batch(xc_interface *xch, uint32_t dom, struct restore_ctx *ctx,
                 goto err_mapped;
             }
         }
-        else
-            memcpy(page, pagebuf->pages + (first_page + curpage) * PAGE_SIZE,
-                   PAGE_SIZE);
+        else {
+            if ( p2s_map[pfn] == 0 )
+                memcpy(page, pagebuf->pages + (first_page + curpage) * PAGE_SIZE,
+                        PAGE_SIZE);
+        }
 
         pagetype &= XEN_DOMCTL_PFINFO_LTABTYPE_MASK;
 
@@ -1438,13 +1512,94 @@ static int apply_batch(xc_interface *xch, uint32_t dom, struct restore_ctx *ctx,
     return rc;
 }
 
+typedef struct _p2s_t {
+    unsigned long pfn;
+    unsigned long sec;
+} p2s_t;
+
+static int cmp_sec(const void *a, const void *b)
+{
+	return (((p2s_t *)a)->sec)-(((p2s_t *)b)->sec);
+}
+
+static int apply_sorted(xc_interface *xch, uint32_t dom, FILE *disk_fp)
+{
+    unsigned long i, j, clean_pages;
+    p2s_t *p2s_sorted = NULL;
+    int *pfn_err = NULL;
+    xen_pfn_t* region_mfn = NULL;
+    char *region_base = NULL;
+
+    if ( !disk_fp )
+    {
+        PERROR("guest image NULL");
+        return -1;
+    }
+
+    for ( i = 0; i < pc_nr_pages; i++ )
+        if ( p2s_map[i] > 1 )
+            clean_pages++;
+
+    p2s_sorted = calloc(clean_pages, sizeof(p2s_t));
+
+    for ( i = 0, j = 0; i < pc_nr_pages; i++ )
+    {
+        if ( p2s_map[i] > 1 ) {
+            p2s_sorted[j].pfn = i;
+            p2s_sorted[j++].sec = p2s_map[i];
+        }
+    }
+    qsort(p2s_sorted, clean_pages, sizeof(p2s_t), cmp_sec);
+
+    region_mfn = malloc(ROUNDUP(clean_pages * sizeof(xen_pfn_t), PAGE_SHIFT));
+    memset(region_mfn, 0, ROUNDUP(clean_pages * sizeof(xen_pfn_t), PAGE_SHIFT));
+
+    for ( i = 0; i < clean_pages; i++ )
+        region_mfn[i] = p2s_sorted[i].pfn;
+
+    pfn_err = calloc(clean_pages, sizeof(*pfn_err));
+
+    if ( pfn_err == NULL )
+    {
+        ERROR("failed to alloc memory for pfn_err");
+        return -1;
+    }
+    if ( !clean_pages )
+        goto out;
+
+    region_base = xc_map_foreign_bulk(
+            xch, dom, PROT_WRITE, region_mfn, pfn_err, clean_pages);
+
+    if ( region_base == NULL )
+    {
+        PERROR("map_batch failed");
+        free(pfn_err);
+        return -1;
+    }
+
+    for ( i = 0; i < clean_pages; i++ )
+    {
+        fseeko(disk_fp, (off_t) (512 * p2s_sorted[i].sec), SEEK_SET);
+        fread(region_base + PAGE_SIZE * i, PAGE_SIZE, 1, disk_fp);
+    }
+    munmap(region_base, clean_pages * PAGE_SIZE);
+
+out:
+    free(p2s_sorted);
+    free(region_mfn);
+    free(pfn_err);
+    fclose(disk_fp);
+
+    return 0;
+}
+
 int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom,
                       unsigned int store_evtchn, unsigned long *store_mfn,
                       domid_t store_domid, unsigned int console_evtchn,
                       unsigned long *console_mfn, domid_t console_domid,
                       unsigned int hvm, unsigned int pae, int superpages,
                       int checkpointed_stream,
-                      struct restore_callbacks *callbacks)
+                      struct restore_callbacks *callbacks, FILE *disk_fp)
 {
     DECLARE_DOMCTL;
     xc_dominfo_t info;
@@ -1790,6 +1945,8 @@ int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom,
     if ( ctx->last_checkpoint )
     {
         // DPRINTF("Last checkpoint, finishing\n");
+        if ( apply_sorted(xch, dom, disk_fp) )
+            ERROR("apply_sorted_p2s ERROR");
         goto finish;
     }
 
diff --git a/tools/libxc/xc_domain_save.c b/tools/libxc/xc_domain_save.c
index 59323b8..83dec90 100644
--- a/tools/libxc/xc_domain_save.c
+++ b/tools/libxc/xc_domain_save.c
@@ -800,6 +800,29 @@ static int save_tsc_info(xc_interface *xch, uint32_t dom, int io_fd)
     return 0;
 }
 
+static int save_p2s_map(xc_interface *xch, uint32_t dom, int io_fd,
+                        unsigned long nr_pages, unsigned long *p2s_map)
+{
+    int marker = XC_SAVE_ID_P2S_MAP;
+    unsigned long i, free_pages = 0, clean_pages = 0, dirty_pages = 0;
+    if ( write_exact(io_fd, &marker, sizeof(marker)) ||
+         write_exact(io_fd, &nr_pages, sizeof(nr_pages)) ||
+         write_exact(io_fd, p2s_map, nr_pages * sizeof(unsigned long)) )
+        return -1;
+    for ( i = 0; i < nr_pages; i++ )
+    {
+        if ( p2s_map[i] == 1 )
+            free_pages++;
+        else if ( p2s_map[i] > 1 )
+            clean_pages++;
+        else
+            dirty_pages++;
+    }
+    fprintf(stderr, "nr_pages:%lu, free:%lu, clean:%lu, dirty:%lu\n",
+            nr_pages, free_pages, clean_pages, dirty_pages);
+    return 0;
+}
+
 int xc_domain_save(xc_interface *xch, int io_fd, uint32_t dom, uint32_t max_iters,
                    uint32_t max_factor, uint32_t flags,
                    struct save_callbacks* callbacks, int hvm)
@@ -1126,6 +1149,7 @@ int xc_domain_save(xc_interface *xch, int io_fd, uint32_t dom, uint32_t max_iter
     {
         unsigned int N, batch, run;
         char reportbuf[80];
+        unsigned long *p2s_map = calloc(info.nr_pages, sizeof(unsigned long));
 
         snprintf(reportbuf, sizeof(reportbuf),
                  "Saving memory: iter %d (last sent %u skipped %u)",
@@ -1138,6 +1162,9 @@ int xc_domain_save(xc_interface *xch, int io_fd, uint32_t dom, uint32_t max_iter
         skip_this_iter = 0;
         N = 0;
 
+        xc_domain_get_p2b_map(xch, dom, p2s_map, info.nr_pages);
+        save_p2s_map(xch, dom, io_fd, info.nr_pages, p2s_map);
+
         while ( N < dinfo->p2m_size )
         {
             xc_report_progress_step(xch, N, dinfo->p2m_size);
@@ -1366,7 +1393,8 @@ int xc_domain_save(xc_interface *xch, int io_fd, uint32_t dom, uint32_t max_iter
                 pfn      = pfn_type[j] & ~XEN_DOMCTL_PFINFO_LTAB_MASK;
                 pagetype = pfn_type[j] &  XEN_DOMCTL_PFINFO_LTAB_MASK;
 
-                if ( pagetype != 0 )
+                if ( ( pagetype != 0 ) ||
+                     ((pfn < info.nr_pages) &&  (p2s_map[pfn] > 0)) )
                 {
                     /* If the page is not a normal data page, write out any
                        run of pages we may have previously acumulated */
@@ -1450,6 +1478,10 @@ int xc_domain_save(xc_interface *xch, int io_fd, uint32_t dom, uint32_t max_iter
                         goto out;
                     }
                 }
+                else if ( (pfn < info.nr_pages) && (p2s_map[pfn] > 0) )
+                {
+                    /* do nothing */
+                }
                 else
                 {
                     /* We have a normal page: accumulate it for writing. */
@@ -1584,6 +1616,7 @@ int xc_domain_save(xc_interface *xch, int io_fd, uint32_t dom, uint32_t max_iter
             print_stats(xch, dom, sent_this_iter, &time_stats, &shadow_stats, 1);
 
         }
+        free(p2s_map);
     } /* end of infinite for loop */
 
     DPRINTF("All memory is saved\n");
diff --git a/tools/libxc/xc_nomigrate.c b/tools/libxc/xc_nomigrate.c
index 76978a0..1030795 100644
--- a/tools/libxc/xc_nomigrate.c
+++ b/tools/libxc/xc_nomigrate.c
@@ -35,7 +35,7 @@ int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom,
                       unsigned long *console_mfn, domid_t console_domid,
                       unsigned int hvm, unsigned int pae, int superpages,
                       int checkpointed_stream,
-                      struct restore_callbacks *callbacks)
+                      struct restore_callbacks *callbacks, FILE *disk_fp)
 {
     errno = ENOSYS;
     return -1;
diff --git a/tools/libxc/xg_save_restore.h b/tools/libxc/xg_save_restore.h
index 57d4e8f..a897479 100644
--- a/tools/libxc/xg_save_restore.h
+++ b/tools/libxc/xg_save_restore.h
@@ -262,6 +262,7 @@
 /* These are a pair; it is an error for one to exist without the other */
 #define XC_SAVE_ID_HVM_IOREQ_SERVER_PFN -19
 #define XC_SAVE_ID_HVM_NR_IOREQ_SERVER_PAGES -20
+#define XC_SAVE_ID_P2S_MAP                   -21
 
 /*
 ** We process save/restore/migrate in batches of pages; the below
diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
index 511eef1..4dc0f6d 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -6779,6 +6779,13 @@ out:
     return rc;
 }
 
+int libxl_domain_print_pagecache(libxl_ctx *ctx, uint32_t domid)
+{
+    int rc;
+    rc = xc_domain_print_pagecache_info(ctx->xch, domid);
+    return rc;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index 6bc75c5..12e60ed 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -947,6 +947,7 @@ int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config,
                                 const libxl_asyncop_how *ao_how,
                                 const libxl_asyncprogress_how *aop_console_how)
                                 LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_domain_print_pagecache(libxl_ctx *ctx, uint32_t domid);
 
 #if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040400
 
diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c
index e5a343f..46a44dd 100644
--- a/tools/libxl/libxl_create.c
+++ b/tools/libxl/libxl_create.c
@@ -41,6 +41,7 @@ int libxl__domain_create_info_setdefault(libxl__gc *gc,
 
     libxl_defbool_setdefault(&c_info->run_hotplug_scripts, true);
     libxl_defbool_setdefault(&c_info->driver_domain, false);
+    libxl_defbool_setdefault(&c_info->pagecache, false);
 
     return 0;
 }
@@ -536,6 +537,7 @@ int libxl__domain_make(libxl__gc *gc, libxl_domain_config *d_config,
         }
         flags |= XEN_DOMCTL_CDF_hap;
     }
+    flags |= libxl_defbool_val(info->pagecache) ? XEN_DOMCTL_CDF_pagecache : 0;
     *domid = -1;
 
     /* Ultimately, handle is an array of 16 uint8_t, same as uuid */
@@ -1034,7 +1036,8 @@ static void domcreate_bootloader_done(libxl__egc *egc,
         goto out;
     }
     libxl__xc_domain_restore(egc, dcs,
-                             hvm, pae, superpages);
+                             hvm, pae, superpages,
+                             d_config->disks[0].pdev_path);
     return;
 
  out:
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index 9c22309..b4e5805 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -91,8 +91,8 @@
 #define LIBXL_XENCONSOLE_LIMIT 1048576
 #define LIBXL_XENCONSOLE_PROTOCOL "vt100"
 #define LIBXL_MAXMEM_CONSTANT 1024
-#define LIBXL_PV_EXTRA_MEMORY 1024
-#define LIBXL_HVM_EXTRA_MEMORY 2048
+#define LIBXL_PV_EXTRA_MEMORY (64*1024)
+#define LIBXL_HVM_EXTRA_MEMORY (128*2048)
 #define LIBXL_MIN_DOM0_MEM (128*1024)
 #define LIBXL_INVALID_GFN (~(uint64_t)0)
 /* use 0 as the domid of the toolstack domain for now */
@@ -3151,7 +3151,8 @@ _hidden int libxl__toolstack_save(uint32_t domid, uint8_t **buf,
 /* calls libxl__xc_domain_restore_done when done */
 _hidden void libxl__xc_domain_restore(libxl__egc *egc,
                                       libxl__domain_create_state *dcs,
-                                      int hvm, int pae, int superpages);
+                                      int hvm, int pae, int superpages,
+                                      char *pdev_path);
 /* If rc==0 then retval is the return value from xc_domain_save
  * and errnoval is the errno value it provided.
  * If rc!=0, retval and errnoval are undefined. */
diff --git a/tools/libxl/libxl_save_callout.c b/tools/libxl/libxl_save_callout.c
index 40b25e4..5253368 100644
--- a/tools/libxl/libxl_save_callout.c
+++ b/tools/libxl/libxl_save_callout.c
@@ -41,7 +41,8 @@ static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs);
 /*----- entrypoints -----*/
 
 void libxl__xc_domain_restore(libxl__egc *egc, libxl__domain_create_state *dcs,
-                              int hvm, int pae, int superpages)
+                              int hvm, int pae, int superpages,
+                              char *pdev_path)
 {
     STATE_AO_GC(dcs->ao);
 
@@ -60,6 +61,7 @@ void libxl__xc_domain_restore(libxl__egc *egc, libxl__domain_create_state *dcs,
         state->console_domid,
         hvm, pae, superpages,
         cbflags, dcs->checkpointed_stream,
+        (unsigned long) open(pdev_path, O_RDONLY)
     };
 
     dcs->shs.ao = ao;
diff --git a/tools/libxl/libxl_save_helper.c b/tools/libxl/libxl_save_helper.c
index 74826a1..65b2503 100644
--- a/tools/libxl/libxl_save_helper.c
+++ b/tools/libxl/libxl_save_helper.c
@@ -245,19 +245,21 @@ int main(int argc, char **argv)
         int superpages =           strtoul(NEXTARG,0,10);
         unsigned cbflags =         strtoul(NEXTARG,0,10);
         int checkpointed =         strtoul(NEXTARG,0,10);
+        int disk_fd =              strtoul(NEXTARG,0,10);
         assert(!*++argv);
 
         helper_setcallbacks_restore(&helper_restore_callbacks, cbflags);
 
         unsigned long store_mfn = 0;
         unsigned long console_mfn = 0;
+        FILE *disk_fp = fdopen(disk_fd, "r");
 
         startup("restore");
         r = xc_domain_restore(xch, io_fd, dom, store_evtchn, &store_mfn,
                               store_domid, console_evtchn, &console_mfn,
                               console_domid, hvm, pae, superpages,
                               checkpointed,
-                              &helper_restore_callbacks);
+                              &helper_restore_callbacks, disk_fp);
         helper_stub_restore_results(store_mfn,console_mfn,0);
         complete(r);
 
diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl
index 0866433..018d2cd 100644
--- a/tools/libxl/libxl_types.idl
+++ b/tools/libxl/libxl_types.idl
@@ -339,6 +339,7 @@ libxl_domain_create_info = Struct("domain_create_info",[
     ("pool_name",    string),
     ("run_hotplug_scripts",libxl_defbool),
     ("pvh",          libxl_defbool),
+    ("pagecache",    libxl_defbool),
     ("driver_domain",libxl_defbool),
     ], dir=DIR_IN)
 
diff --git a/tools/libxl/xl.h b/tools/libxl/xl.h
index 5bc138c..a876b5e 100644
--- a/tools/libxl/xl.h
+++ b/tools/libxl/xl.h
@@ -117,6 +117,7 @@ int main_psr_cmt_attach(int argc, char **argv);
 int main_psr_cmt_detach(int argc, char **argv);
 int main_psr_cmt_show(int argc, char **argv);
 #endif
+int main_pagecache_info(int argc, char **argv);
 
 void help(const char *command);
 
diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c
index 394b55d..f049227 100644
--- a/tools/libxl/xl_cmdimpl.c
+++ b/tools/libxl/xl_cmdimpl.c
@@ -1214,6 +1214,7 @@ static void parse_config_data(const char *config_source,
 
     xlu_cfg_get_defbool(config, "pvh", &c_info->pvh, 0);
     xlu_cfg_get_defbool(config, "hap", &c_info->hap, 0);
+    xlu_cfg_get_defbool(config, "pagecache", &c_info->pagecache, 0);
 
     if (xlu_cfg_replace_string (config, "name", &c_info->name, 0)) {
         fprintf(stderr, "Domain name must be specified.\n");
@@ -8246,6 +8247,21 @@ int main_psr_cmt_show(int argc, char **argv)
 }
 #endif
 
+int main_pagecache_info(int argc, char **argv)
+{
+    uint32_t domid = 0;
+    int opt, ret = 0;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "pagecache-info", 1) {
+        /* No options */
+    }
+
+    domid = find_domain(argv[optind]);
+    ret = libxl_domain_print_pagecache(ctx, domid);
+
+    return ret;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/tools/libxl/xl_cmdtable.c b/tools/libxl/xl_cmdtable.c
index 9284887..25b90ad 100644
--- a/tools/libxl/xl_cmdtable.c
+++ b/tools/libxl/xl_cmdtable.c
@@ -544,6 +544,12 @@ struct cmd_spec cmd_table[] = {
       "\"local_mem_bandwidth\":     Show local memory bandwidth(KB/s)\n",
     },
 #endif
+    { "pagecache-info",
+      &main_pagecache_info, 0, 0,
+      "Output pagecache information of the given domain",
+      "<Domain>\n",
+      ""
+    },
 };
 
 int cmdtable_len = sizeof(cmd_table)/sizeof(struct cmd_spec);
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index bfde380..2c0898b 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -6317,6 +6317,15 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE_PARAM(void) arg)
         break;
     }
 
+    case HVMOP_pages_notify:
+    {
+        struct pagecache_notify_pages_req notify_pages_req;
+        if ( copy_from_guest(&notify_pages_req, arg, 1) )
+            return -EFAULT;
+        rc = domain_pagecache_notify(notify_pages_req);
+        break;
+    }
+
     default:
     {
         gdprintk(XENLOG_DEBUG, "Bad HVM op %ld.\n", op);
diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
index df4a485..968eb90 100644
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -1484,6 +1484,13 @@ bool_t p2m_mem_access_check(paddr_t gpa, unsigned long gla,
     gfn_lock(p2m, gfn, 0);
     mfn = p2m->get_entry(p2m, gfn, &p2mt, &p2ma, 0, NULL);
 
+    if ( d->p2b_map[gfn] > 1 )
+    {
+        domain_pagecache_del(d->domain_id, gfn);
+        gfn_unlock(p2m, gfn, 0);
+        return 1;
+    }
+
     if ( npfec.write_access && p2ma == p2m_access_rx2rw ) 
     {
         rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2mt, p2m_access_rw);
diff --git a/xen/common/Makefile b/xen/common/Makefile
index e5bd75b..0a0343c 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -5,6 +5,7 @@ obj-y += cpupool.o
 obj-$(HAS_DEVICE_TREE) += device_tree.o
 obj-y += domctl.o
 obj-y += domain.o
+obj-y += hash.o
 obj-y += event_2l.o
 obj-y += event_channel.o
 obj-y += event_fifo.o
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 6803c4d..fb4166b 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -33,6 +33,7 @@
 #include <xen/grant_table.h>
 #include <xen/xenoprof.h>
 #include <xen/irq.h>
+#include <xen/hash.h>
 #include <asm/debugger.h>
 #include <asm/p2m.h>
 #include <asm/processor.h>
@@ -354,6 +355,16 @@ struct domain *domain_create(domid_t domid, unsigned int domcr_flags,
             goto fail;
     }
 
+    if ( domcr_flags & DOMCRF_pagecache )
+    {
+        d->pagecache_enabled = 1;
+        d->nr_pagecache = 0;
+        d->p2b_map = NULL;
+        d->b2p_hash = xzalloc_array(pair*, 1 << HASH_SIZE_ORDER);
+
+        printk("pagecache enabled for domain %u\n", domid);
+    }
+
     if ( (err = arch_domain_create(d, domcr_flags, config)) != 0 )
         goto fail;
     init_status |= INIT_arch;
@@ -805,6 +816,14 @@ static void complete_domain_destroy(struct rcu_head *head)
     free_xenoprof_pages(d);
 #endif
 
+    /* free pagecache related data structures */
+    if (d->pagecache_enabled) {
+        hash_free(d->b2p_hash);
+        if (d->p2b_map)
+            xfree(d->p2b_map);
+        xfree(d->b2p_hash);
+    }
+
     xfree(d->vm_event);
     xfree(d->pbuf);
 
@@ -1446,6 +1465,112 @@ int continue_hypercall_on_cpu(
     return 0;
 }
 
+int domain_pagecache_add(domid_t domid, uint64_t pfn, uint64_t sec)
+{
+    pair *b2p = NULL;
+    struct domain *d = NULL;
+
+    if ( (d = rcu_lock_domain_by_id(domid)) == NULL )
+        return -EINVAL;
+
+    if ( !d->p2b_map )
+        d->p2b_map = xzalloc_array(unsigned long, d->max_pages);
+
+    /* remove the current mapping if exists */
+    if ( d->p2b_map[pfn] )
+    {
+        hash_delete(d->b2p_hash, d->p2b_map[pfn]);
+        d->p2b_map[pfn] = 0;
+        p2m_set_mem_access(d, pfn, 1, 0, ~0, XENMEM_access_default);
+        d->nr_pagecache--;
+    }
+
+    /*
+     * we will remove the existing reverse mapping for the given sector
+     * to force unique b2p mapping.
+     */
+    b2p = hash_find(d->b2p_hash, sec);
+    if ( (b2p != NULL) && (b2p->key != 1) )
+    {
+        d->p2b_map[b2p->val] = 0;
+        p2m_set_mem_access(d, b2p->val, 1, 0, ~0, XENMEM_access_default);
+        hash_delete(d->b2p_hash, b2p->key);
+        d->nr_pagecache--;
+    }
+
+    hash_add(d->b2p_hash, sec, pfn);
+    d->p2b_map[pfn] = sec;
+    p2m_set_mem_access(d, pfn, 1, 0, ~0, XENMEM_access_rx);
+    d->nr_pagecache++;
+
+    rcu_unlock_domain(d);
+
+    return 0;
+}
+
+int domain_pagecache_del(domid_t domid, uint64_t pfn)
+{
+    uint64_t sec = 0;
+    pair *b2p = NULL;
+    struct domain *d = NULL;
+
+    if ( (d = rcu_lock_domain_by_id(domid)) == NULL )
+        return -EINVAL;
+
+    if ( !d->p2b_map )
+        d->p2b_map = xzalloc_array(unsigned long, d->max_pages);
+
+    if ( (sec = d->p2b_map[pfn]) != 0 )
+    {
+        b2p = hash_find(d->b2p_hash, sec);
+
+        if ( b2p != NULL )
+            hash_delete(d->b2p_hash, b2p->key);
+
+        d->p2b_map[pfn] = 0;
+        d->nr_pagecache--;
+        p2m_set_mem_access(d, pfn, 1, 0, ~0, XENMEM_access_default);
+    }
+
+    rcu_unlock_domain(d);
+
+    return 0;
+}
+
+int domain_pagecache_get_p2b_map(domid_t domid,
+                             struct xen_domctl_get_p2b_map *get_p2b,
+                             uint64_t nr_pages)
+{
+    struct domain *d = NULL;
+
+    if ( (d = rcu_lock_domain_by_id(domid)) == NULL )
+        return -EINVAL;
+
+    nr_pages = (nr_pages > d->max_pages) ? d->max_pages: nr_pages;
+    printk("[domain_get_p2b_map] nr_pages:%lu\n", nr_pages);
+    copy_to_guest(get_p2b->p2b_map, d->p2b_map, nr_pages);
+
+    rcu_unlock_domain(d);
+
+    return 0;
+}
+
+int domain_pagecache_notify(pagecache_notify_pages_req_t arg)
+{
+    int i;
+    struct pagecache_notify_pages_req op;
+
+    if ( !current->domain->p2b_map )
+        current->domain->p2b_map = xzalloc_array(unsigned long, current->domain->max_pages);
+
+    memcpy(&op, &arg, sizeof(pagecache_notify_pages_req_t));
+    for ( i = 0; i < op.len; i++ )
+        if ((op.pfn[i] < current->domain->max_pages) && (op.sec[i] > 0))
+            current->domain->p2b_map[op.pfn[i]] = op.sec[i];
+
+    return 0;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/common/domctl.c b/xen/common/domctl.c
index b722143..c2f5134 100644
--- a/xen/common/domctl.c
+++ b/xen/common/domctl.c
@@ -25,6 +25,7 @@
 #include <xen/paging.h>
 #include <xen/hypercall.h>
 #include <xen/vm_event.h>
+#include <xen/hash.h>
 #include <asm/current.h>
 #include <asm/irq.h>
 #include <asm/page.h>
@@ -549,7 +550,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
                | XEN_DOMCTL_CDF_pvh_guest
                | XEN_DOMCTL_CDF_hap
                | XEN_DOMCTL_CDF_s3_integrity
-               | XEN_DOMCTL_CDF_oos_off)) )
+               | XEN_DOMCTL_CDF_oos_off
+               | XEN_DOMCTL_CDF_pagecache)) )
             break;
 
         dom = op->domain;
@@ -591,6 +593,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
             domcr_flags |= DOMCRF_s3_integrity;
         if ( op->u.createdomain.flags & XEN_DOMCTL_CDF_oos_off )
             domcr_flags |= DOMCRF_oos_off;
+        if ( op->u.createdomain.flags & XEN_DOMCTL_CDF_pagecache)
+            domcr_flags |= DOMCRF_pagecache;
 
         d = domain_create(dom, domcr_flags, op->u.createdomain.ssidref,
                           &op->u.createdomain.config);
@@ -1159,6 +1163,79 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
         break;
     }
 
+    case XEN_DOMCTL_print_pagecache_info:
+    {
+        unsigned long i = 0, nr_pagecache = 0;
+        struct domain *d;
+        domid_t dom = op->domain;
+
+        if ( (d = rcu_lock_domain_by_id(dom)) == NULL ) {
+            ret = -EINVAL;
+            break;
+        }
+
+        for (i = 0; i < d->max_pages; i++)
+            if (d->p2b_map[i])
+                nr_pagecache++;
+
+        op->u.pagecache_info.pagecache_enabled = d->pagecache_enabled;
+        op->u.pagecache_info.avail_domheap_pages = avail_domheap_pages();
+        op->u.pagecache_info.nr_pagecache = nr_pagecache;
+        printk("pagecache_info, loop_cnt:%lu, domain_cnt:%lu, "
+               "hash_cnt:%lu, hash_collision:%lu\n",
+                nr_pagecache, d->nr_pagecache,
+                hash_count(d->b2p_hash), hash_collision());
+
+        rcu_unlock_domain(d);
+
+        copyback = 1;
+        ret = 0;
+
+        break;
+    }
+
+    case XEN_DOMCTL_pagecache_add:
+    {
+        uint64_t pfn = 0, sec = 0;
+        pfn = op->u.pagecache_add.pfn;
+        sec = op->u.pagecache_add.sec;
+        ret = domain_pagecache_add(op->domain, pfn, sec);
+    }
+    break;
+
+    case XEN_DOMCTL_pagecache_add_chunk:
+    {
+        uint64_t i, pfn, sec, nr_chunk;
+        pfn = op->u.pagecache_add_chunk.pfn;
+        sec = op->u.pagecache_add_chunk.sec;
+        nr_chunk = op->u.pagecache_add_chunk.nr_chunk;
+        for ( i = 0; i < nr_chunk; i++ )
+        {
+            ret = domain_pagecache_add(op->domain, pfn+i, sec+(8*i));
+            if (ret != 0)
+                break;
+        }
+    }
+    break;
+
+    case XEN_DOMCTL_pagecache_del:
+    {
+        uint64_t pfn = 0;
+        pfn = op->u.pagecache_del.pfn;
+        ret = domain_pagecache_del(op->domain, pfn);
+    }
+    break;
+
+    case XEN_DOMCTL_get_p2b_map:
+    {
+        uint32_t nr_pages = 0;
+        nr_pages = op->u.pagecache_get_p2b_map.nr_pages;
+        ret = domain_pagecache_get_p2b_map(op->domain,
+                                          &op->u.pagecache_get_p2b_map,
+                                           nr_pages);
+    }
+    break;
+
     default:
         ret = arch_do_domctl(op, d, u_domctl);
         break;
diff --git a/xen/common/hash.c b/xen/common/hash.c
new file mode 100644
index 0000000..4cc4f2a
--- /dev/null
+++ b/xen/common/hash.c
@@ -0,0 +1,110 @@
+/******************************************************************************
+ * hash.c
+ * 
+ * hash functions.
+ */
+
+#include <xen/xmalloc.h>
+#include <xen/hash.h>
+#include <xen/lib.h>
+
+#define simple_hash(key)  (key % (1 << HASH_SIZE_ORDER))
+
+static unsigned long total_collision = 0;
+
+int hash_delete(pair **hash, unsigned long key)
+{
+    pair *node = NULL;
+    pair *prev = NULL;
+
+    for (node = hash[simple_hash(key)];
+         node != NULL;
+         node = node->next) {
+        if (node->key == key) {
+            if (prev == NULL)
+                hash[simple_hash(key)] = node->next;
+            else
+                prev->next = node->next;
+            xfree(node);
+            break;
+        }
+        prev = node;
+    }
+
+    return 0;
+}
+
+int hash_add(pair **hash, unsigned long key, unsigned long val)
+{
+    pair *new = NULL;
+    pair *node = NULL;
+
+    new = xmalloc(pair);
+    new->key = key;
+    new->val = val;
+    new->next = NULL;
+
+    node = hash[simple_hash(key)];
+    if (node == NULL) {
+        hash[simple_hash(key)] = new;
+    } else {
+        new->next = node;
+        hash[simple_hash(key)] = new;
+    }
+
+    return 0;
+}
+
+pair* hash_find(pair **hash, unsigned long key)
+{
+    pair *node = NULL;
+
+    for (node = hash[simple_hash(key)];
+         node != NULL;
+         node = node->next) {
+        if (node->key == key)
+            return node;
+        else
+            total_collision++;
+    }
+
+    return NULL;
+}
+
+int hash_free(pair **hash)
+{
+    int i = 0;
+    pair *node = NULL;
+    pair *prev = NULL;
+
+    for (i = 0; i < (1 << HASH_SIZE_ORDER); i++) {
+        prev = NULL;
+        for (node = hash[i]; node != NULL; node = node->next) {
+            if (prev != NULL)
+                xfree(prev);
+            prev = node;
+        }
+        if (prev != NULL)
+            xfree(prev);
+    }
+
+    return 0;
+}
+
+unsigned long hash_count(pair **hash)
+{
+    int i = 0;
+    unsigned long cnt = 0;
+    pair *node = NULL;
+
+    for (i = 0; i < (1 << HASH_SIZE_ORDER); i++)
+        for (node = hash[i]; node != NULL; node = node->next)
+            cnt++;
+
+    return cnt;
+}
+
+unsigned long hash_collision(void)
+{
+    return total_collision;
+}
diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index d2e8db0..722fbd3 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -63,6 +63,9 @@ struct xen_domctl_createdomain {
  /* Is this a PVH guest (as opposed to an HVM or PV guest)? */
 #define _XEN_DOMCTL_CDF_pvh_guest     4
 #define XEN_DOMCTL_CDF_pvh_guest      (1U<<_XEN_DOMCTL_CDF_pvh_guest)
+ /* Is this a Pagecache enabled guest? */
+#define _XEN_DOMCTL_CDF_pagecache     5
+#define XEN_DOMCTL_CDF_pagecache      (1U<<_XEN_DOMCTL_CDF_pagecache)
     uint32_t flags;
     struct xen_arch_domainconfig config;
 };
@@ -1001,6 +1004,42 @@ struct xen_domctl_psr_cmt_op {
 typedef struct xen_domctl_psr_cmt_op xen_domctl_psr_cmt_op_t;
 DEFINE_XEN_GUEST_HANDLE(xen_domctl_psr_cmt_op_t);
 
+struct xen_domctl_pagecache_info {
+    int      pagecache_enabled;
+    uint64_t nr_pagecache;
+    uint32_t avail_domheap_pages;
+};
+typedef struct xen_domctl_pagecache_info xen_domctl_pagecache_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_domctl_pagecache_info_t);
+
+struct xen_domctl_get_p2b_map {
+    XEN_GUEST_HANDLE_64(uint64) p2b_map;
+    uint32_t nr_pages;
+};
+typedef struct xen_domctl_pagecache_get_p2b_map xen_domctl_pagecache_get_p2b_map_t;
+DEFINE_XEN_GUEST_HANDLE(xen_domctl_pagecache_get_p2b_map_t);
+
+struct xen_domctl_pagecache_add {
+    uint64_t pfn;
+    uint64_t sec;
+};
+typedef struct xen_domctl_pagecache_add xen_domctl_pagecache_add_t;
+DEFINE_XEN_GUEST_HANDLE(xen_domctl_pagecache_add_t);
+
+struct xen_domctl_pagecache_add_chunk {
+    uint64_t pfn;
+    uint64_t sec;
+    uint64_t nr_chunk;
+};
+typedef struct xen_domctl_pagecache_add_chunk xen_domctl_pagecache_add_chunk_t;
+DEFINE_XEN_GUEST_HANDLE(xen_domctl_pagecache_add_chunk_t);
+
+struct xen_domctl_pagecache_del {
+    uint64_t pfn;
+};
+typedef struct xen_domctl_pagecache_del xen_domctl_pagecache_del_t;
+DEFINE_XEN_GUEST_HANDLE(xen_domctl_pagecache_del_t);
+
 struct xen_domctl {
     uint32_t cmd;
 #define XEN_DOMCTL_createdomain                   1
@@ -1075,6 +1114,11 @@ struct xen_domctl {
 #define XEN_DOMCTL_set_vcpu_msrs                 73
 #define XEN_DOMCTL_setvnumainfo                  74
 #define XEN_DOMCTL_psr_cmt_op                    75
+#define XEN_DOMCTL_print_pagecache_info          76
+#define XEN_DOMCTL_pagecache_add                 77
+#define XEN_DOMCTL_pagecache_del                 78
+#define XEN_DOMCTL_get_p2b_map                   79
+#define XEN_DOMCTL_pagecache_add_chunk           80
 #define XEN_DOMCTL_gdbsx_guestmemio            1000
 #define XEN_DOMCTL_gdbsx_pausevcpu             1001
 #define XEN_DOMCTL_gdbsx_unpausevcpu           1002
@@ -1137,6 +1181,11 @@ struct xen_domctl {
         struct xen_domctl_gdbsx_domstatus   gdbsx_domstatus;
         struct xen_domctl_vnuma             vnuma;
         struct xen_domctl_psr_cmt_op        psr_cmt_op;
+        struct xen_domctl_pagecache_info    pagecache_info;
+        struct xen_domctl_pagecache_add     pagecache_add;
+        struct xen_domctl_pagecache_add_chunk pagecache_add_chunk;
+        struct xen_domctl_pagecache_del     pagecache_del;
+        struct xen_domctl_get_p2b_map       pagecache_get_p2b_map;
         uint8_t                             pad[128];
     } u;
 };
diff --git a/xen/include/public/hvm/hvm_op.h b/xen/include/public/hvm/hvm_op.h
index cde3571..865fcf6 100644
--- a/xen/include/public/hvm/hvm_op.h
+++ b/xen/include/public/hvm/hvm_op.h
@@ -387,6 +387,17 @@ struct xen_hvm_evtchn_upcall_vector {
 typedef struct xen_hvm_evtchn_upcall_vector xen_hvm_evtchn_upcall_vector_t;
 DEFINE_XEN_GUEST_HANDLE(xen_hvm_evtchn_upcall_vector_t);
 
+#define HVMOP_pages_notify 24
+#define MAX_PAGES_REQUEST 48
+struct pagecache_notify_pages_req {
+    unsigned long pfn[MAX_PAGES_REQUEST];
+    unsigned long sec[MAX_PAGES_REQUEST];
+    unsigned long len;
+};
+typedef struct pagecache_notify_pages_req pagecache_notify_pages_req_t;
+DEFINE_XEN_GUEST_HANDLE(pagecache_notify_pages_req_t);
+
+
 #endif /* defined(__i386__) || defined(__x86_64__) */
 
 #endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */
diff --git a/xen/include/xen/domain.h b/xen/include/xen/domain.h
index 848db8a..5953205 100644
--- a/xen/include/xen/domain.h
+++ b/xen/include/xen/domain.h
@@ -91,6 +91,13 @@ void domctl_lock_release(void);
 int continue_hypercall_on_cpu(
     unsigned int cpu, long (*func)(void *data), void *data);
 
+int domain_pagecache_add(domid_t domid, uint64_t pfn, uint64_t sec);
+int domain_pagecache_del(domid_t domid, uint64_t pfn);
+int domain_pagecache_get_p2b_map(domid_t domid,
+                                 struct xen_domctl_get_p2b_map *get_p2b,
+                                 uint64_t nr_pages);
+int domain_pagecache_notify(pagecache_notify_pages_req_t arg);
+
 extern unsigned int xen_processor_pmbits;
 
 extern bool_t opt_dom0_vcpus_pin;
diff --git a/xen/include/xen/hash.h b/xen/include/xen/hash.h
index 0658c8b..9a38eeb 100644
--- a/xen/include/xen/hash.h
+++ b/xen/include/xen/hash.h
@@ -13,6 +13,7 @@
  * them can use shifts and additions instead of multiplications for
  * machines where multiplications are slow.
  */
+#define HASH_SIZE_ORDER 20
 #if BITS_PER_LONG == 32
 /* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
 #define GOLDEN_RATIO_PRIME 0x9e370001UL
@@ -23,6 +24,13 @@
 #error Define GOLDEN_RATIO_PRIME for your wordsize.
 #endif
 
+typedef struct _pair {
+    unsigned long key;
+    unsigned long val;
+    struct _pair *next;
+} pair;
+
+
 static inline unsigned long hash_long(unsigned long val, unsigned int bits)
 {
     unsigned long hash = val;
@@ -55,4 +63,11 @@ static inline unsigned long hash_ptr(void *ptr, unsigned int bits)
 {
     return hash_long((unsigned long)ptr, bits);
 }
+
+int hash_delete(pair **hash, unsigned long key);
+int hash_add(pair **hash, unsigned long key, unsigned long val);
+pair* hash_find(pair **hash, unsigned long key);
+int hash_free(pair **hash);
+unsigned long hash_count(pair **hash);
+unsigned long hash_collision(void);
 #endif /* _XEN_HASH_H */
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index 80c6f62..f538fa4 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -19,6 +19,7 @@
 #include <xen/perfc.h>
 #include <asm/atomic.h>
 #include <xen/wait.h>
+#include <xen/hash.h>
 #include <public/xen.h>
 #include <public/domctl.h>
 #include <public/sysctl.h>
@@ -330,6 +331,14 @@ struct domain
     struct domain   *next_in_list;
     struct domain   *next_in_hashbucket;
 
+    /* Pagecache. */
+    spinlock_t       pagecache_lock;
+    unsigned int     pagecache_enabled;
+    unsigned long    nr_pagecache;
+    unsigned long   *p2b_map;
+    pair           **b2p_hash;
+
+
     struct list_head rangesets;
     spinlock_t       rangesets_lock;
 
@@ -551,6 +560,9 @@ struct domain *domain_create(domid_t domid, unsigned int domcr_flags,
  /* DOMCRF_pvh: Create PV domain in HVM container. */
 #define _DOMCRF_pvh             5
 #define DOMCRF_pvh              (1U<<_DOMCRF_pvh)
+ /* DOMCRF_pagecache: Create pagecache domain. */
+#define _DOMCRF_pagecache             6
+#define DOMCRF_pagecache              (1U<<_DOMCRF_pagecache)
 
 /*
  * rcu_lock_domain_by_id() is more efficient than get_domain_by_id().
