diff options
| -rw-r--r-- | .mailmap | 1 | ||||
| -rw-r--r-- | Documentation/arch/arm64/silicon-errata.rst | 4 | ||||
| -rw-r--r-- | Documentation/virt/kvm/api.rst | 6 | ||||
| -rw-r--r-- | Documentation/virt/kvm/devices/s390_flic.rst | 15 | ||||
| -rw-r--r-- | MAINTAINERS | 2 | ||||
| -rw-r--r-- | arch/arm64/include/asm/kvm_hypevents.h | 1 | ||||
| -rw-r--r-- | arch/arm64/kvm/arm.c | 25 | ||||
| -rw-r--r-- | arch/arm64/kvm/hyp/nvhe/hyp-main.c | 1 | ||||
| -rw-r--r-- | arch/arm64/kvm/hyp_trace.c | 36 | ||||
| -rw-r--r-- | arch/arm64/kvm/mmu.c | 4 | ||||
| -rw-r--r-- | arch/arm64/kvm/vgic/vgic-its.c | 25 | ||||
| -rw-r--r-- | arch/arm64/kvm/vgic/vgic-v3.c | 8 | ||||
| -rw-r--r-- | arch/arm64/kvm/vgic/vgic.c | 18 | ||||
| -rw-r--r-- | arch/s390/include/asm/kvm_host.h | 5 | ||||
| -rw-r--r-- | arch/s390/kvm/interrupt.c | 56 | ||||
| -rw-r--r-- | arch/s390/kvm/pci.c | 107 | ||||
| -rw-r--r-- | arch/s390/kvm/pci.h | 2 | ||||
| -rw-r--r-- | arch/x86/kvm/mmu/mmu.c | 22 | ||||
| -rw-r--r-- | arch/x86/kvm/mmu/page_track.c | 32 | ||||
| -rw-r--r-- | arch/x86/kvm/svm/avic.c | 8 | ||||
| -rw-r--r-- | arch/x86/kvm/vmx/vmx_ops.h | 8 | ||||
| -rw-r--r-- | arch/x86/kvm/x86.c | 6 | ||||
| -rw-r--r-- | include/kvm/arm_vgic.h | 3 |
23 files changed, 298 insertions, 97 deletions
@@ -296,6 +296,7 @@ Frank Rowand <frowand.list@gmail.com> <frank.rowand@sony.com> Frank Rowand <frowand.list@gmail.com> <frank.rowand@sonymobile.com> Frank Rowand <frowand.list@gmail.com> <frowand@mvista.com> Frank Zago <fzago@systemfabricworks.com> +Fuad Tabba <fuad.tabba@linux.dev> <tabba@google.com> Gao Xiang <xiang@kernel.org> <gaoxiang25@huawei.com> Gao Xiang <xiang@kernel.org> <hsiangkao@aol.com> Gao Xiang <xiang@kernel.org> <hsiangkao@linux.alibaba.com> diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst index 014aa1c215a1..88b4aa45a206 100644 --- a/Documentation/arch/arm64/silicon-errata.rst +++ b/Documentation/arch/arm64/silicon-errata.rst @@ -55,10 +55,14 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | Ampere | AmpereOne | AC03_CPU_38 | AMPERE_ERRATUM_AC03_CPU_38 | +----------------+-----------------+-----------------+-----------------------------+ +| Ampere | AmpereOne | AC03_CPU_57 | N/A | ++----------------+-----------------+-----------------+-----------------------------+ | Ampere | AmpereOne AC04 | AC04_CPU_10 | AMPERE_ERRATUM_AC03_CPU_38 | +----------------+-----------------+-----------------+-----------------------------+ | Ampere | AmpereOne AC04 | AC04_CPU_23 | AMPERE_ERRATUM_AC04_CPU_23 | +----------------+-----------------+-----------------+-----------------------------+ +| Ampere | AmpereOne AC04 | AC04_CPU_29 | N/A | ++----------------+-----------------+-----------------+-----------------------------+ +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A510 | #2457168 | ARM64_ERRATUM_2457168 | +----------------+-----------------+-----------------+-----------------------------+ diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index a5f9ee92f43e..e3003a241d5b 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -8414,6 +8414,12 @@ When this capability is enabled all memory in memslots must be mapped as attempts to create a memslot with an invalid mmap will result in an -EINVAL return. +``guest_memfd``, even though it is an anonymous file, is not supported with MTE. +Attempting to create a memslot backed by ``guest_memfd`` when the MTE capability +is enabled, or attempting to enable the MTE capability after +``guest_memfd``-backed memslots have been created, will result in an -EINVAL +return. + When enabled the VMM may make use of the ``KVM_ARM_MTE_COPY_TAGS`` ioctl to perform a bulk copy of tags to/from the guest. diff --git a/Documentation/virt/kvm/devices/s390_flic.rst b/Documentation/virt/kvm/devices/s390_flic.rst index b784f8016748..983c858b444a 100644 --- a/Documentation/virt/kvm/devices/s390_flic.rst +++ b/Documentation/virt/kvm/devices/s390_flic.rst @@ -112,9 +112,20 @@ Groups: mask or unmask the adapter, as specified in mask KVM_S390_IO_ADAPTER_MAP - This is now a no-op. The mapping is purely done by the irq route. + Map an adapter indicator or summary page for long-term pinning so that + interrupt injection can be performed in atomic context. If long-term + pinning is not possible (e.g. file-backed memory), the page is verified + via a short-term pin and the ioctl returns success; interrupt injection + will use the non-atomic irqfd path with short-term pinning on each + interrupt. In Secure Execution mode this is a no-op and the ioctl + returns success. + KVM_S390_IO_ADAPTER_UNMAP - This is now a no-op. The mapping is purely done by the irq route. + Unmap a previously mapped adapter indicator or summary page and release + the long-term pin. If the page was not long-term pinned (e.g. file-backed + memory), the map entry is removed and success is returned; if no prior + map entry exists, -ENOENT is returned. In Secure Execution mode this is + a no-op and the ioctl returns success. KVM_DEV_FLIC_AISM modify the adapter-interruption-suppression mode for a given isc if the diff --git a/MAINTAINERS b/MAINTAINERS index 2f9472c1a090..62d53465ef9b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14193,7 +14193,7 @@ F: virt/kvm/* KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64) M: Marc Zyngier <maz@kernel.org> M: Oliver Upton <oupton@kernel.org> -R: Fuad Tabba <tabba@google.com> +R: Fuad Tabba <fuad.tabba@linux.dev> R: Joey Gouly <joey.gouly@arm.com> R: Steffen Eiden <seiden@linux.ibm.com> R: Suzuki K Poulose <suzuki.poulose@arm.com> diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/asm/kvm_hypevents.h index 743c49bd878f..5f6e6789d121 100644 --- a/arch/arm64/include/asm/kvm_hypevents.h +++ b/arch/arm64/include/asm/kvm_hypevents.h @@ -12,6 +12,7 @@ enum hyp_enter_exit_reason { HYP_REASON_SMC, HYP_REASON_HVC, + HYP_REASON_SYS, HYP_REASON_PSCI, HYP_REASON_HOST_ABORT, HYP_REASON_GUEST_EXIT, diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 50adfff75be8..9a6c72a18672 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -149,14 +149,27 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, set_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER, &kvm->arch.flags); break; - case KVM_CAP_ARM_MTE: - mutex_lock(&kvm->lock); - if (system_supports_mte() && !kvm->created_vcpus) { - r = 0; - set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags); + case KVM_CAP_ARM_MTE: { + struct kvm_memory_slot *memslot; + int bkt; + + guard(mutex)(&kvm->lock); + if (!system_supports_mte() || kvm->created_vcpus) + break; + + r = 0; + guard(mutex)(&kvm->slots_lock); + kvm_for_each_memslot(memslot, bkt, kvm_memslots(kvm)) { + if (kvm_slot_has_gmem(memslot)) { + r = -EINVAL; + break; + } } - mutex_unlock(&kvm->lock); + if (r == 0) + set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags); break; + + } case KVM_CAP_ARM_SYSTEM_SUSPEND: r = 0; set_bit(KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED, &kvm->arch.flags); diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index d3c69de698f4..d3df96ed8ba4 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -932,6 +932,7 @@ void handle_trap(struct kvm_cpu_context *host_ctxt) handle_host_mem_abort(host_ctxt); break; case ESR_ELx_EC_SYS64: + trace_hyp_enter(host_ctxt, HYP_REASON_SYS); if (handle_host_mte(esr)) break; fallthrough; diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c index 2411b4c32932..9644c424819b 100644 --- a/arch/arm64/kvm/hyp_trace.c +++ b/arch/arm64/kvm/hyp_trace.c @@ -37,8 +37,6 @@ static struct hyp_trace_clock { u32 shift; struct delayed_work work; struct completion ready; - struct mutex lock; - bool running; } hyp_clock; static void __hyp_clock_work(struct work_struct *work) @@ -110,12 +108,9 @@ static void hyp_trace_clock_enable(struct hyp_trace_clock *hyp_clock, bool enabl { struct system_time_snapshot snap; - if (hyp_clock->running == enable) - return; - if (!enable) { cancel_delayed_work_sync(&hyp_clock->work); - hyp_clock->running = false; + return; } ktime_get_snapshot_id(CLOCK_BOOTTIME, &snap); @@ -128,7 +123,6 @@ static void hyp_trace_clock_enable(struct hyp_trace_clock *hyp_clock, bool enabl INIT_DELAYED_WORK(&hyp_clock->work, __hyp_clock_work); schedule_delayed_work(&hyp_clock->work, msecs_to_jiffies(CLOCK_INIT_MS)); wait_for_completion(&hyp_clock->ready); - hyp_clock->running = true; } /* Access to this struct within the trace_remote_callbacks are protected by the trace_remote lock */ @@ -160,6 +154,7 @@ static int hyp_trace_buffer_alloc_bpages_backing(struct hyp_trace_buffer *trace_ int nr_bpages = (PAGE_ALIGN(size) / PAGE_SIZE) + 1; size_t backing_size; void *start; + int ret; backing_size = PAGE_ALIGN(sizeof(struct simple_buffer_page) * nr_bpages * num_possible_cpus()); @@ -168,10 +163,16 @@ static int hyp_trace_buffer_alloc_bpages_backing(struct hyp_trace_buffer *trace_ if (!start) return -ENOMEM; + ret = __map_hyp(start, backing_size); + if (ret) { + free_pages_exact(start, backing_size); + return ret; + } + trace_buffer->desc->bpages_backing_start = (unsigned long)start; trace_buffer->desc->bpages_backing_size = backing_size; - return __map_hyp(start, backing_size); + return ret; } static void hyp_trace_buffer_free_bpages_backing(struct hyp_trace_buffer *trace_buffer) @@ -228,18 +229,22 @@ static int hyp_trace_buffer_share_hyp(struct hyp_trace_buffer *trace_buffer) static struct trace_buffer_desc *hyp_trace_load(unsigned long size, void *priv) { struct hyp_trace_buffer *trace_buffer = priv; + size_t desc_size, tb_desc_size; struct hyp_trace_desc *desc; - size_t desc_size; int ret; if (WARN_ON(trace_buffer->desc)) return ERR_PTR(-EINVAL); - desc_size = trace_buffer_desc_size(size, num_possible_cpus()); + tb_desc_size = trace_buffer_desc_size(size, num_possible_cpus()); + desc_size = size_add(tb_desc_size, offsetof(struct hyp_trace_desc, trace_buffer_desc)); if (desc_size == SIZE_MAX) return ERR_PTR(-E2BIG); desc_size = PAGE_ALIGN(desc_size); + if (!desc_size) + return ERR_PTR(-E2BIG); + desc = (struct hyp_trace_desc *)alloc_pages_exact(desc_size, GFP_KERNEL); if (!desc) return ERR_PTR(-ENOMEM); @@ -255,7 +260,7 @@ static struct trace_buffer_desc *hyp_trace_load(unsigned long size, void *priv) if (ret) goto err_free_desc; - ret = trace_remote_alloc_buffer(&desc->trace_buffer_desc, desc_size, size, + ret = trace_remote_alloc_buffer(&desc->trace_buffer_desc, tb_desc_size, size, cpu_possible_mask); if (ret) goto err_free_backing; @@ -304,9 +309,15 @@ static void hyp_trace_unload(struct trace_buffer_desc *desc, void *priv) static int hyp_trace_enable_tracing(bool enable, void *priv) { + int ret; + hyp_trace_clock_enable(&hyp_clock, enable); - return kvm_call_hyp_nvhe(__tracing_enable, enable); + ret = kvm_call_hyp_nvhe(__tracing_enable, enable); + if (ret) + hyp_trace_clock_enable(&hyp_clock, !enable); + + return ret; } static int hyp_trace_swap_reader_page(unsigned int cpu, void *priv) @@ -398,6 +409,7 @@ static const char *__hyp_enter_exit_reason_str(u8 reason) static const char strs[][12] = { "smc", "hvc", + "sys", "psci", "host_abort", "guest_exit", diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 6c941aaa10c6..2d95203386ba 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -2652,6 +2652,10 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new)) return -EINVAL; + /* guest_memfd is incompatible with MTE. */ + if (kvm_slot_has_gmem(new) && kvm_has_mte(kvm)) + return -EINVAL; + hva = new->userspace_addr; reg_end = hva + (new->npages << PAGE_SHIFT); diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 740b39875728..36ab3e492915 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -116,18 +116,27 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid, kfree(irq); irq = oldirq; } else { - ret = xa_err(__xa_store(&dist->lpi_xa, intid, irq, 0)); + /* + * The entry is either empty or contains a dead LPI (refcount=0) + * from the deferred release path, pending cleanup by + * vgic_release_deleted_lpis(). Evict and free it if present. + */ + oldirq = __xa_store(&dist->lpi_xa, intid, irq, + GFP_NOWAIT | __GFP_ACCOUNT); + ret = xa_err(oldirq); + if (ret) { + xa_unlock_irqrestore(&dist->lpi_xa, flags); + kfree(irq); + + return ERR_PTR(ret); + } + + if (oldirq && !WARN_ON_ONCE(refcount_read(&oldirq->refcount))) + kfree_rcu(oldirq, rcu); } xa_unlock_irqrestore(&dist->lpi_xa, flags); - if (ret) { - xa_release(&dist->lpi_xa, intid); - kfree(irq); - - return ERR_PTR(ret); - } - /* * We "cache" the configuration table entries in our struct vgic_irq's. * However we only have those structs for mapped IRQs, so we read in diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c index 9e841e7afd4a..7aa417440f6a 100644 --- a/arch/arm64/kvm/vgic/vgic-v3.c +++ b/arch/arm64/kvm/vgic/vgic-v3.c @@ -275,7 +275,13 @@ void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val) lr = vgic_v3_compute_lr(vcpu, irq) & ~ICH_LR_ACTIVE_BIT; } - if (lr & ICH_LR_HW) + /* + * In the nested state, the irq has already been deactivated via the HW + * bit in the LR. Deactivating again would be harmless except AmpereOne + * errata AC03_CPU_57, AC04_CPU_29 could cause irq delivery to break if + * the deactivation hits the highest priority pending irq. + */ + if ((lr & ICH_LR_HW) && !vgic_state_is_nested(vcpu)) vgic_v3_deactivate_phys(FIELD_GET(ICH_LR_PHYS_ID_MASK, lr)); vgic_v3_fold_lr(vcpu, lr); diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index ccb7e3a90cd0..74bace10a22e 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -146,11 +146,7 @@ static __must_check bool __vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) static __must_check bool vgic_put_irq_norelease(struct kvm *kvm, struct vgic_irq *irq) { - if (!__vgic_put_irq(kvm, irq)) - return false; - - irq->pending_release = true; - return true; + return __vgic_put_irq(kvm, irq); } void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) @@ -167,12 +163,14 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) guard(spinlock_irqsave)(&dist->lpi_xa.xa_lock); } - if (!__vgic_put_irq(kvm, irq)) + if (!irq_is_lpi(kvm, irq->intid)) return; - xa_lock_irqsave(&dist->lpi_xa, flags); - vgic_release_lpi_locked(dist, irq); - xa_unlock_irqrestore(&dist->lpi_xa, flags); + if (refcount_dec_and_lock_irqsave(&irq->refcount, + &dist->lpi_xa.xa_lock, &flags)) { + vgic_release_lpi_locked(dist, irq); + xa_unlock_irqrestore(&dist->lpi_xa, flags); + } } static void vgic_release_deleted_lpis(struct kvm *kvm) @@ -184,7 +182,7 @@ static void vgic_release_deleted_lpis(struct kvm *kvm) xa_lock_irqsave(&dist->lpi_xa, flags); xa_for_each(&dist->lpi_xa, intid, irq) { - if (irq->pending_release) + if (!refcount_read(&irq->refcount)) vgic_release_lpi_locked(dist, irq); } diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index eaa34c5bd3c1..c172f9b212d1 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -476,6 +476,11 @@ struct s390_map_info { __u64 guest_addr; __u64 addr; struct page *page; + /* + * True if the page is long-term pinned. False if long-term pinning + * failed and this entry exists only to preserve MAP/UNMAP symmetry. + */ + bool pinned; }; struct s390_io_adapter { diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 9e3e6b0d72ad..7514d9e2403c 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -2520,8 +2520,22 @@ static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr) map->addr = host_addr; map->page = pin_map_page(kvm, host_addr, FOLL_LONGTERM); if (!map->page) { - ret = -EINVAL; - goto out; + /* + * Long-term pinning may fail for memory types such as file-backed + * memory. Verify that short-term pinning succeeds so that the + * non-atomic irqfd path can handle interrupt injection. + */ + map->page = pin_map_page(kvm, host_addr, 0); + if (!map->page) { + ret = -EINVAL; + goto out; + } + unpin_user_page(map->page); + map->page = NULL; + map->pinned = false; + /* Add an entry to preserve MAP/UNMAP symmetry. */ + } else { + map->pinned = true; } spin_lock_irqsave(&adapter->maps_lock, flags); if (adapter->nr_maps < MAX_S390_ADAPTER_MAPS) { @@ -2532,7 +2546,7 @@ static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr) ret = -EINVAL; } spin_unlock_irqrestore(&adapter->maps_lock, flags); - if (ret) + if (ret && map->page) unpin_user_page(map->page); out: if (ret) @@ -2546,6 +2560,7 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr) struct s390_map_info *map, *tmp, *map_to_free; struct page *map_page_to_put = NULL; u64 map_addr_to_mark = 0; + bool map_pinned = false; unsigned long flags; int found = 0, idx; @@ -2560,6 +2575,7 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr) list_del(&map->list); map_page_to_put = map->page; map_addr_to_mark = map->guest_addr; + map_pinned = map->pinned; map_to_free = map; break; } @@ -2568,11 +2584,18 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr) if (found) { kfree(map_to_free); - idx = srcu_read_lock(&kvm->srcu); - mark_page_dirty(kvm, map_addr_to_mark >> PAGE_SHIFT); - set_page_dirty_lock(map_page_to_put); - srcu_read_unlock(&kvm->srcu, idx); - unpin_user_page(map_page_to_put); + if (map_pinned) { + /* + * Only long-term pinned pages need to be marked dirty + * and released. Fallback entries exist only for + * MAP/UNMAP symmetry. + */ + idx = srcu_read_lock(&kvm->srcu); + mark_page_dirty(kvm, map_addr_to_mark >> PAGE_SHIFT); + set_page_dirty_lock(map_page_to_put); + srcu_read_unlock(&kvm->srcu, idx); + unpin_user_page(map_page_to_put); + } } return found ? 0 : -ENOENT; @@ -2598,11 +2621,13 @@ void kvm_s390_unmap_all_adapters(struct kvm *kvm) list_for_each_entry_safe(map, tmp, &local_list, list) { list_del(&map->list); - idx = srcu_read_lock(&kvm->srcu); - mark_page_dirty(kvm, map->guest_addr >> PAGE_SHIFT); - set_page_dirty_lock(map->page); - srcu_read_unlock(&kvm->srcu, idx); - unpin_user_page(map->page); + if (map->pinned) { + idx = srcu_read_lock(&kvm->srcu); + mark_page_dirty(kvm, map->guest_addr >> PAGE_SHIFT); + set_page_dirty_lock(map->page); + srcu_read_unlock(&kvm->srcu, idx); + unpin_user_page(map->page); + } kfree(map); } } @@ -2929,8 +2954,11 @@ static struct s390_map_info *get_map_info(struct s390_io_adapter *adapter, return NULL; list_for_each_entry(map, &adapter->maps, list) { - if (map->addr == addr) + if (map->addr == addr) { + if (!map->pinned) + return NULL; return map; + } } return NULL; } diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 720bb58cabe2..50f5ec79600e 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -190,40 +190,61 @@ static int kvm_zpci_clear_airq(struct zpci_dev *zdev) return cc ? -EIO : 0; } -static inline void unaccount_mem(unsigned long nr_pages) +static inline void unaccount_mem(struct kvm_zdev *kzdev, unsigned long nr_pages) { - struct user_struct *user = get_uid(current_user()); + struct user_struct *user = kzdev->user_account; + struct mm_struct *mm_account = kzdev->mm_account; - if (user) + if (user) { atomic_long_sub(nr_pages, &user->locked_vm); - if (current->mm) - atomic64_sub(nr_pages, ¤t->mm->pinned_vm); + free_uid(user); + kzdev->user_account = NULL; + } + + if (mm_account) { + atomic64_sub(nr_pages, &mm_account->pinned_vm); + mmdrop(mm_account); + kzdev->mm_account = NULL; + } } -static inline int account_mem(unsigned long nr_pages) +static inline int account_mem(struct kvm_zdev *kzdev, unsigned long nr_pages) { struct user_struct *user = get_uid(current_user()); unsigned long page_limit, cur_pages, new_pages; + int rc = 0; page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; cur_pages = atomic_long_read(&user->locked_vm); do { new_pages = cur_pages + nr_pages; - if (new_pages > page_limit) - return -ENOMEM; + if (new_pages > page_limit) { + rc = -ENOMEM; + goto out; + } } while (!atomic_long_try_cmpxchg(&user->locked_vm, &cur_pages, new_pages)); - atomic64_add(nr_pages, ¤t->mm->pinned_vm); + if (current->mm) { + mmgrab(current->mm); + atomic64_add(nr_pages, ¤t->mm->pinned_vm); + } + + kzdev->user_account = user; + kzdev->mm_account = current->mm; return 0; + +out: + free_uid(user); + return rc; } static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, bool assist) { struct page *pages[1], *aibv_page, *aisb_page = NULL; - unsigned int msi_vecs, idx; + unsigned int msi_vecs, idx, size; struct zpci_gaite *gaite; unsigned long hva, bit; struct kvm *kvm; @@ -237,6 +258,10 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, if (zdev->gisa == 0) return -EINVAL; + /* AIF already enabled for the device */ + if (zdev->kzdev->fib.fmt0.aibv != 0) + return -EINVAL; + kvm = zdev->kzdev->kvm; msi_vecs = min_t(unsigned int, fib->fmt0.noi, zdev->max_msi); @@ -246,6 +271,14 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, return gisc; /* Replace AIBV address */ + size = BITS_TO_LONGS(msi_vecs + fib->fmt0.aibvo) * sizeof(unsigned long); + npages = DIV_ROUND_UP((fib->fmt0.aibv & ~PAGE_MASK) + size, PAGE_SIZE); + /* AIBV cannot span more than 1 page */ + if (npages > 1) { + rc = -EINVAL; + goto out; + } + idx = srcu_read_lock(&kvm->srcu); hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aibv)); npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, pages); @@ -261,6 +294,12 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, /* Pin the guest AISB if one was specified */ if (fib->fmt0.sum == 1) { + /* AISB must be dword aligned */ + if (fib->fmt0.aisb & 0x7) { + rc = -EINVAL; + goto unpin1; + } + idx = srcu_read_lock(&kvm->srcu); hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aisb)); npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, @@ -275,20 +314,28 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, } /* Account for pinned pages, roll back on failure */ - if (account_mem(pcount)) + rc = account_mem(zdev->kzdev, pcount); + if (rc) goto unpin2; /* AISB must be allocated before we can fill in GAITE */ mutex_lock(&aift->aift_lock); bit = airq_iv_alloc_bit(aift->sbv); - if (bit == -1UL) + if (bit == -1UL) { + rc = -ENOMEM; goto unlock; + } zdev->aisb = bit; /* store the summary bit number */ zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | AIRQ_IV_BITLOCK | AIRQ_IV_GUESTVEC, phys_to_virt(fib->fmt0.aibv)); + if (!zdev->aibv) { + rc = -ENOMEM; + goto free_aisb; + } + spin_lock_irq(&aift->gait_lock); gaite = aift->gait + zdev->aisb; @@ -311,21 +358,39 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, aift->kzdev[zdev->aisb] = zdev->kzdev; spin_unlock_irq(&aift->gait_lock); - /* Update guest FIB for re-issue */ - fib->fmt0.aisbo = zdev->aisb & 63; - fib->fmt0.aisb = virt_to_phys(aift->sbv->vector) + (zdev->aisb / 64) * 8; - fib->fmt0.isc = gisc; - /* Save some guest fib values in the host for later use */ - zdev->kzdev->fib.fmt0.isc = fib->fmt0.isc; + zdev->kzdev->fib.fmt0.isc = gisc; zdev->kzdev->fib.fmt0.aibv = fib->fmt0.aibv; - mutex_unlock(&aift->aift_lock); /* Issue the clp to setup the irq now */ rc = kvm_zpci_set_airq(zdev); - return rc; + if (!rc) { + mutex_unlock(&aift->aift_lock); + return rc; + } + + /* Start cleanup */ + zdev->kzdev->fib.fmt0.isc = 0; + zdev->kzdev->fib.fmt0.aibv = 0; + spin_lock_irq(&aift->gait_lock); + gaite->count--; + gaite->aisb = 0; + gaite->gisc = 0; + gaite->aisbo = 0; + gaite->gisa = 0; + aift->kzdev[zdev->aisb] = NULL; + spin_unlock_irq(&aift->gait_lock); + + airq_iv_release(zdev->aibv); + zdev->aibv = NULL; + +free_aisb: + airq_iv_free_bit(aift->sbv, zdev->aisb); + zdev->aisb = 0; unlock: + if (pcount > 0) + unaccount_mem(zdev->kzdev, pcount); mutex_unlock(&aift->aift_lock); unpin2: if (fib->fmt0.sum == 1) @@ -396,7 +461,7 @@ static int kvm_s390_pci_aif_disable(struct zpci_dev *zdev, bool force) pcount++; } if (pcount > 0) - unaccount_mem(pcount); + unaccount_mem(kzdev, pcount); out: mutex_unlock(&aift->aift_lock); diff --git a/arch/s390/kvm/pci.h b/arch/s390/kvm/pci.h index ff0972dd5e71..fdf8c7bf4ed0 100644 --- a/arch/s390/kvm/pci.h +++ b/arch/s390/kvm/pci.h @@ -22,6 +22,8 @@ struct kvm_zdev { struct kvm *kvm; struct zpci_fib fib; struct list_head entry; + struct user_struct *user_account; + struct mm_struct *mm_account; }; struct zpci_gaite { diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 22cf222d3033..66e69d2a41b3 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -722,6 +722,26 @@ static struct kvm_lpage_info *lpage_info_slot(gfn_t gfn, return &slot->arch.lpage_info[level - 2][idx]; } +static bool kvm_gfn_is_lpage_allowed(struct kvm *kvm, + const struct kvm_memory_slot *slot, + gfn_t gfn, int level) +{ + const struct kvm_memory_slot *other_slot; + + BUILD_BUG_ON(KVM_MAX_NR_ADDRESS_SPACES > 2); + + if (lpage_info_slot(gfn, slot, level)->disallow_lpage) + return false; + + if (kvm_arch_nr_memslot_as_ids(kvm) > 1) { + other_slot = __gfn_to_memslot(__kvm_memslots(kvm, slot->as_id ^ 1), gfn); + if (other_slot && lpage_info_slot(gfn, other_slot, level)->disallow_lpage) + return false; + } + + return true; +} + /* * The most significant bit in disallow_lpage tracks whether or not memory * attributes are mixed, i.e. not identical for all gfns at the current level. @@ -2968,7 +2988,7 @@ int mmu_try_to_unsync_pages(struct kvm *kvm, const struct kvm_memory_slot *slot, * write-protected (see above), thus if the gfn can be mapped with a * hugepage and isn't write-tracked, it can't have a shadow page. */ - if (!lpage_info_slot(gfn, slot, PG_LEVEL_2M)->disallow_lpage) + if (kvm_gfn_is_lpage_allowed(kvm, slot, gfn, PG_LEVEL_2M)) return 0; /* diff --git a/arch/x86/kvm/mmu/page_track.c b/arch/x86/kvm/mmu/page_track.c index 1b17b12393a8..7e8195a311bb 100644 --- a/arch/x86/kvm/mmu/page_track.c +++ b/arch/x86/kvm/mmu/page_track.c @@ -130,13 +130,23 @@ void __kvm_write_track_remove_gfn(struct kvm *kvm, kvm_mmu_gfn_allow_lpage(slot, gfn); } -/* - * check if the corresponding access on the specified guest page is tracked. - */ +static bool __kvm_gfn_is_write_tracked(const struct kvm_memory_slot *slot, + gfn_t gfn) +{ + int index; + + if (!slot) + return false; + + index = gfn_to_index(gfn, slot->base_gfn, PG_LEVEL_4K); + return !!READ_ONCE(slot->arch.gfn_write_track[index]); +} + +/* check if write access is tracked on the specified guest page. */ bool kvm_gfn_is_write_tracked(struct kvm *kvm, const struct kvm_memory_slot *slot, gfn_t gfn) { - int index; + const struct kvm_memory_slot *other_slot; if (!slot) return false; @@ -144,8 +154,18 @@ bool kvm_gfn_is_write_tracked(struct kvm *kvm, if (!kvm_page_track_write_tracking_enabled(kvm)) return false; - index = gfn_to_index(gfn, slot->base_gfn, PG_LEVEL_4K); - return !!READ_ONCE(slot->arch.gfn_write_track[index]); + BUILD_BUG_ON(KVM_MAX_NR_ADDRESS_SPACES > 2); + + if (__kvm_gfn_is_write_tracked(slot, gfn)) + return true; + + if (kvm_arch_nr_memslot_as_ids(kvm) > 1) { + other_slot = __gfn_to_memslot(__kvm_memslots(kvm, slot->as_id ^ 1), gfn); + if (__kvm_gfn_is_write_tracked(other_slot, gfn)) + return true; + } + + return false; } #ifdef CONFIG_KVM_EXTERNAL_WRITE_TRACKING diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index 58e493a80cb0..16e09a8fe85e 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -240,14 +240,6 @@ static void avic_deactivate_vmcb(struct vcpu_svm *svm) if (!is_sev_es_guest(&svm->vcpu)) svm_set_intercept(svm, INTERCEPT_CR8_WRITE); - /* - * If running nested and the guest uses its own MSR bitma |
