linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v10 0/9] fsdax: introduce fs query to support reflink
@ 2022-01-27 12:40 Shiyang Ruan
  2022-01-27 12:40 ` [PATCH v10 1/9] dax: Introduce holder for dax_device Shiyang Ruan
                   ` (8 more replies)
  0 siblings, 9 replies; 36+ messages in thread
From: Shiyang Ruan @ 2022-01-27 12:40 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: djwong, dan.j.williams, david, hch, jane.chu

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

Changes since V9:
  - Remove dax write/read lock patch
  - Remove dax_lock_entry() in dax_lock_mapping_entry() and rename it to
      dax_load_page()
  - Wrap dax code in memory-failure.c with #if IS_ENABLED(CONFIG_FS_DAX)
  - Wrap xfs_dax_failure_fn() with #if IS_ENABLED(CONFIG_MEMORY_FAILURE)
  - Move PAGE_MAPPING_DAX_COW into page-flags.h

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()
|  |* xfs_dax_notify_failure()
|  |--------------------------
|  |   xfs_rmap_query_range()
|  |    xfs_dax_failure_fn()
|  |    * corrupted on metadata
|  |       try to recover data, call xfs_force_shutdown()
|  |    * corrupted on file data
|  |       try to recover data, call mf_dax_kill_procs()
|* normal case
|-------------
|mf_generic_kill_procs()

==
Shiyang Ruan (9):
  dax: Introduce holder for dax_device
  mm: factor helpers for memory_failure_dev_pagemap
  pagemap,pmem: Introduce ->memory_failure()
  fsdax: fix function description
  fsdax: Introduce dax_load_page()
  mm: move pgoff_address() to vma_pgoff_address()
  mm: Introduce mf_dax_kill_procs() for fsdax case
  xfs: Implement ->notify_failure() for XFS
  fsdax: set a CoW flag when associate reflink mappings

 drivers/dax/super.c         |  62 ++++++++++
 drivers/nvdimm/pmem.c       |  16 +++
 fs/dax.c                    | 123 +++++++++++++++----
 fs/xfs/Makefile             |   1 +
 fs/xfs/xfs_buf.c            |  12 ++
 fs/xfs/xfs_fsops.c          |   3 +
 fs/xfs/xfs_mount.h          |   1 +
 fs/xfs/xfs_notify_failure.c | 222 ++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_notify_failure.h |  10 ++
 include/linux/dax.h         |  37 ++++++
 include/linux/memremap.h    |  12 ++
 include/linux/mm.h          |  17 +++
 include/linux/page-flags.h  |   6 +
 mm/memory-failure.c         | 234 +++++++++++++++++++++++++-----------
 14 files changed, 666 insertions(+), 90 deletions(-)
 create mode 100644 fs/xfs/xfs_notify_failure.c
 create mode 100644 fs/xfs/xfs_notify_failure.h

-- 
2.34.1





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

* [PATCH v10 1/9] dax: Introduce holder for dax_device
  2022-01-27 12:40 [PATCH v10 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
@ 2022-01-27 12:40 ` Shiyang Ruan
  2022-01-27 16:13   ` kernel test robot
                     ` (2 more replies)
  2022-01-27 12:40 ` [PATCH v10 2/9] mm: factor helpers for memory_failure_dev_pagemap Shiyang Ruan
                   ` (7 subsequent siblings)
  8 siblings, 3 replies; 36+ messages in thread
From: Shiyang Ruan @ 2022-01-27 12:40 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: djwong, dan.j.williams, david, hch, jane.chu

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
   instance 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 | 62 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/dax.h | 29 +++++++++++++++++++++
 2 files changed, 91 insertions(+)

diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index e3029389d809..8e2733b78437 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -21,6 +21,9 @@
  * @cdev: optional character interface for "device dax"
  * @private: dax driver private data
  * @flags: state and boolean properties
+ * @ops: operations for dax_device
+ * @holder_data: holder of a dax_device: could be filesystem or mapped device
+ * @holder_ops: operations for the inner holder
  */
 struct dax_device {
 	struct inode inode;
@@ -28,6 +31,8 @@ struct dax_device {
 	void *private;
 	unsigned long flags;
 	const struct dax_operations *ops;
+	void *holder_data;
+	const struct dax_holder_operations *holder_ops;
 };
 
 static dev_t dax_devt;
@@ -193,6 +198,29 @@ 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, u64 off,
+			      u64 len, int mf_flags)
+{
+	int rc, id;
+
+	id = dax_read_lock();
+	if (!dax_alive(dax_dev)) {
+		rc = -ENXIO;
+		goto out;
+	}
+
+	if (!dax_dev->holder_ops) {
+		rc = -EOPNOTSUPP;
+		goto out;
+	}
+
+	rc = dax_dev->holder_ops->notify_failure(dax_dev, off, len, mf_flags);
+out:
+	dax_read_unlock(id);
+	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)
@@ -268,6 +296,10 @@ void kill_dax(struct dax_device *dax_dev)
 
 	clear_bit(DAXDEV_ALIVE, &dax_dev->flags);
 	synchronize_srcu(&dax_srcu);
+
+	/* clear holder data */
+	dax_dev->holder_ops = NULL;
+	dax_dev->holder_data = NULL;
 }
 EXPORT_SYMBOL_GPL(kill_dax);
 
@@ -409,6 +441,36 @@ void put_dax(struct dax_device *dax_dev)
 }
 EXPORT_SYMBOL_GPL(put_dax);
 
+void dax_register_holder(struct dax_device *dax_dev, void *holder,
+		const struct dax_holder_operations *ops)
+{
+	if (!dax_alive(dax_dev))
+		return;
+
+	dax_dev->holder_data = holder;
+	dax_dev->holder_ops = ops;
+}
+EXPORT_SYMBOL_GPL(dax_register_holder);
+
+void dax_unregister_holder(struct dax_device *dax_dev)
+{
+	if (!dax_alive(dax_dev))
+		return;
+
+	dax_dev->holder_data = NULL;
+	dax_dev->holder_ops = NULL;
+}
+EXPORT_SYMBOL_GPL(dax_unregister_holder);
+
+void *dax_get_holder(struct dax_device *dax_dev)
+{
+	if (!dax_alive(dax_dev))
+		return NULL;
+
+	return dax_dev->holder_data;
+}
+EXPORT_SYMBOL_GPL(dax_get_holder);
+
 /**
  * inode_dax: convert a public inode into its dax_dev
  * @inode: An inode with i_cdev pointing to a dax_dev
diff --git a/include/linux/dax.h b/include/linux/dax.h
index 9fc5f99a0ae2..96cfc63b12fd 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -34,6 +34,22 @@ struct dax_operations {
 
 #if IS_ENABLED(CONFIG_DAX)
 struct dax_device *alloc_dax(void *private, const struct dax_operations *ops);
+struct dax_holder_operations {
+	/*
+	 * notify_failure - notify memory failure into inner holder device
+	 * @dax_dev: the dax device which contains the holder
+	 * @offset: offset on this dax device where memory failure occurs
+	 * @len: length of this memory failure event
+	 * @flags: action flags for memory failure handler
+	 */
+	int (*notify_failure)(struct dax_device *dax_dev, u64 offset,
+			u64 len, int mf_flags);
+};
+
+void dax_register_holder(struct dax_device *dax_dev, void *holder,
+		const struct dax_holder_operations *ops);
+void dax_unregister_holder(struct dax_device *dax_dev);
+void *dax_get_holder(struct dax_device *dax_dev);
 void put_dax(struct dax_device *dax_dev);
 void kill_dax(struct dax_device *dax_dev);
 void dax_write_cache(struct dax_device *dax_dev, bool wc);
@@ -53,6 +69,17 @@ static inline bool daxdev_mapping_supported(struct vm_area_struct *vma,
 	return dax_synchronous(dax_dev);
 }
 #else
+static inline void dax_register_holder(struct dax_device *dax_dev, void *holder,
+		const struct dax_holder_operations *ops)
+{
+}
+static inline void dax_unregister_holder(struct dax_device *dax_dev)
+{
+}
+static inline void *dax_get_holder(struct dax_device *dax_dev)
+{
+	return NULL;
+}
 static inline struct dax_device *alloc_dax(void *private,
 		const struct dax_operations *ops)
 {
@@ -185,6 +212,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, u64 off, u64 len,
+		int mf_flags);
 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.34.1





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

* [PATCH v10 2/9] mm: factor helpers for memory_failure_dev_pagemap
  2022-01-27 12:40 [PATCH v10 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
  2022-01-27 12:40 ` [PATCH v10 1/9] dax: Introduce holder for dax_device Shiyang Ruan
@ 2022-01-27 12:40 ` Shiyang Ruan
  2022-02-01 21:03   ` Matthew Wilcox
  2022-02-15 22:11   ` Dan Williams
  2022-01-27 12:40 ` [PATCH v10 3/9] pagemap,pmem: Introduce ->memory_failure() Shiyang Ruan
                   ` (6 subsequent siblings)
  8 siblings, 2 replies; 36+ messages in thread
From: Shiyang Ruan @ 2022-01-27 12:40 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: djwong, dan.j.williams, david, hch, jane.chu, Christoph Hellwig

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>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 mm/memory-failure.c | 141 ++++++++++++++++++++++++--------------------
 1 file changed, 77 insertions(+), 64 deletions(-)

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 14ae5c18e776..98b6144e4b9b 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1500,6 +1500,80 @@ 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 dev_pagemap *pgmap)
+{
+	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;
+
+	if (hwpoison_filter(page))
+		return 0;
+
+	if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
+		/*
+		 * TODO: Handle HMM pages which may need coordination
+		 * with device-side memory.
+		 */
+		return -EBUSY;
+	}
+
+	/*
+	 * Use this flag as an indication that the dax page has been
+	 * remapped UC to prevent speculative consumption of poison.
+	 */
+	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, &to_kill, true);
+
+	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);
@@ -1576,12 +1650,8 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 		struct dev_pagemap *pgmap)
 {
 	struct page *page = pfn_to_page(pfn);
-	unsigned long size = 0;
-	struct to_kill *tk;
 	LIST_HEAD(tokill);
-	int rc = -EBUSY;
-	loff_t start;
-	dax_entry_t cookie;
+	int rc = -ENXIO;
 
 	if (flags & MF_COUNT_INCREASED)
 		/*
@@ -1590,67 +1660,10 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 		put_page(page);
 
 	/* device metadata space is not recoverable */
-	if (!pgmap_pfn_valid(pgmap, pfn)) {
-		rc = -ENXIO;
-		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)
+	if (!pgmap_pfn_valid(pgmap, pfn))
 		goto out;
 
-	if (hwpoison_filter(page)) {
-		rc = 0;
-		goto unlock;
-	}
-
-	if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
-		/*
-		 * TODO: Handle HMM pages which may need coordination
-		 * with device-side memory.
-		 */
-		goto unlock;
-	}
-
-	/*
-	 * Use this flag as an indication that the dax page has been
-	 * remapped UC to prevent speculative consumption of poison.
-	 */
-	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, false, pfn, flags);
-	rc = 0;
-unlock:
-	dax_unlock_page(page, cookie);
+	rc = mf_generic_kill_procs(pfn, flags, pgmap);
 out:
 	/* drop pgmap ref acquired in caller */
 	put_dev_pagemap(pgmap);
-- 
2.34.1





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

* [PATCH v10 3/9] pagemap,pmem: Introduce ->memory_failure()
  2022-01-27 12:40 [PATCH v10 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
  2022-01-27 12:40 ` [PATCH v10 1/9] dax: Introduce holder for dax_device Shiyang Ruan
  2022-01-27 12:40 ` [PATCH v10 2/9] mm: factor helpers for memory_failure_dev_pagemap Shiyang Ruan
@ 2022-01-27 12:40 ` Shiyang Ruan
  2022-02-15 22:38   ` Dan Williams
  2022-01-27 12:40 ` [PATCH v10 4/9] fsdax: fix function description Shiyang Ruan
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 36+ messages in thread
From: Shiyang Ruan @ 2022-01-27 12:40 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: djwong, dan.j.williams, david, hch, jane.chu, Christoph Hellwig

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.

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>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 drivers/nvdimm/pmem.c    | 16 ++++++++++++++++
 include/linux/memremap.h | 12 ++++++++++++
 mm/memory-failure.c      | 14 ++++++++++++++
 3 files changed, 42 insertions(+)

diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 58d95242a836..0a6e8698d086 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -366,6 +366,20 @@ static void pmem_release_disk(void *__pmem)
 	blk_cleanup_disk(pmem->disk);
 }
 
+static int pmem_pagemap_memory_failure(struct dev_pagemap *pgmap,
+		unsigned long pfn, u64 len, int mf_flags)
+{
+	struct pmem_device *pmem =
+			container_of(pgmap, struct pmem_device, pgmap);
+	u64 offset = PFN_PHYS(pfn) - pmem->phys_addr - pmem->data_offset;
+
+	return dax_holder_notify_failure(pmem->dax_dev, offset, len, mf_flags);
+}
+
+static const struct dev_pagemap_ops fsdax_pagemap_ops = {
+	.memory_failure		= pmem_pagemap_memory_failure,
+};
+
 static int pmem_attach_disk(struct device *dev,
 		struct nd_namespace_common *ndns)
 {
@@ -427,6 +441,7 @@ static int pmem_attach_disk(struct device *dev,
 	pmem->pfn_flags = PFN_DEV;
 	if (is_nd_pfn(dev)) {
 		pmem->pgmap.type = MEMORY_DEVICE_FS_DAX;
+		pmem->pgmap.ops = &fsdax_pagemap_ops;
 		addr = devm_memremap_pages(dev, &pmem->pgmap);
 		pfn_sb = nd_pfn->pfn_sb;
 		pmem->data_offset = le64_to_cpu(pfn_sb->dataoff);
@@ -440,6 +455,7 @@ static int pmem_attach_disk(struct device *dev,
 		pmem->pgmap.range.end = res->end;
 		pmem->pgmap.nr_range = 1;
 		pmem->pgmap.type = MEMORY_DEVICE_FS_DAX;
+		pmem->pgmap.ops = &fsdax_pagemap_ops;
 		addr = devm_memremap_pages(dev, &pmem->pgmap);
 		pmem->pfn_flags |= PFN_MAP;
 		bb_range = pmem->pgmap.range;
diff --git a/include/linux/memremap.h b/include/linux/memremap.h
index 1fafcc38acba..f739318b496f 100644
--- a/include/linux/memremap.h
+++ b/include/linux/memremap.h
@@ -77,6 +77,18 @@ 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 mf_flags is finally passed to the recover
+	 * function through the whole notify routine.
+	 *
+	 * When this is not implemented, or it returns -EOPNOTSUPP, the caller
+	 * will fall back to a common handler called mf_generic_kill_procs().
+	 */
+	int (*memory_failure)(struct dev_pagemap *pgmap, unsigned long pfn,
+			      u64 len, int mf_flags);
 };
 
 #define PGMAP_ALTMAP_VALID	(1 << 0)
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 98b6144e4b9b..b2d13eba1071 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1663,6 +1663,20 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
 	if (!pgmap_pfn_valid(pgmap, pfn))
 		goto out;
 
+	/*
+	 * 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, PAGE_SIZE, flags);
+		/*
+		 * Fall back to generic handler too if operation is not
+		 * supported inside the driver/device/filesystem.
+		 */
+		if (rc != -EOPNOTSUPP)
+			goto out;
+	}
+
 	rc = mf_generic_kill_procs(pfn, flags, pgmap);
 out:
 	/* drop pgmap ref acquired in caller */
-- 
2.34.1





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

* [PATCH v10 4/9] fsdax: fix function description
  2022-01-27 12:40 [PATCH v10 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (2 preceding siblings ...)
  2022-01-27 12:40 ` [PATCH v10 3/9] pagemap,pmem: Introduce ->memory_failure() Shiyang Ruan
@ 2022-01-27 12:40 ` Shiyang Ruan
  2022-02-02 13:04   ` Christoph Hellwig
  2022-01-27 12:40 ` [PATCH v10 5/9] fsdax: Introduce dax_load_page() Shiyang Ruan
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 36+ messages in thread
From: Shiyang Ruan @ 2022-01-27 12:40 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: djwong, dan.j.williams, david, hch, jane.chu, Christoph Hellwig

The function name has been changed, so the description should be updated
too.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 fs/dax.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/dax.c b/fs/dax.c
index cd03485867a7..c8d57080c1aa 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -390,7 +390,7 @@ static struct page *dax_busy_page(void *entry)
 }
 
 /*
- * dax_lock_mapping_entry - Lock the DAX entry corresponding to a page
+ * dax_lock_page - Lock the DAX entry corresponding to a page
  * @page: The page whose entry we want to lock
  *
  * Context: Process context.
-- 
2.34.1





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

* [PATCH v10 5/9] fsdax: Introduce dax_load_page()
  2022-01-27 12:40 [PATCH v10 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (3 preceding siblings ...)
  2022-01-27 12:40 ` [PATCH v10 4/9] fsdax: fix function description Shiyang Ruan
@ 2022-01-27 12:40 ` Shiyang Ruan
  2022-02-16  1:34   ` Dan Williams
  2022-01-27 12:40 ` [PATCH v10 6/9] mm: move pgoff_address() to vma_pgoff_address() Shiyang Ruan
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 36+ messages in thread
From: Shiyang Ruan @ 2022-01-27 12:40 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: djwong, dan.j.williams, david, hch, jane.chu, Christoph Hellwig

The current dax_lock_page() locks dax entry by obtaining mapping and
index in page.  To support 1-to-N RMAP in NVDIMM, we need a new function
to lock a specific dax entry corresponding to this file's mapping,index.
And output the page corresponding to the specific dax entry for caller
use.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 fs/dax.c            | 44 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/dax.h |  8 ++++++++
 2 files changed, 52 insertions(+)

diff --git a/fs/dax.c b/fs/dax.c
index c8d57080c1aa..964512107c23 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -455,6 +455,50 @@ void dax_unlock_page(struct page *page, dax_entry_t cookie)
 	dax_unlock_entry(&xas, (void *)cookie);
 }
 
+/*
+ * dax_load_page - Load the page corresponding to a (mapping,offset)
+ * @mapping: the file's mapping whose entry we want to load
+ * @index:   the offset within this file
+ * @page:    output the dax page corresponding to this dax entry
+ *
+ * Return: error if it isn't a dax mapping, otherwise 0.
+ */
+int dax_load_page(struct address_space *mapping, pgoff_t index,
+		struct page **page)
+{
+	XA_STATE(xas, &mapping->i_pages, 0);
+	void *entry;
+
+	if (!dax_mapping(mapping))
+		return -EBUSY;
+
+	rcu_read_lock();
+	for (;;) {
+		entry = NULL;
+		xas_lock_irq(&xas);
+		xas_set(&xas, index);
+		entry = xas_load(&xas);
+		if (dax_is_locked(entry)) {
+			rcu_read_unlock();
+			wait_entry_unlocked(&xas, entry);
+			rcu_read_lock();
+			continue;
+		}
+		if (entry &&
+		    !dax_is_zero_entry(entry) && !dax_is_empty_entry(entry)) {
+			/*
+			 * Output the page if the dax entry exists and isn't
+			 * a zero or empty entry.
+			 */
+			*page = pfn_to_page(dax_to_pfn(entry));
+		}
+		xas_unlock_irq(&xas);
+		break;
+	}
+	rcu_read_unlock();
+	return 0;
+}
+
 /*
  * Find page cache entry at given index. If it is a DAX entry, return it
  * with the entry locked. If the page cache doesn't contain an entry at
diff --git a/include/linux/dax.h b/include/linux/dax.h
index 96cfc63b12fd..530ff9733dd9 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -155,6 +155,8 @@ 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);
+int dax_load_page(struct address_space *mapping,
+		unsigned long index, struct page **page);
 #else
 static inline struct page *dax_layout_busy_page(struct address_space *mapping)
 {
@@ -182,6 +184,12 @@ static inline dax_entry_t dax_lock_page(struct page *page)
 static inline void dax_unlock_page(struct page *page, dax_entry_t cookie)
 {
 }
+
+static inline int dax_load_page(struct address_space *mapping,
+		unsigned long index, struct page **page)
+{
+	return 0;
+}
 #endif
 
 int dax_zero_range(struct inode *inode, loff_t pos, loff_t len, bool *did_zero,
-- 
2.34.1





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

* [PATCH v10 6/9] mm: move pgoff_address() to vma_pgoff_address()
  2022-01-27 12:40 [PATCH v10 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (4 preceding siblings ...)
  2022-01-27 12:40 ` [PATCH v10 5/9] fsdax: Introduce dax_load_page() Shiyang Ruan
@ 2022-01-27 12:40 ` Shiyang Ruan
  2022-02-16  1:37   ` Dan Williams
  2022-01-27 12:40 ` [PATCH v10 7/9] mm: Introduce mf_dax_kill_procs() for fsdax case Shiyang Ruan
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 36+ messages in thread
From: Shiyang Ruan @ 2022-01-27 12:40 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: djwong, dan.j.williams, david, hch, jane.chu, Christoph Hellwig

Since it is not a DAX-specific function, move it into mm and rename it
to be a generic helper.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 fs/dax.c           | 12 +-----------
 include/linux/mm.h | 13 +++++++++++++
 2 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/fs/dax.c b/fs/dax.c
index 964512107c23..250794a5b789 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -834,16 +834,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)
@@ -863,7 +853,7 @@ static void dax_entry_mkclean(struct address_space *mapping, pgoff_t index,
 		if (!(vma->vm_flags & VM_SHARED))
 			continue;
 
-		address = pgoff_address(index, vma);
+		address = vma_pgoff_address(vma, index);
 
 		/*
 		 * follow_invalidate_pte() will use the range to call
diff --git a/include/linux/mm.h b/include/linux/mm.h
index e1a84b1e6787..9b1d56c5c224 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2816,6 +2816,19 @@ static inline unsigned long vma_pages(struct vm_area_struct *vma)
 	return (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
 }
 
+/*
+ * Get user virtual address at the specific offset within a vma.
+ */
+static inline unsigned long vma_pgoff_address(struct vm_area_struct *vma,
+					      pgoff_t pgoff)
+{
+	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;
+}
+
 /* Look up the first VMA which exactly match the interval vm_start ... vm_end */
 static inline struct vm_area_struct *find_exact_vma(struct mm_struct *mm,
 				unsigned long vm_start, unsigned long vm_end)
-- 
2.34.1





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

* [PATCH v10 7/9] mm: Introduce mf_dax_kill_procs() for fsdax case
  2022-01-27 12:40 [PATCH v10 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (5 preceding siblings ...)
  2022-01-27 12:40 ` [PATCH v10 6/9] mm: move pgoff_address() to vma_pgoff_address() Shiyang Ruan
@ 2022-01-27 12:40 ` Shiyang Ruan
  2022-02-16  1:47   ` Dan Williams
  2022-02-16  1:49   ` Dan Williams
  2022-01-27 12:40 ` [PATCH v10 8/9] xfs: Implement ->notify_failure() for XFS Shiyang Ruan
  2022-01-27 12:40 ` [PATCH v10 9/9] fsdax: set a CoW flag when associate reflink mappings Shiyang Ruan
  8 siblings, 2 replies; 36+ messages in thread
From: Shiyang Ruan @ 2022-01-27 12:40 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: djwong, dan.j.williams, david, hch, jane.chu

This function is called at the end of RMAP routine, i.e. filesystem
recovery function, to collect and kill processes using a shared page of
DAX file.  The difference with mf_generic_kill_procs() is, it accepts
file's (mapping,offset) instead of struct page because different files'
mappings and offsets may share the same page in fsdax mode.
It will be called when filesystem's RMAP results are found.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
---
 include/linux/mm.h  |  4 ++
 mm/memory-failure.c | 91 +++++++++++++++++++++++++++++++++++++++------
 2 files changed, 84 insertions(+), 11 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 9b1d56c5c224..0420189e4788 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3195,6 +3195,10 @@ enum mf_flags {
 	MF_SOFT_OFFLINE = 1 << 3,
 	MF_UNPOISON = 1 << 4,
 };
+#if IS_ENABLED(CONFIG_FS_DAX)
+int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index,
+		      unsigned long count, int mf_flags);
+#endif /* CONFIG_FS_DAX */
 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 b2d13eba1071..8d123cc4102e 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -304,10 +304,9 @@ void shake_page(struct page *p)
 }
 EXPORT_SYMBOL_GPL(shake_page);
 
-static unsigned long dev_pagemap_mapping_shift(struct page *page,
-		struct vm_area_struct *vma)
+static unsigned long dev_pagemap_mapping_shift(struct vm_area_struct *vma,
+		unsigned long address)
 {
-	unsigned long address = vma_address(page, vma);
 	unsigned long ret = 0;
 	pgd_t *pgd;
 	p4d_t *p4d;
@@ -347,9 +346,8 @@ 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,
-		       struct vm_area_struct *vma,
-		       struct list_head *to_kill)
+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)
 {
 	struct to_kill *tk;
 
@@ -360,9 +358,15 @@ 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 not used for fsdax, we need
+		 * calculate the address based on the vma.
+		 */
+		if (p->pgmap->type == MEMORY_DEVICE_FS_DAX)
+			tk->addr = vma_pgoff_address(vma, pgoff);
+		tk->size_shift = dev_pagemap_mapping_shift(vma, tk->addr);
+	} else
 		tk->size_shift = page_shift(compound_head(p));
 
 	/*
@@ -510,7 +514,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);
@@ -546,12 +550,40 @@ 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, 0, vma, to_kill);
+		}
+	}
+	read_unlock(&tasklist_lock);
+	i_mmap_unlock_read(mapping);
+}
+
+#if IS_ENABLED(CONFIG_FS_DAX)
+/*
+ * Collect processes when the error hit a fsdax page.
+ */
+static void collect_procs_fsdax(struct page *page,
+		struct address_space *mapping, pgoff_t pgoff,
+		struct list_head *to_kill)
+{
+	struct vm_area_struct *vma;
+	struct task_struct *tsk;
+
+	i_mmap_lock_read(mapping);
+	read_lock(&tasklist_lock);
+	for_each_process(tsk) {
+		struct task_struct *t = task_early_kill(tsk, true);
+
+		if (!t)
+			continue;
+		vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, pgoff) {
+			if (vma->vm_mm == t->mm)
+				add_to_kill(t, page, pgoff, vma, to_kill);
 		}
 	}
 	read_unlock(&tasklist_lock);
 	i_mmap_unlock_read(mapping);
 }
