Prepare to use folio->private to hold information write grouping and
streaming write.  These are implemented in the same commit as they both
make use of folio->private and will be both checked at the same time in
several places.

"Write grouping" involves ordering the writeback of groups of writes, such
as is needed for ceph snaps.  A group is represented by a
filesystem-supplied object which must contain a netfs_group struct.  This
contains just a refcount and a pointer to a destructor.

"Streaming write" is the storage of data in folios that are marked dirty,
but not uptodate, to avoid unnecessary reads of data.  This is represented
by a netfs_folio struct.  This contains the offset and length of the
modified region plus the otherwise displaced write grouping pointer.

The way folio->private is multiplexed is:

 (1) If private is NULL then neither is in operation on a dirty folio.

 (2) If private is set, with bit 0 clear, then this points to a group.

 (3) If private is set, with bit 0 set, then this points to a netfs_folio
     struct (with bit 0 AND'ed out).

Signed-off-by: David Howells <dhowe...@redhat.com>
cc: Jeff Layton <jlay...@kernel.org>
cc: linux-cachefs@redhat.com
cc: linux-fsde...@vger.kernel.org
cc: linux...@kvack.org
---
 fs/netfs/internal.h   | 28 ++++++++++++++++++++++++++
 fs/netfs/misc.c       | 46 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/netfs.h | 41 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 115 insertions(+)

diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h
index 46183dad4d50..83418a918ee1 100644
--- a/fs/netfs/internal.h
+++ b/fs/netfs/internal.h
@@ -147,6 +147,34 @@ static inline bool netfs_is_cache_enabled(struct 
netfs_inode *ctx)
 #endif
 }
 
+/*
+ * Get a ref on a netfs group attached to a dirty page (e.g. a ceph snap).
+ */
+static inline struct netfs_group *netfs_get_group(struct netfs_group 
*netfs_group)
+{
+       if (netfs_group)
+               refcount_inc(&netfs_group->ref);
+       return netfs_group;
+}
+
+/*
+ * Dispose of a netfs group attached to a dirty page (e.g. a ceph snap).
+ */
+static inline void netfs_put_group(struct netfs_group *netfs_group)
+{
+       if (netfs_group && refcount_dec_and_test(&netfs_group->ref))
+               netfs_group->free(netfs_group);
+}
+
+/*
+ * Dispose of a netfs group attached to a dirty page (e.g. a ceph snap).
+ */
+static inline void netfs_put_group_many(struct netfs_group *netfs_group, int 
nr)
+{
+       if (netfs_group && refcount_sub_and_test(nr, &netfs_group->ref))
+               netfs_group->free(netfs_group);
+}
+
 /*****************************************************************************/
 /*
  * debug tracing
diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c
index c70f856f3129..8a2a56f1f623 100644
--- a/fs/netfs/misc.c
+++ b/fs/netfs/misc.c
@@ -159,9 +159,55 @@ void netfs_clear_buffer(struct xarray *buffer)
  */
 void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length)
 {
+       struct netfs_folio *finfo = NULL;
+       size_t flen = folio_size(folio);
+
        _enter("{%lx},%zx,%zx", folio_index(folio), offset, length);
 
        folio_wait_fscache(folio);
+
+       if (!folio_test_private(folio))
+               return;
+
+       finfo = netfs_folio_info(folio);
+
+       if (offset == 0 && length >= flen)
+               goto erase_completely;
+
+       if (finfo) {
+               /* We have a partially uptodate page from a streaming write. */
+               unsigned int fstart = finfo->dirty_offset;
+               unsigned int fend = fstart + finfo->dirty_len;
+               unsigned int end = offset + length;
+
+               if (offset >= fend)
+                       return;
+               if (end <= fstart)
+                       return;
+               if (offset <= fstart && end >= fend)
+                       goto erase_completely;
+               if (offset <= fstart && end > fstart)
+                       goto reduce_len;
+               if (offset > fstart && end >= fend)
+                       goto move_start;
+               /* A partial write was split.  The caller has already zeroed
+                * it, so just absorb the hole.
+                */
+       }
+       return;
+
+erase_completely:
+       netfs_put_group(netfs_folio_group(folio));
+       folio_detach_private(folio);
+       folio_clear_uptodate(folio);
+       kfree(finfo);
+       return;
+reduce_len:
+       finfo->dirty_len = offset + length - finfo->dirty_offset;
+       return;
+move_start:
+       finfo->dirty_len -= offset - finfo->dirty_offset;
+       finfo->dirty_offset = offset;
 }
 EXPORT_SYMBOL(netfs_invalidate_folio);
 
diff --git a/include/linux/netfs.h b/include/linux/netfs.h
index 39b3eeefa03c..11a073506f98 100644
--- a/include/linux/netfs.h
+++ b/include/linux/netfs.h
@@ -142,6 +142,47 @@ struct netfs_inode {
 #define NETFS_ICTX_ODIRECT     0               /* The file has DIO in progress 
*/
 };
 
+/*
+ * A netfs group - for instance a ceph snap.  This is marked on dirty pages and
+ * pages marked with a group must be flushed before they can be written under
+ * the domain of another group.
+ */
+struct netfs_group {
+       refcount_t              ref;
+       void (*free)(struct netfs_group *netfs_group);
+};
+
+/*
+ * Information about a dirty page (attached only if necessary).
+ * folio->private
+ */
+struct netfs_folio {
+       struct netfs_group      *netfs_group;   /* Filesystem's grouping marker 
(or NULL). */
+       unsigned int            dirty_offset;   /* Write-streaming dirty data 
offset */
+       unsigned int            dirty_len;      /* Write-streaming dirty data 
length */
+};
+#define NETFS_FOLIO_INFO       0x1UL   /* OR'd with folio->private. */
+
+static inline struct netfs_folio *netfs_folio_info(struct folio *folio)
+{
+       void *priv = folio_get_private(folio);
+
+       if ((unsigned long)priv & NETFS_FOLIO_INFO)
+               return (struct netfs_folio *)((unsigned long)priv & 
~NETFS_FOLIO_INFO);
+       return NULL;
+}
+
+static inline struct netfs_group *netfs_folio_group(struct folio *folio)
+{
+       struct netfs_folio *finfo;
+       void *priv = folio_get_private(folio);
+
+       finfo = netfs_folio_info(folio);
+       if (finfo)
+               return finfo->netfs_group;
+       return priv;
+}
+
 /*
  * Resources required to do operations on a cache.
  */
--
Linux-cachefs mailing list
Linux-cachefs@redhat.com
https://listman.redhat.com/mailman/listinfo/linux-cachefs

Reply via email to