Commit f83e38fc authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-6.11-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip

Pull xen updates from Juergen Gross:

 - some trivial cleanups

 - a fix for the Xen timer

 - add boot time selectable debug capability to the Xen multicall
   handling

 - two fixes for the recently added Xen irqfd handling

* tag 'for-linus-6.11-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip:
  x86/xen: remove deprecated xen_nopvspin boot parameter
  x86/xen: eliminate some private header files
  x86/xen: make some functions static
  xen: make multicall debug boot time selectable
  xen/arm: Convert comma to semicolon
  xen: privcmd: Fix possible access to a freed kirqfd instance
  xen: privcmd: Switch from mutex to spinlock for irqfds
  xen: add missing MODULE_DESCRIPTION() macros
  x86/xen: Convert comma to semicolon
  x86/xen/time: Reduce Xen timer tick
  xen/manage: Constify struct shutdown_handler
parents e55037c8 9fe6a8c5
...@@ -7420,17 +7420,18 @@ ...@@ -7420,17 +7420,18 @@
Crash from Xen panic notifier, without executing late Crash from Xen panic notifier, without executing late
panic() code such as dumping handler. panic() code such as dumping handler.
xen_mc_debug [X86,XEN,EARLY]
Enable multicall debugging when running as a Xen PV guest.
Enabling this feature will reduce performance a little
bit, so it should only be enabled for obtaining extended
debug data in case of multicall errors.
xen_msr_safe= [X86,XEN,EARLY] xen_msr_safe= [X86,XEN,EARLY]
Format: <bool> Format: <bool>
Select whether to always use non-faulting (safe) MSR Select whether to always use non-faulting (safe) MSR
access functions when running as Xen PV guest. The access functions when running as Xen PV guest. The
default value is controlled by CONFIG_XEN_PV_MSR_SAFE. default value is controlled by CONFIG_XEN_PV_MSR_SAFE.
xen_nopvspin [X86,XEN,EARLY]
Disables the qspinlock slowpath using Xen PV optimizations.
This parameter is obsoleted by "nopvspin" parameter, which
has equivalent effect for XEN platform.
xen_nopv [X86] xen_nopv [X86]
Disables the PV optimizations forcing the HVM guest to Disables the PV optimizations forcing the HVM guest to
run as generic HVM guest with no PV drivers. run as generic HVM guest with no PV drivers.
......
...@@ -109,7 +109,7 @@ int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops, ...@@ -109,7 +109,7 @@ int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
* immediate unmapping. * immediate unmapping.
*/ */
map_ops[i].status = GNTST_general_error; map_ops[i].status = GNTST_general_error;
unmap.host_addr = map_ops[i].host_addr, unmap.host_addr = map_ops[i].host_addr;
unmap.handle = map_ops[i].handle; unmap.handle = map_ops[i].handle;
map_ops[i].handle = INVALID_GRANT_HANDLE; map_ops[i].handle = INVALID_GRANT_HANDLE;
if (map_ops[i].flags & GNTMAP_device_map) if (map_ops[i].flags & GNTMAP_device_map)
......
...@@ -10,8 +10,6 @@ ...@@ -10,8 +10,6 @@
#include <xen/xen.h> #include <xen/xen.h>
#include <xen/interface/physdev.h> #include <xen/interface/physdev.h>
#include "xen-ops.h" #include "xen-ops.h"
#include "pmu.h"
#include "smp.h"
static unsigned int xen_io_apic_read(unsigned apic, unsigned reg) static unsigned int xen_io_apic_read(unsigned apic, unsigned reg)
{ {
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "debugfs.h" #include "xen-ops.h"
static struct dentry *d_xen_debug; static struct dentry *d_xen_debug;
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _XEN_DEBUGFS_H
#define _XEN_DEBUGFS_H
struct dentry * __init xen_init_debugfs(void);
#endif /* _XEN_DEBUGFS_H */
...@@ -20,8 +20,6 @@ ...@@ -20,8 +20,6 @@
#include <asm/setup.h> #include <asm/setup.h>
#include "xen-ops.h" #include "xen-ops.h"
#include "smp.h"
#include "pmu.h"
EXPORT_SYMBOL_GPL(hypercall_page); EXPORT_SYMBOL_GPL(hypercall_page);
......
...@@ -28,8 +28,6 @@ ...@@ -28,8 +28,6 @@
#include <asm/xen/page.h> #include <asm/xen/page.h>
#include "xen-ops.h" #include "xen-ops.h"
#include "mmu.h"
#include "smp.h"
static unsigned long shared_info_pfn; static unsigned long shared_info_pfn;
......
...@@ -85,10 +85,6 @@ ...@@ -85,10 +85,6 @@
#endif #endif
#include "xen-ops.h" #include "xen-ops.h"
#include "mmu.h"
#include "smp.h"
#include "multicalls.h"
#include "pmu.h"
#include "../kernel/cpu/cpu.h" /* get_cpu_cap() */ #include "../kernel/cpu/cpu.h" /* get_cpu_cap() */
......
...@@ -5,8 +5,7 @@ ...@@ -5,8 +5,7 @@
#include <asm/xen/hypercall.h> #include <asm/xen/hypercall.h>
#include <xen/interface/memory.h> #include <xen/interface/memory.h>
#include "multicalls.h" #include "xen-ops.h"
#include "mmu.h"
unsigned long arbitrary_virt_to_mfn(void *vaddr) unsigned long arbitrary_virt_to_mfn(void *vaddr)
{ {
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _XEN_MMU_H
#include <linux/linkage.h>
#include <asm/page.h>
enum pt_level {
PT_PGD,
PT_P4D,
PT_PUD,
PT_PMD,
PT_PTE
};
bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn);
void set_pte_mfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags);
pte_t xen_ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep);
void xen_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr,
pte_t *ptep, pte_t pte);
unsigned long xen_read_cr2_direct(void);
extern void xen_init_mmu_ops(void);
extern void xen_hvm_init_mmu_ops(void);
#endif /* _XEN_MMU_H */
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
#include <xen/interface/xen.h> #include <xen/interface/xen.h>
#include <xen/hvm.h> #include <xen/hvm.h>
#include "mmu.h" #include "xen-ops.h"
#ifdef CONFIG_PROC_VMCORE #ifdef CONFIG_PROC_VMCORE
/* /*
......
...@@ -82,9 +82,7 @@ ...@@ -82,9 +82,7 @@
#include <xen/hvc-console.h> #include <xen/hvc-console.h>
#include <xen/swiotlb-xen.h> #include <xen/swiotlb-xen.h>
#include "multicalls.h" #include "xen-ops.h"
#include "mmu.h"
#include "debugfs.h"
/* /*
* Prototypes for functions called via PV_CALLEE_SAVE_REGS_THUNK() in order * Prototypes for functions called via PV_CALLEE_SAVE_REGS_THUNK() in order
...@@ -128,7 +126,7 @@ static DEFINE_SPINLOCK(xen_reservation_lock); ...@@ -128,7 +126,7 @@ static DEFINE_SPINLOCK(xen_reservation_lock);
* looking at another vcpu's cr3 value, it should use this variable. * looking at another vcpu's cr3 value, it should use this variable.
*/ */
DEFINE_PER_CPU(unsigned long, xen_cr3); /* cr3 stored as physaddr */ DEFINE_PER_CPU(unsigned long, xen_cr3); /* cr3 stored as physaddr */
DEFINE_PER_CPU(unsigned long, xen_current_cr3); /* actual vcpu cr3 */ static DEFINE_PER_CPU(unsigned long, xen_current_cr3); /* actual vcpu cr3 */
static phys_addr_t xen_pt_base, xen_pt_size __initdata; static phys_addr_t xen_pt_base, xen_pt_size __initdata;
...@@ -305,16 +303,17 @@ static void xen_set_pte(pte_t *ptep, pte_t pteval) ...@@ -305,16 +303,17 @@ static void xen_set_pte(pte_t *ptep, pte_t pteval)
__xen_set_pte(ptep, pteval); __xen_set_pte(ptep, pteval);
} }
pte_t xen_ptep_modify_prot_start(struct vm_area_struct *vma, static pte_t xen_ptep_modify_prot_start(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep) unsigned long addr, pte_t *ptep)
{ {
/* Just return the pte as-is. We preserve the bits on commit */ /* Just return the pte as-is. We preserve the bits on commit */
trace_xen_mmu_ptep_modify_prot_start(vma->vm_mm, addr, ptep, *ptep); trace_xen_mmu_ptep_modify_prot_start(vma->vm_mm, addr, ptep, *ptep);
return *ptep; return *ptep;
} }
void xen_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, static void xen_ptep_modify_prot_commit(struct vm_area_struct *vma,
pte_t *ptep, pte_t pte) unsigned long addr,
pte_t *ptep, pte_t pte)
{ {
struct mmu_update u; struct mmu_update u;
......
...@@ -23,26 +23,21 @@ ...@@ -23,26 +23,21 @@
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/hardirq.h> #include <linux/hardirq.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/jump_label.h>
#include <linux/printk.h>
#include <asm/xen/hypercall.h> #include <asm/xen/hypercall.h>
#include "multicalls.h" #include "xen-ops.h"
#include "debugfs.h"
#define MC_BATCH 32 #define MC_BATCH 32
#define MC_DEBUG 0
#define MC_ARGS (MC_BATCH * 16) #define MC_ARGS (MC_BATCH * 16)
struct mc_buffer { struct mc_buffer {
unsigned mcidx, argidx, cbidx; unsigned mcidx, argidx, cbidx;
struct multicall_entry entries[MC_BATCH]; struct multicall_entry entries[MC_BATCH];
#if MC_DEBUG
struct multicall_entry debug[MC_BATCH];
void *caller[MC_BATCH];
#endif
unsigned char args[MC_ARGS]; unsigned char args[MC_ARGS];
struct callback { struct callback {
void (*fn)(void *); void (*fn)(void *);
...@@ -50,13 +45,98 @@ struct mc_buffer { ...@@ -50,13 +45,98 @@ struct mc_buffer {
} callbacks[MC_BATCH]; } callbacks[MC_BATCH];
}; };
struct mc_debug_data {
struct multicall_entry entries[MC_BATCH];
void *caller[MC_BATCH];
size_t argsz[MC_BATCH];
unsigned long *args[MC_BATCH];
};
static DEFINE_PER_CPU(struct mc_buffer, mc_buffer); static DEFINE_PER_CPU(struct mc_buffer, mc_buffer);
static struct mc_debug_data mc_debug_data_early __initdata;
static struct mc_debug_data __percpu *mc_debug_data __refdata =
&mc_debug_data_early;
DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags); DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags);
static struct static_key mc_debug __ro_after_init;
static bool mc_debug_enabled __initdata;
static int __init xen_parse_mc_debug(char *arg)
{
mc_debug_enabled = true;
static_key_slow_inc(&mc_debug);
return 0;
}
early_param("xen_mc_debug", xen_parse_mc_debug);
static int __init mc_debug_enable(void)
{
struct mc_debug_data __percpu *mcdb;
unsigned long flags;
if (!mc_debug_enabled)
return 0;
mcdb = alloc_percpu(struct mc_debug_data);
if (!mcdb) {
pr_err("xen_mc_debug inactive\n");
static_key_slow_dec(&mc_debug);
return -ENOMEM;
}
/* Be careful when switching to percpu debug data. */
local_irq_save(flags);
xen_mc_flush();
mc_debug_data = mcdb;
local_irq_restore(flags);
pr_info("xen_mc_debug active\n");
return 0;
}
early_initcall(mc_debug_enable);
/* Number of parameters of hypercalls used via multicalls. */
static const uint8_t hpcpars[] = {
[__HYPERVISOR_mmu_update] = 4,
[__HYPERVISOR_stack_switch] = 2,
[__HYPERVISOR_fpu_taskswitch] = 1,
[__HYPERVISOR_update_descriptor] = 2,
[__HYPERVISOR_update_va_mapping] = 3,
[__HYPERVISOR_mmuext_op] = 4,
};
static void print_debug_data(struct mc_buffer *b, struct mc_debug_data *mcdb,
int idx)
{
unsigned int arg;
unsigned int opidx = mcdb->entries[idx].op & 0xff;
unsigned int pars = 0;
pr_err(" call %2d: op=%lu result=%ld caller=%pS ", idx + 1,
mcdb->entries[idx].op, b->entries[idx].result,
mcdb->caller[idx]);
if (opidx < ARRAY_SIZE(hpcpars))
pars = hpcpars[opidx];
if (pars) {
pr_cont("pars=");
for (arg = 0; arg < pars; arg++)
pr_cont("%lx ", mcdb->entries[idx].args[arg]);
}
if (mcdb->argsz[idx]) {
pr_cont("args=");
for (arg = 0; arg < mcdb->argsz[idx] / 8; arg++)
pr_cont("%lx ", mcdb->args[idx][arg]);
}
pr_cont("\n");
}
void xen_mc_flush(void) void xen_mc_flush(void)
{ {
struct mc_buffer *b = this_cpu_ptr(&mc_buffer); struct mc_buffer *b = this_cpu_ptr(&mc_buffer);
struct multicall_entry *mc; struct multicall_entry *mc;
struct mc_debug_data *mcdb = NULL;
int ret = 0; int ret = 0;
unsigned long flags; unsigned long flags;
int i; int i;
...@@ -69,10 +149,11 @@ void xen_mc_flush(void) ...@@ -69,10 +149,11 @@ void xen_mc_flush(void)
trace_xen_mc_flush(b->mcidx, b->argidx, b->cbidx); trace_xen_mc_flush(b->mcidx, b->argidx, b->cbidx);
#if MC_DEBUG if (static_key_false(&mc_debug)) {
memcpy(b->debug, b->entries, mcdb = this_cpu_ptr(mc_debug_data);
b->mcidx * sizeof(struct multicall_entry)); memcpy(mcdb->entries, b->entries,
#endif b->mcidx * sizeof(struct multicall_entry));
}
switch (b->mcidx) { switch (b->mcidx) {
case 0: case 0:
...@@ -103,21 +184,14 @@ void xen_mc_flush(void) ...@@ -103,21 +184,14 @@ void xen_mc_flush(void)
pr_err("%d of %d multicall(s) failed: cpu %d\n", pr_err("%d of %d multicall(s) failed: cpu %d\n",
ret, b->mcidx, smp_processor_id()); ret, b->mcidx, smp_processor_id());
for (i = 0; i < b->mcidx; i++) { for (i = 0; i < b->mcidx; i++) {
if (b->entries[i].result < 0) { if (static_key_false(&mc_debug)) {
#if MC_DEBUG print_debug_data(b, mcdb, i);
pr_err(" call %2d: op=%lu arg=[%lx] result=%ld\t%pS\n", } else if (b->entries[i].result < 0) {
i + 1,
b->debug[i].op,
b->debug[i].args[0],
b->entries[i].result,
b->caller[i]);
#else
pr_err(" call %2d: op=%lu arg=[%lx] result=%ld\n", pr_err(" call %2d: op=%lu arg=[%lx] result=%ld\n",
i + 1, i + 1,
b->entries[i].op, b->entries[i].op,
b->entries[i].args[0], b->entries[i].args[0],
b->entries[i].result); b->entries[i].result);
#endif
} }
} }
} }
...@@ -155,9 +229,13 @@ struct multicall_space __xen_mc_entry(size_t args) ...@@ -155,9 +229,13 @@ struct multicall_space __xen_mc_entry(size_t args)
} }
ret.mc = &b->entries[b->mcidx]; ret.mc = &b->entries[b->mcidx];
#if MC_DEBUG if (static_key_false(&mc_debug)) {
b->caller[b->mcidx] = __builtin_return_address(0); struct mc_debug_data *mcdb = this_cpu_ptr(mc_debug_data);
#endif
mcdb->caller[b->mcidx] = __builtin_return_address(0);
mcdb->argsz[b->mcidx] = args;
mcdb->args[b->mcidx] = (unsigned long *)(&b->args[argidx]);
}
b->mcidx++; b->mcidx++;
ret.args = &b->args[argidx]; ret.args = &b->args[argidx];
b->argidx = argidx + args; b->argidx = argidx + args;
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _XEN_MULTICALLS_H
#define _XEN_MULTICALLS_H
#include <trace/events/xen.h>
#include "xen-ops.h"
/* Multicalls */
struct multicall_space
{
struct multicall_entry *mc;
void *args;
};
/* Allocate room for a multicall and its args */
struct multicall_space __xen_mc_entry(size_t args);
DECLARE_PER_CPU(unsigned long, xen_mc_irq_flags);
/* Call to start a batch of multiple __xen_mc_entry()s. Must be
paired with xen_mc_issue() */
static inline void xen_mc_batch(void)
{
unsigned long flags;
/* need to disable interrupts until this entry is complete */
local_irq_save(flags);
trace_xen_mc_batch(xen_get_lazy_mode());
__this_cpu_write(xen_mc_irq_flags, flags);
}
static inline struct multicall_space xen_mc_entry(size_t args)
{
xen_mc_batch();
return __xen_mc_entry(args);
}
/* Flush all pending multicalls */
void xen_mc_flush(void);
/* Issue a multicall if we're not in a lazy mode */
static inline void xen_mc_issue(unsigned mode)
{
trace_xen_mc_issue(mode);
if ((xen_get_lazy_mode() & mode) == 0)
xen_mc_flush();
/* restore flags saved in xen_mc_batch */
local_irq_restore(this_cpu_read(xen_mc_irq_flags));
}
/* Set up a callback to be called when the current batch is flushed */
void xen_mc_callback(void (*fn)(void *), void *data);
/*
* Try to extend the arguments of the previous multicall command. The
* previous command's op must match. If it does, then it attempts to
* extend the argument space allocated to the multicall entry by
* arg_size bytes.
*
* The returned multicall_space will return with mc pointing to the
* command on success, or NULL on failure, and args pointing to the
* newly allocated space.
*/
struct multicall_space xen_mc_extend_args(unsigned long op, size_t arg_size);
#endif /* _XEN_MULTICALLS_H */
...@@ -81,7 +81,6 @@ ...@@ -81,7 +81,6 @@
#include <xen/balloon.h> #include <xen/balloon.h>
#include <xen/grant_table.h> #include <xen/grant_table.h>
#include "multicalls.h"
#include "xen-ops.h" #include "xen-ops.h"
#define P2M_MID_PER_PAGE (PAGE_SIZE / sizeof(unsigned long *)) #define P2M_MID_PER_PAGE (PAGE_SIZE / sizeof(unsigned long *))
...@@ -730,7 +729,7 @@ int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops, ...@@ -730,7 +729,7 @@ int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
* immediate unmapping. * immediate unmapping.
*/ */
map_ops[i].status = GNTST_general_error; map_ops[i].status = GNTST_general_error;
unmap[0].host_addr = map_ops[i].host_addr, unmap[0].host_addr = map_ops[i].host_addr;
unmap[0].handle = map_ops[i].handle; unmap[0].handle = map_ops[i].handle;
map_ops[i].handle = INVALID_GRANT_HANDLE; map_ops[i].handle = INVALID_GRANT_HANDLE;
if (map_ops[i].flags & GNTMAP_device_map) if (map_ops[i].flags & GNTMAP_device_map)
...@@ -740,7 +739,7 @@ int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops, ...@@ -740,7 +739,7 @@ int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
if (kmap_ops) { if (kmap_ops) {
kmap_ops[i].status = GNTST_general_error; kmap_ops[i].status = GNTST_general_error;
unmap[1].host_addr = kmap_ops[i].host_addr, unmap[1].host_addr = kmap_ops[i].host_addr;
unmap[1].handle = kmap_ops[i].handle; unmap[1].handle = kmap_ops[i].handle;
kmap_ops[i].handle = INVALID_GRANT_HANDLE; kmap_ops[i].handle = INVALID_GRANT_HANDLE;
if (kmap_ops[i].flags & GNTMAP_device_map) if (kmap_ops[i].flags & GNTMAP_device_map)
...@@ -795,7 +794,6 @@ int clear_foreign_p2m_mapping(struct gnttab_unmap_grant_ref *unmap_ops, ...@@ -795,7 +794,6 @@ int clear_foreign_p2m_mapping(struct gnttab_unmap_grant_ref *unmap_ops,
#ifdef CONFIG_XEN_DEBUG_FS #ifdef CONFIG_XEN_DEBUG_FS
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include "debugfs.h"
static int p2m_dump_show(struct seq_file *m, void *v) static int p2m_dump_show(struct seq_file *m, void *v)
{ {
static const char * const type_name[] = { static const char * const type_name[] = {
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include <xen/interface/xenpmu.h> #include <xen/interface/xenpmu.h>
#include "xen-ops.h" #include "xen-ops.h"
#include "pmu.h"
/* x86_pmu.handle_irq definition */ /* x86_pmu.handle_irq definition */
#include "../events/perf_event.h" #include "../events/perf_event.h"
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __XEN_PMU_H
#define __XEN_PMU_H
#include <xen/interface/xenpmu.h>
extern bool is_xen_pmu;
irqreturn_t xen_pmu_irq_handler(int irq, void *dev_id);
#ifdef CONFIG_XEN_HAVE_VPMU
void xen_pmu_init(int cpu);
void xen_pmu_finish(int cpu);
#else
static inline void xen_pmu_init(int cpu) {}
static inline void xen_pmu_finish(int cpu) {}
#endif
bool pmu_msr_read(unsigned int msr, uint64_t *val, int *err);
bool pmu_msr_write(unsigned int msr, uint32_t low, uint32_t high, int *err);
int pmu_apic_update(uint32_t reg);
unsigned long long xen_read_pmc(int counter);
#endif /* __XEN_PMU_H */
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#include <xen/features.h> #include <xen/features.h>
#include <xen/hvc-console.h> #include <xen/hvc-console.h>
#include "xen-ops.h" #include "xen-ops.h"
#include "mmu.h"
#define GB(x) ((uint64_t)(x) * 1024 * 1024 * 1024) #define GB(x) ((uint64_t)(x) * 1024 * 1024 * 1024)
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include <xen/hvc-console.h> #include <xen/hvc-console.h>
#include "xen-ops.h" #include "xen-ops.h"
#include "smp.h"
static DEFINE_PER_CPU(struct xen_common_irq, xen_resched_irq) = { .irq = -1 }; static DEFINE_PER_CPU(struct xen_common_irq, xen_resched_irq) = { .irq = -1 };
static DEFINE_PER_CPU(struct xen_common_irq, xen_callfunc_irq) = { .irq = -1 }; static DEFINE_PER_CPU(struct xen_common_irq, xen_callfunc_irq) = { .irq = -1 };
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _XEN_SMP_H
#ifdef CONFIG_SMP
void asm_cpu_bringup_and_idle(void);
asmlinkage void cpu_bringup_and_idle(void);
extern void xen_send_IPI_mask(const struct cpumask *mask,
int vector);
extern void xen_send_IPI_mask_allbutself(const struct cpumask *mask,
int vector);
extern void xen_send_IPI_allbutself(int vector);
extern void xen_send_IPI_all(int vector);
extern void xen_send_IPI_self(int vector);
extern int xen_smp_intr_init(unsigned int cpu);
extern void xen_smp_intr_free(unsigned int cpu);
int xen_smp_intr_init_pv(unsigned int cpu);
void xen_smp_intr_free_pv(unsigned int cpu);
void xen_smp_count_cpus(void);
void xen_smp_cpus_done(unsigned int max_cpus);
void xen_smp_send_reschedule(int cpu);
void xen_smp_send_call_function_ipi(const struct cpumask *mask);
void xen_smp_send_call_function_single_ipi(int cpu);
void __noreturn xen_cpu_bringup_again(unsigned long stack);
struct xen_common_irq {
int irq;
char *name;
};
#else /* CONFIG_SMP */
static inline int xen_smp_intr_init(unsigned int cpu)
{
return 0;
}
static inline void xen_smp_intr_free(unsigned int cpu) {}
static inline int xen_smp_intr_init_pv(unsigned int cpu)
{
return 0;
}
static inline void xen_smp_intr_free_pv(unsigned int cpu) {}
static inline void xen_smp_count_cpus(void) { }
#endif /* CONFIG_SMP */
#endif
...@@ -5,8 +5,6 @@ ...@@ -5,8 +5,6 @@
#include <xen/events.h> #include <xen/events.h>
#include "xen-ops.h" #include "xen-ops.h"
#include "smp.h"
static void __init xen_hvm_smp_prepare_boot_cpu(void) static void __init xen_hvm_smp_prepare_boot_cpu(void)
{ {
......
...@@ -46,9 +46,6 @@ ...@@ -46,9 +46,6 @@
#include <xen/hvc-console.h> #include <xen/hvc-console.h>
#include "xen-ops.h" #include "xen-ops.h"
#include "mmu.h"
#include "smp.h"
#include "pmu.h"
cpumask_var_t xen_cpu_initialized_map; cpumask_var_t xen_cpu_initialized_map;
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
static DEFINE_PER_CPU(int, lock_kicker_irq) = -1; static DEFINE_PER_CPU(int, lock_kicker_irq) = -1;
static DEFINE_PER_CPU(char *, irq_name); static DEFINE_PER_CPU(char *, irq_name);
static DEFINE_PER_CPU(atomic_t, xen_qlock_wait_nest); static DEFINE_PER_CPU(atomic_t, xen_qlock_wait_nest);
static bool xen_pvspin = true;
static void xen_qlock_kick(int cpu) static void xen_qlock_kick(int cpu)
{ {
...@@ -68,7 +67,7 @@ void xen_init_lock_cpu(int cpu) ...@@ -68,7 +67,7 @@ void xen_init_lock_cpu(int cpu)
int irq; int irq;
char *name; char *name;
if (!xen_pvspin) if (nopvspin)
return; return;
WARN(per_cpu(lock_kicker_irq, cpu) >= 0, "spinlock on CPU%d exists on IRQ%d!\n", WARN(per_cpu(lock_kicker_irq, cpu) >= 0, "spinlock on CPU%d exists on IRQ%d!\n",
...@@ -95,7 +94,7 @@ void xen_uninit_lock_cpu(int cpu) ...@@ -95,7 +94,7 @@ void xen_uninit_lock_cpu(int cpu)
{ {
int irq; int irq;
if (!xen_pvspin) if (nopvspin)
return; return;
kfree(per_cpu(irq_name, cpu)); kfree(per_cpu(irq_name, cpu));
...@@ -125,10 +124,10 @@ PV_CALLEE_SAVE_REGS_THUNK(xen_vcpu_stolen); ...@@ -125,10 +124,10 @@ PV_CALLEE_SAVE_REGS_THUNK(xen_vcpu_stolen);
void __init xen_init_spinlocks(void) void __init xen_init_spinlocks(void)
{ {
/* Don't need to use pvqspinlock code if there is only 1 vCPU. */ /* Don't need to use pvqspinlock code if there is only 1 vCPU. */
if (num_possible_cpus() == 1 || nopvspin) if (num_possible_cpus() == 1)
xen_pvspin = false; nopvspin = true;
if (!xen_pvspin) { if (nopvspin) {
printk(KERN_DEBUG "xen: PV spinlocks disabled\n"); printk(KERN_DEBUG "xen: PV spinlocks disabled\n");
static_branch_disable(&virt_spin_lock_key); static_branch_disable(&virt_spin_lock_key);
return; return;
...@@ -143,12 +142,3 @@ void __init xen_init_spinlocks(void) ...@@ -143,12 +142,3 @@ void __init xen_init_spinlocks(void)
pv_ops.lock.kick = xen_qlock_kick; pv_ops.lock.kick = xen_qlock_kick;
pv_ops.lock.vcpu_is_preempted = PV_CALLEE_SAVE(xen_vcpu_stolen); pv_ops.lock.vcpu_is_preempted = PV_CALLEE_SAVE(xen_vcpu_stolen);
} }
static __init int xen_parse_nopvspin(char *arg)
{
pr_notice("\"xen_nopvspin\" is deprecated, please use \"nopvspin\" instead\n");
xen_pvspin = false;
return 0;
}
early_param("xen_nopvspin", xen_parse_nopvspin);
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
#include <asm/fixmap.h> #include <asm/fixmap.h>
#include "xen-ops.h" #include "xen-ops.h"
#include "mmu.h"
#include "pmu.h"
static DEFINE_PER_CPU(u64, spec_ctrl); static DEFINE_PER_CPU(u64, spec_ctrl);
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
#include "xen-ops.h" #include "xen-ops.h"
/* Minimum amount of time until next clock event fires */ /* Minimum amount of time until next clock event fires */
#define TIMER_SLOP 100000 #define TIMER_SLOP 1
static u64 xen_sched_clock_offset __read_mostly; static u64 xen_sched_clock_offset __read_mostly;
......
...@@ -5,8 +5,15 @@ ...@@ -5,8 +5,15 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/clocksource.h> #include <linux/clocksource.h>
#include <linux/irqreturn.h> #include <linux/irqreturn.h>
#include <linux/linkage.h>
#include <xen/interface/xenpmu.h>
#include <xen/xen-ops.h> #include <xen/xen-ops.h>
#include <asm/page.h>
#include <trace/events/xen.h>
/* These are code, but not functions. Defined in entry.S */ /* These are code, but not functions. Defined in entry.S */
extern const char xen_failsafe_callback[]; extern const char xen_failsafe_callback[];
...@@ -23,14 +30,11 @@ void xen_copy_trap_info(struct trap_info *traps); ...@@ -23,14 +30,11 @@ void xen_copy_trap_info(struct trap_info *traps);
DECLARE_PER_CPU_ALIGNED(struct vcpu_info, xen_vcpu_info); DECLARE_PER_CPU_ALIGNED(struct vcpu_info, xen_vcpu_info);
DECLARE_PER_CPU(unsigned long, xen_cr3); DECLARE_PER_CPU(unsigned long, xen_cr3);
DECLARE_PER_CPU(unsigned long, xen_current_cr3);
extern struct start_info *xen_start_info; extern struct start_info *xen_start_info;
extern struct shared_info xen_dummy_shared_info; extern struct shared_info xen_dummy_shared_info;
extern struct shared_info *HYPERVISOR_shared_info; extern struct shared_info *HYPERVISOR_shared_info;
extern bool xen_fifo_events;
void xen_setup_mfn_list_list(void); void xen_setup_mfn_list_list(void);
void xen_build_mfn_list_list(void); void xen_build_mfn_list_list(void);
void xen_setup_machphys_mapping(void); void xen_setup_machphys_mapping(void);
...@@ -177,4 +181,142 @@ static inline void xen_hvm_post_suspend(int suspend_cancelled) {} ...@@ -177,4 +181,142 @@ static inline void xen_hvm_post_suspend(int suspend_cancelled) {}
void xen_add_extra_mem(unsigned long start_pfn, unsigned long n_pfns); void xen_add_extra_mem(unsigned long start_pfn, unsigned long n_pfns);
struct dentry * __init xen_init_debugfs(void);
enum pt_level {
PT_PGD,
PT_P4D,
PT_PUD,
PT_PMD,
PT_PTE
};
bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn);
void set_pte_mfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags);
unsigned long xen_read_cr2_direct(void);
void xen_init_mmu_ops(void);
void xen_hvm_init_mmu_ops(void);
/* Multicalls */
struct multicall_space
{
struct multicall_entry *mc;
void *args;
};
/* Allocate room for a multicall and its args */
struct multicall_space __xen_mc_entry(size_t args);
DECLARE_PER_CPU(unsigned long, xen_mc_irq_flags);
/* Call to start a batch of multiple __xen_mc_entry()s. Must be
paired with xen_mc_issue() */
static inline void xen_mc_batch(void)
{
unsigned long flags;
/* need to disable interrupts until this entry is complete */
local_irq_save(flags);
trace_xen_mc_batch(xen_get_lazy_mode());
__this_cpu_write(xen_mc_irq_flags, flags);
}
static inline struct multicall_space xen_mc_entry(size_t args)
{
xen_mc_batch();
return __xen_mc_entry(args);
}
/* Flush all pending multicalls */
void xen_mc_flush(void);
/* Issue a multicall if we're not in a lazy mode */
static inline void xen_mc_issue(unsigned mode)
{
trace_xen_mc_issue(mode);
if ((xen_get_lazy_mode() & mode) == 0)
xen_mc_flush();
/* restore flags saved in xen_mc_batch */
local_irq_restore(this_cpu_read(xen_mc_irq_flags));
}
/* Set up a callback to be called when the current batch is flushed */
void xen_mc_callback(void (*fn)(void *), void *data);
/*
* Try to extend the arguments of the previous multicall command. The
* previous command's op must match. If it does, then it attempts to
* extend the argument space allocated to the multicall entry by
* arg_size bytes.
*
* The returned multicall_space will return with mc pointing to the
* command on success, or NULL on failure, and args pointing to the
* newly allocated space.
*/
struct multicall_space xen_mc_extend_args(unsigned long op, size_t arg_size);
extern bool is_xen_pmu;
irqreturn_t xen_pmu_irq_handler(int irq, void *dev_id);
#ifdef CONFIG_XEN_HAVE_VPMU
void xen_pmu_init(int cpu);
void xen_pmu_finish(int cpu);
#else
static inline void xen_pmu_init(int cpu) {}
static inline void xen_pmu_finish(int cpu) {}
#endif
bool pmu_msr_read(unsigned int msr, uint64_t *val, int *err);
bool pmu_msr_write(unsigned int msr, uint32_t low, uint32_t high, int *err);
int pmu_apic_update(uint32_t reg);
unsigned long long xen_read_pmc(int counter);
#ifdef CONFIG_SMP
void asm_cpu_bringup_and_idle(void);
asmlinkage void cpu_bringup_and_idle(void);
extern void xen_send_IPI_mask(const struct cpumask *mask,
int vector);
extern void xen_send_IPI_mask_allbutself(const struct cpumask *mask,
int vector);
extern void xen_send_IPI_allbutself(int vector);
extern void xen_send_IPI_all(int vector);
extern void xen_send_IPI_self(int vector);
extern int xen_smp_intr_init(unsigned int cpu);
extern void xen_smp_intr_free(unsigned int cpu);
int xen_smp_intr_init_pv(unsigned int cpu);
void xen_smp_intr_free_pv(unsigned int cpu);
void xen_smp_count_cpus(void);
void xen_smp_cpus_done(unsigned int max_cpus);
void xen_smp_send_reschedule(int cpu);
void xen_smp_send_call_function_ipi(const struct cpumask *mask);
void xen_smp_send_call_function_single_ipi(int cpu);
void __noreturn xen_cpu_bringup_again(unsigned long stack);
struct xen_common_irq {
int irq;
char *name;
};
#else /* CONFIG_SMP */
static inline int xen_smp_intr_init(unsigned int cpu)
{
return 0;
}
static inline void xen_smp_intr_free(unsigned int cpu) {}
static inline int xen_smp_intr_init_pv(unsigned int cpu)
{
return 0;
}
static inline void xen_smp_intr_free_pv(unsigned int cpu) {}
static inline void xen_smp_count_cpus(void) { }
#endif /* CONFIG_SMP */
#endif /* XEN_OPS_H */ #endif /* XEN_OPS_H */
...@@ -729,4 +729,5 @@ static void __exit evtchn_cleanup(void) ...@@ -729,4 +729,5 @@ static void __exit evtchn_cleanup(void)
module_init(evtchn_init); module_init(evtchn_init);
module_exit(evtchn_cleanup); module_exit(evtchn_cleanup);
MODULE_DESCRIPTION("Xen /dev/xen/evtchn device driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -208,7 +208,7 @@ static void do_reboot(void) ...@@ -208,7 +208,7 @@ static void do_reboot(void)
orderly_reboot(); orderly_reboot();
} }
static struct shutdown_handler shutdown_handlers[] = { static const struct shutdown_handler shutdown_handlers[] = {
{ "poweroff", true, do_poweroff }, { "poweroff", true, do_poweroff },
{ "halt", false, do_poweroff }, { "halt", false, do_poweroff },
{ "reboot", true, do_reboot }, { "reboot", true, do_reboot },
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "privcmd.h" #include "privcmd.h"
MODULE_DESCRIPTION("Xen Mmap of hypercall buffers");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
struct privcmd_buf_private { struct privcmd_buf_private {
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/srcu.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/errno.h> #include <linux/errno.h>
...@@ -48,6 +49,7 @@ ...@@ -48,6 +49,7 @@
#include "privcmd.h" #include "privcmd.h"
MODULE_DESCRIPTION("Xen hypercall passthrough driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#define PRIV_VMA_LOCKED ((void *)1) #define PRIV_VMA_LOCKED ((void *)1)
...@@ -845,7 +847,8 @@ static long privcmd_ioctl_mmap_resource(struct file *file, ...@@ -845,7 +847,8 @@ static long privcmd_ioctl_mmap_resource(struct file *file,
#ifdef CONFIG_XEN_PRIVCMD_EVENTFD #ifdef CONFIG_XEN_PRIVCMD_EVENTFD
/* Irqfd support */ /* Irqfd support */
static struct workqueue_struct *irqfd_cleanup_wq; static struct workqueue_struct *irqfd_cleanup_wq;
static DEFINE_MUTEX(irqfds_lock); static DEFINE_SPINLOCK(irqfds_lock);
DEFINE_STATIC_SRCU(irqfds_srcu);
static LIST_HEAD(irqfds_list); static LIST_HEAD(irqfds_list);
struct privcmd_kernel_irqfd { struct privcmd_kernel_irqfd {
...@@ -873,6 +876,9 @@ static void irqfd_shutdown(struct work_struct *work) ...@@ -873,6 +876,9 @@ static void irqfd_shutdown(struct work_struct *work)
container_of(work, struct privcmd_kernel_irqfd, shutdown); container_of(work, struct privcmd_kernel_irqfd, shutdown);
u64 cnt; u64 cnt;
/* Make sure irqfd has been initialized in assign path */
synchronize_srcu(&irqfds_srcu);
eventfd_ctx_remove_wait_queue(kirqfd->eventfd, &kirqfd->wait, &cnt); eventfd_ctx_remove_wait_queue(kirqfd->eventfd, &kirqfd->wait, &cnt);
eventfd_ctx_put(kirqfd->eventfd); eventfd_ctx_put(kirqfd->eventfd);
kfree(kirqfd); kfree(kirqfd);
...@@ -909,9 +915,11 @@ irqfd_wakeup(wait_queue_entry_t *wait, unsigned int mode, int sync, void *key) ...@@ -909,9 +915,11 @@ irqfd_wakeup(wait_queue_entry_t *wait, unsigned int mode, int sync, void *key)
irqfd_inject(kirqfd); irqfd_inject(kirqfd);
if (flags & EPOLLHUP) { if (flags & EPOLLHUP) {
mutex_lock(&irqfds_lock); unsigned long flags;
spin_lock_irqsave(&irqfds_lock, flags);
irqfd_deactivate(kirqfd); irqfd_deactivate(kirqfd);
mutex_unlock(&irqfds_lock); spin_unlock_irqrestore(&irqfds_lock, flags);
} }
return 0; return 0;
...@@ -929,10 +937,11 @@ irqfd_poll_func(struct file *file, wait_queue_head_t *wqh, poll_table *pt) ...@@ -929,10 +937,11 @@ irqfd_poll_func(struct file *file, wait_queue_head_t *wqh, poll_table *pt)
static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd) static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd)
{ {
struct privcmd_kernel_irqfd *kirqfd, *tmp; struct privcmd_kernel_irqfd *kirqfd, *tmp;
unsigned long flags;
__poll_t events; __poll_t events;
struct fd f; struct fd f;
void *dm_op; void *dm_op;
int ret; int ret, idx;
kirqfd = kzalloc(sizeof(*kirqfd) + irqfd->size, GFP_KERNEL); kirqfd = kzalloc(sizeof(*kirqfd) + irqfd->size, GFP_KERNEL);
if (!kirqfd) if (!kirqfd)
...@@ -968,18 +977,19 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd) ...@@ -968,18 +977,19 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd)
init_waitqueue_func_entry(&kirqfd->wait, irqfd_wakeup); init_waitqueue_func_entry(&kirqfd->wait, irqfd_wakeup);
init_poll_funcptr(&kirqfd->pt, irqfd_poll_func); init_poll_funcptr(&kirqfd->pt, irqfd_poll_func);
mutex_lock(&irqfds_lock); spin_lock_irqsave(&irqfds_lock, flags);
list_for_each_entry(tmp, &irqfds_list, list) { list_for_each_entry(tmp, &irqfds_list, list) {
if (kirqfd->eventfd == tmp->eventfd) { if (kirqfd->eventfd == tmp->eventfd) {
ret = -EBUSY; ret = -EBUSY;
mutex_unlock(&irqfds_lock); spin_unlock_irqrestore(&irqfds_lock, flags);
goto error_eventfd; goto error_eventfd;
} }
} }
idx = srcu_read_lock(&irqfds_srcu);
list_add_tail(&kirqfd->list, &irqfds_list); list_add_tail(&kirqfd->list, &irqfds_list);
mutex_unlock(&irqfds_lock); spin_unlock_irqrestore(&irqfds_lock, flags);
/* /*
* Check if there was an event already pending on the eventfd before we * Check if there was an event already pending on the eventfd before we
...@@ -989,6 +999,8 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd) ...@@ -989,6 +999,8 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd)
if (events & EPOLLIN) if (events & EPOLLIN)
irqfd_inject(kirqfd); irqfd_inject(kirqfd);
srcu_read_unlock(&irqfds_srcu, idx);
/* /*
* Do not drop the file until the kirqfd is fully initialized, otherwise * Do not drop the file until the kirqfd is fully initialized, otherwise
* we might race against the EPOLLHUP. * we might race against the EPOLLHUP.
...@@ -1011,12 +1023,13 @@ static int privcmd_irqfd_deassign(struct privcmd_irqfd *irqfd) ...@@ -1011,12 +1023,13 @@ static int privcmd_irqfd_deassign(struct privcmd_irqfd *irqfd)
{ {
struct privcmd_kernel_irqfd *kirqfd; struct privcmd_kernel_irqfd *kirqfd;
struct eventfd_ctx *eventfd; struct eventfd_ctx *eventfd;
unsigned long flags;
eventfd = eventfd_ctx_fdget(irqfd->fd); eventfd = eventfd_ctx_fdget(irqfd->fd);
if (IS_ERR(eventfd)) if (IS_ERR(eventfd))
return PTR_ERR(eventfd); return PTR_ERR(eventfd);
mutex_lock(&irqfds_lock); spin_lock_irqsave(&irqfds_lock, flags);
list_for_each_entry(kirqfd, &irqfds_list, list) { list_for_each_entry(kirqfd, &irqfds_list, list) {
if (kirqfd->eventfd == eventfd) { if (kirqfd->eventfd == eventfd) {
...@@ -1025,7 +1038,7 @@ static int privcmd_irqfd_deassign(struct privcmd_irqfd *irqfd) ...@@ -1025,7 +1038,7 @@ static int privcmd_irqfd_deassign(struct privcmd_irqfd *irqfd)
} }
} }
mutex_unlock(&irqfds_lock); spin_unlock_irqrestore(&irqfds_lock, flags);
eventfd_ctx_put(eventfd); eventfd_ctx_put(eventfd);
...@@ -1073,13 +1086,14 @@ static int privcmd_irqfd_init(void) ...@@ -1073,13 +1086,14 @@ static int privcmd_irqfd_init(void)
static void privcmd_irqfd_exit(void) static void privcmd_irqfd_exit(void)
{ {
struct privcmd_kernel_irqfd *kirqfd, *tmp; struct privcmd_kernel_irqfd *kirqfd, *tmp;
unsigned long flags;
mutex_lock(&irqfds_lock); spin_lock_irqsave(&irqfds_lock, flags);
list_for_each_entry_safe(kirqfd, tmp, &irqfds_list, list) list_for_each_entry_safe(kirqfd, tmp, &irqfds_list, list)
irqfd_deactivate(kirqfd); irqfd_deactivate(kirqfd);
mutex_unlock(&irqfds_lock); spin_unlock_irqrestore(&irqfds_lock, flags);
destroy_workqueue(irqfd_cleanup_wq); destroy_workqueue(irqfd_cleanup_wq);
} }
......
...@@ -1708,5 +1708,6 @@ static void __exit xen_pcibk_cleanup(void) ...@@ -1708,5 +1708,6 @@ static void __exit xen_pcibk_cleanup(void)
module_init(xen_pcibk_init); module_init(xen_pcibk_init);
module_exit(xen_pcibk_cleanup); module_exit(xen_pcibk_cleanup);
MODULE_DESCRIPTION("Xen PCI-device stub driver");
MODULE_LICENSE("Dual BSD/GPL"); MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("xen-backend:pci"); MODULE_ALIAS("xen-backend:pci");
...@@ -144,4 +144,6 @@ static inline void xen_evtchn_close(evtchn_port_t port) ...@@ -144,4 +144,6 @@ static inline void xen_evtchn_close(evtchn_port_t port)
BUG(); BUG();
} }
extern bool xen_fifo_events;
#endif /* _XEN_EVENTS_H */ #endif /* _XEN_EVENTS_H */
...@@ -583,7 +583,7 @@ EXPORT_SYMBOL(queued_spin_lock_slowpath); ...@@ -583,7 +583,7 @@ EXPORT_SYMBOL(queued_spin_lock_slowpath);
#include "qspinlock_paravirt.h" #include "qspinlock_paravirt.h"
#include "qspinlock.c" #include "qspinlock.c"
bool nopvspin __initdata; bool nopvspin;
static __init int parse_nopvspin(char *arg) static __init int parse_nopvspin(char *arg)
{ {
nopvspin = true; nopvspin = true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment