anolis: dax, xfs: support memory failure without XFS rmapbt

ANBZ: #8858

Record the inode mapping into `page->mapping` instead of just marking
PAGE_MAPPING_DAX_SHARED.

Since `page->private` is not used for now, let's record `pgoff` into
`page->private` instead of `page->index` to match the upstream logic.

Finally, use `ip->i_reflink_opt_ip` to get the other reflink file
since such files are only reflinked by FICLONE other than
FICLONERANGE.

Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Link: https://gitee.com/anolis/cloud-kernel/pulls/3087
This commit is contained in:
Gao Xiang 2024-04-23 07:39:06 +08:00 committed by 小龙
parent f46433162e
commit 51f524bd80
5 changed files with 151 additions and 12 deletions

View File

@ -336,23 +336,34 @@ static unsigned long dax_end_pfn(void *entry)
static inline bool dax_page_is_shared(struct page *page)
{
return page->mapping == PAGE_MAPPING_DAX_SHARED;
return (unsigned long)READ_ONCE(page->mapping) & PAGE_MAPPING_DAX_SHARED;
}
/*
* Set the page->mapping with PAGE_MAPPING_DAX_SHARED flag, increase the
* refcount.
*/
static inline void dax_page_share_get(struct page *page)
static inline void dax_page_share_get(struct page *page,
struct address_space *mapping, pgoff_t index)
{
if (page->mapping != PAGE_MAPPING_DAX_SHARED) {
struct address_space *oldmapping = READ_ONCE(page->mapping);
if (!((unsigned long)oldmapping & PAGE_MAPPING_DAX_SHARED)) {
/*
* Reset the index if the page was already mapped
* regularly before.
*/
if (page->mapping)
if (oldmapping)
page->share = 1;
page->mapping = PAGE_MAPPING_DAX_SHARED;
if (test_bit(AS_FSDAX_NORMAP, &mapping->flags)) {
/* Note that we (ab)use page->private to keep index for now */
WRITE_ONCE(page->private, index);
/* paired with smp_mb() in xfs_dax_notify_ddev_failure2() */
smp_mb();
}
WRITE_ONCE(page->mapping,
(void *)((unsigned long)mapping | PAGE_MAPPING_DAX_SHARED));
}
page->share++;
}
@ -381,7 +392,7 @@ static void dax_associate_entry(void *entry, struct address_space *mapping,
struct page *page = pfn_to_page(pfn);
if (shared) {
dax_page_share_get(page);
dax_page_share_get(page, mapping, index);
} else {
WARN_ON_ONCE(page->mapping);
page->mapping = mapping;

View File

@ -1170,6 +1170,11 @@ xfs_file_remap_range(
src->i_reflink_opt_ip = dest;
dest->i_reflink_opt_ip = src;
mutex_unlock(&mp->m_reflink_opt_lock);
if (!xfs_sb_version_hasrmapbt(&mp->m_sb)) {
set_bit(AS_FSDAX_NORMAP, &VFS_I(src)->i_mapping->flags);
set_bit(AS_FSDAX_NORMAP, &VFS_I(dest)->i_mapping->flags);
}
}
out_unlock:

View File

@ -21,6 +21,7 @@
#include <linux/mm.h>
#include <linux/dax.h>
#include <linux/pfn_t.h>
struct xfs_failure_info {
xfs_agblock_t startblock;
@ -167,6 +168,128 @@ xfs_dax_notify_ddev_failure(
return error;
}
static int
xfs_mf_dax_kill_procs(
struct xfs_mount *mp,
struct address_space *mapping,
pgoff_t pgoff,
unsigned long nrpages,
int mf_flags,
bool share)
{
int rc, rc2 = 0;
if (share) {
struct xfs_inode *ip = XFS_I(mapping->host);
mutex_lock(&mp->m_reflink_opt_lock);
if (ip->i_reflink_opt_ip) {
rc2 = mf_dax_kill_procs(VFS_I(ip->i_reflink_opt_ip)->i_mapping,
pgoff, nrpages, mf_flags);
} else {
xfs_warn(mp, "this mode should be only used with REFLINK_PRIMARY|REFLINK_SECONDARY @ ino %llu",
ip->i_ino);
}
mutex_unlock(&mp->m_reflink_opt_lock);
}
rc = mf_dax_kill_procs(mapping, pgoff, nrpages, mf_flags);
iput(mapping->host);
return rc ? rc : rc2;
}
static int
xfs_dax_notify_ddev_failure2(
struct dax_device *dax_dev,
struct xfs_mount *mp,
loff_t pos,
size_t size,
int mf_flags)
{
struct address_space *lmapping = NULL;
bool lshare = false;
pfn_t pfn;
pgoff_t pgoff, lpgoff;
unsigned long nrpages;
long length;
int rc, id;
rc = bdev_dax_pgoff(mp->m_ddev_targp->bt_bdev, pos >> SECTOR_SHIFT,
size, &pgoff);
if (rc)
return rc;
id = dax_read_lock();
length = dax_direct_access(dax_dev, pgoff, PHYS_PFN(size),
NULL, &pfn);
if (length < 0) {
rc = length;
goto out;
}
if (PFN_PHYS(length) < size) {
rc = -EINVAL;
goto out;
}
rc = 0;
while (length) {
struct page *page;
struct address_space *mapping;
bool share = false;
page = pfn_t_to_page(pfn);
pfn.val++;
--length;
retry:
rcu_read_lock();
mapping = page ? READ_ONCE(page->mapping) : NULL;
if (mapping) {
share = (unsigned long)mapping & PAGE_MAPPING_DAX_SHARED;
mapping = (void *)((unsigned long)mapping & ~PAGE_MAPPING_DAX_SHARED);
if (!igrab(mapping->host)) {
rcu_read_unlock();
goto retry;
}
/* paired with smp_mb() in dax_page_share_get() to ensure valid index */
smp_mb();
if (!share) {
pgoff = READ_ONCE(page->index);
} else {
WARN_ON(!test_bit(AS_FSDAX_NORMAP, &mapping->flags));
pgoff = READ_ONCE(page->private);
}
}
rcu_read_unlock();
if (lmapping) {
if (mapping != lmapping || share != lshare ||
lpgoff + nrpages != pgoff) {
rc = xfs_mf_dax_kill_procs(mp, lmapping, lpgoff,
nrpages, mf_flags, lshare);
if (rc)
break;
} else {
nrpages++;
continue;
}
}
lmapping = mapping;
lpgoff = pgoff;
lshare = share;
nrpages = 1;
}
if (lmapping) {
int rc2;
rc2 = xfs_mf_dax_kill_procs(mp, lmapping, lpgoff, nrpages, mf_flags, lshare);
if (!rc)
rc = rc2;
}
out:
dax_read_unlock(id);
return rc;
}
static int
xfs_dax_notify_failure(
struct dax_device *dax_dev,
@ -196,11 +319,6 @@ xfs_dax_notify_failure(
return -EFSCORRUPTED;
}
if (!xfs_sb_version_hasrmapbt(&mp->m_sb)) {
xfs_debug(mp, "notify_failure() needs rmapbt enabled!");
return -EOPNOTSUPP;
}
ddev_start = get_start_sect(mp->m_ddev_targp->bt_bdev) << SECTOR_SHIFT;
ddev_end = ddev_start +
i_size_read(mp->m_ddev_targp->bt_bdev->bd_inode) - 1;
@ -221,6 +339,9 @@ xfs_dax_notify_failure(
if (offset + len - 1 > ddev_end)
len = ddev_end - offset + 1;
if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
return xfs_dax_notify_ddev_failure2(dax_dev, mp, offset, len,
mf_flags);
return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len),
mf_flags);
}

View File

@ -607,7 +607,7 @@ __PAGEFLAG(Inited, inited, PF_NO_COMPOUND)
* 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_SHARED ((void *)0x1)
#define PAGE_MAPPING_DAX_SHARED 0x1UL
static __always_inline int PageMappingFlags(struct page *page)

View File

@ -32,6 +32,8 @@ enum mapping_flags {
AS_NO_WRITEBACK_TAGS = 5,
AS_THP_SUPPORT = 6, /* THPs supported */
AS_ZEROPAGE = 7, /* Filled file hole with zero page */
AS_FSDAX_NORMAP = 30,
};
/**