+#endif /* CONFIG_FS_DAX */
 
 /*
  * Collect the processes who have the corrupted page mapped to kill.
@@ -1574,6 +1606,43 @@ static int mf_generic_kill_procs(unsigned long long pfn, int flags,
 	return 0;
 }
 
+#if IS_ENABLED(CONFIG_FS_DAX)
+/**
+ * mf_dax_kill_procs - Collect and kill processes who are using this file range
+ * @mapping:	the file in use
+ * @index:	start pgoff of the range within the file
+ * @count:	length of the range, in unit of PAGE_SIZE
+ * @mf_flags:	memory failure flags
+ */
+int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index,
+		unsigned long count, int mf_flags)
+{
+	LIST_HEAD(to_kill);
+	int rc;
+	struct page *page;
+	size_t end = index + count;
+
+	mf_flags |= MF_ACTION_REQUIRED | MF_MUST_KILL;
+
+	for (; index < end; index++) {
+		page = NULL;
+		rc = dax_load_page(mapping, index, &page);
+		if (rc)
+			return rc;
+		if (!page)
+			continue;
+
+		SetPageHWPoison(page);
+
+		collect_procs_fsdax(page, mapping, index, &to_kill);
+		unmap_and_kill(&to_kill, page_to_pfn(page), mapping,
+				index, mf_flags);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mf_dax_kill_procs);
+#endif /* CONFIG_FS_DAX */
+
 static int memory_failure_hugetlb(unsigned long pfn, int flags)
 {
 	struct page *p = pfn_to_page(pfn);
-- 
2.34.1





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

* [PATCH v10 8/9] xfs: Implement ->notify_failure() for XFS
  2022-01-27 12:40 [PATCH v10 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (6 preceding siblings ...)
  2022-01-27 12:40 ` [PATCH v10 7/9] mm: Introduce mf_dax_kill_procs() for fsdax case Shiyang Ruan
@ 2022-01-27 12:40 ` Shiyang Ruan
  2022-01-27 17:56   ` kernel test robot
                     ` (3 more replies)
  2022-01-27 12:40 ` [PATCH v10 9/9] fsdax: set a CoW flag when associate reflink mappings Shiyang Ruan
  8 siblings, 4 replies; 36+ messages in thread
From: Shiyang Ruan @ 2022-01-27 12:40 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: djwong, dan.j.williams, david, hch, jane.chu

Introduce xfs_notify_failure.c to handle failure related works, such as
implement ->notify_failure(), register/unregister dax holder in xfs, and
so on.

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.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
---
 fs/xfs/Makefile             |   1 +
 fs/xfs/xfs_buf.c            |  12 ++
 fs/xfs/xfs_fsops.c          |   3 +
 fs/xfs/xfs_mount.h          |   1 +
 fs/xfs/xfs_notify_failure.c | 222 ++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_notify_failure.h |  10 ++
 6 files changed, 249 insertions(+)
 create mode 100644 fs/xfs/xfs_notify_failure.c
 create mode 100644 fs/xfs/xfs_notify_failure.h

diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index 04611a1068b4..389970b3e13b 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -84,6 +84,7 @@ xfs-y				+= xfs_aops.o \
 				   xfs_message.o \
 				   xfs_mount.o \
 				   xfs_mru_cache.o \
+				   xfs_notify_failure.o \
 				   xfs_pwork.o \
 				   xfs_reflink.o \
 				   xfs_stats.o \
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index b45e0d50a405..017010b3d601 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -19,6 +19,7 @@
 #include "xfs_errortag.h"
 #include "xfs_error.h"
 #include "xfs_ag.h"
+#include "xfs_notify_failure.h"
 
 static struct kmem_cache *xfs_buf_cache;
 
@@ -1892,6 +1893,8 @@ xfs_free_buftarg(
 	list_lru_destroy(&btp->bt_lru);
 
 	blkdev_issue_flush(btp->bt_bdev);
+	if (btp->bt_daxdev)
+		dax_unregister_holder(btp->bt_daxdev);
 	fs_put_dax(btp->bt_daxdev);
 
 	kmem_free(btp);
@@ -1946,6 +1949,15 @@ xfs_alloc_buftarg(
 	btp->bt_dev =  bdev->bd_dev;
 	btp->bt_bdev = bdev;
 	btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off);
+	if (btp->bt_daxdev) {
+		if (dax_get_holder(btp->bt_daxdev)) {
+			xfs_err(mp, "DAX device already in use?!");
+			goto error_free;
+		}
+
+		dax_register_holder(btp->bt_daxdev, mp,
+				&xfs_dax_holder_operations);
+	}
 
 	/*
 	 * Buffer IO error rate limiting. Limit it to no more than 10 messages
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 33e26690a8c4..d4d36c5bef11 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -542,6 +542,9 @@ xfs_do_force_shutdown(
 	} else if (flags & SHUTDOWN_CORRUPT_INCORE) {
 		tag = XFS_PTAG_SHUTDOWN_CORRUPT;
 		why = "Corruption of in-memory data";
+	} else if (flags & SHUTDOWN_CORRUPT_ONDISK) {
+		tag = XFS_PTAG_SHUTDOWN_CORRUPT;
+		why = "Corruption of on-disk metadata";
 	} else {
 		tag = XFS_PTAG_SHUTDOWN_IOERROR;
 		why = "Metadata I/O Error";
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 00720a02e761..47ff4ac53c4c 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -435,6 +435,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_ONDISK	0x0010  /* corrupt metadata on device */
 
 #define XFS_SHUTDOWN_STRINGS \
 	{ SHUTDOWN_META_IO_ERROR,	"metadata_io" }, \
diff --git a/fs/xfs/xfs_notify_failure.c b/fs/xfs/xfs_notify_failure.c
new file mode 100644
index 000000000000..6abaa043f4bc
--- /dev/null
+++ b/fs/xfs/xfs_notify_failure.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Fujitsu.  All Rights Reserved.
+ */
+
+#include "xfs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_alloc.h"
+#include "xfs_bit.h"
+#include "xfs_btree.h"
+#include "xfs_inode.h"
+#include "xfs_icache.h"
+#include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
+#include "xfs_rtalloc.h"
+#include "xfs_trans.h"
+
+#include <linux/mm.h>
+#include <linux/dax.h>
+
+struct failure_info {
+	xfs_agblock_t		startblock;
+	xfs_extlen_t		blockcount;
+	int			mf_flags;
+};
+
+#if IS_ENABLED(CONFIG_MEMORY_FAILURE) && IS_ENABLED(CONFIG_FS_DAX)
+static pgoff_t
+xfs_failure_pgoff(
+	struct xfs_mount		*mp,
+	const struct xfs_rmap_irec	*rec,
+	const struct failure_info	*notify)
+{
+	uint64_t			pos = rec->rm_offset;
+
+	if (notify->startblock > rec->rm_startblock)
+		pos += XFS_FSB_TO_B(mp,
+				notify->startblock - rec->rm_startblock);
+	return pos >> PAGE_SHIFT;
+}
+
+static unsigned long
+xfs_failure_pgcnt(
+	struct xfs_mount		*mp,
+	const struct xfs_rmap_irec	*rec,
+	const struct failure_info	*notify)
+{
+	xfs_agblock_t			end_rec;
+	xfs_agblock_t			end_notify;
+	xfs_agblock_t			start_cross;
+	xfs_agblock_t			end_cross;
+
+	start_cross = max(rec->rm_startblock, notify->startblock);
+
+	end_rec = rec->rm_startblock + rec->rm_blockcount;
+	end_notify = notify->startblock + notify->blockcount;
+	end_cross = min(end_rec, end_notify);
+
+	return XFS_FSB_TO_B(mp, end_cross - start_cross) >> PAGE_SHIFT;
+}
+
+static int
+xfs_dax_failure_fn(
+	struct xfs_btree_cur		*cur,
+	const struct xfs_rmap_irec	*rec,
+	void				*data)
+{
+	struct xfs_mount		*mp = cur->bc_mp;
+	struct xfs_inode		*ip;
+	struct failure_info		*notify = data;
+	int				error = 0;
+
+	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
+	    (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
+		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
+		return -EFSCORRUPTED;
+	}
+
+	/* Get files that incore, filter out others that are not in use. */
+	error = xfs_iget(mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE,
+			 0, &ip);
+	/* Continue the rmap query if the inode isn't incore */
+	if (error == -ENODATA)
+		return 0;
+	if (error)
+		return error;
+
+	error = mf_dax_kill_procs(VFS_I(ip)->i_mapping,
+				  xfs_failure_pgoff(mp, rec, notify),
+				  xfs_failure_pgcnt(mp, rec, notify),
+				  notify->mf_flags);
+	xfs_irele(ip);
+	return error;
+}
+#else
+static int
+xfs_dax_failure_fn(
+	struct xfs_btree_cur		*cur,
+	const struct xfs_rmap_irec	*rec,
+	void				*data)
+{
+	struct xfs_mount		*mp = cur->bc_mp;
+
+	/* No other option besides shutting down the fs. */
+	xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
+	return -EFSCORRUPTED;
+}
+#endif /* CONFIG_MEMORY_FAILURE && CONFIG_FS_DAX */
+
+static int
+xfs_dax_notify_ddev_failure(
+	struct xfs_mount	*mp,
+	xfs_daddr_t		daddr,
+	xfs_daddr_t		bblen,
+	int			mf_flags)
+{
+	struct xfs_trans	*tp = NULL;
+	struct xfs_btree_cur	*cur = NULL;
+	struct xfs_buf		*agf_bp = NULL;
+	struct failure_info	notify;
+	int			error = 0;
+	xfs_fsblock_t		fsbno = XFS_DADDR_TO_FSB(mp, daddr);
+	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(mp, fsbno);
+	xfs_fsblock_t		end_fsbno = XFS_DADDR_TO_FSB(mp, daddr + bblen);
+	xfs_agnumber_t		end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno);
+
+	/*
+	 * Once a file is found by rmap, we take the intersection of two ranges:
+	 * notification range and file extent range, to make sure we won't go
+	 * out of scope.
+	 */
+	notify.mf_flags = mf_flags;
+	notify.startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
+	notify.blockcount = XFS_BB_TO_FSB(mp, bblen);
+
+	error = xfs_trans_alloc_empty(mp, &tp);
+	if (error)
+		return error;
+
+	for (; agno <= end_agno; agno++) {
+		struct xfs_rmap_irec	ri_low = { };
+		struct xfs_rmap_irec	ri_high;
+
+		error = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
+		if (error)
+			break;
+
+		cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, agf_bp->b_pag);
+
+		/*
+		 * Set the rmap range from ri_low to ri_high, which represents
+		 * a [start, end] where we looking for the files or metadata.
+		 * The part of range out of a AG will be ignored.  So, it's fine
+		 * to set ri_low to "startblock" in all loops.  When it reaches
+		 * the last AG, set the ri_high to "endblock" to make sure we
+		 * actually end at the end.
+		 */
+		memset(&ri_high, 0xFF, sizeof(ri_high));
+		ri_low.rm_startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
+		if (agno == end_agno)
+			ri_high.rm_startblock = XFS_FSB_TO_AGBNO(mp, end_fsbno);
+
+		error = xfs_rmap_query_range(cur, &ri_low, &ri_high,
+				xfs_dax_failure_fn, &notify);
+		xfs_btree_del_cursor(cur, error);
+		xfs_trans_brelse(tp, agf_bp);
+		if (error)
+			break;
+
+		fsbno = XFS_AGB_TO_FSB(mp, agno + 1, 0);
+	}
+
+	xfs_trans_cancel(tp);
+	return error;
+}
+
+static int
+xfs_dax_notify_failure(
+	struct dax_device	*dax_dev,
+	u64			offset,
+	u64			len,
+	int			mf_flags)
+{
+	struct xfs_mount	*mp = dax_get_holder(dax_dev);
+
+	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_daxdev == dax_dev) {
+		xfs_warn(mp,
+			 "notify_failure() not supported on 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_ONDISK);
+		return -EFSCORRUPTED;
+	}
+
+	if (!xfs_has_rmapbt(mp)) {
+		xfs_warn(mp, "notify_failure() needs rmapbt enabled!");
+		return -EOPNOTSUPP;
+	}
+
+	if (offset < mp->m_ddev_targp->bt_dax_part_off ||
+	    ((offset + len) > mp->m_ddev_targp->bt_bdev->bd_nr_sectors <<
+				SECTOR_SHIFT)) {
+		xfs_warn(mp, "notify_failure() goes out of the scope.");
+		return -ENXIO;
+	}
+
+	offset -= mp->m_ddev_targp->bt_dax_part_off;
+	return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len),
+			mf_flags);
+}
+
+const struct dax_holder_operations xfs_dax_holder_operations = {
+	.notify_failure		= xfs_dax_notify_failure,
+};
diff --git a/fs/xfs/xfs_notify_failure.h b/fs/xfs/xfs_notify_failure.h
new file mode 100644
index 000000000000..f40cb315e7ce
--- /dev/null
+++ b/fs/xfs/xfs_notify_failure.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Fujitsu.  All Rights Reserved.
+ */
+#ifndef __XFS_NOTIFY_FAILURE_H__
+#define __XFS_NOTIFY_FAILURE_H__
+
+extern const struct dax_holder_operations xfs_dax_holder_operations;
+
+#endif  /* __XFS_NOTIFY_FAILURE_H__ */
-- 
2.34.1





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

* [PATCH v10 9/9] fsdax: set a CoW flag when associate reflink mappings
  2022-01-27 12:40 [PATCH v10 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
                   ` (7 preceding siblings ...)
  2022-01-27 12:40 ` [PATCH v10 8/9] xfs: Implement ->notify_failure() for XFS Shiyang Ruan
@ 2022-01-27 12:40 ` Shiyang Ruan
  2022-02-16  2:09   ` Dan Williams
  8 siblings, 1 reply; 36+ messages in thread
From: Shiyang Ruan @ 2022-01-27 12:40 UTC (permalink / raw)
  To: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: djwong, dan.j.williams, david, hch, jane.chu

Introduce a PAGE_MAPPING_DAX_COW flag to support association with CoW file
mappings.  In this case, the dax-RMAP already takes the responsibility
to look up for shared files by given dax page.  The page->mapping is no
longer to used for rmap but for marking that this dax page is shared.
And to make sure disassociation works fine, we use page->index as
refcount, and clear page->mapping to the initial state when page->index
is decreased to 0.

With the help of this new flag, it is able to distinguish normal case
and CoW case, and keep the warning in normal case.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
---
 fs/dax.c                   | 65 ++++++++++++++++++++++++++++++++------
 include/linux/page-flags.h |  6 ++++
 2 files changed, 62 insertions(+), 9 deletions(-)

diff --git a/fs/dax.c b/fs/dax.c
index 250794a5b789..88879c579c1f 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -334,13 +334,46 @@ static unsigned long dax_end_pfn(void *entry)
 	for (pfn = dax_to_pfn(entry); \
 			pfn < dax_end_pfn(entry); pfn++)
 
+static inline void dax_mapping_set_cow_flag(struct address_space *mapping)
+{
+	mapping = (struct address_space *)PAGE_MAPPING_DAX_COW;
+}
+
+static inline bool dax_mapping_is_cow(struct address_space *mapping)
+{
+	return (unsigned long)mapping == PAGE_MAPPING_DAX_COW;
+}
+
 /*
- * TODO: for reflink+dax we need a way to associate a single page with
- * multiple address_space instances at different linear_page_index()
- * offsets.
+ * Set or Update the page->mapping with FS_DAX_MAPPING_COW flag.
+ * Return true if it is an Update.
+ */
+static inline bool dax_mapping_set_cow(struct page *page)
+{
+	if (page->mapping) {
+		/* flag already set */
+		if (dax_mapping_is_cow(page->mapping))
+			return false;
+
+		/*
+		 * This page has been mapped even before it is shared, just
+		 * need to set this FS_DAX_MAPPING_COW flag.
+		 */
+		dax_mapping_set_cow_flag(page->mapping);
+		return true;
+	}
+	/* Newly associate CoW mapping */
+	dax_mapping_set_cow_flag(page->mapping);
+	return false;
+}
+
+/*
+ * When it is called in dax_insert_entry(), the cow flag will indicate that
+ * whether this entry is shared by multiple files.  If so, set the page->mapping
+ * to be FS_DAX_MAPPING_COW, and use page->index as refcount.
  */
 static void dax_associate_entry(void *entry, struct address_space *mapping,
-		struct vm_area_struct *vma, unsigned long address)
+		struct vm_area_struct *vma, unsigned long address, bool cow)
 {
 	unsigned long size = dax_entry_size(entry), pfn, index;
 	int i = 0;
@@ -352,9 +385,17 @@ static void dax_associate_entry(void *entry, struct address_space *mapping,
 	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++;
+		if (cow) {
+			if (dax_mapping_set_cow(page)) {
+				/* Was normal, now updated to CoW */
+				page->index = 2;
+			} else
+				page->index++;
+		} else {
+			WARN_ON_ONCE(page->mapping);
+			page->mapping = mapping;
+			page->index = index + i++;
+		}
 	}
 }
 
@@ -370,7 +411,12 @@ static void dax_disassociate_entry(void *entry, struct address_space *mapping,
 		struct page *page = pfn_to_page(pfn);
 
 		WARN_ON_ONCE(trunc && page_ref_count(page) > 1);
-		WARN_ON_ONCE(page->mapping && page->mapping != mapping);
+		if (!dax_mapping_is_cow(page->mapping)) {
+			/* keep the CoW flag if this page is still shared */
+			if (page->index-- > 0)
+				continue;
+		} else
+			WARN_ON_ONCE(page->mapping && page->mapping != mapping);
 		page->mapping = NULL;
 		page->index = 0;
 	}
@@ -810,7 +856,8 @@ static void *dax_insert_entry(struct xa_state *xas,
 		void *old;
 
 		dax_disassociate_entry(entry, mapping, false);
-		dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address);
+		dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address,
+				false);
 		/*
 		 * Only swap our new entry into the page cache if the current
 		 * entry is a zero page or an empty entry.  If a normal PTE or
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 1c3b6e5c8bfd..6370d279795a 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -572,6 +572,12 @@ __PAGEFLAG(Reported, reported, PF_NO_COMPOUND)
 #define PAGE_MAPPING_KSM	(PAGE_MAPPING_ANON | PAGE_MAPPING_MOVABLE)
 #define PAGE_MAPPING_FLAGS	(PAGE_MAPPING_ANON | PAGE_MAPPING_MOVABLE)
 
+/*
+ * Different with flags above, this flag is used only for fsdax mode.  It
+ * indicates that this page->mapping is now under reflink case.
+ */
+#define PAGE_MAPPING_DAX_COW	0x1
+
 static __always_inline int PageMappingFlags(struct page *page)
 {
 	return ((unsigned long)page->mapping & PAGE_MAPPING_FLAGS) != 0;
-- 
2.34.1





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

* Re: [PATCH v10 1/9] dax: Introduce holder for dax_device
  2022-01-27 12:40 ` [PATCH v10 1/9] dax: Introduce holder for dax_device Shiyang Ruan
@ 2022-01-27 16:13   ` kernel test robot
  2022-01-27 16:44   ` kernel test robot
  2022-02-02 13:03   ` Christoph Hellwig
  2 siblings, 0 replies; 36+ messages in thread
From: kernel test robot @ 2022-01-27 16:13 UTC (permalink / raw)
  To: Shiyang Ruan, linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: llvm, kbuild-all, djwong, dan.j.williams, david, hch, jane.chu

Hi Shiyang,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linux/master]
[also build test WARNING on linus/master v5.17-rc1 next-20220127]
[cannot apply to xfs-linux/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/20220127-204239
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2c271fe77d52a0555161926c232cd5bc07178b39
config: arm-imx_v4_v5_defconfig (https://download.01.org/0day-ci/archive/20220128/202201280053.mWAlT70p-lkp@intel.com/config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project f32dccb9a43b02ce4e540d6ba5dbbdb188f2dc7d)
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 arm cross compiling tool for clang build
        # apt-get install binutils-arm-linux-gnueabi
        # https://github.com/0day-ci/linux/commit/57669ed05e93b37d995c5247eebe218ab2058c9a
        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/20220127-204239
        git checkout 57669ed05e93b37d995c5247eebe218ab2058c9a
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash fs/iomap/

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

   In file included from fs/iomap/buffered-io.c:13:
>> include/linux/dax.h:73:16: warning: declaration of 'struct dax_holder_operations' will not be visible outside of this function [-Wvisibility]
                   const struct dax_holder_operations *ops)
                                ^
   1 warning generated.


vim +73 include/linux/dax.h

    48	
    49	void dax_register_holder(struct dax_device *dax_dev, void *holder,
    50			const struct dax_holder_operations *ops);
    51	void dax_unregister_holder(struct dax_device *dax_dev);
    52	void *dax_get_holder(struct dax_device *dax_dev);
    53	void put_dax(struct dax_device *dax_dev);
    54	void kill_dax(struct dax_device *dax_dev);
    55	void dax_write_cache(struct dax_device *dax_dev, bool wc);
    56	bool dax_write_cache_enabled(struct dax_device *dax_dev);
    57	bool dax_synchronous(struct dax_device *dax_dev);
    58	void set_dax_synchronous(struct dax_device *dax_dev);
    59	/*
    60	 * Check if given mapping is supported by the file / underlying device.
    61	 */
    62	static inline bool daxdev_mapping_supported(struct vm_area_struct *vma,
    63						     struct dax_device *dax_dev)
    64	{
    65		if (!(vma->vm_flags & VM_SYNC))
    66			return true;
    67		if (!IS_DAX(file_inode(vma->vm_file)))
    68			return false;
    69		return dax_synchronous(dax_dev);
    70	}
    71	#else
    72	static inline void dax_register_holder(struct dax_device *dax_dev, void *holder,
  > 73			const struct dax_holder_operations *ops)
    74	{
    75	}
    76	static inline void dax_unregister_holder(struct dax_device *dax_dev)
    77	{
    78	}
    79	static inline void *dax_get_holder(struct dax_device *dax_dev)
    80	{
    81		return NULL;
    82	}
    83	static inline struct dax_device *alloc_dax(void *private,
    84			const struct dax_operations *ops)
    85	{
    86		/*
    87		 * Callers should check IS_ENABLED(CONFIG_DAX) to know if this
    88		 * NULL is an error or expected.
    89		 */
    90		return NULL;
    91	}
    92	static inline void put_dax(struct dax_device *dax_dev)
    93	{
    94	}
    95	static inline void kill_dax(struct dax_device *dax_dev)
    96	{
    97	}
    98	static inline void dax_write_cache(struct dax_device *dax_dev, bool wc)
    99	{
   100	}
   101	static inline bool dax_write_cache_enabled(struct dax_device *dax_dev)
   102	{
   103		return false;
   104	}
   105	static inline bool dax_synchronous(struct dax_device *dax_dev)
   106	{
   107		return true;
   108	}
   109	static inline void set_dax_synchronous(struct dax_device *dax_dev)
   110	{
   111	}
   112	static inline bool daxdev_mapping_supported(struct vm_area_struct *vma,
   113					struct dax_device *dax_dev)
   114	{
   115		return !(vma->vm_flags & VM_SYNC);
   116	}
   117	#endif
   118	

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


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

* Re: [PATCH v10 1/9] dax: Introduce holder for dax_device
  2022-01-27 12:40 ` [PATCH v10 1/9] dax: Introduce holder for dax_device Shiyang Ruan
  2022-01-27 16:13   ` kernel test robot
@ 2022-01-27 16:44   ` kernel test robot
  2022-02-02 13:03   ` Christoph Hellwig
  2 siblings, 0 replies; 36+ messages in thread
From: kernel test robot @ 2022-01-27 16:44 UTC (permalink / raw)
  To: Shiyang Ruan, linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: kbuild-all, djwong, dan.j.williams, david, hch, jane.chu

Hi Shiyang,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linux/master]
[also build test WARNING on linus/master v5.17-rc1 next-20220127]
[cannot apply to xfs-linux/for-next hnaz-mm/master]
[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/20220127-204239
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2c271fe77d52a0555161926c232cd5bc07178b39
config: powerpc-allnoconfig (https://download.01.org/0day-ci/archive/20220128/202201280035.A565CZYV-lkp@intel.com/config)
compiler: powerpc-linux-gcc (GCC) 11.2.0
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
        # https://github.com/0day-ci/linux/commit/57669ed05e93b37d995c5247eebe218ab2058c9a
        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/20220127-204239
        git checkout 57669ed05e93b37d995c5247eebe218ab2058c9a
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=powerpc SHELL=/bin/bash

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

   In file included from mm/filemap.c:15:
>> include/linux/dax.h:73:30: warning: 'struct dax_holder_operations' declared inside parameter list will not be visible outside of this definition or declaration
      73 |                 const struct dax_holder_operations *ops)
         |                              ^~~~~~~~~~~~~~~~~~~~~


vim +73 include/linux/dax.h

    48	
    49	void dax_register_holder(struct dax_device *dax_dev, void *holder,
    50			const struct dax_holder_operations *ops);
    51	void dax_unregister_holder(struct dax_device *dax_dev);
    52	void *dax_get_holder(struct dax_device *dax_dev);
    53	void put_dax(struct dax_device *dax_dev);
    54	void kill_dax(struct dax_device *dax_dev);
    55	void dax_write_cache(struct dax_device *dax_dev, bool wc);
    56	bool dax_write_cache_enabled(struct dax_device *dax_dev);
    57	bool dax_synchronous(struct dax_device *dax_dev);
    58	void set_dax_synchronous(struct dax_device *dax_dev);
    59	/*
    60	 * Check if given mapping is supported by the file / underlying device.
    61	 */
    62	static inline bool daxdev_mapping_supported(struct vm_area_struct *vma,
    63						     struct dax_device *dax_dev)
    64	{
    65		if (!(vma->vm_flags & VM_SYNC))
    66			return true;
    67		if (!IS_DAX(file_inode(vma->vm_file)))
    68			return false;
    69		return dax_synchronous(dax_dev);
    70	}
    71	#else
    72	static inline void dax_register_holder(struct dax_device *dax_dev, void *holder,
  > 73			const struct dax_holder_operations *ops)
    74	{
    75	}
    76	static inline void dax_unregister_holder(struct dax_device *dax_dev)
    77	{
    78	}
    79	static inline void *dax_get_holder(struct dax_device *dax_dev)
    80	{
    81		return NULL;
    82	}
    83	static inline struct dax_device *alloc_dax(void *private,
    84			const struct dax_operations *ops)
    85	{
    86		/*
    87		 * Callers should check IS_ENABLED(CONFIG_DAX) to know if this
    88		 * NULL is an error or expected.
    89		 */
    90		return NULL;
    91	}
    92	static inline void put_dax(struct dax_device *dax_dev)
    93	{
    94	}
    95	static inline void kill_dax(struct dax_device *dax_dev)
    96	{
    97	}
    98	static inline void dax_write_cache(struct dax_device *dax_dev, bool wc)
    99	{
   100	}
   101	static inline bool dax_write_cache_enabled(struct dax_device *dax_dev)
   102	{
   103		return false;
   104	}
   105	static inline bool dax_synchronous(struct dax_device *dax_dev)
   106	{
   107		return true;
   108	}
   109	static inline void set_dax_synchronous(struct dax_device *dax_dev)
   110	{
   111	}
   112	static inline bool daxdev_mapping_supported(struct vm_area_struct *vma,
   113					struct dax_device *dax_dev)
   114	{
   115		return !(vma->vm_flags & VM_SYNC);
   116	}
   117	#endif
   118	

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


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

* Re: [PATCH v10 8/9] xfs: Implement ->notify_failure() for XFS
  2022-01-27 12:40 ` [PATCH v10 8/9] xfs: Implement ->notify_failure() for XFS Shiyang Ruan
@ 2022-01-27 17:56   ` kernel test robot
  2022-01-27 19:39   ` kernel test robot
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 36+ messages in thread
From: kernel test robot @ 2022-01-27 17:56 UTC (permalink / raw)
  To: Shiyang Ruan, linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: kbuild-all, djwong, dan.j.williams, david, hch, jane.chu

Hi Shiyang,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linux/master]
[also build test WARNING on linus/master v5.17-rc1 next-20220127]
[cannot apply to xfs-linux/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/20220127-204239
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2c271fe77d52a0555161926c232cd5bc07178b39
config: ia64-defconfig (https://download.01.org/0day-ci/archive/20220128/202201280101.4ECasWmD-lkp@intel.com/config)
compiler: ia64-linux-gcc (GCC) 11.2.0
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
        # https://github.com/0day-ci/linux/commit/cb7650562991fc273fbf4c53b6e3db4bb9bb0b5e
        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/20220127-204239
        git checkout cb7650562991fc273fbf4c53b6e3db4bb9bb0b5e
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=ia64 SHELL=/bin/bash fs/xfs/

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

   In file included from fs/xfs/xfs_buf.h:14,
                    from fs/xfs/xfs_linux.h:80,
                    from fs/xfs/xfs.h:22,
                    from fs/xfs/xfs_notify_failure.c:6:
   include/linux/dax.h:73:30: warning: 'struct dax_holder_operations' declared inside parameter list will not be visible outside of this definition or declaration
      73 |                 const struct dax_holder_operations *ops)
         |                              ^~~~~~~~~~~~~~~~~~~~~
   fs/xfs/xfs_notify_failure.c:220:14: error: variable 'xfs_dax_holder_operations' has initializer but incomplete type
     220 | const struct dax_holder_operations xfs_dax_holder_operations = {
         |              ^~~~~~~~~~~~~~~~~~~~~
   fs/xfs/xfs_notify_failure.c:221:10: error: 'const struct dax_holder_operations' has no member named 'notify_failure'
     221 |         .notify_failure         = xfs_dax_notify_failure,
         |          ^~~~~~~~~~~~~~
>> fs/xfs/xfs_notify_failure.c:221:35: warning: excess elements in struct initializer
     221 |         .notify_failure         = xfs_dax_notify_failure,
         |                                   ^~~~~~~~~~~~~~~~~~~~~~
   fs/xfs/xfs_notify_failure.c:221:35: note: (near initialization for 'xfs_dax_holder_operations')
   fs/xfs/xfs_notify_failure.c:220:36: error: storage size of 'xfs_dax_holder_operations' isn't known
     220 | const struct dax_holder_operations xfs_dax_holder_operations = {
         |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~


vim +221 fs/xfs/xfs_notify_failure.c

   219	
   220	const struct dax_holder_operations xfs_dax_holder_operations = {
 > 221		.notify_failure		= xfs_dax_notify_failure,

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


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

* Re: [PATCH v10 8/9] xfs: Implement ->notify_failure() for XFS
  2022-01-27 12:40 ` [PATCH v10 8/9] xfs: Implement ->notify_failure() for XFS Shiyang Ruan
  2022-01-27 17:56   ` kernel test robot
@ 2022-01-27 19:39   ` kernel test robot
  2022-02-01 20:41   ` Darrick J. Wong
  2022-02-16  1:56   ` [PATCH v10 " Dan Williams
  3 siblings, 0 replies; 36+ messages in thread
From: kernel test robot @ 2022-01-27 19:39 UTC (permalink / raw)
  To: Shiyang Ruan, linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel
  Cc: kbuild-all, djwong, dan.j.williams, david, hch, jane.chu

Hi Shiyang,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linux/master]
[also build test ERROR on linus/master v5.17-rc1 next-20220127]
[cannot apply to xfs-linux/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/20220127-204239
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2c271fe77d52a0555161926c232cd5bc07178b39
config: ia64-defconfig (https://download.01.org/0day-ci/archive/20220128/202201280314.SI8wtlfT-lkp@intel.com/config)
compiler: ia64-linux-gcc (GCC) 11.2.0
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
        # https://github.com/0day-ci/linux/commit/cb7650562991fc273fbf4c53b6e3db4bb9bb0b5e
        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/20220127-204239
        git checkout cb7650562991fc273fbf4c53b6e3db4bb9bb0b5e
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=ia64 SHELL=/bin/bash fs/

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

   In file included from fs/xfs/xfs_buf.h:14,
                    from fs/xfs/xfs_linux.h:80,
                    from fs/xfs/xfs.h:22,
                    from fs/xfs/xfs_buf.c:6:
   include/linux/dax.h:73:30: warning: 'struct dax_holder_operations' declared inside parameter list will not be visible outside of this definition or declaration
      73 |                 const struct dax_holder_operations *ops)
         |                              ^~~~~~~~~~~~~~~~~~~~~
   fs/xfs/xfs_buf.c: In function 'xfs_alloc_buftarg':
>> fs/xfs/xfs_buf.c:1959:33: error: passing argument 3 of 'dax_register_holder' from incompatible pointer type [-Werror=incompatible-pointer-types]
    1959 |                                 &xfs_dax_holder_operations);
         |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~
         |                                 |
         |                                 const struct dax_holder_operations *
   In file included from fs/xfs/xfs_buf.h:14,
                    from fs/xfs/xfs_linux.h:80,
                    from fs/xfs/xfs.h:22,
                    from fs/xfs/xfs_buf.c:6:
   include/linux/dax.h:73:53: note: expected 'const struct dax_holder_operations *' but argument is of type 'const struct dax_holder_operations *'
      73 |                 const struct dax_holder_operations *ops)
         |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
   cc1: some warnings being treated as errors
--
   In file included from fs/xfs/xfs_buf.h:14,
                    from fs/xfs/xfs_linux.h:80,
                    from fs/xfs/xfs.h:22,
                    from fs/xfs/xfs_notify_failure.c:6:
   include/linux/dax.h:73:30: warning: 'struct dax_holder_operations' declared inside parameter list will not be visible outside of this definition or declaration
      73 |                 const struct dax_holder_operations *ops)
         |                              ^~~~~~~~~~~~~~~~~~~~~
>> fs/xfs/xfs_notify_failure.c:220:14: error: variable 'xfs_dax_holder_operations' has initializer but incomplete type
     220 | const struct dax_holder_operations xfs_dax_holder_operations = {
         |              ^~~~~~~~~~~~~~~~~~~~~
>> fs/xfs/xfs_notify_failure.c:221:10: error: 'const struct dax_holder_operations' has no member named 'notify_failure'
     221 |         .notify_failure         = xfs_dax_notify_failure,
         |          ^~~~~~~~~~~~~~
   fs/xfs/xfs_notify_failure.c:221:35: warning: excess elements in struct initializer
     221 |         .notify_failure         = xfs_dax_notify_failure,
         |                                   ^~~~~~~~~~~~~~~~~~~~~~
   fs/xfs/xfs_notify_failure.c:221:35: note: (near initialization for 'xfs_dax_holder_operations')
>> fs/xfs/xfs_notify_failure.c:220:36: error: storage size of 'xfs_dax_holder_operations' isn't known
     220 | const struct dax_holder_operations xfs_dax_holder_operations = {
         |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~


vim +/dax_register_holder +1959 fs/xfs/xfs_buf.c

  1938	
  1939	struct xfs_buftarg *
  1940	xfs_alloc_buftarg(
  1941		struct xfs_mount	*mp,
  1942		struct block_device	*bdev)
  1943	{
  1944		xfs_buftarg_t		*btp;
  1945	
  1946		btp = kmem_zalloc(sizeof(*btp), KM_NOFS);
  1947	
  1948		btp->bt_mount = mp;
  1949		btp->bt_dev =  bdev->bd_dev;
  1950		btp->bt_bdev = bdev;
  1951		btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off);
  1952		if (btp->bt_daxdev) {
  1953			if (dax_get_holder(btp->bt_daxdev)) {
  1954				xfs_err(mp, "DAX device already in use?!");
  1955				goto error_free;
  1956			}
  1957	
  1958			dax_register_holder(btp->bt_daxdev, mp,
> 1959					&xfs_dax_holder_operations);
  1960		}
  1961	
  1962		/*
  1963		 * Buffer IO error rate limiting. Limit it to no more than 10 messages
  1964		 * per 30 seconds so as to not spam logs too much on repeated errors.
  1965		 */
  1966		ratelimit_state_init(&btp->bt_ioerror_rl, 30 * HZ,
  1967				     DEFAULT_RATELIMIT_BURST);
  1968	
  1969		if (xfs_setsize_buftarg_early(btp, bdev))
  1970			goto error_free;
  1971	
  1972		if (list_lru_init(&btp->bt_lru))
  1973			goto error_free;
  1974	
  1975		if (percpu_counter_init(&btp->bt_io_count, 0, GFP_KERNEL))
  1976			goto error_lru;
  1977	
  1978		btp->bt_shrinker.count_objects = xfs_buftarg_shrink_count;
  1979		btp->bt_shrinker.scan_objects = xfs_buftarg_shrink_scan;
  1980		btp->bt_shrinker.seeks = DEFAULT_SEEKS;
  1981		btp->bt_shrinker.flags = SHRINKER_NUMA_AWARE;
  1982		if (register_shrinker(&btp->bt_shrinker))
  1983			goto error_pcpu;
  1984		return btp;
  1985	
  1986	error_pcpu:
  1987		percpu_counter_destroy(&btp->bt_io_count);
  1988	error_lru:
  1989		list_lru_destroy(&btp->bt_lru);
  1990	error_free:
  1991		kmem_free(btp);
  1992		return NULL;
  1993	}
  1994	

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


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

* Re: [PATCH v10 8/9] xfs: Implement ->notify_failure() for XFS
  2022-01-27 12:40 ` [PATCH v10 8/9] xfs: Implement ->notify_failure() for XFS Shiyang Ruan
  2022-01-27 17:56   ` kernel test robot
  2022-01-27 19:39   ` kernel test robot
@ 2022-02-01 20:41   ` Darrick J. Wong
  2022-02-13 13:02     ` [PATCH v10.1 " Shiyang Ruan
  2022-02-16  1:56   ` [PATCH v10 " Dan Williams
  3 siblings, 1 reply; 36+ messages in thread
From: Darrick J. Wong @ 2022-02-01 20:41 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel,
	dan.j.williams, david, hch, jane.chu

On Thu, Jan 27, 2022 at 08:40:57PM +0800, Shiyang Ruan wrote:
> Introduce xfs_notify_failure.c to handle failure related works, such as
> implement ->notify_failure(), register/unregister dax holder in xfs, and
> so on.
> 
> 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.
> 
> Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
> ---
>  fs/xfs/Makefile             |   1 +
>  fs/xfs/xfs_buf.c            |  12 ++
>  fs/xfs/xfs_fsops.c          |   3 +
>  fs/xfs/xfs_mount.h          |   1 +
>  fs/xfs/xfs_notify_failure.c | 222 ++++++++++++++++++++++++++++++++++++
>  fs/xfs/xfs_notify_failure.h |  10 ++
>  6 files changed, 249 insertions(+)
>  create mode 100644 fs/xfs/xfs_notify_failure.c
>  create mode 100644 fs/xfs/xfs_notify_failure.h
> 
> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> index 04611a1068b4..389970b3e13b 100644
> --- a/fs/xfs/Makefile
> +++ b/fs/xfs/Makefile
> @@ -84,6 +84,7 @@ xfs-y				+= xfs_aops.o \
>  				   xfs_message.o \
>  				   xfs_mount.o \
>  				   xfs_mru_cache.o \
> +				   xfs_notify_failure.o \
>  				   xfs_pwork.o \
>  				   xfs_reflink.o \
>  				   xfs_stats.o \
> diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
> index b45e0d50a405..017010b3d601 100644
> --- a/fs/xfs/xfs_buf.c
> +++ b/fs/xfs/xfs_buf.c
> @@ -19,6 +19,7 @@
>  #include "xfs_errortag.h"
>  #include "xfs_error.h"
>  #include "xfs_ag.h"
> +#include "xfs_notify_failure.h"
>  
>  static struct kmem_cache *xfs_buf_cache;
>  
> @@ -1892,6 +1893,8 @@ xfs_free_buftarg(
>  	list_lru_destroy(&btp->bt_lru);
>  
>  	blkdev_issue_flush(btp->bt_bdev);
> +	if (btp->bt_daxdev)
> +		dax_unregister_holder(btp->bt_daxdev);
>  	fs_put_dax(btp->bt_daxdev);
>  
>  	kmem_free(btp);
> @@ -1946,6 +1949,15 @@ xfs_alloc_buftarg(
>  	btp->bt_dev =  bdev->bd_dev;
>  	btp->bt_bdev = bdev;
>  	btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off);
> +	if (btp->bt_daxdev) {
> +		if (dax_get_holder(btp->bt_daxdev)) {
> +			xfs_err(mp, "DAX device already in use?!");
> +			goto error_free;
> +		}
> +
> +		dax_register_holder(btp->bt_daxdev, mp,
> +				&xfs_dax_holder_operations);

Um... is XFS required to take a lock here?  How do we prevent parallel
mounts of filesystems on two partitions from breaking each other?

> +	}
>  
>  	/*
>  	 * Buffer IO error rate limiting. Limit it to no more than 10 messages
> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> index 33e26690a8c4..d4d36c5bef11 100644
> --- a/fs/xfs/xfs_fsops.c
> +++ b/fs/xfs/xfs_fsops.c
> @@ -542,6 +542,9 @@ xfs_do_force_shutdown(
>  	} else if (flags & SHUTDOWN_CORRUPT_INCORE) {
>  		tag = XFS_PTAG_SHUTDOWN_CORRUPT;
>  		why = "Corruption of in-memory data";
> +	} else if (flags & SHUTDOWN_CORRUPT_ONDISK) {
> +		tag = XFS_PTAG_SHUTDOWN_CORRUPT;
> +		why = "Corruption of on-disk metadata";
>  	} else {
>  		tag = XFS_PTAG_SHUTDOWN_IOERROR;
>  		why = "Metadata I/O Error";
> diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
> index 00720a02e761..47ff4ac53c4c 100644
> --- a/fs/xfs/xfs_mount.h
> +++ b/fs/xfs/xfs_mount.h
> @@ -435,6 +435,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_ONDISK	0x0010  /* corrupt metadata on device */
>  
>  #define XFS_SHUTDOWN_STRINGS \
>  	{ SHUTDOWN_META_IO_ERROR,	"metadata_io" }, \
> diff --git a/fs/xfs/xfs_notify_failure.c b/fs/xfs/xfs_notify_failure.c
> new file mode 100644
> index 000000000000..6abaa043f4bc
> --- /dev/null
> +++ b/fs/xfs/xfs_notify_failure.c
> @@ -0,0 +1,222 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 Fujitsu.  All Rights Reserved.

2022?

> + */
> +
> +#include "xfs.h"
> +#include "xfs_shared.h"
> +#include "xfs_format.h"
> +#include "xfs_log_format.h"
> +#include "xfs_trans_resv.h"
> +#include "xfs_mount.h"
> +#include "xfs_alloc.h"
> +#include "xfs_bit.h"
> +#include "xfs_btree.h"
> +#include "xfs_inode.h"
> +#include "xfs_icache.h"
> +#include "xfs_rmap.h"
> +#include "xfs_rmap_btree.h"
> +#include "xfs_rtalloc.h"
> +#include "xfs_trans.h"
> +
> +#include <linux/mm.h>
> +#include <linux/dax.h>
> +
> +struct failure_info {
> +	xfs_agblock_t		startblock;
> +	xfs_extlen_t		blockcount;
> +	int			mf_flags;
> +};
> +
> +#if IS_ENABLED(CONFIG_MEMORY_FAILURE) && IS_ENABLED(CONFIG_FS_DAX)
> +static pgoff_t
> +xfs_failure_pgoff(
> +	struct xfs_mount		*mp,
> +	const struct xfs_rmap_irec	*rec,
> +	const struct failure_info	*notify)
> +{
> +	uint64_t			pos = rec->rm_offset;
> +
> +	if (notify->startblock > rec->rm_startblock)
> +		pos += XFS_FSB_TO_B(mp,
> +				notify->startblock - rec->rm_startblock);
> +	return pos >> PAGE_SHIFT;
> +}
> +
> +static unsigned long
> +xfs_failure_pgcnt(
> +	struct xfs_mount		*mp,
> +	const struct xfs_rmap_irec	*rec,
> +	const struct failure_info	*notify)
> +{
> +	xfs_agblock_t			end_rec;
> +	xfs_agblock_t			end_notify;
> +	xfs_agblock_t			start_cross;
> +	xfs_agblock_t			end_cross;
> +
> +	start_cross = max(rec->rm_startblock, notify->startblock);
> +
> +	end_rec = rec->rm_startblock + rec->rm_blockcount;
> +	end_notify = notify->startblock + notify->blockcount;
> +	end_cross = min(end_rec, end_notify);
> +
> +	return XFS_FSB_TO_B(mp, end_cross - start_cross) >> PAGE_SHIFT;
> +}
> +
> +static int
> +xfs_dax_failure_fn(
> +	struct xfs_btree_cur		*cur,
> +	const struct xfs_rmap_irec	*rec,
> +	void				*data)
> +{
> +	struct xfs_mount		*mp = cur->bc_mp;
> +	struct xfs_inode		*ip;
> +	struct failure_info		*notify = data;
> +	int				error = 0;
> +
> +	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
> +	    (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
> +		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
> +		return -EFSCORRUPTED;
> +	}
> +
> +	/* Get files that incore, filter out others that are not in use. */
> +	error = xfs_iget(mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE,
> +			 0, &ip);
> +	/* Continue the rmap query if the inode isn't incore */
> +	if (error == -ENODATA)
> +		return 0;
> +	if (error)
> +		return error;
> +
> +	error = mf_dax_kill_procs(VFS_I(ip)->i_mapping,
> +				  xfs_failure_pgoff(mp, rec, notify),
> +				  xfs_failure_pgcnt(mp, rec, notify),
> +				  notify->mf_flags);
> +	xfs_irele(ip);
> +	return error;
> +}
> +#else
> +static int
> +xfs_dax_failure_fn(
> +	struct xfs_btree_cur		*cur,
> +	const struct xfs_rmap_irec	*rec,
> +	void				*data)
> +{
> +	struct xfs_mount		*mp = cur->bc_mp;
> +
> +	/* No other option besides shutting down the fs. */
> +	xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
> +	return -EFSCORRUPTED;
> +}
> +#endif /* CONFIG_MEMORY_FAILURE && CONFIG_FS_DAX */
> +
> +static int
> +xfs_dax_notify_ddev_failure(
> +	struct xfs_mount	*mp,
> +	xfs_daddr_t		daddr,
> +	xfs_daddr_t		bblen,
> +	int			mf_flags)
> +{
> +	struct xfs_trans	*tp = NULL;
> +	struct xfs_btree_cur	*cur = NULL;
> +	struct xfs_buf		*agf_bp = NULL;
> +	struct failure_info	notify;
> +	int			error = 0;
> +	xfs_fsblock_t		fsbno = XFS_DADDR_TO_FSB(mp, daddr);
> +	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(mp, fsbno);
> +	xfs_fsblock_t		end_fsbno = XFS_DADDR_TO_FSB(mp, daddr + bblen);
> +	xfs_agnumber_t		end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno);
> +
> +	/*
> +	 * Once a file is found by rmap, we take the intersection of two ranges:
> +	 * notification range and file extent range, to make sure we won't go
> +	 * out of scope.
> +	 */
> +	notify.mf_flags = mf_flags;
> +	notify.startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
> +	notify.blockcount = XFS_BB_TO_FSB(mp, bblen);

This looks even more incorrect than what V9 did, because the notify
structure helps the per-AG rmap record iterator pick the records (within
that AG) that overlap the failed area, but we haven't selected any AGs
yet.

> +
> +	error = xfs_trans_alloc_empty(mp, &tp);
> +	if (error)
> +		return error;
> +
> +	for (; agno <= end_agno; agno++) {
> +		struct xfs_rmap_irec	ri_low = { };
> +		struct xfs_rmap_irec	ri_high;

So declare @notify here:

		struct failure_info	notify;

> +
> +		error = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
> +		if (error)
> +			break;
> +
> +		cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, agf_bp->b_pag);
> +
> +		/*
> +		 * Set the rmap range from ri_low to ri_high, which represents
> +		 * a [start, end] where we looking for the files or metadata.
> +		 * The part of range out of a AG will be ignored.  So, it's fine
> +		 * to set ri_low to "startblock" in all loops.  When it reaches
> +		 * the last AG, set the ri_high to "endblock" to make sure we
> +		 * actually end at the end.
> +		 */
> +		memset(&ri_high, 0xFF, sizeof(ri_high));
> +		ri_low.rm_startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
> +		if (agno == end_agno)
> +			ri_high.rm_startblock = XFS_FSB_TO_AGBNO(mp, end_fsbno);

And initialize it here:

		struct xfs_agf		*agf = agf_bp->b_addr;
		xfs_agblock_t		agend;

		agend = min(be32_to_cpu(agf->agf_length),
				ri_high.rm_startblock);
		notify.startblock = ri_low.rm_startblock;
		notify.blockcount = agend - ri_low.rm_startblock;

Before passing @notify to the rmap iteration.

> +
> +		error = xfs_rmap_query_range(cur, &ri_low, &ri_high,
> +				xfs_dax_failure_fn, &notify);
> +		xfs_btree_del_cursor(cur, error);
> +		xfs_trans_brelse(tp, agf_bp);
> +		if (error)
> +			break;
> +
> +		fsbno = XFS_AGB_TO_FSB(mp, agno + 1, 0);
> +	}
> +
> +	xfs_trans_cancel(tp);
> +	return error;
> +}
> +
> +static int
> +xfs_dax_notify_failure(
> +	struct dax_device	*dax_dev,
> +	u64			offset,
> +	u64			len,
> +	int			mf_flags)
> +{
> +	struct xfs_mount	*mp = dax_get_holder(dax_dev);
> +
> +	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_daxdev == dax_dev) {
> +		xfs_warn(mp,
> +			 "notify_failure() not supported on 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_ONDISK);
> +		return -EFSCORRUPTED;
> +	}
> +
> +	if (!xfs_has_rmapbt(mp)) {
> +		xfs_warn(mp, "notify_failure() needs rmapbt enabled!");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	if (offset < mp->m_ddev_targp->bt_dax_part_off ||
> +	    ((offset + len) > mp->m_ddev_targp->bt_bdev->bd_nr_sectors <<
> +				SECTOR_SHIFT)) {

I think this logic is wrong?

If the failed region overlaps the entire fs, offset will be less than
bt_dax_part_off.

We're looking for reasons to ignore the failure event, right?

So if the failed area is before the start of the fs then we can ignore
it:

	if (offset + len < mp->m_ddev_targp->bt_dax_part_off)
		return -ENXIO;

and if the failed area starts after the fs then we can ignore that too:

	bdev_end = mp->m_ddev_targp->bt_dax_part_off +
		   mp->m_ddev_targp->bt_bdev->bd_nr_sectors;
	if (offset > bdev_end)
		return -ENXIO;

Right?

--D

> +		xfs_warn(mp, "notify_failure() goes out of the scope.");
> +		return -ENXIO;
> +	}
> +
> +	offset -= mp->m_ddev_targp->bt_dax_part_off;
> +	return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len),
> +			mf_flags);
> +}
> +
> +const struct dax_holder_operations xfs_dax_holder_operations = {
> +	.notify_failure		= xfs_dax_notify_failure,
> +};
> diff --git a/fs/xfs/xfs_notify_failure.h b/fs/xfs/xfs_notify_failure.h
> new file mode 100644
> index 000000000000..f40cb315e7ce
> --- /dev/null
> +++ b/fs/xfs/xfs_notify_failure.h
> @@ -0,0 +1,10 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 Fujitsu.  All Rights Reserved.
> + */
> +#ifndef __XFS_NOTIFY_FAILURE_H__
> +#define __XFS_NOTIFY_FAILURE_H__
> +
> +extern const struct dax_holder_operations xfs_dax_holder_operations;
> +
> +#endif  /* __XFS_NOTIFY_FAILURE_H__ */
> -- 
> 2.34.1
> 
> 
> 


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

* Re: [PATCH v10 2/9] mm: factor helpers for memory_failure_dev_pagemap
  2022-01-27 12:40 ` [PATCH v10 2/9] mm: factor helpers for memory_failure_dev_pagemap Shiyang Ruan
@ 2022-02-01 21:03   ` Matthew Wilcox
  2022-02-15 22:11   ` Dan Williams
  1 sibling, 0 replies; 36+ messages in thread
From: Matthew Wilcox @ 2022-02-01 21:03 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, djwong,
	dan.j.williams, david, hch, jane.chu, Christoph Hellwig

On Thu, Jan 27, 2022 at 08:40:51PM +0800, Shiyang Ruan wrote:
> +static int mf_generic_kill_procs(unsigned long long pfn, int flags,
> +		struct dev_pagemap *pgmap)
> +{
> +	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;
> +
> +	if (hwpoison_filter(page))
> +		return 0;
> +
> +	if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
> +		/*
> +		 * TODO: Handle HMM pages which may need coordination
> +		 * with device-side memory.
> +		 */
> +		return -EBUSY;
> +	}
> +
> +	/*
> +	 * Use this flag as an indication that the dax page has been
> +	 * remapped UC to prevent speculative consumption of poison.
> +	 */
> +	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, &to_kill, true);
> +
> +	unmap_and_kill(&to_kill, pfn, page->mapping, page->index, flags);
> +	dax_unlock_page(page, cookie);
> +	return 0;
> +}

There's an assumption here that fsdax only has order-0 pages.
pfn_to_page() is going to return the precise page for that pfn, but then
page->mapping and page->index are not valid for tail pages.

I'm currently trying to folio-ise memory-failure.c, and it is not
going well!  There are several places where it's hard to tell
what should happen.



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

* Re: [PATCH v10 1/9] dax: Introduce holder for dax_device
  2022-01-27 12:40 ` [PATCH v10 1/9] dax: Introduce holder for dax_device Shiyang Ruan
  2022-01-27 16:13   ` kernel test robot
  2022-01-27 16:44   ` kernel test robot
@ 2022-02-02 13:03   ` Christoph Hellwig
  2022-02-13 12:58     ` [PATCH v10.1 " Shiyang Ruan
  2 siblings, 1 reply; 36+ messages in thread
From: Christoph Hellwig @ 2022-02-02 13:03 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, djwong,
	dan.j.williams, david, hch, jane.chu

On Thu, Jan 27, 2022 at 08:40:50PM +0800, Shiyang Ruan wrote:
> +void dax_register_holder(struct dax_device *dax_dev, void *holder,
> +		const struct dax_holder_operations *ops)
> +{
> +	if (!dax_alive(dax_dev))
> +		return;
> +
> +	dax_dev->holder_data = holder;
> +	dax_dev->holder_ops = ops;

This needs to return an error if there is another holder already.  And
some kind of locking to prevent concurrent registrations.

Also please add kerneldoc comments for the new exported functions.

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

get tends to imply getting a reference.  Maybe just dax_holder()?
That being said I can't see where we'd even want to use the holder
outside of this file.


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

* Re: [PATCH v10 4/9] fsdax: fix function description
  2022-01-27 12:40 ` [PATCH v10 4/9] fsdax: fix function description Shiyang Ruan
@ 2022-02-02 13:04   ` Christoph Hellwig
  2022-02-15 23:51     ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: Christoph Hellwig @ 2022-02-02 13:04 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, djwong,
	dan.j.williams, david, hch, jane.chu, Christoph Hellwig

Dan, can you send this to Linus for 5.17 to get it out of the queue?


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

* [PATCH v10.1 1/9] dax: Introduce holder for dax_device
  2022-02-02 13:03   ` Christoph Hellwig
@ 2022-02-13 12:58     ` Shiyang Ruan
  2022-02-15 22:06       ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: Shiyang Ruan @ 2022-02-13 12:58 UTC (permalink / raw)
  To: hch
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel, djwong,
	dan.j.williams, david, jane.chu

v10.1 update:
 - Fix build error reported by kernel test robot
 - Add return code to dax_register_holder()
 - Add write lock to prevent concurrent registrations
 - Rename dax_get_holder() to dax_holder()
 - Add kernel doc for new added functions

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
   instance 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 | 95 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/dax.h | 30 ++++++++++++++
 2 files changed, 125 insertions(+)

diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index e3029389d809..d7fb4c36bf16 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -21,6 +21,9 @@
  * @cdev: optional character interface for "device dax"
  * @private: dax driver private data
  * @flags: state and boolean properties
+ * @ops: operations for dax_device
+ * @holder_data: holder of a dax_device: could be filesystem or mapped device
+ * @holder_ops: operations for the inner holder
  */
 struct dax_device {
 	struct inode inode;
@@ -28,6 +31,9 @@ struct dax_device {
 	void *private;
 	unsigned long flags;
 	const struct dax_operations *ops;
+	void *holder_data;
+	struct percpu_rw_semaphore holder_rwsem;
+	const struct dax_holder_operations *holder_ops;
 };
 
 static dev_t dax_devt;
@@ -193,6 +199,29 @@ 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, u64 off,
+			      u64 len, int mf_flags)
+{
+	int rc, id;
+
+	id = dax_read_lock();
+	if (!dax_alive(dax_dev)) {
+		rc = -ENXIO;
+		goto out;
+	}
+
+	if (!dax_dev->holder_ops) {
+		rc = -EOPNOTSUPP;
+		goto out;
+	}
+
+	rc = dax_dev->holder_ops->notify_failure(dax_dev, off, len, mf_flags);
+out:
+	dax_read_unlock(id);
+	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)
@@ -268,6 +297,13 @@ void kill_dax(struct dax_device *dax_dev)
 
 	clear_bit(DAXDEV_ALIVE, &dax_dev->flags);
 	synchronize_srcu(&dax_srcu);
+
+	/* Lock to prevent concurrent registrations. */
+	percpu_down_write(&dax_dev->holder_rwsem);
+	/* clear holder data */
+	dax_dev->holder_ops = NULL;
+	dax_dev->holder_data = NULL;
+	percpu_up_write(&dax_dev->holder_rwsem);
 }
 EXPORT_SYMBOL_GPL(kill_dax);
 
@@ -393,6 +429,7 @@ struct dax_device *alloc_dax(void *private, const struct dax_operations *ops)
 
 	dax_dev->ops = ops;
 	dax_dev->private = private;
+	percpu_init_rwsem(&dax_dev->holder_rwsem);
 	return dax_dev;
 
  err_dev:
@@ -409,6 +446,64 @@ void put_dax(struct dax_device *dax_dev)
 }
 EXPORT_SYMBOL_GPL(put_dax);
 
+/**
+ * dax_holder() - obtain the holder of a dax device
+ * @dax_dev: a dax_device instance
+
+ * Return: the holder's data which represents the holder if registered,
+ * otherwize NULL.
+ */
+void *dax_holder(struct dax_device *dax_dev)
+{
+	if (!dax_alive(dax_dev))
+		return NULL;
+
+	return dax_dev->holder_data;
+}
+EXPORT_SYMBOL_GPL(dax_holder);
+
+/**
+ * dax_register_holder() - register a holder to a dax device
+ * @dax_dev: a dax_device instance
+ * @holder: a pointer to a holder's data which represents the holder
+ * @ops: operations of this holder
+
+ * Return: negative errno if an error occurs, otherwise 0.
+ */
+int dax_register_holder(struct dax_device *dax_dev, void *holder,
+		const struct dax_holder_operations *ops)
+{
+	if (!dax_alive(dax_dev))
+		return -ENXIO;
+
+	/* Already registered */
+	if (dax_holder(dax_dev))
+		return -EBUSY;
+
+	/* Lock to prevent concurrent registrations. */
+	percpu_down_write(&dax_dev->holder_rwsem);
+	dax_dev->holder_data = holder;
+	dax_dev->holder_ops = ops;
+	percpu_up_write(&dax_dev->holder_rwsem);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dax_register_holder);
+
+/**
+ * dax_unregister_holder() - unregister the holder for a dax device
+ * @dax_dev: a dax_device instance
+ */
+void dax_unregister_holder(struct dax_device *dax_dev)
+{
+	if (!dax_alive(dax_dev))
+		return;
+
+	dax_dev->holder_data = NULL;
+	dax_dev->holder_ops = NULL;
+}
+EXPORT_SYMBOL_GPL(dax_unregister_holder);
+
 /**
  * inode_dax: convert a public inode into its dax_dev
  * @inode: An inode with i_cdev pointing to a dax_dev
diff --git a/include/linux/dax.h b/include/linux/dax.h
index 9fc5f99a0ae2..9800d84e5b7d 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -32,8 +32,24 @@ struct dax_operations {
 	int (*zero_page_range)(struct dax_device *, pgoff_t, size_t);
 };
 
+struct dax_holder_operations {
+	/*
+	 * notify_failure - notify memory failure into inner holder device
+	 * @dax_dev: the dax device which contains the holder
+	 * @offset: offset on this dax device where memory failure occurs
+	 * @len: length of this memory failure event
+	 * @flags: action flags for memory failure handler
+	 */
+	int (*notify_failure)(struct dax_device *dax_dev, u64 offset,
+			u64 len, int mf_flags);
+};
+
 #if IS_ENABLED(CONFIG_DAX)
 struct dax_device *alloc_dax(void *private, const struct dax_operations *ops);
+int dax_register_holder(struct dax_device *dax_dev, void *holder,
+		const struct dax_holder_operations *ops);
+void dax_unregister_holder(struct dax_device *dax_dev);
+void *dax_holder(struct dax_device *dax_dev);
 void put_dax(struct dax_device *dax_dev);
 void kill_dax(struct dax_device *dax_dev);
 void dax_write_cache(struct dax_device *dax_dev, bool wc);
@@ -53,6 +69,18 @@ static inline bool daxdev_mapping_supported(struct vm_area_struct *vma,
 	return dax_synchronous(dax_dev);
 }
 #else
+static inline int dax_register_holder(struct dax_device *dax_dev, void *holder,
+		const struct dax_holder_operations *ops)
+{
+	return 0;
+}
+static inline void dax_unregister_holder(struct dax_device *dax_dev)
+{
+}
+static inline void *dax_holder(struct dax_device *dax_dev)
+{
+	return NULL;
+}
 static inline struct dax_device *alloc_dax(void *private,
 		const struct dax_operations *ops)
 {
@@ -185,6 +213,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, u64 off, u64 len,
+		int mf_flags);
 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.34.1





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

* [PATCH v10.1 8/9] xfs: Implement ->notify_failure() for XFS
  2022-02-01 20:41   ` Darrick J. Wong
@ 2022-02-13 13:02     ` Shiyang Ruan
  2022-02-15  1:46       ` Darrick J. Wong
  0 siblings, 1 reply; 36+ messages in thread
From: Shiyang Ruan @ 2022-02-13 13:02 UTC (permalink / raw)
  To: djwong
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel,
	dan.j.williams, david, hch, jane.chu

v10.1 update:
 - Handle the error code returns by dax_register_holder()
 - In v10.1, dax_register_holder() will hold a write lock so XFS
     doesn't need to hold a lock
 - Fix the mistake in failure notification over two AGs
 - Fix the year in copyright message

Introduce xfs_notify_failure.c to handle failure related works, such as
implement ->notify_failure(), register/unregister dax holder in xfs, and
so on.

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.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
---
 fs/xfs/Makefile             |   1 +
 fs/xfs/xfs_buf.c            |  12 ++
 fs/xfs/xfs_fsops.c          |   3 +
 fs/xfs/xfs_mount.h          |   1 +
 fs/xfs/xfs_notify_failure.c | 225 ++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_notify_failure.h |  10 ++
 6 files changed, 252 insertions(+)
 create mode 100644 fs/xfs/xfs_notify_failure.c
 create mode 100644 fs/xfs/xfs_notify_failure.h

diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index 04611a1068b4..389970b3e13b 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -84,6 +84,7 @@ xfs-y				+= xfs_aops.o \
 				   xfs_message.o \
 				   xfs_mount.o \
 				   xfs_mru_cache.o \
+				   xfs_notify_failure.o \
 				   xfs_pwork.o \
 				   xfs_reflink.o \
 				   xfs_stats.o \
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index b45e0d50a405..941e8825cee6 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -19,6 +19,7 @@
 #include "xfs_errortag.h"
 #include "xfs_error.h"
 #include "xfs_ag.h"
+#include "xfs_notify_failure.h"
 
 static struct kmem_cache *xfs_buf_cache;
 
@@ -1892,6 +1893,8 @@ xfs_free_buftarg(
 	list_lru_destroy(&btp->bt_lru);
 
 	blkdev_issue_flush(btp->bt_bdev);
+	if (btp->bt_daxdev)
+		dax_unregister_holder(btp->bt_daxdev);
 	fs_put_dax(btp->bt_daxdev);
 
 	kmem_free(btp);
@@ -1939,6 +1942,7 @@ xfs_alloc_buftarg(
 	struct block_device	*bdev)
 {
 	xfs_buftarg_t		*btp;
+	int			error;
 
 	btp = kmem_zalloc(sizeof(*btp), KM_NOFS);
 
@@ -1946,6 +1950,14 @@ xfs_alloc_buftarg(
 	btp->bt_dev =  bdev->bd_dev;
 	btp->bt_bdev = bdev;
 	btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off);
+	if (btp->bt_daxdev) {
+		error = dax_register_holder(btp->bt_daxdev, mp,
+				&xfs_dax_holder_operations);
+		if (error) {
+			xfs_err(mp, "DAX device already in use?!");
+			goto error_free;
+		}
+	}
 
 	/*
 	 * Buffer IO error rate limiting. Limit it to no more than 10 messages
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 33e26690a8c4..d4d36c5bef11 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -542,6 +542,9 @@ xfs_do_force_shutdown(
 	} else if (flags & SHUTDOWN_CORRUPT_INCORE) {
 		tag = XFS_PTAG_SHUTDOWN_CORRUPT;
 		why = "Corruption of in-memory data";
+	} else if (flags & SHUTDOWN_CORRUPT_ONDISK) {
+		tag = XFS_PTAG_SHUTDOWN_CORRUPT;
+		why = "Corruption of on-disk metadata";
 	} else {
 		tag = XFS_PTAG_SHUTDOWN_IOERROR;
 		why = "Metadata I/O Error";
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 00720a02e761..47ff4ac53c4c 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -435,6 +435,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_ONDISK	0x0010  /* corrupt metadata on device */
 
 #define XFS_SHUTDOWN_STRINGS \
 	{ SHUTDOWN_META_IO_ERROR,	"metadata_io" }, \
diff --git a/fs/xfs/xfs_notify_failure.c b/fs/xfs/xfs_notify_failure.c
new file mode 100644
index 000000000000..aa67662210a1
--- /dev/null
+++ b/fs/xfs/xfs_notify_failure.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Fujitsu.  All Rights Reserved.
+ */
+
+#include "xfs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_alloc.h"
+#include "xfs_bit.h"
+#include "xfs_btree.h"
+#include "xfs_inode.h"
+#include "xfs_icache.h"
+#include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
+#include "xfs_rtalloc.h"
+#include "xfs_trans.h"
+
+#include <linux/mm.h>
+#include <linux/dax.h>
+
+struct failure_info {
+	xfs_agblock_t		startblock;
+	xfs_extlen_t		blockcount;
+	int			mf_flags;
+};
+
+#if IS_ENABLED(CONFIG_MEMORY_FAILURE) && IS_ENABLED(CONFIG_FS_DAX)
+static pgoff_t
+xfs_failure_pgoff(
+	struct xfs_mount		*mp,
+	const struct xfs_rmap_irec	*rec,
+	const struct failure_info	*notify)
+{
+	uint64_t			pos = rec->rm_offset;
+
+	if (notify->startblock > rec->rm_startblock)
+		pos += XFS_FSB_TO_B(mp,
+				notify->startblock - rec->rm_startblock);
+	return pos >> PAGE_SHIFT;
+}
+
+static unsigned long
+xfs_failure_pgcnt(
+	struct xfs_mount		*mp,
+	const struct xfs_rmap_irec	*rec,
+	const struct failure_info	*notify)
+{
+	xfs_agblock_t			end_rec;
+	xfs_agblock_t			end_notify;
+	xfs_agblock_t			start_cross;
+	xfs_agblock_t			end_cross;
+
+	start_cross = max(rec->rm_startblock, notify->startblock);
+
+	end_rec = rec->rm_startblock + rec->rm_blockcount;
+	end_notify = notify->startblock + notify->blockcount;
+	end_cross = min(end_rec, end_notify);
+
+	return XFS_FSB_TO_B(mp, end_cross - start_cross) >> PAGE_SHIFT;
+}
+
+static int
+xfs_dax_failure_fn(
+	struct xfs_btree_cur		*cur,
+	const struct xfs_rmap_irec	*rec,
+	void				*data)
+{
+	struct xfs_mount		*mp = cur->bc_mp;
+	struct xfs_inode		*ip;
+	struct failure_info		*notify = data;
+	int				error = 0;
+
+	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
+	    (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
+		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
+		return -EFSCORRUPTED;
+	}
+
+	/* Get files that incore, filter out others that are not in use. */
+	error = xfs_iget(mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE,
+			 0, &ip);
+	/* Continue the rmap query if the inode isn't incore */
+	if (error == -ENODATA)
+		return 0;
+	if (error)
+		return error;
+
+	error = mf_dax_kill_procs(VFS_I(ip)->i_mapping,
+				  xfs_failure_pgoff(mp, rec, notify),
+				  xfs_failure_pgcnt(mp, rec, notify),
+				  notify->mf_flags);
+	xfs_irele(ip);
+	return error;
+}
+#else
+static int
+xfs_dax_failure_fn(
+	struct xfs_btree_cur		*cur,
+	const struct xfs_rmap_irec	*rec,
+	void				*data)
+{
+	struct xfs_mount		*mp = cur->bc_mp;
+
+	/* No other option besides shutting down the fs. */
+	xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
+	return -EFSCORRUPTED;
+}
+#endif /* CONFIG_MEMORY_FAILURE && CONFIG_FS_DAX */
+
+static int
+xfs_dax_notify_ddev_failure(
+	struct xfs_mount	*mp,
+	xfs_daddr_t		daddr,
+	xfs_daddr_t		bblen,
+	int			mf_flags)
+{
+	struct xfs_trans	*tp = NULL;
+	struct xfs_btree_cur	*cur = NULL;
+	struct xfs_buf		*agf_bp = NULL;
+	int			error = 0;
+	xfs_fsblock_t		fsbno = XFS_DADDR_TO_FSB(mp, daddr);
+	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(mp, fsbno);
+	xfs_fsblock_t		end_fsbno = XFS_DADDR_TO_FSB(mp, daddr + bblen);
+	xfs_agnumber_t		end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno);
+
+	error = xfs_trans_alloc_empty(mp, &tp);
+	if (error)
+		return error;
+
+	for (; agno <= end_agno; agno++) {
+		struct xfs_rmap_irec	ri_low = { };
+		struct xfs_rmap_irec	ri_high;
+		struct failure_info	notify;
+		struct xfs_agf		*agf;
+		xfs_agblock_t		agend;
+
+		error = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
+		if (error)
+			break;
+
+		cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, agf_bp->b_pag);
+
+		/*
+		 * Set the rmap range from ri_low to ri_high, which represents
+		 * a [start, end] where we looking for the files or metadata.
+		 * The part of range out of a AG will be ignored.  So, it's fine
+		 * to set ri_low to "startblock" in all loops.  When it reaches
+		 * the last AG, set the ri_high to "endblock" to make sure we
+		 * actually end at the end.
+		 */
+		memset(&ri_high, 0xFF, sizeof(ri_high));
+		ri_low.rm_startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
+		if (agno == end_agno)
+			ri_high.rm_startblock = XFS_FSB_TO_AGBNO(mp, end_fsbno);
+
+		agf = agf_bp->b_addr;
+		agend = min(be32_to_cpu(agf->agf_length),
+				ri_high.rm_startblock);
+		notify.startblock = ri_low.rm_startblock;
+		notify.blockcount = agend - ri_low.rm_startblock;
+
+		error = xfs_rmap_query_range(cur, &ri_low, &ri_high,
+				xfs_dax_failure_fn, &notify);
+		xfs_btree_del_cursor(cur, error);
+		xfs_trans_brelse(tp, agf_bp);
+		if (error)
+			break;
+
+		fsbno = XFS_AGB_TO_FSB(mp, agno + 1, 0);
+	}
+
+	xfs_trans_cancel(tp);
+	return error;
+}
+
+static int
+xfs_dax_notify_failure(
+	struct dax_device	*dax_dev,
+	u64			offset,
+	u64			len,
+	int			mf_flags)
+{
+	struct xfs_mount	*mp = dax_holder(dax_dev);
+
+	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_daxdev == dax_dev) {
+		xfs_warn(mp,
+			 "notify_failure() not supported on 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_ONDISK);
+		return -EFSCORRUPTED;
+	}
+
+	if (!xfs_has_rmapbt(mp)) {
+		xfs_warn(mp, "notify_failure() needs rmapbt enabled!");
+		return -EOPNOTSUPP;
+	}
+
+	/* Ignore the range out of filesystem area */
+	if ((offset + len) < mp->m_ddev_targp->bt_dax_part_off)
+		return -ENXIO;
+	if (offset > (mp->m_ddev_targp->bt_dax_part_off +
+			mp->m_ddev_targp->bt_bdev->bd_nr_sectors))
+		return -ENXIO;
+
+	if (offset > mp->m_ddev_targp->bt_dax_part_off)
+		offset -= mp->m_ddev_targp->bt_dax_part_off;
+	else
+		offset = 0;
+
+	return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len),
+			mf_flags);
+}
+
+const struct dax_holder_operations xfs_dax_holder_operations = {
+	.notify_failure		= xfs_dax_notify_failure,
+};
diff --git a/fs/xfs/xfs_notify_failure.h b/fs/xfs/xfs_notify_failure.h
new file mode 100644
index 000000000000..76187b9620f9
--- /dev/null
+++ b/fs/xfs/xfs_notify_failure.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Fujitsu.  All Rights Reserved.
+ */
+#ifndef __XFS_NOTIFY_FAILURE_H__
+#define __XFS_NOTIFY_FAILURE_H__
+
+extern const struct dax_holder_operations xfs_dax_holder_operations;
+
+#endif  /* __XFS_NOTIFY_FAILURE_H__ */
-- 
2.34.1





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

* Re: [PATCH v10.1 8/9] xfs: Implement ->notify_failure() for XFS
  2022-02-13 13:02     ` [PATCH v10.1 " Shiyang Ruan
@ 2022-02-15  1:46       ` Darrick J. Wong
  2022-02-15  9:42         ` Shiyang Ruan
  0 siblings, 1 reply; 36+ messages in thread
From: Darrick J. Wong @ 2022-02-15  1:46 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel,
	dan.j.williams, david, hch, jane.chu

On Sun, Feb 13, 2022 at 09:02:24PM +0800, Shiyang Ruan wrote:
> v10.1 update:
>  - Handle the error code returns by dax_register_holder()
>  - In v10.1, dax_register_holder() will hold a write lock so XFS
>      doesn't need to hold a lock
>  - Fix the mistake in failure notification over two AGs
>  - Fix the year in copyright message
> 
> Introduce xfs_notify_failure.c to handle failure related works, such as
> implement ->notify_failure(), register/unregister dax holder in xfs, and
> so on.
> 
> 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.
> 
> Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
> ---
>  fs/xfs/Makefile             |   1 +
>  fs/xfs/xfs_buf.c            |  12 ++
>  fs/xfs/xfs_fsops.c          |   3 +
>  fs/xfs/xfs_mount.h          |   1 +
>  fs/xfs/xfs_notify_failure.c | 225 ++++++++++++++++++++++++++++++++++++
>  fs/xfs/xfs_notify_failure.h |  10 ++
>  6 files changed, 252 insertions(+)
>  create mode 100644 fs/xfs/xfs_notify_failure.c
>  create mode 100644 fs/xfs/xfs_notify_failure.h
> 
> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> index 04611a1068b4..389970b3e13b 100644
> --- a/fs/xfs/Makefile
> +++ b/fs/xfs/Makefile
> @@ -84,6 +84,7 @@ xfs-y				+= xfs_aops.o \
>  				   xfs_message.o \
>  				   xfs_mount.o \
>  				   xfs_mru_cache.o \
> +				   xfs_notify_failure.o \
>  				   xfs_pwork.o \
>  				   xfs_reflink.o \
>  				   xfs_stats.o \
> diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
> index b45e0d50a405..941e8825cee6 100644
> --- a/fs/xfs/xfs_buf.c
> +++ b/fs/xfs/xfs_buf.c
> @@ -19,6 +19,7 @@
>  #include "xfs_errortag.h"
>  #include "xfs_error.h"
>  #include "xfs_ag.h"
> +#include "xfs_notify_failure.h"
>  
>  static struct kmem_cache *xfs_buf_cache;
>  
> @@ -1892,6 +1893,8 @@ xfs_free_buftarg(
>  	list_lru_destroy(&btp->bt_lru);
>  
>  	blkdev_issue_flush(btp->bt_bdev);
> +	if (btp->bt_daxdev)
> +		dax_unregister_holder(btp->bt_daxdev);
>  	fs_put_dax(btp->bt_daxdev);
>  
>  	kmem_free(btp);
> @@ -1939,6 +1942,7 @@ xfs_alloc_buftarg(
>  	struct block_device	*bdev)
>  {
>  	xfs_buftarg_t		*btp;
> +	int			error;
>  
>  	btp = kmem_zalloc(sizeof(*btp), KM_NOFS);
>  
> @@ -1946,6 +1950,14 @@ xfs_alloc_buftarg(
>  	btp->bt_dev =  bdev->bd_dev;
>  	btp->bt_bdev = bdev;
>  	btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off);
> +	if (btp->bt_daxdev) {
> +		error = dax_register_holder(btp->bt_daxdev, mp,
> +				&xfs_dax_holder_operations);
> +		if (error) {
> +			xfs_err(mp, "DAX device already in use?!");
> +			goto error_free;
> +		}
> +	}
>  
>  	/*
>  	 * Buffer IO error rate limiting. Limit it to no more than 10 messages
> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> index 33e26690a8c4..d4d36c5bef11 100644
> --- a/fs/xfs/xfs_fsops.c
> +++ b/fs/xfs/xfs_fsops.c
> @@ -542,6 +542,9 @@ xfs_do_force_shutdown(
>  	} else if (flags & SHUTDOWN_CORRUPT_INCORE) {
>  		tag = XFS_PTAG_SHUTDOWN_CORRUPT;
>  		why = "Corruption of in-memory data";
> +	} else if (flags & SHUTDOWN_CORRUPT_ONDISK) {
> +		tag = XFS_PTAG_SHUTDOWN_CORRUPT;
> +		why = "Corruption of on-disk metadata";
>  	} else {
>  		tag = XFS_PTAG_SHUTDOWN_IOERROR;
>  		why = "Metadata I/O Error";
> diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
> index 00720a02e761..47ff4ac53c4c 100644
> --- a/fs/xfs/xfs_mount.h
> +++ b/fs/xfs/xfs_mount.h
> @@ -435,6 +435,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_ONDISK	0x0010  /* corrupt metadata on device */
>  
>  #define XFS_SHUTDOWN_STRINGS \
>  	{ SHUTDOWN_META_IO_ERROR,	"metadata_io" }, \
> diff --git a/fs/xfs/xfs_notify_failure.c b/fs/xfs/xfs_notify_failure.c
> new file mode 100644
> index 000000000000..aa67662210a1
> --- /dev/null
> +++ b/fs/xfs/xfs_notify_failure.c
> @@ -0,0 +1,225 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2022 Fujitsu.  All Rights Reserved.
> + */
> +
> +#include "xfs.h"
> +#include "xfs_shared.h"
> +#include "xfs_format.h"
> +#include "xfs_log_format.h"
> +#include "xfs_trans_resv.h"
> +#include "xfs_mount.h"
> +#include "xfs_alloc.h"
> +#include "xfs_bit.h"
> +#include "xfs_btree.h"
> +#include "xfs_inode.h"
> +#include "xfs_icache.h"
> +#include "xfs_rmap.h"
> +#include "xfs_rmap_btree.h"
> +#include "xfs_rtalloc.h"
> +#include "xfs_trans.h"
> +
> +#include <linux/mm.h>
> +#include <linux/dax.h>
> +
> +struct failure_info {
> +	xfs_agblock_t		startblock;
> +	xfs_extlen_t		blockcount;
> +	int			mf_flags;
> +};
> +
> +#if IS_ENABLED(CONFIG_MEMORY_FAILURE) && IS_ENABLED(CONFIG_FS_DAX)
> +static pgoff_t
> +xfs_failure_pgoff(
> +	struct xfs_mount		*mp,
> +	const struct xfs_rmap_irec	*rec,
> +	const struct failure_info	*notify)
> +{
> +	uint64_t			pos = rec->rm_offset;
> +
> +	if (notify->startblock > rec->rm_startblock)
> +		pos += XFS_FSB_TO_B(mp,
> +				notify->startblock - rec->rm_startblock);
> +	return pos >> PAGE_SHIFT;
> +}
> +
> +static unsigned long
> +xfs_failure_pgcnt(
> +	struct xfs_mount		*mp,
> +	const struct xfs_rmap_irec	*rec,
> +	const struct failure_info	*notify)
> +{
> +	xfs_agblock_t			end_rec;
> +	xfs_agblock_t			end_notify;
> +	xfs_agblock_t			start_cross;
> +	xfs_agblock_t			end_cross;
> +
> +	start_cross = max(rec->rm_startblock, notify->startblock);
> +
> +	end_rec = rec->rm_startblock + rec->rm_blockcount;
> +	end_notify = notify->startblock + notify->blockcount;
> +	end_cross = min(end_rec, end_notify);
> +
> +	return XFS_FSB_TO_B(mp, end_cross - start_cross) >> PAGE_SHIFT;
> +}
> +
> +static int
> +xfs_dax_failure_fn(
> +	struct xfs_btree_cur		*cur,
> +	const struct xfs_rmap_irec	*rec,
> +	void				*data)
> +{
> +	struct xfs_mount		*mp = cur->bc_mp;
> +	struct xfs_inode		*ip;
> +	struct failure_info		*notify = data;
> +	int				error = 0;
> +
> +	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
> +	    (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
> +		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
> +		return -EFSCORRUPTED;
> +	}
> +
> +	/* Get files that incore, filter out others that are not in use. */
> +	error = xfs_iget(mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE,
> +			 0, &ip);
> +	/* Continue the rmap query if the inode isn't incore */
> +	if (error == -ENODATA)
> +		return 0;
> +	if (error)
> +		return error;
> +
> +	error = mf_dax_kill_procs(VFS_I(ip)->i_mapping,
> +				  xfs_failure_pgoff(mp, rec, notify),
> +				  xfs_failure_pgcnt(mp, rec, notify),
> +				  notify->mf_flags);
> +	xfs_irele(ip);
> +	return error;
> +}
> +#else
> +static int
> +xfs_dax_failure_fn(
> +	struct xfs_btree_cur		*cur,
> +	const struct xfs_rmap_irec	*rec,
> +	void				*data)
> +{
> +	struct xfs_mount		*mp = cur->bc_mp;
> +
> +	/* No other option besides shutting down the fs. */
> +	xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
> +	return -EFSCORRUPTED;
> +}
> +#endif /* CONFIG_MEMORY_FAILURE && CONFIG_FS_DAX */
> +
> +static int
> +xfs_dax_notify_ddev_failure(
> +	struct xfs_mount	*mp,
> +	xfs_daddr_t		daddr,
> +	xfs_daddr_t		bblen,
> +	int			mf_flags)
> +{
> +	struct xfs_trans	*tp = NULL;
> +	struct xfs_btree_cur	*cur = NULL;
> +	struct xfs_buf		*agf_bp = NULL;
> +	int			error = 0;
> +	xfs_fsblock_t		fsbno = XFS_DADDR_TO_FSB(mp, daddr);
> +	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(mp, fsbno);
> +	xfs_fsblock_t		end_fsbno = XFS_DADDR_TO_FSB(mp, daddr + bblen);
> +	xfs_agnumber_t		end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno);
> +
> +	error = xfs_trans_alloc_empty(mp, &tp);
> +	if (error)
> +		return error;
> +
> +	for (; agno <= end_agno; agno++) {
> +		struct xfs_rmap_irec	ri_low = { };
> +		struct xfs_rmap_irec	ri_high;
> +		struct failure_info	notify;
> +		struct xfs_agf		*agf;
> +		xfs_agblock_t		agend;
> +
> +		error = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
> +		if (error)
> +			break;
> +
> +		cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, agf_bp->b_pag);
> +
> +		/*
> +		 * Set the rmap range from ri_low to ri_high, which represents
> +		 * a [start, end] where we looking for the files or metadata.
> +		 * The part of range out of a AG will be ignored.  So, it's fine
> +		 * to set ri_low to "startblock" in all loops.  When it reaches
> +		 * the last AG, set the ri_high to "endblock" to make sure we
> +		 * actually end at the end.
> +		 */
> +		memset(&ri_high, 0xFF, sizeof(ri_high));
> +		ri_low.rm_startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
> +		if (agno == end_agno)
> +			ri_high.rm_startblock = XFS_FSB_TO_AGBNO(mp, end_fsbno);
> +
> +		agf = agf_bp->b_addr;
> +		agend = min(be32_to_cpu(agf->agf_length),
> +				ri_high.rm_startblock);
> +		notify.startblock = ri_low.rm_startblock;
> +		notify.blockcount = agend - ri_low.rm_startblock;
> +
> +		error = xfs_rmap_query_range(cur, &ri_low, &ri_high,
> +				xfs_dax_failure_fn, &notify);
> +		xfs_btree_del_cursor(cur, error);
> +		xfs_trans_brelse(tp, agf_bp);
> +		if (error)
> +			break;
> +
> +		fsbno = XFS_AGB_TO_FSB(mp, agno + 1, 0);
> +	}
> +
> +	xfs_trans_cancel(tp);
> +	return error;
> +}
> +
> +static int
> +xfs_dax_notify_failure(
> +	struct dax_device	*dax_dev,
> +	u64			offset,
> +	u64			len,
> +	int			mf_flags)
> +{
> +	struct xfs_mount	*mp = dax_holder(dax_dev);
> +
> +	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_daxdev == dax_dev) {
> +		xfs_warn(mp,
> +			 "notify_failure() not supported on 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_ONDISK);
> +		return -EFSCORRUPTED;
> +	}
> +
> +	if (!xfs_has_rmapbt(mp)) {
> +		xfs_warn(mp, "notify_failure() needs rmapbt enabled!");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	/* Ignore the range out of filesystem area */
> +	if ((offset + len) < mp->m_ddev_targp->bt_dax_part_off)
> +		return -ENXIO;
> +	if (offset > (mp->m_ddev_targp->bt_dax_part_off +
> +			mp->m_ddev_targp->bt_bdev->bd_nr_sectors))
> +		return -ENXIO;
> +
> +	if (offset > mp->m_ddev_targp->bt_dax_part_off)
> +		offset -= mp->m_ddev_targp->bt_dax_part_off;
> +	else
> +		offset = 0;

I think you need to adjust len if you adjust @offset.

You might also want to clamp len so that it doesn't go beyond the end of
the filesystem.

--D

> +
> +	return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len),
> +			mf_flags);
> +}
> +
> +const struct dax_holder_operations xfs_dax_holder_operations = {
> +	.notify_failure		= xfs_dax_notify_failure,
> +};
> diff --git a/fs/xfs/xfs_notify_failure.h b/fs/xfs/xfs_notify_failure.h
> new file mode 100644
> index 000000000000..76187b9620f9
> --- /dev/null
> +++ b/fs/xfs/xfs_notify_failure.h
> @@ -0,0 +1,10 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2022 Fujitsu.  All Rights Reserved.
> + */
> +#ifndef __XFS_NOTIFY_FAILURE_H__
> +#define __XFS_NOTIFY_FAILURE_H__
> +
> +extern const struct dax_holder_operations xfs_dax_holder_operations;
> +
> +#endif  /* __XFS_NOTIFY_FAILURE_H__ */
> -- 
> 2.34.1
> 
> 
> 


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

* Re: [PATCH v10.1 8/9] xfs: Implement ->notify_failure() for XFS
  2022-02-15  1:46       ` Darrick J. Wong
@ 2022-02-15  9:42         ` Shiyang Ruan
  0 siblings, 0 replies; 36+ messages in thread
From: Shiyang Ruan @ 2022-02-15  9:42 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: linux-kernel, linux-xfs, nvdimm, linux-mm, linux-fsdevel,
	dan.j.williams, david, hch, jane.chu



在 2022/2/15 9:46, Darrick J. Wong 写道:
> On Sun, Feb 13, 2022 at 09:02:24PM +0800, Shiyang Ruan wrote:
>> v10.1 update:
>>   - Handle the error code returns by dax_register_holder()
>>   - In v10.1, dax_register_holder() will hold a write lock so XFS
>>       doesn't need to hold a lock
>>   - Fix the mistake in failure notification over two AGs
>>   - Fix the year in copyright message
>>
>> Introduce xfs_notify_failure.c to handle failure related works, such as
>> implement ->notify_failure(), register/unregister dax holder in xfs, and
>> so on.
>>
>> 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.
>>
>> Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
>> ---
>>   fs/xfs/Makefile             |   1 +
>>   fs/xfs/xfs_buf.c            |  12 ++
>>   fs/xfs/xfs_fsops.c          |   3 +
>>   fs/xfs/xfs_mount.h          |   1 +
>>   fs/xfs/xfs_notify_failure.c | 225 ++++++++++++++++++++++++++++++++++++
>>   fs/xfs/xfs_notify_failure.h |  10 ++
>>   6 files changed, 252 insertions(+)
>>   create mode 100644 fs/xfs/xfs_notify_failure.c
>>   create mode 100644 fs/xfs/xfs_notify_failure.h
>>
>> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
>> index 04611a1068b4..389970b3e13b 100644
>> --- a/fs/xfs/Makefile
>> +++ b/fs/xfs/Makefile
>> @@ -84,6 +84,7 @@ xfs-y				+= xfs_aops.o \
>>   				   xfs_message.o \
>>   				   xfs_mount.o \
>>   				   xfs_mru_cache.o \
>> +				   xfs_notify_failure.o \
>>   				   xfs_pwork.o \
>>   				   xfs_reflink.o \
>>   				   xfs_stats.o \
>> diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
>> index b45e0d50a405..941e8825cee6 100644
>> --- a/fs/xfs/xfs_buf.c
>> +++ b/fs/xfs/xfs_buf.c
>> @@ -19,6 +19,7 @@
>>   #include "xfs_errortag.h"
>>   #include "xfs_error.h"
>>   #include "xfs_ag.h"
>> +#include "xfs_notify_failure.h"
>>   
>>   static struct kmem_cache *xfs_buf_cache;
>>   
>> @@ -1892,6 +1893,8 @@ xfs_free_buftarg(
>>   	list_lru_destroy(&btp->bt_lru);
>>   
>>   	blkdev_issue_flush(btp->bt_bdev);
>> +	if (btp->bt_daxdev)
>> +		dax_unregister_holder(btp->bt_daxdev);
>>   	fs_put_dax(btp->bt_daxdev);
>>   
>>   	kmem_free(btp);
>> @@ -1939,6 +1942,7 @@ xfs_alloc_buftarg(
>>   	struct block_device	*bdev)
>>   {
>>   	xfs_buftarg_t		*btp;
>> +	int			error;
>>   
>>   	btp = kmem_zalloc(sizeof(*btp), KM_NOFS);
>>   
>> @@ -1946,6 +1950,14 @@ xfs_alloc_buftarg(
>>   	btp->bt_dev =  bdev->bd_dev;
>>   	btp->bt_bdev = bdev;
>>   	btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off);
>> +	if (btp->bt_daxdev) {
>> +		error = dax_register_holder(btp->bt_daxdev, mp,
>> +				&xfs_dax_holder_operations);
>> +		if (error) {
>> +			xfs_err(mp, "DAX device already in use?!");
>> +			goto error_free;
>> +		}
>> +	}
>>   
>>   	/*
>>   	 * Buffer IO error rate limiting. Limit it to no more than 10 messages
>> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
>> index 33e26690a8c4..d4d36c5bef11 100644
>> --- a/fs/xfs/xfs_fsops.c
>> +++ b/fs/xfs/xfs_fsops.c
>> @@ -542,6 +542,9 @@ xfs_do_force_shutdown(
>>   	} else if (flags & SHUTDOWN_CORRUPT_INCORE) {
>>   		tag = XFS_PTAG_SHUTDOWN_CORRUPT;
>>   		why = "Corruption of in-memory data";
>> +	} else if (flags & SHUTDOWN_CORRUPT_ONDISK) {
>> +		tag = XFS_PTAG_SHUTDOWN_CORRUPT;
>> +		why = "Corruption of on-disk metadata";
>>   	} else {
>>   		tag = XFS_PTAG_SHUTDOWN_IOERROR;
>>   		why = "Metadata I/O Error";
>> diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
>> index 00720a02e761..47ff4ac53c4c 100644
>> --- a/fs/xfs/xfs_mount.h
>> +++ b/fs/xfs/xfs_mount.h
>> @@ -435,6 +435,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_ONDISK	0x0010  /* corrupt metadata on device */
>>   
>>   #define XFS_SHUTDOWN_STRINGS \
>>   	{ SHUTDOWN_META_IO_ERROR,	"metadata_io" }, \
>> diff --git a/fs/xfs/xfs_notify_failure.c b/fs/xfs/xfs_notify_failure.c
>> new file mode 100644
>> index 000000000000..aa67662210a1
>> --- /dev/null
>> +++ b/fs/xfs/xfs_notify_failure.c
>> @@ -0,0 +1,225 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2022 Fujitsu.  All Rights Reserved.
>> + */
>> +
>> +#include "xfs.h"
>> +#include "xfs_shared.h"
>> +#include "xfs_format.h"
>> +#include "xfs_log_format.h"
>> +#include "xfs_trans_resv.h"
>> +#include "xfs_mount.h"
>> +#include "xfs_alloc.h"
>> +#include "xfs_bit.h"
>> +#include "xfs_btree.h"
>> +#include "xfs_inode.h"
>> +#include "xfs_icache.h"
>> +#include "xfs_rmap.h"
>> +#include "xfs_rmap_btree.h"
>> +#include "xfs_rtalloc.h"
>> +#include "xfs_trans.h"
>> +
>> +#include <linux/mm.h>
>> +#include <linux/dax.h>
>> +
>> +struct failure_info {
>> +	xfs_agblock_t		startblock;
>> +	xfs_extlen_t		blockcount;
>> +	int			mf_flags;
>> +};
>> +
>> +#if IS_ENABLED(CONFIG_MEMORY_FAILURE) && IS_ENABLED(CONFIG_FS_DAX)
>> +static pgoff_t
>> +xfs_failure_pgoff(
>> +	struct xfs_mount		*mp,
>> +	const struct xfs_rmap_irec	*rec,
>> +	const struct failure_info	*notify)
>> +{
>> +	uint64_t			pos = rec->rm_offset;
>> +
>> +	if (notify->startblock > rec->rm_startblock)
>> +		pos += XFS_FSB_TO_B(mp,
>> +				notify->startblock - rec->rm_startblock);
>> +	return pos >> PAGE_SHIFT;
>> +}
>> +
>> +static unsigned long
>> +xfs_failure_pgcnt(
>> +	struct xfs_mount		*mp,
>> +	const struct xfs_rmap_irec	*rec,
>> +	const struct failure_info	*notify)
>> +{
>> +	xfs_agblock_t			end_rec;
>> +	xfs_agblock_t			end_notify;
>> +	xfs_agblock_t			start_cross;
>> +	xfs_agblock_t			end_cross;
>> +
>> +	start_cross = max(rec->rm_startblock, notify->startblock);
>> +
>> +	end_rec = rec->rm_startblock + rec->rm_blockcount;
>> +	end_notify = notify->startblock + notify->blockcount;
>> +	end_cross = min(end_rec, end_notify);
>> +
>> +	return XFS_FSB_TO_B(mp, end_cross - start_cross) >> PAGE_SHIFT;
>> +}
>> +
>> +static int
>> +xfs_dax_failure_fn(
>> +	struct xfs_btree_cur		*cur,
>> +	const struct xfs_rmap_irec	*rec,
>> +	void				*data)
>> +{
>> +	struct xfs_mount		*mp = cur->bc_mp;
>> +	struct xfs_inode		*ip;
>> +	struct failure_info		*notify = data;
>> +	int				error = 0;
>> +
>> +	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
>> +	    (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
>> +		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
>> +		return -EFSCORRUPTED;
>> +	}
>> +
>> +	/* Get files that incore, filter out others that are not in use. */
>> +	error = xfs_iget(mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE,
>> +			 0, &ip);
>> +	/* Continue the rmap query if the inode isn't incore */
>> +	if (error == -ENODATA)
>> +		return 0;
>> +	if (error)
>> +		return error;
>> +
>> +	error = mf_dax_kill_procs(VFS_I(ip)->i_mapping,
>> +				  xfs_failure_pgoff(mp, rec, notify),
>> +				  xfs_failure_pgcnt(mp, rec, notify),
>> +				  notify->mf_flags);
>> +	xfs_irele(ip);
>> +	return error;
>> +}
>> +#else
>> +static int
>> +xfs_dax_failure_fn(
>> +	struct xfs_btree_cur		*cur,
>> +	const struct xfs_rmap_irec	*rec,
>> +	void				*data)
>> +{
>> +	struct xfs_mount		*mp = cur->bc_mp;
>> +
>> +	/* No other option besides shutting down the fs. */
>> +	xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
>> +	return -EFSCORRUPTED;
>> +}
>> +#endif /* CONFIG_MEMORY_FAILURE && CONFIG_FS_DAX */
>> +
>> +static int
>> +xfs_dax_notify_ddev_failure(
>> +	struct xfs_mount	*mp,
>> +	xfs_daddr_t		daddr,
>> +	xfs_daddr_t		bblen,
>> +	int			mf_flags)
>> +{
>> +	struct xfs_trans	*tp = NULL;
>> +	struct xfs_btree_cur	*cur = NULL;
>> +	struct xfs_buf		*agf_bp = NULL;
>> +	int			error = 0;
>> +	xfs_fsblock_t		fsbno = XFS_DADDR_TO_FSB(mp, daddr);
>> +	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(mp, fsbno);
>> +	xfs_fsblock_t		end_fsbno = XFS_DADDR_TO_FSB(mp, daddr + bblen);
>> +	xfs_agnumber_t		end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno);
>> +
>> +	error = xfs_trans_alloc_empty(mp, &tp);
>> +	if (error)
>> +		return error;
>> +
>> +	for (; agno <= end_agno; agno++) {
>> +		struct xfs_rmap_irec	ri_low = { };
>> +		struct xfs_rmap_irec	ri_high;
>> +		struct failure_info	notify;
>> +		struct xfs_agf		*agf;
>> +		xfs_agblock_t		agend;
>> +
>> +		error = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
>> +		if (error)
>> +			break;
>> +
>> +		cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, agf_bp->b_pag);
>> +
>> +		/*
>> +		 * Set the rmap range from ri_low to ri_high, which represents
>> +		 * a [start, end] where we looking for the files or metadata.
>> +		 * The part of range out of a AG will be ignored.  So, it's fine
>> +		 * to set ri_low to "startblock" in all loops.  When it reaches
>> +		 * the last AG, set the ri_high to "endblock" to make sure we
>> +		 * actually end at the end.
>> +		 */
>> +		memset(&ri_high, 0xFF, sizeof(ri_high));
>> +		ri_low.rm_startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
>> +		if (agno == end_agno)
>> +			ri_high.rm_startblock = XFS_FSB_TO_AGBNO(mp, end_fsbno);
>> +
>> +		agf = agf_bp->b_addr;
>> +		agend = min(be32_to_cpu(agf->agf_length),
>> +				ri_high.rm_startblock);
>> +		notify.startblock = ri_low.rm_startblock;
>> +		notify.blockcount = agend - ri_low.rm_startblock;
>> +
>> +		error = xfs_rmap_query_range(cur, &ri_low, &ri_high,
>> +				xfs_dax_failure_fn, &notify);
>> +		xfs_btree_del_cursor(cur, error);
>> +		xfs_trans_brelse(tp, agf_bp);
>> +		if (error)
>> +			break;
>> +
>> +		fsbno = XFS_AGB_TO_FSB(mp, agno + 1, 0);
>> +	}
>> +
>> +	xfs_trans_cancel(tp);
>> +	return error;
>> +}
>> +
>> +static int
>> +xfs_dax_notify_failure(
>> +	struct dax_device	*dax_dev,
>> +	u64			offset,
>> +	u64			len,
>> +	int			mf_flags)
>> +{
>> +	struct xfs_mount	*mp = dax_holder(dax_dev);
>> +
>> +	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_daxdev == dax_dev) {
>> +		xfs_warn(mp,
>> +			 "notify_failure() not supported on 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_ONDISK);
>> +		return -EFSCORRUPTED;
>> +	}
>> +
>> +	if (!xfs_has_rmapbt(mp)) {
>> +		xfs_warn(mp, "notify_failure() needs rmapbt enabled!");
>> +		return -EOPNOTSUPP;
>> +	}
>> +
>> +	/* Ignore the range out of filesystem area */
>> +	if ((offset + len) < mp->m_ddev_targp->bt_dax_part_off)
>> +		return -ENXIO;
>> +	if (offset > (mp->m_ddev_targp->bt_dax_part_off +
>> +			mp->m_ddev_targp->bt_bdev->bd_nr_sectors))
>> +		return -ENXIO;
>> +
>> +	if (offset > mp->m_ddev_targp->bt_dax_part_off)
>> +		offset -= mp->m_ddev_targp->bt_dax_part_off;
>> +	else
>> +		offset = 0;
> 
> I think you need to adjust len if you adjust @offset.
> 
> You might also want to clamp len so that it doesn't go beyond the end of
> the filesystem.

Yes, I forgot to think of that.  Will fix it.


--
Thanks,
Ruan.

> 
> --D
> 
>> +
>> +	return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len),
>> +			mf_flags);
>> +}
>> +
>> +const struct dax_holder_operations xfs_dax_holder_operations = {
>> +	.notify_failure		= xfs_dax_notify_failure,
>> +};
>> diff --git a/fs/xfs/xfs_notify_failure.h b/fs/xfs/xfs_notify_failure.h
>> new file mode 100644
>> index 000000000000..76187b9620f9
>> --- /dev/null
>> +++ b/fs/xfs/xfs_notify_failure.h
>> @@ -0,0 +1,10 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2022 Fujitsu.  All Rights Reserved.
>> + */
>> +#ifndef __XFS_NOTIFY_FAILURE_H__
>> +#define __XFS_NOTIFY_FAILURE_H__
>> +
>> +extern const struct dax_holder_operations xfs_dax_holder_operations;
>> +
>> +#endif  /* __XFS_NOTIFY_FAILURE_H__ */
>> -- 
>> 2.34.1
>>
>>
>>




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

* Re: [PATCH v10.1 1/9] dax: Introduce holder for dax_device
  2022-02-13 12:58     ` [PATCH v10.1 " Shiyang Ruan
@ 2022-02-15 22:06       ` Dan Williams
  0 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2022-02-15 22:06 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: Christoph Hellwig, Linux Kernel Mailing List, linux-xfs,
	Linux NVDIMM, Linux MM, linux-fsdevel, Darrick J. Wong, david,
	Jane Chu

On Sun, Feb 13, 2022 at 4:58 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>
> v10.1 update:
>  - Fix build error reported by kernel test robot
>  - Add return code to dax_register_holder()
>  - Add write lock to prevent concurrent registrations
>  - Rename dax_get_holder() to dax_holder()
>  - Add kernel doc for new added functions
>
> 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
>    instance 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 | 95 +++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/dax.h | 30 ++++++++++++++
>  2 files changed, 125 insertions(+)
>
> diff --git a/drivers/dax/super.c b/drivers/dax/super.c
> index e3029389d809..d7fb4c36bf16 100644
> --- a/drivers/dax/super.c
> +++ b/drivers/dax/super.c
> @@ -21,6 +21,9 @@
>   * @cdev: optional character interface for "device dax"
>   * @private: dax driver private data
>   * @flags: state and boolean properties
> + * @ops: operations for dax_device
> + * @holder_data: holder of a dax_device: could be filesystem or mapped device
> + * @holder_ops: operations for the inner holder
>   */
>  struct dax_device {
>         struct inode inode;
> @@ -28,6 +31,9 @@ struct dax_device {
>         void *private;
>         unsigned long flags;
>         const struct dax_operations *ops;
> +       void *holder_data;
> +       struct percpu_rw_semaphore holder_rwsem;

This lock is not necessary, see below...

> +       const struct dax_holder_operations *holder_ops;
>  };
>
>  static dev_t dax_devt;
> @@ -193,6 +199,29 @@ 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, u64 off,
> +                             u64 len, int mf_flags)
> +{
> +       int rc, id;
> +
> +       id = dax_read_lock();
> +       if (!dax_alive(dax_dev)) {
> +               rc = -ENXIO;
> +               goto out;
> +       }
> +
> +       if (!dax_dev->holder_ops) {
> +               rc = -EOPNOTSUPP;
> +               goto out;
> +       }
> +
> +       rc = dax_dev->holder_ops->notify_failure(dax_dev, off, len, mf_flags);
> +out:
> +       dax_read_unlock(id);
> +       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)
> @@ -268,6 +297,13 @@ void kill_dax(struct dax_device *dax_dev)
>
>         clear_bit(DAXDEV_ALIVE, &dax_dev->flags);
>         synchronize_srcu(&dax_srcu);
> +
> +       /* Lock to prevent concurrent registrations. */
> +       percpu_down_write(&dax_dev->holder_rwsem);
> +       /* clear holder data */
> +       dax_dev->holder_ops = NULL;
> +       dax_dev->holder_data = NULL;
> +       percpu_up_write(&dax_dev->holder_rwsem);
>  }
>  EXPORT_SYMBOL_GPL(kill_dax);
>
> @@ -393,6 +429,7 @@ struct dax_device *alloc_dax(void *private, const struct dax_operations *ops)
>
>         dax_dev->ops = ops;
>         dax_dev->private = private;
> +       percpu_init_rwsem(&dax_dev->holder_rwsem);
>         return dax_dev;
>
>   err_dev:
> @@ -409,6 +446,64 @@ void put_dax(struct dax_device *dax_dev)
>  }
>  EXPORT_SYMBOL_GPL(put_dax);
>
> +/**
> + * dax_holder() - obtain the holder of a dax device
> + * @dax_dev: a dax_device instance
> +
> + * Return: the holder's data which represents the holder if registered,
> + * otherwize NULL.
> + */
> +void *dax_holder(struct dax_device *dax_dev)
> +{
> +       if (!dax_alive(dax_dev))
> +               return NULL;
> +
> +       return dax_dev->holder_data;
> +}
> +EXPORT_SYMBOL_GPL(dax_holder);
> +
> +/**
> + * dax_register_holder() - register a holder to a dax device
> + * @dax_dev: a dax_device instance
> + * @holder: a pointer to a holder's data which represents the holder
> + * @ops: operations of this holder
> +
> + * Return: negative errno if an error occurs, otherwise 0.
> + */
> +int dax_register_holder(struct dax_device *dax_dev, void *holder,
> +               const struct dax_holder_operations *ops)
> +{
> +       if (!dax_alive(dax_dev))
> +               return -ENXIO;
> +
> +       /* Already registered */
> +       if (dax_holder(dax_dev))
> +               return -EBUSY;

Delete this...

> +
> +       /* Lock to prevent concurrent registrations. */
> +       percpu_down_write(&dax_dev->holder_rwsem);

...and just use cmpxchg:

if (cmpxchg(&dax_dev->holder_data, NULL, holder))
    return -EBUSY;
dax_dev->holder_ops = ops;

...and then on the release side you can require that the caller
specify @holder before clearing to make the unregistration symmetric:

if (cmpxchg(&dax_dev->holder_data, holder, NULL) != holder))
    return -EBUSY;
dax_dev->holder_ops = NULL;


> +       dax_dev->holder_data = holder;
> +       dax_dev->holder_ops = ops;
> +       percpu_up_write(&dax_dev->holder_rwsem);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(dax_register_holder);
> +
> +/**
> + * dax_unregister_holder() - unregister the holder for a dax device
> + * @dax_dev: a dax_device instance
> + */
> +void dax_unregister_holder(struct dax_device *dax_dev)

Per above, require the holder to pass in their holder_data again.

> +{
> +       if (!dax_alive(dax_dev))
> +               return;
> +
> +       dax_dev->holder_data = NULL;
> +       dax_dev->holder_ops = NULL;
> +}
> +EXPORT_SYMBOL_GPL(dax_unregister_holder);
> +
>  /**
>   * inode_dax: convert a public inode into its dax_dev
>   * @inode: An inode with i_cdev pointing to a dax_dev
> diff --git a/include/linux/dax.h b/include/linux/dax.h
> index 9fc5f99a0ae2..9800d84e5b7d 100644
> --- a/include/linux/dax.h
> +++ b/include/linux/dax.h
> @@ -32,8 +32,24 @@ struct dax_operations {
>         int (*zero_page_range)(struct dax_device *, pgoff_t, size_t);
>  };
>
> +struct dax_holder_operations {
> +       /*
> +        * notify_failure - notify memory failure into inner holder device
> +        * @dax_dev: the dax device which contains the holder
> +        * @offset: offset on this dax device where memory failure occurs
> +        * @len: length of this memory failure event
> +        * @flags: action flags for memory failure handler
> +        */
> +       int (*notify_failure)(struct dax_device *dax_dev, u64 offset,
> +                       u64 len, int mf_flags);
> +};
> +
>  #if IS_ENABLED(CONFIG_DAX)
>  struct dax_device *alloc_dax(void *private, const struct dax_operations *ops);
> +int dax_register_holder(struct dax_device *dax_dev, void *holder,
> +               const struct dax_holder_operations *ops);
> +void dax_unregister_holder(struct dax_device *dax_dev);
> +void *dax_holder(struct dax_device *dax_dev);
>  void put_dax(struct dax_device *dax_dev);
>  void kill_dax(struct dax_device *dax_dev);
>  void dax_write_cache(struct dax_device *dax_dev, bool wc);
> @@ -53,6 +69,18 @@ static inline bool daxdev_mapping_supported(struct vm_area_struct *vma,
>         return dax_synchronous(dax_dev);
>  }
>  #else
> +static inline int dax_register_holder(struct dax_device *dax_dev, void *holder,
> +               const struct dax_holder_operations *ops)
> +{
> +       return 0;
> +}
> +static inline void dax_unregister_holder(struct dax_device *dax_dev)
> +{
> +}
> +static inline void *dax_holder(struct dax_device *dax_dev)
> +{
> +       return NULL;
> +}
>  static inline struct dax_device *alloc_dax(void *private,
>                 const struct dax_operations *ops)
>  {
> @@ -185,6 +213,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, u64 off, u64 len,
> +               int mf_flags);
>  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.34.1
>
>
>


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

* Re: [PATCH v10 2/9] mm: factor helpers for memory_failure_dev_pagemap
  2022-01-27 12:40 ` [PATCH v10 2/9] mm: factor helpers for memory_failure_dev_pagemap Shiyang Ruan
  2022-02-01 21:03   ` Matthew Wilcox
@ 2022-02-15 22:11   ` Dan Williams
  1 sibling, 0 replies; 36+ messages in thread
From: Dan Williams @ 2022-02-15 22:11 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: Linux Kernel Mailing List, linux-xfs, Linux NVDIMM, Linux MM,
	linux-fsdevel, Darrick J. Wong, david, Christoph Hellwig,
	Jane Chu, Christoph Hellwig

On Thu, Jan 27, 2022 at 4:41 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>
> 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>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Reviewed-by: Christoph Hellwig <hch@lst.de>

Looks good,

Reviewed-by: Dan Williams <dan.j.williams@intel.com>


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

* Re: [PATCH v10 3/9] pagemap,pmem: Introduce ->memory_failure()
  2022-01-27 12:40 ` [PATCH v10 3/9] pagemap,pmem: Introduce ->memory_failure() Shiyang Ruan
@ 2022-02-15 22:38   ` Dan Williams
  0 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2022-02-15 22:38 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: Linux Kernel Mailing List, linux-xfs, Linux NVDIMM, Linux MM,
	linux-fsdevel, Darrick J. Wong, david, Christoph Hellwig,
	Jane Chu, Christoph Hellwig

On Thu, Jan 27, 2022 at 4:41 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>
> 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.
>
> 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>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> ---
>  drivers/nvdimm/pmem.c    | 16 ++++++++++++++++
>  include/linux/memremap.h | 12 ++++++++++++
>  mm/memory-failure.c      | 14 ++++++++++++++
>  3 files changed, 42 insertions(+)
>
> diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
> index 58d95242a836..0a6e8698d086 100644
> --- a/drivers/nvdimm/pmem.c
> +++ b/drivers/nvdimm/pmem.c
> @@ -366,6 +366,20 @@ static void pmem_release_disk(void *__pmem)
>         blk_cleanup_disk(pmem->disk);
>  }
>
> +static int pmem_pagemap_memory_failure(struct dev_pagemap *pgmap,
> +               unsigned long pfn, u64 len, int mf_flags)
> +{
> +       struct pmem_device *pmem =
> +                       container_of(pgmap, struct pmem_device, pgmap);
> +       u64 offset = PFN_PHYS(pfn) - pmem->phys_addr - pmem->data_offset;
> +
> +       return dax_holder_notify_failure(pmem->dax_dev, offset, len, mf_flags);
> +}
> +
> +static const struct dev_pagemap_ops fsdax_pagemap_ops = {
> +       .memory_failure         = pmem_pagemap_memory_failure,
> +};
> +
>  static int pmem_attach_disk(struct device *dev,
>                 struct nd_namespace_common *ndns)
>  {
> @@ -427,6 +441,7 @@ static int pmem_attach_disk(struct device *dev,
>         pmem->pfn_flags = PFN_DEV;
>         if (is_nd_pfn(dev)) {
>                 pmem->pgmap.type = MEMORY_DEVICE_FS_DAX;
> +               pmem->pgmap.ops = &fsdax_pagemap_ops;
>                 addr = devm_memremap_pages(dev, &pmem->pgmap);
>                 pfn_sb = nd_pfn->pfn_sb;
>                 pmem->data_offset = le64_to_cpu(pfn_sb->dataoff);
> @@ -440,6 +455,7 @@ static int pmem_attach_disk(struct device *dev,
>                 pmem->pgmap.range.end = res->end;
>                 pmem->pgmap.nr_range = 1;
>                 pmem->pgmap.type = MEMORY_DEVICE_FS_DAX;
> +               pmem->pgmap.ops = &fsdax_pagemap_ops;
>                 addr = devm_memremap_pages(dev, &pmem->pgmap);
>                 pmem->pfn_flags |= PFN_MAP;
>                 bb_range = pmem->pgmap.range;
> diff --git a/include/linux/memremap.h b/include/linux/memremap.h
> index 1fafcc38acba..f739318b496f 100644
> --- a/include/linux/memremap.h
> +++ b/include/linux/memremap.h
> @@ -77,6 +77,18 @@ 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 mf_flags is finally passed to the recover
> +        * function through the whole notify routine.
> +        *
> +        * When this is not implemented, or it returns -EOPNOTSUPP, the caller
> +        * will fall back to a common handler called mf_generic_kill_procs().
> +        */
> +       int (*memory_failure)(struct dev_pagemap *pgmap, unsigned long pfn,
> +                             u64 len, int mf_flags);

I think it is odd to have the start address be in terms of pfns and
the length by in terms of bytes. I would either change @len to
@nr_pages, or change @pfn to @phys and make it a phys_addr_t.

Otherwise you can add,

Reviewed-by: Dan Williams <dan.j.williams@intel.com>


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

* Re: [PATCH v10 4/9] fsdax: fix function description
  2022-02-02 13:04   ` Christoph Hellwig
@ 2022-02-15 23:51     ` Dan Williams
  0 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2022-02-15 23:51 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Shiyang Ruan, Linux Kernel Mailing List, linux-xfs, Linux NVDIMM,
	Linux MM, linux-fsdevel, Darrick J. Wong, david, Jane Chu,
	Christoph Hellwig

On Wed, Feb 2, 2022 at 5:04 AM Christoph Hellwig <hch@infradead.org> wrote:
>
> Dan, can you send this to Linus for 5.17 to get it out of the queue?

Sure.


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

* Re: [PATCH v10 5/9] fsdax: Introduce dax_load_page()
  2022-01-27 12:40 ` [PATCH v10 5/9] fsdax: Introduce dax_load_page() Shiyang Ruan
@ 2022-02-16  1:34   ` Dan Williams
  2022-02-16  3:02     ` Shiyang Ruan
  0 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2022-02-16  1:34 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: Linux Kernel Mailing List, linux-xfs, Linux NVDIMM, Linux MM,
	linux-fsdevel, Darrick J. Wong, david, Christoph Hellwig,
	Jane Chu, Christoph Hellwig

On Thu, Jan 27, 2022 at 4:41 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>
> The current dax_lock_page() locks dax entry by obtaining mapping and
> index in page.  To support 1-to-N RMAP in NVDIMM, we need a new function
> to lock a specific dax entry

I do not see a call to dax_lock_entry() in this function, what keeps
this lookup valid after xas_unlock_irq()?


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

* Re: [PATCH v10 6/9] mm: move pgoff_address() to vma_pgoff_address()
  2022-01-27 12:40 ` [PATCH v10 6/9] mm: move pgoff_address() to vma_pgoff_address() Shiyang Ruan
@ 2022-02-16  1:37   ` Dan Williams
  0 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2022-02-16  1:37 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: Linux Kernel Mailing List, linux-xfs, Linux NVDIMM, Linux MM,
	linux-fsdevel, Darrick J. Wong, david, Christoph Hellwig,
	Jane Chu, Christoph Hellwig

On Thu, Jan 27, 2022 at 4:41 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>
> Since it is not a DAX-specific function, move it into mm and rename it
> to be a generic helper.
>
> Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
> Reviewed-by: Christoph Hellwig <hch@lst.de>

Looks ok to me.

Reviewed-by: Dan Williams <dan.j.williams@intel.com>


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

* Re: [PATCH v10 7/9] mm: Introduce mf_dax_kill_procs() for fsdax case
  2022-01-27 12:40 ` [PATCH v10 7/9] mm: Introduce mf_dax_kill_procs() for fsdax case Shiyang Ruan
@ 2022-02-16  1:47   ` Dan Williams
  2022-02-16  1:49   ` Dan Williams
  1 sibling, 0 replies; 36+ messages in thread
From: Dan Williams @ 2022-02-16  1:47 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: Linux Kernel Mailing List, linux-xfs, Linux NVDIMM, Linux MM,
	linux-fsdevel, Darrick J. Wong, david, Christoph Hellwig,
	Jane Chu

On Thu, Jan 27, 2022 at 4:41 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>
> This function is called at the end of RMAP routine, i.e. filesystem
> recovery function, to collect and kill processes using a shared page of
> DAX file.  The difference with mf_generic_kill_procs() is, it accepts
> file's (mapping,offset) instead of struct page because different files'
> mappings and offsets may share the same page in fsdax mode.
> It will be called when filesystem's RMAP results are found.
>
> Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
> ---
>  include/linux/mm.h  |  4 ++
>  mm/memory-failure.c | 91 +++++++++++++++++++++++++++++++++++++++------
>  2 files changed, 84 insertions(+), 11 deletions(-)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 9b1d56c5c224..0420189e4788 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -3195,6 +3195,10 @@ enum mf_flags {
>         MF_SOFT_OFFLINE = 1 << 3,
>         MF_UNPOISON = 1 << 4,
>  };
> +#if IS_ENABLED(CONFIG_FS_DAX)
> +int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index,
> +                     unsigned long count, int mf_flags);
> +#endif /* CONFIG_FS_DAX */
>  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 b2d13eba1071..8d123cc4102e 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -304,10 +304,9 @@ void shake_page(struct page *p)
>  }
>  EXPORT_SYMBOL_GPL(shake_page);
>
> -static unsigned long dev_pagemap_mapping_shift(struct page *page,
> -               struct vm_area_struct *vma)
> +static unsigned long dev_pagemap_mapping_shift(struct vm_area_struct *vma,
> +               unsigned long address)
>  {
> -       unsigned long address = vma_address(page, vma);
>         unsigned long ret = 0;
>         pgd_t *pgd;
>         p4d_t *p4d;
> @@ -347,9 +346,8 @@ 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,
> -                      struct vm_area_struct *vma,
> -                      struct list_head *to_kill)
> +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)
>  {
>         struct to_kill *tk;
>
> @@ -360,9 +358,15 @@ 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 not used for fsdax, we need
> +                * calculate the address based on the vma.
> +                */
> +               if (p->pgmap->type == MEMORY_DEVICE_FS_DAX)
> +                       tk->addr = vma_pgoff_address(vma, pgoff);
> +               tk->size_shift = dev_pagemap_mapping_shift(vma, tk->addr);
> +       } else
>                 tk->size_shift = page_shift(compound_head(p));
>
>         /*
> @@ -510,7 +514,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);

Why is the @pgoff argument 0? @pgoff is available. Not that I expect
dax pages will ever be anonymous, might as well not leave that land
mine for future refactoring.

Other than that you can add:

Reviewed-by: Dan Williams <dan.j.williams@intel.com>


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

* Re: [PATCH v10 7/9] mm: Introduce mf_dax_kill_procs() for fsdax case
  2022-01-27 12:40 ` [PATCH v10 7/9] mm: Introduce mf_dax_kill_procs() for fsdax case Shiyang Ruan
  2022-02-16  1:47   ` Dan Williams
@ 2022-02-16  1:49   ` Dan Williams
  1 sibling, 0 replies; 36+ messages in thread
From: Dan Williams @ 2022-02-16  1:49 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: Linux Kernel Mailing List, linux-xfs, Linux NVDIMM, Linux MM,
	linux-fsdevel, Darrick J. Wong, david, Christoph Hellwig,
	Jane Chu

On Thu, Jan 27, 2022 at 4:41 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>
> This function is called at the end of RMAP routine, i.e. filesystem
> recovery function, to collect and kill processes using a shared page of
> DAX file.  The difference with mf_generic_kill_procs() is, it accepts
> file's (mapping,offset) instead of struct page because different files'
> mappings and offsets may share the same page in fsdax mode.
> It will be called when filesystem's RMAP results are found.
>
> Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
> ---
>  include/linux/mm.h  |  4 ++
>  mm/memory-failure.c | 91 +++++++++++++++++++++++++++++++++++++++------
>  2 files changed, 84 insertions(+), 11 deletions(-)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 9b1d56c5c224..0420189e4788 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -3195,6 +3195,10 @@ enum mf_flags {
>         MF_SOFT_OFFLINE = 1 << 3,
>         MF_UNPOISON = 1 << 4,
>  };
> +#if IS_ENABLED(CONFIG_FS_DAX)
> +int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index,
> +                     unsigned long count, int mf_flags);
> +#endif /* CONFIG_FS_DAX */
>  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 b2d13eba1071..8d123cc4102e 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -304,10 +304,9 @@ void shake_page(struct page *p)
>  }
>  EXPORT_SYMBOL_GPL(shake_page);
>
> -static unsigned long dev_pagemap_mapping_shift(struct page *page,
> -               struct vm_area_struct *vma)
> +static unsigned long dev_pagemap_mapping_shift(struct vm_area_struct *vma,
> +               unsigned long address)
>  {
> -       unsigned long address = vma_address(page, vma);
>         unsigned long ret = 0;
>         pgd_t *pgd;
>         p4d_t *p4d;
> @@ -347,9 +346,8 @@ 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,
> -                      struct vm_area_struct *vma,
> -                      struct list_head *to_kill)
> +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)
>  {
>         struct to_kill *tk;
>
> @@ -360,9 +358,15 @@ 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 not used for fsdax, we need
> +                * calculate the address based on the vma.
> +                */
> +               if (p->pgmap->type == MEMORY_DEVICE_FS_DAX)
> +                       tk->addr = vma_pgoff_address(vma, pgoff);
> +               tk->size_shift = dev_pagemap_mapping_shift(vma, tk->addr);
> +       } else
>                 tk->size_shift = page_shift(compound_head(p));
>
>         /*
> @@ -510,7 +514,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);
> @@ -546,12 +550,40 @@ 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, 0, vma, to_kill);
> +               }
> +       }
> +       read_unlock(&tasklist_lock);
> +       i_mmap_unlock_read(mapping);
> +}
> +
> +#if IS_ENABLED(CONFIG_FS_DAX)
> +/*
> + * Collect processes when the error hit a fsdax page.
> + */
> +static void collect_procs_fsdax(struct page *page,
> +               struct address_space *mapping, pgoff_t pgoff,
> +               struct list_head *to_kill)
> +{
> +       struct vm_area_struct *vma;
> +       struct task_struct *tsk;
> +
> +       i_mmap_lock_read(mapping);
> +       read_lock(&tasklist_lock);
> +       for_each_process(tsk) {
> +               struct task_struct *t = task_early_kill(tsk, true);
> +
> +               if (!t)
> +                       continue;
> +               vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, pgoff) {
> +                       if (vma->vm_mm == t->mm)
> +                               add_to_kill(t, page, pgoff, vma, to_kill);
>                 }
>         }
>         read_unlock(&tasklist_lock);
>         i_mmap_unlock_read(mapping);
>  }
> +#endif /* CONFIG_FS_DAX */
>
>  /*
>   * Collect the processes who have the corrupted page mapped to kill.
> @@ -1574,6 +1606,43 @@ static int mf_generic_kill_procs(unsigned long long pfn, int flags,
>         return 0;
>  }
>
> +#if IS_ENABLED(CONFIG_FS_DAX)
> +/**
> + * mf_dax_kill_procs - Collect and kill processes who are using this file range
> + * @mapping:   the file in use
> + * @index:     start pgoff of the range within the file
> + * @count:     length of the range, in unit of PAGE_SIZE
> + * @mf_flags:  memory failure flags
> + */
> +int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index,
> +               unsigned long count, int mf_flags)
> +{
> +       LIST_HEAD(to_kill);
> +       int rc;
> +       struct page *page;
> +       size_t end = index + count;
> +
> +       mf_flags |= MF_ACTION_REQUIRED | MF_MUST_KILL;
> +
> +       for (; index < end; index++) {
> +               page = NULL;
> +               rc = dax_load_page(mapping, index, &page);
> +               if (rc)
> +                       return rc;
> +               if (!page)
> +                       continue;
> +
> +               SetPageHWPoison(page);
> +
> +               collect_procs_fsdax(page, mapping, index, &to_kill);
> +               unmap_and_kill(&to_kill, page_to_pfn(page), mapping,
> +                               index, mf_flags);

Depending on the answer to the question in patch5 there may need to be
a put_page() or dax_unlock_page() here.


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

* Re: [PATCH v10 8/9] xfs: Implement ->notify_failure() for XFS
  2022-01-27 12:40 ` [PATCH v10 8/9] xfs: Implement ->notify_failure() for XFS Shiyang Ruan
                     ` (2 preceding siblings ...)
  2022-02-01 20:41   ` Darrick J. Wong
@ 2022-02-16  1:56   ` Dan Williams
  3 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2022-02-16  1:56 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: Linux Kernel Mailing List, linux-xfs, Linux NVDIMM, Linux MM,
	linux-fsdevel, Darrick J. Wong, david, Christoph Hellwig,
	Jane Chu

On Thu, Jan 27, 2022 at 4:41 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>
> Introduce xfs_notify_failure.c to handle failure related works, such as
> implement ->notify_failure(), register/unregister dax holder in xfs, and
> so on.
>
> 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.
>
> Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
> ---
>  fs/xfs/Makefile             |   1 +
>  fs/xfs/xfs_buf.c            |  12 ++
>  fs/xfs/xfs_fsops.c          |   3 +
>  fs/xfs/xfs_mount.h          |   1 +
>  fs/xfs/xfs_notify_failure.c | 222 ++++++++++++++++++++++++++++++++++++
>  fs/xfs/xfs_notify_failure.h |  10 ++
>  6 files changed, 249 insertions(+)
>  create mode 100644 fs/xfs/xfs_notify_failure.c
>  create mode 100644 fs/xfs/xfs_notify_failure.h
>
> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> index 04611a1068b4..389970b3e13b 100644
> --- a/fs/xfs/Makefile
> +++ b/fs/xfs/Makefile
> @@ -84,6 +84,7 @@ xfs-y                         += xfs_aops.o \
>                                    xfs_message.o \
>                                    xfs_mount.o \
>                                    xfs_mru_cache.o \
> +                                  xfs_notify_failure.o \
>                                    xfs_pwork.o \
>                                    xfs_reflink.o \
>                                    xfs_stats.o \
> diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
> index b45e0d50a405..017010b3d601 100644
> --- a/fs/xfs/xfs_buf.c
> +++ b/fs/xfs/xfs_buf.c
> @@ -19,6 +19,7 @@
>  #include "xfs_errortag.h"
>  #include "xfs_error.h"
>  #include "xfs_ag.h"
> +#include "xfs_notify_failure.h"
>
>  static struct kmem_cache *xfs_buf_cache;
>
> @@ -1892,6 +1893,8 @@ xfs_free_buftarg(
>         list_lru_destroy(&btp->bt_lru);
>
>         blkdev_issue_flush(btp->bt_bdev);
> +       if (btp->bt_daxdev)
> +               dax_unregister_holder(btp->bt_daxdev);
>         fs_put_dax(btp->bt_daxdev);
>
>         kmem_free(btp);
> @@ -1946,6 +1949,15 @@ xfs_alloc_buftarg(
>         btp->bt_dev =  bdev->bd_dev;
>         btp->bt_bdev = bdev;
>         btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off);
> +       if (btp->bt_daxdev) {
> +               if (dax_get_holder(btp->bt_daxdev)) {
> +                       xfs_err(mp, "DAX device already in use?!");

Per the earlier feedback this can be checked atomically inside of
dax_register_holder() with cmpxchg().

> +                       goto error_free;
> +               }
> +
> +               dax_register_holder(btp->bt_daxdev, mp,
> +                               &xfs_dax_holder_operations);
> +       }
>
>         /*
>          * Buffer IO error rate limiting. Limit it to no more than 10 messages
> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> index 33e26690a8c4..d4d36c5bef11 100644
> --- a/fs/xfs/xfs_fsops.c
> +++ b/fs/xfs/xfs_fsops.c
> @@ -542,6 +542,9 @@ xfs_do_force_shutdown(
>         } else if (flags & SHUTDOWN_CORRUPT_INCORE) {
>                 tag = XFS_PTAG_SHUTDOWN_CORRUPT;
>                 why = "Corruption of in-memory data";
> +       } else if (flags & SHUTDOWN_CORRUPT_ONDISK) {
> +               tag = XFS_PTAG_SHUTDOWN_CORRUPT;
> +               why = "Corruption of on-disk metadata";
>         } else {
>                 tag = XFS_PTAG_SHUTDOWN_IOERROR;
>                 why = "Metadata I/O Error";
> diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
> index 00720a02e761..47ff4ac53c4c 100644
> --- a/fs/xfs/xfs_mount.h
> +++ b/fs/xfs/xfs_mount.h
> @@ -435,6 +435,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_ONDISK        0x0010  /* corrupt metadata on device */
>
>  #define XFS_SHUTDOWN_STRINGS \
>         { SHUTDOWN_META_IO_ERROR,       "metadata_io" }, \
> diff --git a/fs/xfs/xfs_notify_failure.c b/fs/xfs/xfs_notify_failure.c
> new file mode 100644
> index 000000000000..6abaa043f4bc
> --- /dev/null
> +++ b/fs/xfs/xfs_notify_failure.c
> @@ -0,0 +1,222 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 Fujitsu.  All Rights Reserved.
> + */
> +
> +#include "xfs.h"
> +#include "xfs_shared.h"
> +#include "xfs_format.h"
> +#include "xfs_log_format.h"
> +#include "xfs_trans_resv.h"
> +#include "xfs_mount.h"
> +#include "xfs_alloc.h"
> +#include "xfs_bit.h"
> +#include "xfs_btree.h"
> +#include "xfs_inode.h"
> +#include "xfs_icache.h"
> +#include "xfs_rmap.h"
> +#include "xfs_rmap_btree.h"
> +#include "xfs_rtalloc.h"
> +#include "xfs_trans.h"
> +
> +#include <linux/mm.h>
> +#include <linux/dax.h>
> +
> +struct failure_info {
> +       xfs_agblock_t           startblock;
> +       xfs_extlen_t            blockcount;
> +       int                     mf_flags;
> +};
> +
> +#if IS_ENABLED(CONFIG_MEMORY_FAILURE) && IS_ENABLED(CONFIG_FS_DAX)
> +static pgoff_t
> +xfs_failure_pgoff(
> +       struct xfs_mount                *mp,
> +       const struct xfs_rmap_irec      *rec,
> +       const struct failure_info       *notify)
> +{
> +       uint64_t                        pos = rec->rm_offset;
> +
> +       if (notify->startblock > rec->rm_startblock)
> +               pos += XFS_FSB_TO_B(mp,
> +                               notify->startblock - rec->rm_startblock);
> +       return pos >> PAGE_SHIFT;
> +}
> +
> +static unsigned long
> +xfs_failure_pgcnt(
> +       struct xfs_mount                *mp,
> +       const struct xfs_rmap_irec      *rec,
> +       const struct failure_info       *notify)
> +{
> +       xfs_agblock_t                   end_rec;
> +       xfs_agblock_t                   end_notify;
> +       xfs_agblock_t                   start_cross;
> +       xfs_agblock_t                   end_cross;
> +
> +       start_cross = max(rec->rm_startblock, notify->startblock);
> +
> +       end_rec = rec->rm_startblock + rec->rm_blockcount;
> +       end_notify = notify->startblock + notify->blockcount;
> +       end_cross = min(end_rec, end_notify);
> +
> +       return XFS_FSB_TO_B(mp, end_cross - start_cross) >> PAGE_SHIFT;
> +}
> +
> +static int
> +xfs_dax_failure_fn(
> +       struct xfs_btree_cur            *cur,
> +       const struct xfs_rmap_irec      *rec,
> +       void                            *data)
> +{
> +       struct xfs_mount                *mp = cur->bc_mp;
> +       struct xfs_inode                *ip;
> +       struct failure_info             *notify = data;
> +       int                             error = 0;
> +
> +       if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
> +           (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
> +               xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
> +               return -EFSCORRUPTED;
> +       }
> +
> +       /* Get files that incore, filter out others that are not in use. */
> +       error = xfs_iget(mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE,
> +                        0, &ip);
> +       /* Continue the rmap query if the inode isn't incore */
> +       if (error == -ENODATA)
> +               return 0;
> +       if (error)
> +               return error;
> +
> +       error = mf_dax_kill_procs(VFS_I(ip)->i_mapping,
> +                                 xfs_failure_pgoff(mp, rec, notify),
> +                                 xfs_failure_pgcnt(mp, rec, notify),
> +                                 notify->mf_flags);
> +       xfs_irele(ip);
> +       return error;
> +}
> +#else
> +static int
> +xfs_dax_failure_fn(
> +       struct xfs_btree_cur            *cur,
> +       const struct xfs_rmap_irec      *rec,
> +       void                            *data)
> +{
> +       struct xfs_mount                *mp = cur->bc_mp;
> +
> +       /* No other option besides shutting down the fs. */
> +       xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
> +       return -EFSCORRUPTED;
> +}
> +#endif /* CONFIG_MEMORY_FAILURE && CONFIG_FS_DAX */
> +
> +static int
> +xfs_dax_notify_ddev_failure(
> +       struct xfs_mount        *mp,
> +       xfs_daddr_t             daddr,
> +       xfs_daddr_t             bblen,
> +       int                     mf_flags)
> +{
> +       struct xfs_trans        *tp = NULL;
> +       struct xfs_btree_cur    *cur = NULL;
> +       struct xfs_buf          *agf_bp = NULL;
> +       struct failure_info     notify;
> +       int                     error = 0;
> +       xfs_fsblock_t           fsbno = XFS_DADDR_TO_FSB(mp, daddr);
> +       xfs_agnumber_t          agno = XFS_FSB_TO_AGNO(mp, fsbno);
> +       xfs_fsblock_t           end_fsbno = XFS_DADDR_TO_FSB(mp, daddr + bblen);
> +       xfs_agnumber_t          end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno);
> +
> +       /*
> +        * Once a file is found by rmap, we take the intersection of two ranges:
> +        * notification range and file extent range, to make sure we won't go
> +        * out of scope.
> +        */
> +       notify.mf_flags = mf_flags;
> +       notify.startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
> +       notify.blockcount = XFS_BB_TO_FSB(mp, bblen);
> +
> +       error = xfs_trans_alloc_empty(mp, &tp);
> +       if (error)
> +               return error;
> +
> +       for (; agno <= end_agno; agno++) {
> +               struct xfs_rmap_irec    ri_low = { };
> +               struct xfs_rmap_irec    ri_high;
> +
> +               error = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
> +               if (error)
> +                       break;
> +
> +               cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, agf_bp->b_pag);
> +
> +               /*
> +                * Set the rmap range from ri_low to ri_high, which represents
> +                * a [start, end] where we looking for the files or metadata.
> +                * The part of range out of a AG will be ignored.  So, it's fine
> +                * to set ri_low to "startblock" in all loops.  When it reaches
> +                * the last AG, set the ri_high to "endblock" to make sure we
> +                * actually end at the end.
> +                */
> +               memset(&ri_high, 0xFF, sizeof(ri_high));
> +               ri_low.rm_startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
> +               if (agno == end_agno)
> +                       ri_high.rm_startblock = XFS_FSB_TO_AGBNO(mp, end_fsbno);
> +
> +               error = xfs_rmap_query_range(cur, &ri_low, &ri_high,
> +                               xfs_dax_failure_fn, &notify);
> +               xfs_btree_del_cursor(cur, error);
> +               xfs_trans_brelse(tp, agf_bp);
> +               if (error)
> +                       break;
> +
> +               fsbno = XFS_AGB_TO_FSB(mp, agno + 1, 0);
> +       }
> +
> +       xfs_trans_cancel(tp);
> +       return error;
> +}
> +
> +static int
> +xfs_dax_notify_failure(
> +       struct dax_device       *dax_dev,
> +       u64                     offset,
> +       u64                     len,
> +       int                     mf_flags)
> +{
> +       struct xfs_mount        *mp = dax_get_holder(dax_dev);
> +
> +       if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_daxdev == dax_dev) {
> +               xfs_warn(mp,
> +                        "notify_failure() not supported on 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_ONDISK);
> +               return -EFSCORRUPTED;
> +       }
> +
> +       if (!xfs_has_rmapbt(mp)) {
> +               xfs_warn(mp, "notify_failure() needs rmapbt enabled!");

Doesn't this need to be resolved at mount time?

> +               return -EOPNOTSUPP;
> +       }
> +
> +       if (offset < mp->m_ddev_targp->bt_dax_part_off ||
> +           ((offset + len) > mp->m_ddev_targp->bt_bdev->bd_nr_sectors <<
> +                               SECTOR_SHIFT)) {

With the removal of partition support bt_dax_part_off can never be
non-zero and the offset / len validation should be done against the
boundaries of the dax device in terms of physical page offset and
nr_pages.

> +               xfs_warn(mp, "notify_failure() goes out of the scope.");
> +               return -ENXIO;
> +       }
> +
> +       offset -= mp->m_ddev_targp->bt_dax_part_off;
> +       return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len),
> +                       mf_flags);

