fscache, cachefiles: Rewrite
Subsystems (14)
David Howells
maintainerv4 cover fscache, cachefiles: Rewrite
Here's a set of patches implements a rewrite of the fscache driver and a matching rewrite of the cachefiles driver, significantly simplifying the code compared to what's upstream, removing the complex operation scheduling and object state machine in favour of something much smaller and simpler.
The patchset is structured such that the first few patches disable fscache use by the network filesystems using it, remove the cachefiles driver entirely and as much of the fscache driver as can be got away with without causing build failures in the network filesystems. The patches after that recreate fscache and then cachefiles, attempting to add the pieces in a logical order. Finally, the filesystems are reenabled and then the very last patch changes the documentation.
WHY REWRITE?
============
Fscache's operation scheduling API was intended to handle sequencing of cache operations, which were all required (where possible) to run asynchronously in parallel with the operations being done by the network filesystem, whilst allowing the cache to be brought online and offline and to interrupt service for invalidation.
With the advent of the tmpfile capacity in the VFS, however, an opportunity arises to do invalidation much more simply, without having to wait for I/O that's actually in progress: Cachefiles can simply create a tmpfile, cut over the file pointer for the backing object attached to a cookie and abandon the in-progress I/O, dismissing it upon completion.
Future work here would involve using Omar Sandoval's vfs_link() with AT_LINK_REPLACE[1] to allow an extant file to be displaced by a new hard link from a tmpfile as currently I have to unlink the old file first.
These patches can also simplify the object state handling as I/O operations to the cache don't all have to be brought to a stop in order to invalidate a file. To that end, and with an eye on to writing a new backing cache model in the future, I've taken the opportunity to simplify the indexing structure.
I've separated the index cookie concept from the file cookie concept by C type now. The former is now called a "volume cookie" (struct fscache_volume) and there is a container of file cookies. There are then just the two levels. All the index cookie levels are collapsed into a single volume cookie, and this has a single printable string as a key. For instance, an AFS volume would have a key of something like "afs,example.com,1000555", combining the filesystem name, cell name and volume ID. This is freeform, but must not have '/' chars in it.
I've also eliminated all pointers back from fscache into the network filesystem. This required the duplication of a little bit of data in the cookie (cookie key, coherency data and file size), but it's not actually that much. This gets rid of problems with making sure we keep netfs data structures around so that the cache can access them.
These patches mean that most of the code that was in the drivers before is simply gone and those drivers are now almost entirely new code. That being the case, there doesn't seem any particular reason to try and maintain bisectability across it. Further, there has to be a point in the middle where things are cut over as there's a single point everything has to go through (ie. /dev/cachefiles) and it can't be in use by two drivers at once.
ISSUES YET OUTSTANDING
======================
There are some issues still outstanding, unaddressed by this patchset, that will need fixing in future patchsets, but that don't stop this series from being usable:
(1) The cachefiles driver needs to stop using the backing filesystem's metadata to store information about what parts of the cache are populated. This is not reliable with modern extent-based filesystems.
Fixing this is deferred to a separate patchset as it involves negotiation with the network filesystem and the VM as to how much data to download to fulfil a read - which brings me on to (2)...
(2) NFS and CIFS do not take account of how the cache would like I/O to be structured to meet its granularity requirements. Previously, the cache used page granularity, which was fine as the network filesystems also dealt in page granularity, and the backing filesystem (ext4, xfs or whatever) did whatever it did out of sight. However, we now have folios to deal with and the cache will now have to store its own metadata to track its contents.
The change I'm looking at making for cachefiles is to store content bitmaps in one or more xattrs and making a bit in the map correspond to something like a 256KiB block. However, the size of an xattr and the fact that they have to be read/updated in one go means that I'm looking at covering 1GiB of data per 512-byte map and storing each map in an xattr. Cachefiles has the potential to grow into a fully fledged filesystem of its very own if I'm not careful.
However, I'm also looking at changing things even more radically and going to a different model of how the cache is arranged and managed - one that's more akin to the way, say, openafs does things - which brings me on to (3)...
(3) The way cachefilesd does culling is very inefficient for large caches and it would be better to move it into the kernel if I can as cachefilesd has to keep asking the kernel if it can cull a file. Changing the way the backend works would allow this to be addressed.
BITS THAT MAY BE CONTROVERSIAL
==============================
There are some bits I've added that may be controversial:
(1) I've provided a flag, S_KERNEL_FILE, that cachefiles uses to check if a files is already being used by some other kernel service (e.g. a duplicate cachefiles cache in the same directory) and reject it if it is. This isn't entirely necessary, but it helps prevent accidental data corruption.
I don't want to use S_SWAPFILE as that has other effects, but quite possibly swapon() should set S_KERNEL_FILE too.
Note that it doesn't prevent userspace from interfering, though perhaps it should. (I have made it prevent a marked directory from being rmdir-able).
(2) Cachefiles wants to keep the backing file for a cookie open whilst we might need to write to it from network filesystem writeback. The problem is that the network filesystem unuses its cookie when its file is closed, and so we have nothing pinning the cachefiles file open and it will get closed automatically after a short time to avoid EMFILE/ENFILE problems.
Reopening the cache file, however, is a problem if this is being done due to writeback triggered by exit(). Some filesystems will oops if we try to open a file in that context because they want to access current->fs or suchlike.
To get around this, I added the following:
(A) An inode flag, I_PINNING_FSCACHE_WB, to be set on a network filesystem inode to indicate that we have a usage count on the cookie caching that inode.
(B) A flag in struct writeback_control, unpinned_fscache_wb, that is set when __writeback_single_inode() clears the last dirty page from i_pages - at which point it clears I_PINNING_FSCACHE_WB and sets this flag.
This has to be done here so that clearing I_PINNING_FSCACHE_WB can be done atomically with the check of PAGECACHE_TAG_DIRTY that clears I_DIRTY_PAGES.
(C) A function, fscache_set_page_dirty(), which if it is not set, sets I_PINNING_FSCACHE_WB and calls fscache_use_cookie() to pin the cache resources.
(D) A function, fscache_unpin_writeback(), to be called by ->write_inode() to unuse the cookie.
(E) A function, fscache_clear_inode_writeback(), to be called when the inode is evicted, before clear_inode() is called. This cleans up any lingering I_PINNING_FSCACHE_WB.
The network filesystem can then use these tools to make sure that fscache_write_to_cache() can write locally modified data to the cache as well as to the server.
For the future, I'm working on write helpers for netfs lib that should allow this facility to be removed by keeping track of the dirty regions separately - but that's incomplete at the moment and is also going to be affected by folios, one way or another, since it deals with pages.
These patches can be found also on:
https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git/log/?h=fscache-rewrite
David
Changes
=======
ver #4:
- Dropped a pair of patches to try and cope with multipage folios in afs_write_begin/end() - it should really be done in the caller[7].
- Fixed the use of sizeof with memset in cifs.
- Removed an extraneous kdoc param.
- Added a patch to add a tracepoint for fscache_use/unuse_cookie().
- In cifs, tcon->vol_create_time is __le64 so doesn't need cpu_to_le64().
- Add an expanded version of a patch to use current_is_kswapd() instead of !gfpflags_allow_blocking()[8].
- Removed a couple of debugging print statements.
ver #3:
- Fixed a race in the cookie state machine between LRU discard and relinquishment[4].
- Fixed up the hashing to make it portable[5].
- Fixed up some netfs coherency data to make it portable.
- Fixed some missing NFS_FSCACHE=n fallback functions in nfs[6].
- Added a patch to store volume coherency data in an xattr.
- Added a check that the cookie is unhashed before being freed.
- Fixed fscache to use remove_proc_subtree() to remove /proc/fs/fscache/.
ver #2:
- Fix an unused-var warning due to CONFIG_9P_FSCACHE=n.
- Use gfpflags_allow_blocking() rather than using flag directly.
- Fixed some error logging in a couple of cachefiles functions.
- Fixed an error check in the fscache volume allocation.
- Need to unmark an inode we've moved to the graveyard before unlocking.
- Upgraded to -rc4 to allow for upstream changes to cifs.
- Should only change to inval state if can get access to cache.
- Don't hold n_accesses elevated whilst cache is bound to a cookie, but rather add a flag that prevents the state machine from being queued when n_accesses reaches 0.
- Remove the unused cookie pointer field from the fscache_acquire tracepoint.
- Added missing transition to LRU_DISCARDING state.
- Added two ceph patches from Jeff Layton[2].
- Remove NFS_INO_FSCACHE as it's no longer used.
- In NFS, need to unuse a cookie on file-release, not inode-clear.
- Filled in the NFS cache I/O routines, borrowing from the previously posted fallback I/O code[3].
References
==========
These patches have been published for review before, firstly as part of a larger set:
Then as a cut-down set:
I split out a set to just restructure the I/O, which got merged back in to this one:
... and a larger set to do the conversion, also merged back into this one:
Older versions of this one:
Proposals/information about the design have been published here:
And requests for information:
I've posted partial patches to try and help 9p and cifs along:
---
Dave Wysochanski (1):
nfs: Convert to new fscache volume/cookie API
David Howells (65):
fscache, cachefiles: Disable configuration
cachefiles: Delete the cachefiles driver pending rewrite
fscache: Remove the contents of the fscache driver, pending rewrite
netfs: Display the netfs inode number in the netfs_read tracepoint
netfs: Pass a flag to ->prepare_write() to say if there's no alloc'd space
fscache: Introduce new driver
fscache: Implement a hash function
fscache: Implement cache registration
fscache: Implement volume registration
fscache: Implement cookie registration
fscache: Implement cache-level access helpers
fscache: Implement volume-level access helpers
fscache: Implement cookie-level access helpers
fscache: Implement functions add/remove a cache
fscache: Provide and use cache methods to lookup/create/free a volume
fscache: Add a function for a cache backend to note an I/O error
fscache: Implement simple cookie state machine
fscache: Implement cookie user counting and resource pinning
fscache: Implement cookie invalidation
fscache: Provide a means to begin an operation
fscache: Count data storage objects in a cache
fscache: Provide read/write stat counters for the cache
fscache: Provide a function to let the netfs update its coherency data
netfs: Pass more information on how to deal with a hole in the cache
fscache: Implement raw I/O interface
fscache: Implement higher-level write I/O interface
vfs, fscache: Implement pinning of cache usage for writeback
fscache: Provide a function to note the release of a page
fscache: Provide a function to resize a cookie
cachefiles: Introduce rewritten driver
cachefiles: Define structs
cachefiles: Add some error injection support
cachefiles: Add a couple of tracepoints for logging errors
cachefiles: Add cache error reporting macro
cachefiles: Add security derivation
cachefiles: Register a miscdev and parse commands over it
cachefiles: Provide a function to check how much space there is
vfs, cachefiles: Mark a backing file in use with an inode flag
cachefiles: Implement a function to get/create a directory in the cache
cachefiles: Implement cache registration and withdrawal
cachefiles: Implement volume support
cachefiles: Add tracepoints for calls to the VFS
cachefiles: Implement object lifecycle funcs
cachefiles: Implement key to filename encoding
cachefiles: Implement metadata/coherency data storage in xattrs
cachefiles: Mark a backing file in use with an inode flag
cachefiles: Implement culling daemon commands
cachefiles: Implement backing file wrangling
cachefiles: Implement begin and end I/O operation
cachefiles: Implement cookie resize for truncate
cachefiles: Implement the I/O routines
fscache, cachefiles: Store the volume coherency data
cachefiles: Allow cachefiles to actually function
fscache, cachefiles: Display stats of no-space events
fscache, cachefiles: Display stat of culling events
afs: Convert afs to use the new fscache API
afs: Copy local writes to the cache when writing to the server
afs: Skip truncation on the server of data we haven't written yet
9p: Use fscache indexing rewrite and reenable caching
9p: Copy local writes to the cache when writing to the server
nfs: Implement cache I/O by accessing the cache directly
cifs: Support fscache indexing rewrite (untested)
fscache: Rewrite documentation
fscache: Add a tracepoint for cookie use/unuse
9p, afs, ceph, cifs, nfs: Use current_is_kswapd() rather than gfpflags_allow_blocking()
Jeff Layton (2):
ceph: conversion to new fscache API
ceph: add fscache writeback support
.../filesystems/caching/backend-api.rst | 850 ++++------ .../filesystems/caching/cachefiles.rst | 6 +- Documentation/filesystems/caching/fscache.rst | 525 ++---- Documentation/filesystems/caching/index.rst | 4 +- .../filesystems/caching/netfs-api.rst | 1136 ++++--------- Documentation/filesystems/caching/object.rst | 313 ---- .../filesystems/caching/operations.rst | 210 --- Documentation/filesystems/netfs_library.rst | 16 +- fs/9p/Kconfig | 2 +- fs/9p/cache.c | 195 +-- fs/9p/cache.h | 25 +- fs/9p/v9fs.c | 17 +- fs/9p/v9fs.h | 13 +- fs/9p/vfs_addr.c | 57 +- fs/9p/vfs_dir.c | 13 + fs/9p/vfs_file.c | 3 +- fs/9p/vfs_inode.c | 26 +- fs/9p/vfs_inode_dotl.c | 3 +- fs/9p/vfs_super.c | 3 + fs/afs/Kconfig | 2 +- fs/afs/Makefile | 3 - fs/afs/cache.c | 68 - fs/afs/cell.c | 12 - fs/afs/file.c | 38 +- fs/afs/inode.c | 101 +- fs/afs/internal.h | 37 +- fs/afs/main.c | 14 - fs/afs/super.c | 1 + fs/afs/volume.c | 29 +- fs/afs/write.c | 88 +- fs/cachefiles/Kconfig | 7 + fs/cachefiles/Makefile | 6 +- fs/cachefiles/bind.c | 278 ---- fs/cachefiles/cache.c | 378 +++++ fs/cachefiles/daemon.c | 180 +- fs/cachefiles/error_inject.c | 46 + fs/cachefiles/interface.c | 747 ++++----- fs/cachefiles/internal.h | 270 +-- fs/cachefiles/io.c | 330 +++- fs/cachefiles/key.c | 201 ++- fs/cachefiles/main.c | 22 +- fs/cachefiles/namei.c | 1223 ++++++-------- fs/cachefiles/rdwr.c | 972 ----------- fs/cachefiles/security.c | 2 +- fs/cachefiles/volume.c | 139 ++ fs/cachefiles/xattr.c | 421 ++--- fs/ceph/Kconfig | 2 +- fs/ceph/addr.c | 102 +- fs/ceph/cache.c | 218 +-- fs/ceph/cache.h | 97 +- fs/ceph/caps.c | 3 +- fs/ceph/file.c | 13 +- fs/ceph/inode.c | 22 +- fs/ceph/super.c | 10 +- fs/ceph/super.h | 3 +- fs/cifs/Kconfig | 2 +- fs/cifs/Makefile | 2 +- fs/cifs/cache.c | 105 -- fs/cifs/cifsfs.c | 11 +- fs/cifs/cifsglob.h | 5 +- fs/cifs/connect.c | 12 - fs/cifs/file.c | 64 +- fs/cifs/fscache.c | 333 +--- fs/cifs/fscache.h | 126 +- fs/cifs/inode.c | 36 +- fs/fs-writeback.c | 8 + fs/fscache/Makefile | 6 +- fs/fscache/cache.c | 618 +++---- fs/fscache/cookie.c | 1448 +++++++++-------- fs/fscache/fsdef.c | 98 -- fs/fscache/internal.h | 317 +--- fs/fscache/io.c | 376 ++++- fs/fscache/main.c | 147 +- fs/fscache/netfs.c | 74 - fs/fscache/object.c | 1125 ------------- fs/fscache/operation.c | 633 ------- fs/fscache/page.c | 1242 -------------- fs/fscache/proc.c | 47 +- fs/fscache/stats.c | 293 +--- fs/fscache/volume.c | 517 ++++++ fs/namei.c | 3 +- fs/netfs/read_helper.c | 10 +- fs/nfs/Kconfig | 2 +- fs/nfs/Makefile | 2 +- fs/nfs/client.c | 4 - fs/nfs/direct.c | 2 + fs/nfs/file.c | 13 +- fs/nfs/fscache-index.c | 140 -- fs/nfs/fscache.c | 490 ++---- fs/nfs/fscache.h | 180 +- fs/nfs/inode.c | 11 +- fs/nfs/nfstrace.h | 1 - fs/nfs/read.c | 25 +- fs/nfs/super.c | 28 +- fs/nfs/write.c | 8 +- include/linux/fs.h | 4 + include/linux/fscache-cache.h | 614 ++----- include/linux/fscache.h | 1021 +++++------- include/linux/netfs.h | 15 +- include/linux/nfs_fs.h | 1 - include/linux/nfs_fs_sb.h | 9 +- include/linux/writeback.h | 1 + include/trace/events/cachefiles.h | 527 ++++-- include/trace/events/fscache.h | 642 ++++---- include/trace/events/netfs.h | 5 +- 105 files changed, 7396 insertions(+), 13509 deletions(-) delete mode 100644 Documentation/filesystems/caching/object.rst delete mode 100644 Documentation/filesystems/caching/operations.rst delete mode 100644 fs/afs/cache.c delete mode 100644 fs/cachefiles/bind.c create mode 100644 fs/cachefiles/cache.c create mode 100644 fs/cachefiles/error_inject.c delete mode 100644 fs/cachefiles/rdwr.c create mode 100644 fs/cachefiles/volume.c delete mode 100644 fs/cifs/cache.c delete mode 100644 fs/fscache/fsdef.c delete mode 100644 fs/fscache/netfs.c delete mode 100644 fs/fscache/object.c delete mode 100644 fs/fscache/operation.c delete mode 100644 fs/fscache/page.c create mode 100644 fs/fscache/volume.c delete mode 100644 fs/nfs/fscache-index.c
David Howells
maintainerPATCH v4 1/68 fscache, cachefiles: Disable configuration
Disable fscache and cachefiles in Kconfig whilst it is rewritten.
---
fs/9p/Kconfig | 2 +- fs/afs/Kconfig | 2 +- fs/ceph/Kconfig | 2 +- fs/cifs/Kconfig | 2 +- fs/fscache/Kconfig | 3 +++ fs/nfs/Kconfig | 2 +- 6 files changed, 8 insertions(+), 5 deletions(-)
fs/9p/Kconfig +1 -1
@@ -14,7 +14,7 @@ config 9P_FS if 9P_FS config 9P_FSCACHE bool "Enable 9P client caching support" - depends on 9P_FS=m && FSCACHE || 9P_FS=y && FSCACHE=y + depends on 9P_FS=m && FSCACHE_OLD_API || 9P_FS=y && FSCACHE_OLD_API=y help Choose Y here to enable persistent, read-only local caching support for 9p clients using FS-Cache
fs/afs/Kconfig +1 -1
@@ -25,7 +25,7 @@ config AFS_DEBUG config AFS_FSCACHE bool "Provide AFS client caching support" - depends on AFS_FS=m && FSCACHE || AFS_FS=y && FSCACHE=y + depends on AFS_FS=m && FSCACHE_OLD_API || AFS_FS=y && FSCACHE_OLD_API=y help Say Y here if you want AFS data to be cached locally on disk through the generic filesystem cache manager
fs/ceph/Kconfig +1 -1
@@ -21,7 +21,7 @@ config CEPH_FS if CEPH_FS config CEPH_FSCACHE bool "Enable Ceph client caching support" - depends on CEPH_FS=m && FSCACHE || CEPH_FS=y && FSCACHE=y + depends on CEPH_FS=m && FSCACHE_OLD_API || CEPH_FS=y && FSCACHE_OLD_API=y help Choose Y here to enable persistent, read-only local caching support for Ceph clients using FS-Cache
fs/cifs/Kconfig +1 -1
@@ -188,7 +188,7 @@ config CIFS_SMB_DIRECT config CIFS_FSCACHE bool "Provide CIFS client caching support" - depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y + depends on CIFS=m && FSCACHE_OLD_API || CIFS=y && FSCACHE_OLD_API=y help Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data to be cached locally on disk through the general filesystem cache
fs/fscache/Kconfig +3 -0
@@ -38,3 +38,6 @@ config FSCACHE_DEBUG enabled by setting bits in /sys/modules/fscache/parameter/debug. See Documentation/filesystems/caching/fscache.rst for more information. + +config FSCACHE_OLD_API + bool
fs/nfs/Kconfig +1 -1
@@ -170,7 +170,7 @@ config ROOT_NFS config NFS_FSCACHE bool "Provide NFS client caching support" - depends on NFS_FS=m && FSCACHE || NFS_FS=y && FSCACHE=y + depends on NFS_FS=m && FSCACHE_OLD_API || NFS_FS=y && FSCACHE_OLD_API=y help Say Y here if you want NFS data to be cached locally on disc through the general filesystem cache manager
David Howells
maintainerPATCH v4 2/68 cachefiles: Delete the cachefiles driver pending rewrite
In reply to David HowellsDelete the code from the cachefiles driver to make it easier to rewrite and resubmit in a logical manner.
---
fs/Kconfig | 1 fs/Makefile | 1 fs/cachefiles/Kconfig | 21 - fs/cachefiles/Makefile | 18 - fs/cachefiles/bind.c | 278 ---------- fs/cachefiles/daemon.c | 748 --------------------------- fs/cachefiles/interface.c | 572 --------------------- fs/cachefiles/internal.h | 350 ------------- fs/cachefiles/io.c | 420 --------------- fs/cachefiles/key.c | 155 ------ fs/cachefiles/main.c | 95 --- fs/cachefiles/namei.c | 1018 ------------------------------------- fs/cachefiles/rdwr.c | 972 ----------------------------------- fs/cachefiles/security.c | 112 ---- fs/cachefiles/xattr.c | 324 ------------ include/trace/events/cachefiles.h | 321 ------------ 16 files changed, 5406 deletions(-) delete mode 100644 fs/cachefiles/Kconfig delete mode 100644 fs/cachefiles/Makefile delete mode 100644 fs/cachefiles/bind.c delete mode 100644 fs/cachefiles/daemon.c delete mode 100644 fs/cachefiles/interface.c delete mode 100644 fs/cachefiles/internal.h delete mode 100644 fs/cachefiles/io.c delete mode 100644 fs/cachefiles/key.c delete mode 100644 fs/cachefiles/main.c delete mode 100644 fs/cachefiles/namei.c delete mode 100644 fs/cachefiles/rdwr.c delete mode 100644 fs/cachefiles/security.c delete mode 100644 fs/cachefiles/xattr.c delete mode 100644 include/trace/events/cachefiles.h
fs/Kconfig +0 -1
@@ -132,7 +132,6 @@ menu "Caches" source "fs/netfs/Kconfig" source "fs/fscache/Kconfig" -source "fs/cachefiles/Kconfig" endmenu
fs/Makefile +0 -1
@@ -125,7 +125,6 @@ obj-$(CONFIG_AFS_FS) += afs/ obj-$(CONFIG_NILFS2_FS) += nilfs2/ obj-$(CONFIG_BEFS_FS) += befs/ obj-$(CONFIG_HOSTFS) += hostfs/ -obj-$(CONFIG_CACHEFILES) += cachefiles/ obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_TRACING) += tracefs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/
fs/cachefiles/Kconfig deleted +0 -21
@@ -1,21 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -config CACHEFILES - tristate "Filesystem caching on files" - depends on FSCACHE && BLOCK - help - This permits use of a mounted filesystem as a cache for other - filesystems - primarily networking filesystems - thus allowing fast - local disk to enhance the speed of slower devices. - - See Documentation/filesystems/caching/cachefiles.rst for more - information. - -config CACHEFILES_DEBUG - bool "Debug CacheFiles" - depends on CACHEFILES - help - This permits debugging to be dynamically enabled in the filesystem - caching on files module. If this is set, the debugging output may be - enabled by setting bits in /sys/modules/cachefiles/parameter/debug or - by including a debugging specifier in /etc/cachefilesd.conf.
fs/cachefiles/Makefile deleted +0 -18
@@ -1,18 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for caching in a mounted filesystem -# - -cachefiles-y := \ - bind.o \ - daemon.o \ - interface.o \ - io.o \ - key.o \ - main.o \ - namei.o \ - rdwr.o \ - security.o \ - xattr.o - -obj-$(CONFIG_CACHEFILES) := cachefiles.o
fs/cachefiles/bind.c deleted +0 -278
278 lines not on this page · raw message
fs/cachefiles/daemon.c deleted +0 -748
748 lines not on this page · raw message
fs/cachefiles/interface.c deleted +0 -572
572 lines not on this page · raw message
fs/cachefiles/internal.h deleted +0 -350
350 lines not on this page · raw message
fs/cachefiles/io.c deleted +0 -420
420 lines not on this page · raw message
fs/cachefiles/key.c deleted +0 -155
155 lines not on this page · raw message
fs/cachefiles/main.c deleted +0 -95
95 lines not on this page · raw message
fs/cachefiles/namei.c deleted +0 -1018
329 lines not on this page · raw message
fs/cachefiles/rdwr.c deleted +0 -972
972 lines not on this page · raw message
fs/cachefiles/security.c deleted +0 -112
112 lines not on this page · raw message
fs/cachefiles/xattr.c deleted +0 -324
324 lines not on this page · raw message
include/trace/events/cachefiles.h deleted +0 -321
321 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
David Howells
maintainerPATCH v4 3/68 fscache: Remove the contents of the fscache driver, pending rewrite
In reply to David HowellsRemove the code that comprises the fscache driver as it's going to be substantially rewritten, with the majority of the code being erased in the rewrite.
A small piece of linux/fscache.h is left as that is #included by a bunch of network filesystems.
---
fs/9p/vfs_addr.c | 8 fs/Makefile | 1 fs/afs/file.c | 8 fs/fscache/Kconfig | 39 - fs/fscache/Makefile | 20 - fs/fscache/cache.c | 416 ------------- fs/fscache/cookie.c | 1071 ---------------------------------- fs/fscache/fsdef.c | 98 --- fs/fscache/internal.h | 461 --------------- fs/fscache/io.c | 116 ---- fs/fscache/main.c | 230 ------- fs/fscache/netfs.c | 74 -- fs/fscache/object.c | 1125 ------------------------------------ fs/fscache/operation.c | 633 -------------------- fs/fscache/page.c | 1242 ---------------------------------------- fs/fscache/proc.c | 71 -- fs/fscache/stats.c | 283 --------- include/linux/fscache-cache.h | 548 ------------------ include/linux/fscache.h | 851 --------------------------- include/trace/events/fscache.h | 523 ----------------- 20 files changed, 13 insertions(+), 7805 deletions(-) delete mode 100644 fs/fscache/Makefile delete mode 100644 fs/fscache/cache.c delete mode 100644 fs/fscache/cookie.c delete mode 100644 fs/fscache/fsdef.c delete mode 100644 fs/fscache/internal.h delete mode 100644 fs/fscache/io.c delete mode 100644 fs/fscache/main.c delete mode 100644 fs/fscache/netfs.c delete mode 100644 fs/fscache/object.c delete mode 100644 fs/fscache/operation.c delete mode 100644 fs/fscache/page.c delete mode 100644 fs/fscache/proc.c delete mode 100644 fs/fscache/stats.c delete mode 100644 include/trace/events/fscache.h
fs/9p/vfs_addr.c +5 -3
@@ -76,9 +76,7 @@ static void v9fs_req_cleanup(struct address_space *mapping, void *priv)*/ static bool v9fs_is_cache_enabled (struct inode *inode) { -struct fscache_cookie *cookie =v9fs_inode_cookie (V9FS_I (inode)); - -return fscache_cookie_enabled (cookie) && !hlist_empty (&cookie->backing_objects); +return fscache_cookie_enabled (v9fs_inode_cookie (V9FS_I (inode))); }/**
@@ -87,9 +85,13 @@ static bool v9fs_is_cache_enabled(struct inode *inode)*/ static int v9fs_begin_cache_operation (struct netfs_read_request *rreq) { +#ifdef CONFIG_9P_FSCACHE struct fscache_cookie *cookie =v9fs_inode_cookie (V9FS_I (rreq->inode));return fscache_begin_read_operation (rreq, cookie); +#else +return -ENOBUFS ; +#endif }static const struct netfs_read_request_ops v9fs_req_ops = {
fs/Makefile +0 -1
@@ -67,7 +67,6 @@ obj-$(CONFIG_DLM) += dlm/ # Do not add any filesystems before this line obj-$(CONFIG_NETFS_SUPPORT) += netfs/ -obj-$(CONFIG_FSCACHE) += fscache/ obj-$(CONFIG_REISERFS_FS) += reiserfs/ obj-$(CONFIG_EXT4_FS) += ext4/ # We place ext4 before ext2 so that clean ext3 root fs's do NOT mount using the
fs/afs/file.c +5 -3
@@ -352,16 +352,18 @@ static void afs_init_rreq(struct netfs_read_request *rreq, struct file *file)static bool afs_is_cache_enabled (struct inode *inode) { -struct fscache_cookie *cookie =afs_vnode_cache (AFS_FS_I (inode)); - -return fscache_cookie_enabled (cookie) && !hlist_empty (&cookie->backing_objects); +return fscache_cookie_enabled (afs_vnode_cache (AFS_FS_I (inode))); }static int afs_begin_cache_operation (struct netfs_read_request *rreq) { +#ifdef CONFIG_AFS_FSCACHE struct afs_vnode *vnode =AFS_FS_I (rreq->inode);return fscache_begin_read_operation (rreq,afs_vnode_cache (vnode)); +#else +return -ENOBUFS ; +#endif } static int afs_check_write_begin(struct file *file, loff_t pos, unsigned len,
fs/fscache/Kconfig +0 -39
@@ -1,43 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -config FSCACHE - tristate "General filesystem local caching manager" - select NETFS_SUPPORT - help - This option enables a generic filesystem caching manager that can be - used by various network and other filesystems to cache data locally. - Different sorts of caches can be plugged in, depending on the - resources available. - - See Documentation/filesystems/caching/fscache.rst for more information. - -config FSCACHE_STATS - bool "Gather statistical information on local caching" - depends on FSCACHE && PROC_FS - select NETFS_STATS - help - This option causes statistical information to be gathered on local - caching and exported through file: - - /proc/fs/fscache/stats - - The gathering of statistics adds a certain amount of overhead to - execution as there are a quite a few stats gathered, and on a - multi-CPU system these may be on cachelines that keep bouncing - between CPUs. On the other hand, the stats are very useful for - debugging purposes. Saying 'Y' here is recommended. - - See Documentation/filesystems/caching/fscache.rst for more information. - -config FSCACHE_DEBUG - bool "Debug FS-Cache" - depends on FSCACHE - help - This permits debugging to be dynamically enabled in the local caching - management module. If this is set, the debugging output may be - enabled by setting bits in /sys/modules/fscache/parameter/debug. - - See Documentation/filesystems/caching/fscache.rst for more information. - config FSCACHE_OLD_API bool
fs/fscache/Makefile deleted +0 -20
@@ -1,20 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for general filesystem caching code -# - -fscache-y := \ - cache.o \ - cookie.o \ - fsdef.o \ - io.o \ - main.o \ - netfs.o \ - object.o \ - operation.o \ - page.o - -fscache-$(CONFIG_PROC_FS) += proc.o -fscache-$(CONFIG_FSCACHE_STATS) += stats.o - -obj-$(CONFIG_FSCACHE) := fscache.o
fs/fscache/cache.c deleted +0 -416
416 lines not on this page · raw message
fs/fscache/cookie.c deleted +0 -1071
1,071 lines not on this page · raw message
fs/fscache/fsdef.c deleted +0 -98
98 lines not on this page · raw message
fs/fscache/internal.h deleted +0 -461
461 lines not on this page · raw message
fs/fscache/io.c deleted +0 -116
116 lines not on this page · raw message
fs/fscache/main.c deleted +0 -230
230 lines not on this page · raw message
fs/fscache/netfs.c deleted +0 -74
74 lines not on this page · raw message
fs/fscache/object.c deleted +0 -1125
420 lines not on this page · raw message
fs/fscache/operation.c deleted +0 -633
633 lines not on this page · raw message
fs/fscache/page.c deleted +0 -1242
1,242 lines not on this page · raw message
fs/fscache/proc.c deleted +0 -71
71 lines not on this page · raw message
fs/fscache/stats.c deleted +0 -283
283 lines not on this page · raw message
include/linux/fscache-cache.h +1 -547
548 lines not on this page · raw message
include/linux/fscache.h +2 -849
851 lines not on this page · raw message
include/trace/events/fscache.h deleted +0 -523
523 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
David Howells
maintainerPATCH v4 4/68 netfs: Display the netfs inode number in the netfs_read tracepoint
In reply to David HowellsDisplay the netfs inode number in the netfs_read tracepoint so that this can be used to correlate with the cachefiles_prep_read tracepoint.
---
include/trace/events/netfs.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
include/trace/events/netfs.h +4 -1
@@ -135,6 +135,7 @@ TRACE_EVENT(netfs_read, __field(loff_t, start ) __field(size_t, len ) __field(enum netfs_read_trace, what ) +__field (unsigned int , netfs_inode ) ), TP_fast_assign(
@@ -143,12 +144,14 @@ TRACE_EVENT(netfs_read, __entry->start = start; __entry->len = len; __entry->what = what; + __entry->netfs_inode = rreq->inode->i_ino; ), -TP_printk ("R=%08x %s c=%08x s=%llx %zx" , +TP_printk ("R=%08x %s c=%08x ni=%x s=%llx %zx" , __entry->rreq,__print_symbolic (__entry->what, netfs_read_traces), __entry->cookie, + __entry->netfs_inode, __entry->start, __entry->len) );
David Howells
maintainerPATCH v4 5/68 netfs: Pass a flag to ->prepare_write() to say if there's no alloc'd space
In reply to David HowellsPass a flag to ->prepare_write() to indicate if there's definitely no space allocated in the cache yet (for instance if we've already checked as we were asked to do a read).
---
fs/netfs/read_helper.c | 2 +- include/linux/netfs.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-)
fs/netfs/read_helper.c +1 -1
@@ -323,7 +323,7 @@ static void netfs_rreq_do_write_to_cache(struct netfs_read_request *rreq) } ret = cres->ops->prepare_write (cres, &subreq->start, &subreq->len, - rreq->i_size); + rreq->i_size, true);if (ret <0 ) {trace_netfs_failure (rreq, subreq, ret, netfs_fail_prepare_write);trace_netfs_sreq (subreq, netfs_sreq_trace_write_skip);
include/linux/netfs.h +2 -1
@@ -232,7 +232,8 @@ struct netfs_cache_ops {* actually do. */ int (*prepare_write)(struct netfs_cache_resources *cres, -loff_t *_start,size_t *_len,loff_t i_size); +loff_t *_start,size_t *_len,loff_t i_size, +bool no_space_allocated_yet); };struct readahead_control ;
David Howells
maintainerPATCH v4 6/68 fscache: Introduce new driver
In reply to David HowellsIntroduce basic skeleton of the new, rewritten fscache driver.
Changes
=======
ver #3:
- Use remove_proc_subtree(), not remove_proc_entry() to remove a populated dir.
---
fs/Makefile | 1 fs/fscache/Kconfig | 39 +++++++++ fs/fscache/Makefile | 12 +++ fs/fscache/internal.h | 183 ++++++++++++++++++++++++++++++++++++++++ fs/fscache/main.c | 65 ++++++++++++++ fs/fscache/proc.c | 42 +++++++++ fs/fscache/stats.c | 22 +++++ include/linux/fscache-cache.h | 2 include/linux/fscache.h | 6 + include/trace/events/fscache.h | 49 +++++++++++ 10 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 fs/fscache/Makefile create mode 100644 fs/fscache/internal.h create mode 100644 fs/fscache/main.c create mode 100644 fs/fscache/proc.c create mode 100644 fs/fscache/stats.c create mode 100644 include/trace/events/fscache.h
fs/Makefile +1 -0
@@ -67,6 +67,7 @@ obj-$(CONFIG_DLM) += dlm/ # Do not add any filesystems before this line obj-$(CONFIG_NETFS_SUPPORT) += netfs/ +obj-$(CONFIG_FSCACHE) += fscache/ obj-$(CONFIG_REISERFS_FS) += reiserfs/ obj-$(CONFIG_EXT4_FS) += ext4/ # We place ext4 before ext2 so that clean ext3 root fs's do NOT mount using the
fs/fscache/Kconfig +39 -0
@@ -1,4 +1,43 @@ # SPDX-License-Identifier: GPL-2.0-only +config FSCACHE + tristate "General filesystem local caching manager" + select NETFS_SUPPORT + help + This option enables a generic filesystem caching manager that can be + used by various network and other filesystems to cache data locally. + Different sorts of caches can be plugged in, depending on the + resources available. + + See Documentation/filesystems/caching/fscache.rst for more information. + +config FSCACHE_STATS + bool "Gather statistical information on local caching" + depends on FSCACHE && PROC_FS + select NETFS_STATS + help + This option causes statistical information to be gathered on local + caching and exported through file: + + /proc/fs/fscache/stats + + The gathering of statistics adds a certain amount of overhead to + execution as there are a quite a few stats gathered, and on a + multi-CPU system these may be on cachelines that keep bouncing + between CPUs. On the other hand, the stats are very useful for + debugging purposes. Saying 'Y' here is recommended. + + See Documentation/filesystems/caching/fscache.rst for more information. + +config FSCACHE_DEBUG + bool "Debug FS-Cache" + depends on FSCACHE + help + This permits debugging to be dynamically enabled in the local caching + management module. If this is set, the debugging output may be + enabled by setting bits in /sys/modules/fscache/parameter/debug. + + See Documentation/filesystems/caching/fscache.rst for more information. + config FSCACHE_OLD_API bool
fs/fscache/Makefile new file +12 -0
@@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for general filesystem caching code +# + +fscache-y := \ + main.o + +fscache-$(CONFIG_PROC_FS) += proc.o +fscache-$(CONFIG_FSCACHE_STATS) += stats.o + +obj-$(CONFIG_FSCACHE) := fscache.o
fs/fscache/internal.h new file +183 -0
183 lines not on this page · raw message
fs/fscache/main.c new file +65 -0
65 lines not on this page · raw message
fs/fscache/proc.c new file +42 -0
42 lines not on this page · raw message
fs/fscache/stats.c new file +22 -0
@@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* FS-Cache statistics +* +* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. +* Written by David Howells ([email protected]) +*/ + +#define FSCACHE_DEBUG_LEVEL CACHE +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include "internal.h" + +/* +* display the general statistics +*/ +int fscache_stats_show (struct seq_file *m,void *v) +{ +seq_puts (m,"FS-Cache statistics\n" ); + +netfs_stats_show (m); +return 0 ; +}
include/linux/fscache-cache.h +2 -0
@@ -16,4 +16,6 @@#include <linux/fscache.h> +extern struct workqueue_struct *fscache_wq; +#endif /* _LINUX_FSCACHE_CACHE_H */
include/linux/fscache.h +5 -1
@@ -1,7 +1,7 @@/* SPDX-License-Identifier: GPL-2.0-or-later */ /* General filesystem caching interface * -* Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved. +* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. * Written by David Howells ([email protected]) * * NOTE!!! See:
@@ -18,9 +18,13 @@#include <linux/netfs.h> #if defined(CONFIG_FSCACHE ) || defined(CONFIG_FSCACHE_MODULE ) +#define __fscache_available (1) +#define fscache_available () (1)#define fscache_cookie_valid (cookie) (cookie)#define fscache_cookie_enabled (cookie) (cookie)#else +#define __fscache_available (0) +#define fscache_available () (0)#define fscache_cookie_valid (cookie) (0)#define fscache_cookie_enabled (cookie) (0)#endif
include/trace/events/fscache.h new file +49 -0
49 lines not on this page · raw message
David Howells
maintainerPATCH v4 7/68 fscache: Implement a hash function
In reply to David HowellsImplement a function to generate hashes. It needs to be stable over time and endianness-independent as the hashes will appear on disk in future patches. It can assume that its input is a multiple of four bytes in size and alignment.
This is borrowed from the VFS and simplified. le32_to_cpu() is added to make it endianness-independent.
Changes
=======
ver #3:
- Read the data being hashed in an endianness-independent way[1].
- Change the size parameter to be in bytes rather than words.
---
fs/fscache/internal.h | 2 ++ fs/fscache/main.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+)
fs/fscache/internal.h +2 -0
@@ -22,6 +22,8 @@*/ extern unsigned fscache_debug; +extern unsigned int fscache_hash (unsigned int salt,const void *data,size_t len); +/* * proc.c */
fs/fscache/main.c +40 -0
@@ -24,6 +24,46 @@ MODULE_PARM_DESC(fscache_debug,struct workqueue_struct *fscache_wq;EXPORT_SYMBOL (fscache_wq); +/* +* Mixing scores (in bits) for (7,20): +* Input delta: 1-bit 2-bit +* 1 round: 330.3 9201.6 +* 2 rounds: 1246.4 25475.4 +* 3 rounds: 1907.1 31295.1 +* 4 rounds: 2042.3 31718.6 +* Perfect: 2048 31744 +* (32*64) (32*31/2 * 64) +*/ +#define HASH_MIX (x, y, a) \ + ( x ^= (a), \ + y ^= x, x = rol32(x, 7),\ + x += y, y = rol32(y,20),\ + y *= 9 ) + +static inline unsigned int fold_hash (unsigned long x,unsigned long y) +{ +/* Use arch-optimized multiply if one exists */ +return __hash_32 (y ^__hash_32 (x)); +} + +/* +* Generate a hash. This is derived from full_name_hash(), but we want to be +* sure it is arch independent and that it doesn't change as bits of the +* computed hash value might appear on disk. The caller must guarantee that +* the source data is a multiple of four bytes in size. +*/ +unsigned int fscache_hash (unsigned int salt,const void *data,size_t len) +{ +const __le32 *p = data; +unsigned int a, x =0 , y = salt, n = len /sizeof (__le32); + +for (; n; n--) { + a =le32_to_cpu (*p++); +HASH_MIX (x, y, a); + } +return fold_hash (x, y); +} +/* * initialise the fs caching module */
David Howells
maintainerPATCH v4 8/68 fscache: Implement cache registration
In reply to David HowellsImplement a register of caches and provide functions to manage it.
Two functions are provided for the cache backend to use:
(1) Acquire a cache cookie:
struct fscache_cache *fscache_acquire_cache(const char *name)
This gets the cache cookie for a cache of the specified name and moves it to the preparation state. If a nameless cache cookie exists, that will be given this name and used.
(2) Relinquish a cache cookie:
void fscache_relinquish_cache(struct fscache_cache *cache);
This relinquishes a cache cookie, cleans it and makes it available if it's still referenced by a network filesystem.
Note that network filesystems don't deal with cache cookies directly, but rather go straight to the volume registration.
---
fs/fscache/Makefile | 1 fs/fscache/cache.c | 274 ++++++++++++++++++++++++++++++++++++++++ fs/fscache/internal.h | 33 +++++ fs/fscache/proc.c | 4 + include/linux/fscache-cache.h | 34 +++++ include/trace/events/fscache.h | 43 ++++++ 6 files changed, 389 insertions(+) create mode 100644 fs/fscache/cache.c
fs/fscache/Makefile +1 -0
@@ -4,6 +4,7 @@ # fscache-y := \ + cache.o \ main.o fscache-$(CONFIG_PROC_FS) += proc.o
fs/fscache/cache.c new file +274 -0
274 lines not on this page · raw message
fs/fscache/internal.h +33 -0
@@ -17,6 +17,39 @@#include <linux/sched.h> #include <linux/seq_file.h> +/* +* cache.c +*/ +#ifdef CONFIG_PROC_FS +extern const struct seq_operations fscache_caches_seq_ops; +#endif +struct fscache_cache *fscache_lookup_cache (const char *name,bool is_cache); +void fscache_put_cache (struct fscache_cache *cache,enum fscache_cache_trace where); + +static inline enum fscache_cache_state fscache_cache_state (const struct fscache_cache *cache) +{ +return smp_load_acquire (&cache->state); +} + +static inline bool fscache_cache_is_live (const struct fscache_cache *cache) +{ +return fscache_cache_state (cache) ==FSCACHE_CACHE_IS_ACTIVE ; +} + +static inline void fscache_set_cache_state (struct fscache_cache *cache, +enum fscache_cache_state new_state) +{ +smp_store_release (&cache->state, new_state); + +} + +static inline bool fscache_set_cache_state_maybe (struct fscache_cache *cache, +enum fscache_cache_state old_state, +enum fscache_cache_state new_state) +{ +return try_cmpxchg_release (&cache->state, &old_state, new_state); +} +/* * main.c */
fs/fscache/proc.c +4 -0
@@ -19,6 +19,10 @@ int __init fscache_proc_init(void)if (!proc_mkdir ("fs/fscache" ,NULL )) goto error_dir; +if (!proc_create_seq ("fs/fscache/caches" ,S_IFREG |0444 ,NULL , + &fscache_caches_seq_ops)) + goto error; +#ifdef CONFIG_FSCACHE_STATS if (!proc_create_single ("fs/fscache/stats" ,S_IFREG |0444 ,NULL , fscache_stats_show))
include/linux/fscache-cache.h +34 -0
@@ -16,6 +16,40 @@#include <linux/fscache.h> +enum fscache_cache_trace ; +enum fscache_access_trace ; + +enum fscache_cache_state { +FSCACHE_CACHE_IS_NOT_PRESENT ,/* No cache is present for this name */ +FSCACHE_CACHE_IS_PREPARING ,/* A cache is preparing to come live */ +FSCACHE_CACHE_IS_ACTIVE ,/* Attached cache is active and can be used */ +FSCACHE_CACHE_GOT_IOERROR ,/* Attached cache stopped on I/O error */ +FSCACHE_CACHE_IS_WITHDRAWN ,/* Attached cache is being withdrawn */ +#define NR__FSCACHE_CACHE_STATE (FSCACHE_CACHE_IS_WITHDRAWN + 1) +}; + +/* +* Cache cookie. +*/ +struct fscache_cache { +struct list_head cache_link;/* Link in cache list */ +void *cache_priv;/* Private cache data (or NULL) */ +refcount_t ref; +atomic_t n_volumes;/* Number of active volumes; */ +atomic_t n_accesses;/* Number of in-progress accesses on the cache */ +atomic_t object_count;/* no. of live objects in this cache */ +unsigned int debug_id; +enum fscache_cache_state state; +char *name; +}; +extern struct workqueue_struct *fscache_wq; +/* +* out-of-line cache backend functions +*/ +extern struct rw_semaphore fscache_addremove_sem; +extern struct fscache_cache *fscache_acquire_cache (const char *name); +extern void fscache_relinquish_cache (struct fscache_cache *cache); +#endif /* _LINUX_FSCACHE_CACHE_H */
include/trace/events/fscache.h +43 -0
66 lines not on this page · raw message
David Howells
maintainerPATCH v4 9/68 fscache: Implement volume registration
In reply to David HowellsAdd functions to the fscache API to allow volumes to be acquired and relinquished by the network filesystem. A volume is an index of data storage cache objects. A volume is represented by a volume cookie in the API. A filesystem would typically create a volume for a superblock and then create per-inode cookies within it.
To request a volume, the filesystem calls:
struct fscache_volume * fscache_acquire_volume(const char *volume_key,
const char *cache_name, const void *coherency_data, size_t coherency_len)
The volume_key is a printable string used to match the volume in the cache. It should not contain any '/' characters. For AFS, for example, this would be "afs,<cellname>,<volume_id>", e.g. "afs,example.com,523001".
The cache_name can be NULL, but if not it should be a string indicating the name of the cache to use if there's more than one available.
The coherency data, if given, is an arbitrarily-sized blob that's attached to the volume and is compared when the volume is looked up. If it doesn't match, the old volume is judged to be out of date and it and everything within it is discarded.
Acquiring a volume twice concurrently is disallowed, though the function will wait if an old volume cookie is being relinquishing.
When a network filesystem has finished with a volume, it should return the volume cookie by calling:
void
fscache_relinquish_volume(struct fscache_volume *volume,
const void *coherency_data, bool invalidate)
If invalidate is true, the entire volume will be discarded; if false, the volume will be synced and the coherency data will be updated.
Changes
=======
ver #4:
- Removed an extraneous param from kdoc on fscache_relinquish_volume()[3].
ver #3:
- fscache_hash()'s size parameter is now in bytes. Use __le32 as the unit to round up to.
- When comparing cookies, simply see if the attributes are the same rather than subtracting them to produce a strcmp-style return[2].
- Make the coherency data an arbitrary blob rather than a u64, but don't store it for the moment.
ver #2:
- Fix error check[1].
- Make a fscache_acquire_volume() return errors, including EBUSY if a conflicting volume cookie already exists. No error is printed now - that's left to the netfs.
---
fs/fscache/Makefile | 3 fs/fscache/internal.h | 14 ++ fs/fscache/proc.c | 4 fs/fscache/stats.c | 12 + fs/fscache/volume.c | 340 ++++++++++++++++++++++++++++++++++++++++ include/linux/fscache.h | 84 ++++++++++ include/trace/events/fscache.h | 61 +++++++ 7 files changed, 516 insertions(+), 2 deletions(-) create mode 100644 fs/fscache/volume.c
fs/fscache/Makefile +2 -1
@@ -5,7 +5,8 @@ fscache-y := \ cache.o \ - main.o + main.o \ + volume.o fscache-$(CONFIG_PROC_FS) += proc.o fscache-$(CONFIG_FSCACHE_STATS) += stats.o
fs/fscache/internal.h +14 -0
@@ -72,6 +72,9 @@ extern void fscache_proc_cleanup(void);* stats.c */ #ifdef CONFIG_FSCACHE_STATS +extern atomic_t fscache_n_volumes; +extern atomic_t fscache_n_volumes_collision; +extern atomic_t fscache_n_volumes_nomem;static inline void fscache_stat (atomic_t *stat) {
@@ -93,6 +96,17 @@ int fscache_stats_show(struct seq_file *m, void *v);#define fscache_stat_d (stat) do {} while (0)#endif +/* +* volume.c +*/ +extern const struct seq_operations fscache_volumes_seq_ops; + +struct fscache_volume *fscache_get_volume (struct fscache_volume *volume, +enum fscache_volume_trace where); +void fscache_put_volume (struct fscache_volume *volume, +enum fscache_volume_trace where); +void fscache_create_volume (struct fscache_volume *volume,bool wait); +/*****************************************************************************/ /*
fs/fscache/proc.c +4 -0
@@ -23,6 +23,10 @@ int __init fscache_proc_init(void) &fscache_caches_seq_ops)) goto error; +if (!proc_create_seq ("fs/fscache/volumes" ,S_IFREG |0444 ,NULL , + &fscache_volumes_seq_ops)) + goto error; +#ifdef CONFIG_FSCACHE_STATS if (!proc_create_single ("fs/fscache/stats" ,S_IFREG |0444 ,NULL , fscache_stats_show))
fs/fscache/stats.c +12 -0
@@ -10,12 +10,24 @@#include <linux/seq_file.h> #include "internal.h" +/* +* operation counters +*/ +atomic_t fscache_n_volumes; +atomic_t fscache_n_volumes_collision; +atomic_t fscache_n_volumes_nomem; +/* * display the general statistics */ int fscache_stats_show (struct seq_file *m,void *v) {seq_puts (m,"FS-Cache statistics\n" ); +seq_printf (m,"Cookies: v=%d vcol=%u voom=%u\n" , +atomic_read (&fscache_n_volumes), +atomic_read (&fscache_n_volumes_collision), +atomic_read (&fscache_n_volumes_nomem) + );netfs_stats_show (m);return 0 ;
fs/fscache/volume.c new file +340 -0
340 lines not on this page · raw message
include/linux/fscache.h +84 -0
97 lines not on this page · raw message
include/trace/events/fscache.h +60 -1
90 lines not on this page · raw message
David Howells
maintainerPATCH v4 10/68 fscache: Implement cookie registration
In reply to David HowellsAdd functions to the fscache API to allow data file cookies to be acquired and relinquished by the network filesystem. It is intended that the filesystem will create such cookies per-inode under a volume.
To request a cookie, the filesystem should call:
struct fscache_cookie * fscache_acquire_cookie(struct fscache_volume *volume,
u8 advice,
const void *index_key, size_t index_key_len, const void *aux_data, size_t aux_data_len, loff_t object_size)
The filesystem must first have created a volume cookie, which is passed in here. If it passes in NULL then the function will just return a NULL cookie.
A binary key should be passed in index_key and is of size index_key_len. This is saved in the cookie and is used to locate the associated data in the cache.
A coherency data buffer of size aux_data_len will be allocated and initialised from the buffer pointed to by aux_data. This is used to validate cache objects when they're opened and is stored on disk with them when they're committed. The data is stored in the cookie and will be updateable by various functions in later patches.
The object_size must also be given. This is also used to perform a coherency check and to size the backing storage appropriately.
This function disallows a cookie from being acquired twice in parallel, though it will cause the second user to wait if the first is busy relinquishing its cookie.
When a network filesystem has finished with a cookie, it should call:
void
fscache_relinquish_cookie(struct fscache_volume *volume,
bool retire)
If retire is true, any backing data will be discarded immediately.
Changes
=======
ver #3:
- fscache_hash()'s size parameter is now in bytes. Use __le32 as the unit to round up to.
- When comparing cookies, simply see if the attributes are the same rather than subtracting them to produce a strcmp-style return[1].
- Add a check to see if the cookie is still hashed at the point of freeing.
ver #2:
- Don't hold n_accesses elevated whilst cache is bound to a cookie, but rather add a flag that prevents the state machine from being queued when n_accesses reaches 0.
- Remove the unused cookie pointer field from the fscache_acquire tracepoint.
---
fs/fscache/Makefile | 1 fs/fscache/cookie.c | 497 ++++++++++++++++++++++++++++++++++++++++ fs/fscache/internal.h | 23 ++ fs/fscache/main.c | 12 + fs/fscache/proc.c | 4 fs/fscache/stats.c | 28 ++ include/linux/fscache-cache.h | 22 ++ include/linux/fscache.h | 134 +++++++++++ include/trace/events/fscache.h | 111 +++++++++ 9 files changed, 831 insertions(+), 1 deletion(-) create mode 100644 fs/fscache/cookie.c
fs/fscache/Makefile +1 -0
@@ -5,6 +5,7 @@ fscache-y := \ cache.o \ + cookie.o \ main.o \ volume.o
fs/fscache/cookie.c new file +497 -0
497 lines not on this page · raw message
fs/fscache/internal.h +23 -0
@@ -50,6 +50,20 @@ static inline bool fscache_set_cache_state_maybe(struct fscache_cache *cache,return try_cmpxchg_release (&cache->state, &old_state, new_state); } +/* +* cookie.c +*/ +extern struct kmem_cache *fscache_cookie_jar; +extern const struct seq_operations fscache_cookies_seq_ops; + +extern void fscache_print_cookie (struct fscache_cookie *cookie,char prefix); +static inline void fscache_see_cookie (struct fscache_cookie *cookie, +enum fscache_cookie_trace where) +{ +trace_fscache_cookie (cookie->debug_id,refcount_read (&cookie->ref), + where); +} +/* * main.c */
@@ -75,6 +89,15 @@ extern void fscache_proc_cleanup(void);extern atomic_t fscache_n_volumes;extern atomic_t fscache_n_volumes_collision;extern atomic_t fscache_n_volumes_nomem; +extern atomic_t fscache_n_cookies; + +extern atomic_t fscache_n_acquires; +extern atomic_t fscache_n_acquires_ok; +extern atomic_t fscache_n_acquires_oom; + +extern atomic_t fscache_n_relinquishes; +extern atomic_t fscache_n_relinquishes_retire; +extern atomic_t fscache_n_relinquishes_dropped;static inline void fscache_stat (atomic_t *stat) {
fs/fscache/main.c +12 -0
@@ -79,9 +79,20 @@ static int __init fscache_init(void)if (ret <0 ) goto error_proc; + fscache_cookie_jar =kmem_cache_create ("fscache_cookie_jar" , +sizeof (struct fscache_cookie ), +0 ,0 ,NULL ); +if (!fscache_cookie_jar) { +pr_notice ("Failed to allocate a cookie jar\n" ); + ret = -ENOMEM ; + goto error_cookie_jar; + } +pr_notice ("Loaded\n" );return 0 ; +error_cookie_jar: +fscache_proc_cleanup (); error_proc:destroy_workqueue (fscache_wq); error_wq:
@@ -97,6 +108,7 @@ static void __exit fscache_exit(void) {_enter ("" ); +kmem_cache_destroy (fscache_cookie_jar);fscache_proc_cleanup ();destroy_workqueue (fscache_wq);pr_notice ("Unloaded\n" );
fs/fscache/proc.c +4 -0
@@ -27,6 +27,10 @@ int __init fscache_proc_init(void) &fscache_volumes_seq_ops)) goto error; +if (!proc_create_seq ("fs/fscache/cookies" ,S_IFREG |0444 ,NULL , + &fscache_cookies_seq_ops)) + goto error; +#ifdef CONFIG_FSCACHE_STATS if (!proc_create_single ("fs/fscache/stats" ,S_IFREG |0444 ,NULL , fscache_stats_show))
fs/fscache/stats.c +27 -1
@@ -16,6 +16,18 @@atomic_t fscache_n_volumes;atomic_t fscache_n_volumes_collision;atomic_t fscache_n_volumes_nomem; +atomic_t fscache_n_cookies; + +atomic_t fscache_n_acquires; +atomic_t fscache_n_acquires_ok; +atomic_t fscache_n_acquires_oom; + +atomic_t fscache_n_updates; +EXPORT_SYMBOL (fscache_n_updates); + +atomic_t fscache_n_relinquishes; +atomic_t fscache_n_relinquishes_retire; +atomic_t fscache_n_relinquishes_dropped;/* * display the general statistics
@@ -23,12 +35,26 @@ atomic_t fscache_n_volumes_nomem;int fscache_stats_show (struct seq_file *m,void *v) {seq_puts (m,"FS-Cache statistics\n" ); -seq_printf (m,"Cookies: v=%d vcol=%u voom=%u\n" , +seq_printf (m,"Cookies: n=%d v=%d vcol=%u voom=%u\n" , +atomic_read (&fscache_n_cookies),atomic_read (&fscache_n_volumes),atomic_read (&fscache_n_volumes_collision),atomic_read (&fscache_n_volumes_nomem) ); +seq_printf (m,"Acquire: n=%u ok=%u oom=%u\n" , +atomic_read (&fscache_n_acquires), +atomic_read (&fscache_n_acquires_ok), +atomic_read (&fscache_n_acquires_oom)); + +seq_printf (m,"Updates: n=%u\n" , +atomic_read (&fscache_n_updates)); + +seq_printf (m,"Relinqs: n=%u rtr=%u drop=%u\n" , +atomic_read (&fscache_n_relinquishes), +atomic_read (&fscache_n_relinquishes_retire), +atomic_read (&fscache_n_relinquishes_dropped)); +netfs_stats_show (m);return 0 ; }
include/linux/fscache-cache.h +22 -0
@@ -17,6 +17,7 @@#include <linux/fscache.h> enum fscache_cache_trace ; +enum fscache_cookie_trace ;enum fscache_access_trace ;enum fscache_cache_state {
@@ -52,4 +53,25 @@ extern struct rw_semaphore fscache_addremove_sem;extern struct fscache_cache *fscache_acquire_cache (const char *name);extern void fscache_relinquish_cache (struct fscache_cache *cache); +extern struct fscache_cookie *fscache_get_cookie (struct fscache_cookie *cookie, +enum fscache_cookie_trace where); +extern void fscache_put_cookie (struct fscache_cookie *cookie, +enum fscache_cookie_trace where); +extern void fscache_set_cookie_state (struct fscache_cookie *cookie, +enum fscache_cookie_state state); + +/** +* fscache_get_key - Get a pointer to the cookie key +* @cookie: The cookie to query +* +* Return a pointer to the where a cookie's key is stored. +*/ +static inline void *fscache_get_key (struct fscache_cookie *cookie) +{ +if (cookie->key_len <=sizeof (cookie->inline_key)) +return cookie->inline_key; +else +return cookie->key; +} +#endif /* _LINUX_FSCACHE_CACHE_H */
include/linux/fscache.h +134 -0
156 lines not on this page · raw message
include/trace/events/fscache.h +111 -0
135 lines not on this page · raw message
David Howells
maintainerPATCH v4 11/68 fscache: Implement cache-level access helpers
In reply to David HowellsAdd a pair of functions to pin/unpin a cache that we're wanting to do a high-level access to (such as creating or removing a volume):
bool fscache_begin_cache_access(struct fscache_cache *cache, enum fscache_access_trace why); void fscache_end_cache_access(struct fscache_cache *cache, enum fscache_access_trace why);
The way the access gate works/will work is:
(1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTIVE), then we return false to indicate access was not permitted.
(2) If the cache tests as live, then we increment the n_accesses count and then recheck the liveness, ending the access if it ceased to be live.
(3) When we end the access, we decrement n_accesses and wake up the any waiters if it reaches 0.
(4) Whilst the cache is caching, n_accesses is kept artificially incremented to prevent wakeups from happening.
(5) When the cache is taken offline, the state is changed to prevent new accesses, n_accesses is decremented and we wait for n_accesses to become 0.
Note that some of this is implemented in a later patch.
---
fs/fscache/cache.c | 62 ++++++++++++++++++++++++++++++++++++++++ fs/fscache/internal.h | 2 + fs/fscache/main.c | 2 + include/trace/events/fscache.h | 41 ++++++++++++++++++++++++++ 4 files changed, 107 insertions(+)
fs/fscache/cache.c +62 -0
@@ -216,6 +216,68 @@ void fscache_relinquish_cache(struct fscache_cache *cache) }EXPORT_SYMBOL (fscache_relinquish_cache); +/** +* fscache_begin_cache_access - Pin a cache so it can be accessed +* @cache: The cache-level cookie +* @why: An indication of the circumstances of the access for tracing +* +* Attempt to pin the cache to prevent it from going away whilst we're +* accessing it and returns true if successful. This works as follows: +* +* (1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTIVE), +* then we return false to indicate access was not permitted. +* +* (2) If the cache tests as live, then we increment the n_accesses count and +* then recheck the liveness, ending the access if it ceased to be live. +* +* (3) When we end the access, we decrement n_accesses and wake up the any +* waiters if it reaches 0. +* +* (4) Whilst the cache is caching, n_accesses is kept artificially +* incremented to prevent wakeups from happening. +* +* (5) When the cache is taken offline, the state is changed to prevent new +* accesses, n_accesses is decremented and we wait for n_accesses to +* become 0. +*/ +bool fscache_begin_cache_access (struct fscache_cache *cache,enum fscache_access_trace why) +{ +int n_accesses; + +if (!fscache_cache_is_live (cache)) +return false; + + n_accesses =atomic_inc_return (&cache->n_accesses); +smp_mb__after_atomic ();/* Reread live flag after n_accesses */ +trace_fscache_access_cache (cache->debug_id,refcount_read (&cache->ref), + n_accesses, why); +if (!fscache_cache_is_live (cache)) { +fscache_end_cache_access (cache, fscache_access_unlive); +return false; + } +return true; +} + +/** +* fscache_end_cache_access - Unpin a cache at the end of an access. +* @cache: The cache-level cookie +* @why: An indication of the circumstances of the access for tracing +* +* Unpin a cache after we've accessed it. The @why indicator is merely +* provided for tracing purposes. +*/ +void fscache_end_cache_access (struct fscache_cache *cache,enum fscache_access_trace why) +{ +int n_accesses; + +smp_mb__before_atomic (); + n_accesses =atomic_dec_return (&cache->n_accesses); +trace_fscache_access_cache (cache->debug_id,refcount_read (&cache->ref), + n_accesses, why); +if (n_accesses ==0 ) +wake_up_var (&cache->n_accesses); +} +#ifdef CONFIG_PROC_FS static const char fscache_cache_states[NR__FSCACHE_CACHE_STATE ] ="-PAEW" ;
fs/fscache/internal.h +2 -0
@@ -23,6 +23,8 @@#ifdef CONFIG_PROC_FS extern const struct seq_operations fscache_caches_seq_ops;#endif +bool fscache_begin_cache_access (struct fscache_cache *cache,enum fscache_access_trace why); +void fscache_end_cache_access (struct fscache_cache *cache,enum fscache_access_trace why);struct fscache_cache *fscache_lookup_cache (const char *name,bool is_cache);void fscache_put_cache (struct fscache_cache *cache,enum fscache_cache_trace where);
fs/fscache/main.c +2 -0
@@ -21,6 +21,8 @@ module_param_named(debug, fscache_debug, uint,MODULE_PARM_DESC (fscache_debug,"FS-Cache debugging mask" ); +EXPORT_TRACEPOINT_SYMBOL (fscache_access_cache); +struct workqueue_struct *fscache_wq;EXPORT_SYMBOL (fscache_wq);
include/trace/events/fscache.h +41 -0
65 lines not on this page · raw message
David Howells
maintainerPATCH v4 12/68 fscache: Implement volume-level access helpers
In reply to David HowellsAdd a pair of helper functions to manage access to a volume, pinning the volume in place for the duration to prevent cache withdrawal from removing it:
bool fscache_begin_volume_access(struct fscache_volume *volume, enum fscache_access_trace why); void fscache_end_volume_access(struct fscache_volume *volume, enum fscache_access_trace why);
The way the access gate on the volume works/will work is:
(1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTIVE), then we return false to indicate access was not permitted.
(2) If the cache tests as live, then we increment the volume's n_accesses count and then recheck the cache liveness, ending the access if it ceased to be live.
(3) When we end the access, we decrement the volume's n_accesses and wake up the any waiters if it reaches 0.
(4) Whilst the cache is caching, the volume's n_accesses is kept artificially incremented to prevent wakeups from happening.
(5) When the cache is taken offline, the state is changed to prevent new accesses, the volume's n_accesses is decremented and we wait for it to become 0.
---
fs/fscache/internal.h | 3 + fs/fscache/main.c | 1 fs/fscache/volume.c | 84 ++++++++++++++++++++++++++++++++++++++++ include/linux/fscache-cache.h | 4 ++ include/trace/events/fscache.h | 34 ++++++++++++++++ 5 files changed, 126 insertions(+)
fs/fscache/internal.h +3 -0
@@ -130,6 +130,9 @@ struct fscache_volume *fscache_get_volume(struct fscache_volume *volume, enum fscache_volume_trace where);void fscache_put_volume (struct fscache_volume *volume,enum fscache_volume_trace where); +bool fscache_begin_volume_access (struct fscache_volume *volume, +struct fscache_cookie *cookie, +enum fscache_access_trace why);void fscache_create_volume (struct fscache_volume *volume,bool wait);
fs/fscache/main.c +1 -0
@@ -22,6 +22,7 @@ MODULE_PARM_DESC(fscache_debug, "FS-Cache debugging mask");EXPORT_TRACEPOINT_SYMBOL (fscache_access_cache); +EXPORT_TRACEPOINT_SYMBOL (fscache_access_volume);struct workqueue_struct *fscache_wq;EXPORT_SYMBOL (fscache_wq);
fs/fscache/volume.c +84 -0
90 lines not on this page · raw message
include/linux/fscache-cache.h +4 -0
@@ -53,6 +53,10 @@ extern struct rw_semaphore fscache_addremove_sem;extern struct fscache_cache *fscache_acquire_cache (const char *name);extern void fscache_relinquish_cache (struct fscache_cache *cache); +extern void fscache_end_volume_access (struct fscache_volume *volume, +struct fscache_cookie *cookie, +enum fscache_access_trace why); +extern struct fscache_cookie *fscache_get_cookie (struct fscache_cookie *cookie,enum fscache_cookie_trace where); extern void fscache_put_cookie(struct fscache_cookie *cookie,
include/trace/events/fscache.h +34 -0
@@ -43,6 +43,7 @@ enum fscache_volume_trace { fscache_volume_put_relinquish, fscache_volume_see_create_work, fscache_volume_see_hash_wake, + fscache_volume_wait_create_work, }; enum fscache_cookie_trace {
@@ -245,6 +246,39 @@ TRACE_EVENT(fscache_access_cache, __entry->n_accesses) ); +TRACE_EVENT (fscache_access_volume, + TP_PROTO(unsigned int volume_debug_id, + unsigned int cookie_debug_id, + int ref, + int n_accesses, + enum fscache_access_trace why), + +TP_ARGS (volume_debug_id, cookie_debug_id, ref, n_accesses, why), + +TP_STRUCT__entry ( + __field(unsigned int, volume ) + __field(unsigned int, cookie ) + __field(int, ref ) + __field(int, n_accesses ) + __field(enum fscache_access_trace, why ) + ), + +TP_fast_assign ( + __entry->volume = volume_debug_id; + __entry->cookie = cookie_debug_id; + __entry->ref = ref; + __entry->n_accesses = n_accesses; + __entry->why = why; + ), + +TP_printk ("V=%08x c=%08x %s r=%d a=%d" , + __entry->volume, + __entry->cookie, +__print_symbolic (__entry->why, fscache_access_traces), + __entry->ref, + __entry->n_accesses) + ); + TRACE_EVENT(fscache_acquire, TP_PROTO(struct fscache_cookie *cookie),
David Howells
maintainerPATCH v4 13/68 fscache: Implement cookie-level access helpers
In reply to David HowellsAdd a number of helper functions to manage access to a cookie, pinning the cache object in place for the duration to prevent cache withdrawal from removing it:
(1) void fscache_init_access_gate(struct fscache_cookie *cookie);
This function initialises the access count when a cache binds to a cookie. An extra ref is taken on the access count to prevent wakeups while the cache is active. We're only interested in the wakeup when a cookie is being withdrawn and we're waiting for it to quiesce - at which point the counter will be decremented before the wait.
The FSCACHE_COOKIE_NACC_ELEVATED flag is set on the cookie to keep track of the extra ref in order to handle a race between relinquishment and withdrawal both trying to drop the extra ref.
(2) bool fscache_begin_cookie_access(struct fscache_cookie *cookie, enum fscache_access_trace why);
This function attempts to begin access upon a cookie, pinning it in place if it's cached. If successful, it returns true and leaves a the access count incremented.
(3) void fscache_end_cookie_access(struct fscache_cookie *cookie, enum fscache_access_trace why);
This function drops the access count obtained by (2), permitting object withdrawal to take place when it reaches zero.
A tracepoint is provided to track changes to the access counter on a cookie.
Changes
=======
ver #2:
- Don't hold n_accesses elevated whilst cache is bound to a cookie, but rather add a flag that prevents the state machine from being queued when n_accesses reaches 0.
---
fs/fscache/cookie.c | 98 ++++++++++++++++++++++++++++++++++++++++ fs/fscache/internal.h | 3 + fs/fscache/main.c | 1 include/linux/fscache-cache.h | 2 + include/trace/events/fscache.h | 29 ++++++++++++ 5 files changed, 133 insertions(+)
fs/fscache/cookie.c +98 -0
@@ -62,6 +62,104 @@ static void fscache_free_cookie(struct fscache_cookie *cookie)kmem_cache_free (fscache_cookie_jar, cookie); } +/* +* Initialise the access gate on a cookie by setting a flag to prevent the +* state machine from being queued when the access counter transitions to 0. +* We're only interested in this when we withdraw caching services from the +* cookie. +*/ +static void fscache_init_access_gate (struct fscache_cookie *cookie) +{ +int n_accesses; + + n_accesses =atomic_read (&cookie->n_accesses); +trace_fscache_access (cookie->debug_id,refcount_read (&cookie->ref), + n_accesses, fscache_access_cache_pin); +set_bit (FSCACHE_COOKIE_NO_ACCESS_WAKE , &cookie->flags); +} + +/** +* fscache_end_cookie_access - Unpin a cache at the end of an access. +* @cookie: A data file cookie +* @why: An indication of the circumstances of the access for tracing +* +* Unpin a cache cookie after we've accessed it and bring a deferred +* relinquishment or withdrawal state into effect. +* +* The @why indicator is provided for tracing purposes. +*/ +void fscache_end_cookie_access (struct fscache_cookie *cookie, +enum fscache_access_trace why) +{ +int n_accesses; + +smp_mb__before_atomic (); + n_accesses =atomic_dec_return (&cookie->n_accesses); +trace_fscache_access (cookie->debug_id,refcount_read (&cookie->ref), + n_accesses, why); +if (n_accesses ==0 && + !test_bit (FSCACHE_COOKIE_NO_ACCESS_WAKE , &cookie->flags)) { +// PLACEHOLDER: Need to poke the state machine + } +} +EXPORT_SYMBOL (fscache_end_cookie_access); + +/* +* Pin the cache behind a cookie so that we can access it. +*/ +static void __fscache_begin_cookie_access (struct fscache_cookie *cookie, +enum fscache_access_trace why) +{ +int n_accesses; + + n_accesses =atomic_inc_return (&cookie->n_accesses); +smp_mb__after_atomic ();/* (Future) read state after is-caching. +* Reread n_accesses after is-caching +*/ +trace_fscache_access (cookie->debug_id,refcount_read (&cookie->ref), + n_accesses, why); +} + +/** +* fscache_begin_cookie_access - Pin a cache so data can be accessed +* @cookie: A data file cookie +* @why: An indication of the circumstances of the access for tracing +* +* Attempt to pin the cache to prevent it from going away whilst we're +* accessing data and returns true if successful. This works as follows: +* +* (1) If the cookie is not being cached (ie. FSCACHE_COOKIE_IS_CACHING is not +* set), we return false to indicate access was not permitted. +* +* (2) If the cookie is being cached, we increment its n_accesses count and +* then recheck the IS_CACHING flag, ending the access if it got cleared. +* +* (3) When we end the access, we decrement the cookie's n_accesses and wake +* up the any waiters if it reaches 0. +* +* (4) Whilst the cookie is actively being cached, its n_accesses is kept +* artificially incremented to prevent wakeups from happening. +* +* (5) When the cache is taken offline or if the cookie is culled, the flag is +* cleared to prevent new accesses, the cookie's n_accesses is decremented +* and we wait for it to become 0. +* +* The @why indicator are merely provided for tracing purposes. +*/ +bool fscache_begin_cookie_access (struct fscache_cookie *cookie, +enum fscache_access_trace why) +{ +if (!test_bit (FSCACHE_COOKIE_IS_CACHING , &cookie->flags)) +return false; +__fscache_begin_cookie_access (cookie, why); +if (!test_bit (FSCACHE_COOKIE_IS_CACHING , &cookie->flags) || + !fscache_cache_is_live (cookie->volume->cache)) { +fscache_end_cookie_access (cookie, fscache_access_unlive); +return false; + } +return true; +} +static inline void wake_up_cookie_state (struct fscache_cookie *cookie) {/* Use a barrier to ensure that waiters see the state variable
fs/fscache/internal.h +3 -0
@@ -59,6 +59,9 @@ extern struct kmem_cache *fscache_cookie_jar;extern const struct seq_operations fscache_cookies_seq_ops;extern void fscache_print_cookie (struct fscache_cookie *cookie,char prefix); +extern bool fscache_begin_cookie_access (struct fscache_cookie *cookie, +enum fscache_access_trace why); +static inline void fscache_see_cookie (struct fscache_cookie *cookie,enum fscache_cookie_trace where) {
fs/fscache/main.c +1 -0
@@ -23,6 +23,7 @@ MODULE_PARM_DESC(fscache_debug,EXPORT_TRACEPOINT_SYMBOL (fscache_access_cache);EXPORT_TRACEPOINT_SYMBOL (fscache_access_volume); +EXPORT_TRACEPOINT_SYMBOL (fscache_access);struct workqueue_struct *fscache_wq;EXPORT_SYMBOL (fscache_wq);
include/linux/fscache-cache.h +2 -0
@@ -61,6 +61,8 @@ extern struct fscache_cookie *fscache_get_cookie(struct fscache_cookie *cookie, enum fscache_cookie_trace where);extern void fscache_put_cookie (struct fscache_cookie *cookie,enum fscache_cookie_trace where); +extern void fscache_end_cookie_access (struct fscache_cookie *cookie, +enum fscache_access_trace why);extern void fscache_set_cookie_state (struct fscache_cookie *cookie,enum fscache_cookie_state state);
include/trace/events/fscache.h +29 -0
@@ -279,6 +279,35 @@ TRACE_EVENT(fscache_access_volume, __entry->n_accesses) ); +TRACE_EVENT (fscache_access, + TP_PROTO(unsigned int cookie_debug_id, + int ref, + int n_accesses, + enum fscache_access_trace why), + +TP_ARGS (cookie_debug_id, ref, n_accesses, why), + +TP_STRUCT__entry ( + __field(unsigned int, cookie ) + __field(int, ref ) + __field(int, n_accesses ) + __field(enum fscache_access_trace, why ) + ), + +TP_fast_assign ( + __entry->cookie = cookie_debug_id; + __entry->ref = ref; + __entry->n_accesses = n_accesses; + __entry->why = why; + ), + +TP_printk ("c=%08x %s r=%d a=%d" , + __entry->cookie, +__print_symbolic (__entry->why, fscache_access_traces), + __entry->ref, + __entry->n_accesses) + ); + TRACE_EVENT(fscache_acquire, TP_PROTO(struct fscache_cookie *cookie),
David Howells
maintainerPATCH v4 14/68 fscache: Implement functions add/remove a cache
In reply to David HowellsImplement functions to allow the cache backend to add or remove a cache:
(1) Declare a cache to be live:
int fscache_add_cache(struct fscache_cache *cache,
const struct fscache_cache_ops *ops, void *cache_priv);
Take a previously acquired cache cookie, set the operations table and private data and mark the cache open for access.
(2) Withdraw a cache from service:
void fscache_withdraw_cache(struct fscache_cache *cache);
This marks the cache as withdrawn and thus prevents further cache-level and volume-level accesses.
---
fs/fscache/cache.c | 70 +++++++++++++++++++++++++++++++++++++++++ include/linux/fscache-cache.h | 13 ++++++++ 2 files changed, 83 insertions(+)
fs/fscache/cache.c +70 -0
@@ -210,12 +210,55 @@ void fscache_relinquish_cache(struct fscache_cache *cache) fscache_cache_put_prep_failed : fscache_cache_put_relinquish; + cache->ops =NULL ; cache->cache_priv =NULL ;smp_store_release (&cache->state,FSCACHE_CACHE_IS_NOT_PRESENT );fscache_put_cache (cache, where); }EXPORT_SYMBOL (fscache_relinquish_cache); +/** +* fscache_add_cache - Declare a cache as being open for business +* @cache: The cache-level cookie representing the cache +* @ops: Table of cache operations to use +* @cache_priv: Private data for the cache record +* +* Add a cache to the system, making it available for netfs's to use. +* +* See Documentation/filesystems/caching/backend-api.rst for a complete +* description. +*/ +int fscache_add_cache (struct fscache_cache *cache, +const struct fscache_cache_ops *ops, +void *cache_priv) +{ +int n_accesses; + +_enter ("{%s,%s}" , ops->name, cache->name); + +BUG_ON (fscache_cache_state (cache) !=FSCACHE_CACHE_IS_PREPARING ); + +/* Get a ref on the cache cookie and keep its n_accesses counter raised +* by 1 to prevent wakeups from transitioning it to 0 until we're +* withdrawing caching services from it. +*/ + n_accesses =atomic_inc_return (&cache->n_accesses); +trace_fscache_access_cache (cache->debug_id,refcount_read (&cache->ref), + n_accesses, fscache_access_cache_pin); + +down_write (&fscache_addremove_sem); + + cache->ops = ops; + cache->cache_priv = cache_priv; +fscache_set_cache_state (cache,FSCACHE_CACHE_IS_ACTIVE ); + +up_write (&fscache_addremove_sem); +pr_notice ("Cache \"%s\" added (type %s)\n" , cache->name, ops->name); +_leave (" = 0 [%s]" , cache->name); +return 0 ; +} +EXPORT_SYMBOL (fscache_add_cache); +/** * fscache_begin_cache_access - Pin a cache so it can be accessed * @cache: The cache-level cookie
@@ -278,6 +321,33 @@ void fscache_end_cache_access(struct fscache_cache *cache, enum fscache_access_twake_up_var (&cache->n_accesses); } +/** +* fscache_withdraw_cache - Withdraw a cache from the active service +* @cache: The cache cookie +* +* Begin the process of withdrawing a cache from service. This stops new +* cache-level and volume-level accesses from taking place and waits for +* currently ongoing cache-level accesses to end. +*/ +void fscache_withdraw_cache (struct fscache_cache *cache) +{ +int n_accesses; + +pr_notice ("Withdrawing cache \"%s\" (%u objs)\n" , + cache->name,atomic_read (&cache->object_count)); + +fscache_set_cache_state (cache,FSCACHE_CACHE_IS_WITHDRAWN ); + +/* Allow wakeups on dec-to-0 */ + n_accesses =atomic_dec_return (&cache->n_accesses); +trace_fscache_access_cache (cache->debug_id,refcount_read (&cache->ref), + n_accesses, fscache_access_cache_unpin); + +wait_var_event (&cache->n_accesses, +atomic_read (&cache->n_accesses) ==0 ); +} +EXPORT_SYMBOL (fscache_withdraw_cache); +#ifdef CONFIG_PROC_FS static const char fscache_cache_states[NR__FSCACHE_CACHE_STATE ] ="-PAEW" ;
include/linux/fscache-cache.h +13 -0
@@ -33,6 +33,7 @@ enum fscache_cache_state {* Cache cookie. */ struct fscache_cache { +const struct fscache_cache_ops *ops;struct list_head cache_link;/* Link in cache list */ void *cache_priv;/* Private cache data (or NULL) */ refcount_t ref;
@@ -44,6 +45,14 @@ struct fscache_cache {char *name; }; +/* +* cache operations +*/ +struct fscache_cache_ops { +/* name of cache provider */ +const char *name; +}; +extern struct workqueue_struct *fscache_wq;/*
@@ -52,6 +61,10 @@ extern struct workqueue_struct *fscache_wq;extern struct rw_semaphore fscache_addremove_sem;extern struct fscache_cache *fscache_acquire_cache (const char *name);extern void fscache_relinquish_cache (struct fscache_cache *cache); +extern int fscache_add_cache (struct fscache_cache *cache, +const struct fscache_cache_ops *ops, +void *cache_priv); +extern void fscache_withdraw_cache (struct fscache_cache *cache); extern void fscache_end_volume_access(struct fscache_volume *volume, struct fscache_cookie *cookie,
David Howells
maintainerPATCH v4 15/68 fscache: Provide and use cache methods to lookup/create/free a volume
In reply to David HowellsAdd cache methods to lookup, create and remove a volume.
Looking up or creating the volume requires the cache pinning for access; freeing the volume requires the volume pinning for access. The ->acquire_volume() method is used to ask the cache backend to lookup and, if necessary, create a volume; the ->free_volume() method is used to free the resources for a volume.
---
fs/fscache/volume.c | 89 +++++++++++++++++++++++++++++++++++++++- include/linux/fscache-cache.h | 7 +++ include/trace/events/fscache.h | 11 ++++- 3 files changed, 103 insertions(+), 4 deletions(-)
fs/fscache/volume.c +86 -3
@@ -15,6 +15,8 @@ static struct hlist_bl_head fscache_volume_hash[1 << fscache_volume_hash_shift];static atomic_t fscache_volume_debug_id;static LIST_HEAD (fscache_volumes); +static void fscache_create_volume_work (struct work_struct *work); +struct fscache_volume *fscache_get_volume (struct fscache_volume *volume,enum fscache_volume_trace where) {
@@ -213,7 +215,7 @@ static struct fscache_volume *fscache_alloc_volume(const char *volume_key, volume->cache = cache;INIT_LIST_HEAD (&volume->proc_link); -INIT_WORK (&volume->work,NULL /* PLACEHOLDER */ ); +INIT_WORK (&volume->work, fscache_create_volume_work);refcount_set (&volume->ref,1 );spin_lock_init (&volume->lock);
@@ -249,6 +251,58 @@ static struct fscache_volume *fscache_alloc_volume(const char *volume_key,return NULL ; } +/* +* Create a volume's representation on disk. Have a volume ref and a cache +* access we have to release. +*/ +static void fscache_create_volume_work (struct work_struct *work) +{ +const struct fscache_cache_ops *ops; +struct fscache_volume *volume = + container_of(work, struct fscache_volume, work); + +fscache_see_volume (volume, fscache_volume_see_create_work); + + ops = volume->cache->ops; +if (ops->acquire_volume) + ops->acquire_volume (volume); +fscache_end_cache_access (volume->cache, + fscache_access_acquire_volume_end); + +clear_bit_unlock (FSCACHE_VOLUME_CREATING , &volume->flags); +wake_up_bit (&volume->flags,FSCACHE_VOLUME_CREATING ); +fscache_put_volume (volume, fscache_volume_put_create_work); +} + +/* +* Dispatch a worker thread to create a volume's representation on disk. +*/ +void fscache_create_volume (struct fscache_volume *volume,bool wait) +{ +if (test_and_set_bit (FSCACHE_VOLUME_CREATING , &volume->flags)) + goto maybe_wait; +if (volume->cache_priv) + goto no_wait;/* We raced */ +if (!fscache_begin_cache_access (volume->cache, + fscache_access_acquire_volume)) + goto no_wait; + +fscache_get_volume (volume, fscache_volume_get_create_work); +if (!schedule_work (&volume->work)) +fscache_put_volume (volume, fscache_volume_put_create_work); + +maybe_wait: +if (wait) { +fscache_see_volume (volume, fscache_volume_wait_create_work); +wait_on_bit (&volume->flags,FSCACHE_VOLUME_CREATING , +TASK_UNINTERRUPTIBLE ); + } +return ; +no_wait: +clear_bit_unlock (FSCACHE_VOLUME_CREATING , &volume->flags); +wake_up_bit (&volume->flags,FSCACHE_VOLUME_CREATING ); +} +/* * Acquire a volume representation cookie and link it to a (proposed) cache. */
@@ -269,7 +323,7 @@ struct fscache_volume *__fscache_acquire_volume(const char *volume_key,return ERR_PTR (-EBUSY ); } -// PLACEHOLDER: Create the volume if we have a cache available +fscache_create_volume (volume, false);return volume; }EXPORT_SYMBOL (__fscache_acquire_volume);
@@ -316,7 +370,12 @@ static void fscache_free_volume(struct fscache_volume *volume)struct fscache_cache *cache = volume->cache;if (volume->cache_priv) { -// PLACEHOLDER: Detach any attached cache +__fscache_begin_volume_access (volume,NULL , + fscache_access_relinquish_volume); +if (volume->cache_priv) + cache->ops->free_volume (volume); +fscache_end_volume_access (volume,NULL , + fscache_access_relinquish_volume_end); }down_write (&fscache_addremove_sem);
@@ -369,6 +428,30 @@ void __fscache_relinquish_volume(struct fscache_volume *volume, }EXPORT_SYMBOL (__fscache_relinquish_volume); +/** +* fscache_withdraw_volume - Withdraw a volume from being cached +* @volume: Volume cookie +* +* Withdraw a cache volume from service, waiting for all accesses to complete +* before returning. +*/ +void fscache_withdraw_volume (struct fscache_volume *volume) +{ +int n_accesses; + +_debug ("withdraw V=%x" , volume->debug_id); + +/* Allow wakeups on dec-to-0 */ + n_accesses =atomic_dec_return (&volume->n_accesses); +trace_fscache_access_volume (volume->debug_id,0 , +refcount_read (&volume->ref), + n_accesses, fscache_access_cache_unpin); + +wait_var_event (&volume->n_accesses, +atomic_read (&volume->n_accesses) ==0 ); +} +EXPORT_SYMBOL (fscache_withdraw_volume); +#ifdef CONFIG_PROC_FS /* * Generate a list of volumes in /proc/fs/fscache/volumes
include/linux/fscache-cache.h +7 -0
@@ -51,6 +51,12 @@ struct fscache_cache {struct fscache_cache_ops {/* name of cache provider */ const char *name; + +/* Acquire a volume */ +void (*acquire_volume)(struct fscache_volume *volume); + +/* Free the cache's data attached to a volume */ +void (*free_volume)(struct fscache_volume *volume); };extern struct workqueue_struct *fscache_wq;
@@ -65,6 +71,7 @@ extern int fscache_add_cache(struct fscache_cache *cache,const struct fscache_cache_ops *ops, void *cache_priv);extern void fscache_withdraw_cache (struct fscache_cache *cache); +extern void fscache_withdraw_volume (struct fscache_volume *volume); extern void fscache_end_volume_access(struct fscache_volume *volume, struct fscache_cookie *cookie,
include/trace/events/fscache.h +10 -1
@@ -64,8 +64,12 @@ enum fscache_cookie_trace { };enum fscache_access_trace { + fscache_access_acquire_volume, + fscache_access_acquire_volume_end, fscache_access_cache_pin, fscache_access_cache_unpin, + fscache_access_relinquish_volume, + fscache_access_relinquish_volume_end, fscache_access_unlive, };
@@ -96,7 +100,8 @@ enum fscache_access_trace { EM(fscache_volume_put_hash_collision, "PUT hcoll") \ EM(fscache_volume_put_relinquish, "PUT relnq") \ EM(fscache_volume_see_create_work, "SEE creat") \ - E_(fscache_volume_see_hash_wake, "SEE hwake") + EM(fscache_volume_see_hash_wake, "SEE hwake") \ + E_(fscache_volume_wait_create_work, "WAIT crea")#define fscache_cookie_traces \ EM(fscache_cookie_collision, "*COLLIDE*") \
@@ -115,8 +120,12 @@ enum fscache_access_trace { E_(fscache_cookie_see_work, "- work ")#define fscache_access_traces \ + EM(fscache_access_acquire_volume, "BEGIN acq_vol") \ + EM(fscache_access_acquire_volume_end, "END acq_vol") \ EM(fscache_access_cache_pin, "PIN cache ") \ EM(fscache_access_cache_unpin, "UNPIN cache ") \ + EM(fscache_access_relinquish_volume, "BEGIN rlq_vol") \ + EM(fscache_access_relinquish_volume_end,"END rlq_vol") \ E_(fscache_access_unlive, "END unlive ")/*
David Howells
maintainerPATCH v4 16/68 fscache: Add a function for a cache backend to note an I/O error
In reply to David HowellsAdd a function to the backend API to note an I/O error in a cache.
---
fs/fscache/cache.c | 20 ++++++++++++++++++++ include/linux/fscache-cache.h | 2 ++ 2 files changed, 22 insertions(+)
fs/fscache/cache.c +20 -0
@@ -321,6 +321,26 @@ void fscache_end_cache_access(struct fscache_cache *cache, enum fscache_access_twake_up_var (&cache->n_accesses); } +/** +* fscache_io_error - Note a cache I/O error +* @cache: The record describing the cache +* +* Note that an I/O error occurred in a cache and that it should no longer be +* used for anything. This also reports the error into the kernel log. +* +* See Documentation/filesystems/caching/backend-api.rst for a complete +* description. +*/ +void fscache_io_error (struct fscache_cache *cache) +{ +if (fscache_set_cache_state_maybe (cache, +FSCACHE_CACHE_IS_ACTIVE , +FSCACHE_CACHE_GOT_IOERROR )) +pr_err ("Cache '%s' stopped due to I/O error\n" , + cache->name); +} +EXPORT_SYMBOL (fscache_io_error); +/** * fscache_withdraw_cache - Withdraw a cache from the active service * @cache: The cache cookie
include/linux/fscache-cache.h +2 -0
@@ -73,6 +73,8 @@ extern int fscache_add_cache(struct fscache_cache *cache,extern void fscache_withdraw_cache (struct fscache_cache *cache);extern void fscache_withdraw_volume (struct fscache_volume *volume); +extern void fscache_io_error (struct fscache_cache *cache); +extern void fscache_end_volume_access (struct fscache_volume *volume,struct fscache_cookie *cookie,enum fscache_access_trace why);
David Howells
maintainerPATCH v4 17/68 fscache: Implement simple cookie state machine
In reply to David HowellsImplement a very simple cookie state machine to handle lookup, invalidation, withdrawal, relinquishment and, to be added later, commit on LRU discard.
Three cache methods are provided: ->lookup_cookie() to look up and, if necessary, create a data storage object; ->withdraw_cookie() to free the resources associated with that object and potentially delete it; and ->prepare_to_write(), to do prepare for changes to the cached data to be modified locally.
Changes
=======
ver #3:
- Fix a race between LRU discard and relinquishment whereby the former would override the latter and thus the latter would never happen[1].
ver #2:
- Don't hold n_accesses elevated whilst cache is bound to a cookie, but rather add a flag that prevents the state machine from being queued when n_accesses reaches 0.
---
fs/fscache/cookie.c | 313 +++++++++++++++++++++++++++++++++++----- include/linux/fscache-cache.h | 27 +++ include/trace/events/fscache.h | 4 + 3 files changed, 300 insertions(+), 44 deletions(-)
fs/fscache/cookie.c +271 -42
@@ -15,7 +15,8 @@struct kmem_cache *fscache_cookie_jar; -static void fscache_drop_cookie (struct fscache_cookie *cookie); +static void fscache_cookie_worker (struct work_struct *work); +static void fscache_unhash_cookie (struct fscache_cookie *cookie);#define fscache_cookie_hash_shift 15static struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_shift];
@@ -62,6 +63,19 @@ static void fscache_free_cookie(struct fscache_cookie *cookie)kmem_cache_free (fscache_cookie_jar, cookie); } +static void __fscache_queue_cookie (struct fscache_cookie *cookie) +{ +if (!queue_work (fscache_wq, &cookie->work)) +fscache_put_cookie (cookie, fscache_cookie_put_over_queued); +} + +static void fscache_queue_cookie (struct fscache_cookie *cookie, +enum fscache_cookie_trace where) +{ +fscache_get_cookie (cookie, where); +__fscache_queue_cookie (cookie); +} +/* * Initialise the access gate on a cookie by setting a flag to prevent the * state machine from being queued when the access counter transitions to 0.
@@ -98,9 +112,8 @@ void fscache_end_cookie_access(struct fscache_cookie *cookie,trace_fscache_access (cookie->debug_id,refcount_read (&cookie->ref), n_accesses, why);if (n_accesses ==0 && - !test_bit (FSCACHE_COOKIE_NO_ACCESS_WAKE , &cookie->flags)) { -// PLACEHOLDER: Need to poke the state machine - } + !test_bit (FSCACHE_COOKIE_NO_ACCESS_WAKE , &cookie->flags)) +fscache_queue_cookie (cookie, fscache_cookie_get_end_access); }EXPORT_SYMBOL (fscache_end_cookie_access);
@@ -171,35 +184,58 @@ static inline void wake_up_cookie_state(struct fscache_cookie *cookie)wake_up_var (&cookie->state); } +/* +* Change the state a cookie is at and wake up anyone waiting for that. Impose +* an ordering between the stuff stored in the cookie and the state member. +* Paired with fscache_cookie_state(). +*/ static void __fscache_set_cookie_state (struct fscache_cookie *cookie,enum fscache_cookie_state state) { - cookie->state = state; +smp_store_release (&cookie->state, state); } -/* -* Change the state a cookie is at and wake up anyone waiting for that - but -* only if the cookie isn't already marked as being in a cleanup state. -*/ -void fscache_set_cookie_state (struct fscache_cookie *cookie, -enum fscache_cookie_state state) +static void fscache_set_cookie_state (struct fscache_cookie *cookie, +enum fscache_cookie_state state) { -bool changed = false; -spin_lock (&cookie->lock); -switch (cookie->state) { -case FSCACHE_COOKIE_STATE_RELINQUISHING : -break ; -default : -__fscache_set_cookie_state (cookie, state); - changed = true; -break ; - } +__fscache_set_cookie_state (cookie, state);spin_unlock (&cookie->lock); -if (changed) -wake_up_cookie_state (cookie); +wake_up_cookie_state (cookie); +} + +/** +* fscache_cookie_lookup_negative - Note negative lookup +* @cookie: The cookie that was being looked up +* +* Note that some part of the metadata path in the cache doesn't exist and so +* we can release any waiting readers in the certain knowledge that there's +* nothing for them to actually read. +* +* This function uses no locking and must only be called from the state machine. +*/ +void fscache_cookie_lookup_negative (struct fscache_cookie *cookie) +{ +set_bit (FSCACHE_COOKIE_NO_DATA_TO_READ , &cookie->flags); +fscache_set_cookie_state (cookie,FSCACHE_COOKIE_STATE_CREATING ); +} +EXPORT_SYMBOL (fscache_cookie_lookup_negative); + +/** +* fscache_caching_failed - Report that a failure stopped caching on a cookie +* @cookie: The cookie that was affected +* +* Tell fscache that caching on a cookie needs to be stopped due to some sort +* of failure. +* +* This function uses no locking and must only be called from the state machine. +*/ +void fscache_caching_failed (struct fscache_cookie *cookie) +{ +clear_bit (FSCACHE_COOKIE_IS_CACHING , &cookie->flags); +fscache_set_cookie_state (cookie,FSCACHE_COOKIE_STATE_FAILED ); } -EXPORT_SYMBOL (fscache_set_cookie_state); +EXPORT_SYMBOL (fscache_caching_failed);/* * Set the index key in a cookie. The cookie struct has space for a 16-byte
@@ -291,10 +327,10 @@ static struct fscache_cookie *fscache_alloc_cookie(refcount_set (&cookie->ref,1 ); cookie->debug_id =atomic_inc_return (&fscache_cookie_debug_id); - cookie->state =FSCACHE_COOKIE_STATE_QUIESCENT ;spin_lock_init (&cookie->lock);INIT_LIST_HEAD (&cookie->commit_link); -INIT_WORK (&cookie->work,NULL /* PLACEHOLDER */ ); +INIT_WORK (&cookie->work, fscache_cookie_worker); +__fscache_set_cookie_state (cookie,FSCACHE_COOKIE_STATE_QUIESCENT );write_lock (&fscache_cookies_lock);list_add_tail (&cookie->proc_link, &fscache_cookies);
@@ -417,6 +453,192 @@ struct fscache_cookie *__fscache_acquire_cookie( }EXPORT_SYMBOL (__fscache_acquire_cookie); +/* +* Prepare a cache object to be written to. +*/ +static void fscache_prepare_to_write (struct fscache_cookie *cookie) +{ + cookie->volume->cache->ops->prepare_to_write (cookie); +} + +/* +* Look up a cookie in the cache. +*/ +static void fscache_perform_lookup (struct fscache_cookie *cookie) +{ +enum fscache_access_trace trace = fscache_access_lookup_cookie_end_failed; +bool need_withdraw = false; + +_enter ("" ); + +if (!cookie->volume->cache_priv) { +fscache_create_volume (cookie->volume, true); +if (!cookie->volume->cache_priv) { +fscache_set_cookie_state (cookie,FSCACHE_COOKIE_STATE_QUIESCENT ); + goto out; + } + } + +if (!cookie->volume->cache->ops->lookup_cookie (cookie)) { +if (cookie->state !=FSCACHE_COOKIE_STATE_FAILED ) +fscache_set_cookie_state (cookie,FSCACHE_COOKIE_STATE_QUIESCENT ); + need_withdraw = true; +_leave (" [fail]" ); + goto out; + } + +fscache_see_cookie (cookie, fscache_cookie_see_active); +fscache_set_cookie_state (cookie,FSCACHE_COOKIE_STATE_ACTIVE ); + trace = fscache_access_lookup_cookie_end; + +out: +fscache_end_cookie_access (cookie, trace); +if (need_withdraw) +fscache_withdraw_cookie (cookie); +fscache_end_volume_access (cookie->volume, cookie, trace); +} + +/* +* Perform work upon the cookie, such as committing its cache state, +* relinquishing it or withdrawing the backing cache. We're protected from the +* cache going away under us as object withdrawal must come through this +* non-reentrant work item. +*/ +static void fscache_cookie_state_machine (struct fscache_cookie *cookie) +{ +enum fscache_cookie_state state; +bool wake = false; + +_enter ("c=%x" , cookie->debug_id); + +again: +spin_lock (&cookie->lock); +again_locked: + state = cookie->state; +switch (state) { +case FSCACHE_COOKIE_STATE_QUIESCENT : +/* The QUIESCENT state is jumped to the LOOKING_UP state by +* fscache_use_cookie(). +*/ + +if (atomic_read (&cookie->n_accesses) ==0 && +test_bit (FSCACHE_COOKIE_DO_RELINQUISH , &cookie->flags)) { +__fscache_set_cookie_state (cookie, +FSCACHE_COOKIE_STATE_RELINQUISHING ); + wake = true; + goto again_locked; + } +break ; + +case FSCACHE_COOKIE_STATE_LOOKING_UP : +spin_unlock (&cookie->lock); +fscache_init_access_gate (cookie); +fscache_perform_lookup (cookie); + goto again; + +case FSCACHE_COOKIE_STATE_ACTIVE : +if (test_and_clear_bit (FSCACHE_COOKIE_DO_PREP_TO_WRITE , &cookie->flags)) { +spin_unlock (&cookie->lock); +fscache_prepare_to_write (cookie); +spin_lock (&cookie->lock); + } + fallthrough; + +case FSCACHE_COOKIE_STATE_FAILED : +if (atomic_read (&cookie->n_accesses) !=0 ) +break ; +if (test_bit (FSCACHE_COOKIE_DO_RELINQUISH , &cookie->flags)) { +__fscache_set_cookie_state (cookie, +FSCACHE_COOKIE_STATE_RELINQUISHING ); + wake = true; + goto again_locked; + } +if (test_bit (FSCACHE_COOKIE_DO_WITHDRAW , &cookie->flags)) { +__fscache_set_cookie_state (cookie, +FSCACHE_COOKIE_STATE_WITHDRAWING ); + wake = true; + goto again_locked; + } +break ; + +case FSCACHE_COOKIE_STATE_RELINQUISHING : +case FSCACHE_COOKIE_STATE_WITHDRAWING : +if (cookie->cache_priv) { +spin_unlock (&cookie->lock); + cookie->volume->cache->ops->withdraw_cookie (cookie); +spin_lock (&cookie->lock); + } + +switch (state) { +case FSCACHE_COOKIE_STATE_RELINQUISHING : +fscache_see_cookie (cookie, fscache_cookie_see_relinquish); +fscache_unhash_cookie (cookie); +__fscache_set_cookie_state (cookie, +FSCACHE_COOKIE_STATE_DROPPED ); + wake = true; + goto out; +case FSCACHE_COOKIE_STATE_WITHDRAWING : +fscache_see_cookie (cookie, fscache_cookie_see_withdraw); +break ; +default : +BUG (); + } + +clear_bit (FSCACHE_COOKIE_NEEDS_UPDATE , &cookie->flags); +clear_bit (FSCACHE_COOKIE_DO_WITHDRAW , &cookie->flags); +clear_bit (FSCACHE_COOKIE_DO_LRU_DISCARD , &cookie->flags); +clear_bit (FSCACHE_COOKIE_DO_PREP_TO_WRITE , &cookie->flags); +set_bit (FSCACHE_COOKIE_NO_DATA_TO_READ , &cookie->flags); +__fscache_set_cookie_state (cookie,FSCACHE_COOKIE_STATE_QUIESCENT ); + wake = true; + goto again_locked; + +case FSCACHE_COOKIE_STATE_DROPPED : +break ; + +default : +WARN_ONCE (1 ,"Cookie %x in unexpected state %u\n" , + cookie->debug_id, state); +break ; + } + +out: +spin_unlock (&cookie->lock); +if (wake) +wake_up_cookie_state (cookie); +_leave ("" ); +} + +static void fscache_cookie_worker (struct work_struct *work) +{ + struct fscache_cookie *cookie = container_of(work, struct fscache_cookie, work); + +fscache_see_cookie (cookie, fscache_cookie_see_work); +fscache_cookie_state_machine (cookie); +fscache_put_cookie (cookie, fscache_cookie_put_work); +} + +/* +* Wait for the object to become inactive. The cookie's work item will be +* scheduled when someone transitions n_accesses to 0 - but if someone's +* already done that, schedule it anyway. +*/ +static void __fscache_withdraw_cookie (struct fscache_cookie *cookie) +{ +int n_accesses; +bool unpinned; + + unpinned =test_and_clear_bit (FSCACHE_COOKIE_NO_ACCESS_WAKE , &cookie->flags); + +/* Need to read the access count after unpinning */ + n_accesses =atomic_read (&cookie->n_accesses); +if (unpinned) +trace_fscache_access (cookie->debug_id,refcount_read (&cookie->ref), + n_accesses, fscache_access_cache_unpin); +if (n_accesses ==0 ) +fscache_queue_cookie (cookie, fscache_cookie_get_end_access); +} +/* * Remove a cookie from the hash table. */
@@ -432,21 +654,27 @@ static void fscache_unhash_cookie(struct fscache_cookie *cookie)hlist_bl_del (&cookie->hash_link);clear_bit (FSCACHE_COOKIE_IS_HASHED , &cookie->flags);hlist_bl_unlock (h); +fscache_stat (&fscache_n_relinquishes_dropped); } -/* -* Finalise a cookie after all its resources have been disposed of. -*/ -static void fscache_drop_cookie (struct fscache_cookie *cookie) +static void fscache_drop_withdraw_cookie (struct fscache_cookie *cookie) { -spin_lock (&cookie->lock); -__fscache_set_cookie_state (cookie,FSCACHE_COOKIE_STATE_DROPPED ); -spin_unlock (&cookie->lock); -wake_up_cookie_state (cookie); +__fscache_withdraw_cookie (cookie); +} -fscache_unhash_cookie (cookie); -fscache_stat (&fscache_n_relinquishes_dropped); +/** +* fscache_withdraw_cookie - Mark a cookie for withdrawal +* @cookie: The cookie to be withdrawn. +* +* Allow the cache backend to withdraw the backing for a cookie for its own +* reasons, even if that cookie is in active use. +*/ +void fscache_withdraw_cookie (struct fscache_cookie *cookie) +{ +set_bit (FSCACHE_COOKIE_DO_WITHDRAW , &cookie->flags); +fscache_drop_withdraw_cookie (cookie); } +EXPORT_SYMBOL (fscache_withdraw_cookie);/* * Allow the netfs to release a cookie back to the cache.
@@ -473,12 +701,13 @@ void __fscache_relinquish_cookie(struct fscache_cookie *cookie, bool retire) ASSERTCMP(atomic_read(&cookie->volume->n_cookies), >, 0);atomic_dec (&cookie->volume->n_cookies); -set_bit (FSCACHE_COOKIE_DO_RELINQUISH , &cookie->flags); - -if (test_bit (FSCACHE_COOKIE_HAS_BEEN_CACHED , &cookie->flags)) - ;// PLACEHOLDER: Do something here if the cookie was cached -else -fscache_drop_cookie (cookie); +if (test_bit (FSCACHE_COOKIE_HAS_BEEN_CACHED , &cookie->flags)) { +set_bit (FSCACHE_COOKIE_DO_RELINQUISH , &cookie->flags); +fscache_drop_withdraw_cookie (cookie); + }else { +fscache_set_cookie_state (cookie,FSCACHE_COOKIE_STATE_DROPPED ); +fscache_unhash_cookie (cookie); + }fscache_put_cookie (cookie, fscache_cookie_put_relinquish); }EXPORT_SYMBOL (__fscache_relinquish_cookie);
include/linux/fscache-cache.h +25 -2
@@ -57,6 +57,15 @@ struct fscache_cache_ops {/* Free the cache's data attached to a volume */ void (*free_volume)(struct fscache_volume *volume); + +/* Look up a cookie in the cache */ +bool (*lookup_cookie)(struct fscache_cookie *cookie); + +/* Withdraw an object without any cookie access counts held */ +void (*withdraw_cookie)(struct fscache_cookie *cookie); + +/* Prepare to write to a live cache object */ +void (*prepare_to_write)(struct fscache_cookie *cookie); };extern struct workqueue_struct *fscache_wq;
@@ -72,6 +81,7 @@ extern int fscache_add_cache(struct fscache_cache *cache, void *cache_priv);extern void fscache_withdraw_cache (struct fscache_cache *cache);extern void fscache_withdraw_volume (struct fscache_volume *volume); +extern void fscache_withdraw_cookie (struct fscache_cookie *cookie);extern void fscache_io_error (struct fscache_cache *cache);
@@ -85,8 +95,21 @@ extern void fscache_put_cookie(struct fscache_cookie *cookie, enum fscache_cookie_trace where);extern void fscache_end_cookie_access (struct fscache_cookie *cookie,enum fscache_access_trace why); -extern void fscache_set_cookie_state (struct fscache_cookie *cookie, -enum fscache_cookie_state state); +extern void fscache_cookie_lookup_negative (struct fscache_cookie *cookie); +extern void fscache_caching_failed (struct fscache_cookie *cookie); + +/** +* fscache_cookie_state - Read the state of a cookie +* @cookie: The cookie to query +* +* Get the state of a cookie, imposing an ordering between the cookie contents +* and the state value. Paired with fscache_set_cookie_state(). +*/ +static inline +enum fscache_cookie_state fscache_cookie_state (struct fscache_cookie *cookie) +{ +return smp_load_acquire (&cookie->state); +}/** * fscache_get_key - Get a pointer to the cookie key
include/trace/events/fscache.h +4 -0
@@ -68,6 +68,8 @@ enum fscache_access_trace { fscache_access_acquire_volume_end, fscache_access_cache_pin, fscache_access_cache_unpin, + fscache_access_lookup_cookie_end, + fscache_access_lookup_cookie_end_failed, fscache_access_relinquish_volume, fscache_access_relinquish_volume_end, fscache_access_unlive,
@@ -124,6 +126,8 @@ enum fscache_access_trace { EM(fscache_access_acquire_volume_end, "END acq_vol") \ EM(fscache_access_cache_pin, "PIN cache ") \ EM(fscache_access_cache_unpin, "UNPIN cache ") \ + EM(fscache_access_lookup_cookie_end, "END lookup ") \ + EM(fscache_access_lookup_cookie_end_failed,"END lookupf") \ EM(fscache_access_relinquish_volume, "BEGIN rlq_vol") \ EM(fscache_access_relinquish_volume_end,"END rlq_vol") \E_ (fscache_access_unlive,"END unlive " )
David Howells
maintainerPATCH v4 18/68 fscache: Implement cookie user counting and resource pinning
In reply to David HowellsProvide a pair of functions to count the number of users of a cookie (open files, writeback, invalidation, resizing, reads, writes), to obtain and pin resources for the cookie and to prevent culling for the whilst there are users.
The first function marks a cookie as being in use:
void fscache_use_cookie(struct fscache_cookie *cookie, bool will_modify);
The caller should indicate the cookie to use and whether or not the caller is in a context that may modify the cookie (e.g. a file open O_RDWR).
If the cookie is not already resourced, fscache will ask the cache backend in the background to do whatever it needs to look up, create or otherwise obtain the resources necessary to access data. This is pinned to the cookie and may not be culled, though it may be withdrawn if the cache as a whole is withdrawn.
The second function removes the in-use mark from a cookie and, optionally, updates the coherency data:
void fscache_unuse_cookie(struct fscache_cookie *cookie,
const void *aux_data, const loff_t *object_size);
If non-NULL, the aux_data buffer and/or the object_size will be saved into the cookie and will be set on the backing store when the object is committed.
If this removes the last usage on a cookie, the cookie is placed onto an LRU list from which it will be removed and closed after a couple of seconds if it doesn't get reused. This prevents resource overload in the cache - in particular it prevents it from holding too many files open.
Changes
=======
ver #2:
- Fix fscache_unuse_cookie() to use atomic_dec_and_lock() to avoid a potential race if the cookie gets reused before it completes the unusement.
- Added missing transition to LRU_DISCARDING state.
---
fs/fscache/cookie.c | 218 ++++++++++++++++++++++++++++++++++++++++ fs/fscache/internal.h | 5 + fs/fscache/stats.c | 12 ++ include/linux/fscache.h | 82 +++++++++++++++ include/trace/events/fscache.h | 12 ++ 5 files changed, 327 insertions(+), 2 deletions(-)
fs/fscache/cookie.c +217 -1
@@ -15,6 +15,8 @@struct kmem_cache *fscache_cookie_jar; +static void fscache_cookie_lru_timed_out (struct timer_list *timer); +static void fscache_cookie_lru_worker (struct work_struct *work);static void fscache_cookie_worker (struct work_struct *work);static void fscache_unhash_cookie (struct fscache_cookie *cookie);
@@ -22,7 +24,12 @@ static void fscache_unhash_cookie(struct fscache_cookie *cookie);static struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_shift];static LIST_HEAD (fscache_cookies);static DEFINE_RWLOCK (fscache_cookies_lock); -static const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR ] ="-LCAFWRD" ; +static LIST_HEAD (fscache_cookie_lru); +static DEFINE_SPINLOCK (fscache_cookie_lru_lock); +DEFINE_TIMER (fscache_cookie_lru_timer, fscache_cookie_lru_timed_out); +static DECLARE_WORK(fscache_cookie_lru_work, fscache_cookie_lru_worker); +static const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR ] ="-LCAFUWRD" ; +unsigned int fscache_lru_cookie_timeout =10 *HZ ;void fscache_print_cookie (struct fscache_cookie *cookie,char prefix) {
@@ -47,6 +54,14 @@ void fscache_print_cookie(struct fscache_cookie *cookie, char prefix)static void fscache_free_cookie (struct fscache_cookie *cookie) { +if (WARN_ON_ONCE (!list_empty (&cookie->commit_link))) { +spin_lock (&fscache_cookie_lru_lock); +list_del_init (&cookie->commit_link); +spin_unlock (&fscache_cookie_lru_lock); +fscache_stat_d (&fscache_n_cookies_lru); +fscache_stat (&fscache_n_cookies_lru_removed); + } +if (WARN_ON_ONCE (test_bit (FSCACHE_COOKIE_IS_HASHED , &cookie->flags))) {fscache_print_cookie (cookie,'F' );return ;
@@ -498,6 +513,126 @@ static void fscache_perform_lookup(struct fscache_cookie *cookie)fscache_end_volume_access (cookie->volume, cookie, trace); } +/* +* Begin the process of looking up a cookie. We offload the actual process to +* a worker thread. +*/ +static bool fscache_begin_lookup (struct fscache_cookie *cookie,bool will_modify) +{ +if (will_modify) { +set_bit (FSCACHE_COOKIE_LOCAL_WRITE , &cookie->flags); +set_bit (FSCACHE_COOKIE_DO_PREP_TO_WRITE , &cookie->flags); + } +if (!fscache_begin_volume_access (cookie->volume, cookie, + fscache_access_lookup_cookie)) +return false; + +__fscache_begin_cookie_access (cookie, fscache_access_lookup_cookie); +__fscache_set_cookie_state (cookie,FSCACHE_COOKIE_STATE_LOOKING_UP ); +set_bit (FSCACHE_COOKIE_IS_CACHING , &cookie->flags); +set_bit (FSCACHE_COOKIE_HAS_BEEN_CACHED , &cookie->flags); +return true; +} + +/* +* Start using the cookie for I/O. This prevents the backing object from being +* reaped by VM pressure. +*/ +void __fscache_use_cookie (struct fscache_cookie *cookie,bool will_modify) +{ +enum fscache_cookie_state state; +bool queue = false; + +_enter ("c=%08x" , cookie->debug_id); + +if (WARN (test_bit (FSCACHE_COOKIE_RELINQUISHED , &cookie->flags), +"Trying to use relinquished cookie\n" )) +return ; + +spin_lock (&cookie->lock); + +atomic_inc (&cookie->n_active); + +again: + state =fscache_cookie_state (cookie); +switch (state) { +case FSCACHE_COOKIE_STATE_QUIESCENT : + queue =fscache_begin_lookup (cookie, will_modify); +break ; + +case FSCACHE_COOKIE_STATE_LOOKING_UP : +case FSCACHE_COOKIE_STATE_CREATING : +if (will_modify) +set_bit (FSCACHE_COOKIE_LOCAL_WRITE , &cookie->flags); +break ; +case FSCACHE_COOKIE_STATE_ACTIVE : +if (will_modify && + !test_and_set_bit (FSCACHE_COOKIE_LOCAL_WRITE , &cookie->flags)) { +set_bit (FSCACHE_COOKIE_DO_PREP_TO_WRITE , &cookie->flags); + queue = true; + } +break ; + +case FSCACHE_COOKIE_STATE_FAILED : +case FSCACHE_COOKIE_STATE_WITHDRAWING : +break ; + +case FSCACHE_COOKIE_STATE_LRU_DISCARDING : +spin_unlock (&cookie->lock); +wait_var_event (&cookie->state, +fscache_cookie_state (cookie) != +FSCACHE_COOKIE_STATE_LRU_DISCARDING ); +spin_lock (&cookie->lock); + goto again; + +case FSCACHE_COOKIE_STATE_DROPPED : +case FSCACHE_COOKIE_STATE_RELINQUISHING : +WARN (1 ,"Can't use cookie in state %u\n" , state); +break ; + } + +spin_unlock (&cookie->lock); +if (queue) +fscache_queue_cookie (cookie, fscache_cookie_get_use_work); +_leave ("" ); +} +EXPORT_SYMBOL (__fscache_use_cookie); + +static void fscache_unuse_cookie_locked (struct fscache_cookie *cookie) +{ +clear_bit (FSCACHE_COOKIE_DISABLED , &cookie->flags); +if (!test_bit (FSCACHE_COOKIE_IS_CACHING , &cookie->flags)) +return ; + + cookie->unused_at = jiffies; +spin_lock (&fscache_cookie_lru_lock); +if (list_empty (&cookie->commit_link)) { +fscache_get_cookie (cookie, fscache_cookie_get_lru); +fscache_stat (&fscache_n_cookies_lru); + } +list_move_tail (&cookie->commit_link, &fscache_cookie_lru); + +spin_unlock (&fscache_cookie_lru_lock); +timer_reduce (&fscache_cookie_lru_timer, + jiffies + fscache_lru_cookie_timeout); +} + +/* +* Stop using the cookie for I/O. +*/ +void __fscache_unuse_cookie (struct fscache_cookie *cookie, +const void *aux_data,const loff_t *object_size) +{ +if (aux_data || object_size) +__fscache_update_cookie (cookie, aux_data, object_size); + +if (atomic_dec_and_lock (&cookie->n_active, &cookie->lock)) { +fscache_unuse_cookie_locked (cookie); +spin_unlock (&cookie->lock); + } +} +EXPORT_SYMBOL (__fscache_unuse_cookie); +/* * Perform work upon the cookie, such as committing its cache state, * relinquishing it or withdrawing the backing cache. We're protected from the
@@ -542,6 +677,12 @@ static void fscache_cookie_state_machine(struct fscache_cookie *cookie)fscache_prepare_to_write (cookie);spin_lock (&cookie->lock); } +if (test_bit (FSCACHE_COOKIE_DO_LRU_DISCARD , &cookie->flags)) { +__fscache_set_cookie_state (cookie, +FSCACHE_COOKIE_STATE_LRU_DISCARDING ); + wake = true; + goto again_locked; + } fallthrough;case FSCACHE_COOKIE_STATE_FAILED :
@@ -561,6 +702,7 @@ static void fscache_cookie_state_machine(struct fscache_cookie *cookie) }break ; +case FSCACHE_COOKIE_STATE_LRU_DISCARDING :case FSCACHE_COOKIE_STATE_RELINQUISHING :case FSCACHE_COOKIE_STATE_WITHDRAWING :if (cookie->cache_priv) {
@@ -577,6 +719,9 @@ static void fscache_cookie_state_machine(struct fscache_cookie *cookie) FSCACHE_COOKIE_STATE_DROPPED); wake = true; goto out; +case FSCACHE_COOKIE_STATE_LRU_DISCARDING : +fscache_see_cookie (cookie, fscache_cookie_see_lru_discard); +break ;case FSCACHE_COOKIE_STATE_WITHDRAWING :fscache_see_cookie (cookie, fscache_cookie_see_withdraw);break ;
@@ -639,6 +784,76 @@ static void __fscache_withdraw_cookie(struct fscache_cookie *cookie)fscache_queue_cookie (cookie, fscache_cookie_get_end_access); } +static void fscache_cookie_lru_do_one (struct fscache_cookie *cookie) +{ +fscache_see_cookie (cookie, fscache_cookie_see_lru_do_one); + +spin_lock (&cookie->lock); +if (cookie->state !=FSCACHE_COOKIE_STATE_ACTIVE || +time_before (jiffies, cookie->unused_at + fscache_lru_cookie_timeout) || +atomic_read (&cookie->n_active) >0 ) { +spin_unlock (&cookie->lock); +fscache_stat (&fscache_n_cookies_lru_removed); + }else { +set_bit (FSCACHE_COOKIE_DO_LRU_DISCARD , &cookie->flags); +spin_unlock (&cookie->lock); +fscache_stat (&fscache_n_cookies_lru_expired); +_debug ("lru c=%x" , cookie->debug_id); +__fscache_withdraw_cookie (cookie); + } + +fscache_put_cookie (cookie, fscache_cookie_put_lru); +} + +static void fscache_cookie_lru_worker (struct work_struct *work) +{ +struct fscache_cookie *cookie; +unsigned long unused_at; + +spin_lock (&fscache_cookie_lru_lock); + +while (!list_empty (&fscache_cookie_lru)) { + cookie =list_first_entry (&fscache_cookie_lru, + struct fscache_cookie, commit_link); + unused_at = cookie->unused_at + fscache_lru_cookie_timeout; +if (time_before (jiffies, unused_at)) { +timer_reduce (&fscache_cookie_lru_timer, unused_at); +break ; + } + +list_del_init (&cookie->commit_link); +fscache_stat_d (&fscache_n_cookies_lru); +spin_unlock (&fscache_cookie_lru_lock); +fscache_cookie_lru_do_one (cookie); +spin_lock (&fscache_cookie_lru_lock); + } + +spin_unlock (&fscache_cookie_lru_lock); +} + +static void fscache_cookie_lru_timed_out (struct timer_list *timer) +{ +queue_work (fscache_wq, &fscache_cookie_lru_work); +} + +static void fscache_cookie_drop_from_lru (struct fscache_cookie *cookie) +{ +bool need_put = false; + +if (!list_empty (&cookie->commit_link)) { +spin_lock (&fscache_cookie_lru_lock); +if (!list_empty (&cookie->commit_link)) { +list_del_init (&cookie->commit_link); +fscache_stat_d (&fscache_n_cookies_lru); +fscache_stat (&fscache_n_cookies_lru_dropped); + need_put = true; + } +spin_unlock (&fscache_cookie_lru_lock); +if (need_put) +fscache_put_cookie (cookie, fscache_cookie_put_lru); + } +} +/* * Remove a cookie from the hash table. */
@@ -659,6 +874,7 @@ static void fscache_unhash_cookie(struct fscache_cookie *cookie)static void fscache_drop_withdraw_cookie (struct fscache_cookie *cookie) { +fscache_cookie_drop_from_lru (cookie);__fscache_withdraw_cookie (cookie); }
fs/fscache/internal.h +5 -0
@@ -57,6 +57,7 @@ static inline bool fscache_set_cache_state_maybe(struct fscache_cache *cache,*/ extern struct kmem_cache *fscache_cookie_jar;extern const struct seq_operations fscache_cookies_seq_ops; +extern struct timer_list fscache_cookie_lru_timer;extern void fscache_print_cookie (struct fscache_cookie *cookie,char prefix); extern bool fscache_begin_cookie_access(struct fscache_cookie *cookie,
@@ -95,6 +96,10 @@ extern atomic_t fscache_n_volumes;extern atomic_t fscache_n_volumes_collision;extern atomic_t fscache_n_volumes_nomem;extern atomic_t fscache_n_cookies; +extern atomic_t fscache_n_cookies_lru; +extern atomic_t fscache_n_cookies_lru_expired; +extern atomic_t fscache_n_cookies_lru_removed; +extern atomic_t fscache_n_cookies_lru_dropped;extern atomic_t fscache_n_acquires;extern atomic_t fscache_n_acquires_ok;
fs/fscache/stats.c +12 -0
@@ -17,6 +17,10 @@ atomic_t fscache_n_volumes;atomic_t fscache_n_volumes_collision;atomic_t fscache_n_volumes_nomem;atomic_t fscache_n_cookies; +atomic_t fscache_n_cookies_lru; +atomic_t fscache_n_cookies_lru_expired; +atomic_t fscache_n_cookies_lru_removed; +atomic_t fscache_n_cookies_lru_dropped;atomic_t fscache_n_acquires;atomic_t fscache_n_acquires_ok;
@@ -47,6 +51,14 @@ int fscache_stats_show(struct seq_file *m, void *v)atomic_read (&fscache_n_acquires_ok), atomic_read(&fscache_n_acquires_oom)); +seq_printf (m,"LRU : n=%u exp=%u rmv=%u drp=%u at=%ld\n" , +atomic_read (&fscache_n_cookies_lru), +atomic_read (&fscache_n_cookies_lru_expired), +atomic_read (&fscache_n_cookies_lru_removed), +atomic_read (&fscache_n_cookies_lru_dropped), +timer_pending (&fscache_cookie_lru_timer) ? + fscache_cookie_lru_timer.expires - jiffies :0 ); +seq_printf (m,"Updates: n=%u\n" ,atomic_read (&fscache_n_updates));
include/linux/fscache.h +81 -1
115 lines not on this page · raw message
include/trace/events/fscache.h +12 -0
@@ -51,13 +51,18 @@ enum fscache_cookie_trace { fscache_cookie_discard, fscache_cookie_get_end_access, fscache_cookie_get_hash_collision, + fscache_cookie_get_lru, + fscache_cookie_get_use_work, fscache_cookie_new_acquire, fscache_cookie_put_hash_collision, + fscache_cookie_put_lru, fscache_cookie_put_over_queued, fscache_cookie_put_relinquish, fscache_cookie_put_withdrawn, fscache_cookie_put_work, fscache_cookie_see_active, + fscache_cookie_see_lru_discard, + fscache_cookie_see_lru_do_one, fscache_cookie_see_relinquish, fscache_cookie_see_withdraw, fscache_cookie_see_work,
@@ -68,6 +73,7 @@ enum fscache_access_trace { fscache_access_acquire_volume_end, fscache_access_cache_pin, fscache_access_cache_unpin, + fscache_access_lookup_cookie, fscache_access_lookup_cookie_end, fscache_access_lookup_cookie_end_failed, fscache_access_relinquish_volume,
@@ -110,13 +116,18 @@ enum fscache_access_trace { EM(fscache_cookie_discard, "DISCARD ") \ EM(fscache_cookie_get_hash_collision, "GET hcoll") \ EM(fscache_cookie_get_end_access, "GQ endac") \ + EM(fscache_cookie_get_lru, "GET lru ") \ + EM(fscache_cookie_get_use_work, "GQ use ") \ EM(fscache_cookie_new_acquire, "NEW acq ") \ EM(fscache_cookie_put_hash_collision, "PUT hcoll") \ + EM(fscache_cookie_put_lru, "PUT lru ") \ EM(fscache_cookie_put_over_queued, "PQ overq") \ EM(fscache_cookie_put_relinquish, "PUT relnq") \ EM(fscache_cookie_put_withdrawn, "PUT wthdn") \ EM(fscache_cookie_put_work, "PQ work ") \ EM(fscache_cookie_see_active, "- activ") \ + EM(fscache_cookie_see_lru_discard, "- x-lru") \ + EM(fscache_cookie_see_lru_do_one, "- lrudo") \ EM(fscache_cookie_see_relinquish, "- x-rlq") \ EM(fscache_cookie_see_withdraw, "- x-wth") \E_ (fscache_cookie_see_work,"- work " )
@@ -126,6 +137,7 @@ enum fscache_access_trace { EM(fscache_access_acquire_volume_end, "END acq_vol") \ EM(fscache_access_cache_pin, "PIN cache ") \ EM(fscache_access_cache_unpin, "UNPIN cache ") \ + EM(fscache_access_lookup_cookie, "BEGIN lookup ") \ EM(fscache_access_lookup_cookie_end, "END lookup ") \ EM(fscache_access_lookup_cookie_end_failed,"END lookupf") \EM (fscache_access_relinquish_volume,"BEGIN rlq_vol" ) \
David Howells
maintainerPATCH v4 19/68 fscache: Implement cookie invalidation
In reply to David HowellsAdd a function to invalidate the cache behind a cookie:
void fscache_invalidate(struct fscache_cookie *cookie,
const void *aux_data, loff_t size, unsigned int flags)
This causes any cached data for the specified cookie to be discarded. If the cookie is marked as being in use, a new cache object will be created if possible and future I/O will use that instead. In-flight I/O should be abandoned (writes) or reconsidered (reads). Each time it is called cookie->inval_counter is incremented and this can be used to detect invalidation at the end of an I/O operation.
The coherency data attached to the cookie can be updated and the cookie size should be reset. One flag is available, FSCACHE_INVAL_DIO_WRITE, which should be used to indicate invalidation due to a DIO write on a file. This will temporarily disable caching for this cookie.
Changes
=======
ver #2:
- Should only change to inval state if can get access to cache.
---
fs/fscache/cookie.c | 88 ++++++++++++++++++++++++++++++++++++++++ fs/fscache/internal.h | 2 + fs/fscache/stats.c | 5 ++ include/linux/fscache-cache.h | 4 ++ include/linux/fscache.h | 31 ++++++++++++++ include/linux/netfs.h | 1 include/trace/events/fscache.h | 25 +++++++++++ 7 files changed, 155 insertions(+), 1 deletion(-)
fs/fscache/cookie.c +87 -1
@@ -19,6 +19,7 @@ static void fscache_cookie_lru_timed_out(struct timer_list *timer);static void fscache_cookie_lru_worker (struct work_struct *work);static void fscache_cookie_worker (struct work_struct *work);static void fscache_unhash_cookie (struct fscache_cookie *cookie); +static void fscache_perform_invalidation (struct fscache_cookie *cookie);#define fscache_cookie_hash_shift 15static struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_shift];
@@ -28,7 +29,7 @@ static LIST_HEAD(fscache_cookie_lru);static DEFINE_SPINLOCK (fscache_cookie_lru_lock);DEFINE_TIMER (fscache_cookie_lru_timer, fscache_cookie_lru_timed_out); static DECLARE_WORK(fscache_cookie_lru_work, fscache_cookie_lru_worker); -static const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR ] ="-LCAFUWRD" ; +static const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR ] ="-LCAIFUWRD" ;unsigned int fscache_lru_cookie_timeout =10 *HZ ;void fscache_print_cookie (struct fscache_cookie *cookie,char prefix)
@@ -236,6 +237,19 @@ void fscache_cookie_lookup_negative(struct fscache_cookie *cookie) }EXPORT_SYMBOL (fscache_cookie_lookup_negative); +/** +* fscache_resume_after_invalidation - Allow I/O to resume after invalidation +* @cookie: The cookie that was invalidated +* +* Tell fscache that invalidation is sufficiently complete that I/O can be +* allowed again. +*/ +void fscache_resume_after_invalidation (struct fscache_cookie *cookie) +{ +fscache_set_cookie_state (cookie,FSCACHE_COOKIE_STATE_ACTIVE ); +} +EXPORT_SYMBOL (fscache_resume_after_invalidation); +/** * fscache_caching_failed - Report that a failure stopped caching on a cookie * @cookie: The cookie that was affected
@@ -566,6 +580,7 @@ void __fscache_use_cookie(struct fscache_cookie *cookie, bool will_modify)set_bit (FSCACHE_COOKIE_LOCAL_WRITE , &cookie->flags);break ;case FSCACHE_COOKIE_STATE_ACTIVE : +case FSCACHE_COOKIE_STATE_INVALIDATING :if (will_modify && !test_and_set_bit (FSCACHE_COOKIE_LOCAL_WRITE , &cookie->flags)) {set_bit (FSCACHE_COOKIE_DO_PREP_TO_WRITE , &cookie->flags);
@@ -671,6 +686,11 @@ static void fscache_cookie_state_machine(struct fscache_cookie *cookie)fscache_perform_lookup (cookie); goto again; +case FSCACHE_COOKIE_STATE_INVALIDATING : +spin_unlock (&cookie->lock); +fscache_perform_invalidation (cookie); + goto again; +case FSCACHE_COOKIE_STATE_ACTIVE :if (test_and_clear_bit (FSCACHE_COOKIE_DO_PREP_TO_WRITE , &cookie->flags)) {spin_unlock (&cookie->lock);
@@ -962,6 +982,72 @@ struct fscache_cookie *fscache_get_cookie(struct fscache_cookie *cookie, }EXPORT_SYMBOL (fscache_get_cookie); +/* +* Ask the cache to effect invalidation of a cookie. +*/ +static void fscache_perform_invalidation (struct fscache_cookie *cookie) +{ +if (!cookie->volume->cache->ops->invalidate_cookie (cookie)) +fscache_caching_failed (cookie); +fscache_end_cookie_access (cookie, fscache_access_invalidate_cookie_end); +} + +/* +* Invalidate an object. +*/ +void __fscache_invalidate (struct fscache_cookie *cookie, +const void *aux_data,loff_t new_size, +unsigned int flags) +{ +bool is_caching; + +_enter ("c=%x" , cookie->debug_id); + +fscache_stat (&fscache_n_invalidates); + +if (WARN (test_bit (FSCACHE_COOKIE_RELINQUISHED , &cookie->flags), +"Trying to invalidate relinquished cookie\n" )) +return ; + +if ((flags &FSCACHE_INVAL_DIO_WRITE ) && +test_and_set_bit (FSCACHE_COOKIE_DISABLED , &cookie->flags)) +return ; + +spin_lock (&cookie->lock); +set_bit (FSCACHE_COOKIE_NO_DATA_TO_READ , &cookie->flags); +fscache_update_aux (cookie, aux_data, &new_size); + cookie->inval_counter++; +trace_fscache_invalidate (cookie, new_size); + +switch (cookie->state) { +case FSCACHE_COOKIE_STATE_INVALIDATING :/* is_still_valid will catch it */ +default : +spin_unlock (&cookie->lock); +_leave (" [no %u]" , cookie->state); +return ; + +case FSCACHE_COOKIE_STATE_LOOKING_UP : +case FSCACHE_COOKIE_STATE_CREATING : +spin_unlock (&cookie->lock); +_leave (" [look %x]" , cookie->inval_counter); +return ; + +case FSCACHE_COOKIE_STATE_ACTIVE : + is_caching =fscache_begin_cookie_access ( + cookie, fscache_access_invalidate_cookie); +if (is_caching) +__fscache_set_cookie_state (cookie,FSCACHE_COOKIE_STATE_INVALIDATING ); +spin_unlock (&cookie->lock); +wake_up_cookie_state (cookie); + +if (is_caching) +fscache_queue_cookie (cookie, fscache_cookie_get_inval_work); +_leave (" [inv]" ); +return ; + } +} +EXPORT_SYMBOL (__fscache_invalidate); +/* * Generate a list of extant cookies in /proc/fs/fscache/cookies */
fs/fscache/internal.h +2 -0
@@ -105,6 +105,8 @@ extern atomic_t fscache_n_acquires;extern atomic_t fscache_n_acquires_ok;extern atomic_t fscache_n_acquires_oom; +extern atomic_t fscache_n_invalidates; +extern atomic_t fscache_n_relinquishes;extern atomic_t fscache_n_relinquishes_retire;extern atomic_t fscache_n_relinquishes_dropped;
fs/fscache/stats.c +5 -0
@@ -26,6 +26,8 @@ atomic_t fscache_n_acquires;atomic_t fscache_n_acquires_ok;atomic_t fscache_n_acquires_oom; +atomic_t fscache_n_invalidates; +atomic_t fscache_n_updates;EXPORT_SYMBOL (fscache_n_updates);
@@ -59,6 +61,9 @@ int fscache_stats_show(struct seq_file *m, void *v)timer_pending (&fscache_cookie_lru_timer) ? fscache_cookie_lru_timer.expires - jiffies : 0); +seq_printf (m,"Invals : n=%u\n" , +atomic_read (&fscache_n_invalidates)); +seq_printf (m,"Updates: n=%u\n" ,atomic_read (&fscache_n_updates));
include/linux/fscache-cache.h +4 -0
@@ -64,6 +64,9 @@ struct fscache_cache_ops {/* Withdraw an object without any cookie access counts held */ void (*withdraw_cookie)(struct fscache_cookie *cookie); +/* Invalidate an object */ +bool (*invalidate_cookie)(struct fscache_cookie *cookie); +/* Prepare to write to a live cache object */ void (*prepare_to_write)(struct fscache_cookie *cookie); };
@@ -96,6 +99,7 @@ extern void fscache_put_cookie(struct fscache_cookie *cookie,extern void fscache_end_cookie_access (struct fscache_cookie *cookie,enum fscache_access_trace why);extern void fscache_cookie_lookup_negative (struct fscache_cookie *cookie); +extern void fscache_resume_after_invalidation (struct fscache_cookie *cookie);extern void fscache_caching_failed (struct fscache_cookie *cookie);/**
include/linux/fscache.h +31 -0
@@ -39,6 +39,8 @@ struct fscache_cookie;#define FSCACHE_ADV_WRITE_CACHE 0x00/* Do cache if written to locally */ #define FSCACHE_ADV_WRITE_NOCACHE 0x02/* Don't cache if written to locally */ +#define FSCACHE_INVAL_DIO_WRITE 0x01/* Invalidate due to DIO write */ +/* * Data object state. */
@@ -47,6 +49,7 @@ enum fscache_cookie_state {FSCACHE_COOKIE_STATE_LOOKING_UP ,/* The cache object is being looked up */ FSCACHE_COOKIE_STATE_CREATING ,/* The cache object is being created */ FSCACHE_COOKIE_STATE_ACTIVE ,/* The cache is active, readable and writable */ +FSCACHE_COOKIE_STATE_INVALIDATING ,/* The cache is being invalidated */ FSCACHE_COOKIE_STATE_FAILED ,/* The cache failed, withdraw to clear */ FSCACHE_COOKIE_STATE_LRU_DISCARDING ,/* The cookie is being discarded by the LRU */ FSCACHE_COOKIE_STATE_WITHDRAWING, /* The cookie is being withdrawn */
@@ -153,6 +156,7 @@ extern struct fscache_cookie *__fscache_acquire_cookie(extern void __fscache_use_cookie (struct fscache_cookie *,bool );extern void __fscache_unuse_cookie (struct fscache_cookie *,const void *,const loff_t *);extern void __fscache_relinquish_cookie (struct fscache_cookie *,bool ); +extern void __fscache_invalidate (struct fscache_cookie *,const void *,loff_t ,unsigned int );/** * fscache_acquire_volume - Register a volume as desiring caching services
@@ -327,4 +331,31 @@ void __fscache_update_cookie(struct fscache_cookie *cookie, const void *aux_data set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &cookie->flags); } +/** +* fscache_invalidate - Notify cache that an object needs invalidation +* @cookie: The cookie representing the cache object +* @aux_data: The updated auxiliary data for the cookie (may be NULL) +* @size: The revised size of the object. +* @flags: Invalidation flags (FSCACHE_INVAL_*) +* +* Notify the cache that an object is needs to be invalidated and that it +* should abort any retrievals or stores it is doing on the cache. This +* increments inval_counter on the cookie which can be used by the caller to +* reconsider I/O requests as they complete. +* +* If @flags has FSCACHE_INVAL_DIO_WRITE set, this indicates that this is due +* to a direct I/O write and will cause caching to be disabled on this cookie +* until it is completely unused. +* +* See Documentation/filesystems/caching/netfs-api.rst for a complete +* description. +*/ +static inline +void fscache_invalidate (struct fscache_cookie *cookie, +const void *aux_data,loff_t size,unsigned int flags) +{ +if (fscache_cookie_enabled (cookie)) +__fscache_invalidate (cookie, aux_data, size, flags); +} +#endif /* _LINUX_FSCACHE_H */
include/linux/netfs.h +1 -0
@@ -124,6 +124,7 @@ struct netfs_cache_resources {void *cache_priv;void *cache_priv2;unsigned int debug_id;/* Cookie debug ID */ +unsigned int inval_counter;/* object->inval_counter at begin_op */ };/*
include/trace/events/fscache.h +25 -0
@@ -51,6 +51,7 @@ enum fscache_cookie_trace { fscache_cookie_discard, fscache_cookie_get_end_access, fscache_cookie_get_hash_collision, + fscache_cookie_get_inval_work, fscache_cookie_get_lru, fscache_cookie_get_use_work, fscache_cookie_new_acquire,
@@ -73,6 +74,8 @@ enum fscache_access_trace { fscache_access_acquire_volume_end, fscache_access_cache_pin, fscache_access_cache_unpin, + fscache_access_invalidate_cookie, + fscache_access_invalidate_cookie_end, fscache_access_lookup_cookie, fscache_access_lookup_cookie_end, fscache_access_lookup_cookie_end_failed,
@@ -116,6 +119,7 @@ enum fscache_access_trace { EM(fscache_cookie_discard, "DISCARD ") \ EM(fscache_cookie_get_hash_collision, "GET hcoll") \ EM(fscache_cookie_get_end_access, "GQ endac") \ + EM(fscache_cookie_get_inval_work, "GQ inval") \ EM(fscache_cookie_get_lru, "GET lru ") \ EM(fscache_cookie_get_use_work, "GQ use ") \EM (fscache_cookie_new_acquire,"NEW acq " ) \
@@ -137,6 +141,8 @@ enum fscache_access_trace { EM(fscache_access_acquire_volume_end, "END acq_vol") \ EM(fscache_access_cache_pin, "PIN cache ") \ EM(fscache_access_cache_unpin, "UNPIN cache ") \ + EM(fscache_access_invalidate_cookie, "BEGIN inval ") \ + EM(fscache_access_invalidate_cookie_end,"END inval ") \ EM(fscache_access_lookup_cookie, "BEGIN lookup ") \ EM(fscache_access_lookup_cookie_end, "END lookup ") \EM (fscache_access_lookup_cookie_end_failed,"END lookupf" ) \
@@ -385,6 +391,25 @@ TRACE_EVENT(fscache_relinquish, __entry->n_active, __entry->flags, __entry->retire) ); +TRACE_EVENT (fscache_invalidate, + TP_PROTO(struct fscache_cookie *cookie, loff_t new_size), + +TP_ARGS (cookie, new_size), + +TP_STRUCT__entry ( + __field(unsigned int, cookie ) +__field (loff_t, new_size ) + ), + +TP_fast_assign ( + __entry->cookie = cookie->debug_id; + __entry->new_size = new_size; + ), + +TP_printk ("c=%08x sz=%llx" , + __entry->cookie, __entry->new_size) + ); +#endif /* _TRACE_FSCACHE_H */ /* This part must be outside protection */
David Howells
maintainerPATCH v4 20/68 fscache: Provide a means to begin an operation
In reply to David HowellsProvide a function to begin a read operation:
int fscache_begin_read_operation(
struct netfs_cache_resources *cres, struct fscache_cookie *cookie)
This is primarily intended to be called by network filesystems on behalf of netfslib, but may also be called to use the I/O access functions directly. It attaches the resources required by the cache to cres struct from the supplied cookie.
This holds access to the cache behind the cookie for the duration of the operation and forces cache withdrawal and cookie invalidation to perform synchronisation on the operation. cres->inval_counter is set from the cookie at this point so that it can be compared at the end of the operation.
Note that this does not guarantee that the cache state is fully set up and able to perform I/O immediately; looking up and creation may be left in progress in the background. The operations intended to be called by the network filesystem, such as reading and writing, are expected to wait for the cookie to move to the correct state.
This will, however, potentially sleep, waiting for a certain minimum state to be set or for operations such as invalidate to advance far enough that I/O can resume.
Also provide a function for the cache to call to wait for the cache object to get to a state where it can be used for certain things:
bool fscache_wait_for_operation(struct netfs_cache_resources *cres, enum fscache_want_stage stage);
This looks at the cache resources provided by the begin function and waits for them to get to an appropriate stage. There's a choice of wanting just some parameters (FSCACHE_WANT_PARAM) or the ability to do I/O (FSCACHE_WANT_READ or FSCACHE_WANT_WRITE).
---
fs/fscache/Makefile | 1 fs/fscache/internal.h | 11 +++ fs/fscache/io.c | 151 ++++++++++++++++++++++++++++++++++++++++ include/linux/fscache-cache.h | 11 +++ include/linux/fscache.h | 49 +++++++++++++ include/trace/events/fscache.h | 6 ++ 6 files changed, 229 insertions(+) create mode 100644 fs/fscache/io.c
fs/fscache/Makefile +1 -0
@@ -6,6 +6,7 @@ fscache-y := \ cache.o \ cookie.o \ + io.o \ main.o \ volume.o
fs/fscache/internal.h +11 -0
@@ -70,6 +70,17 @@ static inline void fscache_see_cookie(struct fscache_cookie *cookie, where); } +/* +* io.c +*/ +static inline void fscache_end_operation (struct netfs_cache_resources *cres) +{ +const struct netfs_cache_ops *ops =fscache_operation_valid (cres); + +if (ops) + ops->end_operation (cres); +} +/* * main.c */
fs/fscache/io.c new file +151 -0
151 lines not on this page · raw message
include/linux/fscache-cache.h +11 -0
@@ -67,6 +67,10 @@ struct fscache_cache_ops {/* Invalidate an object */ bool (*invalidate_cookie)(struct fscache_cookie *cookie); +/* Begin an operation for the netfs lib */ +bool (*begin_operation)(struct netfs_cache_resources *cres, +enum fscache_want_state want_state); +/* Prepare to write to a live cache object */ void (*prepare_to_write)(struct fscache_cookie *cookie); };
@@ -101,6 +105,8 @@ extern void fscache_end_cookie_access(struct fscache_cookie *cookie,extern void fscache_cookie_lookup_negative (struct fscache_cookie *cookie);extern void fscache_resume_after_invalidation (struct fscache_cookie *cookie);extern void fscache_caching_failed (struct fscache_cookie *cookie); +extern bool fscache_wait_for_operation (struct netfs_cache_resources *cred, +enum fscache_want_state state);/** * fscache_cookie_state - Read the state of a cookie
@@ -129,4 +135,9 @@ static inline void *fscache_get_key(struct fscache_cookie *cookie) return cookie->key; } +static inline struct fscache_cookie *fscache_cres_cookie (struct netfs_cache_resources *cres) +{ +return cres->cache_priv; +} +#endif /* _LINUX_FSCACHE_CACHE_H */
include/linux/fscache.h +49 -0
65 lines not on this page · raw message
include/trace/events/fscache.h +6 -0
@@ -76,6 +76,9 @@ enum fscache_access_trace { fscache_access_cache_unpin, fscache_access_invalidate_cookie, fscache_access_invalidate_cookie_end, + fscache_access_io_not_live, + fscache_access_io_read, + fscache_access_io_wait, fscache_access_lookup_cookie, fscache_access_lookup_cookie_end, fscache_access_lookup_cookie_end_failed,
@@ -143,6 +146,9 @@ enum fscache_access_trace { EM(fscache_access_cache_unpin, "UNPIN cache ") \ EM(fscache_access_invalidate_cookie, "BEGIN inval ") \ EM(fscache_access_invalidate_cookie_end,"END inval ") \ + EM(fscache_access_io_not_live, "END io_notl") \ + EM(fscache_access_io_read, "BEGIN io_read") \ + EM(fscache_access_io_wait, "WAIT io ") \ EM(fscache_access_lookup_cookie, "BEGIN lookup ") \ EM(fscache_access_lookup_cookie_end, "END lookup ") \EM (fscache_access_lookup_cookie_end_failed,"END lookupf" ) \
David Howells
maintainerPATCH v4 21/68 fscache: Count data storage objects in a cache
In reply to David HowellsCount the data storage objects that are currently allocated in a cache. This is used to pin certain cache structures until cache withdrawal is complete.
Three helpers are provided to manage and make use of the count:
(1) void fscache_count_object(struct fscache_cache *cache);
This should be called by the cache backend to note that an object has been allocated and attached to the cache.
(2) void fscache_uncount_object(struct fscache_cache *cache);
This should be called by the backend to note that an object has been destroyed. This sends a wakeup event that allows cache withdrawal to proceed if it was waiting for that object.
(3) void fscache_wait_for_objects(struct fscache_cache *cache);
This can be used by the backend to wait for all outstanding cache object to be destroyed.
Each cache's counter is displayed as part of /proc/fs/fscache/caches.
---
fs/fscache/cache.c | 2 ++ include/linux/fscache-cache.h | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+)
fs/fscache/cache.c +2 -0
@@ -13,6 +13,8 @@static LIST_HEAD (fscache_caches);DECLARE_RWSEM (fscache_addremove_sem);EXPORT_SYMBOL (fscache_addremove_sem); +DECLARE_WAIT_QUEUE_HEAD (fscache_clearance_waiters); +EXPORT_SYMBOL (fscache_clearance_waiters);static atomic_t fscache_cache_debug_id;
include/linux/fscache-cache.h +39 -0
@@ -76,6 +76,7 @@ struct fscache_cache_ops { };extern struct workqueue_struct *fscache_wq; +extern wait_queue_head_t fscache_clearance_waiters;/* * out-of-line cache backend functions
@@ -140,4 +141,42 @@ static inline struct fscache_cookie *fscache_cres_cookie(struct netfs_cache_reso return cres->cache_priv; } +/** +* fscache_count_object - Tell fscache that an object has been added +* @cache: The cache to account to +* +* Tell fscache that an object has been added to the cache. This prevents the +* cache from tearing down the cache structure until the object is uncounted. +*/ +static inline void fscache_count_object (struct fscache_cache *cache) +{ +atomic_inc (&cache->object_count); +} + +/** +* fscache_uncount_object - Tell fscache that an object has been removed +* @cache: The cache to account to +* +* Tell fscache that an object has been removed from the cache and will no +* longer be accessed. After this point, the cache cookie may be destroyed. +*/ +static inline void fscache_uncount_object (struct fscache_cache *cache) +{ +if (atomic_dec_and_test (&cache->object_count)) +wake_up_all (&fscache_clearance_waiters); +} + +/** +* fscache_wait_for_objects - Wait for all objects to be withdrawn +* @cache: The cache to query +* +* Wait for all extant objects in a cache to finish being withdrawn +* and go away. +*/ +static inline void fscache_wait_for_objects (struct fscache_cache *cache) +{ +wait_event (fscache_clearance_waiters, +atomic_read (&cache->object_count) ==0 ); +} +#endif /* _LINUX_FSCACHE_CACHE_H */
David Howells
maintainerPATCH v4 22/68 fscache: Provide read/write stat counters for the cache
In reply to David HowellsProvide read/write stat counters for the cache backend to use.
---
fs/fscache/stats.c | 9 +++++++++ include/linux/fscache-cache.h | 10 ++++++++++ 2 files changed, 19 insertions(+)
fs/fscache/stats.c +9 -0
@@ -35,6 +35,11 @@ atomic_t fscache_n_relinquishes;atomic_t fscache_n_relinquishes_retire;atomic_t fscache_n_relinquishes_dropped; +atomic_t fscache_n_read; +EXPORT_SYMBOL (fscache_n_read); +atomic_t fscache_n_write; +EXPORT_SYMBOL (fscache_n_write); +/* * display the general statistics */
@@ -72,6 +77,10 @@ int fscache_stats_show(struct seq_file *m, void *v)atomic_read (&fscache_n_relinquishes_retire), atomic_read(&fscache_n_relinquishes_dropped)); +seq_printf (m,"IO : rd=%u wr=%u\n" , +atomic_read (&fscache_n_read), +atomic_read (&fscache_n_write)); +netfs_stats_show (m);return 0 ; }
include/linux/fscache-cache.h +10 -0
@@ -179,4 +179,14 @@ static inline void fscache_wait_for_objects(struct fscache_cache *cache) atomic_read(&cache->object_count) == 0); } +#ifdef CONFIG_FSCACHE_STATS +extern atomic_t fscache_n_read; +extern atomic_t fscache_n_write; +#define fscache_count_read () atomic_inc(&fscache_n_read) +#define fscache_count_write () atomic_inc(&fscache_n_write) +#else +#define fscache_count_read () do {} while(0) +#define fscache_count_write () do {} while(0) +#endif +#endif /* _LINUX_FSCACHE_CACHE_H */
David Howells
maintainerPATCH v4 23/68 fscache: Provide a function to let the netfs update its coherency data
In reply to David HowellsProvide a function to let the netfs update its coherency data:
void fscache_update_cookie(struct fscache_cookie *cookie,
const void *aux_data, const loff_t *object_size);
This will update the auxiliary data and/or the size of the object attached to a cookie if either pointer is not-NULL and flag that the disk needs to be updated.
Note that fscache_unuse_cookie() also allows this to be done.
---
include/linux/fscache.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
include/linux/fscache.h +22 -0
@@ -338,6 +338,28 @@ void __fscache_update_cookie(struct fscache_cookie *cookie, const void *aux_dataset_bit (FSCACHE_COOKIE_NEEDS_UPDATE , &cookie->flags); } +/** +* fscache_update_cookie - Request that a cache object be updated +* @cookie: The cookie representing the cache object +* @aux_data: The updated auxiliary data for the cookie (may be NULL) +* @object_size: The current size of the object (may be NULL) +* +* Request an update of the index data for the cache object associated with the +* cookie. The auxiliary data on the cookie will be updated first if @aux_data +* is set and the object size will be updated and the object possibly trimmed +* if @object_size is set. +* +* See Documentation/filesystems/caching/netfs-api.rst for a complete +* description. +*/ +static inline +void fscache_update_cookie (struct fscache_cookie *cookie,const void *aux_data, +const loff_t *object_size) +{ +if (fscache_cookie_enabled (cookie)) +__fscache_update_cookie (cookie, aux_data, object_size); +} +/** * fscache_invalidate - Notify cache that an object needs invalidation * @cookie: The cookie representing the cache object
David Howells
maintainerPATCH v4 24/68 netfs: Pass more information on how to deal with a hole in the cache
In reply to David HowellsPass more information to the cache on how to deal with a hole if it encounters one when trying to read from the cache. Three options are provided:
(1) NETFS_READ_HOLE_IGNORE. Read the hole along with the data, assuming it to be a punched-out extent by the backing filesystem.
(2) NETFS_READ_HOLE_CLEAR. If there's a hole, erase the requested region of the cache and clear the read buffer.
(3) NETFS_READ_HOLE_FAIL. Fail the read if a hole is detected.
---
fs/netfs/read_helper.c | 8 ++++---- include/linux/netfs.h | 11 ++++++++++- 2 files changed, 14 insertions(+), 5 deletions(-)
fs/netfs/read_helper.c +4 -4
@@ -170,7 +170,7 @@ static void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error*/ static void netfs_read_from_cache (struct netfs_read_request *rreq,struct netfs_read_subrequest *subreq, -bool seek_data) +enum netfs_read_from_hole read_hole) {struct netfs_cache_resources *cres = &rreq->cache_resources;struct iov_iter iter;
@@ -180,7 +180,7 @@ static void netfs_read_from_cache(struct netfs_read_request *rreq, subreq->start + subreq->transferred, subreq->len - subreq->transferred); - cres->ops->read (cres, subreq->start, &iter, seek_data, + cres->ops->read (cres, subreq->start, &iter, read_hole, netfs_cache_read_terminated, subreq); }
@@ -461,7 +461,7 @@ static void netfs_rreq_short_read(struct netfs_read_request *rreq,netfs_get_read_subrequest (subreq);atomic_inc (&rreq->nr_rd_ops);if (subreq->source ==NETFS_READ_FROM_CACHE ) -netfs_read_from_cache (rreq, subreq, true); +netfs_read_from_cache (rreq, subreq,NETFS_READ_HOLE_CLEAR );else netfs_read_from_server (rreq, subreq); }
@@ -789,7 +789,7 @@ static bool netfs_rreq_submit_slice(struct netfs_read_request *rreq,netfs_read_from_server (rreq, subreq);break ;case NETFS_READ_FROM_CACHE : -netfs_read_from_cache (rreq, subreq, false); +netfs_read_from_cache (rreq, subreq,NETFS_READ_HOLE_IGNORE );break ;default :BUG ();
include/linux/netfs.h +10 -1
@@ -196,6 +196,15 @@ struct netfs_read_request_ops {void (*cleanup)(struct address_space *mapping,void *netfs_priv); }; +/* +* How to handle reading from a hole. +*/ +enum netfs_read_from_hole { +NETFS_READ_HOLE_IGNORE , +NETFS_READ_HOLE_CLEAR , +NETFS_READ_HOLE_FAIL , +}; +/* * Table of operations for access to a cache. This is obtained by * rreq->ops->begin_cache_operation().
@@ -208,7 +217,7 @@ struct netfs_cache_ops {int (*read)(struct netfs_cache_resources *cres,loff_t start_pos,struct iov_iter *iter, -bool seek_data, +enum netfs_read_from_hole read_hole,netfs_io_terminated_t term_func,void *term_func_priv);
David Howells
maintainerPATCH v4 25/68 fscache: Implement raw I/O interface
In reply to David HowellsProvide a pair of functions to perform raw I/O on the cache. The first function allows an arbitrary asynchronous direct-IO read to be made against a cache object, though the read should be aligned and sized appropriately for the backing device:
int fscache_read(struct netfs_cache_resources *cres,
loff_t start_pos,
struct iov_iter *iter, enum netfs_read_from_hole read_hole, netfs_io_terminated_t term_func, void *term_func_priv);
The cache resources must have been previously initialised by fscache_begin_read_operation(). A read operation is sent to the backing filesystem, starting at start_pos within the file. The size of the read is specified by the iterator, as is the location of the output buffer.
If there is a hole in the data it can be ignored and left to the backing filesystem to deal with (NETFS_READ_HOLE_IGNORE), a hole at the beginning can be skipped over and the buffer padded with zeros (NETFS_READ_HOLE_CLEAR) or -ENODATA can be given (NETFS_READ_HOLE_FAIL).
If term_func is not NULL, the operation may be performed asynchronously. Upon completion, successful or otherwise, (*term_func)() will be called and passed term_func_priv, along with an error or the amount of data transferred. If the op is run asynchronously, fscache_read() will return -EIOCBQUEUED.
The second function allows an arbitrary asynchronous direct-IO write to be made against a cache object, though the write should be aligned and sized appropriately for the backing device:
int fscache_write(struct netfs_cache_resources *cres,
loff_t start_pos,
struct iov_iter *iter, netfs_io_terminated_t term_func, void *term_func_priv);
This works in very similar way to fscache_read(), except that there's no need to deal with holes (they're just overwritten).
The caller is responsible for preventing concurrent overlapping writes.
---
include/linux/fscache.h | 74 ++++++++++++++++++++++++++++++++++++++++ include/trace/events/fscache.h | 2 + 2 files changed, 76 insertions(+)
include/linux/fscache.h +74 -0
@@ -429,4 +429,78 @@ int fscache_begin_read_operation(struct netfs_cache_resources *cres, return -ENOBUFS; } +/** +* fscache_read - Start a read from the cache. +* @cres: The cache resources to use +* @start_pos: The beginning file offset in the cache file +* @iter: The buffer to fill - and also the length +* @read_hole: How to handle a hole in the data. +* @term_func: The function to call upon completion +* @term_func_priv: The private data for @term_func +* +* Start a read from the cache. @cres indicates the cache object to read from +* and must be obtained by a call to fscache_begin_operation() beforehand. +* +* The data is read into the iterator, @iter, and that also indicates the size +* of the operation. @start_pos is the start position in the file, though if +* @seek_data is set appropriately, the cache can use SEEK_DATA to find the +* next piece of data, writing zeros for the hole into the iterator. +* +* Upon termination of the operation, @term_func will be called and supplied +* with @term_func_priv plus the amount of data written, if successful, or the +* error code otherwise. +* +* @read_hole indicates how a partially populated region in the cache should be +* handled. It can be one of a number of settings: +* +* NETFS_READ_HOLE_IGNORE - Just try to read (may return a short read). +* +* NETFS_READ_HOLE_CLEAR - Seek for data, clearing the part of the buffer +* skipped over, then do as for IGNORE. +* +* NETFS_READ_HOLE_FAIL - Give ENODATA if we encounter a hole. +*/ +static inline +int fscache_read (struct netfs_cache_resources *cres, +loff_t start_pos, +struct iov_iter *iter, +enum netfs_read_from_hole read_hole, +netfs_io_terminated_t term_func, +void *term_func_priv) +{ +const struct netfs_cache_ops *ops =fscache_operation_valid (cres); +return ops->read (cres, start_pos, iter, read_hole, + term_func, term_func_priv); +} + +/** +* fscache_write - Start a write to the cache. +* @cres: The cache resources to use +* @start_pos: The beginning file offset in the cache file +* @iter: The data to write - and also the length +* @term_func: The function to call upon completion +* @term_func_priv: The private data for @term_func +* +* Start a write to the cache. @cres indicates the cache object to write to and +* must be obtained by a call to fscache_begin_operation() beforehand. +* +* The data to be written is obtained from the iterator, @iter, and that also +* indicates the size of the operation. @start_pos is the start position in +* the file. +* +* Upon termination of the operation, @term_func will be called and supplied +* with @term_func_priv plus the amount of data written, if successful, or the +* error code otherwise. +*/ +static inline +int fscache_write (struct netfs_cache_resources *cres, +loff_t start_pos, +struct iov_iter *iter, +netfs_io_terminated_t term_func, +void *term_func_priv) +{ +const struct netfs_cache_ops *ops =fscache_operation_valid (cres); +return ops->write (cres, start_pos, iter, term_func, term_func_priv); +} +#endif /* _LINUX_FSCACHE_H */
include/trace/events/fscache.h +2 -0
@@ -79,6 +79,7 @@ enum fscache_access_trace { fscache_access_io_not_live, fscache_access_io_read, fscache_access_io_wait, + fscache_access_io_write, fscache_access_lookup_cookie, fscache_access_lookup_cookie_end, fscache_access_lookup_cookie_end_failed,
@@ -149,6 +150,7 @@ enum fscache_access_trace { EM(fscache_access_io_not_live, "END io_notl") \ EM(fscache_access_io_read, "BEGIN io_read") \ EM(fscache_access_io_wait, "WAIT io ") \ + EM(fscache_access_io_write, "BEGIN io_writ") \ EM(fscache_access_lookup_cookie, "BEGIN lookup ") \ EM(fscache_access_lookup_cookie_end, "END lookup ") \EM (fscache_access_lookup_cookie_end_failed,"END lookupf" ) \
David Howells
maintainerPATCH v4 26/68 fscache: Implement higher-level write I/O interface
In reply to David HowellsProvide a higher-level function than fscache_write() to perform a write from an inode's pagecache to the cache, whilst fending off concurrent writes by means of the PG_fscache mark on a page:
void fscache_write_to_cache(struct fscache_cookie *cookie,
struct address_space *mapping, loff_t start,
size_t len,
loff_t i_size, netfs_io_terminated_t term_func, void *term_func_priv, bool caching);
If caching is false, this function does nothing except call (*term_func)() if given. It assumes that, in such a case, PG_fscache will not have been set on the pages.
Otherwise, if caching is true, this function requires the source pages to have had PG_fscache set on them before calling. start and len define the region of the file to be modified and i_size indicates the new file size. The source pages are extracted from the mapping.
term_func and term_func_priv work as for fscache_write(). The PG_fscache marks will be cleared at the end of the operation, before term_func is called or the function otherwise returns.
There is an additonal helper function to clear the PG_fscache bits from a range of pages:
void fscache_clear_page_bits(struct fscache_cookie *cookie,
struct address_space *mapping, loff_t start, size_t len, bool caching);
If caching is true, the pages to be managed are expected to be located on mapping in the range defined by start and len. If caching is false, it does nothing.
---
fs/fscache/io.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fscache.h | 63 ++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+)
fs/fscache/io.c +104 -0
@@ -149,3 +149,107 @@ int __fscache_begin_read_operation(struct netfs_cache_resources *cres, fscache_access_io_read); }EXPORT_SYMBOL (__fscache_begin_read_operation); + +struct fscache_write_request { +struct netfs_cache_resources cache_resources; +struct address_space *mapping; +loff_t start; +size_t len; +bool set_bits; +netfs_io_terminated_t term_func; +void *term_func_priv; +}; + +void __fscache_clear_page_bits (struct address_space *mapping, +loff_t start,size_t len) +{ +pgoff_t first = start /PAGE_SIZE ; +pgoff_t last = (start + len -1 ) /PAGE_SIZE ; +struct page *page; + +if (len) { +XA_STATE (xas, &mapping->i_pages, first); + +rcu_read_lock (); +xas_for_each (&xas, page, last) { +end_page_fscache (page); + } +rcu_read_unlock (); + } +} +EXPORT_SYMBOL (__fscache_clear_page_bits); + +/* +* Deal with the completion of writing the data to the cache. +*/ +static void fscache_wreq_done (void *priv,ssize_t transferred_or_error, +bool was_async) +{ +struct fscache_write_request *wreq = priv; + +fscache_clear_page_bits (fscache_cres_cookie (&wreq->cache_resources), + wreq->mapping, wreq->start, wreq->len, + wreq->set_bits); + +if (wreq->term_func) + wreq->term_func (wreq->term_func_priv, transferred_or_error, + was_async); +fscache_end_operation (&wreq->cache_resources); +kfree (wreq); +} + +void __fscache_write_to_cache (struct fscache_cookie *cookie, +struct address_space *mapping, +loff_t start,size_t len,loff_t i_size, +netfs_io_terminated_t term_func, +void *term_func_priv, +bool cond) +{ +struct fscache_write_request *wreq; +struct netfs_cache_resources *cres; +struct iov_iter iter; +int ret = -ENOBUFS ; + +if (len ==0 ) + goto abandon; + +_enter ("%llx,%zx" , start, len); + + wreq =kzalloc (sizeof (struct fscache_write_request ),GFP_NOFS ); +if (!wreq) + goto abandon; + wreq->mapping = mapping; + wreq->start = start; + wreq->len = len; + wreq->set_bits = cond; + wreq->term_func = term_func; + wreq->term_func_priv = term_func_priv; + + cres = &wreq->cache_resources; +if (fscache_begin_operation (cres, cookie,FSCACHE_WANT_WRITE , + fscache_access_io_write) <0 ) + goto abandon_free; + + ret = cres->ops->prepare_write (cres, &start, &len, i_size, false); +if (ret <0 ) + goto abandon_end; + +/* TODO: Consider clearing page bits now for space the write isn't +* covering. This is more complicated than it appears when THPs are +* taken into account. +*/ + +iov_iter_xarray (&iter,WRITE , &mapping->i_pages, start, len); +fscache_write (cres, start, &iter, fscache_wreq_done, wreq); +return ; + +abandon_end: +return fscache_wreq_done (wreq, ret, false); +abandon_free: +kfree (wreq); +abandon: +fscache_clear_page_bits (cookie, mapping, start, len, cond); +if (term_func) +term_func (term_func_priv, ret, false); +} +EXPORT_SYMBOL (__fscache_write_to_cache);
include/linux/fscache.h +63 -0
73 lines not on this page · raw message
David Howells
maintainerPATCH v4 27/68 vfs, fscache: Implement pinning of cache usage for writeback
In reply to David HowellsCachefiles has a problem in that it needs to keep the backing file for a cookie open whilst there are local modifications pending that need to be written to it. However, we don't want to keep the file open indefinitely, as that causes EMFILE/ENFILE/ENOMEM problems.
Reopening the cache file, however, is a problem if this is being done due to writeback triggered by exit(). Some filesystems will oops if we try to open a file in that context because they want to access current->fs or other resources that have already been dismantled.
To get around this, I added the following:
(1) An inode flag, I_PINNING_FSCACHE_WB, to be set on a network filesystem inode to indicate that we have a usage count on the cookie caching that inode.
(2) A flag in struct writeback_control, unpinned_fscache_wb, that is set when __writeback_single_inode() clears the last dirty page from i_pages - at which point it clears I_PINNING_FSCACHE_WB and sets this flag.
This has to be done here so that clearing I_PINNING_FSCACHE_WB can be done atomically with the check of PAGECACHE_TAG_DIRTY that clears I_DIRTY_PAGES.
(3) A function, fscache_set_page_dirty(), which if it is not set, sets I_PINNING_FSCACHE_WB and calls fscache_use_cookie() to pin the cache resources.
(4) A function, fscache_unpin_writeback(), to be called by ->write_inode() to unuse the cookie.
(5) A function, fscache_clear_inode_writeback(), to be called when the inode is evicted, before clear_inode() is called. This cleans up any lingering I_PINNING_FSCACHE_WB.
The network filesystem can then use these tools to make sure that fscache_write_to_cache() can write locally modified data to the cache as well as to the server.
For the future, I'm working on write helpers for netfs lib that should allow this facility to be removed by keeping track of the dirty regions separately - but that's incomplete at the moment and is also going to be affected by folios, one way or another, since it deals with pages
---
fs/fs-writeback.c | 8 ++++++++ fs/fscache/io.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 3 +++ include/linux/fscache.h | 41 +++++++++++++++++++++++++++++++++++++++++ include/linux/writeback.h | 1 + 5 files changed, 91 insertions(+)
fs/fs-writeback.c +8 -0
@@ -1666,6 +1666,13 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)if (mapping_tagged (mapping,PAGECACHE_TAG_DIRTY )) inode->i_state |=I_DIRTY_PAGES ; +else if (unlikely (inode->i_state &I_PINNING_FSCACHE_WB )) { +if (!(inode->i_state &I_DIRTY_PAGES )) { + inode->i_state &= ~I_PINNING_FSCACHE_WB ; + wbc->unpinned_fscache_wb = true; + dirty |=I_PINNING_FSCACHE_WB ;/* Cause write_inode */ + } + }spin_unlock (&inode->i_lock);
@@ -1675,6 +1682,7 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)if (ret ==0 ) ret = err; } + wbc->unpinned_fscache_wb = false;trace_writeback_single_inode (inode, wbc, nr_to_write);return ret; }
fs/fscache/io.c +38 -0
@@ -150,6 +150,44 @@ int __fscache_begin_read_operation(struct netfs_cache_resources *cres, }EXPORT_SYMBOL (__fscache_begin_read_operation); +/** +* fscache_set_page_dirty - Mark page dirty and pin a cache object for writeback +* @page: The page being dirtied +* @cookie: The cookie referring to the cache object +* +* Set the dirty flag on a page and pin an in-use cache object in memory when +* dirtying a page so that writeback can later write to it. This is intended +* to be called from the filesystem's ->set_page_dirty() method. +* +* Returns 1 if PG_dirty was set on the page, 0 otherwise. +*/ +int fscache_set_page_dirty (struct page *page,struct fscache_cookie *cookie) +{ +struct inode *inode = page->mapping->host; +bool need_use = false; + +_enter ("" ); + +if (!__set_page_dirty_nobuffers (page)) +return 0 ; +if (!fscache_cookie_valid (cookie)) +return 1 ; + +if (!(inode->i_state &I_PINNING_FSCACHE_WB )) { +spin_lock (&inode->i_lock); +if (!(inode->i_state &I_PINNING_FSCACHE_WB )) { + inode->i_state |=I_PINNING_FSCACHE_WB ; + need_use = true; + } +spin_unlock (&inode->i_lock); + +if (need_use) +fscache_use_cookie (cookie, true); + } +return 1 ; +} +EXPORT_SYMBOL (fscache_set_page_dirty); +struct fscache_write_request {struct netfs_cache_resources cache_resources;struct address_space *mapping;
include/linux/fs.h +3 -0
@@ -2418,6 +2418,8 @@ static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,* Used to detect that mark_inode_dirty() should not move * inode between dirty lists. * +* I_PINNING_FSCACHE_WB Inode is pinning an fscache object for writeback. +* * Q: What is the difference between I_WILL_FREE and I_FREEING? */ #define I_DIRTY_SYNC (1 << 0)
@@ -2440,6 +2442,7 @@ static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,#define I_CREATING (1 << 15)#define I_DONTCACHE (1 << 16)#define I_SYNC_QUEUED (1 << 17) +#define I_PINNING_FSCACHE_WB (1 << 18)#define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC)#define I_DIRTY (I_DIRTY_INODE | I_DIRTY_PAGES)
include/linux/fscache.h +41 -0
51 lines not on this page · raw message
include/linux/writeback.h +1 -0
@@ -68,6 +68,7 @@ struct writeback_control { unsigned for_reclaim:1; /* Invoked from the page allocator */ unsigned range_cyclic:1; /* range_start is cyclic */ unsigned for_sync:1; /* sync(2) WB_SYNC_ALL writeback */ + unsigned unpinned_fscache_wb:1; /* Cleared I_PINNING_FSCACHE_WB *//* * When writeback IOs are bounced through async layers, only the
David Howells
maintainerPATCH v4 28/68 fscache: Provide a function to note the release of a page
In reply to David HowellsProvide a function to be called from a network filesystem's releasepage method to indicate that a page has been released that might have been a reflection of data upon the server - and now that data must be reloaded from the server or the cache.
This is used to end an optimisation for empty files, in particular files that have just been created locally, whereby we know there cannot yet be any data that we would need to read from the server or the cache.
---
include/linux/fscache.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
include/linux/fscache.h +16 -0
@@ -607,4 +607,20 @@ static inline void fscache_clear_inode_writeback(struct fscache_cookie *cookie, } } +/** +* fscache_note_page_release - Note that a netfs page got released +* @cookie: The cookie corresponding to the file +* +* Note that a page that has been copied to the cache has been released. This +* means that future reads will need to look in the cache to see if it's there. +*/ +static inline +void fscache_note_page_release (struct fscache_cookie *cookie) +{ +if (cookie && +test_bit (FSCACHE_COOKIE_HAVE_DATA , &cookie->flags) && +test_bit (FSCACHE_COOKIE_NO_DATA_TO_READ , &cookie->flags)) +clear_bit (FSCACHE_COOKIE_NO_DATA_TO_READ , &cookie->flags); +} +#endif /* _LINUX_FSCACHE_H */
Jeff Layton
maintainerRe: [PATCH v4 28/68] fscache: Provide a function to note the release of a page
Quoted 36 lines · David Howells
Provide a function to be called from a network filesystem's releasepage method to indicate that a page has been released that might have been a reflection of data upon the server - and now that data must be reloaded from the server or the cache.
This is used to end an optimisation for empty files, in particular files that have just been created locally, whereby we know there cannot yet be any data that we would need to read from the server or the cache.
Signed-off-by David Howells <[email protected]>Link https://lore.kernel.org/r/163819617128.215744.4725572296135656508.stgit@warthog.procyon.org.uk/ # v1 open here---
include/linux/fscache.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)include/linux/fscache.h +7
-0@@ -607,4 +607,20 @@ static inline void fscache_clear_inode_writeback(struct fscache_cookie *cookie, } } +/** +* fscache_note_page_release - Note that a netfs page got released +* @cookie: The cookie corresponding to the file +* +* Note that a page that has been copied to the cache has been released. This +* means that future reads will need to look in the cache to see if it's there. +*/
include/linux/fscache.h static inline void fscache_clear_inode_writeback(struct fscache_cookie *cookie, · line 626
Quoted 10 lines
include/linux/fscache.h +9
-0+static inline +void fscache_note_page_release (struct fscache_cookie *cookie) +{ +if (cookie && +test_bit (FSCACHE_COOKIE_HAVE_DATA , &cookie->flags) && +test_bit (FSCACHE_COOKIE_NO_DATA_TO_READ , &cookie->flags)) +clear_bit (FSCACHE_COOKIE_NO_DATA_TO_READ , &cookie->flags); +} +#endif /* _LINUX_FSCACHE_H */
Is this logic correct?
FSCACHE_COOKIE_HAVE_DATA gets set in cachefiles_write_complete, but will that ever be called on a cookie that has no data? Will we ever call cachefiles_write at all when there is no data to be written?
--
Jeff Layton <[email protected]>
David Howells
maintainerRe: [PATCH v4 28/68] fscache: Provide a function to note the release of a page
Quoted 22 lines · Jeff Layton
Quoted 9 lines
include/linux/fscache.h +7
-0+/** +* fscache_note_page_release - Note that a netfs page got released +* @cookie: The cookie corresponding to the file +* +* Note that a page that has been copied to the cache has been released. This +* means that future reads will need to look in the cache to see if it's there. +*/ include/linux/fscache.h static inline void fscache_clear_inode_writeback(struct fscache_cookie *cookie, · line 626
Quoted 10 lines
include/linux/fscache.h +9
-0+static inline +void fscache_note_page_release (struct fscache_cookie *cookie) +{ +if (cookie && +test_bit (FSCACHE_COOKIE_HAVE_DATA , &cookie->flags) && +test_bit (FSCACHE_COOKIE_NO_DATA_TO_READ , &cookie->flags)) +clear_bit (FSCACHE_COOKIE_NO_DATA_TO_READ , &cookie->flags); +} +#endif /* _LINUX_FSCACHE_H */ Is this logic correct?
Quoted 3 lines
FSCACHE_COOKIE_HAVE_DATAgets set incachefiles_write_complete, but will that ever be called on a cookie that has no data? Will we ever callcachefiles_writeat all when there is no data to be written?
FSCACHE_COOKIE_NO_DATA_TO_READ is set if we have no data in the cache yet (ie. the backing file lookup was negative, the file is 0 length or the cookie got invalidated). It means that we have no data in the cache, not that the file is necessarily empty on the server.
FSCACHE_COOKIE_HAVE_DATA is set once we've stored data in the backing file. From that point on, we have data we *could* read - however, it's covered by pages in the netfs pagecache until at such time one of those covering pages is released.
So if we've written data to the cache (HAVE_DATA) and there wasn't any data in the cache when we started (NO_DATA_TO_READ), it may no longer be true that we can skip reading from the cache.
Read skipping is done by cachefiles_prepare_read().
Note that I'm not doing tracking on a per-page basis, but only on a per-file basis.
David
David Howells
maintainerPATCH v4 29/68 fscache: Provide a function to resize a cookie
In reply to David HowellsProvide a function to change the size of the storage attached to a cookie, to match the size of the file being cached when it's changed by truncate or fallocate:
void fscache_resize_cookie(struct fscache_cookie *cookie, loff_t new_size);
This acts synchronously and is expected to run under the inode lock of the caller.
---
fs/fscache/internal.h | 3 +++ fs/fscache/io.c | 25 +++++++++++++++++++++++++ fs/fscache/stats.c | 9 +++++++-- include/linux/fscache-cache.h | 4 ++++ include/linux/fscache.h | 18 ++++++++++++++++++ include/trace/events/fscache.h | 25 +++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 2 deletions(-)
fs/fscache/internal.h +3 -0
@@ -122,6 +122,9 @@ extern atomic_t fscache_n_relinquishes;extern atomic_t fscache_n_relinquishes_retire;extern atomic_t fscache_n_relinquishes_dropped; +extern atomic_t fscache_n_resizes; +extern atomic_t fscache_n_resizes_null; +static inline void fscache_stat (atomic_t *stat) {atomic_inc (stat);
fs/fscache/io.c +25 -0
@@ -291,3 +291,28 @@ void __fscache_write_to_cache(struct fscache_cookie *cookie,term_func (term_func_priv, ret, false); }EXPORT_SYMBOL (__fscache_write_to_cache); + +/* +* Change the size of a backing object. +*/ +void __fscache_resize_cookie (struct fscache_cookie *cookie,loff_t new_size) +{ +struct netfs_cache_resources cres; + +trace_fscache_resize (cookie, new_size); +if (fscache_begin_operation (&cres, cookie,FSCACHE_WANT_WRITE , + fscache_access_io_resize) ==0 ) { +fscache_stat (&fscache_n_resizes); +set_bit (FSCACHE_COOKIE_NEEDS_UPDATE , &cookie->flags); + +/* We cannot defer a resize as we need to do it inside the +* netfs's inode lock so that we're serialised with respect to +* writes. +*/ + cookie->volume->cache->ops->resize_cookie (&cres, new_size); +fscache_end_operation (&cres); + }else { +fscache_stat (&fscache_n_resizes_null); + } +} +EXPORT_SYMBOL (__fscache_resize_cookie);
fs/fscache/stats.c +7 -2
@@ -35,6 +35,9 @@ atomic_t fscache_n_relinquishes;atomic_t fscache_n_relinquishes_retire;atomic_t fscache_n_relinquishes_dropped; +atomic_t fscache_n_resizes; +atomic_t fscache_n_resizes_null; +atomic_t fscache_n_read;EXPORT_SYMBOL (fscache_n_read);atomic_t fscache_n_write;
@@ -69,8 +72,10 @@ int fscache_stats_show(struct seq_file *m, void *v)seq_printf (m,"Invals : n=%u\n" ,atomic_read (&fscache_n_invalidates)); -seq_printf (m,"Updates: n=%u\n" , -atomic_read (&fscache_n_updates)); +seq_printf (m,"Updates: n=%u rsz=%u rsn=%u\n" , +atomic_read (&fscache_n_updates), +atomic_read (&fscache_n_resizes), +atomic_read (&fscache_n_resizes_null)); seq_printf(m, "Relinqs: n=%u rtr=%u drop=%u\n", atomic_read(&fscache_n_relinquishes),
include/linux/fscache-cache.h +4 -0
@@ -64,6 +64,10 @@ struct fscache_cache_ops {/* Withdraw an object without any cookie access counts held */ void (*withdraw_cookie)(struct fscache_cookie *cookie); +/* Change the size of a data object */ +void (*resize_cookie)(struct netfs_cache_resources *cres, +loff_t new_size); +/* Invalidate an object */ bool (*invalidate_cookie)(struct fscache_cookie *cookie);
include/linux/fscache.h +18 -0
@@ -163,6 +163,7 @@ extern struct fscache_cookie *__fscache_acquire_cookie(extern void __fscache_use_cookie (struct fscache_cookie *,bool );extern void __fscache_unuse_cookie (struct fscache_cookie *,const void *,const loff_t *);extern void __fscache_relinquish_cookie (struct fscache_cookie *,bool ); +extern void __fscache_resize_cookie (struct fscache_cookie *,loff_t );extern void __fscache_invalidate (struct fscache_cookie *,const void *,loff_t ,unsigned int );extern int __fscache_begin_read_operation (struct netfs_cache_resources *,struct fscache_cookie *);
@@ -366,6 +367,23 @@ void fscache_update_cookie(struct fscache_cookie *cookie, const void *aux_data,__fscache_update_cookie (cookie, aux_data, object_size); } +/** +* fscache_resize_cookie - Request that a cache object be resized +* @cookie: The cookie representing the cache object +* @new_size: The new size of the object (may be NULL) +* +* Request that the size of an object be changed. +* +* See Documentation/filesystems/caching/netfs-api.txt for a complete +* description. +*/ +static inline +void fscache_resize_cookie (struct fscache_cookie *cookie,loff_t new_size) +{ +if (fscache_cookie_enabled (cookie)) +__fscache_resize_cookie (cookie, new_size); +} +/** * fscache_invalidate - Notify cache that an object needs invalidation * @cookie: The cookie representing the cache object
include/trace/events/fscache.h +25 -0
@@ -78,6 +78,7 @@ enum fscache_access_trace { fscache_access_invalidate_cookie_end, fscache_access_io_not_live, fscache_access_io_read, + fscache_access_io_resize, fscache_access_io_wait, fscache_access_io_write, fscache_access_lookup_cookie,
@@ -149,6 +150,7 @@ enum fscache_access_trace { EM(fscache_access_invalidate_cookie_end,"END inval ") \ EM(fscache_access_io_not_live, "END io_notl") \ EM(fscache_access_io_read, "BEGIN io_read") \ + EM(fscache_access_io_resize, "BEGIN io_resz") \ EM(fscache_access_io_wait, "WAIT io ") \ EM(fscache_access_io_write, "BEGIN io_writ") \EM (fscache_access_lookup_cookie,"BEGIN lookup " ) \
@@ -418,6 +420,29 @@ TRACE_EVENT(fscache_invalidate, __entry->cookie, __entry->new_size) ); +TRACE_EVENT (fscache_resize, + TP_PROTO(struct fscache_cookie *cookie, loff_t new_size), + +TP_ARGS (cookie, new_size), + +TP_STRUCT__entry ( + __field(unsigned int, cookie ) + __field(loff_t, old_size ) +__field (loff_t, new_size ) + ), + +TP_fast_assign ( + __entry->cookie = cookie->debug_id; + __entry->old_size = cookie->object_size; + __entry->new_size = new_size; + ), + +TP_printk ("c=%08x os=%08llx sz=%08llx" , + __entry->cookie, + __entry->old_size, + __entry->new_size) + ); +#endif /* _TRACE_FSCACHE_H */ /* This part must be outside protection */
David Howells
maintainerPATCH v4 30/68 cachefiles: Introduce rewritten driver
In reply to David HowellsIntroduce basic skeleton of the rewritten cachefiles driver including config options so that it can be enabled for compilation.
---
fs/Kconfig | 1 fs/Makefile | 1 fs/cachefiles/Kconfig | 21 +++++++ fs/cachefiles/Makefile | 9 +++ fs/cachefiles/internal.h | 115 +++++++++++++++++++++++++++++++++++++ fs/cachefiles/main.c | 53 +++++++++++++++++ include/trace/events/cachefiles.h | 49 ++++++++++++++++ 7 files changed, 249 insertions(+) create mode 100644 fs/cachefiles/Kconfig create mode 100644 fs/cachefiles/Makefile create mode 100644 fs/cachefiles/internal.h create mode 100644 fs/cachefiles/main.c create mode 100644 include/trace/events/cachefiles.h
fs/Kconfig +1 -0
@@ -132,6 +132,7 @@ menu "Caches" source "fs/netfs/Kconfig" source "fs/fscache/Kconfig" +source "fs/cachefiles/Kconfig" endmenu
fs/Makefile +1 -0
@@ -125,6 +125,7 @@ obj-$(CONFIG_AFS_FS) += afs/ obj-$(CONFIG_NILFS2_FS) += nilfs2/ obj-$(CONFIG_BEFS_FS) += befs/ obj-$(CONFIG_HOSTFS) += hostfs/ +obj-$(CONFIG_CACHEFILES) += cachefiles/ obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_TRACING) += tracefs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/
fs/cachefiles/Kconfig new file +21 -0
@@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config CACHEFILES + tristate "Filesystem caching on files" + depends on FSCACHE && BLOCK + help + This permits use of a mounted filesystem as a cache for other + filesystems - primarily networking filesystems - thus allowing fast + local disk to enhance the speed of slower devices. + + See Documentation/filesystems/caching/cachefiles.rst for more + information. + +config CACHEFILES_DEBUG + bool "Debug CacheFiles" + depends on CACHEFILES + help + This permits debugging to be dynamically enabled in the filesystem + caching on files module. If this is set, the debugging output may be + enabled by setting bits in /sys/modules/cachefiles/parameter/debug or + by including a debugging specifier in /etc/cachefilesd.conf.
fs/cachefiles/Makefile new file +9 -0
@@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for caching in a mounted filesystem +# + +cachefiles-y := \ + main.o + +obj-$(CONFIG_CACHEFILES) := cachefiles.o
fs/cachefiles/internal.h new file +115 -0
115 lines not on this page · raw message
fs/cachefiles/main.c new file +53 -0
53 lines not on this page · raw message
include/trace/events/cachefiles.h new file +49 -0
49 lines not on this page · raw message
David Howells
maintainerPATCH v4 31/68 cachefiles: Define structs
In reply to David HowellsDefine the cachefiles_cache struct that's going to carry the cache-level parameters and state of a cache.
Define the beginning of the cachefiles_object struct that's going to carry the state for a data storage object. For the moment this is just a debugging ID for logging purposes.
---
fs/cachefiles/internal.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)
fs/cachefiles/internal.h +46 -0
@@ -16,6 +16,52 @@#include <linux/cred.h> #include <linux/security.h> +struct cachefiles_cache ; +struct cachefiles_object ; + +/* +* Data file records. +*/ +struct cachefiles_object { +int debug_id;/* debugging ID */ +}; + +/* +* Cache files cache definition +*/ +struct cachefiles_cache { +struct vfsmount *mnt;/* mountpoint holding the cache */ +struct file *cachefilesd;/* manager daemon handle */ +const struct cred *cache_cred;/* security override for accessing cache */ +struct mutex daemon_mutex;/* command serialisation mutex */ +wait_queue_head_t daemon_pollwq;/* poll waitqueue for daemon */ +atomic_t gravecounter;/* graveyard uniquifier */ +atomic_t f_released;/* number of objects released lately */ +atomic_long_t b_released;/* number of blocks released lately */ +unsigned frun_percent;/* when to stop culling (% files) */ +unsigned fcull_percent;/* when to start culling (% files) */ +unsigned fstop_percent;/* when to stop allocating (% files) */ +unsigned brun_percent;/* when to stop culling (% blocks) */ +unsigned bcull_percent;/* when to start culling (% blocks) */ +unsigned bstop_percent;/* when to stop allocating (% blocks) */ +unsigned bsize;/* cache's block size */ +unsigned bshift;/* min(ilog2(PAGE_SIZE / bsize), 0) */ +uint64_t frun;/* when to stop culling */ +uint64_t fcull;/* when to start culling */ +uint64_t fstop;/* when to stop allocating */ +sector_t brun;/* when to stop culling */ +sector_t bcull;/* when to start culling */ +sector_t bstop;/* when to stop allocating */ +unsigned long flags; +#define CACHEFILES_READY 0/* T if cache prepared */ +#define CACHEFILES_DEAD 1/* T if cache dead */ +#define CACHEFILES_CULLING 2/* T if cull engaged */ +#define CACHEFILES_STATE_CHANGED 3/* T if state changed (poll trigger) */ +char *rootdirname;/* name of cache root directory */ +char *secctx;/* LSM security context */ +char *tag;/* cache binding tag */ +}; +/* * Debug tracing.
David Howells
maintainerPATCH v4 32/68 cachefiles: Add some error injection support
In reply to David HowellsAdd support for injecting ENOSPC or EIO errors. This needs to be enabled by CONFIG_CACHEFILES_ERROR_INJECTION=y. Once enabled, ENOSPC on things like write and mkdir can be triggered by:
echo 1 >/proc/sys/cachefiles/error_injection
and EIO can be triggered on most operations by:
echo 2 >/proc/sys/cachefiles/error_injection
---
fs/cachefiles/Kconfig | 7 ++++++ fs/cachefiles/Makefile | 2 ++ fs/cachefiles/error_inject.c | 46 ++++++++++++++++++++++++++++++++++++++++++ fs/cachefiles/internal.h | 42 +++++++++++++++++++++++++++++++++++++- fs/cachefiles/main.c | 12 +++++++++++ 5 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/error_inject.c
fs/cachefiles/Kconfig +7 -0
@@ -19,3 +19,10 @@ config CACHEFILES_DEBUG caching on files module. If this is set, the debugging output may be enabled by setting bits in /sys/modules/cachefiles/parameter/debug or by including a debugging specifier in /etc/cachefilesd.conf. + +config CACHEFILES_ERROR_INJECTION + bool "Provide error injection for cachefiles" + depends on CACHEFILES && SYSCTL + help + This permits error injection to be enabled in cachefiles whilst a + cache is in service.
fs/cachefiles/Makefile +2 -0
@@ -6,4 +6,6 @@ cachefiles-y := \ main.o +cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) += error_inject.o + obj-$(CONFIG_CACHEFILES) := cachefiles.o
fs/cachefiles/error_inject.c new file +46 -0
46 lines not on this page · raw message
fs/cachefiles/internal.h +41 -1
48 lines not on this page · raw message
fs/cachefiles/main.c +12 -0
@@ -36,8 +36,18 @@ MODULE_LICENSE("GPL");*/ static int __init cachefiles_init (void ) { +int ret; + + ret =cachefiles_register_error_injection (); +if (ret <0 ) + goto error_einj; +pr_info ("Loaded\n" );return 0 ; + +error_einj: +pr_err ("failed to register: %d\n" , ret); +return ret; }fs_initcall (cachefiles_init);
@@ -48,6 +58,8 @@ fs_initcall(cachefiles_init); static void __exit cachefiles_exit(void) {pr_info ("Unloading\n" ); + +cachefiles_unregister_error_injection (); }module_exit (cachefiles_exit);
David Howells
maintainerPATCH v4 33/68 cachefiles: Add a couple of tracepoints for logging errors
In reply to David HowellsAdd two trace points to log errors, one for vfs operations like mkdir or create, and one for I/O operations, like read, write or truncate.
Also add the beginnings of a struct that is going to represent a data file and place a debugging ID in it for the tracepoints to record.
---
fs/cachefiles/internal.h | 1 include/trace/events/cachefiles.h | 94 +++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+)
fs/cachefiles/internal.h +1 -0
@@ -62,6 +62,7 @@ struct cachefiles_cache {char *tag;/* cache binding tag */ }; +#include <trace/events/cachefiles.h> /* * error_inject.c
include/trace/events/cachefiles.h +94 -0
117 lines not on this page · raw message
David Howells
maintainerPATCH v4 34/68 cachefiles: Add cache error reporting macro
In reply to David HowellsAdd a macro to report a cache I/O error and to tell fscache that the cache is in trouble.
Also add a pointer to the fscache cache cookie from the cachefiles_cache struct as we need that to pass to fscache_io_error().
---
fs/cachefiles/internal.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
fs/cachefiles/internal.h +11 -0
@@ -30,6 +30,7 @@ struct cachefiles_object {* Cache files cache definition */ struct cachefiles_cache { +struct fscache_cache *cache;/* Cache cookie */ struct vfsmount *mnt;/* mountpoint holding the cache */ struct file *cachefilesd;/* manager daemon handle */ const struct cred *cache_cred;/* security override for accessing cache */
@@ -103,6 +104,16 @@ static inline int cachefiles_inject_remove_error(void)return cachefiles_error_injection_state &2 ? -EIO :0 ; } +/* +* Error handling +*/ +#define cachefiles_io_error (___cache,FMT , ...) \ +do { \ + pr_err("I/O Error: " FMT"\n", ##__VA_ARGS__); \ + fscache_io_error((___cache)->cache); \ + set_bit(CACHEFILES_DEAD, &(___cache)->flags); \ +} while (0) +/* * Debug tracing
David Howells
maintainerPATCH v4 35/68 cachefiles: Add security derivation
In reply to David HowellsImplement code to derive a new set of creds for the cachefiles to use when making VFS or I/O calls and to change the auditing info since the application interacting with the network filesystem is not accessing the cache directly. Cachefiles uses override_creds() to change the effective creds temporarily.
set_security_override_from_ctx() is called to derive the LSM 'label' that the cachefiles driver will act with. set_create_files_as() is called to determine the LSM 'label' that will be applied to files and directories created in the cache. These functions alter the new creds.
Also implement a couple of functions to wrap the calls to begin/end cred overriding.
---
fs/cachefiles/Makefile | 3 + fs/cachefiles/internal.h | 20 ++++++++ fs/cachefiles/security.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/security.c
fs/cachefiles/Makefile +2 -1
@@ -4,7 +4,8 @@ # cachefiles-y := \ - main.o + main.o \ + security.o cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) += error_inject.o
fs/cachefiles/internal.h +20 -0
@@ -104,6 +104,26 @@ static inline int cachefiles_inject_remove_error(void)return cachefiles_error_injection_state &2 ? -EIO :0 ; } +/* +* security.c +*/ +extern int cachefiles_get_security_ID (struct cachefiles_cache *cache); +extern int cachefiles_determine_cache_security (struct cachefiles_cache *cache, +struct dentry *root, +const struct cred **_saved_cred); + +static inline void cachefiles_begin_secure (struct cachefiles_cache *cache, +const struct cred **_saved_cred) +{ + *_saved_cred =override_creds (cache->cache_cred); +} + +static inline void cachefiles_end_secure (struct cachefiles_cache *cache, +const struct cred *saved_cred) +{ +revert_creds (saved_cred); +} +/* * Error handling */
fs/cachefiles/security.c new file +112 -0
112 lines not on this page · raw message
David Howells
maintainerPATCH v4 36/68 cachefiles: Register a miscdev and parse commands over it
In reply to David HowellsRegister a misc device with which to talk to the daemon. The misc device holds a cache set up through it around and closing the device kills the cache.
cachefilesd communicates with the kernel by passing it single-line text commands. Parse these and use them to parameterise the cache state. This does not implement the command to actually bring a cache online. That's left for later.
---
fs/cachefiles/Makefile | 1 fs/cachefiles/daemon.c | 725 ++++++++++++++++++++++++++++++++++++++++++++++ fs/cachefiles/internal.h | 14 + fs/cachefiles/main.c | 12 + 4 files changed, 752 insertions(+) create mode 100644 fs/cachefiles/daemon.c
fs/cachefiles/Makefile +1 -0
@@ -4,6 +4,7 @@ # cachefiles-y := \ + daemon.o \ main.o \ security.o
fs/cachefiles/daemon.c new file +725 -0
725 lines not on this page · raw message
fs/cachefiles/internal.h +14 -0
@@ -65,6 +65,20 @@ struct cachefiles_cache {#include <trace/events/cachefiles.h> +/* +* note change of state for daemon +*/ +static inline void cachefiles_state_changed (struct cachefiles_cache *cache) +{ +set_bit (CACHEFILES_STATE_CHANGED , &cache->flags); +wake_up_all (&cache->daemon_pollwq); +} + +/* +* daemon.c +*/ +extern const struct file_operations cachefiles_daemon_fops; +/* * error_inject.c */
fs/cachefiles/main.c +12 -0
@@ -31,6 +31,12 @@ MODULE_DESCRIPTION("Mounted-filesystem based cache");MODULE_AUTHOR ("Red Hat, Inc." );MODULE_LICENSE ("GPL" ); +static struct miscdevice cachefiles_dev = { + .minor =MISC_DYNAMIC_MINOR , + .name ="cachefiles" , + .fops = &cachefiles_daemon_fops, +}; +/* * initialise the fs caching module */
@@ -41,10 +47,15 @@ static int __init cachefiles_init(void) ret =cachefiles_register_error_injection ();if (ret <0 ) goto error_einj; + ret =misc_register (&cachefiles_dev); +if (ret <0 ) + goto error_dev;pr_info ("Loaded\n" );return 0 ; +error_dev: +cachefiles_unregister_error_injection (); error_einj:pr_err ("failed to register: %d\n" , ret);return ret;
@@ -59,6 +70,7 @@ static void __exit cachefiles_exit(void) {pr_info ("Unloading\n" ); +misc_deregister (&cachefiles_dev);cachefiles_unregister_error_injection (); }
David Howells
maintainerPATCH v4 37/68 cachefiles: Provide a function to check how much space there is
In reply to David HowellsProvide a function to check how much space there is. This also flips the state on the cache and will signal the daemon to inform it of the change and to ask it to do some culling if necessary.
We will also need to subtract the amount of data currently being written to the cache (cache->b_writing) from the amount of available space to avoid hitting ENOSPC accidentally.
---
fs/cachefiles/Makefile | 1 fs/cachefiles/cache.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++ fs/cachefiles/daemon.c | 2 - fs/cachefiles/internal.h | 7 +++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/cache.c
fs/cachefiles/Makefile +1 -0
@@ -4,6 +4,7 @@ # cachefiles-y := \ + cache.o \ daemon.o \ main.o \ security.o
fs/cachefiles/cache.c new file +103 -0
103 lines not on this page · raw message
fs/cachefiles/daemon.c +1 -1
@@ -167,7 +167,7 @@ static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer,return 0 ;/* check how much space the cache has */ -// PLACEHOLDER: Check space +cachefiles_has_space (cache,0 ,0 );/* summarise */ f_released =atomic_xchg (&cache->f_released,0 );
fs/cachefiles/internal.h +7 -0
@@ -39,6 +39,7 @@ struct cachefiles_cache {atomic_t gravecounter;/* graveyard uniquifier */ atomic_t f_released;/* number of objects released lately */ atomic_long_t b_released;/* number of blocks released lately */ +atomic_long_t b_writing;/* Number of blocks being written */ unsigned frun_percent;/* when to stop culling (% files) */ unsigned fcull_percent;/* when to start culling (% files) */ unsigned fstop_percent;/* when to stop allocating (% files) */
@@ -74,6 +75,12 @@ static inline void cachefiles_state_changed(struct cachefiles_cache *cache)wake_up_all (&cache->daemon_pollwq); } +/* +* cache.c +*/ +extern int cachefiles_has_space (struct cachefiles_cache *cache, +unsigned fnr,unsigned bnr); +/* * daemon.c */
David Howells
maintainerPATCH v4 38/68 vfs, cachefiles: Mark a backing file in use with an inode flag
In reply to David HowellsUse an inode flag, S_KERNEL_FILE, to mark that a backing file is in use by the kernel to prevent cachefiles or other kernel services from interfering with that file.
Alter rmdir to reject attempts to remove a directory marked with this flag. This is used by cachefiles to prevent cachefilesd from removing them.
Using S_SWAPFILE instead isn't really viable as that has other effects in the I/O paths.
Changes
=======
ver #3:
- Check for the object pointer being NULL in the tracepoints rather than the caller.
---
fs/cachefiles/Makefile | 1 + fs/cachefiles/namei.c | 43 +++++++++++++++++++++++++++++++++++++ fs/namei.c | 3 ++- include/linux/fs.h | 1 + include/trace/events/cachefiles.h | 42 ++++++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/namei.c
fs/cachefiles/Makefile +1 -0
@@ -7,6 +7,7 @@ cachefiles-y := \ cache.o \ daemon.o \ main.o \ + namei.o \ security.o cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) += error_inject.o
fs/cachefiles/namei.c new file +43 -0
43 lines not on this page · raw message
fs/namei.c +2 -1
@@ -3958,7 +3958,8 @@ int vfs_rmdir(struct user_namespace *mnt_userns, struct inode *dir,inode_lock (dentry->d_inode); error = -EBUSY ; -if (is_local_mountpoint (dentry)) +if (is_local_mountpoint (dentry) || + (dentry->d_inode->i_flags &S_KERNEL_FILE )) goto out; error =security_inode_rmdir (dir, dentry);
include/linux/fs.h +1 -0
@@ -2249,6 +2249,7 @@ struct super_operations {#define S_ENCRYPTED (1 << 14)/* Encrypted file (using fs/crypto/) */ #define S_CASEFOLD (1 << 15)/* Casefolded file */ #define S_VERITY (1 << 16)/* Verity file (using fs/verity/) */ +#define S_KERNEL_FILE (1 << 17)/* File is in use by the kernel (eg. fs/cachefiles) */ /* * Note that nosuid etc flags are inode-specific: setting some file-system
include/trace/events/cachefiles.h +42 -0
48 lines not on this page · raw message
Jeff Layton
maintainerRe: [PATCH v4 38/68] vfs, cachefiles: Mark a backing file in use with an inode flag
Quoted 61 lines · David Howells
Use an inode flag,
S_KERNEL_FILE, to mark that a backing file is in use by the kernel to prevent cachefiles or other kernel services from interfering with that file.Alter rmdir to reject attempts to remove a directory marked with this flag. This is used by cachefiles to prevent cachefilesd from removing them.
Using
S_SWAPFILEinstead isn't really viable as that has other effects in the I/O paths.Changes
=======
ver #3:
- Check for the object pointer being NULL in the tracepoints rather than the caller.Signed-off-by David Howells <[email protected]>Link https://lore.kernel.org/r/163819630256.215744.4815885535039369574.stgit@warthog.procyon.org.uk/ # v1 open here---
fs/cachefiles/Makefile | 1 + fs/cachefiles/namei.c | 43 +++++++++++++++++++++++++++++++++++++ fs/namei.c | 3 ++- include/linux/fs.h | 1 + include/trace/events/cachefiles.h | 42 ++++++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/namei.cfs/cachefiles/Makefile +1
-0@@ -7,6 +7,7 @@ cachefiles-y := \ cache.o \ daemon.o \ main.o \ + namei.o \ security.o cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) += error_inject.ofs/cachefiles/namei.c new file +12
-0@@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* CacheFiles path walking and related routines +* +* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. +* Written by David Howells ([email protected]) +*/ + +#include <linux/fs.h> +#include "internal.h" + +/* +* Mark the backing file as being a cache file if it's not already in use. The
fs/cachefiles/namei.c line 22
Quoted 10 lines
fs/cachefiles/namei.c new file +10
-0+* mark tells the culling request command that it's not allowed to cull the +* file or directory. The caller must hold the inode lock. +*/ +static bool __cachefiles_mark_inode_in_use (struct cachefiles_object *object, +struct dentry *dentry) +{ +struct inode *inode =d_backing_inode (dentry); +bool can_use = false; + +if (!(inode->i_flags &S_KERNEL_FILE )) {
nit: most of the other S_* flags have a corresponding IS_* macro. Should this be:
IS_KERNEL_FILE(inode)
?
Quoted 102 lines
fs/cachefiles/namei.c new file +21
-0+ inode->i_flags |=S_KERNEL_FILE ; +trace_cachefiles_mark_active (object, inode); + can_use = true; + }else { +pr_notice ("cachefiles: Inode already in use: %pd\n" , dentry); + } + +return can_use; +} + +/* +* Unmark a backing inode. The caller must hold the inode lock. +*/ +static void __cachefiles_unmark_inode_in_use (struct cachefiles_object *object, +struct dentry *dentry) +{ +struct inode *inode =d_backing_inode (dentry); + + inode->i_flags &= ~S_KERNEL_FILE ; +trace_cachefiles_mark_inactive (object, inode); +}fs/namei.c +2
-1@@ -3958,7 +3958,8 @@ int vfs_rmdir(struct user_namespace *mnt_userns, struct inode *dir,inode_lock (dentry->d_inode); error = -EBUSY ; -if (is_local_mountpoint (dentry)) +if (is_local_mountpoint (dentry) || + (dentry->d_inode->i_flags &S_KERNEL_FILE )) goto out; error =security_inode_rmdir (dir, dentry);include/linux/fs.h +1
-0@@ -2249,6 +2249,7 @@ struct super_operations {#define S_ENCRYPTED (1 << 14)/* Encrypted file (using fs/crypto/) */ #define S_CASEFOLD (1 << 15)/* Casefolded file */ #define S_VERITY (1 << 16)/* Verity file (using fs/verity/) */ +#define S_KERNEL_FILE (1 << 17)/* File is in use by the kernel (eg. fs/cachefiles) */ /* * Note that nosuid etc flags are inode-specific: setting some file-system include/trace/events/cachefiles.h +42
-0@@ -83,6 +83,48 @@ cachefiles_error_traces;#define E_ (a, b) { a, b } +TRACE_EVENT (cachefiles_mark_active, + TP_PROTO(struct cachefiles_object *obj, + struct inode *inode), + +TP_ARGS (obj, inode), + +/* Note that obj may be NULL */ +TP_STRUCT__entry ( + __field(unsigned int, obj ) +__field (ino_t, inode ) + ), + +TP_fast_assign ( + __entry->obj = obj ? obj->debug_id : 0; + __entry->inode = inode->i_ino; + ), + +TP_printk ("o=%08x i=%lx" , + __entry->obj, __entry->inode) + ); + +TRACE_EVENT (cachefiles_mark_inactive, + TP_PROTO(struct cachefiles_object *obj, + struct inode *inode), + +TP_ARGS (obj, inode), + +/* Note that obj may be NULL */ +TP_STRUCT__entry ( + __field(unsigned int, obj ) +__field (ino_t, inode ) + ), + +TP_fast_assign ( + __entry->obj = obj ? obj->debug_id : 0; + __entry->inode = inode->i_ino; + ), + +TP_printk ("o=%08x i=%lx" , + __entry->obj, __entry->inode) + ); + TRACE_EVENT(cachefiles_vfs_error, TP_PROTO(struct cachefiles_object *obj, struct inode *backer, int error, enum cachefiles_error_trace where),
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v4 38/68] vfs, cachefiles: Mark a backing file in use with an inode flag
In reply to David HowellsQuoted 98 lines · David Howells
Use an inode flag,
S_KERNEL_FILE, to mark that a backing file is in use by the kernel to prevent cachefiles or other kernel services from interfering with that file.Alter rmdir to reject attempts to remove a directory marked with this flag. This is used by cachefiles to prevent cachefilesd from removing them.
Using
S_SWAPFILEinstead isn't really viable as that has other effects in the I/O paths.Changes
=======
ver #3:
- Check for the object pointer being NULL in the tracepoints rather than the caller.Signed-off-by David Howells <[email protected]>Link https://lore.kernel.org/r/163819630256.215744.4815885535039369574.stgit@warthog.procyon.org.uk/ # v1 open here---
fs/cachefiles/Makefile | 1 + fs/cachefiles/namei.c | 43 +++++++++++++++++++++++++++++++++++++ fs/namei.c | 3 ++- include/linux/fs.h | 1 + include/trace/events/cachefiles.h | 42 ++++++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/namei.cfs/cachefiles/Makefile +1
-0@@ -7,6 +7,7 @@ cachefiles-y := \ cache.o \ daemon.o \ main.o \ + namei.o \ security.o cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) += error_inject.ofs/cachefiles/namei.c new file +43
-0@@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* CacheFiles path walking and related routines +* +* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. +* Written by David Howells ([email protected]) +*/ + +#include <linux/fs.h> +#include "internal.h" + +/* +* Mark the backing file as being a cache file if it's not already in use. The +* mark tells the culling request command that it's not allowed to cull the +* file or directory. The caller must hold the inode lock. +*/ +static bool __cachefiles_mark_inode_in_use (struct cachefiles_object *object, +struct dentry *dentry) +{ +struct inode *inode =d_backing_inode (dentry); +bool can_use = false; + +if (!(inode->i_flags &S_KERNEL_FILE )) { + inode->i_flags |=S_KERNEL_FILE ; +trace_cachefiles_mark_active (object, inode); + can_use = true; + }else { +pr_notice ("cachefiles: Inode already in use: %pd\n" , dentry); + } + +return can_use; +} + +/* +* Unmark a backing inode. The caller must hold the inode lock. +*/ +static void __cachefiles_unmark_inode_in_use (struct cachefiles_object *object, +struct dentry *dentry) +{ +struct inode *inode =d_backing_inode (dentry); + + inode->i_flags &= ~S_KERNEL_FILE ; +trace_cachefiles_mark_inactive (object, inode); +}fs/namei.c +0
-0
fs/namei.c int vfs_rmdir(struct user_namespace *mnt_userns, struct inode *dir, · line 3962
Quoted 6 lines
fs/namei.c +2
-1@@ -3958,7 +3958,8 @@ int vfs_rmdir(struct user_namespace *mnt_userns, struct inode *dir,inode_lock (dentry->d_inode); error = -EBUSY ; -if (is_local_mountpoint (dentry)) +if (is_local_mountpoint (dentry) || + (dentry->d_inode->i_flags &S_KERNEL_FILE ))
Better as this check to the many other checks in may_delete()
include/linux/fs.h struct super_operations { · line 2253
Quoted 13 lines
fs/namei.c +0
-0goto out; error =security_inode_rmdir (dir, dentry);include/linux/fs.h +1
-0@@ -2249,6 +2249,7 @@ struct super_operations {#define S_ENCRYPTED (1 << 14)/* Encrypted file (using fs/crypto/) */ #define S_CASEFOLD (1 << 15)/* Casefolded file */ #define S_VERITY (1 << 16)/* Verity file (using fs/verity/) */ +#define S_KERNEL_FILE (1 << 17)/* File is in use by the kernel (eg. fs/cachefiles) */
Trying to brand this flag as a generic "in use by kernel" is misleading. Modules other than cachefiles cannot set/clear this flag, because then cachefiles won't know that it is allowed to set/clear the flag.
So I think it would be better to call it for what it really is - an inode flag that is controlled by cachefiles.
Also, the name KERNEL_FILE for a directory is a bit confusing IMO. Perhaps S_CACHEFILES?
Thanks,
Amir.
Matthew Wilcox
maintainerRe: [PATCH v4 38/68] vfs, cachefiles: Mark a backing file in use with an inode flag
Quoted 5 lines · Amir Goldstein
Quoted 1 line
+#define
S_KERNEL_FILE(1 << 17) /* File is in use by the kernel (eg. fs/cachefiles) */Trying to brand this flag as a generic "in use by kernel" is misleading. Modules other than cachefiles cannot set/clear this flag, because then cachefiles won't know that it is allowed to set/clear the flag.
Huh? If some other kernel module sets it, cachefiles will try to set it, see it's already set, and fail. Presumably cachefiles does not go round randomly "unusing" files that it has not previously started using.
I mean, yes, obviously, it's a kernel module, it can set and clear whatever flags it likes on any inode in the system, but conceptually, it's an advisory whole-file lock.
David Howells
maintainerRe: [PATCH v4 38/68] vfs, cachefiles: Mark a backing file in use with an inode flag
Quoted 4 lines · Matthew Wilcox
Huh? If some other kernel module sets it, cachefiles will try to set it, see it's already set, and fail. Presumably cachefiles does not go round randomly "unusing" files that it has not previously started using.
Correct. It only unuses a file that it marked in-use in the first place.
David
Re: [PATCH v4 38/68] vfs, cachefiles: Mark a backing file in use with an inode flag
In reply to Matthew WilcoxQuoted 7 lines · Matthew Wilcox
Quoted 5 lines · Amir Goldstein
Quoted 1 line
+#define
S_KERNEL_FILE(1 << 17) /* File is in use by the kernel (eg. fs/cachefiles) */Trying to brand this flag as a generic "in use by kernel" is misleading. Modules other than cachefiles cannot set/clear this flag, because then cachefiles won't know that it is allowed to set/clear the flag.
Quoted 7 lines
Huh? If some other kernel module sets it, cachefiles will try to set it, see it's already set, and fail. Presumably cachefiles does not go round randomly "unusing" files that it has not previously started using.
I mean, yes, obviously, it's a kernel module, it can set and clear whatever flags it likes on any inode in the system, but conceptually, it's an advisory whole-file lock.
So let's name it that way. We have plenty of files in kernel use using filp_open and this flag very obviously means something else.
David Howells
maintainerRe: [PATCH v4 38/68] vfs, cachefiles: Mark a backing file in use with an inode flag
Quoted 2 lines · Christoph Hellwig
So let's name it that way. We have plenty of files in kernel use using
filp_openand this flag very obviously means something else.
S_KERNEL_LOCK?
David
David Howells
maintainerRe: [PATCH v4 38/68] vfs, cachefiles: Mark a backing file in use with an inode flag
In reply to Amir GoldsteinQuoted 5 lines · Amir Goldstein
fs/namei.c int vfs_rmdir(struct user_namespace *mnt_userns, struct inode *dir, · line 3962
Quoted 3 lines
fs/namei.c +2
-1-if (is_local_mountpoint (dentry)) +if (is_local_mountpoint (dentry) || + (dentry->d_inode->i_flags &S_KERNEL_FILE ))Better as this check to the many other checks in
may_delete()
Okay. It will make things a bit more complicated, so I'll do it in a follow up patch. The problem is that it will prevent the cachefiles driver in the kernel from renaming directories and unlinking files as it's currently removing the mark *after* moving/deleting them.
Quoted 6 lines
Quoted 2 lines
+#define
S_KERNEL_FILE(1 << 17) /* File is in use by the kernel (eg. fs/cachefiles) */Trying to brand this flag as a generic "in use by kernel" is misleading. Modules other than cachefiles cannot set/clear this flag, because then cachefiles won't know that it is allowed to set/clear the flag.
If the flag is set, then cachefiles thinks some other kernel driver is using the file and it shouldn't try to use it. It doesn't matter who has it open.
It should never happen as other kernel drivers shouldn't be poking around inside cachefiles's cache, but possibly someone could misconfigure something.
David
David Howells
maintainerPATCH v4 39/68 cachefiles: Implement a function to get/create a directory in the cache
In reply to David HowellsImplement a function to get/create structural directories in the cache. This is used for setting up a cache and creating volume substructures. The directory in memory are marked with the S_KERNEL_FILE inode flag whilst they're in use to tell rmdir to reject attempts to remove them.
Changes
=======
ver #3:
- Return an indication as to whether the directory was freshly created.
---
fs/cachefiles/internal.h | 9 +++ fs/cachefiles/namei.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+)
fs/cachefiles/internal.h +9 -0
@@ -125,6 +125,15 @@ static inline int cachefiles_inject_remove_error(void)return cachefiles_error_injection_state &2 ? -EIO :0 ; } +/* +* namei.c +*/ +extern struct dentry *cachefiles_get_directory (struct cachefiles_cache *cache, +struct dentry *dir, +const char *name, +bool *_is_new); +extern void cachefiles_put_directory (struct dentry *dir); +/* * security.c */
fs/cachefiles/namei.c +141 -0
150 lines not on this page · raw message
David Howells
maintainerPATCH v4 40/68 cachefiles: Implement cache registration and withdrawal
In reply to David HowellsDo the following:
(1) Fill out cachefiles_daemon_add_cache() so that it sets up the cache directories and registers the cache with cachefiles.
(2) Add a function to do the top-level part of cache withdrawal and unregistration.
(3) Add a function to sync a cache.
---
fs/cachefiles/Makefile | 1 fs/cachefiles/cache.c | 207 +++++++++++++++++++++++++++++++++++++++++++++ fs/cachefiles/daemon.c | 8 +- fs/cachefiles/interface.c | 18 ++++ fs/cachefiles/internal.h | 9 ++ 5 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 fs/cachefiles/interface.c
fs/cachefiles/Makefile +1 -0
@@ -6,6 +6,7 @@ cachefiles-y := \ cache.o \ daemon.o \ + interface.o \ main.o \ namei.o \ security.o
fs/cachefiles/cache.c +207 -0
216 lines not on this page · raw message
fs/cachefiles/daemon.c +5 -3
@@ -702,6 +702,7 @@ static int cachefiles_daemon_bind(struct cachefiles_cache *cache, char *args)pr_warn ("Cache is disabled for development\n" );return -ENOANO ;// Don't allow the cache to operate yet +//return cachefiles_add_cache(cache); }/*
@@ -711,10 +712,11 @@ static void cachefiles_daemon_unbind(struct cachefiles_cache *cache) {_enter ("" ); -if (test_bit (CACHEFILES_READY , &cache->flags)) { -// PLACEHOLDER: Withdraw cache - } +if (test_bit (CACHEFILES_READY , &cache->flags)) +cachefiles_withdraw_cache (cache); +cachefiles_put_directory (cache->graveyard); +cachefiles_put_directory (cache->store);mntput (cache->mnt);kfree (cache->rootdirname);
fs/cachefiles/interface.c new file +18 -0
@@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* FS-Cache interface to CacheFiles +* +* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. +* Written by David Howells ([email protected]) +*/ + +#include <linux/slab.h> +#include <linux/mount.h> +#include <linux/xattr.h> +#include <linux/file.h> +#include <linux/falloc.h> +#include <trace/events/fscache.h> +#include "internal.h" + +const struct fscache_cache_ops cachefiles_cache_ops = { + .name ="cachefiles" , +};
fs/cachefiles/internal.h +9 -0
@@ -32,6 +32,8 @@ struct cachefiles_object {struct cachefiles_cache {struct fscache_cache *cache;/* Cache cookie */ struct vfsmount *mnt;/* mountpoint holding the cache */ +struct dentry *store;/* Directory into which live objects go */ +struct dentry *graveyard;/* directory into which dead objects go */ struct file *cachefilesd;/* manager daemon handle */ const struct cred *cache_cred;/* security override for accessing cache */ struct mutex daemon_mutex;/* command serialisation mutex */
@@ -78,8 +80,10 @@ static inline void cachefiles_state_changed(struct cachefiles_cache *cache)/* * cache.c */ +extern int cachefiles_add_cache (struct cachefiles_cache *cache);extern int cachefiles_has_space (struct cachefiles_cache *cache,unsigned fnr,unsigned bnr); +extern void cachefiles_withdraw_cache (struct cachefiles_cache *cache);/* * daemon.c
@@ -125,6 +129,11 @@ static inline int cachefiles_inject_remove_error(void)return cachefiles_error_injection_state &2 ? -EIO :0 ; } +/* +* interface.c +*/ +extern const struct fscache_cache_ops cachefiles_cache_ops; +/* * namei.c */
Jeff Layton
maintainerRe: [PATCH v4 40/68] cachefiles: Implement cache registration and withdrawal
Quoted 85 lines · David Howells
Do the following:
(1) Fill out
cachefiles_daemon_add_cache()so that it sets up the cache directories and registers the cache with cachefiles.(2) Add a function to do the top-level part of cache withdrawal and unregistration.
(3) Add a function to sync a cache.
Signed-off-by David Howells <[email protected]>Link https://lore.kernel.org/r/163819633175.215744.10857127598041268340.stgit@warthog.procyon.org.uk/ # v1 open here---
fs/cachefiles/Makefile | 1 fs/cachefiles/cache.c | 207 +++++++++++++++++++++++++++++++++++++++++++++ fs/cachefiles/daemon.c | 8 +- fs/cachefiles/interface.c | 18 ++++ fs/cachefiles/internal.h | 9 ++ 5 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 fs/cachefiles/interface.cfs/cachefiles/Makefile +1
-0@@ -6,6 +6,7 @@ cachefiles-y := \ cache.o \ daemon.o \ + interface.o \ main.o \ namei.o \ security.ofs/cachefiles/cache.c +40
-0@@ -10,6 +10,166 @@#include <linux/namei.h> #include "internal.h" +/* +* Bring a cache online. +*/ +int cachefiles_add_cache (struct cachefiles_cache *cache) +{ +struct fscache_cache *cache_cookie; +struct path path; +struct kstatfs stats; +struct dentry *graveyard, *cachedir, *root; +const struct cred *saved_cred; +int ret; + +_enter ("" ); + + cache_cookie =fscache_acquire_cache (cache->tag); +if (IS_ERR (cache_cookie)) +return PTR_ERR (cache_cookie); + +/* we want to work under the module's security ID */ + ret =cachefiles_get_security_ID (cache); +if (ret <0 ) + goto error_getsec; + +cachefiles_begin_secure (cache, &saved_cred); + +/* look up the directory at the root of the cache */ + ret =kern_path (cache->rootdirname,LOOKUP_DIRECTORY , &path); +if (ret <0 ) + goto error_open_root; + + cache->mnt = path.mnt; + root = path.dentry; + + ret = -EINVAL ; +if (mnt_user_ns (path.mnt) != &init_user_ns) { +pr_warn ("File cache on idmapped mounts not supported" ); + goto error_unsupported; + } + +/* check parameters */
fs/cachefiles/cache.c line 62
Quoted 10 lines
fs/cachefiles/cache.c +10
-0+ ret = -EOPNOTSUPP ; +if (d_is_negative (root) || + !d_backing_inode (root)->i_op->lookup || + !d_backing_inode (root)->i_op->mkdir || + !(d_backing_inode (root)->i_opflags &IOP_XATTR ) || + !root->d_sb->s_op->statfs || + !root->d_sb->s_op->sync_fs || + root->d_sb->s_blocksize >PAGE_SIZE ) + goto error_unsupported; +
That's quite a collection of tests.
Most are obvious, but some comments explaining the need for others would not be a bad thing. In particular, why is s_blocksize > PAGE_SIZE unsupported?
Also, should you vet whether the fs supports i_op->tmpfile here ?
Quoted 253 lines
fs/cachefiles/cache.c +157
-0+ ret = -EROFS ; +if (sb_rdonly (root->d_sb)) + goto error_unsupported; + +/* determine the security of the on-disk cache as this governs +* security ID of files we create */ + ret =cachefiles_determine_cache_security (cache, root, &saved_cred); +if (ret <0 ) + goto error_unsupported; + +/* get the cache size and blocksize */ + ret =vfs_statfs (&path, &stats); +if (ret <0 ) + goto error_unsupported; + + ret = -ERANGE ; +if (stats.f_bsize <=0 ) + goto error_unsupported; + + ret = -EOPNOTSUPP ; +if (stats.f_bsize >PAGE_SIZE ) + goto error_unsupported; + + cache->bsize = stats.f_bsize; + cache->bshift =0 ; +if (stats.f_bsize <PAGE_SIZE ) + cache->bshift =PAGE_SHIFT -ilog2 (stats.f_bsize); + +_debug ("blksize %u (shift %u)" , + cache->bsize, cache->bshift); + +_debug ("size %llu, avail %llu" , + (unsigned long long ) stats.f_blocks, + (unsigned long long ) stats.f_bavail); + +/* set up caching limits */ +do_div (stats.f_files,100 ); + cache->fstop = stats.f_files * cache->fstop_percent; + cache->fcull = stats.f_files * cache->fcull_percent; + cache->frun = stats.f_files * cache->frun_percent; + +_debug ("limits {%llu,%llu,%llu} files" , + (unsigned long long ) cache->frun, + (unsigned long long ) cache->fcull, + (unsigned long long ) cache->fstop); + + stats.f_blocks >>= cache->bshift; +do_div (stats.f_blocks,100 ); + cache->bstop = stats.f_blocks * cache->bstop_percent; + cache->bcull = stats.f_blocks * cache->bcull_percent; + cache->brun = stats.f_blocks * cache->brun_percent; + +_debug ("limits {%llu,%llu,%llu} blocks" , + (unsigned long long ) cache->brun, + (unsigned long long ) cache->bcull, + (unsigned long long ) cache->bstop); + +/* get the cache directory and check its type */ + cachedir =cachefiles_get_directory (cache, root,"cache" ,NULL ); +if (IS_ERR (cachedir)) { + ret =PTR_ERR (cachedir); + goto error_unsupported; + } + + cache->store = cachedir; + +/* get the graveyard directory */ + graveyard =cachefiles_get_directory (cache, root,"graveyard" ,NULL ); +if (IS_ERR (graveyard)) { + ret =PTR_ERR (graveyard); + goto error_unsupported; + } + + cache->graveyard = graveyard; + cache->cache = cache_cookie; + + ret =fscache_add_cache (cache_cookie, &cachefiles_cache_ops, cache); +if (ret <0 ) + goto error_add_cache; + +/* done */ +set_bit (CACHEFILES_READY , &cache->flags); +dput (root); + +pr_info ("File cache on %s registered\n" , cache_cookie->name); + +/* check how much space the cache has */ +cachefiles_has_space (cache,0 ,0 ); +cachefiles_end_secure (cache, saved_cred); +_leave (" = 0 [%px]" , cache->cache); +return 0 ; + +error_add_cache: +cachefiles_put_directory (cache->graveyard); + cache->graveyard =NULL ; +error_unsupported: +cachefiles_put_directory (cache->store); + cache->store =NULL ; +mntput (cache->mnt); + cache->mnt =NULL ; +dput (root); +error_open_root: +cachefiles_end_secure (cache, saved_cred); +error_getsec: +fscache_relinquish_cache (cache_cookie); + cache->cache =NULL ; +pr_err ("Failed to register: %d\n" , ret); +return ret; +} +/* * See if we have space for a number of pages and/or a number of files in the * cache @@ -101,3 +261,50 @@ int cachefiles_has_space(struct cachefiles_cache *cache,_leave (" = %d" , ret);return ret; } + +/* +* Sync a cache to backing disk. +*/ +static void cachefiles_sync_cache (struct cachefiles_cache *cache) +{ +const struct cred *saved_cred; +int ret; + +_enter ("%s" , cache->cache->name); + +/* make sure all pages pinned by operations on behalf of the netfs are +* written to disc */ +cachefiles_begin_secure (cache, &saved_cred); +down_read (&cache->mnt->mnt_sb->s_umount); + ret =sync_filesystem (cache->mnt->mnt_sb); +up_read (&cache->mnt->mnt_sb->s_umount); +cachefiles_end_secure (cache, saved_cred); + +if (ret == -EIO ) +cachefiles_io_error (cache, +"Attempt to sync backing fs superblock returned error %d" , + ret); +} + +/* +* Withdraw cache objects. +*/ +void cachefiles_withdraw_cache (struct cachefiles_cache *cache) +{ +struct fscache_cache *fscache = cache->cache; + +pr_info ("File cache on %s unregistering\n" , fscache->name); + +fscache_withdraw_cache (fscache); + +/* we now have to destroy all the active objects pertaining to this +* cache - which we do by passing them off to thread pool to be +* disposed of */ +// PLACEHOLDER: Withdraw objects +fscache_wait_for_objects (fscache); + +// PLACEHOLDER: Withdraw volume +cachefiles_sync_cache (cache); + cache->cache =NULL ; +fscache_relinquish_cache (fscache); +}fs/cachefiles/daemon.c +5
-3@@ -702,6 +702,7 @@ static int cachefiles_daemon_bind(struct cachefiles_cache *cache, char *args)pr_warn ("Cache is disabled for development\n" );return -ENOANO ;// Don't allow the cache to operate yet +//return cachefiles_add_cache(cache); }/* @@ -711,10 +712,11 @@ static void cachefiles_daemon_unbind(struct cachefiles_cache *cache) {_enter ("" ); -if (test_bit (CACHEFILES_READY , &cache->flags)) { -// PLACEHOLDER: Withdraw cache - } +if (test_bit (CACHEFILES_READY , &cache->flags)) +cachefiles_withdraw_cache (cache); +cachefiles_put_directory (cache->graveyard); +cachefiles_put_directory (cache->store);mntput (cache->mnt);kfree (cache->rootdirname);fs/cachefiles/interface.c new file +18
-0@@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* FS-Cache interface to CacheFiles +* +* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. +* Written by David Howells ([email protected]) +*/ + +#include <linux/slab.h> +#include <linux/mount.h> +#include <linux/xattr.h> +#include <linux/file.h> +#include <linux/falloc.h> +#include <trace/events/fscache.h> +#include "internal.h" + +const struct fscache_cache_ops cachefiles_cache_ops = { + .name ="cachefiles" , +};fs/cachefiles/internal.h +9
-0@@ -32,6 +32,8 @@ struct cachefiles_object {struct cachefiles_cache {struct fscache_cache *cache;/* Cache cookie */ struct vfsmount *mnt;/* mountpoint holding the cache */ +struct dentry *store;/* Directory into which live objects go */ +struct dentry *graveyard;/* directory into which dead objects go */ struct file *cachefilesd;/* manager daemon handle */ const struct cred *cache_cred;/* security override for accessing cache */ struct mutex daemon_mutex;/* command serialisation mutex */ @@ -78,8 +80,10 @@ static inline void cachefiles_state_changed(struct cachefiles_cache *cache)/* * cache.c */ +extern int cachefiles_add_cache (struct cachefiles_cache *cache);extern int cachefiles_has_space (struct cachefiles_cache *cache,unsigned fnr,unsigned bnr); +extern void cachefiles_withdraw_cache (struct cachefiles_cache *cache);/* * daemon.c @@ -125,6 +129,11 @@ static inline int cachefiles_inject_remove_error(void)return cachefiles_error_injection_state &2 ? -EIO :0 ; } +/* +* interface.c +*/ +extern const struct fscache_cache_ops cachefiles_cache_ops; +/* * namei.c */
Signature · 1 line
Jeff Layton <[email protected]>
David Howells
maintainerRe: [PATCH v4 40/68] cachefiles: Implement cache registration and withdrawal
Quoted 14 lines · Jeff Layton
fs/cachefiles/cache.c line 62
Quoted 11 lines
fs/cachefiles/cache.c +11
-0+/* check parameters */ + ret = -EOPNOTSUPP ; +if (d_is_negative (root) || + !d_backing_inode (root)->i_op->lookup || + !d_backing_inode (root)->i_op->mkdir || + !(d_backing_inode (root)->i_opflags &IOP_XATTR ) || + !root->d_sb->s_op->statfs || + !root->d_sb->s_op->sync_fs || + root->d_sb->s_blocksize >PAGE_SIZE ) + goto error_unsupported; +That's quite a collection of tests.
Quoted 3 lines
Most are obvious, but some comments explaining the need for others would not be a bad thing. In particular, why is
s_blocksize>PAGE_SIZEunsupported?
It can't do page-sized DIO requests to a filesystem with a block size larger than a page. In the future I can work around that in conjunction with netfslib by expanding read and write sizes.
Quoted 1 line
Also, should you vet whether the fs supports
i_op->tmpfilehere ?
That's a good idea.
David
David Howells
maintainerPATCH v4 41/68 cachefiles: Implement volume support
In reply to David HowellsImplement support for creating the directory layout for a volume on disk and setting up and withdrawing volume caching.
Each volume has a directory named for the volume key under the root of the cache (prefixed with an 'I' to indicate to cachefilesd that it's an index) and then creates a bunch of hash bucket subdirectories under that (named as '@' plus a hex number) in which cookie files will be created.
---
fs/cachefiles/Makefile | 3 + fs/cachefiles/cache.c | 28 ++++++++++- fs/cachefiles/daemon.c | 2 + fs/cachefiles/interface.c | 2 + fs/cachefiles/internal.h | 20 ++++++++ fs/cachefiles/volume.c | 118 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 fs/cachefiles/volume.c
fs/cachefiles/Makefile +2 -1
@@ -9,7 +9,8 @@ cachefiles-y := \ interface.o \ main.o \ namei.o \ - security.o + security.o \ + volume.o cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) += error_inject.o
fs/cachefiles/cache.c +27 -1
@@ -262,6 +262,32 @@ int cachefiles_has_space(struct cachefiles_cache *cache,return ret; } +/* +* Withdraw volumes. +*/ +static void cachefiles_withdraw_volumes (struct cachefiles_cache *cache) +{ +_enter ("" ); + +for (;;) { +struct cachefiles_volume *volume =NULL ; + +spin_lock (&cache->object_list_lock); +if (!list_empty (&cache->volumes)) { + volume =list_first_entry (&cache->volumes, + struct cachefiles_volume, cache_link); +list_del_init (&volume->cache_link); + } +spin_unlock (&cache->object_list_lock); +if (!volume) +break ; + +cachefiles_withdraw_volume (volume); + } + +_leave ("" ); +} +/* * Sync a cache to backing disk. */
@@ -303,7 +329,7 @@ void cachefiles_withdraw_cache(struct cachefiles_cache *cache)// PLACEHOLDER: Withdraw objects fscache_wait_for_objects (fscache); -// PLACEHOLDER: Withdraw volume +cachefiles_withdraw_volumes (cache);cachefiles_sync_cache (cache); cache->cache =NULL ;fscache_relinquish_cache (fscache);
fs/cachefiles/daemon.c +2 -0
@@ -105,6 +105,8 @@ static int cachefiles_daemon_open(struct inode *inode, struct file *file)mutex_init (&cache->daemon_mutex);init_waitqueue_head (&cache->daemon_pollwq); +INIT_LIST_HEAD (&cache->volumes); +spin_lock_init (&cache->object_list_lock);/* set default caching limits * - limit at 1% free space and/or free files
fs/cachefiles/interface.c +2 -0
@@ -15,4 +15,6 @@const struct fscache_cache_ops cachefiles_cache_ops = { .name ="cachefiles" , + .acquire_volume = cachefiles_acquire_volume, + .free_volume = cachefiles_free_volume, };
fs/cachefiles/internal.h +20 -0
@@ -19,6 +19,17 @@struct cachefiles_cache ;struct cachefiles_object ; +/* +* Cached volume representation. +*/ +struct cachefiles_volume { +struct cachefiles_cache *cache; +struct list_head cache_link;/* Link in cache->volumes */ +struct fscache_volume *vcookie;/* The netfs's representation */ +struct dentry *dentry;/* The volume dentry */ +struct dentry *fanout[256 ];/* Fanout subdirs */ +}; +/* * Data file records. */
@@ -35,6 +46,8 @@ struct cachefiles_cache {struct dentry *store;/* Directory into which live objects go */ struct dentry *graveyard;/* directory into which dead objects go */ struct file *cachefilesd;/* manager daemon handle */ +struct list_head volumes;/* List of volume objects */ +spinlock_t object_list_lock;/* Lock for volumes and object_list */ const struct cred *cache_cred;/* security override for accessing cache */ struct mutex daemon_mutex;/* command serialisation mutex */ wait_queue_head_t daemon_pollwq;/* poll waitqueue for daemon */
@@ -163,6 +176,13 @@ static inline void cachefiles_end_secure(struct cachefiles_cache *cache,revert_creds (saved_cred); } +/* +* volume.c +*/ +void cachefiles_acquire_volume (struct fscache_volume *volume); +void cachefiles_free_volume (struct fscache_volume *volume); +void cachefiles_withdraw_volume (struct cachefiles_volume *volume); +/* * Error handling */
fs/cachefiles/volume.c new file +118 -0
118 lines not on this page · raw message
David Howells
maintainerPATCH v4 42/68 cachefiles: Add tracepoints for calls to the VFS
In reply to David HowellsAdd tracepoints in cachefiles to monitor when it does various VFS operations, such as mkdir.
---
include/trace/events/cachefiles.h | 176 +++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+)
include/trace/events/cachefiles.h +176 -0
@@ -18,6 +18,21 @@#ifndef __CACHEFILES_DECLARE_TRACE_ENUMS_ONCE_ONLY#define __CACHEFILES_DECLARE_TRACE_ENUMS_ONCE_ONLY +enum fscache_why_object_killed { +FSCACHE_OBJECT_IS_STALE , +FSCACHE_OBJECT_IS_WEIRD , +FSCACHE_OBJECT_INVALIDATED , +FSCACHE_OBJECT_NO_SPACE , +FSCACHE_OBJECT_WAS_RETIRED , +FSCACHE_OBJECT_WAS_CULLED , +}; + +enum cachefiles_trunc_trace { + cachefiles_trunc_dio_adjust, + cachefiles_trunc_expand_tmpfile, + cachefiles_trunc_shrink, +}; +enum cachefiles_error_trace { cachefiles_trace_fallocate_error, cachefiles_trace_getxattr_error,
@@ -43,6 +58,19 @@ enum cachefiles_error_trace {/* * Define enum -> string mappings for display. */ +#define cachefiles_obj_kill_traces \ + EM(FSCACHE_OBJECT_IS_STALE, "stale") \ + EM(FSCACHE_OBJECT_IS_WEIRD, "weird") \ + EM(FSCACHE_OBJECT_INVALIDATED, "inval") \ + EM(FSCACHE_OBJECT_NO_SPACE, "no_space") \ + EM(FSCACHE_OBJECT_WAS_RETIRED, "was_retired") \ + E_(FSCACHE_OBJECT_WAS_CULLED, "was_culled") + +#define cachefiles_trunc_traces \ + EM(cachefiles_trunc_dio_adjust, "DIOADJ") \ + EM(cachefiles_trunc_expand_tmpfile, "EXPTMP") \ + E_(cachefiles_trunc_shrink, "SHRINK") +#define cachefiles_error_traces \ EM(cachefiles_trace_fallocate_error, "fallocate") \ EM(cachefiles_trace_getxattr_error, "getxattr") \
@@ -71,6 +99,8 @@ enum cachefiles_error_trace {#define EM (a, b) TRACE_DEFINE_ENUM(a);#define E_ (a, b) TRACE_DEFINE_ENUM(a); +cachefiles_obj_kill_traces; +cachefiles_trunc_traces; cachefiles_error_traces;/*
@@ -83,6 +113,152 @@ cachefiles_error_traces;#define E_ (a, b) { a, b } +TRACE_EVENT (cachefiles_lookup, + TP_PROTO(struct cachefiles_object *obj, + struct dentry *de), + +TP_ARGS (obj, de), + +TP_STRUCT__entry ( + __field(unsigned int, obj ) + __field(short, error ) + __field(unsigned long, ino ) + ), + +TP_fast_assign ( + __entry->obj = obj->debug_id; + __entry->ino = (!IS_ERR(de) && d_backing_inode(de) ? + d_backing_inode(de)->i_ino : 0); + __entry->error = IS_ERR(de) ? PTR_ERR(de) : 0; + ), + +TP_printk ("o=%08x i=%lx e=%d" , + __entry->obj, __entry->ino, __entry->error) + ); + +TRACE_EVENT (cachefiles_tmpfile, + TP_PROTO(struct cachefiles_object *obj, struct inode *backer), + +TP_ARGS (obj, backer), + +TP_STRUCT__entry ( + __field(unsigned int, obj ) + __field(unsigned int, backer ) + ), + +TP_fast_assign ( + __entry->obj = obj->debug_id; + __entry->backer = backer->i_ino; + ), + +TP_printk ("o=%08x b=%08x" , + __entry->obj, + __entry->backer) + ); + +TRACE_EVENT (cachefiles_link, + TP_PROTO(struct cachefiles_object *obj, struct inode *backer), + +TP_ARGS (obj, backer), + +TP_STRUCT__entry ( + __field(unsigned int, obj ) + __field(unsigned int, backer ) + ), + +TP_fast_assign ( + __entry->obj = obj->debug_id; + __entry->backer = backer->i_ino; + ), + +TP_printk ("o=%08x b=%08x" , + __entry->obj, + __entry->backer) + ); + +TRACE_EVENT (cachefiles_unlink, + TP_PROTO(struct cachefiles_object *obj, + struct dentry *de, + enum fscache_why_object_killed why), + +TP_ARGS (obj, de, why), + +/* Note that obj may be NULL */ +TP_STRUCT__entry ( + __field(unsigned int, obj ) + __field(struct dentry *, de ) + __field(enum fscache_why_object_killed, why ) + ), + +TP_fast_assign ( + __entry->obj = obj ? obj->debug_id : UINT_MAX; + __entry->de = de; + __entry->why = why; + ), + +TP_printk ("o=%08x d=%p w=%s" , + __entry->obj, __entry->de, +__print_symbolic (__entry->why, cachefiles_obj_kill_traces)) + ); + +TRACE_EVENT (cachefiles_rename, + TP_PROTO(struct cachefiles_object *obj, + struct dentry *de, + struct dentry *to, + enum fscache_why_object_killed why), + +TP_ARGS (obj, de, to, why), + +/* Note that obj may be NULL */ +TP_STRUCT__entry ( + __field(unsigned int, obj ) + __field(struct dentry *, de ) + __field(struct dentry *, to ) + __field(enum fscache_why_object_killed, why ) + ), + +TP_fast_assign ( + __entry->obj = obj ? obj->debug_id : UINT_MAX; + __entry->de = de; + __entry->to = to; + __entry->why = why; + ), + +TP_printk ("o=%08x d=%p t=%p w=%s" , + __entry->obj, __entry->de, __entry->to, +__print_symbolic (__entry->why, cachefiles_obj_kill_traces)) + ); + +TRACE_EVENT (cachefiles_trunc, + TP_PROTO(struct cachefiles_object *obj, struct inode *backer, + loff_t from, loff_t to, enum cachefiles_trunc_trace why), + +TP_ARGS (obj, backer, from, to, why), + +TP_STRUCT__entry ( + __field(unsigned int, obj ) + __field(unsigned int, backer ) + __field(enum cachefiles_trunc_trace, why ) + __field(loff_t, from ) +__field (loff_t, to ) + ), + +TP_fast_assign ( + __entry->obj = obj->debug_id; + __entry->backer = backer->i_ino; + __entry->from = from; + __entry->to = to; + __entry->why = why; + ), + +TP_printk ("o=%08x b=%08x %s l=%llx->%llx" , + __entry->obj, + __entry->backer, +__print_symbolic (__entry->why, cachefiles_trunc_traces), + __entry->from, + __entry->to) + ); + TRACE_EVENT(cachefiles_mark_active, TP_PROTO(struct cachefiles_object *obj, struct inode *inode),
David Howells
maintainerPATCH v4 43/68 cachefiles: Implement object lifecycle funcs
In reply to David HowellsImplement allocate, get, see and put functions for the cachefiles_object struct. The members of the struct we're going to need are also added.
Additionally, implement a lifecycle tracepoint.
---
fs/cachefiles/interface.c | 86 +++++++++++++++++++++++++++++++++++++ fs/cachefiles/internal.h | 35 ++++++++++++++- fs/cachefiles/main.c | 16 +++++++ include/trace/events/cachefiles.h | 58 +++++++++++++++++++++++++ include/trace/events/fscache.h | 4 ++ 5 files changed, 197 insertions(+), 2 deletions(-)
fs/cachefiles/interface.c +86 -0
@@ -13,6 +13,92 @@#include <trace/events/fscache.h> #include "internal.h" +static atomic_t cachefiles_object_debug_id; + +/* +* Allocate a cache object record. +*/ +static +struct cachefiles_object *cachefiles_alloc_object (struct fscache_cookie *cookie) +{ +struct fscache_volume *vcookie = cookie->volume; +struct cachefiles_volume *volume = vcookie->cache_priv; +struct cachefiles_object *object; + +_enter ("{%s},%x," , vcookie->key, cookie->debug_id); + + object =kmem_cache_zalloc (cachefiles_object_jar,GFP_KERNEL ); +if (!object) +return NULL ; + +refcount_set (&object->ref,1 ); + +spin_lock_init (&object->lock); +INIT_LIST_HEAD (&object->cache_link); + object->volume = volume; + object->debug_id =atomic_inc_return (&cachefiles_object_debug_id); + object->cookie =fscache_get_cookie (cookie, fscache_cookie_get_attach_object); + +fscache_count_object (vcookie->cache); +trace_cachefiles_ref (object->debug_id, cookie->debug_id,1 , + cachefiles_obj_new); +return object; +} + +/* +* Note that an object has been seen. +*/ +void cachefiles_see_object (struct cachefiles_object *object, +enum cachefiles_obj_ref_trace why) +{ +trace_cachefiles_ref (object->debug_id, object->cookie->debug_id, +refcount_read (&object->ref), why); +} + +/* +* Increment the usage count on an object; +*/ +struct cachefiles_object *cachefiles_grab_object (struct cachefiles_object *object, +enum cachefiles_obj_ref_trace why) +{ +int r; + +__refcount_inc (&object->ref, &r); +trace_cachefiles_ref (object->debug_id, object->cookie->debug_id, r, why); +return object; +} + +/* +* dispose of a reference to an object +*/ +void cachefiles_put_object (struct cachefiles_object *object, +enum cachefiles_obj_ref_trace why) +{ +unsigned int object_debug_id = object->debug_id; +unsigned int cookie_debug_id = object->cookie->debug_id; +struct fscache_cache *cache; +bool done; +int r; + + done =__refcount_dec_and_test (&object->ref, &r); +trace_cachefiles_ref (object_debug_id, cookie_debug_id, r, why); +if (done) { +_debug ("- kill object OBJ%x" , object_debug_id); + + ASSERTCMP(object->file, ==, NULL); + +kfree (object->d_name); + + cache = object->volume->cache->cache; +fscache_put_cookie (object->cookie, fscache_cookie_put_object); + object->cookie =NULL ; +kmem_cache_free (cachefiles_object_jar, object); +fscache_uncount_object (cache); + } + +_leave ("" ); +} +const struct fscache_cache_ops cachefiles_cache_ops = { .name ="cachefiles" , .acquire_volume = cachefiles_acquire_volume,
fs/cachefiles/internal.h +33 -2
@@ -19,6 +19,16 @@struct cachefiles_cache ;struct cachefiles_object ; +enum cachefiles_content { +/* These values are saved on disk */ +CACHEFILES_CONTENT_NO_DATA =0 ,/* No content stored */ +CACHEFILES_CONTENT_SINGLE =1 ,/* Content is monolithic, all is present */ +CACHEFILES_CONTENT_ALL =2 ,/* Content is all present, no map */ +CACHEFILES_CONTENT_BACKFS_MAP =3 ,/* Content is piecemeal, mapped through backing fs */ +CACHEFILES_CONTENT_DIRTY =4 ,/* Content is dirty (only seen on disk) */ + nr__cachefiles_content +}; +/* * Cached volume representation. */
@@ -31,10 +41,20 @@ struct cachefiles_volume { };/* -* Data file records. +* Backing file state. */ struct cachefiles_object { -int debug_id;/* debugging ID */ +struct fscache_cookie *cookie;/* Netfs data storage object cookie */ +struct cachefiles_volume *volume;/* Cache volume that holds this object */ +struct list_head cache_link;/* Link in cache->*_list */ +struct file *file;/* The file representing this object */ +char *d_name;/* Backing file name */ +int debug_id; +spinlock_t lock; +refcount_t ref; +u8 d_name_len;/* Length of filename */ +enum cachefiles_content content_info:8 ;/* Info about content presence */ +unsigned long flags; };/*
@@ -146,6 +166,17 @@ static inline int cachefiles_inject_remove_error(void)* interface.c */ extern const struct fscache_cache_ops cachefiles_cache_ops; +extern void cachefiles_see_object (struct cachefiles_object *object, +enum cachefiles_obj_ref_trace why); +extern struct cachefiles_object *cachefiles_grab_object (struct cachefiles_object *object, +enum cachefiles_obj_ref_trace why); +extern void cachefiles_put_object (struct cachefiles_object *object, +enum cachefiles_obj_ref_trace why); + +/* +* main.c +*/ +extern struct kmem_cache *cachefiles_object_jar;/* * namei.c
fs/cachefiles/main.c +16 -0
@@ -31,6 +31,8 @@ MODULE_DESCRIPTION("Mounted-filesystem based cache");MODULE_AUTHOR ("Red Hat, Inc." );MODULE_LICENSE ("GPL" ); +struct kmem_cache *cachefiles_object_jar; +static struct miscdevice cachefiles_dev = { .minor =MISC_DYNAMIC_MINOR , .name ="cachefiles" ,
@@ -51,9 +53,22 @@ static int __init cachefiles_init(void)if (ret <0 ) goto error_dev; +/* create an object jar */ + ret = -ENOMEM ; + cachefiles_object_jar = +kmem_cache_create ("cachefiles_object_jar" , +sizeof (struct cachefiles_object ), +0 ,SLAB_HWCACHE_ALIGN ,NULL ); +if (!cachefiles_object_jar) { +pr_notice ("Failed to allocate an object jar\n" ); + goto error_object_jar; + } +pr_info ("Loaded\n" );return 0 ; +error_object_jar: +misc_deregister (&cachefiles_dev); error_dev:cachefiles_unregister_error_injection (); error_einj:
@@ -70,6 +85,7 @@ static void __exit cachefiles_exit(void) {pr_info ("Unloading\n" ); +kmem_cache_destroy (cachefiles_object_jar);misc_deregister (&cachefiles_dev);cachefiles_unregister_error_injection (); }
include/trace/events/cachefiles.h +58 -0
82 lines not on this page · raw message
include/trace/events/fscache.h +4 -0
@@ -49,6 +49,7 @@ enum fscache_volume_trace {enum fscache_cookie_trace { fscache_cookie_collision, fscache_cookie_discard, + fscache_cookie_get_attach_object, fscache_cookie_get_end_access, fscache_cookie_get_hash_collision, fscache_cookie_get_inval_work,
@@ -57,6 +58,7 @@ enum fscache_cookie_trace { fscache_cookie_new_acquire, fscache_cookie_put_hash_collision, fscache_cookie_put_lru, + fscache_cookie_put_object, fscache_cookie_put_over_queued, fscache_cookie_put_relinquish, fscache_cookie_put_withdrawn,
@@ -122,6 +124,7 @@ enum fscache_access_trace {#define fscache_cookie_traces \ EM(fscache_cookie_collision, "*COLLIDE*") \ EM(fscache_cookie_discard, "DISCARD ") \ + EM(fscache_cookie_get_attach_object, "GET attch") \ EM(fscache_cookie_get_hash_collision, "GET hcoll") \ EM(fscache_cookie_get_end_access, "GQ endac") \ EM(fscache_cookie_get_inval_work, "GQ inval") \
@@ -130,6 +133,7 @@ enum fscache_access_trace { EM(fscache_cookie_new_acquire, "NEW acq ") \ EM(fscache_cookie_put_hash_collision, "PUT hcoll") \ EM(fscache_cookie_put_lru, "PUT lru ") \ + EM(fscache_cookie_put_object, "PUT obj ") \ EM(fscache_cookie_put_over_queued, "PQ overq") \ EM(fscache_cookie_put_relinquish, "PUT relnq") \EM (fscache_cookie_put_withdrawn,"PUT wthdn" ) \
David Howells
maintainerPATCH v4 44/68 cachefiles: Implement key to filename encoding
In reply to David HowellsImplement a function to encode a binary cookie key as something that can be used as a filename. Four options are considered:
(1) All printable chars with no '/' characters. Prepend a 'D' to indicate the encoding but otherwise use as-is.
(2) Appears to be an array of __be32. Encode as 'S' plus a list of hex-encoded 32-bit ints separated by commas. If a number is 0, it is rendered as "" instead of "0".
(3) Appears to be an array of __le32. Encoded as (2) but with a 'T' encoding prefix.
(4) Encoded as base64 with an 'E' prefix plus a second char indicating how much padding is involved. A non-standard base64 encoding is used because '/' cannot be used in the encoded form.
If (1) is not possible, whichever of (2), (3) or (4) produces the shortest string is selected (hex-encoding a number may be less dense than base64 encoding it).
Note that the prefix characters have to be selected from the set [DEIJST@] lest cachefilesd remove the files because it recognise the name.
Changes
=======
ver #2:
- Fix a short allocation that didn't allow for a string terminator[1]
---
fs/cachefiles/Makefile | 1 fs/cachefiles/internal.h | 5 ++ fs/cachefiles/key.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 fs/cachefiles/key.c
fs/cachefiles/Makefile +1 -0
@@ -7,6 +7,7 @@ cachefiles-y := \ cache.o \ daemon.o \ interface.o \ + key.o \ main.o \ namei.o \ security.o \
fs/cachefiles/internal.h +5 -0
@@ -173,6 +173,11 @@ extern struct cachefiles_object *cachefiles_grab_object(struct cachefiles_objectextern void cachefiles_put_object (struct cachefiles_object *object,enum cachefiles_obj_ref_trace why); +/* +* key.c +*/ +extern bool cachefiles_cook_key (struct cachefiles_object *object); +/* * main.c */
fs/cachefiles/key.c new file +138 -0
138 lines not on this page · raw message
Jeff Layton
maintainerRe: [PATCH v4 44/68] cachefiles: Implement key to filename encoding
Quoted 13 lines · David Howells
Implement a function to encode a binary cookie key as something that can be used as a filename. Four options are considered:
(1) All printable chars with no '/' characters. Prepend a 'D' to indicate the encoding but otherwise use as-is.
(2) Appears to be an array of
__be32. Encode as 'S' plus a list of hex-encoded 32-bit ints separated by commas. If a number is 0, it is rendered as "" instead of "0".(3) Appears to be an array of
__le32. Encoded as (2) but with a 'T' encoding prefix.
Quoted 8 lines
(4) Encoded as base64 with an 'E' prefix plus a second char indicating how much padding is involved. A non-standard base64 encoding is used because '/' cannot be used in the encoded form.
If (1) is not possible, whichever of (2), (3) or (4) produces the shortest string is selected (hex-encoding a number may be less dense than base64 encoding it).
Since most cookies are fairly small, is there any real benefit to optimizing for length here? How much inflation are we talking about?
Quoted 176 lines
Note that the prefix characters have to be selected from the set [DEIJST@] lest cachefilesd remove the files because it recognise the name.
Changes
=======
ver #2:
- Fix a short allocation that didn't allow for a string terminator[1]Signed-off-by David Howells <[email protected]>Link https://lore.kernel.org/r/163819640393.215744.15212364106412961104.stgit@warthog.procyon.org.uk/ # v1 open here---
fs/cachefiles/Makefile | 1 fs/cachefiles/internal.h | 5 ++ fs/cachefiles/key.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 fs/cachefiles/key.cfs/cachefiles/Makefile +1
-0@@ -7,6 +7,7 @@ cachefiles-y := \ cache.o \ daemon.o \ interface.o \ + key.o \ main.o \ namei.o \ security.o \fs/cachefiles/internal.h +5
-0@@ -173,6 +173,11 @@ extern struct cachefiles_object *cachefiles_grab_object(struct cachefiles_objectextern void cachefiles_put_object (struct cachefiles_object *object,enum cachefiles_obj_ref_trace why); +/* +* key.c +*/ +extern bool cachefiles_cook_key (struct cachefiles_object *object); +/* * main.c */ fs/cachefiles/key.c new file +120
-0@@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Key to pathname encoder +* +* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. +* Written by David Howells ([email protected]) +*/ + +#include <linux/slab.h> +#include "internal.h" + +static const char cachefiles_charmap[64 ] = +"0123456789" /* 0 - 9 */ +"abcdefghijklmnopqrstuvwxyz" /* 10 - 35 */ +"ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* 36 - 61 */ +"_-" /* 62 - 63 */ + ; + +static const char cachefiles_filecharmap[256 ] = { +/* we skip space and tab and control chars */ + [33 ...46 ] =1 ,/* '!' -> '.' */ +/* we skip '/' as it's significant to pathwalk */ + [48 ...127 ] =1 ,/* '0' -> '~' */ +}; + +static inline unsigned int how_many_hex_digits (unsigned int x) +{ +return x ?round_up (ilog2 (x) +1 ,4 ) /4 :0 ; +} + +/* +* turn the raw key into something cooked +* - the key may be up to NAME_MAX in length (including the length word) +* - "base64" encode the strange keys, mapping 3 bytes of raw to four of +* cooked +* - need to cut the cooked key into 252 char lengths (189 raw bytes) +*/ +bool cachefiles_cook_key (struct cachefiles_object *object) +{ +const u8 *key =fscache_get_key (object->cookie), *kend; +unsigned char ch; +unsigned int acc, i, n, nle, nbe, keylen = object->cookie->key_len; +unsigned int b64len, len, print, pad; +char *name, sep; + +_enter (",%u,%*phN" , keylen, keylen, key); + +BUG_ON (keylen >NAME_MAX -3 ); + + print =1 ; +for (i =0 ; i < keylen; i++) { + ch = key[i]; + print &= cachefiles_filecharmap[ch]; + } + +/* If the path is usable ASCII, then we render it directly */ +if (print) { + len =1 + keylen; + name =kmalloc (len +1 ,GFP_KERNEL ); +if (!name) +return false; + + name[0 ] ='D' ;/* Data object type, string encoding */ +memcpy (name +1 , key, keylen); + goto success; + } + +/* See if it makes sense to encode it as "hex,hex,hex" for each 32-bit +* chunk. We rely on the key having been padded out to a whole number +* of 32-bit words. +*/ + n =round_up (keylen,4 ); + nbe = nle =0 ; +for (i =0 ; i < n; i +=4 ) { +u32 be =be32_to_cpu (*(__be32 *)(key + i)); +u32 le =le32_to_cpu (*(__le32 *)(key + i)); + + nbe +=1 +how_many_hex_digits (be); + nle +=1 +how_many_hex_digits (le); + } + + b64len =DIV_ROUND_UP (keylen,3 ); + pad = b64len *3 - keylen; + b64len =2 + b64len *4 ;/* Length if we base64-encode it */ +_debug ("len=%u nbe=%u nle=%u b64=%u" , keylen, nbe, nle, b64len); +if (nbe < b64len || nle < b64len) { +unsigned int nlen =min (nbe, nle) +1 ; + name =kmalloc (nlen,GFP_KERNEL ); +if (!name) +return false; + sep = (nbe <= nle) ?'S' :'T' ;/* Encoding indicator */ + len =0 ; +for (i =0 ; i < n; i +=4 ) { +u32 x; +if (nbe <= nle) + x =be32_to_cpu (*(__be32 *)(key + i)); +else + x =le32_to_cpu (*(__le32 *)(key + i)); + name[len++] = sep; +if (x !=0 ) + len +=snprintf (name + len, nlen - len,"%x" , x); + sep =',' ; + } + goto success; + } + +/* We need to base64-encode it */ + name =kmalloc (b64len +1 ,GFP_KERNEL ); +if (!name) +return false; + + name[0 ] ='E' ; + name[1 ] ='0' + pad; + len =2 ; + kend = key + keylen; + do { + acc = *key++; + if (key < kend) { + acc |= *key++ << 8; + if (key < kend) + acc |= *key++ << 16;
fs/cachefiles/key.c line 130
Quoted 10 lines
fs/cachefiles/key.c new file +10
-0+ } + + name[len++] = cachefiles_charmap[acc &63 ]; + acc >>=6 ; + name[len++] = cachefiles_charmap[acc &63 ]; + acc >>=6 ; + name[len++] = cachefiles_charmap[acc &63 ]; + acc >>=6 ; + name[len++] = cachefiles_charmap[acc &63 ]; + }while (key < kend);
It might be good to eventually consolidate this code with the base64 scheme that fscrypt uses. Are they compatible? If so, then that can be done in a later merge.
Quoted 10 lines
fs/cachefiles/key.c new file +8
-0+ +success: + name[len] =0 ; + object->d_name = name; + object->d_name_len = len; +_leave (" = %s" , object->d_name); +return true; +}
Signature · 1 line
Jeff Layton <[email protected]>
David Howells
maintainerRe: [PATCH v4 44/68] cachefiles: Implement key to filename encoding
Quoted 2 lines · Jeff Layton
Since most cookies are fairly small, is there any real benefit to optimizing for length here? How much inflation are we talking about?
Taking AFS as an example, a vnode is represented at the file level by two
numbers: a 32-bit or 96-bit file ID and a 32-bit uniquifier. If it's a 96-bit file ID, a lot of the time, the upper 64-bits will be zero, so we're talking something like:
S421d4,1f07f34,,
instead of:
S000421d401f07f340000000000000000
or:
E0AAQh1AHwfzQAAAAAAAAAAA==
The first makes for a more readable name in the cache. The real fun is with NFS, where the name can be very long. For one that's just 5 words in length:
T81010001,1,20153e2,,a906194b
instead of:
T8101000100000001020153e200000000a906194b
or:
E0gQEAAQAAAAECAVPiAAAAAKkGGUs=
(The letter on the front represents the encoding scheme; in the base64 encoding the second digit indicates the amount of padding).
I don't know how much difference it makes to the backing filesystem's directory packing - and it may depend on the particular filesystem.
David
David Howells
maintainerPATCH v4 45/68 cachefiles: Implement metadata/coherency data storage in xattrs
In reply to David HowellsUse an xattr on each backing file in the cache to store some metadata, such as the content type and the coherency data.
Five content types are defined:
(0) No content stored.
(1) The file contains a single monolithic blob and must be all or nothing. This would be used for something like an AFS directory or a symlink.
(2) The file is populated with content completely up to a point with nothing beyond that.
(3) The file has a map attached and is sparsely populated. This would be stored in one or more additional xattrs.
(4) The file is dirty, being in the process of local modification and the contents are not necessarily represented correctly by the metadata. The file should be deleted if this is seen on binding.
---
fs/cachefiles/Makefile | 3 - fs/cachefiles/internal.h | 21 ++++ fs/cachefiles/xattr.c | 181 +++++++++++++++++++++++++++++++++++++ include/trace/events/cachefiles.h | 56 +++++++++++ 4 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/xattr.c
fs/cachefiles/Makefile +2 -1
@@ -11,7 +11,8 @@ cachefiles-y := \ main.o \ namei.o \ security.o \ - volume.o + volume.o \ + xattr.o cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) += error_inject.o
fs/cachefiles/internal.h +21 -0
@@ -55,6 +55,7 @@ struct cachefiles_object {u8 d_name_len;/* Length of filename */ enum cachefiles_content content_info:8; /* Info about content presence */unsigned long flags; +#define CACHEFILES_OBJECT_USING_TMPFILE 0/* Have an unlinked tmpfile */ };/*
@@ -219,6 +220,17 @@ void cachefiles_acquire_volume(struct fscache_volume *volume);void cachefiles_free_volume (struct fscache_volume *volume);void cachefiles_withdraw_volume (struct cachefiles_volume *volume); +/* +* xattr.c +*/ +extern int cachefiles_set_object_xattr (struct cachefiles_object *object); +extern int cachefiles_check_auxdata (struct cachefiles_object *object, +struct file *file); +extern int cachefiles_remove_object_xattr (struct cachefiles_cache *cache, +struct cachefiles_object *object, +struct dentry *dentry); +extern void cachefiles_prepare_to_write (struct fscache_cookie *cookie); +/* * Error handling */
@@ -229,6 +241,15 @@ do { \set_bit (CACHEFILES_DEAD , &(___cache)->flags); \ } while (0) +#define cachefiles_io_error_obj (object,FMT , ...) \ +do { \ + struct cachefiles_cache *___cache; \ + \ + ___cache = (object)->volume->cache; \ + cachefiles_io_error(___cache, FMT " [o=%08x]", ##__VA_ARGS__, \ + (object)->debug_id); \ +} while (0) +/* * Debug tracing
fs/cachefiles/xattr.c new file +181 -0
181 lines not on this page · raw message
include/trace/events/cachefiles.h +56 -0
80 lines not on this page · raw message
David Howells
maintainerPATCH v4 46/68 cachefiles: Mark a backing file in use with an inode flag
In reply to David HowellsUse an inode flag, S_KERNEL_FILE, to mark that a backing file is in use by the kernel to prevent cachefiles or other kernel services from interfering with that file.
Using S_SWAPFILE instead isn't really viable as that has other effects in the I/O paths.
---
fs/cachefiles/internal.h | 2 ++ fs/cachefiles/namei.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+)
fs/cachefiles/internal.h +2 -0
@@ -187,6 +187,8 @@ extern struct kmem_cache *cachefiles_object_jar;/* * namei.c */ +extern void cachefiles_unmark_inode_in_use (struct cachefiles_object *object, +struct file *file); extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, struct dentry *dir, const char *name,
fs/cachefiles/namei.c +35 -0
@@ -31,6 +31,18 @@ static bool __cachefiles_mark_inode_in_use(struct cachefiles_object *object,return can_use; } +static bool cachefiles_mark_inode_in_use (struct cachefiles_object *object, +struct dentry *dentry) +{ +struct inode *inode =d_backing_inode (dentry); +bool can_use; + +inode_lock (inode); + can_use =__cachefiles_mark_inode_in_use (object, dentry); +inode_unlock (inode); +return can_use; +} +/* * Unmark a backing inode. The caller must hold the inode lock. */
@@ -43,6 +55,29 @@ static void __cachefiles_unmark_inode_in_use(struct cachefiles_object *object,trace_cachefiles_mark_inactive (object, inode); } +/* +* Unmark a backing inode and tell cachefilesd that there's something that can +* be culled. +*/ +void cachefiles_unmark_inode_in_use (struct cachefiles_object *object, +struct file *file) +{ +struct cachefiles_cache *cache = object->volume->cache; +struct inode *inode =file_inode (file); + +if (inode) { +inode_lock (inode); +__cachefiles_unmark_inode_in_use (object, file->f_path.dentry); +inode_unlock (inode); + +if (!test_bit (CACHEFILES_OBJECT_USING_TMPFILE , &object->flags)) { +atomic_long_add (inode->i_blocks, &cache->b_released); +if (atomic_inc_return (&cache->f_released)) +cachefiles_state_changed (cache); + } + } +} +/* * get a subdirectory */
Jeff Layton
maintainerRe: [PATCH v4 46/68] cachefiles: Mark a backing file in use with an inode flag
Quoted 76 lines · David Howells
Use an inode flag,
S_KERNEL_FILE, to mark that a backing file is in use by the kernel to prevent cachefiles or other kernel services from interfering with that file.Using
S_SWAPFILEinstead isn't really viable as that has other effects in the I/O paths.Signed-off-by David Howells <[email protected]>Link https://lore.kernel.org/r/163819642273.215744.6414248677118690672.stgit@warthog.procyon.org.uk/ # v1 open here---
fs/cachefiles/internal.h | 2 ++ fs/cachefiles/namei.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+)fs/cachefiles/internal.h +2
-0@@ -187,6 +187,8 @@ extern struct kmem_cache *cachefiles_object_jar;/* * namei.c */ +extern void cachefiles_unmark_inode_in_use (struct cachefiles_object *object, +struct file *file); extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, struct dentry *dir, const char *name,fs/cachefiles/namei.c +28
-0@@ -31,6 +31,18 @@ static bool __cachefiles_mark_inode_in_use(struct cachefiles_object *object,return can_use; } +static bool cachefiles_mark_inode_in_use (struct cachefiles_object *object, +struct dentry *dentry) +{ +struct inode *inode =d_backing_inode (dentry); +bool can_use; + +inode_lock (inode); + can_use =__cachefiles_mark_inode_in_use (object, dentry); +inode_unlock (inode); +return can_use; +} +/* * Unmark a backing inode. The caller must hold the inode lock. */ @@ -43,6 +55,29 @@ static void __cachefiles_unmark_inode_in_use(struct cachefiles_object *object,trace_cachefiles_mark_inactive (object, inode); } +/* +* Unmark a backing inode and tell cachefilesd that there's something that can +* be culled. +*/ +void cachefiles_unmark_inode_in_use (struct cachefiles_object *object, +struct file *file) +{ +struct cachefiles_cache *cache = object->volume->cache; +struct inode *inode =file_inode (file); + +if (inode) { +inode_lock (inode); +__cachefiles_unmark_inode_in_use (object, file->f_path.dentry); +inode_unlock (inode); + +if (!test_bit (CACHEFILES_OBJECT_USING_TMPFILE , &object->flags)) {
fs/cachefiles/namei.c static void __cachefiles_unmark_inode_in_use(struct cachefiles_object *object, · line 83
Quoted 10 lines
fs/cachefiles/namei.c +7
-0+atomic_long_add (inode->i_blocks, &cache->b_released); +if (atomic_inc_return (&cache->f_released)) +cachefiles_state_changed (cache); + } + } +} +/* * get a subdirectory */
Probably, this patch should be merged with #38. The commit logs are the same, and they are at least somewhat related.
Signature · 1 line
Jeff Layton <[email protected]>
David Howells
maintainerRe: [PATCH v4 46/68] cachefiles: Mark a backing file in use with an inode flag
Quoted 2 lines · Jeff Layton
Probably, this patch should be merged with #38. The commit logs are the same, and they are at least somewhat related.
That's not so simple, unfortunately. Patch 46 requires bits of patches 43 and 45 in addition to patch 38 and patch 39 depends on patch 38.
David
David Howells
maintainerPATCH v4 47/68 cachefiles: Implement culling daemon commands
In reply to David HowellsImplement the ability for the userspace daemon to try and cull a file or directory in the cache. Two daemon commands are implemented:
(1) The "inuse" command. This queries if a file is in use or whether it can be deleted. It checks the S_KERNEL_FILE flag on the inode referred to by the specified filename.
(2) The "cull" command. This asks for a file or directory to be removed, where removal means either unlinking it or moving it to the graveyard directory for userspace to dismantle.
Changes
=======
ver #2:
- Fix logging of wrong error[1].
- Need to unmark an inode we've moved to the graveyard before unlocking.
---
fs/cachefiles/daemon.c | 4 - fs/cachefiles/internal.h | 11 ++ fs/cachefiles/namei.c | 307 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 320 insertions(+), 2 deletions(-)
fs/cachefiles/daemon.c +2 -2
@@ -574,7 +574,7 @@ static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args) goto notdir;cachefiles_begin_secure (cache, &saved_cred); - ret = -ENOANO ;// PLACEHOLDER: Do culling + ret =cachefiles_cull (cache, path.dentry, args);cachefiles_end_secure (cache, saved_cred);path_put (&path);
@@ -645,7 +645,7 @@ static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args) goto notdir;cachefiles_begin_secure (cache, &saved_cred); - ret = -ENOANO ;// PLACEHOLDER: Check if in use + ret =cachefiles_check_in_use (cache, path.dentry, args);cachefiles_end_secure (cache, saved_cred);path_put (&path);
fs/cachefiles/internal.h +11 -0
@@ -189,12 +189,23 @@ extern struct kmem_cache *cachefiles_object_jar;*/ extern void cachefiles_unmark_inode_in_use (struct cachefiles_object *object,struct file *file); +extern int cachefiles_bury_object (struct cachefiles_cache *cache, +struct cachefiles_object *object, +struct dentry *dir, +struct dentry *rep, +enum fscache_why_object_killed why);extern struct dentry *cachefiles_get_directory (struct cachefiles_cache *cache,struct dentry *dir,const char *name,bool *_is_new);extern void cachefiles_put_directory (struct dentry *dir); +extern int cachefiles_cull (struct cachefiles_cache *cache,struct dentry *dir, +char *filename); + +extern int cachefiles_check_in_use (struct cachefiles_cache *cache, +struct dentry *dir,char *filename); +/* * security.c */
fs/cachefiles/namei.c +307 -0
310 lines not on this page · raw message
David Howells
maintainerPATCH v4 48/68 cachefiles: Implement backing file wrangling
In reply to David HowellsImplement the wrangling of backing files, including the following pieces:
(1) Lookup and creation of a file on disk, using a tmpfile if the file isn't yet present. The file is then opened, sized for DIO and the file handle is attached to the cachefiles_object struct. The inode is marked to indicate that it's in use by a kernel service.
(2) Invalidation of an object, creating a tmpfile and switching the file pointer in the cachefiles object.
(3) Committing a file to disk, including setting the coherency xattr on it and, if necessary, creating a hard link to it.
Note that this would be a good place to use Omar Sandoval's vfs_link() with AT_LINK_REPLACE[1] as I may have to unlink an old file before I can link a tmpfile into place.
(4) Withdrawal of open objects when a cache is being withdrawn or a cookie is relinquished. This involves committing or discarding the file.
Changes
=======
ver #2:
- Fix logging of wrong error[1].
---
fs/cachefiles/cache.c | 32 ++++- fs/cachefiles/daemon.c | 1 fs/cachefiles/interface.c | 260 +++++++++++++++++++++++++++++++++++++ fs/cachefiles/internal.h | 9 + fs/cachefiles/namei.c | 318 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 619 insertions(+), 1 deletion(-)
fs/cachefiles/cache.c +31 -1
@@ -262,6 +262,36 @@ int cachefiles_has_space(struct cachefiles_cache *cache,return ret; } +/* +* Mark all the objects as being out of service and queue them all for cleanup. +*/ +static void cachefiles_withdraw_objects (struct cachefiles_cache *cache) +{ +struct cachefiles_object *object; +unsigned int count =0 ; + +_enter ("" ); + +spin_lock (&cache->object_list_lock); + +while (!list_empty (&cache->object_list)) { + object =list_first_entry (&cache->object_list, + struct cachefiles_object, cache_link); +cachefiles_see_object (object, cachefiles_obj_see_withdrawal); +list_del_init (&object->cache_link); +fscache_withdraw_cookie (object->cookie); + count++; +if ((count &63 ) ==0 ) { +spin_unlock (&cache->object_list_lock); +cond_resched (); +spin_lock (&cache->object_list_lock); + } + } + +spin_unlock (&cache->object_list_lock); +_leave (" [%u objs]" , count); +} +/* * Withdraw volumes. */
@@ -326,7 +356,7 @@ void cachefiles_withdraw_cache(struct cachefiles_cache *cache)/* we now have to destroy all the active objects pertaining to this * cache - which we do by passing them off to thread pool to be * disposed of */ -// PLACEHOLDER: Withdraw objects +cachefiles_withdraw_objects (cache);fscache_wait_for_objects (fscache);cachefiles_withdraw_volumes (cache);
fs/cachefiles/daemon.c +1 -0
@@ -106,6 +106,7 @@ static int cachefiles_daemon_open(struct inode *inode, struct file *file)mutex_init (&cache->daemon_mutex);init_waitqueue_head (&cache->daemon_pollwq);INIT_LIST_HEAD (&cache->volumes); +INIT_LIST_HEAD (&cache->object_list);spin_lock_init (&cache->object_list_lock);/* set default caching limits
fs/cachefiles/interface.c +260 -0
268 lines not on this page · raw message
fs/cachefiles/internal.h +9 -0
@@ -16,6 +16,8 @@#include <linux/cred.h> #include <linux/security.h> +#define CACHEFILES_DIO_BLOCK_SIZE 4096 +struct cachefiles_cache ;struct cachefiles_object ;
@@ -68,6 +70,7 @@ struct cachefiles_cache {struct dentry *graveyard;/* directory into which dead objects go */ struct file *cachefilesd;/* manager daemon handle */ struct list_head volumes;/* List of volume objects */ +struct list_head object_list;/* List of active objects */ spinlock_t object_list_lock;/* Lock for volumes and object_list */ const struct cred *cache_cred;/* security override for accessing cache */ struct mutex daemon_mutex;/* command serialisation mutex */
@@ -194,6 +197,9 @@ extern int cachefiles_bury_object(struct cachefiles_cache *cache,struct dentry *dir, struct dentry *rep, enum fscache_why_object_killed why); +extern int cachefiles_delete_object (struct cachefiles_object *object, +enum fscache_why_object_killed why); +extern bool cachefiles_look_up_object (struct cachefiles_object *object); extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, struct dentry *dir, const char *name,
@@ -205,6 +211,9 @@ extern int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir,extern int cachefiles_check_in_use (struct cachefiles_cache *cache,struct dentry *dir,char *filename); +extern struct file *cachefiles_create_tmpfile (struct cachefiles_object *object); +extern bool cachefiles_commit_tmpfile (struct cachefiles_cache *cache, +struct cachefiles_object *object);/* * security.c
fs/cachefiles/namei.c +318 -0
324 lines not on this page · raw message
David Howells
maintainerPATCH v4 49/68 cachefiles: Implement begin and end I/O operation
In reply to David HowellsImplement the methods for beginning and ending an I/O operation.
When called to begin an I/O operation, we are guaranteed that the cookie has reached a certain stage (we're called by fscache after it has done a suitable wait).
If a file is available, we paste a ref over into the cache resources for the I/O routines to use. This means that the object can be invalidated whilst the I/O is ongoing without the need to synchronise as the file pointer in the object is replaced, but the file pointer in the cache resources is unaffected.
Ending the operation just requires ditching any refs we have and dropping the access guarantee that fscache got for us on the cookie.
---
fs/cachefiles/Makefile | 1 + fs/cachefiles/interface.c | 1 + fs/cachefiles/internal.h | 18 +++++++++++++ fs/cachefiles/io.c | 57 ++++++++++++++++++++++++++++++++++++++++ include/trace/events/fscache.h | 2 + 5 files changed, 79 insertions(+) create mode 100644 fs/cachefiles/io.c
fs/cachefiles/Makefile +1 -0
@@ -7,6 +7,7 @@ cachefiles-y := \ cache.o \ daemon.o \ interface.o \ + io.o \ key.o \ main.o \ namei.o \
fs/cachefiles/interface.c +1 -0
@@ -362,5 +362,6 @@ const struct fscache_cache_ops cachefiles_cache_ops = { .lookup_cookie = cachefiles_lookup_cookie, .withdraw_cookie = cachefiles_withdraw_cookie, .invalidate_cookie = cachefiles_invalidate_cookie, + .begin_operation = cachefiles_begin_operation, .prepare_to_write = cachefiles_prepare_to_write, };
fs/cachefiles/internal.h +18 -0
@@ -105,6 +105,18 @@ struct cachefiles_cache {#include <trace/events/cachefiles.h> +static inline +struct file *cachefiles_cres_file (struct netfs_cache_resources *cres) +{ +return cres->cache_priv2; +} + +static inline +struct cachefiles_object *cachefiles_cres_object (struct netfs_cache_resources *cres) +{ +return fscache_cres_cookie (cres)->cache_priv; +} +/* * note change of state for daemon */
@@ -177,6 +189,12 @@ extern struct cachefiles_object *cachefiles_grab_object(struct cachefiles_objectextern void cachefiles_put_object (struct cachefiles_object *object,enum cachefiles_obj_ref_trace why); +/* +* io.c +*/ +extern bool cachefiles_begin_operation (struct netfs_cache_resources *cres, +enum fscache_want_state want_state); +/* * key.c */
fs/cachefiles/io.c new file +57 -0
57 lines not on this page · raw message
include/trace/events/fscache.h +2 -0
@@ -78,6 +78,7 @@ enum fscache_access_trace { fscache_access_cache_unpin, fscache_access_invalidate_cookie, fscache_access_invalidate_cookie_end, + fscache_access_io_end, fscache_access_io_not_live, fscache_access_io_read, fscache_access_io_resize,
@@ -152,6 +153,7 @@ enum fscache_access_trace { EM(fscache_access_cache_unpin, "UNPIN cache ") \ EM(fscache_access_invalidate_cookie, "BEGIN inval ") \ EM(fscache_access_invalidate_cookie_end,"END inval ") \ + EM(fscache_access_io_end, "END io ") \ EM(fscache_access_io_not_live, "END io_notl") \ EM(fscache_access_io_read, "BEGIN io_read") \EM (fscache_access_io_resize,"BEGIN io_resz" ) \
David Howells
maintainerPATCH v4 50/68 cachefiles: Implement cookie resize for truncate
In reply to David HowellsImplement resizing an object, using truncate and/or fallocate to adjust the object.
---
fs/cachefiles/interface.c | 78 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+)
fs/cachefiles/interface.c +78 -0
@@ -220,6 +220,83 @@ static bool cachefiles_lookup_cookie(struct fscache_cookie *cookie)return false; } +/* +* Shorten the backing object to discard any dirty data and free up +* any unused granules. +*/ +static bool cachefiles_shorten_object (struct cachefiles_object *object, +struct file *file,loff_t new_size) +{ +struct cachefiles_cache *cache = object->volume->cache; +struct inode *inode =file_inode (file); +loff_t i_size, dio_size; +int ret; + + dio_size =round_up (new_size,CACHEFILES_DIO_BLOCK_SIZE ); + i_size =i_size_read (inode); + +trace_cachefiles_trunc (object, inode, i_size, dio_size, + cachefiles_trunc_shrink); + ret =cachefiles_inject_remove_error (); +if (ret ==0 ) + ret =vfs_truncate (&file->f_path, dio_size); +if (ret <0 ) { +trace_cachefiles_io_error (object,file_inode (file), ret, + cachefiles_trace_trunc_error); +cachefiles_io_error_obj (object,"Trunc-to-size failed %d" , ret); +cachefiles_remove_object_xattr (cache, object, file->f_path.dentry); +return false; + } + +if (new_size < dio_size) { +trace_cachefiles_trunc (object, inode, dio_size, new_size, + cachefiles_trunc_dio_adjust); + ret =cachefiles_inject_write_error (); +if (ret ==0 ) + ret =vfs_fallocate (file,FALLOC_FL_ZERO_RANGE , + new_size, dio_size); +if (ret <0 ) { +trace_cachefiles_io_error (object,file_inode (file), ret, + cachefiles_trace_fallocate_error); +cachefiles_io_error_obj (object,"Trunc-to-dio-size failed %d" , ret); +cachefiles_remove_object_xattr (cache, object, file->f_path.dentry); +return false; + } + } + +return true; +} + +/* +* Resize the backing object. +*/ +static void cachefiles_resize_cookie (struct netfs_cache_resources *cres, +loff_t new_size) +{ +struct cachefiles_object *object =cachefiles_cres_object (cres); +struct cachefiles_cache *cache = object->volume->cache; +struct fscache_cookie *cookie = object->cookie; +const struct cred *saved_cred; +struct file *file =cachefiles_cres_file (cres); +loff_t old_size = cookie->object_size; + +_enter ("%llu->%llu" , old_size, new_size); + +if (new_size < old_size) { +cachefiles_begin_secure (cache, &saved_cred); +cachefiles_shorten_object (object, file, new_size); +cachefiles_end_secure (cache, saved_cred); + object->cookie->object_size = new_size; +return ; + } + +/* The file is being expanded. We don't need to do anything +* particularly. cookie->initial_size doesn't change and so the point +* at which we have to download before doesn't change. +*/ + cookie->object_size = new_size; +} +/* * Commit changes to the object as we drop it. */
@@ -363,5 +440,6 @@ const struct fscache_cache_ops cachefiles_cache_ops = { .withdraw_cookie = cachefiles_withdraw_cookie, .invalidate_cookie = cachefiles_invalidate_cookie, .begin_operation = cachefiles_begin_operation, + .resize_cookie = cachefiles_resize_cookie, .prepare_to_write = cachefiles_prepare_to_write, };
David Howells
maintainerPATCH v4 51/68 cachefiles: Implement the I/O routines
In reply to David HowellsImplement the I/O routines for cachefiles. There are two sets of routines
here: preparation and actual I/O.
Preparation for read involves looking to see whether there is data present, and how much. Netfslib tells us what it wants us to do and we have the option of adjusting shrinking and telling it whether to read from the cache, download from the server or simply clear a region.
Preparation for write involves checking for space and defending against possibly running short of space, if necessary punching out a hole in the file so that we don't leave old data in the cache if we update the coherency information.
Then there's a read routine and a write routine. They wait for the cookie state to move to something appropriate and then start a potentially asynchronous direct I/O operation upon it.
Changes
=======
ver #2:
- Fix a misassigned variable[1].
---
fs/cachefiles/io.c | 514 +++++++++++++++++++++++++++++++++++++ include/trace/events/cachefiles.h | 121 +++++++++ 2 files changed, 635 insertions(+)
fs/cachefiles/io.c +514 -0
@@ -14,6 +14,516 @@#include <trace/events/fscache.h> #include "internal.h" +struct cachefiles_kiocb { +struct kiocb iocb; +refcount_t ki_refcnt; +loff_t start; +union { +size_t skipped; +size_t len; + }; +struct cachefiles_object *object; +netfs_io_terminated_t term_func; +void *term_func_priv; +bool was_async; +unsigned int inval_counter;/* Copy of cookie->inval_counter */ +u64 b_writing; +}; + +static inline void cachefiles_put_kiocb (struct cachefiles_kiocb *ki) +{ +if (refcount_dec_and_test (&ki->ki_refcnt)) { +cachefiles_put_object (ki->object, cachefiles_obj_put_ioreq); +fput (ki->iocb.ki_filp); +kfree (ki); + } +} + +/* +* Handle completion of a read from the cache. +*/ +static void cachefiles_read_complete (struct kiocb *iocb,long ret) +{ + struct cachefiles_kiocb *ki = container_of(iocb, struct cachefiles_kiocb, iocb); +struct inode *inode =file_inode (ki->iocb.ki_filp); + +_enter ("%ld" , ret); + +if (ret <0 ) +trace_cachefiles_io_error (ki->object, inode, ret, + cachefiles_trace_read_error); + +if (ki->term_func) { +if (ret >=0 ) { +if (ki->object->cookie->inval_counter == ki->inval_counter) + ki->skipped += ret; +else + ret = -ESTALE ; + } + + ki->term_func (ki->term_func_priv, ret, ki->was_async); + } + +cachefiles_put_kiocb (ki); +} + +/* +* Initiate a read from the cache. +*/ +static int cachefiles_read (struct netfs_cache_resources *cres, +loff_t start_pos, +struct iov_iter *iter, +enum netfs_read_from_hole read_hole, +netfs_io_terminated_t term_func, +void *term_func_priv) +{ +struct cachefiles_object *object; +struct cachefiles_kiocb *ki; +struct file *file; +unsigned int old_nofs; +ssize_t ret = -ENOBUFS ; +size_t len =iov_iter_count (iter), skipped =0 ; + +if (!fscache_wait_for_operation (cres,FSCACHE_WANT_READ )) + goto presubmission_error; + +fscache_count_read (); + object =cachefiles_cres_object (cres); + file =cachefiles_cres_file (cres); + +_enter ("%pD,%li,%llx,%zx/%llx" , + file,file_inode (file)->i_ino, start_pos, len, +i_size_read (file_inode (file))); + +/* If the caller asked us to seek for data before doing the read, then +* we should do that now. If we find a gap, we fill it with zeros. +*/ +if (read_hole !=NETFS_READ_HOLE_IGNORE ) { +loff_t off = start_pos, off2; + + off2 =cachefiles_inject_read_error (); +if (off2 ==0 ) + off2 =vfs_llseek (file, off,SEEK_DATA ); +if (off2 <0 && off2 >= (loff_t)-MAX_ERRNO && off2 != -ENXIO ) { + skipped =0 ; + ret = off2; + goto presubmission_error; + } + +if (off2 == -ENXIO || off2 >= start_pos + len) { +/* The region is beyond the EOF or there's no more data +* in the region, so clear the rest of the buffer and +* return success. +*/ + ret = -ENODATA ; +if (read_hole ==NETFS_READ_HOLE_FAIL ) + goto presubmission_error; + +iov_iter_zero (len, iter); + skipped = len; + ret =0 ; + goto presubmission_error; + } + + skipped = off2 - off; +iov_iter_zero (skipped, iter); + } + + ret = -ENOMEM ; + ki =kzalloc (sizeof (struct cachefiles_kiocb ),GFP_KERNEL ); +if (!ki) + goto presubmission_error; + +refcount_set (&ki->ki_refcnt,2 ); + ki->iocb.ki_filp = file; + ki->iocb.ki_pos = start_pos + skipped; + ki->iocb.ki_flags =IOCB_DIRECT ; + ki->iocb.ki_hint =ki_hint_validate (file_write_hint (file)); + ki->iocb.ki_ioprio =get_current_ioprio (); + ki->skipped = skipped; + ki->object = object; + ki->inval_counter = cres->inval_counter; + ki->term_func = term_func; + ki->term_func_priv = term_func_priv; + ki->was_async = true; + +if (ki->term_func) + ki->iocb.ki_complete = cachefiles_read_complete; + +get_file (ki->iocb.ki_filp); +cachefiles_grab_object (object, cachefiles_obj_get_ioreq); + +trace_cachefiles_read (object,file_inode (file), ki->iocb.ki_pos, len - skipped); + old_nofs =memalloc_nofs_save (); + ret =cachefiles_inject_read_error (); +if (ret ==0 ) + ret =vfs_iocb_iter_read (file, &ki->iocb, iter); +memalloc_nofs_restore (old_nofs); +switch (ret) { +case -EIOCBQUEUED : + goto in_progress; + +case -ERESTARTSYS : +case -ERESTARTNOINTR : +case -ERESTARTNOHAND : +case -ERESTART_RESTARTBLOCK : +/* There's no easy way to restart the syscall since other AIO's +* may be already running. Just fail this IO with EINTR. +*/ + ret = -EINTR ; + fallthrough; +default : + ki->was_async = false; +cachefiles_read_complete (&ki->iocb, ret); +if (ret >0 ) + ret =0 ; +break ; + } + +in_progress: +cachefiles_put_kiocb (ki); +_leave (" = %zd" , ret); +return ret; + +presubmission_error: +if (term_func) +term_func (term_func_priv, ret <0 ? ret : skipped, false); +return ret; +} + +/* +* Handle completion of a write to the cache. +*/ +static void cachefiles_write_complete (struct kiocb *iocb,long ret) +{ + struct cachefiles_kiocb *ki = container_of(iocb, struct cachefiles_kiocb, iocb); +struct cachefiles_object *object = ki->object; +struct inode *inode =file_inode (ki->iocb.ki_filp); + +_enter ("%ld" , ret); + +/* Tell lockdep we inherited freeze protection from submission thread */ +__sb_writers_acquired (inode->i_sb,SB_FREEZE_WRITE ); +__sb_end_write (inode->i_sb,SB_FREEZE_WRITE ); + +if (ret <0 ) +trace_cachefiles_io_error (object, inode, ret, + cachefiles_trace_write_error); + +atomic_long_sub (ki->b_writing, &object->volume->cache->b_writing); +set_bit (FSCACHE_COOKIE_HAVE_DATA , &object->cookie->flags); +if (ki->term_func) + ki->term_func (ki->term_func_priv, ret, ki->was_async); +cachefiles_put_kiocb (ki); +} + +/* +* Initiate a write to the cache. +*/ +static int cachefiles_write (struct netfs_cache_resources *cres, +loff_t start_pos, +struct iov_iter *iter, +netfs_io_terminated_t term_func, +void *term_func_priv) +{ +struct cachefiles_object *object; +struct cachefiles_cache *cache; +struct cachefiles_kiocb *ki; +struct inode *inode; +struct file *file; +unsigned int old_nofs; +ssize_t ret = -ENOBUFS ; +size_t len =iov_iter_count (iter); + +if (!fscache_wait_for_operation (cres,FSCACHE_WANT_WRITE )) + goto presubmission_error; +fscache_count_write (); + object =cachefiles_cres_object (cres); + cache = object->volume->cache; + file =cachefiles_cres_file (cres); + +_enter ("%pD,%li,%llx,%zx/%llx" , + file,file_inode (file)->i_ino, start_pos, len, +i_size_read (file_inode (file))); + + ret = -ENOMEM ; + ki =kzalloc (sizeof (struct cachefiles_kiocb ),GFP_KERNEL ); +if (!ki) + goto presubmission_error; + +refcount_set (&ki->ki_refcnt,2 ); + ki->iocb.ki_filp = file; + ki->iocb.ki_pos = start_pos; + ki->iocb.ki_flags =IOCB_DIRECT |IOCB_WRITE ; + ki->iocb.ki_hint =ki_hint_validate (file_write_hint (file)); + ki->iocb.ki_ioprio =get_current_ioprio (); + ki->object = object; + ki->inval_counter = cres->inval_counter; + ki->start = start_pos; + ki->len = len; + ki->term_func = term_func; + ki->term_func_priv = term_func_priv; + ki->was_async = true; + ki->b_writing = (len + (1 << cache->bshift)) >> cache->bshift; + +if (ki->term_func) + ki->iocb.ki_complete = cachefiles_write_complete; +atomic_long_add (ki->b_writing, &cache->b_writing); + +/* Open-code file_start_write here to grab freeze protection, which +* will be released by another thread in aio_complete_rw(). Fool +* lockdep by telling it the lock got released so that it doesn't +* complain about the held lock when we return to userspace. +*/ + inode =file_inode (file); +__sb_start_write (inode->i_sb,SB_FREEZE_WRITE ); +__sb_writers_release (inode->i_sb,SB_FREEZE_WRITE ); + +get_file (ki->iocb.ki_filp); +cachefiles_grab_object (object, cachefiles_obj_get_ioreq); + +trace_cachefiles_write (object, inode, ki->iocb.ki_pos, len); + old_nofs =memalloc_nofs_save (); + ret =cachefiles_inject_write_error (); +if (ret ==0 ) + ret =vfs_iocb_iter_write (file, &ki->iocb, iter); +memalloc_nofs_restore (old_nofs); +switch (ret) { +case -EIOCBQUEUED : + goto in_progress; + +case -ERESTARTSYS : +case -ERESTARTNOINTR : +case -ERESTARTNOHAND : +case -ERESTART_RESTARTBLOCK : +/* There's no easy way to restart the syscall since other AIO's +* may be already running. Just fail this IO with EINTR. +*/ + ret = -EINTR ; + fallthrough; +default : + ki->was_async = false; +cachefiles_write_complete (&ki->iocb, ret); +if (ret >0 ) + ret =0 ; +break ; + } + +in_progress: +cachefiles_put_kiocb (ki); +_leave (" = %zd" , ret); +return ret; + +presubmission_error: +if (term_func) +term_func (term_func_priv, ret, false); +return ret; +} + +/* +* Prepare a read operation, shortening it to a cached/uncached +* boundary as appropriate. +*/ +static enum netfs_read_source cachefiles_prepare_read (struct netfs_read_subrequest *subreq, +loff_t i_size) +{ +enum cachefiles_prepare_read_trace why; +struct netfs_read_request *rreq = subreq->rreq; +struct netfs_cache_resources *cres = &rreq->cache_resources; +struct cachefiles_object *object; +struct cachefiles_cache *cache; +struct fscache_cookie *cookie =fscache_cres_cookie (cres); +const struct cred *saved_cred; +struct file *file =cachefiles_cres_file (cres); +enum netfs_read_source ret =NETFS_DOWNLOAD_FROM_SERVER ; +loff_t off, to; +ino_t ino = file ?file_inode (file)->i_ino :0 ; + +_enter ("%zx @%llx/%llx" , subreq->len, subreq->start, i_size); + +if (subreq->start >= i_size) { + ret =NETFS_FILL_WITH_ZEROES ; + why = cachefiles_trace_read_after_eof; + goto out_no_object; + } + +if (test_bit (FSCACHE_COOKIE_NO_DATA_TO_READ , &cookie->flags)) { +__set_bit (NETFS_SREQ_WRITE_TO_CACHE , &subreq->flags); + why = cachefiles_trace_read_no_data; + goto out_no_object; + } + +/* The object and the file may be being created in the background. */ +if (!file) { + why = cachefiles_trace_read_no_file; +if (!fscache_wait_for_operation (cres,FSCACHE_WANT_READ )) + goto out_no_object; + file =cachefiles_cres_file (cres); +if (!file) + goto out_no_object; + ino =file_inode (file)->i_ino; + } + + object =cachefiles_cres_object (cres); + cache = object->volume->cache; +cachefiles_begin_secure (cache, &saved_cred); + + off =cachefiles_inject_read_error (); +if (off ==0 ) + off =vfs_llseek (file, subreq->start,SEEK_DATA ); +if (off <0 && off >= (loff_t)-MAX_ERRNO ) { +if (off == (loff_t)-ENXIO ) { + why = cachefiles_trace_read_seek_nxio; + goto download_and_store; + } +trace_cachefiles_io_error (object,file_inode (file), off, + cachefiles_trace_seek_error); + why = cachefiles_trace_read_seek_error; + goto out; + } + +if (off >= subreq->start + subreq->len) { + why = cachefiles_trace_read_found_hole; + goto download_and_store; + } + +if (off > subreq->start) { + off =round_up (off, cache->bsize); + subreq->len = off - subreq->start; + why = cachefiles_trace_read_found_part; + goto download_and_store; + } + + to =cachefiles_inject_read_error (); +if (to ==0 ) + to =vfs_llseek (file, subreq->start,SEEK_HOLE ); +if (to <0 && to >= (loff_t)-MAX_ERRNO ) { +trace_cachefiles_io_error (object,file_inode (file), to, + cachefiles_trace_seek_error); + why = cachefiles_trace_read_seek_error; + goto out; + } + +if (to < subreq->start + subreq->len) { +if (subreq->start + subreq->len >= i_size) + to =round_up (to, cache->bsize); +else + to =round_down (to, cache->bsize); + subreq->len = to - subreq->start; + } + + why = cachefiles_trace_read_have_data; + ret =NETFS_READ_FROM_CACHE ; + goto out; + +download_and_store: +__set_bit (NETFS_SREQ_WRITE_TO_CACHE , &subreq->flags); +out: +cachefiles_end_secure (cache, saved_cred); +out_no_object: +trace_cachefiles_prep_read (subreq, ret, why, ino); +return ret; +} + +/* +* Prepare for a write to occur. +*/ +static int __cachefiles_prepare_write (struct netfs_cache_resources *cres, +loff_t *_start,size_t *_len,loff_t i_size, +bool no_space_allocated_yet) +{ +struct cachefiles_object *object =cachefiles_cres_object (cres); +struct cachefiles_cache *cache = object->volume->cache; +struct file *file =cachefiles_cres_file (cres); +loff_t start = *_start, pos; +size_t len = *_len, down; +int ret; + +/* Round to DIO size */ + down = start -round_down (start,PAGE_SIZE ); + *_start = start - down; + *_len =round_up (down + len,PAGE_SIZE ); + +/* We need to work out whether there's sufficient disk space to perform +* the write - but we can skip that check if we have space already +* allocated. +*/ +if (no_space_allocated_yet) + goto check_space; + + pos =cachefiles_inject_read_error (); +if (pos ==0 ) + pos =vfs_llseek (file, *_start,SEEK_DATA ); +if (pos <0 && pos >= (loff_t)-MAX_ERRNO ) { +if (pos == -ENXIO ) + goto check_space;/* Unallocated tail */ +trace_cachefiles_io_error (object,file_inode (file), pos, + cachefiles_trace_seek_error); +return pos; + } +if ((u64 )pos >= (u64)*_start + *_len) + goto check_space;/* Unallocated region */ + +/* We have a block that's at least partially filled - if we're low on +* space, we need to see if it's fully allocated. If it's not, we may +* want to cull it. +*/ +if (cachefiles_has_space (cache,0 , *_len /PAGE_SIZE ) ==0 ) +return 0 ;/* Enough space to simply overwrite the whole block */ + + pos =cachefiles_inject_read_error (); +if (pos ==0 ) + pos =vfs_llseek (file, *_start,SEEK_HOLE ); +if (pos <0 && pos >= (loff_t)-MAX_ERRNO ) { +trace_cachefiles_io_error (object,file_inode (file), pos, + cachefiles_trace_seek_error); +return pos; + } +if ((u64 )pos >= (u64)*_start + *_len) +return 0 ;/* Fully allocated */ + +/* Partially allocated, but insufficient space: cull. */ + ret =cachefiles_inject_remove_error (); +if (ret ==0 ) + ret =vfs_fallocate (file,FALLOC_FL_PUNCH_HOLE |FALLOC_FL_KEEP_SIZE , + *_start, *_len); +if (ret <0 ) { +trace_cachefiles_io_error (object,file_inode (file), ret, + cachefiles_trace_fallocate_error); +cachefiles_io_error_obj (object, +"CacheFiles: fallocate failed (%d)\n" , ret); + ret = -EIO ; + } + +return ret; + +check_space: +return cachefiles_has_space (cache,0 , *_len /PAGE_SIZE ); +} + +static int cachefiles_prepare_write (struct netfs_cache_resources *cres, +loff_t *_start,size_t *_len,loff_t i_size, +bool no_space_allocated_yet) +{ +struct cachefiles_object *object =cachefiles_cres_object (cres); +struct cachefiles_cache *cache = object->volume->cache; +const struct cred *saved_cred; +int ret; + +if (!cachefiles_cres_file (cres)) { +if (!fscache_wait_for_operation (cres,FSCACHE_WANT_WRITE )) +return -ENOBUFS ; +if (!cachefiles_cres_file (cres)) +return -ENOBUFS ; + } + +cachefiles_begin_secure (cache, &saved_cred); + ret =__cachefiles_prepare_write (cres, _start, _len, i_size, + no_space_allocated_yet); +cachefiles_end_secure (cache, saved_cred); +return ret; +} +/* * Clean up an operation. */
@@ -28,6 +538,10 @@ static void cachefiles_end_operation(struct netfs_cache_resources *cres)static const struct netfs_cache_ops cachefiles_netfs_cache_ops = { .end_operation = cachefiles_end_operation, + .read = cachefiles_read, + .write = cachefiles_write, + .prepare_read = cachefiles_prepare_read, + .prepare_write = cachefiles_prepare_write, };/*
include/trace/events/cachefiles.h +121 -0
145 lines not on this page · raw message
David Howells
maintainerPATCH v4 52/68 fscache, cachefiles: Store the volume coherency data
In reply to David HowellsStore the volume coherency data in an xattr and check it when we rebind the volume. If it doesn't match the cache volume is moved to the graveyard and rebuilt anew.
Changes
=======
ver #4:
- Remove a couple of debugging prints.
---
fs/cachefiles/internal.h | 2 + fs/cachefiles/volume.c | 25 +++++++++++- fs/cachefiles/xattr.c | 78 +++++++++++++++++++++++++++++++++++++ fs/fscache/volume.c | 14 ++++++- include/linux/fscache.h | 2 + include/trace/events/cachefiles.h | 42 +++++++++++++++++++- 6 files changed, 157 insertions(+), 6 deletions(-)
fs/cachefiles/internal.h +2 -0
@@ -270,6 +270,8 @@ extern int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,struct cachefiles_object *object, struct dentry *dentry);extern void cachefiles_prepare_to_write (struct fscache_cookie *cookie); +extern bool cachefiles_set_volume_xattr (struct cachefiles_volume *volume); +extern int cachefiles_check_volume_xattr (struct cachefiles_volume *volume);/* * Error handling
fs/cachefiles/volume.c +23 -2
@@ -22,7 +22,8 @@ void cachefiles_acquire_volume(struct fscache_volume *vcookie)struct dentry *vdentry, *fan;size_t len;char *name; -int n_accesses, i; +bool is_new = false; +int ret, n_accesses, i;_enter ("" );
@@ -43,11 +44,29 @@ void cachefiles_acquire_volume(struct fscache_volume *vcookie)memcpy (name +1 , vcookie->key +1 , len); name[len +1 ] =0 ; - vdentry =cachefiles_get_directory (cache, cache->store, name,NULL ); +retry: + vdentry =cachefiles_get_directory (cache, cache->store, name, &is_new);if (IS_ERR (vdentry)) goto error_name; volume->dentry = vdentry; +if (is_new) { +if (!cachefiles_set_volume_xattr (volume)) + goto error_dir; + }else { + ret =cachefiles_check_volume_xattr (volume); +if (ret <0 ) { +if (ret != -ESTALE ) + goto error_dir; +inode_lock_nested (d_inode (cache->store),I_MUTEX_PARENT ); +cachefiles_bury_object (cache,NULL , cache->store, vdentry, +FSCACHE_VOLUME_IS_WEIRD ); +cachefiles_put_directory (volume->dentry); +cond_resched (); + goto retry; + } + } +for (i =0 ; i <256 ; i++) {sprintf (name,"@%02x" , i); fan =cachefiles_get_directory (cache, vdentry, name,NULL );
@@ -74,6 +93,7 @@ void cachefiles_acquire_volume(struct fscache_volume *vcookie) error_fan:for (i =0 ; i <256 ; i++)cachefiles_put_directory (volume->fanout[i]); +error_dir:cachefiles_put_directory (volume->dentry); error_name:kfree (name);
@@ -114,5 +134,6 @@ void cachefiles_free_volume(struct fscache_volume *vcookie)void cachefiles_withdraw_volume (struct cachefiles_volume *volume) {fscache_withdraw_volume (volume->vcookie); +cachefiles_set_volume_xattr (volume);__cachefiles_free_volume (volume); }
fs/cachefiles/xattr.c +78 -0
81 lines not on this page · raw message
fs/fscache/volume.c +12 -2
@@ -205,15 +205,22 @@ static struct fscache_volume *fscache_alloc_volume(const char *volume_key,size_t klen, hlen;char *key; +if (!coherency_data) + coherency_len =0 ; + cache =fscache_lookup_cache (cache_name, false);if (IS_ERR (cache))return NULL ; - volume =kzalloc (sizeof (*volume),GFP_KERNEL ); + volume =kzalloc (struct_size (volume, coherency, coherency_len), +GFP_KERNEL );if (!volume) goto err_cache; volume->cache = cache; + volume->coherency_len = coherency_len; +if (coherency_data) +memcpy (volume->coherency, coherency_data, coherency_len);INIT_LIST_HEAD (&volume->proc_link);INIT_WORK (&volume->work, fscache_create_volume_work);refcount_set (&volume->ref,1 );
@@ -421,8 +428,11 @@ void __fscache_relinquish_volume(struct fscache_volume *volume,if (WARN_ON (test_and_set_bit (FSCACHE_VOLUME_RELINQUISHED , &volume->flags)))return ; -if (invalidate) +if (invalidate) {set_bit (FSCACHE_VOLUME_INVALIDATE , &volume->flags); + }else if (coherency_data) { +memcpy (volume->coherency, coherency_data, volume->coherency_len); + }fscache_put_volume (volume, fscache_volume_put_relinquish); }
include/linux/fscache.h +2 -0
@@ -87,6 +87,8 @@ struct fscache_volume {#define FSCACHE_VOLUME_COLLIDED_WITH 2/* Volume was collided with */ #define FSCACHE_VOLUME_ACQUIRE_PENDING 3/* Volume is waiting to complete acquisition */ #define FSCACHE_VOLUME_CREATING 4/* Volume is being created on disk */ +u8 coherency_len;/* Length of the coherency data */ +u8 coherency[];/* Coherency data */ };/*
include/trace/events/cachefiles.h +40 -2
72 lines not on this page · raw message
David Howells
maintainerPATCH v4 53/68 cachefiles: Allow cachefiles to actually function
In reply to David HowellsRemove the block that allowed cachefiles to be compiled but prevented it from actually starting a cache.
---
fs/cachefiles/daemon.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
fs/cachefiles/daemon.c +1 -3
@@ -703,9 +703,7 @@ static int cachefiles_daemon_bind(struct cachefiles_cache *cache, char *args)return -EBUSY ; } -pr_warn ("Cache is disabled for development\n" ); -return -ENOANO ;// Don't allow the cache to operate yet -//return cachefiles_add_cache(cache); +return cachefiles_add_cache (cache); }/*
David Howells
maintainerPATCH v4 54/68 fscache, cachefiles: Display stats of no-space events
In reply to David HowellsAdd stat counters of no-space events that caused caching not to happen and display in /proc/fs/fscache/stats.
---
fs/cachefiles/cache.c | 18 +++++++++++++++--- fs/cachefiles/daemon.c | 2 +- fs/cachefiles/internal.h | 11 +++++++++-- fs/cachefiles/io.c | 7 +++++-- fs/cachefiles/namei.c | 6 ++++-- fs/fscache/stats.c | 8 ++++++++ include/linux/fscache-cache.h | 6 ++++++ 7 files changed, 48 insertions(+), 10 deletions(-)
fs/cachefiles/cache.c +15 -3
@@ -147,7 +147,7 @@ int cachefiles_add_cache(struct cachefiles_cache *cache)pr_info ("File cache on %s registered\n" , cache_cookie->name);/* check how much space the cache has */ -cachefiles_has_space (cache,0 ,0 ); +cachefiles_has_space (cache,0 ,0 , cachefiles_has_space_check);cachefiles_end_secure (cache, saved_cred);_leave (" = 0 [%px]" , cache->cache);return 0 ;
@@ -175,7 +175,8 @@ int cachefiles_add_cache(struct cachefiles_cache *cache)* cache */ int cachefiles_has_space (struct cachefiles_cache *cache, -unsigned fnr,unsigned bnr) +unsigned fnr,unsigned bnr, +enum cachefiles_has_space_for reason) {struct kstatfs stats;u64 b_avail, b_writing;
@@ -233,7 +234,7 @@ int cachefiles_has_space(struct cachefiles_cache *cache, ret = -ENOBUFS ;if (stats.f_ffree < cache->fstop || b_avail < cache->bstop) - goto begin_cull; + goto stop_and_begin_cull; ret =0 ; if (stats.f_ffree < cache->fcull ||
@@ -252,6 +253,17 @@ int cachefiles_has_space(struct cachefiles_cache *cache,//_leave(" = 0"); return 0 ; +stop_and_begin_cull: +switch (reason) { +case cachefiles_has_space_for_write: +fscache_count_no_write_space (); +break ; +case cachefiles_has_space_for_create: +fscache_count_no_create_space (); +break ; +default : +break ; + } begin_cull:if (!test_and_set_bit (CACHEFILES_CULLING , &cache->flags)) {_debug ("### CULL CACHE ###" );
fs/cachefiles/daemon.c +1 -1
@@ -170,7 +170,7 @@ static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer,return 0 ;/* check how much space the cache has */ -cachefiles_has_space (cache,0 ,0 ); +cachefiles_has_space (cache,0 ,0 , cachefiles_has_space_check);/* summarise */ f_released =atomic_xchg (&cache->f_released,0 );
fs/cachefiles/internal.h +9 -2
@@ -130,10 +130,17 @@ static inline void cachefiles_state_changed(struct cachefiles_cache *cache)* cache.c */ extern int cachefiles_add_cache (struct cachefiles_cache *cache); -extern int cachefiles_has_space (struct cachefiles_cache *cache, -unsigned fnr,unsigned bnr);extern void cachefiles_withdraw_cache (struct cachefiles_cache *cache); +enum cachefiles_has_space_for { + cachefiles_has_space_check, + cachefiles_has_space_for_write, + cachefiles_has_space_for_create, +}; +extern int cachefiles_has_space (struct cachefiles_cache *cache, +unsigned fnr,unsigned bnr, +enum cachefiles_has_space_for reason); +/* * daemon.c */
fs/cachefiles/io.c +5 -2
@@ -468,7 +468,8 @@ static int __cachefiles_prepare_write(struct netfs_cache_resources *cres,* space, we need to see if it's fully allocated. If it's not, we may * want to cull it. */ -if (cachefiles_has_space (cache,0 , *_len /PAGE_SIZE ) ==0 ) +if (cachefiles_has_space (cache,0 , *_len /PAGE_SIZE , + cachefiles_has_space_check) ==0 )return 0 ;/* Enough space to simply overwrite the whole block */ pos =cachefiles_inject_read_error ();
@@ -483,6 +484,7 @@ static int __cachefiles_prepare_write(struct netfs_cache_resources *cres,return 0 ;/* Fully allocated */ /* Partially allocated, but insufficient space: cull. */ +fscache_count_no_write_space (); ret =cachefiles_inject_remove_error ();if (ret ==0 ) ret = vfs_fallocate(file, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
@@ -498,7 +500,8 @@ static int __cachefiles_prepare_write(struct netfs_cache_resources *cres,return ret; check_space: -return cachefiles_has_space (cache,0 , *_len /PAGE_SIZE ); +return cachefiles_has_space (cache,0 , *_len /PAGE_SIZE , + cachefiles_has_space_for_write); } static int cachefiles_prepare_write(struct netfs_cache_resources *cres,
fs/cachefiles/namei.c +4 -2
@@ -115,7 +115,8 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,/* we need to create the subdir if it doesn't exist yet */ if (d_is_negative (subdir)) { - ret =cachefiles_has_space (cache,1 ,0 ); + ret =cachefiles_has_space (cache,1 ,0 , + cachefiles_has_space_for_create);if (ret <0 ) goto mkdir_error;
@@ -513,7 +514,8 @@ static bool cachefiles_create_file(struct cachefiles_object *object)struct file *file;int ret; - ret =cachefiles_has_space (object->volume->cache,1 ,0 ); + ret =cachefiles_has_space (object->volume->cache,1 ,0 , + cachefiles_has_space_for_create);if (ret <0 )return false;
fs/fscache/stats.c +8 -0
@@ -42,6 +42,10 @@ atomic_t fscache_n_read;EXPORT_SYMBOL (fscache_n_read);atomic_t fscache_n_write;EXPORT_SYMBOL (fscache_n_write); +atomic_t fscache_n_no_write_space; +EXPORT_SYMBOL (fscache_n_no_write_space); +atomic_t fscache_n_no_create_space; +EXPORT_SYMBOL (fscache_n_no_create_space);/* * display the general statistics
@@ -82,6 +86,10 @@ int fscache_stats_show(struct seq_file *m, void *v)atomic_read (&fscache_n_relinquishes_retire), atomic_read(&fscache_n_relinquishes_dropped)); +seq_printf (m,"NoSpace: nwr=%u ncr=%u\n" , +atomic_read (&fscache_n_no_write_space), +atomic_read (&fscache_n_no_create_space)); +seq_printf (m,"IO : rd=%u wr=%u\n" ,atomic_read (&fscache_n_read),atomic_read (&fscache_n_write));
include/linux/fscache-cache.h +6 -0
@@ -186,11 +186,17 @@ static inline void fscache_wait_for_objects(struct fscache_cache *cache)#ifdef CONFIG_FSCACHE_STATS extern atomic_t fscache_n_read;extern atomic_t fscache_n_write; +extern atomic_t fscache_n_no_write_space; +extern atomic_t fscache_n_no_create_space;#define fscache_count_read () atomic_inc(&fscache_n_read)#define fscache_count_write () atomic_inc(&fscache_n_write) +#define fscache_count_no_write_space () atomic_inc(&fscache_n_no_write_space) +#define fscache_count_no_create_space () atomic_inc(&fscache_n_no_create_space)#else #define fscache_count_read () do {} while(0)#define fscache_count_write () do {} while(0) +#define fscache_count_no_write_space () do {} while(0) +#define fscache_count_no_create_space () do {} while(0)#endif #endif /* _LINUX_FSCACHE_CACHE_H */
David Howells
maintainerPATCH v4 55/68 fscache, cachefiles: Display stat of culling events
In reply to David HowellsAdd a stat counter of culling events whereby the cache backend culls a file to make space (when asked by cachefilesd in this case) and display in /proc/fs/fscache/stats.
---
fs/cachefiles/namei.c | 1 + fs/fscache/stats.c | 7 +++++-- include/linux/fscache-cache.h | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-)
fs/cachefiles/namei.c +1 -0
@@ -805,6 +805,7 @@ int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir,if (ret <0 ) goto error; +fscache_count_culled ();dput (victim);_leave (" = 0" );return 0 ;
fs/fscache/stats.c +5 -2
@@ -46,6 +46,8 @@ atomic_t fscache_n_no_write_space;EXPORT_SYMBOL (fscache_n_no_write_space);atomic_t fscache_n_no_create_space;EXPORT_SYMBOL (fscache_n_no_create_space); +atomic_t fscache_n_culled; +EXPORT_SYMBOL (fscache_n_culled);/* * display the general statistics
@@ -86,9 +88,10 @@ int fscache_stats_show(struct seq_file *m, void *v)atomic_read (&fscache_n_relinquishes_retire), atomic_read(&fscache_n_relinquishes_dropped)); -seq_printf (m,"NoSpace: nwr=%u ncr=%u\n" , +seq_printf (m,"NoSpace: nwr=%u ncr=%u cull=%u\n" ,atomic_read (&fscache_n_no_write_space), -atomic_read (&fscache_n_no_create_space)); +atomic_read (&fscache_n_no_create_space), +atomic_read (&fscache_n_culled)); seq_printf(m, "IO : rd=%u wr=%u\n", atomic_read(&fscache_n_read),
include/linux/fscache-cache.h +3 -0
@@ -188,15 +188,18 @@ extern atomic_t fscache_n_read;extern atomic_t fscache_n_write;extern atomic_t fscache_n_no_write_space;extern atomic_t fscache_n_no_create_space; +extern atomic_t fscache_n_culled;#define fscache_count_read () atomic_inc(&fscache_n_read)#define fscache_count_write () atomic_inc(&fscache_n_write)#define fscache_count_no_write_space () atomic_inc(&fscache_n_no_write_space)#define fscache_count_no_create_space () atomic_inc(&fscache_n_no_create_space) +#define fscache_count_culled () atomic_inc(&fscache_n_culled)#else #define fscache_count_read () do {} while(0)#define fscache_count_write () do {} while(0)#define fscache_count_no_write_space () do {} while(0)#define fscache_count_no_create_space () do {} while(0) +#define fscache_count_culled () do {} while(0)#endif #endif /* _LINUX_FSCACHE_CACHE_H */
David Howells
maintainerPATCH v4 56/68 afs: Convert afs to use the new fscache API
In reply to David HowellsChange the afs filesystem to support the new afs driver.
The following changes have been made:
(1) The fscache_netfs struct is no more, and there's no need to register the filesystem as a whole. There's also no longer a cell cookie.
(2) The volume cookie is now an fscache_volume cookie, allocated with fscache_acquire_volume(). This function takes three parameters: a string representing the "volume" in the index, a string naming the cache to use (or NULL) and a u64 that conveys coherency metadata for the volume.
For afs, I've made it render the volume name string as:
"afs,<cell>,<volume_id>"
and the coherency data is currently 0.
(3) The fscache_cookie_def is no more and needed information is passed directly to fscache_acquire_cookie(). The cache no longer calls back into the filesystem, but rather metadata changes are indicated at other times.
fscache_acquire_cookie() is passed the same keying and coherency information as before, except that these are now stored in big endian form instead of cpu endian. This makes the cache more copyable.
(4) fscache_use_cookie() and fscache_unuse_cookie() are called when a file is opened or closed to prevent a cache file from being culled and to keep resources to hand that are needed to do I/O.
fscache_use_cookie() is given an indication if the cache is likely to be modified locally (e.g. the file is open for writing).
fscache_unuse_cookie() is given a coherency update if we had the file open for writing and will update that.
(5) fscache_invalidate() is now given uptodate auxiliary data and a file size. It can also take a flag to indicate if this was due to a DIO write. This is wrapped into afs_fscache_invalidate() now for convenience.
(6) fscache_resize() now gets called from the finalisation of afs_setattr(), and afs_setattr() does use/unuse of the cookie around the call to support this.
(7) fscache_note_page_release() is called from afs_release_page().
(8) Use a killable wait in nfs_vm_page_mkwrite() when waiting for PG_fscache to be cleared.
Render the parts of the cookie key for an afs inode cookie as big endian.
Changes
=======
ver #2:
- Use gfpflags_allow_blocking() rather than using flag directly.
- fscache_acquire_volume() now returns errors.
---
fs/afs/Kconfig | 2 +- fs/afs/Makefile | 3 -- fs/afs/cache.c | 68 ----------------------------------------------------- fs/afs/cell.c | 12 --------- fs/afs/file.c | 29 ++++++++++++++++++----- fs/afs/inode.c | 50 +++++++++++++++++++-------------------- fs/afs/internal.h | 32 ++++++++++++++----------- fs/afs/main.c | 14 ----------- fs/afs/volume.c | 29 +++++++++++++++++------ fs/afs/write.c | 1 - 10 files changed, 89 insertions(+), 151 deletions(-) delete mode 100644 fs/afs/cache.c
fs/afs/Kconfig +1 -1
@@ -25,7 +25,7 @@ config AFS_DEBUG config AFS_FSCACHE bool "Provide AFS client caching support" - depends on AFS_FS=m && FSCACHE_OLD_API || AFS_FS=y && FSCACHE_OLD_API=y + depends on AFS_FS=m && FSCACHE || AFS_FS=y && FSCACHE=y help Say Y here if you want AFS data to be cached locally on disk through the generic filesystem cache manager
fs/afs/Makefile +0 -3
@@ -3,10 +3,7 @@ # Makefile for Red Hat Linux AFS client. # -afs-cache-$(CONFIG_AFS_FSCACHE) := cache.o - kafs-y := \ - $(afs-cache-y) \ addr_list.o \ callback.o \ cell.o \
fs/afs/cache.c deleted +0 -68
68 lines not on this page · raw message
fs/afs/cell.c +0 -12
@@ -680,13 +680,6 @@ static int afs_activate_cell(struct afs_net *net, struct afs_cell *cell)return ret; } -#ifdef CONFIG_AFS_FSCACHE - cell->cache =fscache_acquire_cookie (afs_cache_netfs.primary_index, - &afs_cell_cache_index_def, - cell->name,strlen (cell->name), -NULL ,0 , - cell,0 , true); -#endif ret =afs_proc_cell_setup (cell);if (ret <0 )return ret;
@@ -723,11 +716,6 @@ static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell)afs_dynroot_rmdir (net, cell);mutex_unlock (&net->proc_cells_lock); -#ifdef CONFIG_AFS_FSCACHE -fscache_relinquish_cookie (cell->cache,NULL , false); - cell->cache =NULL ; -#endif -_leave ("" ); }
fs/afs/file.c +23 -6
@@ -158,7 +158,9 @@ int afs_open(struct inode *inode, struct file *file)if (file->f_flags &O_TRUNC )set_bit (AFS_VNODE_NEW_CONTENT , &vnode->flags); - + +fscache_use_cookie (afs_vnode_cache (vnode), file->f_mode &FMODE_WRITE ); + file->private_data = af;_leave (" = 0" );return 0 ;
@@ -177,8 +179,10 @@ int afs_open(struct inode *inode, struct file *file)*/ int afs_release (struct inode *inode,struct file *file) { +struct afs_vnode_cache_aux aux;struct afs_vnode *vnode =AFS_FS_I (inode);struct afs_file *af = file->private_data; +loff_t i_size;int ret =0 ;_enter ("{%llx:%llu}," , vnode->fid.vid, vnode->fid.vnode);
@@ -189,6 +193,15 @@ int afs_release(struct inode *inode, struct file *file) file->private_data =NULL ;if (af->wb)afs_put_wb_key (af->wb); + +if ((file->f_mode &FMODE_WRITE )) { + i_size =i_size_read (&vnode->vfs_inode); +afs_set_cache_aux (vnode, &aux); +fscache_unuse_cookie (afs_vnode_cache (vnode), &aux, &i_size); + }else { +fscache_unuse_cookie (afs_vnode_cache (vnode),NULL ,NULL ); + } +key_put (af->key);kfree (af);afs_prune_wb_keys (vnode);
@@ -352,7 +365,9 @@ static void afs_init_rreq(struct netfs_read_request *rreq, struct file *file)static bool afs_is_cache_enabled (struct inode *inode) { -return fscache_cookie_enabled (afs_vnode_cache (AFS_FS_I (inode))); +struct fscache_cookie *cookie =afs_vnode_cache (AFS_FS_I (inode)); + +return fscache_cookie_enabled (cookie) && cookie->cache_priv; }static int afs_begin_cache_operation (struct netfs_read_request *rreq)
@@ -360,7 +375,8 @@ static int afs_begin_cache_operation(struct netfs_read_request *rreq)#ifdef CONFIG_AFS_FSCACHE struct afs_vnode *vnode =AFS_FS_I (rreq->inode); -return fscache_begin_read_operation (rreq,afs_vnode_cache (vnode)); +return fscache_begin_read_operation (&rreq->cache_resources, +afs_vnode_cache (vnode));#else return -ENOBUFS ;#endif
@@ -482,23 +498,24 @@ static void afs_invalidatepage(struct page *page, unsigned int offset,* release a page and clean up its private state if it's not busy * - return true if the page can now be released, false if not */ -static int afs_releasepage (struct page *page,gfp_t gfp_flags) +static int afs_releasepage (struct page *page,gfp_t gfp) {struct folio *folio =page_folio (page);struct afs_vnode *vnode =AFS_FS_I (folio_inode (folio));_enter ("{{%llx:%llu}[%lu],%lx},%x" , vnode->fid.vid, vnode->fid.vnode,folio_index (folio), folio->flags, - gfp_flags); + gfp);/* deny if page is being written to the cache and the caller hasn't * elected to wait */ #ifdef CONFIG_AFS_FSCACHE if (folio_test_fscache (folio)) { -if (!(gfp_flags & __GFP_DIRECT_RECLAIM) || !(gfp_flags & __GFP_FS)) +if (!gfpflags_allow_blocking (gfp) || !(gfp & __GFP_FS))return false;folio_wait_fscache (folio); } +fscache_note_page_release (afs_vnode_cache (vnode));#endif if (folio_test_private (folio)) {
fs/afs/inode.c +25 -25
92 lines not on this page · raw message
fs/afs/internal.h +18 -14
@@ -14,7 +14,6 @@#include <linux/key.h> #include <linux/workqueue.h> #include <linux/sched.h> -#define FSCACHE_USE_NEW_IO_API #include <linux/fscache.h> #include <linux/backing-dev.h> #include <linux/uuid.h>
@@ -364,9 +363,6 @@ struct afs_cell {struct key *anonymous_key;/* anonymous user key for this cell */ struct work_struct manager;/* Manager for init/deinit/dns */ struct hlist_node proc_link;/* /proc cell list link */ -#ifdef CONFIG_AFS_FSCACHE -struct fscache_cookie *cache;/* caching cookie */ -#endif time64_t dns_expiry;/* Time AFSDB/SRV record expires */ time64_t last_inactive;/* Time of last drop of usage count */ atomic_t ref;/* Struct refcount */
@@ -590,7 +586,7 @@ struct afs_volume {#define AFS_VOLUME_BUSY 5/* - T if volume busy notice given */ #define AFS_VOLUME_MAYBE_NO_IBULK 6/* - T if some servers don't have InlineBulkStatus */ #ifdef CONFIG_AFS_FSCACHE -struct fscache_cookie *cache;/* caching cookie */ +struct fscache_volume *cache;/* Caching cookie */ #endif struct afs_server_list __rcu *servers;/* List of servers on which volume resides */ rwlock_t servers_lock;/* Lock for ->servers */
@@ -872,9 +868,24 @@ struct afs_operation {* Cache auxiliary data. */ struct afs_vnode_cache_aux { -u64 data_version; +__be64 data_version; } __packed; +static inline void afs_set_cache_aux (struct afs_vnode *vnode, +struct afs_vnode_cache_aux *aux) +{ + aux->data_version =cpu_to_be64 (vnode->status.data_version); +} + +static inline void afs_invalidate_cache (struct afs_vnode *vnode,unsigned int flags) +{ +struct afs_vnode_cache_aux aux; + +afs_set_cache_aux (vnode, &aux); +fscache_invalidate (afs_vnode_cache (vnode), &aux, +i_size_read (&vnode->vfs_inode), flags); +} +/* * We use folio->private to hold the amount of the folio that we've written to, * splitting the field into two parts. However, we need to represent a range
@@ -962,13 +973,6 @@ extern void afs_merge_fs_addr6(struct afs_addr_list *, __be32 *, u16);*/ #ifdef CONFIG_AFS_FSCACHE extern struct fscache_netfs afs_cache_netfs; -extern struct fscache_cookie_def afs_cell_cache_index_def; -extern struct fscache_cookie_def afs_volume_cache_index_def; -extern struct fscache_cookie_def afs_vnode_cache_index_def; -#else -#define afs_cell_cache_index_def (*(struct fscache_cookie_def *) NULL) -#define afs_volume_cache_index_def (*(struct fscache_cookie_def *) NULL) -#define afs_vnode_cache_index_def (*(struct fscache_cookie_def *) NULL)#endif /*
@@ -1506,7 +1510,7 @@ extern struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *,* volume.c */ extern struct afs_volume *afs_create_volume (struct afs_fs_context *); -extern void afs_activate_volume (struct afs_volume *); +extern int afs_activate_volume (struct afs_volume *);extern void afs_deactivate_volume (struct afs_volume *);extern struct afs_volume *afs_get_volume (struct afs_volume *,enum afs_volume_trace );extern void afs_put_volume (struct afs_net *,struct afs_volume *,enum afs_volume_trace );
fs/afs/main.c +0 -14
@@ -186,13 +186,6 @@ static int __init afs_init(void)if (!afs_lock_manager) goto error_lockmgr; -#ifdef CONFIG_AFS_FSCACHE -/* we want to be able to cache */ - ret =fscache_register_netfs (&afs_cache_netfs); -if (ret <0 ) - goto error_cache; -#endif - ret =register_pernet_device (&afs_net_ops);if (ret <0 ) goto error_net;
@@ -215,10 +208,6 @@ static int __init afs_init(void) error_fs:unregister_pernet_device (&afs_net_ops); error_net: -#ifdef CONFIG_AFS_FSCACHE -fscache_unregister_netfs (&afs_cache_netfs); -error_cache: -#endif destroy_workqueue (afs_lock_manager); error_lockmgr:destroy_workqueue (afs_async_calls);
@@ -245,9 +234,6 @@ static void __exit afs_exit(void)proc_remove (afs_proc_symlink);afs_fs_exit ();unregister_pernet_device (&afs_net_ops); -#ifdef CONFIG_AFS_FSCACHE -fscache_unregister_netfs (&afs_cache_netfs); -#endif destroy_workqueue (afs_lock_manager);destroy_workqueue (afs_async_calls);destroy_workqueue (afs_wq);
fs/afs/volume.c +22 -7
@@ -268,15 +268,30 @@ void afs_put_volume(struct afs_net *net, struct afs_volume *volume,/* * Activate a volume. */ -void afs_activate_volume (struct afs_volume *volume) +int afs_activate_volume (struct afs_volume *volume) {#ifdef CONFIG_AFS_FSCACHE - volume->cache =fscache_acquire_cookie (volume->cell->cache, - &afs_volume_cache_index_def, - &volume->vid,sizeof (volume->vid), -NULL ,0 , - volume,0 , true); +struct fscache_volume *vcookie; +char *name; + + name =kasprintf (GFP_KERNEL ,"afs,%s,%llx" , + volume->cell->name, volume->vid); +if (!name) +return -ENOMEM ; + + vcookie =fscache_acquire_volume (name,NULL ,NULL ,0 ); +if (IS_ERR (vcookie)) { +if (vcookie !=ERR_PTR (-EBUSY )) { +kfree (name); +return PTR_ERR (vcookie); + } +pr_err ("AFS: Cache volume key already in use (%s)\n" , name); + vcookie =NULL ; + } + volume->cache = vcookie; +kfree (name);#endif +return 0 ; }/*
@@ -287,7 +302,7 @@ void afs_deactivate_volume(struct afs_volume *volume)_enter ("%s" , volume->name);#ifdef CONFIG_AFS_FSCACHE -fscache_relinquish_cookie (volume->cache,NULL , +fscache_relinquish_volume (volume->cache,NULL ,test_bit (AFS_VOLUME_DELETED , &volume->flags)); volume->cache =NULL ;#endif
fs/afs/write.c +0 -1
@@ -12,7 +12,6 @@#include <linux/writeback.h> #include <linux/pagevec.h> #include <linux/netfs.h> -#include <linux/fscache.h> #include "internal.h" /*
David Howells
maintainerPATCH v4 57/68 afs: Copy local writes to the cache when writing to the server
In reply to David HowellsWhen writing to the server from afs_writepage() or afs_writepages(), copy the data to the cache object too.
To make this possible, the cookie must have its active users count incremented when the page is dirtied and kept incremented until we manage to clean up all the pages. This allows the writeback to take place after the last file struct is released.
---
fs/afs/file.c | 6 ++++ fs/afs/inode.c | 8 +++-- fs/afs/internal.h | 5 +++ fs/afs/super.c | 1 + fs/afs/write.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 92 insertions(+), 15 deletions(-)
fs/afs/file.c +6 -0
@@ -416,6 +416,12 @@ static void afs_readahead(struct readahead_control *ractl)netfs_readahead (ractl, &afs_req_ops,NULL ); } +int afs_write_inode (struct inode *inode,struct writeback_control *wbc) +{ +fscache_unpin_writeback (wbc,afs_vnode_cache (AFS_FS_I (inode))); +return 0 ; +} +/* * Adjust the dirty region of the page on truncation or full invalidation, * getting rid of the markers altogether if the region is entirely invalidated.
fs/afs/inode.c +5 -3
@@ -761,9 +761,8 @@ int afs_drop_inode(struct inode *inode)*/ void afs_evict_inode (struct inode *inode) { -struct afs_vnode *vnode; - - vnode =AFS_FS_I (inode); +struct afs_vnode_cache_aux aux; +struct afs_vnode *vnode =AFS_FS_I (inode); _enter("{%llx:%llu.%d}", vnode->fid.vid,
@@ -775,6 +774,9 @@ void afs_evict_inode(struct inode *inode) ASSERTCMP(inode->i_ino, ==, vnode->fid.vnode);truncate_inode_pages_final (&inode->i_data); + +afs_set_cache_aux (vnode, &aux); +fscache_clear_inode_writeback (afs_vnode_cache (vnode), inode, &aux);clear_inode (inode);while (!list_empty (&vnode->wb_keys)) {
fs/afs/internal.h +5 -0
@@ -1072,6 +1072,7 @@ extern int afs_release(struct inode *, struct file *);extern int afs_fetch_data (struct afs_vnode *,struct afs_read *);extern struct afs_read *afs_alloc_read (gfp_t );extern void afs_put_read (struct afs_read *); +extern int afs_write_inode (struct inode *,struct writeback_control *);static inline struct afs_read *afs_get_read (struct afs_read *req) {
@@ -1519,7 +1520,11 @@ extern int afs_check_volume_status(struct afs_volume *, struct afs_operation *);/* * write.c */ +#ifdef CONFIG_AFS_FSCACHE extern int afs_set_page_dirty (struct page *); +#else +#define afs_set_page_dirty __set_page_dirty_nobuffers +#endif extern int afs_write_begin (struct file *file,struct address_space *mapping,loff_t pos,unsigned len,unsigned flags,struct page **pagep,void **fsdata);
fs/afs/super.c +1 -0
@@ -55,6 +55,7 @@ int afs_net_id;static const struct super_operations afs_super_ops = { .statfs = afs_statfs, .alloc_inode = afs_alloc_inode, + .write_inode = afs_write_inode, .drop_inode = afs_drop_inode, .destroy_inode = afs_destroy_inode, .free_inode = afs_free_inode,
fs/afs/write.c +75 -12
180 lines not on this page · raw message
David Howells
maintainerPATCH v4 58/68 afs: Skip truncation on the server of data we haven't written yet
In reply to David HowellsDon't send a truncation RPC to the server if we're only shortening data that's in the pagecache and is beyond the server's EOF.
Also don't automatically force writeback on setattr, but do wait to store RPCs that are in the region to be removed on a shortening truncation.
---
fs/afs/inode.c | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-)
fs/afs/inode.c +35 -10
@@ -848,42 +848,67 @@ static const struct afs_operation_ops afs_setattr_operation = {int afs_setattr (struct user_namespace *mnt_userns,struct dentry *dentry,struct iattr *attr) { +const unsigned int supported = +ATTR_SIZE |ATTR_MODE |ATTR_UID |ATTR_GID | +ATTR_MTIME |ATTR_MTIME_SET |ATTR_TIMES_SET |ATTR_TOUCH ;struct afs_operation *op;struct afs_vnode *vnode =AFS_FS_I (d_inode (dentry)); +struct inode *inode = &vnode->vfs_inode; +loff_t i_size;int ret;_enter ("{%llx:%llu},{n=%pd},%x" , vnode->fid.vid, vnode->fid.vnode, dentry, attr->ia_valid); -if (!(attr->ia_valid & (ATTR_SIZE |ATTR_MODE |ATTR_UID |ATTR_GID | -ATTR_MTIME |ATTR_MTIME_SET |ATTR_TIMES_SET | -ATTR_TOUCH ))) { +if (!(attr->ia_valid & supported)) {_leave (" = 0 [unsupported]" );return 0 ; } + i_size =i_size_read (inode);if (attr->ia_valid &ATTR_SIZE ) { -if (!S_ISREG (vnode->vfs_inode.i_mode)) +if (!S_ISREG (inode->i_mode))return -EISDIR ; - ret =inode_newsize_ok (&vnode->vfs_inode, attr->ia_size); + ret =inode_newsize_ok (inode, attr->ia_size);if (ret)return ret; -if (attr->ia_size ==i_size_read (&vnode->vfs_inode)) +if (attr->ia_size == i_size) attr->ia_valid &= ~ATTR_SIZE ; }fscache_use_cookie (afs_vnode_cache (vnode), true); -/* flush any dirty data outstanding on a regular file */ -if (S_ISREG (vnode->vfs_inode.i_mode)) -filemap_write_and_wait (vnode->vfs_inode.i_mapping); -/* Prevent any new writebacks from starting whilst we do this. */ down_write (&vnode->validate_lock); +if ((attr->ia_valid &ATTR_SIZE ) &&S_ISREG (inode->i_mode)) { +loff_t size = attr->ia_size; + +/* Wait for any outstanding writes to the server to complete */ +loff_t from =min (size, i_size); +loff_t to =max (size, i_size); + ret =filemap_fdatawait_range (inode->i_mapping, from, to); +if (ret <0 ) + goto out_unlock; + +/* Don't talk to the server if we're just shortening in-memory +* writes that haven't gone to the server yet. +*/ +if (!(attr->ia_valid & (supported & ~ATTR_SIZE & ~ATTR_MTIME )) && + attr->ia_size < i_size && + attr->ia_size > vnode->status.size) { +truncate_pagecache (inode, attr->ia_size); +fscache_resize_cookie (afs_vnode_cache (vnode), + attr->ia_size); +i_size_write (inode, attr->ia_size); + ret =0 ; + goto out_unlock; + } + } + op =afs_alloc_operation (((attr->ia_valid &ATTR_FILE ) ?afs_file_key (attr->ia_file) :NULL ), vnode->volume);
David Howells
maintainerPATCH v4 59/68 9p: Use fscache indexing rewrite and reenable caching
In reply to David HowellsChange the 9p filesystem to take account of the changes to fscache's indexing rewrite and reenable caching in 9p.
The following changes have been made:
(1) The fscache_netfs struct is no more, and there's no need to register the filesystem as a whole.
(2) The session cookie is now an fscache_volume cookie, allocated with fscache_acquire_volume(). That takes three parameters: a string representing the "volume" in the index, a string naming the cache to use (or NULL) and a u64 that conveys coherency metadata for the volume.
For 9p, I've made it render the volume name string as:
"9p,<devname>,<cachetag>"
where the cachetag is replaced by the aname if it wasn't supplied.
This probably needs rethinking a bit as the aname can have slashes in it. It might be better to hash the cachetag and use the hash or I could substitute commas for the slashes or something.
(3) The fscache_cookie_def is no more and needed information is passed directly to fscache_acquire_cookie(). The cache no longer calls back into the filesystem, but rather metadata changes are indicated at other times.
fscache_acquire_cookie() is passed the same keying and coherency information as before.
(4) The functions to set/reset/flush cookies are removed and fscache_use_cookie() and fscache_unuse_cookie() are used instead.
fscache_use_cookie() is passed a flag to indicate if the cookie is opened for writing. fscache_unuse_cookie() is passed updates for the metadata if we changed it (ie. if the file was opened for writing).
These are called when the file is opened or closed.
(5) wait_on_page_bit[_killable]() is replaced with the specific wait functions for the bits waited upon.
(6) I've got rid of some of the 9p-specific cache helper functions and called things like fscache_relinquish_cookie() directly as they'll optimise away if v9fs_inode_cookie() returns an unconditional NULL (which will be the case if CONFIG_9P_FSCACHE=n).
(7) v9fs_vfs_setattr() is made to call fscache_resize() to change the size of the cache object.
Notes:
(A) We should call fscache_invalidate() if we detect that the server's copy of a file got changed by a third party, but I don't know where to do that. We don't need to do that when allocating the cookie as we get a check-and-invalidate when we initially bind to the cache object.
(B) The copy-to-cache-on-writeback side of things will be handled in separate patch.
Changes
=======
ver #3:
- Canonicalise the cookie key and coherency data to make them endianness-independent.
ver #2:
- Use gfpflags_allow_blocking() rather than using flag directly.
- fscache_acquire_volume() now returns errors.
---
fs/9p/Kconfig | 2 fs/9p/cache.c | 195 +++++++++--------------------------------------- fs/9p/cache.h | 25 +----- fs/9p/v9fs.c | 17 +--- fs/9p/v9fs.h | 13 +++ fs/9p/vfs_addr.c | 8 +- fs/9p/vfs_dir.c | 13 +++ fs/9p/vfs_file.c | 3 - fs/9p/vfs_inode.c | 22 +++-- fs/9p/vfs_inode_dotl.c | 3 - 10 files changed, 91 insertions(+), 210 deletions(-)
fs/9p/Kconfig +1 -1
@@ -14,7 +14,7 @@ config 9P_FS if 9P_FS config 9P_FSCACHE bool "Enable 9P client caching support" - depends on 9P_FS=m && FSCACHE_OLD_API || 9P_FS=y && FSCACHE_OLD_API=y + depends on 9P_FS=m && FSCACHE || 9P_FS=y && FSCACHE=y help Choose Y here to enable persistent, read-only local caching support for 9p clients using FS-Cache
fs/9p/cache.c +35 -160
221 lines not on this page · raw message
fs/9p/cache.h +3 -22
@@ -7,26 +7,15 @@#ifndef _9P_CACHE_H#define _9P_CACHE_H -#define FSCACHE_USE_NEW_IO_API +#include <linux/fscache.h> #ifdef CONFIG_9P_FSCACHE -extern struct fscache_netfs v9fs_cache_netfs; -extern const struct fscache_cookie_def v9fs_cache_session_index_def; -extern const struct fscache_cookie_def v9fs_cache_inode_index_def; - -extern void v9fs_cache_session_get_cookie (struct v9fs_session_info *v9ses); -extern void v9fs_cache_session_put_cookie (struct v9fs_session_info *v9ses); +extern int v9fs_cache_session_get_cookie (struct v9fs_session_info *v9ses, +const char *dev_name);extern void v9fs_cache_inode_get_cookie (struct inode *inode); -extern void v9fs_cache_inode_put_cookie (struct inode *inode); -extern void v9fs_cache_inode_flush_cookie (struct inode *inode); -extern void v9fs_cache_inode_set_cookie (struct inode *inode,struct file *filp); -extern void v9fs_cache_inode_reset_cookie (struct inode *inode); - -extern int __v9fs_cache_register (void ); -extern void __v9fs_cache_unregister (void );#else /* CONFIG_9P_FSCACHE */
@@ -34,13 +23,5 @@ static inline void v9fs_cache_inode_get_cookie(struct inode *inode) { } -static inline void v9fs_cache_inode_put_cookie (struct inode *inode) -{ -} - -static inline void v9fs_cache_inode_set_cookie (struct inode *inode,struct file *file) -{ -} -#endif /* CONFIG_9P_FSCACHE */ #endif /* _9P_CACHE_H */
fs/9p/v9fs.c +6 -11
@@ -469,7 +469,11 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,#ifdef CONFIG_9P_FSCACHE /* register the session for caching */ -v9fs_cache_session_get_cookie (v9ses); +if (v9ses->cache ==CACHE_LOOSE || v9ses->cache ==CACHE_FSCACHE ) { + rc =v9fs_cache_session_get_cookie (v9ses, dev_name); +if (rc <0 ) + goto err_clnt; + }#endif spin_lock (&v9fs_sessionlist_lock);list_add (&v9ses->slist, &v9fs_sessionlist);
@@ -502,8 +506,7 @@ void v9fs_session_close(struct v9fs_session_info *v9ses) }#ifdef CONFIG_9P_FSCACHE -if (v9ses->fscache) -v9fs_cache_session_put_cookie (v9ses); +fscache_relinquish_volume (v9fs_session_cache (v9ses),NULL , false);kfree (v9ses->cachetag);#endif kfree (v9ses->uname);
@@ -665,20 +668,12 @@ static int v9fs_cache_register(void) ret =v9fs_init_inode_cache ();if (ret <0 )return ret; -#ifdef CONFIG_9P_FSCACHE - ret =fscache_register_netfs (&v9fs_cache_netfs); -if (ret <0 ) -v9fs_destroy_inode_cache (); -#endif return ret; }static void v9fs_cache_unregister (void ) {v9fs_destroy_inode_cache (); -#ifdef CONFIG_9P_FSCACHE -fscache_unregister_netfs (&v9fs_cache_netfs); -#endif }/**
fs/9p/v9fs.h +11 -2
@@ -89,7 +89,7 @@ struct v9fs_session_info {unsigned int cache;#ifdef CONFIG_9P_FSCACHE char *cachetag; -struct fscache_cookie *fscache; +struct fscache_volume *fscache;#endif char *uname;/* user name to mount as */
@@ -109,7 +109,6 @@ struct v9fs_session_info {struct v9fs_inode {#ifdef CONFIG_9P_FSCACHE -struct mutex fscache_lock;struct fscache_cookie *fscache;#endif struct p9_qid qid;
@@ -133,6 +132,16 @@ static inline struct fscache_cookie *v9fs_inode_cookie(struct v9fs_inode *v9inod#endif } +static inline struct fscache_volume *v9fs_session_cache (struct v9fs_session_info *v9ses) +{ +#ifdef CONFIG_9P_FSCACHE +return v9ses->fscache; +#else +return NULL ; +#endif +} + +extern int v9fs_show_options (struct seq_file *m,struct dentry *root); struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
fs/9p/vfs_addr.c +5 -3
@@ -76,7 +76,9 @@ static void v9fs_req_cleanup(struct address_space *mapping, void *priv)*/ static bool v9fs_is_cache_enabled (struct inode *inode) { -return fscache_cookie_enabled (v9fs_inode_cookie (V9FS_I (inode))); +struct fscache_cookie *cookie =v9fs_inode_cookie (V9FS_I (inode)); + +return fscache_cookie_enabled (cookie) && cookie->cache_priv; }/**
@@ -88,7 +90,7 @@ static int v9fs_begin_cache_operation(struct netfs_read_request *rreq)#ifdef CONFIG_9P_FSCACHE struct fscache_cookie *cookie =v9fs_inode_cookie (V9FS_I (rreq->inode)); -return fscache_begin_read_operation (rreq, cookie); +return fscache_begin_read_operation (&rreq->cache_resources, cookie);#else return -ENOBUFS ;#endif
@@ -140,7 +142,7 @@ static int v9fs_release_page(struct page *page, gfp_t gfp)return 0 ;#ifdef CONFIG_9P_FSCACHE if (folio_test_fscache (folio)) { -if (!(gfp & __GFP_DIRECT_RECLAIM) || !(gfp & __GFP_FS)) +if (!gfpflags_allow_blocking (gfp) || !(gfp & __GFP_FS))return 0 ;folio_wait_fscache (folio); }
fs/9p/vfs_dir.c +13 -0
@@ -17,6 +17,7 @@#include <linux/idr.h> #include <linux/slab.h> #include <linux/uio.h> +#include <linux/fscache.h> #include <net/9p/9p.h> #include <net/9p/client.h>
@@ -205,7 +206,10 @@ static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)int v9fs_dir_release (struct inode *inode,struct file *filp) { +struct v9fs_inode *v9inode =V9FS_I (inode);struct p9_fid *fid; +__le32 version; +loff_t i_size; fid = filp->private_data; p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
@@ -216,6 +220,15 @@ int v9fs_dir_release(struct inode *inode, struct file *filp)spin_unlock (&inode->i_lock);p9_client_clunk (fid); } + +if ((filp->f_mode &FMODE_WRITE )) { + version =cpu_to_le32 (v9inode->qid.version); + i_size =i_size_read (inode); +fscache_unuse_cookie (v9fs_inode_cookie (v9inode), + &version, &i_size); + }else { +fscache_unuse_cookie (v9fs_inode_cookie (v9inode),NULL ,NULL ); + }return 0 ; }
fs/9p/vfs_file.c +2 -1
@@ -93,7 +93,8 @@ int v9fs_file_open(struct inode *inode, struct file *file) }mutex_unlock (&v9inode->v_mutex);if (v9ses->cache ==CACHE_LOOSE || v9ses->cache ==CACHE_FSCACHE ) -v9fs_cache_inode_set_cookie (inode, file); +fscache_use_cookie (v9fs_inode_cookie (v9inode), + file->f_mode &FMODE_WRITE );v9fs_open_fid_add (inode, fid);return 0 ; out_error:
fs/9p/vfs_inode.c +13 -9
@@ -233,7 +233,6 @@ struct inode *v9fs_alloc_inode(struct super_block *sb)return NULL ;#ifdef CONFIG_9P_FSCACHE v9inode->fscache =NULL ; -mutex_init (&v9inode->fscache_lock);#endif v9inode->writeback_fid =NULL ; v9inode->cache_validity =0 ;
@@ -386,7 +385,7 @@ void v9fs_evict_inode(struct inode *inode)clear_inode (inode);filemap_fdatawrite (&inode->i_data); -v9fs_cache_inode_put_cookie (inode); +fscache_relinquish_cookie (v9fs_inode_cookie (v9inode), false);/* clunk the fid stashed in writeback_fid */ if (v9inode->writeback_fid) {p9_client_clunk (v9inode->writeback_fid);
@@ -869,7 +868,8 @@ v9fs_vfs_atomic_open(struct inode *dir, struct dentry *dentry, file->private_data = fid;if (v9ses->cache ==CACHE_LOOSE || v9ses->cache ==CACHE_FSCACHE ) -v9fs_cache_inode_set_cookie (d_inode (dentry), file); +fscache_use_cookie (v9fs_inode_cookie (v9inode), + file->f_mode &FMODE_WRITE );v9fs_open_fid_add (inode, fid); file->f_mode |=FMODE_CREATED ;
@@ -1072,6 +1072,8 @@ static int v9fs_vfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, struct iattr *iattr) { int retval, use_dentry = 0; +struct inode *inode =d_inode (dentry); +struct v9fs_inode *v9inode =V9FS_I (inode);struct v9fs_session_info *v9ses;struct p9_fid *fid =NULL ;struct p9_wstat wstat;
@@ -1117,7 +1119,7 @@ static int v9fs_vfs_setattr(struct user_namespace *mnt_userns,/* Write all dirty data */ if (d_is_reg (dentry)) -filemap_write_and_wait (d_inode (dentry)->i_mapping); +filemap_write_and_wait (inode->i_mapping); retval =p9_client_wstat (fid, &wstat);
@@ -1128,13 +1130,15 @@ static int v9fs_vfs_setattr(struct user_namespace *mnt_userns,return retval;if ((iattr->ia_valid &ATTR_SIZE ) && - iattr->ia_size !=i_size_read (d_inode (dentry))) -truncate_setsize (d_inode (dentry), iattr->ia_size); + iattr->ia_size !=i_size_read (inode)) { +truncate_setsize (inode, iattr->ia_size); +fscache_resize_cookie (v9fs_inode_cookie (v9inode), iattr->ia_size); + } -v9fs_invalidate_inode_attr (d_inode (dentry)); +v9fs_invalidate_inode_attr (inode); -setattr_copy (&init_user_ns,d_inode (dentry), iattr); -mark_inode_dirty (d_inode (dentry)); +setattr_copy (&init_user_ns, inode, iattr); +mark_inode_dirty (inode);return 0 ; }
fs/9p/vfs_inode_dotl.c +2 -1
@@ -344,7 +344,8 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry, goto err_clunk_old_fid; file->private_data = ofid;if (v9ses->cache ==CACHE_LOOSE || v9ses->cache ==CACHE_FSCACHE ) -v9fs_cache_inode_set_cookie (inode, file); +fscache_use_cookie (v9fs_inode_cookie (v9inode), + file->f_mode &FMODE_WRITE );v9fs_open_fid_add (inode, ofid); file->f_mode |=FMODE_CREATED ; out:
David Howells
maintainerPATCH v4 60/68 9p: Copy local writes to the cache when writing to the server
In reply to David HowellsWhen writing to the server from v9fs_vfs_writepage(), copy the data to the cache object too.
To make this possible, the cookie must have its active users count incremented when the page is dirtied and kept incremented until we manage to clean up all the pages. This allows the writeback to take place after the last file struct is released.
This is done by taking a use on the cookie in v9fs_set_page_dirty() if we haven't already done so (controlled by the I_PINNING_FSCACHE_WB flag) and dropping the pin in v9fs_write_inode() if __writeback_single_inode() clears all the outstanding dirty pages (conveyed by the unpinned_fscache_wb flag in the writeback_control struct).
Inode eviction must also clear the flag after truncating away all the outstanding pages.
In the future this will be handled more gracefully by netfslib.
Changes
=======
ver #3:
- Canonicalise the coherency data to make it endianness-independent.
ver #2:
- Fix an unused-var warning due to CONFIG_9P_FSCACHE=n[1].
---
fs/9p/vfs_addr.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- fs/9p/vfs_inode.c | 4 ++++ fs/9p/vfs_super.c | 3 +++ 3 files changed, 52 insertions(+), 1 deletion(-)
fs/9p/vfs_addr.c +45 -1
@@ -137,6 +137,7 @@ static void v9fs_vfs_readahead(struct readahead_control *ractl)static int v9fs_release_page (struct page *page,gfp_t gfp) {struct folio *folio =page_folio (page); +struct inode *inode =folio_inode (folio);if (folio_test_private (folio))return 0 ;
@@ -147,6 +148,7 @@ static int v9fs_release_page(struct page *page, gfp_t gfp) folio_wait_fscache(folio); }#endif +fscache_note_page_release (v9fs_inode_cookie (V9FS_I(inode )));return 1 ; }
@@ -165,10 +167,25 @@ static void v9fs_invalidate_page(struct page *page, unsigned int offset,folio_wait_fscache (folio); } +static void v9fs_write_to_cache_done (void *priv,ssize_t transferred_or_error, +bool was_async) +{ +struct v9fs_inode *v9inode = priv; +__le32 version; + +if (IS_ERR_VALUE (transferred_or_error) && + transferred_or_error != -ENOBUFS ) { + version =cpu_to_le32 (v9inode->qid.version); +fscache_invalidate (v9fs_inode_cookie (v9inode), &version, +i_size_read (&v9inode->vfs_inode),0 ); + } +} +static int v9fs_vfs_write_folio_locked (struct folio *folio) {struct inode *inode =folio_inode (folio);struct v9fs_inode *v9inode =V9FS_I (inode); +struct fscache_cookie *cookie =v9fs_inode_cookie (v9inode);loff_t start =folio_pos (folio);loff_t i_size =i_size_read (inode);struct iov_iter from;
@@ -185,10 +202,21 @@ static int v9fs_vfs_write_folio_locked(struct folio *folio)/* We should have writeback_fid always set */ BUG_ON (!v9inode->writeback_fid); +folio_wait_fscache (folio);folio_start_writeback (folio);p9_client_write (v9inode->writeback_fid, start, &from, &err); +if (err ==0 && +fscache_cookie_enabled (cookie) && +test_bit (FSCACHE_COOKIE_IS_CACHING , &cookie->flags)) { +folio_start_fscache (folio); +fscache_write_to_cache (v9fs_inode_cookie (v9inode), +folio_mapping (folio), start, len, i_size, + v9fs_write_to_cache_done, v9inode, + true); + } +folio_end_writeback (folio);return err; }
@@ -307,6 +335,7 @@ static int v9fs_write_end(struct file *filp, struct address_space *mapping,loff_t last_pos = pos + copied;struct folio *folio =page_folio (subpage);struct inode *inode = mapping->host; +struct v9fs_inode *v9inode =V9FS_I (inode);p9_debug (P9_DEBUG_VFS ,"filp %p, mapping %p\n" , filp, mapping);
@@ -326,6 +355,7 @@ static int v9fs_write_end(struct file *filp, struct address_space *mapping,if (last_pos > inode->i_size) {inode_add_bytes (inode, last_pos - inode->i_size);i_size_write (inode, last_pos); +fscache_update_cookie (v9fs_inode_cookie (v9inode),NULL , &last_pos); }folio_mark_dirty (folio); out:
@@ -335,11 +365,25 @@ static int v9fs_write_end(struct file *filp, struct address_space *mapping,return copied; } +#ifdef CONFIG_9P_FSCACHE +/* +* Mark a page as having been made dirty and thus needing writeback. We also +* need to pin the cache object to write back to. +*/ +static int v9fs_set_page_dirty (struct page *page) +{ +struct v9fs_inode *v9inode =V9FS_I (page->mapping->host); + +return fscache_set_page_dirty (page,v9fs_inode_cookie (v9inode)); +} +#else +#define v9fs_set_page_dirty __set_page_dirty_nobuffers +#endif const struct address_space_operations v9fs_addr_operations = { .readpage = v9fs_vfs_readpage, .readahead = v9fs_vfs_readahead, - .set_page_dirty = __set_page_dirty_nobuffers, + .set_page_dirty = v9fs_set_page_dirty, .writepage = v9fs_vfs_writepage, .write_begin = v9fs_write_begin, .write_end = v9fs_write_end,
fs/9p/vfs_inode.c +4 -0
@@ -380,8 +380,12 @@ struct inode *v9fs_get_inode(struct super_block *sb, umode_t mode, dev_t rdev)void v9fs_evict_inode (struct inode *inode) {struct v9fs_inode *v9inode =V9FS_I (inode); +__le32 version;truncate_inode_pages_final (&inode->i_data); + version =cpu_to_le32 (v9inode->qid.version); +fscache_clear_inode_writeback (v9fs_inode_cookie (v9inode), inode, + &version);clear_inode (inode);filemap_fdatawrite (&inode->i_data);
fs/9p/vfs_super.c +3 -0
@@ -20,6 +20,7 @@#include <linux/slab.h> #include <linux/statfs.h> #include <linux/magic.h> +#include <linux/fscache.h> #include <net/9p/9p.h> #include <net/9p/client.h>
@@ -309,6 +310,7 @@ static int v9fs_write_inode(struct inode *inode,__mark_inode_dirty (inode,I_DIRTY_DATASYNC );return ret; } +fscache_unpin_writeback (wbc,v9fs_inode_cookie (v9inode));return 0 ; }
@@ -332,6 +334,7 @@ static int v9fs_write_inode_dotl(struct inode *inode,__mark_inode_dirty (inode,I_DIRTY_DATASYNC );return ret; } +fscache_unpin_writeback (wbc,v9fs_inode_cookie (v9inode));return 0 ; }
David Howells
maintainerPATCH v4 61/68 nfs: Convert to new fscache volume/cookie API
In reply to David HowellsFrom: Dave Wysochanski <[email protected]>
Change the nfs filesystem to support fscache's indexing rewrite and reenable caching in nfs.
The following changes have been made:
(1) The fscache_netfs struct is no more, and there's no need to register the filesystem as a whole.
(2) The session cookie is now an fscache_volume cookie, allocated with fscache_acquire_volume(). That takes three parameters: a string representing the "volume" in the index, a string naming the cache to use (or NULL) and a u64 that conveys coherency metadata for the volume.
For nfs, I've made it render the volume name string as:
"nfs,<ver>,<family>,<address>,<port>,<fsidH>,<fsidL>*<,param>[,<uniq>]"
(3) The fscache_cookie_def is no more and needed information is passed directly to fscache_acquire_cookie(). The cache no longer calls back into the filesystem, but rather metadata changes are indicated at other times.
fscache_acquire_cookie() is passed the same keying and coherency information as before.
(4) fscache_enable/disable_cookie() have been removed.
Call fscache_use_cookie() and fscache_unuse_cookie() when a file is opened or closed to prevent a cache file from being culled and to keep resources to hand that are needed to do I/O.
If a file is opened for writing, we invalidate it with FSCACHE_INVAL_DIO_WRITE in lieu of doing writeback to the cache, thereby making it cease caching until all currently open files are closed. This should give the same behaviour as the uptream code. Making the cache store local modifications isn't straightforward for NFS, so that's left for future patches.
(5) fscache_invalidate() now needs to be given uptodate auxiliary data and a file size. It also takes a flag to indicate if this was due to a DIO write.
(6) Call nfs_fscache_invalidate() with FSCACHE_INVAL_DIO_WRITE on a file to which a DIO write is made.
(7) Call fscache_note_page_release() from nfs_release_page().
(8) Use a killable wait in nfs_vm_page_mkwrite() when waiting for PG_fscache to be cleared.
(9) The functions to read and write data to/from the cache are stubbed out pending a conversion to use netfslib.
Changes
=======
ver #3:
- Added missing =n fallback for nfs_fscache_release_file()[1][2].
ver #2:
- Use gfpflags_allow_blocking() rather than using flag directly.
- fscache_acquire_volume() now returns errors.
- Remove NFS_INO_FSCACHE as it's no longer used.
- Need to unuse a cookie on file-release, not inode-clear.
---
fs/nfs/Kconfig | 2 fs/nfs/Makefile | 2 fs/nfs/client.c | 4 fs/nfs/direct.c | 2 fs/nfs/file.c | 13 + fs/nfs/fscache-index.c | 140 --------------- fs/nfs/fscache.c | 434 +++++++++++---------------------------------- fs/nfs/fscache.h | 127 ++++--------- fs/nfs/inode.c | 11 - fs/nfs/nfstrace.h | 1 fs/nfs/super.c | 28 ++- fs/nfs/write.c | 1 include/linux/nfs_fs.h | 1 include/linux/nfs_fs_sb.h | 9 - 14 files changed, 172 insertions(+), 603 deletions(-) delete mode 100644 fs/nfs/fscache-index.c
fs/nfs/Kconfig +1 -1
@@ -170,7 +170,7 @@ config ROOT_NFS config NFS_FSCACHE bool "Provide NFS client caching support" - depends on NFS_FS=m && FSCACHE_OLD_API || NFS_FS=y && FSCACHE_OLD_API=y + depends on NFS_FS=m && FSCACHE || NFS_FS=y && FSCACHE=y help Say Y here if you want NFS data to be cached locally on disc through the general filesystem cache manager
fs/nfs/Makefile +1 -1
@@ -12,7 +12,7 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \ export.o sysfs.o fs_context.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o nfs-$(CONFIG_SYSCTL) += sysctl.o -nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o +nfs-$(CONFIG_NFS_FSCACHE) += fscache.o obj-$(CONFIG_NFS_V2) += nfsv2.o nfsv2-y := nfs2super.o proc.o nfs2xdr.o
fs/nfs/client.c +0 -4
@@ -183,8 +183,6 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) clp->cl_net =get_net (cl_init->net); clp->cl_principal ="*" ; -nfs_fscache_get_client_cookie (clp); -return clp; error_cleanup:
@@ -238,8 +236,6 @@ static void pnfs_init_server(struct nfs_server *server)*/ void nfs_free_client (struct nfs_client *clp) { -nfs_fscache_release_client_cookie (clp); -/* -EIO all pending I/O */ if (!IS_ERR (clp->cl_rpcclient))rpc_shutdown_client (clp->cl_rpcclient);
fs/nfs/direct.c +2 -0
@@ -59,6 +59,7 @@#include "internal.h" #include "iostat.h" #include "pnfs.h" +#include "fscache.h" #define NFSDBG_FACILITY NFSDBG_VFS
@@ -959,6 +960,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, struct iov_iter *iter) }else { result = requested; } +nfs_fscache_invalidate (inode,FSCACHE_INVAL_DIO_WRITE ); out_release:nfs_direct_req_release (dreq); out:
fs/nfs/file.c +8 -5
@@ -84,6 +84,7 @@ nfs_file_release(struct inode *inode, struct file *filp)nfs_inc_stats (inode,NFSIOS_VFSRELEASE );nfs_file_clear_open_context (filp); +nfs_fscache_release_file (inode, filp);return 0 ; }EXPORT_SYMBOL_GPL (nfs_file_release);
@@ -415,8 +416,7 @@ static void nfs_invalidate_page(struct page *page, unsigned int offset,return ;/* Cancel any unstarted writes on this page */ nfs_wb_page_cancel (page_file_mapping (page)->host, page); - -nfs_fscache_invalidate_page (page, page->mapping->host); +wait_on_page_fscache (page); }/*
@@ -475,12 +475,11 @@ static void nfs_check_dirty_writeback(struct page *page,static int nfs_launder_page (struct page *page) {struct inode *inode =page_file_mapping (page)->host; -struct nfs_inode *nfsi =NFS_I (inode);dfprintk (PAGECACHE ,"NFS: launder_page(%ld, %llu)\n" , inode->i_ino, (long long )page_offset (page)); -nfs_fscache_wait_on_page_write (nfsi, page); +wait_on_page_fscache (page);return nfs_wb_page (inode, page); }
@@ -555,7 +554,11 @@ static vm_fault_t nfs_vm_page_mkwrite(struct vm_fault *vmf)sb_start_pagefault (inode->i_sb);/* make sure the cache has finished storing the page */ -nfs_fscache_wait_on_page_write (NFS_I (inode), page); +if (PageFsCache (page) && +wait_on_page_fscache_killable (vmf->page) <0 ) { + ret =VM_FAULT_RETRY ; + goto out; + }wait_on_bit_action (&NFS_I (inode)->flags,NFS_INO_INVALIDATING , nfs_wait_bit_killable,TASK_KILLABLE );
fs/nfs/fscache-index.c deleted +0 -140
140 lines not on this page · raw message
fs/nfs/fscache.c +105 -329
554 lines not on this page · raw message
fs/nfs/fscache.h +35 -92
194 lines not on this page · raw message
fs/nfs/inode.c +2 -9
@@ -209,7 +209,7 @@ void nfs_set_cache_invalid(struct inode *inode, unsigned long flags)if (!nfs_has_xattr_cache (nfsi)) flags &= ~NFS_INO_INVALID_XATTR ;if (flags &NFS_INO_INVALID_DATA ) -nfs_fscache_invalidate (inode); +nfs_fscache_invalidate (inode,0 ); flags &= ~(NFS_INO_REVAL_PAGECACHE |NFS_INO_REVAL_FORCED ); nfsi->cache_validity |= flags;
@@ -1289,6 +1289,7 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map {int ret; +nfs_fscache_invalidate (inode,0 );if (mapping->nrpages !=0 ) {if (S_ISREG (inode->i_mode)) { ret =nfs_sync_mapping (mapping);
@@ -1300,7 +1301,6 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapreturn ret; }nfs_inc_stats (inode,NFSIOS_DATAINVALIDATE ); -nfs_fscache_wait_on_invalidate (inode); dfprintk(PAGECACHE, "NFS: (%s/%Lu) data cache invalidated\n", inode->i_sb->s_id,
@@ -2374,10 +2374,6 @@ static int __init init_nfs_fs(void)if (err <0 ) goto out9; - err =nfs_fscache_register (); -if (err <0 ) - goto out8; - err =nfsiod_start ();if (err) goto out7;
@@ -2429,8 +2425,6 @@ static int __init init_nfs_fs(void) out6:nfsiod_stop (); out7: -nfs_fscache_unregister (); -out8:unregister_pernet_subsys (&nfs_net_ops); out9:nfs_sysfs_exit ();
@@ -2445,7 +2439,6 @@ static void __exit exit_nfs_fs(void)nfs_destroy_readpagecache ();nfs_destroy_inodecache ();nfs_destroy_nfspagecache (); -nfs_fscache_unregister ();unregister_pernet_subsys (&nfs_net_ops);rpc_proc_unregister (&init_net,"nfs" );unregister_nfs_fs ();
fs/nfs/nfstrace.h +0 -1
@@ -42,7 +42,6 @@ { BIT(NFS_INO_ACL_LRU_SET), "ACL_LRU_SET" }, \ { BIT(NFS_INO_INVALIDATING), "INVALIDATING" }, \ { BIT(NFS_INO_FSCACHE), "FSCACHE" }, \ - { BIT(NFS_INO_FSCACHE_LOCK), "FSCACHE_LOCK" }, \ { BIT(NFS_INO_LAYOUTCOMMIT), "NEED_LAYOUTCOMMIT" }, \ { BIT(NFS_INO_LAYOUTCOMMITTING), "LAYOUTCOMMIT" }, \ { BIT(NFS_INO_LAYOUTSTATS), "LAYOUTSTATS" }, \
fs/nfs/super.c +15 -13
@@ -1204,42 +1204,42 @@ static int nfs_compare_super(struct super_block *sb, struct fs_context *fc) }#ifdef CONFIG_NFS_FSCACHE -static void nfs_get_cache_cookie (struct super_block *sb, -struct nfs_fs_context *ctx) +static int nfs_get_cache_cookie (struct super_block *sb, +struct nfs_fs_context *ctx) {struct nfs_server *nfss =NFS_SB (sb);char *uniq =NULL ;int ulen =0 ; - nfss->fscache_key =NULL ; nfss->fscache =NULL ;if (!ctx) -return ; +return 0 ;if (ctx->clone_data.sb) {struct nfs_server *mnt_s =NFS_SB (ctx->clone_data.sb);if (!(mnt_s->options &NFS_OPTION_FSCACHE )) -return ; -if (mnt_s->fscache_key) { - uniq = mnt_s->fscache_key->key.uniquifier; - ulen = mnt_s->fscache_key->key.uniq_len; +return 0 ; +if (mnt_s->fscache_uniq) { + uniq = mnt_s->fscache_uniq; + ulen =strlen (uniq); } }else {if (!(ctx->options &NFS_OPTION_FSCACHE )) -return ; +return 0 ;if (ctx->fscache_uniq) { uniq = ctx->fscache_uniq; ulen =strlen (ctx->fscache_uniq); } } -nfs_fscache_get_super_cookie (sb, uniq, ulen); +return nfs_fscache_get_super_cookie (sb, uniq, ulen); }#else -static void nfs_get_cache_cookie (struct super_block *sb, -struct nfs_fs_context *ctx) +static int nfs_get_cache_cookie (struct super_block *sb, +struct nfs_fs_context *ctx) { +return 0 ; }#endif
@@ -1299,7 +1299,9 @@ int nfs_get_tree_common(struct fs_context *fc) s->s_blocksize_bits = bsize; s->s_blocksize =1U << bsize; } -nfs_get_cache_cookie (s, ctx); + error =nfs_get_cache_cookie (s, ctx); +if (error <0 ) + goto error_splat_super; } error =nfs_get_root (s, fc);
fs/nfs/write.c +1 -0
@@ -294,6 +294,7 @@ static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int cnfs_inc_stats (inode,NFSIOS_EXTENDWRITE ); out:spin_unlock (&inode->i_lock); +nfs_fscache_invalidate (inode,0 ); }/* A writeback failed: mark the page as bad, and invalidate the page cache */
include/linux/nfs_fs.h +0 -1
@@ -275,7 +275,6 @@ struct nfs4_copy_state {#define NFS_INO_ACL_LRU_SET (2)/* Inode is on the LRU list */ #define NFS_INO_INVALIDATING (3)/* inode is being invalidated */ #define NFS_INO_FSCACHE (5)/* inode can be cached by FS-Cache */ -#define NFS_INO_FSCACHE_LOCK (6)/* FS-Cache cookie management lock */ #define NFS_INO_FORCE_READDIR (7)/* force readdirplus */ #define NFS_INO_LAYOUTCOMMIT (9)/* layoutcommit required */ #define NFS_INO_LAYOUTCOMMITTING (10)/* layoutcommit inflight */
include/linux/nfs_fs_sb.h +2 -7
@@ -120,11 +120,6 @@ struct nfs_client {* This is used to generate the mv0 callback address. */ char cl_ipaddr[48 ]; - -#ifdef CONFIG_NFS_FSCACHE -struct fscache_cookie *fscache;/* client index cache cookie */ -#endif -struct net *cl_net;struct list_head pending_cb_stateids; };
@@ -194,8 +189,8 @@ struct nfs_server {struct nfs_auth_info auth_info;/* parsed auth flavors */ #ifdef CONFIG_NFS_FSCACHE -struct nfs_fscache_key *fscache_key;/* unique key for superblock */ -struct fscache_cookie *fscache;/* superblock cookie */ +struct fscache_volume *fscache;/* superblock cookie */ +char *fscache_uniq;/* Uniquifier (or NULL) */ #endif u32 pnfs_blksize;/* layout_blksize attr */
David Howells
maintainerPATCH v4 62/68 nfs: Implement cache I/O by accessing the cache directly
In reply to David HowellsMove NFS to using fscache DIO API instead of the old upstream I/O API as that has been removed. This is a stopgap solution as the intention is that at sometime in the future, the cache will move to using larger blocks and won't be able to store individual pages in order to deal with the potential for data corruption due to the backing filesystem being able insert/remove bridging blocks of zeros into its extent list[1].
NFS then reads and writes cache pages synchronously and one page at a time.
The preferred change would be to use the netfs lib, but the new I/O API can be used directly. It's just that as the cache now needs to track data for itself, caching blocks may exceed page size...
This code is somewhat borrowed from my "fallback I/O" patchset[2].
Changes
=======
ver #3:
- Restore lost =n fallback for nfs_fscache_release_page()[2].
---
fs/fscache/io.c | 8 +++ fs/nfs/fscache.c | 126 ++++++++++++++++++++++++++++++++++++++--------- fs/nfs/fscache.h | 52 ++++--------------- fs/nfs/read.c | 25 +++------ fs/nfs/write.c | 7 ++- include/linux/fscache.h | 28 ++++++++++ 6 files changed, 163 insertions(+), 83 deletions(-)
fs/fscache/io.c +8 -0
@@ -150,6 +150,14 @@ int __fscache_begin_read_operation(struct netfs_cache_resources *cres, }EXPORT_SYMBOL (__fscache_begin_read_operation); +int __fscache_begin_write_operation (struct netfs_cache_resources *cres, +struct fscache_cookie *cookie) +{ +return fscache_begin_operation (cres, cookie,FSCACHE_WANT_PARAMS , + fscache_access_io_write); +} +EXPORT_SYMBOL (__fscache_begin_write_operation); +/** * fscache_set_page_dirty - Mark page dirty and pin a cache object for writeback * @page: The page being dirtied
fs/nfs/fscache.c +103 -23
151 lines not on this page · raw message
fs/nfs/fscache.h +11 -41
85 lines not on this page · raw message
fs/nfs/read.c +8 -17
@@ -123,7 +123,7 @@ static void nfs_readpage_release(struct nfs_page *req, int error)struct address_space *mapping =page_file_mapping (page);if (PageUptodate (page)) -nfs_readpage_to_fscache (inode, page,0 ); +nfs_readpage_to_fscache (inode, page);else if (!PageError (page) && !PagePrivate (page))generic_error_remove_page (mapping, page);unlock_page (page);
@@ -305,6 +305,12 @@ readpage_async_filler(void *data, struct page *page) aligned_len = min_t(unsigned int, ALIGN(len, rsize), PAGE_SIZE); +if (!IS_SYNC (page->mapping->host)) { + error =nfs_readpage_from_fscache (page->mapping->host, page); +if (error ==0 ) + goto out_unlock; + } + new =nfs_create_request (desc->ctx, page,0 , aligned_len);if (IS_ERR (new)) goto out_error;
@@ -320,6 +326,7 @@ readpage_async_filler(void *data, struct page *page)return 0 ; out_error: error =PTR_ERR (new); +out_unlock:unlock_page (page); out:return error;
@@ -366,12 +373,6 @@ int nfs_readpage(struct file *file, struct page *page) desc.ctx =get_nfs_open_context (nfs_file_open_context (file));xchg (&desc.ctx->error,0 ); -if (!IS_SYNC (inode)) { - ret =nfs_readpage_from_fscache (desc.ctx, inode, page); -if (ret ==0 ) - goto out_wait; - } -nfs_pageio_init_read (&desc.pgio, inode, false, &nfs_async_read_completion_ops);
@@ -381,7 +382,6 @@ int nfs_readpage(struct file *file, struct page *page)nfs_pageio_complete_read (&desc.pgio); ret = desc.pgio.pg_error <0 ? desc.pgio.pg_error :0 ; -out_wait:if (!ret) { ret =wait_on_page_locked_killable (page);if (!PageUptodate (page) && !ret)
@@ -419,14 +419,6 @@ int nfs_readpages(struct file *file, struct address_space *mapping, }else desc.ctx =get_nfs_open_context (nfs_file_open_context (file)); -/* attempt to read as many of the pages as possible from the cache -* - this returns -ENOBUFS immediately if the cookie is negative -*/ - ret =nfs_readpages_from_fscache (desc.ctx, inode, mapping, - pages, &nr_pages); -if (ret ==0 ) - goto read_complete;/* all pages were read */ -nfs_pageio_init_read (&desc.pgio, inode, false, &nfs_async_read_completion_ops);
@@ -434,7 +426,6 @@ int nfs_readpages(struct file *file, struct address_space *mapping,nfs_pageio_complete_read (&desc.pgio); -read_complete:put_nfs_open_context (desc.ctx); out:trace_nfs_aop_readahead_done (inode, nr_pages, ret);
fs/nfs/write.c +5 -2
@@ -2126,8 +2126,11 @@ int nfs_migrate_page(struct address_space *mapping, struct page *newpage,if (PagePrivate (page))return -EBUSY ; -if (!nfs_fscache_release_page (page,GFP_KERNEL )) -return -EBUSY ; +if (PageFsCache (page)) { +if (mode ==MIGRATE_ASYNC ) +return -EBUSY ; +wait_on_page_fscache (page); + }return migrate_page (mapping, newpage, page, mode); }
include/linux/fscache.h +28 -0
@@ -168,6 +168,7 @@ extern void __fscache_relinquish_cookie(struct fscache_cookie *, bool);extern void __fscache_resize_cookie (struct fscache_cookie *,loff_t );extern void __fscache_invalidate (struct fscache_cookie *,const void *,loff_t ,unsigned int );extern int __fscache_begin_read_operation (struct netfs_cache_resources *,struct fscache_cookie *); +extern int __fscache_begin_write_operation (struct netfs_cache_resources *,struct fscache_cookie *); extern void __fscache_write_to_cache(struct fscache_cookie *, struct address_space *, loff_t, size_t, loff_t, netfs_io_terminated_t, void *,
@@ -499,6 +500,33 @@ int fscache_read(struct netfs_cache_resources *cres, term_func, term_func_priv); } +/** +* fscache_begin_write_operation - Begin a write operation for the netfs lib +* @cres: The cache resources for the write being performed +* @cookie: The cookie representing the cache object +* +* Begin a write operation on behalf of the netfs helper library. @cres +* indicates the cache resources to which the operation state should be +* attached; @cookie indicates the cache object that will be accessed. +* +* @cres->inval_counter is set from @cookie->inval_counter for comparison at +* the end of the operation. This allows invalidation during the operation to +* be detected by the caller. +* +* Returns: +* * 0 - Success +* * -ENOBUFS - No caching available +* * Other error code from the cache, such as -ENOMEM. +*/ +static inline +int fscache_begin_write_operation (struct netfs_cache_resources *cres, +struct fscache_cookie *cookie) +{ +if (fscache_cookie_enabled (cookie)) +return __fscache_begin_write_operation (cres, cookie); +return -ENOBUFS ; +} +/** * fscache_write - Start a write to the cache. * @cres: The cache resources to use
David Howells
maintainerPATCH v4 63/68 cifs: Support fscache indexing rewrite (untested)
In reply to David HowellsChange the cifs filesystem to take account of the changes to fscache's indexing rewrite and reenable caching in cifs.
The following changes have been made:
(1) The fscache_netfs struct is no more, and there's no need to register the filesystem as a whole.
(2) The session cookie is now an fscache_volume cookie, allocated with fscache_acquire_volume(). That takes three parameters: a string representing the "volume" in the index, a string naming the cache to use (or NULL) and a u64 that conveys coherency metadata for the volume.
For cifs, I've made it render the volume name string as:
"cifs,<ipaddress>,<sharename>"
where the sharename has '/' characters replaced with ';'.
This probably needs rethinking a bit as the total name could exceed the maximum filename component length.
Further, the coherency data is currently just set to 0. It needs something else doing with it - I wonder if it would suffice simply to sum the resource_id, vol_create_time and vol_serial_number or maybe hash them.
(3) The fscache_cookie_def is no more and needed information is passed directly to fscache_acquire_cookie(). The cache no longer calls back into the filesystem, but rather metadata changes are indicated at other times.
fscache_acquire_cookie() is passed the same keying and coherency information as before.
(4) The functions to set/reset cookies are removed and fscache_use_cookie() and fscache_unuse_cookie() are used instead.
fscache_use_cookie() is passed a flag to indicate if the cookie is opened for writing. fscache_unuse_cookie() is passed updates for the metadata if we changed it (ie. if the file was opened for writing).
These are called when the file is opened or closed.
(5) cifs_setattr_*() are made to call fscache_resize() to change the size of the cache object.
(6) The functions to read and write data are stubbed out pending a conversion to use netfslib.
Changes
=======
ver #4:
- Fixed the use of sizeof with memset.
- tcon->vol_create_time is __le64 so doesn't need cpu_to_le64().
ver #3:
- Canonicalise the cifs coherency data to make the cache portable.
- Set volume coherency data.
ver #2:
- Use gfpflags_allow_blocking() rather than using flag directly.
- Upgraded to -rc4 to allow for upstream changes[1].
- fscache_acquire_volume() now returns errors.
---
fs/cifs/Kconfig | 2 fs/cifs/Makefile | 2 fs/cifs/cache.c | 105 ---------------- fs/cifs/cifsfs.c | 11 -- fs/cifs/cifsglob.h | 5 - fs/cifs/connect.c | 12 -- fs/cifs/file.c | 64 +++++++--- fs/cifs/fscache.c | 333 ++++++++++++---------------------------------------- fs/cifs/fscache.h | 126 ++++++-------------- fs/cifs/inode.c | 36 ++++-- 10 files changed, 191 insertions(+), 505 deletions(-) delete mode 100644 fs/cifs/cache.c
fs/cifs/Kconfig +1 -1
@@ -188,7 +188,7 @@ config CIFS_SMB_DIRECT config CIFS_FSCACHE bool "Provide CIFS client caching support" - depends on CIFS=m && FSCACHE_OLD_API || CIFS=y && FSCACHE_OLD_API=y + depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y help Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data to be cached locally on disk through the general filesystem cache
fs/cifs/Makefile +1 -1
@@ -25,7 +25,7 @@ cifs-$(CONFIG_CIFS_DFS_UPCALL) += cifs_dfs_ref.o dfs_cache.o cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o -cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o +cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o
fs/cifs/cache.c deleted +0 -105
105 lines not on this page · raw message
fs/cifs/cifsfs.c +3 -8
@@ -396,6 +396,8 @@ static void cifs_evict_inode(struct inode *inode) {truncate_inode_pages_final (&inode->i_data); +if (inode->i_state &I_PINNING_FSCACHE_WB ) +cifs_fscache_unuse_inode_cookie (inode, true);clear_inode (inode); }
@@ -1624,13 +1626,9 @@ init_cifs(void) goto out_destroy_cifsoplockd_wq; } - rc =cifs_fscache_register (); -if (rc) - goto out_destroy_deferredclose_wq; - rc =cifs_init_inodecache ();if (rc) - goto out_unreg_fscache; + goto out_destroy_deferredclose_wq; rc =cifs_init_mids ();if (rc)
@@ -1692,8 +1690,6 @@ init_cifs(void)cifs_destroy_mids (); out_destroy_inodecache:cifs_destroy_inodecache (); -out_unreg_fscache: -cifs_fscache_unregister (); out_destroy_deferredclose_wq:destroy_workqueue (deferredclose_wq); out_destroy_cifsoplockd_wq:
@@ -1729,7 +1725,6 @@ exit_cifs(void)cifs_destroy_request_bufs ();cifs_destroy_mids ();cifs_destroy_inodecache (); -cifs_fscache_unregister ();destroy_workqueue (deferredclose_wq);destroy_workqueue (cifsoplockd_wq);destroy_workqueue (decrypt_wq);
fs/cifs/cifsglob.h +1 -4
@@ -659,9 +659,6 @@ struct TCP_Server_Info {unsigned int total_read;/* total amount of data read in this pass */ atomic_t in_send;/* requests trying to send */ atomic_t num_waiters;/* blocked waiting to get in sendrecv */ -#ifdef CONFIG_CIFS_FSCACHE -struct fscache_cookie *fscache;/* client index cache cookie */ -#endif #ifdef CONFIG_CIFS_STATS2 atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS ];/* total requests by cmd */ atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS ];/* count resps > 1 sec */
@@ -1117,7 +1114,7 @@ struct cifs_tcon {__u32 max_bytes_copy;#ifdef CONFIG_CIFS_FSCACHE u64 resource_id;/* server resource id */ -struct fscache_cookie *fscache;/* cookie for share */ +struct fscache_volume *fscache;/* cookie for share */ #endif struct list_head pending_opens;/* list of incomplete opens */ struct cached_fid crfid;/* Cached root fid */
fs/cifs/connect.c +0 -12
@@ -1396,10 +1396,6 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)cifs_crypto_secmech_release (server); -/* fscache server cookies are based on primary channel only */ -if (!CIFS_SERVER_IS_CHAN (server)) -cifs_fscache_release_client_cookie (server); -kfree (server->session_key.response); server->session_key.response =NULL ; server->session_key.len =0 ;
@@ -1559,14 +1555,6 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,list_add (&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);spin_unlock (&cifs_tcp_ses_lock); -/* fscache server cookies are based on primary channel only */ -if (!CIFS_SERVER_IS_CHAN (tcp_ses)) -cifs_fscache_get_client_cookie (tcp_ses); -#ifdef CONFIG_CIFS_FSCACHE - else - tcp_ses->fscache = tcp_ses->primary_server->fscache; -#endif /* CONFIG_CIFS_FSCACHE */ -/* queue echo request delayed work */ queue_delayed_work (cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval);
fs/cifs/file.c +43 -21
136 lines not on this page · raw message
fs/cifs/fscache.c +80 -253
408 lines not on this page · raw message
fs/cifs/fscache.h +39 -87
175 lines not on this page · raw message
fs/cifs/inode.c +23 -13
@@ -1298,10 +1298,7 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) inode->i_flags |=S_NOATIME |S_NOCMTIME ;if (inode->i_state &I_NEW ) { inode->i_ino = hash; -#ifdef CONFIG_CIFS_FSCACHE -/* initialize per-inode cache cookie pointer */ -CIFS_I (inode)->fscache =NULL ; -#endif +cifs_fscache_get_inode_cookie (inode);unlock_new_inode (inode); } }
@@ -1376,12 +1373,18 @@ struct inode *cifs_root_iget(struct super_block *sb) inode =ERR_PTR (rc); } -/* -* The cookie is initialized from volume info returned above. -* Inside cifs_fscache_get_super_cookie it checks -* that we do not get super cookie twice. -*/ -cifs_fscache_get_super_cookie (tcon); +if (!rc) { +/* +* The cookie is initialized from volume info returned above. +* Inside cifs_fscache_get_super_cookie it checks +* that we do not get super cookie twice. +*/ + rc =cifs_fscache_get_super_cookie (tcon); +if (rc <0 ) { +iget_failed (inode); + inode =ERR_PTR (rc); + } + } out:kfree (path);
@@ -2270,6 +2273,8 @@ cifs_dentry_needs_reval(struct dentry *dentry)int cifs_invalidate_mapping (struct inode *inode) { +struct cifs_fscache_inode_coherency_data cd; +struct cifsInodeInfo *cifsi =CIFS_I (inode);int rc =0 ;if (inode->i_mapping && inode->i_mapping->nrpages !=0 ) {
@@ -2279,7 +2284,8 @@ cifs_invalidate_mapping(struct inode *inode) __func__, inode); } -cifs_fscache_reset_inode_cookie (inode); +cifs_fscache_fill_coherency (&cifsi->vfs_inode, &cd); +fscache_invalidate (cifs_inode_cookie (inode), &cd,i_size_read (inode),0 );return rc; }
@@ -2784,8 +2790,10 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) goto out;if ((attrs->ia_valid &ATTR_SIZE ) && - attrs->ia_size !=i_size_read (inode)) + attrs->ia_size !=i_size_read (inode)) {truncate_setsize (inode, attrs->ia_size); +fscache_resize_cookie (cifs_inode_cookie (inode), attrs->ia_size); + }setattr_copy (&init_user_ns, inode, attrs);mark_inode_dirty (inode);
@@ -2980,8 +2988,10 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) goto cifs_setattr_exit;if ((attrs->ia_valid &ATTR_SIZE ) && - attrs->ia_size !=i_size_read (inode)) + attrs->ia_size !=i_size_read (inode)) {truncate_setsize (inode, attrs->ia_size); +fscache_resize_cookie (cifs_inode_cookie (inode), attrs->ia_size); + }setattr_copy (&init_user_ns, inode, attrs);mark_inode_dirty (inode);
David Howells
maintainerRe: [PATCH v4 63/68] cifs: Support fscache indexing rewrite (untested)
This patch isn't quite right and needs a fix. The attached patch fixes it. I'll fold that in and post a v5 of this patch.
David
---
cifs: Fix the fscache cookie management
Fix the fscache cookie management in cifs in the following ways:
(1) The cookie should be released in cifs_evict_inode() after it has been unused from being pinned by cifs_set_page_dirty().
(2) The cookie shouldn't be released in cifsFileInfo_put_final(). That's dealing with closing a file, not getting rid of an inode. The cookie needs to persist beyond the last file close because writepages may happen after closure.
(3) The cookie needs to be used in cifs_atomic_open() to match cifs_open(). As yet unknown files being opened for writing seem to go by the former route rather than the latter.
This can be triggered by something like:
# systemctl start cachefilesd
# mount //carina/test /xfstest.test -o user=shares,pass=xxxx.fsc
# bash 5</xfstest.test/bar
# echo hello >/xfstest.test/barThe key is to open the file R/O and then open it R/W and write to it whilst it's still open for R/O. A cookie isn't acquired if it's just opened R/W as it goes through the atomic_open method rather than the open method.
--- fs/cifs/cifsfs.c | 8 ++++++++ fs/cifs/dir.c | 4 ++++ fs/cifs/file.c | 2 -- 3 files changed, 12 insertions(+), 2 deletions(-)
fs/cifs/cifsfs.c +8 -0
@@ -398,6 +398,7 @@ cifs_evict_inode(struct inode *inode)truncate_inode_pages_final (&inode->i_data);if (inode->i_state &I_PINNING_FSCACHE_WB )cifs_fscache_unuse_inode_cookie (inode, true); +cifs_fscache_release_inode_cookie (inode);clear_inode (inode); }
@@ -722,6 +723,12 @@ static int cifs_show_stats(struct seq_file *s, struct dentry *root) }#endif +static int cifs_write_inode (struct inode *inode,struct writeback_control *wbc) +{ +fscache_unpin_writeback (wbc,cifs_inode_cookie (inode)); +return 0 ; +} +static int cifs_drop_inode (struct inode *inode) {struct cifs_sb_info *cifs_sb =CIFS_SB (inode->i_sb);
@@ -734,6 +741,7 @@ static int cifs_drop_inode(struct inode *inode)static const struct super_operations cifs_super_ops = { .statfs = cifs_statfs, .alloc_inode = cifs_alloc_inode, + .write_inode = cifs_write_inode, .free_inode = cifs_free_inode, .drop_inode = cifs_drop_inode, .evict_inode = cifs_evict_inode,
fs/cifs/dir.c +4 -0
@@ -22,6 +22,7 @@#include "cifs_unicode.h" #include "fs_context.h" #include "cifs_ioctl.h" +#include "fscache.h" static void renew_parental_timestamps (struct dentry *direntry)
@@ -509,6 +510,9 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, rc = -ENOMEM ; } +fscache_use_cookie (cifs_inode_cookie (file_inode (file)), + file->f_mode &FMODE_WRITE ); + out:cifs_put_tlink (tlink); out_free_xid:
fs/cifs/file.c +0 -2
@@ -376,8 +376,6 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file)struct cifsLockInfo *li, *tmp;struct super_block *sb = inode->i_sb; -cifs_fscache_release_inode_cookie (inode); -/* * Delete any outstanding lock records. We'll lose them when the file * is closed anyway.
Re: [PATCH v4 63/68] cifs: Support fscache indexing rewrite (untested)
Quoted 100 lines · David Howells
This patch isn't quite right and needs a fix. The attached patch fixes it. I'll fold that in and post a v5 of this patch.
David
---
cifs: Fix the fscache cookie managementFix the fscache cookie management in cifs in the following ways:
(1) The cookie should be released in
cifs_evict_inode()after it has been unused from being pinned bycifs_set_page_dirty().(2) The cookie shouldn't be released in
cifsFileInfo_put_final(). That's dealing with closing a file, not getting rid of an inode. The cookie needs to persist beyond the last file close because writepages may happen after closure.(3) The cookie needs to be used in
cifs_atomic_open()to matchcifs_open(). As yet unknown files being opened for writing seem to go by the former route rather than the latter.This can be triggered by something like:
# systemctl start cachefilesd # mount //carina/test /xfstest.test -o user=shares,pass=xxxx.fsc # bash 5</xfstest.test/bar # echo hello >/xfstest.test/barThe key is to open the file R/O and then open it R/W and write to it whilst it's still open for R/O. A cookie isn't acquired if it's just opened R/W as it goes through the
atomic_openmethod rather than the open method.Signed-off-by David Howells <[email protected]>--- fs/cifs/cifsfs.c | 8 ++++++++ fs/cifs/dir.c | 4 ++++ fs/cifs/file.c | 2 -- 3 files changed, 12 insertions(+), 2 deletions(-)fs/cifs/cifsfs.c +8
-0@@ -398,6 +398,7 @@ cifs_evict_inode(struct inode *inode)truncate_inode_pages_final (&inode->i_data);if (inode->i_state &I_PINNING_FSCACHE_WB )cifs_fscache_unuse_inode_cookie (inode, true); +cifs_fscache_release_inode_cookie (inode);clear_inode (inode); }@@ -722,6 +723,12 @@ static int cifs_show_stats(struct seq_file *s, struct dentry *root) }#endif +static int cifs_write_inode (struct inode *inode,struct writeback_control *wbc) +{ +fscache_unpin_writeback (wbc,cifs_inode_cookie (inode)); +return 0 ; +} +static int cifs_drop_inode (struct inode *inode) {struct cifs_sb_info *cifs_sb =CIFS_SB (inode->i_sb);@@ -734,6 +741,7 @@ static int cifs_drop_inode(struct inode *inode)static const struct super_operations cifs_super_ops = { .statfs = cifs_statfs, .alloc_inode = cifs_alloc_inode, + .write_inode = cifs_write_inode, .free_inode = cifs_free_inode, .drop_inode = cifs_drop_inode, .evict_inode = cifs_evict_inode,fs/cifs/dir.c +4
-0@@ -22,6 +22,7 @@#include "cifs_unicode.h" #include "fs_context.h" #include "cifs_ioctl.h" +#include "fscache.h" static void renew_parental_timestamps (struct dentry *direntry)@@ -509,6 +510,9 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, rc = -ENOMEM ; } +fscache_use_cookie (cifs_inode_cookie (file_inode (file)), + file->f_mode &FMODE_WRITE ); + out:cifs_put_tlink (tlink); out_free_xid:fs/cifs/file.c +0
-0
fs/cifs/file.c static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file) · line 381
Quoted 8 lines
fs/cifs/file.c +0
-2@@ -376,8 +376,6 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file)struct cifsLockInfo *li, *tmp;struct super_block *sb = inode->i_sb; -cifs_fscache_release_inode_cookie (inode); -/* * Delete any outstanding lock records. We'll lose them when the file * is closed anyway.
Looks good.
David Howells
maintainerPATCH v5 63/68 cifs: Support fscache indexing rewrite
In reply to David HowellsChange the cifs filesystem to take account of the changes to fscache's indexing rewrite and reenable caching in cifs.
The following changes have been made:
(1) The fscache_netfs struct is no more, and there's no need to register the filesystem as a whole.
(2) The session cookie is now an fscache_volume cookie, allocated with fscache_acquire_volume(). That takes three parameters: a string representing the "volume" in the index, a string naming the cache to use (or NULL) and a u64 that conveys coherency metadata for the volume.
For cifs, I've made it render the volume name string as:
"cifs,<ipaddress>,<sharename>"
where the sharename has '/' characters replaced with ';'.
This probably needs rethinking a bit as the total name could exceed the maximum filename component length.
Further, the coherency data is currently just set to 0. It needs something else doing with it - I wonder if it would suffice simply to sum the resource_id, vol_create_time and vol_serial_number or maybe hash them.
(3) The fscache_cookie_def is no more and needed information is passed directly to fscache_acquire_cookie(). The cache no longer calls back into the filesystem, but rather metadata changes are indicated at other times.
fscache_acquire_cookie() is passed the same keying and coherency information as before.
(4) The functions to set/reset cookies are removed and fscache_use_cookie() and fscache_unuse_cookie() are used instead.
fscache_use_cookie() is passed a flag to indicate if the cookie is opened for writing. fscache_unuse_cookie() is passed updates for the metadata if we changed it (ie. if the file was opened for writing).
These are called when the file is opened or closed.
(5) cifs_setattr_*() are made to call fscache_resize() to change the size of the cache object.
(6) The functions to read and write data are stubbed out pending a conversion to use netfslib.
Changes
=======
ver #5:
- Fixed a couple of bits of cookie handling[2]:
- The cookie should be released in cifs_evict_inode(), not cifsFileInfo_put_final(). The cookie needs to persist beyond file closure so that writepages will be able to write to it.
- fscache_use_cookie() needs to be called in cifs_atomic_open() as it is for cifs_open().
ver #4:
- Fixed the use of sizeof with memset.
- tcon->vol_create_time is __le64 so doesn't need cpu_to_le64().
ver #3:
- Canonicalise the cifs coherency data to make the cache portable.
- Set volume coherency data.
ver #2:
- Use gfpflags_allow_blocking() rather than using flag directly.
- Upgraded to -rc4 to allow for upstream changes[1].
- fscache_acquire_volume() now returns errors.
--- fs/cifs/Kconfig | 2 fs/cifs/Makefile | 2 fs/cifs/cache.c | 105 ---------------- fs/cifs/cifsfs.c | 19 +-- fs/cifs/cifsglob.h | 5 fs/cifs/connect.c | 12 - fs/cifs/dir.c | 4 fs/cifs/file.c | 66 ++++++---- fs/cifs/fscache.c | 333 ++++++++++++----------------------------------------- fs/cifs/fscache.h | 126 ++++++-------------- fs/cifs/inode.c | 36 +++-- 11 files changed, 203 insertions(+), 507 deletions(-)
fs/cifs/Kconfig +1 -1
@@ -188,7 +188,7 @@ config CIFS_SMB_DIRECT config CIFS_FSCACHE bool "Provide CIFS client caching support" - depends on CIFS=m && FSCACHE_OLD_API || CIFS=y && FSCACHE_OLD_API=y + depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y help Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data to be cached locally on disk through the general filesystem cache
fs/cifs/Makefile +1 -1
@@ -25,7 +25,7 @@ cifs-$(CONFIG_CIFS_DFS_UPCALL) += cifs_dfs_ref.o dfs_cache.o cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o -cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o +cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o
fs/cifs/cache.c deleted +0 -105
105 lines not on this page · raw message
fs/cifs/cifsfs.c +11 -8
@@ -396,6 +396,9 @@ static void cifs_evict_inode(struct inode *inode) {truncate_inode_pages_final (&inode->i_data); +if (inode->i_state &I_PINNING_FSCACHE_WB ) +cifs_fscache_unuse_inode_cookie (inode, true); +cifs_fscache_release_inode_cookie (inode);clear_inode (inode); }
@@ -720,6 +723,12 @@ static int cifs_show_stats(struct seq_file *s, struct dentry *root) }#endif +static int cifs_write_inode (struct inode *inode,struct writeback_control *wbc) +{ +fscache_unpin_writeback (wbc,cifs_inode_cookie (inode)); +return 0 ; +} +static int cifs_drop_inode (struct inode *inode) {struct cifs_sb_info *cifs_sb =CIFS_SB (inode->i_sb);
@@ -732,6 +741,7 @@ static int cifs_drop_inode(struct inode *inode)static const struct super_operations cifs_super_ops = { .statfs = cifs_statfs, .alloc_inode = cifs_alloc_inode, + .write_inode = cifs_write_inode, .free_inode = cifs_free_inode, .drop_inode = cifs_drop_inode, .evict_inode = cifs_evict_inode,
@@ -1624,13 +1634,9 @@ init_cifs(void) goto out_destroy_cifsoplockd_wq; } - rc =cifs_fscache_register (); -if (rc) - goto out_destroy_deferredclose_wq; - rc =cifs_init_inodecache ();if (rc) - goto out_unreg_fscache; + goto out_destroy_deferredclose_wq; rc =cifs_init_mids ();if (rc)
@@ -1692,8 +1698,6 @@ init_cifs(void)cifs_destroy_mids (); out_destroy_inodecache:cifs_destroy_inodecache (); -out_unreg_fscache: -cifs_fscache_unregister (); out_destroy_deferredclose_wq:destroy_workqueue (deferredclose_wq); out_destroy_cifsoplockd_wq:
@@ -1729,7 +1733,6 @@ exit_cifs(void)cifs_destroy_request_bufs ();cifs_destroy_mids ();cifs_destroy_inodecache (); -cifs_fscache_unregister ();destroy_workqueue (deferredclose_wq);destroy_workqueue (cifsoplockd_wq);destroy_workqueue (decrypt_wq);
fs/cifs/cifsglob.h +1 -4
@@ -659,9 +659,6 @@ struct TCP_Server_Info {unsigned int total_read;/* total amount of data read in this pass */ atomic_t in_send;/* requests trying to send */ atomic_t num_waiters;/* blocked waiting to get in sendrecv */ -#ifdef CONFIG_CIFS_FSCACHE -struct fscache_cookie *fscache;/* client index cache cookie */ -#endif #ifdef CONFIG_CIFS_STATS2 atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS ];/* total requests by cmd */ atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS ];/* count resps > 1 sec */
@@ -1117,7 +1114,7 @@ struct cifs_tcon {__u32 max_bytes_copy;#ifdef CONFIG_CIFS_FSCACHE u64 resource_id;/* server resource id */ -struct fscache_cookie *fscache;/* cookie for share */ +struct fscache_volume *fscache;/* cookie for share */ #endif struct list_head pending_opens;/* list of incomplete opens */ struct cached_fid crfid;/* Cached root fid */
fs/cifs/connect.c +0 -12
@@ -1396,10 +1396,6 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)cifs_crypto_secmech_release (server); -/* fscache server cookies are based on primary channel only */ -if (!CIFS_SERVER_IS_CHAN (server)) -cifs_fscache_release_client_cookie (server); -kfree (server->session_key.response); server->session_key.response =NULL ; server->session_key.len =0 ;
@@ -1559,14 +1555,6 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,list_add (&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);spin_unlock (&cifs_tcp_ses_lock); -/* fscache server cookies are based on primary channel only */ -if (!CIFS_SERVER_IS_CHAN (tcp_ses)) -cifs_fscache_get_client_cookie (tcp_ses); -#ifdef CONFIG_CIFS_FSCACHE - else - tcp_ses->fscache = tcp_ses->primary_server->fscache; -#endif /* CONFIG_CIFS_FSCACHE */ -/* queue echo request delayed work */ queue_delayed_work (cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval);
fs/cifs/dir.c +4 -0
@@ -22,6 +22,7 @@#include "cifs_unicode.h" #include "fs_context.h" #include "cifs_ioctl.h" +#include "fscache.h" static void renew_parental_timestamps (struct dentry *direntry)
@@ -509,6 +510,9 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, rc = -ENOMEM ; } +fscache_use_cookie (cifs_inode_cookie (file_inode (file)), + file->f_mode &FMODE_WRITE ); + out:cifs_put_tlink (tlink); out_free_xid:
fs/cifs/file.c +43 -23
144 lines not on this page · raw message
fs/cifs/fscache.c +80 -253
408 lines not on this page · raw message
fs/cifs/fscache.h +39 -87
175 lines not on this page · raw message
fs/cifs/inode.c +23 -13
@@ -1298,10 +1298,7 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) inode->i_flags |=S_NOATIME |S_NOCMTIME ;if (inode->i_state &I_NEW ) { inode->i_ino = hash; -#ifdef CONFIG_CIFS_FSCACHE -/* initialize per-inode cache cookie pointer */ -CIFS_I (inode)->fscache =NULL ; -#endif +cifs_fscache_get_inode_cookie (inode);unlock_new_inode (inode); } }
@@ -1376,12 +1373,18 @@ struct inode *cifs_root_iget(struct super_block *sb) inode =ERR_PTR (rc); } -/* -* The cookie is initialized from volume info returned above. -* Inside cifs_fscache_get_super_cookie it checks -* that we do not get super cookie twice. -*/ -cifs_fscache_get_super_cookie (tcon); +if (!rc) { +/* +* The cookie is initialized from volume info returned above. +* Inside cifs_fscache_get_super_cookie it checks +* that we do not get super cookie twice. +*/ + rc =cifs_fscache_get_super_cookie (tcon); +if (rc <0 ) { +iget_failed (inode); + inode =ERR_PTR (rc); + } + } out:kfree (path);
@@ -2270,6 +2273,8 @@ cifs_dentry_needs_reval(struct dentry *dentry)int cifs_invalidate_mapping (struct inode *inode) { +struct cifs_fscache_inode_coherency_data cd; +struct cifsInodeInfo *cifsi =CIFS_I (inode);int rc =0 ;if (inode->i_mapping && inode->i_mapping->nrpages !=0 ) {
@@ -2279,7 +2284,8 @@ cifs_invalidate_mapping(struct inode *inode) __func__, inode); } -cifs_fscache_reset_inode_cookie (inode); +cifs_fscache_fill_coherency (&cifsi->vfs_inode, &cd); +fscache_invalidate (cifs_inode_cookie (inode), &cd,i_size_read (inode),0 );return rc; }
@@ -2784,8 +2790,10 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) goto out;if ((attrs->ia_valid &ATTR_SIZE ) && - attrs->ia_size !=i_size_read (inode)) + attrs->ia_size !=i_size_read (inode)) {truncate_setsize (inode, attrs->ia_size); +fscache_resize_cookie (cifs_inode_cookie (inode), attrs->ia_size); + }setattr_copy (&init_user_ns, inode, attrs);mark_inode_dirty (inode);
@@ -2980,8 +2988,10 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) goto cifs_setattr_exit;if ((attrs->ia_valid &ATTR_SIZE ) && - attrs->ia_size !=i_size_read (inode)) + attrs->ia_size !=i_size_read (inode)) {truncate_setsize (inode, attrs->ia_size); +fscache_resize_cookie (cifs_inode_cookie (inode), attrs->ia_size); + }setattr_copy (&init_user_ns, inode, attrs);mark_inode_dirty (inode);