Linux-Fsdevel Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH 00/10] fsdax: introduce fs query to support reflink
@ 2020-12-30 16:55 Shiyang Ruan
  2020-12-30 16:55 ` [PATCH 01/10] pagemap: Introduce ->memory_failure() Shiyang Ruan
                   ` (9 more replies)
  0 siblings, 10 replies; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-30 16:55 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, dan.j.williams, david,
	hch, song, rgoldwyn, qi.fuli, y-goto

This patchset is aimed to support shared pages tracking for fsdax.

Change from RFC v3:
  - Do not lock dax entry in memory failure handler
  - Add a helper function for corrupted_range
  - Add restrictions in xfs code
  - Fix code style
  - remove the useless association and lock in fsdax

Change from RFC v2:
  - Adjust the order of patches
  - Divide the infrastructure and the drivers that use it
  - Rebased to v5.10

Change from RFC v1:
  - Introduce ->block_lost() for block device
  - Support mapped device
  - Add 'not available' warning for realtime device in XFS
  - Rebased to v5.10-rc1

This patchset moves owner tracking from dax_assocaite_entry() to pmem
device driver, by introducing an interface ->memory_failure() of struct
pagemap.  This interface is called by memory_failure() in mm, and
implemented by pmem device.  Then pmem device calls its ->corrupted_range()
to find the filesystem which the corrupted data located in, and call
filesystem handler to track files or metadata assocaited with this page.
Finally we are able to try to fix the corrupted data in filesystem and do
other necessary processing, such as killing processes who are using the
files affected.

The call trace is like this:
memory_failure()
 pgmap->ops->memory_failure()      => pmem_pgmap_memory_failure()
  gendisk->fops->corrupted_range() => - pmem_corrupted_range()
                                      - md_blk_corrupted_range()
   sb->s_ops->currupted_range()    => xfs_fs_corrupted_range()
    xfs_rmap_query_range()
     xfs_currupt_helper()
      * corrupted on metadata
          try to recover data, call xfs_force_shutdown()
      * corrupted on file data 
          try to recover data, call mf_dax_mapping_kill_procs()

The fsdax & reflink support for XFS is not contained in this patchset.

(Rebased on v5.10)
--

Shiyang Ruan (10):
  pagemap: Introduce ->memory_failure()
  blk: Introduce ->corrupted_range() for block device
  fs: Introduce ->corrupted_range() for superblock
  mm, fsdax: Refactor memory-failure handler for dax mapping
  mm, pmem: Implement ->memory_failure() in pmem driver
  pmem: Implement ->corrupted_range() for pmem driver
  dm: Introduce ->rmap() to find bdev offset
  md: Implement ->corrupted_range()
  xfs: Implement ->corrupted_range() for XFS
  fs/dax: remove useless functions

 block/genhd.c                 |  12 ++++
 drivers/md/dm-linear.c        |   8 +++
 drivers/md/dm.c               |  54 ++++++++++++++
 drivers/nvdimm/pmem.c         |  43 +++++++++++
 fs/block_dev.c                |  37 ++++++++++
 fs/dax.c                      | 115 ++++-------------------------
 fs/xfs/xfs_fsops.c            |   5 ++
 fs/xfs/xfs_mount.h            |   1 +
 fs/xfs/xfs_super.c            | 107 +++++++++++++++++++++++++++
 include/linux/blkdev.h        |   2 +
 include/linux/dax.h           |   3 +-
 include/linux/device-mapper.h |   2 +
 include/linux/fs.h            |   2 +
 include/linux/genhd.h         |   3 +
 include/linux/memremap.h      |   8 +++
 include/linux/mm.h            |   9 +++
 mm/memory-failure.c           | 131 ++++++++++++++++++----------------
 17 files changed, 375 insertions(+), 167 deletions(-)

-- 
2.29.2




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

* [PATCH 01/10] pagemap: Introduce ->memory_failure()
  2020-12-30 16:55 [PATCH 00/10] fsdax: introduce fs query to support reflink Shiyang Ruan
@ 2020-12-30 16:55 ` Shiyang Ruan
  2020-12-30 16:55 ` [PATCH 02/10] blk: Introduce ->corrupted_range() for block device Shiyang Ruan
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-30 16:55 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, dan.j.williams, david,
	hch, song, rgoldwyn, qi.fuli, y-goto

When memory-failure occurs, we call this function which is implemented
by each kind of devices.  For the fsdax case, pmem device driver
implements it.  Pmem device driver will find out the block device where
the error page locates in, and try to get the filesystem on this block
device.  And finally call filesystem handler to deal with the error.
The filesystem will try to recover the corrupted data if possiable.

Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
---
 include/linux/memremap.h | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/include/linux/memremap.h b/include/linux/memremap.h
index 79c49e7f5c30..0bcf2b1e20bd 100644
--- a/include/linux/memremap.h
+++ b/include/linux/memremap.h
@@ -87,6 +87,14 @@ struct dev_pagemap_ops {
 	 * the page back to a CPU accessible page.
 	 */
 	vm_fault_t (*migrate_to_ram)(struct vm_fault *vmf);
+
+	/*
+	 * Handle the memory failure happens on one page.  Notify the processes
+	 * who are using this page, and try to recover the data on this page
+	 * if necessary.
+	 */
+	int (*memory_failure)(struct dev_pagemap *pgmap, unsigned long pfn,
+			      int flags);
 };
 
 #define PGMAP_ALTMAP_VALID	(1 << 0)
-- 
2.29.2




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

* [PATCH 02/10] blk: Introduce ->corrupted_range() for block device
  2020-12-30 16:55 [PATCH 00/10] fsdax: introduce fs query to support reflink Shiyang Ruan
  2020-12-30 16:55 ` [PATCH 01/10] pagemap: Introduce ->memory_failure() Shiyang Ruan
@ 2020-12-30 16:55 ` Shiyang Ruan
  2021-01-08  9:55   ` Christoph Hellwig
  2020-12-30 16:55 ` [PATCH 03/10] fs: Introduce ->corrupted_range() for superblock Shiyang Ruan
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-30 16:55 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, dan.j.williams, david,
	hch, song, rgoldwyn, qi.fuli, y-goto

In fsdax mode, the memory failure happens on block device.  So, it is
needed to introduce an interface for block devices.  Each kind of block
device can handle the memory failure in ther own ways.

Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
---
 include/linux/blkdev.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 033eb5f73b65..45256fe84fa7 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1858,6 +1858,8 @@ struct block_device_operations {
 	int (*report_zones)(struct gendisk *, sector_t sector,
 			unsigned int nr_zones, report_zones_cb cb, void *data);
 	char *(*devnode)(struct gendisk *disk, umode_t *mode);
+	int (*corrupted_range)(struct gendisk *disk, struct block_device *bdev,
+			       loff_t offset, size_t len, void *data);
 	struct module *owner;
 	const struct pr_ops *pr_ops;
 };
-- 
2.29.2




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

* [PATCH 03/10] fs: Introduce ->corrupted_range() for superblock
  2020-12-30 16:55 [PATCH 00/10] fsdax: introduce fs query to support reflink Shiyang Ruan
  2020-12-30 16:55 ` [PATCH 01/10] pagemap: Introduce ->memory_failure() Shiyang Ruan
  2020-12-30 16:55 ` [PATCH 02/10] blk: Introduce ->corrupted_range() for block device Shiyang Ruan
@ 2020-12-30 16:55 ` Shiyang Ruan
  2021-01-08  9:56   ` Christoph Hellwig
  2020-12-30 16:55 ` [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping Shiyang Ruan
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-30 16:55 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, dan.j.williams, david,
	hch, song, rgoldwyn, qi.fuli, y-goto

Memory failure occurs in fsdax mode will finally be handled in
filesystem.  We introduce this interface to find out files or metadata
affected by the corrupted range, and try to recover the corrupted data
if possiable.

Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
---
 include/linux/fs.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 8667d0cdc71e..282e2139b23e 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1965,6 +1965,8 @@ struct super_operations {
 				  struct shrink_control *);
 	long (*free_cached_objects)(struct super_block *,
 				    struct shrink_control *);
+	int (*corrupted_range)(struct super_block *sb, struct block_device *bdev,
+			       loff_t offset, size_t len, void *data);
 };
 
 /*
-- 
2.29.2




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

* [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping
  2020-12-30 16:55 [PATCH 00/10] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (2 preceding siblings ...)
  2020-12-30 16:55 ` [PATCH 03/10] fs: Introduce ->corrupted_range() for superblock Shiyang Ruan
@ 2020-12-30 16:55 ` Shiyang Ruan
  2021-01-06 15:41   ` Jan Kara
  2021-01-14 20:38   ` Dan Williams
  2020-12-30 16:55 ` [PATCH 05/10] mm, pmem: Implement ->memory_failure() in pmem driver Shiyang Ruan
                   ` (5 subsequent siblings)
  9 siblings, 2 replies; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-30 16:55 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, dan.j.williams, david,
	hch, song, rgoldwyn, qi.fuli, y-goto

The current memory_failure_dev_pagemap() can only handle single-mapped
dax page for fsdax mode.  The dax page could be mapped by multiple files
and offsets if we let reflink feature & fsdax mode work together.  So,
we refactor current implementation to support handle memory failure on
each file and offset.

Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
---
 fs/dax.c            | 21 +++++++++++
 include/linux/dax.h |  1 +
 include/linux/mm.h  |  9 +++++
 mm/memory-failure.c | 91 ++++++++++++++++++++++++++++++++++-----------
 4 files changed, 100 insertions(+), 22 deletions(-)

diff --git a/fs/dax.c b/fs/dax.c
index 5b47834f2e1b..799210cfa687 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -378,6 +378,27 @@ static struct page *dax_busy_page(void *entry)
 	return NULL;
 }
 
+/*
+ * dax_load_pfn - Load pfn of the DAX entry corresponding to a page
+ * @mapping: The file whose entry we want to load
+ * @index:   The offset where the DAX entry located in
+ *
+ * Return:   pfn of the DAX entry
+ */
+unsigned long dax_load_pfn(struct address_space *mapping, unsigned long index)
+{
+	XA_STATE(xas, &mapping->i_pages, index);
+	void *entry;
+	unsigned long pfn;
+
+	xas_lock_irq(&xas);
+	entry = xas_load(&xas);
+	pfn = dax_to_pfn(entry);
+	xas_unlock_irq(&xas);
+
+	return pfn;
+}
+
 /*
  * dax_lock_mapping_entry - Lock the DAX entry corresponding to a page
  * @page: The page whose entry we want to lock
diff --git a/include/linux/dax.h b/include/linux/dax.h
index b52f084aa643..89e56ceeffc7 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -150,6 +150,7 @@ int dax_writeback_mapping_range(struct address_space *mapping,
 
 struct page *dax_layout_busy_page(struct address_space *mapping);
 struct page *dax_layout_busy_page_range(struct address_space *mapping, loff_t start, loff_t end);
+unsigned long dax_load_pfn(struct address_space *mapping, unsigned long index);
 dax_entry_t dax_lock_page(struct page *page);
 void dax_unlock_page(struct page *page, dax_entry_t cookie);
 #else
diff --git a/include/linux/mm.h b/include/linux/mm.h
index db6ae4d3fb4e..db3059a1853e 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1141,6 +1141,14 @@ static inline bool is_device_private_page(const struct page *page)
 		page->pgmap->type == MEMORY_DEVICE_PRIVATE;
 }
 
+static inline bool is_device_fsdax_page(const struct page *page)
+{
+	return IS_ENABLED(CONFIG_DEV_PAGEMAP_OPS) &&
+		IS_ENABLED(CONFIG_DEVICE_PRIVATE) &&
+		is_zone_device_page(page) &&
+		page->pgmap->type == MEMORY_DEVICE_FS_DAX;
+}
+
 static inline bool is_pci_p2pdma_page(const struct page *page)
 {
 	return IS_ENABLED(CONFIG_DEV_PAGEMAP_OPS) &&
@@ -3030,6 +3038,7 @@ enum mf_flags {
 	MF_MUST_KILL = 1 << 2,
 	MF_SOFT_OFFLINE = 1 << 3,
 };
+extern int mf_dax_mapping_kill_procs(struct address_space *mapping, pgoff_t index, int flags);
 extern int memory_failure(unsigned long pfn, int flags);
 extern void memory_failure_queue(unsigned long pfn, int flags);
 extern void memory_failure_queue_kick(int cpu);
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 5d880d4eb9a2..37bc6e2a9564 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -56,6 +56,7 @@
 #include <linux/kfifo.h>
 #include <linux/ratelimit.h>
 #include <linux/page-isolation.h>
+#include <linux/dax.h>
 #include "internal.h"
 #include "ras/ras_event.h"
 
@@ -120,6 +121,9 @@ static int hwpoison_filter_dev(struct page *p)
 	if (PageSlab(p))
 		return -EINVAL;
 
+	if (is_device_fsdax_page(p))
+		return 0;
+
 	mapping = page_mapping(p);
 	if (mapping == NULL || mapping->host == NULL)
 		return -EINVAL;
@@ -290,9 +294,8 @@ void shake_page(struct page *p, int access)
 EXPORT_SYMBOL_GPL(shake_page);
 
 static unsigned long dev_pagemap_mapping_shift(struct page *page,
-		struct vm_area_struct *vma)
+		struct vm_area_struct *vma, unsigned long address)
 {
-	unsigned long address = vma_address(page, vma);
 	pgd_t *pgd;
 	p4d_t *p4d;
 	pud_t *pud;
@@ -333,8 +336,8 @@ static unsigned long dev_pagemap_mapping_shift(struct page *page,
  * Uses GFP_ATOMIC allocations to avoid potential recursions in the VM.
  */
 static void add_to_kill(struct task_struct *tsk, struct page *p,
-		       struct vm_area_struct *vma,
-		       struct list_head *to_kill)
+			struct address_space *mapping, pgoff_t pgoff,
+			struct vm_area_struct *vma, struct list_head *to_kill)
 {
 	struct to_kill *tk;
 
@@ -345,9 +348,12 @@ static void add_to_kill(struct task_struct *tsk, struct page *p,
 	}
 
 	tk->addr = page_address_in_vma(p, vma);
-	if (is_zone_device_page(p))
-		tk->size_shift = dev_pagemap_mapping_shift(p, vma);
-	else
+	if (is_zone_device_page(p)) {
+		if (is_device_fsdax_page(p))
+			tk->addr = vma->vm_start +
+					((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
+		tk->size_shift = dev_pagemap_mapping_shift(p, vma, tk->addr);
+	} else
 		tk->size_shift = page_shift(compound_head(p));
 
 	/*
@@ -495,7 +501,7 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill,
 			if (!page_mapped_in_vma(page, vma))
 				continue;
 			if (vma->vm_mm == t->mm)
-				add_to_kill(t, page, vma, to_kill);
+				add_to_kill(t, page, NULL, 0, vma, to_kill);
 		}
 	}
 	read_unlock(&tasklist_lock);
@@ -505,24 +511,19 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill,
 /*
  * Collect processes when the error hit a file mapped page.
  */
-static void collect_procs_file(struct page *page, struct list_head *to_kill,
-				int force_early)
+static void collect_procs_file(struct page *page, struct address_space *mapping,
+		pgoff_t pgoff, struct list_head *to_kill, int force_early)
 {
 	struct vm_area_struct *vma;
 	struct task_struct *tsk;
-	struct address_space *mapping = page->mapping;
-	pgoff_t pgoff;
 
 	i_mmap_lock_read(mapping);
 	read_lock(&tasklist_lock);
-	pgoff = page_to_pgoff(page);
 	for_each_process(tsk) {
 		struct task_struct *t = task_early_kill(tsk, force_early);
-
 		if (!t)
 			continue;
-		vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff,
-				      pgoff) {
+		vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, pgoff) {
 			/*
 			 * Send early kill signal to tasks where a vma covers
 			 * the page but the corrupted page is not necessarily
@@ -531,7 +532,7 @@ static void collect_procs_file(struct page *page, struct list_head *to_kill,
 			 * to be informed of all such data corruptions.
 			 */
 			if (vma->vm_mm == t->mm)
-				add_to_kill(t, page, vma, to_kill);
+				add_to_kill(t, page, mapping, pgoff, vma, to_kill);
 		}
 	}
 	read_unlock(&tasklist_lock);
@@ -550,7 +551,8 @@ static void collect_procs(struct page *page, struct list_head *tokill,
 	if (PageAnon(page))
 		collect_procs_anon(page, tokill, force_early);
 	else
-		collect_procs_file(page, tokill, force_early);
+		collect_procs_file(page, page->mapping, page_to_pgoff(page),
+				   tokill, force_early);
 }
 
 static const char *action_name[] = {
@@ -1147,6 +1149,50 @@ static int try_to_split_thp_page(struct page *page, const char *msg)
 	return 0;
 }
 
+int mf_dax_mapping_kill_procs(struct address_space *mapping, pgoff_t index, int flags)
+{
+	const bool unmap_success = true;
+	unsigned long pfn, size = 0;
+	struct to_kill *tk;
+	LIST_HEAD(to_kill);
+	int rc = -EBUSY;
+	loff_t start;
+
+	/* load the pfn of the dax mapping file */
+	pfn = dax_load_pfn(mapping, index);
+	if (!pfn)
+		return rc;
+	/*
+	 * Unlike System-RAM there is no possibility to swap in a
+	 * different physical page at a given virtual address, so all
+	 * userspace consumption of ZONE_DEVICE memory necessitates
+	 * SIGBUS (i.e. MF_MUST_KILL)
+	 */
+	flags |= MF_ACTION_REQUIRED | MF_MUST_KILL;
+	collect_procs_file(pfn_to_page(pfn), mapping, index, &to_kill,
+			   flags & MF_ACTION_REQUIRED);
+
+	list_for_each_entry(tk, &to_kill, nd)
+		if (tk->size_shift)
+			size = max(size, 1UL << tk->size_shift);
+	if (size) {
+		/*
+		 * Unmap the largest mapping to avoid breaking up
+		 * device-dax mappings which are constant size. The
+		 * actual size of the mapping being torn down is
+		 * communicated in siginfo, see kill_proc()
+		 */
+		start = (index << PAGE_SHIFT) & ~(size - 1);
+		unmap_mapping_range(mapping, start, start + size, 0);
+	}
+
+	kill_procs(&to_kill, flags & MF_MUST_KILL, !unmap_success,
+		   pfn, flags);
+	rc = 0;
+	return rc;
+}
+EXPORT_SYMBOL_GPL(mf_dax_mapping_kill_procs);
+
 static int memory_failure_hugetlb(unsigned long pfn, int flags)
 {
 	struct page *p = pfn_to_page(pfn);
@@ -1226,7 +1272,7 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 	const bool unmap_success = true;
 	unsigned long size = 0;
 	struct to_kill *tk;
-	LIST_HEAD(tokill);
+	LIST_HEAD(to_kill);
 	int rc = -EBUSY;
 	loff_t start;
 	dax_entry_t cookie;
@@ -1268,9 +1314,10 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 	 * SIGBUS (i.e. MF_MUST_KILL)
 	 */
 	flags |= MF_ACTION_REQUIRED | MF_MUST_KILL;
-	collect_procs(page, &tokill, flags & MF_ACTION_REQUIRED);
+	collect_procs_file(page, page->mapping, page->index, &to_kill,
+			   flags & MF_ACTION_REQUIRED);
 
-	list_for_each_entry(tk, &tokill, nd)
+	list_for_each_entry(tk, &to_kill, nd)
 		if (tk->size_shift)
 			size = max(size, 1UL << tk->size_shift);
 	if (size) {
@@ -1283,7 +1330,7 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 		start = (page->index << PAGE_SHIFT) & ~(size - 1);
 		unmap_mapping_range(page->mapping, start, start + size, 0);
 	}
-	kill_procs(&tokill, flags & MF_MUST_KILL, !unmap_success, pfn, flags);
+	kill_procs(&to_kill, flags & MF_MUST_KILL, !unmap_success, pfn, flags);
 	rc = 0;
 unlock:
 	dax_unlock_page(page, cookie);
-- 
2.29.2




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

* [PATCH 05/10] mm, pmem: Implement ->memory_failure() in pmem driver
  2020-12-30 16:55 [PATCH 00/10] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (3 preceding siblings ...)
  2020-12-30 16:55 ` [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping Shiyang Ruan
@ 2020-12-30 16:55 ` Shiyang Ruan
  2021-01-06 15:55   ` Jan Kara
  2020-12-30 16:55 ` [PATCH 06/10] pmem: Implement ->corrupted_range() for " Shiyang Ruan
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-30 16:55 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, dan.j.williams, david,
	hch, song, rgoldwyn, qi.fuli, y-goto

Call the ->memory_failure() which is implemented by pmem driver, in
order to finally notify filesystem to handle the corrupted data.  The
old collecting and killing processes are moved into
mf_dax_mapping_kill_procs(), which will be called by filesystem.

Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
---
 drivers/nvdimm/pmem.c | 24 +++++++++++++++++++++
 mm/memory-failure.c   | 50 +++++--------------------------------------
 2 files changed, 29 insertions(+), 45 deletions(-)

diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 875076b0ea6c..4a114937c43b 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -363,9 +363,33 @@ static void pmem_release_disk(void *__pmem)
 	put_disk(pmem->disk);
 }
 
+static int pmem_pagemap_memory_failure(struct dev_pagemap *pgmap,
+		unsigned long pfn, int flags)
+{
+	struct pmem_device *pdev;
+	struct gendisk *disk;
+	loff_t disk_offset;
+	int rc = 0;
+	unsigned long size = page_size(pfn_to_page(pfn));
+
+	pdev = container_of(pgmap, struct pmem_device, pgmap);
+	disk = pdev->disk;
+	if (!disk)
+		return -ENXIO;
+
+	disk_offset = PFN_PHYS(pfn) - pdev->phys_addr - pdev->data_offset;
+	if (disk->fops->corrupted_range) {
+		rc = disk->fops->corrupted_range(disk, NULL, disk_offset, size, &flags);
+		if (rc == -ENODEV)
+			rc = -ENXIO;
+	}
+	return rc;
+}
+
 static const struct dev_pagemap_ops fsdax_pagemap_ops = {
 	.kill			= pmem_pagemap_kill,
 	.cleanup		= pmem_pagemap_cleanup,
+	.memory_failure		= pmem_pagemap_memory_failure,
 };
 
 static int pmem_attach_disk(struct device *dev,
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 37bc6e2a9564..0109ad607fb8 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1269,28 +1269,11 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 		struct dev_pagemap *pgmap)
 {
 	struct page *page = pfn_to_page(pfn);
-	const bool unmap_success = true;
-	unsigned long size = 0;
-	struct to_kill *tk;
-	LIST_HEAD(to_kill);
 	int rc = -EBUSY;
-	loff_t start;
-	dax_entry_t cookie;
-
-	/*
-	 * Prevent the inode from being freed while we are interrogating
-	 * the address_space, typically this would be handled by
-	 * lock_page(), but dax pages do not use the page lock. This
-	 * also prevents changes to the mapping of this pfn until
-	 * poison signaling is complete.
-	 */
-	cookie = dax_lock_page(page);
-	if (!cookie)
-		goto out;
 
 	if (hwpoison_filter(page)) {
 		rc = 0;
-		goto unlock;
+		goto out;
 	}
 
 	if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
@@ -1298,7 +1281,7 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 		 * TODO: Handle HMM pages which may need coordination
 		 * with device-side memory.
 		 */
-		goto unlock;
+		goto out;
 	}
 
 	/*
@@ -1307,33 +1290,10 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 	 */
 	SetPageHWPoison(page);
 
-	/*
-	 * Unlike System-RAM there is no possibility to swap in a
-	 * different physical page at a given virtual address, so all
-	 * userspace consumption of ZONE_DEVICE memory necessitates
-	 * SIGBUS (i.e. MF_MUST_KILL)
-	 */
-	flags |= MF_ACTION_REQUIRED | MF_MUST_KILL;
-	collect_procs_file(page, page->mapping, page->index, &to_kill,
-			   flags & MF_ACTION_REQUIRED);
+	/* call driver to handle the memory failure */
+	if (pgmap->ops->memory_failure)
+		rc = pgmap->ops->memory_failure(pgmap, pfn, flags);
 
-	list_for_each_entry(tk, &to_kill, nd)
-		if (tk->size_shift)
-			size = max(size, 1UL << tk->size_shift);
-	if (size) {
-		/*
-		 * Unmap the largest mapping to avoid breaking up
-		 * device-dax mappings which are constant size. The
-		 * actual size of the mapping being torn down is
-		 * communicated in siginfo, see kill_proc()
-		 */
-		start = (page->index << PAGE_SHIFT) & ~(size - 1);
-		unmap_mapping_range(page->mapping, start, start + size, 0);
-	}
-	kill_procs(&to_kill, flags & MF_MUST_KILL, !unmap_success, pfn, flags);
-	rc = 0;
-unlock:
-	dax_unlock_page(page, cookie);
 out:
 	/* drop pgmap ref acquired in caller */
 	put_dev_pagemap(pgmap);
-- 
2.29.2




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

* [PATCH 06/10] pmem: Implement ->corrupted_range() for pmem driver
  2020-12-30 16:55 [PATCH 00/10] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (4 preceding siblings ...)
  2020-12-30 16:55 ` [PATCH 05/10] mm, pmem: Implement ->memory_failure() in pmem driver Shiyang Ruan
@ 2020-12-30 16:55 ` Shiyang Ruan
  2020-12-30 16:55 ` [PATCH 07/10] dm: Introduce ->rmap() to find bdev offset Shiyang Ruan
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-30 16:55 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, dan.j.williams, david,
	hch, song, rgoldwyn, qi.fuli, y-goto

Obtain the superblock of a pmem disk, and call filesystem's
->corrupted_range() to handle the corrupted data.

Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
---
 block/genhd.c         | 12 ++++++++++++
 drivers/nvdimm/pmem.c | 24 ++++++++++++++++++++++++
 include/linux/genhd.h |  1 +
 3 files changed, 37 insertions(+)

diff --git a/block/genhd.c b/block/genhd.c
index 9387f050c248..436adce123b2 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1058,6 +1058,18 @@ struct block_device *bdget_disk(struct gendisk *disk, int partno)
 }
 EXPORT_SYMBOL(bdget_disk);
 
