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:
parent
f46433162e
commit
51f524bd80
23
fs/dax.c
23
fs/dax.c
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue