[RFC v2 2/3] zsmalloc: mark its page "PageMigration"

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Most of idea is same with prev version that mark zsmalloc's page
"PageMigration" and introduce the function for the interfaces
zs_isolatepage, zs_isolatepage and zs_migratepage.

But I put data of zs from struct page to struct migration.

Signed-off-by: Hui Zhu <zhuhui@xxxxxxxxxx>
---
 include/linux/migrate.h |  13 ++
 mm/zsmalloc.c           | 605 ++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 578 insertions(+), 40 deletions(-)

diff --git a/include/linux/migrate.h b/include/linux/migrate.h
index 8b8caba..b8f9448 100644
--- a/include/linux/migrate.h
+++ b/include/linux/migrate.h
@@ -32,6 +32,19 @@ struct migration {
 	void (*put)(struct page *page);
 	int (*move)(struct page *page, struct page *newpage, int force,
 		       enum migrate_mode mode);
+#ifdef CONFIG_ZSMALLOC
+	struct {
+		/* For all zs_page.  */
+		struct list_head zs_lru;
+		struct page *zs_page;
+		void *zs_class;
+
+		/* For all zs_page first_page.  */
+		int zs_fg;
+		unsigned zs_inuse;
+		unsigned zs_objects;
+	};
+#endif
 };
 
 #define PAGE_MIGRATION_MAPCOUNT_VALUE (-512)
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 3134a37..5282a03 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -21,8 +21,11 @@
  *		starting in this page. For the first page, this is
  *		always 0, so we use this field (aka freelist) to point
  *		to the first free object in zspage.
- *	page->lru: links together all component pages (except the first page)
- *		of a zspage
+ *	zs_page_lru(page): links together all component pages (except the
+		first page) of a zspage
+ *	page->migration->zs_class (CONFIG_MIGRATION): class of the zspage
+ *	page->migration->zs_fg (CONFIG_MIGRATION): fullness group
+ *		of the zspage
  *
  *	For _first_ page only:
  *
@@ -33,11 +36,12 @@
  *	page->freelist: points to the first free object in zspage.
  *		Free objects are linked together using in-place
  *		metadata.
- *	page->objects: maximum number of objects we can store in this
+ *	zs_page_objects(page): maximum number of objects we can store in this
  *		zspage (class->zspage_order * PAGE_SIZE / class->size)
- *	page->lru: links together first pages of various zspages.
+ *	zs_page_lru(page): links together first pages of various zspages.
  *		Basically forming list of zspages in a fullness group.
- *	page->mapping: class index and fullness group of the zspage
+ *	page->mapping(no CONFIG_MIGRATION): class index and fullness group
+ *		of the zspage
  *
  * Usage of struct page flags:
  *	PG_private: identifies the first component page
@@ -64,6 +68,9 @@
 #include <linux/debugfs.h>
 #include <linux/zsmalloc.h>
 #include <linux/zpool.h>
+#include <linux/migrate.h>
+#include <linux/rwlock.h>
+#include <linux/mm.h>
 
 /*
  * This must be power of 2 and greater than of equal to sizeof(link_free).
@@ -214,6 +221,8 @@ struct size_class {
 
 	/* huge object: pages_per_zspage == 1 && maxobj_per_zspage == 1 */
 	bool huge;
