diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index fdaea3422c30..0c58f6aa5609 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -135,23 +135,35 @@ static struct folio *kvm_gmem_get_folio(struct inode *inode, pgoff_t index) /* TODO: Support huge pages. */ struct mempolicy *policy; struct folio *folio; + gfp_t gfp; + int ret; /* * Fast-path: See if folio is already present in mapping to avoid * policy_lookup. */ +repeat: folio = __filemap_get_folio(inode->i_mapping, index, FGP_LOCK | FGP_ACCESSED, 0); if (!IS_ERR(folio)) return folio; + gfp = mapping_gfp_mask(inode->i_mapping); + policy = mpol_shared_policy_lookup(&GMEM_I(inode)->policy, index); - folio = __filemap_get_folio_mpol(inode->i_mapping, index, - FGP_LOCK | FGP_ACCESSED | FGP_CREAT, - mapping_gfp_mask(inode->i_mapping), policy); + folio = filemap_alloc_folio(gfp, 0, policy); mpol_cond_put(policy); + if (!folio) + return ERR_PTR(-ENOMEM); - return folio; + ret = filemap_add_folio(inode->i_mapping, folio, index, gfp); + if (ret) + folio_put(folio); + + if (ret == -EEXIST) + goto repeat; + + return ret ? ERR_PTR(ret) : folio; } static enum kvm_gfn_range_filter kvm_gmem_get_invalidate_filter(struct inode *inode)