[RFC PATCH v2 3/5] mm, shmem: Add shmem_locate function

From: Jerome Marchand
Date: Mon Sep 15 2014 - 10:25:46 EST


The shmem subsytem is kind of a black box: the generic mm code can't
always know where a specific page physically is. This patch adds the
shmem_locate() function to find out the physical location of shmem
pages (resident, in swap or swapcache). If the optional argument count
isn't NULL and the page is resident, it also returns the mapcount value
of this page.
This is intended to allow finer accounting of shmem/tmpfs pages.

Signed-off-by: Jerome Marchand <jmarchan@xxxxxxxxxx>
---
include/linux/shmem_fs.h | 6 ++++++
mm/shmem.c | 29 +++++++++++++++++++++++++++++
2 files changed, 35 insertions(+)

diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index 50777b5..99992cf 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -42,6 +42,11 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
return container_of(inode, struct shmem_inode_info, vfs_inode);
}

+#define SHMEM_NOTPRESENT 1 /* page is not present in memory */
+#define SHMEM_RESIDENT 2 /* page is resident in RAM */
+#define SHMEM_SWAPCACHE 3 /* page is in swap cache */
+#define SHMEM_SWAP 4 /* page is paged out */
+
/*
* Functions in mm/shmem.c called directly from elsewhere:
*/
@@ -59,6 +64,7 @@ extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
pgoff_t index, gfp_t gfp_mask);
extern void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end);
extern int shmem_unuse(swp_entry_t entry, struct page *page);
+extern int shmem_locate(struct vm_area_struct *vma, pgoff_t pgoff, int *count);

static inline struct page *shmem_read_mapping_page(
struct address_space *mapping, pgoff_t index)
diff --git a/mm/shmem.c b/mm/shmem.c
index d547345..134a422 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1350,6 +1350,35 @@ static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
return ret;
}

+int shmem_locate(struct vm_area_struct *vma, pgoff_t pgoff, int *count)
+{
+ struct address_space *mapping = file_inode(vma->vm_file)->i_mapping;
+ struct page *page;
+ swp_entry_t swap;
+ int ret;
+
+ page = find_get_entry(mapping, pgoff);
+ if (!page) /* Not yet initialised? */
+ return SHMEM_NOTPRESENT;
+
+ if (!radix_tree_exceptional_entry(page)) {
+ ret = SHMEM_RESIDENT;
+ if (count)
+ *count = page_mapcount(page);
+ goto out;
+ }
+
+ swap = radix_to_swp_entry(page);
+ page = find_get_page(swap_address_space(swap), swap.val);
+ if (!page)
+ return SHMEM_SWAP;
+ ret = SHMEM_SWAPCACHE;
+
+out:
+ page_cache_release(page);
+ return ret;
+}
+
#ifdef CONFIG_NUMA
static int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *mpol)
{
--
1.9.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/