linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink
@ 2020-12-15 12:14 Shiyang Ruan
  2020-12-15 12:14 ` [RFC PATCH v3 1/9] pagemap: Introduce ->memory_failure() Shiyang Ruan
                   ` (9 more replies)
  0 siblings, 10 replies; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-15 12:14 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 a try to resolve the problem of tracking shared page
for fsdax.

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

Change from 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 (9):
  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

 block/genhd.c                 |  12 +++
 drivers/md/dm-linear.c        |   8 ++
 drivers/md/dm.c               |  66 +++++++++++++++
 drivers/nvdimm/pmem.c         |  51 ++++++++++++
 fs/block_dev.c                |  21 +++++
 fs/dax.c                      |  24 +++---
 fs/xfs/xfs_fsops.c            |  10 +++
 fs/xfs/xfs_mount.h            |   2 +
 fs/xfs/xfs_super.c            |  93 +++++++++++++++++++++
 include/linux/blkdev.h        |   2 +
 include/linux/dax.h           |   5 +-
 include/linux/device-mapper.h |   2 +
 include/linux/fs.h            |   2 +
 include/linux/genhd.h         |   8 ++
 include/linux/memremap.h      |   8 ++
 include/linux/mm.h            |   9 ++
 mm/memory-failure.c           | 150 +++++++++++++++++++---------------
 17 files changed, 391 insertions(+), 82 deletions(-)

-- 
2.29.2





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

* [RFC PATCH v3 1/9] pagemap: Introduce ->memory_failure()
  2020-12-15 12:14 [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
@ 2020-12-15 12:14 ` Shiyang Ruan
  2020-12-15 12:14 ` [RFC PATCH v3 2/9] 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-15 12:14 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 related	[flat|nested] 27+ messages in thread

* [RFC PATCH v3 2/9] blk: Introduce ->corrupted_range() for block device
  2020-12-15 12:14 [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
  2020-12-15 12:14 ` [RFC PATCH v3 1/9] pagemap: Introduce ->memory_failure() Shiyang Ruan
@ 2020-12-15 12:14 ` Shiyang Ruan
  2020-12-15 12:14 ` [RFC PATCH v3 3/9] fs: Introduce ->corrupted_range() for superblock Shiyang Ruan
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-15 12:14 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 related	[flat|nested] 27+ messages in thread

* [RFC PATCH v3 3/9] fs: Introduce ->corrupted_range() for superblock
  2020-12-15 12:14 [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
  2020-12-15 12:14 ` [RFC PATCH v3 1/9] pagemap: Introduce ->memory_failure() Shiyang Ruan
  2020-12-15 12:14 ` [RFC PATCH v3 2/9] blk: Introduce ->corrupted_range() for block device Shiyang Ruan
@ 2020-12-15 12:14 ` Shiyang Ruan
  2020-12-15 12:14 ` [RFC PATCH v3 4/9] mm, fsdax: Refactor memory-failure handler for dax mapping Shiyang Ruan
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-15 12:14 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 related	[flat|nested] 27+ messages in thread

* [RFC PATCH v3 4/9] mm, fsdax: Refactor memory-failure handler for dax mapping
  2020-12-15 12:14 [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (2 preceding siblings ...)
  2020-12-15 12:14 ` [RFC PATCH v3 3/9] fs: Introduce ->corrupted_range() for superblock Shiyang Ruan
@ 2020-12-15 12:14 ` Shiyang Ruan
  2020-12-16 21:26   ` Dave Chinner
  2020-12-15 12:14 ` [RFC PATCH v3 5/9] mm, pmem: Implement ->memory_failure() in pmem driver Shiyang Ruan
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-15 12:14 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            |  24 +++++-----
 include/linux/dax.h |   5 +-
 include/linux/mm.h  |   9 ++++
 mm/memory-failure.c | 112 +++++++++++++++++++++++++++++++++-----------
 4 files changed, 108 insertions(+), 42 deletions(-)

diff --git a/fs/dax.c b/fs/dax.c
index 5b47834f2e1b..1c17c4605bc0 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -379,14 +379,17 @@ static struct page *dax_busy_page(void *entry)
 }
 
 /*
- * dax_lock_mapping_entry - Lock the DAX entry corresponding to a page
- * @page: The page whose entry we want to lock
+ * dax_lock - Lock the DAX entry corresponding to a page
+ * @mapping: The file whose entry we want to lock
+ * @index:   The offset where the DAX entry located in
+ * @pfnp:    Page frame number of the locked entry
  *
  * 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)
+dax_entry_t dax_lock(struct address_space *mapping, unsigned long index,
+		unsigned long *pfnp)
 {
 	XA_STATE(xas, NULL, 0);
 	void *entry;
@@ -394,8 +397,6 @@ dax_entry_t dax_lock_page(struct page *page)
 	/* 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;
@@ -413,11 +414,7 @@ dax_entry_t dax_lock_page(struct page *page)
 
 		xas.xa = &mapping->i_pages;
 		xas_lock_irq(&xas);
-		if (mapping != page->mapping) {
-			xas_unlock_irq(&xas);
-			continue;
-		}
-		xas_set(&xas, page->index);
+		xas_set(&xas, index);
 		entry = xas_load(&xas);
 		if (dax_is_locked(entry)) {
 			rcu_read_unlock();
@@ -427,16 +424,17 @@ dax_entry_t dax_lock_page(struct page *page)
 		}
 		dax_lock_entry(&xas, entry);
 		xas_unlock_irq(&xas);
+		*pfnp = dax_to_pfn(entry);
 		break;
 	}
 	rcu_read_unlock();
 	return (dax_entry_t)entry;
 }
 
-void dax_unlock_page(struct page *page, dax_entry_t cookie)
+void dax_unlock(struct address_space *mapping, unsigned long index,
+		dax_entry_t cookie)
 {
-	struct address_space *mapping = page->mapping;
-	XA_STATE(xas, &mapping->i_pages, page->index);
+	XA_STATE(xas, &mapping->i_pages, index);
 
 	if (S_ISCHR(mapping->host->i_mode))
 		return;
diff --git a/include/linux/dax.h b/include/linux/dax.h
index b52f084aa643..b24675af1de8 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -150,8 +150,9 @@ 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);
-dax_entry_t dax_lock_page(struct page *page);
-void dax_unlock_page(struct page *page, dax_entry_t cookie);
+dax_entry_t dax_lock(struct address_space *mapping, unsigned long index, unsigned long *pfnp);
+void dax_unlock(struct address_space *mapping, unsigned long index,
+		dax_entry_t cookie);
 #else
 static inline bool bdev_dax_supported(struct block_device *bdev,
 		int blocksize)
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..03a4f4c1b803 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -120,6 +120,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 +293,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 +335,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 +347,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 +500,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 +510,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 +531,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 +550,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 +1148,60 @@ 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;
+	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(mapping, index, &pfn);
+	if (!cookie)
+		goto unlock;
+
+	/*
+	 * 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;
+unlock:
+	dax_unlock(mapping, index, cookie);
+	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);
@@ -1223,10 +1278,12 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 		struct dev_pagemap *pgmap)
 {
 	struct page *page = pfn_to_page(pfn);
+	struct address_space *mapping = page->mapping;
+	pgoff_t index = page->index;
 	const bool unmap_success = true;
-	unsigned long size = 0;
+	unsigned long size = 0, dummy_pfn;
 	struct to_kill *tk;
-	LIST_HEAD(tokill);
+	LIST_HEAD(to_kill);
 	int rc = -EBUSY;
 	loff_t start;
 	dax_entry_t cookie;
@@ -1238,7 +1295,7 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 	 * also prevents changes to the mapping of this pfn until
 	 * poison signaling is complete.
 	 */
-	cookie = dax_lock_page(page);
+	cookie = dax_lock(mapping, index, &dummy_pfn);
 	if (!cookie)
 		goto out;
 
@@ -1268,9 +1325,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, mapping, 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) {
@@ -1280,13 +1338,13 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 		 * 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);
+		start = (index << PAGE_SHIFT) & ~(size - 1);
+		unmap_mapping_range(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);
+	dax_unlock(mapping, index, cookie);
 out:
 	/* drop pgmap ref acquired in caller */
 	put_dev_pagemap(pgmap);
-- 
2.29.2





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

* [RFC PATCH v3 5/9] mm, pmem: Implement ->memory_failure() in pmem driver
  2020-12-15 12:14 [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (3 preceding siblings ...)
  2020-12-15 12:14 ` [RFC PATCH v3 4/9] mm, fsdax: Refactor memory-failure handler for dax mapping Shiyang Ruan
@ 2020-12-15 12:14 ` Shiyang Ruan
  2020-12-15 12:14 ` [RFC PATCH v3 6/9] pmem: Implement ->corrupted_range() for " Shiyang Ruan
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-15 12:14 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   | 62 +++++++------------------------------------
 2 files changed, 34 insertions(+), 52 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 03a4f4c1b803..10b39b14b4d7 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1278,38 +1278,19 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 		struct dev_pagemap *pgmap)
 {
 	struct page *page = pfn_to_page(pfn);
-	struct address_space *mapping = page->mapping;
-	pgoff_t index = page->index;
-	const bool unmap_success = true;
-	unsigned long size = 0, dummy_pfn;
-	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(mapping, index, &dummy_pfn);
-	if (!cookie)
-		goto out;
-
-	if (hwpoison_filter(page)) {
-		rc = 0;
-		goto unlock;
-	}
+	int rc;
 
 	if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
 		/*
 		 * TODO: Handle HMM pages which may need coordination
 		 * with device-side memory.
 		 */
-		goto unlock;
+		goto out;
+	}
+
+	if (hwpoison_filter(page)) {
+		rc = 0;
+		goto out;
 	}
 
 	/*
@@ -1318,33 +1299,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, mapping, 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 = (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;
-unlock:
-	dax_unlock(mapping, index, cookie);
 out:
 	/* drop pgmap ref acquired in caller */
 	put_dev_pagemap(pgmap);
-- 
2.29.2





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

* [RFC PATCH v3 6/9] pmem: Implement ->corrupted_range() for pmem driver
  2020-12-15 12:14 [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (4 preceding siblings ...)
  2020-12-15 12:14 ` [RFC PATCH v3 5/9] mm, pmem: Implement ->memory_failure() in pmem driver Shiyang Ruan
@ 2020-12-15 12:14 ` Shiyang Ruan
  2020-12-15 12:14 ` [RFC PATCH v3 7/9] 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-15 12:14 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 related	[flat|nested] 27+ messages in thread

* [RFC PATCH v3 7/9] dm: Introduce ->rmap() to find bdev offset
  2020-12-15 12:14 [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (5 preceding siblings ...)
  2020-12-15 12:14 ` [RFC PATCH v3 6/9] pmem: Implement ->corrupted_range() for " Shiyang Ruan
@ 2020-12-15 12:14 ` Shiyang Ruan
  2020-12-15 12:14 ` [RFC PATCH v3 8/9] md: Implement ->corrupted_range() Shiyang Ruan
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-15 12:14 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 related	[flat|nested] 27+ messages in thread

* [RFC PATCH v3 8/9] md: Implement ->corrupted_range()
  2020-12-15 12:14 [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (6 preceding siblings ...)
  2020-12-15 12:14 ` [RFC PATCH v3 7/9] dm: Introduce ->rmap() to find bdev offset Shiyang Ruan
@ 2020-12-15 12:14 ` Shiyang Ruan
  2020-12-15 20:51   ` Darrick J. Wong
  2020-12-16  5:43   ` Jane Chu
  2020-12-15 12:14 ` [RFC PATCH v3 9/9] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
  2020-12-16 20:55 ` [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Jane Chu
  9 siblings, 2 replies; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-15 12:14 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       | 66 +++++++++++++++++++++++++++++++++++++++++++
 drivers/nvdimm/pmem.c |  9 ++++--
 fs/block_dev.c        | 21 ++++++++++++++
 include/linux/genhd.h |  7 +++++
 4 files changed, 100 insertions(+), 3 deletions(-)

diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 4e0cbfe3f14d..9da1f9322735 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -507,6 +507,71 @@ 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;
+	struct super_block *sb;
+	int srcu_idx, i, rc = 0;
+	bool found = false;
+	sector_t disk_sec, target_sec = to_sector(target_offset);
+
+	map = dm_get_live_table(md, &srcu_idx);
+	if (!map)
+		return -ENODEV;
+
+	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_sec};
+
+			found = ti->type->iterate_devices(ti, dm_blk_corrupt_fn, &bc);
+			if (!found)
+				continue;
+			disk_sec = ti->type->rmap(ti, target_sec);
+			break;
+		}
+	}
+
+	if (!found) {
+		rc = -ENODEV;
+		goto out;
+	}
+
+	sb = get_super(md_bdev);
+	if (!sb) {
+		rc = bd_disk_holder_corrupted_range(md_bdev, to_bytes(disk_sec), len, data);
+		goto out;
+	} else if (sb->s_op->corrupted_range) {
+		loff_t off = to_bytes(disk_sec - get_start_sect(md_bdev));
+
+		rc = sb->s_op->corrupted_range(sb, md_bdev, off, len, data);
+	}
+	drop_super(sb);
+
+out:
+	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 +3149,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..e8cfaf860149 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -267,11 +267,14 @@ static int pmem_corrupted_range(struct gendisk *disk, struct block_device *bdev,
 
 	bdev_offset = (disk_sector - get_start_sect(bdev)) << SECTOR_SHIFT;
 	sb = get_super(bdev);
-	if (sb && sb->s_op->corrupted_range) {
+	if (!sb) {
+		rc = bd_disk_holder_corrupted_range(bdev, bdev_offset, len, data);
+		goto out;
+	} else if (sb->s_op->corrupted_range)
 		rc = sb->s_op->corrupted_range(sb, bdev, bdev_offset, len, data);
-		drop_super(sb);
-	}
+	drop_super(sb);
 
+out:
 	bdput(bdev);
 	return rc;
 }
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 9e84b1928b94..d3e6bddb8041 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1171,6 +1171,27 @@ struct bd_holder_disk {
 	int			refcnt;
 };
 
+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;
+}
+EXPORT_SYMBOL_GPL(bd_disk_holder_corrupted_range);
+
 static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
 						  struct gendisk *disk)
 {
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index ed06209008b8..fba247b852fa 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -382,9 +382,16 @@ int blkdev_ioctl(struct block_device *, fmode_t, unsigned, unsigned long);
 long compat_blkdev_ioctl(struct file *, unsigned, unsigned long);
 
 #ifdef CONFIG_SYSFS
+int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
+				   size_t len, void *data);
 int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk);
 void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk);
 #else
+int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
+				   size_t len, void *data)
+{
+	return 0;
+}
 static inline int bd_link_disk_holder(struct block_device *bdev,
 				      struct gendisk *disk)
 {
-- 
2.29.2





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

* [RFC PATCH v3 9/9] xfs: Implement ->corrupted_range() for XFS
  2020-12-15 12:14 [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (7 preceding siblings ...)
  2020-12-15 12:14 ` [RFC PATCH v3 8/9] md: Implement ->corrupted_range() Shiyang Ruan
@ 2020-12-15 12:14 ` Shiyang Ruan
  2020-12-15 20:40   ` Darrick J. Wong
  2020-12-16 20:55 ` [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Jane Chu
  9 siblings, 1 reply; 27+ messages in thread
From: Shiyang Ruan @ 2020-12-15 12:14 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 | 10 +++++
 fs/xfs/xfs_mount.h |  2 +
 fs/xfs/xfs_super.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 105 insertions(+)

diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index ef1d5bb88b93..0ec1b44bfe88 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -501,6 +501,16 @@ 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 (flags & SHUTDOWN_CORRUPT_DATA) {
+		xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_CORRUPT,
+"Corruption of on-disk file data 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..e36c07553486 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -274,6 +274,8 @@ 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 */
+#define SHUTDOWN_CORRUPT_DATA	0x0020  /* corrupt file data on device */
 
 /*
  * Flags for xfs_mountfs
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index e3e229e52512..30202de7e89d 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,93 @@ 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;
+	int				rc = 0;
+	int				*flags = data;
+
+	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner)) {
+		// 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;
+
+		if (IS_DAX(VFS_I(ip)))
+			rc = mf_dax_mapping_kill_procs(VFS_I(ip)->i_mapping,
+						       rec->rm_offset, *flags);
+
+		// 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		bc = 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			rc = 0;
+
+	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_bdev == bdev) {
+		xfs_warn(mp, "storage lost support not available for realtime device!");
+		return 0;
+	}
+
+	rc = xfs_trans_alloc_empty(mp, &tp);
+	if (rc)
+		return rc;
+
+	rc = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
+	if (rc)
+		return rc;
+
+	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 = bc;
+
+	rc = xfs_rmap_query_range(cur, &rmap_low, &rmap_high, xfs_corrupt_helper, data);
+	if (rc == -ECANCELED)
+		rc = 0;
+	if (rc == -EFSCORRUPTED)
+		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_META);
+
+	xfs_btree_del_cursor(cur, rc);
+	xfs_trans_brelse(tp, agf_bp);
+	return rc;
+}
+
 static const struct super_operations xfs_super_operations = {
 	.alloc_inode		= xfs_fs_alloc_inode,
 	.destroy_inode		= xfs_fs_destroy_inode,
@@ -1116,6 +1208,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 related	[flat|nested] 27+ messages in thread

* Re: [RFC PATCH v3 9/9] xfs: Implement ->corrupted_range() for XFS
  2020-12-15 12:14 ` [RFC PATCH v3 9/9] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
@ 2020-12-15 20:40   ` Darrick J. Wong
  2020-12-18  2:31     ` Ruan Shiyang
  0 siblings, 1 reply; 27+ messages in thread
From: Darrick J. Wong @ 2020-12-15 20:40 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 Tue, Dec 15, 2020 at 08:14:14PM +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 | 10 +++++
>  fs/xfs/xfs_mount.h |  2 +
>  fs/xfs/xfs_super.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 105 insertions(+)
> 
> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> index ef1d5bb88b93..0ec1b44bfe88 100644
> --- a/fs/xfs/xfs_fsops.c
> +++ b/fs/xfs/xfs_fsops.c
> @@ -501,6 +501,16 @@ 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 (flags & SHUTDOWN_CORRUPT_DATA) {
> +		xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_CORRUPT,
> +"Corruption of on-disk file data 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..e36c07553486 100644
> --- a/fs/xfs/xfs_mount.h
> +++ b/fs/xfs/xfs_mount.h
> @@ -274,6 +274,8 @@ 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 */
> +#define SHUTDOWN_CORRUPT_DATA	0x0020  /* corrupt file data on device */

This symbol isn't used anywhere.  I don't know why we'd shut down the fs
for data loss, as we don't do that anywhere else in xfs.

>  
>  /*
>   * Flags for xfs_mountfs
> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index e3e229e52512..30202de7e89d 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,93 @@ 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;
> +	int				rc = 0;

Note: we usually use the name "error", not "rc".

> +	int				*flags = data;
> +
> +	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner)) {

There are a few more things to check here to detect if metadata has been
lost.  The first is that any loss in the extended attribute information
is considered filesystem metadata; and the second is that loss of an
extent btree block is also metadata.

IOWs, this check should be:

	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
		return -EFSCORRUPTED;
	}

> +		// 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;
> +
> +		if (IS_DAX(VFS_I(ip)))
> +			rc = mf_dax_mapping_kill_procs(VFS_I(ip)->i_mapping,
> +						       rec->rm_offset, *flags);

If the file isn't S_DAX, should we call mapping_set_error here so
that the next fsync() will also return EIO?

> +
> +		// 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		bc = 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			rc = 0;
> +
> +	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_bdev == bdev) {
> +		xfs_warn(mp, "storage lost support not available for realtime device!");
> +		return 0;
> +	}

This ought to kill the fs when an external log device is configured:

	if (mp->m_logdev_targp &&
	    mp->m_logdev_targp != mp->m_ddev_targp &&
	    mp->m_logdev_targp->bt_bdev == bdev) {
		xfs_error(mp, "ondisk log corrupt, shutting down fs!");
		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_META);
		return 0;
	}

Then, we need to check explicitly for rmap support:

	if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
		return 0;

so that we screen out filesystems that don't have rmap enabled.

> +	rc = xfs_trans_alloc_empty(mp, &tp);
> +	if (rc)
> +		return rc;
> +
> +	rc = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
> +	if (rc)
> +		return rc;
> +
> +	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 = bc;
> +
> +	rc = xfs_rmap_query_range(cur, &rmap_low, &rmap_high, xfs_corrupt_helper, data);
> +	if (rc == -ECANCELED)
> +		rc = 0;

I don't think anything returns ECANCELED here...

--D

> +	if (rc == -EFSCORRUPTED)
> +		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_META);
> +
> +	xfs_btree_del_cursor(cur, rc);
> +	xfs_trans_brelse(tp, agf_bp);
> +	return rc;
> +}
> +
>  static const struct super_operations xfs_super_operations = {
>  	.alloc_inode		= xfs_fs_alloc_inode,
>  	.destroy_inode		= xfs_fs_destroy_inode,
> @@ -1116,6 +1208,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: [RFC PATCH v3 8/9] md: Implement ->corrupted_range()
  2020-12-15 12:14 ` [RFC PATCH v3 8/9] md: Implement ->corrupted_range() Shiyang Ruan
@ 2020-12-15 20:51   ` Darrick J. Wong
  2020-12-15 23:28     ` Dave Chinner
  2020-12-18  2:11     ` Ruan Shiyang
  2020-12-16  5:43   ` Jane Chu
  1 sibling, 2 replies; 27+ messages in thread
From: Darrick J. Wong @ 2020-12-15 20:51 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, Theodore Ts'o

On Tue, Dec 15, 2020 at 08:14:13PM +0800, 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>
> ---
>  drivers/md/dm.c       | 66 +++++++++++++++++++++++++++++++++++++++++++
>  drivers/nvdimm/pmem.c |  9 ++++--
>  fs/block_dev.c        | 21 ++++++++++++++
>  include/linux/genhd.h |  7 +++++
>  4 files changed, 100 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/md/dm.c b/drivers/md/dm.c
> index 4e0cbfe3f14d..9da1f9322735 100644
> --- a/drivers/md/dm.c
> +++ b/drivers/md/dm.c
> @@ -507,6 +507,71 @@ 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;
> +	struct super_block *sb;
> +	int srcu_idx, i, rc = 0;
> +	bool found = false;
> +	sector_t disk_sec, target_sec = to_sector(target_offset);
> +
> +	map = dm_get_live_table(md, &srcu_idx);
> +	if (!map)
> +		return -ENODEV;
> +
> +	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_sec};
> +
> +			found = ti->type->iterate_devices(ti, dm_blk_corrupt_fn, &bc);
> +			if (!found)
> +				continue;
> +			disk_sec = ti->type->rmap(ti, target_sec);

What happens if the dm device has multiple reverse mappings because the
physical storage is being shared at multiple LBAs?  (e.g. a
deduplication target)

> +			break;
> +		}
> +	}
> +
> +	if (!found) {
> +		rc = -ENODEV;
> +		goto out;
> +	}
> +
> +	sb = get_super(md_bdev);
> +	if (!sb) {
> +		rc = bd_disk_holder_corrupted_range(md_bdev, to_bytes(disk_sec), len, data);
> +		goto out;
> +	} else if (sb->s_op->corrupted_range) {
> +		loff_t off = to_bytes(disk_sec - get_start_sect(md_bdev));
> +
> +		rc = sb->s_op->corrupted_range(sb, md_bdev, off, len, data);

This "call bd_disk_holder_corrupted_range or sb->s_op->corrupted_range"
logic appears twice; should it be refactored into a common helper?

Or, should the superblock dispatch part move to
bd_disk_holder_corrupted_range?

> +	}
> +	drop_super(sb);
> +
> +out:
> +	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 +3149,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..e8cfaf860149 100644
> --- a/drivers/nvdimm/pmem.c
> +++ b/drivers/nvdimm/pmem.c
> @@ -267,11 +267,14 @@ static int pmem_corrupted_range(struct gendisk *disk, struct block_device *bdev,
>  
>  	bdev_offset = (disk_sector - get_start_sect(bdev)) << SECTOR_SHIFT;
>  	sb = get_super(bdev);
> -	if (sb && sb->s_op->corrupted_range) {
> +	if (!sb) {
> +		rc = bd_disk_holder_corrupted_range(bdev, bdev_offset, len, data);
> +		goto out;
> +	} else if (sb->s_op->corrupted_range)
>  		rc = sb->s_op->corrupted_range(sb, bdev, bdev_offset, len, data);
> -		drop_super(sb);

This is out of scope for this patch(set) but do you think that the scsi
disk driver should intercept media errors from sense data and call
->corrupted_range too?  ISTR Ted muttering that one of his employers had
a patchset to do more with sense data than the upstream kernel currently
does...

> -	}
> +	drop_super(sb);
>  
> +out:
>  	bdput(bdev);
>  	return rc;
>  }
> diff --git a/fs/block_dev.c b/fs/block_dev.c
> index 9e84b1928b94..d3e6bddb8041 100644
> --- a/fs/block_dev.c
> +++ b/fs/block_dev.c
> @@ -1171,6 +1171,27 @@ struct bd_holder_disk {
>  	int			refcnt;
>  };
>  
> +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;
> +}
> +EXPORT_SYMBOL_GPL(bd_disk_holder_corrupted_range);
> +
>  static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
>  						  struct gendisk *disk)
>  {
> diff --git a/include/linux/genhd.h b/include/linux/genhd.h
> index ed06209008b8..fba247b852fa 100644
> --- a/include/linux/genhd.h
> +++ b/include/linux/genhd.h
> @@ -382,9 +382,16 @@ int blkdev_ioctl(struct block_device *, fmode_t, unsigned, unsigned long);
>  long compat_blkdev_ioctl(struct file *, unsigned, unsigned long);
>  
>  #ifdef CONFIG_SYSFS
> +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
> +				   size_t len, void *data);
>  int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk);
>  void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk);
>  #else
> +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
> +				   size_t len, void *data)
> +{
> +	return 0;
> +}
>  static inline int bd_link_disk_holder(struct block_device *bdev,
>  				      struct gendisk *disk)
>  {
> -- 
> 2.29.2
> 
> 
> 


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

* Re: [RFC PATCH v3 8/9] md: Implement ->corrupted_range()
  2020-12-15 20:51   ` Darrick J. Wong
@ 2020-12-15 23:28     ` Dave Chinner
  2020-12-18  2:11     ` Ruan Shiyang
  1 sibling, 0 replies; 27+ messages in thread
From: Dave Chinner @ 2020-12-15 23:28 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Shiyang Ruan, linux-kernel, linux-xfs, linux-nvdimm, linux-mm,
	linux-fsdevel, linux-raid, dan.j.williams, hch, song, rgoldwyn,
	qi.fuli, y-goto, Theodore Ts'o

On Tue, Dec 15, 2020 at 12:51:02PM -0800, Darrick J. Wong wrote:
> On Tue, Dec 15, 2020 at 08:14:13PM +0800, Shiyang Ruan wrote:
> > diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
> > index 4688bff19c20..e8cfaf860149 100644
> > --- a/drivers/nvdimm/pmem.c
> > +++ b/drivers/nvdimm/pmem.c
> > @@ -267,11 +267,14 @@ static int pmem_corrupted_range(struct gendisk *disk, struct block_device *bdev,
> >  
> >  	bdev_offset = (disk_sector - get_start_sect(bdev)) << SECTOR_SHIFT;
> >  	sb = get_super(bdev);
> > -	if (sb && sb->s_op->corrupted_range) {
> > +	if (!sb) {
> > +		rc = bd_disk_holder_corrupted_range(bdev, bdev_offset, len, data);
> > +		goto out;
> > +	} else if (sb->s_op->corrupted_range)
> >  		rc = sb->s_op->corrupted_range(sb, bdev, bdev_offset, len, data);
> > -		drop_super(sb);
> 
> This is out of scope for this patch(set) but do you think that the scsi
> disk driver should intercept media errors from sense data and call
> ->corrupted_range too?  ISTR Ted muttering that one of his employers had
> a patchset to do more with sense data than the upstream kernel currently
> does...

Most definitely!

That's the whole point of layering corrupt range reporting through
the device layers like this - the corrupted range reporting is not
limited specifically to pmem devices and so generic storage failures
(e.g.  RAID failures, hardware media failures, etc) can be reported
back up to the filesystem and we can take immediate, appropriate
action, including reporting to userspace that they just lost data in
file X at offset Y...

Combine that with the proposed "watch_sb()" syscall for reporting
such errors in a generic manner to interested listeners, and we've
got a fairly solid generic path for reporting data loss events to
userspace for an appropriate user-defined action to be taken...

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com


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

* Re: [RFC PATCH v3 8/9] md: Implement ->corrupted_range()
  2020-12-15 12:14 ` [RFC PATCH v3 8/9] md: Implement ->corrupted_range() Shiyang Ruan
  2020-12-15 20:51   ` Darrick J. Wong
@ 2020-12-16  5:43   ` Jane Chu
  2020-12-18  1:50     ` Ruan Shiyang
  1 sibling, 1 reply; 27+ messages in thread
From: Jane Chu @ 2020-12-16  5:43 UTC (permalink / raw)
  To: Shiyang Ruan, linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, david, hch, song,
	rgoldwyn, qi.fuli, y-goto

On 12/15/2020 4:14 AM, Shiyang Ruan wrote:
>   #ifdef CONFIG_SYSFS
> +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
> +				   size_t len, void *data);
>   int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk);
>   void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk);
>   #else
> +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,

Did you mean
   static inline int bd_disk_holder_corrupted_range(..
?

thanks,
-jane

> +				   size_t len, void *data)
> +{
> +	return 0;
> +}
>   static inline int bd_link_disk_holder(struct block_device *bdev,
>   				      struct gendisk *disk)


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

* Re: [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink
  2020-12-15 12:14 [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (8 preceding siblings ...)
  2020-12-15 12:14 ` [RFC PATCH v3 9/9] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
@ 2020-12-16 20:55 ` Jane Chu
  2020-12-18  2:44   ` Ruan Shiyang
  9 siblings, 1 reply; 27+ messages in thread
From: Jane Chu @ 2020-12-16 20:55 UTC (permalink / raw)
  To: Shiyang Ruan, 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

Hi, Shiyang,

On 12/15/2020 4:14 AM, Shiyang Ruan wrote:
> 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)

So I tried the patchset with pmem error injection, the SIGBUS payload
does not look right -

** SIGBUS(7): **
** si_addr(0x(nil)), si_lsb(0xC), si_code(0x4, BUS_MCEERR_AR) **

I expect the payload looks like

** si_addr(0x7f3672e00000), si_lsb(0x15), si_code(0x4, BUS_MCEERR_AR) **

thanks,
-jane






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

* Re: [RFC PATCH v3 4/9] mm, fsdax: Refactor memory-failure handler for dax mapping
  2020-12-15 12:14 ` [RFC PATCH v3 4/9] mm, fsdax: Refactor memory-failure handler for dax mapping Shiyang Ruan
@ 2020-12-16 21:26   ` Dave Chinner
  2020-12-18  1:48     ` Ruan Shiyang
  0 siblings, 1 reply; 27+ messages in thread
From: Dave Chinner @ 2020-12-16 21:26 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, hch, song, rgoldwyn,
	qi.fuli, y-goto

On Tue, Dec 15, 2020 at 08:14:09PM +0800, 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>
> ---
.....
>  static const char *action_name[] = {
> @@ -1147,6 +1148,60 @@ 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;
> +	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(mapping, index, &pfn);
> +	if (!cookie)
> +		goto unlock;

Why do we need to prevent the inode from going away here? This
function gets called by XFS after doing an xfs_iget() call to grab
the inode that owns the block. Hence the the inode (and the mapping)
are guaranteed to be referenced and can't go away. Hence for the
filesystem based callers, this whole "dax_lock()" thing can go away.

So, AFAICT, the dax_lock() stuff is only necessary when the
filesystem can't be used to resolve the owner of physical page that
went bad....

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com


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

* Re: [RFC PATCH v3 4/9] mm, fsdax: Refactor memory-failure handler for dax mapping
  2020-12-16 21:26   ` Dave Chinner
@ 2020-12-18  1:48     ` Ruan Shiyang
  0 siblings, 0 replies; 27+ messages in thread
From: Ruan Shiyang @ 2020-12-18  1:48 UTC (permalink / raw)
  To: Dave Chinner
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, darrick.wong, dan.j.williams, hch, song, rgoldwyn,
	qi.fuli, y-goto



On 2020/12/17 上午5:26, Dave Chinner wrote:
> On Tue, Dec 15, 2020 at 08:14:09PM +0800, 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>
>> ---
> .....
>>   static const char *action_name[] = {
>> @@ -1147,6 +1148,60 @@ 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;
>> +	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(mapping, index, &pfn);
>> +	if (!cookie)
>> +		goto unlock;
> 
> Why do we need to prevent the inode from going away here? This
> function gets called by XFS after doing an xfs_iget() call to grab
> the inode that owns the block. Hence the the inode (and the mapping)
> are guaranteed to be referenced and can't go away. Hence for the
> filesystem based callers, this whole "dax_lock()" thing can go away >
> So, AFAICT, the dax_lock() stuff is only necessary when the
> filesystem can't be used to resolve the owner of physical page that
> went bad....

Yes, you are right.  I made a mistake in the calling sequence here. 
Thanks for pointing out.


--
Thanks,
Ruan Shiyang.

> 
> Cheers,
> 
> Dave.
> 




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

* Re: [RFC PATCH v3 8/9] md: Implement ->corrupted_range()
  2020-12-16  5:43   ` Jane Chu
@ 2020-12-18  1:50     ` Ruan Shiyang
  0 siblings, 0 replies; 27+ messages in thread
From: Ruan Shiyang @ 2020-12-18  1:50 UTC (permalink / raw)
  To: Jane Chu, linux-kernel, linux-xfs, linux-nvdimm, linux-mm
  Cc: linux-fsdevel, linux-raid, darrick.wong, david, hch, song,
	rgoldwyn, qi.fuli, y-goto



On 2020/12/16 下午1:43, Jane Chu wrote:
> On 12/15/2020 4:14 AM, Shiyang Ruan wrote:
>>   #ifdef CONFIG_SYSFS
>> +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t 
>> off,
>> +                   size_t len, void *data);
>>   int bd_link_disk_holder(struct block_device *bdev, struct gendisk 
>> *disk);
>>   void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk 
>> *disk);
>>   #else
>> +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t 
>> off,
> 
> Did you mean
>    static inline int bd_disk_holder_corrupted_range(..
> ?

Yes, it's my fault.  Thanks a lot.


--
Thanks,
Ruan Shiyang.

> 
> thanks,
> -jane
> 
>> +                   size_t len, void *data)
>> +{
>> +    return 0;
>> +}
>>   static inline int bd_link_disk_holder(struct block_device *bdev,
>>                         struct gendisk *disk)
> 
> 




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

* Re: [RFC PATCH v3 8/9] md: Implement ->corrupted_range()
  2020-12-15 20:51   ` Darrick J. Wong
  2020-12-15 23:28     ` Dave Chinner
@ 2020-12-18  2:11     ` Ruan Shiyang
  2021-01-04 23:34       ` Darrick J. Wong
  1 sibling, 1 reply; 27+ messages in thread
From: Ruan Shiyang @ 2020-12-18  2:11 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, dan.j.williams, david, hch, song, rgoldwyn, qi.fuli,
	y-goto, Theodore Ts'o



On 2020/12/16 上午4:51, Darrick J. Wong wrote:
> On Tue, Dec 15, 2020 at 08:14:13PM +0800, 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>
>> ---
>>   drivers/md/dm.c       | 66 +++++++++++++++++++++++++++++++++++++++++++
>>   drivers/nvdimm/pmem.c |  9 ++++--
>>   fs/block_dev.c        | 21 ++++++++++++++
>>   include/linux/genhd.h |  7 +++++
>>   4 files changed, 100 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/md/dm.c b/drivers/md/dm.c
>> index 4e0cbfe3f14d..9da1f9322735 100644
>> --- a/drivers/md/dm.c
>> +++ b/drivers/md/dm.c
>> @@ -507,6 +507,71 @@ 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;
>> +	struct super_block *sb;
>> +	int srcu_idx, i, rc = 0;
>> +	bool found = false;
>> +	sector_t disk_sec, target_sec = to_sector(target_offset);
>> +
>> +	map = dm_get_live_table(md, &srcu_idx);
>> +	if (!map)
>> +		return -ENODEV;
>> +
>> +	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_sec};
>> +
>> +			found = ti->type->iterate_devices(ti, dm_blk_corrupt_fn, &bc);
>> +			if (!found)
>> +				continue;
>> +			disk_sec = ti->type->rmap(ti, target_sec);
> 
> What happens if the dm device has multiple reverse mappings because the
> physical storage is being shared at multiple LBAs?  (e.g. a
> deduplication target)

I thought that the dm device knows the mapping relationship, and it can 
be done by implementation of ->rmap() in each target.  Did I understand 
it wrong?

> 
>> +			break;
>> +		}
>> +	}
>> +
>> +	if (!found) {
>> +		rc = -ENODEV;
>> +		goto out;
>> +	}
>> +
>> +	sb = get_super(md_bdev);
>> +	if (!sb) {
>> +		rc = bd_disk_holder_corrupted_range(md_bdev, to_bytes(disk_sec), len, data);
>> +		goto out;
>> +	} else if (sb->s_op->corrupted_range) {
>> +		loff_t off = to_bytes(disk_sec - get_start_sect(md_bdev));
>> +
>> +		rc = sb->s_op->corrupted_range(sb, md_bdev, off, len, data);
> 
> This "call bd_disk_holder_corrupted_range or sb->s_op->corrupted_range"
> logic appears twice; should it be refactored into a common helper?
> 
> Or, should the superblock dispatch part move to
> bd_disk_holder_corrupted_range?

bd_disk_holder_corrupted_range() requires SYSFS configuration.  I 
introduce it to handle those block devices that can not obtain 
superblock by `get_super()`.

Usually, if we create filesystem directly on a pmem device, or make some 
partitions at first, we can use `get_super()` to get the superblock.  In 
other case, such as creating a LVM on pmem device, `get_super()` does 
not work.

So, I think refactoring it into a common helper looks better.


--
Thanks,
Ruan Shiyang.

> 
>> +	}
>> +	drop_super(sb);
>> +
>> +out:
>> +	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 +3149,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..e8cfaf860149 100644
>> --- a/drivers/nvdimm/pmem.c
>> +++ b/drivers/nvdimm/pmem.c
>> @@ -267,11 +267,14 @@ static int pmem_corrupted_range(struct gendisk *disk, struct block_device *bdev,
>>   
>>   	bdev_offset = (disk_sector - get_start_sect(bdev)) << SECTOR_SHIFT;
>>   	sb = get_super(bdev);
>> -	if (sb && sb->s_op->corrupted_range) {
>> +	if (!sb) {
>> +		rc = bd_disk_holder_corrupted_range(bdev, bdev_offset, len, data);
>> +		goto out;
>> +	} else if (sb->s_op->corrupted_range)
>>   		rc = sb->s_op->corrupted_range(sb, bdev, bdev_offset, len, data);
>> -		drop_super(sb);
> 
> This is out of scope for this patch(set) but do you think that the scsi
> disk driver should intercept media errors from sense data and call
> ->corrupted_range too?  ISTR Ted muttering that one of his employers had
> a patchset to do more with sense data than the upstream kernel currently
> does...
> 
>> -	}
>> +	drop_super(sb);
>>   
>> +out:
>>   	bdput(bdev);
>>   	return rc;
>>   }
>> diff --git a/fs/block_dev.c b/fs/block_dev.c
>> index 9e84b1928b94..d3e6bddb8041 100644
>> --- a/fs/block_dev.c
>> +++ b/fs/block_dev.c
>> @@ -1171,6 +1171,27 @@ struct bd_holder_disk {
>>   	int			refcnt;
>>   };
>>   
>> +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;
>> +}
>> +EXPORT_SYMBOL_GPL(bd_disk_holder_corrupted_range);
>> +
>>   static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
>>   						  struct gendisk *disk)
>>   {
>> diff --git a/include/linux/genhd.h b/include/linux/genhd.h
>> index ed06209008b8..fba247b852fa 100644
>> --- a/include/linux/genhd.h
>> +++ b/include/linux/genhd.h
>> @@ -382,9 +382,16 @@ int blkdev_ioctl(struct block_device *, fmode_t, unsigned, unsigned long);
>>   long compat_blkdev_ioctl(struct file *, unsigned, unsigned long);
>>   
>>   #ifdef CONFIG_SYSFS
>> +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
>> +				   size_t len, void *data);
>>   int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk);
>>   void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk);
>>   #else
>> +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
>> +				   size_t len, void *data)
>> +{
>> +	return 0;
>> +}
>>   static inline int bd_link_disk_holder(struct block_device *bdev,
>>   				      struct gendisk *disk)
>>   {
>> -- 
>> 2.29.2
>>
>>
>>
> 
> 




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

* Re: [RFC PATCH v3 9/9] xfs: Implement ->corrupted_range() for XFS
  2020-12-15 20:40   ` Darrick J. Wong
@ 2020-12-18  2:31     ` Ruan Shiyang
  0 siblings, 0 replies; 27+ messages in thread
From: Ruan Shiyang @ 2020-12-18  2:31 UTC (permalink / raw)
  To: Darrick J. Wong
  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 2020/12/16 上午4:40, Darrick J. Wong wrote:
> On Tue, Dec 15, 2020 at 08:14:14PM +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 | 10 +++++
>>   fs/xfs/xfs_mount.h |  2 +
>>   fs/xfs/xfs_super.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 105 insertions(+)
>>
>> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
>> index ef1d5bb88b93..0ec1b44bfe88 100644
>> --- a/fs/xfs/xfs_fsops.c
>> +++ b/fs/xfs/xfs_fsops.c
>> @@ -501,6 +501,16 @@ 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 (flags & SHUTDOWN_CORRUPT_DATA) {
>> +		xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_CORRUPT,
>> +"Corruption of on-disk file data 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..e36c07553486 100644
>> --- a/fs/xfs/xfs_mount.h
>> +++ b/fs/xfs/xfs_mount.h
>> @@ -274,6 +274,8 @@ 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 */
>> +#define SHUTDOWN_CORRUPT_DATA	0x0020  /* corrupt file data on device */
> 
> This symbol isn't used anywhere.  I don't know why we'd shut down the fs
> for data loss, as we don't do that anywhere else in xfs.

I prepared this flag for the later use if possible.  But since it seems 
unnecessary, I will remove it in the next version.

> 
>>   
>>   /*
>>    * Flags for xfs_mountfs
>> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
>> index e3e229e52512..30202de7e89d 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,93 @@ 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;
>> +	int				rc = 0;
> 
> Note: we usually use the name "error", not "rc".

OK.

> 
>> +	int				*flags = data;
>> +
>> +	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner)) {
> 
> There are a few more things to check here to detect if metadata has been
> lost.  The first is that any loss in the extended attribute information
> is considered filesystem metadata; and the second is that loss of an
> extent btree block is also metadata.
> 
> IOWs, this check should be:
> 
> 	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
> 		return -EFSCORRUPTED;
> 	}

Thanks for pointing out.

> 
>> +		// 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;
>> +
>> +		if (IS_DAX(VFS_I(ip)))
>> +			rc = mf_dax_mapping_kill_procs(VFS_I(ip)->i_mapping,
>> +						       rec->rm_offset, *flags);
> 
> If the file isn't S_DAX, should we call mapping_set_error here so
> that the next fsync() will also return EIO?

Nice idea.  I will try.

> 
>> +
>> +		// 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		bc = 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			rc = 0;
>> +
>> +	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_bdev == bdev) {
>> +		xfs_warn(mp, "storage lost support not available for realtime device!");
>> +		return 0;
>> +	}
> 
> This ought to kill the fs when an external log device is configured:
> 
> 	if (mp->m_logdev_targp &&
> 	    mp->m_logdev_targp != mp->m_ddev_targp &&
> 	    mp->m_logdev_targp->bt_bdev == bdev) {
> 		xfs_error(mp, "ondisk log corrupt, shutting down fs!");
> 		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_META);
> 		return 0;
> 	}
> 
> Then, we need to check explicitly for rmap support:
> 
> 	if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
> 		return 0;
> 
> so that we screen out filesystems that don't have rmap enabled.

Ah...  I was too negligent to think of this.  That's very thoughtful of 
you.  Thanks.

> 
>> +	rc = xfs_trans_alloc_empty(mp, &tp);
>> +	if (rc)
>> +		return rc;
>> +
>> +	rc = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
>> +	if (rc)
>> +		return rc;
>> +
>> +	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 = bc;
>> +
>> +	rc = xfs_rmap_query_range(cur, &rmap_low, &rmap_high, xfs_corrupt_helper, data);
>> +	if (rc == -ECANCELED)
>> +		rc = 0;
> 
> I don't think anything returns ECANCELED here...

Yes.  Will remove it.


--
Thanks,
Ruan Shiyang.
> 
> --D
> 
>> +	if (rc == -EFSCORRUPTED)
>> +		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_META);
>> +
>> +	xfs_btree_del_cursor(cur, rc);
>> +	xfs_trans_brelse(tp, agf_bp);
>> +	return rc;
>> +}
>> +
>>   static const struct super_operations xfs_super_operations = {
>>   	.alloc_inode		= xfs_fs_alloc_inode,
>>   	.destroy_inode		= xfs_fs_destroy_inode,
>> @@ -1116,6 +1208,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: [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink
  2020-12-16 20:55 ` [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Jane Chu
@ 2020-12-18  2:44   ` Ruan Shiyang
  2020-12-18  3:49     ` Darrick J. Wong
  0 siblings, 1 reply; 27+ messages in thread
From: Ruan Shiyang @ 2020-12-18  2:44 UTC (permalink / raw)
  To: Jane Chu, 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



On 2020/12/17 上午4:55, Jane Chu wrote:
> Hi, Shiyang,
> 
> On 12/15/2020 4:14 AM, Shiyang Ruan wrote:
>> 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)
> 
> So I tried the patchset with pmem error injection, the SIGBUS payload
> does not look right -
> 
> ** SIGBUS(7): **
> ** si_addr(0x(nil)), si_lsb(0xC), si_code(0x4, BUS_MCEERR_AR) **
> 
> I expect the payload looks like
> 
> ** si_addr(0x7f3672e00000), si_lsb(0x15), si_code(0x4, BUS_MCEERR_AR) **

Thanks for testing.  I test the SIGBUS by writing a program which calls 
madvise(... ,MADV_HWPOISON) to inject memory-failure.  It just shows 
that the program is killed by SIGBUS.  I cannot get any detail from it. 
  So, could you please show me the right way(test tools) to test it?


--
Thanks,
Ruan Shiyang.

> 
> thanks,
> -jane
> 
> 
> 
> 
> 
> 




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

* Re: [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink
  2020-12-18  2:44   ` Ruan Shiyang
@ 2020-12-18  3:49     ` Darrick J. Wong
  2020-12-18  9:13       ` Ruan Shiyang
  0 siblings, 1 reply; 27+ messages in thread
From: Darrick J. Wong @ 2020-12-18  3:49 UTC (permalink / raw)
  To: Ruan Shiyang
  Cc: Jane Chu, linux-kernel, linux-xfs, linux-nvdimm, linux-mm,
	linux-fsdevel, linux-raid, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto

On Fri, Dec 18, 2020 at 10:44:26AM +0800, Ruan Shiyang wrote:
> 
> 
> On 2020/12/17 上午4:55, Jane Chu wrote:
> > Hi, Shiyang,
> > 
> > On 12/15/2020 4:14 AM, Shiyang Ruan wrote:
> > > 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)
> > 
> > So I tried the patchset with pmem error injection, the SIGBUS payload
> > does not look right -
> > 
> > ** SIGBUS(7): **
> > ** si_addr(0x(nil)), si_lsb(0xC), si_code(0x4, BUS_MCEERR_AR) **
> > 
> > I expect the payload looks like
> > 
> > ** si_addr(0x7f3672e00000), si_lsb(0x15), si_code(0x4, BUS_MCEERR_AR) **
> 
> Thanks for testing.  I test the SIGBUS by writing a program which calls
> madvise(... ,MADV_HWPOISON) to inject memory-failure.  It just shows that
> the program is killed by SIGBUS.  I cannot get any detail from it.  So,
> could you please show me the right way(test tools) to test it?

I'm assuming that Jane is using a program that calls sigaction to
install a SIGBUS handler, and dumps the entire siginfo_t structure
whenever it receives one...

--D

> 
> --
> Thanks,
> Ruan Shiyang.
> 
> > 
> > thanks,
> > -jane
> > 
> > 
> > 
> > 
> > 
> > 
> 
> 


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

* Re: [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink
  2020-12-18  3:49     ` Darrick J. Wong
@ 2020-12-18  9:13       ` Ruan Shiyang
  2021-01-08 18:14         ` Jane Chu
  0 siblings, 1 reply; 27+ messages in thread
From: Ruan Shiyang @ 2020-12-18  9:13 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Jane Chu, linux-kernel, linux-xfs, linux-nvdimm, linux-mm,
	linux-fsdevel, linux-raid, dan.j.williams, david, hch, song,
	rgoldwyn, qi.fuli, y-goto



On 2020/12/18 上午11:49, Darrick J. Wong wrote:
> On Fri, Dec 18, 2020 at 10:44:26AM +0800, Ruan Shiyang wrote:
>>
>>
>> On 2020/12/17 上午4:55, Jane Chu wrote:
>>> Hi, Shiyang,
>>>
>>> On 12/15/2020 4:14 AM, Shiyang Ruan wrote:
>>>> 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)
>>>
>>> So I tried the patchset with pmem error injection, the SIGBUS payload
>>> does not look right -
>>>
>>> ** SIGBUS(7): **
>>> ** si_addr(0x(nil)), si_lsb(0xC), si_code(0x4, BUS_MCEERR_AR) **
>>>
>>> I expect the payload looks like
>>>
>>> ** si_addr(0x7f3672e00000), si_lsb(0x15), si_code(0x4, BUS_MCEERR_AR) **
>>
>> Thanks for testing.  I test the SIGBUS by writing a program which calls
>> madvise(... ,MADV_HWPOISON) to inject memory-failure.  It just shows that
>> the program is killed by SIGBUS.  I cannot get any detail from it.  So,
>> could you please show me the right way(test tools) to test it?
> 
> I'm assuming that Jane is using a program that calls sigaction to
> install a SIGBUS handler, and dumps the entire siginfo_t structure
> whenever it receives one...

OK.  Let me try it and figure out what's wrong in it.


--
Thanks,
Ruan Shiyang.

> 
> --D
> 
>>
>> --
>> Thanks,
>> Ruan Shiyang.
>>
>>>
>>> thanks,
>>> -jane
>>>
>>>
>>>
>>>
>>>
>>>
>>
>>
> 
> 




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

* Re: [RFC PATCH v3 8/9] md: Implement ->corrupted_range()
  2020-12-18  2:11     ` Ruan Shiyang
@ 2021-01-04 23:34       ` Darrick J. Wong
  2021-01-08  9:52         ` Ruan Shiyang
  0 siblings, 1 reply; 27+ messages in thread
From: Darrick J. Wong @ 2021-01-04 23:34 UTC (permalink / raw)
  To: Ruan Shiyang
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, dan.j.williams, david, hch, song, rgoldwyn, qi.fuli,
	y-goto, Theodore Ts'o

On Fri, Dec 18, 2020 at 10:11:54AM +0800, Ruan Shiyang wrote:
> 
> 
> On 2020/12/16 上午4:51, Darrick J. Wong wrote:
> > On Tue, Dec 15, 2020 at 08:14:13PM +0800, 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>
> > > ---
> > >   drivers/md/dm.c       | 66 +++++++++++++++++++++++++++++++++++++++++++
> > >   drivers/nvdimm/pmem.c |  9 ++++--
> > >   fs/block_dev.c        | 21 ++++++++++++++
> > >   include/linux/genhd.h |  7 +++++
> > >   4 files changed, 100 insertions(+), 3 deletions(-)
> > > 
> > > diff --git a/drivers/md/dm.c b/drivers/md/dm.c
> > > index 4e0cbfe3f14d..9da1f9322735 100644
> > > --- a/drivers/md/dm.c
> > > +++ b/drivers/md/dm.c
> > > @@ -507,6 +507,71 @@ 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;
> > > +	struct super_block *sb;
> > > +	int srcu_idx, i, rc = 0;
> > > +	bool found = false;
> > > +	sector_t disk_sec, target_sec = to_sector(target_offset);
> > > +
> > > +	map = dm_get_live_table(md, &srcu_idx);
> > > +	if (!map)
> > > +		return -ENODEV;
> > > +
> > > +	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_sec};
> > > +
> > > +			found = ti->type->iterate_devices(ti, dm_blk_corrupt_fn, &bc);
> > > +			if (!found)
> > > +				continue;
> > > +			disk_sec = ti->type->rmap(ti, target_sec);
> > 
> > What happens if the dm device has multiple reverse mappings because the
> > physical storage is being shared at multiple LBAs?  (e.g. a
> > deduplication target)
> 
> I thought that the dm device knows the mapping relationship, and it can be
> done by implementation of ->rmap() in each target.  Did I understand it
> wrong?

The dm device /does/ know the mapping relationship.  I'm asking what
happens if there are *multiple* mappings.  For example, a deduplicating
dm device could observe that the upper level code wrote some data to
sector 200 and now it wants to write the same data to sector 500.
Instead of writing twice, it simply maps sector 500 in its LBA space to
the same space that it mapped sector 200.

Pretend that sector 200 on the dm-dedupe device maps to sector 64 on the
underlying storage (call it /dev/pmem1 and let's say it's the only
target sitting underneath the dm-dedupe device).

If /dev/pmem1 then notices that sector 64 has gone bad, it will start
calling ->corrupted_range handlers until it calls dm_blk_corrupted_range
on the dm-dedupe device.  At least in theory, the dm-dedupe driver's
rmap method ought to return both (64 -> 200) and (64 -> 500) so that
dm_blk_corrupted_range can pass on both corruption notices to whatever's
sitting atop the dedupe device.

At the moment, your ->rmap prototype is only capable of returning one
sector_t mapping per target, and there's only the one target under the
dedupe device, so we cannot report the loss of sectors 200 and 500 to
whatever device is sitting on top of dm-dedupe.

--D

> > 
> > > +			break;
> > > +		}
> > > +	}
> > > +
> > > +	if (!found) {
> > > +		rc = -ENODEV;
> > > +		goto out;
> > > +	}
> > > +
> > > +	sb = get_super(md_bdev);
> > > +	if (!sb) {
> > > +		rc = bd_disk_holder_corrupted_range(md_bdev, to_bytes(disk_sec), len, data);
> > > +		goto out;
> > > +	} else if (sb->s_op->corrupted_range) {
> > > +		loff_t off = to_bytes(disk_sec - get_start_sect(md_bdev));
> > > +
> > > +		rc = sb->s_op->corrupted_range(sb, md_bdev, off, len, data);
> > 
> > This "call bd_disk_holder_corrupted_range or sb->s_op->corrupted_range"
> > logic appears twice; should it be refactored into a common helper?
> > 
> > Or, should the superblock dispatch part move to
> > bd_disk_holder_corrupted_range?
> 
> bd_disk_holder_corrupted_range() requires SYSFS configuration.  I introduce
> it to handle those block devices that can not obtain superblock by
> `get_super()`.
> 
> Usually, if we create filesystem directly on a pmem device, or make some
> partitions at first, we can use `get_super()` to get the superblock.  In
> other case, such as creating a LVM on pmem device, `get_super()` does not
> work.
> 
> So, I think refactoring it into a common helper looks better.
> 
> 
> --
> Thanks,
> Ruan Shiyang.
> 
> > 
> > > +	}
> > > +	drop_super(sb);
> > > +
> > > +out:
> > > +	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 +3149,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..e8cfaf860149 100644
> > > --- a/drivers/nvdimm/pmem.c
> > > +++ b/drivers/nvdimm/pmem.c
> > > @@ -267,11 +267,14 @@ static int pmem_corrupted_range(struct gendisk *disk, struct block_device *bdev,
> > >   	bdev_offset = (disk_sector - get_start_sect(bdev)) << SECTOR_SHIFT;
> > >   	sb = get_super(bdev);
> > > -	if (sb && sb->s_op->corrupted_range) {
> > > +	if (!sb) {
> > > +		rc = bd_disk_holder_corrupted_range(bdev, bdev_offset, len, data);
> > > +		goto out;
> > > +	} else if (sb->s_op->corrupted_range)
> > >   		rc = sb->s_op->corrupted_range(sb, bdev, bdev_offset, len, data);
> > > -		drop_super(sb);
> > 
> > This is out of scope for this patch(set) but do you think that the scsi
> > disk driver should intercept media errors from sense data and call
> > ->corrupted_range too?  ISTR Ted muttering that one of his employers had
> > a patchset to do more with sense data than the upstream kernel currently
> > does...
> > 
> > > -	}
> > > +	drop_super(sb);
> > > +out:
> > >   	bdput(bdev);
> > >   	return rc;
> > >   }
> > > diff --git a/fs/block_dev.c b/fs/block_dev.c
> > > index 9e84b1928b94..d3e6bddb8041 100644
> > > --- a/fs/block_dev.c
> > > +++ b/fs/block_dev.c
> > > @@ -1171,6 +1171,27 @@ struct bd_holder_disk {
> > >   	int			refcnt;
> > >   };
> > > +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;
> > > +}
> > > +EXPORT_SYMBOL_GPL(bd_disk_holder_corrupted_range);
> > > +
> > >   static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
> > >   						  struct gendisk *disk)
> > >   {
> > > diff --git a/include/linux/genhd.h b/include/linux/genhd.h
> > > index ed06209008b8..fba247b852fa 100644
> > > --- a/include/linux/genhd.h
> > > +++ b/include/linux/genhd.h
> > > @@ -382,9 +382,16 @@ int blkdev_ioctl(struct block_device *, fmode_t, unsigned, unsigned long);
> > >   long compat_blkdev_ioctl(struct file *, unsigned, unsigned long);
> > >   #ifdef CONFIG_SYSFS
> > > +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
> > > +				   size_t len, void *data);
> > >   int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk);
> > >   void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk);
> > >   #else
> > > +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
> > > +				   size_t len, void *data)
> > > +{
> > > +	return 0;
> > > +}
> > >   static inline int bd_link_disk_holder(struct block_device *bdev,
> > >   				      struct gendisk *disk)
> > >   {
> > > -- 
> > > 2.29.2
> > > 
> > > 
> > > 
> > 
> > 
> 
> 


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

* Re: [RFC PATCH v3 8/9] md: Implement ->corrupted_range()
  2021-01-04 23:34       ` Darrick J. Wong
@ 2021-01-08  9:52         ` Ruan Shiyang
  2021-01-08 19:05           ` Darrick J. Wong
  0 siblings, 1 reply; 27+ messages in thread
From: Ruan Shiyang @ 2021-01-08  9:52 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, dan.j.williams, david, hch, song, rgoldwyn, qi.fuli,
	y-goto, Theodore Ts'o



On 2021/1/5 上午7:34, Darrick J. Wong wrote:
> On Fri, Dec 18, 2020 at 10:11:54AM +0800, Ruan Shiyang wrote:
>>
>>
>> On 2020/12/16 上午4:51, Darrick J. Wong wrote:
>>> On Tue, Dec 15, 2020 at 08:14:13PM +0800, 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>
>>>> ---
>>>>    drivers/md/dm.c       | 66 +++++++++++++++++++++++++++++++++++++++++++
>>>>    drivers/nvdimm/pmem.c |  9 ++++--
>>>>    fs/block_dev.c        | 21 ++++++++++++++
>>>>    include/linux/genhd.h |  7 +++++
>>>>    4 files changed, 100 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/drivers/md/dm.c b/drivers/md/dm.c
>>>> index 4e0cbfe3f14d..9da1f9322735 100644
>>>> --- a/drivers/md/dm.c
>>>> +++ b/drivers/md/dm.c
>>>> @@ -507,6 +507,71 @@ 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;
>>>> +	struct super_block *sb;
>>>> +	int srcu_idx, i, rc = 0;
>>>> +	bool found = false;
>>>> +	sector_t disk_sec, target_sec = to_sector(target_offset);
>>>> +
>>>> +	map = dm_get_live_table(md, &srcu_idx);
>>>> +	if (!map)
>>>> +		return -ENODEV;
>>>> +
>>>> +	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_sec};
>>>> +
>>>> +			found = ti->type->iterate_devices(ti, dm_blk_corrupt_fn, &bc);
>>>> +			if (!found)
>>>> +				continue;
>>>> +			disk_sec = ti->type->rmap(ti, target_sec);
>>>
>>> What happens if the dm device has multiple reverse mappings because the
>>> physical storage is being shared at multiple LBAs?  (e.g. a
>>> deduplication target)
>>
>> I thought that the dm device knows the mapping relationship, and it can be
>> done by implementation of ->rmap() in each target.  Did I understand it
>> wrong?
> 
> The dm device /does/ know the mapping relationship.  I'm asking what
> happens if there are *multiple* mappings.  For example, a deduplicating
> dm device could observe that the upper level code wrote some data to
> sector 200 and now it wants to write the same data to sector 500.
> Instead of writing twice, it simply maps sector 500 in its LBA space to
> the same space that it mapped sector 200.
> 
> Pretend that sector 200 on the dm-dedupe device maps to sector 64 on the
> underlying storage (call it /dev/pmem1 and let's say it's the only
> target sitting underneath the dm-dedupe device).
> 
> If /dev/pmem1 then notices that sector 64 has gone bad, it will start
> calling ->corrupted_range handlers until it calls dm_blk_corrupted_range
> on the dm-dedupe device.  At least in theory, the dm-dedupe driver's
> rmap method ought to return both (64 -> 200) and (64 -> 500) so that
> dm_blk_corrupted_range can pass on both corruption notices to whatever's
> sitting atop the dedupe device.
> 
> At the moment, your ->rmap prototype is only capable of returning one
> sector_t mapping per target, and there's only the one target under the
> dedupe device, so we cannot report the loss of sectors 200 and 500 to
> whatever device is sitting on top of dm-dedupe.

Got it.  I didn't know there is a kind of dm device called dm-dedupe. 
Thanks for the guidance.


--
Thanks,
Ruan Shiyang.

> 
> --D
> 
>>>
>>>> +			break;
>>>> +		}
>>>> +	}
>>>> +
>>>> +	if (!found) {
>>>> +		rc = -ENODEV;
>>>> +		goto out;
>>>> +	}
>>>> +
>>>> +	sb = get_super(md_bdev);
>>>> +	if (!sb) {
>>>> +		rc = bd_disk_holder_corrupted_range(md_bdev, to_bytes(disk_sec), len, data);
>>>> +		goto out;
>>>> +	} else if (sb->s_op->corrupted_range) {
>>>> +		loff_t off = to_bytes(disk_sec - get_start_sect(md_bdev));
>>>> +
>>>> +		rc = sb->s_op->corrupted_range(sb, md_bdev, off, len, data);
>>>
>>> This "call bd_disk_holder_corrupted_range or sb->s_op->corrupted_range"
>>> logic appears twice; should it be refactored into a common helper?
>>>
>>> Or, should the superblock dispatch part move to
>>> bd_disk_holder_corrupted_range?
>>
>> bd_disk_holder_corrupted_range() requires SYSFS configuration.  I introduce
>> it to handle those block devices that can not obtain superblock by
>> `get_super()`.
>>
>> Usually, if we create filesystem directly on a pmem device, or make some
>> partitions at first, we can use `get_super()` to get the superblock.  In
>> other case, such as creating a LVM on pmem device, `get_super()` does not
>> work.
>>
>> So, I think refactoring it into a common helper looks better.
>>
>>
>> --
>> Thanks,
>> Ruan Shiyang.
>>
>>>
>>>> +	}
>>>> +	drop_super(sb);
>>>> +
>>>> +out:
>>>> +	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 +3149,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..e8cfaf860149 100644
>>>> --- a/drivers/nvdimm/pmem.c
>>>> +++ b/drivers/nvdimm/pmem.c
>>>> @@ -267,11 +267,14 @@ static int pmem_corrupted_range(struct gendisk *disk, struct block_device *bdev,
>>>>    	bdev_offset = (disk_sector - get_start_sect(bdev)) << SECTOR_SHIFT;
>>>>    	sb = get_super(bdev);
>>>> -	if (sb && sb->s_op->corrupted_range) {
>>>> +	if (!sb) {
>>>> +		rc = bd_disk_holder_corrupted_range(bdev, bdev_offset, len, data);
>>>> +		goto out;
>>>> +	} else if (sb->s_op->corrupted_range)
>>>>    		rc = sb->s_op->corrupted_range(sb, bdev, bdev_offset, len, data);
>>>> -		drop_super(sb);
>>>
>>> This is out of scope for this patch(set) but do you think that the scsi
>>> disk driver should intercept media errors from sense data and call
>>> ->corrupted_range too?  ISTR Ted muttering that one of his employers had
>>> a patchset to do more with sense data than the upstream kernel currently
>>> does...
>>>
>>>> -	}
>>>> +	drop_super(sb);
>>>> +out:
>>>>    	bdput(bdev);
>>>>    	return rc;
>>>>    }
>>>> diff --git a/fs/block_dev.c b/fs/block_dev.c
>>>> index 9e84b1928b94..d3e6bddb8041 100644
>>>> --- a/fs/block_dev.c
>>>> +++ b/fs/block_dev.c
>>>> @@ -1171,6 +1171,27 @@ struct bd_holder_disk {
>>>>    	int			refcnt;
>>>>    };
>>>> +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;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(bd_disk_holder_corrupted_range);
>>>> +
>>>>    static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
>>>>    						  struct gendisk *disk)
>>>>    {
>>>> diff --git a/include/linux/genhd.h b/include/linux/genhd.h
>>>> index ed06209008b8..fba247b852fa 100644
>>>> --- a/include/linux/genhd.h
>>>> +++ b/include/linux/genhd.h
>>>> @@ -382,9 +382,16 @@ int blkdev_ioctl(struct block_device *, fmode_t, unsigned, unsigned long);
>>>>    long compat_blkdev_ioctl(struct file *, unsigned, unsigned long);
>>>>    #ifdef CONFIG_SYSFS
>>>> +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
>>>> +				   size_t len, void *data);
>>>>    int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk);
>>>>    void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk);
>>>>    #else
>>>> +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
>>>> +				   size_t len, void *data)
>>>> +{
>>>> +	return 0;
>>>> +}
>>>>    static inline int bd_link_disk_holder(struct block_device *bdev,
>>>>    				      struct gendisk *disk)
>>>>    {
>>>> -- 
>>>> 2.29.2
>>>>
>>>>
>>>>
>>>
>>>
>>
>>
> 
> 




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

* Re: [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink
  2020-12-18  9:13       ` Ruan Shiyang
@ 2021-01-08 18:14         ` Jane Chu
  0 siblings, 0 replies; 27+ messages in thread
From: Jane Chu @ 2021-01-08 18:14 UTC (permalink / raw)
  To: Ruan Shiyang, Darrick J. Wong
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, dan.j.williams, david, hch, song, rgoldwyn, qi.fuli,
	y-goto

Hi, Shiyang,

On 12/18/2020 1:13 AM, Ruan Shiyang wrote:
>>>>
>>>> So I tried the patchset with pmem error injection, the SIGBUS payload
>>>> does not look right -
>>>>
>>>> ** SIGBUS(7): **
>>>> ** si_addr(0x(nil)), si_lsb(0xC), si_code(0x4, BUS_MCEERR_AR) **
>>>>
>>>> I expect the payload looks like
>>>>
>>>> ** si_addr(0x7f3672e00000), si_lsb(0x15), si_code(0x4, 
>>>> BUS_MCEERR_AR) **
>>>
>>> Thanks for testing.  I test the SIGBUS by writing a program which calls
>>> madvise(... ,MADV_HWPOISON) to inject memory-failure.  It just shows 
>>> that
>>> the program is killed by SIGBUS.  I cannot get any detail from it.  So,
>>> could you please show me the right way(test tools) to test it?
>>
>> I'm assuming that Jane is using a program that calls sigaction to
>> install a SIGBUS handler, and dumps the entire siginfo_t structure
>> whenever it receives one...

Yes, thanks Darrick.

> 
> OK.  Let me try it and figure out what's wrong in it.

I injected poison via "ndctl inject-error",  not expecting it made any 
difference though.

Any luck?

thanks,
-jane



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

* Re: [RFC PATCH v3 8/9] md: Implement ->corrupted_range()
  2021-01-08  9:52         ` Ruan Shiyang
@ 2021-01-08 19:05           ` Darrick J. Wong
  0 siblings, 0 replies; 27+ messages in thread
From: Darrick J. Wong @ 2021-01-08 19:05 UTC (permalink / raw)
  To: Ruan Shiyang
  Cc: linux-kernel, linux-xfs, linux-nvdimm, linux-mm, linux-fsdevel,
	linux-raid, dan.j.williams, david, hch, song, rgoldwyn, qi.fuli,
	y-goto, Theodore Ts'o

On Fri, Jan 08, 2021 at 05:52:11PM +0800, Ruan Shiyang wrote:
> 
> 
> On 2021/1/5 上午7:34, Darrick J. Wong wrote:
> > On Fri, Dec 18, 2020 at 10:11:54AM +0800, Ruan Shiyang wrote:
> > > 
> > > 
> > > On 2020/12/16 上午4:51, Darrick J. Wong wrote:
> > > > On Tue, Dec 15, 2020 at 08:14:13PM +0800, 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>
> > > > > ---
> > > > >    drivers/md/dm.c       | 66 +++++++++++++++++++++++++++++++++++++++++++
> > > > >    drivers/nvdimm/pmem.c |  9 ++++--
> > > > >    fs/block_dev.c        | 21 ++++++++++++++
> > > > >    include/linux/genhd.h |  7 +++++
> > > > >    4 files changed, 100 insertions(+), 3 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/md/dm.c b/drivers/md/dm.c
> > > > > index 4e0cbfe3f14d..9da1f9322735 100644
> > > > > --- a/drivers/md/dm.c
> > > > > +++ b/drivers/md/dm.c
> > > > > @@ -507,6 +507,71 @@ 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;
> > > > > +	struct super_block *sb;
> > > > > +	int srcu_idx, i, rc = 0;
> > > > > +	bool found = false;
> > > > > +	sector_t disk_sec, target_sec = to_sector(target_offset);
> > > > > +
> > > > > +	map = dm_get_live_table(md, &srcu_idx);
> > > > > +	if (!map)
> > > > > +		return -ENODEV;
> > > > > +
> > > > > +	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_sec};
> > > > > +
> > > > > +			found = ti->type->iterate_devices(ti, dm_blk_corrupt_fn, &bc);
> > > > > +			if (!found)
> > > > > +				continue;
> > > > > +			disk_sec = ti->type->rmap(ti, target_sec);
> > > > 
> > > > What happens if the dm device has multiple reverse mappings because the
> > > > physical storage is being shared at multiple LBAs?  (e.g. a
> > > > deduplication target)
> > > 
> > > I thought that the dm device knows the mapping relationship, and it can be
> > > done by implementation of ->rmap() in each target.  Did I understand it
> > > wrong?
> > 
> > The dm device /does/ know the mapping relationship.  I'm asking what
> > happens if there are *multiple* mappings.  For example, a deduplicating
> > dm device could observe that the upper level code wrote some data to
> > sector 200 and now it wants to write the same data to sector 500.
> > Instead of writing twice, it simply maps sector 500 in its LBA space to
> > the same space that it mapped sector 200.
> > 
> > Pretend that sector 200 on the dm-dedupe device maps to sector 64 on the
> > underlying storage (call it /dev/pmem1 and let's say it's the only
> > target sitting underneath the dm-dedupe device).
> > 
> > If /dev/pmem1 then notices that sector 64 has gone bad, it will start
> > calling ->corrupted_range handlers until it calls dm_blk_corrupted_range
> > on the dm-dedupe device.  At least in theory, the dm-dedupe driver's
> > rmap method ought to return both (64 -> 200) and (64 -> 500) so that
> > dm_blk_corrupted_range can pass on both corruption notices to whatever's
> > sitting atop the dedupe device.
> > 
> > At the moment, your ->rmap prototype is only capable of returning one
> > sector_t mapping per target, and there's only the one target under the
> > dedupe device, so we cannot report the loss of sectors 200 and 500 to
> > whatever device is sitting on top of dm-dedupe.
> 
> Got it.  I didn't know there is a kind of dm device called dm-dedupe. Thanks
> for the guidance.

There isn't one upstream, but there are out of tree deduplication
drivers (VDO) and in principle any dm target can have multiple forward
mappings to a single block on the lower device.

--D

> 
> --
> Thanks,
> Ruan Shiyang.
> 
> > 
> > --D
> > 
> > > > 
> > > > > +			break;
> > > > > +		}
> > > > > +	}
> > > > > +
> > > > > +	if (!found) {
> > > > > +		rc = -ENODEV;
> > > > > +		goto out;
> > > > > +	}
> > > > > +
> > > > > +	sb = get_super(md_bdev);
> > > > > +	if (!sb) {
> > > > > +		rc = bd_disk_holder_corrupted_range(md_bdev, to_bytes(disk_sec), len, data);
> > > > > +		goto out;
> > > > > +	} else if (sb->s_op->corrupted_range) {
> > > > > +		loff_t off = to_bytes(disk_sec - get_start_sect(md_bdev));
> > > > > +
> > > > > +		rc = sb->s_op->corrupted_range(sb, md_bdev, off, len, data);
> > > > 
> > > > This "call bd_disk_holder_corrupted_range or sb->s_op->corrupted_range"
> > > > logic appears twice; should it be refactored into a common helper?
> > > > 
> > > > Or, should the superblock dispatch part move to
> > > > bd_disk_holder_corrupted_range?
> > > 
> > > bd_disk_holder_corrupted_range() requires SYSFS configuration.  I introduce
> > > it to handle those block devices that can not obtain superblock by
> > > `get_super()`.
> > > 
> > > Usually, if we create filesystem directly on a pmem device, or make some
> > > partitions at first, we can use `get_super()` to get the superblock.  In
> > > other case, such as creating a LVM on pmem device, `get_super()` does not
> > > work.
> > > 
> > > So, I think refactoring it into a common helper looks better.
> > > 
> > > 
> > > --
> > > Thanks,
> > > Ruan Shiyang.
> > > 
> > > > 
> > > > > +	}
> > > > > +	drop_super(sb);
> > > > > +
> > > > > +out:
> > > > > +	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 +3149,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..e8cfaf860149 100644
> > > > > --- a/drivers/nvdimm/pmem.c
> > > > > +++ b/drivers/nvdimm/pmem.c
> > > > > @@ -267,11 +267,14 @@ static int pmem_corrupted_range(struct gendisk *disk, struct block_device *bdev,
> > > > >    	bdev_offset = (disk_sector - get_start_sect(bdev)) << SECTOR_SHIFT;
> > > > >    	sb = get_super(bdev);
> > > > > -	if (sb && sb->s_op->corrupted_range) {
> > > > > +	if (!sb) {
> > > > > +		rc = bd_disk_holder_corrupted_range(bdev, bdev_offset, len, data);
> > > > > +		goto out;
> > > > > +	} else if (sb->s_op->corrupted_range)
> > > > >    		rc = sb->s_op->corrupted_range(sb, bdev, bdev_offset, len, data);
> > > > > -		drop_super(sb);
> > > > 
> > > > This is out of scope for this patch(set) but do you think that the scsi
> > > > disk driver should intercept media errors from sense data and call
> > > > ->corrupted_range too?  ISTR Ted muttering that one of his employers had
> > > > a patchset to do more with sense data than the upstream kernel currently
> > > > does...
> > > > 
> > > > > -	}
> > > > > +	drop_super(sb);
> > > > > +out:
> > > > >    	bdput(bdev);
> > > > >    	return rc;
> > > > >    }
> > > > > diff --git a/fs/block_dev.c b/fs/block_dev.c
> > > > > index 9e84b1928b94..d3e6bddb8041 100644
> > > > > --- a/fs/block_dev.c
> > > > > +++ b/fs/block_dev.c
> > > > > @@ -1171,6 +1171,27 @@ struct bd_holder_disk {
> > > > >    	int			refcnt;
> > > > >    };
> > > > > +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;
> > > > > +}
> > > > > +EXPORT_SYMBOL_GPL(bd_disk_holder_corrupted_range);
> > > > > +
> > > > >    static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
> > > > >    						  struct gendisk *disk)
> > > > >    {
> > > > > diff --git a/include/linux/genhd.h b/include/linux/genhd.h
> > > > > index ed06209008b8..fba247b852fa 100644
> > > > > --- a/include/linux/genhd.h
> > > > > +++ b/include/linux/genhd.h
> > > > > @@ -382,9 +382,16 @@ int blkdev_ioctl(struct block_device *, fmode_t, unsigned, unsigned long);
> > > > >    long compat_blkdev_ioctl(struct file *, unsigned, unsigned long);
> > > > >    #ifdef CONFIG_SYSFS
> > > > > +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
> > > > > +				   size_t len, void *data);
> > > > >    int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk);
> > > > >    void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk);
> > > > >    #else
> > > > > +int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
> > > > > +				   size_t len, void *data)
> > > > > +{
> > > > > +	return 0;
> > > > > +}
> > > > >    static inline int bd_link_disk_holder(struct block_device *bdev,
> > > > >    				      struct gendisk *disk)
> > > > >    {
> > > > > -- 
> > > > > 2.29.2
> > > > > 
> > > > > 
> > > > > 
> > > > 
> > > > 
> > > 
> > > 
> > 
> > 
> 
> 


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

end of thread, other threads:[~2021-01-08 19:07 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-15 12:14 [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
2020-12-15 12:14 ` [RFC PATCH v3 1/9] pagemap: Introduce ->memory_failure() Shiyang Ruan
2020-12-15 12:14 ` [RFC PATCH v3 2/9] blk: Introduce ->corrupted_range() for block device Shiyang Ruan
2020-12-15 12:14 ` [RFC PATCH v3 3/9] fs: Introduce ->corrupted_range() for superblock Shiyang Ruan
2020-12-15 12:14 ` [RFC PATCH v3 4/9] mm, fsdax: Refactor memory-failure handler for dax mapping Shiyang Ruan
2020-12-16 21:26   ` Dave Chinner
2020-12-18  1:48     ` Ruan Shiyang
2020-12-15 12:14 ` [RFC PATCH v3 5/9] mm, pmem: Implement ->memory_failure() in pmem driver Shiyang Ruan
2020-12-15 12:14 ` [RFC PATCH v3 6/9] pmem: Implement ->corrupted_range() for " Shiyang Ruan
2020-12-15 12:14 ` [RFC PATCH v3 7/9] dm: Introduce ->rmap() to find bdev offset Shiyang Ruan
2020-12-15 12:14 ` [RFC PATCH v3 8/9] md: Implement ->corrupted_range() Shiyang Ruan
2020-12-15 20:51   ` Darrick J. Wong
2020-12-15 23:28     ` Dave Chinner
2020-12-18  2:11     ` Ruan Shiyang
2021-01-04 23:34       ` Darrick J. Wong
2021-01-08  9:52         ` Ruan Shiyang
2021-01-08 19:05           ` Darrick J. Wong
2020-12-16  5:43   ` Jane Chu
2020-12-18  1:50     ` Ruan Shiyang
2020-12-15 12:14 ` [RFC PATCH v3 9/9] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
2020-12-15 20:40   ` Darrick J. Wong
2020-12-18  2:31     ` Ruan Shiyang
2020-12-16 20:55 ` [RFC PATCH v3 0/9] fsdax: introduce fs query to support reflink Jane Chu
2020-12-18  2:44   ` Ruan Shiyang
2020-12-18  3:49     ` Darrick J. Wong
2020-12-18  9:13       ` Ruan Shiyang
2021-01-08 18:14         ` Jane Chu

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