Import of kernel-4.18.0-553.144.1.el8_10

This commit is contained in:
almalinux-bot-kernel 2026-07-15 05:07:25 +00:00
parent 68a5b68746
commit a1ce35f52e
21 changed files with 469 additions and 203 deletions

View File

@ -12,7 +12,7 @@ RHEL_MINOR = 10
#
# Use this spot to avoid future merge conflicts.
# Do not trim this comment.
RHEL_RELEASE = 553.141.1
RHEL_RELEASE = 553.144.1
#
# ZSTREAM

View File

@ -182,6 +182,8 @@ struct kmem_cache *mmu_page_header_cache;
static struct percpu_counter kvm_total_used_mmu_pages;
static void mmu_spte_set(u64 *sptep, u64 spte);
static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp,
u64 *spte, struct list_head *invalid_list);
struct kvm_mmu_role_regs {
const unsigned long cr0;
@ -1125,28 +1127,6 @@ static void drop_spte(struct kvm *kvm, u64 *sptep)
rmap_remove(kvm, sptep);
}
static bool __drop_large_spte(struct kvm *kvm, u64 *sptep)
{
if (is_large_pte(*sptep)) {
WARN_ON(sptep_to_sp(sptep)->role.level == PG_LEVEL_4K);
drop_spte(kvm, sptep);
return true;
}
return false;
}
static void drop_large_spte(struct kvm_vcpu *vcpu, u64 *sptep)
{
if (__drop_large_spte(vcpu->kvm, sptep)) {
struct kvm_mmu_page *sp = sptep_to_sp(sptep);
kvm_flush_remote_tlbs_with_address(vcpu->kvm, sp->gfn,
KVM_PAGES_PER_HPAGE(sp->role.level));
}
}
/*
* Write-protect on the specified @sptep, @pt_protect indicates whether
* spte write-protection is caused by protecting shadow page table.
@ -1684,7 +1664,7 @@ static void drop_parent_pte(struct kvm_mmu_page *sp,
mmu_spte_clear_no_track(parent_pte);
}
static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, int direct)
static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, bool direct)
{
struct kvm_mmu_page *sp;
@ -2002,58 +1982,9 @@ static void clear_sp_write_flooding_count(u64 *spte)
__clear_sp_write_flooding_count(sptep_to_sp(spte));
}
static union kvm_mmu_page_role kvm_mmu_child_role(struct kvm_vcpu *vcpu,
gva_t gaddr, unsigned level,
int direct,
unsigned int access)
static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, gfn_t gfn,
union kvm_mmu_page_role role)
{
union kvm_mmu_page_role role;
unsigned quadrant;
role = vcpu->arch.mmu->mmu_role.base;
role.level = level;
role.direct = direct;
role.access = access;
if (role.has_4_byte_gpte) {
quadrant = gaddr >> (PAGE_SHIFT + (PT64_PT_BITS * level));
quadrant &= (1 << ((PT32_PT_BITS - PT64_PT_BITS) * level)) - 1;
role.quadrant = quadrant;
}
return role;
}
/*
* Returns true if @sptep already links to a present, non-large shadow page
* that matches @gfn and @role, i.e. it is safe to keep walking through the
* existing child instead of replacing it. The gfn of a direct shadow page
* only tracks its *first* mapping, so an intervening guest PTE change can
* leave a present SPTE pointing at a child that was allocated for a
* different gfn and/or role; blindly reusing it leads to a stale rmap entry
* (and eventually a use-after-free) once the mismatched child is zapped.
*/
static bool shadow_page_role_matches(u64 *sptep, gfn_t gfn,
union kvm_mmu_page_role role)
{
struct kvm_mmu_page *child;
if (!is_shadow_present_pte(*sptep) || is_large_pte(*sptep))
return false;
child = to_shadow_page(*sptep & PT64_BASE_ADDR_MASK);
return child->gfn == gfn && child->role.word == role.word;
}
static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
gfn_t gfn,
gva_t gaddr,
unsigned level,
int direct,
unsigned int access)
{
bool direct_mmu = vcpu->arch.mmu->direct_map;
union kvm_mmu_page_role role = kvm_mmu_child_role(vcpu, gaddr, level,
direct, access);
struct hlist_head *sp_list;
struct kvm_mmu_page *sp;
int ret;
@ -2077,13 +2008,13 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
* Unsync pages must not be left as is, because the new
* upper-level page will be write-protected.
*/
if (level > PG_LEVEL_4K && sp->unsync)
if (role.level > PG_LEVEL_4K && sp->unsync)
kvm_mmu_prepare_zap_page(vcpu->kvm, sp,
&invalid_list);
continue;
}
if (direct_mmu)
if (sp->role.direct)
goto trace_get_page;
if (sp->unsync) {
@ -2117,14 +2048,14 @@ trace_get_page:
++vcpu->kvm->stat.mmu_cache_miss;
sp = kvm_mmu_alloc_page(vcpu, direct);
sp = kvm_mmu_alloc_page(vcpu, role.direct);
sp->gfn = gfn;
sp->role = role;
hlist_add_head(&sp->hash_link, sp_list);
if (!direct) {
if (!sp->role.direct) {
account_shadowed(vcpu->kvm, sp);
if (level == PG_LEVEL_4K && kvm_vcpu_write_protect_gfn(vcpu, gfn))
if (role.level == PG_LEVEL_4K && kvm_vcpu_write_protect_gfn(vcpu, gfn))
kvm_flush_remote_tlbs_with_address(vcpu->kvm, gfn, 1);
}
trace_kvm_mmu_get_page(sp, true);
@ -2136,6 +2067,60 @@ out:
return sp;
}
static union kvm_mmu_page_role kvm_mmu_child_role(u64 *sptep, bool direct, unsigned int access)
{
struct kvm_mmu_page *parent_sp = sptep_to_sp(sptep);
union kvm_mmu_page_role role;
role = parent_sp->role;
role.level--;
role.access = access;
role.direct = direct;
/*
* If the guest has 4-byte PTEs then that means it's using 32-bit,
* 2-level, non-PAE paging. KVM shadows such guests with PAE paging
* (i.e. 8-byte PTEs). The difference in PTE size means that KVM must
* shadow each guest page table with multiple shadow page tables, which
* requires extra bookkeeping in the role.
*
* Specifically, to shadow the guest's page directory (which covers a
* 4GiB address space), KVM uses 4 PAE page directories, each mapping
* 1GiB of the address space. @role.quadrant encodes which quarter of
* the address space each maps.
*
* To shadow the guest's page tables (which each map a 4MiB region), KVM
* uses 2 PAE page tables, each mapping a 2MiB region. For these,
* @role.quadrant encodes which half of the region they map.
*
* Note, the 4 PAE page directories are pre-allocated and the quadrant
* assigned in mmu_alloc_root(). So only page tables need to be handled
* here.
*/
if (role.has_4_byte_gpte) {
WARN_ON_ONCE(role.level != PG_LEVEL_4K);
role.quadrant = (sptep - parent_sp->spt) % 2;
}
return role;
}
static struct kvm_mmu_page *kvm_mmu_get_child_sp(struct kvm_vcpu *vcpu,
u64 *sptep, gfn_t gfn,
bool direct, unsigned int access)
{
union kvm_mmu_page_role role = kvm_mmu_child_role(sptep, direct, access);
if (is_shadow_present_pte(*sptep) &&
!is_large_pte(*sptep) &&
spte_to_child_sp(*sptep) &&
spte_to_child_sp(*sptep)->gfn == gfn &&
spte_to_child_sp(*sptep)->role.word == role.word)
return ERR_PTR(-EEXIST);
return kvm_mmu_get_page(vcpu, gfn, role);
}
static void shadow_walk_init_using_root(struct kvm_shadow_walk_iterator *iterator,
struct kvm_vcpu *vcpu, hpa_t root,
u64 addr)
@ -2199,13 +2184,25 @@ static void shadow_walk_next(struct kvm_shadow_walk_iterator *iterator)
__shadow_walk_next(iterator, *iterator->sptep);
}
static void link_shadow_page(struct kvm_vcpu *vcpu, u64 *sptep,
struct kvm_mmu_page *sp)
static void __link_shadow_page(struct kvm_vcpu *vcpu,
struct kvm_mmu_memory_cache *cache, u64 *sptep,
struct kvm_mmu_page *sp)
{
u64 spte;
BUILD_BUG_ON(VMX_EPT_WRITABLE_MASK != PT_WRITABLE_MASK);
if (is_shadow_present_pte(*sptep)) {
struct kvm_mmu_page *parent_sp;
LIST_HEAD(invalid_list);
parent_sp = sptep_to_sp(sptep);
WARN_ON_ONCE(parent_sp->role.level == PG_LEVEL_4K);
mmu_page_zap_pte(vcpu->kvm, parent_sp, sptep, &invalid_list);
kvm_mmu_remote_flush_or_zap(vcpu->kvm, &invalid_list, true);
}
spte = make_nonleaf_spte(sp->spt, sp_ad_disabled(sp));
mmu_spte_set(sptep, spte);
@ -2216,6 +2213,12 @@ static void link_shadow_page(struct kvm_vcpu *vcpu, u64 *sptep,
mark_unsync(sptep);
}
static void link_shadow_page(struct kvm_vcpu *vcpu, u64 *sptep,
struct kvm_mmu_page *sp)
{
__link_shadow_page(vcpu, &vcpu->arch.mmu_pte_list_desc_cache, sptep, sp);
}
static void validate_direct_spte(struct kvm_vcpu *vcpu, u64 *sptep,
unsigned direct_access)
{
@ -2977,24 +2980,9 @@ static int __direct_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
if (it.level == fault->goal_level)
break;
drop_large_spte(vcpu, it.sptep);
if (is_shadow_present_pte(*it.sptep)) {
union kvm_mmu_page_role role;
LIST_HEAD(invalid_list);
role = kvm_mmu_child_role(vcpu, it.addr, it.level - 1,
true, ACC_ALL);
if (shadow_page_role_matches(it.sptep, base_gfn, role))
continue;
mmu_page_zap_pte(vcpu->kvm, sptep_to_sp(it.sptep),
it.sptep, &invalid_list);
kvm_mmu_remote_flush_or_zap(vcpu->kvm, &invalid_list,
true);
}
sp = kvm_mmu_get_page(vcpu, base_gfn, it.addr,
it.level - 1, true, ACC_ALL);
sp = kvm_mmu_get_child_sp(vcpu, it.sptep, base_gfn, true, ACC_ALL);
if (sp == ERR_PTR(-EEXIST))
continue;
link_shadow_page(vcpu, it.sptep, sp);
if (fault->is_tdp && fault->huge_page_disallowed &&
@ -3381,12 +3369,19 @@ static int mmu_check_root(struct kvm_vcpu *vcpu, gfn_t root_gfn)
return ret;
}
static hpa_t mmu_alloc_root(struct kvm_vcpu *vcpu, gfn_t gfn, gva_t gva,
u8 level, bool direct)
static hpa_t mmu_alloc_root(struct kvm_vcpu *vcpu, gfn_t gfn, int quadrant,
u8 level)
{
union kvm_mmu_page_role role = vcpu->arch.mmu->mmu_role.base;
struct kvm_mmu_page *sp;
sp = kvm_mmu_get_page(vcpu, gfn, gva, level, direct, ACC_ALL);
role.level = level;
role.quadrant = quadrant;
WARN_ON_ONCE(quadrant && !role.has_4_byte_gpte);
WARN_ON_ONCE(role.direct && role.has_4_byte_gpte);
sp = kvm_mmu_get_page(vcpu, gfn, role);
++sp->root_count;
return __pa(sp->spt);
@ -3409,7 +3404,7 @@ static int mmu_alloc_direct_roots(struct kvm_vcpu *vcpu)
root = kvm_tdp_mmu_get_vcpu_root_hpa(vcpu);
mmu->root.hpa = root;
} else if (shadow_root_level >= PT64_ROOT_4LEVEL) {
root = mmu_alloc_root(vcpu, 0, 0, shadow_root_level, true);
root = mmu_alloc_root(vcpu, 0, 0, shadow_root_level);
mmu->root.hpa = root;
} else if (shadow_root_level == PT32E_ROOT_LEVEL) {
if (WARN_ON_ONCE(!mmu->pae_root)) {
@ -3420,8 +3415,8 @@ static int mmu_alloc_direct_roots(struct kvm_vcpu *vcpu)
for (i = 0; i < 4; ++i) {
WARN_ON_ONCE(IS_VALID_PAE_ROOT(mmu->pae_root[i]));
root = mmu_alloc_root(vcpu, i << (30 - PAGE_SHIFT),
i << 30, PT32_ROOT_LEVEL, true);
root = mmu_alloc_root(vcpu, i << (30 - PAGE_SHIFT), 0,
PT32_ROOT_LEVEL);
mmu->pae_root[i] = root | PT_PRESENT_MASK |
shadow_me_mask;
}
@ -3505,9 +3500,8 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu)
struct kvm_mmu *mmu = vcpu->arch.mmu;
u64 pdptrs[4], pm_mask;
gfn_t root_gfn, root_pgd;
int quadrant, i, r;
hpa_t root;
unsigned i;
int r;
root_pgd = mmu->get_guest_pgd(vcpu);
root_gfn = root_pgd >> PAGE_SHIFT;
@ -3545,7 +3539,7 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu)
*/
if (mmu->root_level >= PT64_ROOT_4LEVEL) {
root = mmu_alloc_root(vcpu, root_gfn, 0,
mmu->shadow_root_level, false);
mmu->shadow_root_level);
mmu->root.hpa = root;
goto set_root_pgd;
}
@ -3590,8 +3584,15 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu)
root_gfn = pdptrs[i] >> PAGE_SHIFT;
}
root = mmu_alloc_root(vcpu, root_gfn, i << 30,
PT32_ROOT_LEVEL, false);
/*
* If shadowing 32-bit non-PAE page tables, each PAE page
* directory maps one quarter of the guest's non-PAE page
* directory. Othwerise each PAE page direct shadows one guest
* PAE page directory so that quadrant should be 0.
*/
quadrant = mmu->mmu_role.base.has_4_byte_gpte ? i : 0;
root = mmu_alloc_root(vcpu, root_gfn, quadrant, PT32_ROOT_LEVEL);
mmu->pae_root[i] = root | pm_mask;
}
@ -6043,20 +6044,13 @@ restart:
pfn = spte_to_pfn(*sptep);
/*
* Direct shadow page can be replaced by a hugepage if the host
* mapping level allows it and the memslot maps all of the host
* hugepage. Note! If the memslot maps only part of the
* hugepage, sp->gfn may be below slot->base_gfn, and querying
* the max mapping level would cause an out-of-bounds lpage_info
* access. So the gfn bounds check *must* be done first.
*
* Indirect shadow pages are created when the guest page tables
* are using 4K pages. Since the host mapping is always
* constrained by the page size in the guest, indirect shadow
* pages are never collapsible.
* We cannot do huge page mapping for indirect shadow pages,
* which are found on the last rmap (level = 1) when not using
* tdp; such shadow pages are synced with the page table in
* the guest, and the guest page table is using 4K page size
* mapping if the indirect sp has level = 1.
*/
if (sp->role.direct && is_gfn_in_memslot(slot, sp->gfn) &&
!kvm_is_reserved_pfn(pfn) &&
if (sp->role.direct && !kvm_is_reserved_pfn(pfn) &&
sp->role.level < kvm_mmu_max_mapping_level(kvm, slot, sp->gfn,
pfn, PG_LEVEL_NUM)) {
pte_list_remove(kvm, rmap_head, sptep);

View File

@ -674,32 +674,13 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault,
gfn_t table_gfn;
clear_sp_write_flooding_count(it.sptep);
drop_large_spte(vcpu, it.sptep);
table_gfn = gw->table_gfn[it.level - 2];
access = gw->pt_access[it.level - 2];
sp = kvm_mmu_get_child_sp(vcpu, it.sptep, table_gfn,
false, access);
if (is_shadow_present_pte(*it.sptep)) {
union kvm_mmu_page_role role;
LIST_HEAD(invalid_list);
role = kvm_mmu_child_role(vcpu, fault->addr,
it.level - 1, false, access);
if (!shadow_page_role_matches(it.sptep, table_gfn,
role)) {
mmu_page_zap_pte(vcpu->kvm,
sptep_to_sp(it.sptep),
it.sptep, &invalid_list);
kvm_mmu_remote_flush_or_zap(vcpu->kvm,
&invalid_list,
true);
}
}
sp = NULL;
if (!is_shadow_present_pte(*it.sptep)) {
sp = kvm_mmu_get_page(vcpu, table_gfn, fault->addr,
it.level-1, false, access);
if (sp != ERR_PTR(-EEXIST)) {
/*
* We must synchronize the pagetable before linking it
* because the guest doesn't need to flush tlb when
@ -728,7 +709,7 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault,
if (FNAME(gpte_changed)(vcpu, gw, it.level - 1))
goto out_gpte_changed;
if (sp)
if (sp != ERR_PTR(-EEXIST))
link_shadow_page(vcpu, it.sptep, sp);
}
@ -752,34 +733,15 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault,
validate_direct_spte(vcpu, it.sptep, direct_access);
drop_large_spte(vcpu, it.sptep);
sp = kvm_mmu_get_child_sp(vcpu, it.sptep, base_gfn,
true, direct_access);
if (sp == ERR_PTR(-EEXIST))
continue;
if (is_shadow_present_pte(*it.sptep)) {
union kvm_mmu_page_role role;
LIST_HEAD(invalid_list);
role = kvm_mmu_child_role(vcpu, fault->addr,
it.level - 1, true,
direct_access);
if (!shadow_page_role_matches(it.sptep, base_gfn,
role)) {
mmu_page_zap_pte(vcpu->kvm,
sptep_to_sp(it.sptep),
it.sptep, &invalid_list);
kvm_mmu_remote_flush_or_zap(vcpu->kvm,
&invalid_list,
true);
}
}
if (!is_shadow_present_pte(*it.sptep)) {
sp = kvm_mmu_get_page(vcpu, base_gfn, fault->addr,
it.level - 1, true, direct_access);
link_shadow_page(vcpu, it.sptep, sp);
if (fault->huge_page_disallowed &&
fault->req_level >= it.level)
account_huge_nx_page(vcpu->kvm, sp);
}
link_shadow_page(vcpu, it.sptep, sp);
if (fault->huge_page_disallowed &&
fault->req_level >= it.level)
account_huge_nx_page(vcpu->kvm, sp);
}
if (WARN_ON_ONCE(it.level != fault->goal_level))

View File

@ -266,6 +266,11 @@ static inline bool is_executable_pte(u64 spte)
return (spte & (shadow_x_mask | shadow_nx_mask)) == shadow_x_mask;
}
static inline struct kvm_mmu_page *spte_to_child_sp(u64 spte)
{
return to_shadow_page(spte & PT64_BASE_ADDR_MASK);
}
static inline kvm_pfn_t spte_to_pfn(u64 pte)
{
return (pte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT;

View File

@ -31,6 +31,7 @@
#include <linux/memremap.h>
#include <linux/nmi.h>
#include <linux/gfp.h>
#include <linux/iommu.h>
#include <linux/kcore.h>
#include <asm/processor.h>
@ -880,6 +881,9 @@ static void __meminit free_pagetable(struct page *page, int order)
unsigned long magic;
unsigned int nr_pages = 1 << order;
/* Flush IOMMU paging structure caches before freeing PT page */
iommu_sva_invalidate_kva_range(PAGE_OFFSET, TLB_FLUSH_ALL);
/* bootmem page has reserved flag */
if (PageReserved(page)) {
__ClearPageReserved(page);

View File

@ -16,6 +16,7 @@
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/cc_platform.h>
#include <linux/iommu.h>
#include <asm/e820/api.h>
#include <asm/processor.h>
@ -1072,6 +1073,8 @@ static bool try_to_free_pte_page(pte_t *pte)
if (!pte_none(pte[i]))
return false;
/* Flush IOMMU paging structure caches before freeing PT page */
iommu_sva_invalidate_kva_range(PAGE_OFFSET, TLB_FLUSH_ALL);
free_page((unsigned long)pte);
return true;
}
@ -1084,6 +1087,8 @@ static bool try_to_free_pmd_page(pmd_t *pmd)
if (!pmd_none(pmd[i]))
return false;
/* Flush IOMMU paging structure caches before freeing PT page */
iommu_sva_invalidate_kva_range(PAGE_OFFSET, TLB_FLUSH_ALL);
free_page((unsigned long)pmd);
return true;
}

View File

@ -2,6 +2,7 @@
#include <linux/mm.h>
#include <linux/gfp.h>
#include <linux/hugetlb.h>
#include <linux/iommu.h>
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
#include <asm/tlb.h>
@ -763,6 +764,9 @@ int pud_free_pmd_page(pud_t *pud, unsigned long addr)
/* INVLPG to clear all paging-structure caches */
flush_tlb_kernel_range(addr, addr + PAGE_SIZE-1);
/* Flush IOMMU paging structure caches before freeing PT pages */
iommu_sva_invalidate_kva_range(PAGE_OFFSET, TLB_FLUSH_ALL);
for (i = 0; i < PTRS_PER_PMD; i++) {
if (!pmd_none(pmd_sv[i])) {
pte = (pte_t *)pmd_page_vaddr(pmd_sv[i]);
@ -799,6 +803,8 @@ int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)
/* INVLPG to clear all paging-structure caches */
flush_tlb_kernel_range(addr, addr + PAGE_SIZE-1);
/* Flush IOMMU paging structure caches before freeing PT page */
iommu_sva_invalidate_kva_range(PAGE_OFFSET, TLB_FLUSH_ALL);
free_page((unsigned long)pte);
return 1;

View File

@ -32,6 +32,7 @@
#include <linux/cc_platform.h>
#include "amd_iommu.h"
#include "../iommu-sva-lib.h"
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Joerg Roedel <jroedel@suse.de>");
@ -342,6 +343,9 @@ static void free_pasid_states(struct device_state *dev_state)
/* Clear the pasid state so that the pasid can be re-used */
clear_pasid_state(dev_state, pasid_state->pasid);
/* Untrack before tearing down the notifier */
iommu_sva_untrack_mm(pasid_state->mm);
/*
* This will call the mn_release function and
* unbind the PASID
@ -669,6 +673,10 @@ int amd_iommu_bind_pasid(struct pci_dev *pdev, u32 pasid,
/* Now we are ready to handle faults */
pasid_state->invalid = false;
ret = iommu_sva_track_mm(mm);
if (ret)
goto out_clear_state;
/*
* Drop the reference to the mm_struct here. We rely on the
* mmu_notifier release call-back to inform us when the mm
@ -726,6 +734,8 @@ void amd_iommu_unbind_pasid(struct pci_dev *pdev, u32 pasid)
/* Clear the pasid state so that the pasid can be re-used */
clear_pasid_state(dev_state, pasid_state->pasid);
iommu_sva_untrack_mm(pasid_state->mm);
/*
* Call mmu_notifier_unregister to drop our reference
* to pasid_state->mm

View File

@ -227,6 +227,30 @@ static void intel_flush_svm_range(struct intel_svm *svm, unsigned long address,
rcu_read_unlock();
}
static void intel_flush_svm_all(struct intel_svm *svm)
{
struct device_domain_info *info;
struct intel_svm_dev *sdev;
rcu_read_lock();
list_for_each_entry_rcu(sdev, &svm->devs, list) {
info = get_domain_info(sdev->dev);
qi_flush_piotlb(sdev->iommu, sdev->did, svm->pasid,
0, -1UL, 0);
if (info->ats_enabled) {
qi_flush_dev_iotlb_pasid(sdev->iommu, sdev->sid,
info->pfsid, svm->pasid,
sdev->qdep, 0,
64 - VTD_PAGE_SHIFT);
quirk_extra_dev_tlb_flush(info, 0,
64 - VTD_PAGE_SHIFT,
svm->pasid, sdev->qdep);
}
}
rcu_read_unlock();
}
/* Pages have been freed at this point */
static void intel_invalidate_range(struct mmu_notifier *mn,
struct mm_struct *mm,
@ -234,6 +258,11 @@ static void intel_invalidate_range(struct mmu_notifier *mn,
{
struct intel_svm *svm = container_of(mn, struct intel_svm, notifier);
if (start == 0 && end == -1UL) {
intel_flush_svm_all(svm);
return;
}
intel_flush_svm_range(svm, start,
(end - start + PAGE_SIZE - 1) >> VTD_PAGE_SHIFT, 0);
}
@ -348,6 +377,14 @@ static struct iommu_sva *intel_svm_bind_mm(struct intel_iommu *iommu,
kfree(svm);
return ERR_PTR(ret);
}
ret = iommu_sva_track_mm(mm);
if (ret) {
pasid_private_remove(mm->pasid);
mmu_notifier_unregister(&svm->notifier, mm);
kfree(svm);
return ERR_PTR(ret);
}
}
/* Find the matching device in svm list */
@ -396,6 +433,7 @@ free_sdev:
kfree(sdev);
free_svm:
if (list_empty(&svm->devs)) {
iommu_sva_untrack_mm(mm);
mmu_notifier_unregister(&svm->notifier, mm);
pasid_private_remove(mm->pasid);
kfree(svm);
@ -439,6 +477,7 @@ static int intel_svm_unbind_mm(struct device *dev, u32 pasid)
kfree_rcu(sdev, rcu);
if (list_empty(&svm->devs)) {
iommu_sva_untrack_mm(mm);
if (svm->notifier.ops)
mmu_notifier_unregister(&svm->notifier, mm);
pasid_private_remove(svm->pasid);

View File

@ -4,6 +4,14 @@
*/
#include <linux/mutex.h>
#include <linux/sched/mm.h>
#include <linux/spinlock.h>
#include <linux/atomic.h>
#include <linux/list.h>
#include <linux/rcupdate.h>
#include <linux/rculist.h>
#include <linux/slab.h>
#include <linux/mmu_notifier.h>
#include <linux/iommu.h>
#include "iommu-sva-lib.h"
@ -69,3 +77,132 @@ struct mm_struct *iommu_sva_find(ioasid_t pasid)
return ioasid_find(&iommu_sva_pasid, pasid, __mmget_not_zero);
}
EXPORT_SYMBOL_GPL(iommu_sva_find);
/*
* Track mm_structs with active SVA bindings so we can flush IOMMU
* cached translations when kernel page table pages are freed.
*/
struct sva_mm {
struct mm_struct *mm;
struct list_head list;
struct rcu_head rcu;
int refcount;
};
static DEFINE_SPINLOCK(sva_mm_lock);
static LIST_HEAD(sva_mm_list);
static atomic_t sva_mm_count = ATOMIC_INIT(0);
/**
* iommu_sva_track_mm - Start tracking an mm for kernel PT change notification
* @mm: the mm_struct to track
*
* Called by IOMMU drivers when SVA is bound for this mm. If the mm is
* already tracked, increments the refcount. Otherwise allocates a new
* tracking entry. The mm's mmu_notifier chain must already include the
* IOMMU driver's invalidate_range callback (registered during SVA bind).
*
* Returns 0 on success, -ENOMEM on allocation failure.
*/
int iommu_sva_track_mm(struct mm_struct *mm)
{
struct sva_mm *smm;
unsigned long flags;
spin_lock_irqsave(&sva_mm_lock, flags);
list_for_each_entry(smm, &sva_mm_list, list) {
if (smm->mm == mm) {
smm->refcount++;
spin_unlock_irqrestore(&sva_mm_lock, flags);
return 0;
}
}
spin_unlock_irqrestore(&sva_mm_lock, flags);
smm = kzalloc(sizeof(*smm), GFP_KERNEL);
if (WARN_ON(!smm))
return -ENOMEM;
smm->mm = mm;
smm->refcount = 1;
spin_lock_irqsave(&sva_mm_lock, flags);
{
struct sva_mm *existing;
list_for_each_entry(existing, &sva_mm_list, list) {
if (existing->mm == mm) {
existing->refcount++;
spin_unlock_irqrestore(&sva_mm_lock, flags);
kfree(smm);
return 0;
}
}
}
list_add_rcu(&smm->list, &sva_mm_list);
atomic_inc(&sva_mm_count);
spin_unlock_irqrestore(&sva_mm_lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(iommu_sva_track_mm);
/**
* iommu_sva_untrack_mm - Stop tracking an mm for kernel PT change notification
* @mm: the mm_struct to untrack
*
* Called by IOMMU drivers when SVA is unbound for this mm. Decrements
* the refcount and removes the tracking entry when it reaches zero.
*/
void iommu_sva_untrack_mm(struct mm_struct *mm)
{
struct sva_mm *smm;
unsigned long flags;
spin_lock_irqsave(&sva_mm_lock, flags);
list_for_each_entry(smm, &sva_mm_list, list) {
if (smm->mm == mm) {
if (--smm->refcount == 0) {
list_del_rcu(&smm->list);
atomic_dec(&sva_mm_count);
spin_unlock_irqrestore(&sva_mm_lock, flags);
kfree_rcu(smm, rcu);
return;
}
spin_unlock_irqrestore(&sva_mm_lock, flags);
return;
}
}
spin_unlock_irqrestore(&sva_mm_lock, flags);
WARN(1, "iommu_sva_untrack_mm: mm %px not found\n", mm);
}
EXPORT_SYMBOL_GPL(iommu_sva_untrack_mm);
/**
* iommu_sva_invalidate_kva_range - Flush IOMMU caches before kernel PT free
* @start: Start of kernel virtual address range
* @end: End of kernel virtual address range
*
* Called from x86 mm code before freeing kernel page table pages.
* Iterates all tracked SVA-bound mm_structs and calls
* mmu_notifier_invalidate_range() for each, triggering IOTLB flushes
* via the drivers' invalidate_range callbacks.
*
* Fast path: atomic_read() returns 0 when no SVA is active.
*/
void iommu_sva_invalidate_kva_range(unsigned long start, unsigned long end)
{
struct sva_mm *smm;
if (!atomic_read(&sva_mm_count))
return;
rcu_read_lock();
list_for_each_entry_rcu(smm, &sva_mm_list, list) {
if (mmget_not_zero(smm->mm)) {
mmu_notifier_invalidate_range(smm->mm, start, end);
mmput_async(smm->mm);
}
}
rcu_read_unlock();
}

View File

@ -11,6 +11,10 @@
int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max);
struct mm_struct *iommu_sva_find(ioasid_t pasid);
/* SVA mm tracking for kernel page table change notification */
int iommu_sva_track_mm(struct mm_struct *mm);
void iommu_sva_untrack_mm(struct mm_struct *mm);
/* I/O Page fault */
struct device;
struct iommu_fault;

View File

@ -385,6 +385,7 @@ xfs_reflink_fill_cow_hole(
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
xfs_filblks_t resaligned;
unsigned int seq_before = READ_ONCE(ip->i_df.if_seq);
xfs_extlen_t resblks;
int nimaps;
int error;
@ -404,6 +405,22 @@ xfs_reflink_fill_cow_hole(
*lockmode = XFS_ILOCK_EXCL;
/*
* The data fork mapping may have changed while we dropped the ILOCK
* (a racing O_DIRECT writer under IOLOCK_SHARED can complete a full
* CoW cycle including xfs_reflink_end_cow(), which remaps this offset
* and drops the refcount of the old shared block). Re-read it so the
* shared-status recheck below and the caller's in-place iomap both
* operate on the current mapping rather than a stale physical block.
*/
if (seq_before != READ_ONCE(ip->i_df.if_seq)) {
nimaps = 1;
error = xfs_bmapi_read(ip, imap->br_startoff,
imap->br_blockcount, imap, &nimaps, 0);
if (error)
goto out_trans_cancel;
}
error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found);
if (error || !*shared)
goto out_trans_cancel;
@ -452,6 +469,8 @@ xfs_reflink_fill_delalloc(
bool found;
do {
unsigned int seq_before = READ_ONCE(ip->i_df.if_seq);
xfs_iunlock(ip, *lockmode);
*lockmode = 0;
@ -462,6 +481,23 @@ xfs_reflink_fill_delalloc(
*lockmode = XFS_ILOCK_EXCL;
/*
* The data fork mapping may have changed while we dropped the
* ILOCK (a racing O_DIRECT writer under IOLOCK_SHARED can
* complete a full CoW cycle including xfs_reflink_end_cow(),
* which remaps this offset and drops the refcount of the old
* shared block). Re-read it so the shared-status recheck
* below and the caller's in-place iomap both operate on the
* current mapping rather than a stale physical block.
*/
if (seq_before != READ_ONCE(ip->i_df.if_seq)) {
nimaps = 1;
error = xfs_bmapi_read(ip, imap->br_startoff,
imap->br_blockcount, imap, &nimaps, 0);
if (error)
goto out_trans_cancel;
}
error = xfs_find_trim_cow_extent(ip, imap, cmap, shared,
&found);
if (error || !*shared)

View File

@ -743,6 +743,7 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev,
struct mm_struct *mm);
void iommu_sva_unbind_device(struct iommu_sva *handle);
u32 iommu_sva_get_pasid(struct iommu_sva *handle);
void iommu_sva_invalidate_kva_range(unsigned long start, unsigned long end);
#else /* CONFIG_IOMMU_API */
@ -1118,6 +1119,9 @@ static inline u32 iommu_sva_get_pasid(struct iommu_sva *handle)
return IOMMU_PASID_INVALID;
}
static inline void iommu_sva_invalidate_kva_range(unsigned long start,
unsigned long end) {}
static inline struct iommu_fwspec *dev_iommu_fwspec_get(struct device *dev)
{
return NULL;

View File

@ -1378,11 +1378,6 @@ static inline struct rcuwait *kvm_arch_vcpu_get_wait(struct kvm_vcpu *vcpu)
#endif
}
static inline bool is_gfn_in_memslot(const struct kvm_memory_slot *slot, gfn_t gfn)
{
return gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages;
}
/*
* Wake a vCPU if necessary, but don't do any stats/metadata updates. Returns
* true if the vCPU was blocking and was awakened, false otherwise.
@ -1469,7 +1464,7 @@ try_get_memslot(struct kvm_memory_slot *slot, gfn_t gfn)
if (!slot)
return NULL;
if (is_gfn_in_memslot(slot, gfn))
if (gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages)
return slot;
else
return NULL;

View File

@ -1,2 +1,2 @@
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
kernel.centos,1,Red Hat,kernel-core,4.18.0-553.141.2.el8.x86_64,mailto:secalert@redhat.com
kernel.centos,1,Red Hat,kernel-core,4.18.0-553.144.1.el8.x86_64,mailto:secalert@redhat.com

View File

@ -22,6 +22,36 @@
struct atm_vcc *sigd = NULL;
/*
* find_get_vcc - validate and get a reference to a vcc pointer
* @vcc: the vcc pointer to validate
*
* This function validates that @vcc points to a registered VCC in vcc_hash.
* If found, it increments the socket reference count and returns the vcc.
* The caller must call sock_put(sk_atm(vcc)) when done.
*
* Returns the vcc pointer if valid, NULL otherwise.
*/
static struct atm_vcc *find_get_vcc(struct atm_vcc *vcc)
{
int i;
read_lock(&vcc_sklist_lock);
for (i = 0; i < VCC_HTABLE_SIZE; i++) {
struct sock *s;
sk_for_each(s, &vcc_hash[i]) {
if (atm_sk(s) == vcc) {
sock_hold(s);
read_unlock(&vcc_sklist_lock);
return vcc;
}
}
}
read_unlock(&vcc_sklist_lock);
return NULL;
}
static void sigd_put_skb(struct sk_buff *skb)
{
if (!sigd) {
@ -69,7 +99,14 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)
msg = (struct atmsvc_msg *) skb->data;
WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc));
vcc = *(struct atm_vcc **) &msg->vcc;
vcc = find_get_vcc(*(struct atm_vcc **)&msg->vcc);
if (!vcc) {
pr_debug("invalid vcc pointer in msg\n");
dev_kfree_skb(skb);
return -EINVAL;
}
pr_debug("%d (0x%lx)\n", (int)msg->type, (unsigned long)vcc);
sk = sk_atm(vcc);
@ -100,7 +137,16 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)
clear_bit(ATM_VF_WAITING, &vcc->flags);
break;
case as_indicate:
vcc = *(struct atm_vcc **)&msg->listen_vcc;
/* Release the reference from msg->vcc, we'll use msg->listen_vcc instead */
sock_put(sk);
vcc = find_get_vcc(*(struct atm_vcc **)&msg->listen_vcc);
if (!vcc) {
pr_debug("invalid listen_vcc pointer in msg\n");
dev_kfree_skb(skb);
return -EINVAL;
}
sk = sk_atm(vcc);
pr_debug("as_indicate!!!\n");
lock_sock(sk);
@ -115,6 +161,8 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)
sk->sk_state_change(sk);
as_indicate_complete:
release_sock(sk);
/* Paired with find_get_vcc(msg->listen_vcc) above */
sock_put(sk);
return 0;
case as_close:
set_bit(ATM_VF_RELEASED, &vcc->flags);
@ -131,11 +179,15 @@ as_indicate_complete:
break;
default:
pr_alert("bad message type %d\n", (int)msg->type);
/* Paired with find_get_vcc(msg->vcc) above */
sock_put(sk);
return -EINVAL;
}
sk->sk_state_change(sk);
out:
dev_kfree_skb(skb);
/* Paired with find_get_vcc(msg->vcc) above */
sock_put(sk);
return 0;
}

View File

@ -199,11 +199,12 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
f = br_fdb_find_rcu(br, n->ha, vid);
if (f) {
const struct net_bridge_port *dst = READ_ONCE(f->dst);
bool replied = false;
if ((p && (p->flags & BR_PROXYARP)) ||
(f->dst && (f->dst->flags & (BR_PROXYARP_WIFI |
BR_NEIGH_SUPPRESS)))) {
(dst && (dst->flags & (BR_PROXYARP_WIFI |
BR_NEIGH_SUPPRESS)))) {
if (!vid)
br_arp_send(br, p, skb->dev, sip, tip,
sha, n->ha, sha, 0, 0);
@ -459,9 +460,10 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
f = br_fdb_find_rcu(br, n->ha, vid);
if (f) {
const struct net_bridge_port *dst = READ_ONCE(f->dst);
bool replied = false;
if (f->dst && (f->dst->flags & BR_NEIGH_SUPPRESS)) {
if (dst && (dst->flags & BR_NEIGH_SUPPRESS)) {
if (vid != 0)
br_nd_send(br, p, skb, n,
skb->vlan_proto,

View File

@ -240,6 +240,7 @@ struct net_device *br_fdb_find_port(const struct net_device *br_dev,
const unsigned char *addr,
__u16 vid)
{
const struct net_bridge_port *dst;
struct net_bridge_fdb_entry *f;
struct net_device *dev = NULL;
struct net_bridge *br;
@ -252,8 +253,11 @@ struct net_device *br_fdb_find_port(const struct net_device *br_dev,
br = netdev_priv(br_dev);
rcu_read_lock();
f = br_fdb_find_rcu(br, addr, vid);
if (f && f->dst)
dev = f->dst->dev;
if (f) {
dst = READ_ONCE(f->dst);
if (dst)
dev = dst->dev;
}
rcu_read_unlock();
return dev;
@ -343,7 +347,7 @@ static void fdb_delete_local(struct net_bridge *br,
vg = nbp_vlan_group(op);
if (op != p && ether_addr_equal(op->dev->dev_addr, addr) &&
(!vid || br_vlan_find(vg, vid))) {
f->dst = op;
WRITE_ONCE(f->dst, op);
clear_bit(BR_FDB_ADDED_BY_USER, &f->flags);
return;
}
@ -354,7 +358,7 @@ static void fdb_delete_local(struct net_bridge *br,
/* Maybe bridge device has same hw addr? */
if (p && ether_addr_equal(br->dev->dev_addr, addr) &&
(!vid || (v && br_vlan_should_use(v)))) {
f->dst = NULL;
WRITE_ONCE(f->dst, NULL);
clear_bit(BR_FDB_ADDED_BY_USER, &f->flags);
return;
}
@ -641,6 +645,7 @@ int br_fdb_test_addr(struct net_device *dev, unsigned char *addr)
int br_fdb_fillbuf(struct net_bridge *br, void *buf,
unsigned long maxnum, unsigned long skip)
{
const struct net_bridge_port *dst;
struct net_bridge_fdb_entry *f;
struct __fdb_entry *fe = buf;
int num = 0;
@ -656,7 +661,8 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
continue;
/* ignore pseudo entry for local MAC address */
if (!f->dst)
dst = READ_ONCE(f->dst);
if (!dst)
continue;
if (skip) {
@ -668,8 +674,8 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
memcpy(fe->mac_addr, f->key.addr.addr, ETH_ALEN);
/* due to ABI compat need to split into hi/lo */
fe->port_no = f->dst->port_no;
fe->port_hi = f->dst->port_no >> 8;
fe->port_no = dst->port_no;
fe->port_hi = dst->port_no >> 8;
fe->is_local = test_bit(BR_FDB_LOCAL, &f->flags);
if (!test_bit(BR_FDB_STATIC, &f->flags))
@ -782,9 +788,11 @@ int br_fdb_dump(struct sk_buff *skb,
rcu_read_lock();
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
const struct net_bridge_port *dst = READ_ONCE(f->dst);
if (*idx < cb->args[2])
goto skip;
if (filter_dev && (!f->dst || f->dst->dev != filter_dev)) {
if (filter_dev && (!dst || dst->dev != filter_dev)) {
if (filter_dev != dev)
goto skip;
/* !f->dst is a special case for bridge
@ -792,10 +800,10 @@ int br_fdb_dump(struct sk_buff *skb,
* Therefore need a little more filtering
* we only want to dump the !f->dst case
*/
if (f->dst)
if (dst)
goto skip;
}
if (!filter_dev && f->dst)
if (!filter_dev && dst)
goto skip;
err = fdb_fill_info(skb, br, f,

View File

@ -30,6 +30,9 @@ ebt_snat_tg(struct sk_buff *skb, const struct xt_action_param *par)
const struct arphdr *ap;
struct arphdr _ah;
if (skb_ensure_writable(skb, sizeof(_ah) + ETH_ALEN))
return EBT_DROP;
ap = skb_header_pointer(skb, 0, sizeof(_ah), &_ah);
if (ap == NULL)
return EBT_DROP;

View File

@ -650,7 +650,7 @@ static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt,
sch_tree_lock(sch);
for (i = nbands; i < oldbands; i++) {
if (i >= q->nstrict && q->classes[i].qdisc->q.qlen)
if (cl_is_active(&q->classes[i]))
list_del_init(&q->classes[i].alist);
qdisc_purge_queue(q->classes[i].qdisc);
}

View File

@ -610,12 +610,12 @@ int __xfrm_state_delete(struct xfrm_state *x)
x->km.state = XFRM_STATE_DEAD;
spin_lock(&net->xfrm.xfrm_state_lock);
list_del(&x->km.all);
hlist_del_rcu(&x->bydst);
hlist_del_rcu(&x->bysrc);
if (x->km.seq)
hlist_del_rcu(&x->byseq);
if (x->id.spi)
hlist_del_rcu(&x->byspi);
hlist_del_init_rcu(&x->bydst);
hlist_del_init_rcu(&x->bysrc);
if (!hlist_unhashed(&x->byseq))
hlist_del_init_rcu(&x->byseq);
if (!hlist_unhashed(&x->byspi))
hlist_del_init_rcu(&x->byspi);
net->xfrm.state_num--;
spin_unlock(&net->xfrm.xfrm_state_lock);