+
+	atomic_t count;
 };
 
 /*
@@ -279,6 +288,12 @@ struct mapping_area {
 	bool huge;
 };
 
+#ifdef CONFIG_MIGRATION
+static rwlock_t zs_class_rwlock;
+static rwlock_t zs_tag_rwlock;
+struct kmem_cache *zs_migration_cachep;
+#endif
+
 static int create_handle_cache(struct zs_pool *pool)
 {
 	pool->handle_cachep = kmem_cache_create("zs_handle", ZS_HANDLE_SIZE,
@@ -294,7 +309,7 @@ static void destroy_handle_cache(struct zs_pool *pool)
 static unsigned long alloc_handle(struct zs_pool *pool)
 {
 	return (unsigned long)kmem_cache_alloc(pool->handle_cachep,
-		pool->flags & ~__GFP_HIGHMEM);
+		pool->flags & ~(__GFP_HIGHMEM | __GFP_MOVABLE));
 }
 
 static void free_handle(struct zs_pool *pool, unsigned long handle)
@@ -307,6 +322,41 @@ static void record_obj(unsigned long handle, unsigned long obj)
 	*(unsigned long *)handle = obj;
 }
 
+#ifdef CONFIG_MIGRATION
+#define zs_page_lru(page)	((page)->migration->zs_lru)
+#define zs_page_inuse(page)	((page)->migration->zs_inuse)
+#define zs_page_objects(page)	((page)->migration->zs_objects)
+
+static struct migration *alloc_migration(gfp_t flags)
+{
+	return (struct migration *)kmem_cache_alloc(zs_migration_cachep,
+		flags & ~(__GFP_HIGHMEM | __GFP_MOVABLE));
+}
+
+static void free_migration(struct migration *migration)
+{
+	kmem_cache_free(zs_migration_cachep, (void *)migration);
+}
+
+void zs_put_page(struct page *page)
+{
+	if (put_page_testzero(page)) {
+		if (page->migration) {
+			free_migration(page->migration);
+			page->migration = NULL;
+		}
+		free_hot_cold_page(page, 0);
+	}
+}
+
+#else
+#define zs_page_lru(page)	((page)->lru)
+#define zs_page_inuse(page)	((page)->inuse)
+#define zs_page_objects(page)	((page)->objects)
+
+#define zs_put_page(page)	put_page(page)
+#endif
+
 /* zpool driver */
 
 #ifdef CONFIG_ZPOOL
@@ -404,6 +454,7 @@ static int is_last_page(struct page *page)
 	return PagePrivate2(page);
 }
 
+#ifndef CONFIG_MIGRATION
 static void get_zspage_mapping(struct page *page, unsigned int *class_idx,
 				enum fullness_group *fullness)
 {
@@ -425,6 +476,7 @@ static void set_zspage_mapping(struct page *page, unsigned int class_idx,
 			(fullness & FULLNESS_MASK);
 	page->mapping = (struct address_space *)m;
 }
+#endif
 
 /*
  * zsmalloc divides the pool into various size classes where each
@@ -612,8 +664,8 @@ static enum fullness_group get_fullness_group(struct page *page)
 	enum fullness_group fg;
 	BUG_ON(!is_first_page(page));
 
-	inuse = page->inuse;
-	max_objects = page->objects;
+	inuse = zs_page_inuse(page);
+	max_objects = zs_page_objects(page);
 
 	if (inuse == 0)
 		fg = ZS_EMPTY;
@@ -656,8 +708,8 @@ static void insert_zspage(struct page *page, struct size_class *class,
 	 * We want to see more ZS_FULL pages and less almost
 	 * empty/full. Put pages with higher ->inuse first.
 	 */
-	list_add_tail(&page->lru, &(*head)->lru);
-	if (page->inuse >= (*head)->inuse)
+	list_add_tail(&zs_page_lru(page), &zs_page_lru(*head));
+	if (zs_page_inuse(page) >= zs_page_inuse(*head))
 		*head = page;
 }
 