Same here, all offset adjustment code can be dropped because failure
notification should be disabled at mount time if the mount point is
not associated with a whole disk device.

> +}
> +
> +const struct dax_holder_operations xfs_dax_holder_operations = {
> +       .notify_failure         = xfs_dax_notify_failure,
> +};
> diff --git a/fs/xfs/xfs_notify_failure.h b/fs/xfs/xfs_notify_failure.h
> new file mode 100644
> index 000000000000..f40cb315e7ce
> --- /dev/null
> +++ b/fs/xfs/xfs_notify_failure.h
> @@ -0,0 +1,10 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 Fujitsu.  All Rights Reserved.
> + */
> +#ifndef __XFS_NOTIFY_FAILURE_H__
> +#define __XFS_NOTIFY_FAILURE_H__
> +
> +extern const struct dax_holder_operations xfs_dax_holder_operations;
> +
> +#endif  /* __XFS_NOTIFY_FAILURE_H__ */
> --
> 2.34.1
>
>
>


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

* Re: [PATCH v10 9/9] fsdax: set a CoW flag when associate reflink mappings
  2022-01-27 12:40 ` [PATCH v10 9/9] fsdax: set a CoW flag when associate reflink mappings Shiyang Ruan
@ 2022-02-16  2:09   ` Dan Williams
  2022-02-16  2:55     ` Shiyang Ruan
  0 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2022-02-16  2:09 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: Linux Kernel Mailing List, linux-xfs, Linux NVDIMM, Linux MM,
	linux-fsdevel, Darrick J. Wong, david, Christoph Hellwig,
	Jane Chu

On Thu, Jan 27, 2022 at 4:41 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>
> Introduce a PAGE_MAPPING_DAX_COW flag to support association with CoW file
> mappings.  In this case, the dax-RMAP already takes the responsibility
> to look up for shared files by given dax page.  The page->mapping is no
> longer to used for rmap but for marking that this dax page is shared.
> And to make sure disassociation works fine, we use page->index as
> refcount, and clear page->mapping to the initial state when page->index
> is decreased to 0.
>
> With the help of this new flag, it is able to distinguish normal case
> and CoW case, and keep the warning in normal case.
>
> Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
> ---
>  fs/dax.c                   | 65 ++++++++++++++++++++++++++++++++------
>  include/linux/page-flags.h |  6 ++++
>  2 files changed, 62 insertions(+), 9 deletions(-)
>
> diff --git a/fs/dax.c b/fs/dax.c
> index 250794a5b789..88879c579c1f 100644
> --- a/fs/dax.c
> +++ b/fs/dax.c
> @@ -334,13 +334,46 @@ static unsigned long dax_end_pfn(void *entry)
>         for (pfn = dax_to_pfn(entry); \
>                         pfn < dax_end_pfn(entry); pfn++)
>
> +static inline void dax_mapping_set_cow_flag(struct address_space *mapping)
> +{
> +       mapping = (struct address_space *)PAGE_MAPPING_DAX_COW;
> +}
> +
> +static inline bool dax_mapping_is_cow(struct address_space *mapping)
> +{
> +       return (unsigned long)mapping == PAGE_MAPPING_DAX_COW;
> +}
> +
>  /*
> - * TODO: for reflink+dax we need a way to associate a single page with
> - * multiple address_space instances at different linear_page_index()
> - * offsets.
> + * Set or Update the page->mapping with FS_DAX_MAPPING_COW flag.
> + * Return true if it is an Update.
> + */
> +static inline bool dax_mapping_set_cow(struct page *page)
> +{
> +       if (page->mapping) {
> +               /* flag already set */
> +               if (dax_mapping_is_cow(page->mapping))
> +                       return false;
> +
> +               /*
> +                * This page has been mapped even before it is shared, just
> +                * need to set this FS_DAX_MAPPING_COW flag.
> +                */
> +               dax_mapping_set_cow_flag(page->mapping);
> +               return true;
> +       }
> +       /* Newly associate CoW mapping */
> +       dax_mapping_set_cow_flag(page->mapping);
> +       return false;
> +}
> +
> +/*
> + * When it is called in dax_insert_entry(), the cow flag will indicate that
> + * whether this entry is shared by multiple files.  If so, set the page->mapping
> + * to be FS_DAX_MAPPING_COW, and use page->index as refcount.
>   */
>  static void dax_associate_entry(void *entry, struct address_space *mapping,
> -               struct vm_area_struct *vma, unsigned long address)
> +               struct vm_area_struct *vma, unsigned long address, bool cow)
>  {
>         unsigned long size = dax_entry_size(entry), pfn, index;
>         int i = 0;
> @@ -352,9 +385,17 @@ static void dax_associate_entry(void *entry, struct address_space *mapping,
>         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++;
> +               if (cow) {
> +                       if (dax_mapping_set_cow(page)) {
> +                               /* Was normal, now updated to CoW */
> +                               page->index = 2;
> +                       } else
> +                               page->index++;
> +               } else {
> +                       WARN_ON_ONCE(page->mapping);
> +                       page->mapping = mapping;
> +                       page->index = index + i++;
> +               }
>         }
>  }
>
> @@ -370,7 +411,12 @@ static void dax_disassociate_entry(void *entry, struct address_space *mapping,
>                 struct page *page = pfn_to_page(pfn);
>
>                 WARN_ON_ONCE(trunc && page_ref_count(page) > 1);
> -               WARN_ON_ONCE(page->mapping && page->mapping != mapping);
> +               if (!dax_mapping_is_cow(page->mapping)) {
> +                       /* keep the CoW flag if this page is still shared */
> +                       if (page->index-- > 0)
> +                               continue;
> +               } else
> +                       WARN_ON_ONCE(page->mapping && page->mapping != mapping);
>                 page->mapping = NULL;
>                 page->index = 0;
>         }
> @@ -810,7 +856,8 @@ static void *dax_insert_entry(struct xa_state *xas,
>                 void *old;
>
>                 dax_disassociate_entry(entry, mapping, false);
> -               dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address);
> +               dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address,
> +                               false);

Where is the caller that passes 'true'? Also when that caller arrives
introduce a separate dax_associate_cow_entry() as that's easier to
read than dax_associate_entry(..., true) in case someone does not
remember what that boolean flag means.

However, it's not clear to me that this approach is a good idea given
that the filesystem is the source of truth for how many address_spaces
this page mapping might be duplicated. What about a iomap_page_ops for
fsdax to ask the filesystem when it is ok to clear the mapping
association for a page?

>                 /*
>                  * Only swap our new entry into the page cache if the current
>                  * entry is a zero page or an empty entry.  If a normal PTE or
> diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
> index 1c3b6e5c8bfd..6370d279795a 100644
> --- a/include/linux/page-flags.h
> +++ b/include/linux/page-flags.h
> @@ -572,6 +572,12 @@ __PAGEFLAG(Reported, reported, PF_NO_COMPOUND)
>  #define PAGE_MAPPING_KSM       (PAGE_MAPPING_ANON | PAGE_MAPPING_MOVABLE)
>  #define PAGE_MAPPING_FLAGS     (PAGE_MAPPING_ANON | PAGE_MAPPING_MOVABLE)
>
> +/*
> + * Different with flags above, this flag is used only for fsdax mode.  It
> + * indicates that this page->mapping is now under reflink case.
> + */
> +#define PAGE_MAPPING_DAX_COW   0x1
> +
>  static __always_inline int PageMappingFlags(struct page *page)
>  {
>         return ((unsigned long)page->mapping & PAGE_MAPPING_FLAGS) != 0;
> --
> 2.34.1
>
>
>


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

* Re: [PATCH v10 9/9] fsdax: set a CoW flag when associate reflink mappings
  2022-02-16  2:09   ` Dan Williams
@ 2022-02-16  2:55     ` Shiyang Ruan
  2022-02-16  3:09       ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: Shiyang Ruan @ 2022-02-16  2:55 UTC (permalink / raw)
  To: Dan Williams
  Cc: Linux Kernel Mailing List, linux-xfs, Linux NVDIMM, Linux MM,
	linux-fsdevel, Darrick J. Wong, david, Christoph Hellwig,
	Jane Chu



在 2022/2/16 10:09, Dan Williams 写道:
> On Thu, Jan 27, 2022 at 4:41 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>>
>> Introduce a PAGE_MAPPING_DAX_COW flag to support association with CoW file
>> mappings.  In this case, the dax-RMAP already takes the responsibility
>> to look up for shared files by given dax page.  The page->mapping is no
>> longer to used for rmap but for marking that this dax page is shared.
>> And to make sure disassociation works fine, we use page->index as
>> refcount, and clear page->mapping to the initial state when page->index
>> is decreased to 0.
>>
>> With the help of this new flag, it is able to distinguish normal case
>> and CoW case, and keep the warning in normal case.
>>
>> Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
>> ---
>>   fs/dax.c                   | 65 ++++++++++++++++++++++++++++++++------
>>   include/linux/page-flags.h |  6 ++++
>>   2 files changed, 62 insertions(+), 9 deletions(-)
>>
>> diff --git a/fs/dax.c b/fs/dax.c
>> index 250794a5b789..88879c579c1f 100644
>> --- a/fs/dax.c
>> +++ b/fs/dax.c
>> @@ -334,13 +334,46 @@ static unsigned long dax_end_pfn(void *entry)
>>          for (pfn = dax_to_pfn(entry); \
>>                          pfn < dax_end_pfn(entry); pfn++)
>>
>> +static inline void dax_mapping_set_cow_flag(struct address_space *mapping)
>> +{
>> +       mapping = (struct address_space *)PAGE_MAPPING_DAX_COW;
>> +}
>> +
>> +static inline bool dax_mapping_is_cow(struct address_space *mapping)
>> +{
>> +       return (unsigned long)mapping == PAGE_MAPPING_DAX_COW;
>> +}
>> +
>>   /*
>> - * TODO: for reflink+dax we need a way to associate a single page with
>> - * multiple address_space instances at different linear_page_index()
>> - * offsets.
>> + * Set or Update the page->mapping with FS_DAX_MAPPING_COW flag.
>> + * Return true if it is an Update.
>> + */
>> +static inline bool dax_mapping_set_cow(struct page *page)
>> +{
>> +       if (page->mapping) {
>> +               /* flag already set */
>> +               if (dax_mapping_is_cow(page->mapping))
>> +                       return false;
>> +
>> +               /*
>> +                * This page has been mapped even before it is shared, just
>> +                * need to set this FS_DAX_MAPPING_COW flag.
>> +                */
>> +               dax_mapping_set_cow_flag(page->mapping);
>> +               return true;
>> +       }
>> +       /* Newly associate CoW mapping */
>> +       dax_mapping_set_cow_flag(page->mapping);
>> +       return false;
>> +}
>> +
>> +/*
>> + * When it is called in dax_insert_entry(), the cow flag will indicate that
>> + * whether this entry is shared by multiple files.  If so, set the page->mapping
>> + * to be FS_DAX_MAPPING_COW, and use page->index as refcount.
>>    */
>>   static void dax_associate_entry(void *entry, struct address_space *mapping,
>> -               struct vm_area_struct *vma, unsigned long address)
>> +               struct vm_area_struct *vma, unsigned long address, bool cow)
>>   {
>>          unsigned long size = dax_entry_size(entry), pfn, index;
>>          int i = 0;
>> @@ -352,9 +385,17 @@ static void dax_associate_entry(void *entry, struct address_space *mapping,
>>          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++;
>> +               if (cow) {
>> +                       if (dax_mapping_set_cow(page)) {
>> +                               /* Was normal, now updated to CoW */
>> +                               page->index = 2;
>> +                       } else
>> +                               page->index++;
>> +               } else {
>> +                       WARN_ON_ONCE(page->mapping);
>> +                       page->mapping = mapping;
>> +                       page->index = index + i++;
>> +               }
>>          }
>>   }
>>
>> @@ -370,7 +411,12 @@ static void dax_disassociate_entry(void *entry, struct address_space *mapping,
>>                  struct page *page = pfn_to_page(pfn);
>>
>>                  WARN_ON_ONCE(trunc && page_ref_count(page) > 1);
>> -               WARN_ON_ONCE(page->mapping && page->mapping != mapping);
>> +               if (!dax_mapping_is_cow(page->mapping)) {
>> +                       /* keep the CoW flag if this page is still shared */
>> +                       if (page->index-- > 0)
>> +                               continue;
>> +               } else
>> +                       WARN_ON_ONCE(page->mapping && page->mapping != mapping);
>>                  page->mapping = NULL;
>>                  page->index = 0;
>>          }
>> @@ -810,7 +856,8 @@ static void *dax_insert_entry(struct xa_state *xas,
>>                  void *old;
>>
>>                  dax_disassociate_entry(entry, mapping, false);
>> -               dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address);
>> +               dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address,
>> +                               false);
> 
> Where is the caller that passes 'true'? Also when that caller arrives
> introduce a separate dax_associate_cow_entry() as that's easier to
> read than dax_associate_entry(..., true) in case someone does not
> remember what that boolean flag means.

This flag is supposed to be used when CoW support is introduced.  When 
it is a CoW operation, which is decided by iomap & srcmap's flag, this 
flag will be set true.

I think I should describe it in detail in the commit message.

> 
> However, it's not clear to me that this approach is a good idea given
> that the filesystem is the source of truth for how many address_spaces
> this page mapping might be duplicated. What about a iomap_page_ops for
> fsdax to ask the filesystem when it is ok to clear the mapping
> association for a page?

I'll think how to implement it in this way.


--
Thanks,
Ruan.

> 
>>                  /*
>>                   * Only swap our new entry into the page cache if the current
>>                   * entry is a zero page or an empty entry.  If a normal PTE or
>> diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
>> index 1c3b6e5c8bfd..6370d279795a 100644
>> --- a/include/linux/page-flags.h
>> +++ b/include/linux/page-flags.h
>> @@ -572,6 +572,12 @@ __PAGEFLAG(Reported, reported, PF_NO_COMPOUND)
>>   #define PAGE_MAPPING_KSM       (PAGE_MAPPING_ANON | PAGE_MAPPING_MOVABLE)
>>   #define PAGE_MAPPING_FLAGS     (PAGE_MAPPING_ANON | PAGE_MAPPING_MOVABLE)
>>
>> +/*
>> + * Different with flags above, this flag is used only for fsdax mode.  It
>> + * indicates that this page->mapping is now under reflink case.
>> + */
>> +#define PAGE_MAPPING_DAX_COW   0x1
>> +
>>   static __always_inline int PageMappingFlags(struct page *page)
>>   {
>>          return ((unsigned long)page->mapping & PAGE_MAPPING_FLAGS) != 0;
>> --
>> 2.34.1
>>
>>
>>




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

* Re: [PATCH v10 5/9] fsdax: Introduce dax_load_page()
  2022-02-16  1:34   ` Dan Williams
