Commit a3fe778c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'stable/frontswap.v16-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/konrad/mm

Pull frontswap feature from Konrad Rzeszutek Wilk:
 "Frontswap provides a "transcendent memory" interface for swap pages.
  In some environments, dramatic performance savings may be obtained
  because swapped pages are saved in RAM (or a RAM-like device) instead
  of a swap disk.  This tag provides the basic infrastructure along with
  some changes to the existing backends."

Fix up trivial conflict in mm/Makefile due to removal of swap token code
changing a line next to the new frontswap entry.

This pull request came in before the merge window even opened, it got
delayed to after the merge window by me just wanting to make sure it had
actual users.  Apparently IBM is using this on their embedded side, and
Jan Beulich says that it's already made available for SLES and OpenSUSE
users.

Also acked by Rik van Riel, and Konrad points to other people liking it
too.  So in it goes.

By Dan Magenheimer (4) and Konrad Rzeszutek Wilk (2)
via Konrad Rzeszutek Wilk
* tag 'stable/frontswap.v16-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/konrad/mm:
  frontswap: s/put_page/store/g s/get_page/load
  MAINTAINER: Add myself for the frontswap API
  mm: frontswap: config and doc files
  mm: frontswap: core frontswap functionality
  mm: frontswap: core swap subsystem hooks and headers
  mm: frontswap: add frontswap header file
parents 9171c670 165c8aed
This diff is collapsed.
...@@ -2930,6 +2930,13 @@ F: Documentation/power/freezing-of-tasks.txt ...@@ -2930,6 +2930,13 @@ F: Documentation/power/freezing-of-tasks.txt
F: include/linux/freezer.h F: include/linux/freezer.h
F: kernel/freezer.c F: kernel/freezer.c
FRONTSWAP API
M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
L: linux-kernel@vger.kernel.org
S: Maintained
F: mm/frontswap.c
F: include/linux/frontswap.h
FS-CACHE: LOCAL CACHING FOR NETWORK FILESYSTEMS FS-CACHE: LOCAL CACHING FOR NETWORK FILESYSTEMS
M: David Howells <dhowells@redhat.com> M: David Howells <dhowells@redhat.com>
L: linux-cachefs@redhat.com L: linux-cachefs@redhat.com
......
...@@ -3002,7 +3002,7 @@ static inline struct tmem_oid oswiz(unsigned type, u32 ind) ...@@ -3002,7 +3002,7 @@ static inline struct tmem_oid oswiz(unsigned type, u32 ind)
return oid; return oid;
} }
static int zcache_frontswap_put_page(unsigned type, pgoff_t offset, static int zcache_frontswap_store(unsigned type, pgoff_t offset,
struct page *page) struct page *page)
{ {
u64 ind64 = (u64)offset; u64 ind64 = (u64)offset;
...@@ -3025,7 +3025,7 @@ static int zcache_frontswap_put_page(unsigned type, pgoff_t offset, ...@@ -3025,7 +3025,7 @@ static int zcache_frontswap_put_page(unsigned type, pgoff_t offset,
/* returns 0 if the page was successfully gotten from frontswap, -1 if /* returns 0 if the page was successfully gotten from frontswap, -1 if
* was not present (should never happen!) */ * was not present (should never happen!) */
static int zcache_frontswap_get_page(unsigned type, pgoff_t offset, static int zcache_frontswap_load(unsigned type, pgoff_t offset,
struct page *page) struct page *page)
{ {
u64 ind64 = (u64)offset; u64 ind64 = (u64)offset;
...@@ -3080,8 +3080,8 @@ static void zcache_frontswap_init(unsigned ignored) ...@@ -3080,8 +3080,8 @@ static void zcache_frontswap_init(unsigned ignored)
} }
static struct frontswap_ops zcache_frontswap_ops = { static struct frontswap_ops zcache_frontswap_ops = {
.put_page = zcache_frontswap_put_page, .store = zcache_frontswap_store,
.get_page = zcache_frontswap_get_page, .load = zcache_frontswap_load,
.invalidate_page = zcache_frontswap_flush_page, .invalidate_page = zcache_frontswap_flush_page,
.invalidate_area = zcache_frontswap_flush_area, .invalidate_area = zcache_frontswap_flush_area,
.init = zcache_frontswap_init .init = zcache_frontswap_init
......
...@@ -1835,7 +1835,7 @@ static int zcache_frontswap_poolid = -1; ...@@ -1835,7 +1835,7 @@ static int zcache_frontswap_poolid = -1;
* Swizzling increases objects per swaptype, increasing tmem concurrency * Swizzling increases objects per swaptype, increasing tmem concurrency
* for heavy swaploads. Later, larger nr_cpus -> larger SWIZ_BITS * for heavy swaploads. Later, larger nr_cpus -> larger SWIZ_BITS
* Setting SWIZ_BITS to 27 basically reconstructs the swap entry from * Setting SWIZ_BITS to 27 basically reconstructs the swap entry from
* frontswap_get_page(), but has side-effects. Hence using 8. * frontswap_load(), but has side-effects. Hence using 8.
*/ */
#define SWIZ_BITS 8 #define SWIZ_BITS 8
#define SWIZ_MASK ((1 << SWIZ_BITS) - 1) #define SWIZ_MASK ((1 << SWIZ_BITS) - 1)
...@@ -1849,7 +1849,7 @@ static inline struct tmem_oid oswiz(unsigned type, u32 ind) ...@@ -1849,7 +1849,7 @@ static inline struct tmem_oid oswiz(unsigned type, u32 ind)
return oid; return oid;
} }
static int zcache_frontswap_put_page(unsigned type, pgoff_t offset, static int zcache_frontswap_store(unsigned type, pgoff_t offset,
struct page *page) struct page *page)
{ {
u64 ind64 = (u64)offset; u64 ind64 = (u64)offset;
...@@ -1870,7 +1870,7 @@ static int zcache_frontswap_put_page(unsigned type, pgoff_t offset, ...@@ -1870,7 +1870,7 @@ static int zcache_frontswap_put_page(unsigned type, pgoff_t offset,
/* returns 0 if the page was successfully gotten from frontswap, -1 if /* returns 0 if the page was successfully gotten from frontswap, -1 if
* was not present (should never happen!) */ * was not present (should never happen!) */
static int zcache_frontswap_get_page(unsigned type, pgoff_t offset, static int zcache_frontswap_load(unsigned type, pgoff_t offset,
struct page *page) struct page *page)
{ {
u64 ind64 = (u64)offset; u64 ind64 = (u64)offset;
...@@ -1919,8 +1919,8 @@ static void zcache_frontswap_init(unsigned ignored) ...@@ -1919,8 +1919,8 @@ static void zcache_frontswap_init(unsigned ignored)
} }
static struct frontswap_ops zcache_frontswap_ops = { static struct frontswap_ops zcache_frontswap_ops = {
.put_page = zcache_frontswap_put_page, .store = zcache_frontswap_store,
.get_page = zcache_frontswap_get_page, .load = zcache_frontswap_load,
.invalidate_page = zcache_frontswap_flush_page, .invalidate_page = zcache_frontswap_flush_page,
.invalidate_area = zcache_frontswap_flush_area, .invalidate_area = zcache_frontswap_flush_area,
.init = zcache_frontswap_init .init = zcache_frontswap_init
......
...@@ -269,7 +269,7 @@ static inline struct tmem_oid oswiz(unsigned type, u32 ind) ...@@ -269,7 +269,7 @@ static inline struct tmem_oid oswiz(unsigned type, u32 ind)
} }
/* returns 0 if the page was successfully put into frontswap, -1 if not */ /* returns 0 if the page was successfully put into frontswap, -1 if not */
static int tmem_frontswap_put_page(unsigned type, pgoff_t offset, static int tmem_frontswap_store(unsigned type, pgoff_t offset,
struct page *page) struct page *page)
{ {
u64 ind64 = (u64)offset; u64 ind64 = (u64)offset;
...@@ -295,7 +295,7 @@ static int tmem_frontswap_put_page(unsigned type, pgoff_t offset, ...@@ -295,7 +295,7 @@ static int tmem_frontswap_put_page(unsigned type, pgoff_t offset,
* returns 0 if the page was successfully gotten from frontswap, -1 if * returns 0 if the page was successfully gotten from frontswap, -1 if
* was not present (should never happen!) * was not present (should never happen!)
*/ */
static int tmem_frontswap_get_page(unsigned type, pgoff_t offset, static int tmem_frontswap_load(unsigned type, pgoff_t offset,
struct page *page) struct page *page)
{ {
u64 ind64 = (u64)offset; u64 ind64 = (u64)offset;
...@@ -362,8 +362,8 @@ static int __init no_frontswap(char *s) ...@@ -362,8 +362,8 @@ static int __init no_frontswap(char *s)
__setup("nofrontswap", no_frontswap); __setup("nofrontswap", no_frontswap);
static struct frontswap_ops __initdata tmem_frontswap_ops = { static struct frontswap_ops __initdata tmem_frontswap_ops = {
.put_page = tmem_frontswap_put_page, .store = tmem_frontswap_store,
.get_page = tmem_frontswap_get_page, .load = tmem_frontswap_load,
.invalidate_page = tmem_frontswap_flush_page, .invalidate_page = tmem_frontswap_flush_page,
.invalidate_area = tmem_frontswap_flush_area, .invalidate_area = tmem_frontswap_flush_area,
.init = tmem_frontswap_init .init = tmem_frontswap_init
......
#ifndef _LINUX_FRONTSWAP_H
#define _LINUX_FRONTSWAP_H
#include <linux/swap.h>
#include <linux/mm.h>
#include <linux/bitops.h>
struct frontswap_ops {
void (*init)(unsigned);
int (*store)(unsigned, pgoff_t, struct page *);
int (*load)(unsigned, pgoff_t, struct page *);
void (*invalidate_page)(unsigned, pgoff_t);
void (*invalidate_area)(unsigned);
};
extern bool frontswap_enabled;
extern struct frontswap_ops
frontswap_register_ops(struct frontswap_ops *ops);
extern void frontswap_shrink(unsigned long);
extern unsigned long frontswap_curr_pages(void);
extern void frontswap_writethrough(bool);
extern void __frontswap_init(unsigned type);
extern int __frontswap_store(struct page *page);
extern int __frontswap_load(struct page *page);
extern void __frontswap_invalidate_page(unsigned, pgoff_t);
extern void __frontswap_invalidate_area(unsigned);
#ifdef CONFIG_FRONTSWAP
static inline bool frontswap_test(struct swap_info_struct *sis, pgoff_t offset)
{
bool ret = false;
if (frontswap_enabled && sis->frontswap_map)
ret = test_bit(offset, sis->frontswap_map);
return ret;
}
static inline void frontswap_set(struct swap_info_struct *sis, pgoff_t offset)
{
if (frontswap_enabled && sis->frontswap_map)
set_bit(offset, sis->frontswap_map);
}
static inline void frontswap_clear(struct swap_info_struct *sis, pgoff_t offset)
{
if (frontswap_enabled && sis->frontswap_map)
clear_bit(offset, sis->frontswap_map);
}
static inline void frontswap_map_set(struct swap_info_struct *p,
unsigned long *map)
{
p->frontswap_map = map;
}
static inline unsigned long *frontswap_map_get(struct swap_info_struct *p)
{
return p->frontswap_map;
}
#else
/* all inline routines become no-ops and all externs are ignored */
#define frontswap_enabled (0)
static inline bool frontswap_test(struct swap_info_struct *sis, pgoff_t offset)
{
return false;
}
static inline void frontswap_set(struct swap_info_struct *sis, pgoff_t offset)
{
}
static inline void frontswap_clear(struct swap_info_struct *sis, pgoff_t offset)
{
}
static inline void frontswap_map_set(struct swap_info_struct *p,
unsigned long *map)
{
}
static inline unsigned long *frontswap_map_get(struct swap_info_struct *p)
{
return NULL;
}
#endif
static inline int frontswap_store(struct page *page)
{
int ret = -1;
if (frontswap_enabled)
ret = __frontswap_store(page);
return ret;
}
static inline int frontswap_load(struct page *page)
{
int ret = -1;
if (frontswap_enabled)
ret = __frontswap_load(page);
return ret;
}
static inline void frontswap_invalidate_page(unsigned type, pgoff_t offset)
{
if (frontswap_enabled)
__frontswap_invalidate_page(type, offset);
}
static inline void frontswap_invalidate_area(unsigned type)
{
if (frontswap_enabled)
__frontswap_invalidate_area(type);
}
static inline void frontswap_init(unsigned type)
{
if (frontswap_enabled)
__frontswap_init(type);
}
#endif /* _LINUX_FRONTSWAP_H */
...@@ -197,6 +197,10 @@ struct swap_info_struct { ...@@ -197,6 +197,10 @@ struct swap_info_struct {
struct block_device *bdev; /* swap device or bdev of swap file */ struct block_device *bdev; /* swap device or bdev of swap file */
struct file *swap_file; /* seldom referenced */ struct file *swap_file; /* seldom referenced */
unsigned int old_block_size; /* seldom referenced */ unsigned int old_block_size; /* seldom referenced */
#ifdef CONFIG_FRONTSWAP
unsigned long *frontswap_map; /* frontswap in-use, one bit per page */
atomic_t frontswap_pages; /* frontswap pages in-use counter */
#endif
}; };
struct swap_list_t { struct swap_list_t {
......
#ifndef _LINUX_SWAPFILE_H
#define _LINUX_SWAPFILE_H
/*
* these were static in swapfile.c but frontswap.c needs them and we don't
* want to expose them to the dozens of source files that include swap.h
*/
extern spinlock_t swap_lock;
extern struct swap_list_t swap_list;
extern struct swap_info_struct *swap_info[];
extern int try_to_unuse(unsigned int, bool, unsigned long);
#endif /* _LINUX_SWAPFILE_H */
...@@ -389,3 +389,20 @@ config CLEANCACHE ...@@ -389,3 +389,20 @@ config CLEANCACHE
in a negligible performance hit. in a negligible performance hit.
If unsure, say Y to enable cleancache If unsure, say Y to enable cleancache
config FRONTSWAP
bool "Enable frontswap to cache swap pages if tmem is present"
depends on SWAP
default n
help
Frontswap is so named because it can be thought of as the opposite
of a "backing" store for a swap device. The data is stored into
"transcendent memory", memory that is not directly accessible or
addressable by the kernel and is of unknown and possibly
time-varying size. When space in transcendent memory is available,
a significant swap I/O reduction may be achieved. When none is
available, all frontswap calls are reduced to a single pointer-
compare-against-NULL resulting in a negligible performance hit
and swap data is stored as normal on the matching swap device.
If unsure, say Y to enable frontswap.
...@@ -29,6 +29,7 @@ obj-$(CONFIG_HAVE_MEMBLOCK) += memblock.o ...@@ -29,6 +29,7 @@ obj-$(CONFIG_HAVE_MEMBLOCK) += memblock.o
obj-$(CONFIG_BOUNCE) += bounce.o obj-$(CONFIG_BOUNCE) += bounce.o
obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o
obj-$(CONFIG_FRONTSWAP) += frontswap.o
obj-$(CONFIG_HAS_DMA) += dmapool.o obj-$(CONFIG_HAS_DMA) += dmapool.o
obj-$(CONFIG_HUGETLBFS) += hugetlb.o obj-$(CONFIG_HUGETLBFS) += hugetlb.o
obj-$(CONFIG_NUMA) += mempolicy.o obj-$(CONFIG_NUMA) += mempolicy.o
......
/*
* Frontswap frontend
*
* This code provides the generic "frontend" layer to call a matching
* "backend" driver implementation of frontswap. See
* Documentation/vm/frontswap.txt for more information.
*
* Copyright (C) 2009-2012 Oracle Corp. All rights reserved.
* Author: Dan Magenheimer
*
* This work is licensed under the terms of the GNU GPL, version 2.
*/
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/swap.h>
#include <linux/swapops.h>
#include <linux/proc_fs.h>
#include <linux/security.h>
#include <linux/capability.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include <linux/frontswap.h>
#include <linux/swapfile.h>
/*
* frontswap_ops is set by frontswap_register_ops to contain the pointers
* to the frontswap "backend" implementation functions.
*/
static struct frontswap_ops frontswap_ops __read_mostly;
/*
* This global enablement flag reduces overhead on systems where frontswap_ops
* has not been registered, so is preferred to the slower alternative: a
* function call that checks a non-global.
*/
bool frontswap_enabled __read_mostly;
EXPORT_SYMBOL(frontswap_enabled);
/*
* If enabled, frontswap_store will return failure even on success. As
* a result, the swap subsystem will always write the page to swap, in
* effect converting frontswap into a writethrough cache. In this mode,
* there is no direct reduction in swap writes, but a frontswap backend
* can unilaterally "reclaim" any pages in use with no data loss, thus
* providing increases control over maximum memory usage due to frontswap.
*/
static bool frontswap_writethrough_enabled __read_mostly;
#ifdef CONFIG_DEBUG_FS
/*
* Counters available via /sys/kernel/debug/frontswap (if debugfs is
* properly configured). These are for information only so are not protected
* against increment races.
*/
static u64 frontswap_loads;
static u64 frontswap_succ_stores;
static u64 frontswap_failed_stores;
static u64 frontswap_invalidates;
static inline void inc_frontswap_loads(void) {
frontswap_loads++;
}
static inline void inc_frontswap_succ_stores(void) {
frontswap_succ_stores++;
}
static inline void inc_frontswap_failed_stores(void) {
frontswap_failed_stores++;
}
static inline void inc_frontswap_invalidates(void) {
frontswap_invalidates++;
}
#else
static inline void inc_frontswap_loads(void) { }
static inline void inc_frontswap_succ_stores(void) { }
static inline void inc_frontswap_failed_stores(void) { }
static inline void inc_frontswap_invalidates(void) { }
#endif
/*
* Register operations for frontswap, returning previous thus allowing
* detection of multiple backends and possible nesting.
*/
struct frontswap_ops frontswap_register_ops(struct frontswap_ops *ops)
{
struct frontswap_ops old = frontswap_ops;
frontswap_ops = *ops;
frontswap_enabled = true;
return old;
}
EXPORT_SYMBOL(frontswap_register_ops);
/*
* Enable/disable frontswap writethrough (see above).
*/
void frontswap_writethrough(bool enable)
{
frontswap_writethrough_enabled = enable;
}
EXPORT_SYMBOL(frontswap_writethrough);
/*
* Called when a swap device is swapon'd.
*/
void __frontswap_init(unsigned type)
{
struct swap_info_struct *sis = swap_info[type];
BUG_ON(sis == NULL);
if (sis->frontswap_map == NULL)
return;
if (frontswap_enabled)
(*frontswap_ops.init)(type);
}
EXPORT_SYMBOL(__frontswap_init);
/*
* "Store" data from a page to frontswap and associate it with the page's
* swaptype and offset. Page must be locked and in the swap cache.
* If frontswap already contains a page with matching swaptype and
* offset, the frontswap implmentation may either overwrite the data and
* return success or invalidate the page from frontswap and return failure.
*/
int __frontswap_store(struct page *page)
{
int ret = -1, dup = 0;
swp_entry_t entry = { .val = page_private(page), };
int type = swp_type(entry);
struct swap_info_struct *sis = swap_info[type];
pgoff_t offset = swp_offset(entry);
BUG_ON(!PageLocked(page));
BUG_ON(sis == NULL);
if (frontswap_test(sis, offset))
dup = 1;
ret = (*frontswap_ops.store)(type, offset, page);
if (ret == 0) {
frontswap_set(sis, offset);
inc_frontswap_succ_stores();
if (!dup)
atomic_inc(&sis->frontswap_pages);
} else if (dup) {
/*
failed dup always results in automatic invalidate of
the (older) page from frontswap
*/
frontswap_clear(sis, offset);
atomic_dec(&sis->frontswap_pages);
inc_frontswap_failed_stores();
} else
inc_frontswap_failed_stores();
if (frontswap_writethrough_enabled)
/* report failure so swap also writes to swap device */
ret = -1;
return ret;
}
EXPORT_SYMBOL(__frontswap_store);
/*
* "Get" data from frontswap associated with swaptype and offset that were
* specified when the data was put to frontswap and use it to fill the
* specified page with data. Page must be locked and in the swap cache.
*/
int __frontswap_load(struct page *page)
{
int ret = -1;
swp_entry_t entry = { .val = page_private(page), };
int type = swp_type(entry);
struct swap_info_struct *sis = swap_info[type];
pgoff_t offset = swp_offset(entry);
BUG_ON(!PageLocked(page));
BUG_ON(sis == NULL);
if (frontswap_test(sis, offset))
ret = (*frontswap_ops.load)(type, offset, page);
if (ret == 0)
inc_frontswap_loads();
return ret;
}
EXPORT_SYMBOL(__frontswap_load);
/*
* Invalidate any data from frontswap associated with the specified swaptype
* and offset so that a subsequent "get" will fail.
*/
void __frontswap_invalidate_page(unsigned type, pgoff_t offset)
{
struct swap_info_struct *sis = swap_info[type];
BUG_ON(sis == NULL);
if (frontswap_test(sis, offset)) {
(*frontswap_ops.invalidate_page)(type, offset);
atomic_dec(&sis->frontswap_pages);
frontswap_clear(sis, offset);
inc_frontswap_invalidates();
}
}
EXPORT_SYMBOL(__frontswap_invalidate_page);
/*
* Invalidate all data from frontswap associated with all offsets for the
* specified swaptype.
*/
void __frontswap_invalidate_area(unsigned type)
{
struct swap_info_struct *sis = swap_info[type];
BUG_ON(sis == NULL);
if (sis->frontswap_map == NULL)
return;
(*frontswap_ops.invalidate_area)(type);
atomic_set(&sis->frontswap_pages, 0);
memset(sis->frontswap_map, 0, sis->max / sizeof(long));
}
EXPORT_SYMBOL(__frontswap_invalidate_area);
/*
* Frontswap, like a true swap device, may unnecessarily retain pages
* under certain circumstances; "shrink" frontswap is essentially a
* "partial swapoff" and works by calling try_to_unuse to attempt to
* unuse enough frontswap pages to attempt to -- subject to memory
* constraints -- reduce the number of pages in frontswap to the
* number given in the parameter target_pages.
*/
void frontswap_shrink(unsigned long target_pages)
{
struct swap_info_struct *si = NULL;
int si_frontswap_pages;
unsigned long total_pages = 0, total_pages_to_unuse;
unsigned long pages = 0, pages_to_unuse = 0;
int type;
bool locked = false;
/*
* we don't want to hold swap_lock while doing a very
* lengthy try_to_unuse, but swap_list may change
* so restart scan from swap_list.head each time
*/
spin_lock(&swap_lock);
locked = true;
total_pages = 0;
for (type = swap_list.head; type >= 0; type = si->next) {
si = swap_info[type];
total_pages += atomic_read(&si->frontswap_pages);
}
if (total_pages <= target_pages)
goto out;
total_pages_to_unuse = total_pages - target_pages;
for (type = swap_list.head; type >= 0; type = si->next) {
si = swap_info[type];
si_frontswap_pages = atomic_read(&si->frontswap_pages);
if (total_pages_to_unuse < si_frontswap_pages)
pages = pages_to_unuse = total_pages_to_unuse;
else {
pages = si_frontswap_pages;
pages_to_unuse = 0; /* unuse all */
}
/* ensure there is enough RAM to fetch pages from frontswap */
if (security_vm_enough_memory_mm(current->mm, pages))
continue;
vm_unacct_memory(pages);
break;
}
if (type < 0)
goto out;
locked = false;
spin_unlock(&swap_lock);
try_to_unuse(type, true, pages_to_unuse);
out:
if (locked)
spin_unlock(&swap_lock);
return;
}
EXPORT_SYMBOL(frontswap_shrink);
/*
* Count and return the number of frontswap pages across all
* swap devices. This is exported so that backend drivers can
* determine current usage without reading debugfs.
*/
unsigned long frontswap_curr_pages(void)
{
int type;
unsigned long totalpages = 0;
struct swap_info_struct *si = NULL;
spin_lock(&swap_lock);
for (type = swap_list.head; type >= 0; type = si->next) {
si = swap_info[type];
totalpages += atomic_read(&si->frontswap_pages);
}
spin_unlock(&swap_lock);
return totalpages;
}
EXPORT_SYMBOL(frontswap_curr_pages);
static int __init init_frontswap(void)
{
#ifdef CONFIG_DEBUG_FS
struct dentry *root = debugfs_create_dir("frontswap", NULL);
if (root == NULL)
return -ENXIO;
debugfs_create_u64("loads", S_IRUGO, root, &frontswap_loads);
debugfs_create_u64("succ_stores", S_IRUGO, root, &frontswap_succ_stores);
debugfs_create_u64("failed_stores", S_IRUGO, root,
&frontswap_failed_stores);
debugfs_create_u64("invalidates", S_IRUGO,
root, &frontswap_invalidates);
#endif
return 0;
}
module_init(init_frontswap);
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/bio.h> #include <linux/bio.h>
#include <linux/swapops.h> #include <linux/swapops.h>
#include <linux/writeback.h> #include <linux/writeback.h>
#include <linux/frontswap.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
static struct bio *get_swap_bio(gfp_t gfp_flags, static struct bio *get_swap_bio(gfp_t gfp_flags,
...@@ -98,6 +99,12 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) ...@@ -98,6 +99,12 @@ int swap_writepage(struct page *page, struct writeback_control *wbc)
unlock_page(page); unlock_page(page);
goto out; goto out;
} }
if (frontswap_store(page) == 0) {
set_page_writeback(page);
unlock_page(page);
end_page_writeback(page);
goto out;
}
bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write); bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write);
if (bio == NULL) { if (bio == NULL) {
set_page_dirty(page); set_page_dirty(page);
...@@ -122,6 +129,11 @@ int swap_readpage(struct page *page) ...@@ -122,6 +129,11 @@ int swap_readpage(struct page *page)
VM_BUG_ON(!PageLocked(page)); VM_BUG_ON(!PageLocked(page));
VM_BUG_ON(PageUptodate(page)); VM_BUG_ON(PageUptodate(page));
if (frontswap_load(page) == 0) {
SetPageUptodate(page);
unlock_page(page);
goto out;
}
bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read); bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read);
if (bio == NULL) { if (bio == NULL) {
unlock_page(page); unlock_page(page);
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#include <linux/memcontrol.h> #include <linux/memcontrol.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/oom.h> #include <linux/oom.h>
#include <linux/frontswap.h>
#include <linux/swapfile.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
...@@ -42,7 +44,7 @@ static bool swap_count_continued(struct swap_info_struct *, pgoff_t, ...@@ -42,7 +44,7 @@ static bool swap_count_continued(struct swap_info_struct *, pgoff_t,
static void free_swap_count_continuations(struct swap_info_struct *); static void free_swap_count_continuations(struct swap_info_struct *);
static sector_t map_swap_entry(swp_entry_t, struct block_device**); static sector_t map_swap_entry(swp_entry_t, struct block_device**);
static DEFINE_SPINLOCK(swap_lock); DEFINE_SPINLOCK(swap_lock);
static unsigned int nr_swapfiles; static unsigned int nr_swapfiles;
long nr_swap_pages; long nr_swap_pages;
long total_swap_pages; long total_swap_pages;
...@@ -53,9 +55,9 @@ static const char Unused_file[] = "Unused swap file entry "; ...@@ -53,9 +55,9 @@ static const char Unused_file[] = "Unused swap file entry ";
static const char Bad_offset[] = "Bad swap offset entry "; static const char Bad_offset[] = "Bad swap offset entry ";
static const char Unused_offset[] = "Unused swap offset entry "; static const char Unused_offset[] = "Unused swap offset entry ";
static struct swap_list_t swap_list = {-1, -1}; struct swap_list_t swap_list = {-1, -1};
static struct swap_info_struct *swap_info[MAX_SWAPFILES]; struct swap_info_struct *swap_info[MAX_SWAPFILES];
static DEFINE_MUTEX(swapon_mutex); static DEFINE_MUTEX(swapon_mutex);
...@@ -556,6 +558,7 @@ static unsigned char swap_entry_free(struct swap_info_struct *p, ...@@ -556,6 +558,7 @@ static unsigned char swap_entry_free(struct swap_info_struct *p,
swap_list.next = p->type; swap_list.next = p->type;
nr_swap_pages++; nr_swap_pages++;
p->inuse_pages--; p->inuse_pages--;
frontswap_invalidate_page(p->type, offset);
if ((p->flags & SWP_BLKDEV) && if ((p->flags & SWP_BLKDEV) &&
disk->fops->swap_slot_free_notify) disk->fops->swap_slot_free_notify)
disk->fops->swap_slot_free_notify(p->bdev, offset); disk->fops->swap_slot_free_notify(p->bdev, offset);
...@@ -985,11 +988,12 @@ static int unuse_mm(struct mm_struct *mm, ...@@ -985,11 +988,12 @@ static int unuse_mm(struct mm_struct *mm,
} }
/* /*
* Scan swap_map from current position to next entry still in use. * Scan swap_map (or frontswap_map if frontswap parameter is true)
* from current position to next entry still in use.
* Recycle to start on reaching the end, returning 0 when empty. * Recycle to start on reaching the end, returning 0 when empty.
*/ */
static unsigned int find_next_to_unuse(struct swap_info_struct *si, static unsigned int find_next_to_unuse(struct swap_info_struct *si,
unsigned int prev) unsigned int prev, bool frontswap)
{ {
unsigned int max = si->max; unsigned int max = si->max;
unsigned int i = prev; unsigned int i = prev;
...@@ -1015,6 +1019,12 @@ static unsigned int find_next_to_unuse(struct swap_info_struct *si, ...@@ -1015,6 +1019,12 @@ static unsigned int find_next_to_unuse(struct swap_info_struct *si,
prev = 0; prev = 0;
i = 1; i = 1;
} }
if (frontswap) {
if (frontswap_test(si, i))
break;
else
continue;
}
count = si->swap_map[i]; count = si->swap_map[i];
if (count && swap_count(count) != SWAP_MAP_BAD) if (count && swap_count(count) != SWAP_MAP_BAD)
break; break;
...@@ -1026,8 +1036,12 @@ static unsigned int find_next_to_unuse(struct swap_info_struct *si, ...@@ -1026,8 +1036,12 @@ static unsigned int find_next_to_unuse(struct swap_info_struct *si,
* We completely avoid races by reading each swap page in advance, * We completely avoid races by reading each swap page in advance,
* and then search for the process using it. All the necessary * and then search for the process using it. All the necessary
* page table adjustments can then be made atomically. * page table adjustments can then be made atomically.
*
* if the boolean frontswap is true, only unuse pages_to_unuse pages;
* pages_to_unuse==0 means all pages; ignored if frontswap is false
*/ */
static int try_to_unuse(unsigned int type) int try_to_unuse(unsigned int type, bool frontswap,
unsigned long pages_to_unuse)
{ {
struct swap_info_struct *si = swap_info[type]; struct swap_info_struct *si = swap_info[type];
struct mm_struct *start_mm; struct mm_struct *start_mm;
...@@ -1060,7 +1074,7 @@ static int try_to_unuse(unsigned int type) ...@@ -1060,7 +1074,7 @@ static int try_to_unuse(unsigned int type)
* one pass through swap_map is enough, but not necessarily: * one pass through swap_map is enough, but not necessarily:
* there are races when an instance of an entry might be missed. * there are races when an instance of an entry might be missed.
*/ */
while ((i = find_next_to_unuse(si, i)) != 0) { while ((i = find_next_to_unuse(si, i, frontswap)) != 0) {
if (signal_pending(current)) { if (signal_pending(current)) {
retval = -EINTR; retval = -EINTR;
break; break;
...@@ -1227,6 +1241,10 @@ static int try_to_unuse(unsigned int type) ...@@ -1227,6 +1241,10 @@ static int try_to_unuse(unsigned int type)
* interactive performance. * interactive performance.
*/ */
cond_resched(); cond_resched();
if (frontswap && pages_to_unuse > 0) {
if (!--pages_to_unuse)
break;
}
} }
mmput(start_mm); mmput(start_mm);
...@@ -1486,7 +1504,8 @@ static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) ...@@ -1486,7 +1504,8 @@ static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span)
} }
static void enable_swap_info(struct swap_info_struct *p, int prio, static void enable_swap_info(struct swap_info_struct *p, int prio,
unsigned char *swap_map) unsigned char *swap_map,
unsigned long *frontswap_map)
{ {
int i, prev; int i, prev;
...@@ -1496,6 +1515,7 @@ static void enable_swap_info(struct swap_info_struct *p, int prio, ...@@ -1496,6 +1515,7 @@ static void enable_swap_info(struct swap_info_struct *p, int prio,
else else
p->prio = --least_priority; p->prio = --least_priority;
p->swap_map = swap_map; p->swap_map = swap_map;
frontswap_map_set(p, frontswap_map);
p->flags |= SWP_WRITEOK; p->flags |= SWP_WRITEOK;
nr_swap_pages += p->pages; nr_swap_pages += p->pages;
total_swap_pages += p->pages; total_swap_pages += p->pages;
...@@ -1512,6 +1532,7 @@ static void enable_swap_info(struct swap_info_struct *p, int prio, ...@@ -1512,6 +1532,7 @@ static void enable_swap_info(struct swap_info_struct *p, int prio,
swap_list.head = swap_list.next = p->type; swap_list.head = swap_list.next = p->type;
else else
swap_info[prev]->next = p->type; swap_info[prev]->next = p->type;
frontswap_init(p->type);
spin_unlock(&swap_lock); spin_unlock(&swap_lock);
} }
...@@ -1585,7 +1606,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) ...@@ -1585,7 +1606,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
spin_unlock(&swap_lock); spin_unlock(&swap_lock);
oom_score_adj = test_set_oom_score_adj(OOM_SCORE_ADJ_MAX); oom_score_adj = test_set_oom_score_adj(OOM_SCORE_ADJ_MAX);
err = try_to_unuse(type); err = try_to_unuse(type, false, 0); /* force all pages to be unused */
compare_swap_oom_score_adj(OOM_SCORE_ADJ_MAX, oom_score_adj); compare_swap_oom_score_adj(OOM_SCORE_ADJ_MAX, oom_score_adj);
if (err) { if (err) {
...@@ -1596,7 +1617,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) ...@@ -1596,7 +1617,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
* sys_swapoff for this swap_info_struct at this point. * sys_swapoff for this swap_info_struct at this point.
*/ */
/* re-insert swap space back into swap_list */ /* re-insert swap space back into swap_list */
enable_swap_info(p, p->prio, p->swap_map); enable_swap_info(p, p->prio, p->swap_map, frontswap_map_get(p));
goto out_dput; goto out_dput;
} }
...@@ -1622,9 +1643,11 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) ...@@ -1622,9 +1643,11 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
swap_map = p->swap_map; swap_map = p->swap_map;
p->swap_map = NULL; p->swap_map = NULL;
p->flags = 0; p->flags = 0;
frontswap_invalidate_area(type);
spin_unlock(&swap_lock); spin_unlock(&swap_lock);
mutex_unlock(&swapon_mutex); mutex_unlock(&swapon_mutex);
vfree(swap_map); vfree(swap_map);
vfree(frontswap_map_get(p));
/* Destroy swap account informatin */ /* Destroy swap account informatin */
swap_cgroup_swapoff(type); swap_cgroup_swapoff(type);
...@@ -1988,6 +2011,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) ...@@ -1988,6 +2011,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
sector_t span; sector_t span;
unsigned long maxpages; unsigned long maxpages;
unsigned char *swap_map = NULL; unsigned char *swap_map = NULL;
unsigned long *frontswap_map = NULL;
struct page *page = NULL; struct page *page = NULL;
struct inode *inode = NULL; struct inode *inode = NULL;
...@@ -2071,6 +2095,9 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) ...@@ -2071,6 +2095,9 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
error = nr_extents; error = nr_extents;
goto bad_swap; goto bad_swap;
} }
/* frontswap enabled? set up bit-per-page map for frontswap */
if (frontswap_enabled)
frontswap_map = vzalloc(maxpages / sizeof(long));
if (p->bdev) { if (p->bdev) {
if (blk_queue_nonrot(bdev_get_queue(p->bdev))) { if (blk_queue_nonrot(bdev_get_queue(p->bdev))) {
...@@ -2086,14 +2113,15 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) ...@@ -2086,14 +2113,15 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
if (swap_flags & SWAP_FLAG_PREFER) if (swap_flags & SWAP_FLAG_PREFER)
prio = prio =
(swap_flags & SWAP_FLAG_PRIO_MASK) >> SWAP_FLAG_PRIO_SHIFT; (swap_flags & SWAP_FLAG_PRIO_MASK) >> SWAP_FLAG_PRIO_SHIFT;
enable_swap_info(p, prio, swap_map); enable_swap_info(p, prio, swap_map, frontswap_map);
printk(KERN_INFO "Adding %uk swap on %s. " printk(KERN_INFO "Adding %uk swap on %s. "
"Priority:%d extents:%d across:%lluk %s%s\n", "Priority:%d extents:%d across:%lluk %s%s%s\n",
p->pages<<(PAGE_SHIFT-10), name, p->prio, p->pages<<(PAGE_SHIFT-10), name, p->prio,
nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10), nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10),
(p->flags & SWP_SOLIDSTATE) ? "SS" : "", (p->flags & SWP_SOLIDSTATE) ? "SS" : "",
(p->flags & SWP_DISCARDABLE) ? "D" : ""); (p->flags & SWP_DISCARDABLE) ? "D" : "",
(frontswap_map) ? "FS" : "");
mutex_unlock(&swapon_mutex); mutex_unlock(&swapon_mutex);
atomic_inc(&proc_poll_event); atomic_inc(&proc_poll_event);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment