These patches add local caching for network filesystems such as NFS.
The patches can roughly be broken down into a number of sets:
(*) 01-keys-inc-payload.diff
(*) 02-keys-search-keyring.diff
(*) 03-keys-callout-blob.diff
Three patches to the keyring code made to help the CIFS people.
Included because of patches 05-08.
(*) 04-keys-get-label.diff
A patch to allow the security label of a key to be retrieved.
Included because of patches 05-08.
(*) 05-security-current-fsugid.diff
(*) 06-security-separate-task-bits.diff
(*) 07-security-subjective.diff
(*) 08-security-kernel_service-class.diff
(*) 09-security-kernel-service.diff
(*) 10-security-nfsd.diff
Patches to permit the subjective security of a task to be overridden.
All the security details in task_struct are decanted into a new struct
that task_struct then has two pointers two: one that defines the
objective security of that task (how other tasks may affect it) and one
that defines the subjective security (how it may affect other objects).
Note that I have dropped the idea of struct cred for the moment. With
the amount of stuff that was excluded from it, it wasn't actually any
use to me. However, it can be added later.
Required for cachefiles.
(*) 11-release-page.diff
(*) 12-fscache-page-flags.diff
(*) 13-add_wait_queue_tail.diff
(*) 14-fscache.diff
Patches to provide a local caching facility for network filesystems.
(*) 15-cachefiles-ia64.diff
(*) 16-cachefiles-ext3-f_mapping.diff
(*) 17-cachefiles-write.diff
(*) 18-cachefiles-monitor.diff
(*) 19-cachefiles-export.diff
(*) 20-cachefiles.diff
Patches to provide a local cache in a directory of an already mounted
filesystem.
(*) 21-nfs-comment.diff
(*) 22-nfs-fscache-option.diff
(*) 23-nfs-fscache-kconfig.diff
(*) 24-nfs-fscache-top-index.diff
(*) 25-nfs-fscache-server-obj.diff
(*) 26-nfs-fsc...Hi David, Have you got before/after benchmark results? Regards, Daniel -
See attached.
These show a couple of things:
(1) Dealing with lots of metadata slows things down a lot. Note the result of
looking and reading lots of small files with tar (the last result). The
NFS client has to both consult the NFS server *and* the cache. Not only
that, but any asynchronicity the cache may like to do is rendered
ineffective by the fact tar wants to do a read on a file pretty much
directly after opening it.
(2) Getting metadata from the local disk fs is slower than pulling it across
an unshared gigabit ethernet from a server that already has it in memory.
These points don't mean that fscache is no use, just that you have to consider
carefully whether it's of use to *you* given your particular situation, and
that depends on various factors.
Note that currently FS-Caching is disabled for individual NFS files opened for
writing as there's no way to handle the coherency problems thereby introduced.
David
---
===========================
FS-CACHE FOR NFS BENCHMARKS
===========================
(*) The NFS client has a 1.86GHz Core2 Duo CPU and 1GB of RAM.
(*) The NFS client has a Seagate ST380211AS 80GB 7200rpm SATA disk on an
interface running in AHCI mode. The chipset is an Intel G965.
(*) A partition of approx 4.5GB is committed to caching, and is formatted as
Ext3 with a blocksize of 4096 and directory indices.
(*) The NFS client is using SELinux.
(*) The NFS server is running an in-kernel NFSd, and has a 2.66GHz Core2 Duo
CPU and 6GB of RAM. The chipset is an Intel P965.
(*) The NFS client is connected to the NFS server by Gigabit Ethernet.
(*) The NFS mount is made with defaults for all options not relating to the
cache:
warthog:/warthog /warthog nfs
rw,vers=3,rsize=1048576,wsize=1048576,hard,proto=tcp,timeo=600,
retrans=2,sec=sys,fsc,addr=w.x.y.z 0 0
==================
FEW BIG FILES TEST
==================
Where:
(*) The NFS server has...Attached here are results using BTRFS (patched so that it'll work at all) rather than Ext3 on the client on the partition backing the cache. Note that I didn't bother redoing the tests that didn't involve a cache as the choice of filesystem backing the cache should have no bearing on the result. Generally, completely cold caches shouldn't show much variation as all the writing can be done completely asynchronously, provided the client doesn't fill its RAM. The interesting case is where the disk cache is warm, but the pagecache is cold (ie: just after a reboot after filling the caches). Here, for the two big files case, BTRFS appears quite a bit better than Ext3, showing a 21% reduction in time for the smaller case and a 13% reduction for the larger case. For the many small/medium files case, BTRFS performed significantly better (15% reduction in time) in the case where the caches were completely cold. I'm not sure why, though - perhaps because it doesn't execute a write_begin() stage during the write_one_page() call and thus doesn't go allocating disk blocks to back the data, but instead allocates them later. More surprising is that BTRFS performed significantly worse (15% increase in time) in the case where the cache on disk was fully populated and then the machine had been rebooted to clear the pagecaches. It's important to note that I've only run each test once apiece, so the numbers should be taken with a modicum of salt (bad statistics and all that). David --- =========================== FEW BIG FILES TEST ON BTRFS =========================== Completely cold caches: [root@andromeda ~]# time cat /warthog/bigfile >/dev/null real 0m2.124s user 0m0.000s sys 0m1.260s [root@andromeda ~]# time cat /warthog/biggerfile >/dev/null real 0m4.538s user 0m0.000s sys 0m2.624s Warm NFS pagecache: [root@andromeda ~]# time cat /warthog/bigfile >/dev/null real 0m0.061s user 0m0.000s sys 0m0.064s [root@andromeda...
And here are XFS results. Tuning XFS makes a *really* big difference for the lots of small/medium files being tarred case. However, in general BTRFS is much better. David --- ========================= FEW BIG FILES TEST ON XFS ========================= Completely cold caches: [root@andromeda ~]# time cat /warthog/bigfile >/dev/null real 0m2.286s user 0m0.000s sys 0m1.828s [root@andromeda ~]# time cat /warthog/biggerfile >/dev/null real 0m4.228s user 0m0.000s sys 0m1.360s Warm NFS pagecache: [root@andromeda ~]# time cat /warthog/bigfile >/dev/null real 0m0.058s user 0m0.000s sys 0m0.060s [root@andromeda ~]# time cat /warthog/biggerfile >/dev/null real 0m0.122s user 0m0.000s sys 0m0.120s Warm XFS pagecache, cold NFS pagecache: [root@andromeda ~]# time cat /warthog/bigfile >/dev/null real 0m0.181s user 0m0.000s sys 0m0.180s [root@andromeda ~]# time cat /warthog/biggerfile >/dev/null real 0m1.034s user 0m0.000s sys 0m0.404s Warm on-disk cache, cold pagecaches: [root@andromeda ~]# time cat /warthog/bigfile >/dev/null real 0m1.540s user 0m0.000s sys 0m0.256s [root@andromeda ~]# time cat /warthog/biggerfile >/dev/null real 0m3.003s user 0m0.000s sys 0m0.532s ========================================== MANY SMALL/MEDIUM FILE READING TEST ON XFS ========================================== Completely cold caches: [root@andromeda ~]# time tar cf - /warthog/aaa >/dev/zero real 4m56.827s user 0m0.180s sys 0m6.668s Warm NFS pagecache: [root@andromeda ~]# time tar cf - /warthog/aaa >/dev/zero real 0m15.084s user 0m0.212s sys 0m5.008s Warm XFS pagecache, cold NFS pagecache: [root@andromeda ~]# time tar cf - /warthog/aaa >/dev/zero real 0m13.547s user 0m0.220s sys 0m5.652s Warm on-disk cache, cold pagecaches: [root@andromeda ~]# time tar c...
Thanks for trying this, of course I'll ask you to try again with the latest I'm afraid I don't have a good handle on the filesystem operations that result If your write_one_page call does parts of btrfs_file_write, you'll get delayed allocation for anything bigger than 8k by default. <= 8k will get packed Which FS operations are included here? Finding all the files or just an unmount? Btrfs defrags metadata in the background, and unmount has to wait for that defrag to finish. Thanks again, Chris -
Here you go. The numbers are very similar. David ================================= FEW BIG FILES TEST ON BTRFS v0.13 ================================= Completely cold caches: [root@andromeda ~]# time cat /warthog/bigfile >/dev/null real 0m2.202s user 0m0.000s sys 0m1.716s [root@andromeda ~]# time cat /warthog/biggerfile >/dev/null real 0m4.212s user 0m0.000s sys 0m0.896s Warm BTRFS pagecache, cold NFS pagecache: [root@andromeda ~]# time cat /warthog/bigfile >/dev/null real 0m0.197s user 0m0.000s sys 0m0.192s [root@andromeda ~]# time cat /warthog/biggerfile >/dev/null real 0m0.376s user 0m0.000s sys 0m0.372s Warm on-disk cache, cold pagecaches: [root@andromeda ~]# time cat /warthog/bigfile >/dev/null real 0m1.543s user 0m0.004s sys 0m1.448s [root@andromeda ~]# time cat /warthog/biggerfile >/dev/null real 0m3.111s user 0m0.000s sys 0m2.856s ================================================== MANY SMALL/MEDIUM FILE READING TEST ON BTRFS v0.13 ================================================== Completely cold caches: [root@andromeda ~]# time tar cf - /warthog/aaa >/dev/zero real 0m31.575s user 0m0.176s sys 0m6.316s Warm BTRFS pagecache, cold NFS pagecache: [root@andromeda ~]# time tar cf - /warthog/aaa >/dev/zero real 0m16.081s user 0m0.164s sys 0m5.528s Warm on-disk cache, cold pagecaches: [root@andromeda ~]# time tar cf - /warthog/aaa >/dev/zero real 2m15.245s user 0m0.064s sys 0m2.808s -
I'm not sure what you're asking. When the cache is cold, we determine that we can't read from the cache very quickly. We then read data from the server and, in the background, create the metadata in the cache and store the data to it (by copying netfs pages to backingfs pages). When the cache is warm, we read the data from the cache, copying the data from the backingfs pages to the netfs pages. We use bmap() to ascertain that there is data to be read, otherwise we detect a hole and fallback to reading from the server. Looking up cache object involves a sequence of lookup() ops and getxattr() ops on the backingfs. Should an object not exist, we defer creation of that object to a background thread and do lookups(), mkdirs() and setxattrs() and a create() to manufacture the object. We read data from an object by calling readpages() on the backingfs to bring the data into the pagecache. We monitor the PG_lock bits to find out when each page is read or has completed with an error. Writing pages to the cache is done completely in the background. PG_fscache_write is set on a page when it is handed to fscache to storage, then at some point a background thread wakes up and calls write_one_page() in the backingfs to write that page to the cache file. At the moment, this copies the data into a backingfs page which is then marked PG_dirty, and the BTRFS might not be doing any writing at all here - apart from local atimes (used by cache culling), that is. What it does have to do is lots of lookups, reads and getxattrs, all of which are synchronous. David -
Hi David, I am trying to spot the numbers that show the sweet spot for this optimization, without much success so far. Who is supposed to win big? Is this mainly about reducing the load on the server, or is the client supposed to win even with a lightly loaded server? When you say Ext3 cache vs NFS cache is the first on the server and the second on the client? Regards, Daniel -
The filesystem on the server is pretty much irrelevant as long as (a) it
doesn't change, and (b) all the data is in memory on the server anyway.
The way the client works is like this:
+---------+
| |
| NFS |--+
| | |
+---------+ | +----------+
| | |
+---------+ +-->| |
| | | |
| AFS |----->| FS-Cache |
| | | |--+
+---------+ +-->| | |
| | | | +--------------+ +--------------+
+---------+ | +----------+ | | | | |
| | | +-->| CacheFiles |-->| Ext3 |
| ISOFS |--+ | /var/cache | | /dev/sda6 |
| | +--------------+ +--------------+
+---------+
(1) NFS, say, asks FS-Cache to store/retrieve data for it;
(2) FS-Cache asks the cache backend, in this case CacheFiles to honour the
operation;
(3) CacheFiles 'opens' a file in a mounted filesystem, say Ext3, and does read
and write operations of a sort on it;
(4) Ext3 decides how the cache data is laid out on disk - CacheFiles just
What are you trying to do exactly? Are you actually playing with it, or just
These are difficult questions to answer. The obvious answer to both is "it
depends", and the real answer to both is "it's a compromise".
Inserting a cache adds overhead: you have to look in the cache to see if your
objects are mirrored there, and then you have to look in the cache to see if
the data you want is stored there; and then you might have to go to the server
anyway and then schedule a copy to be stored in the cache.
The characteristics of this type of cache depend on a number of things: the
filesystem backing it being the most obvious variable, but also how fragmented
it is and the properties of the disk drive or drives it is on.
Wh...Thanks for the excellent ascii art, that cleared up the confusion right Trying to see if you are offering enough of a win to justify testing it, and if that works out, then going shopping for a bin of rotten vegetables to throw at your design, which I hope you will perceive as useful. In short I am looking for a reason to throw engineering effort at it. From the numbers you have posted I think you are missing some basic efficiencies that could take this design from the sorta-ok zone to wow! I think you may already be in the wow zone for taking load off a server and I know of applications where an NFS server gets hammered so badly that having the client suck a little in the unloaded case is a price worth paying. But the whole idea would be much more attractive if the But looking up the object in the cache should be nearly free - much less than a microsecond per block. If not then there are design issues. I suspect that you are doing yourself a disservice by going all the way So without the persistent cache it can omit the LOOKUP and just send the Doesn't that just mean you have to preload the lookup table for the persistent cache so you can determine whether you are caching the data Ah I should have read ahead. I think the correct answer is "a lot". Your big can-t-get-there-from-here is the round trip to the server to determine whether you should read from the local cache. Got any ideas? And where is the Trond-meister in all of this? Regards, Daniel -
One thing that you have to remember: my test setup is pretty much the worst-case for being appropriate for showing the need for caching to improve performance. There's a single client and a single server, they've got GigE networking between them that has very little other load, and the server has Not really, it's just that this lashup could be considered designed to show The problem is that you have to do a database lookup of some sort, possibly involving several synchronous disk operations. CacheFiles does a disk lookup by taking the key given to it by NFS, turning it into a set of file or directory names, and doing a short pathwalk to the target cache file. Throwing in extra indices won't necessarily help. What matters is how quick the backing filesystem is at doing lookups. As it turns out, Ext3 is What 'it'? Note that the get the filehandle, you have to do a LOOKUP op. With the cache, we could actually cache the results of lookups that we've done, however, we don't know that the results are still valid without going to the server:-/ Where "lookup table" == "dcache". That would be good yes. cachefilesd prescans all the files in the cache, which ought to do just that, but it Quite possibly. It'll allow me to dispense with at least one fs lookup call I'm not sure what you mean. Your statement should probably read "... to Keeping quiet as far as I can tell. David -
Right, so the obvious optimization strategy for this corner of it is to decimate the synchronous disk ops for the average case, for which there All understood. I am eventually going to suggest cutting the backing filesystem entirely out of the picture, with a view to improving both efficiency and transparency, hopefully with a code size reduction as well. But you are up and running with the filesystem approach, enough to tackle the basic algorithm questions, which is worth a lot. I really do not like idea of force fitting this cache into a generic vfs model. Sun was collectively smoking some serious crack when they cooked that one up. But there is also the ageless principle "isness is Which would require a change to NFS, not an option because you hope to work with standard servers? Of course with years to think about this, the required protocol changes were put into v4. Not. /me hopes for an NFS hack to show up and explain the thinking there Actually, there are many situations where changing both the client (you must do that anyway) and the server is logistically practical. In fact that is true for all actual use cases I know of for this cache model. So elaborating the protocol is not an option to reject out of hand. A hack along those lines could (should?) be provided as an opportunistic option. Have you completely exhausted optimization ideas for the file handle What I tried to say. So still... got any ideas? That extra synchronous network round trip is a killer. Can it be made streaming/async to keep /me does the Trond summoning dance Daniel -
You still need a database to manage the cache. A filesystem such as Ext3
makes a very handy database for four reasons:
(1) It exists and works.
(2) It has a well defined interface within the kernel.
(3) I can place my cache on, say, my root partition on my laptop. I don't
have to dedicate a partition to the cache.
(4) Userspace cache management tools (such as cachefilesd) have an already
existing interface to use: rmdir, unlink, open, getdents, etc..
I do have a cache-on-blockdev thing, but it's basically a wandering tree
filesystem inside. It is, or was, much faster than ext3 on a clean cache, but
it degrades horribly over time because my free space reclamation sucks - it
gradually randomises the block allocation sequence over time.
What do you mean? I'm not doing it like Sun. The cache is a side path from
the netfs. It should be transparent to the user, the VFS and the server.
The only place it might not be transparent is that you might to have to
instruct the netfs mount to use the cache. I'd prefer to do it some other way
than passing parameters to mount, though, as (1) this causes fun with NIS
distributed automounter maps, and (2) people are asking for a finer grain of
control than per-mountpoint. Unfortunately, I can't seem to find a way to do
I don't think there's much I can do about NFS. It requires the filesystem
from which the NFS server is dealing to have inode uniquifiers, which are then
incorporated into the file handle. I don't think the NFS protocol itself
No, but there aren't many. CacheFiles doesn't actually do very much, and it's
hard to reduce that not very much. The most obvious thing is to prepopulate
the dcache, but that's at the expense of memory usage.
Actually, if I cache the name => FH mapping I used last time, I can make a
start on looking up in the cache whilst simultaneously accessing the server.
If what's on the server has changed, I can ditch the speculative cache lookup
I was making and start a new cac...Well, the AFS paper that was referenced earlier was written around the time of 10bt and 100bt. Local disk caching worked well then. There should also be some papers at CITI about disk caching over slower connections, and disconnected operation (which should still be applicable today). There are still winners from local disk caching, but their numbers have been reduced. Server load reduction should be a win. I'm not sure if it's worth it from a security/manageability standpoint, but I haven't looked that closely at David's code. -Dan -----Original Message----- From: Daniel Phillips [mailto:phillips@phunq.net] Sent: Thursday, February 21, 2008 2:44 PM To: David Howells Cc: Myklebust, Trond; nfsv4@linux-nfs.org; linux-kernel@vger.kernel.org; linux-fsdevel@vger.kernel.org; linux-security-module@vger.kernel.org; selinux@tycho.nsa.gov; casey@schaufler-ca.com Subject: Re: [PATCH 00/37] Permit filesystem local caching Hi David, I am trying to spot the numbers that show the sweet spot for this optimization, without much success so far. Who is supposed to win big? Is this mainly about reducing the load on the server, or is the client supposed to win even with a lightly loaded server? When you say Ext3 cache vs NFS cache is the first on the server and the second on the client? Regards, Daniel _______________________________________________ NFSv4 mailing list NFSv4@linux-nfs.org http://linux-nfs.org/cgi-bin/mailman/listinfo/nfsv4 -
Hi David, Your results remind me of this in case you're interested... http://www.citi.umich.edu/techreports/reports/citi-tr-92-3.pdf -
I need to get a new hard drive for my test machine before I can go and get some more up to date benchmark results. It does seem, however, that the I/O error handling capabilities of FS-Cache work properly:-) David -
Seems *really* weird that every time you send this, patch 6 doesn't seem to reach me in any of my mailboxes... (did get it from the url you listed) I'm sorry if I miss where you explicitly state this, but is it safe to assume, as perusing the patches suggests, that 1. tsk->sec never changes other than in task_alloc_security()? 2. tsk->act_as is only ever dereferenced from (a) current-> except (b) in do_coredump? (thereby carefully avoiding locking issues) I'd still like to see some performance numbers. Not to object to these patches, just to make sure there's no need to try and optimize more of the dereferences away when they're not needed. Oh, manually copied from patch 6, I see you have in the task_security struct definition: kernel_cap_t cap_bset; /* ? */ That comment can be filled in with 'capability bounding set' (for this task and all its future descendents). thanks, -
It's the largest of the patches, so that's not entirely surprising. Hence why I hope that the performance impact is minimal. The kernel should spend very Thanks. David -
Add read context retention so that FS-Cache can call back into NFS when a read
operation on the cache fails EIO rather than reading data. This permits NFS to
then fetch the data from the server instead using the appropriate security
context.
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/nfs/fscache-index.c | 26 ++++++++++++++++++++++++++
1 files changed, 26 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/fscache-index.c b/fs/nfs/fscache-index.c
index eec8e7e..af9f06b 100644
--- a/fs/nfs/fscache-index.c
+++ b/fs/nfs/fscache-index.c
@@ -285,6 +285,30 @@ static void nfs_cache_inode_now_uncached(void *cookie_netfs_data)
}
/*
+ * Get an extra reference on a read context.
+ * - This function can be absent if the completion function doesn't require a
+ * context.
+ * - The read context is passed back to NFS in the event that a data read on the
+ * cache fails with EIO - in which case the server must be contacted to
+ * retrieve the data, which requires the read context for security.
+ */
+static void nfs_fh_get_context(void *cookie_netfs_data, void *context)
+{
+ get_nfs_open_context(context);
+}
+
+/*
+ * Release an extra reference on a read context.
+ * - This function can be absent if the completion function doesn't require a
+ * context.
+ */
+static void nfs_fh_put_context(void *cookie_netfs_data, void *context)
+{
+ if (context)
+ put_nfs_open_context(context);
+}
+
+/*
* Define the inode object for FS-Cache. This is used to describe an inode
* object to fscache_acquire_cookie(). It is keyed by the NFS file handle for
* an inode.
@@ -301,4 +325,6 @@ const struct fscache_cookie_def nfs_cache_inode_object_def = {
.get_aux = nfs_cache_inode_get_aux,
.check_aux = nfs_cache_inode_check_aux,
.now_uncached = nfs_cache_inode_now_uncached,
+ .get_context = nfs_fh_get_context,
+ .put_context = nfs_fh_put_context,
};
-Read pages from an FS-Cache data storage object representing an inode into an
NFS inode.
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/nfs/fscache.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/nfs/fscache.h | 47 +++++++++++++++++++++++
fs/nfs/read.c | 18 +++++++++
3 files changed, 176 insertions(+), 1 deletions(-)
diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c
index d475ff5..438cc9b 100644
--- a/fs/nfs/fscache.c
+++ b/fs/nfs/fscache.c
@@ -344,5 +344,115 @@ void __nfs_fscache_invalidate_page(struct page *page, struct inode *inode)
BUG_ON(!PageLocked(page));
fscache_uncache_page(nfsi->fscache, page);
- nfs_add_stats(page->mapping->host, NFSIOS_FSCACHE_UNCACHE, 1);
+ nfs_add_stats(inode, NFSIOS_FSCACHE_UNCACHE, 1);
+}
+
+/*
+ * Handle completion of a page being read from the cache.
+ * - Called in process (keventd) context.
+ */
+static void nfs_readpage_from_fscache_complete(struct page *page,
+ void *context,
+ int error)
+{
+ dfprintk(FSCACHE,
+ "NFS: readpage_from_fscache_complete (0x%p/0x%p/%d)\n",
+ page, context, error);
+
+ /* if the read completes with an error, we just unlock the page and let
+ * the VM reissue the readpage */
+ if (!error) {
+ SetPageUptodate(page);
+ unlock_page(page);
+ } else {
+ error = nfs_readpage_async(context, page->mapping->host, page);
+ if (error)
+ unlock_page(page);
+ }
+}
+
+/*
+ * Retrieve a page from fscache
+ */
+int __nfs_readpage_from_fscache(struct nfs_open_context *ctx,
+ struct inode *inode, struct page *page)
+{
+ int ret;
+
+ dfprintk(FSCACHE,
+ "NFS: readpage_from_fscache(fsc:%p/p:%p(i:%lx f:%lx)/0x%p)\n",
+ NFS_I(inode)->fscache, page, page->index, page->flags, inode);
+
+ ret = fscache_read_or_alloc_page(NFS_I(inode)->fscache,
+ page,
+ nfs_readpage_from_fscache_complete,
+ ctx,
+ GFP_KERNEL);
+
+ switch (ret) {
+ case 0: /* read BIO submitted (page ...Add an address space operation to write one single page of data to an inode at
a page-aligned location (thus permitting the implementation to be highly
optimised). The data source is a single page.
This is used by CacheFiles to store the contents of netfs pages into their
backing file pages.
Supply a generic implementation for this that uses the write_begin() and
write_end() address_space operations to bind a copy directly into the page
cache.
Hook the Ext2 and Ext3 operations to the generic implementation.
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/ext2/inode.c | 2 ++
fs/ext3/inode.c | 3 +++
include/linux/fs.h | 7 ++++++
mm/filemap.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 73 insertions(+), 0 deletions(-)
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index c620068..f483014 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -792,6 +792,7 @@ const struct address_space_operations ext2_aops = {
.direct_IO = ext2_direct_IO,
.writepages = ext2_writepages,
.migratepage = buffer_migrate_page,
+ .write_one_page = generic_file_buffered_write_one_page,
};
const struct address_space_operations ext2_aops_xip = {
@@ -810,6 +811,7 @@ const struct address_space_operations ext2_nobh_aops = {
.direct_IO = ext2_direct_IO,
.writepages = ext2_writepages,
.migratepage = buffer_migrate_page,
+ .write_one_page = generic_file_buffered_write_one_page,
};
/*
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index c976123..0209f3b 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -1776,6 +1776,7 @@ static const struct address_space_operations ext3_ordered_aops = {
.releasepage = ext3_releasepage,
.direct_IO = ext3_direct_IO,
.migratepage = buffer_migrate_page,
+ .write_one_page = generic_file_buffered_write_one_page,
};
static const struct address_space_operations ext3_writeback_aops = {
@@ -1790,6 +1791,7 @@ static const struct address_space_operations...Define and create server-level cache index objects (as managed by nfs_client structs). Each server object is created in the NFS top-level index object and is itself an index into which superblock-level objects are inserted. Ideally there would be one superblock-level object per server, and the former would be folded into the latter; however, since the "nosharecache" option exists this isn't possible. The server object key is a sequence consisting of: (1) NFS version (2) Server address family (eg: AF_INET or AF_INET6) (3) Server port. (4) Server IP address. The key blob is of variable length, depending on the length of (4). The server object is given no coherency data to carry in the auxiliary data permitted by the cache. Signed-off-by: David Howells <dhowells@redhat.com> --- fs/nfs/Makefile | 2 + fs/nfs/client.c | 5 +++ fs/nfs/fscache-index.c | 65 +++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/fscache.c | 52 ++++++++++++++++++++++++++++++++++++ fs/nfs/fscache.h | 10 +++++++ include/linux/nfs_fs_sb.h | 4 +++ 6 files changed, 137 insertions(+), 1 deletions(-) create mode 100644 fs/nfs/fscache.c diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 6d7176d..d848c97 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -16,4 +16,4 @@ nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ nfs4namespace.o nfs-$(CONFIG_NFS_DIRECTIO) += direct.o nfs-$(CONFIG_SYSCTL) += sysctl.o -nfs-$(CONFIG_NFS_FSCACHE) += fscache-index.o +nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o diff --git a/fs/nfs/client.c b/fs/nfs/client.c index c5c0175..51e9346 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -45,6 +45,7 @@ #include "delegation.h" #include "iostat.h" #include "internal.h" +#include "fscache.h" #define NFSDBG_FACILITY NFSDBG_CLIENT @@ -151,6 +152,8 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ ...
Store pages from an NFS inode into the cache data storage object associated
with that inode.
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/nfs/fscache.c | 26 ++++++++++++++++++++++++++
fs/nfs/fscache.h | 16 ++++++++++++++++
fs/nfs/read.c | 5 +++++
3 files changed, 47 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c
index 438cc9b..50ae70f 100644
--- a/fs/nfs/fscache.c
+++ b/fs/nfs/fscache.c
@@ -456,3 +456,29 @@ int __nfs_readpages_from_fscache(struct nfs_open_context *ctx,
return ret;
}
+
+/*
+ * Store a newly fetched page in fscache
+ * - PG_fscache must be set on the page
+ */
+void __nfs_readpage_to_fscache(struct inode *inode, struct page *page, int sync)
+{
+ int ret;
+
+ dfprintk(FSCACHE,
+ "NFS: readpage_to_fscache(fsc:%p/p:%p(i:%lx f:%lx)/%d)\n",
+ NFS_I(inode)->fscache, page, page->index, page->flags, sync);
+
+ ret = fscache_write_page(NFS_I(inode)->fscache, page, GFP_KERNEL);
+ dfprintk(FSCACHE,
+ "NFS: readpage_to_fscache: p:%p(i:%lu f:%lx) ret %d\n",
+ page, page->index, page->flags, ret);
+
+ if (ret != 0) {
+ fscache_uncache_page(NFS_I(inode)->fscache, page);
+ nfs_add_stats(inode, NFSIOS_FSCACHE_WRITE_FAIL, 1);
+ nfs_add_stats(inode, NFSIOS_FSCACHE_UNCACHE, 1);
+ } else {
+ nfs_add_stats(inode, NFSIOS_FSCACHE_WRITE_OK, 1);
+ }
+}
diff --git a/fs/nfs/fscache.h b/fs/nfs/fscache.h
index 4c1e1a8..6264cd8 100644
--- a/fs/nfs/fscache.h
+++ b/fs/nfs/fscache.h
@@ -94,6 +94,7 @@ extern int __nfs_readpage_from_fscache(struct nfs_open_context *,
extern int __nfs_readpages_from_fscache(struct nfs_open_context *,
struct inode *, struct address_space *,
struct list_head *, unsigned *);
+extern void __nfs_readpage_to_fscache(struct inode *, struct page *, int);
/*
* release the caching state associated with a page if undergoing complete page
@@ -133,6 +134,19 @@ static inline int nfs_readpages_from_fscache(struct nfs_open_context *c...Add some new NFS I/O event counters for FS-Cache events. They have to be
added as byte counters because I may need to be able to increase the numbers
by more than 1 at a time.
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/nfs/iostat.h | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/iostat.h b/fs/nfs/iostat.h
index 6350ecb..0e3b170 100644
--- a/fs/nfs/iostat.h
+++ b/fs/nfs/iostat.h
@@ -60,6 +60,13 @@ enum nfs_stat_bytecounters {
NFSIOS_SERVERWRITTENBYTES,
NFSIOS_READPAGES,
NFSIOS_WRITEPAGES,
+#ifdef CONFIG_NFS_FSCACHE
+ NFSIOS_FSCACHE_READ_OK,
+ NFSIOS_FSCACHE_READ_FAIL,
+ NFSIOS_FSCACHE_WRITE_OK,
+ NFSIOS_FSCACHE_WRITE_FAIL,
+ NFSIOS_FSCACHE_UNCACHE,
+#endif
__NFSIOS_BYTESMAX,
};
-Add NFS mount options to allow the local caching support to be enabled. The attached patch makes it possible for the NFS filesystem to be told to make use of the network filesystem local caching service (FS-Cache). To be able to use this, a recent nfsutils package is required. There are three variant NFS mount options that can be added to a mount command to control caching for a mount. Only the last one specified takes effect: (*) Adding "fsc" will request caching. (*) Adding "fsc=<string>" will request caching and also specify a uniquifier. (*) Adding "nofsc" will disable caching. For example: mount warthog:/ /a -o fsc The cache of a particular superblock (NFS FSID) will be shared between all mounts of that volume, provided they have the same connection parameters and are not marked 'nosharecache'. Where it is otherwise impossible to distinguish superblocks because all the parameters are identical, but the 'nosharecache' option is supplied, a uniquifying string must be supplied, else only the first mount will be permitted to use the cache. If there's a key collision, then the second mount will disable caching and give a warning into the kernel log. Signed-off-by: David Howells <dhowells@redhat.com> --- fs/nfs/client.c | 2 ++ fs/nfs/internal.h | 1 + fs/nfs/super.c | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 0 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index d67d52f..8357f68 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -669,6 +669,7 @@ static int nfs_init_server(struct nfs_server *server, /* Initialise the client representation from the mount data */ server->flags = data->flags & NFS_MOUNT_FLAGMASK; + server->options = data->options; if (data->rsize) server->rsize = nfs_block_size(data->rsize, NULL); @@ -1056,6 +1057,7 @@ static int nfs4_init_server(struct nfs_server *server, /* Initialise the client representation from th...
FS-Cache page management for NFS. This includes hooking the releasing and
invalidation of pages marked with PG_fscache (aka PG_private_2) and waiting for
completion of the write-to-cache flag (PG_fscache_write aka PG_owner_priv_2).
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/nfs/file.c | 17 +++++++++++++----
fs/nfs/fscache.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
fs/nfs/fscache.h | 22 ++++++++++++++++++++++
3 files changed, 84 insertions(+), 4 deletions(-)
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 26a073b..60db3ea 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -35,6 +35,7 @@
#include "delegation.h"
#include "internal.h"
#include "iostat.h"
+#include "fscache.h"
#define NFSDBG_FACILITY NFSDBG_FILE
@@ -358,7 +359,7 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
* Partially or wholly invalidate a page
* - Release the private state associated with a page if undergoing complete
* page invalidation
- * - Called if either PG_private or PG_private_2 is set on the page
+ * - Called if either PG_private or PG_fscache is set on the page
* - Caller holds page lock
*/
static void nfs_invalidate_page(struct page *page, unsigned long offset)
@@ -367,30 +368,35 @@ static void nfs_invalidate_page(struct page *page, unsigned long offset)
return;
/* Cancel any unstarted writes on this page */
nfs_wb_page_cancel(page->mapping->host, page);
+
+ nfs_fscache_invalidate_page(page, page->mapping->host);
}
/*
* Attempt to release the private state associated with a page
- * - Called if either PG_private or PG_private_2 is set on the page
+ * - Called if either PG_private or PG_fscache is set on the page
* - Caller holds page lock
* - Return true (may release page) or false (may not)
*/
static int nfs_release_page(struct page *page, gfp_t gfp)
{
/* If PagePrivate() is set, then the page is not freeable */
- return 0;
+ if (PagePrivate(pag...Recruit a couple of page flags to aid in cache management. The following extra
flags are defined:
(1) PG_fscache (PG_private_2)
The marked page is backed by a local cache and is pinning resources in the
cache driver.
(2) PG_fscache_write (PG_owner_priv_2)
The marked page is being written to the local cache. The page may not be
modified whilst this is in progress.
If PG_fscache is set, then things that checked for PG_private will now also
check for that. This includes things like truncation and page invalidation.
The function page_has_private() had been added to make the checks for both
PG_private and PG_private_2 at the same time.
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/splice.c | 2 +-
include/linux/page-flags.h | 39 +++++++++++++++++++++++++++++++++++++--
include/linux/pagemap.h | 11 +++++++++++
mm/filemap.c | 18 ++++++++++++++++++
mm/migrate.c | 2 +-
mm/page_alloc.c | 3 +++
mm/readahead.c | 9 +++++----
mm/swap.c | 4 ++--
mm/swap_state.c | 4 ++--
mm/truncate.c | 10 +++++-----
mm/vmscan.c | 2 +-
11 files changed, 86 insertions(+), 18 deletions(-)
diff --git a/fs/splice.c b/fs/splice.c
index 9b559ee..f2a7a06 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -58,7 +58,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe,
*/
wait_on_page_writeback(page);
- if (PagePrivate(page))
+ if (page_has_private(page))
try_to_release_page(page, GFP_KERNEL);
/*
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index bbad43f..cc16c23 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -77,25 +77,32 @@
#define PG_active 6
#define PG_slab 7 /* slab debug (Suparna wants this) */
-#define PG_owner_priv_1 8 /* Owner use. If pagecache, fs may use*/
+#define PG_owner_priv_1...This one-line patch fixes the missing export of copy_page introduced by the cachefile patches. This patch is not yet upstream, but is required for cachefile on ia64. It will be pushed upstream when cachefile goes upstream. Signed-off-by: Prarit Bhargava <prarit@redhat.com> Signed-off-by: David Howells <dhowells@redhat.com> --- arch/ia64/kernel/ia64_ksyms.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/arch/ia64/kernel/ia64_ksyms.c b/arch/ia64/kernel/ia64_ksyms.c index 8e7193d..3e544f4 100644 --- a/arch/ia64/kernel/ia64_ksyms.c +++ b/arch/ia64/kernel/ia64_ksyms.c @@ -46,6 +46,7 @@ EXPORT_SYMBOL(__do_clear_user); EXPORT_SYMBOL(__strlen_user); EXPORT_SYMBOL(__strncpy_from_user); EXPORT_SYMBOL(__strnlen_user); +EXPORT_SYMBOL(copy_page); /* from arch/ia64/lib */ extern void __divsi3(void); -
nfs_readpage_async() needs to be non-static so that it can be used as a
fallback for the local on-disk caching should an EIO crop up when reading the
cache.
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/nfs/read.c | 4 ++--
include/linux/nfs_fs.h | 2 ++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 3d7d963..725a5a2 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -114,8 +114,8 @@ static void nfs_readpage_truncate_uninitialised_page(struct nfs_read_data *data)
}
}
-static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
- struct page *page)
+int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
+ struct page *page)
{
LIST_HEAD(one_request);
struct nfs_page *new;
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index d9adb53..d1d545e 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -505,6 +505,8 @@ extern int nfs_readpages(struct file *, struct address_space *,
struct list_head *, unsigned);
extern int nfs_readpage_result(struct rpc_task *, struct nfs_read_data *);
extern void nfs_readdata_release(void *data);
+extern int nfs_readpage_async(struct nfs_open_context *, struct inode *,
+ struct page *);
/*
* Allocate nfs_read_data structures
-Display the local caching state in /proc/fs/nfsfs/volumes.
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/nfs/client.c | 7 ++++---
fs/nfs/fscache.h | 15 +++++++++++++++
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 51e9346..d67d52f 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -1451,7 +1451,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)
/* display header on line 1 */
if (v == &nfs_volume_list) {
- seq_puts(m, "NV SERVER PORT DEV FSID\n");
+ seq_puts(m, "NV SERVER PORT DEV FSID FSC\n");
return 0;
}
/* display one transport per line on subsequent lines */
@@ -1465,12 +1465,13 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)
(unsigned long long) server->fsid.major,
(unsigned long long) server->fsid.minor);
- seq_printf(m, "v%u %s %s %-7s %-17s\n",
+ seq_printf(m, "v%u %s %s %-7s %-17s %s\n",
clp->rpc_ops->version,
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR),
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT),
dev,
- fsid);
+ fsid,
+ nfs_server_fscache_state(server));
return 0;
}
diff --git a/fs/nfs/fscache.h b/fs/nfs/fscache.h
index 6264cd8..5f7806f 100644
--- a/fs/nfs/fscache.h
+++ b/fs/nfs/fscache.h
@@ -146,6 +146,16 @@ static inline void nfs_readpage_to_fscache(struct inode *inode,
__nfs_readpage_to_fscache(inode, page, sync);
}
+/*
+ * indicate the client caching state as readable text
+ */
+static inline const char *nfs_server_fscache_state(struct nfs_server *server)
+{
+ if (server->fscache && (server->options & NFS_OPTION_FSCACHE))
+ return "yes";
+ return "no ";
+}
+
#else /* CONFIG_NFS_FSCACHE */
static inline int nfs_fscache_register(void) { return 0; }
@@ -195,5 +205,10 @@ static inline int nfs_readpages_from_fscache(struct nfs_open_context *ctx,
static inli...Add a function to install a monitor on the page lock waitqueue for a particular
page, thus allowing the page being unlocked to be detected.
This is used by CacheFiles to detect read completion on a page in the backing
filesystem so that it can then copy the data to the waiting netfs page.
Signed-off-by: David Howells <dhowells@redhat.com>
---
include/linux/pagemap.h | 5 +++++
mm/filemap.c | 18 ++++++++++++++++++
2 files changed, 23 insertions(+), 0 deletions(-)
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index c8bd762..76b5307 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -242,6 +242,11 @@ static inline void wait_on_page_owner_priv_2(struct page *page)
extern void end_page_owner_priv_2(struct page *page);
/*
+ * Add an arbitrary waiter to a page's wait queue
+ */
+extern void add_page_wait_queue(struct page *page, wait_queue_t *waiter);
+
+/*
* Fault a userspace page into pagetables. Return non-zero on a fault.
*
* This assumes that two userspace pages are always sufficient. That's
diff --git a/mm/filemap.c b/mm/filemap.c
index a583f44..561e6c7 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -548,6 +548,24 @@ void wait_on_page_bit(struct page *page, int bit_nr)
EXPORT_SYMBOL(wait_on_page_bit);
/**
+ * add_page_wait_queue - Add an arbitrary waiter to a page's wait queue
+ * @page - Page defining the wait queue of interest
+ * @waiter - Waiter to add to the queue
+ *
+ * Add an arbitrary @waiter to the wait queue for the nominated @page.
+ */
+void add_page_wait_queue(struct page *page, wait_queue_t *waiter)
+{
+ wait_queue_head_t *q = page_waitqueue(page);
+ unsigned long flags;
+
+ spin_lock_irqsave(&q->lock, flags);
+ __add_wait_queue(q, waiter);
+ spin_unlock_irqrestore(&q->lock, flags);
+}
+EXPORT_SYMBOL_GPL(add_page_wait_queue);
+
+/**
* unlock_page - unlock a locked page
* @page: the page
*
-Invalidate the FsCache page flags on the pages belonging to an inode when the
cache backing that NFS inode is removed.
This allows a live cache to be withdrawn.
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/nfs/fscache-index.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 files changed, 40 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/fscache-index.c b/fs/nfs/fscache-index.c
index c3c63fa..eec8e7e 100644
--- a/fs/nfs/fscache-index.c
+++ b/fs/nfs/fscache-index.c
@@ -246,6 +246,45 @@ static enum fscache_checkaux nfs_cache_inode_check_aux(void *cookie_netfs_data,
}
/*
+ * Indication from FS-Cache that the cookie is no longer cached
+ * - This function is called when the backing store currently caching a cookie
+ * is removed
+ * - The netfs should use this to clean up any markers indicating cached pages
+ * - This is mandatory for any object that may have data
+ */
+static void nfs_cache_inode_now_uncached(void *cookie_netfs_data)
+{
+ struct nfs_inode *nfsi = cookie_netfs_data;
+ struct pagevec pvec;
+ pgoff_t first;
+ int loop, nr_pages;
+
+ pagevec_init(&pvec, 0);
+ first = 0;
+
+ dprintk("NFS: nfs_inode_now_uncached: nfs_inode 0x%p\n", nfsi);
+
+ for (;;) {
+ /* grab a bunch of pages to unmark */
+ nr_pages = pagevec_lookup(&pvec,
+ nfsi->vfs_inode.i_mapping,
+ first,
+ PAGEVEC_SIZE - pagevec_count(&pvec));
+ if (!nr_pages)
+ break;
+
+ for (loop = 0; loop < nr_pages; loop++)
+ ClearPageFsCache(pvec.pages[loop]);
+
+ first = pvec.pages[nr_pages - 1]->index + 1;
+
+ pvec.nr = nr_pages;
+ pagevec_release(&pvec);
+ cond_resched();
+ }
+}
+
+/*
* Define the inode object for FS-Cache. This is used to describe an inode
* object to fscache_acquire_cookie(). It is keyed by the NFS file handle for
* an inode.
@@ -261,4 +300,5 @@ const struct fscache_cookie_def nfs_cache_inode_object_def = {
.get_attr = nfs_cache_inode_get_attr,
.get_aux = nfs_cache_inode...Change all the usages of file->f_mapping in ext3_*write_end() functions to use
the mapping argument directly. This has two consequences:
(*) Consistency. Without this patch sometimes one is used and sometimes the
other is.
(*) A NULL file pointer can be passed. This feature is then made use of by
the generic hook in the next patch, which is used by CacheFiles to write
pages to a file without setting up a file struct.
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/ext3/inode.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index eb95670..c976123 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -1215,7 +1215,7 @@ static int ext3_generic_write_end(struct file *file,
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata)
{
- struct inode *inode = file->f_mapping->host;
+ struct inode *inode = mapping->host;
copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
@@ -1240,7 +1240,7 @@ static int ext3_ordered_write_end(struct file *file,
struct page *page, void *fsdata)
{
handle_t *handle = ext3_journal_current_handle();
- struct inode *inode = file->f_mapping->host;
+ struct inode *inode = mapping->host;
unsigned from, to;
int ret = 0, ret2;
@@ -1281,7 +1281,7 @@ static int ext3_writeback_write_end(struct file *file,
struct page *page, void *fsdata)
{
handle_t *handle = ext3_journal_current_handle();
- struct inode *inode = file->f_mapping->host;
+ struct inode *inode = mapping->host;
int ret = 0, ret2;
loff_t new_i_size;
-Remove the temporarily embedded task security record from task_struct. Instead it is made to dangle from the task_struct::sec and task_struct::act_as pointers with references counted for each. do_coredump() is made to create a copy of the security record, modify it and then use that to override the main one for a task. sys_faccessat() is made to do the same. The process and session keyrings are moved from signal_struct into a new thread_group_security struct. This is then refcounted, with pointers coming from the task_security struct instead of from signal_struct. The keyring functions then take pointers to task_security structs rather than task_structs for their security contexts. This is so that request_key() can proceed asynchronously without having to worry about the initiator task's act_as pointer changing. The LSM hooks for dealing with task security are modified to deal with the task security struct directly rather than going via the task_struct as appopriate. This permits the subjective security context of a task to be overridden by changing its act_as pointer without altering its objective security pointer, and thus not breaking signalling, ptrace, etc. whilst the override is in force. Signed-off-by: David Howells <dhowells@redhat.com> --- fs/exec.c | 15 +- fs/open.c | 37 ++--- include/linux/init_task.h | 18 -- include/linux/key-ui.h | 10 + include/linux/key.h | 31 +--- include/linux/sched.h | 40 ++++- include/linux/security.h | 43 ++++- kernel/Makefile | 2 kernel/cred.c | 140 ++++++++++++++++++ kernel/exit.c | 1 kernel/fork.c | 40 ++--- kernel/kmod.c | 10 + kernel/sys.c | 16 +- kernel/user.c | 2 net/rxrpc/ar-key.c | 4 - security/dummy.c ...
No objections from the Smack side. Thank you. Casey Schaufler casey@schaufler-ca.com -
Add a keyctl() function to get the security label of a key.
The following is added to Documentation/keys.txt:
(*) Get the LSM security context attached to a key.
long keyctl(KEYCTL_GET_SECURITY, key_serial_t key, char *buffer,
size_t buflen)
This function returns a string that represents the LSM security context
attached to a key in the buffer provided.
Unless there's an error, it always returns the amount of data it could
produce, even if that's too big for the buffer, but it won't copy more
than requested to userspace. If the buffer pointer is NULL then no copy
will take place.
A NUL character is included at the end of the string if the buffer is
sufficiently big. This is included in the returned count. If no LSM is
in force then an empty string will be returned.
A process must have view permission on the key for this function to be
successful.
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Stephen Smalley <sds@tycho.nsa.gov>
---
Documentation/keys.txt | 21 +++++++++++++++
include/linux/keyctl.h | 1 +
include/linux/security.h | 20 +++++++++++++-
security/dummy.c | 8 ++++++
security/keys/compat.c | 3 ++
security/keys/keyctl.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++
security/security.c | 5 +++
security/selinux/hooks.c | 21 +++++++++++++--
8 files changed, 141 insertions(+), 4 deletions(-)
diff --git a/Documentation/keys.txt b/Documentation/keys.txt
index b82d38d..be424b0 100644
--- a/Documentation/keys.txt
+++ b/Documentation/keys.txt
@@ -711,6 +711,27 @@ The keyctl syscall functions are:
The assumed authoritative key is inherited across fork and exec.
+ (*) Get the LSM security context attached to a key.
+
+ long keyctl(KEYCTL_GET_SECURITY, key_serial_t key, char *buffer,
+ size_t buflen)
+
+ This function returns a string that represents the LSM security context
+ attached t...Define and create superblock-level cache index objects (as managed by
nfs_server structs).
Each superblock object is created in a server level index object and is itself
an index into which inode-level objects are inserted.
Ideally there would be one superblock-level object per server, and the former
would be folded into the latter; however, since the "nosharecache" option
exists this isn't possible.
The superblock object key is a sequence consisting of:
(1) Certain superblock s_flags.
(2) Various connection parameters that serve to distinguish superblocks for
sget().
(3) The volume FSID.
(4) The security flavour.
(5) The uniquifier length.
(6) The uniquifier text. This is normally an empty string, unless the fsc=xyz
mount option was used to explicitly specify a uniquifier.
The key blob is of variable length, depending on the length of (6).
The superblock object is given no coherency data to carry in the auxiliary data
permitted by the cache. It is assumed that the superblock is always coherent.
This patch also adds uniquification handling such that two otherwise identical
superblocks, at least one of which is marked "nosharecache", won't end up
trying to share the on-disk cache. It will be possible to manually provide a
uniquifier through a mount option with a later patch to avoid the error
otherwise produced.
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/nfs/fscache-index.c | 34 +++++++++++++
fs/nfs/fscache.c | 116 +++++++++++++++++++++++++++++++++++++++++++++
fs/nfs/fscache.h | 49 +++++++++++++++++++
fs/nfs/internal.h | 3 +
fs/nfs/super.c | 8 ++-
include/linux/nfs_fs_sb.h | 5 ++
6 files changed, 213 insertions(+), 2 deletions(-)
diff --git a/fs/nfs/fscache-index.c b/fs/nfs/fscache-index.c
index 25ac4a1..b5a52e3 100644
--- a/fs/nfs/fscache-index.c
+++ b/fs/nfs/fscache-index.c
@@ -116,3 +116,37 @@ const struct fscache_cookie_def nfs_cache_server_...Bind data storage objects in the local cache to NFS inodes.
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/nfs/fscache.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++
fs/nfs/fscache.h | 19 +++++++
fs/nfs/inode.c | 39 ++++++++++++--
include/linux/nfs_fs.h | 10 ++++
4 files changed, 193 insertions(+), 6 deletions(-)
diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c
index cbd09f0..c0e0320 100644
--- a/fs/nfs/fscache.c
+++ b/fs/nfs/fscache.c
@@ -166,3 +166,134 @@ void nfs_fscache_release_super_cookie(struct super_block *sb)
nfss->fscache_key = NULL;
}
}
+
+/*
+ * Initialise the per-inode cache cookie pointer for an NFS inode.
+ */
+void nfs_fscache_init_inode_cookie(struct inode *inode)
+{
+ NFS_I(inode)->fscache = NULL;
+ if (S_ISREG(inode->i_mode))
+ set_bit(NFS_INO_FSCACHE, &NFS_I(inode)->flags);
+}
+
+/*
+ * Get the per-inode cache cookie for an NFS inode.
+ */
+void nfs_fscache_enable_inode_cookie(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct nfs_inode *nfsi = NFS_I(inode);
+
+ if (nfsi->fscache || !NFS_FSCACHE(inode))
+ return;
+
+ if ((NFS_SB(sb)->options & NFS_OPTION_FSCACHE)) {
+ nfsi->fscache = fscache_acquire_cookie(
+ NFS_SB(sb)->fscache,
+ &nfs_cache_inode_object_def,
+ nfsi);
+
+ dfprintk(FSCACHE, "NFS: get FH cookie (0x%p/0x%p/0x%p)\n",
+ sb, nfsi, nfsi->fscache);
+ }
+}
+
+/*
+ * Release a per-inode cookie.
+ */
+void nfs_fscache_release_inode_cookie(struct inode *inode)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+
+ dfprintk(FSCACHE, "NFS: clear cookie (0x%p/0x%p)\n",
+ nfsi, nfsi->fscache);
+
+ fscache_relinquish_cookie(nfsi->fscache, 0);
+ nfsi->fscache = NULL;
+}
+
+/*
+ * Retire a per-inode cookie, destroying the data attached to it.
+ */
+void nfs_fscache_zap_inode_cookie(struct inode *inode)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+
+ dfprintk(FSCACHE, "NFS: z...Add FS-Cache option bit to nfs_server struct. This is set to indicate local
on-disk caching is enabled for a particular superblock.
Also add debug bit for local caching operations.
Signed-off-by: David Howells <dhowells@redhat.com>
---
include/linux/nfs_fs.h | 1 +
include/linux/nfs_fs_sb.h | 2 ++
2 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index a69ba80..14894c9 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -578,6 +578,7 @@ extern void * nfs_root_data(void);
#define NFSDBG_CALLBACK 0x0100
#define NFSDBG_CLIENT 0x0200
#define NFSDBG_MOUNT 0x0400
+#define NFSDBG_FSCACHE 0x0800
#define NFSDBG_ALL 0xFFFF
#ifdef __KERNEL__
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 3423c67..e7c4cdd 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -99,6 +99,8 @@ struct nfs_server {
unsigned int acdirmin;
unsigned int acdirmax;
unsigned int namelen;
+ unsigned int options; /* extra options enabled by mount */
+#define NFS_OPTION_FSCACHE 0x00000001 /* - local caching enabled */
struct nfs_fsid fsid;
__u64 maxfilesize; /* maximum file size */
-Add a 'kernel_service' object class to SELinux and give this object class two
access vectors: 'use_as_override' and 'create_files_as'.
The first vector is used to grant a process the right to nominate an alternate
process security ID for the kernel to use as an override for the SELinux
subjective security when accessing stuff on behalf of another process.
For example, CacheFiles when accessing the cache on behalf on a process
accessing an NFS file needs to use a subjective security ID appropriate to the
cache rather then the one the calling process is using. The cachefilesd
daemon will nominate the security ID to be used.
The second vector is used to grant a process the right to nominate a file
creation label for a kernel service to use.
Signed-off-by: David Howells <dhowells@redhat.com>
---
security/selinux/include/av_perm_to_string.h | 2 ++
security/selinux/include/av_permissions.h | 2 ++
security/selinux/include/class_to_string.h | 1 +
security/selinux/include/flask.h | 1 +
4 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
index d569669..fd6bef7 100644
--- a/security/selinux/include/av_perm_to_string.h
+++ b/security/selinux/include/av_perm_to_string.h
@@ -171,3 +171,5 @@
S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect")
S_(SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, "mmap_zero")
S_(SECCLASS_PEER, PEER__RECV, "recv")
+ S_(SECCLASS_KERNEL_SERVICE, KERNEL_SERVICE__USE_AS_OVERRIDE, "use_as_override")
+ S_(SECCLASS_KERNEL_SERVICE, KERNEL_SERVICE__CREATE_FILES_AS, "create_files_as")
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
index 75b4131..02ddf8d 100644
--- a/security/selinux/include/av_permissions.h
+++ b/security/selinux/include/av_permissions.h
@@ -836,3 +836,5 @@
#define DCCP_SOCKET__NAME_CONNECT 0x00800000UL
#defi...Increase the size of a payload that can be used to instantiate a key in
add_key() and keyctl_instantiate_key(). This permits huge CIFS SPNEGO blobs to
be passed around. The limit is raised to 1MB. If kmalloc() can't allocate a
buffer of sufficient size, vmalloc() will be tried instead.
Signed-off-by: David Howells <dhowells@redhat.com>
---
security/keys/keyctl.c | 38 ++++++++++++++++++++++++++++++--------
1 files changed, 30 insertions(+), 8 deletions(-)
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index d9ca15c..8ec8432 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -19,6 +19,7 @@
#include <linux/capability.h>
#include <linux/string.h>
#include <linux/err.h>
+#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include "internal.h"
@@ -62,9 +63,10 @@ asmlinkage long sys_add_key(const char __user *_type,
char type[32], *description;
void *payload;
long ret;
+ bool vm;
ret = -EINVAL;
- if (plen > 32767)
+ if (plen > 1024 * 1024 - 1)
goto error;
/* draw all the data into kernel space */
@@ -81,11 +83,18 @@ asmlinkage long sys_add_key(const char __user *_type,
/* pull the payload in if one was supplied */
payload = NULL;
+ vm = false;
if (_payload) {
ret = -ENOMEM;
payload = kmalloc(plen, GFP_KERNEL);
- if (!payload)
- goto error2;
+ if (!payload) {
+ if (plen <= PAGE_SIZE)
+ goto error2;
+ vm = true;
+ payload = vmalloc(plen);
+ if (!payload)
+ goto error2;
+ }
ret = -EFAULT;
if (copy_from_user(payload, _payload, plen) != 0)
@@ -113,7 +122,10 @@ asmlinkage long sys_add_key(const char __user *_type,
key_ref_put(keyring_ref);
error3:
- kfree(payload);
+ if (!vm)
+ kfree(payload);
+ else
+ vfree(payload);
error2:
kfree(description);
error:
@@ -821,9 +833,10 @@ long keyctl_instantiate_key(key_serial_t id,
key_ref_t keyring_ref;
void *payload;
long ret;
+ bool vm = false;
...Define and create inode-level cache data storage objects (as managed by nfs_inode structs). Each inode-level object is created in a superblock-level index object and is itself a data storage object into which pages from the inode are stored. The inode object key is the NFS file handle for the inode. The inode object is given coherency data to carry in the auxiliary data permitted by the cache. This is a sequence made up of: (1) i_mtime from the NFS inode. (2) i_ctime from the NFS inode. (3) i_size from the NFS inode. As the cache is a persistent cache, the auxiliary data is checked when a new NFS in-memory inode is set up that matches an already existing data storage object in the cache. If the coherency data is the same, the on-disk object is retained and used; if not, it is scrapped and a new one created. Signed-off-by: David Howells <dhowells@redhat.com> --- fs/nfs/fscache-index.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/fscache.h | 1 2 files changed, 113 insertions(+), 0 deletions(-) diff --git a/fs/nfs/fscache-index.c b/fs/nfs/fscache-index.c index b5a52e3..c3c63fa 100644 --- a/fs