@@ -677,13 +729,23 @@ static void remove_zspage(struct page *page, struct size_class *class,
 
 	head = &class->fullness_list[fullness];
 	BUG_ON(!*head);
-	if (list_empty(&(*head)->lru))
+	if (list_empty(&zs_page_lru(*head)))
 		*head = NULL;
-	else if (*head == page)
-		*head = (struct page *)list_entry((*head)->lru.next,
+	else if (*head == page) {
+#ifdef CONFIG_MIGRATION
+		struct migration *migration;
+
+		migration = (struct migration *)
+				list_entry(zs_page_lru(*head).next,
+					   struct migration, zs_lru);
+		*head = migration->zs_page;
+#else
+		*head = (struct page *)list_entry(zs_page_lru(*head).next,
 					struct page, lru);
+#endif
+	}
 
-	list_del_init(&page->lru);
+	list_del_init(&zs_page_lru(page));
 	zs_stat_dec(class, fullness == ZS_ALMOST_EMPTY ?
 			CLASS_ALMOST_EMPTY : CLASS_ALMOST_FULL, 1);
 }
@@ -700,19 +762,29 @@ static void remove_zspage(struct page *page, struct size_class *class,
 static enum fullness_group fix_fullness_group(struct size_class *class,
 						struct page *page)
 {
+#ifndef CONFIG_MIGRATION
 	int class_idx;
+#endif
 	enum fullness_group currfg, newfg;
 
 	BUG_ON(!is_first_page(page));
 
+#ifdef CONFIG_MIGRATION
+	currfg = page->migration->zs_fg;
+#else
 	get_zspage_mapping(page, &class_idx, &currfg);
+#endif
 	newfg = get_fullness_group(page);
 	if (newfg == currfg)
 		goto out;
 
 	remove_zspage(page, class, currfg);
 	insert_zspage(page, class, newfg);
+#ifdef CONFIG_MIGRATION
+	page->migration->zs_fg = newfg;
+#else
 	set_zspage_mapping(page, class_idx, newfg);
+#endif
 
 out:
 	return newfg;
@@ -775,8 +847,18 @@ static struct page *get_next_page(struct page *page)
 		next = NULL;
 	else if (is_first_page(page))
 		next = (struct page *)page_private(page);
-	else
-		next = list_entry(page->lru.next, struct page, lru);
+	else {
+#ifdef CONFIG_MIGRATION
+		struct migration *migration;
+
+		migration = (struct migration *)
+				list_entry(zs_page_lru(page).next,
+					   struct migration, zs_lru);
+		next = migration->zs_page;
+#else
+		next = list_entry(zs_page_lru(page).next, struct page, lru);
+#endif
+	}
 
 	return next;
 }
@@ -809,9 +891,14 @@ static void *location_to_obj(struct page *page, unsigned long obj_idx)
 static void obj_to_location(unsigned long obj, struct page **page,
 				unsigned long *obj_idx)
 {
-	obj >>= OBJ_TAG_BITS;
-	*page = pfn_to_page(obj >> OBJ_INDEX_BITS);
-	*obj_idx = (obj & OBJ_INDEX_MASK);
+	if (obj == 0) {
+		*page = NULL;
+		*obj_idx = 0;
+	} else {
+		obj >>= OBJ_TAG_BITS;
+		*page = pfn_to_page(obj >> OBJ_INDEX_BITS);
+		*obj_idx = (obj & OBJ_INDEX_MASK);
+	}
 }
 
 static unsigned long handle_to_obj(unsigned long handle)
@@ -859,39 +946,59 @@ static void unpin_tag(unsigned long handle)
 	clear_bit_unlock(HANDLE_PIN_BIT, ptr);
 }
 
+
 static void reset_page(struct page *page)
 {
+#ifdef CONFIG_MIGRATION
+	/* Lock the page to protect the atomic access of page->migration.  */
+	lock_page(page);
+#endif
 	clear_bit(PG_private, &page->flags);
 	clear_bit(PG_private_2, &page->flags);
 	set_page_private(page, 0);
+#ifndef CONFIG_MIGRATION
 	page->mapping = NULL;
+#endif
 	page->freelist = NULL;
 	page_mapcount_reset(page);
+#ifdef CONFIG_MIGRATION
+	unlock_page(page);
+#endif
 }
 
 static void free_zspage(struct page *first_page)
 {
-	struct page *nextp, *tmp, *head_extra;
+#ifdef CONFIG_MIGRATION
+	struct migration *tmp, *nextm;
+#else
+	struct page *tmp;
+#endif
+	struct page *nextp, *head_extra;
 
 	BUG_ON(!is_first_page(first_page));
-	BUG_ON(first_page->inuse);
+	BUG_ON(zs_page_inuse(first_page));
 
 	head_extra = (struct page *)page_private(first_page);
 
 	reset_page(first_page);
-	__free_page(first_page);
+	zs_put_page(first_page);
 
 	/* zspage with only 1 system page */
 	if (!head_extra)
 		return;
-
-	list_for_each_entry_safe(nextp, tmp, &head_extra->lru, lru) {
-		list_del(&nextp->lru);
+#ifdef CONFIG_MIGRATION
+	list_for_each_entry_safe(nextm, tmp, &zs_page_lru(head_extra),
+				 zs_lru) {
+		nextp = nextm->zs_page;
+#else
+	list_for_each_entry_safe(nextp, tmp, &zs_page_lru(head_extra), lru) {
+#endif
+		list_del(&zs_page_lru(nextp));
 		reset_page(nextp);
-		__free_page(nextp);
+		zs_put_page(nextp);
 	}
 	reset_page(head_extra);
-	__free_page(head_extra);
+	zs_put_page(head_extra);
 }
 
 /* Initialize a newly allocated zspage */
@@ -937,6 +1044,311 @@ static void init_zspage(struct page *first_page, struct size_class *class)
 	}
 }
 
+#ifdef CONFIG_MIGRATION
+static void
+get_class(struct size_class *class)
+{
+	atomic_inc(&class->count);
+}
+
+static void
+put_class(struct size_class *class)
+{
+	if (atomic_dec_and_test(&class->count))
+		kfree(class);
+}
+
+static int zs_isolatepage(struct page *page)
+{
+	int ret = -EBUSY;
+
+	if (!get_page_unless_zero(page))
+		return -EBUSY;
+
+	read_lock(&zs_class_rwlock);
+	lock_page(page);
+
+	if (page_count(page) != 2)
+		goto put_out;
+	if (!page->migration)
+		goto put_out;
+	get_class(page->migration->zs_class);
+
+	ret = 0;
+out:
+	unlock_page(page);
+	read_unlock(&zs_class_rwlock);
+	return ret;
+
+put_out:
+	zs_put_page(page);
+	goto out;
+}
+
+static void zs_putpage(struct page *page)
+{
+	put_class(page->migration->zs_class);
+	zs_put_page(page);
+}
+
+struct zspage_loop_struct {
+	struct size_class *class;
+	struct page *page;
+	struct page *newpage;
+	void *newaddr;
+
+	struct page *cur_page;
+	void *cur_addr;
+
+	unsigned long offset;
+	unsigned int idx;
+};
+
+static void
+zspage_migratepage_obj_callback(unsigned long head,
+				struct zspage_loop_struct *zls)
+{
+	BUG_ON(zls == NULL);
+
+	if (head & OBJ_ALLOCATED_TAG) {
+		unsigned long copy_size;
+		unsigned long newobj;
+		unsigned long handle;
+
+		/* Migratepage allocated just need handle the zls->page.  */
+		if (zls->cur_page != zls->page)
+			return;
+
+		copy_size = zls->class->size;
+
+		if (zls->offset + copy_size > PAGE_SIZE)
+			copy_size = PAGE_SIZE - zls->offset;
+
+		newobj = (unsigned long)location_to_obj(zls->newpage, zls->idx);
+
+		/* Remove OBJ_ALLOCATED_TAG will get the real handle.  */
+		handle = head & ~OBJ_ALLOCATED_TAG;
+		record_obj(handle, newobj);
+
+		/* Copy allocated chunk to allocated chunk.
+		 * Handle is included in it.
+		 */
+		memcpy(zls->newaddr + zls->offset,
+		       zls->cur_addr + zls->offset, copy_size);
+	} else {
+		struct link_free *link;
+		unsigned long obj;
+		unsigned long tmp_idx;
+		struct page *tmp_page;
+
+		link = (struct link_free *)(zls->cur_addr + zls->offset);
+		obj = (unsigned long)link->next;
+
+		obj_to_location(obj, &tmp_page, &tmp_idx);
+		if (tmp_page == zls->page) {
+			/* Update new obj with newpage to current link.  */
+			obj = (unsigned long)location_to_obj(zls->newpage,
+							     tmp_idx);
+			link->next = (void *)obj;
+		}
+
+		if (zls->cur_page == zls->page) {
+			/* Update obj to link of newaddr.  */
+			link = (struct link_free *)(zls->newaddr + zls->offset);
+			link->next = (void *)obj;
+		}
+	}
+}
+
+static void
+zspage_loop_1(struct size_class *class, struct page *cur_page,
+	      struct zspage_loop_struct *zls,
+	      void (*callback)(unsigned long head,
+			       struct zspage_loop_struct *zls))
+{
+	void *addr;
+	unsigned long m_offset = 0;
+	unsigned int obj_idx = 0;
+
+	if (!is_first_page(cur_page))
+		m_offset = cur_page->index;
+
+	addr = kmap_atomic(cur_page);
+
+	if (zls) {
+		zls->cur_page = cur_page;
+		zls->cur_addr = addr;
+	}
+
+	while (m_offset < PAGE_SIZE) {
+		unsigned long head = obj_to_head(class, cur_page,
+						 addr + m_offset);
+
+		if (zls) {
+			zls->offset = m_offset;
+			zls->idx = obj_idx;
+		}
+
+		callback(head, zls);
+
+		m_offset += class->size;
+		obj_idx++;
+	}
+
+	kunmap_atomic(addr);
+}
+
+static void
+zspage_loop(struct size_class *class, struct page *first_page,
+	    struct page *page, struct page *newpage,
+	    void (*callback)(unsigned long head,
+			     struct zspage_loop_struct *zls))
+{
+	struct page *cur_page;
+	struct zspage_loop_struct zl;
+	struct zspage_loop_struct *zls = NULL;
+
+	BUG_ON(!is_first_page(first_page));
+
+	if (page) {
+		zls = &zl;
+		zls->class = class;
+		zls->page = page;
+		zls->newpage = newpage;
+		zls->newaddr = kmap_atomic(zls->newpage);
+	}
+
+	cur_page = first_page;
+	while (cur_page) {
+		zspage_loop_1(class, cur_page, zls, callback);
+		cur_page = get_next_page(cur_page);
+	}
+
+	if (zls)
+		kunmap_atomic(zls->newaddr);
+}
+
+static int
+zs_movepage(struct page *page, struct page *newpage, int force,
+	    enum migrate_mode mode)
+{
+	int ret = -EAGAIN;
+	struct size_class *class = page->migration->zs_class;
+	struct page *first_page;
+
+	write_lock(&zs_tag_rwlock);
+	spin_lock(&class->lock);
+
+	if (page_count(page) <= 1)
+		goto out;
+
+	first_page = get_first_page(page);
+
+	INIT_LIST_HEAD(&newpage->lru);
+	if (page == first_page) {	/* first page */
+		struct page **head;
+
+		newpage->freelist = page->freelist;
+		SetPagePrivate(newpage);
+
+		if (class->huge) {
+			unsigned long handle = page_private(page);
+			unsigned long obj
+				= (unsigned long)location_to_obj(newpage, 0);
+
+			if (handle != 0) {
+				void *addr, *newaddr;
+
+				/* The page is allocated.  */
+				handle = handle & ~OBJ_ALLOCATED_TAG;
+				record_obj(handle, obj);
+				addr = kmap_atomic(page);
+				newaddr = kmap_atomic(newpage);
+				memcpy(newaddr, addr, class->size);
+				kunmap_atomic(newaddr);
+				kunmap_atomic(addr);
+			} else
+				newpage->freelist = (void *)obj;
+			set_page_private(newpage, handle);
+		} else {
+			struct page *head_extra
+				= (struct page *)page_private(page);
+
+			if (head_extra) {
+				struct migration *nextm;
+
+				head_extra->first_page = newpage;
+				list_for_each_entry(nextm,
+						    &zs_page_lru(head_extra),
+						    zs_lru)
+					nextm->zs_page->first_page = newpage;
+			}
+			set_page_private(newpage, (unsigned long)head_extra);
+		}
+
+		head = &class->fullness_list[first_page->migration->zs_fg];
+		BUG_ON(!*head);
+		if (*head == page)
+			*head = newpage;
+	} else {
+		void *addr, *newaddr;
+
+		newpage->first_page = page->first_page;
+		newpage->index = page->index;
+
+		if (page->index > 0) {
+			addr = kmap_atomic(page);
+			newaddr = kmap_atomic(newpage);
+			memcpy(newaddr, addr, page->index);
+			kunmap_atomic(newaddr);
+			kunmap_atomic(addr);
+		}
+	}
+	if (is_last_page(page))	/* last page */
+		SetPagePrivate2(newpage);
+
+	if (!class->huge) {
+		zspage_loop(class, first_page, page, newpage,
+			    zspage_migratepage_obj_callback);
+	}
+
+	/* Add newpage to zspage.  */
+	if (first_page == page)
+		first_page = newpage;
+	else {
+		if ((struct page *)page_private(first_page) == page)
+			set_page_private(first_page, (unsigned long)newpage);
+	}
+	newpage->migration = page->migration;
+	newpage->migration->zs_page = newpage;
+
+	if (!class->huge) {
+		struct page *tmp_page;
+		unsigned long tmp_idx;
+
+		/* Update first_page->freelist if need.  */
+		obj_to_location((unsigned long)first_page->freelist,
+				&tmp_page, &tmp_idx);
+		if (tmp_page == page)
+			first_page->freelist = location_to_obj(newpage,
+							       tmp_idx);
+	}
+
+	get_page(newpage);
+	__SetPageMigration(newpage);
+
+	page->migration = NULL;
+	reset_page(page);
+	zs_put_page(page);
+
+	ret = MIGRATEPAGE_SUCCESS;
+out:
+	spin_unlock(&class->lock);
+	write_unlock(&zs_tag_rwlock);
+	return ret;
+}
+#endif
+
 /*
  * Allocate a zspage for the given size class
  */
@@ -948,11 +1360,12 @@ static struct page *alloc_zspage(struct size_class *class, gfp_t flags)
 	/*
 	 * Allocate individual pages and link them together as:
 	 * 1. first page->private = first sub-page
-	 * 2. all sub-pages are linked together using page->lru
+	 * 2. all sub-pages are linked together using zs_page_lru
 	 * 3. each sub-page is linked to the first page using page->first_page
 	 *
 	 * For each size class, First/Head pages are linked together using
-	 * page->lru. Also, we set PG_private to identify the first page
+	 * zs_page_lru.
+	 * Also, we set PG_private to identify the first page
 	 * (i.e. no other sub-page has this flag set) and PG_private_2 to
 	 * identify the last page.
 	 */
@@ -963,20 +1376,35 @@ static struct page *alloc_zspage(struct size_class *class, gfp_t flags)
 		page = alloc_page(flags);
 		if (!page)
 			goto cleanup;
+#ifdef CONFIG_MIGRATION
+		page->migration = alloc_migration(flags);
+		if (!page->migration) {
+			__free_page(page);
+			goto cleanup;
+		}
+#endif
 
 		INIT_LIST_HEAD(&page->lru);
+#ifdef CONFIG_MIGRATION
+		page->migration->isolate = zs_isolatepage;
+		page->migration->put = zs_putpage;
+		page->migration->move = zs_movepage;
+		INIT_LIST_HEAD(&page->migration->zs_lru);
+		page->migration->zs_page = page;
+		page->migration->zs_class = class;
+#endif
 		if (i == 0) {	/* first page */
 			SetPagePrivate(page);
 			set_page_private(page, 0);
 			first_page = page;
-			first_page->inuse = 0;
+			zs_page_inuse(first_page) = 0;
 		}
 		if (i == 1)
 			set_page_private(first_page, (unsigned long)page);
 		if (i >= 1)
 			page->first_page = first_page;
 		if (i >= 2)
-			list_add(&page->lru, &prev_page->lru);
+			list_add(&zs_page_lru(page), &zs_page_lru(prev_page));
 		if (i == class->pages_per_zspage - 1)	/* last page */
 			SetPagePrivate2(page);
 		prev_page = page;
@@ -986,7 +1414,8 @@ static struct page *alloc_zspage(struct size_class *class, gfp_t flags)
 
 	first_page->freelist = location_to_obj(first_page, 0);
 	/* Maximum number of objects we can store in this zspage */
-	first_page->objects = class->pages_per_zspage * PAGE_SIZE / class->size;
+	zs_page_objects(first_page)
+		= class->pages_per_zspage * PAGE_SIZE / class->size;
 
 	error = 0; /* Success */
 
@@ -1221,7 +1650,7 @@ static bool zspage_full(struct page *page)
 {
 	BUG_ON(!is_first_page(page));
 
-	return page->inuse == page->objects;
+	return zs_page_inuse(page) == zs_page_objects(page);
 }
 
 unsigned long zs_get_total_pages(struct zs_pool *pool)
@@ -1250,12 +1679,15 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle,
 	struct page *page;
 	unsigned long obj, obj_idx, off;
 
+#ifndef CONFIG_MIGRATION
 	unsigned int class_idx;
+#endif
 	enum fullness_group fg;
 	struct size_class *class;
 	struct mapping_area *area;
 	struct page *pages[2];
 	void *ret;
+	struct page *first_page;
 
 	BUG_ON(!handle);
 
@@ -1267,12 +1699,22 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle,
 	BUG_ON(in_interrupt());
 
 	/* From now on, migration cannot move the object */
+#ifdef CONFIG_MIGRATION
+	read_lock(&zs_tag_rwlock);
+#endif
 	pin_tag(handle);
 
 	obj = handle_to_obj(handle);
 	obj_to_location(obj, &page, &obj_idx);
-	get_zspage_mapping(get_first_page(page), &class_idx, &fg);
+	first_page = get_first_page(page);
+#ifdef CONFIG_MIGRATION
+	fg = first_page->migration->zs_fg;
+	class = first_page->migration->zs_class;
+#else
+	get_zspage_mapping(first_page, &class_idx, &fg);
 	class = pool->size_class[class_idx];
+#endif
+
 	off = obj_idx_to_offset(page, obj_idx, class->size);
 
 	area = &get_cpu_var(zs_map_area);
@@ -1302,18 +1744,26 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
 {
 	struct page *page;
 	unsigned long obj, obj_idx, off;
-
+#ifndef CONFIG_MIGRATION
 	unsigned int class_idx;
+#endif
 	enum fullness_group fg;
 	struct size_class *class;
 	struct mapping_area *area;
+	struct page *first_page;
 
 	BUG_ON(!handle);
 
 	obj = handle_to_obj(handle);
 	obj_to_location(obj, &page, &obj_idx);
-	get_zspage_mapping(get_first_page(page), &class_idx, &fg);
+	first_page = get_first_page(page);
+#ifdef CONFIG_MIGRATION
+	fg = first_page->migration->zs_fg;
+	class = first_page->migration->zs_class;
+#else
+	get_zspage_mapping(first_page, &class_idx, &fg);
 	class = pool->size_class[class_idx];
+#endif
 	off = obj_idx_to_offset(page, obj_idx, class->size);
 
 	area = this_cpu_ptr(&zs_map_area);
@@ -1330,6 +1780,9 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
 	}
 	put_cpu_var(zs_map_area);
 	unpin_tag(handle);
+#ifdef CONFIG_MIGRATION
+	read_unlock(&zs_tag_rwlock);
+#endif
 }
 EXPORT_SYMBOL_GPL(zs_unmap_object);
 
