nvdimm.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/9] fsdax: introduce fs query to support reflink
@ 2021-06-28  0:02 Shiyang Ruan
  2021-06-28  0:02 ` [PATCH v5 1/9] pagemap: Introduce ->memory_failure() Shiyang Ruan
                   ` (8 more replies)
  0 siblings, 9 replies; 20+ messages in thread
From: Shiyang Ruan @ 2021-06-28  0:02 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, dm-devel
  Cc: darrick.wong, dan.j.williams, david, hch, agk, snitzer, rgoldwyn

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

Change from V4:
  - Add nr_pfns for ->memory_failure() to support range based notification
  - Remove struct bdev in dax_holder_notify_failure()
  - Add rwsem for dax_holder
  - Rename functions to (*_)notify_failure()
  - Remove sb->corrupted_range(), implement holder_ops in filesystem and
      MD driver instead
  - Reorganize the patchset, make it easy to review
  - Rebased to v5.13-rc7

This patchset moves owner tracking from dax_assocaite_entry() to pmem
device driver, by introducing an interface ->memory_failure() for struct
pagemap.  This interface is called by memory_failure() in mm, and
implemented by pmem device.

Then call holder operations to find the filesystem which the corrupted
data located in, and call filesystem handler to track files or metadata
associated 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()
|* fsdax case
|------------
|pgmap->ops->memory_failure()      => pmem_pgmap_memory_failure()
| dax_holder_notify_failure()      =>
|  dax_device->holder_ops->notify_failure() =>
|                                     - xfs_dax_notify_failure()
|                                     - md_dax_notify_failure()
|  |* xfs_dax_notify_failure()
|  |--------------------------
|  |   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_kill_procs()
|  |* md_dax_notify_failure()
|  |-------------------------
|      md_targets->iterate_devices()
|      md_targets->rmap()          => linear_rmap()
|       dax_holder_notify_failure()
|* normal case
|-------------
 mf_generic_kill_procs()

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

(Rebased on v5.13-rc7)
==

Shiyang Ruan (9):
  pagemap: Introduce ->memory_failure()
  dax: Introduce holder for dax_device
  mm: factor helpers for memory_failure_dev_pagemap
  pmem,mm: Implement ->memory_failure in pmem driver
  mm: Introduce mf_dax_kill_procs() for fsdax case
  xfs: Implement ->corrupted_range() for XFS
  dm: Introduce ->rmap() to find bdev offset
  md: Implement dax_holder_operations
  fs/dax: Remove useless functions

 block/genhd.c                 |  30 +++++++
 drivers/dax/super.c           |  49 ++++++++++
 drivers/md/dm-linear.c        |  20 +++++
 drivers/md/dm.c               | 126 +++++++++++++++++++++++++-
 drivers/nvdimm/pmem.c         |  13 +++
 fs/dax.c                      |  73 ++++-----------
 fs/xfs/xfs_fsops.c            |   5 ++
 fs/xfs/xfs_mount.h            |   1 +
 fs/xfs/xfs_super.c            | 140 +++++++++++++++++++++++++++++
 include/linux/dax.h           |  27 ++++++
 include/linux/device-mapper.h |   5 ++
 include/linux/genhd.h         |   1 +
 include/linux/memremap.h      |   9 ++
 include/linux/mm.h            |  10 +++
 mm/memory-failure.c           | 165 ++++++++++++++++++++++------------
 15 files changed, 564 insertions(+), 110 deletions(-)

--
2.32.0




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

* [PATCH v5 1/9] pagemap: Introduce ->memory_failure()
  2021-06-28  0:02 [PATCH v5 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
@ 2021-06-28  0:02 ` Shiyang Ruan
  2021-06-28  0:02 ` [PATCH v5 2/9] dax: Introduce holder for dax_device Shiyang Ruan
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Shiyang Ruan @ 2021-06-28  0:02 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, dm-devel
  Cc: darrick.wong, dan.j.williams, david, hch, agk, snitzer, rgoldwyn

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 filesystem in which
the corrupted page located in.  And finally call filesystem handler to
deal with this error.

The filesystem will try to recover the corrupted data if necessary.

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

diff --git a/include/linux/memremap.h b/include/linux/memremap.h
index 45a79da89c5f..d96da47cc249 100644
--- a/include/linux/memremap.h
+++ b/include/linux/memremap.h
@@ -87,6 +87,15 @@ 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 a range of pfns.  Notify the
+	 * processes who are using these pfns, and try to recover the data on
+	 * them if necessary.  The flag is finally passed to the recover
+	 * function through the whole notify routine.
+	 */
+	int (*memory_failure)(struct dev_pagemap *pgmap, unsigned long pfn,
+			      unsigned long nr_pfns, int flags);
 };

 #define PGMAP_ALTMAP_VALID	(1 << 0)
--
2.32.0




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

