linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/14] Small step toward KSM for file back page.
@ 2020-10-07  1:05 jglisse
  2020-10-07  1:05 ` [PATCH 02/14] fs: define filler_t as a function pointer type jglisse
                   ` (13 more replies)
  0 siblings, 14 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

This patchset is a step toward a larger objective: generalize existing
KSM into a mechanism allowing exclusive write control for a page; either
anonymous memory (like KSM today) or file back page (modulo GUP which
would block that like it does today for KSM).

Exclusive write control page allow multiple different features to be
implemented:

    - KSM kernel share page, ie de-duplicate pages with same content
      to use a single page for all. From many pages to one read only
      page. We have that today for anonymous memory only. The overall
      patchset extends it to file back page ie sharing the same struct
      page accross different file or accross same file. This can be
      be usefull for containers for instance ... or for deduplication
      in same file.

    - NUMA duplication, duplicate a page into multiple local read only
      copy. This is the opposite of KSM in a sense, instead of saving
      memory. Using more memory to get better memory access performance.
      For instance duplicating libc code to local node copy; or big
      read only dataset duplicated on each nodes.

    - Exclusive write access, owner of page write protection is the only
      that can write to the page (and must still abide by fs rules for
      fileback page in respect to writeback...). One use case is for
      fast atomic operation using non atomic instruction. For instance
      by PCIE device, if all mapping of the page is read only then PCIE
      device driver knows device write can not race with CPU write. This
      is a performance optimization.

    - Use main memory as cache for persistent memory ie the page is
      read only and write will trigger callback and different strategy
      can be use like write combining (ie acumulating change in main
      memory before copying to persistent memory).

Like KSM today such protection can be broken at _any_ time. The owner
of the protection gets a callback (KSM code for instance get calls) so
that it can unprotect the page. Breaking protection should not block
and must happens quickly (like KSM code today).


Convertion of existing KSM into generic mechanism is straightforward
for anonymous page (just factorize out KSM code that deals with page
protection from KSM code that deals with de-duplication).


The big changes here is the support for file back pages. The idea to
achieve it is that we almost always have the mapping a page belongs
to within the call stack as we operate on such page either from:
  - Syscall/kernel against a file (file -> inode -> mapping).
  - Syscall/kernel against virtual address (vma -> file -> mapping).
  - Write back for a given mapping (mapping -> pages).

They are few exceptions:
  - Reclaim, but reclaim does not care about mapping. Reclaim wants to
    unmap page to free it up. So all we have to do is provide special
    path to do that just like KSM does today for anonymous pages.

  - Compaction, again we do not care about the mapping for compaction.
    All we need is way to move page (ie migrate).

  - Flush data cache on some architecture the cache line are tag with
    the virtual address so when flushing a page we need to find all of
    its virtual addresses. Again we do not care about the mapping, we
    just need a way to find all virtual address in all process pointing
    to the page.

  - GUP user that want to set a page dirty. This is easy, we just do
    not allow protection to work on GUPed page and GUP also will break
    the protection. There is just no way to synchronize with GUP user
    as they violate all mm and fs rules anyway.

  - Some proc fs and other memory debugging API. Here we do not care
    about the mapping but about the page states. Also some of those
    API works on virtual address for which we can easily get the vma
    and thus the mapping.


So when we have the mapping for a page from the context and not from
page->mapping then we can use it as a key to lookup private and index
fields value for the page.

To avoid any regression risk, only protected pages sees their fields
overloaded. It means that if you are not using the page protection then
the page->mapping, page->private and page->index all stays as they are
today. Also page->mapping is always use as canonical place to lookup
the page mapping for unprotected page so that any existing code will
keep working as it does today even if the mapping we get from the
context does not match the page->mapping. More on this below.


Overview:
=========

The core idea is pretty dumb, it is just about passing new mapping
argument to every function that get a page and need the mapping
corresponding to that page. Most of the changes are done through
semantic patches. Adding new function argument on itself does not
bring any risk. The risk is in making sure that the mapping we pass as
function argument is the one corresponding to the page. To avoid any
regression we keep using page->mapping as the canonical mapping even
if it does not match what we get as a new argument ie:

Today:
    foo(struct page *page)
    {
        ... local variable declaration ...
        struct address_space *mapping = page->mapping;

        ... use mapping or inode from mapping ...
    }

After:
    foo(struct page *page, struct address_space *mapping)
    {
        ... local variable declaration ...

        mapping = fs_page_mapping(page, mapping);

        ... use mapping or inode from mapping ...
    }

With:
    struct address_space *fs_page_mapping(struct page *page,
                              struct address_space *mapping)
    {
        if (!PageProtected(page)) {
            WARN_ON(page->mapping != mapping);
            return page->mapping;
        }
        return mapping;
    }

So as long as you are not using page protection the end result before
and after the patchset is the same ie the same mapping value will be
use.


Strategy for passing down mapping of page:
==========================================

To make it easier to review that correct mapping is pass down, changes
are split in 3 steps:
    - First adds new __mapping argument to all functions that will need
      it and do not have it already (or inode as we can get mapping from
      inode). The argument is never use and thus in this step call site
      pass MAPPING_NULL which is just macro that alias to NULL but is
      easier to grep for.

    - Second replace MAPPING_NULL with the __mapping argument whereever
      possible. The rational is that if a function is passing down the
      mapping and already have it as a function argument then the real
      difficulty is not in that function but in caller of that function
      where we must ascertain that the mapping we pass down is the
      correct one.

      Replace any local MAPPING_NULL with local mapping variable or
      inode (some convertion are done manualy when they are multiple
      possible choice or when the automatic convertion would be wrong).

      At the end of this step there should not be any MAPPING_NULL left
      anywhere.

    - Finaly we replace any local mapping variable with __mapping and
      we add a check ie going from:
        foo(struct address_mapping *__mapping, struct page *page, ...){
            struct address_space *mapping = page->mapping;
            ...
        }
     To:
        foo(struct address_mapping *mapping, struct page *page, ...) {
            ...
            mapping = fs_page_mapping(mapping, page);
        }
     With:
        fs_page_mapping(struct address_mapping *mapping,
                        struct page *page) {
            if (!PageProtected(page))
                return page->mapping;
            return mapping;
        }

     Do the same for local inode that are looked up from page->mapping.

     So as long as you do not use the protection mechanism you will
     use the same mapping/inode as today.

I hope that over enough time we can build confidence so that people
start using page protection and KSM for file back pages without any
fear of regression. But in the meantime as explained in above section,
code will keep working as it does today if page is not protected.


----------------------------------------------------------------------


The present patchset just add mapping argument to the various vfs call-
backs. It does not make use of that new parameter to avoid regression.
I am posting this whole things as small contain patchset as it is rather
big and i would like to make progress step by step.


----------------------------------------------------------------------
FAQ:
----------------------------------------------------------------------

Why multiple pass in the coccinelle patches ?

    Two reasons for this:
      - To modify callback you first need to identify callback. It is
        common enough to have callback define in one file and set as
        callback in another. For that to work you would have one pass
        to identify all function use as a callback. Then anoter pass
        executed for each of the callback identified in the first pass
        to update each of them (like adding a new arugment).

      - Run time, some of the coccinelle patch are complex enough that
        they have a long runtime so to avoid spending hours on semantic
        patching it is better to run a first pass (with very simple
        semantic patch) that identify all the files that will need to
        be updated and then a second pass on each of the file with the
        actual update.


Why wrapping page->flags/index/private/mapping ?
    A protected page can (is not necessary the case) alias to multiple
    original page (also see "page alias" in glossary). Each alias do
    correspond to a different original page which had a different file
    (and thus mapping), likely at a different offset (page->index) and
    with different properties (page->flags and page->private). So we
    need to keep around all the original informations and we use the
    page and mapping (or anon_vma) as key to lookup alias and get the
    correct information for it.


Why keeping page->private for each page alias ?
    For KSM or NUMA duplication we expect the page to be uptodate and
    clean and thus we should be able to free buffers (or fs specific
    struct) and thus no need to keep around private.

    However for page exclusive access we will not alias the page with
    any other page and we might want to keep the page in a writeable
    state and thus we need to keep the page->private around.


Why a lot of patch move local variable out of declaration ?
    Many patch modify code from:
        foo(...) {
            some_type1 some_name1 = some_arg1;
            some_type2 some_name2 = some_arg2 || some_name1;
            DECLARATION...

            STATEMENT...
        }

    To:
        foo(...) {
            some_type1 some_name1;
            some_type2 some_name2;
            DECLARATION...

            some_name1 = some_arg1;
            some_name2 = some_arg2 || some_name1;

            STATEMENT...
        }

    This is done so that we can add a test to check if mapping/inode
    we get in argument does match the page->mapping field. We need to
    add such test after all declaration but before any assignment to
    local variable. Hence why declaration initializer need to become
    regular statement after all declarations.

    Saddly no way around that and this lead to a lot of code churn.


----------------------------------------------------------------------
Glossary:
----------------------------------------------------------------------

page alias
    A protected page can alias with different file and/or vma (and thus
    anon_vma). A page alias is just the information that correspond to
    each of the file/vma for that that.

    For instance let say that PA was a file back page it had:
        PA->mapping == mapping corresponding to the file PA belong
        PA->index   == offset into the file for PA
        PA->private == fs specific information (like buffer_head)
        PA->flags   == fs/mm specific informations

    PB was a anonymous page, it had:
        PB->mapping == anon_vma for the vma into which PB is map
        PB->index   == offset into the vma
        PB->private == usualy 0 but can contain a swap entry
        PB->flags   == fs/mm specific informations

    Now if PA and PB are merge together into a protect page then the
    protected page will have an alias struct for each of the original
    page ie:
        struct page_alias {
            struct list list;
            unsigned long flags;
            unsigned long index;
            void *mapping;
            void *private;
        };
        struct pxa {
            struct list aliases;
            ...
        };

    So now for the PXA (protected page) that replace PA and PB we have:
        PP->mapping == pointer to struct pxa for the page

    Note that PP->index/private are undefine for now but could be use
    for optimization like storing the most recent alias lookup.

    And the alias list will have one entry for PA and one for PB:
        PA_alias.mapping = PA.mapping
        PA_alias.private = PA.private
        PA_alias.index   = PA.index
        PA_alias.flags   = PA.flags

        PB_alias.mapping = PB.mapping
        PB_alias.private = PB.private
        PB_alias.index   = PB.index
        PB_alias.flags   = PB.flags


raw page
    A page that is not use as anonymous or file back. Many cases, can
    be a free page, a page use by a device driver or some kernel
    features. Each case can sometimes make use of the various struct
    page fields (mapping, private, index, lru, ...).

    To get proper education this patchset wrap usage of those field in
    those code with raw_page*() helpers. This also make it clears what
    kind of page we expect to see in such driver/feature.


Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>


Jérôme Glisse (14):
  mm/pxa: page exclusive access add header file for all helpers.
  fs: define filler_t as a function pointer type
  fs: directly use a_ops->freepage() instead of a local copy of it.
  mm: add struct address_space to readpage() callback
  mm: add struct address_space to writepage() callback
  mm: add struct address_space to set_page_dirty() callback
  mm: add struct address_space to invalidatepage() callback
  mm: add struct address_space to releasepage() callback
  mm: add struct address_space to freepage() callback
  mm: add struct address_space to putback_page() callback
  mm: add struct address_space to launder_page() callback
  mm: add struct address_space to is_partially_uptodate() callback
  mm: add struct address_space to isolate_page() callback
  mm: add struct address_space to is_dirty_writeback() callback

 drivers/gpu/drm/i915/gem/i915_gem_shmem.c |  3 +-
 drivers/video/fbdev/core/fb_defio.c       |  3 +-
 fs/9p/vfs_addr.c                          | 24 ++++++---
 fs/adfs/inode.c                           |  6 ++-
 fs/affs/file.c                            |  9 ++--
 fs/affs/symlink.c                         |  4 +-
 fs/afs/dir.c                              | 15 ++++--
 fs/afs/file.c                             | 18 ++++---
 fs/afs/internal.h                         |  7 +--
 fs/afs/write.c                            |  9 ++--
 fs/befs/linuxvfs.c                        | 13 +++--
 fs/bfs/file.c                             |  6 ++-
 fs/block_dev.c                            | 10 ++--
 fs/btrfs/ctree.h                          |  3 +-
 fs/btrfs/disk-io.c                        | 16 +++---
 fs/btrfs/extent_io.c                      |  5 +-
 fs/btrfs/file.c                           |  2 +-
 fs/btrfs/free-space-cache.c               |  2 +-
 fs/btrfs/inode.c                          | 24 +++++----
 fs/btrfs/ioctl.c                          |  2 +-
 fs/btrfs/relocation.c                     |  2 +-
 fs/btrfs/send.c                           |  2 +-
 fs/buffer.c                               | 14 +++--
 fs/cachefiles/rdwr.c                      |  6 +--
 fs/ceph/addr.c                            | 26 +++++----
 fs/cifs/cifssmb.c                         |  2 +-
 fs/cifs/file.c                            | 15 ++++--
 fs/coda/symlink.c                         |  4 +-
 fs/cramfs/inode.c                         |  3 +-
 fs/ecryptfs/mmap.c                        |  8 ++-
 fs/efs/inode.c                            |  3 +-
 fs/efs/symlink.c                          |  4 +-
 fs/erofs/data.c                           |  4 +-
 fs/erofs/super.c                          |  8 +--
 fs/erofs/zdata.c                          |  4 +-
 fs/exfat/inode.c                          |  6 ++-
 fs/ext2/inode.c                           | 11 ++--
 fs/ext4/inode.c                           | 35 +++++++-----
 fs/f2fs/checkpoint.c                      |  8 +--
 fs/f2fs/data.c                            | 20 ++++---
 fs/f2fs/f2fs.h                            |  8 +--
 fs/f2fs/node.c                            |  8 +--
 fs/fat/inode.c                            |  6 ++-
 fs/freevxfs/vxfs_immed.c                  |  6 ++-
 fs/freevxfs/vxfs_subr.c                   |  6 ++-
 fs/fuse/dir.c                             |  4 +-
 fs/fuse/file.c                            |  9 ++--
 fs/gfs2/aops.c                            | 29 ++++++----
 fs/gfs2/inode.h                           |  3 +-
 fs/gfs2/meta_io.c                         |  4 +-
 fs/hfs/inode.c                            |  9 ++--
 fs/hfsplus/inode.c                        | 10 ++--
 fs/hostfs/hostfs_kern.c                   |  6 ++-
 fs/hpfs/file.c                            |  6 ++-
 fs/hpfs/namei.c                           |  4 +-
 fs/hugetlbfs/inode.c                      |  3 +-
 fs/iomap/buffered-io.c                    | 15 +++---
 fs/iomap/seek.c                           |  2 +-
 fs/isofs/compress.c                       |  3 +-
 fs/isofs/inode.c                          |  3 +-
 fs/isofs/rock.c                           |  4 +-
 fs/jffs2/file.c                           | 11 ++--
 fs/jffs2/os-linux.h                       |  3 +-
 fs/jfs/inode.c                            |  6 ++-
 fs/jfs/jfs_metapage.c                     | 15 ++++--
 fs/libfs.c                                | 13 +++--
 fs/minix/inode.c                          |  6 ++-
 fs/mpage.c                                |  2 +-
 fs/nfs/dir.c                              | 14 ++---
 fs/nfs/file.c                             | 16 +++---
 fs/nfs/read.c                             |  6 ++-
 fs/nfs/symlink.c                          |  7 +--
 fs/nfs/write.c                            |  9 ++--
 fs/nilfs2/inode.c                         | 11 ++--
 fs/nilfs2/mdt.c                           |  3 +-
 fs/nilfs2/page.c                          |  2 +-
 fs/nilfs2/segment.c                       |  4 +-
 fs/ntfs/aops.c                            | 10 ++--
 fs/ntfs/file.c                            |  2 +-
 fs/ocfs2/aops.c                           |  9 ++--
 fs/ocfs2/symlink.c                        |  4 +-
 fs/omfs/file.c                            |  6 ++-
 fs/orangefs/inode.c                       | 38 +++++++------
 fs/qnx4/inode.c                           |  3 +-
 fs/qnx6/inode.c                           |  3 +-
 fs/reiserfs/inode.c                       | 20 ++++---
 fs/romfs/super.c                          |  3 +-
 fs/squashfs/file.c                        |  4 +-
 fs/squashfs/symlink.c                     |  4 +-
 fs/sysv/itree.c                           |  6 ++-
 fs/ubifs/file.c                           | 21 +++++---
 fs/udf/file.c                             |  7 ++-
 fs/udf/inode.c                            |  8 +--
 fs/udf/symlink.c                          |  4 +-
 fs/ufs/inode.c                            |  6 ++-
 fs/vboxsf/file.c                          |  6 ++-
 fs/xfs/xfs_aops.c                         |  6 +--
 fs/zonefs/super.c                         |  6 ++-
 include/linux/balloon_compaction.h        | 11 ++--
 include/linux/buffer_head.h               | 12 +++--
 include/linux/fs.h                        | 38 +++++++------
 include/linux/iomap.h                     | 15 +++---
 include/linux/mm.h                        | 11 +++-
 include/linux/nfs_fs.h                    |  5 +-
 include/linux/page-xa.h                   | 66 +++++++++++++++++++++++
 include/linux/pagemap.h                   |  6 +--
 include/linux/swap.h                      | 10 ++--
 mm/balloon_compaction.c                   |  5 +-
 mm/filemap.c                              | 33 ++++++------
 mm/migrate.c                              |  6 +--
 mm/page-writeback.c                       | 16 +++---
 mm/page_io.c                              | 11 ++--
 mm/readahead.c                            |  6 +--
 mm/shmem.c                                |  5 +-
 mm/truncate.c                             |  9 ++--
 mm/vmscan.c                               | 12 ++---
 mm/z3fold.c                               |  6 ++-
 mm/zsmalloc.c                             |  6 ++-
 118 files changed, 722 insertions(+), 385 deletions(-)
 create mode 100644 include/linux/page-xa.h

-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 02/14] fs: define filler_t as a function pointer type
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
@ 2020-10-07  1:05 ` jglisse
  2020-10-07  1:05 ` [PATCH 03/14] fs: directly use a_ops->freepage() instead of a local copy of it jglisse
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

