Import of kernel-4.18.0-553.144.1.el8_10
This commit is contained in:
parent
68a5b68746
commit
a1ce35f52e
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user