* [PATCH v5 2/9] dax: Introduce holder for dax_device
  2021-06-28  0:02 [PATCH v5 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
  2021-06-28  0:02 ` [PATCH v5 1/9] pagemap: Introduce ->memory_failure() Shiyang Ruan
@ 2021-06-28  0:02 ` Shiyang Ruan
  2021-07-19 15:17   ` Christoph Hellwig
  2021-06-28  0:02 ` [PATCH v5 3/9] mm: factor helpers for memory_failure_dev_pagemap Shiyang Ruan
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Shiyang Ruan @ 2021-06-28  0:02 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, dm-devel
  Cc: darrick.wong, dan.j.williams, david, hch, agk, snitzer, rgoldwyn

To easily track filesystem from a pmem device, we introduce a holder for
dax_device structure, and also its operation.  This holder is used to
remember who is using this dax_device:
 - When it is the backend of a filesystem, the holder will be the
   superblock of this filesystem.
 - When this pmem device is one of the targets in a mapped device, the
   holder will be this mapped device.  In this case, the mapped device
   has its own dax_device and it will follow the first rule.  So that we
   can finally track to the filesystem we needed.

The holder and holder_ops will be set when filesystem is being mounted,
or an target device is being activated.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
---
 drivers/dax/super.c | 49 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/dax.h | 17 ++++++++++++++++
 2 files changed, 66 insertions(+)

diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index 5fa6ae9dbc8b..22f0aa3f2e13 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -222,8 +222,11 @@ struct dax_device {
 	struct cdev cdev;
 	const char *host;
 	void *private;
+	struct rw_semaphore holder_rwsem;
+	void *holder_data;
 	unsigned long flags;
 	const struct dax_operations *ops;
+	const struct dax_holder_operations *holder_ops;
 };

 static ssize_t write_cache_show(struct device *dev,
@@ -373,6 +376,24 @@ int dax_zero_page_range(struct dax_device *dax_dev, pgoff_t pgoff,
 }
 EXPORT_SYMBOL_GPL(dax_zero_page_range);

+int dax_holder_notify_failure(struct dax_device *dax_dev, loff_t offset,
+			      size_t size, void *data)
+{
+	int rc = -ENXIO;
+	if (!dax_dev)
+		return rc;
+
+	if (dax_dev->holder_data) {
+		rc = dax_dev->holder_ops->notify_failure(dax_dev, offset,
+							 size, data);
+		if (rc == -ENODEV)
+			rc = -ENXIO;
+	} else
+		rc = -EOPNOTSUPP;
+	return rc;
+}
+EXPORT_SYMBOL_GPL(dax_holder_notify_failure);
+
 #ifdef CONFIG_ARCH_HAS_PMEM_API
 void arch_wb_cache_pmem(void *addr, size_t size);
 void dax_flush(struct dax_device *dax_dev, void *addr, size_t size)
@@ -603,6 +624,7 @@ struct dax_device *alloc_dax(void *private, const char *__host,
 	dax_add_host(dax_dev, host);
 	dax_dev->ops = ops;
 	dax_dev->private = private;
+	init_rwsem(&dax_dev->holder_rwsem);
 	if (flags & DAXDEV_F_SYNC)
 		set_dax_synchronous(dax_dev);

@@ -624,6 +646,33 @@ void put_dax(struct dax_device *dax_dev)
 }
 EXPORT_SYMBOL_GPL(put_dax);

+void dax_set_holder(struct dax_device *dax_dev, void *holder,
+		const struct dax_holder_operations *ops)
+{
+	if (!dax_dev)
+		return;
+	down_write(&dax_dev->holder_rwsem);
+	dax_dev->holder_data = holder;
+	dax_dev->holder_ops = ops;
+	up_write(&dax_dev->holder_rwsem);
+}
+EXPORT_SYMBOL_GPL(dax_set_holder);
+
+void *dax_get_holder(struct dax_device *dax_dev)
+{
+	void *holder_data;
+
+	if (!dax_dev)
+		return NULL;
+
+	down_read(&dax_dev->holder_rwsem);
+	holder_data = dax_dev->holder_data;
+	up_read(&dax_dev->holder_rwsem);
+
+	return holder_data;
+}
+EXPORT_SYMBOL_GPL(dax_get_holder);
+
 /**
  * dax_get_by_host() - temporary lookup mechanism for filesystem-dax
  * @host: alternate name for the device registered by a dax driver
diff --git a/include/linux/dax.h b/include/linux/dax.h
index b52f084aa643..6f4b5c97ceb0 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -38,10 +38,17 @@ struct dax_operations {
 	int (*zero_page_range)(struct dax_device *, pgoff_t, size_t);
 };

+struct dax_holder_operations {
+	int (*notify_failure)(struct dax_device *, loff_t, size_t, void *);
+};
+
 extern struct attribute_group dax_attribute_group;

 #if IS_ENABLED(CONFIG_DAX)
 struct dax_device *dax_get_by_host(const char *host);
+void dax_set_holder(struct dax_device *dax_dev, void *holder,
+		const struct dax_holder_operations *ops);
+void *dax_get_holder(struct dax_device *dax_dev);
 struct dax_device *alloc_dax(void *private, const char *host,
 		const struct dax_operations *ops, unsigned long flags);
 void put_dax(struct dax_device *dax_dev);
@@ -77,6 +84,14 @@ static inline struct dax_device *dax_get_by_host(const char *host)
 {
 	return NULL;
 }
+static inline void dax_set_holder(struct dax_device *dax_dev, void *holder,
+		const struct dax_holder_operations *ops)
+{
+}
+static inline void *dax_get_holder(struct dax_device *dax_dev)
+{
+	return NULL;
+}
 static inline struct dax_device *alloc_dax(void *private, const char *host,
 		const struct dax_operations *ops, unsigned long flags)
 {
@@ -226,6 +241,8 @@ size_t dax_copy_to_iter(struct dax_device *dax_dev, pgoff_t pgoff, void *addr,
 		size_t bytes, struct iov_iter *i);
 int dax_zero_page_range(struct dax_device *dax_dev, pgoff_t pgoff,
 			size_t nr_pages);
+int dax_holder_notify_failure(struct dax_device *dax_dev, loff_t offset,
+		size_t size, void *data);
 void dax_flush(struct dax_device *dax_dev, void *addr, size_t size);

 ssize_t dax_iomap_rw(struct kiocb *iocb, struct iov_iter *iter,
--
2.32.0




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

* [PATCH v5 3/9] mm: factor helpers for memory_failure_dev_pagemap
  2021-06-28  0:02 [PATCH v5 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
  2021-06-28  0:02 ` [PATCH v5 1/9] pagemap: Introduce ->memory_failure() Shiyang Ruan
  2021-06-28  0:02 ` [PATCH v5 2/9] dax: Introduce holder for dax_device Shiyang Ruan
@ 2021-06-28  0:02 ` Shiyang Ruan
  2021-06-28  0:02 ` [PATCH v5 4/9] pmem,mm: Implement ->memory_failure in pmem driver Shiyang Ruan
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Shiyang Ruan @ 2021-06-28  0:02 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, dm-devel
  Cc: darrick.wong, dan.j.williams, david, hch, agk, snitzer, rgoldwyn

memory_failure_dev_pagemap code is a bit complex before introduce RMAP
feature for fsdax.  So it is needed to factor some helper functions to
simplify these code.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
---
 mm/memory-failure.c | 101 +++++++++++++++++++++++++-------------------
 1 file changed, 57 insertions(+), 44 deletions(-)

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 0143d32bc666..068753911946 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1243,6 +1243,60 @@ static int try_to_split_thp_page(struct page *page, const char *msg)
 	return 0;
 }

+static void unmap_and_kill(struct list_head *to_kill, unsigned long pfn,
+		struct address_space *mapping, pgoff_t index, int flags)
+{
+	struct to_kill *tk;
+	unsigned long size = 0;
+
+	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()
+		 */
+		loff_t start = (index << PAGE_SHIFT) & ~(size - 1);
+
+		unmap_mapping_range(mapping, start, size, 0);
+	}
+
+	kill_procs(to_kill, flags & MF_MUST_KILL, false, pfn, flags);
+}
+
+static int mf_generic_kill_procs(unsigned long long pfn, int flags)
+{
+	struct page *page = pfn_to_page(pfn);
+	LIST_HEAD(to_kill);
+	dax_entry_t cookie;
+
+	/*
+	 * Prevent the inode from being freed while we are interrogating
+	 * the address_space, typically this would be handled by
+	 * lock_page(), but dax pages do not use the page lock. This
+	 * also prevents changes to the mapping of this pfn until
+	 * poison signaling is complete.
+	 */
+	cookie = dax_lock_page(page);
+	if (!cookie)
+		return -EBUSY;
+	/*
+	 * 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(page, &to_kill, flags & MF_ACTION_REQUIRED);
+
+	unmap_and_kill(&to_kill, pfn, page->mapping, page->index, flags);
+	dax_unlock_page(page, cookie);
+	return 0;
+}
+
 static int memory_failure_hugetlb(unsigned long pfn, int flags)
 {
 	struct page *p = pfn_to_page(pfn);
@@ -1323,13 +1377,8 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 		struct dev_pagemap *pgmap)
 {
 	struct page *page = pfn_to_page(pfn);
-	const bool unmap_success = true;
-	unsigned long size = 0;
-	struct to_kill *tk;
 	LIST_HEAD(tokill);
 	int rc = -EBUSY;
-	loff_t start;
-	dax_entry_t cookie;

 	if (flags & MF_COUNT_INCREASED)
 		/*
@@ -1343,20 +1392,9 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 		goto out;
 	}

-	/*
-	 * Prevent the inode from being freed while we are interrogating
-	 * the address_space, typically this would be handled by
-	 * lock_page(), but dax pages do not use the page lock. This
-	 * also prevents changes to the mapping of this pfn until
-	 * poison signaling is complete.
-	 */
-	cookie = dax_lock_page(page);
-	if (!cookie)
-		goto out;
-
 	if (hwpoison_filter(page)) {
 		rc = 0;
-		goto unlock;
+		goto out;
 	}

 	if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
@@ -1364,7 +1402,7 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 		 * TODO: Handle HMM pages which may need coordination
 		 * with device-side memory.
 		 */
-		goto unlock;
+		goto out;
 	}

 	/*
@@ -1373,32 +1411,7 @@ 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(page, &tokill, flags & MF_ACTION_REQUIRED);
-
-	list_for_each_entry(tk, &tokill, nd)
-		if (tk->size_shift)
-			size = max(size, 1UL << tk->size_shift);
-	if (size) {
-		/*
-		 * Unmap the largest mapping to avoid breaking up
-		 * device-dax mappings which are constant size. The
-		 * actual size of the mapping being torn down is
-		 * communicated in siginfo, see kill_proc()
-		 */
-		start = (page->index << PAGE_SHIFT) & ~(size - 1);
-		unmap_mapping_range(page->mapping, start, size, 0);
-	}
-	kill_procs(&tokill, flags & MF_MUST_KILL, !unmap_success, pfn, flags);
-	rc = 0;
-unlock:
-	dax_unlock_page(page, cookie);
+	mf_generic_kill_procs(pfn, flags);
 out:
 	/* drop pgmap ref acquired in caller */
 	put_dev_pagemap(pgmap);
--
2.32.0




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

* [PATCH v5 4/9] pmem,mm: Implement ->memory_failure in pmem driver
  2021-06-28  0:02 [PATCH v5 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (2 preceding siblings ...)
  2021-06-28  0:02 ` [PATCH v5 3/9] mm: factor helpers for memory_failure_dev_pagemap Shiyang Ruan
@ 2021-06-28  0:02 ` Shiyang Ruan
  2021-06-28  0:02 ` [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case Shiyang Ruan
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Shiyang Ruan @ 2021-06-28  0:02 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, dm-devel
  Cc: darrick.wong, dan.j.williams, david, hch, agk, snitzer, rgoldwyn

With dax_holder notify support, we are able to notify the memory failure
from pmem driver to upper layers.  If there is something not support in
the notify routine, memory_failure will fall back to the generic hanlder.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
---
 drivers/nvdimm/pmem.c | 13 +++++++++++++
 mm/memory-failure.c   | 14 ++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index ed10a8b66068..95d79dfb2fc2 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -364,9 +364,22 @@ static void pmem_release_disk(void *__pmem)
 	put_disk(pmem->disk);
 }

+static int pmem_pagemap_memory_failure(struct dev_pagemap *pgmap,
+		unsigned long pfn, unsigned long nr_pfns, int flags)
+{
+	struct pmem_device *pmem =
+			container_of(pgmap, struct pmem_device, pgmap);
+	loff_t offset = PFN_PHYS(pfn) - pmem->phys_addr - pmem->data_offset;
+
+	return dax_holder_notify_failure(pmem->dax_dev, offset,
+					 page_size(pfn_to_page(pfn)) * nr_pfns,
+					 &flags);
+}
+
 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 068753911946..30ce1e653c0a 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1411,6 +1411,20 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 	 */
 	SetPageHWPoison(page);

+	/*
+	 * Call driver's implementation to handle the memory failure, otherwise
+	 * fall back to generic handler.
+	 */
+	if (pgmap->ops->memory_failure) {
+		rc = pgmap->ops->memory_failure(pgmap, pfn, 1, flags);
+		/*
+		 * Fall back to generic handler too if operation is not
+		 * supported inside the driver/device/filesystem.
+		 */
+		if (rc != EOPNOTSUPP)
+			goto out;
+	}
+
 	mf_generic_kill_procs(pfn, flags);
 out:
 	/* drop pgmap ref acquired in caller */
--
2.32.0




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

* [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case
  2021-06-28  0:02 [PATCH v5 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (3 preceding siblings ...)
  2021-06-28  0:02 ` [PATCH v5 4/9] pmem,mm: Implement ->memory_failure in pmem driver Shiyang Ruan
@ 2021-06-28  0:02 ` Shiyang Ruan
  2021-06-28  3:23   ` kernel test robot
  2021-06-28 11:49   ` Matthew Wilcox
  2021-06-28  0:02 ` [PATCH v5 6/9] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
                   ` (3 subsequent siblings)
  8 siblings, 2 replies; 20+ messages in thread
From: Shiyang Ruan @ 2021-06-28  0:02 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, dm-devel
  Cc: darrick.wong, dan.j.williams, david, hch, agk, snitzer, rgoldwyn

This function is called at the end of RMAP routine, i.e. filesystem
recovery function.  The difference between mf_generic_kill_procs() is,
mf_dax_kill_procs() accepts file mapping and offset instead of struct
page.  It is because that different file mappings and offsets may share
the same page in fsdax mode.  So, it is called when filesystem RMAP
results are found.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
---
 fs/dax.c            | 31 +++++++++++++++--------
 include/linux/dax.h | 10 ++++++++
 include/linux/mm.h  | 10 ++++++++
 mm/memory-failure.c | 60 +++++++++++++++++++++++++++++++++------------
 4 files changed, 85 insertions(+), 26 deletions(-)

diff --git a/fs/dax.c b/fs/dax.c
index 62352cbcf0f4..7c5d8acd6bc5 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -389,6 +389,27 @@ static struct page *dax_busy_page(void *entry)
 	return NULL;
 }

+/*
+ * dax_load_pfn - Load pfn of the DAX entry corresponding to a page
+ * @mapping:	The file whose entry we want to load
+ * @index:	offset where the DAX entry located in
+ *
+ * Return:	pfn number of the DAX entry
+ */
+unsigned long dax_load_pfn(struct address_space *mapping, unsigned long index)
+{
+	XA_STATE(xas, &mapping->i_pages, index);
+	void *entry;
+	unsigned long pfn;
+
+	xas_lock_irq(&xas);
+	entry = xas_load(&xas);
+	pfn = dax_to_pfn(entry);
+	xas_unlock_irq(&xas);
+
+	return pfn;
+}
+
 /*
  * dax_lock_mapping_entry - Lock the DAX entry corresponding to a page
  * @page: The page whose entry we want to lock
@@ -789,16 +810,6 @@ static void *dax_insert_entry(struct xa_state *xas,
 	return entry;
 }

-static inline
-unsigned long pgoff_address(pgoff_t pgoff, struct vm_area_struct *vma)
-{
-	unsigned long address;
-
-	address = vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
-	VM_BUG_ON_VMA(address < vma->vm_start || address >= vma->vm_end, vma);
-	return address;
-}
-
 /* Walk all mappings of a given index of a file and writeprotect them */
 static void dax_entry_mkclean(struct address_space *mapping, pgoff_t index,
 		unsigned long pfn)
diff --git a/include/linux/dax.h b/include/linux/dax.h
index 6f4b5c97ceb0..fc0c80f09b79 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -165,6 +165,7 @@ int dax_writeback_mapping_range(struct address_space *mapping,

 struct page *dax_layout_busy_page(struct address_space *mapping);
 struct page *dax_layout_busy_page_range(struct address_space *mapping, loff_t start, loff_t end);
+unsigned long dax_load_pfn(struct address_space *mapping, unsigned long index);
 dax_entry_t dax_lock_page(struct page *page);
 void dax_unlock_page(struct page *page, dax_entry_t cookie);
 #else
@@ -259,6 +260,15 @@ static inline bool dax_mapping(struct address_space *mapping)
 {
 	return mapping->host && IS_DAX(mapping->host);
 }
+static inline unsigned long pgoff_address(pgoff_t pgoff,
+		struct vm_area_struct *vma)
+{
+	unsigned long address;
+
+	address = vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
+	VM_BUG_ON_VMA(address < vma->vm_start || address >= vma->vm_end, vma);
+	return address;
+}

 #ifdef CONFIG_DEV_DAX_HMEM_DEVICES
 void hmem_register_device(int target_nid, struct resource *r);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 8ae31622deef..3f101caec30e 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1187,6 +1187,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_FS_DAX) &&
+		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) &&
@@ -3081,6 +3089,8 @@ enum mf_flags {
 	MF_MUST_KILL = 1 << 2,
 	MF_SOFT_OFFLINE = 1 << 3,
 };
+extern int mf_dax_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 30ce1e653c0a..37ac15b223e0 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -120,6 +120,12 @@ static int hwpoison_filter_dev(struct page *p)
 	if (PageSlab(p))
 		return -EINVAL;

+	if (pfn_valid(page_to_pfn(p))) {
+		if (is_device_fsdax_page(p))
+			return 0;
+	} else
+		return -EINVAL;
+
 	mapping = page_mapping(p);
 	if (mapping == NULL || mapping->host == NULL)
 		return -EINVAL;
@@ -290,10 +296,9 @@ void shake_page(struct page *p, int access)
 }
 EXPORT_SYMBOL_GPL(shake_page);

-static unsigned long dev_pagemap_mapping_shift(struct page *page,
+static unsigned long dev_pagemap_mapping_shift(unsigned long address,
 		struct vm_area_struct *vma)
 {
-	unsigned long address = vma_address(page, vma);
 	pgd_t *pgd;
 	p4d_t *p4d;
 	pud_t *pud;
@@ -333,7 +338,7 @@ static unsigned long dev_pagemap_mapping_shift(struct page *page,
  * Schedule a process for later kill.
  * Uses GFP_ATOMIC allocations to avoid potential recursions in the VM.
  */
-static void add_to_kill(struct task_struct *tsk, struct page *p,
+static void add_to_kill(struct task_struct *tsk, struct page *p, pgoff_t pgoff,
 		       struct vm_area_struct *vma,
 		       struct list_head *to_kill)
 {
@@ -346,9 +351,14 @@ 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)) {
+		/* Since page->mapping is no more used for fsdax, we should
+		 * calculate the address in a fsdax way.
+		 */
+		if (is_device_fsdax_page(p))
+			tk->addr = pgoff_address(pgoff, vma);
+		tk->size_shift = dev_pagemap_mapping_shift(tk->addr, vma);
+	} else
 		tk->size_shift = page_shift(compound_head(p));

 	/*
@@ -496,7 +506,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, 0, vma, to_kill);
 		}
 	}
 	read_unlock(&tasklist_lock);
@@ -506,24 +516,20 @@ 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
@@ -532,7 +538,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, pgoff, vma, to_kill);
 		}
 	}
 	read_unlock(&tasklist_lock);
@@ -551,7 +557,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->index, tokill,
+				   force_early);
 }

 static const char *action_name[] = {
@@ -1297,6 +1304,27 @@ static int mf_generic_kill_procs(unsigned long long pfn, int flags)
 	return 0;
 }

+int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index, int flags)
+{
+	LIST_HEAD(to_kill);
+	/* load the pfn of the dax mapping file */
+	unsigned long pfn = dax_load_pfn(mapping, index);
+
+	/*
+	 * 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);
+
+	unmap_and_kill(&to_kill, pfn, mapping, index, flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mf_dax_kill_procs);
+
 static int memory_failure_hugetlb(unsigned long pfn, int flags)
 {
 	struct page *p = pfn_to_page(pfn);
--
2.32.0




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

* [PATCH v5 6/9] xfs: Implement ->corrupted_range() for XFS
  2021-06-28  0:02 [PATCH v5 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (4 preceding siblings ...)
  2021-06-28  0:02 ` [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case Shiyang Ruan
@ 2021-06-28  0:02 ` Shiyang Ruan
  2021-06-28  2:02   ` kernel test robot
  2021-06-28  3:05   ` kernel test robot
  2021-06-28  0:02 ` [PATCH v5 7/9] dm: Introduce ->rmap() to find bdev offset Shiyang Ruan
                   ` (2 subsequent siblings)
  8 siblings, 2 replies; 20+ messages in thread
From: Shiyang Ruan @ 2021-06-28  0:02 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, dm-devel
  Cc: darrick.wong, dan.j.williams, david, hch, agk, snitzer, rgoldwyn

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

If the rmap feature of XFS enabled, we can query it to find files and
metadata which are associated with the corrupt data.  For now all we do
is kill processes with that file mapped into their address spaces, but
future patches could actually do something about corrupt metadata.

After that, the memory failure 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@fujitsu.com>
---
 fs/xfs/xfs_fsops.c |   5 ++
 fs/xfs/xfs_mount.h |   1 +
 fs/xfs/xfs_super.c | 140 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 146 insertions(+)

diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index be9cf88d2ad7..e89ada33d8fc 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -551,6 +551,11 @@ xfs_do_force_shutdown(
 "Corruption of in-memory data detected.  Shutting down filesystem");
 		if (XFS_ERRLEVEL_HIGH <= xfs_error_level)
 			xfs_stack_trace();
+	} else if (flags & SHUTDOWN_CORRUPT_META) {
+		xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_CORRUPT,
+"Corruption of on-disk metadata detected.  Shutting down filesystem");
+		if (XFS_ERRLEVEL_HIGH <= xfs_error_level)
+			xfs_stack_trace();
 	} else if (logerror) {
 		xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_LOGERROR,
 			"Log I/O Error Detected. Shutting down filesystem");
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index bb67274ee23f..c62ccf3e07d0 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -276,6 +276,7 @@ void xfs_do_force_shutdown(struct xfs_mount *mp, int flags, char *fname,
 #define SHUTDOWN_LOG_IO_ERROR	0x0002	/* write attempt to the log failed */
 #define SHUTDOWN_FORCE_UMOUNT	0x0004	/* shutdown from a forced unmount */
 #define SHUTDOWN_CORRUPT_INCORE	0x0008	/* corrupt in-memory data structures */
+#define SHUTDOWN_CORRUPT_META	0x0010  /* corrupt metadata on device */

 /*
  * Flags for xfs_mountfs
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index a2dab05332ac..a986ddf3b248 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -36,11 +36,18 @@
 #include "xfs_bmap_item.h"
 #include "xfs_reflink.h"
 #include "xfs_pwork.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>
 #include <linux/fs_parser.h>
+#include <linux/mm.h>

+static const struct dax_holder_operations xfs_dax_holder_operations;
 static const struct super_operations xfs_super_operations;

 static struct kset *xfs_kset;		/* top-level xfs sysfs dir */
@@ -358,6 +365,7 @@ xfs_close_devices(

 		xfs_free_buftarg(mp->m_logdev_targp);
 		xfs_blkdev_put(logdev);
+		dax_set_holder(dax_logdev, NULL, NULL);
 		fs_put_dax(dax_logdev);
 	}
 	if (mp->m_rtdev_targp) {
@@ -366,9 +374,11 @@ xfs_close_devices(

 		xfs_free_buftarg(mp->m_rtdev_targp);
 		xfs_blkdev_put(rtdev);
+		dax_set_holder(dax_rtdev, NULL, NULL);
 		fs_put_dax(dax_rtdev);
 	}
 	xfs_free_buftarg(mp->m_ddev_targp);
+	dax_set_holder(dax_ddev, NULL, NULL);
 	fs_put_dax(dax_ddev);
 }

@@ -392,6 +402,7 @@ xfs_open_devices(
 	struct block_device	*logdev = NULL, *rtdev = NULL;
 	int			error;

+	dax_set_holder(dax_ddev, mp, &xfs_dax_holder_operations);
 	/*
 	 * Open real time and log devices - order is important.
 	 */
@@ -400,6 +411,9 @@ xfs_open_devices(
 		if (error)
 			goto out;
 		dax_logdev = fs_dax_get_by_bdev(logdev);
+		if (dax_logdev != dax_ddev)
+			dax_set_holder(dax_logdev, mp,
+				       &xfs_dax_holder_operations);
 	}

 	if (mp->m_rtname) {
@@ -414,6 +428,7 @@ xfs_open_devices(
 			goto out_close_rtdev;
 		}
 		dax_rtdev = fs_dax_get_by_bdev(rtdev);
+		dax_set_holder(dax_rtdev, mp, &xfs_dax_holder_operations);
 	}

 	/*
@@ -1076,6 +1091,131 @@ xfs_fs_free_cached_objects(
 	return xfs_reclaim_inodes_nr(XFS_M(sb), sc->nr_to_scan);
 }

+static int
+xfs_corrupt_helper(
+	struct xfs_btree_cur		*cur,
+	struct xfs_rmap_irec		*rec,
+	void				*data)
+{
+	struct xfs_inode		*ip;
+	struct address_space		*mapping;
+	int				error = 0;
+	int				*flags = data;
+
+	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
+	    (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
+		// TODO check and try to fix metadata
+		xfs_force_shutdown(cur->bc_mp, SHUTDOWN_CORRUPT_META);
+		return -EFSCORRUPTED;
+	}
+
+	/*
+	 * Get files that incore, filter out others that are not in use.
+	 */
+	error = xfs_iget(cur->bc_mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE,
+			0, &ip);
+	if (error)
+		return error;
+
+	mapping = VFS_I(ip)->i_mapping;
+
+#ifdef CONFIG_MEMORY_FAILURE
+	error = mf_dax_kill_procs(mapping, rec->rm_offset, *flags);
+#endif
+
+	// TODO try to fix data
+	xfs_irele(ip);
+
+	return error;
+}
+
+static loff_t
+xfs_dax_bdev_offset(
+	struct xfs_mount *mp,
+	struct dax_device *dax_dev,
+	loff_t disk_offset)
+{
+	struct block_device *bdev;
+
+	if (mp->m_ddev_targp->bt_daxdev == dax_dev)
+		bdev = mp->m_ddev_targp->bt_bdev;
+	else if (mp->m_logdev_targp->bt_daxdev == dax_dev)
+		bdev = mp->m_logdev_targp->bt_bdev;
+	else
+		bdev = mp->m_rtdev_targp->bt_bdev;
+
+	return disk_offset - (get_start_sect(bdev) << SECTOR_SHIFT);
+}
+
+static int
+xfs_dax_notify_failure(
+	struct dax_device	*dax_dev,
+	loff_t			offset,
+	size_t			len,
+	void			*data)
+{
+	struct xfs_mount	*mp = dax_get_holder(dax_dev);
+	struct xfs_trans	*tp = NULL;
+	struct xfs_btree_cur	*cur = NULL;
+	struct xfs_buf		*agf_bp = NULL;
+	struct xfs_rmap_irec	rmap_low, rmap_high;
+	loff_t 			bdev_offset = xfs_dax_bdev_offset(mp, dax_dev,
+								  offset);
+	xfs_fsblock_t		fsbno = XFS_B_TO_FSB(mp, bdev_offset);
+	xfs_filblks_t		bcnt = XFS_B_TO_FSB(mp, len);
+	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(mp, fsbno);
+	xfs_agblock_t		agbno = XFS_FSB_TO_AGBNO(mp, fsbno);
+	int			error = 0;
+
+	pr_warn("%s bno: %lld\n", __func__, bdev_offset >> SECTOR_SHIFT);
+
+	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_daxdev == dax_dev) {
+		xfs_warn(mp, "corrupted_range support not available for realtime device!");
+		return -EOPNOTSUPP;
+	}
+	if (mp->m_logdev_targp && mp->m_logdev_targp->bt_daxdev == dax_dev &&
+	    mp->m_logdev_targp != mp->m_ddev_targp) {
+		xfs_err(mp, "ondisk log corrupt, shutting down fs!");
+		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_META);
+		return -EFSCORRUPTED;
+	}
+
+	if (!xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+		xfs_warn(mp, "corrupted_range needs rmapbt enabled!");
+		return -EOPNOTSUPP;
+	}
+
+	error = xfs_trans_alloc_empty(mp, &tp);
+	if (error)
+		return error;
+
+	error = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
+	if (error)
+		goto out_cancel_tp;
+
+	cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, agno);
+
+	/* Construct a range for rmap query */
+	memset(&rmap_low, 0, sizeof(rmap_low));
+	memset(&rmap_high, 0xFF, sizeof(rmap_high));
+	rmap_low.rm_startblock = rmap_high.rm_startblock = agbno;
+	rmap_low.rm_blockcount = rmap_high.rm_blockcount = bcnt;
+
+	error = xfs_rmap_query_range(cur, &rmap_low, &rmap_high,
+				     xfs_corrupt_helper, data);
+
+	xfs_btree_del_cursor(cur, error);
+	xfs_trans_brelse(tp, agf_bp);
+
+out_cancel_tp:
+	xfs_trans_cancel(tp);
+	return error;
+}
+
+static const struct dax_holder_operations xfs_dax_holder_operations = {
+	.notify_failure = xfs_dax_notify_failure,
+};
+
 static const struct super_operations xfs_super_operations = {
 	.alloc_inode		= xfs_fs_alloc_inode,
 	.destroy_inode		= xfs_fs_destroy_inode,
--
2.32.0




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

* [PATCH v5 7/9] dm: Introduce ->rmap() to find bdev offset
  2021-06-28  0:02 [PATCH v5 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (5 preceding siblings ...)
  2021-06-28  0:02 ` [PATCH v5 6/9] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
@ 2021-06-28  0:02 ` Shiyang Ruan
  2021-06-28  0:02 ` [PATCH v5 8/9] md: Implement dax_holder_operations Shiyang Ruan
  2021-06-28  0:02 ` [PATCH v5 9/9] fs/dax: Remove useless functions Shiyang Ruan
  8 siblings, 0 replies; 20+ messages in thread
From: Shiyang Ruan @ 2021-06-28  0:02 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, dm-devel
  Cc: darrick.wong, dan.j.williams, david, hch, agk, snitzer, rgoldwyn

Pmem device could be a target of mapped device.  In order to find out
the global location on a mapped device, we introduce this to translate
offset from target device to mapped 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@fujitsu.com>
---
 block/genhd.c                 | 30 ++++++++++++++++++++++++++++++
 drivers/md/dm-linear.c        | 20 ++++++++++++++++++++
 include/linux/device-mapper.h |  5 +++++
 include/linux/genhd.h         |  1 +
 4 files changed, 56 insertions(+)

diff --git a/block/genhd.c b/block/genhd.c
index 9f8cb7beaad1..75834bd057df 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -718,6 +718,36 @@ struct block_device *bdget_disk(struct gendisk *disk, int partno)
 	return bdev;
 }

+/**
+ * bdget_disk_sector - get block device by given sector number
+ * @disk: gendisk of interest
+ * @sector: sector number
+ *
+ * RETURNS: the found block device where sector locates in
+ */
+struct block_device *bdget_disk_sector(struct gendisk *disk, sector_t sector)
+{
+	struct block_device *part = NULL, *p;
+	unsigned long idx;
+
+	rcu_read_lock();
+	xa_for_each(&disk->part_tbl, idx, p) {
+		if (p->bd_partno == 0)
+			continue;
+		if (p->bd_start_sect <= sector &&
+			sector < p->bd_start_sect + bdev_nr_sectors(p)) {
+			part = p;
+			break;
+		}
+	}
+	rcu_read_unlock();
+	if (!part)
+		part = disk->part0;
+
+	return bdget_disk(disk, part->bd_partno);
+}
+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/md/dm-linear.c b/drivers/md/dm-linear.c
index 92db0f5e7f28..f9f9bc765ba7 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -5,6 +5,7 @@
  */

 #include "dm.h"
+#include "dm-core.h"
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/blkdev.h>
@@ -119,6 +120,24 @@ static void linear_status(struct dm_target *ti, status_type_t type,
 	}
 }

+static int linear_rmap(struct dm_target *ti, sector_t offset,
+		       rmap_callout_fn fn, void *data)
+{
+	struct linear_c *lc = (struct linear_c *) ti->private;
+	struct mapped_device *md = ti->table->md;
+	struct block_device *bdev;
+	sector_t disk_sect = offset - dm_target_offset(ti, lc->start);
+	int rc = -ENODEV;
+
+	bdev = bdget_disk_sector(md->disk, offset);
+	if (!bdev)
+		return rc;
+
+	rc = fn(ti, bdev, disk_sect, data);
+	bdput(bdev);
+	return rc;
+}
+
 static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
 {
 	struct linear_c *lc = (struct linear_c *) ti->private;
@@ -236,6 +255,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 ff700fb6ce1d..89a893565407 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -58,6 +58,10 @@ 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 int (*rmap_callout_fn) (struct dm_target *ti, struct block_device *bdev,
+				sector_t sect, void *data);
+typedef int (*dm_rmap_fn) (struct dm_target *ti, sector_t offset,
+			   rmap_callout_fn fn, void *data);
 typedef int (*dm_clone_and_map_request_fn) (struct dm_target *ti,
 					    struct request *rq,
 					    union map_info *map_context,
@@ -184,6 +188,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;
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 6fc26f7bdf71..2ad70c02c343 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -219,6 +219,7 @@ static inline void add_disk_no_queue_reg(struct gendisk *disk)

 extern void del_gendisk(struct gendisk *gp);
 extern struct block_device *bdget_disk(struct gendisk *disk, int partno);
+extern struct block_device *bdget_disk_sector(struct gendisk *disk, sector_t sector);

 void set_disk_ro(struct gendisk *disk, bool read_only);

--
2.32.0




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

* [PATCH v5 8/9] md: Implement dax_holder_operations
  2021-06-28  0:02 [PATCH v5 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (6 preceding siblings ...)
  2021-06-28  0:02 ` [PATCH v5 7/9] dm: Introduce ->rmap() to find bdev offset Shiyang Ruan
@ 2021-06-28  0:02 ` Shiyang Ruan
  2021-06-28  0:02 ` [PATCH v5 9/9] fs/dax: Remove useless functions Shiyang Ruan
  8 siblings, 0 replies; 20+ messages in thread
From: Shiyang Ruan @ 2021-06-28  0:02 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, dm-devel
  Cc: darrick.wong, dan.j.williams, david, hch, agk, snitzer, rgoldwyn

This is the case where the holder represents a mapped device, or a list
of mapped devices more exactly(because it is possible to create more
than one mapped device on one pmem device).

Find out which mapped device the offset belongs to, and translate the
offset from target device to mapped device.  When it is done, call
dax_corrupted_range() for the holder of this mapped device.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
---
 drivers/md/dm.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 125 insertions(+), 1 deletion(-)

diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index ca2aedd8ee7d..11c9de0eca6d 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -749,7 +749,11 @@ static void dm_put_live_table_fast(struct mapped_device *md) __releases(RCU)
 }

 static char *_dm_claim_ptr = "I belong to device-mapper";
-
+static const struct dax_holder_operations dm_dax_holder_ops;
+struct dm_holder {
+	struct list_head list;
+	struct mapped_device *md;
+};
 /*
  * Open a table device so we can use it as a map destination.
  */
@@ -757,6 +761,8 @@ static int open_table_device(struct table_device *td, dev_t dev,
 			     struct mapped_device *md)
 {
 	struct block_device *bdev;
+	struct list_head *holders;
+	struct dm_holder *holder;

 	int r;

@@ -774,6 +780,19 @@ static int open_table_device(struct table_device *td, dev_t dev,

 	td->dm_dev.bdev = bdev;
 	td->dm_dev.dax_dev = dax_get_by_host(bdev->bd_disk->disk_name);
+	if (!td->dm_dev.dax_dev)
+		return 0;
+
+	holders = dax_get_holder(td->dm_dev.dax_dev);
+	if (!holders) {
+		holders = kmalloc(sizeof(*holders), GFP_KERNEL);
+		INIT_LIST_HEAD(holders);
+		dax_set_holder(td->dm_dev.dax_dev, holders, &dm_dax_holder_ops);
+	}
+	holder = kmalloc(sizeof(*holder), GFP_KERNEL);
+	holder->md = md;
+	list_add_tail(&holder->list, holders);
+
 	return 0;
 }

@@ -782,9 +801,27 @@ static int open_table_device(struct table_device *td, dev_t dev,
  */
 static void close_table_device(struct table_device *td, struct mapped_device *md)
 {
+	struct list_head *holders;
+	struct dm_holder *holder, *n;
+
 	if (!td->dm_dev.bdev)
 		return;

+	holders = dax_get_holder(td->dm_dev.dax_dev);
+	if (holders) {
+		list_for_each_entry_safe(holder, n, holders, list) {
+			if (holder->md == md) {
+				list_del(&holder->list);
+				kfree(holder);
+			}
+		}
+		if (list_empty(holders)) {
+			kfree(holders);
+			/* unset dax_device's holder_data */
+			dax_set_holder(td->dm_dev.dax_dev, NULL, NULL);
+		}
+	}
+
 	bd_unlink_disk_holder(td->dm_dev.bdev, dm_disk(md));
 	blkdev_put(td->dm_dev.bdev, td->dm_dev.mode | FMODE_EXCL);
 	put_dax(td->dm_dev.dax_dev);
@@ -1235,6 +1272,89 @@ static int dm_dax_zero_page_range(struct dax_device *dax_dev, pgoff_t pgoff,
 	return ret;
 }

+#if IS_ENABLED(CONFIG_DAX_DRIVER)
+struct corrupted_hit_info {
+	struct dax_device *dax_dev;
+	sector_t offset;
+};
+
+static int dm_blk_corrupted_hit(struct dm_target *ti, struct dm_dev *dev,
+				sector_t start, sector_t count, void *data)
+{
+	struct corrupted_hit_info *bc = data;
+
+	return bc->dax_dev == (void *)dev->dax_dev &&
+			(start <= bc->offset && bc->offset < start + count);
+}
+
+struct corrupted_do_info {
+	size_t length;
+	void *data;
+};
+
+static int dm_blk_corrupted_do(struct dm_target *ti, struct block_device *bdev,
+			       sector_t sector, void *data)
+{
+	struct mapped_device *md = ti->table->md;
+	struct corrupted_do_info *bc = data;
+
+	return dax_holder_notify_failure(md->dax_dev, to_bytes(sector),
+					 bc->length, bc->data);
+}
+
+static int dm_dax_notify_failure_one(struct mapped_device *md,
+				     struct dax_device *dax_dev,
+				     loff_t offset, size_t length, void *data)
+{
+	struct dm_table *map;
+	struct dm_target *ti;
+	sector_t sect = to_sector(offset);
+	struct corrupted_hit_info hi = {dax_dev, sect};
+	struct corrupted_do_info di = {length, data};
+	int srcu_idx, i, rc = -ENODEV;
+
+	map = dm_get_live_table(md, &srcu_idx);
+	if (!map)
+		return rc;
+
+	/*
+	 * find the target device, and then translate the offset of this target
+	 * to the whole mapped device.
+	 */
+	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))
+			continue;
+		if (!ti->type->iterate_devices(ti, dm_blk_corrupted_hit, &hi))
+			continue;
+
+		rc = ti->type->rmap(ti, sect, dm_blk_corrupted_do, &di);
+		break;
+	}
+
+	dm_put_live_table(md, srcu_idx);
+	return rc;
+}
+
+static int dm_dax_notify_failure(struct dax_device *dax_dev,
+				 loff_t offset, size_t length, void *data)
+{
+	struct dm_holder *holder;
+	struct list_head *holders = dax_get_holder(dax_dev);
+	int rc = -ENODEV;
+
+	list_for_each_entry(holder, holders, list) {
+		rc = dm_dax_notify_failure_one(holder->md, dax_dev, offset,
+					       length, data);
+		if (rc != -ENODEV)
+			break;
+	}
+	return rc;
+}
+#else
+#define dm_dax_notify_failure NULL
+#endif
+
 /*
  * A target may call dm_accept_partial_bio only from the map routine.  It is
  * allowed for all bio types except REQ_PREFLUSH, REQ_OP_ZONE_RESET,
@@ -3157,6 +3277,10 @@ static const struct dax_operations dm_dax_ops = {
 	.zero_page_range = dm_dax_zero_page_range,
 };

+static const struct dax_holder_operations dm_dax_holder_ops = {
+	.notify_failure = dm_dax_notify_failure,
+};
+
 /*
  * module hooks
  */
--
2.32.0




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

* [PATCH v5 9/9] fs/dax: Remove useless functions
  2021-06-28  0:02 [PATCH v5 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (7 preceding siblings ...)
  2021-06-28  0:02 ` [PATCH v5 8/9] md: Implement dax_holder_operations Shiyang Ruan
@ 2021-06-28  0:02 ` Shiyang Ruan
  2021-07-19 15:18   ` Christoph Hellwig
  8 siblings, 1 reply; 20+ messages in thread
From: Shiyang Ruan @ 2021-06-28  0:02 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, dm-devel
  Cc: darrick.wong, dan.j.williams, david, hch, agk, snitzer, rgoldwyn

Since owner tracking is triggerred by pmem device, these functions are
useless.  So remove them.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
---
 fs/dax.c | 46 ----------------------------------------------
 1 file changed, 46 deletions(-)

diff --git a/fs/dax.c b/fs/dax.c
index 7c5d8acd6bc5..cee8e61d1025 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -334,48 +334,6 @@ static unsigned long dax_end_pfn(void *entry)
 	for (pfn = dax_to_pfn(entry); \
 			pfn < dax_end_pfn(entry); pfn++)

-/*
- * TODO: for reflink+dax we need a way to associate a single page with
- * multiple address_space instances at different linear_page_index()
- * offsets.
- */
-static void dax_associate_entry(void *entry, struct address_space *mapping,
-		struct vm_area_struct *vma, unsigned long address)
-{
-	unsigned long size = dax_entry_size(entry), pfn, index;
-	int i = 0;
-
-	if (IS_ENABLED(CONFIG_FS_DAX_LIMITED))
-		return;
-
-	index = linear_page_index(vma, address & ~(size - 1));
-	for_each_mapped_pfn(entry, pfn) {
-		struct page *page = pfn_to_page(pfn);
-
-		WARN_ON_ONCE(page->mapping);
-		page->mapping = mapping;
-		page->index = index + i++;
-	}
-}
-
-static void dax_disassociate_entry(void *entry, struct address_space *mapping,
-		bool trunc)
-{
-	unsigned long pfn;
-
-	if (IS_ENABLED(CONFIG_FS_DAX_LIMITED))
-		return;
-
-	for_each_mapped_pfn(entry, pfn) {
-		struct page *page = pfn_to_page(pfn);
-
-		WARN_ON_ONCE(trunc && page_ref_count(page) > 1);
-		WARN_ON_ONCE(page->mapping && page->mapping != mapping);
-		page->mapping = NULL;
-		page->index = 0;
-	}
-}
-
 static struct page *dax_busy_page(void *entry)
 {
 	unsigned long pfn;
@@ -554,7 +512,6 @@ static void *grab_mapping_entry(struct xa_state *xas,
 			xas_lock_irq(xas);
 		}

-		dax_disassociate_entry(entry, mapping, false);
 		xas_store(xas, NULL);	/* undo the PMD join */
 		dax_wake_entry(xas, entry, WAKE_ALL);
 		mapping->nrpages -= PG_PMD_NR;
@@ -691,7 +648,6 @@ static int __dax_invalidate_entry(struct address_space *mapping,
 	    (xas_get_mark(&xas, PAGECACHE_TAG_DIRTY) ||
 	     xas_get_mark(&xas, PAGECACHE_TAG_TOWRITE)))
 		goto out;
-	dax_disassociate_entry(entry, mapping, trunc);
 	xas_store(&xas, NULL);
 	mapping->nrpages -= 1UL << dax_entry_order(entry);
 	ret = 1;
@@ -785,8 +741,6 @@ static void *dax_insert_entry(struct xa_state *xas,
 	if (dax_is_zero_entry(entry) || dax_is_empty_entry(entry)) {
 		void *old;

-		dax_disassociate_entry(entry, mapping, false);
-		dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address);
 		/*
 		 * Only swap our new entry into the page cache if the current
 		 * entry is a zero page or an empty entry.  If a normal PTE or
--
2.32.0




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

* Re: [PATCH v5 6/9] xfs: Implement ->corrupted_range() for XFS
  2021-06-28  0:02 ` [PATCH v5 6/9] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
@ 2021-06-28  2:02   ` kernel test robot
  2021-06-28  3:05   ` kernel test robot
  1 sibling, 0 replies; 20+ messages in thread
From: kernel test robot @ 2021-06-28  2:02 UTC (permalink / raw)
  To: Shiyang Ruan, linux-kernel, linux-xfs, nvdimm, linux-mm,
	linux-fsdevel, dm-devel
  Cc: kbuild-all, clang-built-linux, darrick.wong, dan.j.williams, david, hch

[-- Attachment #1: Type: text/plain, Size: 3188 bytes --]

Hi Shiyang,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v5.13]
[cannot apply to hnaz-linux-mm/master xfs-linux/for-next dm/for-next next-20210625]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Shiyang-Ruan/fsdax-introduce-fs-query-to-support-reflink/20210628-080346
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 62fb9874f5da54fdb243003b386128037319b219
config: x86_64-randconfig-a002-20210628 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 4c92e31dd0f1bd152eda883af20ff7fbcaa14945)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # https://github.com/0day-ci/linux/commit/6833b9441ae13413e20968838cd8de34b8c69ed4
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Shiyang-Ruan/fsdax-introduce-fs-query-to-support-reflink/20210628-080346
        git checkout 6833b9441ae13413e20968838cd8de34b8c69ed4
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   fs/xfs/xfs_super.c:1103:10: warning: unused variable 'flags' [-Wunused-variable]
           int                             *flags = data;
                                            ^
>> fs/xfs/xfs_super.c:1101:25: warning: variable 'mapping' set but not used [-Wunused-but-set-variable]
           struct address_space            *mapping;
                                            ^
   2 warnings generated.


vim +/mapping +1101 fs/xfs/xfs_super.c

  1093	
  1094	static int
  1095	xfs_corrupt_helper(
  1096		struct xfs_btree_cur		*cur,
  1097		struct xfs_rmap_irec		*rec,
  1098		void				*data)
  1099	{
  1100		struct xfs_inode		*ip;
> 1101		struct address_space		*mapping;
  1102		int				error = 0;
  1103		int				*flags = data;
  1104	
  1105		if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
  1106		    (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
  1107			// TODO check and try to fix metadata
  1108			xfs_force_shutdown(cur->bc_mp, SHUTDOWN_CORRUPT_META);
  1109			return -EFSCORRUPTED;
  1110		}
  1111	
  1112		/*
  1113		 * Get files that incore, filter out others that are not in use.
  1114		 */
  1115		error = xfs_iget(cur->bc_mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE,
  1116				0, &ip);
  1117		if (error)
  1118			return error;
  1119	
  1120		mapping = VFS_I(ip)->i_mapping;
  1121	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 45837 bytes --]

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

* Re: [PATCH v5 6/9] xfs: Implement ->corrupted_range() for XFS
  2021-06-28  0:02 ` [PATCH v5 6/9] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
  2021-06-28  2:02   ` kernel test robot
@ 2021-06-28  3:05   ` kernel test robot
  1 sibling, 0 replies; 20+ messages in thread
From: kernel test robot @ 2021-06-28  3:05 UTC (permalink / raw)
  To: Shiyang Ruan, linux-kernel, linux-xfs, nvdimm, linux-mm,
	linux-fsdevel, dm-devel
  Cc: kbuild-all, darrick.wong, dan.j.williams, david, hch

[-- Attachment #1: Type: text/plain, Size: 2131 bytes --]

Hi Shiyang,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v5.13]
[cannot apply to hnaz-linux-mm/master xfs-linux/for-next dm/for-next next-20210625]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Shiyang-Ruan/fsdax-introduce-fs-query-to-support-reflink/20210628-080346
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 62fb9874f5da54fdb243003b386128037319b219
config: i386-randconfig-a012-20210628 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce (this is a W=1 build):
        # https://github.com/0day-ci/linux/commit/6833b9441ae13413e20968838cd8de34b8c69ed4
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Shiyang-Ruan/fsdax-introduce-fs-query-to-support-reflink/20210628-080346
        git checkout 6833b9441ae13413e20968838cd8de34b8c69ed4
        # save the attached .config to linux build tree
        mkdir build_dir
        make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   ld: fs/xfs/xfs_super.o: in function `xfs_dax_notify_failure':
>> xfs_super.c:(.text+0xc35): undefined reference to `dax_get_holder'
   ld: fs/xfs/xfs_super.o: in function `xfs_open_devices':
>> xfs_super.c:(.text+0xd57): undefined reference to `dax_set_holder'
>> ld: xfs_super.c:(.text+0xdb8): undefined reference to `dax_set_holder'
   ld: fs/xfs/xfs_super.o: in function `xfs_close_devices':
   xfs_super.c:(.text+0xf0a): undefined reference to `dax_set_holder'
   ld: xfs_super.c:(.text+0xf3d): undefined reference to `dax_set_holder'
   ld: xfs_super.c:(.text+0xf59): undefined reference to `dax_set_holder'

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 40041 bytes --]

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