@@ -1350,6 +1803,8 @@ static unsigned long obj_malloc(struct page *first_page,
 
 	vaddr = kmap_atomic(m_page);
 	link = (struct link_free *)vaddr + m_offset / sizeof(*link);
+BUG_ON(first_page == NULL);
+BUG_ON(link == NULL);
 	first_page->freelist = link->next;
 	if (!class->huge)
 		/* record handle in the header of allocated chunk */
@@ -1358,13 +1813,31 @@ static unsigned long obj_malloc(struct page *first_page,
 		/* record handle in first_page->private */
 		set_page_private(first_page, handle);
 	kunmap_atomic(vaddr);
-	first_page->inuse++;
+	zs_page_inuse(first_page)++;
 	zs_stat_inc(class, OBJ_USED, 1);
 
 	return obj;
 }
 
 
+#ifdef CONFIG_MIGRATION
+static void set_zspage_migration(struct size_class *class, struct page *page)
+{
+	struct page *head_extra = (struct page *)page_private(page);
+
+	BUG_ON(!is_first_page(page));
+
+	__SetPageMigration(page);
+	if (!class->huge && head_extra) {
+		struct migration *nextm;
+
+		__SetPageMigration(head_extra);
+		list_for_each_entry(nextm, &zs_page_lru(head_extra), zs_lru)
+			__SetPageMigration(nextm->zs_page);
+	}
+}
+#endif
+
 /**
  * zs_malloc - Allocate block of given size from pool.
  * @pool: pool to allocate from
@@ -1401,16 +1874,21 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
 			free_handle(pool, handle);
 			return 0;
 		}
-
+#ifdef CONFIG_MIGRATION
+		first_page->migration->zs_fg = ZS_EMPTY;
+#else
 		set_zspage_mapping(first_page, class->index, ZS_EMPTY);
+#endif
 		atomic_long_add(class->pages_per_zspage,
 					&pool->pages_allocated);
 
 		spin_lock(&class->lock);
+#ifdef CONFIG_MIGRATION
+		set_zspage_migration(class, first_page);
+#endif
 		zs_stat_inc(class, OBJ_ALLOCATED, get_maxobj_per_zspage(
 				class->size, class->pages_per_zspage));
 	}
-
 	obj = obj_malloc(first_page, class, handle);
 	/* Now move the zspage to another fullness group, if required */
 	fix_fullness_group(class, first_page);
@@ -1446,7 +1924,7 @@ static void obj_free(struct zs_pool *pool, struct size_class *class,
 		set_page_private(first_page, 0);
 	kunmap_atomic(vaddr);
 	first_page->freelist = (void *)obj;
-	first_page->inuse--;
+	zs_page_inuse(first_page)--;
 	zs_stat_dec(class, OBJ_USED, 1);
 }
 
@@ -1454,20 +1932,30 @@ void zs_free(struct zs_pool *pool, unsigned long handle)
 {
 	struct page *first_page, *f_page;
 	unsigned long obj, f_objidx;
+#ifndef CONFIG_MIGRATION
 	int class_idx;
+#endif
 	struct size_class *class;
 	enum fullness_group fullness;
 
 	if (unlikely(!handle))
 		return;
 
+#ifdef CONFIG_MIGRATION
+	read_lock(&zs_tag_rwlock);
+#endif
 	pin_tag(handle);
 	obj = handle_to_obj(handle);
 	obj_to_location(obj, &f_page, &f_objidx);
 	first_page = get_first_page(f_page);
 
+#ifdef CONFIG_MIGRATION
+	fullness = first_page->migration->zs_fg;
+	class = first_page->migration->zs_class;
+#else
 	get_zspage_mapping(first_page, &class_idx, &fullness);
 	class = pool->size_class[class_idx];
+#endif
 
 	spin_lock(&class->lock);
 	obj_free(pool, class, obj);
@@ -1481,6 +1969,9 @@ void zs_free(struct zs_pool *pool, unsigned long handle)
 	}
 	spin_unlock(&class->lock);
 	unpin_tag(handle);
+#ifdef CONFIG_MIGRATION
+	read_unlock(&zs_tag_rwlock);
+#endif
 
 	free_handle(pool, handle);
 }