+struct block_device *bdget_disk_sector(struct gendisk *disk, sector_t sector)
+{
+	struct block_device *bdev = NULL;
+	struct hd_struct *part = disk_map_sector_rcu(disk, sector);
+
+	if (part)
+		bdev = bdget_part(part);
+
+	return bdev;
+}
+EXPORT_SYMBOL(bdget_disk_sector);
+
 /*
  * print a full list of all partitions - intended for places where the root
  * filesystem can't be mounted and thus to give the victim some idea of what
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 4a114937c43b..4688bff19c20 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -253,6 +253,29 @@ static int pmem_rw_page(struct block_device *bdev, sector_t sector,
 	return blk_status_to_errno(rc);
 }
 
+static int pmem_corrupted_range(struct gendisk *disk, struct block_device *bdev,
+				loff_t disk_offset, size_t len, void *data)
+{
+	struct super_block *sb;
+	loff_t bdev_offset;
+	sector_t disk_sector = disk_offset >> SECTOR_SHIFT;
+	int rc = 0;
+
+	bdev = bdget_disk_sector(disk, disk_sector);
+	if (!bdev)
+		return -ENODEV;
+
+	bdev_offset = (disk_sector - get_start_sect(bdev)) << SECTOR_SHIFT;
+	sb = get_super(bdev);
+	if (sb && sb->s_op->corrupted_range) {
+		rc = sb->s_op->corrupted_range(sb, bdev, bdev_offset, len, data);
+		drop_super(sb);
+	}
+
+	bdput(bdev);
+	return rc;
+}
+
 /* see "strong" declaration in tools/testing/nvdimm/pmem-dax.c */
 __weak long __pmem_direct_access(struct pmem_device *pmem, pgoff_t pgoff,
 		long nr_pages, void **kaddr, pfn_t *pfn)
@@ -281,6 +304,7 @@ static const struct block_device_operations pmem_fops = {
 	.owner =		THIS_MODULE,
 	.submit_bio =		pmem_submit_bio,
 	.rw_page =		pmem_rw_page,
+	.corrupted_range =	pmem_corrupted_range,
 };
 
 static int pmem_dax_zero_page_range(struct dax_device *dax_dev, pgoff_t pgoff,
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 03da3f603d30..ed06209008b8 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -303,6 +303,7 @@ static inline void add_disk_no_queue_reg(struct gendisk *disk)
 extern void del_gendisk(struct gendisk *gp);
 extern struct gendisk *get_gendisk(dev_t dev, int *partno);
 extern struct block_device *bdget_disk(struct gendisk *disk, int partno);
+extern struct block_device *bdget_disk_sector(struct gendisk *disk, sector_t sector);
 
 extern void set_device_ro(struct block_device *bdev, int flag);
 extern void set_disk_ro(struct gendisk *disk, int flag);
-- 
2.29.2




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

* [PATCH 07/10] dm: Introduce ->rmap() to find bdev offset
  2020-12-30 16:55 [PATCH 00/10] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (5 preceding siblings ...)
  2020-12-30 16:55 ` [PATCH 06/10] pmem: Implement ->corrupted_range() for " Shiyang Ruan
@ 2020-12-30 16:55 ` Shiyang Ruan
  2020-12-30 16:55 ` [PATCH 08/10] md: Implement ->corrupted_range() Shiyang Ruan
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-30 16:55 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, dan.j.williams, david,
	hch, song, rgoldwyn, qi.fuli, y-goto

Pmem device could be a target of mapped device.  In order to obtain
superblock on the mapped device, we introduce this to translate offset
from target device to md device.

Currently, we implement it on linear target, which is easy to do the
translation.  Other targets will be supported in the future.  However,
some targets may not support it because of the non-linear mapping.

Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
---
 drivers/md/dm-linear.c        | 8 ++++++++
 include/linux/device-mapper.h | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c
index 00774b5d7668..7a7719e9958d 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -119,6 +119,13 @@ static void linear_status(struct dm_target *ti, status_type_t type,
 	}
 }
 
+static sector_t linear_rmap(struct dm_target *ti, sector_t offset)
+{
+	struct linear_c *lc = (struct linear_c *) ti->private;
+
+	return offset - dm_target_offset(ti, lc->start);
+}
+
 static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
 {
 	struct linear_c *lc = (struct linear_c *) ti->private;
@@ -238,6 +245,7 @@ static struct target_type linear_target = {
 	.ctr    = linear_ctr,
 	.dtr    = linear_dtr,
 	.map    = linear_map,
+	.rmap   = linear_rmap,
 	.status = linear_status,
 	.prepare_ioctl = linear_prepare_ioctl,
 	.iterate_devices = linear_iterate_devices,
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 61a66fb8ebb3..4a2afff6937e 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -58,6 +58,7 @@ typedef void (*dm_dtr_fn) (struct dm_target *ti);
  * = 2: The target wants to push back the io
  */
 typedef int (*dm_map_fn) (struct dm_target *ti, struct bio *bio);
+typedef sector_t (*dm_rmap_fn) (struct dm_target *ti, sector_t offset);
 typedef int (*dm_clone_and_map_request_fn) (struct dm_target *ti,
 					    struct request *rq,
 					    union map_info *map_context,
@@ -175,6 +176,7 @@ struct target_type {
 	dm_ctr_fn ctr;
 	dm_dtr_fn dtr;
 	dm_map_fn map;
+	dm_rmap_fn rmap;
 	dm_clone_and_map_request_fn clone_and_map_rq;
 	dm_release_clone_request_fn release_clone_rq;
 	dm_endio_fn end_io;
-- 
2.29.2




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

* [PATCH 08/10] md: Implement ->corrupted_range()
  2020-12-30 16:55 [PATCH 00/10] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (6 preceding siblings ...)
  2020-12-30 16:55 ` [PATCH 07/10] dm: Introduce ->rmap() to find bdev offset Shiyang Ruan
@ 2020-12-30 16:55 ` Shiyang Ruan
  2021-01-06 17:14   ` Jan Kara
  2020-12-30 16:56 ` [PATCH 09/10] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
  2020-12-30 16:56 ` [PATCH 10/10] fs/dax: remove useless functions Shiyang Ruan
  9 siblings, 1 reply; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-30 16:55 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, dan.j.williams, david,
	hch, song, rgoldwyn, qi.fuli, y-goto

With the support of ->rmap(), it is possible to obtain the superblock on
a mapped device.

If a pmem device is used as one target of mapped device, we cannot
obtain its superblock directly.  With the help of SYSFS, the mapped
device can be found on the target devices.  So, we iterate the
bdev->bd_holder_disks to obtain its mapped device.

Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
---
 drivers/md/dm.c       | 54 +++++++++++++++++++++++++++++++++++++++++++
 drivers/nvdimm/pmem.c | 11 +++------
 fs/block_dev.c        | 37 +++++++++++++++++++++++++++++
 include/linux/genhd.h |  2 ++
 4 files changed, 96 insertions(+), 8 deletions(-)

diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 4e0cbfe3f14d..f9955be7afeb 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -507,6 +507,59 @@ static int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
 #define dm_blk_report_zones		NULL
 #endif /* CONFIG_BLK_DEV_ZONED */
 
+struct dm_blk_corrupt {
+	struct block_device *bdev;
+	sector_t offset;
+};
+
+static int dm_blk_corrupt_fn(struct dm_target *ti, struct dm_dev *dev,
+				sector_t start, sector_t len, void *data)
+{
+	struct dm_blk_corrupt *bc = data;
+
+	return bc->bdev == (void *)dev->bdev &&
+			(start <= bc->offset && bc->offset < start + len);
+}
+
+static int dm_blk_corrupted_range(struct gendisk *disk,
+				  struct block_device *target_bdev,
+				  loff_t target_offset, size_t len, void *data)
+{
+	struct mapped_device *md = disk->private_data;
+	struct block_device *md_bdev = md->bdev;
+	struct dm_table *map;
+	struct dm_target *ti;
+	int srcu_idx, i, rc = -ENODEV;
+	bool found = false;
+	sector_t disk_sect, target_sect = to_sector(target_offset);
+
+	map = dm_get_live_table(md, &srcu_idx);
+	if (!map)
+		return rc;
+
+	for (i = 0; i < dm_table_get_num_targets(map); i++) {
+		ti = dm_table_get_target(map, i);
+		if (ti->type->iterate_devices && ti->type->rmap) {
+			struct dm_blk_corrupt bc = {target_bdev, target_sect};
+
+			found = ti->type->iterate_devices(ti, dm_blk_corrupt_fn, &bc);
+			if (!found)
+				continue;
+			disk_sect = ti->type->rmap(ti, target_sect);
+			break;
+		}
+	}
+
+	if (found) {
+		loff_t disk_off = to_bytes(disk_sect);
+		loff_t bdev_off = to_bytes(disk_sect - get_start_sect(md_bdev));
+		rc = bd_corrupted_range(md_bdev, disk_off, bdev_off, len, data);
+	}
+
+	dm_put_live_table(md, srcu_idx);
+	return rc;
+}
+
 static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
 			    struct block_device **bdev)
 {
@@ -3084,6 +3137,7 @@ static const struct block_device_operations dm_blk_dops = {
 	.getgeo = dm_blk_getgeo,
 	.report_zones = dm_blk_report_zones,
 	.pr_ops = &dm_pr_ops,
+	.corrupted_range = dm_blk_corrupted_range,
 	.owner = THIS_MODULE
 };
 
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 4688bff19c20..9f9a2f3bf73b 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -256,21 +256,16 @@ static int pmem_rw_page(struct block_device *bdev, sector_t sector,
 static int pmem_corrupted_range(struct gendisk *disk, struct block_device *bdev,
 				loff_t disk_offset, size_t len, void *data)
 {
-	struct super_block *sb;
 	loff_t bdev_offset;
 	sector_t disk_sector = disk_offset >> SECTOR_SHIFT;
-	int rc = 0;
+	int rc = -ENODEV;
 
 	bdev = bdget_disk_sector(disk, disk_sector);
 	if (!bdev)
-		return -ENODEV;
+		return rc;
 
 	bdev_offset = (disk_sector - get_start_sect(bdev)) << SECTOR_SHIFT;
-	sb = get_super(bdev);
-	if (sb && sb->s_op->corrupted_range) {
-		rc = sb->s_op->corrupted_range(sb, bdev, bdev_offset, len, data);
-		drop_super(sb);
-	}
+	rc = bd_corrupted_range(bdev, bdev_offset, bdev_offset, len, data);
 
 	bdput(bdev);
 	return rc;
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 9e84b1928b94..0e50f0e8e8af 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1171,6 +1171,27 @@ struct bd_holder_disk {
 	int			refcnt;
 };
 
+static int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
+					  size_t len, void *data)
+{
+	struct bd_holder_disk *holder;
+	struct gendisk *disk;
+	int rc = 0;
+
+	if (list_empty(&(bdev->bd_holder_disks)))
+		return -ENODEV;
+
+	list_for_each_entry(holder, &bdev->bd_holder_disks, list) {
+		disk = holder->disk;
+		if (disk->fops->corrupted_range) {
+			rc = disk->fops->corrupted_range(disk, bdev, off, len, data);
+			if (rc != -ENODEV)
+				break;
+		}
+	}
+	return rc;
+}
+
 static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
 						  struct gendisk *disk)
 {
@@ -1378,6 +1399,22 @@ void bd_set_nr_sectors(struct block_device *bdev, sector_t sectors)
 }
 EXPORT_SYMBOL(bd_set_nr_sectors);
 
+int bd_corrupted_range(struct block_device *bdev, loff_t disk_off, loff_t bdev_off, size_t len, void *data)
+{
+	struct super_block *sb = get_super(bdev);
+	int rc = 0;
+
+	if (!sb) {
+		rc = bd_disk_holder_corrupted_range(bdev, disk_off, len, data);
+		return rc;
+	} else if (sb->s_op->corrupted_range)
+		rc = sb->s_op->corrupted_range(sb, bdev, bdev_off, len, data);
+	drop_super(sb);
+
+	return rc;
+}
+EXPORT_SYMBOL(bd_corrupted_range);
+
 static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part);
 
 int bdev_disk_changed(struct block_device *bdev, bool invalidate)
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index ed06209008b8..42290470810d 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -376,6 +376,8 @@ void revalidate_disk_size(struct gendisk *disk, bool verbose);
 bool bdev_check_media_change(struct block_device *bdev);
 int __invalidate_device(struct block_device *bdev, bool kill_dirty);
 void bd_set_nr_sectors(struct block_device *bdev, sector_t sectors);