* Re: [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case
  2021-06-28  0:02 ` [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case Shiyang Ruan
@ 2021-06-28  3:23   ` kernel test robot
  2021-06-28 11:49   ` Matthew Wilcox
  1 sibling, 0 replies; 20+ messages in thread
From: kernel test robot @ 2021-06-28  3:23 UTC (permalink / raw)
  To: Shiyang Ruan, linux-kernel, linux-xfs, nvdimm, linux-mm,
	linux-fsdevel, dm-devel
  Cc: kbuild-all, clang-built-linux, darrick.wong, dan.j.williams, david, hch

[-- Attachment #1: Type: text/plain, Size: 2964 bytes --]

Hi Shiyang,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v5.13 next-20210625]
[cannot apply to hnaz-linux-mm/master xfs-linux/for-next dm/for-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Shiyang-Ruan/fsdax-introduce-fs-query-to-support-reflink/20210628-080346
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 62fb9874f5da54fdb243003b386128037319b219
config: x86_64-randconfig-a006-20210628 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 4c92e31dd0f1bd152eda883af20ff7fbcaa14945)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # https://github.com/0day-ci/linux/commit/dc92f6aa75da7bf5c6f974645a157759691ca36a
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Shiyang-Ruan/fsdax-introduce-fs-query-to-support-reflink/20210628-080346
        git checkout dc92f6aa75da7bf5c6f974645a157759691ca36a
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> mm/memory-failure.c:1325:22: error: implicit declaration of function 'dax_load_pfn' [-Werror,-Wimplicit-function-declaration]
           unsigned long pfn = dax_load_pfn(mapping, index);
                               ^
   1 error generated.


vim +/dax_load_pfn +1325 mm/memory-failure.c

  1320	
  1321	int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index, int flags)
  1322	{
  1323		LIST_HEAD(to_kill);
  1324		/* load the pfn of the dax mapping file */
> 1325		unsigned long pfn = dax_load_pfn(mapping, index);
  1326	
  1327		/*
  1328		 * Unlike System-RAM there is no possibility to swap in a
  1329		 * different physical page at a given virtual address, so all
  1330		 * userspace consumption of ZONE_DEVICE memory necessitates
  1331		 * SIGBUS (i.e. MF_MUST_KILL)
  1332		 */
  1333		flags |= MF_ACTION_REQUIRED | MF_MUST_KILL;
  1334		collect_procs_file(pfn_to_page(pfn), mapping, index, &to_kill,
  1335				   flags & MF_ACTION_REQUIRED);
  1336	
  1337		unmap_and_kill(&to_kill, pfn, mapping, index, flags);
  1338		return 0;
  1339	}
  1340	EXPORT_SYMBOL_GPL(mf_dax_kill_procs);
  1341	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 45536 bytes --]

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

* Re: [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case
  2021-06-28  0:02 ` [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case Shiyang Ruan
  2021-06-28  3:23   ` kernel test robot
@ 2021-06-28 11:49   ` Matthew Wilcox
  2021-06-29  7:49     ` ruansy.fnst
  1 sibling, 1 reply; 20+ messages in thread
From: Matthew Wilcox @ 2021-06-28 11:49 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel,
	dm-devel, darrick.wong, dan.j.williams, david, hch, agk, snitzer,
	rgoldwyn

On Mon, Jun 28, 2021 at 08:02:14AM +0800, Shiyang Ruan wrote:
> +/*
> + * dax_load_pfn - Load pfn of the DAX entry corresponding to a page
> + * @mapping:	The file whose entry we want to load
> + * @index:	offset where the DAX entry located in
> + *
> + * Return:	pfn number of the DAX entry
> + */

This is an externally visible function; why not add the second '*' and
make this kernel-doc?

> +unsigned long dax_load_pfn(struct address_space *mapping, unsigned long index)
> +{
> +	XA_STATE(xas, &mapping->i_pages, index);
> +	void *entry;
> +	unsigned long pfn;
> +
> +	xas_lock_irq(&xas);
> +	entry = xas_load(&xas);
> +	pfn = dax_to_pfn(entry);
> +	xas_unlock_irq(&xas);

Why do you need the i_pages lock to do this?  is the rcu_read_lock()
insufficient?  For that matter, why use the xas functions?  Why not
simply:

	void *entry = xa_load(&mapping->i_pages, index);
	return dax_to_pfn(entry);

Looking at it more though, how do you know this is a PFN entry?
It could be locked, for example.  Or the zero page, or empty.

But I think this is unnecessary; why not just pass the PFN into
mf_dax_kill_procs?


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

* RE: [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case
  2021-06-28 11:49   ` Matthew Wilcox
@ 2021-06-29  7:49     ` ruansy.fnst
  2021-06-29 11:46       ` Matthew Wilcox
  0 siblings, 1 reply; 20+ messages in thread
From: ruansy.fnst @ 2021-06-29  7:49 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel,
	dm-devel, darrick.wong, dan.j.williams, david, hch, agk, snitzer,
	rgoldwyn


> -----Original Message-----
> From: Matthew Wilcox <willy@infradead.org>
> Subject: Re: [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case
> 
> On Mon, Jun 28, 2021 at 08:02:14AM +0800, Shiyang Ruan wrote:
> > +/*
> > + * dax_load_pfn - Load pfn of the DAX entry corresponding to a page
> > + * @mapping:	The file whose entry we want to load
> > + * @index:	offset where the DAX entry located in
> > + *
> > + * Return:	pfn number of the DAX entry
> > + */
> 
> This is an externally visible function; why not add the second '*' and make this
> kernel-doc?

I'll fix this and add kernel-doc.

> 
> > +unsigned long dax_load_pfn(struct address_space *mapping, unsigned
> > +long index) {
> > +	XA_STATE(xas, &mapping->i_pages, index);
> > +	void *entry;
> > +	unsigned long pfn;
> > +
> > +	xas_lock_irq(&xas);
> > +	entry = xas_load(&xas);
> > +	pfn = dax_to_pfn(entry);
> > +	xas_unlock_irq(&xas);
> 
> Why do you need the i_pages lock to do this?  is the rcu_read_lock() insufficient?

I was misusing these locks, not very filmier with them...

So, I think I should learn from dax_lock_page(): rcu_read_lock(), xas_load(&xas, index), and then wait_entry_unlocked(), finally get an unlocked entry, translate to PFN and return.

> For that matter, why use the xas functions?  Why not
> simply:
> 
> 	void *entry = xa_load(&mapping->i_pages, index);
> 	return dax_to_pfn(entry);
> 
> Looking at it more though, how do you know this is a PFN entry?
> It could be locked, for example.  Or the zero page, or empty.

Yes, I didn't take these in consideration.  If this file hasn't been mmapped and accessed, I can't get its PFN rightly.

> 
> But I think this is unnecessary; why not just pass the PFN into mf_dax_kill_procs?

Because the mf_dax_kill_procs() is called in filesystem recovery function, which is at the end of the RMAP routine.  And the PFN has been translated to disk offset in pmem driver in order to do RMAP search in filesystem.  So, if we have to pass it, every function in this routine needs to add an argument for this PFN.  I was hoping I can avoid passing PFN through the whole stack with the help of this dax_load_pfn().

So, based on the above, if a file hasn't been mmapped and accessed, we can't get the right PFN number, which also means no process is associated with this PFN.  Then we don't have to kill any process any more.  Just return with an error code.  mf_dax_kill_porcess() can also return immediately.  How do you think?


--
Thanks,
Ruan.


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

* Re: [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case
  2021-06-29  7:49     ` ruansy.fnst
@ 2021-06-29 11:46       ` Matthew Wilcox
  2021-07-16  6:32         ` ruansy.fnst
  0 siblings, 1 reply; 20+ messages in thread
From: Matthew Wilcox @ 2021-06-29 11:46 UTC (permalink / raw)
  To: ruansy.fnst
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel,
	dm-devel, darrick.wong, dan.j.williams, david, hch, agk, snitzer,
	rgoldwyn

On Tue, Jun 29, 2021 at 07:49:24AM +0000, ruansy.fnst@fujitsu.com wrote:
> > But I think this is unnecessary; why not just pass the PFN into mf_dax_kill_procs?
> 
> Because the mf_dax_kill_procs() is called in filesystem recovery function, which is at the end of the RMAP routine.  And the PFN has been translated to disk offset in pmem driver in order to do RMAP search in filesystem.  So, if we have to pass it, every function in this routine needs to add an argument for this PFN.  I was hoping I can avoid passing PFN through the whole stack with the help of this dax_load_pfn().

OK, I think you need to create:

struct memory_failure {
	phys_addr_t start;
	phys_addr_t end;
	unsigned long flags;
};

(a memory failure might not be an entire page, so working in pfns isn't
the best approach)

Then that can be passed to ->memory_failure() and then deeper to
->notify_failure(), and finally into xfs_corrupt_helper().

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

* RE: [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case
  2021-06-29 11:46       ` Matthew Wilcox
@ 2021-07-16  6:32         ` ruansy.fnst
  0 siblings, 0 replies; 20+ messages in thread
From: ruansy.fnst @ 2021-07-16  6:32 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel,
	dm-devel, darrick.wong, dan.j.williams, david, hch, agk, snitzer,
	rgoldwyn

> From: Matthew Wilcox <willy@infradead.org>
> Subject: Re: [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case
> 
> On Tue, Jun 29, 2021 at 07:49:24AM +0000, ruansy.fnst@fujitsu.com wrote:
> > > But I think this is unnecessary; why not just pass the PFN into
> mf_dax_kill_procs?
> >
> > Because the mf_dax_kill_procs() is called in filesystem recovery function,
> which is at the end of the RMAP routine.  And the PFN has been translated to
> disk offset in pmem driver in order to do RMAP search in filesystem.  So, if we
> have to pass it, every function in this routine needs to add an argument for this
> PFN.  I was hoping I can avoid passing PFN through the whole stack with the
> help of this dax_load_pfn().
> 
> OK, I think you need to create:
> 
> struct memory_failure {
> 	phys_addr_t start;
> 	phys_addr_t end;
> 	unsigned long flags;
> };
> 
> (a memory failure might not be an entire page, so working in pfns isn't the best
> approach)

Do you mean the range of memory failure may less than one page size?  I found those memory_failure* functions are using pfn as their parameter.  So in which case it could be less than one page size?


--
Thanks,
Ruan.

> 
> Then that can be passed to ->memory_failure() and then deeper to
> ->notify_failure(), and finally into xfs_corrupt_helper().

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

* Re: [PATCH v5 2/9] dax: Introduce holder for dax_device
  2021-06-28  0:02 ` [PATCH v5 2/9] dax: Introduce holder for dax_device Shiyang Ruan
@ 2021-07-19 15:17   ` Christoph Hellwig
  2021-07-20 10:37     ` ruansy.fnst
  0 siblings, 1 reply; 20+ messages in thread
From: Christoph Hellwig @ 2021-07-19 15:17 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel,
	dm-devel, darrick.wong, dan.j.williams, david, hch, agk, snitzer,
	rgoldwyn

On Mon, Jun 28, 2021 at 08:02:11AM +0800, Shiyang Ruan wrote:
> +int dax_holder_notify_failure(struct dax_device *dax_dev, loff_t offset,
> +			      size_t size, void *data)
> +{
> +	int rc = -ENXIO;
> +	if (!dax_dev)
> +		return rc;
> +
> +	if (dax_dev->holder_data) {
> +		rc = dax_dev->holder_ops->notify_failure(dax_dev, offset,
> +							 size, data);
> +		if (rc == -ENODEV)
> +			rc = -ENXIO;
> +	} else
> +		rc = -EOPNOTSUPP;

The style looks a little odd.  Why not:

	if (!dax_dev)
		return -ENXIO
	if (!dax_dev->holder_data)
		return -EOPNOTSUPP;
	return dax_dev->holder_ops->notify_failure(dax_dev, offset, size, data);

and let everyone deal with the same errno codes?

Also why do we even need the dax_dev NULL check?

> +void dax_set_holder(struct dax_device *dax_dev, void *holder,
> +		const struct dax_holder_operations *ops)
> +{
> +	if (!dax_dev)
> +		return;

I don't think we really need that check here.

> +void *dax_get_holder(struct dax_device *dax_dev)
> +{
> +	void *holder_data;
> +
> +	if (!dax_dev)
> +		return NULL;

Same here.

> +
> +	down_read(&dax_dev->holder_rwsem);
> +	holder_data = dax_dev->holder_data;
> +	up_read(&dax_dev->holder_rwsem);
> +
> +	return holder_data;

That lock won't protect anything.  I think we simply must have
synchronization to prevent unregistration while the ->notify_failure
call is in progress.

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

* Re: [PATCH v5 9/9] fs/dax: Remove useless functions
  2021-06-28  0:02 ` [PATCH v5 9/9] fs/dax: Remove useless functions Shiyang Ruan
@ 2021-07-19 15:18   ` Christoph Hellwig
  0 siblings, 0 replies; 20+ messages in thread
From: Christoph Hellwig @ 2021-07-19 15:18 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel,
	dm-devel, darrick.wong, dan.j.williams, david, hch, agk, snitzer,
	rgoldwyn

On Mon, Jun 28, 2021 at 08:02:18AM +0800, Shiyang Ruan wrote:
> Since owner tracking is triggerred by pmem device, these functions are
> useless.  So remove them.

What about ext2 and ext4?

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

* RE: [PATCH v5 2/9] dax: Introduce holder for dax_device
  2021-07-19 15:17   ` Christoph Hellwig
@ 2021-07-20 10:37     ` ruansy.fnst
  0 siblings, 0 replies; 20+ messages in thread
From: ruansy.fnst @ 2021-07-20 10:37 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel,
	dm-devel, darrick.wong, dan.j.williams, david, agk, snitzer,
	rgoldwyn

> -----Original Message-----
> Subject: Re: [PATCH v5 2/9] dax: Introduce holder for dax_device
> 
> On Mon, Jun 28, 2021 at 08:02:11AM +0800, Shiyang Ruan wrote:
> > +int dax_holder_notify_failure(struct dax_device *dax_dev, loff_t offset,
> > +			      size_t size, void *data)
> > +{
> > +	int rc = -ENXIO;
> > +	if (!dax_dev)
> > +		return rc;
> > +
> > +	if (dax_dev->holder_data) {
> > +		rc = dax_dev->holder_ops->notify_failure(dax_dev, offset,
> > +							 size, data);
> > +		if (rc == -ENODEV)
> > +			rc = -ENXIO;
> > +	} else
> > +		rc = -EOPNOTSUPP;
> 
> The style looks a little odd.  Why not:
> 
> 	if (!dax_dev)
> 		return -ENXIO
> 	if (!dax_dev->holder_data)
> 		return -EOPNOTSUPP;
> 	return dax_dev->holder_ops->notify_failure(dax_dev, offset, size, data);
> 
> and let everyone deal with the same errno codes?
OK.

> 
> Also why do we even need the dax_dev NULL check?

Because this dax_dev is obtain by fs_dax_get_by_bdev() in XFS and dax_get_by_host() in MD.  According to their definition, NULL may be returned.  So I check the dax_dev here.

> 
> > +void dax_set_holder(struct dax_device *dax_dev, void *holder,
> > +		const struct dax_holder_operations *ops) {
> > +	if (!dax_dev)
> > +		return;
> 
> I don't think we really need that check here.
> 
> > +void *dax_get_holder(struct dax_device *dax_dev) {
> > +	void *holder_data;
> > +
> > +	if (!dax_dev)
> > +		return NULL;
> 
> Same here.
> 
> > +
> > +	down_read(&dax_dev->holder_rwsem);
> > +	holder_data = dax_dev->holder_data;
> > +	up_read(&dax_dev->holder_rwsem);
> > +
> > +	return holder_data;
> 
> That lock won't protect anything.  I think we simply must have synchronization
> to prevent unregistration while the ->notify_failure call is in progress.

Yes, I misunderstood the purpose of the lock. I'll fix this.


--
Thanks,
Ruan.

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

end of thread, other threads:[~2021-07-20 10:38 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-28  0:02 [PATCH v5 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
2021-06-28  0:02 ` [PATCH v5 1/9] pagemap: Introduce ->memory_failure() Shiyang Ruan
2021-06-28  0:02 ` [PATCH v5 2/9] dax: Introduce holder for dax_device Shiyang Ruan
2021-07-19 15:17   ` Christoph Hellwig
2021-07-20 10:37     ` ruansy.fnst
2021-06-28  0:02 ` [PATCH v5 3/9] mm: factor helpers for memory_failure_dev_pagemap Shiyang Ruan
2021-06-28  0:02 ` [PATCH v5 4/9] pmem,mm: Implement ->memory_failure in pmem driver Shiyang Ruan
2021-06-28  0:02 ` [PATCH v5 5/9] mm: Introduce mf_dax_kill_procs() for fsdax case Shiyang Ruan
2021-06-28  3:23   ` kernel test robot
2021-06-28 11:49   ` Matthew Wilcox
2021-06-29  7:49     ` ruansy.fnst
2021-06-29 11:46       ` Matthew Wilcox
2021-07-16  6:32         ` ruansy.fnst
2021-06-28  0:02 ` [PATCH v5 6/9] xfs: Implement ->corrupted_range() for XFS Shiyang Ruan
2021-06-28  2:02   ` kernel test robot
2021-06-28  3:05   ` kernel test robot
2021-06-28  0:02 ` [PATCH v5 7/9] dm: Introduce ->rmap() to find bdev offset Shiyang Ruan
2021-06-28  0:02 ` [PATCH v5 8/9] md: Implement dax_holder_operations Shiyang Ruan
2021-06-28  0:02 ` [PATCH v5 9/9] fs/dax: Remove useless functions Shiyang Ruan
2021-07-19 15:18   ` Christoph Hellwig

This is a public inbox, see mirroring instructions
on how to clone and mirror all data and code used for this inbox