Coccinelle is confuse by filler_t not being a regular function pointer
type. As they are no reason to define filler_t as a non pointer type
redefine it as a function pointer type and update function prototype
accordingly.

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>
---
 fs/nfs/dir.c            | 2 +-
 fs/nfs/symlink.c        | 4 ++--
 include/linux/pagemap.h | 6 +++---
 mm/filemap.c            | 5 ++---
 mm/readahead.c          | 2 +-
 5 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index cb52db9a0cfb7..da1fe71ae810d 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -740,7 +740,7 @@ static
 struct page *get_cache_page(nfs_readdir_descriptor_t *desc)
 {
 	return read_cache_page(desc->file->f_mapping, desc->page_index,
-			nfs_readdir_filler, desc);
+			(filler_t)nfs_readdir_filler, desc);
 }
 
 /*
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index 25ba299fdac2e..76691d94ae5f8 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -66,8 +66,8 @@ static const char *nfs_get_link(struct dentry *dentry,
 		err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping));
 		if (err)
 			return err;
-		page = read_cache_page(&inode->i_data, 0, nfs_symlink_filler,
-				inode);
+		page = read_cache_page(&inode->i_data, 0,
+				(filler_t)nfs_symlink_filler, inode);
 		if (IS_ERR(page))
 			return ERR_CAST(page);
 	}
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 7de11dcd534d6..9acfc605b3bc3 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -264,7 +264,7 @@ static inline gfp_t readahead_gfp_mask(struct address_space *x)
 	return mapping_gfp_mask(x) | __GFP_NORETRY | __GFP_NOWARN;
 }
 
-typedef int filler_t(void *, struct page *);
+typedef int (*filler_t)(void *, struct page *);
 
 pgoff_t page_cache_next_miss(struct address_space *mapping,
 			     pgoff_t index, unsigned long max_scan);
@@ -425,11 +425,11 @@ static inline struct page *grab_cache_page(struct address_space *mapping,
 }
 
 extern struct page * read_cache_page(struct address_space *mapping,
-				pgoff_t index, filler_t *filler, void *data);
+				pgoff_t index, filler_t filler, void *data);
 extern struct page * read_cache_page_gfp(struct address_space *mapping,
 				pgoff_t index, gfp_t gfp_mask);
 extern int read_cache_pages(struct address_space *mapping,
-		struct list_head *pages, filler_t *filler, void *data);
+		struct list_head *pages, filler_t filler, void *data);
 
 static inline struct page *read_mapping_page(struct address_space *mapping,
 				pgoff_t index, void *data)
diff --git a/mm/filemap.c b/mm/filemap.c
index 99c49eeae71b8..2cdbbffc55522 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2942,8 +2942,7 @@ static struct page *wait_on_page_read(struct page *page)
 }
 
 static struct page *do_read_cache_page(struct address_space *mapping,
-				pgoff_t index,
-				int (*filler)(void *, struct page *),
+				pgoff_t index, filler_t filler,
 				void *data,
 				gfp_t gfp)
 {
@@ -3064,7 +3063,7 @@ static struct page *do_read_cache_page(struct address_space *mapping,
  */
 struct page *read_cache_page(struct address_space *mapping,
 				pgoff_t index,
-				int (*filler)(void *, struct page *),
+				filler_t filler,
 				void *data)
 {
 	return do_read_cache_page(mapping, index, filler, data,
diff --git a/mm/readahead.c b/mm/readahead.c
index 3c9a8dd7c56c8..cd67c9cfa931a 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -87,7 +87,7 @@ static void read_cache_pages_invalidate_pages(struct address_space *mapping,
  * Returns: %0 on success, error return by @filler otherwise
  */
 int read_cache_pages(struct address_space *mapping, struct list_head *pages,
-			int (*filler)(void *, struct page *), void *data)
+			filler_t filler, void *data)
 {
 	struct page *page;
 	int ret = 0;
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 03/14] fs: directly use a_ops->freepage() instead of a local copy of it.
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
  2020-10-07  1:05 ` [PATCH 02/14] fs: define filler_t as a function pointer type jglisse
@ 2020-10-07  1:05 ` jglisse
  2020-10-07  1:05 ` [PATCH 04/14] mm: add struct address_space to readpage() callback jglisse
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-fsdevel, linux-mm, Alexander Viro,
	Tejun Heo, Jan Kara, Josef Bacik, Andrew Morton

From: Jérôme Glisse <jglisse@redhat.com>

Coccinelle is confuse with function pointer, convert to directly
use a_ops->freepage() to be nice to coccinelle.

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-mm@kvack.org
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <josef@toxicpanda.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
---
 mm/filemap.c | 12 ++++--------
 mm/vmscan.c  |  7 ++-----
 2 files changed, 6 insertions(+), 13 deletions(-)

diff --git a/mm/filemap.c b/mm/filemap.c
index 2cdbbffc55522..ba892599a2717 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -242,11 +242,8 @@ void __delete_from_page_cache(struct page *page, void *shadow)
 static void page_cache_free_page(struct address_space *mapping,
 				struct page *page)
 {
-	void (*freepage)(struct page *);
-
-	freepage = mapping->a_ops->freepage;
-	if (freepage)
-		freepage(page);
+	if (mapping->a_ops->freepage)
+		mapping->a_ops->freepage(page);
 
 	if (PageTransHuge(page) && !PageHuge(page)) {
 		page_ref_sub(page, HPAGE_PMD_NR);
@@ -790,7 +787,6 @@ EXPORT_SYMBOL(file_write_and_wait_range);
 int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
 {
 	struct address_space *mapping = old->mapping;
-	void (*freepage)(struct page *) = mapping->a_ops->freepage;
 	pgoff_t offset = old->index;
 	XA_STATE(xas, &mapping->i_pages, offset);
 	unsigned long flags;
@@ -819,8 +815,8 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
 	if (PageSwapBacked(new))
 		__inc_lruvec_page_state(new, NR_SHMEM);
 	xas_unlock_irqrestore(&xas, flags);
-	if (freepage)
-		freepage(old);
+	if (mapping->a_ops->freepage)
+		mapping->a_ops->freepage(old);
 	put_page(old);
 
 	return 0;
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 466fc3144fffc..6db869339073d 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -903,9 +903,6 @@ static int __remove_mapping(struct address_space *mapping, struct page *page,
 		xa_unlock_irqrestore(&mapping->i_pages, flags);
 		put_swap_page(page, swap);
 	} else {
-		void (*freepage)(struct page *);
-
-		freepage = mapping->a_ops->freepage;
 		/*
 		 * Remember a shadow entry for reclaimed file cache in
 		 * order to detect refaults, thus thrashing, later on.
@@ -928,8 +925,8 @@ static int __remove_mapping(struct address_space *mapping, struct page *page,
 		__delete_from_page_cache(page, shadow);
 		xa_unlock_irqrestore(&mapping->i_pages, flags);
 
-		if (freepage != NULL)
-			freepage(page);
+		if (mapping->a_ops->freepage != NULL)
+			mapping->a_ops->freepage(page);
 	}
 
 	return 1;
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 04/14] mm: add struct address_space to readpage() callback
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
  2020-10-07  1:05 ` [PATCH 02/14] fs: define filler_t as a function pointer type jglisse
  2020-10-07  1:05 ` [PATCH 03/14] fs: directly use a_ops->freepage() instead of a local copy of it jglisse
@ 2020-10-07  1:05 ` jglisse
  2020-10-07  1:05 ` [PATCH 05/14] mm: add struct address_space to writepage() callback jglisse
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

This is part of patchset to remove dependency on struct page.mapping
field so that we can temporarily update it to point to a special
structure tracking temporary page state (note that original mapping
pointer is preserved and can still be accessed but at a cost).

Add struct address_space to readpage() callback arguments.

Note that this patch does not make use of the new argument, nor does
it use a valid one at call site (by default this patch just use NULL
for new argument value).

Use following script (from root of linux kernel tree):

./that-script.sh that-semantic-patch.spatch

%<--------------------------------------------------------------------
#!/bin/sh
spatch_file=$1

echo PART1 ===========================================================

# P1 find callback functions name
spatch  --dir . --no-includes -D part1 --sp-file $spatch_file

echo PART2 ===========================================================

# P2 change callback function prototype
cat /tmp/unicorn-functions | sort | uniq | while read func ; do
    for file in $( git grep -l $func -- '*.[ch]' ) ; do
        echo $file
        spatch --no-includes --in-place -D part2 \
               -D fn=$func --sp-file $spatch_file $file
    done
done

echo PART 3 ==========================================================

# P3 find all function which call the callback
spatch --dir . --include-headers -D part3 --sp-file $spatch_file

echo PART 4===========================================================

# P4 change all funcitons which call the callback
cat /tmp/unicorn-files | sort | uniq | while read file ; do
    echo $file
    spatch --no-includes --in-place -D part4 \
           --sp-file $spatch_file $file
done
-------------------------------------------------------------------->%

With the following semantic patch:

%<--------------------------------------------------------------------
virtual part1, part2, part3, part4

// ----------------------------------------------------------------------------
// Part 1 is grepping all function that are use as callback for readpage.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part1@
@@
file=open('/tmp/unicorn-functions', 'w')
file.close()

// match function name use as a callback
@p1r2 depends on part1@
identifier I1, FN;
@@
struct address_space_operations I1 = {..., .readpage = FN, ...};

@script:python p1r3 depends on p1r2@
funcname << p1r2.FN;
@@
if funcname != "NULL":
  file=open('/tmp/unicorn-functions', 'a')
  file.write(funcname + '\n')
  file.close()

@p1r4 depends on part1 exists@
expression E1, E2, E3;
identifier FN;
type T1;
@@
{...
(
read_cache_page(E1, E2, (T1)FN, E3)
|
read_cache_pages(E1, E2, (T1)FN, E3)
)
...}

@script:python p1r5 depends on p1r4@
funcname << p1r4.FN;
@@
if funcname != "NULL":
  file=open('/tmp/unicorn-functions', 'a')
  file.write(funcname + '\n')
  print(funcname)
  file.close()

// -------------------------------------------------------------------
// Part 2 modify callback

// Add address_space argument to the function (readpage callback one)
@p2r1 depends on part2@
identifier virtual.fn;
identifier I1, I2;
type T1, T2;
@@
int fn(T1 I1,
+struct address_space *__mapping,
T2 I2) { ... }

@p2r2 depends on part2@
identifier virtual.fn;
identifier I1, I2;
type T1, T2;
@@
int fn(T1 I1,
+struct address_space *__mapping,
T2 I2);

@p2r3 depends on part2@
identifier virtual.fn;
type T1, T2;
@@
int fn(T1,
+struct address_space *,
T2);

@p2r4 depends on part2@
identifier virtual.fn;
expression E1, E2;
@@
fn(E1,
+MAPPING_NULL,
E2)

// ----------------------------------------------------------------------------
// Part 3 is grepping all function that are use the callback for readpage.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part3@
@@
file=open('/tmp/unicorn-files', 'w')
file.write("./include/linux/pagemap.h\n")
file.write("./include/linux/mm.h\n")
file.write("./include/linux/fs.h\n")
file.write("./mm/readahead.c\n")
file.write("./mm/filemap.c\n")
file.close()

@p3r1 depends on part3 exists@
expression E1, E2, E3;
identifier FN;
position P;
@@
FN@P(...) {...
(
E1.a_ops->readpage(E2, E3)
|
E1->a_ops->readpage(E2, E3)
)
...}

@script:python p3r2 depends on p3r1@
P << p3r1.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

@p3r4 depends on part3 exists@
expression E1, E2, E3, E4;
identifier FN;
position P;
@@
FN@P(...) {...
(
read_cache_page(E1, E2, E3, E4)
|
read_cache_pages(E1, E2, E3, E4)
)
...}

@script:python p3r5 depends on p3r4@
P << p3r4.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

// -------------------------------------------------------------------
// Part 4 generic modification
@p4r1 depends on part4@
@@
struct address_space_operations { ... int (*readpage)(struct file *,
+struct address_space *,
struct page *); ... };

@p4r2 depends on part4@
expression E1, E2, E3;
@@
E1.a_ops->readpage(E2,
+MAPPING_NULL,
E3)

@p4r3 depends on part4@
expression E1, E2, E3;
@@
E1->a_ops->readpage(E2,
+MAPPING_NULL,
E3)

@p4r4 depends on part4@
@@
-typedef int (*filler_t)(void *, struct page *);
+typedef int (*filler_t)(void *, struct address_space *, struct page *);

@p4r5 depends on part4 exists@
identifier FN, I1;
expression E1, E2;
@@
FN(..., filler_t I1, ...) {...
I1(E1,
+MAPPING_NULL,
E2)
...}

@p4r6 depends on part4 exists@
struct address_space_operations *aops;
expression E1, E2;
@@
aops->readpage(E1,
+MAPPING_NULL,
E2)
-------------------------------------------------------------------->%

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>
---
 fs/9p/vfs_addr.c            | 11 +++++++----
 fs/adfs/inode.c             |  3 ++-
 fs/affs/file.c              |  6 ++++--
 fs/affs/symlink.c           |  4 +++-
 fs/afs/file.c               |  6 ++++--
 fs/befs/linuxvfs.c          | 13 +++++++++----
 fs/bfs/file.c               |  3 ++-
 fs/block_dev.c              |  4 +++-
 fs/btrfs/ctree.h            |  3 ++-
 fs/btrfs/disk-io.c          |  3 ++-
 fs/btrfs/file.c             |  2 +-
 fs/btrfs/free-space-cache.c |  2 +-
 fs/btrfs/inode.c            |  5 +++--
 fs/btrfs/ioctl.c            |  2 +-
 fs/btrfs/relocation.c       |  2 +-
 fs/btrfs/send.c             |  2 +-
 fs/buffer.c                 |  2 +-
 fs/cachefiles/rdwr.c        |  6 +++---
 fs/ceph/addr.c              |  3 ++-
 fs/cifs/file.c              |  3 ++-
 fs/coda/symlink.c           |  4 +++-
 fs/cramfs/inode.c           |  3 ++-
 fs/ecryptfs/mmap.c          |  4 +++-
 fs/efs/inode.c              |  3 ++-
 fs/efs/symlink.c            |  4 +++-
 fs/erofs/data.c             |  4 +++-
 fs/erofs/zdata.c            |  4 +++-
 fs/exfat/inode.c            |  3 ++-
 fs/ext2/inode.c             |  3 ++-
 fs/ext4/inode.c             |  3 ++-
 fs/f2fs/data.c              |  4 +++-
 fs/fat/inode.c              |  3 ++-
 fs/freevxfs/vxfs_immed.c    |  6 ++++--
 fs/freevxfs/vxfs_subr.c     |  6 ++++--
 fs/fuse/dir.c               |  4 +++-
 fs/fuse/file.c              |  3 ++-
 fs/gfs2/aops.c              |  8 +++++---
 fs/hfs/inode.c              |  3 ++-
 fs/hfsplus/inode.c          |  4 +++-
 fs/hostfs/hostfs_kern.c     |  3 ++-
 fs/hpfs/file.c              |  3 ++-
 fs/hpfs/namei.c             |  4 +++-
 fs/isofs/compress.c         |  3 ++-
 fs/isofs/inode.c            |  3 ++-
 fs/isofs/rock.c             |  4 +++-
 fs/jffs2/file.c             | 11 +++++++----
 fs/jffs2/os-linux.h         |  3 ++-
 fs/jfs/inode.c              |  3 ++-
 fs/jfs/jfs_metapage.c       |  3 ++-
 fs/libfs.c                  |  3 ++-
 fs/minix/inode.c            |  3 ++-
 fs/nfs/dir.c                |  3 ++-
 fs/nfs/file.c               |  2 +-
 fs/nfs/read.c               |  6 ++++--
 fs/nfs/symlink.c            |  3 ++-
 fs/nilfs2/inode.c           |  3 ++-
 fs/ntfs/aops.c              |  3 ++-
 fs/ocfs2/aops.c             |  3 ++-
 fs/ocfs2/symlink.c          |  4 +++-
 fs/omfs/file.c              |  3 ++-
 fs/orangefs/inode.c         |  4 +++-
 fs/qnx4/inode.c             |  3 ++-
 fs/qnx6/inode.c             |  3 ++-
 fs/reiserfs/inode.c         |  3 ++-
 fs/romfs/super.c            |  3 ++-
 fs/squashfs/file.c          |  4 +++-
 fs/squashfs/symlink.c       |  4 +++-
 fs/sysv/itree.c             |  3 ++-
 fs/ubifs/file.c             |  3 ++-
 fs/udf/file.c               |  4 +++-
 fs/udf/inode.c              |  3 ++-
 fs/udf/symlink.c            |  4 +++-
 fs/ufs/inode.c              |  3 ++-
 fs/vboxsf/file.c            |  3 ++-
 fs/xfs/xfs_aops.c           |  2 +-
 fs/zonefs/super.c           |  3 ++-
 include/linux/fs.h          |  5 +++--
 include/linux/nfs_fs.h      |  2 +-
 include/linux/pagemap.h     |  2 +-
 mm/filemap.c                |  9 +++++----
 mm/page_io.c                |  2 +-
 mm/readahead.c              |  4 ++--
 82 files changed, 207 insertions(+), 106 deletions(-)

diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c
index cce9ace651a2d..79fa773dbe95e 100644
--- a/fs/9p/vfs_addr.c
+++ b/fs/9p/vfs_addr.c
@@ -35,7 +35,8 @@
  * @page: structure to page
  *
  */
-static int v9fs_fid_readpage(void *data, struct page *page)
+static int v9fs_fid_readpage(void *data, struct address_space *__mapping,
+			     struct page *page)
 {
 	struct p9_fid *fid = data;
 	struct inode *inode = page->mapping->host;
@@ -80,9 +81,11 @@ static int v9fs_fid_readpage(void *data, struct page *page)
  *
  */
 
-static int v9fs_vfs_readpage(struct file *filp, struct page *page)
+static int v9fs_vfs_readpage(struct file *filp,
+			     struct address_space *__mapping,
+			     struct page *page)
 {
-	return v9fs_fid_readpage(filp->private_data, page);
+	return v9fs_fid_readpage(filp->private_data, MAPPING_NULL, page);
 }
 
 /**
@@ -279,7 +282,7 @@ static int v9fs_write_begin(struct file *filp, struct address_space *mapping,
 	if (len == PAGE_SIZE)
 		goto out;
 
-	retval = v9fs_fid_readpage(v9inode->writeback_fid, page);
+	retval = v9fs_fid_readpage(v9inode->writeback_fid, MAPPING_NULL, page);
 	put_page(page);
 	if (!retval)
 		goto start;
diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c
index 32620f4a7623e..2a29f7d4a4dd0 100644
--- a/fs/adfs/inode.c
+++ b/fs/adfs/inode.c
@@ -38,7 +38,8 @@ static int adfs_writepage(struct page *page, struct writeback_control *wbc)
 	return block_write_full_page(page, adfs_get_block, wbc);
 }
 
-static int adfs_readpage(struct file *file, struct page *page)
+static int adfs_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
 	return block_read_full_page(page, adfs_get_block);
 }
diff --git a/fs/affs/file.c b/fs/affs/file.c
index d91b0133d95da..a20d5a1298335 100644
--- a/fs/affs/file.c
+++ b/fs/affs/file.c
@@ -375,7 +375,8 @@ static int affs_writepage(struct page *page, struct writeback_control *wbc)
 	return block_write_full_page(page, affs_get_block, wbc);
 }
 
-static int affs_readpage(struct file *file, struct page *page)
+static int affs_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
 	return block_read_full_page(page, affs_get_block);
 }
@@ -627,7 +628,8 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)
 }
 
 static int
-affs_readpage_ofs(struct file *file, struct page *page)
+affs_readpage_ofs(struct file *file, struct address_space *__mapping,
+		  struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	u32 to;
diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c
index a7531b26e8f02..f6fca124002e8 100644
--- a/fs/affs/symlink.c
+++ b/fs/affs/symlink.c
@@ -11,7 +11,9 @@
 
 #include "affs.h"
 
-static int affs_symlink_readpage(struct file *file, struct page *page)
+static int affs_symlink_readpage(struct file *file,
+				 struct address_space *__mapping,
+				 struct page *page)
 {
 	struct buffer_head *bh;
 	struct inode *inode = page->mapping->host;
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 371d1488cc549..908f9e3196251 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -17,7 +17,8 @@
 #include "internal.h"
 
 static int afs_file_mmap(struct file *file, struct vm_area_struct *vma);
-static int afs_readpage(struct file *file, struct page *page);
+static int afs_readpage(struct file *file, struct address_space *__mapping,
+			struct page *page);
 static void afs_invalidatepage(struct page *page, unsigned int offset,
 			       unsigned int length);
 static int afs_releasepage(struct page *page, gfp_t gfp_flags);
@@ -388,7 +389,8 @@ int afs_page_filler(void *data, struct page *page)
  * read page from file, directory or symlink, given a file to nominate the key
  * to be used
  */
-static int afs_readpage(struct file *file, struct page *page)
+static int afs_readpage(struct file *file, struct address_space *__mapping,
+			struct page *page)
 {
 	struct key *key;
 	int ret;
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index 2482032021cac..4bf5395fb4eeb 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -40,7 +40,8 @@ MODULE_LICENSE("GPL");
 
 static int befs_readdir(struct file *, struct dir_context *);
 static int befs_get_block(struct inode *, sector_t, struct buffer_head *, int);
-static int befs_readpage(struct file *file, struct page *page);
+static int befs_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page);
 static sector_t befs_bmap(struct address_space *mapping, sector_t block);
 static struct dentry *befs_lookup(struct inode *, struct dentry *,
 				  unsigned int);
@@ -48,7 +49,8 @@ static struct inode *befs_iget(struct super_block *, unsigned long);
 static struct inode *befs_alloc_inode(struct super_block *sb);
 static void befs_free_inode(struct inode *inode);
 static void befs_destroy_inodecache(void);
-static int befs_symlink_readpage(struct file *, struct page *);
+static int befs_symlink_readpage(struct file *, struct address_space *,
+				 struct page *);
 static int befs_utf2nls(struct super_block *sb, const char *in, int in_len,
 			char **out, int *out_len);
 static int befs_nls2utf(struct super_block *sb, const char *in, int in_len,
@@ -109,7 +111,8 @@ static const struct export_operations befs_export_operations = {
  * positions to disk blocks.
  */
 static int
-befs_readpage(struct file *file, struct page *page)
+befs_readpage(struct file *file, struct address_space *__mapping,
+	      struct page *page)
 {
 	return block_read_full_page(page, befs_get_block);
 }
@@ -468,7 +471,9 @@ befs_destroy_inodecache(void)
  * The data stream become link name. Unless the LONG_SYMLINK
  * flag is set.
  */
-static int befs_symlink_readpage(struct file *unused, struct page *page)
+static int befs_symlink_readpage(struct file *unused,
+				 struct address_space *__mapping,
+				 struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	struct super_block *sb = inode->i_sb;
diff --git a/fs/bfs/file.c b/fs/bfs/file.c
index 0dceefc54b48a..4154c23e23e24 100644
--- a/fs/bfs/file.c
+++ b/fs/bfs/file.c
@@ -155,7 +155,8 @@ static int bfs_writepage(struct page *page, struct writeback_control *wbc)
 	return block_write_full_page(page, bfs_get_block, wbc);
 }
 
-static int bfs_readpage(struct file *file, struct page *page)
+static int bfs_readpage(struct file *file, struct address_space *__mapping,
+			struct page *page)
 {
 	return block_read_full_page(page, bfs_get_block);
 }
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 8ae833e004439..b8e6e1995f396 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -598,7 +598,9 @@ static int blkdev_writepage(struct page *page, struct writeback_control *wbc)
 	return block_write_full_page(page, blkdev_get_block, wbc);
 }
 
-static int blkdev_readpage(struct file * file, struct page * page)
+static int blkdev_readpage(struct file * file,
+			   struct address_space *__mapping,
+			   struct page * page)
 {
 	return block_read_full_page(page, blkdev_get_block);
 }
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 9a72896bed2ee..038fe30aadd94 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2976,7 +2976,8 @@ int btrfs_bio_fits_in_stripe(struct page *page, size_t size, struct bio *bio,
 			     unsigned long bio_flags);
 void btrfs_set_range_writeback(struct extent_io_tree *tree, u64 start, u64 end);
 vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf);
-int btrfs_readpage(struct file *file, struct page *page);
+int btrfs_readpage(struct file *file, struct address_space *__mapping,
+		   struct page *page);
 void btrfs_evict_inode(struct inode *inode);
 int btrfs_write_inode(struct inode *inode, struct writeback_control *wbc);
 struct inode *btrfs_alloc_inode(struct super_block *sb);
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 9f72b092bc228..51ca16ab59e07 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -951,7 +951,8 @@ static int btree_writepages(struct address_space *mapping,
 	return btree_write_cache_pages(mapping, wbc);
 }
 
-static int btree_readpage(struct file *file, struct page *page)
+static int btree_readpage(struct file *file, struct address_space *__mapping,
+			  struct page *page)
 {
 	return extent_read_full_page(page, btree_get_extent, 0);
 }
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 4507c3d093994..db14f654973d0 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1385,7 +1385,7 @@ static int prepare_uptodate_page(struct inode *inode,
 
 	if (((pos & (PAGE_SIZE - 1)) || force_uptodate) &&
 	    !PageUptodate(page)) {
-		ret = btrfs_readpage(NULL, page);
+		ret = btrfs_readpage(NULL, MAPPING_NULL, page);
 		if (ret)
 			return ret;
 		lock_page(page);
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index dc82fd0c80cbb..b2d9e45478fd6 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -386,7 +386,7 @@ static int io_ctl_prepare_pages(struct btrfs_io_ctl *io_ctl, bool uptodate)
 		}
 		io_ctl->pages[i] = page;
 		if (uptodate && !PageUptodate(page)) {
-			btrfs_readpage(NULL, page);
+			btrfs_readpage(NULL, MAPPING_NULL, page);
 			lock_page(page);
 			if (page->mapping != inode->i_mapping) {
 				btrfs_err(BTRFS_I(inode)->root->fs_info,
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 9570458aa8471..1213671571bb2 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4560,7 +4560,7 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
 	}
 
 	if (!PageUptodate(page)) {
-		ret = btrfs_readpage(NULL, page);
+		ret = btrfs_readpage(NULL, MAPPING_NULL, page);
 		lock_page(page);
 		if (page->mapping != mapping) {
 			unlock_page(page);
@@ -8005,7 +8005,8 @@ static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
 	return extent_fiemap(inode, fieinfo, start, len);
 }
 
-int btrfs_readpage(struct file *file, struct page *page)
+int btrfs_readpage(struct file *file, struct address_space *__mapping,
+		   struct page *page)
 {
 	return extent_read_full_page(page, btrfs_get_extent, 0);
 }
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 2d9109d9e98f9..9ab9292bfb91d 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1321,7 +1321,7 @@ static int cluster_pages_for_defrag(struct inode *inode,
 		}
 
 		if (!PageUptodate(page)) {
-			btrfs_readpage(NULL, page);
+			btrfs_readpage(NULL, MAPPING_NULL, page);
 			lock_page(page);
 			if (!PageUptodate(page)) {
 				unlock_page(page);
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 4ba1ab9cc76db..09d6165d256bd 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -2734,7 +2734,7 @@ static int relocate_file_extent_cluster(struct inode *inode,
 		}
 
 		if (!PageUptodate(page)) {
-			btrfs_readpage(NULL, page);
+			btrfs_readpage(NULL, MAPPING_NULL, page);
 			lock_page(page);
 			if (!PageUptodate(page)) {
 				unlock_page(page);
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index d9813a5b075ac..e8de679bd8186 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -4853,7 +4853,7 @@ static ssize_t fill_read_buf(struct send_ctx *sctx, u64 offset, u32 len)
 		}
 
 		if (!PageUptodate(page)) {
-			btrfs_readpage(NULL, page);
+			btrfs_readpage(NULL, MAPPING_NULL, page);
 			lock_page(page);
 			if (!PageUptodate(page)) {
 				unlock_page(page);
diff --git a/fs/buffer.c b/fs/buffer.c
index 50bbc99e3d960..c99a468833828 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2855,7 +2855,7 @@ int nobh_truncate_page(struct address_space *mapping,
 
 	/* Ok, it's mapped. Make sure it's up-to-date */
 	if (!PageUptodate(page)) {
-		err = mapping->a_ops->readpage(NULL, page);
+		err = mapping->a_ops->readpage(NULL, MAPPING_NULL, page);
 		if (err) {
 			put_page(page);
 			goto out;
diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c
index 3080cda9e8245..0da0a49b93e6a 100644
--- a/fs/cachefiles/rdwr.c
+++ b/fs/cachefiles/rdwr.c
@@ -119,7 +119,7 @@ static int cachefiles_read_reissue(struct cachefiles_object *object,
 			goto unlock_discard;
 
 		_debug("reissue read");
-		ret = bmapping->a_ops->readpage(NULL, backpage);
+		ret = bmapping->a_ops->readpage(NULL, MAPPING_NULL, backpage);
 		if (ret < 0)
 			goto unlock_discard;
 	}
@@ -281,7 +281,7 @@ static int cachefiles_read_backing_file_one(struct cachefiles_object *object,
 	newpage = NULL;
 
 read_backing_page:
-	ret = bmapping->a_ops->readpage(NULL, backpage);
+	ret = bmapping->a_ops->readpage(NULL, MAPPING_NULL, backpage);
 	if (ret < 0)
 		goto read_error;
 
@@ -519,7 +519,7 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object,
 		newpage = NULL;
 
 	reread_backing_page:
-		ret = bmapping->a_ops->readpage(NULL, backpage);
+		ret = bmapping->a_ops->readpage(NULL, MAPPING_NULL, backpage);
 		if (ret < 0)
 			goto read_error;
 
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 6ea761c84494f..69a19e1b6b2ec 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -288,7 +288,8 @@ static int ceph_do_readpage(struct file *filp, struct page *page)
 	return err < 0 ? err : 0;
 }
 
-static int ceph_readpage(struct file *filp, struct page *page)
+static int ceph_readpage(struct file *filp, struct address_space *__mapping,
+			 struct page *page)
 {
 	int r = ceph_do_readpage(filp, page);
 	if (r != -EINPROGRESS)
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index be46fab4c96d8..f1d974218dfcd 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -4547,7 +4547,8 @@ static int cifs_readpage_worker(struct file *file, struct page *page,
 	return rc;
 }
 
-static int cifs_readpage(struct file *file, struct page *page)
+static int cifs_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
 	loff_t offset = (loff_t)page->index << PAGE_SHIFT;
 	int rc = -EACCES;
diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c
index 8907d05081988..6d2479c056679 100644
--- a/fs/coda/symlink.c
+++ b/fs/coda/symlink.c
@@ -20,7 +20,9 @@
 #include "coda_psdev.h"
 #include "coda_linux.h"
 
-static int coda_symlink_filler(struct file *file, struct page *page)
+static int coda_symlink_filler(struct file *file,
+			       struct address_space *__mapping,
+			       struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	int error;
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 912308600d393..01b61a2463c4f 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -817,7 +817,8 @@ static struct dentry *cramfs_lookup(struct inode *dir, struct dentry *dentry, un
 	return d_splice_alias(inode, dentry);
 }
 
-static int cramfs_readpage(struct file *file, struct page *page)
+static int cramfs_readpage(struct file *file, struct address_space *__mapping,
+			   struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	u32 maxblock;
diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c
index 019572c6b39ac..163cfff9c2a1f 100644
--- a/fs/ecryptfs/mmap.c
+++ b/fs/ecryptfs/mmap.c
@@ -177,7 +177,9 @@ ecryptfs_copy_up_encrypted_with_header(struct page *page,
  *
  * Returns zero on success; non-zero on error.
  */
-static int ecryptfs_readpage(struct file *file, struct page *page)
+static int ecryptfs_readpage(struct file *file,
+			     struct address_space *__mapping,
+			     struct page *page)
 {
 	struct ecryptfs_crypt_stat *crypt_stat =
 		&ecryptfs_inode_to_private(page->mapping->host)->crypt_stat;
diff --git a/fs/efs/inode.c b/fs/efs/inode.c
index 89e73a6f0d361..705ed12ce78f4 100644
--- a/fs/efs/inode.c
+++ b/fs/efs/inode.c
@@ -14,7 +14,8 @@
 #include "efs.h"
 #include <linux/efs_fs_sb.h>
 
-static int efs_readpage(struct file *file, struct page *page)
+static int efs_readpage(struct file *file, struct address_space *__mapping,
+			struct page *page)
 {
 	return block_read_full_page(page,efs_get_block);
 }
diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c
index 923eb91654d5c..ae45c6bd44bc4 100644
--- a/fs/efs/symlink.c
+++ b/fs/efs/symlink.c
@@ -12,7 +12,9 @@
 #include <linux/buffer_head.h>
 #include "efs.h"
 
-static int efs_symlink_readpage(struct file *file, struct page *page)
+static int efs_symlink_readpage(struct file *file,
+				struct address_space *__mapping,
+				struct page *page)
 {
 	char *link = page_address(page);
 	struct buffer_head * bh;
diff --git a/fs/erofs/data.c b/fs/erofs/data.c
index 459ecb42cbd3b..c0ca49afddbd3 100644
--- a/fs/erofs/data.c
+++ b/fs/erofs/data.c
@@ -263,7 +263,9 @@ static inline struct bio *erofs_read_raw_page(struct bio *bio,
  * since we dont have write or truncate flows, so no inode
  * locking needs to be held at the moment.
  */
-static int erofs_raw_access_readpage(struct file *file, struct page *page)
+static int erofs_raw_access_readpage(struct file *file,
+				     struct address_space *__mapping,
+				     struct page *page)
 {
 	erofs_off_t last_block;
 	struct bio *bio;
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index 6c939def00f95..1da99bd146a22 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -1271,7 +1271,9 @@ static void z_erofs_runqueue(struct super_block *sb,
 	z_erofs_decompress_queue(&io[JQ_SUBMIT], pagepool);
 }
 
-static int z_erofs_readpage(struct file *file, struct page *page)
+static int z_erofs_readpage(struct file *file,
+			    struct address_space *__mapping,
+			    struct page *page)
 {
 	struct inode *const inode = page->mapping->host;
 	struct z_erofs_decompress_frontend f = DECOMPRESS_FRONTEND_INIT(inode);
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 7f90204adef53..2c8f3f6b65ae5 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -359,7 +359,8 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
 	return err;
 }
 
-static int exfat_readpage(struct file *file, struct page *page)
+static int exfat_readpage(struct file *file, struct address_space *__mapping,
+			  struct page *page)
 {
 	return mpage_readpage(page, exfat_get_block);
 }
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 415c21f0e7508..46f10453ab32e 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -870,7 +870,8 @@ static int ext2_writepage(struct page *page, struct writeback_control *wbc)
 	return block_write_full_page(page, ext2_get_block, wbc);
 }
 
-static int ext2_readpage(struct file *file, struct page *page)
+static int ext2_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
 	return mpage_readpage(page, ext2_get_block);
 }
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index bf596467c234c..82283d11cd740 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3214,7 +3214,8 @@ static sector_t ext4_bmap(struct address_space *mapping, sector_t block)
 	return iomap_bmap(mapping, block, &ext4_iomap_ops);
 }
 
-static int ext4_readpage(struct file *file, struct page *page)
+static int ext4_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
 	int ret = -EAGAIN;
 	struct inode *inode = page->mapping->host;
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 73683e58a08d5..dbbfaa8cd9602 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2445,7 +2445,9 @@ static int f2fs_mpage_readpages(struct inode *inode,
 	return ret;
 }
 
-static int f2fs_read_data_page(struct file *file, struct page *page)
+static int f2fs_read_data_page(struct file *file,
+			       struct address_space *__mapping,
+			       struct page *page)
 {
 	struct inode *inode = page_file_mapping(page)->host;
 	int ret = -EAGAIN;
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index a0cf99debb1ec..6ef4e7619684d 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -205,7 +205,8 @@ static int fat_writepages(struct address_space *mapping,
 	return mpage_writepages(mapping, wbc, fat_get_block);
 }
 
-static int fat_readpage(struct file *file, struct page *page)
+static int fat_readpage(struct file *file, struct address_space *__mapping,
+			struct page *page)
 {
 	return mpage_readpage(page, fat_get_block);
 }
diff --git a/fs/freevxfs/vxfs_immed.c b/fs/freevxfs/vxfs_immed.c
index bfc780c682fb8..8a0d251121aa3 100644
--- a/fs/freevxfs/vxfs_immed.c
+++ b/fs/freevxfs/vxfs_immed.c
@@ -38,7 +38,8 @@
 #include "vxfs_inode.h"
 
 
-static int	vxfs_immed_readpage(struct file *, struct page *);
+static int	vxfs_immed_readpage(struct file *, struct address_space *,
+				      struct page *);
 
 /*
  * Address space operations for immed files and directories.
@@ -63,7 +64,8 @@ const struct address_space_operations vxfs_immed_aops = {
  *   @page is locked and will be unlocked.
  */
 static int
-vxfs_immed_readpage(struct file *fp, struct page *pp)
+vxfs_immed_readpage(struct file *fp, struct address_space *__mapping,
+		    struct page *pp)
 {
 	struct vxfs_inode_info	*vip = VXFS_INO(pp->mapping->host);
 	u_int64_t	offset = (u_int64_t)pp->index << PAGE_SHIFT;
diff --git a/fs/freevxfs/vxfs_subr.c b/fs/freevxfs/vxfs_subr.c
index e806694d4145e..fcab7a85d6006 100644
--- a/fs/freevxfs/vxfs_subr.c
+++ b/fs/freevxfs/vxfs_subr.c
@@ -38,7 +38,8 @@
 #include "vxfs_extern.h"
 
 
-static int		vxfs_readpage(struct file *, struct page *);
+static int		vxfs_readpage(struct file *, struct address_space *,
+					struct page *);
 static sector_t		vxfs_bmap(struct address_space *, sector_t);
 
 const struct address_space_operations vxfs_aops = {
@@ -156,7 +157,8 @@ vxfs_getblk(struct inode *ip, sector_t iblock,
  *   @page is locked and will be unlocked.
  */
 static int
-vxfs_readpage(struct file *file, struct page *page)
+vxfs_readpage(struct file *file, struct address_space *__mapping,
+	      struct page *page)
 {
 	return block_read_full_page(page, vxfs_getblk);
 }
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 26f028bc760b2..e09a82f826427 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1770,7 +1770,9 @@ void fuse_init_dir(struct inode *inode)
 	fi->rdc.version = 0;
 }
 
-static int fuse_symlink_readpage(struct file *null, struct page *page)
+static int fuse_symlink_readpage(struct file *null,
+				 struct address_space *__mapping,
+				 struct page *page)
 {
 	int err = fuse_readlink_page(page->mapping->host, page);
 
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 43c165e796da2..f7c6b4b711a86 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -839,7 +839,8 @@ static int fuse_do_readpage(struct file *file, struct page *page)
 	return 0;
 }
 
-static int fuse_readpage(struct file *file, struct page *page)
+static int fuse_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	int err;
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index d4af283fc8886..e3b5f5fa08bec 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -468,7 +468,8 @@ static int stuffed_readpage(struct gfs2_inode *ip, struct page *page)
 }
 
 
-static int __gfs2_readpage(void *file, struct page *page)
+static int __gfs2_readpage(void *file, struct address_space *__mapping,
+			   struct page *page)
 {
 	struct gfs2_inode *ip = GFS2_I(page->mapping->host);
 	struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host);
@@ -496,9 +497,10 @@ static int __gfs2_readpage(void *file, struct page *page)
  * @page: The page of the file
  */
 
-static int gfs2_readpage(struct file *file, struct page *page)
+static int gfs2_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
-	return __gfs2_readpage(file, page);
+	return __gfs2_readpage(file, MAPPING_NULL, page);
 }
 
 /**
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index f35a37c65e5ff..ae193e389e22e 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -34,7 +34,8 @@ static int hfs_writepage(struct page *page, struct writeback_control *wbc)
 	return block_write_full_page(page, hfs_get_block, wbc);
 }
 
-static int hfs_readpage(struct file *file, struct page *page)
+static int hfs_readpage(struct file *file, struct address_space *__mapping,
+			struct page *page)
 {
 	return block_read_full_page(page, hfs_get_block);
 }
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index e3da9e96b8357..9955b8dcb8061 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -22,7 +22,9 @@
 #include "hfsplus_raw.h"
 #include "xattr.h"
 
-static int hfsplus_readpage(struct file *file, struct page *page)
+static int hfsplus_readpage(struct file *file,
+			    struct address_space *__mapping,
+			    struct page *page)
 {
 	return block_read_full_page(page, hfsplus_get_block);
 }
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index c070c0d8e3e97..0b78c8bf11717 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -431,7 +431,8 @@ static int hostfs_writepage(struct page *page, struct writeback_control *wbc)
 	return err;
 }
 
-static int hostfs_readpage(struct file *file, struct page *page)
+static int hostfs_readpage(struct file *file, struct address_space *__mapping,
+			   struct page *page)
 {
 	char *buffer;
 	loff_t start = page_offset(page);
diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c
index 077c25128eb74..28be564b9eba8 100644
--- a/fs/hpfs/file.c
+++ b/fs/hpfs/file.c
@@ -116,7 +116,8 @@ static int hpfs_get_block(struct inode *inode, sector_t iblock, struct buffer_he
 	return r;
 }
 
-static int hpfs_readpage(struct file *file, struct page *page)
+static int hpfs_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
 	return mpage_readpage(page, hpfs_get_block);
 }
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index 1aee39160ac5b..02b6457d10c65 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -475,7 +475,9 @@ static int hpfs_rmdir(struct inode *dir, struct dentry *dentry)
 	return err;
 }
 
-static int hpfs_symlink_readpage(struct file *file, struct page *page)
+static int hpfs_symlink_readpage(struct file *file,
+				 struct address_space *__mapping,
+				 struct page *page)
 {
 	char *link = page_address(page);
 	struct inode *i = page->mapping->host;
diff --git a/fs/isofs/compress.c b/fs/isofs/compress.c
index bc12ac7e23127..9811e5e1937d5 100644
--- a/fs/isofs/compress.c
+++ b/fs/isofs/compress.c
@@ -296,7 +296,8 @@ static int zisofs_fill_pages(struct inode *inode, int full_page, int pcount,
  * per reference.  We inject the additional pages into the page
  * cache as a form of readahead.
  */
-static int zisofs_readpage(struct file *file, struct page *page)
+static int zisofs_readpage(struct file *file, struct address_space *__mapping,
+			   struct page *page)
 {
 	struct inode *inode = file_inode(file);
 	struct address_space *mapping = inode->i_mapping;
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 78f5c96c76f31..eef14b3d4a7ef 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -1175,7 +1175,8 @@ struct buffer_head *isofs_bread(struct inode *inode, sector_t block)
 	return sb_bread(inode->i_sb, blknr);
 }
 
-static int isofs_readpage(struct file *file, struct page *page)
+static int isofs_readpage(struct file *file, struct address_space *__mapping,
+			  struct page *page)
 {
 	return mpage_readpage(page, isofs_get_block);
 }
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index 94ef92fe806c4..c27d67aba3ead 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -690,7 +690,9 @@ int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode,
  * readpage() for symlinks: reads symlink contents into the page and either
  * makes it uptodate and returns 0 or returns error (-EIO)
  */
-static int rock_ridge_symlink_readpage(struct file *file, struct page *page)
+static int rock_ridge_symlink_readpage(struct file *file,
+				       struct address_space *__mapping,
+				       struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	struct iso_inode_info *ei = ISOFS_I(inode);
diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c
index f8fb89b10227c..edb6066979391 100644
--- a/fs/jffs2/file.c
+++ b/fs/jffs2/file.c
@@ -27,7 +27,8 @@ static int jffs2_write_end(struct file *filp, struct address_space *mapping,
 static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
 			loff_t pos, unsigned len, unsigned flags,
 			struct page **pagep, void **fsdata);
-static int jffs2_readpage (struct file *filp, struct page *pg);
+static int jffs2_readpage (struct file *filp, struct address_space *__mapping,
+			   struct page *pg);
 
 int jffs2_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
 {
@@ -109,7 +110,8 @@ static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
 	return ret;
 }
 
-int jffs2_do_readpage_unlock(void *data, struct page *pg)
+int jffs2_do_readpage_unlock(void *data, struct address_space *__mapping,
+			     struct page *pg)
 {
 	int ret = jffs2_do_readpage_nolock(data, pg);
 	unlock_page(pg);
@@ -117,13 +119,14 @@ int jffs2_do_readpage_unlock(void *data, struct page *pg)
 }
 
 
-static int jffs2_readpage (struct file *filp, struct page *pg)
+static int jffs2_readpage (struct file *filp, struct address_space *__mapping,
+			   struct page *pg)
 {
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(pg->mapping->host);
 	int ret;
 
 	mutex_lock(&f->sem);
-	ret = jffs2_do_readpage_unlock(pg->mapping->host, pg);
+	ret = jffs2_do_readpage_unlock(pg->mapping->host, MAPPING_NULL, pg);
 	mutex_unlock(&f->sem);
 	return ret;
 }
diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
index ef1cfa61549e6..f711b1cce9abd 100644
--- a/fs/jffs2/os-linux.h
+++ b/fs/jffs2/os-linux.h
@@ -155,7 +155,8 @@ extern const struct file_operations jffs2_file_operations;
 extern const struct inode_operations jffs2_file_inode_operations;
 extern const struct address_space_operations jffs2_file_address_operations;
 int jffs2_fsync(struct file *, loff_t, loff_t, int);
-int jffs2_do_readpage_unlock(void *data, struct page *pg);
+int jffs2_do_readpage_unlock(void *data, struct address_space *__mapping,
+			     struct page *pg);
 
 /* ioctl.c */
 long jffs2_ioctl(struct file *, unsigned int, unsigned long);
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
index 6f65bfa9f18d5..41b77aac394d6 100644
--- a/fs/jfs/inode.c
+++ b/fs/jfs/inode.c
@@ -291,7 +291,8 @@ static int jfs_writepages(struct address_space *mapping,
 	return mpage_writepages(mapping, wbc, jfs_get_block);
 }
 
-static int jfs_readpage(struct file *file, struct page *page)
+static int jfs_readpage(struct file *file, struct address_space *__mapping,
+			struct page *page)
 {
 	return mpage_readpage(page, jfs_get_block);
 }
diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
index a2f5338a5ea18..e9dae1dccb42b 100644
--- a/fs/jfs/jfs_metapage.c
+++ b/fs/jfs/jfs_metapage.c
@@ -468,7 +468,8 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc)
 	return -EIO;
 }
 
-static int metapage_readpage(struct file *fp, struct page *page)
+static int metapage_readpage(struct file *fp, struct address_space *__mapping,
+			     struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	struct bio *bio = NULL;
diff --git a/fs/libfs.c b/fs/libfs.c
index e0d42e977d9af..7df05487cdde6 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -507,7 +507,8 @@ int simple_setattr(struct dentry *dentry, struct iattr *iattr)
 }
 EXPORT_SYMBOL(simple_setattr);
 
-int simple_readpage(struct file *file, struct page *page)
+int simple_readpage(struct file *file, struct address_space *__mapping,
+		    struct page *page)
 {
 	clear_highpage(page);
 	flush_dcache_page(page);
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 7b09a9158e401..9677f0424a72d 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -403,7 +403,8 @@ static int minix_writepage(struct page *page, struct writeback_control *wbc)
 	return block_write_full_page(page, minix_get_block, wbc);
 }
 
-static int minix_readpage(struct file *file, struct page *page)
+static int minix_readpage(struct file *file, struct address_space *__mapping,
+			  struct page *page)
 {
 	return block_read_full_page(page,minix_get_block);
 }
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index da1fe71ae810d..5a5c021967d3f 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -706,7 +706,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
  * We only need to convert from xdr once so future lookups are much simpler
  */
 static
-int nfs_readdir_filler(void *data, struct page* page)
+int nfs_readdir_filler(void *data, struct address_space *__mapping,
+		       struct page* page)
 {
 	nfs_readdir_descriptor_t *desc = data;
 	struct inode	*inode = file_inode(desc->file);
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 63940a7a70be1..02e2112d77f86 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -341,7 +341,7 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping,
 	} else if (!once_thru &&
 		   nfs_want_read_modify_write(file, page, pos, len)) {
 		once_thru = 1;
-		ret = nfs_readpage(file, page);
+		ret = nfs_readpage(file, MAPPING_NULL, page);
 		put_page(page);
 		if (!ret)
 			goto start;
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index eb854f1f86e2e..dadff06079267 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -310,7 +310,8 @@ static void nfs_readpage_result(struct rpc_task *task,
  *  -	The error flag is set for this page. This happens only when a
  *	previous async read operation failed.
  */
-int nfs_readpage(struct file *file, struct page *page)
+int nfs_readpage(struct file *file, struct address_space *__mapping,
+		 struct page *page)
 {
 	struct nfs_open_context *ctx;
 	struct inode *inode = page_file_mapping(page)->host;
@@ -373,7 +374,8 @@ struct nfs_readdesc {
 };
 
 static int
-readpage_async_filler(void *data, struct page *page)
+readpage_async_filler(void *data, struct address_space *__mapping,
+		      struct page *page)
 {
 	struct nfs_readdesc *desc = (struct nfs_readdesc *)data;
 	struct nfs_page *new;
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index 76691d94ae5f8..6f77ea2ba7e08 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -26,7 +26,8 @@
  * and straight-forward than readdir caching.
  */
 
-static int nfs_symlink_filler(void *data, struct page *page)
+static int nfs_symlink_filler(void *data, struct address_space *__mapping,
+			      struct page *page)
 {
 	struct inode *inode = data;
 	int error;
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index 745d371d6fea6..f1e5cc46ce00b 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -141,7 +141,8 @@ int nilfs_get_block(struct inode *inode, sector_t blkoff,
  * @file - file struct of the file to be read
  * @page - the page to be read
  */
-static int nilfs_readpage(struct file *file, struct page *page)
+static int nilfs_readpage(struct file *file, struct address_space *__mapping,
+			  struct page *page)
 {
 	return mpage_readpage(page, nilfs_get_block);
 }
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
index bb0a43860ad26..ca0bcec9ac2f6 100644
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -375,7 +375,8 @@ static int ntfs_read_block(struct page *page)
  *
  * Return 0 on success and -errno on error.
  */
-static int ntfs_readpage(struct file *file, struct page *page)
+static int ntfs_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
 	loff_t i_size;
 	struct inode *vi;
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index 3bfb4147895a0..a5f9686ae100d 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -277,7 +277,8 @@ static int ocfs2_readpage_inline(struct inode *inode, struct page *page)
 	return ret;
 }
 
-static int ocfs2_readpage(struct file *file, struct page *page)
+static int ocfs2_readpage(struct file *file, struct address_space *__mapping,
+			  struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	struct ocfs2_inode_info *oi = OCFS2_I(inode);
diff --git a/fs/ocfs2/symlink.c b/fs/ocfs2/symlink.c
index 94cfacc9bad70..8017957fd6529 100644
--- a/fs/ocfs2/symlink.c
+++ b/fs/ocfs2/symlink.c
@@ -54,7 +54,9 @@
 #include "buffer_head_io.h"
 
 
-static int ocfs2_fast_symlink_readpage(struct file *unused, struct page *page)
+static int ocfs2_fast_symlink_readpage(struct file *unused,
+				       struct address_space *__mapping,
+				       struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	struct buffer_head *bh = NULL;
diff --git a/fs/omfs/file.c b/fs/omfs/file.c
index 2c7b70ee1388c..c55fd61021b65 100644
--- a/fs/omfs/file.c
+++ b/fs/omfs/file.c
@@ -284,7 +284,8 @@ static int omfs_get_block(struct inode *inode, sector_t block,
 	return ret;
 }
 
-static int omfs_readpage(struct file *file, struct page *page)
+static int omfs_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
 	return block_read_full_page(page, omfs_get_block);
 }
diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c
index 48f0547d4850e..6d797c789f035 100644
--- a/fs/orangefs/inode.c
+++ b/fs/orangefs/inode.c
@@ -244,7 +244,9 @@ static int orangefs_writepages(struct address_space *mapping,
 
 static int orangefs_launder_page(struct page *);
 
-static int orangefs_readpage(struct file *file, struct page *page)
+static int orangefs_readpage(struct file *file,
+			     struct address_space *__mapping,
+			     struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	struct iov_iter iter;
diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c
index e8da1cde87b9b..f8d92fa828321 100644
--- a/fs/qnx4/inode.c
+++ b/fs/qnx4/inode.c
@@ -246,7 +246,8 @@ static void qnx4_kill_sb(struct super_block *sb)
 	}
 }
 
-static int qnx4_readpage(struct file *file, struct page *page)
+static int qnx4_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
 	return block_read_full_page(page,qnx4_get_block);
 }
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index 755293c8c71a6..74d24efaf1223 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -94,7 +94,8 @@ static int qnx6_check_blockptr(__fs32 ptr)
 	return 1;
 }
 
-static int qnx6_readpage(struct file *file, struct page *page)
+static int qnx6_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
 	return mpage_readpage(page, qnx6_get_block);
 }
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 1509775da040a..3f7638fd6eeca 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -2738,7 +2738,8 @@ static int reiserfs_write_full_page(struct page *page,
 	goto done;
 }
 
-static int reiserfs_readpage(struct file *f, struct page *page)
+static int reiserfs_readpage(struct file *f, struct address_space *__mapping,
+			     struct page *page)
 {
 	return block_read_full_page(page, reiserfs_get_block);
 }
diff --git a/fs/romfs/super.c b/fs/romfs/super.c
index e582d001f792e..62957a6fe4c15 100644
--- a/fs/romfs/super.c
+++ b/fs/romfs/super.c
@@ -99,7 +99,8 @@ static struct inode *romfs_iget(struct super_block *sb, unsigned long pos);
 /*
  * read a page worth of data from the image
  */
-static int romfs_readpage(struct file *file, struct page *page)
+static int romfs_readpage(struct file *file, struct address_space *__mapping,
+			  struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	loff_t offset, size;
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c
index 7b1128398976e..13ef295affc2f 100644
--- a/fs/squashfs/file.c
+++ b/fs/squashfs/file.c
@@ -444,7 +444,9 @@ static int squashfs_readpage_sparse(struct page *page, int expected)
 	return 0;
 }
 
-static int squashfs_readpage(struct file *file, struct page *page)
+static int squashfs_readpage(struct file *file,
+			     struct address_space *__mapping,
+			     struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
diff --git a/fs/squashfs/symlink.c b/fs/squashfs/symlink.c
index 1430613183e6e..2b9f9fb28b550 100644
--- a/fs/squashfs/symlink.c
+++ b/fs/squashfs/symlink.c
@@ -30,7 +30,9 @@
 #include "squashfs.h"
 #include "xattr.h"
 
-static int squashfs_symlink_readpage(struct file *file, struct page *page)
+static int squashfs_symlink_readpage(struct file *file,
+				     struct address_space *__mapping,
+				     struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	struct super_block *sb = inode->i_sb;
diff --git a/fs/sysv/itree.c b/fs/sysv/itree.c
index bcb67b0cabe7e..eaca724493b3d 100644
--- a/fs/sysv/itree.c
+++ b/fs/sysv/itree.c
@@ -456,7 +456,8 @@ static int sysv_writepage(struct page *page, struct writeback_control *wbc)
 	return block_write_full_page(page,get_block,wbc);
 }
 
-static int sysv_readpage(struct file *file, struct page *page)
+static int sysv_readpage(struct file *file, struct address_space *__mapping,
+			 struct page *page)
 {
 	return block_read_full_page(page,get_block);
 }
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index b77d1637bbbc8..7b868f220ca7d 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -890,7 +890,8 @@ static int ubifs_bulk_read(struct page *page)
 	return err;
 }
 
-static int ubifs_readpage(struct file *file, struct page *page)
+static int ubifs_readpage(struct file *file, struct address_space *__mapping,
+			  struct page *page)
 {
 	if (ubifs_bulk_read(page))
 		return 0;
diff --git a/fs/udf/file.c b/fs/udf/file.c
index 628941a6b79af..6d3a2291856fe 100644
--- a/fs/udf/file.c
+++ b/fs/udf/file.c
@@ -57,7 +57,9 @@ static void __udf_adinicb_readpage(struct page *page)
 	kunmap_atomic(kaddr);
 }
 
-static int udf_adinicb_readpage(struct file *file, struct page *page)
+static int udf_adinicb_readpage(struct file *file,
+				struct address_space *__mapping,
+				struct page *page)
 {
 	BUG_ON(!PageLocked(page));
 	__udf_adinicb_readpage(page);
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index adaba8e8b326e..5bceaf7456be7 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -190,7 +190,8 @@ static int udf_writepages(struct address_space *mapping,
 	return mpage_writepages(mapping, wbc, udf_get_block);
 }
 
-static int udf_readpage(struct file *file, struct page *page)
+static int udf_readpage(struct file *file, struct address_space *__mapping,
+			struct page *page)
 {
 	return mpage_readpage(page, udf_get_block);
 }
diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
index 25ff91c7e94af..c7766a8a53884 100644
--- a/fs/udf/symlink.c
+++ b/fs/udf/symlink.c
@@ -101,7 +101,9 @@ static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
 	return 0;
 }
 
-static int udf_symlink_filler(struct file *file, struct page *page)
+static int udf_symlink_filler(struct file *file,
+			      struct address_space *__mapping,
+			      struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	struct buffer_head *bh = NULL;
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index c843ec858cf7c..4f44e309cfaf5 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -472,7 +472,8 @@ static int ufs_writepage(struct page *page, struct writeback_control *wbc)
 	return block_write_full_page(page,ufs_getfrag_block,wbc);
 }
 
-static int ufs_readpage(struct file *file, struct page *page)
+static int ufs_readpage(struct file *file, struct address_space *__mapping,
+			struct page *page)
 {
 	return block_read_full_page(page,ufs_getfrag_block);
 }
diff --git a/fs/vboxsf/file.c b/fs/vboxsf/file.c
index c4ab5996d97a8..da3716c5d8174 100644
--- a/fs/vboxsf/file.c
+++ b/fs/vboxsf/file.c
@@ -208,7 +208,8 @@ const struct inode_operations vboxsf_reg_iops = {
 	.setattr = vboxsf_setattr
 };
 
-static int vboxsf_readpage(struct file *file, struct page *page)
+static int vboxsf_readpage(struct file *file, struct address_space *__mapping,
+			   struct page *page)
 {
 	struct vboxsf_handle *sf_handle = file->private_data;
 	loff_t off = page_offset(page);
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index b35611882ff9c..3e862efc2d881 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -616,7 +616,7 @@ xfs_vm_bmap(
 STATIC int
 xfs_vm_readpage(
 	struct file		*unused,
-	struct page		*page)
+	struct address_space *__mapping, struct page		*page)
 {
 	return iomap_readpage(page, &xfs_read_iomap_ops);
 }
diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c
index 8ec7c8f109d7d..160190288f711 100644
--- a/fs/zonefs/super.c
+++ b/fs/zonefs/super.c
@@ -74,7 +74,8 @@ static const struct iomap_ops zonefs_iomap_ops = {
 	.iomap_begin	= zonefs_iomap_begin,
 };
 
-static int zonefs_readpage(struct file *unused, struct page *page)
+static int zonefs_readpage(struct file *unused,
+			   struct address_space *__mapping, struct page *page)
 {
 	return iomap_readpage(page, &zonefs_iomap_ops);
 }
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 7519ae003a082..0326f24608544 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -371,7 +371,7 @@ typedef int (*read_actor_t)(read_descriptor_t *, struct page *,
 
 struct address_space_operations {
 	int (*writepage)(struct page *page, struct writeback_control *wbc);
-	int (*readpage)(struct file *, struct page *);
+	int (*readpage)(struct file *, struct address_space *, struct page *);
 
 	/* Write back some dirty pages from this mapping. */
 	int (*writepages)(struct address_space *, struct writeback_control *);
@@ -3227,7 +3227,8 @@ extern void noop_invalidatepage(struct page *page, unsigned int offset,
 		unsigned int length);
 extern ssize_t noop_direct_IO(struct kiocb *iocb, struct iov_iter *iter);
 extern int simple_empty(struct dentry *);
-extern int simple_readpage(struct file *file, struct page *page);
+extern int simple_readpage(struct file *file, struct address_space *__mapping,
+			   struct page *page);
 extern int simple_write_begin(struct file *file, struct address_space *mapping,
 			loff_t pos, unsigned len, unsigned flags,
 			struct page **pagep, void **fsdata);
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index a2c6455ea3fae..6d5b245bfa247 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -562,7 +562,7 @@ nfs_have_writebacks(struct inode *inode)
 /*
  * linux/fs/nfs/read.c
  */
-extern int  nfs_readpage(struct file *, struct page *);
+extern int  nfs_readpage(struct file *, struct address_space *, struct page *);
 extern int  nfs_readpages(struct file *, struct address_space *,
 		struct list_head *, unsigned);
 extern int  nfs_readpage_async(struct nfs_open_context *, struct inode *,
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 9acfc605b3bc3..6473ea9dc1ea9 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -264,7 +264,7 @@ static inline gfp_t readahead_gfp_mask(struct address_space *x)
 	return mapping_gfp_mask(x) | __GFP_NORETRY | __GFP_NOWARN;
 }
 
-typedef int (*filler_t)(void *, struct page *);
+typedef int (*filler_t)(void *, struct address_space *, struct page *);
 
 pgoff_t page_cache_next_miss(struct address_space *mapping,
 			     pgoff_t index, unsigned long max_scan);
diff --git a/mm/filemap.c b/mm/filemap.c
index ba892599a2717..b67a253e5e6c7 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2349,7 +2349,7 @@ ssize_t generic_file_buffered_read(struct kiocb *iocb,
 		 */
 		ClearPageError(page);
 		/* Start the actual read. The read will unlock the page. */
-		error = mapping->a_ops->readpage(filp, page);
+		error = mapping->a_ops->readpage(filp, MAPPING_NULL, page);
 
 		if (unlikely(error)) {
 			if (error == AOP_TRUNCATED_PAGE) {
@@ -2751,7 +2751,7 @@ vm_fault_t filemap_fault(struct vm_fault *vmf)
 	 */
 	ClearPageError(page);
 	fpin = maybe_unlock_mmap_for_io(vmf, fpin);
-	error = mapping->a_ops->readpage(file, page);
+	error = mapping->a_ops->readpage(file, MAPPING_NULL, page);
 	if (!error) {
 		wait_on_page_locked(page);
 		if (!PageUptodate(page))
@@ -2961,9 +2961,10 @@ static struct page *do_read_cache_page(struct address_space *mapping,
 
 filler:
 		if (filler)
-			err = filler(data, page);
+			err = filler(data, MAPPING_NULL, page);
 		else
-			err = mapping->a_ops->readpage(data, page);
+			err = mapping->a_ops->readpage(data, MAPPING_NULL,
+						       page);
 
 		if (err < 0) {
 			put_page(page);
diff --git a/mm/page_io.c b/mm/page_io.c
index e485a6e8a6cdd..8f1748c94c3a8 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -397,7 +397,7 @@ int swap_readpage(struct page *page, bool synchronous)
 		struct file *swap_file = sis->swap_file;
 		struct address_space *mapping = swap_file->f_mapping;
 
-		ret = mapping->a_ops->readpage(swap_file, page);
+		ret = mapping->a_ops->readpage(swap_file, MAPPING_NULL, page);
 		if (!ret)
 			count_vm_event(PSWPIN);
 		goto out;
diff --git a/mm/readahead.c b/mm/readahead.c
index cd67c9cfa931a..b42690f62e3ae 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -102,7 +102,7 @@ int read_cache_pages(struct address_space *mapping, struct list_head *pages,
 		}
 		put_page(page);
 
-		ret = filler(data, page);
+		ret = filler(data, MAPPING_NULL, page);
 		if (unlikely(ret)) {
 			read_cache_pages_invalidate_pages(mapping, pages);
 			break;
@@ -142,7 +142,7 @@ static void read_pages(struct readahead_control *rac, struct list_head *pages,
 		rac->_nr_pages = 0;
 	} else {
 		while ((page = readahead_page(rac))) {
-			aops->readpage(rac->file, page);
+			aops->readpage(rac->file, MAPPING_NULL, page);
 			put_page(page);
 		}
 	}
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 05/14] mm: add struct address_space to writepage() callback
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
                   ` (2 preceding siblings ...)
  2020-10-07  1:05 ` [PATCH 04/14] mm: add struct address_space to readpage() callback jglisse
@ 2020-10-07  1:05 ` jglisse
  2020-10-07  1:05 ` [PATCH 06/14] mm: add struct address_space to set_page_dirty() callback jglisse
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

This is part of patchset to remove dependency on struct page.mapping
field so that we can temporarily update it to point to a special
structure tracking temporary page state (note that original mapping
pointer is preserved and can still be accessed but at a cost).

Add struct address_space to writepage() callback arguments.

Note that this patch does not make use of the new argument, nor does
it use a valid one at call site (by default this patch just use NULL
for new argument value).

Use following script (from root of linux kernel tree):

./that-script.sh that-semantic-patch.spatch

%<--------------------------------------------------------------------
#!/bin/sh
spatch_file=$1

echo PART1 ===========================================================

# P1 find callback functions name
spatch  --dir . --no-includes -D part1 --sp-file $spatch_file

echo PART2 ===========================================================

# P2 change callback function prototype
cat /tmp/unicorn-functions | sort | uniq | while read func ; do
    for file in $( git grep -l $func -- '*.[ch]' ) ; do
        echo $file
        spatch --no-includes --in-place -D part2 \
               -D fn=$func --sp-file $spatch_file $file
    done
done

echo PART 3 ==========================================================

# P3 find all function which call the callback
spatch --dir . --include-headers -D part3 --sp-file $spatch_file

echo PART 4===========================================================

# P4 change all funcitons which call the callback
cat /tmp/unicorn-files | sort | uniq | while read file ; do
    echo $file
    spatch --no-includes --in-place -D part4 \
           --sp-file $spatch_file $file
done
-------------------------------------------------------------------->%

With the following semantic patch:

%<--------------------------------------------------------------------
virtual part1, part2, part3, part4

// ----------------------------------------------------------------------------
// Part 1 is grepping all function that are use as callback for writepage.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part1@
@@
file=open('/tmp/unicorn-functions', 'w')
file.close()

// match function name use as a callback
@p1r2 depends on part1@
identifier I1, FN;
@@
struct address_space_operations I1 = {..., .writepage = FN, ...};

@script:python p1r3 depends on p1r2@
funcname << p1r2.FN;
@@
if funcname != "NULL":
  file=open('/tmp/unicorn-functions', 'a')
  file.write(funcname + '\n')
  file.close()

// -------------------------------------------------------------------
// Part 2 modify callback

// Add address_space argument to the function (writepage callback one)
@p2r1 depends on part2@
identifier virtual.fn;
identifier I1, I2;
type T1, T2;
@@
int fn(
+struct address_space *__mapping,
T1 I1, T2 I2) { ... }

@p2r2 depends on part2@
identifier virtual.fn;
identifier I1, I2;
type T1, T2;
@@
int fn(
+struct address_space *__mapping,
T1 I1, T2 I2);

@p2r3 depends on part2@
identifier virtual.fn;
type T1, T2;
@@
int fn(
+struct address_space *__mapping,
T1, T2);

@p2r4 depends on part2@
identifier virtual.fn;
expression E1, E2;
@@
fn(
+MAPPING_NULL,
E1, E2)

// ----------------------------------------------------------------------------
// Part 3 is grepping all function that are use the callback for writepage.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part3@
@@
file=open('/tmp/unicorn-files', 'w')
file.write("./include/linux/pagemap.h\n")
file.write("./include/linux/mm.h\n")
file.write("./include/linux/fs.h\n")
file.write("./mm/readahead.c\n")
file.write("./mm/filemap.c\n")
file.close()

@p3r1 depends on part3 exists@
expression E1, E2, E3;
identifier FN;
position P;
@@
FN@P(...) {...
(
E1.a_ops->writepage(E2, E3)
|
E1->a_ops->writepage(E2, E3)
)
...}

@script:python p3r2 depends on p3r1@
P << p3r1.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

// -------------------------------------------------------------------
// Part 4 generic modification
@p4r1 depends on part4@
@@
struct address_space_operations { ... int (*writepage)(
+struct address_space *,
struct page *page, ...); ... };

@p4r2 depends on part4@
expression E1, E2, E3;
@@
E1.a_ops->writepage(
+MAPPING_NULL,
E2, E3)

@p4r3 depends on part4@
expression E1, E2, E3;
@@
E1->a_ops->writepage(
+MAPPING_NULL,
E2, E3)
-------------------------------------------------------------------->%

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>
---
 drivers/gpu/drm/i915/gem/i915_gem_shmem.c | 3 ++-
 fs/9p/vfs_addr.c                          | 4 +++-
 fs/adfs/inode.c                           | 3 ++-
 fs/affs/file.c                            | 3 ++-
 fs/afs/internal.h                         | 3 ++-
 fs/afs/write.c                            | 3 ++-
 fs/bfs/file.c                             | 3 ++-
 fs/block_dev.c                            | 3 ++-
 fs/btrfs/inode.c                          | 3 ++-
 fs/ceph/addr.c                            | 3 ++-
 fs/cifs/file.c                            | 3 ++-
 fs/ecryptfs/mmap.c                        | 4 +++-
 fs/exfat/inode.c                          | 3 ++-
 fs/ext2/inode.c                           | 8 +++++---
 fs/ext4/inode.c                           | 2 +-
 fs/f2fs/checkpoint.c                      | 3 ++-
 fs/f2fs/data.c                            | 3 ++-
 fs/f2fs/node.c                            | 3 ++-
 fs/fat/inode.c                            | 3 ++-
 fs/fuse/file.c                            | 3 ++-
 fs/gfs2/aops.c                            | 7 +++++--
 fs/gfs2/meta_io.c                         | 4 +++-
 fs/hfs/inode.c                            | 3 ++-
 fs/hfsplus/inode.c                        | 3 ++-
 fs/hostfs/hostfs_kern.c                   | 3 ++-
 fs/hpfs/file.c                            | 3 ++-
 fs/jfs/inode.c                            | 3 ++-
 fs/jfs/jfs_metapage.c                     | 4 +++-
 fs/minix/inode.c                          | 3 ++-
 fs/mpage.c                                | 2 +-
 fs/nfs/write.c                            | 3 ++-
 fs/nilfs2/inode.c                         | 3 ++-
 fs/nilfs2/mdt.c                           | 3 ++-
 fs/ntfs/aops.c                            | 3 ++-
 fs/ocfs2/aops.c                           | 3 ++-
 fs/omfs/file.c                            | 3 ++-
 fs/orangefs/inode.c                       | 4 +++-
 fs/reiserfs/inode.c                       | 4 +++-
 fs/sysv/itree.c                           | 3 ++-
 fs/ubifs/file.c                           | 3 ++-
 fs/udf/file.c                             | 3 ++-
 fs/udf/inode.c                            | 5 +++--
 fs/ufs/inode.c                            | 3 ++-
 fs/vboxsf/file.c                          | 3 ++-
 fs/xfs/xfs_aops.c                         | 2 +-
 fs/zonefs/super.c                         | 3 ++-
 include/linux/fs.h                        | 3 ++-
 include/linux/nfs_fs.h                    | 3 ++-
 include/linux/swap.h                      | 7 +++++--
 mm/migrate.c                              | 2 +-
 mm/page-writeback.c                       | 4 ++--
 mm/page_io.c                              | 3 ++-
 mm/shmem.c                                | 5 +++--
 mm/vmscan.c                               | 2 +-
 54 files changed, 120 insertions(+), 61 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
index 38113d3c0138e..73c17231c142d 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
@@ -266,7 +266,8 @@ shmem_writeback(struct drm_i915_gem_object *obj)
 			int ret;
 
 			SetPageReclaim(page);
-			ret = mapping->a_ops->writepage(page, &wbc);
+			ret = mapping->a_ops->writepage(MAPPING_NULL, page,
+							&wbc);
 			if (!PageWriteback(page))
 				ClearPageReclaim(page);
 			if (!ret)
diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c
index 79fa773dbe95e..c7a8037df9fcf 100644
--- a/fs/9p/vfs_addr.c
+++ b/fs/9p/vfs_addr.c
@@ -178,7 +178,9 @@ static int v9fs_vfs_writepage_locked(struct page *page)
 	return err;
 }
 
-static int v9fs_vfs_writepage(struct page *page, struct writeback_control *wbc)
+static int v9fs_vfs_writepage(struct address_space *__mapping,
+			      struct page *page,
+			      struct writeback_control *wbc)
 {
 	int retval;
 
diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c
index 2a29f7d4a4dd0..26c1b6e1a47d3 100644
--- a/fs/adfs/inode.c
+++ b/fs/adfs/inode.c
@@ -33,7 +33,8 @@ adfs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh,
 	return 0;
 }
 
-static int adfs_writepage(struct page *page, struct writeback_control *wbc)
+static int adfs_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc)
 {
 	return block_write_full_page(page, adfs_get_block, wbc);
 }
diff --git a/fs/affs/file.c b/fs/affs/file.c
index a20d5a1298335..fd2216031b27e 100644
--- a/fs/affs/file.c
+++ b/fs/affs/file.c
@@ -370,7 +370,8 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul
 	return -ENOSPC;
 }
 
-static int affs_writepage(struct page *page, struct writeback_control *wbc)
+static int affs_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc)
 {
 	return block_write_full_page(page, affs_get_block, wbc);
 }
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 18042b7dab6a8..fc1c80c5ddb88 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -1429,7 +1429,8 @@ extern int afs_write_begin(struct file *file, struct address_space *mapping,
 extern int afs_write_end(struct file *file, struct address_space *mapping,
 			loff_t pos, unsigned len, unsigned copied,
 			struct page *page, void *fsdata);
-extern int afs_writepage(struct page *, struct writeback_control *);
+extern int afs_writepage(struct address_space *__mapping, struct page *,
+			 struct writeback_control *);
 extern int afs_writepages(struct address_space *, struct writeback_control *);
 extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *);
 extern int afs_fsync(struct file *, loff_t, loff_t, int);
diff --git a/fs/afs/write.c b/fs/afs/write.c
index 4b2265cb18917..ef0ea031130af 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -647,7 +647,8 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
  * write a page back to the server
  * - the caller locked the page for us
  */
-int afs_writepage(struct page *page, struct writeback_control *wbc)
+int afs_writepage(struct address_space *__mapping, struct page *page,
+		  struct writeback_control *wbc)
 {
 	int ret;
 
diff --git a/fs/bfs/file.c b/fs/bfs/file.c
index 4154c23e23e24..b57a8b39dd2f7 100644
--- a/fs/bfs/file.c
+++ b/fs/bfs/file.c
@@ -150,7 +150,8 @@ static int bfs_get_block(struct inode *inode, sector_t block,
 	return err;
 }
 
-static int bfs_writepage(struct page *page, struct writeback_control *wbc)
+static int bfs_writepage(struct address_space *__mapping, struct page *page,
+			 struct writeback_control *wbc)
 {
 	return block_write_full_page(page, bfs_get_block, wbc);
 }
diff --git a/fs/block_dev.c b/fs/block_dev.c
index b8e6e1995f396..b50c93932dfdf 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -593,7 +593,8 @@ int thaw_bdev(struct block_device *bdev, struct super_block *sb)
 }
 EXPORT_SYMBOL(thaw_bdev);
 
-static int blkdev_writepage(struct page *page, struct writeback_control *wbc)
+static int blkdev_writepage(struct address_space *__mapping,
+			    struct page *page, struct writeback_control *wbc)
 {
 	return block_write_full_page(page, blkdev_get_block, wbc);
 }
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 1213671571bb2..e73dc72dbd984 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -8011,7 +8011,8 @@ int btrfs_readpage(struct file *file, struct address_space *__mapping,
 	return extent_read_full_page(page, btrfs_get_extent, 0);
 }
 
-static int btrfs_writepage(struct page *page, struct writeback_control *wbc)
+static int btrfs_writepage(struct address_space *__mapping, struct page *page,
+			   struct writeback_control *wbc)
 {
 	struct inode *inode = page->mapping->host;
 	int ret;
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 69a19e1b6b2ec..8d348fb29102f 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -761,7 +761,8 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
 	return err;
 }
 
-static int ceph_writepage(struct page *page, struct writeback_control *wbc)
+static int ceph_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc)
 {
 	int err;
 	struct inode *inode = page->mapping->host;
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index f1d974218dfcd..ca7df2a2dde0f 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -2516,7 +2516,8 @@ cifs_writepage_locked(struct page *page, struct writeback_control *wbc)
 	return rc;
 }
 
-static int cifs_writepage(struct page *page, struct writeback_control *wbc)
+static int cifs_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc)
 {
 	int rc = cifs_writepage_locked(page, wbc);
 	unlock_page(page);
diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c
index 163cfff9c2a1f..dea69ef240f39 100644
--- a/fs/ecryptfs/mmap.c
+++ b/fs/ecryptfs/mmap.c
@@ -48,7 +48,9 @@ struct page *ecryptfs_get_locked_page(struct inode *inode, loff_t index)
  * the lower filesystem.  In OpenPGP-compatible mode, we operate on
  * entire underlying packets.
  */
-static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc)
+static int ecryptfs_writepage(struct address_space *__mapping,
+			      struct page *page,
+			      struct writeback_control *wbc)
 {
 	int rc;
 
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 2c8f3f6b65ae5..e165e7e91a1fe 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -370,7 +370,8 @@ static void exfat_readahead(struct readahead_control *rac)
 	mpage_readahead(rac, exfat_get_block);
 }
 
-static int exfat_writepage(struct page *page, struct writeback_control *wbc)
+static int exfat_writepage(struct address_space *__mapping, struct page *page,
+			   struct writeback_control *wbc)
 {
 	return block_write_full_page(page, exfat_get_block, wbc);
 }
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 46f10453ab32e..21b6b75b0ef0f 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -865,7 +865,8 @@ int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
 				    ext2_get_block);
 }
 
-static int ext2_writepage(struct page *page, struct writeback_control *wbc)
+static int ext2_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc)
 {
 	return block_write_full_page(page, ext2_get_block, wbc);
 }
@@ -921,8 +922,9 @@ ext2_nobh_write_begin(struct file *file, struct address_space *mapping,
 	return ret;
 }
 
-static int ext2_nobh_writepage(struct page *page,
-			struct writeback_control *wbc)
+static int ext2_nobh_writepage(struct address_space *__mapping,
+			       struct page *page,
+			       struct writeback_control *wbc)
 {
 	return nobh_writepage(page, ext2_get_block, wbc);
 }
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 82283d11cd740..f8a4d324a6041 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1969,7 +1969,7 @@ static int __ext4_journalled_writepage(struct page *page,
  * But since we don't do any block allocation we should not deadlock.
  * Page also have the dirty flag cleared so we don't get recurive page_lock.
  */
-static int ext4_writepage(struct page *page,
+static int ext4_writepage(struct address_space *__mapping, struct page *page,
 			  struct writeback_control *wbc)
 {
 	int ret = 0;
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index ff807e14c8911..4c3c1299c628d 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -324,7 +324,8 @@ static int __f2fs_write_meta_page(struct page *page,
 	return AOP_WRITEPAGE_ACTIVATE;
 }
 
-static int f2fs_write_meta_page(struct page *page,
+static int f2fs_write_meta_page(struct address_space *__mapping,
+				struct page *page,
 				struct writeback_control *wbc)
 {
 	return __f2fs_write_meta_page(page, wbc, FS_META_IO);
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index dbbfaa8cd9602..888569093c9f5 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2893,7 +2893,8 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
 	return err;
 }
 
-static int f2fs_write_data_page(struct page *page,
+static int f2fs_write_data_page(struct address_space *__mapping,
+					struct page *page,
 					struct writeback_control *wbc)
 {
 #ifdef CONFIG_F2FS_FS_COMPRESSION
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index cb1b5b61a1dab..290e5fdc3bfb9 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -1650,7 +1650,8 @@ int f2fs_move_node_page(struct page *node_page, int gc_type)
 	return err;
 }
 
-static int f2fs_write_node_page(struct page *page,
+static int f2fs_write_node_page(struct address_space *__mapping,
+				struct page *page,
 				struct writeback_control *wbc)
 {
 	return __write_node_page(page, false, NULL, wbc, false,
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 6ef4e7619684d..18485221c9ca3 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -194,7 +194,8 @@ static int fat_get_block(struct inode *inode, sector_t iblock,
 	return 0;
 }
 
-static int fat_writepage(struct page *page, struct writeback_control *wbc)
+static int fat_writepage(struct address_space *__mapping, struct page *page,
+			 struct writeback_control *wbc)
 {
 	return block_write_full_page(page, fat_get_block, wbc);
 }
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index f7c6b4b711a86..66b31387e878f 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1886,7 +1886,8 @@ static int fuse_writepage_locked(struct page *page)
 	return error;
 }
 
-static int fuse_writepage(struct page *page, struct writeback_control *wbc)
+static int fuse_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc)
 {
 	int err;
 
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index e3b5f5fa08bec..826dd0677fdb9 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -86,7 +86,8 @@ static int gfs2_get_block_noalloc(struct inode *inode, sector_t lblock,
  * @page: The page
  * @wbc: The writeback control
  */
-static int gfs2_writepage(struct page *page, struct writeback_control *wbc)
+static int gfs2_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc)
 {
 	struct inode *inode = page->mapping->host;
 	struct gfs2_inode *ip = GFS2_I(inode);
@@ -178,7 +179,9 @@ static int __gfs2_jdata_writepage(struct page *page, struct writeback_control *w
  *
  */
 
-static int gfs2_jdata_writepage(struct page *page, struct writeback_control *wbc)
+static int gfs2_jdata_writepage(struct address_space *__mapping,
+				struct page *page,
+				struct writeback_control *wbc)
 {
 	struct inode *inode = page->mapping->host;
 	struct gfs2_inode *ip = GFS2_I(inode);
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index 9856cc2e07950..8681d1b551a67 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -30,7 +30,9 @@
 #include "util.h"
 #include "trace_gfs2.h"
 
-static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wbc)
+static int gfs2_aspace_writepage(struct address_space *__mapping,
+				 struct page *page,
+				 struct writeback_control *wbc)
 {
 	struct buffer_head *bh, *head;
 	int nr_underway = 0;
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index ae193e389e22e..101cc5e10524f 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -29,7 +29,8 @@ static const struct inode_operations hfs_file_inode_operations;
 
 #define HFS_VALID_MODE_BITS  (S_IFREG | S_IFDIR | S_IRWXUGO)
 
-static int hfs_writepage(struct page *page, struct writeback_control *wbc)
+static int hfs_writepage(struct address_space *__mapping, struct page *page,
+			 struct writeback_control *wbc)
 {
 	return block_write_full_page(page, hfs_get_block, wbc);
 }
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index 9955b8dcb8061..1654ee206e7e5 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -29,7 +29,8 @@ static int hfsplus_readpage(struct file *file,
 	return block_read_full_page(page, hfsplus_get_block);
 }
 
-static int hfsplus_writepage(struct page *page, struct writeback_control *wbc)
+static int hfsplus_writepage(struct address_space *__mapping,
+			     struct page *page, struct writeback_control *wbc)
 {
 	return block_write_full_page(page, hfsplus_get_block, wbc);
 }
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 0b78c8bf11717..a350d486a42f9 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -396,7 +396,8 @@ static const struct file_operations hostfs_dir_fops = {
 	.fsync		= hostfs_fsync,
 };
 
-static int hostfs_writepage(struct page *page, struct writeback_control *wbc)
+static int hostfs_writepage(struct address_space *__mapping,
+			    struct page *page, struct writeback_control *wbc)
 {
 	struct address_space *mapping = page->mapping;
 	struct inode *inode = mapping->host;
diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c
index 28be564b9eba8..0c3858afe720e 100644
--- a/fs/hpfs/file.c
+++ b/fs/hpfs/file.c
@@ -122,7 +122,8 @@ static int hpfs_readpage(struct file *file, struct address_space *__mapping,
 	return mpage_readpage(page, hpfs_get_block);
 }
 
-static int hpfs_writepage(struct page *page, struct writeback_control *wbc)
+static int hpfs_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc)
 {
 	return block_write_full_page(page, hpfs_get_block, wbc);
 }
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
index 41b77aac394d6..549bd5aa36bc0 100644
--- a/fs/jfs/inode.c
+++ b/fs/jfs/inode.c
@@ -280,7 +280,8 @@ int jfs_get_block(struct inode *ip, sector_t lblock,
 	return rc;
 }
 
-static int jfs_writepage(struct page *page, struct writeback_control *wbc)
+static int jfs_writepage(struct address_space *__mapping, struct page *page,
+			 struct writeback_control *wbc)
 {
 	return block_write_full_page(page, jfs_get_block, wbc);
 }
diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
index e9dae1dccb42b..a6e48e733d3a6 100644
--- a/fs/jfs/jfs_metapage.c
+++ b/fs/jfs/jfs_metapage.c
@@ -332,7 +332,9 @@ static void metapage_write_end_io(struct bio *bio)
 	bio_put(bio);
 }
 
-static int metapage_writepage(struct page *page, struct writeback_control *wbc)
+static int metapage_writepage(struct address_space *__mapping,
+			      struct page *page,
+			      struct writeback_control *wbc)
 {
 	struct bio *bio = NULL;
 	int block_offset;	/* block offset of mp within page */
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 9677f0424a72d..46e169d400be3 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -398,7 +398,8 @@ static int minix_get_block(struct inode *inode, sector_t block,
 		return V2_minix_get_block(inode, block, bh_result, create);
 }
 
-static int minix_writepage(struct page *page, struct writeback_control *wbc)
+static int minix_writepage(struct address_space *__mapping, struct page *page,
+			   struct writeback_control *wbc)
 {
 	return block_write_full_page(page, minix_get_block, wbc);
 }
diff --git a/fs/mpage.c b/fs/mpage.c
index 830e6cc2a9e72..a2bca7b6c90c6 100644
--- a/fs/mpage.c
+++ b/fs/mpage.c
@@ -659,7 +659,7 @@ static int __mpage_writepage(struct page *page, struct writeback_control *wbc,
 		bio = mpage_bio_submit(REQ_OP_WRITE, op_flags, bio);
 
 	if (mpd->use_writepage) {
-		ret = mapping->a_ops->writepage(page, wbc);
+		ret = mapping->a_ops->writepage(MAPPING_NULL, page, wbc);
 	} else {
 		ret = -EAGAIN;
 		goto out;
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 639c34fec04a8..b7fe16714d9cc 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -683,7 +683,8 @@ static int nfs_writepage_locked(struct page *page,
 	return 0;
 }
 
-int nfs_writepage(struct page *page, struct writeback_control *wbc)
+int nfs_writepage(struct address_space *__mapping, struct page *page,
+		  struct writeback_control *wbc)
 {
 	int ret;
 
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index f1e5cc46ce00b..00a22de8e2376 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -170,7 +170,8 @@ static int nilfs_writepages(struct address_space *mapping,
 	return err;
 }
 
-static int nilfs_writepage(struct page *page, struct writeback_control *wbc)
+static int nilfs_writepage(struct address_space *__mapping, struct page *page,
+			   struct writeback_control *wbc)
 {
 	struct inode *inode = page->mapping->host;
 	int err;
diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c
index c0361ce45f62d..bd917df901b0a 100644
--- a/fs/nilfs2/mdt.c
+++ b/fs/nilfs2/mdt.c
@@ -398,7 +398,8 @@ int nilfs_mdt_fetch_dirty(struct inode *inode)
 }
 
 static int
-nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc)
+nilfs_mdt_write_page(struct address_space *__mapping, struct page *page,
+		     struct writeback_control *wbc)
 {
 	struct inode *inode = page->mapping->host;
 	struct super_block *sb;
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
index ca0bcec9ac2f6..d920cb780a4ea 100644
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -1334,7 +1334,8 @@ static int ntfs_write_mst_block(struct page *page,
  *
  * Return 0 on success and -errno on error.
  */
-static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
+static int ntfs_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc)
 {
 	loff_t i_size;
 	struct inode *vi = page->mapping->host;
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index a5f9686ae100d..c597a104e0af4 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -401,7 +401,8 @@ static void ocfs2_readahead(struct readahead_control *rac)
  * mapping can't have disappeared under the dirty pages that it is
  * being asked to write back.
  */
-static int ocfs2_writepage(struct page *page, struct writeback_control *wbc)
+static int ocfs2_writepage(struct address_space *__mapping, struct page *page,
+			   struct writeback_control *wbc)
 {
 	trace_ocfs2_writepage(
 		(unsigned long long)OCFS2_I(page->mapping->host)->ip_blkno,
diff --git a/fs/omfs/file.c b/fs/omfs/file.c
index c55fd61021b65..e130dfda28526 100644
--- a/fs/omfs/file.c
+++ b/fs/omfs/file.c
@@ -295,7 +295,8 @@ static void omfs_readahead(struct readahead_control *rac)
 	mpage_readahead(rac, omfs_get_block);
 }
 
-static int omfs_writepage(struct page *page, struct writeback_control *wbc)
+static int omfs_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc)
 {
 	return block_write_full_page(page, omfs_get_block, wbc);
 }
diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c
index 6d797c789f035..f463cfb435292 100644
--- a/fs/orangefs/inode.c
+++ b/fs/orangefs/inode.c
@@ -66,7 +66,9 @@ static int orangefs_writepage_locked(struct page *page,
 	return ret;
 }
 
-static int orangefs_writepage(struct page *page, struct writeback_control *wbc)
+static int orangefs_writepage(struct address_space *__mapping,
+			      struct page *page,
+			      struct writeback_control *wbc)
 {
 	int ret;
 	ret = orangefs_writepage_locked(page, wbc);
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 3f7638fd6eeca..5a34ab78f66cd 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -2744,7 +2744,9 @@ static int reiserfs_readpage(struct file *f, struct address_space *__mapping,
 	return block_read_full_page(page, reiserfs_get_block);
 }
 
-static int reiserfs_writepage(struct page *page, struct writeback_control *wbc)
+static int reiserfs_writepage(struct address_space *__mapping,
+			      struct page *page,
+			      struct writeback_control *wbc)
 {
 	struct inode *inode = page->mapping->host;
 	reiserfs_wait_on_write_block(inode->i_sb);
diff --git a/fs/sysv/itree.c b/fs/sysv/itree.c
index eaca724493b3d..fa5b348322a63 100644
--- a/fs/sysv/itree.c
+++ b/fs/sysv/itree.c
@@ -451,7 +451,8 @@ int sysv_getattr(const struct path *path, struct kstat *stat,
 	return 0;
 }
 
-static int sysv_writepage(struct page *page, struct writeback_control *wbc)
+static int sysv_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc)
 {
 	return block_write_full_page(page,get_block,wbc);
 }
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 7b868f220ca7d..fcc6c307313f2 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1003,7 +1003,8 @@ static int do_writepage(struct page *page, int len)
  * on the page lock and it would not write the truncated inode node to the
  * journal before we have finished.
  */
-static int ubifs_writepage(struct page *page, struct writeback_control *wbc)
+static int ubifs_writepage(struct address_space *__mapping, struct page *page,
+			   struct writeback_control *wbc)
 {
 	struct inode *inode = page->mapping->host;
 	struct ubifs_info *c = inode->i_sb->s_fs_info;
diff --git a/fs/udf/file.c b/fs/udf/file.c
index 6d3a2291856fe..17f664979eed2 100644
--- a/fs/udf/file.c
+++ b/fs/udf/file.c
@@ -68,7 +68,8 @@ static int udf_adinicb_readpage(struct file *file,
 	return 0;
 }
 
-static int udf_adinicb_writepage(struct page *page,
+static int udf_adinicb_writepage(struct address_space *__mapping,
+				 struct page *page,
 				 struct writeback_control *wbc)
 {
 	struct inode *inode = page->mapping->host;
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 5bceaf7456be7..cf4d8ab143190 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -179,7 +179,8 @@ static void udf_write_failed(struct address_space *mapping, loff_t to)
 	}
 }
 
-static int udf_writepage(struct page *page, struct writeback_control *wbc)
+static int udf_writepage(struct address_space *__mapping, struct page *page,
+			 struct writeback_control *wbc)
 {
 	return block_write_full_page(page, udf_get_block, wbc);
 }
@@ -303,7 +304,7 @@ int udf_expand_file_adinicb(struct inode *inode)
 	/* from now on we have normal address_space methods */
 	inode->i_data.a_ops = &udf_aops;
 	up_write(&iinfo->i_data_sem);
-	err = inode->i_data.a_ops->writepage(page, &udf_wbc);
+	err = inode->i_data.a_ops->writepage(MAPPING_NULL, page, &udf_wbc);
 	if (err) {
 		/* Restore everything back so that we don't lose data... */
 		lock_page(page);
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index 4f44e309cfaf5..5bf96e90d8be4 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -467,7 +467,8 @@ static int ufs_getfrag_block(struct inode *inode, sector_t fragment, struct buff
 	return 0;
 }
 
-static int ufs_writepage(struct page *page, struct writeback_control *wbc)
+static int ufs_writepage(struct address_space *__mapping, struct page *page,
+			 struct writeback_control *wbc)
 {
 	return block_write_full_page(page,ufs_getfrag_block,wbc);
 }
diff --git a/fs/vboxsf/file.c b/fs/vboxsf/file.c
index da3716c5d8174..55d449b7a2b10 100644
--- a/fs/vboxsf/file.c
+++ b/fs/vboxsf/file.c
@@ -251,7 +251,8 @@ static struct vboxsf_handle *vboxsf_get_write_handle(struct vboxsf_inode *sf_i)
 	return sf_handle;
 }
 
-static int vboxsf_writepage(struct page *page, struct writeback_control *wbc)
+static int vboxsf_writepage(struct address_space *__mapping,
+			    struct page *page, struct writeback_control *wbc)
 {
 	struct inode *inode = page->mapping->host;
 	struct vboxsf_inode *sf_i = VBOXSF_I(inode);
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 3e862efc2d881..6827f6226499a 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -559,7 +559,7 @@ static const struct iomap_writeback_ops xfs_writeback_ops = {
 
 STATIC int
 xfs_vm_writepage(
-	struct page		*page,
+	struct address_space *__mapping, struct page		*page,
 	struct writeback_control *wbc)
 {
 	struct xfs_writepage_ctx wpc = { };
diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c
index 160190288f711..ee7175cf209a6 100644
--- a/fs/zonefs/super.c
+++ b/fs/zonefs/super.c
@@ -112,7 +112,8 @@ static const struct iomap_writeback_ops zonefs_writeback_ops = {
 	.map_blocks		= zonefs_map_blocks,
 };
 
-static int zonefs_writepage(struct page *page, struct writeback_control *wbc)
+static int zonefs_writepage(struct address_space *__mapping,
+			    struct page *page, struct writeback_control *wbc)
 {
 	struct iomap_writepage_ctx wpc = { };
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 0326f24608544..acd51e3880762 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -370,7 +370,8 @@ typedef int (*read_actor_t)(read_descriptor_t *, struct page *,
 		unsigned long, unsigned long);
 
 struct address_space_operations {
-	int (*writepage)(struct page *page, struct writeback_control *wbc);
+	int (*writepage)(struct address_space *, struct page *page,
+			 struct writeback_control *wbc);
 	int (*readpage)(struct file *, struct address_space *, struct page *);
 
 	/* Write back some dirty pages from this mapping. */
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 6d5b245bfa247..798497c5ebf0e 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -536,7 +536,8 @@ extern void nfs_complete_unlink(struct dentry *dentry, struct inode *);
  * linux/fs/nfs/write.c
  */
 extern int  nfs_congestion_kb;
-extern int  nfs_writepage(struct page *page, struct writeback_control *wbc);
+extern int  nfs_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc);
 extern int  nfs_writepages(struct address_space *, struct writeback_control *);
 extern int  nfs_flush_incompatible(struct file *file, struct page *page);
 extern int  nfs_updatepage(struct file *, struct page *, unsigned int, unsigned int);
diff --git a/include/linux/swap.h b/include/linux/swap.h
index 661046994db4b..f2355fca8b38b 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -392,7 +392,8 @@ extern void kswapd_stop(int nid);
 
 /* linux/mm/page_io.c */
 extern int swap_readpage(struct page *page, bool do_poll);
-extern int swap_writepage(struct page *page, struct writeback_control *wbc);
+extern int swap_writepage(struct address_space *__mapping, struct page *page,
+			  struct writeback_control *wbc);
 extern void end_swap_bio_write(struct bio *bio);
 extern int __swap_writepage(struct page *page, struct writeback_control *wbc,
 	bio_end_io_t end_write_func);
@@ -557,7 +558,9 @@ static inline struct page *swapin_readahead(swp_entry_t swp, gfp_t gfp_mask,
 	return NULL;
 }
 
-static inline int swap_writepage(struct page *p, struct writeback_control *wbc)
+static inline int swap_writepage(struct address_space *__mapping,
+				 struct page *p,
+				 struct writeback_control *wbc)
 {
 	return 0;
 }
diff --git a/mm/migrate.c b/mm/migrate.c
index 04a98bb2f568f..21beb45356760 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -890,7 +890,7 @@ static int writeout(struct address_space *mapping, struct page *page)
 	 */
 	remove_migration_ptes(page, page, false);
 
-	rc = mapping->a_ops->writepage(page, &wbc);
+	rc = mapping->a_ops->writepage(MAPPING_NULL, page, &wbc);
 
 	if (rc != AOP_WRITEPAGE_ACTIVATE)
 		/* unlocked. Relock */
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 4e4ddd67b71e5..de15d2febc5ae 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -2308,7 +2308,7 @@ static int __writepage(struct page *page, struct writeback_control *wbc,
 		       void *data)
 {
 	struct address_space *mapping = data;
-	int ret = mapping->a_ops->writepage(page, wbc);
+	int ret = mapping->a_ops->writepage(MAPPING_NULL, page, wbc);
 	mapping_set_error(mapping, ret);
 	return ret;
 }
@@ -2386,7 +2386,7 @@ int write_one_page(struct page *page)
 
 	if (clear_page_dirty_for_io(page)) {
 		get_page(page);
-		ret = mapping->a_ops->writepage(page, &wbc);
+		ret = mapping->a_ops->writepage(MAPPING_NULL, page, &wbc);
 		if (ret == 0)
 			wait_on_page_writeback(page);
 		put_page(page);
diff --git a/mm/page_io.c b/mm/page_io.c
index 8f1748c94c3a8..067159b23ee54 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -244,7 +244,8 @@ int generic_swapfile_activate(struct swap_info_struct *sis,
  * We may have stale swap cache pages in memory: notice
  * them here and get rid of the unnecessary final write.
  */
-int swap_writepage(struct page *page, struct writeback_control *wbc)
+int swap_writepage(struct address_space *__mapping, struct page *page,
+		   struct writeback_control *wbc)
 {
 	int ret = 0;
 
diff --git a/mm/shmem.c b/mm/shmem.c
index 8e2b35ba93ad1..6f540e4f3a993 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1356,7 +1356,8 @@ int shmem_unuse(unsigned int type, bool frontswap,
 /*
  * Move the page from the page cache to the swap cache.
  */
-static int shmem_writepage(struct page *page, struct writeback_control *wbc)
+static int shmem_writepage(struct address_space *__mapping, struct page *page,
+			   struct writeback_control *wbc)
 {
 	struct shmem_inode_info *info;
 	struct address_space *mapping;
@@ -1448,7 +1449,7 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
 
 		mutex_unlock(&shmem_swaplist_mutex);
 		BUG_ON(page_mapped(page));
-		swap_writepage(page, wbc);
+		swap_writepage(MAPPING_NULL, page, wbc);
 		return 0;
 	}
 
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 6db869339073d..4322bc5ee2d84 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -825,7 +825,7 @@ static pageout_t pageout(struct page *page, struct address_space *mapping)
 		};
 
 		SetPageReclaim(page);
-		res = mapping->a_ops->writepage(page, &wbc);
+		res = mapping->a_ops->writepage(MAPPING_NULL, page, &wbc);
 		if (res < 0)
 			handle_write_error(mapping, page, res);
 		if (res == AOP_WRITEPAGE_ACTIVATE) {
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 06/14] mm: add struct address_space to set_page_dirty() callback
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
                   ` (3 preceding siblings ...)
  2020-10-07  1:05 ` [PATCH 05/14] mm: add struct address_space to writepage() callback jglisse
@ 2020-10-07  1:05 ` jglisse
  2020-10-07  1:05 ` [PATCH 07/14] mm: add struct address_space to invalidatepage() callback jglisse
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

This is part of patchset to remove dependency on struct page.mapping
field so that we can temporarily update it to point to a special
structure tracking temporary page state (note that original mapping
pointer is preserved and can still be accessed but at a cost).

Add struct address_space to set_page_dirty() callback arguments.

Note that this patch does not make use of the new argument, nor does
it use a valid one at call site (by default this patch just use NULL
for new argument value).

Use following script (from root of linux kernel tree):

./that-script.sh that-semantic-patch.spatch

%<--------------------------------------------------------------------
#!/bin/sh
spatch_file=$1

echo PART1 ===========================================================

# P1 find callback functions name
spatch  --dir . --no-includes -D part1 --sp-file $spatch_file

echo PART2 ===========================================================

# P2 change callback function prototype
cat /tmp/unicorn-functions | sort | uniq | while read func ; do
    for file in $( git grep -l $func -- '*.[ch]' ) ; do
        echo $file
        spatch --no-includes --in-place -D part2 \
               -D fn=$func --sp-file $spatch_file $file
    done
done

echo PART 3 ==========================================================

# P3 find all function which call the callback
spatch --dir . --include-headers -D part3 --sp-file $spatch_file

echo PART 4===========================================================

# P4 change all funcitons which call the callback
cat /tmp/unicorn-files | sort | uniq | while read file ; do
    echo $file
    spatch --no-includes --in-place -D part4 \
           --sp-file $spatch_file $file
done
-------------------------------------------------------------------->%

With the following semantic patch:

%<--------------------------------------------------------------------
virtual part1, part2, part3, part4

// ----------------------------------------------------------------------------
// Part 1 is grepping all function that are use as callback for set_page_dirty.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part1@
@@
file=open('/tmp/unicorn-functions', 'w')
file.close()

// match function name use as a callback
@p1r2 depends on part1@
identifier I1, FN;
@@
struct address_space_operations I1 = {..., .set_page_dirty = FN, ...};

@script:python p1r3 depends on p1r2@
funcname << p1r2.FN;
@@
if funcname != "NULL":
  file=open('/tmp/unicorn-functions', 'a')
  file.write(funcname + '\n')
  file.close()

// -------------------------------------------------------------------
// Part 2 modify callback

// Add address_space argument to the function (set_page_dirty callback one)
@p2r1 depends on part2@
identifier virtual.fn;
identifier I1;
type T1;
@@
int fn(
+struct address_space *__mapping,
T1 I1) { ... }

@p2r2 depends on part2@
identifier virtual.fn;
identifier I1;
type T1;
@@
int fn(
+struct address_space *__mapping,
T1 I1);

@p2r3 depends on part2@
identifier virtual.fn;
type T1;
@@
int fn(
+struct address_space *,
T1);

@p2r4 depends on part2@
identifier virtual.fn;
expression E1;
@@
fn(
+MAPPING_NULL,
E1)

// ----------------------------------------------------------------------------
// Part 3 is grepping all function that are use the callback for set_page_dirty.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part3@
@@
file=open('/tmp/unicorn-files', 'w')
file.write("./include/linux/pagemap.h\n")
file.write("./mm/page-writeback.c\n")
file.write("./include/linux/mm.h\n")
file.write("./include/linux/fs.h\n")
file.close()

@p3r1 depends on part3 exists@
expression E1, E2;
identifier FN;
position P;
@@
FN@P(...) {...
(
E1.a_ops->set_page_dirty(E2)
|
E1->a_ops->set_page_dirty(E2)
)
...}

@script:python p3r2 depends on p3r1@
P << p3r1.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

// -------------------------------------------------------------------
// Part 4 generic modification
@p4r1 depends on part4@
@@
struct address_space_operations { ... int (*set_page_dirty)(
+struct address_space *,
struct page *page); ... };

@p4r2 depends on part4@
expression E1, E2;
@@
E1.a_ops->set_page_dirty(
+MAPPING_NULL,
E2)

@p4r3 depends on part4@
expression E1, E2;
@@
E1->a_ops->set_page_dirty(
+MAPPING_NULL,
E2)

@p4r4 depends on part4@
@@
{...
-int (*spd)(struct page *) = mapping->a_ops->set_page_dirty;
+int (*spd)(struct address_space *, struct page *) = mapping->a_ops->set_page_dirty;
...
return (*spd)(
+MAPPING_NULL,
page);
...}
-------------------------------------------------------------------->%

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>
---
 drivers/video/fbdev/core/fb_defio.c |  3 ++-
 fs/afs/dir.c                        |  3 ++-
 fs/afs/internal.h                   |  2 +-
 fs/afs/write.c                      |  4 ++--
 fs/btrfs/disk-io.c                  |  5 +++--
 fs/btrfs/extent_io.c                |  2 +-
 fs/btrfs/inode.c                    |  8 +++++---
 fs/buffer.c                         |  3 ++-
 fs/ceph/addr.c                      |  5 +++--
 fs/cifs/cifssmb.c                   |  2 +-
 fs/ext4/inode.c                     | 10 ++++++----
 fs/f2fs/checkpoint.c                |  5 +++--
 fs/f2fs/data.c                      |  7 ++++---
 fs/f2fs/node.c                      |  5 +++--
 fs/gfs2/aops.c                      |  5 +++--
 fs/hugetlbfs/inode.c                |  3 ++-
 fs/iomap/buffered-io.c              |  4 ++--
 fs/libfs.c                          |  5 +++--
 fs/nfs/write.c                      |  6 +++---
 fs/nilfs2/inode.c                   |  5 +++--
 fs/nilfs2/page.c                    |  2 +-
 fs/nilfs2/segment.c                 |  4 ++--
 fs/ntfs/aops.c                      |  2 +-
 fs/ntfs/file.c                      |  2 +-
 fs/reiserfs/inode.c                 |  7 ++++---
 fs/ubifs/file.c                     |  9 +++++----
 include/linux/buffer_head.h         |  3 ++-
 include/linux/fs.h                  |  5 +++--
 include/linux/iomap.h               |  2 +-
 include/linux/mm.h                  |  6 ++++--
 include/linux/swap.h                |  3 ++-
 mm/page-writeback.c                 | 12 +++++++-----
 mm/page_io.c                        |  6 +++---
 33 files changed, 90 insertions(+), 65 deletions(-)

diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index a591d291b231a..32340ff1d7243 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -151,7 +151,8 @@ static const struct vm_operations_struct fb_deferred_io_vm_ops = {
 	.page_mkwrite	= fb_deferred_io_mkwrite,
 };
 
-static int fb_deferred_io_set_page_dirty(struct page *page)
+static int fb_deferred_io_set_page_dirty(struct address_space *__mapping,
+					 struct page *page)
 {
 	if (!PageDirty(page))
 		SetPageDirty(page);
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 1d2e61e0ab047..ebcf074bcaaa2 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -44,7 +44,8 @@ static int afs_dir_releasepage(struct page *page, gfp_t gfp_flags);
 static void afs_dir_invalidatepage(struct page *page, unsigned int offset,
 				   unsigned int length);
 
-static int afs_dir_set_page_dirty(struct page *page)
+static int afs_dir_set_page_dirty(struct address_space *__mapping,
+				  struct page *page)
 {
 	BUG(); /* This should never happen. */
 }
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index fc1c80c5ddb88..264f28759c737 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -1422,7 +1422,7 @@ extern int afs_check_volume_status(struct afs_volume *, struct afs_operation *);
 /*
  * write.c
  */
-extern int afs_set_page_dirty(struct page *);
+extern int afs_set_page_dirty(struct address_space *, struct page *);
 extern int afs_write_begin(struct file *file, struct address_space *mapping,
 			loff_t pos, unsigned len, unsigned flags,
 			struct page **pagep, void **fsdata);
diff --git a/fs/afs/write.c b/fs/afs/write.c
index ef0ea031130af..199cbf73b9be4 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -16,10 +16,10 @@
 /*
  * mark a page as having been made dirty and thus needing writeback
  */
-int afs_set_page_dirty(struct page *page)
+int afs_set_page_dirty(struct address_space *__mapping, struct page *page)
 {
 	_enter("");
-	return __set_page_dirty_nobuffers(page);
+	return __set_page_dirty_nobuffers(MAPPING_NULL, page);
 }
 
 /*
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 51ca16ab59e07..7f548f9f5ace1 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -980,7 +980,8 @@ static void btree_invalidatepage(struct page *page, unsigned int offset,
 	}
 }
 
-static int btree_set_page_dirty(struct page *page)
+static int btree_set_page_dirty(struct address_space *__mapping,
+				struct page *page)
 {
 #ifdef DEBUG
 	struct extent_buffer *eb;
@@ -992,7 +993,7 @@ static int btree_set_page_dirty(struct page *page)
 	BUG_ON(!atomic_read(&eb->refs));
 	btrfs_assert_tree_locked(eb);
 #endif
-	return __set_page_dirty_nobuffers(page);
+	return __set_page_dirty_nobuffers(MAPPING_NULL, page);
 }
 
 static const struct address_space_operations btree_aops = {
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index a940edb1e64f2..02569dffe8e14 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -1514,7 +1514,7 @@ void extent_range_redirty_for_io(struct inode *inode, u64 start, u64 end)
 	while (index <= end_index) {
 		page = find_get_page(inode->i_mapping, index);
 		BUG_ON(!page); /* Pages should be in the extent_io_tree */
-		__set_page_dirty_nobuffers(page);
+		__set_page_dirty_nobuffers(MAPPING_NULL, page);
 		account_page_redirty(page);
 		put_page(page);
 		index++;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index e73dc72dbd984..9f35648ba06d8 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -735,7 +735,8 @@ static noinline int compress_file_range(struct async_chunk *async_chunk)
 	if (async_chunk->locked_page &&
 	    (page_offset(async_chunk->locked_page) >= start &&
 	     page_offset(async_chunk->locked_page)) <= end) {
-		__set_page_dirty_nobuffers(async_chunk->locked_page);
+		__set_page_dirty_nobuffers(MAPPING_NULL,
+					   async_chunk->locked_page);
 		/* unlocked later on in the async handlers */
 	}
 
@@ -9814,9 +9815,10 @@ int btrfs_prealloc_file_range_trans(struct inode *inode,
 					   min_size, actual_len, alloc_hint, trans);
 }
 
-static int btrfs_set_page_dirty(struct page *page)
+static int btrfs_set_page_dirty(struct address_space *__mapping,
+				struct page *page)
 {
-	return __set_page_dirty_nobuffers(page);
+	return __set_page_dirty_nobuffers(MAPPING_NULL, page);
 }
 
 static int btrfs_permission(struct inode *inode, int mask)
diff --git a/fs/buffer.c b/fs/buffer.c
index c99a468833828..6fb6cf497feb8 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -638,7 +638,8 @@ EXPORT_SYMBOL_GPL(__set_page_dirty);
  * FIXME: may need to call ->reservepage here as well.  That's rather up to the
  * address_space though.
  */
-int __set_page_dirty_buffers(struct page *page)
+int __set_page_dirty_buffers(struct address_space *__mapping,
+			     struct page *page)
 {
 	int newly_dirty;
 	struct address_space *mapping = page_mapping(page);
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 8d348fb29102f..b2b2c8f8118e4 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -72,7 +72,8 @@ static inline struct ceph_snap_context *page_snap_context(struct page *page)
  * Dirty a page.  Optimistically adjust accounting, on the assumption
  * that we won't race with invalidate.  If we do, readjust.
  */
-static int ceph_set_page_dirty(struct page *page)
+static int ceph_set_page_dirty(struct address_space *__mapping,
+			       struct page *page)
 {
 	struct address_space *mapping = page->mapping;
 	struct inode *inode;
@@ -127,7 +128,7 @@ static int ceph_set_page_dirty(struct page *page)
 	page->private = (unsigned long)snapc;
 	SetPagePrivate(page);
 
-	ret = __set_page_dirty_nobuffers(page);
+	ret = __set_page_dirty_nobuffers(MAPPING_NULL, page);
 	WARN_ON(!PageLocked(page));
 	WARN_ON(!page->mapping);
 
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 0496934feecb7..a555efb817b0c 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -2105,7 +2105,7 @@ cifs_writev_complete(struct work_struct *work)
 	for (i = 0; i < wdata->nr_pages; i++) {
 		struct page *page = wdata->pages[i];
 		if (wdata->result == -EAGAIN)
-			__set_page_dirty_nobuffers(page);
+			__set_page_dirty_nobuffers(MAPPING_NULL, page);
 		else if (wdata->result < 0)
 			SetPageError(page);
 		end_page_writeback(page);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index f8a4d324a6041..528eec0b02bf2 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3589,17 +3589,19 @@ const struct iomap_ops ext4_iomap_report_ops = {
  * So what we do is to mark the page "pending dirty" and next time writepage
  * is called, propagate that into the buffers appropriately.
  */
-static int ext4_journalled_set_page_dirty(struct page *page)
+static int ext4_journalled_set_page_dirty(struct address_space *__mapping,
+					  struct page *page)
 {
 	SetPageChecked(page);
-	return __set_page_dirty_nobuffers(page);
+	return __set_page_dirty_nobuffers(MAPPING_NULL, page);
 }
 
-static int ext4_set_page_dirty(struct page *page)
+static int ext4_set_page_dirty(struct address_space *__mapping,
+			       struct page *page)
 {
 	WARN_ON_ONCE(!PageLocked(page) && !PageDirty(page));
 	WARN_ON_ONCE(!page_has_buffers(page));
-	return __set_page_dirty_buffers(page);
+	return __set_page_dirty_buffers(MAPPING_NULL, page);
 }
 
 static const struct address_space_operations ext4_aops = {
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 4c3c1299c628d..f4594b08270a4 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -432,14 +432,15 @@ long f2fs_sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type,
 	return nwritten;
 }
 
-static int f2fs_set_meta_page_dirty(struct page *page)
+static int f2fs_set_meta_page_dirty(struct address_space *__mapping,
+				    struct page *page)
 {
 	trace_f2fs_set_page_dirty(page, META);
 
 	if (!PageUptodate(page))
 		SetPageUptodate(page);
 	if (!PageDirty(page)) {
-		__set_page_dirty_nobuffers(page);
+		__set_page_dirty_nobuffers(MAPPING_NULL, page);
 		inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_META);
 		f2fs_set_page_private(page, 0);
 		f2fs_trace_pid(page);
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 888569093c9f5..12350175133aa 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -3734,7 +3734,8 @@ int f2fs_release_page(struct page *page, gfp_t wait)
 	return 1;
 }
 
-static int f2fs_set_data_page_dirty(struct page *page)
+static int f2fs_set_data_page_dirty(struct address_space *__mapping,
+				    struct page *page)
 {
 	struct inode *inode = page_file_mapping(page)->host;
 
@@ -3743,7 +3744,7 @@ static int f2fs_set_data_page_dirty(struct page *page)
 	if (!PageUptodate(page))
 		SetPageUptodate(page);
 	if (PageSwapCache(page))
-		return __set_page_dirty_nobuffers(page);
+		return __set_page_dirty_nobuffers(MAPPING_NULL, page);
 
 	if (f2fs_is_atomic_file(inode) && !f2fs_is_commit_atomic_write(inode)) {
 		if (!IS_ATOMIC_WRITTEN_PAGE(page)) {
@@ -3758,7 +3759,7 @@ static int f2fs_set_data_page_dirty(struct page *page)
 	}
 
 	if (!PageDirty(page)) {
-		__set_page_dirty_nobuffers(page);
+		__set_page_dirty_nobuffers(MAPPING_NULL, page);
 		f2fs_update_dirty_page(inode, page);
 		return 1;
 	}
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 290e5fdc3bfb9..648a2d7f307bd 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -2072,7 +2072,8 @@ static int f2fs_write_node_pages(struct address_space *mapping,
 	return 0;
 }
 
-static int f2fs_set_node_page_dirty(struct page *page)
+static int f2fs_set_node_page_dirty(struct address_space *__mapping,
+				    struct page *page)
 {
 	trace_f2fs_set_page_dirty(page, NODE);
 
@@ -2083,7 +2084,7 @@ static int f2fs_set_node_page_dirty(struct page *page)
 		f2fs_inode_chksum_set(F2FS_P_SB(page), page);
 #endif
 	if (!PageDirty(page)) {
-		__set_page_dirty_nobuffers(page);
+		__set_page_dirty_nobuffers(MAPPING_NULL, page);
 		inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_NODES);
 		f2fs_set_page_private(page, 0);
 		f2fs_trace_pid(page);
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 826dd0677fdb9..8911771f95c5c 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -624,10 +624,11 @@ void adjust_fs_space(struct inode *inode)
  * Returns: 1 if it dirtyed the page, or 0 otherwise
  */
  
-static int jdata_set_page_dirty(struct page *page)
+static int jdata_set_page_dirty(struct address_space *__mapping,
+				struct page *page)
 {
 	SetPageChecked(page);
-	return __set_page_dirty_buffers(page);
+	return __set_page_dirty_buffers(MAPPING_NULL, page);
 }
 
 /**
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index b5c109703daaf..b675b615cead0 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -947,7 +947,8 @@ static int hugetlbfs_symlink(struct inode *dir,
 /*
  * mark the head page dirty
  */
-static int hugetlbfs_set_page_dirty(struct page *page)
+static int hugetlbfs_set_page_dirty(struct address_space *__mapping,
+				    struct page *page)
 {
 	struct page *head = compound_head(page);
 
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index bcfc288dba3fb..26f7fe7c80adc 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -661,7 +661,7 @@ iomap_write_begin(struct inode *inode, loff_t pos, unsigned len, unsigned flags,
 }
 
 int
-iomap_set_page_dirty(struct page *page)
+iomap_set_page_dirty(struct address_space *__mapping, struct page *page)
 {
 	struct address_space *mapping = page_mapping(page);
 	int newly_dirty;
@@ -705,7 +705,7 @@ __iomap_write_end(struct inode *inode, loff_t pos, unsigned len,
 	if (unlikely(copied < len && !PageUptodate(page)))
 		return 0;
 	iomap_set_range_uptodate(page, offset_in_page(pos), len);
-	iomap_set_page_dirty(page);
+	iomap_set_page_dirty(MAPPING_NULL, page);
 	return copied;
 }
 
diff --git a/fs/libfs.c b/fs/libfs.c
index 7df05487cdde6..899feec2eb683 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1156,7 +1156,7 @@ int noop_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 }
 EXPORT_SYMBOL(noop_fsync);
 
-int noop_set_page_dirty(struct page *page)
+int noop_set_page_dirty(struct address_space *__mapping, struct page *page)
 {
 	/*
 	 * Unlike __set_page_dirty_no_writeback that handles dirty page
@@ -1206,7 +1206,8 @@ EXPORT_SYMBOL(kfree_link);
  * nop .set_page_dirty method so that people can use .page_mkwrite on
  * anon inodes.
  */
-static int anon_set_page_dirty(struct page *page)
+static int anon_set_page_dirty(struct address_space *__mapping,
+			       struct page *page)
 {
 	return 0;
 };
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index b7fe16714d9cc..c4f04b191b72f 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -808,7 +808,7 @@ static void
 nfs_mark_request_dirty(struct nfs_page *req)
 {
 	if (req->wb_page)
-		__set_page_dirty_nobuffers(req->wb_page);
+		__set_page_dirty_nobuffers(MAPPING_NULL, req->wb_page);
 }
 
 /*
@@ -1376,7 +1376,7 @@ int nfs_updatepage(struct file *file, struct page *page,
 	if (status < 0)
 		nfs_set_pageerror(mapping);
 	else
-		__set_page_dirty_nobuffers(page);
+		__set_page_dirty_nobuffers(MAPPING_NULL, page);
 out:
 	dprintk("NFS:       nfs_updatepage returns %d (isize %lld)\n",
 			status, (long long)i_size_read(inode));
@@ -1792,7 +1792,7 @@ static void
 nfs_commit_resched_write(struct nfs_commit_info *cinfo,
 		struct nfs_page *req)
 {
-	__set_page_dirty_nobuffers(req->wb_page);
+	__set_page_dirty_nobuffers(MAPPING_NULL, req->wb_page);
 }
 
 /*
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index 00a22de8e2376..1cedff7bc4e13 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -201,10 +201,11 @@ static int nilfs_writepage(struct address_space *__mapping, struct page *page,
 	return 0;
 }
 
-static int nilfs_set_page_dirty(struct page *page)
+static int nilfs_set_page_dirty(struct address_space *__mapping,
+				struct page *page)
 {
 	struct inode *inode = page->mapping->host;
-	int ret = __set_page_dirty_nobuffers(page);
+	int ret = __set_page_dirty_nobuffers(MAPPING_NULL, page);
 
 	if (page_has_buffers(page)) {
 		unsigned int nr_dirty = 0;
diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
index b175f1330408a..5137b82fb43d5 100644
--- a/fs/nilfs2/page.c
+++ b/fs/nilfs2/page.c
@@ -270,7 +270,7 @@ int nilfs_copy_dirty_pages(struct address_space *dmap,
 				       "found empty page in dat page cache");
 
 		nilfs_copy_page(dpage, page, 1);
-		__set_page_dirty_nobuffers(dpage);
+		__set_page_dirty_nobuffers(MAPPING_NULL, dpage);
 
 		unlock_page(dpage);
 		put_page(dpage);
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index e3726aca28ed6..1a4ea72ad50f1 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -1732,10 +1732,10 @@ static void nilfs_end_page_io(struct page *page, int err)
 
 	if (!err) {
 		if (!nilfs_page_buffers_clean(page))
-			__set_page_dirty_nobuffers(page);
+			__set_page_dirty_nobuffers(MAPPING_NULL, page);
 		ClearPageError(page);
 	} else {
-		__set_page_dirty_nobuffers(page);
+		__set_page_dirty_nobuffers(MAPPING_NULL, page);
 		SetPageError(page);
 	}
 
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
index d920cb780a4ea..3d3a6b6bc6717 100644
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -1749,7 +1749,7 @@ void mark_ntfs_record_dirty(struct page *page, const unsigned int ofs) {
 		set_buffer_dirty(bh);
 	} while ((bh = bh->b_this_page) != head);
 	spin_unlock(&mapping->private_lock);
-	__set_page_dirty_nobuffers(page);
+	__set_page_dirty_nobuffers(MAPPING_NULL, page);
 	if (unlikely(buffers_to_free)) {
 		do {
 			bh = buffers_to_free->b_this_page;
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index f42967b738eb6..c863f62351a62 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -1660,7 +1660,7 @@ static int ntfs_commit_pages_after_write(struct page **pages,
 			 * Put the page on mapping->dirty_pages, but leave its
 			 * buffers' dirty state as-is.
 			 */
-			__set_page_dirty_nobuffers(page);
+			__set_page_dirty_nobuffers(MAPPING_NULL, page);
 			err = 0;
 		} else
 			ntfs_error(vi->i_sb, "Page is not uptodate.  Written "
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 5a34ab78f66cd..9ef4365c07fdd 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -3209,14 +3209,15 @@ static void reiserfs_invalidatepage(struct page *page, unsigned int offset,
 	return;
 }
 
-static int reiserfs_set_page_dirty(struct page *page)
+static int reiserfs_set_page_dirty(struct address_space *__mapping,
+				   struct page *page)
 {
 	struct inode *inode = page->mapping->host;
 	if (reiserfs_file_data_log(inode)) {
 		SetPageChecked(page);
-		return __set_page_dirty_nobuffers(page);
+		return __set_page_dirty_nobuffers(MAPPING_NULL, page);
 	}
-	return __set_page_dirty_buffers(page);
+	return __set_page_dirty_buffers(MAPPING_NULL, page);
 }
 
 /*
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index fcc6c307313f2..5af8c311d38f0 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -572,7 +572,7 @@ static int ubifs_write_end(struct file *file, struct address_space *mapping,
 	if (!PagePrivate(page)) {
 		SetPagePrivate(page);
 		atomic_long_inc(&c->dirty_pg_cnt);
-		__set_page_dirty_nobuffers(page);
+		__set_page_dirty_nobuffers(MAPPING_NULL, page);
 	}
 
 	if (appending) {
@@ -1446,13 +1446,14 @@ static ssize_t ubifs_write_iter(struct kiocb *iocb, struct iov_iter *from)
 	return generic_file_write_iter(iocb, from);
 }
 
-static int ubifs_set_page_dirty(struct page *page)
+static int ubifs_set_page_dirty(struct address_space *__mapping,
+				struct page *page)
 {
 	int ret;
 	struct inode *inode = page->mapping->host;
 	struct ubifs_info *c = inode->i_sb->s_fs_info;
 
-	ret = __set_page_dirty_nobuffers(page);
+	ret = __set_page_dirty_nobuffers(MAPPING_NULL, page);
 	/*
 	 * An attempt to dirty a page without budgeting for it - should not
 	 * happen.
@@ -1570,7 +1571,7 @@ static vm_fault_t ubifs_vm_page_mkwrite(struct vm_fault *vmf)
 			ubifs_convert_page_budget(c);
 		SetPagePrivate(page);
 		atomic_long_inc(&c->dirty_pg_cnt);
-		__set_page_dirty_nobuffers(page);
+		__set_page_dirty_nobuffers(MAPPING_NULL, page);
 	}
 
 	if (update_time) {
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 6b47f94378c5a..07fe6d613ed9f 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -396,7 +396,8 @@ __bread(struct block_device *bdev, sector_t block, unsigned size)
 	return __bread_gfp(bdev, block, size, __GFP_MOVABLE);
 }
 
-extern int __set_page_dirty_buffers(struct page *page);
+extern int __set_page_dirty_buffers(struct address_space *__mapping,
+				    struct page *page);
 
 #else /* CONFIG_BLOCK */
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index acd51e3880762..0b1e2c231dcf8 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -378,7 +378,7 @@ struct address_space_operations {
 	int (*writepages)(struct address_space *, struct writeback_control *);
 
 	/* Set a page dirty.  Return true if this dirtied it */
-	int (*set_page_dirty)(struct page *page);
+	int (*set_page_dirty)(struct address_space *, struct page *page);
 
 	/*
 	 * Reads in the requested pages. Unlike ->readpage(), this is
@@ -3223,7 +3223,8 @@ extern int simple_rename(struct inode *, struct dentry *,
 extern void simple_recursive_removal(struct dentry *,
                               void (*callback)(struct dentry *));
 extern int noop_fsync(struct file *, loff_t, loff_t, int);
-extern int noop_set_page_dirty(struct page *page);
+extern int noop_set_page_dirty(struct address_space *__mapping,
+			       struct page *page);
 extern void noop_invalidatepage(struct page *page, unsigned int offset,
 		unsigned int length);
 extern ssize_t noop_direct_IO(struct kiocb *iocb, struct iov_iter *iter);
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 4d1d3c3469e9a..781f22ee0a53b 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -156,7 +156,7 @@ ssize_t iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *from,
 		const struct iomap_ops *ops);
 int iomap_readpage(struct page *page, const struct iomap_ops *ops);
 void iomap_readahead(struct readahead_control *, const struct iomap_ops *ops);
-int iomap_set_page_dirty(struct page *page);
+int iomap_set_page_dirty(struct address_space *__mapping, struct page *page);
 int iomap_is_partially_uptodate(struct page *page, unsigned long from,
 		unsigned long count);
 int iomap_releasepage(struct page *page, gfp_t gfp_mask);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index d165961c58c45..1bf229c4176bc 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1796,8 +1796,10 @@ extern void do_invalidatepage(struct page *page, unsigned int offset,
 			      unsigned int length);
 
 void __set_page_dirty(struct page *, struct address_space *, int warn);
-int __set_page_dirty_nobuffers(struct page *page);
-int __set_page_dirty_no_writeback(struct page *page);
+int __set_page_dirty_nobuffers(struct address_space *__mapping,
+			       struct page *page);
+int __set_page_dirty_no_writeback(struct address_space *__mapping,
+				  struct page *page);
 int redirty_page_for_writepage(struct writeback_control *wbc,
 				struct page *page);
 void account_page_dirtied(struct page *page, struct address_space *mapping);
diff --git a/include/linux/swap.h b/include/linux/swap.h
index f2355fca8b38b..b0316c44869d2 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -397,7 +397,8 @@ extern int swap_writepage(struct address_space *__mapping, struct page *page,
 extern void end_swap_bio_write(struct bio *bio);
 extern int __swap_writepage(struct page *page, struct writeback_control *wbc,
 	bio_end_io_t end_write_func);
-extern int swap_set_page_dirty(struct page *page);
+extern int swap_set_page_dirty(struct address_space *__mapping,
+			       struct page *page);
 
 int add_swap_extent(struct swap_info_struct *sis, unsigned long start_page,
 		unsigned long nr_pages, sector_t start_block);
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index de15d2febc5ae..78ead3581040e 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -2403,7 +2403,8 @@ EXPORT_SYMBOL(write_one_page);
 /*
  * For address_spaces which do not use buffers nor write back.
  */
-int __set_page_dirty_no_writeback(struct page *page)
+int __set_page_dirty_no_writeback(struct address_space *__mapping,
+				  struct page *page)
 {
 	if (!PageDirty(page))
 		return !TestSetPageDirty(page);
@@ -2470,7 +2471,8 @@ void account_page_cleaned(struct page *page, struct address_space *mapping,
  * hold the page lock, but e.g. zap_pte_range() calls with the page mapped and
  * the pte lock held, which also locks out truncation.
  */
-int __set_page_dirty_nobuffers(struct page *page)
+int __set_page_dirty_nobuffers(struct address_space *__mapping,
+			       struct page *page)
 {
 	lock_page_memcg(page);
 	if (!TestSetPageDirty(page)) {
@@ -2537,7 +2539,7 @@ int redirty_page_for_writepage(struct writeback_control *wbc, struct page *page)
 	int ret;
 
 	wbc->pages_skipped++;
-	ret = __set_page_dirty_nobuffers(page);
+	ret = __set_page_dirty_nobuffers(MAPPING_NULL, page);
 	account_page_redirty(page);
 	return ret;
 }
@@ -2560,7 +2562,7 @@ int set_page_dirty(struct page *page)
 
 	page = compound_head(page);
 	if (likely(mapping)) {
-		int (*spd)(struct page *) = mapping->a_ops->set_page_dirty;
+		int (*spd)(struct address_space *, struct page *) = mapping->a_ops->set_page_dirty;
 		/*
 		 * readahead/lru_deactivate_page could remain
 		 * PG_readahead/PG_reclaim due to race with end_page_writeback
@@ -2577,7 +2579,7 @@ int set_page_dirty(struct page *page)
 		if (!spd)
 			spd = __set_page_dirty_buffers;
 #endif
-		return (*spd)(page);
+		return (*spd)(MAPPING_NULL, page);
 	}
 	if (!PageDirty(page)) {
 		if (!TestSetPageDirty(page))
diff --git a/mm/page_io.c b/mm/page_io.c
index 067159b23ee54..60617b6420c01 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -452,7 +452,7 @@ int swap_readpage(struct page *page, bool synchronous)
 	return ret;
 }
 
-int swap_set_page_dirty(struct page *page)
+int swap_set_page_dirty(struct address_space *__mapping, struct page *page)
 {
 	struct swap_info_struct *sis = page_swap_info(page);
 
@@ -460,8 +460,8 @@ int swap_set_page_dirty(struct page *page)
 		struct address_space *mapping = sis->swap_file->f_mapping;
 
 		VM_BUG_ON_PAGE(!PageSwapCache(page), page);
-		return mapping->a_ops->set_page_dirty(page);
+		return mapping->a_ops->set_page_dirty(MAPPING_NULL, page);
 	} else {
-		return __set_page_dirty_no_writeback(page);
+		return __set_page_dirty_no_writeback(MAPPING_NULL, page);
 	}
 }
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 07/14] mm: add struct address_space to invalidatepage() callback
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
                   ` (4 preceding siblings ...)
  2020-10-07  1:05 ` [PATCH 06/14] mm: add struct address_space to set_page_dirty() callback jglisse
@ 2020-10-07  1:05 ` jglisse
  2020-10-07  1:05 ` [PATCH 08/14] mm: add struct address_space to releasepage() callback jglisse
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

This is part of patchset to remove dependency on struct page.mapping
field so that we can temporarily update it to point to a special
structure tracking temporary page state (note that original mapping
pointer is preserved and can still be accessed but at a cost).

Add struct address_space to invalidatepage() callback arguments.

Note that this patch does not make use of the new argument, nor does
it use a valid one at call site (by default this patch just use NULL
for new argument value).

Use following script (from root of linux kernel tree):

./that-script.sh that-semantic-patch.spatch

%<--------------------------------------------------------------------
#!/bin/sh
spatch_file=$1

echo PART1 ===========================================================

# P1 find callback functions name
spatch  --dir . --no-includes -D part1 --sp-file $spatch_file

echo PART2 ===========================================================

# P2 change callback function prototype
cat /tmp/unicorn-functions | sort | uniq | while read func ; do
    for file in $( git grep -l $func -- '*.[ch]' ) ; do
        echo $file
        spatch --no-includes --in-place -D part2 \
               -D fn=$func --sp-file $spatch_file $file
    done
done

echo PART 3 ==========================================================

# P3 find all function which call the callback
spatch --dir . --include-headers -D part3 --sp-file $spatch_file

echo PART 4===========================================================

# P4 change all funcitons which call the callback
cat /tmp/unicorn-files | sort | uniq | while read file ; do
    echo $file
    spatch --no-includes --in-place -D part4 \
           --sp-file $spatch_file $file
done
-------------------------------------------------------------------->%

With the following semantic patch:

%<--------------------------------------------------------------------
virtual part1, part2, part3, part4

// ----------------------------------------------------------------------------
// Part 1 is grepping all function that are use as callback for invalidatepage.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part1@
@@
file=open('/tmp/unicorn-functions', 'w')
file.close()

// match function name use as a callback
@p1r2 depends on part1@
identifier I1, FN;
@@
struct address_space_operations I1 = {..., .invalidatepage = FN, ...};

@script:python p1r3 depends on p1r2@
funcname << p1r2.FN;
@@
if funcname != "NULL":
  file=open('/tmp/unicorn-functions', 'a')
  file.write(funcname + '\n')
  file.close()

// -------------------------------------------------------------------
// Part 2 modify callback

// Add address_space argument to the function (invalidatepage callback one)
@p2r1 depends on part2@
identifier virtual.fn;
identifier I1, I2, I3;
type T1, T2, T3;
@@
void fn(
+struct address_space *__mapping,
T1 I1, T2 I2, T3 I3) { ... }

@p2r2 depends on part2@
identifier virtual.fn;
identifier I1, I2, I3;
type T1, T2, T3;
@@
void fn(
+struct address_space *__mapping,
T1 I1, T2 I2, T3 I3);

@p2r3 depends on part2@
identifier virtual.fn;
expression E1, E2, E3;
@@
fn(
+MAPPING_NULL,
E1, E2, E3)

// ----------------------------------------------------------------------------
// Part 3 is grepping all function that are use the callback for invalidatepage.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part3@
@@
file=open('/tmp/unicorn-files', 'w')
file.write("./include/linux/pagemap.h\n")
file.write("./include/linux/mm.h\n")
file.write("./include/linux/fs.h\n")
file.write("./mm/readahead.c\n")
file.write("./mm/truncate.c\n")
file.write("./mm/filemap.c\n")
file.close()

@p3r1 depends on part3 exists@
expression E1, E2, E3, E4;
identifier FN;
position P;
@@
FN@P(...) {...
(
E1.a_ops->invalidatepage(E2, E3, E4)
|
E1->a_ops->invalidatepage(E2, E3, E4)
)
...}

@script:python p3r2 depends on p3r1@
P << p3r1.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

// -------------------------------------------------------------------
// Part 4 generic modification
@p4r1 depends on part4@
@@
struct address_space_operations { ... void (*invalidatepage)(
+struct address_space *,
struct page *, ...); ... };

@p4r2 depends on part4@
expression E1, E2, E3, E4;
@@
E1.a_ops->invalidatepage(
+MAPPING_NULL,
E2, E3, E4)

@p4r3 depends on part4@
expression E1, E2, E3, E4;
@@
E1->a_ops->invalidatepage(
+MAPPING_NULL,
E2, E3, E4)

@p4r4 depends on part4 exists@
identifier I1, FN;
expression E1;
@@
FN (...) {...
void (*I1)(struct page *, unsigned int, unsigned int);
...
I1 = E1->a_ops->invalidatepage;
...}

@p4r5 depends on p4r4 exists@
expression E1, E2, E3;
identifier I1, p4r4.FN;
@@
FN(...) {...
void (*I1)(
+struct address_space *,
struct page *, unsigned int, unsigned int);
...
 (*I1)(
+MAPPING_NULL,
E1, E2, E3);
...}

@p4r6 depends on part4@
expression E1, E2, E3;
@@
{...
-void (*invalidatepage)(struct page *, unsigned int, unsigned int);
+void (*invalidatepage)(struct address_space *, struct page *, unsigned int, unsigned int);
...
 (*invalidatepage)(
+MAPPING_NULL,
E1, E2, E3);
...}
-------------------------------------------------------------------->%

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>
---
 fs/9p/vfs_addr.c            |  3 ++-
 fs/afs/dir.c                |  6 ++++--
 fs/afs/file.c               |  6 ++++--
 fs/btrfs/disk-io.c          |  3 ++-
 fs/btrfs/extent_io.c        |  3 ++-
 fs/btrfs/inode.c            |  3 ++-
 fs/buffer.c                 |  3 ++-
 fs/ceph/addr.c              | 12 ++++++++----
 fs/cifs/file.c              |  3 ++-
 fs/erofs/super.c            |  3 ++-
 fs/ext4/inode.c             | 17 +++++++++++------
 fs/f2fs/data.c              |  3 ++-
 fs/f2fs/f2fs.h              |  5 +++--
 fs/gfs2/aops.c              |  6 ++++--
 fs/iomap/buffered-io.c      |  3 ++-
 fs/jfs/jfs_metapage.c       |  3 ++-
 fs/libfs.c                  |  5 +++--
 fs/nfs/file.c               |  3 ++-
 fs/ntfs/aops.c              |  2 +-
 fs/orangefs/inode.c         |  7 ++++---
 fs/reiserfs/inode.c         |  3 ++-
 fs/ubifs/file.c             |  3 ++-
 fs/xfs/xfs_aops.c           |  2 +-
 include/linux/buffer_head.h |  3 ++-
 include/linux/fs.h          |  8 +++++---
 include/linux/iomap.h       |  5 +++--
 mm/truncate.c               |  5 +++--
 27 files changed, 82 insertions(+), 46 deletions(-)

diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c
index c7a8037df9fcf..357f2e5049c48 100644
--- a/fs/9p/vfs_addr.c
+++ b/fs/9p/vfs_addr.c
@@ -137,7 +137,8 @@ static int v9fs_release_page(struct page *page, gfp_t gfp)
  * @offset: offset in the page
  */
 
-static void v9fs_invalidate_page(struct page *page, unsigned int offset,
+static void v9fs_invalidate_page(struct address_space *__mapping,
+				 struct page *page, unsigned int offset,
 				 unsigned int length)
 {
 	/*
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index ebcf074bcaaa2..d77c13c213d2d 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -41,7 +41,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
 		      struct inode *new_dir, struct dentry *new_dentry,
 		      unsigned int flags);
 static int afs_dir_releasepage(struct page *page, gfp_t gfp_flags);
-static void afs_dir_invalidatepage(struct page *page, unsigned int offset,
+static void afs_dir_invalidatepage(struct address_space *__mapping,
+				   struct page *page, unsigned int offset,
 				   unsigned int length);
 
 static int afs_dir_set_page_dirty(struct address_space *__mapping,
@@ -1990,7 +1991,8 @@ static int afs_dir_releasepage(struct page *page, gfp_t gfp_flags)
  * - release a page and clean up its private data if offset is 0 (indicating
  *   the entire page)
  */
-static void afs_dir_invalidatepage(struct page *page, unsigned int offset,
+static void afs_dir_invalidatepage(struct address_space *__mapping,
+				   struct page *page, unsigned int offset,
 				   unsigned int length)
 {
 	struct afs_vnode *dvnode = AFS_FS_I(page->mapping->host);
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 908f9e3196251..43edfa65c7ac7 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -19,7 +19,8 @@
 static int afs_file_mmap(struct file *file, struct vm_area_struct *vma);
 static int afs_readpage(struct file *file, struct address_space *__mapping,
 			struct page *page);
-static void afs_invalidatepage(struct page *page, unsigned int offset,
+static void afs_invalidatepage(struct address_space *__mapping,
+			       struct page *page, unsigned int offset,
 			       unsigned int length);
 static int afs_releasepage(struct page *page, gfp_t gfp_flags);
 
@@ -607,7 +608,8 @@ static int afs_readpages(struct file *file, struct address_space *mapping,
  * - release a page and clean up its private data if offset is 0 (indicating
  *   the entire page)
  */
-static void afs_invalidatepage(struct page *page, unsigned int offset,
+static void afs_invalidatepage(struct address_space *__mapping,
+			       struct page *page, unsigned int offset,
 			       unsigned int length)
 {
 	struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 7f548f9f5ace1..d57d0a6dd2621 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -965,7 +965,8 @@ static int btree_releasepage(struct page *page, gfp_t gfp_flags)
 	return try_release_extent_buffer(page);
 }
 
-static void btree_invalidatepage(struct page *page, unsigned int offset,
+static void btree_invalidatepage(struct address_space *__mapping,
+				 struct page *page, unsigned int offset,
 				 unsigned int length)
 {
 	struct extent_io_tree *tree;
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 02569dffe8e14..9877f1222b318 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -3632,7 +3632,8 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
 	pg_offset = offset_in_page(i_size);
 	if (page->index > end_index ||
 	   (page->index == end_index && !pg_offset)) {
-		page->mapping->a_ops->invalidatepage(page, 0, PAGE_SIZE);
+		page->mapping->a_ops->invalidatepage(MAPPING_NULL, page, 0,
+						     PAGE_SIZE);
 		unlock_page(page);
 		return 0;
 	}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 9f35648ba06d8..062886fc0e750 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -8091,7 +8091,8 @@ static int btrfs_migratepage(struct address_space *mapping,
 }
 #endif
 
-static void btrfs_invalidatepage(struct page *page, unsigned int offset,
+static void btrfs_invalidatepage(struct address_space *__mapping,
+				 struct page *page, unsigned int offset,
 				 unsigned int length)
 {
 	struct inode *inode = page->mapping->host;
diff --git a/fs/buffer.c b/fs/buffer.c
index 6fb6cf497feb8..1f0f72b76fc2a 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -1499,7 +1499,8 @@ static void discard_buffer(struct buffer_head * bh)
  * point.  Because the caller is about to free (and possibly reuse) those
  * blocks on-disk.
  */
-void block_invalidatepage(struct page *page, unsigned int offset,
+void block_invalidatepage(struct address_space *__mapping, struct page *page,
+			  unsigned int offset,
 			  unsigned int length)
 {
 	struct buffer_head *head, *bh, *next;
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index b2b2c8f8118e4..ed555b0d48bfa 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -140,7 +140,8 @@ static int ceph_set_page_dirty(struct address_space *__mapping,
  * dirty page counters appropriately.  Only called if there is private
  * data on the page.
  */
-static void ceph_invalidatepage(struct page *page, unsigned int offset,
+static void ceph_invalidatepage(struct address_space *__mapping,
+				struct page *page, unsigned int offset,
 				unsigned int length)
 {
 	struct inode *inode;
@@ -708,7 +709,8 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
 	/* is this a partial page at end of file? */
 	if (page_off >= ceph_wbc.i_size) {
 		dout("%p page eof %llu\n", page, ceph_wbc.i_size);
-		page->mapping->a_ops->invalidatepage(page, 0, PAGE_SIZE);
+		page->mapping->a_ops->invalidatepage(MAPPING_NULL, page, 0,
+						     PAGE_SIZE);
 		return 0;
 	}
 
@@ -1004,8 +1006,10 @@ static int ceph_writepages_start(struct address_space *mapping,
 				if ((ceph_wbc.size_stable ||
 				    page_offset(page) >= i_size_read(inode)) &&
 				    clear_page_dirty_for_io(page))
-					mapping->a_ops->invalidatepage(page,
-								0, PAGE_SIZE);
+					mapping->a_ops->invalidatepage(MAPPING_NULL,
+								       page,
+								       0,
+								       PAGE_SIZE);
 				unlock_page(page);
 				continue;
 			}
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index ca7df2a2dde0f..84cb64821036c 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -4702,7 +4702,8 @@ static int cifs_release_page(struct page *page, gfp_t gfp)
 	return cifs_fscache_release_page(page, gfp);
 }
 
-static void cifs_invalidate_page(struct page *page, unsigned int offset,
+static void cifs_invalidate_page(struct address_space *__mapping,
+				 struct page *page, unsigned int offset,
 				 unsigned int length)
 {
 	struct cifsInodeInfo *cifsi = CIFS_I(page->mapping->host);
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index ddaa516c008af..3c0e10d1b4e19 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -295,7 +295,8 @@ static int erofs_managed_cache_releasepage(struct page *page, gfp_t gfp_mask)
 	return ret;
 }
 
-static void erofs_managed_cache_invalidatepage(struct page *page,
+static void erofs_managed_cache_invalidatepage(struct address_space *__mapping,
+					       struct page *page,
 					       unsigned int offset,
 					       unsigned int length)
 {
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 528eec0b02bf2..27b8d57349d88 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -135,7 +135,8 @@ static inline int ext4_begin_ordered_truncate(struct inode *inode,
 						   new_size);
 }
 
-static void ext4_invalidatepage(struct page *page, unsigned int offset,
+static void ext4_invalidatepage(struct address_space *__mapping,
+				struct page *page, unsigned int offset,
 				unsigned int length);
 static int __ext4_journalled_writepage(struct page *page, unsigned int len);
 static int ext4_bh_delay_or_unwritten(handle_t *handle, struct buffer_head *bh);
@@ -1572,7 +1573,8 @@ static void mpage_release_unused_pages(struct mpage_da_data *mpd,
 			if (invalidate) {
 				if (page_mapped(page))
 					clear_page_dirty_for_io(page);
-				block_invalidatepage(page, 0, PAGE_SIZE);
+				block_invalidatepage(MAPPING_NULL, page, 0,
+						     PAGE_SIZE);
 				ClearPageUptodate(page);
 			}
 			unlock_page(page);
@@ -1981,7 +1983,8 @@ static int ext4_writepage(struct address_space *__mapping, struct page *page,
 	bool keep_towrite = false;
 
 	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) {
-		inode->i_mapping->a_ops->invalidatepage(page, 0, PAGE_SIZE);
+		inode->i_mapping->a_ops->invalidatepage(MAPPING_NULL, page, 0,
+							PAGE_SIZE);
 		unlock_page(page);
 		return -EIO;
 	}
@@ -3242,7 +3245,8 @@ static void ext4_readahead(struct readahead_control *rac)
 	ext4_mpage_readpages(inode, rac, NULL);
 }
 
-static void ext4_invalidatepage(struct page *page, unsigned int offset,
+static void ext4_invalidatepage(struct address_space *__mapping,
+				struct page *page, unsigned int offset,
 				unsigned int length)
 {
 	trace_ext4_invalidatepage(page, offset, length);
@@ -3250,7 +3254,7 @@ static void ext4_invalidatepage(struct page *page, unsigned int offset,
 	/* No journalling happens on data buffers when this function is used */
 	WARN_ON(page_has_buffers(page) && buffer_jbd(page_buffers(page)));
 
-	block_invalidatepage(page, offset, length);
+	block_invalidatepage(MAPPING_NULL, page, offset, length);
 }
 
 static int __ext4_journalled_invalidatepage(struct page *page,
@@ -3271,7 +3275,8 @@ static int __ext4_journalled_invalidatepage(struct page *page,
 }
 
 /* Wrapper for aops... */
-static void ext4_journalled_invalidatepage(struct page *page,
+static void ext4_journalled_invalidatepage(struct address_space *__mapping,
+					   struct page *page,
 					   unsigned int offset,
 					   unsigned int length)
 {
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 12350175133aa..b13e430e62435 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -3690,7 +3690,8 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
 	return err;
 }
 
-void f2fs_invalidate_page(struct page *page, unsigned int offset,
+void f2fs_invalidate_page(struct address_space *__mapping, struct page *page,
+							unsigned int offset,
 							unsigned int length)
 {
 	struct inode *inode = page->mapping->host;
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index d9e52a7f3702f..eb6f9aa4007c6 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3469,8 +3469,9 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
 				struct writeback_control *wbc,
 				enum iostat_type io_type,
 				int compr_blocks);
-void f2fs_invalidate_page(struct page *page, unsigned int offset,
-			unsigned int length);
+void f2fs_invalidate_page(struct address_space *__mapping, struct page *page,
+			  unsigned int offset,
+			  unsigned int length);
 int f2fs_release_page(struct page *page, gfp_t wait);
 #ifdef CONFIG_MIGRATION
 int f2fs_migrate_page(struct address_space *mapping, struct page *newpage,
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 8911771f95c5c..0c6d2e99a5243 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -103,7 +103,8 @@ static int gfs2_writepage(struct address_space *__mapping, struct page *page,
 	/* Is the page fully outside i_size? (truncate in progress) */
 	offset = i_size & (PAGE_SIZE-1);
 	if (page->index > end_index || (page->index == end_index && !offset)) {
-		page->mapping->a_ops->invalidatepage(page, 0, PAGE_SIZE);
+		page->mapping->a_ops->invalidatepage(MAPPING_NULL, page, 0,
+						     PAGE_SIZE);
 		goto out;
 	}
 
@@ -680,7 +681,8 @@ static void gfs2_discard(struct gfs2_sbd *sdp, struct buffer_head *bh)
 	unlock_buffer(bh);
 }
 
-static void gfs2_invalidatepage(struct page *page, unsigned int offset,
+static void gfs2_invalidatepage(struct address_space *__mapping,
+				struct page *page, unsigned int offset,
 				unsigned int length)
 {
 	struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host);
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 26f7fe7c80adc..b94729b7088a7 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -494,7 +494,8 @@ iomap_releasepage(struct page *page, gfp_t gfp_mask)
 EXPORT_SYMBOL_GPL(iomap_releasepage);
 
 void
-iomap_invalidatepage(struct page *page, unsigned int offset, unsigned int len)
+iomap_invalidatepage(struct address_space *__mapping, struct page *page,
+		     unsigned int offset, unsigned int len)
 {
 	trace_iomap_invalidatepage(page->mapping->host, offset, len);
 
diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
index a6e48e733d3a6..5be751fa11e0b 100644
--- a/fs/jfs/jfs_metapage.c
+++ b/fs/jfs/jfs_metapage.c
@@ -557,7 +557,8 @@ static int metapage_releasepage(struct page *page, gfp_t gfp_mask)
 	return ret;
 }
 
-static void metapage_invalidatepage(struct page *page, unsigned int offset,
+static void metapage_invalidatepage(struct address_space *__mapping,
+				    struct page *page, unsigned int offset,
 				    unsigned int length)
 {
 	BUG_ON(offset || length < PAGE_SIZE);
diff --git a/fs/libfs.c b/fs/libfs.c
index 899feec2eb683..f4b6db18e62b5 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1172,8 +1172,9 @@ int noop_set_page_dirty(struct address_space *__mapping, struct page *page)
 }
 EXPORT_SYMBOL_GPL(noop_set_page_dirty);
 
-void noop_invalidatepage(struct page *page, unsigned int offset,
-		unsigned int length)
+void noop_invalidatepage(struct address_space *__mapping, struct page *page,
+			 unsigned int offset,
+			 unsigned int length)
 {
 	/*
 	 * There is no page cache to invalidate in the dax case, however
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 02e2112d77f86..381288d686386 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -405,7 +405,8 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
  * - Called if either PG_private or PG_fscache is set on the page
  * - Caller holds page lock
  */
-static void nfs_invalidate_page(struct page *page, unsigned int offset,
+static void nfs_invalidate_page(struct address_space *__mapping,
+				struct page *page, unsigned int offset,
 				unsigned int length)
 {
 	dfprintk(PAGECACHE, "NFS: invalidate_page(%p, %u, %u)\n",
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
index 3d3a6b6bc6717..a9fe68e4c89b5 100644
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -1356,7 +1356,7 @@ static int ntfs_writepage(struct address_space *__mapping, struct page *page,
 		 * The page may have dirty, unmapped buffers.  Make them
 		 * freeable here, so the page does not leak.
 		 */
-		block_invalidatepage(page, 0, PAGE_SIZE);
+		block_invalidatepage(MAPPING_NULL, page, 0, PAGE_SIZE);
 		unlock_page(page);
 		ntfs_debug("Write outside i_size - truncated?");
 		return 0;
diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c
index f463cfb435292..6ea0ec45754dc 100644
--- a/fs/orangefs/inode.c
+++ b/fs/orangefs/inode.c
@@ -448,9 +448,10 @@ static int orangefs_write_end(struct file *file, struct address_space *mapping,
 	return copied;
 }
 
-static void orangefs_invalidatepage(struct page *page,
-				 unsigned int offset,
-				 unsigned int length)
+static void orangefs_invalidatepage(struct address_space *__mapping,
+				    struct page *page,
+				    unsigned int offset,
+				    unsigned int length)
 {
 	struct orangefs_write_range *wr;
 	wr = (struct orangefs_write_range *)page_private(page);
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 9ef4365c07fdd..d35c03a7d3f5b 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -3156,7 +3156,8 @@ static int invalidatepage_can_drop(struct inode *inode, struct buffer_head *bh)
 }
 
 /* clm -- taken from fs/buffer.c:block_invalidate_page */
-static void reiserfs_invalidatepage(struct page *page, unsigned int offset,
+static void reiserfs_invalidatepage(struct address_space *__mapping,
+				    struct page *page, unsigned int offset,
 				    unsigned int length)
 {
 	struct buffer_head *head, *bh, *next;
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 5af8c311d38f0..11ed42c4859f0 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1288,7 +1288,8 @@ int ubifs_setattr(struct dentry *dentry, struct iattr *attr)
 	return err;
 }
 
-static void ubifs_invalidatepage(struct page *page, unsigned int offset,
+static void ubifs_invalidatepage(struct address_space *__mapping,
+				 struct page *page, unsigned int offset,
 				 unsigned int length)
 {
 	struct inode *inode = page->mapping->host;
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 6827f6226499a..24cd33c5f3466 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -548,7 +548,7 @@ xfs_discard_page(
 	if (error && !XFS_FORCED_SHUTDOWN(mp))
 		xfs_alert(mp, "page discard unable to remove delalloc mapping.");
 out_invalidate:
-	iomap_invalidatepage(page, 0, PAGE_SIZE);
+	iomap_invalidatepage(MAPPING_NULL, page, 0, PAGE_SIZE);
 }
 
 static const struct iomap_writeback_ops xfs_writeback_ops = {
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 07fe6d613ed9f..0902142e93f0d 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -214,7 +214,8 @@ extern int buffer_heads_over_limit;
  * Generic address_space_operations implementations for buffer_head-backed
  * address_spaces.
  */
-void block_invalidatepage(struct page *page, unsigned int offset,
+void block_invalidatepage(struct address_space *__mapping, struct page *page,
+			  unsigned int offset,
 			  unsigned int length);
 int block_write_full_page(struct page *page, get_block_t *get_block,
 				struct writeback_control *wbc);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 0b1e2c231dcf8..b471e82546001 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -397,7 +397,8 @@ struct address_space_operations {
 
 	/* Unfortunately this kludge is needed for FIBMAP. Don't use it */
 	sector_t (*bmap)(struct address_space *, sector_t);
-	void (*invalidatepage) (struct page *, unsigned int, unsigned int);
+	void (*invalidatepage) (struct address_space *, struct page *,
+				unsigned int, unsigned int);
 	int (*releasepage) (struct page *, gfp_t);
 	void (*freepage)(struct page *);
 	ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter);
@@ -3225,8 +3226,9 @@ extern void simple_recursive_removal(struct dentry *,
 extern int noop_fsync(struct file *, loff_t, loff_t, int);
 extern int noop_set_page_dirty(struct address_space *__mapping,
 			       struct page *page);
-extern void noop_invalidatepage(struct page *page, unsigned int offset,
-		unsigned int length);
+extern void noop_invalidatepage(struct address_space *__mapping,
+				struct page *page, unsigned int offset,
+				unsigned int length);
 extern ssize_t noop_direct_IO(struct kiocb *iocb, struct iov_iter *iter);
 extern int simple_empty(struct dentry *);
 extern int simple_readpage(struct file *file, struct address_space *__mapping,
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 781f22ee0a53b..45f23d2268365 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -160,8 +160,9 @@ int iomap_set_page_dirty(struct address_space *__mapping, struct page *page);
 int iomap_is_partially_uptodate(struct page *page, unsigned long from,
 		unsigned long count);
 int iomap_releasepage(struct page *page, gfp_t gfp_mask);
-void iomap_invalidatepage(struct page *page, unsigned int offset,
-		unsigned int len);
+void iomap_invalidatepage(struct address_space *__mapping, struct page *page,
+			  unsigned int offset,
+			  unsigned int len);
 #ifdef CONFIG_MIGRATION
 int iomap_migrate_page(struct address_space *mapping, struct page *newpage,
 		struct page *page, enum migrate_mode mode);
diff --git a/mm/truncate.c b/mm/truncate.c
index dd9ebc1da3566..e26b232b66c01 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -152,7 +152,8 @@ static int invalidate_exceptional_entry2(struct address_space *mapping,
 void do_invalidatepage(struct page *page, unsigned int offset,
 		       unsigned int length)
 {
-	void (*invalidatepage)(struct page *, unsigned int, unsigned int);
+	void (*invalidatepage)(struct address_space *, struct page *,
+		               unsigned int, unsigned int);
 
 	invalidatepage = page->mapping->a_ops->invalidatepage;
 #ifdef CONFIG_BLOCK
@@ -160,7 +161,7 @@ void do_invalidatepage(struct page *page, unsigned int offset,
 		invalidatepage = block_invalidatepage;
 #endif
 	if (invalidatepage)
-		(*invalidatepage)(page, offset, length);
+		(*invalidatepage)(MAPPING_NULL, page, offset, length);
 }
 
 /*
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 08/14] mm: add struct address_space to releasepage() callback
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
                   ` (5 preceding siblings ...)
  2020-10-07  1:05 ` [PATCH 07/14] mm: add struct address_space to invalidatepage() callback jglisse
@ 2020-10-07  1:05 ` jglisse
  2020-10-07  1:05 ` [PATCH 09/14] mm: add struct address_space to freepage() callback jglisse
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

This is part of patchset to remove dependency on struct page.mapping
field so that we can temporarily update it to point to a special
structure tracking temporary page state (note that original mapping
pointer is preserved and can still be accessed but at a cost).

Add struct address_space to releasepage() callback arguments.

Note that this patch does not make use of the new argument, nor does
it use a valid one at call site (by default this patch just use NULL
for new argument value).

Use following script (from root of linux kernel tree):

./that-script.sh that-semantic-patch.spatch

%<--------------------------------------------------------------------
#!/bin/sh
spatch_file=$1

echo PART1 ===========================================================

# P1 find callback functions name
spatch  --dir . --no-includes -D part1 --sp-file $spatch_file

echo PART2 ===========================================================

# P2 change callback function prototype
cat /tmp/unicorn-functions | sort | uniq | while read func ; do
    for file in $( git grep -l $func -- '*.[ch]' ) ; do
        echo $file
        spatch --no-includes --in-place -D part2 \
               -D fn=$func --sp-file $spatch_file $file
    done
done

echo PART 3 ==========================================================

# P3 find all function which call the callback
spatch --dir . --include-headers -D part3 --sp-file $spatch_file

echo PART 4===========================================================

# P4 change all funcitons which call the callback
cat /tmp/unicorn-files | sort | uniq | while read file ; do
    echo $file
    spatch --no-includes --in-place -D part4 \
           --sp-file $spatch_file $file
done
-------------------------------------------------------------------->%

With the following semantic patch:

%<--------------------------------------------------------------------
virtual part1, part2, part3, part4

// ----------------------------------------------------------------------------
// Part 1 is grepping all function that are use as callback for releasepage.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part1@
@@
file=open('/tmp/unicorn-functions', 'w')
file.close()

// match function name use as a callback
@p1r2 depends on part1@
identifier I1, FN;
@@
struct address_space_operations I1 = {..., .releasepage = FN, ...};

@script:python p1r3 depends on p1r2@
funcname << p1r2.FN;
@@
if funcname != "NULL":
  file=open('/tmp/unicorn-functions', 'a')
  file.write(funcname + '\n')
  file.close()

// -------------------------------------------------------------------
// Part 2 modify callback

// Add address_space argument to the function (releasepage callback one)
@p2r1 depends on part2@
identifier virtual.fn;
identifier I1, I2;
type T1, T2;
@@
int fn(
+struct address_space *__mapping,
T1 I1, T2 I2) { ... }

@p2r2 depends on part2@
identifier virtual.fn;
identifier I1, I2;
type T1, T2;
@@
int fn(
+struct address_space *__mapping,
T1 I1, T2 I2);

@p2r3 depends on part2@
identifier virtual.fn;
expression E1, E2;
@@
fn(
+MAPPING_NULL,
E1, E2)

// ----------------------------------------------------------------------------
// Part 3 is grepping all function that are use the callback for releasepage.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part3@
@@
file=open('/tmp/unicorn-files', 'w')
file.write("./include/linux/pagemap.h\n")
file.write("./include/linux/mm.h\n")
file.write("./include/linux/fs.h\n")
file.write("./mm/readahead.c\n")
file.write("./mm/filemap.c\n")
file.close()

@p3r1 depends on part3 exists@
expression E1, E2, E3;
identifier FN;
position P;
@@
FN@P(...) {...
(
E1.a_ops->releasepage(E2, E3)
|
E1->a_ops->releasepage(E2, E3)
)
...}

@script:python p3r2 depends on p3r1@
P << p3r1.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

// -------------------------------------------------------------------
// Part 4 generic modification
@p4r1 depends on part4@
@@
struct address_space_operations { ... int (*releasepage)(
+struct address_space *,
struct page *, ...); ... };

@p4r2 depends on part4@
expression E1, E2, E3;
@@
E1.a_ops->releasepage(
+MAPPING_NULL,
E2, E3)

@p4r3 depends on part4@
expression E1, E2, E3;
@@
E1->a_ops->releasepage(
+MAPPING_NULL,
E2, E3)
-------------------------------------------------------------------->%

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>
---
 fs/9p/vfs_addr.c       | 3 ++-
 fs/afs/dir.c           | 6 ++++--
 fs/afs/file.c          | 6 ++++--
 fs/block_dev.c         | 3 ++-
 fs/btrfs/disk-io.c     | 5 +++--
 fs/btrfs/inode.c       | 5 +++--
 fs/ceph/addr.c         | 3 ++-
 fs/cifs/file.c         | 3 ++-
 fs/erofs/super.c       | 5 +++--
 fs/ext4/inode.c        | 3 ++-
 fs/f2fs/data.c         | 3 ++-
 fs/f2fs/f2fs.h         | 3 ++-
 fs/gfs2/aops.c         | 3 ++-
 fs/gfs2/inode.h        | 3 ++-
 fs/hfs/inode.c         | 3 ++-
 fs/hfsplus/inode.c     | 3 ++-
 fs/iomap/buffered-io.c | 3 ++-
 fs/jfs/jfs_metapage.c  | 5 +++--
 fs/nfs/file.c          | 3 ++-
 fs/ocfs2/aops.c        | 3 ++-
 fs/orangefs/inode.c    | 3 ++-
 fs/reiserfs/inode.c    | 3 ++-
 fs/ubifs/file.c        | 3 ++-
 include/linux/fs.h     | 2 +-
 include/linux/iomap.h  | 3 ++-
 mm/filemap.c           | 3 ++-
 26 files changed, 59 insertions(+), 32 deletions(-)

diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c
index 357f2e5049c48..0ae4f31b3d7f2 100644
--- a/fs/9p/vfs_addr.c
+++ b/fs/9p/vfs_addr.c
@@ -123,7 +123,8 @@ static int v9fs_vfs_readpages(struct file *filp, struct address_space *mapping,
  * Returns 1 if the page can be released, false otherwise.
  */
 
-static int v9fs_release_page(struct page *page, gfp_t gfp)
+static int v9fs_release_page(struct address_space *__mapping,
+			     struct page *page, gfp_t gfp)
 {
 	if (PagePrivate(page))
 		return 0;
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index d77c13c213d2d..c27524f35281e 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -40,7 +40,8 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
 static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
 		      struct inode *new_dir, struct dentry *new_dentry,
 		      unsigned int flags);
-static int afs_dir_releasepage(struct page *page, gfp_t gfp_flags);
+static int afs_dir_releasepage(struct address_space *__mapping,
+			       struct page *page, gfp_t gfp_flags);
 static void afs_dir_invalidatepage(struct address_space *__mapping,
 				   struct page *page, unsigned int offset,
 				   unsigned int length);
@@ -1971,7 +1972,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
  * Release a directory 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_dir_releasepage(struct page *page, gfp_t gfp_flags)
+static int afs_dir_releasepage(struct address_space *__mapping,
+			       struct page *page, gfp_t gfp_flags)
 {
 	struct afs_vnode *dvnode = AFS_FS_I(page->mapping->host);
 
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 43edfa65c7ac7..496595240f12b 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -22,7 +22,8 @@ static int afs_readpage(struct file *file, struct address_space *__mapping,
 static void afs_invalidatepage(struct address_space *__mapping,
 			       struct page *page, unsigned int offset,
 			       unsigned int length);
-static int afs_releasepage(struct page *page, gfp_t gfp_flags);
+static int afs_releasepage(struct address_space *__mapping, struct page *page,
+			   gfp_t gfp_flags);
 
 static int afs_readpages(struct file *filp, struct address_space *mapping,
 			 struct list_head *pages, unsigned nr_pages);
@@ -645,7 +646,8 @@ static void afs_invalidatepage(struct address_space *__mapping,
  * 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 address_space *__mapping, struct page *page,
+			   gfp_t gfp_flags)
 {
 	struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
 	unsigned long priv;
diff --git a/fs/block_dev.c b/fs/block_dev.c
index b50c93932dfdf..e4cb73598e3c1 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1935,7 +1935,8 @@ EXPORT_SYMBOL_GPL(blkdev_read_iter);
  * Try to release a page associated with block device when the system
  * is under memory pressure.
  */
-static int blkdev_releasepage(struct page *page, gfp_t wait)
+static int blkdev_releasepage(struct address_space *__mapping,
+			      struct page *page, gfp_t wait)
 {
 	struct super_block *super = BDEV_I(page->mapping->host)->bdev.bd_super;
 
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index d57d0a6dd2621..c9d640cfa51cb 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -957,7 +957,8 @@ static int btree_readpage(struct file *file, struct address_space *__mapping,
 	return extent_read_full_page(page, btree_get_extent, 0);
 }
 
-static int btree_releasepage(struct page *page, gfp_t gfp_flags)
+static int btree_releasepage(struct address_space *__mapping,
+			     struct page *page, gfp_t gfp_flags)
 {
 	if (PageWriteback(page) || PageDirty(page))
 		return 0;
@@ -972,7 +973,7 @@ static void btree_invalidatepage(struct address_space *__mapping,
 	struct extent_io_tree *tree;
 	tree = &BTRFS_I(page->mapping->host)->io_tree;
 	extent_invalidatepage(tree, page, offset);
-	btree_releasepage(page, GFP_NOFS);
+	btree_releasepage(MAPPING_NULL, page, GFP_NOFS);
 	if (PagePrivate(page)) {
 		btrfs_warn(BTRFS_I(page->mapping->host)->root->fs_info,
 			   "page private not zero on page %llu",
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 062886fc0e750..95bf86a871ffb 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -8057,7 +8057,8 @@ static int __btrfs_releasepage(struct page *page, gfp_t gfp_flags)
 	return ret;
 }
 
-static int btrfs_releasepage(struct page *page, gfp_t gfp_flags)
+static int btrfs_releasepage(struct address_space *__mapping,
+			     struct page *page, gfp_t gfp_flags)
 {
 	if (PageWriteback(page) || PageDirty(page))
 		return 0;
@@ -8116,7 +8117,7 @@ static void btrfs_invalidatepage(struct address_space *__mapping,
 
 	tree = &BTRFS_I(inode)->io_tree;
 	if (offset) {
-		btrfs_releasepage(page, GFP_NOFS);
+		btrfs_releasepage(MAPPING_NULL, page, GFP_NOFS);
 		return;
 	}
 
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index ed555b0d48bfa..f6739a7b9ad35 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -172,7 +172,8 @@ static void ceph_invalidatepage(struct address_space *__mapping,
 	ClearPagePrivate(page);
 }
 
-static int ceph_releasepage(struct page *page, gfp_t g)
+static int ceph_releasepage(struct address_space *__mapping,
+			    struct page *page, gfp_t g)
 {
 	dout("%p releasepage %p idx %lu (%sdirty)\n", page->mapping->host,
 	     page, page->index, PageDirty(page) ? "" : "not ");
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 84cb64821036c..38d79a9eafa76 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -4694,7 +4694,8 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,
 	return rc;
 }
 
-static int cifs_release_page(struct page *page, gfp_t gfp)
+static int cifs_release_page(struct address_space *__mapping,
+			     struct page *page, gfp_t gfp)
 {
 	if (PagePrivate(page))
 		return 0;
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index 3c0e10d1b4e19..d4082102c180f 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -281,7 +281,8 @@ static int erofs_fc_parse_param(struct fs_context *fc,
 #ifdef CONFIG_EROFS_FS_ZIP
 static const struct address_space_operations managed_cache_aops;
 
-static int erofs_managed_cache_releasepage(struct page *page, gfp_t gfp_mask)
+static int erofs_managed_cache_releasepage(struct address_space *__mapping,
+					   struct page *page, gfp_t gfp_mask)
 {
 	int ret = 1;	/* 0 - busy */
 	struct address_space *const mapping = page->mapping;
@@ -308,7 +309,7 @@ static void erofs_managed_cache_invalidatepage(struct address_space *__mapping,
 	DBG_BUGON(stop > PAGE_SIZE || stop < length);
 
 	if (offset == 0 && stop == PAGE_SIZE)
-		while (!erofs_managed_cache_releasepage(page, GFP_NOFS))
+		while (!erofs_managed_cache_releasepage(MAPPING_NULL, page, GFP_NOFS))
 			cond_resched();
 }
 
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 27b8d57349d88..2fd0c674136cc 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3283,7 +3283,8 @@ static void ext4_journalled_invalidatepage(struct address_space *__mapping,
 	WARN_ON(__ext4_journalled_invalidatepage(page, offset, length) < 0);
 }
 
-static int ext4_releasepage(struct page *page, gfp_t wait)
+static int ext4_releasepage(struct address_space *__mapping,
+		            struct page *page, gfp_t wait)
 {
 	journal_t *journal = EXT4_JOURNAL(page->mapping->host);
 
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index b13e430e62435..c6444ffd7d6e9 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -3720,7 +3720,8 @@ void f2fs_invalidate_page(struct address_space *__mapping, struct page *page,
 	f2fs_clear_page_private(page);
 }
 
-int f2fs_release_page(struct page *page, gfp_t wait)
+int f2fs_release_page(struct address_space *__mapping, struct page *page,
+		      gfp_t wait)
 {
 	/* If this is dirty page, keep PagePrivate */
 	if (PageDirty(page))
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index eb6f9aa4007c6..6bb30d2192842 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3472,7 +3472,8 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
 void f2fs_invalidate_page(struct address_space *__mapping, struct page *page,
 			  unsigned int offset,
 			  unsigned int length);
-int f2fs_release_page(struct page *page, gfp_t wait);
+int f2fs_release_page(struct address_space *__mapping, struct page *page,
+		      gfp_t wait);
 #ifdef CONFIG_MIGRATION
 int f2fs_migrate_page(struct address_space *mapping, struct page *newpage,
 			struct page *page, enum migrate_mode mode);
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 0c6d2e99a5243..77efc65a412ec 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -723,7 +723,8 @@ static void gfs2_invalidatepage(struct address_space *__mapping,
  * Returns: 1 if the page was put or else 0
  */
 
-int gfs2_releasepage(struct page *page, gfp_t gfp_mask)
+int gfs2_releasepage(struct address_space *__mapping, struct page *page,
+		     gfp_t gfp_mask)
 {
 	struct address_space *mapping = page->mapping;
 	struct gfs2_sbd *sdp = gfs2_mapping2sbd(mapping);
diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h
index b52ecf4ffe634..f1e878353cebb 100644
--- a/fs/gfs2/inode.h
+++ b/fs/gfs2/inode.h
@@ -12,7 +12,8 @@
 #include <linux/mm.h>
 #include "util.h"
 
-extern int gfs2_releasepage(struct page *page, gfp_t gfp_mask);
+extern int gfs2_releasepage(struct address_space *__mapping,
+			    struct page *page, gfp_t gfp_mask);
 extern int gfs2_internal_read(struct gfs2_inode *ip,
 			      char *buf, loff_t *pos, unsigned size);
 extern void gfs2_set_aops(struct inode *inode);
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 101cc5e10524f..8986c8a0a23b2 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -72,7 +72,8 @@ static sector_t hfs_bmap(struct address_space *mapping, sector_t block)
 	return generic_block_bmap(mapping, block, hfs_get_block);
 }
 
-static int hfs_releasepage(struct page *page, gfp_t mask)
+static int hfs_releasepage(struct address_space *__mapping, struct page *page,
+			   gfp_t mask)
 {
 	struct inode *inode = page->mapping->host;
 	struct super_block *sb = inode->i_sb;
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index 1654ee206e7e5..0534280978457 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -66,7 +66,8 @@ static sector_t hfsplus_bmap(struct address_space *mapping, sector_t block)
 	return generic_block_bmap(mapping, block, hfsplus_get_block);
 }
 
-static int hfsplus_releasepage(struct page *page, gfp_t mask)
+static int hfsplus_releasepage(struct address_space *__mapping,
+			       struct page *page, gfp_t mask)
 {
 	struct inode *inode = page->mapping->host;
 	struct super_block *sb = inode->i_sb;
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index b94729b7088a7..091f6656f3d6b 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -476,7 +476,8 @@ iomap_is_partially_uptodate(struct page *page, unsigned long from,
 EXPORT_SYMBOL_GPL(iomap_is_partially_uptodate);
 
 int
-iomap_releasepage(struct page *page, gfp_t gfp_mask)
+iomap_releasepage(struct address_space *__mapping, struct page *page,
+		  gfp_t gfp_mask)
 {
 	trace_iomap_releasepage(page->mapping->host, page_offset(page),
 			PAGE_SIZE);
diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
index 5be751fa11e0b..435b55faca4ff 100644
--- a/fs/jfs/jfs_metapage.c
+++ b/fs/jfs/jfs_metapage.c
@@ -528,7 +528,8 @@ static int metapage_readpage(struct file *fp, struct address_space *__mapping,
 	return -EIO;
 }
 
-static int metapage_releasepage(struct page *page, gfp_t gfp_mask)
+static int metapage_releasepage(struct address_space *__mapping,
+				struct page *page, gfp_t gfp_mask)
 {
 	struct metapage *mp;
 	int ret = 1;
@@ -565,7 +566,7 @@ static void metapage_invalidatepage(struct address_space *__mapping,
 
 	BUG_ON(PageWriteback(page));
 
-	metapage_releasepage(page, 0);
+	metapage_releasepage(MAPPING_NULL, page, 0);
 }
 
 const struct address_space_operations jfs_metapage_aops = {
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 381288d686386..ddfe95d3da057 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -426,7 +426,8 @@ static void nfs_invalidate_page(struct address_space *__mapping,
  * - Caller holds page lock
  * - Return true (may release page) or false (may not)
  */
-static int nfs_release_page(struct page *page, gfp_t gfp)
+static int nfs_release_page(struct address_space *__mapping,
+			    struct page *page, gfp_t gfp)
 {
 	dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page);
 
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index c597a104e0af4..fdd3c6a55d817 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -501,7 +501,8 @@ static sector_t ocfs2_bmap(struct address_space *mapping, sector_t block)
 	return status;
 }
 
-static int ocfs2_releasepage(struct page *page, gfp_t wait)
+static int ocfs2_releasepage(struct address_space *__mapping,
+			     struct page *page, gfp_t wait)
 {
 	if (!page_has_buffers(page))
 		return 0;
diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c
index 6ea0ec45754dc..1534dc2df6e5c 100644
--- a/fs/orangefs/inode.c
+++ b/fs/orangefs/inode.c
@@ -520,7 +520,8 @@ static void orangefs_invalidatepage(struct address_space *__mapping,
 	orangefs_launder_page(page);
 }
 
-static int orangefs_releasepage(struct page *page, gfp_t foo)
+static int orangefs_releasepage(struct address_space *__mapping,
+				struct page *page, gfp_t foo)
 {
 	return !PagePrivate(page);
 }
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index d35c03a7d3f5b..efd149cc897a9 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -3230,7 +3230,8 @@ static int reiserfs_set_page_dirty(struct address_space *__mapping,
  * even in -o notail mode, we can't be sure an old mount without -o notail
  * didn't create files with tails.
  */
-static int reiserfs_releasepage(struct page *page, gfp_t unused_gfp_flags)
+static int reiserfs_releasepage(struct address_space *__mapping,
+				struct page *page, gfp_t unused_gfp_flags)
 {
 	struct inode *inode = page->mapping->host;
 	struct reiserfs_journal *j = SB_JOURNAL(inode->i_sb);
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 11ed42c4859f0..7e00370ca3ed1 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1486,7 +1486,8 @@ static int ubifs_migrate_page(struct address_space *mapping,
 }
 #endif
 
-static int ubifs_releasepage(struct page *page, gfp_t unused_gfp_flags)
+static int ubifs_releasepage(struct address_space *__mapping,
+			     struct page *page, gfp_t unused_gfp_flags)
 {
 	struct inode *inode = page->mapping->host;
 	struct ubifs_info *c = inode->i_sb->s_fs_info;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b471e82546001..989e505de9182 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -399,7 +399,7 @@ struct address_space_operations {
 	sector_t (*bmap)(struct address_space *, sector_t);
 	void (*invalidatepage) (struct address_space *, struct page *,
 				unsigned int, unsigned int);
-	int (*releasepage) (struct page *, gfp_t);
+	int (*releasepage) (struct address_space *, struct page *, gfp_t);
 	void (*freepage)(struct page *);
 	ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter);
 	/*
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 45f23d2268365..cb4b207974756 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -159,7 +159,8 @@ void iomap_readahead(struct readahead_control *, const struct iomap_ops *ops);
 int iomap_set_page_dirty(struct address_space *__mapping, struct page *page);
 int iomap_is_partially_uptodate(struct page *page, unsigned long from,
 		unsigned long count);
-int iomap_releasepage(struct page *page, gfp_t gfp_mask);
+int iomap_releasepage(struct address_space *__mapping, struct page *page,
+		      gfp_t gfp_mask);
 void iomap_invalidatepage(struct address_space *__mapping, struct page *page,
 			  unsigned int offset,
 			  unsigned int len);
diff --git a/mm/filemap.c b/mm/filemap.c
index b67a253e5e6c7..eccd5d0554851 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -3694,7 +3694,8 @@ int try_to_release_page(struct page *page, gfp_t gfp_mask)
 		return 0;
 
 	if (mapping && mapping->a_ops->releasepage)
-		return mapping->a_ops->releasepage(page, gfp_mask);
+		return mapping->a_ops->releasepage(MAPPING_NULL, page,
+						   gfp_mask);
 	return try_to_free_buffers(page);
 }
 
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 09/14] mm: add struct address_space to freepage() callback
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
                   ` (6 preceding siblings ...)
  2020-10-07  1:05 ` [PATCH 08/14] mm: add struct address_space to releasepage() callback jglisse
@ 2020-10-07  1:05 ` jglisse
  2020-10-07  1:05 ` [PATCH 10/14] mm: add struct address_space to putback_page() callback jglisse
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

This is part of patchset to remove dependency on struct page.mapping
field so that we can temporarily update it to point to a special
structure tracking temporary page state (note that original mapping
pointer is preserved and can still be accessed but at a cost).

Add struct address_space to freepage() callback arguments.

Note that this patch does not make use of the new argument, nor does
it use a valid one at call site (by default this patch just use NULL
for new argument value).

Use following script (from root of linux kernel tree):

./that-script.sh that-semantic-patch.spatch

%<--------------------------------------------------------------------
#!/bin/sh
spatch_file=$1

echo PART1 ===========================================================

# P1 find callback functions name
spatch  --dir . --no-includes -D part1 --sp-file $spatch_file

echo PART2 ===========================================================

# P2 change callback function prototype
cat /tmp/unicorn-functions | sort | uniq | while read func ; do
    for file in $( git grep -l $func -- '*.[ch]' ) ; do
        echo $file
        spatch --no-includes --in-place -D part2 \
               -D fn=$func --sp-file $spatch_file $file
    done
done

echo PART 3 ==========================================================

# P3 find all function which call the callback
spatch --dir . --include-headers -D part3 --sp-file $spatch_file

echo PART 4===========================================================

# P4 change all funcitons which call the callback
cat /tmp/unicorn-files | sort | uniq | while read file ; do
    echo $file
    spatch --no-includes --in-place -D part4 \
           --sp-file $spatch_file $file
done
-------------------------------------------------------------------->%

With the following semantic patch:

%<--------------------------------------------------------------------
virtual part1, part2, part3, part4

// ----------------------------------------------------------------------------
// Part 1 is grepping all function that are use as callback for freepage.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part1@
@@
file=open('/tmp/unicorn-functions', 'w')
file.close()

// match function name use as a callback
@p1r2 depends on part1@
identifier I1, FN;
@@
struct address_space_operations I1 = {..., .freepage = FN, ...};

@script:python p1r3 depends on p1r2@
funcname << p1r2.FN;
@@
if funcname != "NULL":
  file=open('/tmp/unicorn-functions', 'a')
  file.write(funcname + '\n')
  file.close()

// -------------------------------------------------------------------
// Part 2 modify callback

// Add address_space argument to the function (freepage callback one)
@p2r1 depends on part2@
identifier virtual.fn;
identifier I1;
type T1;
@@
void fn(
+struct address_space *__mapping,
T1 I1) { ... }

@p2r2 depends on part2@
identifier virtual.fn;
identifier I1;
type T1;
@@
void fn(
+struct address_space *__mapping,
T1 I1);

@p2r3 depends on part2@
identifier virtual.fn;
expression E1;
@@
fn(
+MAPPING_NULL,
E1)

// ----------------------------------------------------------------------------
// Part 3 is grepping all function that use the callback for freepage.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part3@
@@
file=open('/tmp/unicorn-files', 'w')
file.write("./include/linux/pagemap.h\n")
file.write("./include/linux/mm.h\n")
file.write("./include/linux/fs.h\n")
file.write("./mm/readahead.c\n")
file.write("./mm/filemap.c\n")
file.close()

@p3r1 depends on part3 exists@
expression E1, E2;
identifier FN;
position P;
@@
FN@P(...) {...
(
E1.a_ops->freepage(E2)
|
E1->a_ops->freepage(E2)
)
...}

@script:python p3r2 depends on p3r1@
P << p3r1.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

// -------------------------------------------------------------------
// Part 4 generic modification
@p4r1 depends on part4@
@@
struct address_space_operations { ... void (*freepage)(
+struct address_space *,
struct page *, ...); ... };

@p4r2 depends on part4@
expression E1, E2;
@@
E1.a_ops->freepage(
+MAPPING_NULL,
E2)

@p4r3 depends on part4@
expression E1, E2;
@@
E1->a_ops->freepage(
+MAPPING_NULL,
E2)
-------------------------------------------------------------------->%

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>
---
 fs/nfs/dir.c        | 9 +++++----
 fs/orangefs/inode.c | 3 ++-
 include/linux/fs.h  | 2 +-
 mm/filemap.c        | 4 ++--
 mm/truncate.c       | 2 +-
 mm/vmscan.c         | 2 +-
 6 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 5a5c021967d3f..d8e66c98db3ea 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -53,7 +53,7 @@ static int nfs_closedir(struct inode *, struct file *);
 static int nfs_readdir(struct file *, struct dir_context *);
 static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
 static loff_t nfs_llseek_dir(struct file *, loff_t, int);
-static void nfs_readdir_clear_array(struct page*);
+static void nfs_readdir_clear_array(struct address_space *, struct page*);
 
 const struct file_operations nfs_dir_operations = {
 	.llseek		= nfs_llseek_dir,
@@ -177,7 +177,8 @@ void nfs_readdir_init_array(struct page *page)
  * we are freeing strings created by nfs_add_to_readdir_array()
  */
 static
-void nfs_readdir_clear_array(struct page *page)
+void nfs_readdir_clear_array(struct address_space *__mapping,
+			     struct page *page)
 {
 	struct nfs_cache_array *array;
 	int i;
@@ -725,7 +726,7 @@ int nfs_readdir_filler(void *data, struct address_space *__mapping,
 	unlock_page(page);
 	return 0;
  error:
-	nfs_readdir_clear_array(page);
+	nfs_readdir_clear_array(MAPPING_NULL, page);
 	unlock_page(page);
 	return ret;
 }
@@ -875,7 +876,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc)
 	status = nfs_do_filldir(desc);
 
  out_release:
-	nfs_readdir_clear_array(desc->page);
+	nfs_readdir_clear_array(MAPPING_NULL, desc->page);
 	cache_page_release(desc);
  out:
 	dfprintk(DIRCACHE, "NFS: %s: returns %d\n",
diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c
index 1534dc2df6e5c..8b47bcbf0ca4d 100644
--- a/fs/orangefs/inode.c
+++ b/fs/orangefs/inode.c
@@ -526,7 +526,8 @@ static int orangefs_releasepage(struct address_space *__mapping,
 	return !PagePrivate(page);
 }
 
-static void orangefs_freepage(struct page *page)
+static void orangefs_freepage(struct address_space *__mapping,
+			      struct page *page)
 {
 	kfree(detach_page_private(page));
 }
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 989e505de9182..a952aa9d93e7f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -400,7 +400,7 @@ struct address_space_operations {
 	void (*invalidatepage) (struct address_space *, struct page *,
 				unsigned int, unsigned int);
 	int (*releasepage) (struct address_space *, struct page *, gfp_t);
-	void (*freepage)(struct page *);
+	void (*freepage)(struct address_space *, struct page *);
 	ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter);
 	/*
 	 * migrate the contents of a page to the specified target. If
diff --git a/mm/filemap.c b/mm/filemap.c
index eccd5d0554851..faa190598cba8 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -243,7 +243,7 @@ static void page_cache_free_page(struct address_space *mapping,
 				struct page *page)
 {
 	if (mapping->a_ops->freepage)
-		mapping->a_ops->freepage(page);
+		mapping->a_ops->freepage(MAPPING_NULL, page);
 
 	if (PageTransHuge(page) && !PageHuge(page)) {
 		page_ref_sub(page, HPAGE_PMD_NR);
@@ -816,7 +816,7 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
 		__inc_lruvec_page_state(new, NR_SHMEM);
 	xas_unlock_irqrestore(&xas, flags);
 	if (mapping->a_ops->freepage)
-		mapping->a_ops->freepage(old);
+		mapping->a_ops->freepage(MAPPING_NULL, old);
 	put_page(old);
 
 	return 0;
diff --git a/mm/truncate.c b/mm/truncate.c
index e26b232b66c01..e24688115c903 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -653,7 +653,7 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page)
 	xa_unlock_irqrestore(&mapping->i_pages, flags);
 
 	if (mapping->a_ops->freepage)
-		mapping->a_ops->freepage(page);
+		mapping->a_ops->freepage(MAPPING_NULL, page);
 
 	put_page(page);	/* pagecache ref */
 	return 1;
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 4322bc5ee2d84..bae7fb9c3512a 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -926,7 +926,7 @@ static int __remove_mapping(struct address_space *mapping, struct page *page,
 		xa_unlock_irqrestore(&mapping->i_pages, flags);
 
 		if (mapping->a_ops->freepage != NULL)
-			mapping->a_ops->freepage(page);
+			mapping->a_ops->freepage(MAPPING_NULL, page);
 	}
 
 	return 1;
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 10/14] mm: add struct address_space to putback_page() callback
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
                   ` (7 preceding siblings ...)
  2020-10-07  1:05 ` [PATCH 09/14] mm: add struct address_space to freepage() callback jglisse
@ 2020-10-07  1:05 ` jglisse
  2020-10-07  1:06 ` [PATCH 11/14] mm: add struct address_space to launder_page() callback jglisse
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

This is part of patchset to remove dependency on struct page.mapping
field so that we can temporarily update it to point to a special
structure tracking temporary page state (note that original mapping
pointer is preserved and can still be accessed but at a cost).

Add struct address_space to putback_page() callback arguments.

Note that this patch does not make use of the new argument, nor does
it use a valid one at call site (by default this patch just use NULL
for new argument value).

Use following script (from root of linux kernel tree):

./that-script.sh that-semantic-patch.spatch

%<--------------------------------------------------------------------
#!/bin/sh
spatch_file=$1

echo PART1 ===========================================================

# P1 find callback functions name
spatch  --dir . --no-includes -D part1 --sp-file $spatch_file

echo PART2 ===========================================================

# P2 change callback function prototype
cat /tmp/unicorn-functions | sort | uniq | while read func ; do
    for file in $( git grep -l $func -- '*.[ch]' ) ; do
        echo $file
        spatch --no-includes --in-place -D part2 \
               -D fn=$func --sp-file $spatch_file $file
    done
done

echo PART 3 ==========================================================

# P3 find all function which call the callback
spatch --dir . --include-headers -D part3 --sp-file $spatch_file

echo PART 4===========================================================

# P4 change all funcitons which call the callback
cat /tmp/unicorn-files | sort | uniq | while read file ; do
    echo $file
    spatch --no-includes --in-place -D part4 \
           --sp-file $spatch_file $file
done
-------------------------------------------------------------------->%

With the following semantic patch:

%<--------------------------------------------------------------------
virtual part1, part2, part3, part4

// ----------------------------------------------------------------------------
// Part 1 is grepping all function that are use as callback for putback_page.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part1@
@@
file=open('/tmp/unicorn-functions', 'w')
file.close()

// match function name use as a callback
@p1r2 depends on part1@
identifier I1, FN;
@@
struct address_space_operations I1 = {..., .putback_page = FN, ...};

@script:python p1r3 depends on p1r2@
funcname << p1r2.FN;
@@
if funcname != "NULL":
  file=open('/tmp/unicorn-functions', 'a')
  file.write(funcname + '\n')
  file.close()

// -------------------------------------------------------------------
// Part 2 modify callback

// Add address_space argument to the function (putback_page callback one)
@p2r1 depends on part2@
identifier virtual.fn;
identifier I1;
type T1;
@@
void fn(
+struct address_space *__mapping,
T1 I1) { ... }

@p2r2 depends on part2@
identifier virtual.fn;
identifier I1;
type T1;
@@
void fn(
+struct address_space *__mapping,
T1 I1);

@p2r3 depends on part2@
identifier virtual.fn;
expression E1;
@@
fn(
+MAPPING_NULL,
E1)

// ----------------------------------------------------------------------------
// Part 3 is grepping all function that are use the callback for putback_page.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part3@
@@
file=open('/tmp/unicorn-files', 'w')
file.write("./include/linux/pagemap.h\n")
file.write("./include/linux/mm.h\n")
file.write("./include/linux/fs.h\n")
file.write("./mm/readahead.c\n")
file.write("./mm/filemap.c\n")
file.close()

@p3r1 depends on part3 exists@
expression E1, E2;
identifier FN;
position P;
@@
FN@P(...) {...
(
E1.a_ops->putback_page(E2)
|
E1->a_ops->putback_page(E2)
)
...}

@script:python p3r2 depends on p3r1@
P << p3r1.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

// -------------------------------------------------------------------
// Part 4 generic modification
@p4r1 depends on part4@
@@
struct address_space_operations { ... void (*putback_page)(
+struct address_space *,
struct page *); ... };

@p4r2 depends on part4@
expression E1, E2;
@@
E1.a_ops->putback_page(
+MAPPING_NULL,
E2)

@p4r3 depends on part4@
expression E1, E2;
@@
E1->a_ops->putback_page(
+MAPPING_NULL,
E2)
-------------------------------------------------------------------->%

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>
---
 include/linux/balloon_compaction.h | 6 ++++--
 include/linux/fs.h                 | 2 +-
 mm/balloon_compaction.c            | 2 +-
 mm/migrate.c                       | 2 +-
 mm/z3fold.c                        | 3 ++-
 mm/zsmalloc.c                      | 3 ++-
 6 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/include/linux/balloon_compaction.h b/include/linux/balloon_compaction.h
index 338aa27e4773b..07b235161d040 100644
--- a/include/linux/balloon_compaction.h
+++ b/include/linux/balloon_compaction.h
@@ -82,7 +82,8 @@ static inline void balloon_devinfo_init(struct balloon_dev_info *balloon)
 extern const struct address_space_operations balloon_aops;
 extern bool balloon_page_isolate(struct page *page,
 				isolate_mode_t mode);
-extern void balloon_page_putback(struct page *page);
+extern void balloon_page_putback(struct address_space *__mapping,
+				 struct page *page);
 extern int balloon_page_migrate(struct address_space *mapping,
 				struct page *newpage,
 				struct page *page, enum migrate_mode mode);
@@ -160,7 +161,8 @@ static inline bool balloon_page_isolate(struct page *page)
 	return false;
 }
 
-static inline void balloon_page_putback(struct page *page)
+static inline void balloon_page_putback(struct address_space *__mapping,
+					struct page *page)
 {
 	return;
 }
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a952aa9d93e7f..4d0b9c14a5017 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -409,7 +409,7 @@ struct address_space_operations {
 	int (*migratepage) (struct address_space *,
 			struct page *, struct page *, enum migrate_mode);
 	bool (*isolate_page)(struct page *, isolate_mode_t);
-	void (*putback_page)(struct page *);
+	void (*putback_page)(struct address_space *, struct page *);
 	int (*launder_page) (struct page *);
 	int (*is_partially_uptodate) (struct page *, unsigned long,
 					unsigned long);
diff --git a/mm/balloon_compaction.c b/mm/balloon_compaction.c
index 26de020aae7b4..abc4a63df9903 100644
--- a/mm/balloon_compaction.c
+++ b/mm/balloon_compaction.c
@@ -217,7 +217,7 @@ bool balloon_page_isolate(struct page *page, isolate_mode_t mode)
 	return true;
 }
 
-void balloon_page_putback(struct page *page)
+void balloon_page_putback(struct address_space *__mapping, struct page *page)
 {
 	struct balloon_dev_info *b_dev_info = balloon_page_device(page);
 	unsigned long flags;
diff --git a/mm/migrate.c b/mm/migrate.c
index 21beb45356760..3fba7429151bf 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -154,7 +154,7 @@ void putback_movable_page(struct page *page)
 	VM_BUG_ON_PAGE(!PageIsolated(page), page);
 
 	mapping = page_mapping(page);
-	mapping->a_ops->putback_page(page);
+	mapping->a_ops->putback_page(MAPPING_NULL, page);
 	__ClearPageIsolated(page);
 }
 
diff --git a/mm/z3fold.c b/mm/z3fold.c
index 460b0feced26a..37453a14257a4 100644
--- a/mm/z3fold.c
+++ b/mm/z3fold.c
@@ -1668,7 +1668,8 @@ static int z3fold_page_migrate(struct address_space *mapping, struct page *newpa
 	return 0;
 }
 
-static void z3fold_page_putback(struct page *page)
+static void z3fold_page_putback(struct address_space *__mapping,
+				struct page *page)
 {
 	struct z3fold_header *zhdr;
 	struct z3fold_pool *pool;
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index c36fdff9a3713..99d74c6e98216 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -2099,7 +2099,8 @@ static int zs_page_migrate(struct address_space *mapping, struct page *newpage,
 	return ret;
 }
 
-static void zs_page_putback(struct page *page)
+static void zs_page_putback(struct address_space *__mapping,
+			    struct page *page)
 {
 	struct zs_pool *pool;
 	struct size_class *class;
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 11/14] mm: add struct address_space to launder_page() callback
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
                   ` (8 preceding siblings ...)
  2020-10-07  1:05 ` [PATCH 10/14] mm: add struct address_space to putback_page() callback jglisse
@ 2020-10-07  1:06 ` jglisse
  2020-10-07  1:06 ` [PATCH 12/14] mm: add struct address_space to is_partially_uptodate() callback jglisse
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:06 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

This is part of patchset to remove dependency on struct page.mapping
field so that we can temporarily update it to point to a special
structure tracking temporary page state (note that original mapping
pointer is preserved and can still be accessed but at a cost).

Add struct address_space to launder_page() callback arguments.

Note that this patch does not make use of the new argument, nor does
it use a valid one at call site (by default this patch just use NULL
for new argument value).

Use following script (from root of linux kernel tree):

./that-script.sh that-semantic-patch.spatch

%<--------------------------------------------------------------------
#!/bin/sh
spatch_file=$1

echo PART1 ===========================================================

# P1 find callback functions name
spatch  --dir . --no-includes -D part1 --sp-file $spatch_file

echo PART2 ===========================================================

# P2 change callback function prototype
cat /tmp/unicorn-functions | sort | uniq | while read func ; do
    for file in $( git grep -l $func -- '*.[ch]' ) ; do
        echo $file
        spatch --no-includes --in-place -D part2 \
               -D fn=$func --sp-file $spatch_file $file
    done
done

echo PART 3 ==========================================================

# P3 find all function which call the callback
spatch --dir . --include-headers -D part3 --sp-file $spatch_file

echo PART 4===========================================================

# P4 change all funcitons which call the callback
cat /tmp/unicorn-files | sort | uniq | while read file ; do
    echo $file
    spatch --no-includes --in-place -D part4 \
           --sp-file $spatch_file $file
done
-------------------------------------------------------------------->%

With the following semantic patch:

%<--------------------------------------------------------------------
virtual part1, part2, part3, part4

// ----------------------------------------------------------------------------
// Part 1 is grepping all function that are use as callback for launder_page.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part1@
@@
file=open('/tmp/unicorn-functions', 'w')
file.close()

// match function name use as a callback
@p1r2 depends on part1@
identifier I1, FN;
@@
struct address_space_operations I1 = {..., .launder_page = FN, ...};

@script:python p1r3 depends on p1r2@
funcname << p1r2.FN;
@@
if funcname != "NULL":
  file=open('/tmp/unicorn-functions', 'a')
  file.write(funcname + '\n')
  file.close()

// -------------------------------------------------------------------
// Part 2 modify callback

// Add address_space argument to the function (launder_page callback one)
@p2r1 depends on part2@
identifier virtual.fn;
identifier I1;
type T1;
@@
int fn(
+struct address_space *__mapping,
T1 I1) { ... }

@p2r2 depends on part2@
identifier virtual.fn;
identifier I1;
type T1;
@@
int fn(
+struct address_space *__mapping,
T1 I1);

@p2r3 depends on part2@
identifier virtual.fn;
type T1;
@@
int fn(
+struct address_space *__mapping,
T1);

@p2r4 depends on part2@
identifier virtual.fn;
expression E1;
@@
fn(
+MAPPING_NULL,
E1)

// ----------------------------------------------------------------------------
// Part 3 is grepping all function that are use the callback for launder_page.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part3@
@@
file=open('/tmp/unicorn-files', 'w')
file.write("./include/linux/pagemap.h\n")
file.write("./include/linux/mm.h\n")
file.write("./include/linux/fs.h\n")
file.write("./mm/readahead.c\n")
file.write("./mm/filemap.c\n")
file.close()

@p3r1 depends on part3 exists@
expression E1, E2;
identifier FN;
position P;
@@
FN@P(...) {...
(
E1.a_ops->launder_page(E2)
|
E1->a_ops->launder_page(E2)
)
...}

@script:python p3r2 depends on p3r1@
P << p3r1.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

// -------------------------------------------------------------------
// Part 4 generic modification
@p4r1 depends on part4@
@@
struct address_space_operations { ... int (*launder_page)(
+struct address_space *,
struct page *); ... };

@p4r2 depends on part4@
expression E1, E2;
@@
E1.a_ops->launder_page(
+MAPPING_NULL,
E2)

@p4r3 depends on part4@
expression E1, E2;
@@
E1->a_ops->launder_page(
+MAPPING_NULL,
E2)
-------------------------------------------------------------------->%

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>
---
 fs/9p/vfs_addr.c    |  3 ++-
 fs/afs/internal.h   |  2 +-
 fs/afs/write.c      |  2 +-
 fs/cifs/file.c      |  3 ++-
 fs/fuse/file.c      |  3 ++-
 fs/nfs/file.c       |  3 ++-
 fs/orangefs/inode.c | 17 +++++++++--------
 include/linux/fs.h  |  2 +-
 mm/truncate.c       |  2 +-
 9 files changed, 21 insertions(+), 16 deletions(-)

diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c
index 0ae4f31b3d7f2..0cbf9a9050d0c 100644
--- a/fs/9p/vfs_addr.c
+++ b/fs/9p/vfs_addr.c
@@ -209,7 +209,8 @@ static int v9fs_vfs_writepage(struct address_space *__mapping,
  * Returns 0 on success.
  */
 
-static int v9fs_launder_page(struct page *page)
+static int v9fs_launder_page(struct address_space *__mapping,
+			     struct page *page)
 {
 	int retval;
 	struct inode *inode = page->mapping->host;
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 264f28759c737..2cdf86d4200a8 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -1436,7 +1436,7 @@ extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *);
 extern int afs_fsync(struct file *, loff_t, loff_t, int);
 extern vm_fault_t afs_page_mkwrite(struct vm_fault *vmf);
 extern void afs_prune_wb_keys(struct afs_vnode *);
-extern int afs_launder_page(struct page *);
+extern int afs_launder_page(struct address_space *__mapping, struct page *);
 
 /*
  * xattr.c
diff --git a/fs/afs/write.c b/fs/afs/write.c
index 199cbf73b9be4..652b783cd280c 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -890,7 +890,7 @@ void afs_prune_wb_keys(struct afs_vnode *vnode)
 /*
  * Clean up a page during invalidation.
  */
-int afs_launder_page(struct page *page)
+int afs_launder_page(struct address_space *__mapping, struct page *page)
 {
 	struct address_space *mapping = page->mapping;
 	struct afs_vnode *vnode = AFS_FS_I(mapping->host);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 38d79a9eafa76..c6cd5ce627e22 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -4713,7 +4713,8 @@ static void cifs_invalidate_page(struct address_space *__mapping,
 		cifs_fscache_invalidate_page(page, &cifsi->vfs_inode);
 }
 
-static int cifs_launder_page(struct page *page)
+static int cifs_launder_page(struct address_space *__mapping,
+			     struct page *page)
 {
 	int rc = 0;
 	loff_t range_start = page_offset(page);
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 66b31387e878f..4b0f85d0a0641 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -2256,7 +2256,8 @@ static int fuse_write_end(struct file *file, struct address_space *mapping,
 	return copied;
 }
 
-static int fuse_launder_page(struct page *page)
+static int fuse_launder_page(struct address_space *__mapping,
+			     struct page *page)
 {
 	int err = 0;
 	if (clear_page_dirty_for_io(page)) {
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index ddfe95d3da057..b1ba143de48d9 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -474,7 +474,8 @@ static void nfs_check_dirty_writeback(struct page *page,
  * - Caller holds page lock
  * - Return 0 if successful, -error otherwise
  */
-static int nfs_launder_page(struct page *page)
+static int nfs_launder_page(struct address_space *__mapping,
+			    struct page *page)
 {
 	struct inode *inode = page_file_mapping(page)->host;
 	struct nfs_inode *nfsi = NFS_I(inode);
diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c
index 8b47bcbf0ca4d..883f78b5c9fcb 100644
--- a/fs/orangefs/inode.c
+++ b/fs/orangefs/inode.c
@@ -244,7 +244,7 @@ static int orangefs_writepages(struct address_space *mapping,
 	return ret;
 }
 
-static int orangefs_launder_page(struct page *);
+static int orangefs_launder_page(struct address_space *, struct page *);
 
 static int orangefs_readpage(struct file *file,
 			     struct address_space *__mapping,
@@ -273,7 +273,7 @@ static int orangefs_readpage(struct file *file,
 	read_size = 524288;
 
 	if (PageDirty(page))
-		orangefs_launder_page(page);
+		orangefs_launder_page(MAPPING_NULL, page);
 
 	off = page_offset(page);
 	index = off >> PAGE_SHIFT;
@@ -381,7 +381,7 @@ static int orangefs_write_begin(struct file *file,
 		 * since we don't know what's dirty.  This will WARN in
 		 * orangefs_writepage_locked.
 		 */
-		ret = orangefs_launder_page(page);
+		ret = orangefs_launder_page(MAPPING_NULL, page);
 		if (ret)
 			return ret;
 	}
@@ -394,7 +394,7 @@ static int orangefs_write_begin(struct file *file,
 			wr->len += len;
 			goto okay;
 		} else {
-			ret = orangefs_launder_page(page);
+			ret = orangefs_launder_page(MAPPING_NULL, page);
 			if (ret)
 				return ret;
 		}
@@ -517,7 +517,7 @@ static void orangefs_invalidatepage(struct address_space *__mapping,
 	 * Thus the following runs if wr was modified above.
 	 */
 
-	orangefs_launder_page(page);
+	orangefs_launder_page(MAPPING_NULL, page);
 }
 
 static int orangefs_releasepage(struct address_space *__mapping,
@@ -532,7 +532,8 @@ static void orangefs_freepage(struct address_space *__mapping,
 	kfree(detach_page_private(page));
 }
 
-static int orangefs_launder_page(struct page *page)
+static int orangefs_launder_page(struct address_space *__mapping,
+				 struct page *page)
 {
 	int r = 0;
 	struct writeback_control wbc = {
@@ -701,7 +702,7 @@ vm_fault_t orangefs_page_mkwrite(struct vm_fault *vmf)
 		 * since we don't know what's dirty.  This will WARN in
 		 * orangefs_writepage_locked.
 		 */
-		if (orangefs_launder_page(page)) {
+		if (orangefs_launder_page(MAPPING_NULL, page)) {
 			ret = VM_FAULT_LOCKED|VM_FAULT_RETRY;
 			goto out;
 		}
@@ -714,7 +715,7 @@ vm_fault_t orangefs_page_mkwrite(struct vm_fault *vmf)
 			wr->len = PAGE_SIZE;
 			goto okay;
 		} else {
-			if (orangefs_launder_page(page)) {
+			if (orangefs_launder_page(MAPPING_NULL, page)) {
 				ret = VM_FAULT_LOCKED|VM_FAULT_RETRY;
 				goto out;
 			}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 4d0b9c14a5017..3854da5a1bcb9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -410,7 +410,7 @@ struct address_space_operations {
 			struct page *, struct page *, enum migrate_mode);
 	bool (*isolate_page)(struct page *, isolate_mode_t);
 	void (*putback_page)(struct address_space *, struct page *);
-	int (*launder_page) (struct page *);
+	int (*launder_page) (struct address_space *, struct page *);
 	int (*is_partially_uptodate) (struct page *, unsigned long,
 					unsigned long);
 	void (*is_dirty_writeback) (struct page *, bool *, bool *);
diff --git a/mm/truncate.c b/mm/truncate.c
index e24688115c903..c0719e141e34e 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -668,7 +668,7 @@ static int do_launder_page(struct address_space *mapping, struct page *page)
 		return 0;
 	if (page->mapping != mapping || mapping->a_ops->launder_page == NULL)
 		return 0;
-	return mapping->a_ops->launder_page(page);
+	return mapping->a_ops->launder_page(MAPPING_NULL, page);
 }
 
 /**
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 12/14] mm: add struct address_space to is_partially_uptodate() callback
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
                   ` (9 preceding siblings ...)
  2020-10-07  1:06 ` [PATCH 11/14] mm: add struct address_space to launder_page() callback jglisse
@ 2020-10-07  1:06 ` jglisse
  2020-10-07  1:06 ` [PATCH 13/14] mm: add struct address_space to isolate_page() callback jglisse
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:06 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

This is part of patchset to remove dependency on struct page.mapping
field so that we can temporarily update it to point to a special
structure tracking temporary page state (note that original mapping
pointer is preserved and can still be accessed but at a cost).

Add struct address_space to is_partially_uptodate() callback arguments.

Note that this patch does not make use of the new argument, nor does
it use a valid one at call site (by default this patch just use NULL
for new argument value).

Use following script (from root of linux kernel tree):

./that-script.sh that-semantic-patch.spatch

%<--------------------------------------------------------------------
#!/bin/sh
spatch_file=$1

echo PART1 ===========================================================

# P1 find callback functions name
spatch  --dir . --no-includes -D part1 --sp-file $spatch_file

echo PART2 ===========================================================

# P2 change callback function prototype
cat /tmp/unicorn-functions | sort | uniq | while read func ; do
    for file in $( git grep -l $func -- '*.[ch]' ) ; do
        echo $file
        spatch --no-includes --in-place -D part2 \
               -D fn=$func --sp-file $spatch_file $file
    done
done

echo PART 3 ==========================================================

# P3 find all function which call the callback
spatch --dir . --include-headers -D part3 --sp-file $spatch_file

echo PART 4===========================================================

# P4 change all funcitons which call the callback
cat /tmp/unicorn-files | sort | uniq | while read file ; do
    echo $file
    spatch --no-includes --in-place -D part4 \
           --sp-file $spatch_file $file
done
-------------------------------------------------------------------->%

With the following semantic patch:

%<--------------------------------------------------------------------
virtual part1, part2, part3, part4

// ----------------------------------------------------------------------------
// Part 1 is grepping all function that are use as callback for is_partially_uptodate.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part1@
@@
file=open('/tmp/unicorn-functions', 'w')
file.close()

// match function name use as a callback
@p1r2 depends on part1@
identifier I1, FN;
@@
struct address_space_operations I1 = {..., .is_partially_uptodate = FN, ...};

@script:python p1r3 depends on p1r2@
funcname << p1r2.FN;
@@
if funcname != "NULL":
  file=open('/tmp/unicorn-functions', 'a')
  file.write(funcname + '\n')
  file.close()

// -------------------------------------------------------------------
// Part 2 modify callback

// Add address_space argument to the function (is_partially_uptodate callback one)
@p2r1 depends on part2@
identifier virtual.fn;
identifier I1, I2, I3;
type T1, T2, T3;
@@
int fn(
+struct address_space *__mapping,
T1 I1, T2 I2, T3 I3) { ... }

@p2r2 depends on part2@
identifier virtual.fn;
identifier I1, I2, I3;
type T1, T2, T3;
@@
int fn(
+struct address_space *__mapping,
T1 I1, T2 I2, T3 I3);

@p2r3 depends on part2@
identifier virtual.fn;
expression E1, E2, E3;
@@
fn(
+MAPPING_NULL,
E1, E2, E3)

// ----------------------------------------------------------------------------
// Part 3 is grepping all function that are use the callback for is_partially_uptodate.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part3@
@@
file=open('/tmp/unicorn-files', 'w')
file.write("./include/linux/pagemap.h\n")
file.write("./include/linux/mm.h\n")
file.write("./include/linux/fs.h\n")
file.write("./mm/readahead.c\n")
file.write("./mm/filemap.c\n")
file.close()

@p3r1 depends on part3 exists@
expression E1, E2, E3, E4;
identifier FN;
position P;
@@
FN@P(...) {...
(
E1.a_ops->is_partially_uptodate(E2, E3, E4)
|
E1->a_ops->is_partially_uptodate(E2, E3, E4)
)
...}

@script:python p3r2 depends on p3r1@
P << p3r1.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

@p3r3 depends on part3 exists@
struct address_space_operations *AOPS;
expression E1, E2, E3;
identifier FN;
position P;
@@
FN@P(...) {...
AOPS->is_partially_uptodate(E1, E2, E3)
...}

@script:python p3r4 depends on p3r3@
P << p3r3.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

// -------------------------------------------------------------------
// Part 4 generic modification
@p4r1 depends on part4@
@@
struct address_space_operations { ... int (*is_partially_uptodate)(
+struct address_space *,
struct page *, ...); ... };

@p4r2 depends on part4@
expression E1, E2, E3, E4;
@@
E1.a_ops->is_partially_uptodate(
+MAPPING_NULL,
E2, E3, E4)

@p4r3 depends on part4@
expression E1, E2, E3, E4;
@@
E1->a_ops->is_partially_uptodate(
+MAPPING_NULL,
E2, E3, E4)

@p4r4 depends on part4 exists@
struct address_space_operations *AOPS;
expression E1, E2, E3;
@@
{...
AOPS->is_partially_uptodate(
+MAPPING_NULL,
E1, E2, E3)
...}
-------------------------------------------------------------------->%

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>
---
 fs/buffer.c                 | 3 ++-
 fs/iomap/buffered-io.c      | 5 +++--
 fs/iomap/seek.c             | 2 +-
 include/linux/buffer_head.h | 3 ++-
 include/linux/fs.h          | 3 ++-
 include/linux/iomap.h       | 5 +++--
 mm/filemap.c                | 4 ++--
 7 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/fs/buffer.c b/fs/buffer.c
index 1f0f72b76fc2a..7adf0af7530ba 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2213,7 +2213,8 @@ EXPORT_SYMBOL(generic_write_end);
  * Returns true if all buffers which correspond to a file portion
  * we want to read are uptodate.
  */
-int block_is_partially_uptodate(struct page *page, unsigned long from,
+int block_is_partially_uptodate(struct address_space *__mapping,
+				struct page *page, unsigned long from,
 					unsigned long count)
 {
 	unsigned block_start, block_end, blocksize;
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 091f6656f3d6b..c2c8b3f173443 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -449,8 +449,9 @@ EXPORT_SYMBOL_GPL(iomap_readahead);
  * we want to read within the page are uptodate.
  */
 int
-iomap_is_partially_uptodate(struct page *page, unsigned long from,
-		unsigned long count)
+iomap_is_partially_uptodate(struct address_space *__mapping,
+			    struct page *page, unsigned long from,
+			    unsigned long count)
 {
 	struct iomap_page *iop = to_iomap_page(page);
 	struct inode *inode = page->mapping->host;
diff --git a/fs/iomap/seek.c b/fs/iomap/seek.c
index 107ee80c35683..3f09cc0979a4a 100644
--- a/fs/iomap/seek.c
+++ b/fs/iomap/seek.c
@@ -49,7 +49,7 @@ page_seek_hole_data(struct inode *inode, struct page *page, loff_t *lastoff,
 	for (off = 0; off < PAGE_SIZE; off += bsize) {
 		if (offset_in_page(*lastoff) >= off + bsize)
 			continue;
-		if (ops->is_partially_uptodate(page, off, bsize) == seek_data) {
+		if (ops->is_partially_uptodate(MAPPING_NULL, page, off, bsize) == seek_data) {
 			unlock_page(page);
 			return true;
 		}
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 0902142e93f0d..89a3758531889 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -223,7 +223,8 @@ int __block_write_full_page(struct inode *inode, struct page *page,
 			get_block_t *get_block, struct writeback_control *wbc,
 			bh_end_io_t *handler);
 int block_read_full_page(struct page*, get_block_t*);
-int block_is_partially_uptodate(struct page *page, unsigned long from,
+int block_is_partially_uptodate(struct address_space *__mapping,
+				struct page *page, unsigned long from,
 				unsigned long count);
 int block_write_begin(struct address_space *mapping, loff_t pos, unsigned len,
 		unsigned flags, struct page **pagep, get_block_t *get_block);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3854da5a1bcb9..21f179e7c5daa 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -411,7 +411,8 @@ struct address_space_operations {
 	bool (*isolate_page)(struct page *, isolate_mode_t);
 	void (*putback_page)(struct address_space *, struct page *);
 	int (*launder_page) (struct address_space *, struct page *);
-	int (*is_partially_uptodate) (struct page *, unsigned long,
+	int (*is_partially_uptodate) (struct address_space *, struct page *,
+					unsigned long,
 					unsigned long);
 	void (*is_dirty_writeback) (struct page *, bool *, bool *);
 	int (*error_remove_page)(struct address_space *, struct page *);
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index cb4b207974756..53b94e35b02fd 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -157,8 +157,9 @@ ssize_t iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *from,
 int iomap_readpage(struct page *page, const struct iomap_ops *ops);
 void iomap_readahead(struct readahead_control *, const struct iomap_ops *ops);
 int iomap_set_page_dirty(struct address_space *__mapping, struct page *page);
-int iomap_is_partially_uptodate(struct page *page, unsigned long from,
-		unsigned long count);
+int iomap_is_partially_uptodate(struct address_space *__mapping,
+				struct page *page, unsigned long from,
+				unsigned long count);
 int iomap_releasepage(struct address_space *__mapping, struct page *page,
 		      gfp_t gfp_mask);
 void iomap_invalidatepage(struct address_space *__mapping, struct page *page,
diff --git a/mm/filemap.c b/mm/filemap.c
index faa190598cba8..951af134e0bf0 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2244,8 +2244,8 @@ ssize_t generic_file_buffered_read(struct kiocb *iocb,
 			/* Did it get truncated before we got the lock? */
 			if (!page->mapping)
 				goto page_not_up_to_date_locked;
-			if (!mapping->a_ops->is_partially_uptodate(page,
-							offset, iter->count))
+			if (!mapping->a_ops->is_partially_uptodate(MAPPING_NULL, page,
+								   offset, iter->count))
 				goto page_not_up_to_date_locked;
 			unlock_page(page);
 		}
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 13/14] mm: add struct address_space to isolate_page() callback
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
                   ` (10 preceding siblings ...)
  2020-10-07  1:06 ` [PATCH 12/14] mm: add struct address_space to is_partially_uptodate() callback jglisse
@ 2020-10-07  1:06 ` jglisse
  2020-10-07  1:06 ` [PATCH 14/14] mm: add struct address_space to is_dirty_writeback() callback jglisse
  2020-10-07  3:20 ` [PATCH 00/14] Small step toward KSM for file back page Matthew Wilcox
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:06 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

This is part of patchset to remove dependency on struct page.mapping
field so that we can temporarily update it to point to a special
structure tracking temporary page state (note that original mapping
pointer is preserved and can still be accessed but at a cost).

Add struct address_space to isolate_page() callback arguments.

Note that this patch does not make use of the new argument, nor does
it use a valid one at call site (by default this patch just use NULL
for new argument value).

Use following script (from root of linux kernel tree):

./that-script.sh that-semantic-patch.spatch

%<--------------------------------------------------------------------
#!/bin/sh
spatch_file=$1

echo PART1 ===========================================================

# P1 find callback functions name
spatch  --dir . --no-includes -D part1 --sp-file $spatch_file

echo PART2 ===========================================================

# P2 change callback function prototype
cat /tmp/unicorn-functions | sort | uniq | while read func ; do
    for file in $( git grep -l $func -- '*.[ch]' ) ; do
        echo $file
        spatch --no-includes --in-place -D part2 \
               -D fn=$func --sp-file $spatch_file $file
    done
done

echo PART 3 ==========================================================

# P3 find all function which call the callback
spatch --dir . --include-headers -D part3 --sp-file $spatch_file

echo PART 4===========================================================

# P4 change all funcitons which call the callback
cat /tmp/unicorn-files | sort | uniq | while read file ; do
    echo $file
    spatch --no-includes --in-place -D part4 \
           --sp-file $spatch_file $file
done
-------------------------------------------------------------------->%

With the following semantic patch:

%<--------------------------------------------------------------------
virtual part1, part2, part3, part4

// ----------------------------------------------------------------------------
// Part 1 is grepping all function that are use as callback for isolate_page.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part1@
@@
file=open('/tmp/unicorn-functions', 'w')
file.close()

// match function name use as a callback
@p1r2 depends on part1@
identifier I1, FN;
@@
struct address_space_operations I1 = {..., .isolate_page = FN, ...};

@script:python p1r3 depends on p1r2@
funcname << p1r2.FN;
@@
if funcname != "NULL":
  file=open('/tmp/unicorn-functions', 'a')
  file.write(funcname + '\n')
  file.close()

// -------------------------------------------------------------------
// Part 2 modify callback

// Add address_space argument to the function (isolate_page callback one)
@p2r1 depends on part2@
identifier virtual.fn;
identifier I1, I2;
type R, T1, T2;
@@
R fn(
+struct address_space *__mapping,
T1 I1, T2 I2) { ... }

@p2r2 depends on part2@
identifier virtual.fn;
identifier I1, I2;
type R, T1, T2;
@@
R fn(
+struct address_space *__mapping,
T1 I1, T2 I2);

@p2r3 depends on part2@
identifier virtual.fn;
expression E1, E2;
@@
fn(
+MAPPING_NULL,
E1, E2)

// ----------------------------------------------------------------------------
// Part 3 is grepping all function that are use the callback for isolate_page.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part3@
@@
file=open('/tmp/unicorn-files', 'w')
file.write("./include/linux/pagemap.h\n")
file.write("./include/linux/mm.h\n")
file.write("./include/linux/fs.h\n")
file.write("./mm/readahead.c\n")
file.write("./mm/filemap.c\n")
file.close()

@p3r1 depends on part3 exists@
expression E1, E2, E3;
identifier FN;
position P;
@@
FN@P(...) {...
(
E1.a_ops->isolate_page(E2, E3)
|
E1->a_ops->isolate_page(E2, E3)
)
...}

@script:python p3r2 depends on p3r1@
P << p3r1.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

// -------------------------------------------------------------------
// Part 4 generic modification
@p4r1 depends on part4@
type R;
@@
struct address_space_operations { ... R (*isolate_page)(
+struct address_space *,
struct page *, ...); ... };

@p4r2 depends on part4@
expression E1, E2, E3;
@@
E1.a_ops->isolate_page(
+MAPPING_NULL,
E2, E3)

@p4r3 depends on part4@
expression E1, E2, E3;
@@
E1->a_ops->isolate_page(
+MAPPING_NULL,
E2, E3)
-------------------------------------------------------------------->%

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>
---
 include/linux/balloon_compaction.h | 5 +++--
 include/linux/fs.h                 | 3 ++-
 mm/balloon_compaction.c            | 3 ++-
 mm/migrate.c                       | 2 +-
 mm/z3fold.c                        | 3 ++-
 mm/zsmalloc.c                      | 3 ++-
 6 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/include/linux/balloon_compaction.h b/include/linux/balloon_compaction.h
index 07b235161d040..c1e45339a53c1 100644
--- a/include/linux/balloon_compaction.h
+++ b/include/linux/balloon_compaction.h
@@ -80,8 +80,9 @@ static inline void balloon_devinfo_init(struct balloon_dev_info *balloon)
 
 #ifdef CONFIG_BALLOON_COMPACTION
 extern const struct address_space_operations balloon_aops;
-extern bool balloon_page_isolate(struct page *page,
-				isolate_mode_t mode);
+extern bool balloon_page_isolate(struct address_space *__mapping,
+				 struct page *page,
+				 isolate_mode_t mode);
 extern void balloon_page_putback(struct address_space *__mapping,
 				 struct page *page);
 extern int balloon_page_migrate(struct address_space *mapping,
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 21f179e7c5daa..6798a13e3c980 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -408,7 +408,8 @@ struct address_space_operations {
 	 */
 	int (*migratepage) (struct address_space *,
 			struct page *, struct page *, enum migrate_mode);
-	bool (*isolate_page)(struct page *, isolate_mode_t);
+	bool (*isolate_page)(struct address_space *, struct page *,
+			     isolate_mode_t);
 	void (*putback_page)(struct address_space *, struct page *);
 	int (*launder_page) (struct address_space *, struct page *);
 	int (*is_partially_uptodate) (struct address_space *, struct page *,
diff --git a/mm/balloon_compaction.c b/mm/balloon_compaction.c
index abc4a63df9903..ed9120e6c9bfe 100644
--- a/mm/balloon_compaction.c
+++ b/mm/balloon_compaction.c
@@ -203,7 +203,8 @@ EXPORT_SYMBOL_GPL(balloon_page_dequeue);
 
 #ifdef CONFIG_BALLOON_COMPACTION
 
-bool balloon_page_isolate(struct page *page, isolate_mode_t mode)
+bool balloon_page_isolate(struct address_space *__mapping, struct page *page,
+			  isolate_mode_t mode)
 
 {
 	struct balloon_dev_info *b_dev_info = balloon_page_device(page);
diff --git a/mm/migrate.c b/mm/migrate.c
index 3fba7429151bf..a3220050fbe05 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -126,7 +126,7 @@ int isolate_movable_page(struct page *page, isolate_mode_t mode)
 	mapping = page_mapping(page);
 	VM_BUG_ON_PAGE(!mapping, page);
 
-	if (!mapping->a_ops->isolate_page(page, mode))
+	if (!mapping->a_ops->isolate_page(MAPPING_NULL, page, mode))
 		goto out_no_isolated;
 
 	/* Driver shouldn't use PG_isolated bit of page->flags */
diff --git a/mm/z3fold.c b/mm/z3fold.c
index 37453a14257a4..4ca02bb55a56b 100644
--- a/mm/z3fold.c
+++ b/mm/z3fold.c
@@ -1566,7 +1566,8 @@ static u64 z3fold_get_pool_size(struct z3fold_pool *pool)
 	return atomic64_read(&pool->pages_nr);
 }
 
-static bool z3fold_page_isolate(struct page *page, isolate_mode_t mode)
+static bool z3fold_page_isolate(struct address_space *__mapping,
+				struct page *page, isolate_mode_t mode)
 {
 	struct z3fold_header *zhdr;
 	struct z3fold_pool *pool;
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 99d74c6e98216..c896f069e5b0a 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -1914,7 +1914,8 @@ static void replace_sub_page(struct size_class *class, struct zspage *zspage,
 	__SetPageMovable(newpage, page_mapping(oldpage));
 }
 
-static bool zs_page_isolate(struct page *page, isolate_mode_t mode)
+static bool zs_page_isolate(struct address_space *__mapping,
+			    struct page *page, isolate_mode_t mode)
 {
 	struct zs_pool *pool;
 	struct size_class *class;
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 14/14] mm: add struct address_space to is_dirty_writeback() callback
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
                   ` (11 preceding siblings ...)
  2020-10-07  1:06 ` [PATCH 13/14] mm: add struct address_space to isolate_page() callback jglisse
@ 2020-10-07  1:06 ` jglisse
  2020-10-07  3:20 ` [PATCH 00/14] Small step toward KSM for file back page Matthew Wilcox
  13 siblings, 0 replies; 24+ messages in thread
From: jglisse @ 2020-10-07  1:06 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jérôme Glisse, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

From: Jérôme Glisse <jglisse@redhat.com>

This is part of patchset to remove dependency on struct page.mapping
field so that we can temporarily update it to point to a special
structure tracking temporary page state (note that original mapping
pointer is preserved and can still be accessed but at a cost).

Add struct address_space to is_dirty_writeback() callback arguments.

Note that this patch does not make use of the new argument, nor does
it use a valid one at call site (by default this patch just use NULL
for new argument value).

Use following script (from root of linux kernel tree):

./that-script.sh that-semantic-patch.spatch

%<--------------------------------------------------------------------
#!/bin/sh
spatch_file=$1

echo PART1 ===========================================================

# P1 find callback functions name
spatch  --dir . --no-includes -D part1 --sp-file $spatch_file

echo PART2 ===========================================================

# P2 change callback function prototype
cat /tmp/unicorn-functions | sort | uniq | while read func ; do
    for file in $( git grep -l $func -- '*.[ch]' ) ; do
        echo $file
        spatch --no-includes --in-place -D part2 \
               -D fn=$func --sp-file $spatch_file $file
    done
done

echo PART 3 ==========================================================

# P3 find all function which call the callback
spatch --dir . --include-headers -D part3 --sp-file $spatch_file

echo PART 4===========================================================

# P4 change all funcitons which call the callback
cat /tmp/unicorn-files | sort | uniq | while read file ; do
    echo $file
    spatch --no-includes --in-place -D part4 \
           --sp-file $spatch_file $file
done
-------------------------------------------------------------------->%

With the following semantic patch:

%<--------------------------------------------------------------------
virtual part1, part2, part3, part4

// ----------------------------------------------------------------------------
// Part 1 is grepping all function that are use as callback for is_dirty_writeback.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part1@
@@
file=open('/tmp/unicorn-functions', 'w')
file.close()

// match function name use as a callback
@p1r2 depends on part1@
identifier I1, FN;
@@
struct address_space_operations I1 = {..., .is_dirty_writeback = FN, ...};

@script:python p1r3 depends on p1r2@
funcname << p1r2.FN;
@@
if funcname != "NULL":
  file=open('/tmp/unicorn-functions', 'a')
  file.write(funcname + '\n')
  file.close()

// -------------------------------------------------------------------
// Part 2 modify callback

@p2r1 depends on part2@
identifier virtual.fn;
identifier I1, I2, I3;
type T1, T2, T3;
@@
void fn(
+struct address_space *__mapping,
T1 I1, T2 I2, T3 I3) { ... }

@p2r2 depends on part2@
identifier virtual.fn;
identifier I1, I2, I3;
type T1, T2, T3;
@@
void fn(
+struct address_space *__mapping,
T1 I1, T2 I2, T3 I3);

@p2r3 depends on part2@
identifier virtual.fn;
expression E1, E2, E3;
@@
fn(
+MAPPING_NULL,
E1, E2, E3)

// ----------------------------------------------------------------------------
// Part 3 is grepping all function that are use the callback for is_dirty_writeback.

// initialize file where we collect all function name (erase it)
@initialize:python depends on part3@
@@
file=open('/tmp/unicorn-files', 'w')
file.write("./include/linux/pagemap.h\n")
file.write("./include/linux/mm.h\n")
file.write("./include/linux/fs.h\n")
file.write("./mm/readahead.c\n")
file.write("./mm/filemap.c\n")
file.close()

@p3r1 depends on part3 exists@
expression E1, E2, E3, E4;
identifier FN;
position P;
@@
FN@P(...) {...
(
E1.a_ops->is_dirty_writeback(E2, E3, E4)
|
E1->a_ops->is_dirty_writeback(E2, E3, E4)
)
...}

@script:python p3r2 depends on p3r1@
P << p3r1.P;
@@
file=open('/tmp/unicorn-files', 'a')
file.write(P[0].file + '\n')
file.close()

// -------------------------------------------------------------------
// Part 4 generic modification
@p4r1 depends on part4@
@@
struct address_space_operations { ... void (*is_dirty_writeback)(
+struct address_space *,
struct page *, ...); ... };

@p4r2 depends on part4@
expression E1, E2, E3, E4;
@@
E1.a_ops->is_dirty_writeback(
+MAPPING_NULL,
E2, E3, E4)

@p4r3 depends on part4@
expression E1, E2, E3, E4;
@@
E1->a_ops->is_dirty_writeback(
+MAPPING_NULL,
E2, E3, E4)
-------------------------------------------------------------------->%

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Josef Bacik <jbacik@fb.com>
---
 fs/buffer.c                 | 3 ++-
 fs/nfs/file.c               | 5 +++--
 include/linux/buffer_head.h | 3 ++-
 include/linux/fs.h          | 3 ++-
 mm/vmscan.c                 | 3 ++-
 5 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/fs/buffer.c b/fs/buffer.c
index 7adf0af7530ba..d050ef5bf9d7b 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -83,7 +83,8 @@ EXPORT_SYMBOL(unlock_buffer);
  * are unlocked and clean then the PageDirty information is stale. If
  * any of the pages are locked, it is assumed they are locked for IO.
  */
-void buffer_check_dirty_writeback(struct page *page,
+void buffer_check_dirty_writeback(struct address_space *__mapping,
+				     struct page *page,
 				     bool *dirty, bool *writeback)
 {
 	struct buffer_head *head, *bh;
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index b1ba143de48d9..d99b2db7ba3a3 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -437,8 +437,9 @@ static int nfs_release_page(struct address_space *__mapping,
 	return nfs_fscache_release_page(page, gfp);
 }
 
-static void nfs_check_dirty_writeback(struct page *page,
-				bool *dirty, bool *writeback)
+static void nfs_check_dirty_writeback(struct address_space *__mapping,
+				      struct page *page,
+				      bool *dirty, bool *writeback)
 {
 	struct nfs_inode *nfsi;
 	struct address_space *mapping = page_file_mapping(page);
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 89a3758531889..b19998fa8e4d4 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -145,7 +145,8 @@ BUFFER_FNS(Defer_Completion, defer_completion)
 	})
 #define page_has_buffers(page)	PagePrivate(page)
 
-void buffer_check_dirty_writeback(struct page *page,
+void buffer_check_dirty_writeback(struct address_space *__mapping,
+				     struct page *page,
 				     bool *dirty, bool *writeback);
 
 /*
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 6798a13e3c980..ebdb961016925 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -415,7 +415,8 @@ struct address_space_operations {
 	int (*is_partially_uptodate) (struct address_space *, struct page *,
 					unsigned long,
 					unsigned long);
-	void (*is_dirty_writeback) (struct page *, bool *, bool *);
+	void (*is_dirty_writeback) (struct address_space *, struct page *,
+				    bool *, bool *);
 	int (*error_remove_page)(struct address_space *, struct page *);
 
 	/* swapfile support */
diff --git a/mm/vmscan.c b/mm/vmscan.c
index bae7fb9c3512a..d402f94e14f2f 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1058,7 +1058,8 @@ static void page_check_dirty_writeback(struct page *page,
 
 	mapping = page_mapping(page);
 	if (mapping && mapping->a_ops->is_dirty_writeback)
-		mapping->a_ops->is_dirty_writeback(page, dirty, writeback);
+		mapping->a_ops->is_dirty_writeback(MAPPING_NULL, page, dirty,
+						   writeback);
 }
 
 /*
-- 
2.26.2



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/14] Small step toward KSM for file back page.
  2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
                   ` (12 preceding siblings ...)
  2020-10-07  1:06 ` [PATCH 14/14] mm: add struct address_space to is_dirty_writeback() callback jglisse
@ 2020-10-07  3:20 ` Matthew Wilcox
  2020-10-07 14:48   ` Jerome Glisse
  13 siblings, 1 reply; 24+ messages in thread
From: Matthew Wilcox @ 2020-10-07  3:20 UTC (permalink / raw)
  To: jglisse
  Cc: linux-kernel, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

On Tue, Oct 06, 2020 at 09:05:49PM -0400, jglisse@redhat.com wrote:
> The present patchset just add mapping argument to the various vfs call-
> backs. It does not make use of that new parameter to avoid regression.
> I am posting this whole things as small contain patchset as it is rather
> big and i would like to make progress step by step.

Well, that's the problem.  This patch set is gigantic and unreviewable.
And it has no benefits.  The idea you present here was discussed at
LSFMM in Utah and I recall absolutely nobody being in favour of it.
You claim many wonderful features will be unlocked by this, but I think
they can all be achieved without doing any of this very disruptive work.

>  118 files changed, 722 insertions(+), 385 deletions(-)

mmm.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/14] Small step toward KSM for file back page.
  2020-10-07  3:20 ` [PATCH 00/14] Small step toward KSM for file back page Matthew Wilcox
@ 2020-10-07 14:48   ` Jerome Glisse
  2020-10-07 17:05     ` Matthew Wilcox
  0 siblings, 1 reply; 24+ messages in thread
From: Jerome Glisse @ 2020-10-07 14:48 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: linux-kernel, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

On Wed, Oct 07, 2020 at 04:20:13AM +0100, Matthew Wilcox wrote:
> On Tue, Oct 06, 2020 at 09:05:49PM -0400, jglisse@redhat.com wrote:
> > The present patchset just add mapping argument to the various vfs call-
> > backs. It does not make use of that new parameter to avoid regression.
> > I am posting this whole things as small contain patchset as it is rather
> > big and i would like to make progress step by step.
> 
> Well, that's the problem.  This patch set is gigantic and unreviewable.
> And it has no benefits.  The idea you present here was discussed at
> LSFMM in Utah and I recall absolutely nobody being in favour of it.
> You claim many wonderful features will be unlocked by this, but I think
> they can all be achieved without doing any of this very disruptive work.

You have any ideas on how to achieve them without such change ? I will
be more than happy for a simpler solution but i fail to see how you can
work around the need for a pointer inside struct page. Given struct
page can not grow it means you need to be able to overload one of the
existing field, at least i do not see any otherway.

Cheers,
Jérôme



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/14] Small step toward KSM for file back page.
  2020-10-07 14:48   ` Jerome Glisse
@ 2020-10-07 17:05     ` Matthew Wilcox
  2020-10-07 17:54       ` Jerome Glisse
  0 siblings, 1 reply; 24+ messages in thread
From: Matthew Wilcox @ 2020-10-07 17:05 UTC (permalink / raw)
  To: Jerome Glisse
  Cc: linux-kernel, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

On Wed, Oct 07, 2020 at 10:48:35AM -0400, Jerome Glisse wrote:
> On Wed, Oct 07, 2020 at 04:20:13AM +0100, Matthew Wilcox wrote:
> > On Tue, Oct 06, 2020 at 09:05:49PM -0400, jglisse@redhat.com wrote:
> > > The present patchset just add mapping argument to the various vfs call-
> > > backs. It does not make use of that new parameter to avoid regression.
> > > I am posting this whole things as small contain patchset as it is rather
> > > big and i would like to make progress step by step.
> > 
> > Well, that's the problem.  This patch set is gigantic and unreviewable.
> > And it has no benefits.  The idea you present here was discussed at
> > LSFMM in Utah and I recall absolutely nobody being in favour of it.
> > You claim many wonderful features will be unlocked by this, but I think
> > they can all be achieved without doing any of this very disruptive work.
> 
> You have any ideas on how to achieve them without such change ? I will
> be more than happy for a simpler solution but i fail to see how you can
> work around the need for a pointer inside struct page. Given struct
> page can not grow it means you need to be able to overload one of the
> existing field, at least i do not see any otherway.

The one I've spent the most time thinking about is sharing pages between
reflinked files.  My approach is to pull DAX entries into the main page
cache and have them reference the PFN directly.  It's not a struct page,
but we can find a struct page from it if we need it.  The struct page
would belong to a mapping that isn't part of the file.

For other things (NUMA distribution), we can point to something which
isn't a struct page and can be distiguished from a real struct page by a
bit somewhere (I have ideas for at least three bits in struct page that
could be used for this).  Then use a pointer in that data structure to
point to the real page.  Or do NUMA distribution at the inode level.
Have a way to get from (inode, node) to an address_space which contains
just regular pages.

Using main memory to cache DAX could be done today without any data
structure changes.  It just needs the DAX entries pulled up into the
main pagecache.  See earlier item.

Exclusive write access ... you could put a magic value in the pagecache
for pages which are exclusively for someone else's use and handle those
specially.  I don't entirely understand this use case.

I don't have time to work on all of these.  If there's one that
particularly interests you, let's dive deep into it and figure out how
you can do it without committing this kind of violence to struct page.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/14] Small step toward KSM for file back page.
  2020-10-07 17:05     ` Matthew Wilcox
@ 2020-10-07 17:54       ` Jerome Glisse
  2020-10-07 18:33         ` Matthew Wilcox
  2020-10-07 22:09         ` Matthew Wilcox
  0 siblings, 2 replies; 24+ messages in thread
From: Jerome Glisse @ 2020-10-07 17:54 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: linux-kernel, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

On Wed, Oct 07, 2020 at 06:05:58PM +0100, Matthew Wilcox wrote:
> On Wed, Oct 07, 2020 at 10:48:35AM -0400, Jerome Glisse wrote:
> > On Wed, Oct 07, 2020 at 04:20:13AM +0100, Matthew Wilcox wrote:
> > > On Tue, Oct 06, 2020 at 09:05:49PM -0400, jglisse@redhat.com wrote:
> > > > The present patchset just add mapping argument to the various vfs call-
> > > > backs. It does not make use of that new parameter to avoid regression.
> > > > I am posting this whole things as small contain patchset as it is rather
> > > > big and i would like to make progress step by step.
> > > 
> > > Well, that's the problem.  This patch set is gigantic and unreviewable.
> > > And it has no benefits.  The idea you present here was discussed at
> > > LSFMM in Utah and I recall absolutely nobody being in favour of it.
> > > You claim many wonderful features will be unlocked by this, but I think
> > > they can all be achieved without doing any of this very disruptive work.
> > 
> > You have any ideas on how to achieve them without such change ? I will
> > be more than happy for a simpler solution but i fail to see how you can
> > work around the need for a pointer inside struct page. Given struct
> > page can not grow it means you need to be able to overload one of the
> > existing field, at least i do not see any otherway.
> 
> The one I've spent the most time thinking about is sharing pages between
> reflinked files.  My approach is to pull DAX entries into the main page
> cache and have them reference the PFN directly.  It's not a struct page,
> but we can find a struct page from it if we need it.  The struct page
> would belong to a mapping that isn't part of the file.

You would need to do a lot of filesystem specific change to make sure
the fs understand the special mapping. It is doable but i feel it would
have a lot of fs specific part.


> For other things (NUMA distribution), we can point to something which
> isn't a struct page and can be distiguished from a real struct page by a
> bit somewhere (I have ideas for at least three bits in struct page that
> could be used for this).  Then use a pointer in that data structure to
> point to the real page.  Or do NUMA distribution at the inode level.
> Have a way to get from (inode, node) to an address_space which contains
> just regular pages.

How do you find all the copies ? KSM maintains a list for a reasons.
Same would be needed here because if you want to break the write prot
you need to find all the copy first. If you intend to walk page table
then how do you synchronize to avoid more copy to spawn while you
walk reverse mapping, we could lock the struct page i guess. Also how
do you walk device page table which are completely hidden from core mm.


> Using main memory to cache DAX could be done today without any data
> structure changes.  It just needs the DAX entries pulled up into the
> main pagecache.  See earlier item.
> 
> Exclusive write access ... you could put a magic value in the pagecache
> for pages which are exclusively for someone else's use and handle those
> specially.  I don't entirely understand this use case.

For this use case you need a callback to break the protection and it
needs to handle all cases ie not only write by CPU through file mapping
but also file write syscall and other syscall that can write to page
(pipe, ...).


> I don't have time to work on all of these.  If there's one that
> particularly interests you, let's dive deep into it and figure out how

I care about KSM, duplicate NUMA copy (not only for CPU but also
device) and write protection or exclusive write access. In each case
you need a list of all the copy (for KSM of the deduplicated page)
Having a special entry in the page cache does not sound like a good
option in many code path you would need to re-look the page cache to
find out if the page is in special state. If you use a bit flag in
struct page how do you get to the callback or to the copy/alias,
walk all the page tables ?

> you can do it without committing this kind of violence to struct page.

I do not see how i am doing violence to struct page :) The basis of
my approach is to pass down the mapping. We always have the mapping
at the top of the stack (either syscall entry point on a file or
through the vma when working on virtual address).

But we rarely pass down this mapping down the stack into the fs code.
I am only passing down the mapping through the bottom of the stack so
we do not need to rely of page->mapping all the time. I am not trying
to remove the page->mapping field, it is still usefull, i just want
to be able to overload it so that we can make KSM code generic and
allow to reuse that generic part for other usecase.

Cheers,
Jérôme



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/14] Small step toward KSM for file back page.
  2020-10-07 17:54       ` Jerome Glisse
@ 2020-10-07 18:33         ` Matthew Wilcox
  2020-10-07 21:45           ` Jerome Glisse
  2020-10-07 22:09         ` Matthew Wilcox
  1 sibling, 1 reply; 24+ messages in thread
From: Matthew Wilcox @ 2020-10-07 18:33 UTC (permalink / raw)
  To: Jerome Glisse
  Cc: linux-kernel, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

On Wed, Oct 07, 2020 at 01:54:19PM -0400, Jerome Glisse wrote:
> On Wed, Oct 07, 2020 at 06:05:58PM +0100, Matthew Wilcox wrote:
> > On Wed, Oct 07, 2020 at 10:48:35AM -0400, Jerome Glisse wrote:
> > > On Wed, Oct 07, 2020 at 04:20:13AM +0100, Matthew Wilcox wrote:
> > > > On Tue, Oct 06, 2020 at 09:05:49PM -0400, jglisse@redhat.com wrote:
> > > > > The present patchset just add mapping argument to the various vfs call-
> > > > > backs. It does not make use of that new parameter to avoid regression.
> > > > > I am posting this whole things as small contain patchset as it is rather
> > > > > big and i would like to make progress step by step.
> > > > 
> > > > Well, that's the problem.  This patch set is gigantic and unreviewable.
> > > > And it has no benefits.  The idea you present here was discussed at
> > > > LSFMM in Utah and I recall absolutely nobody being in favour of it.
> > > > You claim many wonderful features will be unlocked by this, but I think
> > > > they can all be achieved without doing any of this very disruptive work.
> > > 
> > > You have any ideas on how to achieve them without such change ? I will
> > > be more than happy for a simpler solution but i fail to see how you can
> > > work around the need for a pointer inside struct page. Given struct
> > > page can not grow it means you need to be able to overload one of the
> > > existing field, at least i do not see any otherway.
> > 
> > The one I've spent the most time thinking about is sharing pages between
> > reflinked files.  My approach is to pull DAX entries into the main page
> > cache and have them reference the PFN directly.  It's not a struct page,
> > but we can find a struct page from it if we need it.  The struct page
> > would belong to a mapping that isn't part of the file.
> 
> You would need to do a lot of filesystem specific change to make sure
> the fs understand the special mapping. It is doable but i feel it would
> have a lot of fs specific part.

I can't see any way to make it work without filesystem cooperation.

> > For other things (NUMA distribution), we can point to something which
> > isn't a struct page and can be distiguished from a real struct page by a
> > bit somewhere (I have ideas for at least three bits in struct page that
> > could be used for this).  Then use a pointer in that data structure to
> > point to the real page.  Or do NUMA distribution at the inode level.
> > Have a way to get from (inode, node) to an address_space which contains
> > just regular pages.
> 
> How do you find all the copies ? KSM maintains a list for a reasons.
> Same would be needed here because if you want to break the write prot
> you need to find all the copy first. If you intend to walk page table
> then how do you synchronize to avoid more copy to spawn while you
> walk reverse mapping, we could lock the struct page i guess. Also how
> do you walk device page table which are completely hidden from core mm.

You have the inode and you iterate over each mapping, looking up the page
that's in each mapping.  Or you use the i_mmap tree to find the pages.

> > Using main memory to cache DAX could be done today without any data
> > structure changes.  It just needs the DAX entries pulled up into the
> > main pagecache.  See earlier item.
> > 
> > Exclusive write access ... you could put a magic value in the pagecache
> > for pages which are exclusively for someone else's use and handle those
> > specially.  I don't entirely understand this use case.
> 
> For this use case you need a callback to break the protection and it
> needs to handle all cases ie not only write by CPU through file mapping
> but also file write syscall and other syscall that can write to page
> (pipe, ...).

If the page can't be found in the page cache, then by definition you
won't be able to write to them.

> > I don't have time to work on all of these.  If there's one that
> > particularly interests you, let's dive deep into it and figure out how
> 
> I care about KSM, duplicate NUMA copy (not only for CPU but also
> device) and write protection or exclusive write access. In each case
> you need a list of all the copy (for KSM of the deduplicated page)
> Having a special entry in the page cache does not sound like a good
> option in many code path you would need to re-look the page cache to
> find out if the page is in special state. If you use a bit flag in
> struct page how do you get to the callback or to the copy/alias,
> walk all the page tables ?

Like I said, something that _looks_ like a struct page.  At least looks
enough like a struct page that you can pull a pointer out of the page cache and check the bit.  But since it's not actually a struct page, you can
use the rest of the data structure for pointers to things you want to
track.  Like the real struct page.

> I do not see how i am doing violence to struct page :) The basis of
> my approach is to pass down the mapping. We always have the mapping
> at the top of the stack (either syscall entry point on a file or
> through the vma when working on virtual address).

Yes, you explained all that in Utah.  I wasn't impressed than, and I'm
not impressed now.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/14] Small step toward KSM for file back page.
  2020-10-07 18:33         ` Matthew Wilcox
@ 2020-10-07 21:45           ` Jerome Glisse
  0 siblings, 0 replies; 24+ messages in thread
From: Jerome Glisse @ 2020-10-07 21:45 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: linux-kernel, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

On Wed, Oct 07, 2020 at 07:33:16PM +0100, Matthew Wilcox wrote:
> On Wed, Oct 07, 2020 at 01:54:19PM -0400, Jerome Glisse wrote:
> > On Wed, Oct 07, 2020 at 06:05:58PM +0100, Matthew Wilcox wrote:
> > > On Wed, Oct 07, 2020 at 10:48:35AM -0400, Jerome Glisse wrote:
> > > > On Wed, Oct 07, 2020 at 04:20:13AM +0100, Matthew Wilcox wrote:
> > > > > On Tue, Oct 06, 2020 at 09:05:49PM -0400, jglisse@redhat.com wrote:
> > > For other things (NUMA distribution), we can point to something which

[...]

> > > isn't a struct page and can be distiguished from a real struct page by a
> > > bit somewhere (I have ideas for at least three bits in struct page that
> > > could be used for this).  Then use a pointer in that data structure to
> > > point to the real page.  Or do NUMA distribution at the inode level.
> > > Have a way to get from (inode, node) to an address_space which contains
> > > just regular pages.
> > 
> > How do you find all the copies ? KSM maintains a list for a reasons.
> > Same would be needed here because if you want to break the write prot
> > you need to find all the copy first. If you intend to walk page table
> > then how do you synchronize to avoid more copy to spawn while you
> > walk reverse mapping, we could lock the struct page i guess. Also how
> > do you walk device page table which are completely hidden from core mm.
> 
> You have the inode and you iterate over each mapping, looking up the page
> that's in each mapping.  Or you use the i_mmap tree to find the pages.

This would slow down for everyone as we would have to walk all mapping
each time we try to write to page. Also we a have mechanism for page
write back to avoid race between thread trying to write and write back.
We would also need something similar. Without mediating this through
struct page i do not see how to keep this reasonable from performance
point of view.


> > > I don't have time to work on all of these.  If there's one that
> > > particularly interests you, let's dive deep into it and figure out how
> > 
> > I care about KSM, duplicate NUMA copy (not only for CPU but also
> > device) and write protection or exclusive write access. In each case
> > you need a list of all the copy (for KSM of the deduplicated page)
> > Having a special entry in the page cache does not sound like a good
> > option in many code path you would need to re-look the page cache to
> > find out if the page is in special state. If you use a bit flag in
> > struct page how do you get to the callback or to the copy/alias,
> > walk all the page tables ?
> 
> Like I said, something that _looks_ like a struct page.  At least looks
> enough like a struct page that you can pull a pointer out of the page
> cache and check the bit.  But since it's not actually a struct page,
> you can use the rest of the data structure for pointers to things you
> want to track.  Like the real struct page.

What i fear is the added cost because it means we need to do this look-
up everytime to check and we also need proper locking to avoid races.
Adding an ancilliary struct and trying to keep everything synchronize
seems harder to me.

> 
> > I do not see how i am doing violence to struct page :) The basis of
> > my approach is to pass down the mapping. We always have the mapping
> > at the top of the stack (either syscall entry point on a file or
> > through the vma when working on virtual address).
> 
> Yes, you explained all that in Utah.  I wasn't impressed than, and I'm
> not impressed now.

Is this more of a taste thing or is there something specific you do not
like ?

Cheers,
Jérôme



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/14] Small step toward KSM for file back page.
  2020-10-07 17:54       ` Jerome Glisse
  2020-10-07 18:33         ` Matthew Wilcox
@ 2020-10-07 22:09         ` Matthew Wilcox
  2020-10-08 15:30           ` Jerome Glisse
  1 sibling, 1 reply; 24+ messages in thread
From: Matthew Wilcox @ 2020-10-07 22:09 UTC (permalink / raw)
  To: Jerome Glisse
  Cc: linux-kernel, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

On Wed, Oct 07, 2020 at 01:54:19PM -0400, Jerome Glisse wrote:
> > For other things (NUMA distribution), we can point to something which
> > isn't a struct page and can be distiguished from a real struct page by a
> > bit somewhere (I have ideas for at least three bits in struct page that
> > could be used for this).  Then use a pointer in that data structure to
> > point to the real page.  Or do NUMA distribution at the inode level.
> > Have a way to get from (inode, node) to an address_space which contains
> > just regular pages.
> 
> How do you find all the copies ? KSM maintains a list for a reasons.
> Same would be needed here because if you want to break the write prot
> you need to find all the copy first. If you intend to walk page table
> then how do you synchronize to avoid more copy to spawn while you
> walk reverse mapping, we could lock the struct page i guess. Also how
> do you walk device page table which are completely hidden from core mm.

So ... why don't you put a PageKsm page in the page cache?  That way you
can share code with the current KSM implementation.  You'd need
something like this:

+++ b/mm/filemap.c
@@ -1622,6 +1622,9 @@ struct page *find_lock_entry(struct address_space *mapping
, pgoff_t index)
                lock_page(page);
                /* Has the page been truncated? */
                if (unlikely(page->mapping != mapping)) {
+                       if (PageKsm(page)) {
+                               ...
+                       }
                        unlock_page(page);
                        put_page(page);
                        goto repeat;
@@ -1655,6 +1658,7 @@ struct page *find_lock_entry(struct address_space *mapping, pgoff_t index)
  * * %FGP_WRITE - The page will be written
  * * %FGP_NOFS - __GFP_FS will get cleared in gfp mask
  * * %FGP_NOWAIT - Don't get blocked by page lock
+ * * %FGP_KSM - Return KSM pages
  *
  * If %FGP_LOCK or %FGP_CREAT are specified then the function may sleep even
  * if the %GFP flags specified for %FGP_CREAT are atomic.
@@ -1687,6 +1691,11 @@ struct page *pagecache_get_page(struct address_space *mapping, pgoff_t index,
 
                /* Has the page been truncated? */
                if (unlikely(page->mapping != mapping)) {
+                       if (PageKsm(page) {
+                               if (fgp_flags & FGP_KSM)
+                                       return page;
+                               ...
+                       }
                        unlock_page(page);
                        put_page(page);
                        goto repeat;

I don't know what you want to do when you find a KSM page, so I just left
an ellipsis.



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/14] Small step toward KSM for file back page.
  2020-10-07 22:09         ` Matthew Wilcox
@ 2020-10-08 15:30           ` Jerome Glisse
  2020-10-08 15:43             ` Matthew Wilcox
  0 siblings, 1 reply; 24+ messages in thread
From: Jerome Glisse @ 2020-10-08 15:30 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: linux-kernel, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

On Wed, Oct 07, 2020 at 11:09:16PM +0100, Matthew Wilcox wrote:
> On Wed, Oct 07, 2020 at 01:54:19PM -0400, Jerome Glisse wrote:
> > > For other things (NUMA distribution), we can point to something which
> > > isn't a struct page and can be distiguished from a real struct page by a
> > > bit somewhere (I have ideas for at least three bits in struct page that
> > > could be used for this).  Then use a pointer in that data structure to
> > > point to the real page.  Or do NUMA distribution at the inode level.
> > > Have a way to get from (inode, node) to an address_space which contains
> > > just regular pages.
> > 
> > How do you find all the copies ? KSM maintains a list for a reasons.
> > Same would be needed here because if you want to break the write prot
> > you need to find all the copy first. If you intend to walk page table
> > then how do you synchronize to avoid more copy to spawn while you
> > walk reverse mapping, we could lock the struct page i guess. Also how
> > do you walk device page table which are completely hidden from core mm.
> 
> So ... why don't you put a PageKsm page in the page cache?  That way you
> can share code with the current KSM implementation.  You'd need
> something like this:

I do just that but there is no need to change anything in page cache.
So below code is not necessary. What you need is a way to find all
the copies so if you have a write fault (or any write access) then
from that fault you get the mapping and offset and you use that to
lookup the fs specific informations and de-duplicate the page with
new page and the fs specific informations. Hence the filesystem code
do not need to know anything it all happens in generic common code.

So flow is:

  Same as before:
    1 - write fault (address, vma)
    2 - regular write fault handler -> find page in page cache

  New to common page fault code:
    3 - ksm check in write fault common code (same as ksm today
        for anonymous page fault code path).
    4 - break ksm (address, vma) -> (file offset, mapping)
        4.a - use mapping and file offset to lookup the proper
              fs specific information that were save when the
              page was made ksm.
        4.b - allocate new page and initialize it with that
              information (and page content), update page cache
              and mappings ie all the pte who where pointing to
              the ksm for that mapping at that offset to now use
              the new page (like KSM for anonymous page today).

  Resume regular code path:
        mkwrite /|| set pte ...

Roughly the same for write ioctl (other cases goes through GUP
which itself goes through page fault code path). There is no
need to change page cache in anyway. Just common code path that
enable write to file back page.

The fs specific information is page->private, some of the flags
(page->flags) and page->indexi (file offset). Everytime a page
is deduplicated a copy of that information is save in an alias
struct which you can get to from the the share KSM page (page->
mapping is a pointer to ksm root struct which has a pointer to
list of all aliases).

> 
> +++ b/mm/filemap.c
> @@ -1622,6 +1622,9 @@ struct page *find_lock_entry(struct address_space *mapping
> , pgoff_t index)
>                 lock_page(page);
>                 /* Has the page been truncated? */
>                 if (unlikely(page->mapping != mapping)) {
> +                       if (PageKsm(page)) {
> +                               ...
> +                       }
>                         unlock_page(page);
>                         put_page(page);
>                         goto repeat;
> @@ -1655,6 +1658,7 @@ struct page *find_lock_entry(struct address_space *mapping, pgoff_t index)
>   * * %FGP_WRITE - The page will be written
>   * * %FGP_NOFS - __GFP_FS will get cleared in gfp mask
>   * * %FGP_NOWAIT - Don't get blocked by page lock
> + * * %FGP_KSM - Return KSM pages
>   *
>   * If %FGP_LOCK or %FGP_CREAT are specified then the function may sleep even
>   * if the %GFP flags specified for %FGP_CREAT are atomic.
> @@ -1687,6 +1691,11 @@ struct page *pagecache_get_page(struct address_space *mapping, pgoff_t index,
>  
>                 /* Has the page been truncated? */
>                 if (unlikely(page->mapping != mapping)) {
> +                       if (PageKsm(page) {
> +                               if (fgp_flags & FGP_KSM)
> +                                       return page;
> +                               ...
> +                       }
>                         unlock_page(page);
>                         put_page(page);
>                         goto repeat;
> 
> I don't know what you want to do when you find a KSM page, so I just left
> an ellipsis.
> 



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/14] Small step toward KSM for file back page.
  2020-10-08 15:30           ` Jerome Glisse
@ 2020-10-08 15:43             ` Matthew Wilcox
  2020-10-08 18:48               ` Jerome Glisse
  0 siblings, 1 reply; 24+ messages in thread
From: Matthew Wilcox @ 2020-10-08 15:43 UTC (permalink / raw)
  To: Jerome Glisse
  Cc: linux-kernel, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

On Thu, Oct 08, 2020 at 11:30:28AM -0400, Jerome Glisse wrote:
> On Wed, Oct 07, 2020 at 11:09:16PM +0100, Matthew Wilcox wrote:
> > So ... why don't you put a PageKsm page in the page cache?  That way you
> > can share code with the current KSM implementation.  You'd need
> > something like this:
> 
> I do just that but there is no need to change anything in page cache.

That's clearly untrue.  If you just put a PageKsm page in the page
cache today, here's what will happen on a truncate:

void truncate_inode_pages_range(struct address_space *mapping,
                                loff_t lstart, loff_t lend)
{
...
                struct page *page = find_lock_page(mapping, start - 1);

find_lock_page() does this:
        return pagecache_get_page(mapping, offset, FGP_LOCK, 0);

pagecache_get_page():

repeat:
        page = find_get_entry(mapping, index);
...
        if (fgp_flags & FGP_LOCK) {
...
                if (unlikely(compound_head(page)->mapping != mapping)) {
                        unlock_page(page);
                        put_page(page);
                        goto repeat;

so it's just going to spin.  There are plenty of other codepaths that
would need to be checked.  If you haven't found them, that shows you
don't understand the problem deeply enough yet.

I believe we should solve this problem, but I don't think you're going
about it the right way.

> So flow is:
> 
>   Same as before:
>     1 - write fault (address, vma)
>     2 - regular write fault handler -> find page in page cache
> 
>   New to common page fault code:
>     3 - ksm check in write fault common code (same as ksm today
>         for anonymous page fault code path).
>     4 - break ksm (address, vma) -> (file offset, mapping)
>         4.a - use mapping and file offset to lookup the proper
>               fs specific information that were save when the
>               page was made ksm.
>         4.b - allocate new page and initialize it with that
>               information (and page content), update page cache
>               and mappings ie all the pte who where pointing to
>               the ksm for that mapping at that offset to now use
>               the new page (like KSM for anonymous page today).

But by putting that logic in the page fault path, you've missed
the truncate path.  And maybe other places.  Putting the logic
down in pagecache_get_page() means you _don't_ need to find
all the places that call pagecache_get_page().



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/14] Small step toward KSM for file back page.
  2020-10-08 15:43             ` Matthew Wilcox
@ 2020-10-08 18:48               ` Jerome Glisse
  0 siblings, 0 replies; 24+ messages in thread
From: Jerome Glisse @ 2020-10-08 18:48 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: linux-kernel, linux-mm, linux-fsdevel, Andrew Morton,
	Alexander Viro, Tejun Heo, Jan Kara, Josef Bacik

On Thu, Oct 08, 2020 at 04:43:41PM +0100, Matthew Wilcox wrote:
> On Thu, Oct 08, 2020 at 11:30:28AM -0400, Jerome Glisse wrote:
> > On Wed, Oct 07, 2020 at 11:09:16PM +0100, Matthew Wilcox wrote:
> > > So ... why don't you put a PageKsm page in the page cache?  That way you
> > > can share code with the current KSM implementation.  You'd need
> > > something like this:
> > 
> > I do just that but there is no need to change anything in page cache.
> 
> That's clearly untrue.  If you just put a PageKsm page in the page
> cache today, here's what will happen on a truncate:
> 
> void truncate_inode_pages_range(struct address_space *mapping,
>                                 loff_t lstart, loff_t lend)
> {
> ...
>                 struct page *page = find_lock_page(mapping, start - 1);
> 
> find_lock_page() does this:
>         return pagecache_get_page(mapping, offset, FGP_LOCK, 0);
> 
> pagecache_get_page():
> 
> repeat:
>         page = find_get_entry(mapping, index);
> ...
>         if (fgp_flags & FGP_LOCK) {
> ...
>                 if (unlikely(compound_head(page)->mapping != mapping)) {
>                         unlock_page(page);
>                         put_page(page);
>                         goto repeat;
> 
> so it's just going to spin.  There are plenty of other codepaths that
> would need to be checked.  If you haven't found them, that shows you
> don't understand the problem deeply enough yet.

I also change truncate, splice and few other special cases that do
not goes through GUP/page fault/mkwrite (memory debug too but that's
a different beast).


> I believe we should solve this problem, but I don't think you're going
> about it the right way.

I have done much more than what i posted but there is bug that i
need to hammer down before posting everything and i wanted to get
the discussion started. I guess i will finish tracking that one
down and post the whole thing.


> > So flow is:
> > 
> >   Same as before:
> >     1 - write fault (address, vma)
> >     2 - regular write fault handler -> find page in page cache
> > 
> >   New to common page fault code:
> >     3 - ksm check in write fault common code (same as ksm today
> >         for anonymous page fault code path).
> >     4 - break ksm (address, vma) -> (file offset, mapping)
> >         4.a - use mapping and file offset to lookup the proper
> >               fs specific information that were save when the
> >               page was made ksm.
> >         4.b - allocate new page and initialize it with that
> >               information (and page content), update page cache
> >               and mappings ie all the pte who where pointing to
> >               the ksm for that mapping at that offset to now use
> >               the new page (like KSM for anonymous page today).
> 
> But by putting that logic in the page fault path, you've missed
> the truncate path.  And maybe other places.  Putting the logic
> down in pagecache_get_page() means you _don't_ need to find
> all the places that call pagecache_get_page().

They are cases where pagecache is not even in the loop ie you
already have the page and you do not need to look it up (page
fault, some fs common code, anything that goes through GUP,
memory reclaim, ...). Making all those places having to go
through page cache all the times will slow them down and many
are hot code path that i do not believe we want to slow even
if a feature is not use.

Cheers,
Jérôme



^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2020-10-08 18:49 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-07  1:05 [PATCH 00/14] Small step toward KSM for file back page jglisse
2020-10-07  1:05 ` [PATCH 02/14] fs: define filler_t as a function pointer type jglisse
2020-10-07  1:05 ` [PATCH 03/14] fs: directly use a_ops->freepage() instead of a local copy of it jglisse
2020-10-07  1:05 ` [PATCH 04/14] mm: add struct address_space to readpage() callback jglisse
2020-10-07  1:05 ` [PATCH 05/14] mm: add struct address_space to writepage() callback jglisse
2020-10-07  1:05 ` [PATCH 06/14] mm: add struct address_space to set_page_dirty() callback jglisse
2020-10-07  1:05 ` [PATCH 07/14] mm: add struct address_space to invalidatepage() callback jglisse
2020-10-07  1:05 ` [PATCH 08/14] mm: add struct address_space to releasepage() callback jglisse
2020-10-07  1:05 ` [PATCH 09/14] mm: add struct address_space to freepage() callback jglisse
2020-10-07  1:05 ` [PATCH 10/14] mm: add struct address_space to putback_page() callback jglisse
2020-10-07  1:06 ` [PATCH 11/14] mm: add struct address_space to launder_page() callback jglisse
2020-10-07  1:06 ` [PATCH 12/14] mm: add struct address_space to is_partially_uptodate() callback jglisse
2020-10-07  1:06 ` [PATCH 13/14] mm: add struct address_space to isolate_page() callback jglisse
2020-10-07  1:06 ` [PATCH 14/14] mm: add struct address_space to is_dirty_writeback() callback jglisse
2020-10-07  3:20 ` [PATCH 00/14] Small step toward KSM for file back page Matthew Wilcox
2020-10-07 14:48   ` Jerome Glisse
2020-10-07 17:05     ` Matthew Wilcox
2020-10-07 17:54       ` Jerome Glisse
2020-10-07 18:33         ` Matthew Wilcox
2020-10-07 21:45           ` Jerome Glisse
2020-10-07 22:09         ` Matthew Wilcox
2020-10-08 15:30           ` Jerome Glisse
2020-10-08 15:43             ` Matthew Wilcox
2020-10-08 18:48               ` Jerome Glisse

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).