+int bd_corrupted_range(struct block_device *bdev, loff_t disk_off,
+		       loff_t bdev_off, size_t len, void *data);
 
 /* for drivers/char/raw.c: */
 int blkdev_ioctl(struct block_device *, fmode_t, unsigned, unsigned long);
-- 
2.29.2




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

* [PATCH 09/10] xfs: Implement ->corrupted_range() for XFS
  2020-12-30 16:55 [PATCH 00/10] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (7 preceding siblings ...)
  2020-12-30 16:55 ` [PATCH 08/10] md: Implement ->corrupted_range() Shiyang Ruan
@ 2020-12-30 16:56 ` Shiyang Ruan
  2021-01-04 23:21   ` Darrick J. Wong
  2020-12-30 16:56 ` [PATCH 10/10] fs/dax: remove useless functions Shiyang Ruan
  9 siblings, 1 reply; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-30 16:56 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, dan.j.williams, david,
	hch, song, rgoldwyn, qi.fuli, y-goto

This function is used to handle errors which may cause data lost in
filesystem.  Such as memory failure in fsdax mode.

In XFS, it requires "rmapbt" feature in order to query for files or
metadata which associated to the corrupted data.  Then we could call fs
recover functions to try to repair the corrupted data.(did not
implemented in this patchset)

After that, the memory failure also needs to notify the processes who
are using those files.

Only support data device.  Realtime device is not supported for now.

Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
---
 fs/xfs/xfs_fsops.c |   5 +++
 fs/xfs/xfs_mount.h |   1 +
 fs/xfs/xfs_super.c | 107 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 113 insertions(+)

diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index ef1d5bb88b93..0a2038875d32 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -501,6 +501,11 @@ xfs_do_force_shutdown(
 "Corruption of in-memory data detected.  Shutting down filesystem");
 		if (XFS_ERRLEVEL_HIGH <= xfs_error_level)
 			xfs_stack_trace();
+	} else if (flags & SHUTDOWN_CORRUPT_META) {
+		xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_CORRUPT,
+"Corruption of on-disk metadata detected.  Shutting down filesystem");
+		if (XFS_ERRLEVEL_HIGH <= xfs_error_level)
+			xfs_stack_trace();
 	} else if (logerror) {
 		xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_LOGERROR,
 			"Log I/O Error Detected. Shutting down filesystem");
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index dfa429b77ee2..8f0df67ffcc1 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -274,6 +274,7 @@ void xfs_do_force_shutdown(struct xfs_mount *mp, int flags, char *fname,
 #define SHUTDOWN_LOG_IO_ERROR	0x0002	/* write attempt to the log failed */
 #define SHUTDOWN_FORCE_UMOUNT	0x0004	/* shutdown from a forced unmount */
 #define SHUTDOWN_CORRUPT_INCORE	0x0008	/* corrupt in-memory data structures */
+#define SHUTDOWN_CORRUPT_META	0x0010  /* corrupt metadata on device */
 
 /*
  * Flags for xfs_mountfs
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index e3e229e52512..cbcad419bb9e 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -35,6 +35,11 @@
 #include "xfs_refcount_item.h"
 #include "xfs_bmap_item.h"
 #include "xfs_reflink.h"
+#include "xfs_alloc.h"
+#include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
+#include "xfs_rtalloc.h"
+#include "xfs_bit.h"
 
 #include <linux/magic.h>
 #include <linux/fs_context.h>
@@ -1103,6 +1108,107 @@ xfs_fs_free_cached_objects(
 	return xfs_reclaim_inodes_nr(XFS_M(sb), sc->nr_to_scan);
 }
 
+static int
+xfs_corrupt_helper(
+	struct xfs_btree_cur		*cur,
+	struct xfs_rmap_irec		*rec,
+	void				*data)
+{
+	struct xfs_inode		*ip;
+	struct address_space		*mapping;
+	int				rc = 0;
+	int				*flags = data;
+
+	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
+	    (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
+		// TODO check and try to fix metadata
+		rc = -EFSCORRUPTED;
+	} else {
+		/*
+		 * Get files that incore, filter out others that are not in use.
+		 */
+		rc = xfs_iget(cur->bc_mp, cur->bc_tp, rec->rm_owner,
+			      XFS_IGET_INCORE, 0, &ip);
+		if (rc || !ip)
+			return rc;
+		if (!VFS_I(ip)->i_mapping)
+			goto out;
+
+		mapping = VFS_I(ip)->i_mapping;
+		if (IS_DAX(VFS_I(ip)))
+			rc = mf_dax_mapping_kill_procs(mapping, rec->rm_offset,
+						       *flags);
+		else
+			mapping_set_error(mapping, -EFSCORRUPTED);
+
+		// TODO try to fix data
+out:
+		xfs_irele(ip);
+	}
+
+	return rc;
+}
+
+static int
+xfs_fs_corrupted_range(
+	struct super_block	*sb,
+	struct block_device	*bdev,
+	loff_t			offset,
+	size_t			len,
+	void			*data)
+{
+	struct xfs_mount	*mp = XFS_M(sb);
+	struct xfs_trans	*tp = NULL;
+	struct xfs_btree_cur	*cur = NULL;
+	struct xfs_rmap_irec	rmap_low, rmap_high;
+	struct xfs_buf		*agf_bp = NULL;
+	xfs_fsblock_t		fsbno = XFS_B_TO_FSB(mp, offset);
+	xfs_filblks_t		bcnt = XFS_B_TO_FSB(mp, len);
+	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(mp, fsbno);
+	xfs_agblock_t		agbno = XFS_FSB_TO_AGBNO(mp, fsbno);
+	int			error = 0;
+
+	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_bdev == bdev) {
+		xfs_warn(mp, "corrupted_range support not available for realtime device!");
+		return 0;
+	}
+	if (mp->m_logdev_targp && mp->m_logdev_targp->bt_bdev == bdev &&
+	    mp->m_logdev_targp != mp->m_ddev_targp) {
+		xfs_err(mp, "ondisk log corrupt, shutting down fs!");
+		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_META);
+		return 0;
+	}
+
+	if (!xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+		xfs_warn(mp, "corrupted_range needs rmapbt enabled!");
+		return 0;
+	}
+
+	error = xfs_trans_alloc_empty(mp, &tp);
+	if (error)
+		return error;
+
+	error = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
+	if (error)
+		return error;
+
+	cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, agno);
+
+	/* Construct a range for rmap query */
+	memset(&rmap_low, 0, sizeof(rmap_low));
+	memset(&rmap_high, 0xFF, sizeof(rmap_high));
+	rmap_low.rm_startblock = rmap_high.rm_startblock = agbno;
+	rmap_low.rm_blockcount = rmap_high.rm_blockcount = bcnt;
+
+	error = xfs_rmap_query_range(cur, &rmap_low, &rmap_high, xfs_corrupt_helper, data);
+	if (error == -EFSCORRUPTED)
+		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_META);
+
+	xfs_btree_del_cursor(cur, error);
+	xfs_trans_brelse(tp, agf_bp);
+	return error;
+}
+
 static const struct super_operations xfs_super_operations = {
 	.alloc_inode		= xfs_fs_alloc_inode,
 	.destroy_inode		= xfs_fs_destroy_inode,
@@ -1116,6 +1222,7 @@ static const struct super_operations xfs_super_operations = {
 	.show_options		= xfs_fs_show_options,
 	.nr_cached_objects	= xfs_fs_nr_cached_objects,
 	.free_cached_objects	= xfs_fs_free_cached_objects,
+	.corrupted_range	= xfs_fs_corrupted_range,
 };
 
 static int
-- 
2.29.2




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

* [PATCH 10/10] fs/dax: remove useless functions
  2020-12-30 16:55 [PATCH 00/10] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (8 preceding siblings ...)
  2020-12-30 16:56 ` [PATCH 09/10] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
@ 2020-12-30 16:56 ` Shiyang Ruan
  9 siblings, 0 replies; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-30 16:56 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, dan.j.williams, david,
	hch, song, rgoldwyn, qi.fuli, y-goto

Since owner tarcking is triggerred by pmem device, these functions are
useless.  So remove it.

Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
---
 fs/dax.c            | 112 --------------------------------------------
 include/linux/dax.h |   2 -
 2 files changed, 114 deletions(-)

diff --git a/fs/dax.c b/fs/dax.c
index 799210cfa687..4267de360d79 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -323,48 +323,6 @@ static unsigned long dax_end_pfn(void *entry)
 	for (pfn = dax_to_pfn(entry); \
 			pfn < dax_end_pfn(entry); pfn++)
 
-/*
- * TODO: for reflink+dax we need a way to associate a single page with
- * multiple address_space instances at different linear_page_index()
- * offsets.
- */
-static void dax_associate_entry(void *entry, struct address_space *mapping,
-		struct vm_area_struct *vma, unsigned long address)
-{
-	unsigned long size = dax_entry_size(entry), pfn, index;
-	int i = 0;
-
-	if (IS_ENABLED(CONFIG_FS_DAX_LIMITED))
-		return;
-
-	index = linear_page_index(vma, address & ~(size - 1));
-	for_each_mapped_pfn(entry, pfn) {
-		struct page *page = pfn_to_page(pfn);
-
-		WARN_ON_ONCE(page->mapping);
-		page->mapping = mapping;
-		page->index = index + i++;
-	}
-}
-
-static void dax_disassociate_entry(void *entry, struct address_space *mapping,
-		bool trunc)
-{
-	unsigned long pfn;
-
-	if (IS_ENABLED(CONFIG_FS_DAX_LIMITED))
-		return;
-
-	for_each_mapped_pfn(entry, pfn) {
-		struct page *page = pfn_to_page(pfn);
-
-		WARN_ON_ONCE(trunc && page_ref_count(page) > 1);
-		WARN_ON_ONCE(page->mapping && page->mapping != mapping);
-		page->mapping = NULL;
-		page->index = 0;
-	}
-}
-
 static struct page *dax_busy_page(void *entry)
 {
 	unsigned long pfn;
@@ -399,72 +357,6 @@ unsigned long dax_load_pfn(struct address_space *mapping, unsigned long index)
 	return pfn;
 }
 
-/*
- * dax_lock_mapping_entry - Lock the DAX entry corresponding to a page
- * @page: The page whose entry we want to lock
- *
- * Context: Process context.
- * Return: A cookie to pass to dax_unlock_page() or 0 if the entry could
- * not be locked.
- */
-dax_entry_t dax_lock_page(struct page *page)
-{
-	XA_STATE(xas, NULL, 0);
-	void *entry;
-
-	/* Ensure page->mapping isn't freed while we look at it */
-	rcu_read_lock();
-	for (;;) {
-		struct address_space *mapping = READ_ONCE(page->mapping);
-
-		entry = NULL;
-		if (!mapping || !dax_mapping(mapping))
-			break;
-
-		/*
-		 * In the device-dax case there's no need to lock, a
-		 * struct dev_pagemap pin is sufficient to keep the
-		 * inode alive, and we assume we have dev_pagemap pin
-		 * otherwise we would not have a valid pfn_to_page()
-		 * translation.
-		 */
-		entry = (void *)~0UL;
-		if (S_ISCHR(mapping->host->i_mode))
-			break;
-
-		xas.xa = &mapping->i_pages;
-		xas_lock_irq(&xas);
-		if (mapping != page->mapping) {
-			xas_unlock_irq(&xas);
-			continue;
-		}
-		xas_set(&xas, page->index);
-		entry = xas_load(&xas);
-		if (dax_is_locked(entry)) {
-			rcu_read_unlock();
-			wait_entry_unlocked(&xas, entry);
-			rcu_read_lock();
-			continue;
-		}
-		dax_lock_entry(&xas, entry);
-		xas_unlock_irq(&xas);
-		break;
-	}
-	rcu_read_unlock();
-	return (dax_entry_t)entry;
-}
-
-void dax_unlock_page(struct page *page, dax_entry_t cookie)
-{
-	struct address_space *mapping = page->mapping;
-	XA_STATE(xas, &mapping->i_pages, page->index);
-
-	if (S_ISCHR(mapping->host->i_mode))
-		return;
-
-	dax_unlock_entry(&xas, (void *)cookie);
-}
-
 /*
  * Find page cache entry at given index. If it is a DAX entry, return it
  * with the entry locked. If the page cache doesn't contain an entry at
@@ -543,7 +435,6 @@ static void *grab_mapping_entry(struct xa_state *xas,
 			xas_lock_irq(xas);
 		}
 
-		dax_disassociate_entry(entry, mapping, false);
 		xas_store(xas, NULL);	/* undo the PMD join */
 		dax_wake_entry(xas, entry, true);
 		mapping->nrexceptional--;
@@ -680,7 +571,6 @@ static int __dax_invalidate_entry(struct address_space *mapping,
 	    (xas_get_mark(&xas, PAGECACHE_TAG_DIRTY) ||
 	     xas_get_mark(&xas, PAGECACHE_TAG_TOWRITE)))
 		goto out;
-	dax_disassociate_entry(entry, mapping, trunc);
 	xas_store(&xas, NULL);
 	mapping->nrexceptional--;
 	ret = 1;
@@ -774,8 +664,6 @@ static void *dax_insert_entry(struct xa_state *xas,
 	if (dax_is_zero_entry(entry) || dax_is_empty_entry(entry)) {
 		void *old;
 
-		dax_disassociate_entry(entry, mapping, false);
-		dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address);
 		/*
 		 * Only swap our new entry into the page cache if the current
 		 * entry is a zero page or an empty entry.  If a normal PTE or
diff --git a/include/linux/dax.h b/include/linux/dax.h
index 89e56ceeffc7..c6b8dc094b26 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -151,8 +151,6 @@ int dax_writeback_mapping_range(struct address_space *mapping,
 struct page *dax_layout_busy_page(struct address_space *mapping);
 struct page *dax_layout_busy_page_range(struct address_space *mapping, loff_t start, loff_t end);
 unsigned long dax_load_pfn(struct address_space *mapping, unsigned long index);
-dax_entry_t dax_lock_page(struct page *page);
-void dax_unlock_page(struct page *page, dax_entry_t cookie);
 #else
 static inline bool bdev_dax_supported(struct block_device *bdev,
 		int blocksize)
-- 
2.29.2




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

* Re: [PATCH 09/10] xfs: Implement ->corrupted_range() for XFS
  2020-12-30 16:56 ` [PATCH 09/10] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
@ 2021-01-04 23:21   ` Darrick J. Wong
  0 siblings, 0 replies; 27+ messages in thread
From: Darrick J. Wong @ 2021-01-04 23:21 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, dan.j.williams, david, hch, song, rgoldwyn, qi.fuli,
	y-goto

On Thu, Dec 31, 2020 at 12:56:00AM +0800, Shiyang Ruan wrote:
> This function is used to handle errors which may cause data lost in
> filesystem.  Such as memory failure in fsdax mode.
> 
> In XFS, it requires "rmapbt" feature in order to query for files or
> metadata which associated to the corrupted data.  Then we could call fs
> recover functions to try to repair the corrupted data.(did not
> implemented in this patchset)
> 
> After that, the memory failure also needs to notify the processes who
> are using those files.
> 
> Only support data device.  Realtime device is not supported for now.
> 
> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
> ---
>  fs/xfs/xfs_fsops.c |   5 +++
>  fs/xfs/xfs_mount.h |   1 +
>  fs/xfs/xfs_super.c | 107 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 113 insertions(+)
> 
> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> index ef1d5bb88b93..0a2038875d32 100644
> --- a/fs/xfs/xfs_fsops.c
> +++ b/fs/xfs/xfs_fsops.c
> @@ -501,6 +501,11 @@ xfs_do_force_shutdown(
>  "Corruption of in-memory data detected.  Shutting down filesystem");
>  		if (XFS_ERRLEVEL_HIGH <= xfs_error_level)
>  			xfs_stack_trace();
> +	} else if (flags & SHUTDOWN_CORRUPT_META) {
> +		xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_CORRUPT,
> +"Corruption of on-disk metadata detected.  Shutting down filesystem");
> +		if (XFS_ERRLEVEL_HIGH <= xfs_error_level)
> +			xfs_stack_trace();
>  	} else if (logerror) {
>  		xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_LOGERROR,
>  			"Log I/O Error Detected. Shutting down filesystem");
> diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
> index dfa429b77ee2..8f0df67ffcc1 100644
> --- a/fs/xfs/xfs_mount.h
> +++ b/fs/xfs/xfs_mount.h
> @@ -274,6 +274,7 @@ void xfs_do_force_shutdown(struct xfs_mount *mp, int flags, char *fname,
>  #define SHUTDOWN_LOG_IO_ERROR	0x0002	/* write attempt to the log failed */
>  #define SHUTDOWN_FORCE_UMOUNT	0x0004	/* shutdown from a forced unmount */
>  #define SHUTDOWN_CORRUPT_INCORE	0x0008	/* corrupt in-memory data structures */
> +#define SHUTDOWN_CORRUPT_META	0x0010  /* corrupt metadata on device */
>  
>  /*
>   * Flags for xfs_mountfs
> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index e3e229e52512..cbcad419bb9e 100644
> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c
> @@ -35,6 +35,11 @@
>  #include "xfs_refcount_item.h"
>  #include "xfs_bmap_item.h"
>  #include "xfs_reflink.h"
> +#include "xfs_alloc.h"
> +#include "xfs_rmap.h"
> +#include "xfs_rmap_btree.h"
> +#include "xfs_rtalloc.h"
> +#include "xfs_bit.h"
>  
>  #include <linux/magic.h>
>  #include <linux/fs_context.h>
> @@ -1103,6 +1108,107 @@ xfs_fs_free_cached_objects(
>  	return xfs_reclaim_inodes_nr(XFS_M(sb), sc->nr_to_scan);
>  }
>  
> +static int
> +xfs_corrupt_helper(
> +	struct xfs_btree_cur		*cur,
> +	struct xfs_rmap_irec		*rec,
> +	void				*data)
> +{
> +	struct xfs_inode		*ip;
> +	struct address_space		*mapping;
> +	int				rc = 0;
> +	int				*flags = data;
> +
> +	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
> +	    (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
> +		// TODO check and try to fix metadata
> +		rc = -EFSCORRUPTED;
> +	} else {
> +		/*
> +		 * Get files that incore, filter out others that are not in use.
> +		 */
> +		rc = xfs_iget(cur->bc_mp, cur->bc_tp, rec->rm_owner,
> +			      XFS_IGET_INCORE, 0, &ip);
> +		if (rc || !ip)
> +			return rc;
> +		if (!VFS_I(ip)->i_mapping)
> +			goto out;
> +
> +		mapping = VFS_I(ip)->i_mapping;
> +		if (IS_DAX(VFS_I(ip)))
> +			rc = mf_dax_mapping_kill_procs(mapping, rec->rm_offset,
> +						       *flags);
> +		else
> +			mapping_set_error(mapping, -EFSCORRUPTED);

Hm.  I don't know if EFSCORRUPTED is the right error code for corrupt
file data, since we (so far) have only used it for corrupt metadata.

> +
> +		// TODO try to fix data
> +out:
> +		xfs_irele(ip);
> +	}
> +
> +	return rc;
> +}
> +
> +static int
> +xfs_fs_corrupted_range(
> +	struct super_block	*sb,
> +	struct block_device	*bdev,
> +	loff_t			offset,
> +	size_t			len,
> +	void			*data)
> +{
> +	struct xfs_mount	*mp = XFS_M(sb);
> +	struct xfs_trans	*tp = NULL;
> +	struct xfs_btree_cur	*cur = NULL;
> +	struct xfs_rmap_irec	rmap_low, rmap_high;
> +	struct xfs_buf		*agf_bp = NULL;
> +	xfs_fsblock_t		fsbno = XFS_B_TO_FSB(mp, offset);
> +	xfs_filblks_t		bcnt = XFS_B_TO_FSB(mp, len);
> +	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(mp, fsbno);
> +	xfs_agblock_t		agbno = XFS_FSB_TO_AGBNO(mp, fsbno);
> +	int			error = 0;
> +
> +	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_bdev == bdev) {
> +		xfs_warn(mp, "corrupted_range support not available for realtime device!");
> +		return 0;
> +	}
> +	if (mp->m_logdev_targp && mp->m_logdev_targp->bt_bdev == bdev &&
> +	    mp->m_logdev_targp != mp->m_ddev_targp) {
> +		xfs_err(mp, "ondisk log corrupt, shutting down fs!");
> +		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_META);
> +		return 0;
> +	}
> +
> +	if (!xfs_sb_version_hasrmapbt(&mp->m_sb)) {
> +		xfs_warn(mp, "corrupted_range needs rmapbt enabled!");
> +		return 0;
> +	}
> +
> +	error = xfs_trans_alloc_empty(mp, &tp);
> +	if (error)
> +		return error;
> +
> +	error = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
> +	if (error)
> +		return error;
> +
> +	cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, agno);
> +
> +	/* Construct a range for rmap query */
> +	memset(&rmap_low, 0, sizeof(rmap_low));
> +	memset(&rmap_high, 0xFF, sizeof(rmap_high));
> +	rmap_low.rm_startblock = rmap_high.rm_startblock = agbno;
> +	rmap_low.rm_blockcount = rmap_high.rm_blockcount = bcnt;
> +
> +	error = xfs_rmap_query_range(cur, &rmap_low, &rmap_high, xfs_corrupt_helper, data);
> +	if (error == -EFSCORRUPTED)
> +		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_META);
> +
> +	xfs_btree_del_cursor(cur, error);
> +	xfs_trans_brelse(tp, agf_bp);

The transaction needs to be committed (or cancelled) here, or else it
leaks.

--D

> +	return error;
> +}
> +
>  static const struct super_operations xfs_super_operations = {
>  	.alloc_inode		= xfs_fs_alloc_inode,
>  	.destroy_inode		= xfs_fs_destroy_inode,
> @@ -1116,6 +1222,7 @@ static const struct super_operations xfs_super_operations = {
>  	.show_options		= xfs_fs_show_options,
>  	.nr_cached_objects	= xfs_fs_nr_cached_objects,
>  	.free_cached_objects	= xfs_fs_free_cached_objects,
> +	.corrupted_range	= xfs_fs_corrupted_range,
>  };
>  
>  static int
> -- 
> 2.29.2
> 
> 
> 

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

* Re: [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping
  2020-12-30 16:55 ` [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping Shiyang Ruan
@ 2021-01-06 15:41   ` Jan Kara
  2021-01-12  2:55     ` Ruan Shiyang
  2021-01-14 20:38   ` Dan Williams
  1 sibling, 1 reply; 27+ messages in thread
From: Jan Kara @ 2021-01-06 15:41 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto

On Thu 31-12-20 00:55:55, Shiyang Ruan wrote:
> The current memory_failure_dev_pagemap() can only handle single-mapped
> dax page for fsdax mode.  The dax page could be mapped by multiple files
> and offsets if we let reflink feature & fsdax mode work together.  So,
> we refactor current implementation to support handle memory failure on
> each file and offset.
> 
> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>

Overall this looks OK to me, a few comments below.

> ---
>  fs/dax.c            | 21 +++++++++++
>  include/linux/dax.h |  1 +
>  include/linux/mm.h  |  9 +++++
>  mm/memory-failure.c | 91 ++++++++++++++++++++++++++++++++++-----------
>  4 files changed, 100 insertions(+), 22 deletions(-)
> 
> diff --git a/fs/dax.c b/fs/dax.c
> index 5b47834f2e1b..799210cfa687 100644
> --- a/fs/dax.c
> +++ b/fs/dax.c
> @@ -378,6 +378,27 @@ static struct page *dax_busy_page(void *entry)
>  	return NULL;
>  }
>  
> +/*
> + * dax_load_pfn - Load pfn of the DAX entry corresponding to a page
> + * @mapping: The file whose entry we want to load
> + * @index:   The offset where the DAX entry located in
> + *
> + * Return:   pfn of the DAX entry
> + */
> +unsigned long dax_load_pfn(struct address_space *mapping, unsigned long index)
> +{
> +	XA_STATE(xas, &mapping->i_pages, index);
> +	void *entry;
> +	unsigned long pfn;
> +
> +	xas_lock_irq(&xas);
> +	entry = xas_load(&xas);
> +	pfn = dax_to_pfn(entry);
> +	xas_unlock_irq(&xas);
> +
> +	return pfn;
> +}
> +
>  /*
>   * dax_lock_mapping_entry - Lock the DAX entry corresponding to a page
>   * @page: The page whose entry we want to lock
> diff --git a/include/linux/dax.h b/include/linux/dax.h
> index b52f084aa643..89e56ceeffc7 100644
> --- a/include/linux/dax.h
> +++ b/include/linux/dax.h
> @@ -150,6 +150,7 @@ int dax_writeback_mapping_range(struct address_space *mapping,
>  
>  struct page *dax_layout_busy_page(struct address_space *mapping);
>  struct page *dax_layout_busy_page_range(struct address_space *mapping, loff_t start, loff_t end);
> +unsigned long dax_load_pfn(struct address_space *mapping, unsigned long index);
>  dax_entry_t dax_lock_page(struct page *page);
>  void dax_unlock_page(struct page *page, dax_entry_t cookie);
>  #else
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index db6ae4d3fb4e..db3059a1853e 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -1141,6 +1141,14 @@ static inline bool is_device_private_page(const struct page *page)
>  		page->pgmap->type == MEMORY_DEVICE_PRIVATE;
>  }
>  
> +static inline bool is_device_fsdax_page(const struct page *page)
> +{
> +	return IS_ENABLED(CONFIG_DEV_PAGEMAP_OPS) &&
> +		IS_ENABLED(CONFIG_DEVICE_PRIVATE) &&
> +		is_zone_device_page(page) &&
> +		page->pgmap->type == MEMORY_DEVICE_FS_DAX;
> +}
> +
>  static inline bool is_pci_p2pdma_page(const struct page *page)
>  {
>  	return IS_ENABLED(CONFIG_DEV_PAGEMAP_OPS) &&
> @@ -3030,6 +3038,7 @@ enum mf_flags {
>  	MF_MUST_KILL = 1 << 2,
>  	MF_SOFT_OFFLINE = 1 << 3,
>  };
> +extern int mf_dax_mapping_kill_procs(struct address_space *mapping, pgoff_t index, int flags);
>  extern int memory_failure(unsigned long pfn, int flags);
>  extern void memory_failure_queue(unsigned long pfn, int flags);
>  extern void memory_failure_queue_kick(int cpu);
> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index 5d880d4eb9a2..37bc6e2a9564 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -56,6 +56,7 @@
>  #include <linux/kfifo.h>
>  #include <linux/ratelimit.h>
>  #include <linux/page-isolation.h>
> +#include <linux/dax.h>
>  #include "internal.h"
>  #include "ras/ras_event.h"
>  
> @@ -120,6 +121,9 @@ static int hwpoison_filter_dev(struct page *p)
>  	if (PageSlab(p))
>  		return -EINVAL;
>  
> +	if (is_device_fsdax_page(p))
> +		return 0;
> +
>  	mapping = page_mapping(p);
>  	if (mapping == NULL || mapping->host == NULL)
>  		return -EINVAL;
> @@ -290,9 +294,8 @@ void shake_page(struct page *p, int access)
>  EXPORT_SYMBOL_GPL(shake_page);
>  
>  static unsigned long dev_pagemap_mapping_shift(struct page *page,
> -		struct vm_area_struct *vma)
> +		struct vm_area_struct *vma, unsigned long address)

The 'page' argument is now unused. Drop it?

>  {
> -	unsigned long address = vma_address(page, vma);
>  	pgd_t *pgd;
>  	p4d_t *p4d;
>  	pud_t *pud;
> @@ -333,8 +336,8 @@ static unsigned long dev_pagemap_mapping_shift(struct page *page,
>   * Uses GFP_ATOMIC allocations to avoid potential recursions in the VM.
>   */
>  static void add_to_kill(struct task_struct *tsk, struct page *p,
> -		       struct vm_area_struct *vma,
> -		       struct list_head *to_kill)
> +			struct address_space *mapping, pgoff_t pgoff,
> +			struct vm_area_struct *vma, struct list_head *to_kill)
>  {
>  	struct to_kill *tk;
>  
> @@ -345,9 +348,12 @@ static void add_to_kill(struct task_struct *tsk, struct page *p,
>  	}
>  
>  	tk->addr = page_address_in_vma(p, vma);
> -	if (is_zone_device_page(p))
> -		tk->size_shift = dev_pagemap_mapping_shift(p, vma);
> -	else
> +	if (is_zone_device_page(p)) {
> +		if (is_device_fsdax_page(p))
> +			tk->addr = vma->vm_start +
> +					((pgoff - vma->vm_pgoff) << PAGE_SHIFT);

It seems strange to use 'pgoff' for dax pages and not for any other page.
Why? I'd rather pass correct pgoff from all callers of add_to_kill() and
avoid this special casing...

> +		tk->size_shift = dev_pagemap_mapping_shift(p, vma, tk->addr);
> +	} else
>  		tk->size_shift = page_shift(compound_head(p));
>  
>  	/*
> @@ -495,7 +501,7 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill,
>  			if (!page_mapped_in_vma(page, vma))
>  				continue;
>  			if (vma->vm_mm == t->mm)
> -				add_to_kill(t, page, vma, to_kill);
> +				add_to_kill(t, page, NULL, 0, vma, to_kill);
>  		}
>  	}
>  	read_unlock(&tasklist_lock);
> @@ -505,24 +511,19 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill,
>  /*
>   * Collect processes when the error hit a file mapped page.
>   */
> -static void collect_procs_file(struct page *page, struct list_head *to_kill,
> -				int force_early)
> +static void collect_procs_file(struct page *page, struct address_space *mapping,
> +		pgoff_t pgoff, struct list_head *to_kill, int force_early)
>  {
>  	struct vm_area_struct *vma;
>  	struct task_struct *tsk;
> -	struct address_space *mapping = page->mapping;
> -	pgoff_t pgoff;
>  
>  	i_mmap_lock_read(mapping);
>  	read_lock(&tasklist_lock);
> -	pgoff = page_to_pgoff(page);
>  	for_each_process(tsk) {
>  		struct task_struct *t = task_early_kill(tsk, force_early);
> -
>  		if (!t)
>  			continue;
> -		vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff,
> -				      pgoff) {
> +		vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, pgoff) {
>  			/*
>  			 * Send early kill signal to tasks where a vma covers
>  			 * the page but the corrupted page is not necessarily
> @@ -531,7 +532,7 @@ static void collect_procs_file(struct page *page, struct list_head *to_kill,
>  			 * to be informed of all such data corruptions.
>  			 */
>  			if (vma->vm_mm == t->mm)
> -				add_to_kill(t, page, vma, to_kill);
> +				add_to_kill(t, page, mapping, pgoff, vma, to_kill);
>  		}
>  	}
>  	read_unlock(&tasklist_lock);
> @@ -550,7 +551,8 @@ static void collect_procs(struct page *page, struct list_head *tokill,
>  	if (PageAnon(page))
>  		collect_procs_anon(page, tokill, force_early);
>  	else
> -		collect_procs_file(page, tokill, force_early);
> +		collect_procs_file(page, page->mapping, page_to_pgoff(page),

Why not use page_mapping() helper here? It would be safer for THPs if they
ever get here...

								Honza
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

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

* Re: [PATCH 05/10] mm, pmem: Implement ->memory_failure() in pmem driver
  2020-12-30 16:55 ` [PATCH 05/10] mm, pmem: Implement ->memory_failure() in pmem driver Shiyang Ruan
@ 2021-01-06 15:55   ` Jan Kara
  0 siblings, 0 replies; 27+ messages in thread
From: Jan Kara @ 2021-01-06 15:55 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto

On Thu 31-12-20 00:55:56, Shiyang Ruan wrote:
> Call the ->memory_failure() which is implemented by pmem driver, in
> order to finally notify filesystem to handle the corrupted data.  The
> old collecting and killing processes are moved into
> mf_dax_mapping_kill_procs(), which will be called by filesystem.
> 
> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>

I understand the intent but this patch breaks DAX hwpoison handling for
everybody at this point in the series (nobody implements ->memory_failure()
handler yet) so it is bisection unfriendly. This should really be the last
step in the series once all the other infrastructure is implemented.
Furthermore AFAIU it breaks DAX hwpoison handling terminally for all
filesystems which don't implement ->corrupted_range() - e.g. for ext4.
Your series needs to implement ->corrupted_range() for all filesystems
supporting DAX so that we don't regress current functionality...

								Honza

> ---
>  drivers/nvdimm/pmem.c | 24 +++++++++++++++++++++
>  mm/memory-failure.c   | 50 +++++--------------------------------------
>  2 files changed, 29 insertions(+), 45 deletions(-)
> 
> diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
> index 875076b0ea6c..4a114937c43b 100644
> --- a/drivers/nvdimm/pmem.c
> +++ b/drivers/nvdimm/pmem.c
> @@ -363,9 +363,33 @@ static void pmem_release_disk(void *__pmem)
>  	put_disk(pmem->disk);
>  }
>  
> +static int pmem_pagemap_memory_failure(struct dev_pagemap *pgmap,
> +		unsigned long pfn, int flags)
> +{
> +	struct pmem_device *pdev;
> +	struct gendisk *disk;
> +	loff_t disk_offset;
> +	int rc = 0;
> +	unsigned long size = page_size(pfn_to_page(pfn));
> +
> +	pdev = container_of(pgmap, struct pmem_device, pgmap);
> +	disk = pdev->disk;
> +	if (!disk)
> +		return -ENXIO;
> +
> +	disk_offset = PFN_PHYS(pfn) - pdev->phys_addr - pdev->data_offset;
> +	if (disk->fops->corrupted_range) {
> +		rc = disk->fops->corrupted_range(disk, NULL, disk_offset, size, &flags);
> +		if (rc == -ENODEV)
> +			rc = -ENXIO;
> +	}
> +	return rc;
> +}
> +
>  static const struct dev_pagemap_ops fsdax_pagemap_ops = {
>  	.kill			= pmem_pagemap_kill,
>  	.cleanup		= pmem_pagemap_cleanup,
> +	.memory_failure		= pmem_pagemap_memory_failure,
>  };
>  
>  static int pmem_attach_disk(struct device *dev,
> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index 37bc6e2a9564..0109ad607fb8 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -1269,28 +1269,11 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
>  		struct dev_pagemap *pgmap)
>  {
>  	struct page *page = pfn_to_page(pfn);
> -	const bool unmap_success = true;
> -	unsigned long size = 0;
> -	struct to_kill *tk;
> -	LIST_HEAD(to_kill);
>  	int rc = -EBUSY;
> -	loff_t start;
> -	dax_entry_t cookie;
> -
> -	/*
> -	 * Prevent the inode from being freed while we are interrogating
> -	 * the address_space, typically this would be handled by
> -	 * lock_page(), but dax pages do not use the page lock. This
> -	 * also prevents changes to the mapping of this pfn until
> -	 * poison signaling is complete.
> -	 */
> -	cookie = dax_lock_page(page);
> -	if (!cookie)
> -		goto out;
>  
>  	if (hwpoison_filter(page)) {
>  		rc = 0;
> -		goto unlock;
> +		goto out;
>  	}
>  
>  	if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
> @@ -1298,7 +1281,7 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
>  		 * TODO: Handle HMM pages which may need coordination
>  		 * with device-side memory.
>  		 */
> -		goto unlock;
> +		goto out;
>  	}
>  
>  	/*
> @@ -1307,33 +1290,10 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
>  	 */
>  	SetPageHWPoison(page);
>  
> -	/*
> -	 * Unlike System-RAM there is no possibility to swap in a
> -	 * different physical page at a given virtual address, so all
> -	 * userspace consumption of ZONE_DEVICE memory necessitates
> -	 * SIGBUS (i.e. MF_MUST_KILL)
> -	 */
> -	flags |= MF_ACTION_REQUIRED | MF_MUST_KILL;
> -	collect_procs_file(page, page->mapping, page->index, &to_kill,
> -			   flags & MF_ACTION_REQUIRED);
> +	/* call driver to handle the memory failure */
> +	if (pgmap->ops->memory_failure)
> +		rc = pgmap->ops->memory_failure(pgmap, pfn, flags);
>  
> -	list_for_each_entry(tk, &to_kill, nd)
> -		if (tk->size_shift)
> -			size = max(size, 1UL << tk->size_shift);
> -	if (size) {
> -		/*
> -		 * Unmap the largest mapping to avoid breaking up
> -		 * device-dax mappings which are constant size. The
> -		 * actual size of the mapping being torn down is
> -		 * communicated in siginfo, see kill_proc()
> -		 */
> -		start = (page->index << PAGE_SHIFT) & ~(size - 1);
> -		unmap_mapping_range(page->mapping, start, start + size, 0);
> -	}
> -	kill_procs(&to_kill, flags & MF_MUST_KILL, !unmap_success, pfn, flags);
> -	rc = 0;
> -unlock:
> -	dax_unlock_page(page, cookie);
>  out:
>  	/* drop pgmap ref acquired in caller */
>  	put_dev_pagemap(pgmap);
> -- 
> 2.29.2
> 
> 
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

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

* Re: [PATCH 08/10] md: Implement ->corrupted_range()
  2020-12-30 16:55 ` [PATCH 08/10] md: Implement ->corrupted_range() Shiyang Ruan
@ 2021-01-06 17:14   ` Jan Kara
  2021-01-12 12:45     ` Ruan Shiyang
  0 siblings, 1 reply; 27+ messages in thread
From: Jan Kara @ 2021-01-06 17:14 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto

On Thu 31-12-20 00:55:59, Shiyang Ruan wrote:
> With the support of ->rmap(), it is possible to obtain the superblock on
> a mapped device.
> 
> If a pmem device is used as one target of mapped device, we cannot
> obtain its superblock directly.  With the help of SYSFS, the mapped
> device can be found on the target devices.  So, we iterate the
> bdev->bd_holder_disks to obtain its mapped device.
> 
> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>

Thanks for the patch. Two comments below.

> diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
> index 4688bff19c20..9f9a2f3bf73b 100644
> --- a/drivers/nvdimm/pmem.c
> +++ b/drivers/nvdimm/pmem.c
> @@ -256,21 +256,16 @@ static int pmem_rw_page(struct block_device *bdev, sector_t sector,
>  static int pmem_corrupted_range(struct gendisk *disk, struct block_device *bdev,
>  				loff_t disk_offset, size_t len, void *data)
>  {
> -	struct super_block *sb;
>  	loff_t bdev_offset;
>  	sector_t disk_sector = disk_offset >> SECTOR_SHIFT;
> -	int rc = 0;
> +	int rc = -ENODEV;
>  
>  	bdev = bdget_disk_sector(disk, disk_sector);
>  	if (!bdev)
> -		return -ENODEV;
> +		return rc;
>  
>  	bdev_offset = (disk_sector - get_start_sect(bdev)) << SECTOR_SHIFT;
> -	sb = get_super(bdev);
> -	if (sb && sb->s_op->corrupted_range) {
> -		rc = sb->s_op->corrupted_range(sb, bdev, bdev_offset, len, data);
> -		drop_super(sb);
> -	}
> +	rc = bd_corrupted_range(bdev, bdev_offset, bdev_offset, len, data);
>  
>  	bdput(bdev);
>  	return rc;

This (and the fs/block_dev.c change below) is just refining the function
you've implemented in the patch 6. I think it's confusing to split changes
like this - why not implement things correctly from the start in patch 6?

> diff --git a/fs/block_dev.c b/fs/block_dev.c
> index 9e84b1928b94..0e50f0e8e8af 100644
> --- a/fs/block_dev.c
> +++ b/fs/block_dev.c
> @@ -1171,6 +1171,27 @@ struct bd_holder_disk {
>  	int			refcnt;
>  };
>  
> +static int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
> +					  size_t len, void *data)
> +{
> +	struct bd_holder_disk *holder;
> +	struct gendisk *disk;
> +	int rc = 0;
> +
> +	if (list_empty(&(bdev->bd_holder_disks)))
> +		return -ENODEV;

This will not compile for !CONFIG_SYSFS kernels. Not that it would be
common but still. Also I'm not sure whether using bd_holder_disks like this
is really the right thing to do (when it seems to be only a sysfs thing),
although admittedly I'm not aware of a better way of getting this
information.

								Honza

> +
> +	list_for_each_entry(holder, &bdev->bd_holder_disks, list) {
> +		disk = holder->disk;
> +		if (disk->fops->corrupted_range) {
> +			rc = disk->fops->corrupted_range(disk, bdev, off, len, data);
> +			if (rc != -ENODEV)
> +				break;
> +		}
> +	}
> +	return rc;
> +}
> +
>  static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
>  						  struct gendisk *disk)
>  {
> @@ -1378,6 +1399,22 @@ void bd_set_nr_sectors(struct block_device *bdev, sector_t sectors)
>  }
>  EXPORT_SYMBOL(bd_set_nr_sectors);
>  
> +int bd_corrupted_range(struct block_device *bdev, loff_t disk_off, loff_t bdev_off, size_t len, void *data)
> +{
> +	struct super_block *sb = get_super(bdev);
> +	int rc = 0;
> +
> +	if (!sb) {
> +		rc = bd_disk_holder_corrupted_range(bdev, disk_off, len, data);
> +		return rc;
> +	} else if (sb->s_op->corrupted_range)
> +		rc = sb->s_op->corrupted_range(sb, bdev, bdev_off, len, data);
> +	drop_super(sb);
> +
> +	return rc;
> +}
> +EXPORT_SYMBOL(bd_corrupted_range);
> +
>  static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part);
>  
>  int bdev_disk_changed(struct block_device *bdev, bool invalidate)
> diff --git a/include/linux/genhd.h b/include/linux/genhd.h
> index ed06209008b8..42290470810d 100644
> --- a/include/linux/genhd.h
> +++ b/include/linux/genhd.h
> @@ -376,6 +376,8 @@ void revalidate_disk_size(struct gendisk *disk, bool verbose);
>  bool bdev_check_media_change(struct block_device *bdev);
>  int __invalidate_device(struct block_device *bdev, bool kill_dirty);
>  void bd_set_nr_sectors(struct block_device *bdev, sector_t sectors);
> +int bd_corrupted_range(struct block_device *bdev, loff_t disk_off,
> +		       loff_t bdev_off, size_t len, void *data);
>  
>  /* for drivers/char/raw.c: */
>  int blkdev_ioctl(struct block_device *, fmode_t, unsigned, unsigned long);
> -- 
> 2.29.2
> 
> 
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

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

* Re: [PATCH 02/10] blk: Introduce ->corrupted_range() for block device
  2020-12-30 16:55 ` [PATCH 02/10] blk: Introduce ->corrupted_range() for block device Shiyang Ruan
@ 2021-01-08  9:55   ` Christoph Hellwig
  2021-01-08 19:09     ` Darrick J. Wong
  0 siblings, 1 reply; 27+ messages in thread
From: Christoph Hellwig @ 2021-01-08  9:55 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto

It happens on a dax_device.  We should not interwind dax and block_device
even more after a lot of good work has happened to detangle them.

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

* Re: [PATCH 03/10] fs: Introduce ->corrupted_range() for superblock
  2020-12-30 16:55 ` [PATCH 03/10] fs: Introduce ->corrupted_range() for superblock Shiyang Ruan
@ 2021-01-08  9:56   ` Christoph Hellwig
  0 siblings, 0 replies; 27+ messages in thread
From: Christoph Hellwig @ 2021-01-08  9:56 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto

On Thu, Dec 31, 2020 at 12:55:54AM +0800, Shiyang Ruan wrote:
> Memory failure occurs in fsdax mode will finally be handled in
> filesystem.  We introduce this interface to find out files or metadata
> affected by the corrupted range, and try to recover the corrupted data
> if possiable.
> 
> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
> ---
>  include/linux/fs.h | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 8667d0cdc71e..282e2139b23e 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -1965,6 +1965,8 @@ struct super_operations {
>  				  struct shrink_control *);
>  	long (*free_cached_objects)(struct super_block *,
>  				    struct shrink_control *);
> +	int (*corrupted_range)(struct super_block *sb, struct block_device *bdev,

This adds an overly long line.  But more importantly it must work on
the dax device and not the block device.  I'd also structure the callback
so that it is called on the dax device only, with the file system storing
the super block in a private data member.

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

* Re: [PATCH 02/10] blk: Introduce ->corrupted_range() for block device
  2021-01-08  9:55   ` Christoph Hellwig
@ 2021-01-08 19:09     ` Darrick J. Wong
  0 siblings, 0 replies; 27+ messages in thread
From: Darrick J. Wong @ 2021-01-08 19:09 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Shiyang Ruan, linux-kernel, linux-xfs, linux-nvdimm, linux-mm,
	linux-fsdevel, linux-raid, dan.j.williams, david, song, rgoldwyn,
	qi.fuli, y-goto

On Fri, Jan 08, 2021 at 10:55:00AM +0100, Christoph Hellwig wrote:
> It happens on a dax_device.  We should not interwind dax and block_device
> even more after a lot of good work has happened to detangle them.

I agree that the dax device should not be implied from the block device,
but what happens if regular block device drivers grow the ability to
(say) perform a background integrity scan and want to ->corrupted_range?

--D

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

* Re: [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping
  2021-01-06 15:41   ` Jan Kara
@ 2021-01-12  2:55     ` Ruan Shiyang
  2021-01-13 10:04       ` zhong jiang
  0 siblings, 1 reply; 27+ messages in thread
From: Ruan Shiyang @ 2021-01-12  2:55 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto



On 2021/1/6 下午11:41, Jan Kara wrote:
> On Thu 31-12-20 00:55:55, Shiyang Ruan wrote:
>> The current memory_failure_dev_pagemap() can only handle single-mapped
>> dax page for fsdax mode.  The dax page could be mapped by multiple files
>> and offsets if we let reflink feature & fsdax mode work together.  So,
>> we refactor current implementation to support handle memory failure on
>> each file and offset.
>>
>> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
> 
> Overall this looks OK to me, a few comments below.
> 
>> ---
>>   fs/dax.c            | 21 +++++++++++
>>   include/linux/dax.h |  1 +
>>   include/linux/mm.h  |  9 +++++
>>   mm/memory-failure.c | 91 ++++++++++++++++++++++++++++++++++-----------
>>   4 files changed, 100 insertions(+), 22 deletions(-)

...

>>   
>> @@ -345,9 +348,12 @@ static void add_to_kill(struct task_struct *tsk, struct page *p,
>>   	}
>>   
>>   	tk->addr = page_address_in_vma(p, vma);
>> -	if (is_zone_device_page(p))
>> -		tk->size_shift = dev_pagemap_mapping_shift(p, vma);
>> -	else
>> +	if (is_zone_device_page(p)) {
>> +		if (is_device_fsdax_page(p))
>> +			tk->addr = vma->vm_start +
>> +					((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
> 
> It seems strange to use 'pgoff' for dax pages and not for any other page.
> Why? I'd rather pass correct pgoff from all callers of add_to_kill() and
> avoid this special casing...

Because one fsdax page can be shared by multiple pgoffs.  I have to pass 
each pgoff in each iteration to calculate the address in vma (for 
tk->addr).  Other kinds of pages don't need this.  They can get their 
unique address by calling "page_address_in_vma()".

So, I added this fsdax case here.  This patchset only implemented the 
fsdax case, other cases also need to be added here if to be implemented.


--
Thanks,
Ruan Shiyang.

> 
>> +		tk->size_shift = dev_pagemap_mapping_shift(p, vma, tk->addr);
>> +	} else
>>   		tk->size_shift = page_shift(compound_head(p));
>>   
>>   	/*
>> @@ -495,7 +501,7 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill,
>>   			if (!page_mapped_in_vma(page, vma))
>>   				continue;
>>   			if (vma->vm_mm == t->mm)
>> -				add_to_kill(t, page, vma, to_kill);
>> +				add_to_kill(t, page, NULL, 0, vma, to_kill);
>>   		}
>>   	}
>>   	read_unlock(&tasklist_lock);
>> @@ -505,24 +511,19 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill,
>>   /*
>>    * Collect processes when the error hit a file mapped page.
>>    */
>> -static void collect_procs_file(struct page *page, struct list_head *to_kill,
>> -				int force_early)
>> +static void collect_procs_file(struct page *page, struct address_space *mapping,
>> +		pgoff_t pgoff, struct list_head *to_kill, int force_early)
>>   {
>>   	struct vm_area_struct *vma;
>>   	struct task_struct *tsk;
>> -	struct address_space *mapping = page->mapping;
>> -	pgoff_t pgoff;
>>   
>>   	i_mmap_lock_read(mapping);
>>   	read_lock(&tasklist_lock);
>> -	pgoff = page_to_pgoff(page);
>>   	for_each_process(tsk) {
>>   		struct task_struct *t = task_early_kill(tsk, force_early);
>> -
>>   		if (!t)
>>   			continue;
>> -		vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff,
>> -				      pgoff) {
>> +		vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, pgoff) {
>>   			/*
>>   			 * Send early kill signal to tasks where a vma covers
>>   			 * the page but the corrupted page is not necessarily
>> @@ -531,7 +532,7 @@ static void collect_procs_file(struct page *page, struct list_head *to_kill,
>>   			 * to be informed of all such data corruptions.
>>   			 */
>>   			if (vma->vm_mm == t->mm)
>> -				add_to_kill(t, page, vma, to_kill);
>> +				add_to_kill(t, page, mapping, pgoff, vma, to_kill);
>>   		}
>>   	}
>>   	read_unlock(&tasklist_lock);
>> @@ -550,7 +551,8 @@ static void collect_procs(struct page *page, struct list_head *tokill,
>>   	if (PageAnon(page))
>>   		collect_procs_anon(page, tokill, force_early);
>>   	else
>> -		collect_procs_file(page, tokill, force_early);
>> +		collect_procs_file(page, page->mapping, page_to_pgoff(page),
> 
> Why not use page_mapping() helper here? It would be safer for THPs if they
> ever get here...
> 
> 								Honza
> 



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

* Re: [PATCH 08/10] md: Implement ->corrupted_range()
  2021-01-06 17:14   ` Jan Kara
@ 2021-01-12 12:45     ` Ruan Shiyang
  0 siblings, 0 replies; 27+ messages in thread
From: Ruan Shiyang @ 2021-01-12 12:45 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto



On 2021/1/7 上午1:14, Jan Kara wrote:
> On Thu 31-12-20 00:55:59, Shiyang Ruan wrote:
>> With the support of ->rmap(), it is possible to obtain the superblock on
>> a mapped device.
>>
>> If a pmem device is used as one target of mapped device, we cannot
>> obtain its superblock directly.  With the help of SYSFS, the mapped
>> device can be found on the target devices.  So, we iterate the
>> bdev->bd_holder_disks to obtain its mapped device.
>>
>> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
> 
> Thanks for the patch. Two comments below.
> 
>> diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
>> index 4688bff19c20..9f9a2f3bf73b 100644
>> --- a/drivers/nvdimm/pmem.c
>> +++ b/drivers/nvdimm/pmem.c
>> @@ -256,21 +256,16 @@ static int pmem_rw_page(struct block_device *bdev, sector_t sector,
>>   static int pmem_corrupted_range(struct gendisk *disk, struct block_device *bdev,
>>   				loff_t disk_offset, size_t len, void *data)
>>   {
>> -	struct super_block *sb;
>>   	loff_t bdev_offset;
>>   	sector_t disk_sector = disk_offset >> SECTOR_SHIFT;
>> -	int rc = 0;
>> +	int rc = -ENODEV;
>>   
>>   	bdev = bdget_disk_sector(disk, disk_sector);
>>   	if (!bdev)
>> -		return -ENODEV;
>> +		return rc;
>>   
>>   	bdev_offset = (disk_sector - get_start_sect(bdev)) << SECTOR_SHIFT;
>> -	sb = get_super(bdev);
>> -	if (sb && sb->s_op->corrupted_range) {
>> -		rc = sb->s_op->corrupted_range(sb, bdev, bdev_offset, len, data);
>> -		drop_super(sb);
>> -	}
>> +	rc = bd_corrupted_range(bdev, bdev_offset, bdev_offset, len, data);
>>   
>>   	bdput(bdev);
>>   	return rc;
> 
> This (and the fs/block_dev.c change below) is just refining the function
> you've implemented in the patch 6. I think it's confusing to split changes
> like this - why not implement things correctly from the start in patch 6?

This change added a helper function to find the md devices created on a 
low-level block device, such as a LVM on /dev/pmem0, and calls 
->corrupted_range() for each md device.  The md parts were introduced 
starts from patch 7.  So, I add this change in this patch.

> 
>> diff --git a/fs/block_dev.c b/fs/block_dev.c
>> index 9e84b1928b94..0e50f0e8e8af 100644
>> --- a/fs/block_dev.c
>> +++ b/fs/block_dev.c
>> @@ -1171,6 +1171,27 @@ struct bd_holder_disk {
>>   	int			refcnt;
>>   };
>>   
>> +static int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
>> +					  size_t len, void *data)
>> +{
>> +	struct bd_holder_disk *holder;
>> +	struct gendisk *disk;
>> +	int rc = 0;
>> +
>> +	if (list_empty(&(bdev->bd_holder_disks)))
>> +		return -ENODEV;
> 
> This will not compile for !CONFIG_SYSFS kernels. Not that it would be
> common but still. Also I'm not sure whether using bd_holder_disks like this
> is really the right thing to do (when it seems to be only a sysfs thing),
> although admittedly I'm not aware of a better way of getting this
> information.

I did a lot of tries and finally found this way.  I think I should add a 
judgement that whether CONFIG_SYSFS is turned on.


--
Thanks,
Ruan Shiyang.

> 
> 								Honza
> 
>> +
>> +	list_for_each_entry(holder, &bdev->bd_holder_disks, list) {
>> +		disk = holder->disk;
>> +		if (disk->fops->corrupted_range) {
>> +			rc = disk->fops->corrupted_range(disk, bdev, off, len, data);
>> +			if (rc != -ENODEV)
>> +				break;
>> +		}
>> +	}
>> +	return rc;
>> +}
>> +
>>   static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
>>   						  struct gendisk *disk)
>>   {
>> @@ -1378,6 +1399,22 @@ void bd_set_nr_sectors(struct block_device *bdev, sector_t sectors)
>>   }
>>   EXPORT_SYMBOL(bd_set_nr_sectors);
>>   
>> +int bd_corrupted_range(struct block_device *bdev, loff_t disk_off, loff_t bdev_off, size_t len, void *data)
>> +{
>> +	struct super_block *sb = get_super(bdev);
>> +	int rc = 0;
>> +
>> +	if (!sb) {
>> +		rc = bd_disk_holder_corrupted_range(bdev, disk_off, len, data);
>> +		return rc;
>> +	} else if (sb->s_op->corrupted_range)
>> +		rc = sb->s_op->corrupted_range(sb, bdev, bdev_off, len, data);
>> +	drop_super(sb);
>> +
>> +	return rc;
>> +}
>> +EXPORT_SYMBOL(bd_corrupted_range);
>> +
>>   static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part);
>>   
>>   int bdev_disk_changed(struct block_device *bdev, bool invalidate)
>> diff --git a/include/linux/genhd.h b/include/linux/genhd.h
>> index ed06209008b8..42290470810d 100644
>> --- a/include/linux/genhd.h
>> +++ b/include/linux/genhd.h
>> @@ -376,6 +376,8 @@ void revalidate_disk_size(struct gendisk *disk, bool verbose);
>>   bool bdev_check_media_change(struct block_device *bdev);
>>   int __invalidate_device(struct block_device *bdev, bool kill_dirty);
>>   void bd_set_nr_sectors(struct block_device *bdev, sector_t sectors);
>> +int bd_corrupted_range(struct block_device *bdev, loff_t disk_off,
>> +		       loff_t bdev_off, size_t len, void *data);
>>   
>>   /* for drivers/char/raw.c: */
>>   int blkdev_ioctl(struct block_device *, fmode_t, unsigned, unsigned long);
>> -- 
>> 2.29.2
>>
>>
>>



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

* Re: [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping
  2021-01-12  2:55     ` Ruan Shiyang
@ 2021-01-13 10:04       ` zhong jiang
  2021-01-14  1:44         ` Ruan Shiyang
  0 siblings, 1 reply; 27+ messages in thread
From: zhong jiang @ 2021-01-13 10:04 UTC (permalink / raw)
  To: Ruan Shiyang, Jan Kara
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto


On 2021/1/12 10:55 上午, Ruan Shiyang wrote:
>
>
> On 2021/1/6 下午11:41, Jan Kara wrote:
>> On Thu 31-12-20 00:55:55, Shiyang Ruan wrote:
>>> The current memory_failure_dev_pagemap() can only handle single-mapped
>>> dax page for fsdax mode.  The dax page could be mapped by multiple 
>>> files
>>> and offsets if we let reflink feature & fsdax mode work together.  So,
>>> we refactor current implementation to support handle memory failure on
>>> each file and offset.
>>>
>>> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
>>
>> Overall this looks OK to me, a few comments below.
>>
>>> ---
>>>   fs/dax.c            | 21 +++++++++++
>>>   include/linux/dax.h |  1 +
>>>   include/linux/mm.h  |  9 +++++
>>>   mm/memory-failure.c | 91 
>>> ++++++++++++++++++++++++++++++++++-----------
>>>   4 files changed, 100 insertions(+), 22 deletions(-)
>
> ...
>
>>>   @@ -345,9 +348,12 @@ static void add_to_kill(struct task_struct 
>>> *tsk, struct page *p,
>>>       }
>>>         tk->addr = page_address_in_vma(p, vma);
>>> -    if (is_zone_device_page(p))
>>> -        tk->size_shift = dev_pagemap_mapping_shift(p, vma);
>>> -    else
>>> +    if (is_zone_device_page(p)) {
>>> +        if (is_device_fsdax_page(p))
>>> +            tk->addr = vma->vm_start +
>>> +                    ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
>>
>> It seems strange to use 'pgoff' for dax pages and not for any other 
>> page.
>> Why? I'd rather pass correct pgoff from all callers of add_to_kill() and
>> avoid this special casing...
>
> Because one fsdax page can be shared by multiple pgoffs.  I have to 
> pass each pgoff in each iteration to calculate the address in vma (for 
> tk->addr).  Other kinds of pages don't need this. They can get their 
> unique address by calling "page_address_in_vma()".
>
IMO,   an fsdax page can be shared by multiple files rather than 
multiple pgoffs if fs query support reflink.   Because an page only 
located in an mapping(page->mapping is exclusive),  hence it  only has 
an pgoff or index pointing at the node.

  or  I miss something for the feature ?  thanks,

> So, I added this fsdax case here.  This patchset only implemented the 
> fsdax case, other cases also need to be added here if to be implemented.
>
>
> -- 
> Thanks,
> Ruan Shiyang.
>
>>
>>> +        tk->size_shift = dev_pagemap_mapping_shift(p, vma, tk->addr);
>>> +    } else
>>>           tk->size_shift = page_shift(compound_head(p));
>>>         /*
>>> @@ -495,7 +501,7 @@ static void collect_procs_anon(struct page 
>>> *page, struct list_head *to_kill,
>>>               if (!page_mapped_in_vma(page, vma))
>>>                   continue;
>>>               if (vma->vm_mm == t->mm)
>>> -                add_to_kill(t, page, vma, to_kill);
>>> +                add_to_kill(t, page, NULL, 0, vma, to_kill);
>>>           }
>>>       }
>>>       read_unlock(&tasklist_lock);
>>> @@ -505,24 +511,19 @@ static void collect_procs_anon(struct page 
>>> *page, struct list_head *to_kill,
>>>   /*
>>>    * Collect processes when the error hit a file mapped page.
>>>    */
>>> -static void collect_procs_file(struct page *page, struct list_head 
>>> *to_kill,
>>> -                int force_early)
>>> +static void collect_procs_file(struct page *page, struct 
>>> address_space *mapping,
>>> +        pgoff_t pgoff, struct list_head *to_kill, int force_early)
>>>   {
>>>       struct vm_area_struct *vma;
>>>       struct task_struct *tsk;
>>> -    struct address_space *mapping = page->mapping;
>>> -    pgoff_t pgoff;
>>>         i_mmap_lock_read(mapping);
>>>       read_lock(&tasklist_lock);
>>> -    pgoff = page_to_pgoff(page);
>>>       for_each_process(tsk) {
>>>           struct task_struct *t = task_early_kill(tsk, force_early);
>>> -
>>>           if (!t)
>>>               continue;
>>> -        vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff,
>>> -                      pgoff) {
>>> +        vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, 
>>> pgoff) {
>>>               /*
>>>                * Send early kill signal to tasks where a vma covers
>>>                * the page but the corrupted page is not necessarily
>>> @@ -531,7 +532,7 @@ static void collect_procs_file(struct page 
>>> *page, struct list_head *to_kill,
>>>                * to be informed of all such data corruptions.
>>>                */
>>>               if (vma->vm_mm == t->mm)
>>> -                add_to_kill(t, page, vma, to_kill);
>>> +                add_to_kill(t, page, mapping, pgoff, vma, to_kill);
>>>           }
>>>       }
>>>       read_unlock(&tasklist_lock);
>>> @@ -550,7 +551,8 @@ static void collect_procs(struct page *page, 
>>> struct list_head *tokill,
>>>       if (PageAnon(page))
>>>           collect_procs_anon(page, tokill, force_early);
>>>       else
>>> -        collect_procs_file(page, tokill, force_early);
>>> +        collect_procs_file(page, page->mapping, page_to_pgoff(page),
>>
>> Why not use page_mapping() helper here? It would be safer for THPs if 
>> they
>> ever get here...
>>
>>                                 Honza
>>
>

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

* Re: [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping
  2021-01-13 10:04       ` zhong jiang
@ 2021-01-14  1:44         ` Ruan Shiyang
  2021-01-14  3:26           ` zhong jiang
  0 siblings, 1 reply; 27+ messages in thread
From: Ruan Shiyang @ 2021-01-14  1:44 UTC (permalink / raw)
  To: zhong jiang, Jan Kara
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto



On 2021/1/13 下午6:04, zhong jiang wrote:
> 
> On 2021/1/12 10:55 上午, Ruan Shiyang wrote:
>>
>>
>> On 2021/1/6 下午11:41, Jan Kara wrote:
>>> On Thu 31-12-20 00:55:55, Shiyang Ruan wrote:
>>>> The current memory_failure_dev_pagemap() can only handle single-mapped
>>>> dax page for fsdax mode.  The dax page could be mapped by multiple 
>>>> files
>>>> and offsets if we let reflink feature & fsdax mode work together.  So,
>>>> we refactor current implementation to support handle memory failure on
>>>> each file and offset.
>>>>
>>>> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
>>>
>>> Overall this looks OK to me, a few comments below.
>>>
>>>> ---
>>>>   fs/dax.c            | 21 +++++++++++
>>>>   include/linux/dax.h |  1 +
>>>>   include/linux/mm.h  |  9 +++++
>>>>   mm/memory-failure.c | 91 
>>>> ++++++++++++++++++++++++++++++++++-----------
>>>>   4 files changed, 100 insertions(+), 22 deletions(-)
>>
>> ...
>>
>>>>   @@ -345,9 +348,12 @@ static void add_to_kill(struct task_struct 
>>>> *tsk, struct page *p,
>>>>       }
>>>>         tk->addr = page_address_in_vma(p, vma);
>>>> -    if (is_zone_device_page(p))
>>>> -        tk->size_shift = dev_pagemap_mapping_shift(p, vma);
>>>> -    else
>>>> +    if (is_zone_device_page(p)) {
>>>> +        if (is_device_fsdax_page(p))
>>>> +            tk->addr = vma->vm_start +
>>>> +                    ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
>>>
>>> It seems strange to use 'pgoff' for dax pages and not for any other 
>>> page.
>>> Why? I'd rather pass correct pgoff from all callers of add_to_kill() and
>>> avoid this special casing...
>>
>> Because one fsdax page can be shared by multiple pgoffs.  I have to 
>> pass each pgoff in each iteration to calculate the address in vma (for 
>> tk->addr).  Other kinds of pages don't need this. They can get their 
>> unique address by calling "page_address_in_vma()".
>>
> IMO,   an fsdax page can be shared by multiple files rather than 
> multiple pgoffs if fs query support reflink.   Because an page only 
> located in an mapping(page->mapping is exclusive),  hence it  only has 
> an pgoff or index pointing at the node.
> 
>   or  I miss something for the feature ?  thanks,

Yes, a fsdax page is shared by multiple files because of reflink.  I 
think my description of 'pgoff' here is not correct.  This 'pgoff' means 
the offset within the a file.  (We use rmap to find out all the sharing 
files and their offsets.)  So, I said that "can be shared by multiple 
pgoffs".  It's my bad.

I think I should name it another word to avoid misunderstandings.


--
Thanks,
Ruan Shiyang.

> 
>> So, I added this fsdax case here.  This patchset only implemented the 
>> fsdax case, other cases also need to be added here if to be implemented.
>>
>>
>> -- 
>> Thanks,
>> Ruan Shiyang.
>>
>>>
>>>> +        tk->size_shift = dev_pagemap_mapping_shift(p, vma, tk->addr);
>>>> +    } else
>>>>           tk->size_shift = page_shift(compound_head(p));
>>>>         /*
>>>> @@ -495,7 +501,7 @@ static void collect_procs_anon(struct page 
>>>> *page, struct list_head *to_kill,
>>>>               if (!page_mapped_in_vma(page, vma))
>>>>                   continue;
>>>>               if (vma->vm_mm == t->mm)
>>>> -                add_to_kill(t, page, vma, to_kill);
>>>> +                add_to_kill(t, page, NULL, 0, vma, to_kill);
>>>>           }
>>>>       }
>>>>       read_unlock(&tasklist_lock);
>>>> @@ -505,24 +511,19 @@ static void collect_procs_anon(struct page 
>>>> *page, struct list_head *to_kill,
>>>>   /*
>>>>    * Collect processes when the error hit a file mapped page.
>>>>    */
>>>> -static void collect_procs_file(struct page *page, struct list_head 
>>>> *to_kill,
>>>> -                int force_early)
>>>> +static void collect_procs_file(struct page *page, struct 
>>>> address_space *mapping,
>>>> +        pgoff_t pgoff, struct list_head *to_kill, int force_early)
>>>>   {
>>>>       struct vm_area_struct *vma;
>>>>       struct task_struct *tsk;
>>>> -    struct address_space *mapping = page->mapping;
>>>> -    pgoff_t pgoff;
>>>>         i_mmap_lock_read(mapping);
>>>>       read_lock(&tasklist_lock);
>>>> -    pgoff = page_to_pgoff(page);
>>>>       for_each_process(tsk) {
>>>>           struct task_struct *t = task_early_kill(tsk, force_early);
>>>> -
>>>>           if (!t)
>>>>               continue;
>>>> -        vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff,
>>>> -                      pgoff) {
>>>> +        vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, 
>>>> pgoff) {
>>>>               /*
>>>>                * Send early kill signal to tasks where a vma covers
>>>>                * the page but the corrupted page is not necessarily
>>>> @@ -531,7 +532,7 @@ static void collect_procs_file(struct page 
>>>> *page, struct list_head *to_kill,
>>>>                * to be informed of all such data corruptions.
>>>>                */
>>>>               if (vma->vm_mm == t->mm)
>>>> -                add_to_kill(t, page, vma, to_kill);
>>>> +                add_to_kill(t, page, mapping, pgoff, vma, to_kill);
>>>>           }
>>>>       }
>>>>       read_unlock(&tasklist_lock);
>>>> @@ -550,7 +551,8 @@ static void collect_procs(struct page *page, 
>>>> struct list_head *tokill,
>>>>       if (PageAnon(page))
>>>>           collect_procs_anon(page, tokill, force_early);
>>>>       else
>>>> -        collect_procs_file(page, tokill, force_early);
>>>> +        collect_procs_file(page, page->mapping, page_to_pgoff(page),
>>>
>>> Why not use page_mapping() helper here? It would be safer for THPs if 
>>> they
>>> ever get here...
>>>
>>>                                 Honza
>>>
>>
> 
> 



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

* Re: [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping
  2021-01-14  1:44         ` Ruan Shiyang
@ 2021-01-14  3:26           ` zhong jiang
  2021-01-14  3:52             ` Ruan Shiyang
  0 siblings, 1 reply; 27+ messages in thread
From: zhong jiang @ 2021-01-14  3:26 UTC (permalink / raw)
  To: Ruan Shiyang, Jan Kara
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto


On 2021/1/14 9:44 上午, Ruan Shiyang wrote:
>
>
> On 2021/1/13 下午6:04, zhong jiang wrote:
>>
>> On 2021/1/12 10:55 上午, Ruan Shiyang wrote:
>>>
>>>
>>> On 2021/1/6 下午11:41, Jan Kara wrote:
>>>> On Thu 31-12-20 00:55:55, Shiyang Ruan wrote:
>>>>> The current memory_failure_dev_pagemap() can only handle 
>>>>> single-mapped
>>>>> dax page for fsdax mode.  The dax page could be mapped by multiple 
>>>>> files
>>>>> and offsets if we let reflink feature & fsdax mode work together.  
>>>>> So,
>>>>> we refactor current implementation to support handle memory 
>>>>> failure on
>>>>> each file and offset.
>>>>>
>>>>> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
>>>>
>>>> Overall this looks OK to me, a few comments below.
>>>>
>>>>> ---
>>>>>   fs/dax.c            | 21 +++++++++++
>>>>>   include/linux/dax.h |  1 +
>>>>>   include/linux/mm.h  |  9 +++++
>>>>>   mm/memory-failure.c | 91 
>>>>> ++++++++++++++++++++++++++++++++++-----------
>>>>>   4 files changed, 100 insertions(+), 22 deletions(-)
>>>
>>> ...
>>>
>>>>>   @@ -345,9 +348,12 @@ static void add_to_kill(struct task_struct 
>>>>> *tsk, struct page *p,
>>>>>       }
>>>>>         tk->addr = page_address_in_vma(p, vma);
>>>>> -    if (is_zone_device_page(p))
>>>>> -        tk->size_shift = dev_pagemap_mapping_shift(p, vma);
>>>>> -    else
>>>>> +    if (is_zone_device_page(p)) {
>>>>> +        if (is_device_fsdax_page(p))
>>>>> +            tk->addr = vma->vm_start +
>>>>> +                    ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
>>>>
>>>> It seems strange to use 'pgoff' for dax pages and not for any other 
>>>> page.
>>>> Why? I'd rather pass correct pgoff from all callers of 
>>>> add_to_kill() and
>>>> avoid this special casing...
>>>
>>> Because one fsdax page can be shared by multiple pgoffs.  I have to 
>>> pass each pgoff in each iteration to calculate the address in vma 
>>> (for tk->addr).  Other kinds of pages don't need this. They can get 
>>> their unique address by calling "page_address_in_vma()".
>>>
>> IMO,   an fsdax page can be shared by multiple files rather than 
>> multiple pgoffs if fs query support reflink.   Because an page only 
>> located in an mapping(page->mapping is exclusive), hence it  only has 
>> an pgoff or index pointing at the node.
>>
>>   or  I miss something for the feature ?  thanks,
>
> Yes, a fsdax page is shared by multiple files because of reflink. I 
> think my description of 'pgoff' here is not correct.  This 'pgoff' 
> means the offset within the a file.  (We use rmap to find out all the 
> sharing files and their offsets.)  So, I said that "can be shared by 
> multiple pgoffs".  It's my bad.
>
> I think I should name it another word to avoid misunderstandings.
>
IMO,  All the sharing files should be the same offset to share the fsdax 
page.  why not that ?  As you has said,  a shared fadax page should be 
inserted to different mapping files.  but page->index and page->mapping 
is exclusive.  hence an page only should be placed in an mapping tree.

And In the current patch,  we failed to found out that all process use 
the fsdax page shared by multiple files and kill them.


Thanks,

> -- 
> Thanks,
> Ruan Shiyang.
>
>>
>>> So, I added this fsdax case here.  This patchset only implemented 
>>> the fsdax case, other cases also need to be added here if to be 
>>> implemented.
>>>
>>>
>>> -- 
>>> Thanks,
>>> Ruan Shiyang.
>>>
>>>>
>>>>> +        tk->size_shift = dev_pagemap_mapping_shift(p, vma, 
>>>>> tk->addr);
>>>>> +    } else
>>>>>           tk->size_shift = page_shift(compound_head(p));
>>>>>         /*
>>>>> @@ -495,7 +501,7 @@ static void collect_procs_anon(struct page 
>>>>> *page, struct list_head *to_kill,
>>>>>               if (!page_mapped_in_vma(page, vma))
>>>>>                   continue;
>>>>>               if (vma->vm_mm == t->mm)
>>>>> -                add_to_kill(t, page, vma, to_kill);
>>>>> +                add_to_kill(t, page, NULL, 0, vma, to_kill);
>>>>>           }
>>>>>       }
>>>>>       read_unlock(&tasklist_lock);
>>>>> @@ -505,24 +511,19 @@ static void collect_procs_anon(struct page 
>>>>> *page, struct list_head *to_kill,
>>>>>   /*
>>>>>    * Collect processes when the error hit a file mapped page.
>>>>>    */
>>>>> -static void collect_procs_file(struct page *page, struct 
>>>>> list_head *to_kill,
>>>>> -                int force_early)
>>>>> +static void collect_procs_file(struct page *page, struct 
>>>>> address_space *mapping,
>>>>> +        pgoff_t pgoff, struct list_head *to_kill, int force_early)
>>>>>   {
>>>>>       struct vm_area_struct *vma;
>>>>>       struct task_struct *tsk;
>>>>> -    struct address_space *mapping = page->mapping;
>>>>> -    pgoff_t pgoff;
>>>>>         i_mmap_lock_read(mapping);
>>>>>       read_lock(&tasklist_lock);
>>>>> -    pgoff = page_to_pgoff(page);
>>>>>       for_each_process(tsk) {
>>>>>           struct task_struct *t = task_early_kill(tsk, force_early);
>>>>> -
>>>>>           if (!t)
>>>>>               continue;
>>>>> -        vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff,
>>>>> -                      pgoff) {
>>>>> +        vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, 
>>>>> pgoff) {
>>>>>               /*
>>>>>                * Send early kill signal to tasks where a vma covers
>>>>>                * the page but the corrupted page is not necessarily
>>>>> @@ -531,7 +532,7 @@ static void collect_procs_file(struct page 
>>>>> *page, struct list_head *to_kill,
>>>>>                * to be informed of all such data corruptions.
>>>>>                */
>>>>>               if (vma->vm_mm == t->mm)
>>>>> -                add_to_kill(t, page, vma, to_kill);
>>>>> +                add_to_kill(t, page, mapping, pgoff, vma, to_kill);
>>>>>           }
>>>>>       }
>>>>>       read_unlock(&tasklist_lock);
>>>>> @@ -550,7 +551,8 @@ static void collect_procs(struct page *page, 
>>>>> struct list_head *tokill,
>>>>>       if (PageAnon(page))
>>>>>           collect_procs_anon(page, tokill, force_early);
>>>>>       else
>>>>> -        collect_procs_file(page, tokill, force_early);
>>>>> +        collect_procs_file(page, page->mapping, page_to_pgoff(page),
>>>>
>>>> Why not use page_mapping() helper here? It would be safer for THPs 
>>>> if they
>>>> ever get here...
>>>>
>>>>                                 Honza
>>>>
>>>
>>
>>
>

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

* Re: [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping
  2021-01-14  3:26           ` zhong jiang
@ 2021-01-14  3:52             ` Ruan Shiyang
  2021-01-14  9:38               ` zhong jiang
  0 siblings, 1 reply; 27+ messages in thread
From: Ruan Shiyang @ 2021-01-14  3:52 UTC (permalink / raw)
  To: zhong jiang, Jan Kara
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto



On 2021/1/14 上午11:26, zhong jiang wrote:
> 
> On 2021/1/14 9:44 上午, Ruan Shiyang wrote:
>>
>>
>> On 2021/1/13 下午6:04, zhong jiang wrote:
>>>
>>> On 2021/1/12 10:55 上午, Ruan Shiyang wrote:
>>>>
>>>>
>>>> On 2021/1/6 下午11:41, Jan Kara wrote:
>>>>> On Thu 31-12-20 00:55:55, Shiyang Ruan wrote:
>>>>>> The current memory_failure_dev_pagemap() can only handle 
>>>>>> single-mapped
>>>>>> dax page for fsdax mode.  The dax page could be mapped by multiple 
>>>>>> files
>>>>>> and offsets if we let reflink feature & fsdax mode work together. So,
>>>>>> we refactor current implementation to support handle memory 
>>>>>> failure on
>>>>>> each file and offset.
>>>>>>
>>>>>> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
>>>>>
>>>>> Overall this looks OK to me, a few comments below.
>>>>>
>>>>>> ---
>>>>>>   fs/dax.c            | 21 +++++++++++
>>>>>>   include/linux/dax.h |  1 +
>>>>>>   include/linux/mm.h  |  9 +++++
>>>>>>   mm/memory-failure.c | 91 
>>>>>> ++++++++++++++++++++++++++++++++++-----------
>>>>>>   4 files changed, 100 insertions(+), 22 deletions(-)
>>>>
>>>> ...
>>>>
>>>>>>   @@ -345,9 +348,12 @@ static void add_to_kill(struct task_struct 
>>>>>> *tsk, struct page *p,
>>>>>>       }
>>>>>>         tk->addr = page_address_in_vma(p, vma);
>>>>>> -    if (is_zone_device_page(p))
>>>>>> -        tk->size_shift = dev_pagemap_mapping_shift(p, vma);
>>>>>> -    else
>>>>>> +    if (is_zone_device_page(p)) {
>>>>>> +        if (is_device_fsdax_page(p))
>>>>>> +            tk->addr = vma->vm_start +
>>>>>> +                    ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
>>>>>
>>>>> It seems strange to use 'pgoff' for dax pages and not for any other 
>>>>> page.
>>>>> Why? I'd rather pass correct pgoff from all callers of 
>>>>> add_to_kill() and
>>>>> avoid this special casing...
>>>>
>>>> Because one fsdax page can be shared by multiple pgoffs.  I have to 
>>>> pass each pgoff in each iteration to calculate the address in vma 
>>>> (for tk->addr).  Other kinds of pages don't need this. They can get 
>>>> their unique address by calling "page_address_in_vma()".
>>>>
>>> IMO,   an fsdax page can be shared by multiple files rather than 
>>> multiple pgoffs if fs query support reflink.   Because an page only 
>>> located in an mapping(page->mapping is exclusive), hence it  only has 
>>> an pgoff or index pointing at the node.
>>>
>>>   or  I miss something for the feature ?  thanks,
>>
>> Yes, a fsdax page is shared by multiple files because of reflink. I 
>> think my description of 'pgoff' here is not correct.  This 'pgoff' 
>> means the offset within the a file.  (We use rmap to find out all the 
>> sharing files and their offsets.)  So, I said that "can be shared by 
>> multiple pgoffs".  It's my bad.
>>
>> I think I should name it another word to avoid misunderstandings.
>>
> IMO,  All the sharing files should be the same offset to share the fsdax 
> page.  why not that ? 

The dedupe operation can let different files share their same data 
extent, though offsets are not same.  So, files can share one fsdax page 
at different offset.

> As you has said,  a shared fadax page should be 
> inserted to different mapping files.  but page->index and page->mapping 
> is exclusive.  hence an page only should be placed in an mapping tree.

We can't use page->mapping and page->index here for reflink & fsdax. 
And that's this patchset aims to solve.  I introduced a series of 
->corrupted_range(), from mm to pmem driver to block device and finally 
to filesystem, to use rmap feature of filesystem to find out all files 
sharing same data extent (fsdax page).


--
Thanks,
Ruan Shiyang.

> 
> And In the current patch,  we failed to found out that all process use 
> the fsdax page shared by multiple files and kill them.
> 
> 
> Thanks,
> 
>> -- 
>> Thanks,
>> Ruan Shiyang.
>>
>>>
>>>> So, I added this fsdax case here.  This patchset only implemented 
>>>> the fsdax case, other cases also need to be added here if to be 
>>>> implemented.
>>>>
>>>>
>>>> -- 
>>>> Thanks,
>>>> Ruan Shiyang.
>>>>
>>>>>
>>>>>> +        tk->size_shift = dev_pagemap_mapping_shift(p, vma, 
>>>>>> tk->addr);
>>>>>> +    } else
>>>>>>           tk->size_shift = page_shift(compound_head(p));
>>>>>>         /*
>>>>>> @@ -495,7 +501,7 @@ static void collect_procs_anon(struct page 
>>>>>> *page, struct list_head *to_kill,
>>>>>>               if (!page_mapped_in_vma(page, vma))
>>>>>>                   continue;
>>>>>>               if (vma->vm_mm == t->mm)
>>>>>> -                add_to_kill(t, page, vma, to_kill);
>>>>>> +                add_to_kill(t, page, NULL, 0, vma, to_kill);
>>>>>>           }
>>>>>>       }
>>>>>>       read_unlock(&tasklist_lock);
>>>>>> @@ -505,24 +511,19 @@ static void collect_procs_anon(struct page 
>>>>>> *page, struct list_head *to_kill,
>>>>>>   /*
>>>>>>    * Collect processes when the error hit a file mapped page.
>>>>>>    */
>>>>>> -static void collect_procs_file(struct page *page, struct 
>>>>>> list_head *to_kill,
>>>>>> -                int force_early)
>>>>>> +static void collect_procs_file(struct page *page, struct 
>>>>>> address_space *mapping,
>>>>>> +        pgoff_t pgoff, struct list_head *to_kill, int force_early)
>>>>>>   {
>>>>>>       struct vm_area_struct *vma;
>>>>>>       struct task_struct *tsk;
>>>>>> -    struct address_space *mapping = page->mapping;
>>>>>> -    pgoff_t pgoff;
>>>>>>         i_mmap_lock_read(mapping);
>>>>>>       read_lock(&tasklist_lock);
>>>>>> -    pgoff = page_to_pgoff(page);
>>>>>>       for_each_process(tsk) {
>>>>>>           struct task_struct *t = task_early_kill(tsk, force_early);
>>>>>> -
>>>>>>           if (!t)
>>>>>>               continue;
>>>>>> -        vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff,
>>>>>> -                      pgoff) {
>>>>>> +        vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, 
>>>>>> pgoff) {
>>>>>>               /*
>>>>>>                * Send early kill signal to tasks where a vma covers
>>>>>>                * the page but the corrupted page is not necessarily
>>>>>> @@ -531,7 +532,7 @@ static void collect_procs_file(struct page 
>>>>>> *page, struct list_head *to_kill,
>>>>>>                * to be informed of all such data corruptions.
>>>>>>                */
>>>>>>               if (vma->vm_mm == t->mm)
>>>>>> -                add_to_kill(t, page, vma, to_kill);
>>>>>> +                add_to_kill(t, page, mapping, pgoff, vma, to_kill);
>>>>>>           }
>>>>>>       }
>>>>>>       read_unlock(&tasklist_lock);
>>>>>> @@ -550,7 +551,8 @@ static void collect_procs(struct page *page, 
>>>>>> struct list_head *tokill,
>>>>>>       if (PageAnon(page))
>>>>>>           collect_procs_anon(page, tokill, force_early);
>>>>>>       else
>>>>>> -        collect_procs_file(page, tokill, force_early);
>>>>>> +        collect_procs_file(page, page->mapping, page_to_pgoff(page),
>>>>>
>>>>> Why not use page_mapping() helper here? It would be safer for THPs 
>>>>> if they
>>>>> ever get here...
>>>>>
>>>>>                                 Honza
>>>>>
>>>>
>>>
>>>
>>
> 
> 



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

* Re: [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping
  2021-01-14  3:52             ` Ruan Shiyang
@ 2021-01-14  9:38               ` zhong jiang
  2021-01-14 17:20                 ` Darrick J. Wong
  0 siblings, 1 reply; 27+ messages in thread
From: zhong jiang @ 2021-01-14  9:38 UTC (permalink / raw)
  To: Ruan Shiyang, Jan Kara
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto


On 2021/1/14 11:52 上午, Ruan Shiyang wrote:
>
>
> On 2021/1/14 上午11:26, zhong jiang wrote:
>>
>> On 2021/1/14 9:44 上午, Ruan Shiyang wrote:
>>>
>>>
>>> On 2021/1/13 下午6:04, zhong jiang wrote:
>>>>
>>>> On 2021/1/12 10:55 上午, Ruan Shiyang wrote:
>>>>>
>>>>>
>>>>> On 2021/1/6 下午11:41, Jan Kara wrote:
>>>>>> On Thu 31-12-20 00:55:55, Shiyang Ruan wrote:
>>>>>>> The current memory_failure_dev_pagemap() can only handle 
>>>>>>> single-mapped
>>>>>>> dax page for fsdax mode.  The dax page could be mapped by 
>>>>>>> multiple files
>>>>>>> and offsets if we let reflink feature & fsdax mode work 
>>>>>>> together. So,
>>>>>>> we refactor current implementation to support handle memory 
>>>>>>> failure on
>>>>>>> each file and offset.
>>>>>>>
>>>>>>> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
>>>>>>
>>>>>> Overall this looks OK to me, a few comments below.
>>>>>>
>>>>>>> ---
>>>>>>>   fs/dax.c            | 21 +++++++++++
>>>>>>>   include/linux/dax.h |  1 +
>>>>>>>   include/linux/mm.h  |  9 +++++
>>>>>>>   mm/memory-failure.c | 91 
>>>>>>> ++++++++++++++++++++++++++++++++++-----------
>>>>>>>   4 files changed, 100 insertions(+), 22 deletions(-)
>>>>>
>>>>> ...
>>>>>
>>>>>>>   @@ -345,9 +348,12 @@ static void add_to_kill(struct 
>>>>>>> task_struct *tsk, struct page *p,
>>>>>>>       }
>>>>>>>         tk->addr = page_address_in_vma(p, vma);
>>>>>>> -    if (is_zone_device_page(p))
>>>>>>> -        tk->size_shift = dev_pagemap_mapping_shift(p, vma);
>>>>>>> -    else
>>>>>>> +    if (is_zone_device_page(p)) {
>>>>>>> +        if (is_device_fsdax_page(p))
>>>>>>> +            tk->addr = vma->vm_start +
>>>>>>> +                    ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
>>>>>>
>>>>>> It seems strange to use 'pgoff' for dax pages and not for any 
>>>>>> other page.
>>>>>> Why? I'd rather pass correct pgoff from all callers of 
>>>>>> add_to_kill() and
>>>>>> avoid this special casing...
>>>>>
>>>>> Because one fsdax page can be shared by multiple pgoffs. I have to 
>>>>> pass each pgoff in each iteration to calculate the address in vma 
>>>>> (for tk->addr).  Other kinds of pages don't need this. They can 
>>>>> get their unique address by calling "page_address_in_vma()".
>>>>>
>>>> IMO,   an fsdax page can be shared by multiple files rather than 
>>>> multiple pgoffs if fs query support reflink.   Because an page only 
>>>> located in an mapping(page->mapping is exclusive), hence it  only 
>>>> has an pgoff or index pointing at the node.
>>>>
>>>>   or  I miss something for the feature ?  thanks,
>>>
>>> Yes, a fsdax page is shared by multiple files because of reflink. I 
>>> think my description of 'pgoff' here is not correct.  This 'pgoff' 
>>> means the offset within the a file. (We use rmap to find out all the 
>>> sharing files and their offsets.)  So, I said that "can be shared by 
>>> multiple pgoffs".  It's my bad.
>>>
>>> I think I should name it another word to avoid misunderstandings.
>>>
>> IMO,  All the sharing files should be the same offset to share the 
>> fsdax page.  why not that ? 
>
> The dedupe operation can let different files share their same data 
> extent, though offsets are not same.  So, files can share one fsdax 
> page at different offset.
Ok,  Get it.
>
>> As you has said,  a shared fadax page should be inserted to different 
>> mapping files.  but page->index and page->mapping is exclusive.  
>> hence an page only should be placed in an mapping tree.
>
> We can't use page->mapping and page->index here for reflink & fsdax. 
> And that's this patchset aims to solve.  I introduced a series of 
> ->corrupted_range(), from mm to pmem driver to block device and 
> finally to filesystem, to use rmap feature of filesystem to find out 
> all files sharing same data extent (fsdax page).

 From this patch,  each file has mapping tree,  the shared page will be 
inserted into multiple file mapping tree.  then filesystem use file and 
offset to get the killed process.   Is it correct?

Thanks,

>
>
> -- 
> Thanks,
> Ruan Shiyang.
>
>>
>> And In the current patch,  we failed to found out that all process 
>> use the fsdax page shared by multiple files and kill them.
>>
>>
>> Thanks,
>>
>>> -- 
>>> Thanks,
>>> Ruan Shiyang.
>>>
>>>>
>>>>> So, I added this fsdax case here. This patchset only implemented 
>>>>> the fsdax case, other cases also need to be added here if to be 
>>>>> implemented.
>>>>>
>>>>>
>>>>> -- 
>>>>> Thanks,
>>>>> Ruan Shiyang.
>>>>>
>>>>>>
>>>>>>> +        tk->size_shift = dev_pagemap_mapping_shift(p, vma, 
>>>>>>> tk->addr);
>>>>>>> +    } else
>>>>>>>           tk->size_shift = page_shift(compound_head(p));
>>>>>>>         /*
>>>>>>> @@ -495,7 +501,7 @@ static void collect_procs_anon(struct page 
>>>>>>> *page, struct list_head *to_kill,
>>>>>>>               if (!page_mapped_in_vma(page, vma))
>>>>>>>                   continue;
>>>>>>>               if (vma->vm_mm == t->mm)
>>>>>>> -                add_to_kill(t, page, vma, to_kill);
>>>>>>> +                add_to_kill(t, page, NULL, 0, vma, to_kill);
>>>>>>>           }
>>>>>>>       }
>>>>>>>       read_unlock(&tasklist_lock);
>>>>>>> @@ -505,24 +511,19 @@ static void collect_procs_anon(struct page 
>>>>>>> *page, struct list_head *to_kill,
>>>>>>>   /*
>>>>>>>    * Collect processes when the error hit a file mapped page.
>>>>>>>    */
>>>>>>> -static void collect_procs_file(struct page *page, struct 
>>>>>>> list_head *to_kill,
>>>>>>> -                int force_early)
>>>>>>> +static void collect_procs_file(struct page *page, struct 
>>>>>>> address_space *mapping,
>>>>>>> +        pgoff_t pgoff, struct list_head *to_kill, int force_early)
>>>>>>>   {
>>>>>>>       struct vm_area_struct *vma;
>>>>>>>       struct task_struct *tsk;
>>>>>>> -    struct address_space *mapping = page->mapping;
>>>>>>> -    pgoff_t pgoff;
>>>>>>>         i_mmap_lock_read(mapping);
>>>>>>>       read_lock(&tasklist_lock);
>>>>>>> -    pgoff = page_to_pgoff(page);
>>>>>>>       for_each_process(tsk) {
>>>>>>>           struct task_struct *t = task_early_kill(tsk, 
>>>>>>> force_early);
>>>>>>> -
>>>>>>>           if (!t)
>>>>>>>               continue;
>>>>>>> -        vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff,
>>>>>>> -                      pgoff) {
>>>>>>> +        vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, 
>>>>>>> pgoff) {
>>>>>>>               /*
>>>>>>>                * Send early kill signal to tasks where a vma covers
>>>>>>>                * the page but the corrupted page is not necessarily
>>>>>>> @@ -531,7 +532,7 @@ static void collect_procs_file(struct page 
>>>>>>> *page, struct list_head *to_kill,
>>>>>>>                * to be informed of all such data corruptions.
>>>>>>>                */
>>>>>>>               if (vma->vm_mm == t->mm)
>>>>>>> -                add_to_kill(t, page, vma, to_kill);
>>>>>>> +                add_to_kill(t, page, mapping, pgoff, vma, 
>>>>>>> to_kill);
>>>>>>>           }
>>>>>>>       }
>>>>>>>       read_unlock(&tasklist_lock);
>>>>>>> @@ -550,7 +551,8 @@ static void collect_procs(struct page *page, 
>>>>>>> struct list_head *tokill,
>>>>>>>       if (PageAnon(page))
>>>>>>>           collect_procs_anon(page, tokill, force_early);
>>>>>>>       else
>>>>>>> -        collect_procs_file(page, tokill, force_early);
>>>>>>> +        collect_procs_file(page, page->mapping, 
>>>>>>> page_to_pgoff(page),
>>>>>>
>>>>>> Why not use page_mapping() helper here? It would be safer for 
>>>>>> THPs if they
>>>>>> ever get here...
>>>>>>
>>>>>>                                 Honza
>>>>>>
>>>>>
>>>>
>>>>
>>>
>>
>>
>

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

* Re: [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping
  2021-01-14  9:38               ` zhong jiang
@ 2021-01-14 17:20                 ` Darrick J. Wong
  0 siblings, 0 replies; 27+ messages in thread
From: Darrick J. Wong @ 2021-01-14 17:20 UTC (permalink / raw)
  To: zhong jiang
  Cc: Ruan Shiyang, Jan Kara, linux-kernel, linux-xfs, linux-nvdimm,
	linux-mm, linux-fsdevel, linux-raid, darrick.wong,
	dan.j.williams, david, hch, song, rgoldwyn, qi.fuli, y-goto

On Thu, Jan 14, 2021 at 05:38:33PM +0800, zhong jiang wrote:
> 
> On 2021/1/14 11:52 上午, Ruan Shiyang wrote:
> > 
> > 
> > On 2021/1/14 上午11:26, zhong jiang wrote:
> > > 
> > > On 2021/1/14 9:44 上午, Ruan Shiyang wrote:
> > > > 
> > > > 
> > > > On 2021/1/13 下午6:04, zhong jiang wrote:
> > > > > 
> > > > > On 2021/1/12 10:55 上午, Ruan Shiyang wrote:
> > > > > > 
> > > > > > 
> > > > > > On 2021/1/6 下午11:41, Jan Kara wrote:
> > > > > > > On Thu 31-12-20 00:55:55, Shiyang Ruan wrote:
> > > > > > > > The current memory_failure_dev_pagemap() can
> > > > > > > > only handle single-mapped
> > > > > > > > dax page for fsdax mode.  The dax page could be
> > > > > > > > mapped by multiple files
> > > > > > > > and offsets if we let reflink feature & fsdax
> > > > > > > > mode work together. So,
> > > > > > > > we refactor current implementation to support
> > > > > > > > handle memory failure on
> > > > > > > > each file and offset.
> > > > > > > > 
> > > > > > > > Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
> > > > > > > 
> > > > > > > Overall this looks OK to me, a few comments below.
> > > > > > > 
> > > > > > > > ---
> > > > > > > >   fs/dax.c            | 21 +++++++++++
> > > > > > > >   include/linux/dax.h |  1 +
> > > > > > > >   include/linux/mm.h  |  9 +++++
> > > > > > > >   mm/memory-failure.c | 91
> > > > > > > > ++++++++++++++++++++++++++++++++++-----------
> > > > > > > >   4 files changed, 100 insertions(+), 22 deletions(-)
> > > > > > 
> > > > > > ...
> > > > > > 
> > > > > > > >   @@ -345,9 +348,12 @@ static void
> > > > > > > > add_to_kill(struct task_struct *tsk, struct page
> > > > > > > > *p,
> > > > > > > >       }
> > > > > > > >         tk->addr = page_address_in_vma(p, vma);
> > > > > > > > -    if (is_zone_device_page(p))
> > > > > > > > -        tk->size_shift = dev_pagemap_mapping_shift(p, vma);
> > > > > > > > -    else
> > > > > > > > +    if (is_zone_device_page(p)) {
> > > > > > > > +        if (is_device_fsdax_page(p))
> > > > > > > > +            tk->addr = vma->vm_start +
> > > > > > > > +                    ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
> > > > > > > 
> > > > > > > It seems strange to use 'pgoff' for dax pages and
> > > > > > > not for any other page.
> > > > > > > Why? I'd rather pass correct pgoff from all callers
> > > > > > > of add_to_kill() and
> > > > > > > avoid this special casing...
> > > > > > 
> > > > > > Because one fsdax page can be shared by multiple pgoffs.
> > > > > > I have to pass each pgoff in each iteration to calculate
> > > > > > the address in vma (for tk->addr).  Other kinds of pages
> > > > > > don't need this. They can get their unique address by
> > > > > > calling "page_address_in_vma()".
> > > > > > 
> > > > > IMO,   an fsdax page can be shared by multiple files rather
> > > > > than multiple pgoffs if fs query support reflink.   Because
> > > > > an page only located in an mapping(page->mapping is
> > > > > exclusive), hence it  only has an pgoff or index pointing at
> > > > > the node.
> > > > > 
> > > > >   or  I miss something for the feature ?  thanks,
> > > > 
> > > > Yes, a fsdax page is shared by multiple files because of
> > > > reflink. I think my description of 'pgoff' here is not correct. 
> > > > This 'pgoff' means the offset within the a file. (We use rmap to
> > > > find out all the sharing files and their offsets.)  So, I said
> > > > that "can be shared by multiple pgoffs".  It's my bad.
> > > > 
> > > > I think I should name it another word to avoid misunderstandings.
> > > > 
> > > IMO,  All the sharing files should be the same offset to share the
> > > fsdax page.  why not that ?
> > 
> > The dedupe operation can let different files share their same data
> > extent, though offsets are not same.  So, files can share one fsdax page
> > at different offset.
> Ok,  Get it.
> > 
> > > As you has said,  a shared fadax page should be inserted to
> > > different mapping files.  but page->index and page->mapping is
> > > exclusive.  hence an page only should be placed in an mapping tree.
> > 
> > We can't use page->mapping and page->index here for reflink & fsdax. And
> > that's this patchset aims to solve.  I introduced a series of
> > ->corrupted_range(), from mm to pmem driver to block device and finally
> > to filesystem, to use rmap feature of filesystem to find out all files
> > sharing same data extent (fsdax page).
> 
> From this patch,  each file has mapping tree,  the shared page will be
> inserted into multiple file mapping tree.  then filesystem use file and
> offset to get the killed process.   Is it correct?

FWIW I thought the purpose of this patchset is to remove the (dax)
memory poison code's reliance on the pagecache mapping structure by
pushing poison notifications directly into the filesystem and letting
the filesystem perform reverse lookup operations to figure out which
file(s) have gone bad, and using the file list to call back into the mm
to kill processes.

Once that's done, I think(?) that puts us significantly closer to being
able to share pmem between files in dax mode without having to rewrite
the entire memory manager's mapping and rmapping code to support
sharing.

--D

> Thanks,
> 
> > 
> > 
> > -- 
> > Thanks,
> > Ruan Shiyang.
> > 
> > > 
> > > And In the current patch,  we failed to found out that all process
> > > use the fsdax page shared by multiple files and kill them.
> > > 
> > > 
> > > Thanks,
> > > 
> > > > -- 
> > > > Thanks,
> > > > Ruan Shiyang.
> > > > 
> > > > > 
> > > > > > So, I added this fsdax case here. This patchset only
> > > > > > implemented the fsdax case, other cases also need to be
> > > > > > added here if to be implemented.
> > > > > > 
> > > > > > 
> > > > > > -- 
> > > > > > Thanks,
> > > > > > Ruan Shiyang.
> > > > > > 
> > > > > > > 
> > > > > > > > +        tk->size_shift =
> > > > > > > > dev_pagemap_mapping_shift(p, vma, tk->addr);
> > > > > > > > +    } else
> > > > > > > >           tk->size_shift = page_shift(compound_head(p));
> > > > > > > >         /*
> > > > > > > > @@ -495,7 +501,7 @@ static void
> > > > > > > > collect_procs_anon(struct page *page, struct
> > > > > > > > list_head *to_kill,
> > > > > > > >               if (!page_mapped_in_vma(page, vma))
> > > > > > > >                   continue;
> > > > > > > >               if (vma->vm_mm == t->mm)
> > > > > > > > -                add_to_kill(t, page, vma, to_kill);
> > > > > > > > +                add_to_kill(t, page, NULL, 0, vma, to_kill);
> > > > > > > >           }
> > > > > > > >       }
> > > > > > > >       read_unlock(&tasklist_lock);
> > > > > > > > @@ -505,24 +511,19 @@ static void
> > > > > > > > collect_procs_anon(struct page *page, struct
> > > > > > > > list_head *to_kill,
> > > > > > > >   /*
> > > > > > > >    * Collect processes when the error hit a file mapped page.
> > > > > > > >    */
> > > > > > > > -static void collect_procs_file(struct page
> > > > > > > > *page, struct list_head *to_kill,
> > > > > > > > -                int force_early)
> > > > > > > > +static void collect_procs_file(struct page
> > > > > > > > *page, struct address_space *mapping,
> > > > > > > > +        pgoff_t pgoff, struct list_head *to_kill, int force_early)
> > > > > > > >   {
> > > > > > > >       struct vm_area_struct *vma;
> > > > > > > >       struct task_struct *tsk;
> > > > > > > > -    struct address_space *mapping = page->mapping;
> > > > > > > > -    pgoff_t pgoff;
> > > > > > > >         i_mmap_lock_read(mapping);
> > > > > > > >       read_lock(&tasklist_lock);
> > > > > > > > -    pgoff = page_to_pgoff(page);
> > > > > > > >       for_each_process(tsk) {
> > > > > > > >           struct task_struct *t =
> > > > > > > > task_early_kill(tsk, force_early);
> > > > > > > > -
> > > > > > > >           if (!t)
> > > > > > > >               continue;
> > > > > > > > -        vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff,
> > > > > > > > -                      pgoff) {
> > > > > > > > +        vma_interval_tree_foreach(vma,
> > > > > > > > &mapping->i_mmap, pgoff, pgoff) {
> > > > > > > >               /*
> > > > > > > >                * Send early kill signal to tasks where a vma covers
> > > > > > > >                * the page but the corrupted page is not necessarily
> > > > > > > > @@ -531,7 +532,7 @@ static void
> > > > > > > > collect_procs_file(struct page *page, struct
> > > > > > > > list_head *to_kill,
> > > > > > > >                * to be informed of all such data corruptions.
> > > > > > > >                */
> > > > > > > >               if (vma->vm_mm == t->mm)
> > > > > > > > -                add_to_kill(t, page, vma, to_kill);
> > > > > > > > +                add_to_kill(t, page, mapping,
> > > > > > > > pgoff, vma, to_kill);
> > > > > > > >           }
> > > > > > > >       }
> > > > > > > >       read_unlock(&tasklist_lock);
> > > > > > > > @@ -550,7 +551,8 @@ static void
> > > > > > > > collect_procs(struct page *page, struct
> > > > > > > > list_head *tokill,
> > > > > > > >       if (PageAnon(page))
> > > > > > > >           collect_procs_anon(page, tokill, force_early);
> > > > > > > >       else
> > > > > > > > -        collect_procs_file(page, tokill, force_early);
> > > > > > > > +        collect_procs_file(page, page->mapping,
> > > > > > > > page_to_pgoff(page),
> > > > > > > 
> > > > > > > Why not use page_mapping() helper here? It would be
> > > > > > > safer for THPs if they
> > > > > > > ever get here...
> > > > > > > 
> > > > > > >                                 Honza
> > > > > > > 
> > > > > > 
> > > > > 
> > > > > 
> > > > 
> > > 
> > > 
> > 

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

* Re: [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping
  2020-12-30 16:55 ` [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping Shiyang Ruan
  2021-01-06 15:41   ` Jan Kara
@ 2021-01-14 20:38   ` Dan Williams
  1 sibling, 0 replies; 27+ messages in thread
From: Dan Williams @ 2021-01-14 20:38 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: Linux Kernel Mailing List, linux-xfs, linux-nvdimm, Linux MM,
	linux-fsdevel, linux-raid, Darrick J. Wong, david,
	Christoph Hellwig, song, Goldwyn Rodrigues, qi.fuli, y-goto

On Wed, Dec 30, 2020 at 8:59 AM Shiyang Ruan <ruansy.fnst@cn.fujitsu.com> wrote:
>
> The current memory_failure_dev_pagemap() can only handle single-mapped
> dax page for fsdax mode.  The dax page could be mapped by multiple files
> and offsets if we let reflink feature & fsdax mode work together.  So,
> we refactor current implementation to support handle memory failure on
> each file and offset.
>
> Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com>
> ---
>  fs/dax.c            | 21 +++++++++++
>  include/linux/dax.h |  1 +
>  include/linux/mm.h  |  9 +++++
>  mm/memory-failure.c | 91 ++++++++++++++++++++++++++++++++++-----------
>  4 files changed, 100 insertions(+), 22 deletions(-)
>
> diff --git a/fs/dax.c b/fs/dax.c
> index 5b47834f2e1b..799210cfa687 100644
> --- a/fs/dax.c
> +++ b/fs/dax.c
> @@ -378,6 +378,27 @@ static struct page *dax_busy_page(void *entry)
>         return NULL;
>  }
>
> +/*
> + * dax_load_pfn - Load pfn of the DAX entry corresponding to a page
> + * @mapping: The file whose entry we want to load
> + * @index:   The offset where the DAX entry located in
> + *
> + * Return:   pfn of the DAX entry
> + */
> +unsigned long dax_load_pfn(struct address_space *mapping, unsigned long index)
> +{
> +       XA_STATE(xas, &mapping->i_pages, index);
> +       void *entry;
> +       unsigned long pfn;
> +
> +       xas_lock_irq(&xas);
> +       entry = xas_load(&xas);
> +       pfn = dax_to_pfn(entry);
> +       xas_unlock_irq(&xas);
> +
> +       return pfn;
> +}
> +
>  /*
>   * dax_lock_mapping_entry - Lock the DAX entry corresponding to a page
>   * @page: The page whose entry we want to lock
> diff --git a/include/linux/dax.h b/include/linux/dax.h
> index b52f084aa643..89e56ceeffc7 100644
> --- a/include/linux/dax.h
> +++ b/include/linux/dax.h
> @@ -150,6 +150,7 @@ int dax_writeback_mapping_range(struct address_space *mapping,
>
>  struct page *dax_layout_busy_page(struct address_space *mapping);
>  struct page *dax_layout_busy_page_range(struct address_space *mapping, loff_t start, loff_t end);
> +unsigned long dax_load_pfn(struct address_space *mapping, unsigned long index);
>  dax_entry_t dax_lock_page(struct page *page);
>  void dax_unlock_page(struct page *page, dax_entry_t cookie);
>  #else
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index db6ae4d3fb4e..db3059a1853e 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -1141,6 +1141,14 @@ static inline bool is_device_private_page(const struct page *page)
>                 page->pgmap->type == MEMORY_DEVICE_PRIVATE;
>  }
>
> +static inline bool is_device_fsdax_page(const struct page *page)
> +{
> +       return IS_ENABLED(CONFIG_DEV_PAGEMAP_OPS) &&
> +               IS_ENABLED(CONFIG_DEVICE_PRIVATE) &&
> +               is_zone_device_page(page) &&
> +               page->pgmap->type == MEMORY_DEVICE_FS_DAX;
> +}
> +

Have a look at the recent fixes to pfn_to_online_page() vs DAX pages [1].

This above page type check is racy given that the pfn could stop being
pfn_valid() while this check is running. I think hwpoison_filter()
needs an explicit check for whether the page is already referenced or
not. For example the current call to hwpoison_filter() from
memory_failure_dev_pagemap() is safe because the page has already been
validated as ZONE_DEVICE and is safe to de-reference page->pgmap.

[1]: http://lore.kernel.org/r/161058499000.1840162.702316708443239771.stgit@dwillia2-desk3.amr.corp.intel.com

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

end of thread, other threads:[~2021-01-14 20:39 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-30 16:55 [PATCH 00/10] fsdax: introduce fs query to support reflink Shiyang Ruan
2020-12-30 16:55 ` [PATCH 01/10] pagemap: Introduce ->memory_failure() Shiyang Ruan
2020-12-30 16:55 ` [PATCH 02/10] blk: Introduce ->corrupted_range() for block device Shiyang Ruan
2021-01-08  9:55   ` Christoph Hellwig
2021-01-08 19:09     ` Darrick J. Wong
2020-12-30 16:55 ` [PATCH 03/10] fs: Introduce ->corrupted_range() for superblock Shiyang Ruan
2021-01-08  9:56   ` Christoph Hellwig
2020-12-30 16:55 ` [PATCH 04/10] mm, fsdax: Refactor memory-failure handler for dax mapping Shiyang Ruan
2021-01-06 15:41   ` Jan Kara
2021-01-12  2:55     ` Ruan Shiyang
2021-01-13 10:04       ` zhong jiang
2021-01-14  1:44         ` Ruan Shiyang
2021-01-14  3:26           ` zhong jiang
2021-01-14  3:52             ` Ruan Shiyang
2021-01-14  9:38               ` zhong jiang
2021-01-14 17:20                 ` Darrick J. Wong
2021-01-14 20:38   ` Dan Williams
2020-12-30 16:55 ` [PATCH 05/10] mm, pmem: Implement ->memory_failure() in pmem driver Shiyang Ruan
2021-01-06 15:55   ` Jan Kara
2020-12-30 16:55 ` [PATCH 06/10] pmem: Implement ->corrupted_range() for " Shiyang Ruan
2020-12-30 16:55 ` [PATCH 07/10] dm: Introduce ->rmap() to find bdev offset Shiyang Ruan
2020-12-30 16:55 ` [PATCH 08/10] md: Implement ->corrupted_range() Shiyang Ruan
2021-01-06 17:14   ` Jan Kara
2021-01-12 12:45     ` Ruan Shiyang
2020-12-30 16:56 ` [PATCH 09/10] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
2021-01-04 23:21   ` Darrick J. Wong
2020-12-30 16:56 ` [PATCH 10/10] fs/dax: remove useless functions Shiyang Ruan

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).