mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Cross-merge networking fixes after downstream PR (net-7.3-rc6).
Conflicts:
net/mac80211/tx.c
net/mac80211/ieee80211_i.h
8effab902fa34 ("wifi: mac80211: prevent AP VLAN tx from other interfaces")
78b843974f ("wifi: mac80211: report multicast transmission from lookup_ra_sta()")
https://lore.kernel.org/arJmcuhpJ69wAvYK@sirena.co.uk
drivers/net/ethernet/realtek/r8169_main.c
3cdeaef175 ("r8169: disable EEE on RTL8168h/8111h")
8a3c76523e ("r8169: add support for phylink")
https://lore.kernel.org/ar5vAxEqS8tO8aVT@sirena.org.uk
Adjacent changes:
rust/kernel/net/netlink.rs
5e59169237 ("rust: net: netlink: Migrate to zerocopy's IntoBytes")
0923198be4 ("rust: net: netlink: validate attribute length before casting to `c_int`")
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
f49defea76
2
.mailmap
2
.mailmap
|
|
@ -288,6 +288,8 @@ Ed L. Cashin <ecashin@coraid.com>
|
|||
Elliot Berman <quic_eberman@quicinc.com> <eberman@codeaurora.org>
|
||||
Enric Balletbo i Serra <eballetbo@kernel.org> <enric.balletbo@collabora.com>
|
||||
Enric Balletbo i Serra <eballetbo@kernel.org> <eballetbo@iseebcn.com>
|
||||
Eric Dumazet <edumazet@kernel.org> <edumazet@google.com>
|
||||
Eric Dumazet <edumazet@kernel.org> <eric.dumazet@gmail.com>
|
||||
Erik Kaneda <erik.kaneda@intel.com> <erik.schmauss@intel.com>
|
||||
Ethan Carter Edwards <ethan@ethancedwards.com> Ethan Edwards <ethancarteredwards@gmail.com>
|
||||
Eugen Hristev <ehristev@kernel.org> <eugen.hristev@microchip.com>
|
||||
|
|
|
|||
|
|
@ -3494,7 +3494,7 @@ Kernel parameters
|
|||
|
||||
* SATA link speed limit: 1.5Gbps or 3.0Gbps.
|
||||
|
||||
* Transfer mode: pio[0-7], mwdma[0-4] and udma[0-7].
|
||||
* Transfer mode: pio[0-6], mwdma[0-4] and udma[0-7].
|
||||
udma[/][16,25,33,44,66,100,133] notation is also
|
||||
allowed.
|
||||
|
||||
|
|
@ -3512,7 +3512,7 @@ Kernel parameters
|
|||
|
||||
* [no]ncqtrim: Enable or disable queued DSM TRIM.
|
||||
|
||||
* [no]ncqati: Enable or disable NCQ trim on ATI chipset.
|
||||
* [no]ncqati: Enable or disable NCQ on ATI chipsets.
|
||||
|
||||
* [no]trim: Enable or disable (unqueued) TRIM.
|
||||
|
||||
|
|
@ -3543,11 +3543,9 @@ Kernel parameters
|
|||
|
||||
* max_sec_128: Set transfer size limit to 128 sectors.
|
||||
|
||||
* max_sec_1024: Set or clear transfer size limit to
|
||||
1024 sectors.
|
||||
* max_sec_1024: Set transfer size limit to 1024 sectors.
|
||||
|
||||
* max_sec_lba48: Set or clear transfer size limit to
|
||||
65535 sectors.
|
||||
* max_sec_lba48: Set transfer size limit to 65535 sectors.
|
||||
|
||||
* external: Mark port as external (hotplug-capable).
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ Entries specific to zPCI functions and entries that hold zPCI information.
|
|||
|
||||
A physical function that currently supports a virtual function cannot be
|
||||
powered off until all virtual functions are removed with:
|
||||
echo 0 > /sys/bus/pci/devices/DDDD:BB:dd.f/sriov_numvf
|
||||
echo 0 > /sys/bus/pci/devices/DDDD:BB:dd.f/sriov_numvfs
|
||||
|
||||
* /sys/bus/pci/devices/DDDD:BB:dd.f/:
|
||||
|
||||
|
|
|
|||
80
MAINTAINERS
80
MAINTAINERS
|
|
@ -5143,6 +5143,7 @@ L: netdev@vger.kernel.org
|
|||
L: openwrt-devel@lists.openwrt.org (subscribers-only)
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/net/dsa/brcm,b53.yaml
|
||||
F: Documentation/networking/dsa/b53.rst
|
||||
F: drivers/net/dsa/b53/*
|
||||
F: drivers/net/dsa/bcm_sf2*
|
||||
F: include/linux/dsa/brcm.h
|
||||
|
|
@ -14907,7 +14908,7 @@ M: Damien Le Moal <dlemoal@kernel.org>
|
|||
M: Niklas Cassel <cassel@kernel.org>
|
||||
L: linux-ide@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/libata/linux.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/libata/linux.git for-next
|
||||
F: Documentation/ABI/testing/sysfs-ata
|
||||
F: Documentation/devicetree/bindings/ata/
|
||||
F: drivers/ata/
|
||||
|
|
@ -17210,31 +17211,6 @@ F: include/trace/events/ksm.h
|
|||
F: mm/ksm.c
|
||||
F: mm/mm_slot.h
|
||||
|
||||
MEMORY MANAGEMENT - MEMORY POLICY AND MIGRATION
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
R: Zi Yan <ziy@nvidia.com>
|
||||
R: Matthew Brost <matthew.brost@intel.com>
|
||||
R: Joshua Hahn <joshua.hahnjy@gmail.com>
|
||||
R: Rakie Kim <rakie.kim@sk.com>
|
||||
R: Byungchul Park <byungchul@sk.com>
|
||||
R: Gregory Price <gourry@gourry.net>
|
||||
R: Ying Huang <ying.huang@linux.alibaba.com>
|
||||
R: Alistair Popple <apopple@nvidia.com>
|
||||
L: linux-mm@kvack.org
|
||||
S: Maintained
|
||||
W: http://www.linux-mm.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
|
||||
F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy
|
||||
F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy-weighted-interleave
|
||||
F: include/linux/mempolicy.h
|
||||
F: include/uapi/linux/mempolicy.h
|
||||
F: include/linux/migrate.h
|
||||
F: include/linux/migrate_mode.h
|
||||
F: mm/mempolicy.c
|
||||
F: mm/migrate.c
|
||||
F: mm/migrate_device.c
|
||||
|
||||
MEMORY MANAGEMENT - MGLRU (MULTI-GEN LRU)
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
R: Kairui Song <kasong@tencent.com>
|
||||
|
|
@ -17257,6 +17233,25 @@ F: include/linux/mmzone.h
|
|||
F: mm/vmscan.c
|
||||
F: mm/workingset.c
|
||||
|
||||
MEMORY MANAGEMENT - MIGRATION
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
R: Zi Yan <ziy@nvidia.com>
|
||||
R: Matthew Brost <matthew.brost@intel.com>
|
||||
R: Joshua Hahn <joshua.hahnjy@gmail.com>
|
||||
R: Byungchul Park <byungchul@sk.com>
|
||||
R: Gregory Price <gourry@gourry.net>
|
||||
R: Ying Huang <ying.huang@linux.alibaba.com>
|
||||
R: Alistair Popple <apopple@nvidia.com>
|
||||
L: linux-mm@kvack.org
|
||||
S: Maintained
|
||||
W: http://www.linux-mm.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
|
||||
F: include/linux/migrate.h
|
||||
F: include/linux/migrate_mode.h
|
||||
F: mm/migrate.c
|
||||
F: mm/migrate_device.c
|
||||
|
||||
MEMORY MANAGEMENT - MISC
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
|
|
@ -17272,7 +17267,6 @@ W: http://www.linux-mm.org
|
|||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
|
||||
F: Documentation/ABI/testing/sysfs-kernel-mm
|
||||
F: Documentation/ABI/testing/sysfs-kernel-mm-cma
|
||||
F: Documentation/ABI/testing/sysfs-kernel-mm-memory-tiers
|
||||
F: Documentation/ABI/testing/sysfs-kernel-mm-numa
|
||||
F: Documentation/admin-guide/mm/
|
||||
F: Documentation/mm/
|
||||
|
|
@ -17280,7 +17274,6 @@ F: drivers/char/mem.c
|
|||
F: include/linux/cma.h
|
||||
F: include/linux/dmapool.h
|
||||
F: include/linux/ioremap.h
|
||||
F: include/linux/memory-tiers.h
|
||||
F: include/linux/page_idle.h
|
||||
F: mm/backing-dev.c
|
||||
F: mm/cma.c
|
||||
|
|
@ -17292,7 +17285,6 @@ F: mm/early_ioremap.c
|
|||
F: mm/fadvise.c
|
||||
F: mm/ioremap.c
|
||||
F: mm/mapping_dirty_helpers.c
|
||||
F: mm/memory-tiers.c
|
||||
F: mm/page_idle.c
|
||||
F: mm/pgalloc-track.h
|
||||
F: mm/process_vm_access.c
|
||||
|
|
@ -17309,6 +17301,27 @@ F: mm/numa.c
|
|||
F: mm/numa_emulation.c
|
||||
F: mm/numa_memblks.c
|
||||
|
||||
MEMORY MANAGEMENT - NUMA PLACEMENT
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
M: Gregory Price <gourry@gourry.net>
|
||||
R: Zi Yan <ziy@nvidia.com>
|
||||
R: Joshua Hahn <joshua.hahnjy@gmail.com>
|
||||
R: Rakie Kim <rakie.kim@sk.com>
|
||||
R: Ying Huang <ying.huang@linux.alibaba.com>
|
||||
L: linux-mm@kvack.org
|
||||
S: Maintained
|
||||
W: http://www.linux-mm.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
|
||||
F: Documentation/ABI/testing/sysfs-kernel-mm-memory-tiers
|
||||
F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy
|
||||
F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy-weighted-interleave
|
||||
F: include/linux/memory-tiers.h
|
||||
F: include/linux/mempolicy.h
|
||||
F: include/uapi/linux/mempolicy.h
|
||||
F: mm/memory-tiers.c
|
||||
F: mm/mempolicy.c
|
||||
|
||||
MEMORY MANAGEMENT - OOM KILLER
|
||||
M: Michal Hocko <mhocko@suse.com>
|
||||
R: David Rientjes <rientjes@google.com>
|
||||
|
|
@ -18897,7 +18910,7 @@ F: net/core/drop_monitor.c
|
|||
NETWORKING DRIVERS
|
||||
M: Andrew Lunn <andrew+netdev@lunn.ch>
|
||||
M: "David S. Miller" <davem@davemloft.net>
|
||||
M: Eric Dumazet <edumazet@google.com>
|
||||
M: Eric Dumazet <edumazet@kernel.org>
|
||||
M: Jakub Kicinski <kuba@kernel.org>
|
||||
M: Paolo Abeni <pabeni@redhat.com>
|
||||
L: netdev@vger.kernel.org
|
||||
|
|
@ -19014,7 +19027,7 @@ K: struct\s+phy_port|phy_port_
|
|||
|
||||
NETWORKING [GENERAL]
|
||||
M: "David S. Miller" <davem@davemloft.net>
|
||||
M: Eric Dumazet <edumazet@google.com>
|
||||
M: Eric Dumazet <edumazet@kernel.org>
|
||||
M: Jakub Kicinski <kuba@kernel.org>
|
||||
M: Paolo Abeni <pabeni@redhat.com>
|
||||
R: Simon Horman <horms@kernel.org>
|
||||
|
|
@ -19240,7 +19253,7 @@ F: net/ipv6/seg6*
|
|||
F: tools/testing/selftests/net/srv6*
|
||||
|
||||
NETWORKING [TCP]
|
||||
M: Eric Dumazet <edumazet@google.com>
|
||||
M: Eric Dumazet <edumazet@kernel.org>
|
||||
M: Neal Cardwell <ncardwell@google.com>
|
||||
R: Kuniyuki Iwashima <kuniyu@google.com>
|
||||
L: netdev@vger.kernel.org
|
||||
|
|
@ -19273,7 +19286,7 @@ F: net/tls/
|
|||
F: tools/testing/selftests/net/tls.c
|
||||
|
||||
NETWORKING [SOCKETS]
|
||||
M: Eric Dumazet <edumazet@google.com>
|
||||
M: Eric Dumazet <edumazet@kernel.org>
|
||||
M: Kuniyuki Iwashima <kuniyu@google.com>
|
||||
M: Paolo Abeni <pabeni@redhat.com>
|
||||
M: Willem de Bruijn <willemb@google.com>
|
||||
|
|
@ -20570,6 +20583,7 @@ ORACLE CLUSTER FILESYSTEM 2 (OCFS2)
|
|||
M: Mark Fasheh <mark@fasheh.com>
|
||||
M: Joel Becker <jlbec@evilplan.org>
|
||||
M: Joseph Qi <joseph.qi@linux.alibaba.com>
|
||||
R: Heming Zhao <heming.zhao@suse.com>
|
||||
L: ocfs2-devel@lists.linux.dev
|
||||
S: Supported
|
||||
W: http://ocfs2.wiki.kernel.org
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -2,7 +2,7 @@
|
|||
VERSION = 7
|
||||
PATCHLEVEL = 3
|
||||
SUBLEVEL = 0
|
||||
EXTRAVERSION = -rc4
|
||||
EXTRAVERSION = -rc5
|
||||
NAME = Baby Opossum Posse
|
||||
|
||||
# *DOCUMENTATION*
|
||||
|
|
|
|||
|
|
@ -331,13 +331,9 @@ config ARC_FPU_SAVE_RESTORE
|
|||
for floating point exceptions and rounding modes. These are
|
||||
preserved across task context switch when enabled.
|
||||
|
||||
config ARC_CANT_LLSC
|
||||
def_bool n
|
||||
|
||||
config ARC_HAS_LLSC
|
||||
bool "Insn: LLOCK/SCOND (efficient atomic ops)"
|
||||
default y
|
||||
depends on !ARC_CANT_LLSC
|
||||
|
||||
if ISA_ARCV2
|
||||
|
||||
|
|
|
|||
|
|
@ -156,6 +156,12 @@ static inline u64 __arch_cmpxchg64_relaxed(volatile void *ptr, u64 old, u64 new)
|
|||
}
|
||||
#define arch_cmpxchg64_relaxed __arch_cmpxchg64_relaxed
|
||||
|
||||
static inline s64 arch_atomic64_cmpxchg_relaxed(atomic64_t *v, s64 old, s64 new)
|
||||
{
|
||||
return arch_cmpxchg64_relaxed(&v->counter, old, new);
|
||||
}
|
||||
#define arch_atomic64_cmpxchg_relaxed arch_atomic64_cmpxchg_relaxed
|
||||
|
||||
static inline s64 arch_atomic64_xchg(atomic64_t *ptr, s64 new)
|
||||
{
|
||||
s64 prev;
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
__typeof__(*(ptr)) _n_ = (new); \
|
||||
__typeof__(*(ptr)) _prev_; \
|
||||
\
|
||||
switch(sizeof((_p_))) { \
|
||||
switch (sizeof(*(_p_))) { \
|
||||
case 1: \
|
||||
_prev_ = (__typeof__(*(ptr)))cmpxchg_emu_u8((volatile u8 *__force)_p_, (uintptr_t)_o_, (uintptr_t)_n_); \
|
||||
break; \
|
||||
|
|
@ -102,7 +102,7 @@
|
|||
__typeof__(ptr) _p_ = (ptr); \
|
||||
__typeof__(*(ptr)) _val_ = (val); \
|
||||
\
|
||||
switch(sizeof(*(_p_))) { \
|
||||
switch (sizeof(*(_p_))) { \
|
||||
case 4: \
|
||||
_val_ = __arch_xchg(_p_, _val_); \
|
||||
break; \
|
||||
|
|
|
|||
|
|
@ -593,6 +593,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
|
|||
cpu_id);
|
||||
} else {
|
||||
freq = clk_get_rate(cpu_clk);
|
||||
clk_put(cpu_clk);
|
||||
}
|
||||
if (freq)
|
||||
seq_printf(m, "CPU speed\t: %lu.%02lu Mhz\n",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/irq.h>
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ struct kvm_arch {
|
|||
* Stage 2 paging state for VMs with nested S2 using a virtual
|
||||
* VMID.
|
||||
*/
|
||||
struct kvm_s2_mmu *nested_mmus;
|
||||
struct kvm_s2_mmu **nested_mmus;
|
||||
size_t nested_mmus_size;
|
||||
int nested_mmus_next;
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ static inline u64 translate_ttbr0_el2_to_ttbr0_el1(u64 ttbr0)
|
|||
|
||||
extern bool forward_smc_trap(struct kvm_vcpu *vcpu);
|
||||
extern bool forward_debug_exception(struct kvm_vcpu *vcpu);
|
||||
extern void kvm_init_nested(struct kvm *kvm);
|
||||
extern int kvm_init_nested(struct kvm *kvm);
|
||||
extern void kvm_destroy_nested(struct kvm *kvm);
|
||||
extern int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu);
|
||||
extern void kvm_init_nested_s2_mmu(struct kvm_s2_mmu *mmu);
|
||||
extern struct kvm_s2_mmu *lookup_s2_mmu(struct kvm_vcpu *vcpu);
|
||||
|
|
|
|||
|
|
@ -65,8 +65,7 @@ static inline bool kvm_pkvm_ioctl_allowed(struct kvm *kvm, unsigned int ioctl)
|
|||
int r;
|
||||
|
||||
r = kvm_get_cap_for_kvm_ioctl(ioctl, &ext);
|
||||
|
||||
if (WARN_ON_ONCE(r < 0))
|
||||
if (r < 0)
|
||||
return false;
|
||||
|
||||
return kvm_pkvm_ext_allowed(kvm, ext);
|
||||
|
|
|
|||
|
|
@ -236,8 +236,6 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
|||
mutex_unlock(&kvm->lock);
|
||||
#endif
|
||||
|
||||
kvm_init_nested(kvm);
|
||||
|
||||
ret = kvm_share_hyp(kvm, kvm + 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -252,6 +250,10 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
|||
if (ret)
|
||||
goto err_free_cpumask;
|
||||
|
||||
ret = kvm_init_nested(kvm);
|
||||
if (ret)
|
||||
goto err_uninit_mmu;
|
||||
|
||||
if (is_protected_kvm_enabled()) {
|
||||
/*
|
||||
* If any failures occur after this is successful, make sure to
|
||||
|
|
@ -280,6 +282,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
|||
|
||||
err_uninit_mmu:
|
||||
kvm_uninit_stage2_mmu(kvm);
|
||||
kvm_destroy_nested(kvm);
|
||||
err_free_cpumask:
|
||||
free_cpumask_var(kvm->arch.supported_cpus);
|
||||
err_unshare_kvm:
|
||||
|
|
@ -337,6 +340,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
|
|||
|
||||
kvm_unshare_hyp(kvm, kvm + 1);
|
||||
|
||||
kvm_destroy_nested(kvm);
|
||||
kvm_arm_teardown_hypercalls(kvm);
|
||||
}
|
||||
|
||||
|
|
@ -1129,7 +1133,7 @@ static int kvm_vcpu_suspend(struct kvm_vcpu *vcpu)
|
|||
static int check_vcpu_requests(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (kvm_request_pending(vcpu)) {
|
||||
if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu))
|
||||
if (kvm_test_request(KVM_REQ_VM_DEAD, vcpu))
|
||||
return -EIO;
|
||||
|
||||
if (kvm_check_request(KVM_REQ_SLEEP, vcpu))
|
||||
|
|
|
|||
|
|
@ -1445,7 +1445,7 @@ static const struct encoding_to_trap_config encoding_to_fgt[] __initconst = {
|
|||
SR_FGT(OP_AT_S1E1A, HFGITR, ATS1E1A, 1),
|
||||
SR_FGT(OP_COSP_RCTX, HFGITR, COSPRCTX, 1),
|
||||
SR_FGT(OP_GCSPUSHX, HFGITR, nGCSEPP, 0),
|
||||
SR_FGT(OP_GCSPOPX, HFGITR, nGCSEPP, 0),
|
||||
SR_FGT(OP_GCSPOPCX, HFGITR, nGCSEPP, 0),
|
||||
SR_FGT(OP_GCSPUSHM, HFGITR, nGCSPUSHM_EL1, 0),
|
||||
SR_FGT(OP_BRB_IALL, HFGITR, nBRBIALL, 0),
|
||||
SR_FGT(OP_BRB_INJ, HFGITR, nBRBINJ, 0),
|
||||
|
|
|
|||
|
|
@ -52,8 +52,10 @@ int __pkvm_host_test_clear_young_guest(u64 gfn, u64 nr_pages, bool mkold, struct
|
|||
int __pkvm_host_mkyoung_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu);
|
||||
|
||||
bool addr_is_memory(phys_addr_t phys);
|
||||
bool addr_is_hyp_text(phys_addr_t phys);
|
||||
int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_prot prot);
|
||||
int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id);
|
||||
bool host_stage2_pte_is_hyp_owned(kvm_pte_t pte);
|
||||
int kvm_host_prepare_stage2(void *pgt_pool_base);
|
||||
int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd);
|
||||
void kvm_guest_destroy_stage2(struct pkvm_hyp_vm *vm);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ int __pkvm_create_private_mapping(phys_addr_t phys, size_t size,
|
|||
enum kvm_pgtable_prot prot,
|
||||
unsigned long *haddr);
|
||||
int pkvm_create_stack(phys_addr_t phys, unsigned long *haddr);
|
||||
int pkvm_check_host_ownership(void);
|
||||
int pkvm_alloc_private_va_range(size_t size, unsigned long *haddr);
|
||||
|
||||
#endif /* __KVM_HYP_MM_H */
|
||||
|
|
|
|||
|
|
@ -450,6 +450,14 @@ bool addr_is_memory(phys_addr_t phys)
|
|||
return !!find_mem_range(phys, &range);
|
||||
}
|
||||
|
||||
bool addr_is_hyp_text(phys_addr_t phys)
|
||||
{
|
||||
phys_addr_t start = ALIGN_DOWN(__hyp_pa(__hyp_text_start), PAGE_SIZE);
|
||||
phys_addr_t end = PAGE_ALIGN(__hyp_pa(__hyp_text_end));
|
||||
|
||||
return phys >= start && phys < end;
|
||||
}
|
||||
|
||||
static bool is_in_mem_range(u64 addr, struct kvm_mem_range *range)
|
||||
{
|
||||
return range->start <= addr && addr < range->end;
|
||||
|
|
@ -633,6 +641,18 @@ int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool host_stage2_pte_is_hyp_owned(kvm_pte_t pte)
|
||||
{
|
||||
if (kvm_pte_valid(pte))
|
||||
return false;
|
||||
|
||||
if (FIELD_GET(KVM_INVALID_PTE_TYPE_MASK, pte) !=
|
||||
KVM_HOST_INVALID_PTE_TYPE_DONATION)
|
||||
return false;
|
||||
|
||||
return FIELD_GET(KVM_HOST_DONATION_PTE_OWNER_MASK, pte) == PKVM_ID_HYP;
|
||||
}
|
||||
|
||||
#define KVM_HOST_PTE_OWNER_GUEST_HANDLE_MASK GENMASK(15, 0)
|
||||
/* We need 40 bits for the GFN to cover a 52-bit IPA with 4k pages and LPA2 */
|
||||
#define KVM_HOST_PTE_OWNER_GUEST_GFN_MASK GENMASK(55, 16)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ struct memblock_region hyp_memory[HYP_MEMBLOCK_REGIONS];
|
|||
unsigned int hyp_memblock_nr;
|
||||
|
||||
static u64 __io_map_base;
|
||||
static u64 __io_map_next;
|
||||
|
||||
struct hyp_fixmap_slot {
|
||||
u64 addr;
|
||||
|
|
@ -50,7 +51,7 @@ static int __pkvm_alloc_private_va_range(unsigned long start, size_t size)
|
|||
|
||||
hyp_assert_lock_held(&pkvm_pgd_lock);
|
||||
|
||||
if (!start || start < __io_map_base)
|
||||
if (!start || start < __io_map_next)
|
||||
return -EINVAL;
|
||||
|
||||
/* The allocated size is always a multiple of PAGE_SIZE */
|
||||
|
|
@ -60,7 +61,7 @@ static int __pkvm_alloc_private_va_range(unsigned long start, size_t size)
|
|||
if (cur > __hyp_vmemmap)
|
||||
return -ENOMEM;
|
||||
|
||||
__io_map_base = cur;
|
||||
__io_map_next = cur;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -70,7 +71,7 @@ static int __pkvm_alloc_private_va_range(unsigned long start, size_t size)
|
|||
* @size: The size of the VA range to reserve.
|
||||
* @haddr: The hypervisor virtual start address of the allocation.
|
||||
*
|
||||
* The private virtual address (VA) range is allocated above __io_map_base
|
||||
* The private virtual address (VA) range is allocated above __io_map_next
|
||||
* and aligned based on the order of @size.
|
||||
*
|
||||
* Return: 0 on success or negative error code on failure.
|
||||
|
|
@ -81,7 +82,7 @@ int pkvm_alloc_private_va_range(size_t size, unsigned long *haddr)
|
|||
int ret;
|
||||
|
||||
hyp_spin_lock(&pkvm_pgd_lock);
|
||||
addr = __io_map_base;
|
||||
addr = __io_map_next;
|
||||
ret = __pkvm_alloc_private_va_range(addr, size);
|
||||
hyp_spin_unlock(&pkvm_pgd_lock);
|
||||
|
||||
|
|
@ -341,7 +342,7 @@ static int create_fixblock(void)
|
|||
return -EINVAL;
|
||||
|
||||
hyp_spin_lock(&pkvm_pgd_lock);
|
||||
addr = ALIGN(__io_map_base, PMD_SIZE);
|
||||
addr = ALIGN(__io_map_next, PMD_SIZE);
|
||||
ret = __pkvm_alloc_private_va_range(addr, PMD_SIZE);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
|
@ -426,6 +427,7 @@ int hyp_create_idmap(u32 hyp_va_bits)
|
|||
*/
|
||||
__io_map_base = start & BIT(hyp_va_bits - 2);
|
||||
__io_map_base ^= BIT(hyp_va_bits - 2);
|
||||
__io_map_next = __io_map_base;
|
||||
__hyp_vmemmap = __io_map_base | BIT(hyp_va_bits - 3);
|
||||
|
||||
return __pkvm_create_mappings(start, end - start, start, PAGE_HYP_EXEC);
|
||||
|
|
@ -433,19 +435,19 @@ int hyp_create_idmap(u32 hyp_va_bits)
|
|||
|
||||
int pkvm_create_stack(phys_addr_t phys, unsigned long *haddr)
|
||||
{
|
||||
unsigned long addr, prev_base;
|
||||
unsigned long addr, prev_next;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
hyp_spin_lock(&pkvm_pgd_lock);
|
||||
|
||||
prev_base = __io_map_base;
|
||||
prev_next = __io_map_next;
|
||||
/*
|
||||
* Efficient stack verification using the NVHE_STACK_SHIFT bit implies
|
||||
* an alignment of our allocation on the order of the size.
|
||||
*/
|
||||
size = NVHE_STACK_SIZE * 2;
|
||||
addr = ALIGN(__io_map_base, size);
|
||||
addr = ALIGN(__io_map_next, size);
|
||||
|
||||
ret = __pkvm_alloc_private_va_range(addr, size);
|
||||
if (!ret) {
|
||||
|
|
@ -461,7 +463,7 @@ int pkvm_create_stack(phys_addr_t phys, unsigned long *haddr)
|
|||
ret = kvm_pgtable_hyp_map(&pkvm_pgtable, addr + NVHE_STACK_SIZE,
|
||||
NVHE_STACK_SIZE, phys, PAGE_HYP);
|
||||
if (ret)
|
||||
__io_map_base = prev_base;
|
||||
__io_map_next = prev_next;
|
||||
}
|
||||
hyp_spin_unlock(&pkvm_pgd_lock);
|
||||
|
||||
|
|
@ -470,6 +472,65 @@ int pkvm_create_stack(phys_addr_t phys, unsigned long *haddr)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int check_page_ownership(phys_addr_t phys)
|
||||
{
|
||||
kvm_pte_t pte;
|
||||
bool host_ok;
|
||||
int ret;
|
||||
|
||||
if (addr_is_memory(phys)) {
|
||||
struct hyp_page *page = hyp_phys_to_page(phys);
|
||||
|
||||
if (get_hyp_state(page) != PKVM_PAGE_OWNED ||
|
||||
get_host_state(page) != PKVM_NOPAGE)
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
ret = kvm_pgtable_get_leaf(&host_mmu.pgt, phys, &pte, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Hyp text may stay host-readable, see fix_host_ownership_walker(). */
|
||||
if (kvm_pte_valid(pte) && addr_is_hyp_text(phys))
|
||||
host_ok = !(kvm_pgtable_stage2_pte_prot(pte) & KVM_PGTABLE_PROT_W);
|
||||
else
|
||||
host_ok = host_stage2_pte_is_hyp_owned(pte);
|
||||
|
||||
return host_ok ? 0 : -EPERM;
|
||||
}
|
||||
|
||||
static int check_host_ownership_walker(const struct kvm_pgtable_visit_ctx *ctx,
|
||||
enum kvm_pgtable_walk_flags visit)
|
||||
{
|
||||
phys_addr_t phys, end;
|
||||
int ret;
|
||||
|
||||
if (!kvm_pte_valid(ctx->old))
|
||||
return 0;
|
||||
|
||||
phys = kvm_pte_to_phys(ctx->old);
|
||||
end = phys + kvm_granule_size(ctx->level);
|
||||
for (; phys < end; phys += PAGE_SIZE) {
|
||||
ret = check_page_ownership(phys);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pkvm_check_host_ownership(void)
|
||||
{
|
||||
struct kvm_pgtable_walker walker = {
|
||||
.cb = check_host_ownership_walker,
|
||||
.flags = KVM_PGTABLE_WALK_LEAF,
|
||||
};
|
||||
|
||||
/* The private range and the vmemmap share one quarter of the VA space. */
|
||||
return kvm_pgtable_walk(&pkvm_pgtable, __io_map_base,
|
||||
BIT(pkvm_pgtable.ia_bits - 2), &walker);
|
||||
}
|
||||
|
||||
static void *admit_host_page(void *arg)
|
||||
{
|
||||
struct kvm_hyp_memcache *host_mc = arg;
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
|
|||
if (test_bit(KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS, &host_arch_flags))
|
||||
hyp_vm->kvm.arch.midr_el1 = host_kvm->arch.midr_el1;
|
||||
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_MTE))
|
||||
|
|
@ -379,13 +379,14 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
|
|||
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_PTRAUTH_GENERIC))
|
||||
set_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, allowed_features);
|
||||
|
||||
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_SVE)) {
|
||||
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_SVE))
|
||||
set_bit(KVM_ARM_VCPU_SVE, allowed_features);
|
||||
kvm->arch.flags |= host_arch_flags & BIT(KVM_ARCH_FLAG_GUEST_HAS_SVE);
|
||||
}
|
||||
|
||||
bitmap_and(kvm->arch.vcpu_features, host_kvm->arch.vcpu_features,
|
||||
allowed_features, KVM_VCPU_MAX_FEATURES);
|
||||
out:
|
||||
__assign_bit(KVM_ARCH_FLAG_GUEST_HAS_SVE, &kvm->arch.flags,
|
||||
kvm_vcpu_has_feature(kvm, KVM_ARM_VCPU_SVE));
|
||||
}
|
||||
|
||||
static void unpin_host_vcpu(struct kvm_vcpu *host_vcpu)
|
||||
|
|
@ -398,10 +399,10 @@ static void unpin_host_sve_state(struct pkvm_hyp_vcpu *hyp_vcpu)
|
|||
{
|
||||
void *sve_state;
|
||||
|
||||
if (!vcpu_has_feature(&hyp_vcpu->vcpu, KVM_ARM_VCPU_SVE))
|
||||
sve_state = hyp_vcpu->vcpu.arch.sve_state;
|
||||
if (!sve_state)
|
||||
return;
|
||||
|
||||
sve_state = hyp_vcpu->vcpu.arch.sve_state;
|
||||
hyp_unpin_shared_mem(sve_state,
|
||||
sve_state + vcpu_sve_state_size(&hyp_vcpu->vcpu));
|
||||
}
|
||||
|
|
@ -450,7 +451,7 @@ static int pkvm_vcpu_init_sve(struct pkvm_hyp_vcpu *hyp_vcpu, struct kvm_vcpu *h
|
|||
unsigned int sve_max_vl;
|
||||
size_t sve_state_size;
|
||||
void *sve_state;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (!vcpu_has_feature(vcpu, KVM_ARM_VCPU_SVE)) {
|
||||
vcpu_clear_flag(vcpu, VCPU_SVE_FINALIZED);
|
||||
|
|
@ -459,25 +460,21 @@ static int pkvm_vcpu_init_sve(struct pkvm_hyp_vcpu *hyp_vcpu, struct kvm_vcpu *h
|
|||
|
||||
/* Limit guest vector length to the maximum supported by the host. */
|
||||
sve_max_vl = min(READ_ONCE(host_vcpu->arch.sve_max_vl), kvm_host_sve_max_vl);
|
||||
sve_state_size = sve_state_size_from_vl(sve_max_vl);
|
||||
sve_state = kern_hyp_va(READ_ONCE(host_vcpu->arch.sve_state));
|
||||
|
||||
if (!sve_state || !sve_state_size) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
if (!sve_vl_valid(sve_max_vl) || !sve_state)
|
||||
return -EINVAL;
|
||||
|
||||
sve_state_size = sve_state_size_from_vl(sve_max_vl);
|
||||
|
||||
ret = hyp_pin_shared_mem(sve_state, sve_state + sve_state_size);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
vcpu->arch.sve_state = sve_state;
|
||||
vcpu->arch.sve_max_vl = sve_max_vl;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
clear_bit(KVM_ARM_VCPU_SVE, vcpu->kvm->arch.vcpu_features);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vm_copy_id_regs(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ static int fix_host_ownership_walker(const struct kvm_pgtable_visit_ctx *ctx,
|
|||
case PKVM_PAGE_OWNED:
|
||||
set_hyp_state(page, PKVM_PAGE_OWNED);
|
||||
/* hyp text is RO in the host stage-2 to be inspected on panic. */
|
||||
if (prot == PAGE_HYP_EXEC) {
|
||||
if (addr_is_hyp_text(phys)) {
|
||||
set_host_state(page, PKVM_NOPAGE);
|
||||
return host_stage2_idmap_locked(phys, PAGE_SIZE, KVM_PGTABLE_PROT_R);
|
||||
} else {
|
||||
|
|
@ -269,6 +269,16 @@ static int fix_host_ownership(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* The stacks sit in the private VA range, not the linear map. */
|
||||
for (i = 0; i < hyp_nr_cpus; i++) {
|
||||
struct kvm_nvhe_init_params *params = per_cpu_ptr(&kvm_init_params, i);
|
||||
u64 start = params->stack_hyp_va - NVHE_STACK_SIZE;
|
||||
|
||||
ret = kvm_pgtable_walk(&pkvm_pgtable, start, NVHE_STACK_SIZE, &walker);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -324,6 +334,10 @@ void __noreturn __pkvm_init_finalise(void)
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = pkvm_check_host_ownership();
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = hyp_ffa_init(ffa_proxy_pages);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
|
|
|||
|
|
@ -185,7 +185,8 @@ static int kvm_smccc_set_filter(struct kvm *kvm, struct kvm_smccc_filter __user
|
|||
start = filter.base;
|
||||
end = start + filter.nr_functions - 1;
|
||||
|
||||
if (end < start || filter.action >= NR_SMCCC_FILTER_ACTIONS)
|
||||
if (!filter.nr_functions || end < start ||
|
||||
filter.action >= NR_SMCCC_FILTER_ACTIONS)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&kvm->arch.config_lock);
|
||||
|
|
|
|||
|
|
@ -59,27 +59,36 @@ static phys_addr_t stage2_range_addr_end(phys_addr_t addr, phys_addr_t end)
|
|||
* long will also starve other vCPUs. We have to also make sure that the page
|
||||
* tables are not freed while we released the lock.
|
||||
*/
|
||||
static int stage2_apply_range(struct kvm_s2_mmu *mmu, phys_addr_t addr,
|
||||
static int stage2_apply_range(struct kvm_s2_mmu *mmu, phys_addr_t start,
|
||||
phys_addr_t end,
|
||||
int (*fn)(struct kvm_pgtable *, u64, u64),
|
||||
bool resched)
|
||||
{
|
||||
struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
|
||||
bool lock_dropped = false;
|
||||
phys_addr_t addr = start;
|
||||
int ret;
|
||||
u64 next;
|
||||
|
||||
do {
|
||||
struct kvm_pgtable *pgt = mmu->pgt;
|
||||
/*
|
||||
* We may be raced on PGT teardown when we release the
|
||||
* kvm->mmu_lock. That's fine as the PGT is legitimately no
|
||||
* longer present.
|
||||
*/
|
||||
if (!pgt)
|
||||
return -EINVAL;
|
||||
return lock_dropped ? 0 : -EINVAL;
|
||||
|
||||
next = stage2_range_addr_end(addr, end);
|
||||
ret = fn(pgt, addr, next - addr);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (resched && next != end)
|
||||
if (resched && next != end) {
|
||||
cond_resched_rwlock_write(&kvm->mmu_lock);
|
||||
lock_dropped = true;
|
||||
}
|
||||
} while (addr = next, addr != end);
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -45,11 +45,24 @@ struct vncr_tlb {
|
|||
*/
|
||||
#define S2_MMU_PER_VCPU 2
|
||||
|
||||
void kvm_init_nested(struct kvm *kvm)
|
||||
int kvm_init_nested(struct kvm *kvm)
|
||||
{
|
||||
kvm->arch.nested_mmus = NULL;
|
||||
kvm->arch.nested_mmus = kvmalloc_objs(struct kvm_s2_mmu *,
|
||||
KVM_MAX_VCPUS * S2_MMU_PER_VCPU,
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
kvm->arch.nested_mmus_size = 0;
|
||||
atomic_set(&kvm->arch.vncr_tlb_count, 0);
|
||||
|
||||
return kvm->arch.nested_mmus ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
void kvm_destroy_nested(struct kvm *kvm)
|
||||
{
|
||||
for (int i = 0; i < kvm->arch.nested_mmus_size; i+= S2_MMU_PER_VCPU)
|
||||
kvfree(kvm->arch.nested_mmus[i]);
|
||||
|
||||
kvm->arch.nested_mmus_size = 0;
|
||||
kvfree(kvm->arch.nested_mmus);
|
||||
}
|
||||
|
||||
static int init_nested_s2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu)
|
||||
|
|
@ -70,8 +83,9 @@ static int init_nested_s2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu)
|
|||
int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
struct kvm_s2_mmu *tmp;
|
||||
int num_mmus, ret = 0;
|
||||
int num_mmus;
|
||||
|
||||
lockdep_assert_held(&kvm->arch.config_lock);
|
||||
|
||||
if (test_bit(KVM_ARM_VCPU_HAS_EL2_E2H0, kvm->arch.vcpu_features) &&
|
||||
!cpus_have_final_cap(ARM64_HAS_HCR_NV1))
|
||||
|
|
@ -84,51 +98,40 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
|
|||
if (!vcpu->arch.ctxt.vncr_array)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Let's treat memory allocation failures as benign: If we fail to
|
||||
* allocate anything, return an error and keep the allocated array
|
||||
* alive. Userspace may try to recover by initializing the vcpu
|
||||
* again, and there is no reason to affect the whole VM for this.
|
||||
*/
|
||||
num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU;
|
||||
|
||||
if (num_mmus > kvm->arch.nested_mmus_size) {
|
||||
tmp = kvzalloc_objs(*tmp, num_mmus, GFP_KERNEL_ACCOUNT);
|
||||
struct kvm_s2_mmu *tmp;
|
||||
int i, ret = 0;
|
||||
|
||||
tmp = kvzalloc_objs(*tmp, S2_MMU_PER_VCPU, GFP_KERNEL_ACCOUNT);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
|
||||
write_lock(&kvm->mmu_lock);
|
||||
|
||||
if (kvm->arch.nested_mmus_size) {
|
||||
memcpy(tmp, kvm->arch.nested_mmus,
|
||||
size_mul(sizeof(*tmp), kvm->arch.nested_mmus_size));
|
||||
|
||||
for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
|
||||
tmp[i].pgt->mmu = &tmp[i];
|
||||
for (i = 0; !ret && i < S2_MMU_PER_VCPU; i++) {
|
||||
ret = init_nested_s2_mmu(kvm, &tmp[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
swap(kvm->arch.nested_mmus, tmp);
|
||||
if (ret) {
|
||||
while (--i >= 0)
|
||||
kvm_free_stage2_pgd(&tmp[i]);
|
||||
|
||||
write_unlock(&kvm->mmu_lock);
|
||||
kvfree(tmp);
|
||||
free_page((unsigned long)vcpu->arch.ctxt.vncr_array);
|
||||
vcpu->arch.ctxt.vncr_array = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
kvfree(tmp);
|
||||
guard(write_lock)(&kvm->mmu_lock);
|
||||
|
||||
for (i = 0; i < S2_MMU_PER_VCPU; i++)
|
||||
kvm->arch.nested_mmus[i + kvm->arch.nested_mmus_size] = &tmp[i];
|
||||
|
||||
kvm->arch.nested_mmus_size += S2_MMU_PER_VCPU;
|
||||
}
|
||||
|
||||
for (int i = kvm->arch.nested_mmus_size; !ret && i < num_mmus; i++)
|
||||
ret = init_nested_s2_mmu(kvm, &kvm->arch.nested_mmus[i]);
|
||||
|
||||
if (ret) {
|
||||
for (int i = kvm->arch.nested_mmus_size; i < num_mmus; i++)
|
||||
kvm_free_stage2_pgd(&kvm->arch.nested_mmus[i]);
|
||||
|
||||
free_page((unsigned long)vcpu->arch.ctxt.vncr_array);
|
||||
vcpu->arch.ctxt.vncr_array = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
kvm->arch.nested_mmus_size = num_mmus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -742,7 +745,7 @@ void kvm_s2_mmu_iterate_by_vmid(struct kvm *kvm, u16 vmid,
|
|||
write_lock(&kvm->mmu_lock);
|
||||
|
||||
for (int i = 0; i < kvm->arch.nested_mmus_size; i++) {
|
||||
struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i];
|
||||
struct kvm_s2_mmu *mmu = kvm->arch.nested_mmus[i];
|
||||
|
||||
if (!kvm_s2_mmu_valid(mmu))
|
||||
continue;
|
||||
|
|
@ -784,7 +787,7 @@ struct kvm_s2_mmu *lookup_s2_mmu(struct kvm_vcpu *vcpu)
|
|||
* if S2 translation is disabled.
|
||||
*/
|
||||
for (int i = 0; i < kvm->arch.nested_mmus_size; i++) {
|
||||
struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i];
|
||||
struct kvm_s2_mmu *mmu = kvm->arch.nested_mmus[i];
|
||||
|
||||
if (!kvm_s2_mmu_valid(mmu))
|
||||
continue;
|
||||
|
|
@ -823,7 +826,7 @@ static struct kvm_s2_mmu *get_s2_mmu_nested(struct kvm_vcpu *vcpu)
|
|||
for (i = kvm->arch.nested_mmus_next;
|
||||
i < (kvm->arch.nested_mmus_size + kvm->arch.nested_mmus_next);
|
||||
i++) {
|
||||
s2_mmu = &kvm->arch.nested_mmus[i % kvm->arch.nested_mmus_size];
|
||||
s2_mmu = kvm->arch.nested_mmus[i % kvm->arch.nested_mmus_size];
|
||||
|
||||
if (atomic_read(&s2_mmu->refcnt) == 0)
|
||||
break;
|
||||
|
|
@ -1260,6 +1263,17 @@ void kvm_handle_s1e2_tlbi(struct kvm_vcpu *vcpu, u32 inst, u64 val)
|
|||
invalidate_vncr_va(vcpu->kvm, &scope);
|
||||
}
|
||||
|
||||
static void kvm_invalidate_vncr_ipa_all(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_pgtable *pgt = kvm->arch.mmu.pgt;
|
||||
|
||||
lockdep_assert_held_write(&kvm->mmu_lock);
|
||||
|
||||
/* if the mmu lock was dropped, pgt teardown may have raced. */
|
||||
if (pgt)
|
||||
kvm_invalidate_vncr_ipa(kvm, 0, BIT(pgt->ia_bits));
|
||||
}
|
||||
|
||||
void kvm_nested_s2_wp(struct kvm *kvm)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -1270,13 +1284,13 @@ void kvm_nested_s2_wp(struct kvm *kvm)
|
|||
return;
|
||||
|
||||
for (i = 0; i < kvm->arch.nested_mmus_size; i++) {
|
||||
struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i];
|
||||
struct kvm_s2_mmu *mmu = kvm->arch.nested_mmus[i];
|
||||
|
||||
if (kvm_s2_mmu_valid(mmu))
|
||||
kvm_stage2_wp_range(mmu, 0, kvm_phys_size(mmu));
|
||||
}
|
||||
|
||||
kvm_invalidate_vncr_ipa(kvm, 0, BIT(kvm->arch.mmu.pgt->ia_bits));
|
||||
kvm_invalidate_vncr_ipa_all(kvm);
|
||||
}
|
||||
|
||||
void kvm_nested_s2_unmap(struct kvm *kvm, bool may_block)
|
||||
|
|
@ -1289,13 +1303,13 @@ void kvm_nested_s2_unmap(struct kvm *kvm, bool may_block)
|
|||
return;
|
||||
|
||||
for (i = 0; i < kvm->arch.nested_mmus_size; i++) {
|
||||
struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i];
|
||||
struct kvm_s2_mmu *mmu = kvm->arch.nested_mmus[i];
|
||||
|
||||
if (kvm_s2_mmu_valid(mmu))
|
||||
kvm_stage2_unmap_range(mmu, 0, kvm_phys_size(mmu), may_block);
|
||||
}
|
||||
|
||||
kvm_invalidate_vncr_ipa(kvm, 0, BIT(kvm->arch.mmu.pgt->ia_bits));
|
||||
kvm_invalidate_vncr_ipa_all(kvm);
|
||||
}
|
||||
|
||||
void kvm_nested_s2_flush(struct kvm *kvm)
|
||||
|
|
@ -1308,7 +1322,7 @@ void kvm_nested_s2_flush(struct kvm *kvm)
|
|||
return;
|
||||
|
||||
for (i = 0; i < kvm->arch.nested_mmus_size; i++) {
|
||||
struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i];
|
||||
struct kvm_s2_mmu *mmu = kvm->arch.nested_mmus[i];
|
||||
|
||||
if (kvm_s2_mmu_valid(mmu))
|
||||
kvm_stage2_flush_range(mmu, 0, kvm_phys_size(mmu));
|
||||
|
|
@ -1317,17 +1331,12 @@ void kvm_nested_s2_flush(struct kvm *kvm)
|
|||
|
||||
void kvm_arch_flush_shadow_all(struct kvm *kvm)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < kvm->arch.nested_mmus_size; i++) {
|
||||
struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i];
|
||||
for (int i = 0; i < kvm->arch.nested_mmus_size; i++) {
|
||||
struct kvm_s2_mmu *mmu = kvm->arch.nested_mmus[i];
|
||||
|
||||
if (!WARN_ON(atomic_read(&mmu->refcnt)))
|
||||
kvm_free_stage2_pgd(mmu);
|
||||
}
|
||||
kvfree(kvm->arch.nested_mmus);
|
||||
kvm->arch.nested_mmus = NULL;
|
||||
kvm->arch.nested_mmus_size = 0;
|
||||
kvm_uninit_stage2_mmu(kvm);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -926,6 +926,7 @@ static u64 *demux_wb_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd)
|
|||
struct kvm_guest_debug_arch *dbg = &vcpu->arch.vcpu_debug_state;
|
||||
|
||||
switch (rd->Op2) {
|
||||
case 0b001:
|
||||
case 0b100:
|
||||
return &dbg->dbg_bvr[rd->CRm];
|
||||
case 0b101:
|
||||
|
|
|
|||
|
|
@ -1658,7 +1658,7 @@ static void vgic_mmio_write_its_baser(struct kvm *kvm,
|
|||
unsigned long val)
|
||||
{
|
||||
const struct vgic_its_abi *abi = vgic_its_get_abi(its);
|
||||
u64 entry_size, table_type;
|
||||
u64 old, entry_size, table_type;
|
||||
u64 reg, *regptr, clearbits = 0;
|
||||
|
||||
/* When GITS_CTLR.Enable is 1, we ignore write accesses. */
|
||||
|
|
@ -1681,7 +1681,9 @@ static void vgic_mmio_write_its_baser(struct kvm *kvm,
|
|||
return;
|
||||
}
|
||||
|
||||
reg = update_64bit_reg(*regptr, addr & 7, len, val);
|
||||
old = *regptr;
|
||||
|
||||
reg = update_64bit_reg(old, addr & 7, len, val);
|
||||
reg &= ~GITS_BASER_RO_MASK;
|
||||
reg &= ~clearbits;
|
||||
|
||||
|
|
@ -1691,7 +1693,8 @@ static void vgic_mmio_write_its_baser(struct kvm *kvm,
|
|||
|
||||
*regptr = reg;
|
||||
|
||||
if (!(reg & GITS_BASER_VALID)) {
|
||||
/* The ITS driver rewrites an unchanged GITS_BASER<n> on resume. */
|
||||
if (reg != old) {
|
||||
/* Take the its_lock to prevent a race with a save/restore */
|
||||
mutex_lock(&its->its_lock);
|
||||
switch (table_type) {
|
||||
|
|
@ -1702,6 +1705,8 @@ static void vgic_mmio_write_its_baser(struct kvm *kvm,
|
|||
vgic_its_free_collection_list(kvm, its);
|
||||
break;
|
||||
}
|
||||
/* A concurrent injection may have cached a translation. */
|
||||
vgic_its_invalidate_cache(its);
|
||||
mutex_unlock(&its->its_lock);
|
||||
}
|
||||
}
|
||||
|
|
@ -2019,18 +2024,22 @@ static int vgic_its_attr_regs_access(struct kvm_device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static u32 compute_next_devid_offset(struct list_head *h,
|
||||
static u32 compute_next_devid_offset(struct vgic_its *its, u64 baser,
|
||||
struct its_device *dev)
|
||||
{
|
||||
struct its_device *next;
|
||||
u32 next_offset;
|
||||
struct its_device *next = dev;
|
||||
|
||||
if (list_is_last(&dev->dev_list, h))
|
||||
return 0;
|
||||
next = list_next_entry(dev, dev_list);
|
||||
next_offset = next->device_id - dev->device_id;
|
||||
/*
|
||||
* Point at the next device vgic_its_save_device_tables() saves. It
|
||||
* sorts device_list first, so the subtraction cannot underflow.
|
||||
*/
|
||||
list_for_each_entry_continue(next, &its->device_list, dev_list) {
|
||||
if (vgic_its_check_id(its, baser, next->device_id, NULL))
|
||||
return min_t(u32, next->device_id - dev->device_id,
|
||||
VITS_DTE_MAX_DEVID_OFFSET);
|
||||
}
|
||||
|
||||
return min_t(u32, next_offset, VITS_DTE_MAX_DEVID_OFFSET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 compute_next_eventid_offset(struct list_head *h, struct its_ite *ite)
|
||||
|
|
@ -2271,17 +2280,18 @@ static int vgic_its_restore_itt(struct vgic_its *its, struct its_device *dev)
|
|||
* vgic_its_save_dte - Save a device table entry at a given GPA
|
||||
*
|
||||
* @its: ITS handle
|
||||
* @baser: GITS_BASER<dev> the caller is saving against
|
||||
* @dev: ITS device
|
||||
* @ptr: GPA
|
||||
*/
|
||||
static int vgic_its_save_dte(struct vgic_its *its, struct its_device *dev,
|
||||
gpa_t ptr)
|
||||
static int vgic_its_save_dte(struct vgic_its *its, u64 baser,
|
||||
struct its_device *dev, gpa_t ptr)
|
||||
{
|
||||
u64 val, itt_addr_field;
|
||||
u32 next_offset;
|
||||
|
||||
itt_addr_field = dev->itt_addr >> 8;
|
||||
next_offset = compute_next_devid_offset(&its->device_list, dev);
|
||||
next_offset = compute_next_devid_offset(its, baser, dev);
|
||||
val = (1ULL << KVM_ITS_DTE_VALID_SHIFT |
|
||||
((u64)next_offset << KVM_ITS_DTE_NEXT_SHIFT) |
|
||||
(itt_addr_field << KVM_ITS_DTE_ITTADDR_SHIFT) |
|
||||
|
|
@ -2380,15 +2390,16 @@ static int vgic_its_save_device_tables(struct vgic_its *its)
|
|||
int ret;
|
||||
gpa_t eaddr;
|
||||
|
||||
/* Don't fail a save that userspace must be able to issue. */
|
||||
if (!vgic_its_check_id(its, baser,
|
||||
dev->device_id, &eaddr))
|
||||
return -EINVAL;
|
||||
continue;
|
||||
|
||||
ret = vgic_its_save_itt(its, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = vgic_its_save_dte(its, dev, eaddr);
|
||||
ret = vgic_its_save_dte(its, baser, dev, eaddr);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -2541,9 +2552,6 @@ static int vgic_its_save_collection_table(struct vgic_its *its)
|
|||
max_size = GITS_BASER_NR_PAGES(baser) * SZ_64K;
|
||||
|
||||
list_for_each_entry(collection, &its->collection_list, coll_list) {
|
||||
if (!vgic_its_check_id(its, baser, collection->collection_id, NULL))
|
||||
return -EINVAL;
|
||||
|
||||
ret = vgic_its_save_cte(its, collection, gpa);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -188,12 +188,24 @@
|
|||
#define HGATP_MODE_SHIFT HGATP32_MODE_SHIFT
|
||||
#endif
|
||||
|
||||
/* VSIP & HVIP relation */
|
||||
/*
|
||||
* VSIP & HVIP relation
|
||||
*
|
||||
* The bit positions are same between VSIP and HVIP for interrupt
|
||||
* numbers 13-63, where there's a shift for the SSI, STI and SEI.
|
||||
*/
|
||||
#define VSIP_TO_HVIP_SHIFT (IRQ_VS_SOFT - IRQ_S_SOFT)
|
||||
#define VSIP_VALID_MASK ((_AC(1, UL) << IRQ_S_SOFT) | \
|
||||
#define VSIP_BIAS_MASK ((_AC(1, UL) << IRQ_S_SOFT) | \
|
||||
(_AC(1, UL) << IRQ_S_TIMER) | \
|
||||
(_AC(1, UL) << IRQ_S_EXT) | \
|
||||
(_AC(1, UL) << IRQ_PMU_OVF))
|
||||
(_AC(1, UL) << IRQ_S_EXT))
|
||||
#define VSIP_NO_BIAS_MASK (_AC(1, UL) << IRQ_PMU_OVF)
|
||||
#define VSIP_VALID_MASK (VSIP_BIAS_MASK | VSIP_NO_BIAS_MASK)
|
||||
#define vsip_to_hvip(_vsip) ((((_vsip) & VSIP_BIAS_MASK) << \
|
||||
VSIP_TO_HVIP_SHIFT) | \
|
||||
((_vsip) & VSIP_NO_BIAS_MASK))
|
||||
#define hvip_to_vsip(_hvip) ((((_hvip) >> VSIP_TO_HVIP_SHIFT) & \
|
||||
VSIP_BIAS_MASK) | \
|
||||
((_hvip) & VSIP_NO_BIAS_MASK))
|
||||
|
||||
/* AIA CSR bits */
|
||||
#define TOPI_IID_SHIFT 16
|
||||
|
|
|
|||
|
|
@ -965,9 +965,14 @@ int kvm_riscv_aia_imsic_rw_attr(struct kvm *kvm, unsigned long type,
|
|||
if (!vcpu)
|
||||
return -ENODEV;
|
||||
|
||||
if (mutex_lock_killable(&vcpu->mutex))
|
||||
return -EINTR;
|
||||
|
||||
imsic = vcpu->arch.aia_context.imsic_state;
|
||||
if (!imsic)
|
||||
return -ENODEV;
|
||||
if (!imsic) {
|
||||
rc = -ENODEV;
|
||||
goto out_unlock;
|
||||
}
|
||||
isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
|
||||
|
||||
read_lock_irqsave(&imsic->vsfile_lock, flags);
|
||||
|
|
@ -991,6 +996,8 @@ int kvm_riscv_aia_imsic_rw_attr(struct kvm *kvm, unsigned long type,
|
|||
rc = imsic_vsfile_rw(vsfile_hgei, vsfile_cpu, imsic->nr_eix,
|
||||
isel, write, val);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&vcpu->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -627,6 +627,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
|
|||
int ret;
|
||||
kvm_pfn_t hfn;
|
||||
bool is_hugetlb;
|
||||
bool unused = false;
|
||||
bool writable;
|
||||
unsigned int vma_pageshift;
|
||||
gfn_t gfn = gpa >> PAGE_SHIFT;
|
||||
|
|
@ -707,6 +708,8 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
|
|||
vma_pageshift, current);
|
||||
return 0;
|
||||
}
|
||||
if (is_sigpending_pfn(hfn))
|
||||
return -EINTR;
|
||||
if (is_error_noslot_pfn(hfn))
|
||||
return -EFAULT;
|
||||
|
||||
|
|
@ -719,8 +722,10 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
|
|||
|
||||
write_lock(&kvm->mmu_lock);
|
||||
|
||||
if (mmu_invalidate_retry(kvm, mmu_seq))
|
||||
if (mmu_invalidate_retry(kvm, mmu_seq)) {
|
||||
unused = true;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if we are backed by a THP and thus use block mapping if
|
||||
|
|
@ -743,7 +748,8 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
|
|||
kvm_err("Failed to map in G-stage\n");
|
||||
|
||||
out_unlock:
|
||||
kvm_release_faultin_page(kvm, page, ret && ret != -EEXIST, writable);
|
||||
kvm_release_faultin_page(kvm, page,
|
||||
unused || (ret && ret != -EEXIST), writable);
|
||||
write_unlock(&kvm->mmu_lock);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,12 +42,24 @@ void __kvm_riscv_nacl_hfence(void *shmem,
|
|||
}
|
||||
}
|
||||
|
||||
entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_CONFIG(i);
|
||||
*entp = cpu_to_lelong(control);
|
||||
/*
|
||||
* Per SBI v3.0 section 15.1.2, the Page_Number and Page_Count
|
||||
* words must be updated before the Config word with its Pending
|
||||
* bit set. WRITE_ONCE() stops the compiler from reordering the
|
||||
* stores and smp_wmb() makes the parameter words globally
|
||||
* visible to the SBI implementation (or NACL hardware) before
|
||||
* the Pending bit is set.
|
||||
*/
|
||||
entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_PNUM(i);
|
||||
*entp = cpu_to_lelong(page_num);
|
||||
WRITE_ONCE(*entp, cpu_to_lelong(page_num));
|
||||
entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_PCOUNT(i);
|
||||
*entp = cpu_to_lelong(page_count);
|
||||
WRITE_ONCE(*entp, cpu_to_lelong(page_count));
|
||||
|
||||
/* Ensure the parameter words are visible before the Pending bit */
|
||||
smp_wmb();
|
||||
|
||||
entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_CONFIG(i);
|
||||
WRITE_ONCE(*entp, cpu_to_lelong(control));
|
||||
}
|
||||
|
||||
int kvm_riscv_nacl_enable(void)
|
||||
|
|
|
|||
|
|
@ -491,8 +491,7 @@ bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, u64 mask)
|
|||
bool ret;
|
||||
|
||||
raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
|
||||
ie = ((vcpu->arch.guest_csr.vsie & VSIP_VALID_MASK)
|
||||
<< VSIP_TO_HVIP_SHIFT) & (unsigned long)mask;
|
||||
ie = vsip_to_hvip(vcpu->arch.guest_csr.vsie) & (unsigned long)mask;
|
||||
ie |= vcpu->arch.guest_csr.vsie & ~IRQ_LOCAL_MASK &
|
||||
(unsigned long)mask;
|
||||
ret = vcpu->arch.irqs_pending[0] & ie;
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ int kvm_riscv_vcpu_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
|
|||
}
|
||||
|
||||
/* Print details in-case of error */
|
||||
if (ret < 0) {
|
||||
if (ret < 0 && ret != -EINTR) {
|
||||
kvm_err("VCPU exit error %d\n", ret);
|
||||
kvm_err("SEPC=0x%lx SSTATUS=0x%lx HSTATUS=0x%lx\n",
|
||||
vcpu->arch.guest_context.sepc,
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ static int kvm_riscv_vcpu_general_get_csr(struct kvm_vcpu *vcpu,
|
|||
|
||||
if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) {
|
||||
kvm_riscv_vcpu_flush_interrupts(vcpu);
|
||||
*out_val = (csr->hvip >> VSIP_TO_HVIP_SHIFT) & VSIP_VALID_MASK;
|
||||
*out_val = hvip_to_vsip(csr->hvip);
|
||||
*out_val |= csr->hvip & ~IRQ_LOCAL_MASK;
|
||||
} else
|
||||
*out_val = ((unsigned long *)csr)[reg_num];
|
||||
|
|
@ -293,10 +293,8 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu,
|
|||
|
||||
reg_num = array_index_nospec(reg_num, regs_max);
|
||||
|
||||
if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) {
|
||||
reg_val &= VSIP_VALID_MASK;
|
||||
reg_val <<= VSIP_TO_HVIP_SHIFT;
|
||||
}
|
||||
if (reg_num == KVM_REG_RISCV_CSR_REG(sip))
|
||||
reg_val = vsip_to_hvip(reg_val);
|
||||
|
||||
((unsigned long *)csr)[reg_num] = reg_val;
|
||||
|
||||
|
|
|
|||
|
|
@ -270,12 +270,13 @@ static int pmu_ctr_read(struct kvm_vcpu *vcpu, unsigned long cidx,
|
|||
return -EINVAL;
|
||||
|
||||
pmc->counter_val = kvpmu->fw_event[fevent_code].value;
|
||||
*out_val = pmc->counter_val;
|
||||
} else if (pmc->perf_event) {
|
||||
pmc->counter_val += perf_event_read_value(pmc->perf_event, &enabled, &running);
|
||||
*out_val = pmc->counter_val +
|
||||
perf_event_read_value(pmc->perf_event, &enabled, &running);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
*out_val = pmc->counter_val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -454,6 +455,14 @@ int kvm_riscv_vcpu_pmu_snapshot_set_shmem(struct kvm_vcpu *vcpu, unsigned long s
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear any previously installed snapshot area to avoid leaking
|
||||
* the old sdata and to keep sdata/snapshot_addr consistent if
|
||||
* the re-install fails below.
|
||||
*/
|
||||
if (kvpmu->snapshot_addr != INVALID_GPA)
|
||||
kvm_pmu_clear_snapshot_area(vcpu);
|
||||
|
||||
kvpmu->sdata = kzalloc(snapshot_area_size, GFP_ATOMIC | __GFP_ACCOUNT);
|
||||
if (!kvpmu->sdata) {
|
||||
sbiret = SBI_ERR_FAILURE;
|
||||
|
|
@ -645,7 +654,6 @@ int kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu, unsigned long ctr_base,
|
|||
{
|
||||
struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
|
||||
int i, pmc_index, sbiret = 0;
|
||||
u64 enabled, running;
|
||||
struct kvm_pmc *pmc;
|
||||
int fevent_code;
|
||||
bool snap_flag_set = flags & SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT;
|
||||
|
|
@ -675,14 +683,19 @@ int kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu, unsigned long ctr_base,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (!kvpmu->fw_event[fevent_code].started)
|
||||
if (!kvpmu->fw_event[fevent_code].started) {
|
||||
sbiret = SBI_ERR_ALREADY_STOPPED;
|
||||
|
||||
kvpmu->fw_event[fevent_code].started = false;
|
||||
} else {
|
||||
kvpmu->fw_event[fevent_code].started = false;
|
||||
pmc->counter_val = kvpmu->fw_event[fevent_code].value;
|
||||
}
|
||||
} else if (pmc->perf_event) {
|
||||
if (pmc->started) {
|
||||
/* Stop counting the counter */
|
||||
perf_event_disable(pmc->perf_event);
|
||||
/*
|
||||
* Stop the counter and fold the live count into counter_val.
|
||||
* Reset the event value to avoid redundant accumulation.
|
||||
*/
|
||||
pmc->counter_val += perf_event_pause(pmc->perf_event, true);
|
||||
pmc->started = false;
|
||||
} else {
|
||||
sbiret = SBI_ERR_ALREADY_STOPPED;
|
||||
|
|
@ -696,11 +709,6 @@ int kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu, unsigned long ctr_base,
|
|||
}
|
||||
|
||||
if (snap_flag_set && !sbiret) {
|
||||
if (pmc->cinfo.type == SBI_PMU_CTR_TYPE_FW)
|
||||
pmc->counter_val = kvpmu->fw_event[fevent_code].value;
|
||||
else if (pmc->perf_event)
|
||||
pmc->counter_val += perf_event_read_value(pmc->perf_event,
|
||||
&enabled, &running);
|
||||
/*
|
||||
* The counter and overflow indices in the snapshot region are w.r.to
|
||||
* cbase. Modify the set bit in the counter mask instead of the pmc_index
|
||||
|
|
@ -727,9 +735,10 @@ int kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu, unsigned long ctr_base,
|
|||
}
|
||||
}
|
||||
|
||||
if (shmem_needs_update)
|
||||
kvm_vcpu_write_guest(vcpu, kvpmu->snapshot_addr, kvpmu->sdata,
|
||||
sizeof(struct riscv_pmu_snapshot_data));
|
||||
if (shmem_needs_update &&
|
||||
kvm_vcpu_write_guest(vcpu, kvpmu->snapshot_addr, kvpmu->sdata,
|
||||
sizeof(struct riscv_pmu_snapshot_data)))
|
||||
sbiret = SBI_ERR_FAILURE;
|
||||
|
||||
out:
|
||||
retdata->err_val = sbiret;
|
||||
|
|
|
|||
|
|
@ -95,9 +95,9 @@ static int kvm_sbi_ext_hsm_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
|
|||
ret = kvm_sbi_hsm_vcpu_get_status(vcpu);
|
||||
if (ret >= 0) {
|
||||
retdata->out_val = ret;
|
||||
retdata->err_val = 0;
|
||||
ret = 0;
|
||||
}
|
||||
return 0;
|
||||
break;
|
||||
case SBI_EXT_HSM_HART_SUSPEND:
|
||||
switch (lower_32_bits(cp->a0)) {
|
||||
case SBI_HSM_SUSPEND_RET_DEFAULT:
|
||||
|
|
|
|||
|
|
@ -61,10 +61,13 @@ static enum hrtimer_restart kvm_riscv_vcpu_hrtimer_expired(struct hrtimer *h)
|
|||
|
||||
static int kvm_riscv_vcpu_timer_cancel(struct kvm_vcpu_timer *t)
|
||||
{
|
||||
if (!t->init_done || !t->next_set)
|
||||
if (!t->init_done)
|
||||
return -EINVAL;
|
||||
|
||||
hrtimer_cancel(&t->hrt);
|
||||
|
||||
if (!t->next_set)
|
||||
return -EINVAL;
|
||||
t->next_set = false;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -460,7 +460,11 @@ static int VNAME(var, active_entries)[EARLY_AREAS] __initdata
|
|||
#define __REGISTER_STATIC_DEBUG_INFO(var, name, pages, areas, view) \
|
||||
static int __init VNAME(var, reg)(void) \
|
||||
{ \
|
||||
debug_register_static(&var, (pages), (areas)); \
|
||||
int rc; \
|
||||
\
|
||||
rc = debug_register_static(&var, (pages), (areas)); \
|
||||
if (rc) \
|
||||
return rc; \
|
||||
debug_register_view(&var, (view)); \
|
||||
return 0; \
|
||||
} \
|
||||
|
|
@ -493,7 +497,7 @@ static debug_info_t __refdata var = \
|
|||
static debug_info_t __used __section(".s390dbf_info") *VNAME(var, info) = &var; \
|
||||
__REGISTER_STATIC_DEBUG_INFO(var, name, pages, nr_areas, view)
|
||||
|
||||
void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas);
|
||||
int debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas);
|
||||
|
||||
#endif /* MODULE */
|
||||
|
||||
|
|
|
|||
|
|
@ -434,7 +434,8 @@ static debug_info_t *debug_info_copy(debug_info_t *in, int mode)
|
|||
debug_info_free(rc);
|
||||
} while (1);
|
||||
|
||||
if (mode == NO_AREAS)
|
||||
/* debug_register_static() failure leaves areas NULL, bounds intact */
|
||||
if (mode == NO_AREAS || !in->areas)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < in->nr_areas; i++) {
|
||||
|
|
@ -823,6 +824,9 @@ ssize_t debug_dump(debug_info_t *id, struct debug_view *view,
|
|||
file_private_info_t *p_info;
|
||||
size_t size, offset = 0;
|
||||
|
||||
if (!id)
|
||||
return -EINVAL;
|
||||
|
||||
/* Need space for '\0' byte */
|
||||
if (buf_size < 1)
|
||||
return 0;
|
||||
|
|
@ -950,8 +954,12 @@ EXPORT_SYMBOL(debug_register);
|
|||
*
|
||||
* Note: This function is called automatically via an initcall generated by
|
||||
* DEFINE_STATIC_DEBUG_INFO.
|
||||
*
|
||||
* Return:
|
||||
* - 0 on success
|
||||
* - negative error code on failure
|
||||
*/
|
||||
void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
|
||||
int debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
|
||||
{
|
||||
unsigned long flags;
|
||||
debug_info_t *copy;
|
||||
|
|
@ -959,7 +967,7 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
|
|||
if (!initialized) {
|
||||
pr_err("Tried to register debug feature %s too early\n",
|
||||
id->name);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
debug_get_param(id->name, &id->level, &pages_per_area, false);
|
||||
|
|
@ -975,7 +983,7 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
|
|||
id->active_entries = NULL;
|
||||
raw_spin_unlock_irqrestore(&id->lock, flags);
|
||||
|
||||
return;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Replace static trace area with dynamic copy. */
|
||||
|
|
@ -993,6 +1001,8 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
|
|||
mutex_lock(&debug_mutex);
|
||||
_debug_register(id);
|
||||
mutex_unlock(&debug_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove debugfs entries. */
|
||||
|
|
|
|||
|
|
@ -825,7 +825,7 @@ static int find_secret_in_page(const u8 secret_id[UV_SECRET_ID_LEN],
|
|||
{
|
||||
u16 i;
|
||||
|
||||
for (i = 0; i < list->total_num_secrets; i++) {
|
||||
for (i = 0; i < list->num_secr_stored; i++) {
|
||||
if (memcmp(secret_id, list->secrets[i].id, UV_SECRET_ID_LEN) == 0) {
|
||||
*secret = list->secrets[i].hdr;
|
||||
return 0;
|
||||
|
|
@ -846,11 +846,14 @@ int uv_find_secret(const u8 secret_id[UV_SECRET_ID_LEN],
|
|||
struct uv_secret_list *list,
|
||||
struct uv_secret_list_item_hdr *secret)
|
||||
{
|
||||
u16 start_idx = 0;
|
||||
u16 start_idx;
|
||||
u16 list_rc;
|
||||
int ret;
|
||||
|
||||
list->next_secret_idx = 0;
|
||||
|
||||
do {
|
||||
start_idx = list->next_secret_idx;
|
||||
uv_list_secrets(list, start_idx, &list_rc, NULL);
|
||||
if (list_rc != UVC_RC_EXECUTED && list_rc != UVC_RC_MORE_DATA) {
|
||||
if (list_rc == UVC_RC_INV_CMD)
|
||||
|
|
@ -861,7 +864,6 @@ int uv_find_secret(const u8 secret_id[UV_SECRET_ID_LEN],
|
|||
ret = find_secret_in_page(secret_id, list, secret);
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
start_idx = list->next_secret_idx;
|
||||
} while (list_rc == UVC_RC_MORE_DATA && start_idx < list->next_secret_idx);
|
||||
|
||||
return -ENOENT;
|
||||
|
|
|
|||
|
|
@ -621,17 +621,20 @@ int dat_get_storage_key(union asce asce, gfn_t gfn, union skey *skey)
|
|||
union pte *ptep;
|
||||
int rc;
|
||||
|
||||
again:
|
||||
skey->skey = 0;
|
||||
rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!ptep) {
|
||||
union crste crste;
|
||||
union crste crste = READ_ONCE(*crstep);
|
||||
|
||||
crste = READ_ONCE(*crstep);
|
||||
if (!crste.h.fc || !crste.s.fc1.pr)
|
||||
if (!crste_leaf(crste) && !crste.h.i)
|
||||
goto again;
|
||||
if (!crste.s.fc1.pr)
|
||||
return 0;
|
||||
|
||||
skey->skey = page_get_storage_key(large_crste_to_phys(crste, gfn));
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -662,13 +665,20 @@ int dat_set_storage_key(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gf
|
|||
union pte *ptep;
|
||||
int rc;
|
||||
|
||||
again:
|
||||
rc = dat_entry_walk(mc, gfn, asce, DAT_WALK_LEAF_ALLOC, TABLE_TYPE_PAGE_TABLE,
|
||||
&crstep, &ptep);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!ptep) {
|
||||
page_set_storage_key(large_crste_to_phys(*crstep, gfn), skey.skey, !nq);
|
||||
union crste crste = READ_ONCE(*crstep);
|
||||
|
||||
/* A large page has been split concurrently, try again */
|
||||
if (!crste_leaf(crste))
|
||||
goto again;
|
||||
|
||||
page_set_storage_key(large_crste_to_phys(crste, gfn), skey.skey, !nq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -718,15 +728,22 @@ int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gf
|
|||
union pte *ptep;
|
||||
int rc;
|
||||
|
||||
again:
|
||||
rc = dat_entry_walk(mmc, gfn, asce, DAT_WALK_LEAF_ALLOC, TABLE_TYPE_PAGE_TABLE,
|
||||
&crstep, &ptep);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!ptep) {
|
||||
union crste crste = READ_ONCE(*crstep);
|
||||
|
||||
/* A large page has been split concurrently, try again */
|
||||
if (!crste_leaf(crste))
|
||||
goto again;
|
||||
if (!oldkey)
|
||||
oldkey = &prev;
|
||||
return page_cond_set_storage_key(large_crste_to_phys(*crstep, gfn), skey, oldkey,
|
||||
|
||||
return page_cond_set_storage_key(large_crste_to_phys(crste, gfn), skey, oldkey,
|
||||
nq, mr, mc);
|
||||
}
|
||||
|
||||
|
|
@ -768,7 +785,7 @@ int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey)
|
|||
int rc;
|
||||
|
||||
skey->skey = 0;
|
||||
|
||||
again:
|
||||
rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
|
@ -776,9 +793,12 @@ int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey)
|
|||
if (!ptep) {
|
||||
union crste crste = READ_ONCE(*crstep);
|
||||
|
||||
if (!crste.h.fc || !crste.s.fc1.pr)
|
||||
/* A large page has been split concurrently, try again */
|
||||
if (!crste_leaf(crste) && !crste.h.i)
|
||||
goto again;
|
||||
if (!crste.s.fc1.pr)
|
||||
return 0;
|
||||
skey->skey = page_reset_referenced(large_crste_to_phys(*crstep, gfn)) << 1;
|
||||
skey->skey = page_reset_referenced(large_crste_to_phys(crste, gfn)) << 1;
|
||||
return 0;
|
||||
}
|
||||
old = pgste_get_lock(ptep);
|
||||
|
|
@ -846,19 +866,12 @@ long dat_reset_skeys(union asce asce, gfn_t start)
|
|||
}
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
|
||||
struct slot_priv {
|
||||
unsigned long token;
|
||||
struct kvm_s390_mmu_cache *mc;
|
||||
};
|
||||
|
||||
static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
{
|
||||
struct slot_priv *p = walk->priv;
|
||||
union crste dummy = { .val = p->token };
|
||||
union pte new_pte, pte = READ_ONCE(*ptep);
|
||||
union pgste pgste;
|
||||
|
||||
new_pte = _PTE_TOK(dummy.tok.type, dummy.tok.par);
|
||||
new_pte = walk->priv ? _PTE_EMPTY : _PTE_TOK(_DAT_TOKEN_PIC, PGM_ADDRESSING);
|
||||
|
||||
/* Table entry already in the desired state. */
|
||||
if (pte.val == new_pte.val)
|
||||
|
|
@ -875,10 +888,9 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal
|
|||
static long _dat_slot_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
{
|
||||
union crste new_crste, crste = READ_ONCE(*crstep);
|
||||
struct slot_priv *p = walk->priv;
|
||||
struct kvm_s390_mmu_cache *mc = walk->priv;
|
||||
|
||||
new_crste.val = p->token;
|
||||
new_crste.h.tt = crste.h.tt;
|
||||
new_crste = mc ? _CRSTE_EMPTY(crste.h.tt) : _CRSTE_HOLE(crste.h.tt);
|
||||
|
||||
/* Table entry already in the desired state. */
|
||||
if (crste.val == new_crste.val)
|
||||
|
|
@ -902,7 +914,10 @@ static long _dat_slot_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct d
|
|||
if (!crste.h.fc && !crste.h.i)
|
||||
return 0;
|
||||
/* Split (install a lower level table), and handle things there. */
|
||||
return dat_split_crste(p->mc, crstep, gfn, walk->asce, false);
|
||||
if (mc)
|
||||
return dat_split_crste(mc, crstep, gfn, walk->asce, false);
|
||||
/* A large page should never cross memslots boundaries */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct dat_walk_ops dat_slot_ops = {
|
||||
|
|
@ -910,16 +925,10 @@ static const struct dat_walk_ops dat_slot_ops = {
|
|||
.crste_ops = { _dat_slot_crste, _dat_slot_crste, _dat_slot_crste, _dat_slot_crste, },
|
||||
};
|
||||
|
||||
int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end,
|
||||
u16 type, u16 param)
|
||||
int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end)
|
||||
{
|
||||
struct slot_priv priv = {
|
||||
.token = _CRSTE_TOK(0, type, param).val,
|
||||
.mc = mc,
|
||||
};
|
||||
|
||||
return _dat_walk_gfn_range(start, end, asce, &dat_slot_ops,
|
||||
DAT_WALK_IGN_HOLES | DAT_WALK_ANY, &priv);
|
||||
DAT_WALK_IGN_HOLES | DAT_WALK_ANY, mc);
|
||||
}
|
||||
|
||||
static void pgste_set_unlock_multiple(union pte *first, int n, union pgste *pgstes)
|
||||
|
|
|
|||
|
|
@ -547,8 +547,7 @@ long dat_reset_skeys(union asce asce, gfn_t start);
|
|||
unsigned long dat_get_ptval(struct page_table *table, struct ptval_param param);
|
||||
void dat_set_ptval(struct page_table *table, struct ptval_param param, unsigned long val);
|
||||
|
||||
int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end,
|
||||
u16 type, u16 param);
|
||||
int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end);
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
int dat_set_prefix_notif_bit(union asce asce, gfn_t gfn);
|
||||
|
|
@ -973,16 +972,15 @@ static inline int get_level(union crste *crstep, union pte *ptep)
|
|||
return ptep ? TABLE_TYPE_PAGE_TABLE : crstep->h.tt;
|
||||
}
|
||||
|
||||
static inline int dat_delete_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start,
|
||||
unsigned long npages)
|
||||
static inline int dat_delete_slot(union asce asce, gfn_t start, unsigned long npages)
|
||||
{
|
||||
return dat_set_slot(mc, asce, start, start + npages, _DAT_TOKEN_PIC, PGM_ADDRESSING);
|
||||
return dat_set_slot(NULL, asce, start, start + npages);
|
||||
}
|
||||
|
||||
static inline int dat_create_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start,
|
||||
unsigned long npages)
|
||||
{
|
||||
return dat_set_slot(mc, asce, start, start + npages, _DAT_TOKEN_NONE, 0);
|
||||
return dat_set_slot(mc, asce, start, start + npages);
|
||||
}
|
||||
|
||||
static inline bool crste_is_ucas(union crste crste)
|
||||
|
|
|
|||
|
|
@ -994,11 +994,13 @@ static long _destroy_pages_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct da
|
|||
static long _destroy_pages_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
{
|
||||
phys_addr_t origin, cur, end;
|
||||
union crste crste;
|
||||
|
||||
if (!crstep->h.fc || !crstep->s.fc1.pr)
|
||||
crste = READ_ONCE(*crstep);
|
||||
if (!crste.h.fc || !crste.s.fc1.pr)
|
||||
return 0;
|
||||
|
||||
origin = crste_origin_large(*crstep);
|
||||
origin = crste_origin_large(crste);
|
||||
cur = ((max(gfn, walk->start) - gfn) << PAGE_SHIFT) + origin;
|
||||
end = ((min(next, walk->end) - gfn) << PAGE_SHIFT) + origin;
|
||||
for ( ; cur < end; cur += PAGE_SIZE)
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ int s390_kvm_mmu_prepare_memory_region(struct kvm *kvm,
|
|||
struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change)
|
||||
{
|
||||
struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;
|
||||
int rc = 0;
|
||||
|
||||
if (kvm_is_ucontrol(kvm) && new && new->id < KVM_USER_MEM_SLOTS)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -61,6 +64,10 @@ int s390_kvm_mmu_prepare_memory_region(struct kvm *kvm,
|
|||
* and munmap() stuff in this slot after doing this call at any
|
||||
* time.
|
||||
*/
|
||||
if (change != KVM_MR_MOVE && change != KVM_MR_CREATE) {
|
||||
WARN(1, "Unknown KVM MR CHANGE: %d\n", change);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (new->userspace_addr & ~PAGE_MASK)
|
||||
return -EINVAL;
|
||||
if ((new->base_gfn + new->npages) * PAGE_SIZE > kvm->arch.mem_limit)
|
||||
|
|
@ -69,65 +76,41 @@ int s390_kvm_mmu_prepare_memory_region(struct kvm *kvm,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!kvm_s390_is_migration_mode(kvm))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Turn off migration mode when:
|
||||
* - userspace creates a new memslot with dirty logging off,
|
||||
* - userspace modifies an existing memslot (MOVE or FLAGS_ONLY) and
|
||||
* dirty logging is turned off.
|
||||
* Migration mode expects dirty page logging being enabled to store
|
||||
* its dirty bitmap.
|
||||
*/
|
||||
if (change != KVM_MR_DELETE &&
|
||||
!(new->flags & KVM_MEM_LOG_DIRTY_PAGES))
|
||||
WARN(kvm_s390_vm_stop_migration(kvm),
|
||||
"Failed to stop migration mode");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void s390_kvm_mmu_commit_memory_region(struct kvm *kvm,
|
||||
struct kvm_memory_slot *old,
|
||||
const struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change)
|
||||
{
|
||||
struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;
|
||||
int rc = 0;
|
||||
|
||||
guard(mutex)(&kvm->slots_arch_lock);
|
||||
if (kvm->arch.migration_mode) {
|
||||
/*
|
||||
* Turn off migration mode when:
|
||||
* - userspace creates a new memslot with dirty logging off,
|
||||
* - userspace modifies an existing memslot (MOVE or FLAGS_ONLY)
|
||||
* and dirty logging is turned off.
|
||||
* Migration mode expects dirty page logging being enabled to
|
||||
* store its dirty bitmap.
|
||||
*/
|
||||
if (change != KVM_MR_DELETE &&
|
||||
!(new->flags & KVM_MEM_LOG_DIRTY_PAGES))
|
||||
WARN(kvm_s390_vm_stop_migration(kvm),
|
||||
"Failed to stop migration mode");
|
||||
}
|
||||
|
||||
if (change == KVM_MR_FLAGS_ONLY)
|
||||
return;
|
||||
|
||||
mc = kvm_s390_new_mmu_cache();
|
||||
if (!mc) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
return 0;
|
||||
if (change != KVM_MR_DELETE) {
|
||||
/* Enough capacity to add a new memslot */
|
||||
mc = kvm_s390_new_mmu_cache();
|
||||
if (!mc)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
scoped_guard(write_lock, &kvm->mmu_lock) {
|
||||
kvm_s390_update_cmma_dirty(kvm, old);
|
||||
switch (change) {
|
||||
case KVM_MR_DELETE:
|
||||
rc = dat_delete_slot(mc, kvm->arch.gmap->asce, old->base_gfn, old->npages);
|
||||
break;
|
||||
case KVM_MR_MOVE:
|
||||
rc = dat_delete_slot(mc, kvm->arch.gmap->asce, old->base_gfn, old->npages);
|
||||
if (rc)
|
||||
break;
|
||||
fallthrough;
|
||||
case KVM_MR_CREATE:
|
||||
if (change == KVM_MR_DELETE || change == KVM_MR_MOVE)
|
||||
rc = dat_delete_slot(kvm->arch.gmap->asce, old->base_gfn, old->npages);
|
||||
if (!rc && (change == KVM_MR_MOVE || change == KVM_MR_CREATE))
|
||||
rc = dat_create_slot(mc, kvm->arch.gmap->asce, new->base_gfn, new->npages);
|
||||
break;
|
||||
case KVM_MR_FLAGS_ONLY:
|
||||
break;
|
||||
default:
|
||||
WARN(1, "Unknown KVM MR CHANGE: %d\n", change);
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (rc)
|
||||
pr_warn("failed to commit memory region\n");
|
||||
/*
|
||||
* Can only be triggered if dat_{create,delete}_slot() found an
|
||||
* internal inconsistency or if the mmu cache ran out of memory;
|
||||
* both should be impossible.
|
||||
*/
|
||||
KVM_BUG_ON(rc, kvm);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,5 @@ int s390_kvm_mmu_prepare_memory_region(struct kvm *kvm,
|
|||
const struct kvm_memory_slot *old,
|
||||
struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change);
|
||||
void s390_kvm_mmu_commit_memory_region(struct kvm *kvm,
|
||||
struct kvm_memory_slot *old,
|
||||
const struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change);
|
||||
|
||||
#endif /* ARCH_KVM_GMAP_KVM_MMU_H */
|
||||
|
|
|
|||
|
|
@ -1589,12 +1589,25 @@ static inline int ___gaccess_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg
|
|||
parent = READ_ONCE(sg->parent);
|
||||
if (!parent)
|
||||
return -EAGAIN;
|
||||
retry:
|
||||
scoped_guard(spinlock, &parent->children_lock) {
|
||||
if (READ_ONCE(sg->parent) != parent)
|
||||
return -EAGAIN;
|
||||
sg->invalidated = false;
|
||||
rc = _gaccess_do_shadow(vcpu->arch.mc, sg, saddr, walk);
|
||||
}
|
||||
if (rc == -ENOENT) {
|
||||
struct kvm_memory_slot *slot;
|
||||
struct guest_fault *entries;
|
||||
|
||||
entries = get_entries(walk);
|
||||
slot = kvm_vcpu_gfn_to_memslot(vcpu, entries[LEVEL_MEM].gfn);
|
||||
if (!slot)
|
||||
return PGM_ADDRESSING;
|
||||
rc = gmap_link(vcpu->arch.mc, parent, entries + LEVEL_MEM, slot);
|
||||
if (!rc)
|
||||
goto retry;
|
||||
}
|
||||
if (!rc)
|
||||
kvm_s390_release_faultin_array(vcpu->kvm, walk->raw_entries, false);
|
||||
return rc;
|
||||
|
|
|
|||
|
|
@ -2984,61 +2984,58 @@ static int adapter_indicators_set(struct kvm *kvm,
|
|||
struct s390_io_adapter *adapter,
|
||||
struct kvm_s390_adapter_int *adapter_int)
|
||||
{
|
||||
unsigned long bit;
|
||||
int summary_set, idx;
|
||||
struct s390_map_info *ind_info, *summary_info;
|
||||
void *map;
|
||||
struct page *ind_page, *summary_page;
|
||||
unsigned long flags;
|
||||
unsigned long bit;
|
||||
int summary_set;
|
||||
void *map;
|
||||
|
||||
ind_page = NULL;
|
||||
|
||||
spin_lock_irqsave(&adapter->maps_lock, flags);
|
||||
ind_info = get_map_info(adapter, adapter_int->ind_addr);
|
||||
scoped_guard(spinlock_irqsave, &adapter->maps_lock) {
|
||||
ind_info = get_map_info(adapter, adapter_int->ind_addr);
|
||||
if (ind_info) {
|
||||
map = page_address(ind_info->page);
|
||||
bit = get_ind_bit(ind_info->addr, adapter_int->ind_offset, adapter->swap);
|
||||
set_bit(bit, map);
|
||||
}
|
||||
}
|
||||
if (!ind_info) {
|
||||
spin_unlock_irqrestore(&adapter->maps_lock, flags);
|
||||
ind_page = pin_map_page(kvm, adapter_int->ind_addr, 0);
|
||||
if (!ind_page)
|
||||
return -1;
|
||||
idx = srcu_read_lock(&kvm->srcu);
|
||||
map = page_address(ind_page);
|
||||
bit = get_ind_bit(adapter_int->ind_addr,
|
||||
adapter_int->ind_offset, adapter->swap);
|
||||
set_bit(bit, map);
|
||||
mark_page_dirty(kvm, adapter_int->ind_gaddr >> PAGE_SHIFT);
|
||||
set_page_dirty_lock(ind_page);
|
||||
srcu_read_unlock(&kvm->srcu, idx);
|
||||
unpin_user_page(ind_page);
|
||||
} else {
|
||||
map = page_address(ind_info->page);
|
||||
bit = get_ind_bit(ind_info->addr, adapter_int->ind_offset, adapter->swap);
|
||||
set_bit(bit, map);
|
||||
spin_unlock_irqrestore(&adapter->maps_lock, flags);
|
||||
}
|
||||
scoped_guard(srcu, &kvm->srcu)
|
||||
mark_page_dirty(kvm, gpa_to_gfn(adapter_int->ind_gaddr));
|
||||
|
||||
spin_lock_irqsave(&adapter->maps_lock, flags);
|
||||
summary_info = get_map_info(adapter, adapter_int->summary_addr);
|
||||
scoped_guard(spinlock_irqsave, &adapter->maps_lock) {
|
||||
summary_info = get_map_info(adapter, adapter_int->summary_addr);
|
||||
if (summary_info) {
|
||||
map = page_address(summary_info->page);
|
||||
bit = get_ind_bit(summary_info->addr, adapter_int->summary_offset,
|
||||
adapter->swap);
|
||||
summary_set = test_and_set_bit(bit, map);
|
||||
}
|
||||
}
|
||||
if (!summary_info) {
|
||||
spin_unlock_irqrestore(&adapter->maps_lock, flags);
|
||||
summary_page = pin_map_page(kvm, adapter_int->summary_addr, 0);
|
||||
if (!summary_page)
|
||||
return -1;
|
||||
idx = srcu_read_lock(&kvm->srcu);
|
||||
map = page_address(summary_page);
|
||||
bit = get_ind_bit(adapter_int->summary_addr,
|
||||
adapter_int->summary_offset, adapter->swap);
|
||||
summary_set = test_and_set_bit(bit, map);
|
||||
mark_page_dirty(kvm, adapter_int->summary_gaddr >> PAGE_SHIFT);
|
||||
set_page_dirty_lock(summary_page);
|
||||
srcu_read_unlock(&kvm->srcu, idx);
|
||||
unpin_user_page(summary_page);
|
||||
} else {
|
||||
map = page_address(summary_info->page);
|
||||
bit = get_ind_bit(summary_info->addr, adapter_int->summary_offset,
|
||||
adapter->swap);
|
||||
summary_set = test_and_set_bit(bit, map);
|
||||
spin_unlock_irqrestore(&adapter->maps_lock, flags);
|
||||
}
|
||||
scoped_guard(srcu, &kvm->srcu)
|
||||
mark_page_dirty(kvm, gpa_to_gfn(adapter_int->summary_gaddr));
|
||||
|
||||
return summary_set ? 0 : 1;
|
||||
}
|
||||
|
|
@ -3048,26 +3045,29 @@ static int adapter_indicators_set_fast(struct kvm *kvm,
|
|||
struct kvm_s390_adapter_int *adapter_int,
|
||||
int setbit)
|
||||
{
|
||||
struct s390_map_info *ind_info, *summary_info;
|
||||
unsigned long bit;
|
||||
int summary_set;
|
||||
struct s390_map_info *ind_info, *summary_info;
|
||||
void *map;
|
||||
|
||||
spin_lock(&adapter->maps_lock);
|
||||
guard(srcu)(&kvm->srcu);
|
||||
guard(spinlock)(&adapter->maps_lock);
|
||||
|
||||
ind_info = get_map_info(adapter, adapter_int->ind_addr);
|
||||
if (!ind_info) {
|
||||
spin_unlock(&adapter->maps_lock);
|
||||
if (!ind_info)
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
map = page_address(ind_info->page);
|
||||
bit = get_ind_bit(ind_info->addr, adapter_int->ind_offset, adapter->swap);
|
||||
if (setbit)
|
||||
if (setbit) {
|
||||
set_bit(bit, map);
|
||||
summary_info = get_map_info(adapter, adapter_int->summary_addr);
|
||||
if (!summary_info) {
|
||||
spin_unlock(&adapter->maps_lock);
|
||||
return -EWOULDBLOCK;
|
||||
mark_page_dirty(kvm, gpa_to_gfn(adapter_int->ind_gaddr));
|
||||
}
|
||||
|
||||
summary_info = get_map_info(adapter, adapter_int->summary_addr);
|
||||
if (!summary_info)
|
||||
return -EWOULDBLOCK;
|
||||
|
||||
map = page_address(summary_info->page);
|
||||
bit = get_ind_bit(summary_info->addr, adapter_int->summary_offset,
|
||||
adapter->swap);
|
||||
|
|
@ -3077,7 +3077,8 @@ static int adapter_indicators_set_fast(struct kvm *kvm,
|
|||
summary_set = test_and_set_bit(bit, map);
|
||||
else
|
||||
summary_set = test_and_clear_bit(bit, map);
|
||||
spin_unlock(&adapter->maps_lock);
|
||||
mark_page_dirty(kvm, gpa_to_gfn(adapter_int->summary_gaddr));
|
||||
|
||||
return summary_set ? 0 : 1;
|
||||
}
|
||||
|
||||
|
|
@ -3228,9 +3229,9 @@ int kvm_s390_set_irq_state(struct kvm_vcpu *vcpu, void __user *irqstate, int len
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (storestatus) {
|
||||
n = kvm_s390_store_status_unloaded(vcpu, KVM_S390_STORE_STATUS_NOADDR);
|
||||
scoped_guard(srcu, &vcpu->kvm->srcu)
|
||||
n = kvm_s390_store_status_unloaded(vcpu, KVM_S390_STORE_STATUS_NOADDR);
|
||||
return r ? r : n;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5766,7 +5766,7 @@ static long cmma_d_count_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_
|
|||
return 0;
|
||||
}
|
||||
|
||||
void kvm_s390_update_cmma_dirty(struct kvm *kvm, struct kvm_memory_slot *old)
|
||||
void kvm_s390_update_cmma_dirty(struct kvm *kvm, const struct kvm_memory_slot *old)
|
||||
{
|
||||
const struct dat_walk_ops ops = { .pte_entry = cmma_d_count_pte, };
|
||||
|
||||
|
|
@ -5781,7 +5781,6 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_memory_slot *old,
|
|||
const struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change)
|
||||
{
|
||||
s390_kvm_mmu_commit_memory_region(kvm, old, new, change);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -472,7 +472,7 @@ int __kvm_s390_mprotect_many(struct gmap *gmap, gpa_t gpa, u8 npages, unsigned i
|
|||
unsigned long bits);
|
||||
|
||||
bool kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu);
|
||||
void kvm_s390_update_cmma_dirty(struct kvm *kvm, struct kvm_memory_slot *old);
|
||||
void kvm_s390_update_cmma_dirty(struct kvm *kvm, const struct kvm_memory_slot *old);
|
||||
int kvm_s390_vm_stop_migration(struct kvm *kvm);
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -226,6 +226,7 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev,
|
|||
device_lock(&pdev->dev);
|
||||
if (pdev->error_state == pci_channel_io_perm_failure) {
|
||||
ers_res = PCI_ERS_RESULT_DISCONNECT;
|
||||
status_str = "skipped (permanent failure)";
|
||||
goto out_unlock;
|
||||
}
|
||||
pdev->error_state = pci_channel_io_frozen;
|
||||
|
|
@ -297,8 +298,8 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev,
|
|||
driver->err_handler->resume(pdev);
|
||||
pci_uevent_ers(pdev, PCI_ERS_RESULT_RECOVERED);
|
||||
out_unlock:
|
||||
zpci_report_status(zdev, pdev, "recovery", status_str);
|
||||
device_unlock(&pdev->dev);
|
||||
zpci_report_status(zdev, "recovery", status_str);
|
||||
|
||||
return ers_res;
|
||||
}
|
||||
|
|
@ -361,8 +362,10 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf)
|
|||
|
||||
__zpci_event_print_error(pdev, ccdf);
|
||||
|
||||
if (!pdev)
|
||||
if (!pdev) {
|
||||
zpci_report_status(zdev, NULL, "error event", "no pdev bound");
|
||||
goto no_pdev;
|
||||
}
|
||||
|
||||
switch (ccdf->pec) {
|
||||
case 0x002a: /* Error event concerns FMB */
|
||||
|
|
|
|||
|
|
@ -87,9 +87,23 @@ static struct debug_view debug_log_view = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static ssize_t zpci_report_pdev(struct pci_dev *pdev, char *buf, size_t size)
|
||||
{
|
||||
struct pci_driver *driver;
|
||||
const char *start = buf;
|
||||
char *end = buf + size;
|
||||
|
||||
device_lock_assert(&pdev->dev);
|
||||
buf += scnprintf(buf, end - buf, "state: %s\n", zpci_state_str(pdev->error_state));
|
||||
driver = to_pci_driver(pdev->dev.driver);
|
||||
buf += scnprintf(buf, end - buf, "driver: %s\n", (driver) ? driver->name : "n/a");
|
||||
return buf - start;
|
||||
}
|
||||
|
||||
/**
|
||||
* zpci_report_status - Report the status of operations on a PCI device
|
||||
* @zdev: The PCI device for which to report status
|
||||
* @zdev: The zPCI device for which to report status
|
||||
* @pdev: The PCI device associated with the zdev if any, NULL otherwise
|
||||
* @operation: A string representing the operation reported
|
||||
* @status: A string representing the status of the operation
|
||||
*
|
||||
|
|
@ -103,15 +117,14 @@ static struct debug_view debug_log_view = {
|
|||
*
|
||||
* Return: 0 on success an error code < 0 otherwise.
|
||||
*/
|
||||
int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char *status)
|
||||
int zpci_report_status(struct zpci_dev *zdev, struct pci_dev *pdev,
|
||||
const char *operation, const char *status)
|
||||
{
|
||||
struct zpci_report_error *report;
|
||||
struct pci_driver *driver = NULL;
|
||||
struct pci_dev *pdev = NULL;
|
||||
char *buf, *end;
|
||||
int ret;
|
||||
|
||||
if (!zdev || !zdev->zbus)
|
||||
if (!zdev)
|
||||
return -ENODEV;
|
||||
|
||||
/* Protected virtualization hosts get nothing from us */
|
||||
|
|
@ -121,18 +134,13 @@ int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char
|
|||
report = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!report)
|
||||
return -ENOMEM;
|
||||
if (zdev->zbus->bus)
|
||||
pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn);
|
||||
if (pdev)
|
||||
driver = to_pci_driver(pdev->dev.driver);
|
||||
|
||||
buf = report->data.log_data;
|
||||
end = report->data.log_data + ZPCI_REPORT_DATA_SIZE;
|
||||
buf += scnprintf(buf, end - buf, "report: %s\n", operation);
|
||||
buf += scnprintf(buf, end - buf, "status: %s\n", status);
|
||||
buf += scnprintf(buf, end - buf, "state: %s\n",
|
||||
(pdev) ? zpci_state_str(pdev->error_state) : "n/a");
|
||||
buf += scnprintf(buf, end - buf, "driver: %s\n", (driver) ? driver->name : "n/a");
|
||||
if (pdev)
|
||||
buf += zpci_report_pdev(pdev, buf, end - buf);
|
||||
ret = debug_dump(pci_debug_msg_id, &debug_log_view, buf, end - buf, true);
|
||||
if (ret < 0)
|
||||
pr_err("Reading PCI debug messages failed with code %d\n", ret);
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@
|
|||
*/
|
||||
#ifndef __S390_PCI_REPORT_H
|
||||
#define __S390_PCI_REPORT_H
|
||||
#include <linux/pci.h>
|
||||
|
||||
struct zpci_dev;
|
||||
|
||||
int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char *status);
|
||||
int zpci_report_status(struct zpci_dev *zdev, struct pci_dev *pdev,
|
||||
const char *operation, const char *status);
|
||||
|
||||
#endif /* __S390_PCI_REPORT_H */
|
||||
|
|
|
|||
|
|
@ -74,6 +74,14 @@ int svsm_perform_call_protocol(struct svsm_call *call)
|
|||
|
||||
flags = native_local_irq_save();
|
||||
|
||||
/*
|
||||
* 'caa' is a per-CPU variable. To avoid using a stale or incorrect
|
||||
* 'caa' if the task is preempted or migrated to another CPU after it
|
||||
* is fetched, always fetch 'caa' and then issue the SVSM call with
|
||||
* interrupts disabled. This ensures the correct 'caa' is used.
|
||||
*/
|
||||
call->caa = svsm_get_caa();
|
||||
|
||||
ghcb = __sev_get_ghcb(&state);
|
||||
|
||||
do {
|
||||
|
|
@ -321,7 +329,6 @@ int snp_svsm_vtpm_send_command(u8 *buffer)
|
|||
{
|
||||
struct svsm_call call = {};
|
||||
|
||||
call.caa = svsm_get_caa();
|
||||
call.rax = SVSM_VTPM_CALL(SVSM_VTPM_CMD);
|
||||
call.rcx = __pa(buffer);
|
||||
|
||||
|
|
@ -345,7 +352,6 @@ bool snp_svsm_vtpm_probe(void)
|
|||
if (!snp_vmpl)
|
||||
return false;
|
||||
|
||||
call.caa = svsm_get_caa();
|
||||
call.rax = SVSM_VTPM_CALL(SVSM_VTPM_QUERY);
|
||||
|
||||
if (svsm_perform_call_protocol(&call))
|
||||
|
|
|
|||
|
|
@ -343,11 +343,10 @@ void amd_brs_drain(void)
|
|||
if (!amd_brs_match_plm(event, from, to))
|
||||
continue;
|
||||
|
||||
perf_clear_branch_entry_bitfields(br+nr);
|
||||
|
||||
br[nr].from = from;
|
||||
br[nr].to = to;
|
||||
|
||||
br[nr] = (struct perf_branch_entry){
|
||||
.from = from,
|
||||
.to = to,
|
||||
};
|
||||
nr++;
|
||||
}
|
||||
empty:
|
||||
|
|
|
|||
|
|
@ -184,13 +184,6 @@ void amd_pmu_lbr_read(void)
|
|||
entry.to.split.reserved)
|
||||
continue;
|
||||
|
||||
perf_clear_branch_entry_bitfields(br + out);
|
||||
|
||||
br[out].from = sign_ext_branch_ip(entry.from.split.ip);
|
||||
br[out].to = sign_ext_branch_ip(entry.to.split.ip);
|
||||
br[out].mispred = entry.from.split.mispredict;
|
||||
br[out].predicted = !br[out].mispred;
|
||||
|
||||
/*
|
||||
* Set branch speculation information using the status of
|
||||
* the valid and spec bits.
|
||||
|
|
@ -208,7 +201,14 @@ void amd_pmu_lbr_read(void)
|
|||
* speculative and took the correct path
|
||||
*/
|
||||
idx = (entry.to.split.valid << 1) | entry.to.split.spec;
|
||||
br[out].spec = lbr_spec_map[idx];
|
||||
|
||||
br[out] = (struct perf_branch_entry){
|
||||
.from = sign_ext_branch_ip(entry.from.split.ip),
|
||||
.to = sign_ext_branch_ip(entry.to.split.ip),
|
||||
.mispred = entry.from.split.mispredict,
|
||||
.predicted = !entry.from.split.mispredict,
|
||||
.spec = lbr_spec_map[idx],
|
||||
};
|
||||
out++;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -506,6 +506,8 @@ static struct event_constraint intel_pnc_event_constraints[] = {
|
|||
INTEL_EVENT_CONSTRAINT(0xce, 0x1),
|
||||
|
||||
INTEL_UEVENT_CONSTRAINT(0x01b1, 0x8),
|
||||
INTEL_UEVENT_CONSTRAINT(0x01b2, 0xf),
|
||||
INTEL_UEVENT_CONSTRAINT(0x02b2, 0xf),
|
||||
INTEL_UEVENT_CONSTRAINT(0x0847, 0xf),
|
||||
INTEL_UEVENT_CONSTRAINT(0x0446, 0xf),
|
||||
INTEL_UEVENT_CONSTRAINT(0x0846, 0xf),
|
||||
|
|
@ -520,6 +522,14 @@ static struct extra_reg intel_pnc_extra_regs[] __read_mostly = {
|
|||
INTEL_UEVENT_EXTRA_REG(0x022a, MSR_OMR_1, 0x40ffffff0000ffffull, OMR_1),
|
||||
INTEL_UEVENT_EXTRA_REG(0x042a, MSR_OMR_2, 0x40ffffff0000ffffull, OMR_2),
|
||||
INTEL_UEVENT_EXTRA_REG(0x082a, MSR_OMR_3, 0x40ffffff0000ffffull, OMR_3),
|
||||
INTEL_UEVENT_EXTRA_REG(0x014f, MSR_OMR_0, 0x40ffffff0000ffffull, OMR_0),
|
||||
INTEL_UEVENT_EXTRA_REG(0x024f, MSR_OMR_1, 0x40ffffff0000ffffull, OMR_1),
|
||||
INTEL_UEVENT_EXTRA_REG(0x044f, MSR_OMR_2, 0x40ffffff0000ffffull, OMR_2),
|
||||
INTEL_UEVENT_EXTRA_REG(0x084f, MSR_OMR_3, 0x40ffffff0000ffffull, OMR_3),
|
||||
INTEL_UEVENT_EXTRA_REG(0x01d6, MSR_OMR_0, 0x40ffffff0000ffffull, OMR_0),
|
||||
INTEL_UEVENT_EXTRA_REG(0x02d6, MSR_OMR_1, 0x40ffffff0000ffffull, OMR_1),
|
||||
INTEL_UEVENT_EXTRA_REG(0x04d6, MSR_OMR_2, 0x40ffffff0000ffffull, OMR_2),
|
||||
INTEL_UEVENT_EXTRA_REG(0x08d6, MSR_OMR_3, 0x40ffffff0000ffffull, OMR_3),
|
||||
INTEL_UEVENT_PEBS_LDLAT_EXTRA_REG(0x01cd),
|
||||
INTEL_UEVENT_EXTRA_REG(0x02c6, MSR_PEBS_FRONTEND, 0x9, FE),
|
||||
INTEL_UEVENT_EXTRA_REG(0x03c6, MSR_PEBS_FRONTEND, 0x7fff1f, FE),
|
||||
|
|
@ -5292,12 +5302,15 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr, void *data)
|
|||
struct kvm_pmu *kvm_pmu = (struct kvm_pmu *)data;
|
||||
u64 intel_ctrl = hybrid(cpuc->pmu, intel_ctrl);
|
||||
u64 pebs_mask = cpuc->pebs_enabled & x86_pmu.pebs_capable;
|
||||
int global_ctrl, pebs_enable;
|
||||
u64 guest_pebs_mask;
|
||||
int global_ctrl;
|
||||
|
||||
/*
|
||||
* In addition to obeying exclude_guest/exclude_host, remove bits being
|
||||
* used for PEBS when running a guest, because PEBS writes to virtual
|
||||
* addresses (not physical addresses).
|
||||
* addresses (not physical addresses). If the guest wants to utilize
|
||||
* PEBS, and PEBS can be safely enabled in the guest, bits for the guest's
|
||||
* PEBS-enabled counters will be OR'd back in as appropriate.
|
||||
*/
|
||||
*nr = 0;
|
||||
global_ctrl = (*nr)++;
|
||||
|
|
@ -5327,41 +5340,68 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr, void *data)
|
|||
return arr;
|
||||
}
|
||||
|
||||
if (!kvm_pmu || !x86_pmu.pebs_ept)
|
||||
/*
|
||||
* If the CPU doesn't support PEBS in the guest, then there's nothing
|
||||
* more to do as disabling PMCs via PERF_GLOBAL_CTRL is sufficient on
|
||||
* CPUs with guest/host isolation.
|
||||
*/
|
||||
if (!x86_pmu.pebs_ept)
|
||||
return arr;
|
||||
|
||||
/*
|
||||
* Restrict guest PEBS events to counters that (a) perf supports, (b)
|
||||
* the guest wants to use for PEBS, (c) are not excluded from counting
|
||||
* in the guest, and (d) _are_ excluded from counting in the host.
|
||||
*/
|
||||
guest_pebs_mask = pebs_mask & intel_ctrl & kvm_pmu->pebs_enable &
|
||||
~cpuc->intel_ctrl_host_mask &
|
||||
cpuc->intel_ctrl_guest_mask;
|
||||
|
||||
/*
|
||||
* Disable counters where the guest PMC is different than the host PMC
|
||||
* being used on behalf of the guest, as the PEBS record includes
|
||||
* PERF_GLOBAL_STATUS, i.e. the guest will see overflow status for the
|
||||
* wrong counter(s).
|
||||
*/
|
||||
guest_pebs_mask &= ~kvm_pmu->host_cross_mapped_mask;
|
||||
|
||||
/*
|
||||
* FIXME: Allow guest and host usage of PEBS events to co-exist instead
|
||||
* of disabling guest PEBS entirely if the host is using PEBS.
|
||||
* What exactly goes wrong if guest and host are using PEBS is
|
||||
* unknown.
|
||||
*/
|
||||
if (pebs_mask & ~cpuc->intel_ctrl_guest_mask)
|
||||
guest_pebs_mask = 0;
|
||||
|
||||
/*
|
||||
* Context switch DS_AREA and PEBS_DATA_CFG if and only if PEBS will be
|
||||
* active in the guest; if no records will be generated while the guest
|
||||
* is running, then simply keep the host values resident in hardware.
|
||||
*/
|
||||
arr[(*nr)++] = (struct perf_guest_switch_msr){
|
||||
.msr = MSR_IA32_DS_AREA,
|
||||
.host = (unsigned long)cpuc->ds,
|
||||
.guest = kvm_pmu->ds_area,
|
||||
.guest = guest_pebs_mask ? kvm_pmu->ds_area : (unsigned long)cpuc->ds,
|
||||
};
|
||||
|
||||
if (x86_pmu.intel_cap.pebs_baseline) {
|
||||
arr[(*nr)++] = (struct perf_guest_switch_msr){
|
||||
.msr = MSR_PEBS_DATA_CFG,
|
||||
.host = cpuc->active_pebs_data_cfg,
|
||||
.guest = kvm_pmu->pebs_data_cfg,
|
||||
.guest = guest_pebs_mask ? kvm_pmu->pebs_data_cfg :
|
||||
cpuc->active_pebs_data_cfg,
|
||||
};
|
||||
}
|
||||
|
||||
pebs_enable = (*nr)++;
|
||||
arr[pebs_enable] = (struct perf_guest_switch_msr){
|
||||
.msr = MSR_IA32_PEBS_ENABLE,
|
||||
.host = cpuc->pebs_enabled & ~cpuc->intel_ctrl_guest_mask,
|
||||
.guest = pebs_mask & ~cpuc->intel_ctrl_host_mask & kvm_pmu->pebs_enable,
|
||||
};
|
||||
|
||||
if (arr[pebs_enable].host) {
|
||||
/* Disable guest PEBS if host PEBS is enabled. */
|
||||
arr[pebs_enable].guest = 0;
|
||||
} else {
|
||||
/* Disable guest PEBS thoroughly for cross-mapped PEBS counters. */
|
||||
arr[pebs_enable].guest &= ~kvm_pmu->host_cross_mapped_mask;
|
||||
arr[global_ctrl].guest &= ~kvm_pmu->host_cross_mapped_mask;
|
||||
/* Set hw GLOBAL_CTRL bits for PEBS counter when it runs for guest */
|
||||
arr[global_ctrl].guest |= arr[pebs_enable].guest;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do NOT mess with PEBS_ENABLED. As above, disabling counters via
|
||||
* PERF_GLOBAL_CTRL is sufficient, and loading a stale PEBS_ENABLED,
|
||||
* e.g. on VM-Exit, can put the system in a bad state. Simply enable
|
||||
* counters in PERF_GLOBAL_CTRL, as perf load PEBS_ENABLED with the
|
||||
* full value, i.e. perf *also* relies on PERF_GLOBAL_CTRL.
|
||||
*/
|
||||
arr[global_ctrl].guest |= guest_pebs_mask;
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
|
@ -6561,6 +6601,8 @@ static void intel_pmu_filter(struct pmu *pmu, int cpu, bool *ret)
|
|||
|
||||
PMU_FORMAT_ATTR(offcore_rsp, "config1:0-63");
|
||||
|
||||
PMU_FORMAT_ATTR(offmodule_rsp, "config1:0-63");
|
||||
|
||||
PMU_FORMAT_ATTR(ldlat, "config1:0-15");
|
||||
|
||||
PMU_FORMAT_ATTR(frontend, "config1:0-23");
|
||||
|
|
@ -6609,6 +6651,20 @@ static struct attribute *skl_format_attr[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute *pnc_format_attr_rtm[] = {
|
||||
&format_attr_in_tx.attr,
|
||||
&format_attr_in_tx_cp.attr,
|
||||
&format_attr_offmodule_rsp.attr,
|
||||
&format_attr_ldlat.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute *pnc_format_attr[] = {
|
||||
&format_attr_offmodule_rsp.attr,
|
||||
&format_attr_ldlat.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static __initconst const struct x86_pmu core_pmu = {
|
||||
.name = "core",
|
||||
.handle_irq = x86_pmu_handle_irq,
|
||||
|
|
@ -7474,6 +7530,7 @@ static struct attribute *adl_hybrid_tsx_attrs[] = {
|
|||
FORMAT_ATTR_HYBRID(in_tx, hybrid_big);
|
||||
FORMAT_ATTR_HYBRID(in_tx_cp, hybrid_big);
|
||||
FORMAT_ATTR_HYBRID(offcore_rsp, hybrid_big_small_tiny);
|
||||
FORMAT_ATTR_HYBRID(offmodule_rsp, hybrid_big_small_tiny);
|
||||
FORMAT_ATTR_HYBRID(ldlat, hybrid_big_small_tiny);
|
||||
FORMAT_ATTR_HYBRID(frontend, hybrid_big);
|
||||
|
||||
|
|
@ -7512,6 +7569,23 @@ static struct attribute *mtl_hybrid_extra_attr[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static struct attribute *nvl_hybrid_extra_attr_rtm[] = {
|
||||
ADL_HYBRID_RTM_FORMAT_ATTR,
|
||||
FORMAT_HYBRID_PTR(offmodule_rsp),
|
||||
FORMAT_HYBRID_PTR(ldlat),
|
||||
FORMAT_HYBRID_PTR(frontend),
|
||||
FORMAT_HYBRID_PTR(snoop_rsp),
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute *nvl_hybrid_extra_attr[] = {
|
||||
FORMAT_HYBRID_PTR(offmodule_rsp),
|
||||
FORMAT_HYBRID_PTR(ldlat),
|
||||
FORMAT_HYBRID_PTR(frontend),
|
||||
FORMAT_HYBRID_PTR(snoop_rsp),
|
||||
NULL
|
||||
};
|
||||
|
||||
static bool is_attr_for_this_pmu(struct kobject *kobj, struct attribute *attr)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
|
|
@ -8549,6 +8623,8 @@ __init int intel_pmu_init(void)
|
|||
case INTEL_DIAMONDRAPIDS_X:
|
||||
intel_pmu_init_pnc(NULL);
|
||||
x86_pmu.pebs_latency_data = pnc_latency_data;
|
||||
extra_attr = boot_cpu_has(X86_FEATURE_RTM) ?
|
||||
pnc_format_attr_rtm : pnc_format_attr;
|
||||
|
||||
pr_cont("Panthercove events, ");
|
||||
name = "panthercove";
|
||||
|
|
@ -8557,13 +8633,12 @@ __init int intel_pmu_init(void)
|
|||
glc_common:
|
||||
intel_pmu_init_glc(NULL);
|
||||
intel_pmu_pebs_data_source_skl(true);
|
||||
|
||||
extra_attr = boot_cpu_has(X86_FEATURE_RTM) ?
|
||||
hsw_format_attr : nhm_format_attr;
|
||||
glc_base:
|
||||
x86_pmu.pebs_ept = 1;
|
||||
x86_pmu.hw_config = hsw_hw_config;
|
||||
x86_pmu.get_event_constraints = glc_get_event_constraints;
|
||||
extra_attr = boot_cpu_has(X86_FEATURE_RTM) ?
|
||||
hsw_format_attr : nhm_format_attr;
|
||||
extra_skl_attr = skl_format_attr;
|
||||
mem_attr = glc_events_attrs;
|
||||
td_attr = glc_td_events_attrs;
|
||||
|
|
@ -8785,7 +8860,7 @@ __init int intel_pmu_init(void)
|
|||
mem_attr = mtl_hybrid_mem_attrs;
|
||||
tsx_attr = adl_hybrid_tsx_attrs;
|
||||
extra_attr = boot_cpu_has(X86_FEATURE_RTM) ?
|
||||
mtl_hybrid_extra_attr_rtm : mtl_hybrid_extra_attr;
|
||||
nvl_hybrid_extra_attr_rtm : nvl_hybrid_extra_attr;
|
||||
|
||||
/* Initialize big core specific PerfMon capabilities.*/
|
||||
pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];
|
||||
|
|
@ -8794,8 +8869,6 @@ __init int intel_pmu_init(void)
|
|||
/* Initialize Atom core specific PerfMon capabilities.*/
|
||||
pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];
|
||||
intel_pmu_init_arw(&pmu->pmu);
|
||||
|
||||
intel_pmu_pebs_data_source_lnl();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -277,8 +277,8 @@ static u64 pnc_pebs_l2_hit_data_source[PNC_PEBS_DATA_SOURCE_MAX] = {
|
|||
0, /* 0x06: Reserved */
|
||||
OP_LH | P(LVL, L2) | LEVEL(L2) | P(SNOOP, HIT), /* 0x07: L2 Hit Snoop HIT */
|
||||
OP_LH | P(LVL, L2) | LEVEL(L2) | P(SNOOP, HITM), /* 0x08: L2 Hit Snoop Hit Modified */
|
||||
OP_LH | P(LVL, L2) | LEVEL(L2) | P(SNOOP, MISS), /* 0x09: Prefetch Promotion */
|
||||
OP_LH | P(LVL, L2) | LEVEL(L2) | P(SNOOP, MISS), /* 0x0a: Cross Core Prefetch Promotion */
|
||||
OP_LH | P(LVL, L2) | LEVEL(L2) | P(SNOOP, NONE), /* 0x09: Prefetch Promotion */
|
||||
OP_LH | P(LVL, L2) | LEVEL(L2) | P(SNOOP, NONE), /* 0x0a: Cross Core Prefetch Promotion */
|
||||
0, /* 0x0b: Reserved */
|
||||
0, /* 0x0c: Reserved */
|
||||
0, /* 0x0d: Reserved */
|
||||
|
|
@ -455,6 +455,7 @@ static inline void pebs_set_tlb_lock(u64 *val, bool tlb, bool lock)
|
|||
static u64 __grt_latency_data(struct perf_event *event, u64 status,
|
||||
u8 dse, bool tlb, bool lock, bool blk)
|
||||
{
|
||||
union perf_mem_data_src src;
|
||||
u64 val;
|
||||
|
||||
WARN_ON_ONCE(is_hybrid() &&
|
||||
|
|
@ -470,7 +471,16 @@ static u64 __grt_latency_data(struct perf_event *event, u64 status,
|
|||
else
|
||||
val |= P(BLK, NA);
|
||||
|
||||
return val;
|
||||
src.val = val;
|
||||
|
||||
if (event->hw.flags &
|
||||
(PERF_X86_EVENT_PEBS_LDLAT | PERF_X86_EVENT_PEBS_LD_HSW))
|
||||
src.mem_op = P(OP, LOAD);
|
||||
if (event->hw.flags &
|
||||
(PERF_X86_EVENT_PEBS_STLAT | PERF_X86_EVENT_PEBS_ST_HSW))
|
||||
src.mem_op = P(OP, STORE);
|
||||
|
||||
return src.val;
|
||||
}
|
||||
|
||||
u64 grt_latency_data(struct perf_event *event, u64 status)
|
||||
|
|
@ -528,7 +538,11 @@ static u64 arw_latency_data(struct perf_event *event, u64 status)
|
|||
val |= P(BLK, NA);
|
||||
|
||||
src.val = val;
|
||||
if (event->hw.flags & PERF_X86_EVENT_PEBS_ST_HSW)
|
||||
if (event->hw.flags &
|
||||
(PERF_X86_EVENT_PEBS_LDLAT | PERF_X86_EVENT_PEBS_LD_HSW))
|
||||
src.mem_op = P(OP, LOAD);
|
||||
if (event->hw.flags &
|
||||
(PERF_X86_EVENT_PEBS_STLAT | PERF_X86_EVENT_PEBS_ST_HSW))
|
||||
src.mem_op = P(OP, STORE);
|
||||
|
||||
return src.val;
|
||||
|
|
@ -563,7 +577,11 @@ static u64 lnc_latency_data(struct perf_event *event, u64 status)
|
|||
val |= P(BLK, NA);
|
||||
|
||||
src.val = val;
|
||||
if (event->hw.flags & PERF_X86_EVENT_PEBS_ST_HSW)
|
||||
if (event->hw.flags &
|
||||
(PERF_X86_EVENT_PEBS_LDLAT | PERF_X86_EVENT_PEBS_LD_HSW))
|
||||
src.mem_op = P(OP, LOAD);
|
||||
if (event->hw.flags &
|
||||
(PERF_X86_EVENT_PEBS_STLAT | PERF_X86_EVENT_PEBS_ST_HSW))
|
||||
src.mem_op = P(OP, STORE);
|
||||
|
||||
return src.val;
|
||||
|
|
@ -621,7 +639,11 @@ u64 pnc_latency_data(struct perf_event *event, u64 status)
|
|||
val |= P(BLK, NA);
|
||||
|
||||
src.val = val;
|
||||
if (event->hw.flags & PERF_X86_EVENT_PEBS_ST_HSW)
|
||||
if (event->hw.flags &
|
||||
(PERF_X86_EVENT_PEBS_LDLAT | PERF_X86_EVENT_PEBS_LD_HSW))
|
||||
src.mem_op = P(OP, LOAD);
|
||||
if (event->hw.flags &
|
||||
(PERF_X86_EVENT_PEBS_STLAT | PERF_X86_EVENT_PEBS_ST_HSW))
|
||||
src.mem_op = P(OP, STORE);
|
||||
|
||||
return src.val;
|
||||
|
|
@ -1291,22 +1313,22 @@ struct event_constraint intel_glm_pebs_event_constraints[] = {
|
|||
|
||||
struct event_constraint intel_grt_pebs_event_constraints[] = {
|
||||
/* Allow all events as PEBS with no flags */
|
||||
INTEL_HYBRID_LAT_CONSTRAINT(0x5d0, 0x3),
|
||||
INTEL_HYBRID_LAT_CONSTRAINT(0x6d0, 0x3f),
|
||||
INTEL_HYBRID_LDLAT_CONSTRAINT(0x5d0, 0x3),
|
||||
INTEL_HYBRID_STLAT_CONSTRAINT(0x6d0, 0x3f),
|
||||
EVENT_CONSTRAINT_END
|
||||
};
|
||||
|
||||
struct event_constraint intel_cmt_pebs_event_constraints[] = {
|
||||
/* Allow all events as PEBS with no flags */
|
||||
INTEL_HYBRID_LAT_CONSTRAINT(0x5d0, 0x3),
|
||||
INTEL_HYBRID_LAT_CONSTRAINT(0x6d0, 0xff),
|
||||
INTEL_HYBRID_LDLAT_CONSTRAINT(0x5d0, 0x3),
|
||||
INTEL_HYBRID_STLAT_CONSTRAINT(0x6d0, 0xff),
|
||||
EVENT_CONSTRAINT_END
|
||||
};
|
||||
|
||||
struct event_constraint intel_dkt_pebs_event_constraints[] = {
|
||||
/* Allow all events as PEBS with no flags */
|
||||
INTEL_HYBRID_LAT_CONSTRAINT(0x5d0, 0xff),
|
||||
INTEL_HYBRID_LAT_CONSTRAINT(0x6d0, 0xff),
|
||||
INTEL_HYBRID_LDLAT_CONSTRAINT(0x5d0, 0xff),
|
||||
INTEL_HYBRID_STLAT_CONSTRAINT(0x6d0, 0xff),
|
||||
EVENT_CONSTRAINT_END
|
||||
};
|
||||
|
||||
|
|
@ -1496,24 +1518,8 @@ struct event_constraint intel_lnc_pebs_event_constraints[] = {
|
|||
INTEL_FLAGS_UEVENT_CONSTRAINT(0x012a, 0x1), /* OCR.* events */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT(0x012b, 0x1), /* OCR.* events */
|
||||
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT(0x04a4, 0x1), /* TOPDOWN.BAD_SPEC_SLOTS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT(0x08a4, 0x1), /* TOPDOWN.BR_MISPREDICT_SLOTS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT(0x10a4, 0x8), /* TOPDOWN.MEMORY_BOUND_SLOTS */
|
||||
|
||||
INTEL_HYBRID_LDLAT_CONSTRAINT(0x1cd, 0x3fc),
|
||||
INTEL_HYBRID_STLAT_CONSTRAINT(0x2cd, 0x3),
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x11d0, 0xf), /* MEM_INST_RETIRED.STLB_MISS_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x12d0, 0xf), /* MEM_INST_RETIRED.STLB_MISS_STORES */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x21d0, 0xf), /* MEM_INST_RETIRED.LOCK_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x41d0, 0xf), /* MEM_INST_RETIRED.SPLIT_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x42d0, 0xf), /* MEM_INST_RETIRED.SPLIT_STORES */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x81d0, 0xf), /* MEM_INST_RETIRED.ALL_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x82d0, 0xf), /* MEM_INST_RETIRED.ALL_STORES */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT(0x87d0, 0x3ff), /* MEM_INST_RETIRED.ANY */
|
||||
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD_RANGE(0xd1, 0xd4, 0xf),
|
||||
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT(0xd0, 0xf),
|
||||
|
||||
/*
|
||||
* Everything else is handled by PMU_FL_PEBS_ALL, because we
|
||||
|
|
@ -1526,18 +1532,6 @@ struct event_constraint intel_lnc_pebs_event_constraints[] = {
|
|||
struct event_constraint intel_pnc_pebs_event_constraints[] = {
|
||||
INTEL_HYBRID_LDLAT_CONSTRAINT(0x1cd, 0xfc),
|
||||
INTEL_HYBRID_STLAT_CONSTRAINT(0x2cd, 0x3),
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x11d0, 0xf), /* MEM_INST_RETIRED.STLB_MISS_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x12d0, 0xf), /* MEM_INST_RETIRED.STLB_MISS_STORES */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x21d0, 0xf), /* MEM_INST_RETIRED.LOCK_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x41d0, 0xf), /* MEM_INST_RETIRED.SPLIT_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x42d0, 0xf), /* MEM_INST_RETIRED.SPLIT_STORES */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x81d0, 0xf), /* MEM_INST_RETIRED.ALL_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x82d0, 0xf), /* MEM_INST_RETIRED.ALL_STORES */
|
||||
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD_RANGE(0xd1, 0xd4, 0xf),
|
||||
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT(0xd0, 0xf),
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT(0xd6, 0xf),
|
||||
|
||||
/*
|
||||
* Everything else is handled by PMU_FL_PEBS_ALL, because we
|
||||
|
|
|
|||
|
|
@ -756,10 +756,10 @@ void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc)
|
|||
|
||||
rdmsrq(x86_pmu.lbr_from + lbr_idx, msr_lastbranch.lbr);
|
||||
|
||||
perf_clear_branch_entry_bitfields(br);
|
||||
|
||||
br->from = msr_lastbranch.from;
|
||||
br->to = msr_lastbranch.to;
|
||||
*br = (struct perf_branch_entry){
|
||||
.from = msr_lastbranch.from,
|
||||
.to = msr_lastbranch.to,
|
||||
};
|
||||
br++;
|
||||
}
|
||||
cpuc->lbr_stack.nr = i;
|
||||
|
|
@ -847,14 +847,15 @@ void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc)
|
|||
if (abort && x86_pmu.lbr_double_abort && out > 0)
|
||||
out--;
|
||||
|
||||
perf_clear_branch_entry_bitfields(br+out);
|
||||
br[out].from = from;
|
||||
br[out].to = to;
|
||||
br[out].mispred = mis;
|
||||
br[out].predicted = pred;
|
||||
br[out].in_tx = in_tx;
|
||||
br[out].abort = abort;
|
||||
br[out].cycles = cycles;
|
||||
br[out] = (struct perf_branch_entry){
|
||||
.from = from,
|
||||
.to = to,
|
||||
.mispred = mis,
|
||||
.predicted = pred,
|
||||
.in_tx = in_tx,
|
||||
.abort = abort,
|
||||
.cycles = cycles,
|
||||
};
|
||||
out++;
|
||||
}
|
||||
cpuc->lbr_stack.nr = out;
|
||||
|
|
@ -905,6 +906,7 @@ static void intel_pmu_store_lbr(struct cpu_hw_events *cpuc,
|
|||
struct perf_branch_entry *e;
|
||||
struct lbr_entry *lbr;
|
||||
u64 from, to, info;
|
||||
bool mispred;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < x86_pmu.lbr_nr; i++) {
|
||||
|
|
@ -921,24 +923,27 @@ static void intel_pmu_store_lbr(struct cpu_hw_events *cpuc,
|
|||
to = rdlbr_to(i, lbr);
|
||||
info = rdlbr_info(i, lbr);
|
||||
|
||||
perf_clear_branch_entry_bitfields(e);
|
||||
mispred = get_lbr_mispred(info);
|
||||
|
||||
e->from = from;
|
||||
e->to = to;
|
||||
e->mispred = get_lbr_mispred(info);
|
||||
e->predicted = !e->mispred;
|
||||
e->in_tx = !!(info & LBR_INFO_IN_TX);
|
||||
e->abort = !!(info & LBR_INFO_ABORT);
|
||||
e->cycles = get_lbr_cycles(info);
|
||||
e->type = get_lbr_br_type(info);
|
||||
|
||||
/*
|
||||
* Leverage the reserved field of cpuc->lbr_entries[i] to
|
||||
* temporarily store the branch counters information.
|
||||
* The later code will decide what content can be disclosed
|
||||
* to the perf tool. Pleae see intel_pmu_lbr_counters_reorder().
|
||||
*/
|
||||
e->reserved = (info >> LBR_INFO_BR_CNTR_OFFSET) & LBR_INFO_BR_CNTR_FULL_MASK;
|
||||
*e = (struct perf_branch_entry){
|
||||
.from = from,
|
||||
.to = to,
|
||||
.mispred = mispred,
|
||||
.predicted = !mispred,
|
||||
.in_tx = !!(info & LBR_INFO_IN_TX),
|
||||
.abort = !!(info & LBR_INFO_ABORT),
|
||||
.cycles = get_lbr_cycles(info),
|
||||
.type = get_lbr_br_type(info),
|
||||
/*
|
||||
* Leverage the reserved field of
|
||||
* cpuc->lbr_entries[i] to temporarily store the
|
||||
* branch counters information. The later code will
|
||||
* decide what content can be disclosed to the perf
|
||||
* tool. Pleae see intel_pmu_lbr_counters_reorder().
|
||||
*/
|
||||
.reserved = (info >> LBR_INFO_BR_CNTR_OFFSET) &
|
||||
LBR_INFO_BR_CNTR_FULL_MASK,
|
||||
};
|
||||
}
|
||||
|
||||
cpuc->lbr_stack.nr = i;
|
||||
|
|
|
|||
|
|
@ -517,10 +517,6 @@ struct cpu_hw_events {
|
|||
__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_ST)
|
||||
|
||||
#define INTEL_HYBRID_LAT_CONSTRAINT(c, n) \
|
||||
__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LAT_HYBRID)
|
||||
|
||||
#define INTEL_HYBRID_LDLAT_CONSTRAINT(c, n) \
|
||||
__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LAT_HYBRID|PERF_X86_EVENT_PEBS_LD_HSW)
|
||||
|
|
|
|||
|
|
@ -2108,6 +2108,9 @@ bool filter_mce(struct mce *m)
|
|||
static __always_inline void exc_machine_check_kernel(struct pt_regs *regs)
|
||||
{
|
||||
irqentry_state_t irq_state;
|
||||
unsigned long dr7;
|
||||
|
||||
dr7 = local_db_save();
|
||||
|
||||
WARN_ON_ONCE(user_mode(regs));
|
||||
|
||||
|
|
@ -2116,20 +2119,26 @@ static __always_inline void exc_machine_check_kernel(struct pt_regs *regs)
|
|||
* mce_check_crashing_cpu() for details.
|
||||
*/
|
||||
if (mca_cfg.initialized && mce_check_crashing_cpu())
|
||||
return;
|
||||
goto out;
|
||||
|
||||
irq_state = irqentry_nmi_enter(regs);
|
||||
|
||||
do_machine_check(regs);
|
||||
|
||||
irqentry_nmi_exit(regs, irq_state);
|
||||
out:
|
||||
local_db_restore(dr7);
|
||||
}
|
||||
|
||||
static __always_inline void exc_machine_check_user(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long dr7;
|
||||
|
||||
irqentry_enter_from_user_mode(regs);
|
||||
|
||||
dr7 = local_db_save();
|
||||
do_machine_check(regs);
|
||||
local_db_restore(dr7);
|
||||
|
||||
irqentry_exit_to_user_mode(regs);
|
||||
}
|
||||
|
|
@ -2138,21 +2147,13 @@ static __always_inline void exc_machine_check_user(struct pt_regs *regs)
|
|||
/* MCE hit kernel mode */
|
||||
DEFINE_IDTENTRY_MCE(exc_machine_check)
|
||||
{
|
||||
unsigned long dr7;
|
||||
|
||||
dr7 = local_db_save();
|
||||
exc_machine_check_kernel(regs);
|
||||
local_db_restore(dr7);
|
||||
}
|
||||
|
||||
/* The user mode variant. */
|
||||
DEFINE_IDTENTRY_MCE_USER(exc_machine_check)
|
||||
{
|
||||
unsigned long dr7;
|
||||
|
||||
dr7 = local_db_save();
|
||||
exc_machine_check_user(regs);
|
||||
local_db_restore(dr7);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_FRED
|
||||
|
|
@ -2169,28 +2170,20 @@ DEFINE_IDTENTRY_MCE_USER(exc_machine_check)
|
|||
*/
|
||||
DEFINE_FREDENTRY_MCE(exc_machine_check)
|
||||
{
|
||||
unsigned long dr7;
|
||||
|
||||
dr7 = local_db_save();
|
||||
if (user_mode(regs))
|
||||
exc_machine_check_user(regs);
|
||||
else
|
||||
exc_machine_check_kernel(regs);
|
||||
local_db_restore(dr7);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
/* 32bit unified entry point */
|
||||
DEFINE_IDTENTRY_RAW(exc_machine_check)
|
||||
{
|
||||
unsigned long dr7;
|
||||
|
||||
dr7 = local_db_save();
|
||||
if (user_mode(regs))
|
||||
exc_machine_check_user(regs);
|
||||
else
|
||||
exc_machine_check_kernel(regs);
|
||||
local_db_restore(dr7);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -5058,7 +5058,7 @@ static int kvm_tdp_page_prefault(struct kvm_vcpu *vcpu, gpa_t gpa,
|
|||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
|
||||
if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu))
|
||||
if (kvm_test_request(KVM_REQ_VM_DEAD, vcpu))
|
||||
return -EIO;
|
||||
|
||||
cond_resched();
|
||||
|
|
|
|||
|
|
@ -823,14 +823,6 @@ void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu)
|
|||
|
||||
bool kvm_pmu_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
|
||||
{
|
||||
switch (msr) {
|
||||
case MSR_CORE_PERF_GLOBAL_STATUS:
|
||||
case MSR_CORE_PERF_GLOBAL_CTRL:
|
||||
case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
|
||||
return kvm_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return kvm_pmu_call(msr_idx_to_pmc)(vcpu, msr) ||
|
||||
kvm_pmu_call(is_valid_msr)(vcpu, msr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2125,13 +2125,8 @@ static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!nested_svm_merge_msrpm(vcpu)) {
|
||||
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
||||
vcpu->run->internal.suberror =
|
||||
KVM_INTERNAL_ERROR_EMULATION;
|
||||
vcpu->run->internal.ndata = 0;
|
||||
if (!nested_svm_merge_msrpm(vcpu))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kvm_hv_verify_vp_assist(vcpu))
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -2048,6 +2048,12 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm)
|
|||
src->pages_locked = 0;
|
||||
src->es_active = false;
|
||||
|
||||
/*
|
||||
* Do cache maintenance on the source VM as it is no longer an SEV VM,
|
||||
* i.e. memory reclaim flows won't trigger cache maintenance on the VM.
|
||||
*/
|
||||
sev_writeback_caches(src_kvm);
|
||||
|
||||
list_cut_before(&dst->regions_list, &src->regions_list, &src->regions_list);
|
||||
|
||||
mutex_lock(&sev_mirror_lock);
|
||||
|
|
@ -2187,6 +2193,10 @@ int sev_vm_move_enc_context_from(struct kvm *kvm, unsigned int source_fd)
|
|||
* the set of CPUs from the source. If a CPU was used to run a vCPU in
|
||||
* the source VM but is never used for the destination VM, then the CPU
|
||||
* can only have cached memory that was accessible to the source VM.
|
||||
* Furthermore, KVM *must* perform cache maintenance on the source VM,
|
||||
* as the source VM may have access to memory that the destination VM
|
||||
* does not, i.e. KVM could skip flushes if memory is reclaimed from
|
||||
* the old VM but not the new VM.
|
||||
*/
|
||||
if (!zalloc_cpumask_var(&dst_sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) {
|
||||
ret = -ENOMEM;
|
||||
|
|
@ -2980,13 +2990,17 @@ void sev_vm_destroy(struct kvm *kvm)
|
|||
struct list_head *head = &sev->regions_list;
|
||||
struct list_head *pos, *q;
|
||||
|
||||
/*
|
||||
* Free the mask even if the VM is not *currently* an SEV VM, as it may
|
||||
* have been an SEV VM prior to intra-host migration.
|
||||
*/
|
||||
free_cpumask_var(sev->have_run_cpus);
|
||||
|
||||
if (!sev_guest(kvm))
|
||||
return;
|
||||
|
||||
WARN_ON(!list_empty(&sev->mirror_vms));
|
||||
|
||||
free_cpumask_var(sev->have_run_cpus);
|
||||
|
||||
/*
|
||||
* If this is a mirror VM, remove it from the owner's list of a mirrors
|
||||
* and skip ASID cleanup (the ASID is tied to the lifetime of the owner).
|
||||
|
|
|
|||
|
|
@ -3465,10 +3465,6 @@ static bool nested_get_vmcs12_pages(struct kvm_vcpu *vcpu)
|
|||
} else {
|
||||
pr_debug_ratelimited("%s: no backing for APIC-access address in vmcs12\n",
|
||||
__func__);
|
||||
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
||||
vcpu->run->internal.suberror =
|
||||
KVM_INTERNAL_ERROR_EMULATION;
|
||||
vcpu->run->internal.ndata = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -3539,11 +3535,6 @@ static bool vmx_get_nested_state_pages(struct kvm_vcpu *vcpu)
|
|||
if (!nested_get_evmcs_page(vcpu)) {
|
||||
pr_debug_ratelimited("%s: enlightened vmptrld failed\n",
|
||||
__func__);
|
||||
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
||||
vcpu->run->internal.suberror =
|
||||
KVM_INTERNAL_ERROR_EMULATION;
|
||||
vcpu->run->internal.ndata = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -3915,8 +3906,12 @@ static int nested_vmx_run(struct kvm_vcpu *vcpu, bool launch)
|
|||
|
||||
vmentry_failed:
|
||||
vcpu->arch.nested_run_pending = 0;
|
||||
if (status == NVMX_VMENTRY_KVM_INTERNAL_ERROR)
|
||||
if (status == NVMX_VMENTRY_KVM_INTERNAL_ERROR) {
|
||||
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
||||
vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
|
||||
vcpu->run->internal.ndata = 0;
|
||||
return 0;
|
||||
}
|
||||
if (status == NVMX_VMENTRY_VMEXIT)
|
||||
return 1;
|
||||
WARN_ON_ONCE(status != NVMX_VMENTRY_VMFAIL);
|
||||
|
|
|
|||
|
|
@ -187,6 +187,9 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
|
|||
int ret;
|
||||
|
||||
switch (msr) {
|
||||
case MSR_CORE_PERF_GLOBAL_STATUS:
|
||||
case MSR_CORE_PERF_GLOBAL_CTRL:
|
||||
case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
|
||||
case MSR_CORE_PERF_FIXED_CTR_CTRL:
|
||||
return kvm_pmu_has_perf_global_ctrl(pmu);
|
||||
case MSR_IA32_PEBS_ENABLE:
|
||||
|
|
|
|||
|
|
@ -1995,7 +1995,7 @@ static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu)
|
|||
if (kvm_vcpu_has_events(vcpu) || signal_pending(current))
|
||||
break;
|
||||
|
||||
if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu)) {
|
||||
if (kvm_test_request(KVM_REQ_VM_DEAD, vcpu)) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8060,7 +8060,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
|
|||
bool req_immediate_exit = false;
|
||||
|
||||
if (kvm_request_pending(vcpu)) {
|
||||
if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu)) {
|
||||
if (kvm_test_request(KVM_REQ_VM_DEAD, vcpu)) {
|
||||
r = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -8072,6 +8072,10 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
|
|||
|
||||
if (kvm_check_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu)) {
|
||||
if (unlikely(!kvm_nested_call(get_nested_state_pages)(vcpu))) {
|
||||
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
||||
vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
|
||||
vcpu->run->internal.ndata = 0;
|
||||
kvm_make_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu);
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -886,6 +886,105 @@ static void quirk_clear_strap_no_soft_reset_dev2_f0(struct pci_dev *dev)
|
|||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x15b8, quirk_clear_strap_no_soft_reset_dev2_f0);
|
||||
|
||||
/*
|
||||
* Enhanced atomic operations can cause corruption with 64-bit DMA
|
||||
* on these devices.
|
||||
*/
|
||||
#define RX_ENH_ATOMIC_EN BIT(8)
|
||||
|
||||
static const u32 nbio_7_7_pcie_smn_addrs[] = {
|
||||
0x111401d0,
|
||||
0x111411d0,
|
||||
0x111421d0,
|
||||
0x111431d0,
|
||||
0x111441d0,
|
||||
0x112401d0,
|
||||
0x112411d0,
|
||||
0x112421d0,
|
||||
0x112431d0,
|
||||
0x112441d0,
|
||||
0x112451d0,
|
||||
0x113401d0,
|
||||
0x114401d0,
|
||||
};
|
||||
|
||||
static const u32 nbio_7_11_pcie_smn_addrs[] = {
|
||||
0x112401d0,
|
||||
0x112411d0,
|
||||
0x112421d0,
|
||||
0x112431d0,
|
||||
0x112441d0,
|
||||
0x112451d0,
|
||||
0x113401d0,
|
||||
0x113411d0,
|
||||
0x113421d0,
|
||||
0x113431d0,
|
||||
0x113441d0,
|
||||
0x113451d0,
|
||||
};
|
||||
|
||||
static void quirk_amd_nbio_enhanced_atomic(struct pci_dev *host_bridge,
|
||||
const u32 *smn_addrs,
|
||||
size_t nr_smn_addrs)
|
||||
{
|
||||
bool changed = false;
|
||||
size_t i;
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < nr_smn_addrs; i++) {
|
||||
ret = amd_smn_read(0, smn_addrs[i], &data);
|
||||
if (ret)
|
||||
continue;
|
||||
if (!(data & RX_ENH_ATOMIC_EN))
|
||||
continue;
|
||||
data = data & ~RX_ENH_ATOMIC_EN;
|
||||
ret = amd_smn_write(0, smn_addrs[i], data);
|
||||
if (ret)
|
||||
continue;
|
||||
if (changed)
|
||||
continue;
|
||||
ret = amd_smn_read(0, smn_addrs[i], &data);
|
||||
if (ret)
|
||||
continue;
|
||||
if (data & RX_ENH_ATOMIC_EN)
|
||||
continue;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
pci_info(host_bridge, "enhanced atomics disabled\n");
|
||||
}
|
||||
|
||||
static void quirk_amd_nbio_7_7_disable_enhanced_atomic(struct pci_dev *dev)
|
||||
{
|
||||
quirk_amd_nbio_enhanced_atomic(dev, nbio_7_7_pcie_smn_addrs,
|
||||
ARRAY_SIZE(nbio_7_7_pcie_smn_addrs));
|
||||
}
|
||||
|
||||
static void quirk_amd_nbio_7_11_disable_enhanced_atomic(struct pci_dev *dev)
|
||||
{
|
||||
quirk_amd_nbio_enhanced_atomic(dev, nbio_7_11_pcie_smn_addrs,
|
||||
ARRAY_SIZE(nbio_7_11_pcie_smn_addrs));
|
||||
}
|
||||
|
||||
/* Phoenix, Hawk Point (NBIO 7.7) */
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x14E8,
|
||||
quirk_amd_nbio_7_7_disable_enhanced_atomic);
|
||||
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x14E8,
|
||||
quirk_amd_nbio_7_7_disable_enhanced_atomic);
|
||||
|
||||
/* Strix, Krackan, Strix Halo (NBIO 7.11) */
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x1507,
|
||||
quirk_amd_nbio_7_11_disable_enhanced_atomic);
|
||||
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x1507,
|
||||
quirk_amd_nbio_7_11_disable_enhanced_atomic);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x1122,
|
||||
quirk_amd_nbio_7_11_disable_enhanced_atomic);
|
||||
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x1122,
|
||||
quirk_amd_nbio_7_11_disable_enhanced_atomic);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -2018,12 +2018,17 @@ static int disk_revalidate_zone_resources(struct gendisk *disk,
|
|||
struct blk_revalidate_zone_args *args)
|
||||
{
|
||||
struct queue_limits *lim = &disk->queue->limits;
|
||||
unsigned long long nr_zones;
|
||||
unsigned int pool_size;
|
||||
int ret = 0;
|
||||
|
||||
args->disk = disk;
|
||||
args->nr_zones =
|
||||
DIV_ROUND_UP_ULL(get_capacity(disk), lim->chunk_sectors);
|
||||
nr_zones = DIV_ROUND_UP_ULL(get_capacity(disk), lim->chunk_sectors);
|
||||
if (nr_zones > UINT_MAX) {
|
||||
pr_warn("%s: Too many zones (%llu)\n", disk->disk_name, nr_zones);
|
||||
return -EINVAL;
|
||||
}
|
||||
args->nr_zones = nr_zones;
|
||||
|
||||
/* Cached zone conditions: 1 byte per zone */
|
||||
args->zones_cond = kzalloc(args->nr_zones, GFP_NOIO);
|
||||
|
|
@ -2131,6 +2136,12 @@ static int blk_revalidate_zone_cond(struct blk_zone *zone, unsigned int idx,
|
|||
{
|
||||
enum blk_zone_cond cond = zone->cond;
|
||||
|
||||
if (idx >= args->nr_zones) {
|
||||
pr_warn("%s: Zone report index %u exceeds zone count %u\n",
|
||||
args->disk->disk_name, idx, args->nr_zones);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check that the zone condition is consistent with the zone type. */
|
||||
switch (cond) {
|
||||
case BLK_ZONE_COND_NOT_WP:
|
||||
|
|
|
|||
|
|
@ -515,6 +515,7 @@ void ivpu_prepare_for_reset(struct ivpu_device *vdev)
|
|||
{
|
||||
ivpu_hw_irq_disable(vdev);
|
||||
disable_irq(vdev->irq);
|
||||
atomic_set(&vdev->job_timeout_detected, 0);
|
||||
flush_work(&vdev->irq_dct_work);
|
||||
flush_work(&vdev->context_abort_work);
|
||||
flush_work(&vdev->job_destroy_work);
|
||||
|
|
@ -710,7 +711,7 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
|
|||
vdev->context_xa_limit.max = IVPU_USER_CONTEXT_MAX_SSID;
|
||||
atomic64_set(&vdev->unique_id_counter, 0);
|
||||
atomic_set(&vdev->job_timeout_counter, 0);
|
||||
atomic_set(&vdev->faults_detected, 0);
|
||||
atomic_set(&vdev->job_timeout_detected, 0);
|
||||
xa_init_flags(&vdev->context_xa, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ);
|
||||
xa_init_flags(&vdev->submitted_jobs_xa, XA_FLAGS_ALLOC1);
|
||||
xa_init_flags(&vdev->db_xa, XA_FLAGS_ALLOC1);
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ struct ivpu_device {
|
|||
struct xarray submitted_jobs_xa;
|
||||
struct ivpu_ipc_consumer job_done_consumer;
|
||||
atomic_t job_timeout_counter;
|
||||
atomic_t faults_detected;
|
||||
atomic_t job_timeout_detected;
|
||||
|
||||
atomic64_t unique_id_counter;
|
||||
|
||||
|
|
|
|||
|
|
@ -621,7 +621,6 @@ bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_
|
|||
* status and ensure both are handled in the same way
|
||||
*/
|
||||
job->file_priv->has_mmu_faults = true;
|
||||
atomic_set(&vdev->faults_detected, 1);
|
||||
queue_work(system_percpu_wq, &vdev->context_abort_work);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1175,10 +1174,10 @@ static int reset_engine_and_mark_faulty_contexts(struct ivpu_device *vdev)
|
|||
return ret;
|
||||
|
||||
/*
|
||||
* If faults are detected, ignore guilty contexts from engine reset as NPU may not be stuck
|
||||
* and could return currently running good context and faulty contexts are already marked
|
||||
* If job timeout is detected, read guilty context from engine reset, for other reasons
|
||||
* faulty context is already known
|
||||
*/
|
||||
if (atomic_cmpxchg(&vdev->faults_detected, 1, 0) == 1)
|
||||
if (atomic_cmpxchg(&vdev->job_timeout_detected, 1, 0) == 0)
|
||||
return 0;
|
||||
|
||||
num_impacted_contexts = resp.payload.engine_reset_done.num_impacted_contexts;
|
||||
|
|
|
|||
|
|
@ -964,7 +964,6 @@ void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev)
|
|||
file_priv = xa_load(&vdev->context_xa, ssid);
|
||||
if (file_priv) {
|
||||
if (!READ_ONCE(file_priv->has_mmu_faults)) {
|
||||
atomic_set(&vdev->faults_detected, 1);
|
||||
ivpu_mmu_dump_event(vdev, event);
|
||||
WRITE_ONCE(file_priv->has_mmu_faults, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,6 +229,7 @@ static void ivpu_job_timeout_work(struct work_struct *work)
|
|||
|
||||
ivpu_jsm_state_dump(vdev);
|
||||
ivpu_dev_coredump(vdev);
|
||||
atomic_set(&vdev->job_timeout_detected, 1);
|
||||
queue_work(system_percpu_wq, &vdev->context_abort_work);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2972,9 +2972,10 @@ static void ata_dev_config_lpm(struct ata_device *dev)
|
|||
(dev->id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2)
|
||||
dev->quirks |= ATA_QUIRK_NOLPM;
|
||||
|
||||
/* ATI specific quirk */
|
||||
if ((dev->quirks & ATA_QUIRK_NO_LPM_ON_ATI) &&
|
||||
ata_dev_check_adapter(dev, PCI_VENDOR_ID_ATI))
|
||||
/* ATI and AMD specific quirk */
|
||||
if ((dev->quirks & ATA_QUIRK_NO_LPM_ON_ATI_AND_AMD) &&
|
||||
(ata_dev_check_adapter(dev, PCI_VENDOR_ID_ATI) ||
|
||||
ata_dev_check_adapter(dev, PCI_VENDOR_ID_AMD)))
|
||||
dev->quirks |= ATA_QUIRK_NOLPM;
|
||||
}
|
||||
|
||||
|
|
@ -4136,7 +4137,7 @@ static const char * const ata_quirk_names[] = {
|
|||
[__ATA_QUIRK_MAX_SEC] = "maxsec",
|
||||
[__ATA_QUIRK_MAX_TRIM_128M] = "maxtrim128m",
|
||||
[__ATA_QUIRK_NO_NCQ_ON_ATI] = "noncqonati",
|
||||
[__ATA_QUIRK_NO_LPM_ON_ATI] = "nolpmonati",
|
||||
[__ATA_QUIRK_NO_LPM_ON_ATI_AND_AMD] = "nolpmonatiandamd",
|
||||
[__ATA_QUIRK_NO_ID_DEV_LOG] = "noiddevlog",
|
||||
[__ATA_QUIRK_NO_LOG_DIR] = "nologdir",
|
||||
[__ATA_QUIRK_NO_FUA] = "nofua",
|
||||
|
|
@ -4420,15 +4421,15 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = {
|
|||
{ "Samsung SSD 860*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
|
||||
ATA_QUIRK_ZERO_AFTER_TRIM |
|
||||
ATA_QUIRK_NO_NCQ_ON_ATI |
|
||||
ATA_QUIRK_NO_LPM_ON_ATI },
|
||||
ATA_QUIRK_NO_LPM_ON_ATI_AND_AMD },
|
||||
{ "Samsung SSD 870*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
|
||||
ATA_QUIRK_ZERO_AFTER_TRIM |
|
||||
ATA_QUIRK_NO_NCQ_ON_ATI |
|
||||
ATA_QUIRK_NO_LPM_ON_ATI },
|
||||
ATA_QUIRK_NO_LPM_ON_ATI_AND_AMD },
|
||||
{ "SAMSUNG*MZ7LH*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
|
||||
ATA_QUIRK_ZERO_AFTER_TRIM |
|
||||
ATA_QUIRK_NO_NCQ_ON_ATI |
|
||||
ATA_QUIRK_NO_LPM_ON_ATI },
|
||||
ATA_QUIRK_NO_LPM_ON_ATI_AND_AMD },
|
||||
{ "FCCT*M500*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
|
||||
ATA_QUIRK_ZERO_AFTER_TRIM },
|
||||
|
||||
|
|
|
|||
|
|
@ -261,12 +261,18 @@ static void ata_scsi_set_passthru_sense_fields(struct ata_queued_cmd *qc)
|
|||
|
||||
/* descriptor format */
|
||||
len = sb[7];
|
||||
desc = (char *)scsi_sense_desc_find(sb, len + 8, 9);
|
||||
desc = (char *)scsi_sense_desc_find(sb, SCSI_SENSE_BUFFERSIZE, 9);
|
||||
if (!desc) {
|
||||
if (SCSI_SENSE_BUFFERSIZE < len + 14)
|
||||
/*
|
||||
* The descriptor is written at sb[8 + len] and is 14
|
||||
* bytes long, so it needs len + 22 bytes of buffer.
|
||||
*/
|
||||
if (len + 22 > SCSI_SENSE_BUFFERSIZE)
|
||||
return;
|
||||
sb[7] = len + 14;
|
||||
desc = sb + 8 + len;
|
||||
} else if (desc - sb > SCSI_SENSE_BUFFERSIZE - 14) {
|
||||
return;
|
||||
}
|
||||
desc[0] = 9;
|
||||
desc[1] = 12;
|
||||
|
|
|
|||
|
|
@ -1040,9 +1040,10 @@ static int cacheinfo_cpu_online(unsigned int cpu)
|
|||
rc = cache_add_dev(cpu);
|
||||
if (rc)
|
||||
goto err;
|
||||
if (cpu_map_shared_cache(true, cpu, &cpu_map))
|
||||
if (cpu_map_shared_cache(true, cpu, &cpu_map)) {
|
||||
update_per_cpu_data_slice_size(true, cpu, cpu_map);
|
||||
sched_update_llc_bytes(cpu);
|
||||
sched_update_llc_bytes(cpu_map);
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
free_cache_attributes(cpu);
|
||||
|
|
@ -1059,10 +1060,10 @@ static int cacheinfo_cpu_pre_down(unsigned int cpu)
|
|||
cpu_cache_sysfs_exit(cpu);
|
||||
|
||||
free_cache_attributes(cpu);
|
||||
if (nr_shared > 1)
|
||||
if (nr_shared > 1) {
|
||||
update_per_cpu_data_slice_size(false, cpu, cpu_map);
|
||||
|
||||
sched_update_llc_bytes(cpu);
|
||||
sched_update_llc_bytes(cpu_map);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -289,7 +289,8 @@ void btintel_hw_error(struct hci_dev *hdev, u8 code)
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
bt_dev_err(hdev, "Exception info %s", (char *)(skb->data + 1));
|
||||
bt_dev_err(hdev, "Exception info %.*s", (int)(skb->len - 1),
|
||||
(char *)(skb->data + 1));
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
|
|
@ -408,8 +409,16 @@ int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name)
|
|||
/* DDC file contains one or more DDC structure which has
|
||||
* Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2).
|
||||
*/
|
||||
while (fw->size > fw_ptr - fw->data) {
|
||||
u8 cmd_plen = fw_ptr[0] + sizeof(u8);
|
||||
while (fw->size > (size_t)(fw_ptr - fw->data)) {
|
||||
size_t remaining = fw->size - (fw_ptr - fw->data);
|
||||
unsigned int cmd_plen = fw_ptr[0] + 1U;
|
||||
|
||||
if (cmd_plen < 3 || cmd_plen > U8_MAX || cmd_plen > remaining) {
|
||||
bt_dev_err(hdev, "Malformed DDC record (plen=%u, remaining=%zu)",
|
||||
cmd_plen, remaining);
|
||||
release_firmware(fw);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr,
|
||||
HCI_INIT_TIMEOUT);
|
||||
|
|
|
|||
|
|
@ -1197,7 +1197,7 @@ static int btintel_pcie_recv_frame(struct btintel_pcie_data *data,
|
|||
{
|
||||
int ret;
|
||||
u8 pkt_type;
|
||||
u16 plen;
|
||||
u32 plen;
|
||||
u32 pcie_pkt_type;
|
||||
void *pdata;
|
||||
struct hci_dev *hdev = data->hdev;
|
||||
|
|
@ -2219,6 +2219,16 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev,
|
|||
if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
|
||||
return -ENODEV;
|
||||
|
||||
/* Account for the 4-byte PCIe type header prepended before the
|
||||
* DMA copy. Written as a subtraction to avoid wrap-around on
|
||||
* attacker-controlled skb->len.
|
||||
*/
|
||||
if (skb->len > BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN) {
|
||||
bt_dev_err(hdev, "Packet too large: %u > %u", skb->len,
|
||||
BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
/* Due to the fw limitation, the type header of the packet should be
|
||||
* 4 bytes unlike 1 byte for UART. In UART, the firmware can read
|
||||
* the first byte to get the packet type and redirect the rest of data
|
||||
|
|
|
|||
|
|
@ -1397,8 +1397,10 @@ static void iso_resource_auto_work(struct work_struct *work)
|
|||
} else {
|
||||
// Transit from allocation to reallocation, except if the client requested
|
||||
// deallocation in the meantime.
|
||||
scoped_guard(spinlock_irq, &client->lock)
|
||||
r->todo = ISO_RES_AUTO_REALLOC;
|
||||
scoped_guard(spinlock_irq, &client->lock) {
|
||||
if (r->todo == ISO_RES_AUTO_ALLOC)
|
||||
r->todo = ISO_RES_AUTO_REALLOC;
|
||||
}
|
||||
|
||||
if (channel >= 0)
|
||||
r->params.channels_mask = BIT_ULL(channel);
|
||||
|
|
|
|||
|
|
@ -115,8 +115,12 @@ static int arizona_gpio_direction_out(struct gpio_chip *chip,
|
|||
if (value)
|
||||
value = ARIZONA_GPN_LVL;
|
||||
|
||||
return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
|
||||
ARIZONA_GPN_DIR | ARIZONA_GPN_LVL, value);
|
||||
ret = regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
|
||||
ARIZONA_GPN_DIR | ARIZONA_GPN_LVL, value);
|
||||
if (ret < 0 && (val & ARIZONA_GPN_DIR) && persistent)
|
||||
pm_runtime_put_autosuspend(chip->parent);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int arizona_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
|
|
|
|||
|
|
@ -1034,6 +1034,8 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state)
|
|||
static int mvebu_gpio_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev);
|
||||
u32 edge_cache = ~0U, level_cache = ~0U;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
regmap_write(mvchip->regs, GPIO_OUT_OFF + mvchip->offset,
|
||||
|
|
@ -1045,32 +1047,51 @@ static int mvebu_gpio_resume(struct platform_device *pdev)
|
|||
regmap_write(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset,
|
||||
mvchip->in_pol_reg);
|
||||
|
||||
/*
|
||||
* genirq skips mask_irq() for a line it already considers masked, so
|
||||
* unmasking one behind its back leaves an asserted level line that
|
||||
* nobody masks. Restore only bits the irqchip cache still has set.
|
||||
*
|
||||
* Snapshot the caches under the raw spinlock, but release it before
|
||||
* the regmap writes below: regmap_write() takes a sleepable lock on
|
||||
* PREEMPT_RT.
|
||||
*/
|
||||
if (mvchip->domain) {
|
||||
struct irq_chip_generic *gc;
|
||||
|
||||
gc = irq_get_domain_generic_chip(mvchip->domain, 0);
|
||||
raw_spin_lock_irqsave(&gc->lock, flags);
|
||||
level_cache = gc->chip_types[0].mask_cache_priv;
|
||||
edge_cache = gc->chip_types[1].mask_cache_priv;
|
||||
raw_spin_unlock_irqrestore(&gc->lock, flags);
|
||||
}
|
||||
|
||||
switch (mvchip->soc_variant) {
|
||||
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
||||
case MVEBU_GPIO_SOC_VARIANT_A8K:
|
||||
regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset,
|
||||
mvchip->edge_mask_regs[0]);
|
||||
mvchip->edge_mask_regs[0] & edge_cache);
|
||||
regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset,
|
||||
mvchip->level_mask_regs[0]);
|
||||
mvchip->level_mask_regs[0] & level_cache);
|
||||
break;
|
||||
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
||||
for (i = 0; i < 2; i++) {
|
||||
regmap_write(mvchip->regs,
|
||||
GPIO_EDGE_MASK_MV78200_OFF(i),
|
||||
mvchip->edge_mask_regs[i]);
|
||||
mvchip->edge_mask_regs[i] & edge_cache);
|
||||
regmap_write(mvchip->regs,
|
||||
GPIO_LEVEL_MASK_MV78200_OFF(i),
|
||||
mvchip->level_mask_regs[i]);
|
||||
mvchip->level_mask_regs[i] & level_cache);
|
||||
}
|
||||
break;
|
||||
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
||||
for (i = 0; i < 4; i++) {
|
||||
regmap_write(mvchip->regs,
|
||||
GPIO_EDGE_MASK_ARMADAXP_OFF(i),
|
||||
mvchip->edge_mask_regs[i]);
|
||||
mvchip->edge_mask_regs[i] & edge_cache);
|
||||
regmap_write(mvchip->regs,
|
||||
GPIO_LEVEL_MASK_ARMADAXP_OFF(i),
|
||||
mvchip->level_mask_regs[i]);
|
||||
mvchip->level_mask_regs[i] & level_cache);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = !!(val & BIT(TPS65219_MFP_GPIO_STATUS_MASK));
|
||||
ret = !!(val & TPS65219_MFP_GPIO_STATUS_MASK);
|
||||
dev_warn(dev, "GPIO%d = %d, MULTI_DEVICE_ENABLE, not a standard GPIO\n", offset, ret);
|
||||
|
||||
/*
|
||||
|
|
@ -87,7 +87,7 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
|||
* status bit.
|
||||
*/
|
||||
|
||||
if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
|
||||
if (gc->get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
|
||||
return -ENOTSUPP;
|
||||
|
||||
return ret;
|
||||
|
|
@ -158,8 +158,10 @@ static int tps65214_gpio_change_direction(struct gpio_chip *gc, unsigned int off
|
|||
if (ret)
|
||||
dev_err(dev, "GPIO%d configured as VSEL, not GPIO\n", offset);
|
||||
|
||||
val = direction == GPIO_LINE_DIRECTION_OUT ?
|
||||
TPS65214_GPIO0_DIR_MASK : 0;
|
||||
ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG,
|
||||
TPS65214_GPIO0_DIR_MASK, direction);
|
||||
TPS65214_GPIO0_DIR_MASK, val);
|
||||
if (ret)
|
||||
dev_err(dev, "Fail to change direction to %u for GPIO%d.\n", direction, offset);
|
||||
|
||||
|
|
@ -176,7 +178,7 @@ static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offs
|
|||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN)
|
||||
if (gc->get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN)
|
||||
return 0;
|
||||
|
||||
return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_IN);
|
||||
|
|
@ -190,7 +192,7 @@ static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int off
|
|||
if (offset != TPS6521X_GPIO0_IDX)
|
||||
return 0;
|
||||
|
||||
if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
|
||||
if (gc->get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
|
||||
return 0;
|
||||
|
||||
return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_OUT);
|
||||
|
|
|
|||
|
|
@ -798,15 +798,7 @@ static int zynq_gpio_runtime_resume(struct device *dev)
|
|||
|
||||
static int zynq_gpio_request(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(chip->parent);
|
||||
|
||||
/*
|
||||
* If the device is already active pm_runtime_get() will return 1 on
|
||||
* success, but gpio_request still needs to return 0.
|
||||
*/
|
||||
return ret < 0 ? ret : 0;
|
||||
return pm_runtime_resume_and_get(chip->parent);
|
||||
}
|
||||
|
||||
static void zynq_gpio_free(struct gpio_chip *chip, unsigned int offset)
|
||||
|
|
|
|||
|
|
@ -2172,18 +2172,19 @@ static void gpio_v2_line_info_changed_to_v1(
|
|||
|
||||
#endif /* CONFIG_GPIO_CDEV_V1 */
|
||||
|
||||
static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
|
||||
struct gpio_v2_line_info *info, bool atomic)
|
||||
static int gpio_desc_to_lineinfo(struct gpio_desc *desc,
|
||||
struct gpio_v2_line_info *info, bool atomic)
|
||||
{
|
||||
u32 debounce_period_us;
|
||||
unsigned long dflags;
|
||||
const char *label;
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
CLASS(gpio_chip_guard, guard)(desc);
|
||||
if (!guard.gc)
|
||||
return;
|
||||
return -ENODEV;
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->offset = gpiod_hwgpio(desc);
|
||||
|
||||
if (desc->name)
|
||||
|
|
@ -2258,6 +2259,8 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
|
|||
debounce_period_us;
|
||||
info->num_attrs++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct gpio_chardev_data {
|
||||
|
|
@ -2309,6 +2312,7 @@ static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip,
|
|||
struct gpio_desc *desc;
|
||||
struct gpioline_info lineinfo;
|
||||
struct gpio_v2_line_info lineinfo_v2;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
|
||||
return -EFAULT;
|
||||
|
|
@ -2326,7 +2330,10 @@ static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip,
|
|||
return -EBUSY;
|
||||
}
|
||||
|
||||
gpio_desc_to_lineinfo(desc, &lineinfo_v2, false);
|
||||
ret = gpio_desc_to_lineinfo(desc, &lineinfo_v2, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo);
|
||||
|
||||
if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
|
||||
|
|
@ -2344,6 +2351,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
|
|||
{
|
||||
struct gpio_desc *desc;
|
||||
struct gpio_v2_line_info lineinfo;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
|
||||
return -EFAULT;
|
||||
|
|
@ -2363,7 +2371,10 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
|
|||
if (test_and_set_bit(lineinfo.offset, cdev->watched_lines))
|
||||
return -EBUSY;
|
||||
}
|
||||
gpio_desc_to_lineinfo(desc, &lineinfo, false);
|
||||
|
||||
ret = gpio_desc_to_lineinfo(desc, &lineinfo, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
|
||||
if (watch)
|
||||
|
|
@ -2489,6 +2500,7 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
|
|||
struct lineinfo_changed_ctx *ctx;
|
||||
struct gpio_desc *desc = data;
|
||||
struct file *fp;
|
||||
int ret;
|
||||
|
||||
if (!test_bit(gpiod_hwgpio(desc), cdev->watched_lines))
|
||||
return NOTIFY_DONE;
|
||||
|
|
@ -2519,7 +2531,13 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
|
|||
|
||||
ctx->chg.event_type = action;
|
||||
ctx->chg.timestamp_ns = ktime_get_ns();
|
||||
gpio_desc_to_lineinfo(desc, &ctx->chg.info, true);
|
||||
|
||||
ret = gpio_desc_to_lineinfo(desc, &ctx->chg.info, true);
|
||||
if (ret) {
|
||||
fput(fp);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/* Keep the GPIO device alive until we emit the event. */
|
||||
ctx->gdev = gpio_device_get(desc->gdev);
|
||||
ctx->cdev = cdev;
|
||||
|
|
|
|||
|
|
@ -1029,6 +1029,13 @@ int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode)
|
|||
ret = of_gpiochip_get_lflags(gc, &gpiospec, &lflags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* If no line-name property is present, fall back to the OF
|
||||
* node name as in the previous implementation.
|
||||
*/
|
||||
if (!name)
|
||||
name = to_of_node(fwnode)->name;
|
||||
} else {
|
||||
/*
|
||||
* GPIO_ACTIVE_LOW is currently the only lookup flag
|
||||
|
|
|
|||
|
|
@ -1167,8 +1167,10 @@ int amdgpu_acpi_enumerate_xcc(void)
|
|||
}
|
||||
|
||||
xcc_info = kzalloc_obj(struct amdgpu_acpi_xcc_info);
|
||||
if (!xcc_info)
|
||||
if (!xcc_info) {
|
||||
acpi_dev_put(acpi_dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&xcc_info->list);
|
||||
xcc_info->handle = acpi_device_handle(acpi_dev);
|
||||
|
|
|
|||
|
|
@ -1780,8 +1780,10 @@ static int amdgpu_debugfs_test_ib_show(struct seq_file *m, void *unused)
|
|||
|
||||
/* Avoid accidently unparking the sched thread during GPU reset */
|
||||
r = down_write_killable(&adev->reset_domain->sem);
|
||||
if (r)
|
||||
if (r) {
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* hold on the scheduler */
|
||||
for (i = 0; i < AMDGPU_MAX_RINGS; i++) {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ amdgpu_eviction_fence_suspend_worker(struct work_struct *work)
|
|||
|
||||
mutex_lock(&uq_mgr->userq_mutex);
|
||||
|
||||
/* Fence waits are not allowed in a fence signalling critical section. */
|
||||
amdgpu_userq_wait_for_signal(uq_mgr);
|
||||
|
||||
/*
|
||||
* This is intentionally after taking the userq_mutex since we do
|
||||
* allocate memory while holding this lock, but only after ensuring that
|
||||
|
|
|
|||
|
|
@ -254,7 +254,6 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring,
|
|||
ring->adev = adev;
|
||||
ring->num_hw_submission = sched_hw_submission;
|
||||
ring->sched_score = sched_score;
|
||||
ring->vmid_wait = dma_fence_get_stub();
|
||||
|
||||
ring->idx = adev->num_rings++;
|
||||
adev->rings[ring->idx] = ring;
|
||||
|
|
@ -374,6 +373,7 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring,
|
|||
|
||||
ring->max_dw = max_dw;
|
||||
ring->hw_prio = hw_prio;
|
||||
ring->vmid_wait = dma_fence_get_stub();
|
||||
|
||||
if (!ring->no_scheduler && ring->funcs->type < AMDGPU_HW_IP_NUM) {
|
||||
hw_ip = ring->funcs->type;
|
||||
|
|
|
|||
|
|
@ -184,27 +184,27 @@ static void amdgpu_userq_hang_detect_work(struct work_struct *work)
|
|||
void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
unsigned long timeout_ms;
|
||||
unsigned long timeout_jiffies;
|
||||
|
||||
adev = queue->userq_mgr->adev;
|
||||
/* Determine timeout based on queue type */
|
||||
switch (queue->queue_type) {
|
||||
case AMDGPU_RING_TYPE_GFX:
|
||||
timeout_ms = adev->gfx_timeout;
|
||||
timeout_jiffies = adev->gfx_timeout;
|
||||
break;
|
||||
case AMDGPU_RING_TYPE_COMPUTE:
|
||||
timeout_ms = adev->compute_timeout;
|
||||
timeout_jiffies = adev->compute_timeout;
|
||||
break;
|
||||
case AMDGPU_RING_TYPE_SDMA:
|
||||
timeout_ms = adev->sdma_timeout;
|
||||
timeout_jiffies = adev->sdma_timeout;
|
||||
break;
|
||||
default:
|
||||
timeout_ms = adev->gfx_timeout;
|
||||
timeout_jiffies = adev->gfx_timeout;
|
||||
break;
|
||||
}
|
||||
|
||||
queue_delayed_work(adev->reset_domain->wq, &queue->hang_detect_work,
|
||||
msecs_to_jiffies(timeout_ms));
|
||||
timeout_jiffies);
|
||||
}
|
||||
|
||||
void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell)
|
||||
|
|
@ -1272,7 +1272,7 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr)
|
||||
{
|
||||
struct amdgpu_usermode_queue *queue;
|
||||
|
|
@ -1291,8 +1291,6 @@ amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr)
|
|||
void
|
||||
amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr)
|
||||
{
|
||||
/* Wait for any pending userqueue fence work to finish */
|
||||
amdgpu_userq_wait_for_signal(uq_mgr);
|
||||
amdgpu_userq_evict_all(uq_mgr);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ void amdgpu_userq_mgr_cancel_reset_work(struct amdgpu_device *adev);
|
|||
void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr *userq_mgr);
|
||||
void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr);
|
||||
|
||||
void amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr);
|
||||
void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
|
||||
|
||||
void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr,
|
||||
|
|
|
|||
|
|
@ -2678,6 +2678,7 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm,
|
|||
amdgpu_bo_unref(&root_bo);
|
||||
|
||||
error_free_delayed:
|
||||
dma_fence_put(vm->last_update);
|
||||
dma_fence_put(vm->last_tlb_flush);
|
||||
dma_fence_put(vm->last_unlocked);
|
||||
ttm_lru_bulk_move_fini(&adev->mman.bdev, &vm->lru_bulk_move);
|
||||
|
|
|
|||
|
|
@ -1689,7 +1689,8 @@ static int vcn_v4_0_3_reset_jpeg_pre_helper(struct amdgpu_device *adev, int inst
|
|||
/* if Jobs are still pending after timeout,
|
||||
* We'll handle them in the bottom helper
|
||||
*/
|
||||
amdgpu_fence_wait_polling(ring, wait_seq, adev->video_timeout);
|
||||
amdgpu_fence_wait_polling(ring, wait_seq,
|
||||
jiffies_to_usecs(adev->video_timeout));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1335,7 +1335,8 @@ static int vcn_v5_0_1_reset_jpeg_pre_helper(struct amdgpu_device *adev, int inst
|
|||
/* if Jobs are still pending after timeout,
|
||||
* We'll handle them in the bottom helper
|
||||
*/
|
||||
amdgpu_fence_wait_polling(ring, wait_seq, adev->video_timeout);
|
||||
amdgpu_fence_wait_polling(ring, wait_seq,
|
||||
jiffies_to_usecs(adev->video_timeout));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/time.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/pseudo_fs.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/processor.h>
|
||||
|
|
@ -70,18 +71,54 @@ static const struct class kfd_class = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Cache the address space of the chardev on first open so that the reset
|
||||
* path can drop all userspace mappings of doorbell and MMIO ranges via
|
||||
* unmap_mapping_range().
|
||||
* Private pseudo-filesystem for KFD, Provides a stable, module-owned
|
||||
* inode whose address_space is the unmap target for all /dev/kfd
|
||||
* openers during GPU reset.
|
||||
*/
|
||||
static struct address_space *kfd_dev_mapping;
|
||||
static struct vfsmount *kfd_fs_mnt;
|
||||
static int kfd_fs_cnt;
|
||||
|
||||
static int kfd_fs_init_fs_context(struct fs_context *fc)
|
||||
{
|
||||
return init_pseudo(fc, 0x4b464400 /* "KFD" */) ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
static struct file_system_type kfd_fs_type = {
|
||||
.name = "kfd",
|
||||
.init_fs_context = kfd_fs_init_fs_context,
|
||||
.kill_sb = kill_anon_super,
|
||||
};
|
||||
|
||||
static struct inode *kfd_fs_inode_new(void)
|
||||
{
|
||||
struct inode *inode;
|
||||
int r;
|
||||
|
||||
r = simple_pin_fs(&kfd_fs_type, &kfd_fs_mnt, &kfd_fs_cnt);
|
||||
if (r < 0)
|
||||
return ERR_PTR(r);
|
||||
|
||||
inode = alloc_anon_inode(kfd_fs_mnt->mnt_sb);
|
||||
if (IS_ERR(inode))
|
||||
simple_release_fs(&kfd_fs_mnt, &kfd_fs_cnt);
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
static void kfd_fs_inode_free(struct inode *inode)
|
||||
{
|
||||
if (inode) {
|
||||
iput(inode);
|
||||
simple_release_fs(&kfd_fs_mnt, &kfd_fs_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
static struct inode *kfd_anon_inode;
|
||||
|
||||
void kfd_dev_unmap_mapping_range(loff_t const holebegin, loff_t const holelen)
|
||||
{
|
||||
struct address_space *mapping = READ_ONCE(kfd_dev_mapping);
|
||||
|
||||
if (mapping)
|
||||
unmap_mapping_range(mapping, holebegin, holelen, 1);
|
||||
if (kfd_anon_inode)
|
||||
unmap_mapping_range(kfd_anon_inode->i_mapping, holebegin, holelen, 1);
|
||||
}
|
||||
|
||||
static inline struct kfd_process_device *kfd_lock_pdd_by_id(struct kfd_process *p, __u32 gpu_id)
|
||||
|
|
@ -107,6 +144,13 @@ int kfd_chardev_init(void)
|
|||
{
|
||||
int err = 0;
|
||||
|
||||
kfd_anon_inode = kfd_fs_inode_new();
|
||||
if (IS_ERR(kfd_anon_inode)) {
|
||||
err = PTR_ERR(kfd_anon_inode);
|
||||
kfd_anon_inode = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
kfd_char_dev_major = register_chrdev(0, kfd_dev_name, &kfd_fops);
|
||||
err = kfd_char_dev_major;
|
||||
if (err < 0)
|
||||
|
|
@ -130,6 +174,8 @@ int kfd_chardev_init(void)
|
|||
err_class_create:
|
||||
unregister_chrdev(kfd_char_dev_major, kfd_dev_name);
|
||||
err_register_chrdev:
|
||||
kfd_fs_inode_free(kfd_anon_inode);
|
||||
kfd_anon_inode = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -138,6 +184,8 @@ void kfd_chardev_exit(void)
|
|||
device_destroy(&kfd_class, MKDEV(kfd_char_dev_major, 0));
|
||||
class_unregister(&kfd_class);
|
||||
unregister_chrdev(kfd_char_dev_major, kfd_dev_name);
|
||||
kfd_fs_inode_free(kfd_anon_inode);
|
||||
kfd_anon_inode = NULL;
|
||||
kfd_device = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -150,12 +198,7 @@ static int kfd_open(struct inode *inode, struct file *filep)
|
|||
if (iminor(inode) != 0)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* /dev/kfd is a single chardev so all opens share one inode. Cache
|
||||
* its address_space on the first open for use by the reset path.
|
||||
*/
|
||||
if (!READ_ONCE(kfd_dev_mapping))
|
||||
cmpxchg(&kfd_dev_mapping, NULL, inode->i_mapping);
|
||||
filep->f_mapping = kfd_anon_inode->i_mapping;
|
||||
|
||||
is_32bit_user_mode = in_compat_syscall();
|
||||
|
||||
|
|
|
|||
|
|
@ -5583,8 +5583,10 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
|
|||
|
||||
skip_modeset:
|
||||
/* Release extra reference */
|
||||
if (new_stream)
|
||||
if (new_stream) {
|
||||
dc_stream_release(new_stream);
|
||||
new_stream = NULL;
|
||||
}
|
||||
new_stream = NULL;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -29,14 +29,14 @@ dml_ccflags := $(CC_FLAGS_FPU)
|
|||
dml_rcflags := $(CC_FLAGS_NO_FPU)
|
||||
|
||||
ifneq ($(CONFIG_FRAME_WARN),0)
|
||||
ifeq ($(filter y,$(CONFIG_KASAN)$(CONFIG_KCSAN)),y)
|
||||
ifeq ($(filter y,$(CONFIG_KASAN)$(CONFIG_KCSAN)$(CONFIG_UBSAN)),y)
|
||||
ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_COMPILE_TEST),yy)
|
||||
frame_warn_limit := 4096
|
||||
else
|
||||
frame_warn_limit := 3072
|
||||
endif
|
||||
else
|
||||
frame_warn_limit := 2048
|
||||
frame_warn_limit := 3072
|
||||
endif
|
||||
|
||||
ifeq ($(call test-lt, $(CONFIG_FRAME_WARN), $(frame_warn_limit)),y)
|
||||
|
|
|
|||
|
|
@ -28,14 +28,14 @@ dml2_ccflags := $(CC_FLAGS_FPU)
|
|||
dml2_rcflags := $(CC_FLAGS_NO_FPU)
|
||||
|
||||
ifneq ($(CONFIG_FRAME_WARN),0)
|
||||
ifeq ($(filter y,$(CONFIG_KASAN)$(CONFIG_KCSAN)),y)
|
||||
ifeq ($(filter y,$(CONFIG_KASAN)$(CONFIG_KCSAN)$(CONFIG_UBSAN)),y)
|
||||
ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_COMPILE_TEST),yy)
|
||||
frame_warn_limit := 4096
|
||||
else
|
||||
frame_warn_limit := 3072
|
||||
endif
|
||||
else
|
||||
frame_warn_limit := 2056
|
||||
frame_warn_limit := 3072
|
||||
endif
|
||||
|
||||
ifeq ($(call test-lt, $(CONFIG_FRAME_WARN), $(frame_warn_limit)),y)
|
||||
|
|
|
|||
|
|
@ -1862,7 +1862,7 @@ static int samsung_dsim_register_te_irq(struct samsung_dsim *dsi, struct device
|
|||
int te_gpio_irq;
|
||||
int ret;
|
||||
|
||||
dsi->te_gpio = devm_gpiod_get_optional(dev, "te", GPIOD_IN);
|
||||
dsi->te_gpio = gpiod_get_optional(dev, "te", GPIOD_IN);
|
||||
if (!dsi->te_gpio)
|
||||
return 0;
|
||||
else if (IS_ERR(dsi->te_gpio))
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user