• Linus Torvalds's avatar
    Merge tag 'fscache-rewrite-20220111' of... · 8834147f
    Linus Torvalds authored
    Merge tag 'fscache-rewrite-20220111' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs
    
    Pull fscache rewrite from David Howells:
     "This is a set of patches that rewrites the fscache driver and 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 series 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.
    
      [!] Note: I have dropped the cifs patch for the moment, leaving local
          caching in cifs disabled. I've been having trouble getting that
          working. I think I have it done, but it needs more testing (there
          seem to be some test failures occurring with v5.16 also from
          xfstests), so I propose deferring that patch to the end of the
          merge window.
    
      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 with the dropped patch) 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"
    
    Link: https://lore.kernel.org/all/510611.1641942444@warthog.procyon.org.uk/
    Tested-by: Dominique Martinet <asmadeus@codewreck.org> # 9p
    Tested-by: kafs-testing@auristor.com # afs
    Tested-by: Jeff Layton <jlayton@kernel.org> # ceph
    Tested-by: Dave Wysochanski <dwysocha@redhat.com> # nfs
    Tested-by: Daire Byrne <daire@dneg.com> # nfs
    
    * tag 'fscache-rewrite-20220111' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: (67 commits)
      9p, afs, ceph, nfs: Use current_is_kswapd() rather than gfpflags_allow_blocking()
      fscache: Add a tracepoint for cookie use/unuse
      fscache: Rewrite documentation
      ceph: add fscache writeback support
      ceph: conversion to new fscache API
      nfs: Implement cache I/O by accessing the cache directly
      nfs: Convert to new fscache volume/cookie API
      9p: Copy local writes to the cache when writing to the server
      9p: Use fscache indexing rewrite and reenable caching
      afs: Skip truncation on the server of data we haven't written yet
      afs: Copy local writes to the cache when writing to the server
      afs: Convert afs to use the new fscache API
      fscache, cachefiles: Display stat of culling events
      fscache, cachefiles: Display stats of no-space events
      cachefiles: Allow cachefiles to actually function
      fscache, cachefiles: Store the volume coherency data
      cachefiles: Implement the I/O routines
      cachefiles: Implement cookie resize for truncate
      cachefiles: Implement begin and end I/O operation
      cachefiles: Implement backing file wrangling
      ...
    8834147f
file.c 66.9 KB