@ 2022-02-16  3:02     ` Shiyang Ruan
  2022-02-16  3:07       ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: Shiyang Ruan @ 2022-02-16  3:02 UTC (permalink / raw)
  To: Dan Williams
  Cc: Linux Kernel Mailing List, linux-xfs, Linux NVDIMM, Linux MM,
	linux-fsdevel, Darrick J. Wong, david, Christoph Hellwig,
	Jane Chu, Christoph Hellwig



在 2022/2/16 9:34, Dan Williams 写道:
> On Thu, Jan 27, 2022 at 4:41 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>>
>> The current dax_lock_page() locks dax entry by obtaining mapping and
>> index in page.  To support 1-to-N RMAP in NVDIMM, we need a new function
>> to lock a specific dax entry
> 
> I do not see a call to dax_lock_entry() in this function, what keeps
> this lookup valid after xas_unlock_irq()?

I am not sure if I understood your advice correctly:  You said 
dax_lock_entry() is not necessary in v9[1].  So, I deleted it.

[1]: 
https://lore.kernel.org/linux-xfs/CAPcyv4jVDfpHb1DCW+NLXH2YBgLghCVy8o6wrc02CXx4g-Bv7Q@mail.gmail.com/


--
Thanks,
Ruan.




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

* Re: [PATCH v10 5/9] fsdax: Introduce dax_load_page()
  2022-02-16  3:02     ` Shiyang Ruan
