From a1ce35f52e876d1082654d04c7ffce4cf42c629f Mon Sep 17 00:00:00 2001 From: almalinux-bot-kernel Date: Wed, 15 Jul 2026 05:07:25 +0000 Subject: [PATCH] Import of kernel-4.18.0-553.144.1.el8_10 --- Makefile.rhelver | 2 +- arch/x86/kvm/mmu/mmu.c | 240 ++++++++++++++++---------------- arch/x86/kvm/mmu/paging_tmpl.h | 62 ++------- arch/x86/kvm/mmu/spte.h | 5 + arch/x86/mm/init_64.c | 4 + arch/x86/mm/pat/set_memory.c | 5 + arch/x86/mm/pgtable.c | 6 + drivers/iommu/amd/iommu_v2.c | 10 ++ drivers/iommu/intel/svm.c | 39 ++++++ drivers/iommu/iommu-sva-lib.c | 137 ++++++++++++++++++ drivers/iommu/iommu-sva-lib.h | 4 + fs/xfs/xfs_reflink.c | 36 +++++ include/linux/iommu.h | 4 + include/linux/kvm_host.h | 7 +- kernel.sbat | 2 +- net/atm/signaling.c | 56 +++++++- net/bridge/br_arp_nd_proxy.c | 8 +- net/bridge/br_fdb.c | 28 ++-- net/bridge/netfilter/ebt_snat.c | 3 + net/sched/sch_ets.c | 2 +- net/xfrm/xfrm_state.c | 12 +- 21 files changed, 469 insertions(+), 203 deletions(-) diff --git a/Makefile.rhelver b/Makefile.rhelver index 540378aaa0..0d8b59780a 100644 --- a/Makefile.rhelver +++ b/Makefile.rhelver @@ -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 diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 578d6f6ee9..728d57666f 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -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); diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 911dbe698d..73684e7362 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -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)) diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 80ab0f5cff..24250a2696 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -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; diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index a6495b7f3f..50b19d229e 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -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); diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c index b73568d137..1f059ad82d 100644 --- a/arch/x86/mm/pat/set_memory.c +++ b/arch/x86/mm/pat/set_memory.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -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; } diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c index 61ae6fdc8b..8be1e87d83 100644 --- a/arch/x86/mm/pgtable.c +++ b/arch/x86/mm/pgtable.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -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; diff --git a/drivers/iommu/amd/iommu_v2.c b/drivers/iommu/amd/iommu_v2.c index d35ebb16d6..e064cd9355 100644 --- a/drivers/iommu/amd/iommu_v2.c +++ b/drivers/iommu/amd/iommu_v2.c @@ -32,6 +32,7 @@ #include #include "amd_iommu.h" +#include "../iommu-sva-lib.h" MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Joerg Roedel "); @@ -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 diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index c22070d5ad..6b11859e94 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -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); diff --git a/drivers/iommu/iommu-sva-lib.c b/drivers/iommu/iommu-sva-lib.c index 1065061438..e8f3506a6a 100644 --- a/drivers/iommu/iommu-sva-lib.c +++ b/drivers/iommu/iommu-sva-lib.c @@ -4,6 +4,14 @@ */ #include #include +#include +#include +#include +#include +#include +#include +#include +#include #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(); +} diff --git a/drivers/iommu/iommu-sva-lib.h b/drivers/iommu/iommu-sva-lib.h index 8909ea1094..07d7f1a5fd 100644 --- a/drivers/iommu/iommu-sva-lib.h +++ b/drivers/iommu/iommu-sva-lib.h @@ -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; diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 1066533e0f..2bc57a8dc9 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -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) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 0b9079a0ee..e7d0756190 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -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; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index c37570a3ac..eaebea6426 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -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; diff --git a/kernel.sbat b/kernel.sbat index 6ab49c6860..f3c8843efa 100644 --- a/kernel.sbat +++ b/kernel.sbat @@ -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 diff --git a/net/atm/signaling.c b/net/atm/signaling.c index fbd0c5e7b2..4db18c406d 100644 --- a/net/atm/signaling.c +++ b/net/atm/signaling.c @@ -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; } diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c index 3db1def443..46ff48c80b 100644 --- a/net/bridge/br_arp_nd_proxy.c +++ b/net/bridge/br_arp_nd_proxy.c @@ -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, diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 08ab9a459e..19bf8ba5ef 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -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, diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index 11cf9e9e92..0bb9317873 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -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; diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c index fbf78afe87..cb3ec844fe 100644 --- a/net/sched/sch_ets.c +++ b/net/sched/sch_ets.c @@ -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); } diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index e57288275a..9c5417d50c 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -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);