Dan Magenheimer
2010-Apr-22 13:28 UTC
Cleancache [PATCH 2/7] (was Transcendent Memory): core files
(Sorry for resend... Mail server DNS problems sending to some recipients) Cleancache [PATCH 2/7] (was Transcendent Memory): core files Cleancache core files. Credits: Cleancache_ops design derived from Jeremy Fitzhardinge design for tmem; sysfs code modelled after mm/ksm.c Note that CONFIG_CLEANCACHE defaults to on; all hooks devolve to a compare-pointer-to-NULL so performance impact should be negligible, but can be reduced to zero impact if config''ed off. Signed-off-by: Dan Magenheimer <dan.magenheimer@oracle.com> Diffstat: include/linux/cleancache.h | 88 +++++++++ mm/Kconfig | 22 ++ mm/Makefile | 1 mm/cleancache.c | 198 +++++++++++++++++++++ 4 files changed, 309 insertions(+) --- linux-2.6.34-rc5/include/linux/cleancache.h 1969-12-31 17:00:00.000000000 -0700 +++ linux-2.6.34-rc5-cleancache/include/linux/cleancache.h 2010-04-21 10:06:33.000000000 -0600 @@ -0,0 +1,88 @@ +#ifndef _LINUX_CLEANCACHE_H +#define _LINUX_CLEANCACHE_H + +#include <linux/fs.h> +#include <linux/mm.h> + +struct cleancache_ops { + int (*init_fs)(unsigned long); + int (*init_shared_fs)(char *uuid, unsigned long); + int (*get_page)(int, unsigned long, unsigned long, struct page *); + int (*put_page)(int, unsigned long, unsigned long, struct page *); + int (*flush_page)(int, unsigned long, unsigned long); + int (*flush_inode)(int, unsigned long); + void (*flush_fs)(int); +}; + +extern struct cleancache_ops *cleancache_ops; +extern int __cleancache_get_page(struct page *); +extern int __cleancache_put_page(struct page *); +extern int __cleancache_flush_page(struct address_space *, struct page *); +extern int __cleancache_flush_inode(struct address_space *); + +#ifndef CONFIG_CLEANCACHE +#define cleancache_ops ((struct cleancache_ops *)NULL) +#endif + +static inline int cleancache_init_fs(unsigned long pagesize) +{ + int ret = -1; + + if (cleancache_ops) + ret = (*cleancache_ops->init_fs)(pagesize); + return ret; +} + +static inline int cleancache_init_shared_fs(char *uuid, unsigned long pagesize) +{ + int ret = -1; + + if (cleancache_ops) + ret = (*cleancache_ops->init_shared_fs)(uuid, pagesize); + return ret; +} + +static inline int cleancache_get_page(struct page *page) +{ + int ret = 0; + + if (cleancache_ops) + ret = __cleancache_get_page(page); + return ret; +} + +static inline int cleancache_put_page(struct page *page) +{ + int ret = 0; + + if (cleancache_ops) + ret = __cleancache_put_page(page); + return ret; +} + +static inline int cleancache_flush_page(struct address_space *mapping, + struct page *page) +{ + int ret = 0; + + if (cleancache_ops) + ret = __cleancache_flush_page(mapping, page); + return ret; +} + +static inline int cleancache_flush_inode(struct address_space *mapping) +{ + int ret = 0; + + if (cleancache_ops) + ret = __cleancache_flush_inode(mapping); + return ret; +} + +static inline void cleancache_flush_fs(int pool_id) +{ + if (cleancache_ops && pool_id >= 0) + (*cleancache_ops->flush_fs)(pool_id); +} + +#endif /* _LINUX_CLEANCACHE_H */ --- linux-2.6.34-rc5/mm/cleancache.c 1969-12-31 17:00:00.000000000 -0700 +++ linux-2.6.34-rc5-cleancache/mm/cleancache.c 2010-04-21 10:06:33.000000000 -0600 @@ -0,0 +1,198 @@ +/* mm/cleancache.c + + Copyright (C) 2009-2010 Oracle Corp. All rights reserved. + Author: Dan Magenheimer + + Cleancache can be thought of as a page-granularity victim cache for clean + pages that the kernel''s pageframe replacement algorithm (PFRA) would like + to keep around, but can''t since there isn''t enough memory. So when the + PFRA "evicts" a page, it first attempts to put it into a synchronous + concurrency-safe page-oriented pseudo-RAM device (such as Xen''s Transcendent + Memory, aka "tmem", or in-kernel compressed memory, aka "zmem", or other + RAM-like devices) which is not directly accessible or addressable by the + kernel and is of unknown and possibly time-varying size. And when a + cleancache-enabled filesystem wishes to access a page in a file on disk, + it first checks cleancache to see if it already contains it; if it does, + the page is copied into the kernel and a disk access is avoided. + This pseudo-RAM device links itself to cleancache by setting the + cleancache_ops pointer appropriately and the functions it provides must + conform to certain semantics as follows: + + Most important, cleancache is "ephemeral". Pages which are copied into + cleancache have an indefinite lifetime which is completely unknowable + by the kernel and so may or may not still be in cleancache at any later time. + Thus, as its name implies, cleancache is not suitable for dirty pages. The + pseudo-RAM has complete discretion over what pages to preserve and what + pages to discard and when. + + A filesystem calls "init_fs" to obtain a pool id which, if positive, must be + saved in the filesystem''s superblock; a negative return value indicates + failure. A "put_page" will copy a (presumably about-to-be-evicted) page into + pseudo-RAM and associate it with the pool id, the file inode, and a page + index into the file. (The combination of a pool id, an inode, and an index + is called a "handle".) A "get_page" will copy the page, if found, from + pseudo-RAM into kernel memory. A "flush_page" will ensure the page no longer + is present in pseudo-RAM; a "flush_inode" will flush all pages associated + with the specified inode; and a "flush_fs" will flush all pages in all + inodes specified by the given pool id. + + A "init_shared_fs", like init, obtains a pool id but tells the pseudo-RAM + to treat the pool as shared using a 128-bit UUID as a key. On systems + that may run multiple kernels (such as hard partitioned or virtualized + systems) that may share a clustered filesystem, and where the pseudo-RAM + may be shared among those kernels, calls to init_shared_fs that specify the + same UUID will receive the same pool id, thus allowing the pages to + be shared. Note that any security requirements must be imposed outside + of the kernel (e.g. by "tools" that control the pseudo-RAM). Or a + pseudo-RAM implementation can simply disable shared_init by always + returning a negative value. + + If a get_page is successful on a non-shared pool, the page is flushed (thus + making cleancache an "exclusive" cache). On a shared pool, the page + is NOT flushed on a successful get_page so that it remains accessible to + other sharers. The kernel is responsible for ensuring coherency between + cleancache (shared or not), the page cache, and the filesystem, using + cleancache flush operations as required. + + Note that the pseudo-RAM must enforce put-put-get coherency and get-get + coherency. For the former, if two puts are made to the same handle but + with different data, say AAA by the first put and BBB by the second, a + subsequent get can never return the stale data (AAA). For get-get coherency, + if a get for a given handle fails, subsequent gets for that handle will + never succeed unless preceded by a successful put with that handle. + + Last, pseudo-RAM provides no SMP serialization guarantees; if two + different Linux threads are putting an flushing a page with the same + handle, the results are indeterminate. + + */ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/cleancache.h> + +struct cleancache_ops *cleancache_ops; +EXPORT_SYMBOL(cleancache_ops); + +/* useful stats available via /sys/kernel/mm/frontswap */ +static unsigned long succ_gets; +static unsigned long failed_gets; +static unsigned long puts; +static unsigned long flushes; + +int __cleancache_get_page(struct page *page) +{ + int ret = 0; + int pool_id = page->mapping->host->i_sb->cleancache_poolid; + + if (pool_id >= 0) { + ret = (*cleancache_ops->get_page)(pool_id, + page->mapping->host->i_ino, page->index, page); + if (ret == 1) + succ_gets++; + else + failed_gets++; + } + return ret; +} +EXPORT_SYMBOL(__cleancache_get_page); + +int __cleancache_put_page(struct page *page) +{ + int ret = 0; + int pool_id = page->mapping->host->i_sb->cleancache_poolid; + + if (pool_id >= 0) { + ret = (*cleancache_ops->put_page)(pool_id, + page->mapping->host->i_ino, page->index, page); + puts++; + } + return ret; +} + +int __cleancache_flush_page(struct address_space *mapping, struct page *page) +{ + int ret = 0; + int pool_id = mapping->host->i_sb->cleancache_poolid; + + if (pool_id >= 0) { + ret = (*cleancache_ops->flush_page)(pool_id, + mapping->host->i_ino, page->index); + flushes++; + } + return ret; +} +EXPORT_SYMBOL(__cleancache_flush_page); + +int __cleancache_flush_inode(struct address_space *mapping) +{ + int ret = 0; + int pool_id = mapping->host->i_sb->cleancache_poolid; + + if (pool_id >= 0) { + ret = (*cleancache_ops->flush_inode)(pool_id, + mapping->host->i_ino); + } + return ret; +} +EXPORT_SYMBOL(__cleancache_flush_inode); + +#ifdef CONFIG_SYSFS + +#define CLEANCACHE_ATTR_RO(_name) \ + static struct kobj_attribute _name##_attr = __ATTR_RO(_name) + +static ssize_t succ_gets_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", succ_gets); +} +CLEANCACHE_ATTR_RO(succ_gets); + +static ssize_t failed_gets_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", failed_gets); +} +CLEANCACHE_ATTR_RO(failed_gets); + +static ssize_t puts_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", puts); +} +CLEANCACHE_ATTR_RO(puts); + +static ssize_t flushes_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", flushes); +} +CLEANCACHE_ATTR_RO(flushes); + +static struct attribute *cleancache_attrs[] = { + &succ_gets_attr.attr, + &failed_gets_attr.attr, + &puts_attr.attr, + &flushes_attr.attr, + NULL, +}; + +static struct attribute_group cleancache_attr_group = { + .attrs = cleancache_attrs, + .name = "cleancache", +}; + +#endif /* CONFIG_SYSFS */ + +static int __init init_cleancache(void) +{ +#ifdef CONFIG_SYSFS + int err; + + err = sysfs_create_group(mm_kobj, &cleancache_attr_group); +#endif /* CONFIG_SYSFS */ + return 0; +} +module_init(init_cleancache) --- linux-2.6.34-rc5/mm/Kconfig 2010-04-19 17:29:56.000000000 -0600 +++ linux-2.6.34-rc5-cleancache/mm/Kconfig 2010-04-21 10:06:33.000000000 -0600 @@ -287,3 +287,25 @@ config NOMMU_INITIAL_TRIM_EXCESS of 1 says that all excess pages should be trimmed. See Documentation/nommu-mmap.txt for more information. + +config CLEANCACHE + bool "Enable cleancache pseudo-RAM driver to cache clean pages" + default y + help + Cleancache can be thought of as a page-granularity victim cache + for clean pages that the kernel''s pageframe replacement algorithm + (PFRA) would like to keep around, but can''t since there isn''t enough + memory. So when the PFRA "evicts" a page, it first attempts to put + it into a synchronous concurrency-safe page-oriented pseudo-RAM + device (such as Xen''s Transcendent Memory, aka "tmem") which is not + directly accessible or addressable by the kernel and is of unknown + (and possibly time-varying) size. And when a cleancache-enabled + filesystem wishes to access a page in a file on disk, it first + checks cleancache to see if it already contains it; if it does, + the page is copied into the kernel and a disk access is avoided. + When a pseudo-RAM device is available, a significant I/O reduction + may be achieved. When none is available, all cleancache calls + are reduced to a single pointer-compare-against-NULL resulting + in a negligible performance hit. + + If unsure, say Y to enable cleancache --- linux-2.6.34-rc5/mm/Makefile 2010-04-19 17:29:56.000000000 -0600 +++ linux-2.6.34-rc5-cleancache/mm/Makefile 2010-04-21 10:06:33.000000000 -0600 @@ -44,3 +44,4 @@ obj-$(CONFIG_MEMORY_FAILURE) += memory-f obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o +obj-$(CONFIG_CLEANCACHE) += cleancache.o -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Al Viro
2010-May-14 23:18 UTC
[Ocfs2-devel] Cleancache [PATCH 2/7] (was Transcendent Memory): core files
On Thu, Apr 22, 2010 at 06:28:09AM -0700, Dan Magenheimer wrote:> +struct cleancache_ops { > + int (*init_fs)(unsigned long);unsigned long? Really? Not even size_t?> + int (*init_shared_fs)(char *uuid, unsigned long);Ditto.> + int (*get_page)(int, unsigned long, unsigned long, struct page *);Ugh. First of all, presumably you have some structure behind that index, don't you? Might be a better way to do it. What's more, use of ->i_ino is simply wrong. How stable do you want that to be and how much do you want it to outlive struct address_space in question?>From my reading of your code, it doesn't outlive that anyway, so...The third one is pgoff_t; again, use sane types, _if_ you actually want the argument #3 at all - it can be derived from struct page you are passing there as well.> + int (*put_page)(int, unsigned long, unsigned long, struct page *); > + int (*flush_page)(int, unsigned long, unsigned long); > + int (*flush_inode)(int, unsigned long); > + void (*flush_fs)(int);Same questions as above...
Dan Magenheimer
2010-May-24 20:04 UTC
[Ocfs2-devel] Cleancache [PATCH 2/7] (was Transcendent Memory): core files
> From: Al Viro [mailto:viro at ZenIV.linux.org.uk] > Subject: Re: Cleancache [PATCH 2/7] (was Transcendent Memory): core filesHi Al! Thanks for the feedback! Sorry for the delayed response.> ...again, use sane types...Good point. Will fix types for next rev (using size_t, ino_t, and pgoff_t).> > + int (*get_page)(int, unsigned long, unsigned long, struct page *); > > Ugh. First of all, presumably you have some structure behind that > index, don't you? Might be a better way to do it.Not quite sure what you mean here. The index is really just part of a unique handle for cleancache to identify the (page of) data.> What's more, use of ->i_ino is simply wrong. How stable do you want that > to be and how much do you want it to outlive struct address_space in question? > From my reading of your code, it doesn't outlive that anyway, so...Unless I misunderstand your point, no, the inode never outlives the address space because the specification requires the kernel to ensure coherency; if the inode were about to outlive the address space, the cleancache_flush operations must be invoked (and I think the patch covers all the necessary cases).> The third one is pgoff_t; again, use sane types, _if_ you actually want > the argument #3 at all - it can be derived from struct page you are > passing there as well.I thought it best to declare the _ops so that the struct page is opaque to the "backend" (driver). The kernel-side ("frontend") defines the handle and ensures coherency, so the backend shouldn't be allowed to derive or muck with the three-tuple passed by the kernel. In the existing (Xen tmem) driver, the only operation performed on the struct page parameter is page_to_pfn(). OTOH, I could go one step further and pass a pfn_t instead of a struct page, since it is really only the physical page frame that the backend needs to know about and (synchronously) read/write from/to. Thoughts? Thanks again! Dan
Dan Magenheimer
2010-May-25 02:16 UTC
[Ocfs2-devel] Cleancache [PATCH 2/7] (was Transcendent Memory): core files
> > The third one is pgoff_t; again, use sane types, _if_ you actually > want > > the argument #3 at all - it can be derived from struct page you are > > passing there as well. > > I thought it best to declare the _ops so that the struct page > is opaque to the "backend" (driver). The kernel-side ("frontend") > defines the handle and ensures coherency, so the backend shouldn't > be allowed to derive or muck with the three-tuple passed by the > kernel. In the existing (Xen tmem) driver, the only operation > performed on the struct page parameter is page_to_pfn(). OTOH, > I could go one step further and pass a pfn_t instead of a > struct page, since it is really only the physical page frame that > the backend needs to know about and (synchronously) read/write from/to. > > Thoughts?Silly me. pfn_t is a Xen/KVM type not otherwise used in the kernel AFAICT. Please ignore...