@ 2022-02-16  3:07       ` Dan Williams
  0 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2022-02-16  3:07 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: Linux Kernel Mailing List, linux-xfs, Linux NVDIMM, Linux MM,
	linux-fsdevel, Darrick J. Wong, david, Christoph Hellwig,
	Jane Chu, Christoph Hellwig

On Tue, Feb 15, 2022 at 7:02 PM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>
>
>
> 在 2022/2/16 9:34, Dan Williams 写道:
> > On Thu, Jan 27, 2022 at 4:41 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
> >>
> >> The current dax_lock_page() locks dax entry by obtaining mapping and
> >> index in page.  To support 1-to-N RMAP in NVDIMM, we need a new function
> >> to lock a specific dax entry
> >
> > I do not see a call to dax_lock_entry() in this function, what keeps
> > this lookup valid after xas_unlock_irq()?
>
> I am not sure if I understood your advice correctly:  You said
> dax_lock_entry() is not necessary in v9[1].  So, I deleted it.
>
> [1]:
> https://lore.kernel.org/linux-xfs/CAPcyv4jVDfpHb1DCW+NLXH2YBgLghCVy8o6wrc02CXx4g-Bv7Q@mail.gmail.com/

I also said, "if the filesystem can make those guarantees" it was not
clear whether this helper is being called back from an FS context that
guarantees those associations or not. As far as I can see there is
nothing that protects that association. Apologies for the confusion, I
was misunderstanding where the protection was being enforced in this
case.


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

* Re: [PATCH v10 9/9] fsdax: set a CoW flag when associate reflink mappings
  2022-02-16  2:55     ` Shiyang Ruan
@ 2022-02-16  3:09       ` Dan Williams
  0 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2022-02-16  3:09 UTC (permalink / raw)
  To: Shiyang Ruan
  Cc: Linux Kernel Mailing List, linux-xfs, Linux NVDIMM, Linux MM,
	linux-fsdevel, Darrick J. Wong, david, Christoph Hellwig,
	Jane Chu

On Tue, Feb 15, 2022 at 6:55 PM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
>
>
>
> 在 2022/2/16 10:09, Dan Williams 写道:
> > On Thu, Jan 27, 2022 at 4:41 AM Shiyang Ruan <ruansy.fnst@fujitsu.com> wrote:
> >>
> >> Introduce a PAGE_MAPPING_DAX_COW flag to support association with CoW file
> >> mappings.  In this case, the dax-RMAP already takes the responsibility
> >> to look up for shared files by given dax page.  The page->mapping is no
> >> longer to used for rmap but for marking that this dax page is shared.
> >> And to make sure disassociation works fine, we use page->index as
> >> refcount, and clear page->mapping to the initial state when page->index
> >> is decreased to 0.
> >>
> >> With the help of this new flag, it is able to distinguish normal case
> >> and CoW case, and keep the warning in normal case.
> >>
> >> Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
> >> ---
> >>   fs/dax.c                   | 65 ++++++++++++++++++++++++++++++++------
> >>   include/linux/page-flags.h |  6 ++++
> >>   2 files changed, 62 insertions(+), 9 deletions(-)
> >>
> >> diff --git a/fs/dax.c b/fs/dax.c
> >> index 250794a5b789..88879c579c1f 100644
> >> --- a/fs/dax.c
> >> +++ b/fs/dax.c
> >> @@ -334,13 +334,46 @@ static unsigned long dax_end_pfn(void *entry)
> >>          for (pfn = dax_to_pfn(entry); \
> >>                          pfn < dax_end_pfn(entry); pfn++)
> >>
> >> +static inline void dax_mapping_set_cow_flag(struct address_space *mapping)
> >> +{
> >> +       mapping = (struct address_space *)PAGE_MAPPING_DAX_COW;
> >> +}
> >> +
> >> +static inline bool dax_mapping_is_cow(struct address_space *mapping)
> >> +{
> >> +       return (unsigned long)mapping == PAGE_MAPPING_DAX_COW;
> >> +}
> >> +
> >>   /*
> >> - * TODO: for reflink+dax we need a way to associate a single page with
> >> - * multiple address_space instances at different linear_page_index()
> >> - * offsets.
> >> + * Set or Update the page->mapping with FS_DAX_MAPPING_COW flag.
> >> + * Return true if it is an Update.
> >> + */
> >> +static inline bool dax_mapping_set_cow(struct page *page)
> >> +{
> >> +       if (page->mapping) {
> >> +               /* flag already set */
> >> +               if (dax_mapping_is_cow(page->mapping))
> >> +                       return false;
> >> +
> >> +               /*
> >> +                * This page has been mapped even before it is shared, just
> >> +                * need to set this FS_DAX_MAPPING_COW flag.
> >> +                */
> >> +               dax_mapping_set_cow_flag(page->mapping);
> >> +               return true;
> >> +       }
> >> +       /* Newly associate CoW mapping */
> >> +       dax_mapping_set_cow_flag(page->mapping);
> >> +       return false;
> >> +}
> >> +
> >> +/*
> >> + * When it is called in dax_insert_entry(), the cow flag will indicate that
> >> + * whether this entry is shared by multiple files.  If so, set the page->mapping
> >> + * to be FS_DAX_MAPPING_COW, and use page->index as refcount.
> >>    */
> >>   static void dax_associate_entry(void *entry, struct address_space *mapping,
> >> -               struct vm_area_struct *vma, unsigned long address)
> >> +               struct vm_area_struct *vma, unsigned long address, bool cow)
> >>   {
> >>          unsigned long size = dax_entry_size(entry), pfn, index;
> >>          int i = 0;
> >> @@ -352,9 +385,17 @@ static void dax_associate_entry(void *entry, struct address_space *mapping,
> >>          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++;
> >> +               if (cow) {
> >> +                       if (dax_mapping_set_cow(page)) {
> >> +                               /* Was normal, now updated to CoW */
> >> +                               page->index = 2;
> >> +                       } else
> >> +                               page->index++;
> >> +               } else {
> >> +                       WARN_ON_ONCE(page->mapping);
> >> +                       page->mapping = mapping;
> >> +                       page->index = index + i++;
> >> +               }
> >>          }
> >>   }
> >>
> >> @@ -370,7 +411,12 @@ static void dax_disassociate_entry(void *entry, struct address_space *mapping,
> >>                  struct page *page = pfn_to_page(pfn);
> >>
> >>                  WARN_ON_ONCE(trunc && page_ref_count(page) > 1);
> >> -               WARN_ON_ONCE(page->mapping && page->mapping != mapping);
> >> +               if (!dax_mapping_is_cow(page->mapping)) {
> >> +                       /* keep the CoW flag if this page is still shared */
> >> +                       if (page->index-- > 0)
> >> +                               continue;
> >> +               } else
> >> +                       WARN_ON_ONCE(page->mapping && page->mapping != mapping);
> >>                  page->mapping = NULL;
> >>                  page->index = 0;
> >>          }
> >> @@ -810,7 +856,8 @@ static void *dax_insert_entry(struct xa_state *xas,
> >>                  void *old;
> >>
> >>                  dax_disassociate_entry(entry, mapping, false);
> >> -               dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address);
> >> +               dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address,
> >> +                               false);
> >
> > Where is the caller that passes 'true'? Also when that caller arrives
> > introduce a separate dax_associate_cow_entry() as that's easier to
> > read than dax_associate_entry(..., true) in case someone does not
> > remember what that boolean flag means.
>
> This flag is supposed to be used when CoW support is introduced.

Ok, so should this patch wait and be a part of that series? It's
otherwise confusing to introduce a new capability in a patch set and
not take advantage of it until a separate / later patch set.

> When
> it is a CoW operation, which is decided by iomap & srcmap's flag, this
> flag will be set true.
>
> I think I should describe it in detail in the commit message.

That could help, or move it to the COW support series.

> > However, it's not clear to me that this approach is a good idea given
> > that the filesystem is the source of truth for how many address_spaces
> > this page mapping might be duplicated. What about a iomap_page_ops for
> > fsdax to ask the filesystem when it is ok to clear the mapping
> > association for a page?
>
> I'll think how to implement it in this way.
>
>
> --
> Thanks,
> Ruan.
>
> >
> >>                  /*
> >>                   * Only swap our new entry into the page cache if the current
> >>                   * entry is a zero page or an empty entry.  If a normal PTE or
> >> diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
> >> index 1c3b6e5c8bfd..6370d279795a 100644
> >> --- a/include/linux/page-flags.h
> >> +++ b/include/linux/page-flags.h
> >> @@ -572,6 +572,12 @@ __PAGEFLAG(Reported, reported, PF_NO_COMPOUND)
> >>   #define PAGE_MAPPING_KSM       (PAGE_MAPPING_ANON | PAGE_MAPPING_MOVABLE)
> >>   #define PAGE_MAPPING_FLAGS     (PAGE_MAPPING_ANON | PAGE_MAPPING_MOVABLE)
> >>
> >> +/*
> >> + * Different with flags above, this flag is used only for fsdax mode.  It
> >> + * indicates that this page->mapping is now under reflink case.
> >> + */
> >> +#define PAGE_MAPPING_DAX_COW   0x1
> >> +
> >>   static __always_inline int PageMappingFlags(struct page *page)
> >>   {
> >>          return ((unsigned long)page->mapping & PAGE_MAPPING_FLAGS) != 0;
> >> --
> >> 2.34.1
> >>
> >>
> >>
>
>


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

end of thread, other threads:[~2022-02-16  3:09 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-27 12:40 [PATCH v10 0/9] fsdax: introduce fs query to support reflink Shiyang Ruan
2022-01-27 12:40 ` [PATCH v10 1/9] dax: Introduce holder for dax_device Shiyang Ruan
2022-01-27 16:13   ` kernel test robot
2022-01-27 16:44   ` kernel test robot
2022-02-02 13:03   ` Christoph Hellwig
2022-02-13 12:58     ` [PATCH v10.1 " Shiyang Ruan
2022-02-15 22:06       ` Dan Williams
2022-01-27 12:40 ` [PATCH v10 2/9] mm: factor helpers for memory_failure_dev_pagemap Shiyang Ruan
2022-02-01 21:03   ` Matthew Wilcox
2022-02-15 22:11   ` Dan Williams
2022-01-27 12:40 ` [PATCH v10 3/9] pagemap,pmem: Introduce ->memory_failure() Shiyang Ruan
2022-02-15 22:38   ` Dan Williams
2022-01-27 12:40 ` [PATCH v10 4/9] fsdax: fix function description Shiyang Ruan
2022-02-02 13:04   ` Christoph Hellwig
2022-02-15 23:51     ` Dan Williams
2022-01-27 12:40 ` [PATCH v10 5/9] fsdax: Introduce dax_load_page() Shiyang Ruan
2022-02-16  1:34   ` Dan Williams
2022-02-16  3:02     ` Shiyang Ruan
2022-02-16  3:07       ` Dan Williams
2022-01-27 12:40 ` [PATCH v10 6/9] mm: move pgoff_address() to vma_pgoff_address() Shiyang Ruan
2022-02-16  1:37   ` Dan Williams
2022-01-27 12:40 ` [PATCH v10 7/9] mm: Introduce mf_dax_kill_procs() for fsdax case Shiyang Ruan
2022-02-16  1:47   ` Dan Williams
2022-02-16  1:49   ` Dan Williams
2022-01-27 12:40 ` [PATCH v10 8/9] xfs: Implement ->notify_failure() for XFS Shiyang Ruan
2022-01-27 17:56   ` kernel test robot
2022-01-27 19:39   ` kernel test robot
2022-02-01 20:41   ` Darrick J. Wong
2022-02-13 13:02     ` [PATCH v10.1 " Shiyang Ruan
2022-02-15  1:46       ` Darrick J. Wong
2022-02-15  9:42         ` Shiyang Ruan
2022-02-16  1:56   ` [PATCH v10 " Dan Williams
2022-01-27 12:40 ` [PATCH v10 9/9] fsdax: set a CoW flag when associate reflink mappings Shiyang Ruan
2022-02-16  2:09   ` Dan Williams
2022-02-16  2:55     ` Shiyang Ruan
2022-02-16  3:09       ` Dan Williams

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