@@ -1672,7 +2163,12 @@ static enum fullness_group putback_zspage(struct zs_pool *pool,
 
 	fullness = get_fullness_group(first_page);
 	insert_zspage(first_page, class, fullness);
+#ifdef CONFIG_MIGRATION
+	first_page->migration->zs_class = class;
+	first_page->migration->zs_fg = fullness;
+#else
 	set_zspage_mapping(first_page, class->index, fullness);
+#endif
 
 	if (fullness == ZS_EMPTY) {
 		zs_stat_dec(class, OBJ_ALLOCATED, get_maxobj_per_zspage(
@@ -1928,6 +2424,10 @@ struct zs_pool *zs_create_pool(char *name, gfp_t flags)
 			get_maxobj_per_zspage(size, pages_per_zspage) == 1)
 			class->huge = true;
 		spin_lock_init(&class->lock);
+		atomic_set(&class->count, 0);
+#ifdef CONFIG_MIGRATION
+		get_class(class);
+#endif
 		pool->size_class[i] = class;
 
 		prev_class = class;
@@ -1975,7 +2475,13 @@ void zs_destroy_pool(struct zs_pool *pool)
 					class->size, fg);
 			}
 		}
+#ifdef CONFIG_MIGRATION
+		write_lock(&zs_class_rwlock);
+		put_class(class);
+		write_unlock(&zs_class_rwlock);
+#else
 		kfree(class);
+#endif
 	}
 
 	destroy_handle_cache(pool);
@@ -1992,6 +2498,11 @@ static int __init zs_init(void)
 	if (ret)
 		goto notifier_fail;
 
+#ifdef CONFIG_MIGRATION
+	rwlock_init(&zs_class_rwlock);
+	rwlock_init(&zs_tag_rwlock);
+#endif
+
 	init_zs_size_classes();
 
 #ifdef CONFIG_ZPOOL
@@ -2003,6 +2514,17 @@ static int __init zs_init(void)
 		pr_err("zs stat initialization failed\n");
 		goto stat_fail;
 	}
+
+#ifdef CONFIG_MIGRATION
+	zs_migration_cachep = kmem_cache_create("zs_migration",
+						sizeof(struct migration),
+						0, 0, NULL);
+	if (!zs_migration_cachep) {
+		pr_err("zs migration initialization failed\n");
+		goto stat_fail;
+	}
+#endif
+
 	return 0;
 
 stat_fail:
@@ -2017,6 +2539,9 @@ notifier_fail:
 
 static void __exit zs_exit(void)
 {
+#ifdef CONFIG_MIGRATION
+	kmem_cache_destroy(zs_migration_cachep);
+#endif
 #ifdef CONFIG_ZPOOL
 	zpool_unregister_driver(&zs_zpool_driver);
 #endif
-- 
1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@xxxxxxxxx.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]