mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-29 09:13:38 +00:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
fc993be36f
@ -157,7 +157,7 @@ Each layer
|
||||
2. Enable one of components (smb, auth, vfs, oplock, ipc, conn, rdma)
|
||||
# sudo ksmbd.control -d "smb"
|
||||
|
||||
3. Show what prints are enable.
|
||||
3. Show what prints are enabled.
|
||||
# cat /sys/class/ksmbd-control/debug
|
||||
[smb] auth vfs oplock ipc conn [rdma]
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=================================
|
||||
NETWORK FILESYSTEM HELPER LIBRARY
|
||||
Network Filesystem Helper Library
|
||||
=================================
|
||||
|
||||
.. Contents:
|
||||
@ -37,22 +37,22 @@ into a common call framework.
|
||||
|
||||
The following services are provided:
|
||||
|
||||
* Handles transparent huge pages (THPs).
|
||||
* Handle folios that span multiple pages.
|
||||
|
||||
* Insulates the netfs from VM interface changes.
|
||||
* Insulate the netfs from VM interface changes.
|
||||
|
||||
* Allows the netfs to arbitrarily split reads up into pieces, even ones that
|
||||
don't match page sizes or page alignments and that may cross pages.
|
||||
* Allow the netfs to arbitrarily split reads up into pieces, even ones that
|
||||
don't match folio sizes or folio alignments and that may cross folios.
|
||||
|
||||
* Allows the netfs to expand a readahead request in both directions to meet
|
||||
its needs.
|
||||
* Allow the netfs to expand a readahead request in both directions to meet its
|
||||
needs.
|
||||
|
||||
* Allows the netfs to partially fulfil a read, which will then be resubmitted.
|
||||
* Allow the netfs to partially fulfil a read, which will then be resubmitted.
|
||||
|
||||
* Handles local caching, allowing cached data and server-read data to be
|
||||
* Handle local caching, allowing cached data and server-read data to be
|
||||
interleaved for a single request.
|
||||
|
||||
* Handles clearing of bufferage that aren't on the server.
|
||||
* Handle clearing of bufferage that aren't on the server.
|
||||
|
||||
* Handle retrying of reads that failed, switching reads from the cache to the
|
||||
server as necessary.
|
||||
@ -70,19 +70,19 @@ Read Helper Functions
|
||||
|
||||
Three read helpers are provided::
|
||||
|
||||
* void netfs_readahead(struct readahead_control *ractl,
|
||||
const struct netfs_read_request_ops *ops,
|
||||
void *netfs_priv);``
|
||||
* int netfs_readpage(struct file *file,
|
||||
struct page *page,
|
||||
void netfs_readahead(struct readahead_control *ractl,
|
||||
const struct netfs_read_request_ops *ops,
|
||||
void *netfs_priv);
|
||||
* int netfs_write_begin(struct file *file,
|
||||
int netfs_readpage(struct file *file,
|
||||
struct folio *folio,
|
||||
const struct netfs_read_request_ops *ops,
|
||||
void *netfs_priv);
|
||||
int netfs_write_begin(struct file *file,
|
||||
struct address_space *mapping,
|
||||
loff_t pos,
|
||||
unsigned int len,
|
||||
unsigned int flags,
|
||||
struct page **_page,
|
||||
struct folio **_folio,
|
||||
void **_fsdata,
|
||||
const struct netfs_read_request_ops *ops,
|
||||
void *netfs_priv);
|
||||
@ -103,8 +103,8 @@ Both of these values will be stored into the read request structure.
|
||||
For ->readahead() and ->readpage(), the network filesystem should just jump
|
||||
into the corresponding read helper; whereas for ->write_begin(), it may be a
|
||||
little more complicated as the network filesystem might want to flush
|
||||
conflicting writes or track dirty data and needs to put the acquired page if an
|
||||
error occurs after calling the helper.
|
||||
conflicting writes or track dirty data and needs to put the acquired folio if
|
||||
an error occurs after calling the helper.
|
||||
|
||||
The helpers manage the read request, calling back into the network filesystem
|
||||
through the suppplied table of operations. Waits will be performed as
|
||||
@ -253,7 +253,7 @@ through which it can issue requests and negotiate::
|
||||
void (*issue_op)(struct netfs_read_subrequest *subreq);
|
||||
bool (*is_still_valid)(struct netfs_read_request *rreq);
|
||||
int (*check_write_begin)(struct file *file, loff_t pos, unsigned len,
|
||||
struct page *page, void **_fsdata);
|
||||
struct folio *folio, void **_fsdata);
|
||||
void (*done)(struct netfs_read_request *rreq);
|
||||
void (*cleanup)(struct address_space *mapping, void *netfs_priv);
|
||||
};
|
||||
@ -313,13 +313,14 @@ The operations are as follows:
|
||||
|
||||
There is no return value; the netfs_subreq_terminated() function should be
|
||||
called to indicate whether or not the operation succeeded and how much data
|
||||
it transferred. The filesystem also should not deal with setting pages
|
||||
it transferred. The filesystem also should not deal with setting folios
|
||||
uptodate, unlocking them or dropping their refs - the helpers need to deal
|
||||
with this as they have to coordinate with copying to the local cache.
|
||||
|
||||
Note that the helpers have the pages locked, but not pinned. It is possible
|
||||
to use the ITER_XARRAY iov iterator to refer to the range of the inode that
|
||||
is being operated upon without the need to allocate large bvec tables.
|
||||
Note that the helpers have the folios locked, but not pinned. It is
|
||||
possible to use the ITER_XARRAY iov iterator to refer to the range of the
|
||||
inode that is being operated upon without the need to allocate large bvec
|
||||
tables.
|
||||
|
||||
* ``is_still_valid()``
|
||||
|
||||
@ -330,15 +331,15 @@ The operations are as follows:
|
||||
* ``check_write_begin()``
|
||||
|
||||
[Optional] This is called from the netfs_write_begin() helper once it has
|
||||
allocated/grabbed the page to be modified to allow the filesystem to flush
|
||||
allocated/grabbed the folio to be modified to allow the filesystem to flush
|
||||
conflicting state before allowing it to be modified.
|
||||
|
||||
It should return 0 if everything is now fine, -EAGAIN if the page should be
|
||||
It should return 0 if everything is now fine, -EAGAIN if the folio should be
|
||||
regrabbed and any other error code to abort the operation.
|
||||
|
||||
* ``done``
|
||||
|
||||
[Optional] This is called after the pages in the request have all been
|
||||
[Optional] This is called after the folios in the request have all been
|
||||
unlocked (and marked uptodate if applicable).
|
||||
|
||||
* ``cleanup``
|
||||
@ -390,7 +391,7 @@ The read helpers work by the following general procedure:
|
||||
* If NETFS_SREQ_CLEAR_TAIL was set, a short read will be cleared to the
|
||||
end of the slice instead of reissuing.
|
||||
|
||||
* Once the data is read, the pages that have been fully read/cleared:
|
||||
* Once the data is read, the folios that have been fully read/cleared:
|
||||
|
||||
* Will be marked uptodate.
|
||||
|
||||
@ -398,11 +399,11 @@ The read helpers work by the following general procedure:
|
||||
|
||||
* Unlocked
|
||||
|
||||
* Any pages that need writing to the cache will then have DIO writes issued.
|
||||
* Any folios that need writing to the cache will then have DIO writes issued.
|
||||
|
||||
* Synchronous operations will wait for reading to be complete.
|
||||
|
||||
* Writes to the cache will proceed asynchronously and the pages will have the
|
||||
* Writes to the cache will proceed asynchronously and the folios will have the
|
||||
PG_fscache mark removed when that completes.
|
||||
|
||||
* The request structures will be cleaned up when everything has completed.
|
||||
@ -452,6 +453,9 @@ operation table looks like the following::
|
||||
netfs_io_terminated_t term_func,
|
||||
void *term_func_priv);
|
||||
|
||||
int (*prepare_write)(struct netfs_cache_resources *cres,
|
||||
loff_t *_start, size_t *_len, loff_t i_size);
|
||||
|
||||
int (*write)(struct netfs_cache_resources *cres,
|
||||
loff_t start_pos,
|
||||
struct iov_iter *iter,
|
||||
@ -509,6 +513,14 @@ The methods defined in the table are:
|
||||
indicating whether the termination is definitely happening in the caller's
|
||||
context.
|
||||
|
||||
* ``prepare_write()``
|
||||
|
||||
[Required] Called to adjust a write to the cache and check that there is
|
||||
sufficient space in the cache. The start and length values indicate the
|
||||
size of the write that netfslib is proposing, and this can be adjusted by
|
||||
the cache to respect DIO boundaries. The file size is passed for
|
||||
information.
|
||||
|
||||
* ``write()``
|
||||
|
||||
[Required] Called to write to the cache. The start file offset is given
|
||||
@ -525,4 +537,9 @@ not the read request structure as they could be used in other situations where
|
||||
there isn't a read request structure as well, such as writing dirty data to the
|
||||
cache.
|
||||
|
||||
|
||||
API Function Reference
|
||||
======================
|
||||
|
||||
.. kernel-doc:: include/linux/netfs.h
|
||||
.. kernel-doc:: fs/netfs/read_helper.c
|
||||
|
@ -15994,6 +15994,7 @@ F: arch/mips/generic/board-ranchu.c
|
||||
|
||||
RANDOM NUMBER DRIVER
|
||||
M: "Theodore Ts'o" <tytso@mit.edu>
|
||||
M: Jason A. Donenfeld <Jason@zx2c4.com>
|
||||
S: Maintained
|
||||
F: drivers/char/random.c
|
||||
|
||||
@ -16638,7 +16639,8 @@ F: drivers/iommu/s390-iommu.c
|
||||
|
||||
S390 IUCV NETWORK LAYER
|
||||
M: Julian Wiedmann <jwi@linux.ibm.com>
|
||||
M: Karsten Graul <kgraul@linux.ibm.com>
|
||||
M: Alexandra Winter <wintera@linux.ibm.com>
|
||||
M: Wenjia Zhang <wenjia@linux.ibm.com>
|
||||
L: linux-s390@vger.kernel.org
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
@ -16649,7 +16651,8 @@ F: net/iucv/
|
||||
|
||||
S390 NETWORK DRIVERS
|
||||
M: Julian Wiedmann <jwi@linux.ibm.com>
|
||||
M: Karsten Graul <kgraul@linux.ibm.com>
|
||||
M: Alexandra Winter <wintera@linux.ibm.com>
|
||||
M: Wenjia Zhang <wenjia@linux.ibm.com>
|
||||
L: linux-s390@vger.kernel.org
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
|
2
Makefile
2
Makefile
@ -2,7 +2,7 @@
|
||||
VERSION = 5
|
||||
PATCHLEVEL = 16
|
||||
SUBLEVEL = 0
|
||||
EXTRAVERSION = -rc2
|
||||
EXTRAVERSION = -rc3
|
||||
NAME = Gobble Gobble
|
||||
|
||||
# *DOCUMENTATION*
|
||||
|
10
arch/Kconfig
10
arch/Kconfig
@ -991,6 +991,16 @@ config HAVE_ARCH_COMPAT_MMAP_BASES
|
||||
and vice-versa 32-bit applications to call 64-bit mmap().
|
||||
Required for applications doing different bitness syscalls.
|
||||
|
||||
config PAGE_SIZE_LESS_THAN_64KB
|
||||
def_bool y
|
||||
depends on !ARM64_64K_PAGES
|
||||
depends on !IA64_PAGE_SIZE_64KB
|
||||
depends on !PAGE_SIZE_64KB
|
||||
depends on !PARISC_PAGE_SIZE_64KB
|
||||
depends on !PPC_64K_PAGES
|
||||
depends on !PPC_256K_PAGES
|
||||
depends on !PAGE_SIZE_256KB
|
||||
|
||||
# This allows to use a set of generic functions to determine mmap base
|
||||
# address by giving priority to top-down scheme only if the process
|
||||
# is not in legacy mode (compat task, unlimited stack size or
|
||||
|
@ -91,7 +91,7 @@
|
||||
#define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H)
|
||||
|
||||
/* TCR_EL2 Registers bits */
|
||||
#define TCR_EL2_RES1 ((1 << 31) | (1 << 23))
|
||||
#define TCR_EL2_RES1 ((1U << 31) | (1 << 23))
|
||||
#define TCR_EL2_TBI (1 << 20)
|
||||
#define TCR_EL2_PS_SHIFT 16
|
||||
#define TCR_EL2_PS_MASK (7 << TCR_EL2_PS_SHIFT)
|
||||
@ -276,7 +276,7 @@
|
||||
#define CPTR_EL2_TFP_SHIFT 10
|
||||
|
||||
/* Hyp Coprocessor Trap Register */
|
||||
#define CPTR_EL2_TCPAC (1 << 31)
|
||||
#define CPTR_EL2_TCPAC (1U << 31)
|
||||
#define CPTR_EL2_TAM (1 << 30)
|
||||
#define CPTR_EL2_TTA (1 << 20)
|
||||
#define CPTR_EL2_TFP (1 << CPTR_EL2_TFP_SHIFT)
|
||||
|
@ -403,6 +403,8 @@ typedef bool (*exit_handler_fn)(struct kvm_vcpu *, u64 *);
|
||||
|
||||
static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu);
|
||||
|
||||
static void early_exit_filter(struct kvm_vcpu *vcpu, u64 *exit_code);
|
||||
|
||||
/*
|
||||
* Allow the hypervisor to handle the exit with an exit handler if it has one.
|
||||
*
|
||||
@ -429,6 +431,18 @@ static inline bool kvm_hyp_handle_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
*/
|
||||
static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
{
|
||||
/*
|
||||
* Save PSTATE early so that we can evaluate the vcpu mode
|
||||
* early on.
|
||||
*/
|
||||
vcpu->arch.ctxt.regs.pstate = read_sysreg_el2(SYS_SPSR);
|
||||
|
||||
/*
|
||||
* Check whether we want to repaint the state one way or
|
||||
* another.
|
||||
*/
|
||||
early_exit_filter(vcpu, exit_code);
|
||||
|
||||
if (ARM_EXCEPTION_CODE(*exit_code) != ARM_EXCEPTION_IRQ)
|
||||
vcpu->arch.fault.esr_el2 = read_sysreg_el2(SYS_ESR);
|
||||
|
||||
|
@ -70,6 +70,11 @@ static inline void __sysreg_save_el1_state(struct kvm_cpu_context *ctxt)
|
||||
static inline void __sysreg_save_el2_return_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
ctxt->regs.pc = read_sysreg_el2(SYS_ELR);
|
||||
/*
|
||||
* Guest PSTATE gets saved at guest fixup time in all
|
||||
* cases. We still need to handle the nVHE host side here.
|
||||
*/
|
||||
if (!has_vhe() && ctxt->__hyp_running_vcpu)
|
||||
ctxt->regs.pstate = read_sysreg_el2(SYS_SPSR);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN))
|
||||
|
@ -233,7 +233,7 @@ static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)
|
||||
* Returns false if the guest ran in AArch32 when it shouldn't have, and
|
||||
* thus should exit to the host, or true if a the guest run loop can continue.
|
||||
*/
|
||||
static bool handle_aarch32_guest(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
static void early_exit_filter(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
{
|
||||
struct kvm *kvm = kern_hyp_va(vcpu->kvm);
|
||||
|
||||
@ -248,10 +248,7 @@ static bool handle_aarch32_guest(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
vcpu->arch.target = -1;
|
||||
*exit_code &= BIT(ARM_EXIT_WITH_SERROR_BIT);
|
||||
*exit_code |= ARM_EXCEPTION_IL;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Switch to the guest for legacy non-VHE systems */
|
||||
@ -316,9 +313,6 @@ int __kvm_vcpu_run(struct kvm_vcpu *vcpu)
|
||||
/* Jump in the fire! */
|
||||
exit_code = __guest_enter(vcpu);
|
||||
|
||||
if (unlikely(!handle_aarch32_guest(vcpu, &exit_code)))
|
||||
break;
|
||||
|
||||
/* And we're baaack! */
|
||||
} while (fixup_guest_exit(vcpu, &exit_code));
|
||||
|
||||
|
@ -112,6 +112,10 @@ static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)
|
||||
return hyp_exit_handlers;
|
||||
}
|
||||
|
||||
static void early_exit_filter(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
{
|
||||
}
|
||||
|
||||
/* Switch to the guest for VHE systems running in EL2 */
|
||||
static int __kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
|
@ -3097,7 +3097,7 @@ config STACKTRACE_SUPPORT
|
||||
config PGTABLE_LEVELS
|
||||
int
|
||||
default 4 if PAGE_SIZE_4KB && MIPS_VA_BITS_48
|
||||
default 3 if 64BIT && !PAGE_SIZE_64KB
|
||||
default 3 if 64BIT && (!PAGE_SIZE_64KB || MIPS_VA_BITS_48)
|
||||
default 2
|
||||
|
||||
config MIPS_AUTO_PFN_OFFSET
|
||||
|
@ -52,7 +52,7 @@ endif
|
||||
|
||||
vmlinuzobjs-$(CONFIG_KERNEL_XZ) += $(obj)/ashldi3.o
|
||||
|
||||
vmlinuzobjs-$(CONFIG_KERNEL_ZSTD) += $(obj)/bswapdi.o
|
||||
vmlinuzobjs-$(CONFIG_KERNEL_ZSTD) += $(obj)/bswapdi.o $(obj)/ashldi3.o
|
||||
|
||||
targets := $(notdir $(vmlinuzobjs-y))
|
||||
|
||||
|
@ -1734,8 +1734,6 @@ static inline void decode_cpucfg(struct cpuinfo_mips *c)
|
||||
|
||||
static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu)
|
||||
{
|
||||
decode_configs(c);
|
||||
|
||||
/* All Loongson processors covered here define ExcCode 16 as GSExc. */
|
||||
c->options |= MIPS_CPU_GSEXCEX;
|
||||
|
||||
@ -1796,6 +1794,8 @@ static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu)
|
||||
panic("Unknown Loongson Processor ID!");
|
||||
break;
|
||||
}
|
||||
|
||||
decode_configs(c);
|
||||
}
|
||||
#else
|
||||
static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) { }
|
||||
|
@ -185,7 +185,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
|
||||
seq_puts(m, " tx39_cache");
|
||||
if (cpu_has_octeon_cache)
|
||||
seq_puts(m, " octeon_cache");
|
||||
if (cpu_has_fpu)
|
||||
if (raw_cpu_has_fpu)
|
||||
seq_puts(m, " fpu");
|
||||
if (cpu_has_32fpr)
|
||||
seq_puts(m, " 32fpr");
|
||||
|
@ -202,11 +202,11 @@ _ASM_NOKPROBE_SYMBOL(\name\()_virt)
|
||||
mfspr r1, SPRN_SPRG_THREAD
|
||||
lwz r1, TASK_CPU - THREAD(r1)
|
||||
slwi r1, r1, 3
|
||||
addis r1, r1, emergency_ctx@ha
|
||||
addis r1, r1, emergency_ctx-PAGE_OFFSET@ha
|
||||
#else
|
||||
lis r1, emergency_ctx@ha
|
||||
lis r1, emergency_ctx-PAGE_OFFSET@ha
|
||||
#endif
|
||||
lwz r1, emergency_ctx@l(r1)
|
||||
lwz r1, emergency_ctx-PAGE_OFFSET@l(r1)
|
||||
addi r1, r1, THREAD_SIZE - INT_FRAME_SIZE
|
||||
EXCEPTION_PROLOG_2 0 vmap_stack_overflow
|
||||
prepare_transfer_to_handler
|
||||
|
@ -695,6 +695,7 @@ static void flush_guest_tlb(struct kvm *kvm)
|
||||
"r" (0) : "memory");
|
||||
}
|
||||
asm volatile("ptesync": : :"memory");
|
||||
// POWER9 congruence-class TLBIEL leaves ERAT. Flush it now.
|
||||
asm volatile(PPC_RADIX_INVALIDATE_ERAT_GUEST : : :"memory");
|
||||
} else {
|
||||
for (set = 0; set < kvm->arch.tlb_sets; ++set) {
|
||||
@ -705,6 +706,8 @@ static void flush_guest_tlb(struct kvm *kvm)
|
||||
rb += PPC_BIT(51); /* increment set number */
|
||||
}
|
||||
asm volatile("ptesync": : :"memory");
|
||||
// POWER9 congruence-class TLBIEL leaves ERAT. Flush it now.
|
||||
if (cpu_has_feature(CPU_FTR_ARCH_300))
|
||||
asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT : : :"memory");
|
||||
}
|
||||
}
|
||||
|
@ -12,14 +12,12 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_types.h>
|
||||
#include <asm/csr.h>
|
||||
#include <asm/kvm_vcpu_fp.h>
|
||||
#include <asm/kvm_vcpu_timer.h>
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
#define KVM_MAX_VCPUS (1U << 16)
|
||||
#else
|
||||
#define KVM_MAX_VCPUS (1U << 9)
|
||||
#endif
|
||||
#define KVM_MAX_VCPUS \
|
||||
((HGATP_VMID_MASK >> HGATP_VMID_SHIFT) + 1)
|
||||
|
||||
#define KVM_HALT_POLL_NS_DEFAULT 500000
|
||||
|
||||
|
@ -453,6 +453,12 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm)
|
||||
void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
|
||||
struct kvm_memory_slot *slot)
|
||||
{
|
||||
gpa_t gpa = slot->base_gfn << PAGE_SHIFT;
|
||||
phys_addr_t size = slot->npages << PAGE_SHIFT;
|
||||
|
||||
spin_lock(&kvm->mmu_lock);
|
||||
stage2_unmap_range(kvm, gpa, size, false);
|
||||
spin_unlock(&kvm->mmu_lock);
|
||||
}
|
||||
|
||||
void kvm_arch_commit_memory_region(struct kvm *kvm,
|
||||
|
@ -102,12 +102,6 @@ extern void switch_fpu_return(void);
|
||||
*/
|
||||
extern int cpu_has_xfeatures(u64 xfeatures_mask, const char **feature_name);
|
||||
|
||||
/*
|
||||
* Tasks that are not using SVA have mm->pasid set to zero to note that they
|
||||
* will not have the valid bit set in MSR_IA32_PASID while they are running.
|
||||
*/
|
||||
#define PASID_DISABLED 0
|
||||
|
||||
/* Trap handling */
|
||||
extern int fpu__exception_code(struct fpu *fpu, int trap_nr);
|
||||
extern void fpu_sync_fpstate(struct fpu *fpu);
|
||||
|
@ -742,7 +742,7 @@ dump_kernel_offset(struct notifier_block *self, unsigned long v, void *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *prepare_command_line(void)
|
||||
static char * __init prepare_command_line(void)
|
||||
{
|
||||
#ifdef CONFIG_CMDLINE_BOOL
|
||||
#ifdef CONFIG_CMDLINE_OVERRIDE
|
||||
|
@ -81,7 +81,6 @@ struct kvm_ioapic {
|
||||
unsigned long irq_states[IOAPIC_NUM_PINS];
|
||||
struct kvm_io_device dev;
|
||||
struct kvm *kvm;
|
||||
void (*ack_notifier)(void *opaque, int irq);
|
||||
spinlock_t lock;
|
||||
struct rtc_status rtc_status;
|
||||
struct delayed_work eoi_inject;
|
||||
|
@ -56,7 +56,6 @@ struct kvm_pic {
|
||||
struct kvm_io_device dev_master;
|
||||
struct kvm_io_device dev_slave;
|
||||
struct kvm_io_device dev_elcr;
|
||||
void (*ack_notifier)(void *opaque, int irq);
|
||||
unsigned long irq_states[PIC_NUM_PINS];
|
||||
};
|
||||
|
||||
|
@ -707,7 +707,7 @@ static void pv_eoi_clr_pending(struct kvm_vcpu *vcpu)
|
||||
static int apic_has_interrupt_for_ppr(struct kvm_lapic *apic, u32 ppr)
|
||||
{
|
||||
int highest_irr;
|
||||
if (apic->vcpu->arch.apicv_active)
|
||||
if (kvm_x86_ops.sync_pir_to_irr)
|
||||
highest_irr = static_call(kvm_x86_sync_pir_to_irr)(apic->vcpu);
|
||||
else
|
||||
highest_irr = apic_find_highest_irr(apic);
|
||||
|
@ -1582,7 +1582,7 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
|
||||
flush = kvm_handle_gfn_range(kvm, range, kvm_unmap_rmapp);
|
||||
|
||||
if (is_tdp_mmu_enabled(kvm))
|
||||
flush |= kvm_tdp_mmu_unmap_gfn_range(kvm, range, flush);
|
||||
flush = kvm_tdp_mmu_unmap_gfn_range(kvm, range, flush);
|
||||
|
||||
return flush;
|
||||
}
|
||||
@ -2173,10 +2173,10 @@ static void shadow_walk_init_using_root(struct kvm_shadow_walk_iterator *iterato
|
||||
iterator->shadow_addr = root;
|
||||
iterator->level = vcpu->arch.mmu->shadow_root_level;
|
||||
|
||||
if (iterator->level == PT64_ROOT_4LEVEL &&
|
||||
if (iterator->level >= PT64_ROOT_4LEVEL &&
|
||||
vcpu->arch.mmu->root_level < PT64_ROOT_4LEVEL &&
|
||||
!vcpu->arch.mmu->direct_map)
|
||||
--iterator->level;
|
||||
iterator->level = PT32E_ROOT_LEVEL;
|
||||
|
||||
if (iterator->level == PT32E_ROOT_LEVEL) {
|
||||
/*
|
||||
@ -4855,7 +4855,7 @@ void kvm_init_shadow_npt_mmu(struct kvm_vcpu *vcpu, unsigned long cr0,
|
||||
struct kvm_mmu *context = &vcpu->arch.guest_mmu;
|
||||
struct kvm_mmu_role_regs regs = {
|
||||
.cr0 = cr0,
|
||||
.cr4 = cr4,
|
||||
.cr4 = cr4 & ~X86_CR4_PKE,
|
||||
.efer = efer,
|
||||
};
|
||||
union kvm_mmu_role new_role;
|
||||
@ -4919,7 +4919,7 @@ void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly,
|
||||
context->direct_map = false;
|
||||
|
||||
update_permission_bitmask(context, true);
|
||||
update_pkru_bitmask(context);
|
||||
context->pkru_mask = 0;
|
||||
reset_rsvds_bits_mask_ept(vcpu, context, execonly);
|
||||
reset_ept_shadow_zero_bits_mask(vcpu, context, execonly);
|
||||
}
|
||||
@ -5025,6 +5025,14 @@ void kvm_mmu_after_set_cpuid(struct kvm_vcpu *vcpu)
|
||||
/*
|
||||
* Invalidate all MMU roles to force them to reinitialize as CPUID
|
||||
* information is factored into reserved bit calculations.
|
||||
*
|
||||
* Correctly handling multiple vCPU models with respect to paging and
|
||||
* physical address properties) in a single VM would require tracking
|
||||
* all relevant CPUID information in kvm_mmu_page_role. That is very
|
||||
* undesirable as it would increase the memory requirements for
|
||||
* gfn_track (see struct kvm_mmu_page_role comments). For now that
|
||||
* problem is swept under the rug; KVM's CPUID API is horrific and
|
||||
* it's all but impossible to solve it without introducing a new API.
|
||||
*/
|
||||
vcpu->arch.root_mmu.mmu_role.ext.valid = 0;
|
||||
vcpu->arch.guest_mmu.mmu_role.ext.valid = 0;
|
||||
@ -5032,24 +5040,10 @@ void kvm_mmu_after_set_cpuid(struct kvm_vcpu *vcpu)
|
||||
kvm_mmu_reset_context(vcpu);
|
||||
|
||||
/*
|
||||
* KVM does not correctly handle changing guest CPUID after KVM_RUN, as
|
||||
* MAXPHYADDR, GBPAGES support, AMD reserved bit behavior, etc.. aren't
|
||||
* tracked in kvm_mmu_page_role. As a result, KVM may miss guest page
|
||||
* faults due to reusing SPs/SPTEs. Alert userspace, but otherwise
|
||||
* sweep the problem under the rug.
|
||||
*
|
||||
* KVM's horrific CPUID ABI makes the problem all but impossible to
|
||||
* solve, as correctly handling multiple vCPU models (with respect to
|
||||
* paging and physical address properties) in a single VM would require
|
||||
* tracking all relevant CPUID information in kvm_mmu_page_role. That
|
||||
* is very undesirable as it would double the memory requirements for
|
||||
* gfn_track (see struct kvm_mmu_page_role comments), and in practice
|
||||
* no sane VMM mucks with the core vCPU model on the fly.
|
||||
* Changing guest CPUID after KVM_RUN is forbidden, see the comment in
|
||||
* kvm_arch_vcpu_ioctl().
|
||||
*/
|
||||
if (vcpu->arch.last_vmentry_cpu != -1) {
|
||||
pr_warn_ratelimited("KVM: KVM_SET_CPUID{,2} after KVM_RUN may cause guest instability\n");
|
||||
pr_warn_ratelimited("KVM: KVM_SET_CPUID{,2} will fail after KVM_RUN starting with Linux 5.16\n");
|
||||
}
|
||||
KVM_BUG_ON(vcpu->arch.last_vmentry_cpu != -1, vcpu->kvm);
|
||||
}
|
||||
|
||||
void kvm_mmu_reset_context(struct kvm_vcpu *vcpu)
|
||||
@ -5369,7 +5363,7 @@ void kvm_mmu_invalidate_gva(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
|
||||
|
||||
void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva)
|
||||
{
|
||||
kvm_mmu_invalidate_gva(vcpu, vcpu->arch.mmu, gva, INVALID_PAGE);
|
||||
kvm_mmu_invalidate_gva(vcpu, vcpu->arch.walk_mmu, gva, INVALID_PAGE);
|
||||
++vcpu->stat.invlpg;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_mmu_invlpg);
|
||||
@ -5854,8 +5848,6 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm,
|
||||
void kvm_mmu_zap_collapsible_sptes(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *slot)
|
||||
{
|
||||
bool flush = false;
|
||||
|
||||
if (kvm_memslots_have_rmaps(kvm)) {
|
||||
write_lock(&kvm->mmu_lock);
|
||||
/*
|
||||
@ -5863,17 +5855,14 @@ void kvm_mmu_zap_collapsible_sptes(struct kvm *kvm,
|
||||
* logging at a 4k granularity and never creates collapsible
|
||||
* 2m SPTEs during dirty logging.
|
||||
*/
|
||||
flush = slot_handle_level_4k(kvm, slot, kvm_mmu_zap_collapsible_spte, true);
|
||||
if (flush)
|
||||
if (slot_handle_level_4k(kvm, slot, kvm_mmu_zap_collapsible_spte, true))
|
||||
kvm_arch_flush_remote_tlbs_memslot(kvm, slot);
|
||||
write_unlock(&kvm->mmu_lock);
|
||||
}
|
||||
|
||||
if (is_tdp_mmu_enabled(kvm)) {
|
||||
read_lock(&kvm->mmu_lock);
|
||||
flush = kvm_tdp_mmu_zap_collapsible_sptes(kvm, slot, flush);
|
||||
if (flush)
|
||||
kvm_arch_flush_remote_tlbs_memslot(kvm, slot);
|
||||
kvm_tdp_mmu_zap_collapsible_sptes(kvm, slot);
|
||||
read_unlock(&kvm->mmu_lock);
|
||||
}
|
||||
}
|
||||
@ -6182,23 +6171,46 @@ void kvm_mmu_module_exit(void)
|
||||
mmu_audit_disable();
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the effective recovery period, accounting for '0' meaning "let KVM
|
||||
* select a halving time of 1 hour". Returns true if recovery is enabled.
|
||||
*/
|
||||
static bool calc_nx_huge_pages_recovery_period(uint *period)
|
||||
{
|
||||
/*
|
||||
* Use READ_ONCE to get the params, this may be called outside of the
|
||||
* param setters, e.g. by the kthread to compute its next timeout.
|
||||
*/
|
||||
bool enabled = READ_ONCE(nx_huge_pages);
|
||||
uint ratio = READ_ONCE(nx_huge_pages_recovery_ratio);
|
||||
|
||||
if (!enabled || !ratio)
|
||||
return false;
|
||||
|
||||
*period = READ_ONCE(nx_huge_pages_recovery_period_ms);
|
||||
if (!*period) {
|
||||
/* Make sure the period is not less than one second. */
|
||||
ratio = min(ratio, 3600u);
|
||||
*period = 60 * 60 * 1000 / ratio;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int set_nx_huge_pages_recovery_param(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
bool was_recovery_enabled, is_recovery_enabled;
|
||||
uint old_period, new_period;
|
||||
int err;
|
||||
|
||||
was_recovery_enabled = nx_huge_pages_recovery_ratio;
|
||||
old_period = nx_huge_pages_recovery_period_ms;
|
||||
was_recovery_enabled = calc_nx_huge_pages_recovery_period(&old_period);
|
||||
|
||||
err = param_set_uint(val, kp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
is_recovery_enabled = nx_huge_pages_recovery_ratio;
|
||||
new_period = nx_huge_pages_recovery_period_ms;
|
||||
is_recovery_enabled = calc_nx_huge_pages_recovery_period(&new_period);
|
||||
|
||||
if (READ_ONCE(nx_huge_pages) && is_recovery_enabled &&
|
||||
if (is_recovery_enabled &&
|
||||
(!was_recovery_enabled || old_period > new_period)) {
|
||||
struct kvm *kvm;
|
||||
|
||||
@ -6262,17 +6274,12 @@ static void kvm_recover_nx_lpages(struct kvm *kvm)
|
||||
|
||||
static long get_nx_lpage_recovery_timeout(u64 start_time)
|
||||
{
|
||||
uint ratio = READ_ONCE(nx_huge_pages_recovery_ratio);
|
||||
uint period = READ_ONCE(nx_huge_pages_recovery_period_ms);
|
||||
bool enabled;
|
||||
uint period;
|
||||
|
||||
if (!period && ratio) {
|
||||
/* Make sure the period is not less than one second. */
|
||||
ratio = min(ratio, 3600u);
|
||||
period = 60 * 60 * 1000 / ratio;
|
||||
}
|
||||
enabled = calc_nx_huge_pages_recovery_period(&period);
|
||||
|
||||
return READ_ONCE(nx_huge_pages) && ratio
|
||||
? start_time + msecs_to_jiffies(period) - get_jiffies_64()
|
||||
return enabled ? start_time + msecs_to_jiffies(period) - get_jiffies_64()
|
||||
: MAX_SCHEDULE_TIMEOUT;
|
||||
}
|
||||
|
||||
|
@ -317,9 +317,6 @@ static void handle_removed_tdp_mmu_page(struct kvm *kvm, tdp_ptep_t pt,
|
||||
struct kvm_mmu_page *sp = sptep_to_sp(rcu_dereference(pt));
|
||||
int level = sp->role.level;
|
||||
gfn_t base_gfn = sp->gfn;
|
||||
u64 old_child_spte;
|
||||
u64 *sptep;
|
||||
gfn_t gfn;
|
||||
int i;
|
||||
|
||||
trace_kvm_mmu_prepare_zap_page(sp);
|
||||
@ -327,8 +324,9 @@ static void handle_removed_tdp_mmu_page(struct kvm *kvm, tdp_ptep_t pt,
|
||||
tdp_mmu_unlink_page(kvm, sp, shared);
|
||||
|
||||
for (i = 0; i < PT64_ENT_PER_PAGE; i++) {
|
||||
sptep = rcu_dereference(pt) + i;
|
||||
gfn = base_gfn + i * KVM_PAGES_PER_HPAGE(level);
|
||||
u64 *sptep = rcu_dereference(pt) + i;
|
||||
gfn_t gfn = base_gfn + i * KVM_PAGES_PER_HPAGE(level);
|
||||
u64 old_child_spte;
|
||||
|
||||
if (shared) {
|
||||
/*
|
||||
@ -374,7 +372,7 @@ static void handle_removed_tdp_mmu_page(struct kvm *kvm, tdp_ptep_t pt,
|
||||
shared);
|
||||
}
|
||||
|
||||
kvm_flush_remote_tlbs_with_address(kvm, gfn,
|
||||
kvm_flush_remote_tlbs_with_address(kvm, base_gfn,
|
||||
KVM_PAGES_PER_HPAGE(level + 1));
|
||||
|
||||
call_rcu(&sp->rcu_head, tdp_mmu_free_sp_rcu_callback);
|
||||
@ -1033,8 +1031,8 @@ bool kvm_tdp_mmu_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range,
|
||||
{
|
||||
struct kvm_mmu_page *root;
|
||||
|
||||
for_each_tdp_mmu_root(kvm, root, range->slot->as_id)
|
||||
flush |= zap_gfn_range(kvm, root, range->start, range->end,
|
||||
for_each_tdp_mmu_root_yield_safe(kvm, root, range->slot->as_id, false)
|
||||
flush = zap_gfn_range(kvm, root, range->start, range->end,
|
||||
range->may_block, flush, false);
|
||||
|
||||
return flush;
|
||||
@ -1364,10 +1362,9 @@ void kvm_tdp_mmu_clear_dirty_pt_masked(struct kvm *kvm,
|
||||
* Clear leaf entries which could be replaced by large mappings, for
|
||||
* GFNs within the slot.
|
||||
*/
|
||||
static bool zap_collapsible_spte_range(struct kvm *kvm,
|
||||
static void zap_collapsible_spte_range(struct kvm *kvm,
|
||||
struct kvm_mmu_page *root,
|
||||
const struct kvm_memory_slot *slot,
|
||||
bool flush)
|
||||
const struct kvm_memory_slot *slot)
|
||||
{
|
||||
gfn_t start = slot->base_gfn;
|
||||
gfn_t end = start + slot->npages;
|
||||
@ -1378,10 +1375,8 @@ static bool zap_collapsible_spte_range(struct kvm *kvm,
|
||||
|
||||
tdp_root_for_each_pte(iter, root, start, end) {
|
||||
retry:
|
||||
if (tdp_mmu_iter_cond_resched(kvm, &iter, flush, true)) {
|
||||
flush = false;
|
||||
if (tdp_mmu_iter_cond_resched(kvm, &iter, false, true))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_shadow_present_pte(iter.old_spte) ||
|
||||
!is_last_spte(iter.old_spte, iter.level))
|
||||
@ -1393,6 +1388,7 @@ static bool zap_collapsible_spte_range(struct kvm *kvm,
|
||||
pfn, PG_LEVEL_NUM))
|
||||
continue;
|
||||
|
||||
/* Note, a successful atomic zap also does a remote TLB flush. */
|
||||
if (!tdp_mmu_zap_spte_atomic(kvm, &iter)) {
|
||||
/*
|
||||
* The iter must explicitly re-read the SPTE because
|
||||
@ -1401,30 +1397,24 @@ static bool zap_collapsible_spte_range(struct kvm *kvm,
|
||||
iter.old_spte = READ_ONCE(*rcu_dereference(iter.sptep));
|
||||
goto retry;
|
||||
}
|
||||
flush = true;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return flush;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear non-leaf entries (and free associated page tables) which could
|
||||
* be replaced by large mappings, for GFNs within the slot.
|
||||
*/
|
||||
bool kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *slot,
|
||||
bool flush)
|
||||
void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *slot)
|
||||
{
|
||||
struct kvm_mmu_page *root;
|
||||
|
||||
lockdep_assert_held_read(&kvm->mmu_lock);
|
||||
|
||||
for_each_tdp_mmu_root_yield_safe(kvm, root, slot->as_id, true)
|
||||
flush = zap_collapsible_spte_range(kvm, root, slot, flush);
|
||||
|
||||
return flush;
|
||||
zap_collapsible_spte_range(kvm, root, slot);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -64,9 +64,8 @@ void kvm_tdp_mmu_clear_dirty_pt_masked(struct kvm *kvm,
|
||||
struct kvm_memory_slot *slot,
|
||||
gfn_t gfn, unsigned long mask,
|
||||
bool wrprot);
|
||||
bool kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *slot,
|
||||
bool flush);
|
||||
void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *slot);
|
||||
|
||||
bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm,
|
||||
struct kvm_memory_slot *slot, gfn_t gfn,
|
||||
|
@ -989,17 +989,19 @@ void avic_vcpu_put(struct kvm_vcpu *vcpu)
|
||||
static void avic_set_running(struct kvm_vcpu *vcpu, bool is_run)
|
||||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
int cpu = get_cpu();
|
||||
|
||||
WARN_ON(cpu != vcpu->cpu);
|
||||
svm->avic_is_running = is_run;
|
||||
|
||||
if (!kvm_vcpu_apicv_active(vcpu))
|
||||
return;
|
||||
|
||||
if (kvm_vcpu_apicv_active(vcpu)) {
|
||||
if (is_run)
|
||||
avic_vcpu_load(vcpu, vcpu->cpu);
|
||||
avic_vcpu_load(vcpu, cpu);
|
||||
else
|
||||
avic_vcpu_put(vcpu);
|
||||
}
|
||||
put_cpu();
|
||||
}
|
||||
|
||||
void svm_vcpu_blocking(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
|
@ -1543,28 +1543,50 @@ static bool is_cmd_allowed_from_mirror(u32 cmd_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
static int sev_lock_for_migration(struct kvm *kvm)
|
||||
static int sev_lock_two_vms(struct kvm *dst_kvm, struct kvm *src_kvm)
|
||||
{
|
||||
struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
|
||||
struct kvm_sev_info *dst_sev = &to_kvm_svm(dst_kvm)->sev_info;
|
||||
struct kvm_sev_info *src_sev = &to_kvm_svm(src_kvm)->sev_info;
|
||||
int r = -EBUSY;
|
||||
|
||||
if (dst_kvm == src_kvm)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Bail if this VM is already involved in a migration to avoid deadlock
|
||||
* between two VMs trying to migrate to/from each other.
|
||||
* Bail if these VMs are already involved in a migration to avoid
|
||||
* deadlock between two VMs trying to migrate to/from each other.
|
||||
*/
|
||||
if (atomic_cmpxchg_acquire(&sev->migration_in_progress, 0, 1))
|
||||
if (atomic_cmpxchg_acquire(&dst_sev->migration_in_progress, 0, 1))
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&kvm->lock);
|
||||
if (atomic_cmpxchg_acquire(&src_sev->migration_in_progress, 0, 1))
|
||||
goto release_dst;
|
||||
|
||||
r = -EINTR;
|
||||
if (mutex_lock_killable(&dst_kvm->lock))
|
||||
goto release_src;
|
||||
if (mutex_lock_killable(&src_kvm->lock))
|
||||
goto unlock_dst;
|
||||
return 0;
|
||||
|
||||
unlock_dst:
|
||||
mutex_unlock(&dst_kvm->lock);
|
||||
release_src:
|
||||
atomic_set_release(&src_sev->migration_in_progress, 0);
|
||||
release_dst:
|
||||
atomic_set_release(&dst_sev->migration_in_progress, 0);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void sev_unlock_after_migration(struct kvm *kvm)
|
||||
static void sev_unlock_two_vms(struct kvm *dst_kvm, struct kvm *src_kvm)
|
||||
{
|
||||
struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
|
||||
struct kvm_sev_info *dst_sev = &to_kvm_svm(dst_kvm)->sev_info;
|
||||
struct kvm_sev_info *src_sev = &to_kvm_svm(src_kvm)->sev_info;
|
||||
|
||||
mutex_unlock(&kvm->lock);
|
||||
atomic_set_release(&sev->migration_in_progress, 0);
|
||||
mutex_unlock(&dst_kvm->lock);
|
||||
mutex_unlock(&src_kvm->lock);
|
||||
atomic_set_release(&dst_sev->migration_in_progress, 0);
|
||||
atomic_set_release(&src_sev->migration_in_progress, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -1607,14 +1629,15 @@ static void sev_migrate_from(struct kvm_sev_info *dst,
|
||||
dst->asid = src->asid;
|
||||
dst->handle = src->handle;
|
||||
dst->pages_locked = src->pages_locked;
|
||||
dst->enc_context_owner = src->enc_context_owner;
|
||||
|
||||
src->asid = 0;
|
||||
src->active = false;
|
||||
src->handle = 0;
|
||||
src->pages_locked = 0;
|
||||
src->enc_context_owner = NULL;
|
||||
|
||||
INIT_LIST_HEAD(&dst->regions_list);
|
||||
list_replace_init(&src->regions_list, &dst->regions_list);
|
||||
list_cut_before(&dst->regions_list, &src->regions_list, &src->regions_list);
|
||||
}
|
||||
|
||||
static int sev_es_migrate_from(struct kvm *dst, struct kvm *src)
|
||||
@ -1666,15 +1689,6 @@ int svm_vm_migrate_from(struct kvm *kvm, unsigned int source_fd)
|
||||
bool charged = false;
|
||||
int ret;
|
||||
|
||||
ret = sev_lock_for_migration(kvm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (sev_guest(kvm)) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
source_kvm_file = fget(source_fd);
|
||||
if (!file_is_kvm(source_kvm_file)) {
|
||||
ret = -EBADF;
|
||||
@ -1682,16 +1696,26 @@ int svm_vm_migrate_from(struct kvm *kvm, unsigned int source_fd)
|
||||
}
|
||||
|
||||
source_kvm = source_kvm_file->private_data;
|
||||
ret = sev_lock_for_migration(source_kvm);
|
||||
ret = sev_lock_two_vms(kvm, source_kvm);
|
||||
if (ret)
|
||||
goto out_fput;
|
||||
|
||||
if (!sev_guest(source_kvm)) {
|
||||
if (sev_guest(kvm) || !sev_guest(source_kvm)) {
|
||||
ret = -EINVAL;
|
||||
goto out_source;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
src_sev = &to_kvm_svm(source_kvm)->sev_info;
|
||||
|
||||
/*
|
||||
* VMs mirroring src's encryption context rely on it to keep the
|
||||
* ASID allocated, but below we are clearing src_sev->asid.
|
||||
*/
|
||||
if (src_sev->num_mirrored_vms) {
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
dst_sev->misc_cg = get_current_misc_cg();
|
||||
cg_cleanup_sev = dst_sev;
|
||||
if (dst_sev->misc_cg != src_sev->misc_cg) {
|
||||
@ -1728,13 +1752,11 @@ int svm_vm_migrate_from(struct kvm *kvm, unsigned int source_fd)
|
||||
sev_misc_cg_uncharge(cg_cleanup_sev);
|
||||
put_misc_cg(cg_cleanup_sev->misc_cg);
|
||||
cg_cleanup_sev->misc_cg = NULL;
|
||||
out_source:
|
||||
sev_unlock_after_migration(source_kvm);
|
||||
out_unlock:
|
||||
sev_unlock_two_vms(kvm, source_kvm);
|
||||
out_fput:
|
||||
if (source_kvm_file)
|
||||
fput(source_kvm_file);
|
||||
out_unlock:
|
||||
sev_unlock_after_migration(kvm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1953,76 +1975,60 @@ int svm_vm_copy_asid_from(struct kvm *kvm, unsigned int source_fd)
|
||||
{
|
||||
struct file *source_kvm_file;
|
||||
struct kvm *source_kvm;
|
||||
struct kvm_sev_info source_sev, *mirror_sev;
|
||||
struct kvm_sev_info *source_sev, *mirror_sev;
|
||||
int ret;
|
||||
|
||||
source_kvm_file = fget(source_fd);
|
||||
if (!file_is_kvm(source_kvm_file)) {
|
||||
ret = -EBADF;
|
||||
goto e_source_put;
|
||||
goto e_source_fput;
|
||||
}
|
||||
|
||||
source_kvm = source_kvm_file->private_data;
|
||||
mutex_lock(&source_kvm->lock);
|
||||
ret = sev_lock_two_vms(kvm, source_kvm);
|
||||
if (ret)
|
||||
goto e_source_fput;
|
||||
|
||||
if (!sev_guest(source_kvm)) {
|
||||
/*
|
||||
* Mirrors of mirrors should work, but let's not get silly. Also
|
||||
* disallow out-of-band SEV/SEV-ES init if the target is already an
|
||||
* SEV guest, or if vCPUs have been created. KVM relies on vCPUs being
|
||||
* created after SEV/SEV-ES initialization, e.g. to init intercepts.
|
||||
*/
|
||||
if (sev_guest(kvm) || !sev_guest(source_kvm) ||
|
||||
is_mirroring_enc_context(source_kvm) || kvm->created_vcpus) {
|
||||
ret = -EINVAL;
|
||||
goto e_source_unlock;
|
||||
goto e_unlock;
|
||||
}
|
||||
|
||||
/* Mirrors of mirrors should work, but let's not get silly */
|
||||
if (is_mirroring_enc_context(source_kvm) || source_kvm == kvm) {
|
||||
ret = -EINVAL;
|
||||
goto e_source_unlock;
|
||||
}
|
||||
|
||||
memcpy(&source_sev, &to_kvm_svm(source_kvm)->sev_info,
|
||||
sizeof(source_sev));
|
||||
|
||||
/*
|
||||
* The mirror kvm holds an enc_context_owner ref so its asid can't
|
||||
* disappear until we're done with it
|
||||
*/
|
||||
source_sev = &to_kvm_svm(source_kvm)->sev_info;
|
||||
kvm_get_kvm(source_kvm);
|
||||
|
||||
fput(source_kvm_file);
|
||||
mutex_unlock(&source_kvm->lock);
|
||||
mutex_lock(&kvm->lock);
|
||||
|
||||
/*
|
||||
* Disallow out-of-band SEV/SEV-ES init if the target is already an
|
||||
* SEV guest, or if vCPUs have been created. KVM relies on vCPUs being
|
||||
* created after SEV/SEV-ES initialization, e.g. to init intercepts.
|
||||
*/
|
||||
if (sev_guest(kvm) || kvm->created_vcpus) {
|
||||
ret = -EINVAL;
|
||||
goto e_mirror_unlock;
|
||||
}
|
||||
source_sev->num_mirrored_vms++;
|
||||
|
||||
/* Set enc_context_owner and copy its encryption context over */
|
||||
mirror_sev = &to_kvm_svm(kvm)->sev_info;
|
||||
mirror_sev->enc_context_owner = source_kvm;
|
||||
mirror_sev->active = true;
|
||||
mirror_sev->asid = source_sev.asid;
|
||||
mirror_sev->fd = source_sev.fd;
|
||||
mirror_sev->es_active = source_sev.es_active;
|
||||
mirror_sev->handle = source_sev.handle;
|
||||
mirror_sev->asid = source_sev->asid;
|
||||
mirror_sev->fd = source_sev->fd;
|
||||
mirror_sev->es_active = source_sev->es_active;
|
||||
mirror_sev->handle = source_sev->handle;
|
||||
INIT_LIST_HEAD(&mirror_sev->regions_list);
|
||||
ret = 0;
|
||||
|
||||
/*
|
||||
* Do not copy ap_jump_table. Since the mirror does not share the same
|
||||
* KVM contexts as the original, and they may have different
|
||||
* memory-views.
|
||||
*/
|
||||
|
||||
mutex_unlock(&kvm->lock);
|
||||
return 0;
|
||||
|
||||
e_mirror_unlock:
|
||||
mutex_unlock(&kvm->lock);
|
||||
kvm_put_kvm(source_kvm);
|
||||
return ret;
|
||||
e_source_unlock:
|
||||
mutex_unlock(&source_kvm->lock);
|
||||
e_source_put:
|
||||
e_unlock:
|
||||
sev_unlock_two_vms(kvm, source_kvm);
|
||||
e_source_fput:
|
||||
if (source_kvm_file)
|
||||
fput(source_kvm_file);
|
||||
return ret;
|
||||
@ -2034,17 +2040,24 @@ void sev_vm_destroy(struct kvm *kvm)
|
||||
struct list_head *head = &sev->regions_list;
|
||||
struct list_head *pos, *q;
|
||||
|
||||
WARN_ON(sev->num_mirrored_vms);
|
||||
|
||||
if (!sev_guest(kvm))
|
||||
return;
|
||||
|
||||
/* If this is a mirror_kvm release the enc_context_owner and skip sev cleanup */
|
||||
if (is_mirroring_enc_context(kvm)) {
|
||||
kvm_put_kvm(sev->enc_context_owner);
|
||||
struct kvm *owner_kvm = sev->enc_context_owner;
|
||||
struct kvm_sev_info *owner_sev = &to_kvm_svm(owner_kvm)->sev_info;
|
||||
|
||||
mutex_lock(&owner_kvm->lock);
|
||||
if (!WARN_ON(!owner_sev->num_mirrored_vms))
|
||||
owner_sev->num_mirrored_vms--;
|
||||
mutex_unlock(&owner_kvm->lock);
|
||||
kvm_put_kvm(owner_kvm);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&kvm->lock);
|
||||
|
||||
/*
|
||||
* Ensure that all guest tagged cache entries are flushed before
|
||||
* releasing the pages back to the system for use. CLFLUSH will
|
||||
@ -2064,8 +2077,6 @@ void sev_vm_destroy(struct kvm *kvm)
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&kvm->lock);
|
||||
|
||||
sev_unbind_asid(kvm, sev->handle);
|
||||
sev_asid_free(sev);
|
||||
}
|
||||
|
@ -4651,7 +4651,6 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
|
||||
.load_eoi_exitmap = svm_load_eoi_exitmap,
|
||||
.hwapic_irr_update = svm_hwapic_irr_update,
|
||||
.hwapic_isr_update = svm_hwapic_isr_update,
|
||||
.sync_pir_to_irr = kvm_lapic_find_highest_irr,
|
||||
.apicv_post_state_restore = avic_post_state_restore,
|
||||
|
||||
.set_tss_addr = svm_set_tss_addr,
|
||||
|
@ -79,6 +79,7 @@ struct kvm_sev_info {
|
||||
struct list_head regions_list; /* List of registered regions */
|
||||
u64 ap_jump_table; /* SEV-ES AP Jump Table address */
|
||||
struct kvm *enc_context_owner; /* Owner of copied encryption context */
|
||||
unsigned long num_mirrored_vms; /* Number of VMs sharing this ASID */
|
||||
struct misc_cg *misc_cg; /* For misc cgroup accounting */
|
||||
atomic_t migration_in_progress;
|
||||
};
|
||||
|
@ -1162,29 +1162,26 @@ static void nested_vmx_transition_tlb_flush(struct kvm_vcpu *vcpu,
|
||||
WARN_ON(!enable_vpid);
|
||||
|
||||
/*
|
||||
* If VPID is enabled and used by vmc12, but L2 does not have a unique
|
||||
* TLB tag (ASID), i.e. EPT is disabled and KVM was unable to allocate
|
||||
* a VPID for L2, flush the current context as the effective ASID is
|
||||
* common to both L1 and L2.
|
||||
*
|
||||
* Defer the flush so that it runs after vmcs02.EPTP has been set by
|
||||
* KVM_REQ_LOAD_MMU_PGD (if nested EPT is enabled) and to avoid
|
||||
* redundant flushes further down the nested pipeline.
|
||||
*
|
||||
* If a TLB flush isn't required due to any of the above, and vpid12 is
|
||||
* changing then the new "virtual" VPID (vpid12) will reuse the same
|
||||
* "real" VPID (vpid02), and so needs to be flushed. There's no direct
|
||||
* mapping between vpid02 and vpid12, vpid02 is per-vCPU and reused for
|
||||
* all nested vCPUs. Remember, a flush on VM-Enter does not invalidate
|
||||
* guest-physical mappings, so there is no need to sync the nEPT MMU.
|
||||
* VPID is enabled and in use by vmcs12. If vpid12 is changing, then
|
||||
* emulate a guest TLB flush as KVM does not track vpid12 history nor
|
||||
* is the VPID incorporated into the MMU context. I.e. KVM must assume
|
||||
* that the new vpid12 has never been used and thus represents a new
|
||||
* guest ASID that cannot have entries in the TLB.
|
||||
*/
|
||||
if (!nested_has_guest_tlb_tag(vcpu)) {
|
||||
kvm_make_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu);
|
||||
} else if (is_vmenter &&
|
||||
vmcs12->virtual_processor_id != vmx->nested.last_vpid) {
|
||||
if (is_vmenter && vmcs12->virtual_processor_id != vmx->nested.last_vpid) {
|
||||
vmx->nested.last_vpid = vmcs12->virtual_processor_id;
|
||||
vpid_sync_context(nested_get_vpid02(vcpu));
|
||||
kvm_make_request(KVM_REQ_TLB_FLUSH_GUEST, vcpu);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If VPID is enabled, used by vmc12, and vpid12 is not changing but
|
||||
* does not have a unique TLB tag (ASID), i.e. EPT is disabled and
|
||||
* KVM was unable to allocate a VPID for L2, flush the current context
|
||||
* as the effective ASID is common to both L1 and L2.
|
||||
*/
|
||||
if (!nested_has_guest_tlb_tag(vcpu))
|
||||
kvm_make_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu);
|
||||
}
|
||||
|
||||
static bool is_bitwise_subset(u64 superset, u64 subset, u64 mask)
|
||||
@ -3344,8 +3341,7 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
|
||||
};
|
||||
u32 failed_index;
|
||||
|
||||
if (kvm_check_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu))
|
||||
kvm_vcpu_flush_tlb_current(vcpu);
|
||||
kvm_service_local_tlb_flush_requests(vcpu);
|
||||
|
||||
evaluate_pending_interrupts = exec_controls_get(vmx) &
|
||||
(CPU_BASED_INTR_WINDOW_EXITING | CPU_BASED_NMI_WINDOW_EXITING);
|
||||
@ -4502,9 +4498,8 @@ void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason,
|
||||
(void)nested_get_evmcs_page(vcpu);
|
||||
}
|
||||
|
||||
/* Service the TLB flush request for L2 before switching to L1. */
|
||||
if (kvm_check_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu))
|
||||
kvm_vcpu_flush_tlb_current(vcpu);
|
||||
/* Service pending TLB flush requests for L2 before switching to L1. */
|
||||
kvm_service_local_tlb_flush_requests(vcpu);
|
||||
|
||||
/*
|
||||
* VCPU_EXREG_PDPTR will be clobbered in arch/x86/kvm/vmx/vmx.h between
|
||||
@ -4857,6 +4852,7 @@ static int enter_vmx_operation(struct kvm_vcpu *vcpu)
|
||||
if (!vmx->nested.cached_vmcs12)
|
||||
goto out_cached_vmcs12;
|
||||
|
||||
vmx->nested.shadow_vmcs12_cache.gpa = INVALID_GPA;
|
||||
vmx->nested.cached_shadow_vmcs12 = kzalloc(VMCS12_SIZE, GFP_KERNEL_ACCOUNT);
|
||||
if (!vmx->nested.cached_shadow_vmcs12)
|
||||
goto out_cached_shadow_vmcs12;
|
||||
@ -5289,8 +5285,7 @@ static int handle_vmptrld(struct kvm_vcpu *vcpu)
|
||||
struct gfn_to_hva_cache *ghc = &vmx->nested.vmcs12_cache;
|
||||
struct vmcs_hdr hdr;
|
||||
|
||||
if (ghc->gpa != vmptr &&
|
||||
kvm_gfn_to_hva_cache_init(vcpu->kvm, ghc, vmptr, VMCS12_SIZE)) {
|
||||
if (kvm_gfn_to_hva_cache_init(vcpu->kvm, ghc, vmptr, VMCS12_SIZE)) {
|
||||
/*
|
||||
* Reads from an unbacked page return all 1s,
|
||||
* which means that the 32 bits located at the
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <asm/cpu.h>
|
||||
|
||||
#include "lapic.h"
|
||||
#include "irq.h"
|
||||
#include "posted_intr.h"
|
||||
#include "trace.h"
|
||||
#include "vmx.h"
|
||||
@ -77,13 +78,18 @@ void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)
|
||||
pi_set_on(pi_desc);
|
||||
}
|
||||
|
||||
static bool vmx_can_use_vtd_pi(struct kvm *kvm)
|
||||
{
|
||||
return irqchip_in_kernel(kvm) && enable_apicv &&
|
||||
kvm_arch_has_assigned_device(kvm) &&
|
||||
irq_remapping_cap(IRQ_POSTING_CAP);
|
||||
}
|
||||
|
||||
void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
|
||||
|
||||
if (!kvm_arch_has_assigned_device(vcpu->kvm) ||
|
||||
!irq_remapping_cap(IRQ_POSTING_CAP) ||
|
||||
!kvm_vcpu_apicv_active(vcpu))
|
||||
if (!vmx_can_use_vtd_pi(vcpu->kvm))
|
||||
return;
|
||||
|
||||
/* Set SN when the vCPU is preempted */
|
||||
@ -141,9 +147,7 @@ int pi_pre_block(struct kvm_vcpu *vcpu)
|
||||
struct pi_desc old, new;
|
||||
struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
|
||||
|
||||
if (!kvm_arch_has_assigned_device(vcpu->kvm) ||
|
||||
!irq_remapping_cap(IRQ_POSTING_CAP) ||
|
||||
!kvm_vcpu_apicv_active(vcpu))
|
||||
if (!vmx_can_use_vtd_pi(vcpu->kvm))
|
||||
return 0;
|
||||
|
||||
WARN_ON(irqs_disabled());
|
||||
@ -270,9 +274,7 @@ int pi_update_irte(struct kvm *kvm, unsigned int host_irq, uint32_t guest_irq,
|
||||
struct vcpu_data vcpu_info;
|
||||
int idx, ret = 0;
|
||||
|
||||
if (!kvm_arch_has_assigned_device(kvm) ||
|
||||
!irq_remapping_cap(IRQ_POSTING_CAP) ||
|
||||
!kvm_vcpu_apicv_active(kvm->vcpus[0]))
|
||||
if (!vmx_can_use_vtd_pi(kvm))
|
||||
return 0;
|
||||
|
||||
idx = srcu_read_lock(&kvm->irq_srcu);
|
||||
|
@ -2918,6 +2918,13 @@ static void vmx_flush_tlb_all(struct kvm_vcpu *vcpu)
|
||||
}
|
||||
}
|
||||
|
||||
static inline int vmx_get_current_vpid(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (is_guest_mode(vcpu))
|
||||
return nested_get_vpid02(vcpu);
|
||||
return to_vmx(vcpu)->vpid;
|
||||
}
|
||||
|
||||
static void vmx_flush_tlb_current(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_mmu *mmu = vcpu->arch.mmu;
|
||||
@ -2930,31 +2937,29 @@ static void vmx_flush_tlb_current(struct kvm_vcpu *vcpu)
|
||||
if (enable_ept)
|
||||
ept_sync_context(construct_eptp(vcpu, root_hpa,
|
||||
mmu->shadow_root_level));
|
||||
else if (!is_guest_mode(vcpu))
|
||||
vpid_sync_context(to_vmx(vcpu)->vpid);
|
||||
else
|
||||
vpid_sync_context(nested_get_vpid02(vcpu));
|
||||
vpid_sync_context(vmx_get_current_vpid(vcpu));
|
||||
}
|
||||
|
||||
static void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr)
|
||||
{
|
||||
/*
|
||||
* vpid_sync_vcpu_addr() is a nop if vmx->vpid==0, see the comment in
|
||||
* vpid_sync_vcpu_addr() is a nop if vpid==0, see the comment in
|
||||
* vmx_flush_tlb_guest() for an explanation of why this is ok.
|
||||
*/
|
||||
vpid_sync_vcpu_addr(to_vmx(vcpu)->vpid, addr);
|
||||
vpid_sync_vcpu_addr(vmx_get_current_vpid(vcpu), addr);
|
||||
}
|
||||
|
||||
static void vmx_flush_tlb_guest(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* vpid_sync_context() is a nop if vmx->vpid==0, e.g. if enable_vpid==0
|
||||
* or a vpid couldn't be allocated for this vCPU. VM-Enter and VM-Exit
|
||||
* are required to flush GVA->{G,H}PA mappings from the TLB if vpid is
|
||||
* vpid_sync_context() is a nop if vpid==0, e.g. if enable_vpid==0 or a
|
||||
* vpid couldn't be allocated for this vCPU. VM-Enter and VM-Exit are
|
||||
* required to flush GVA->{G,H}PA mappings from the TLB if vpid is
|
||||
* disabled (VM-Enter with vpid enabled and vpid==0 is disallowed),
|
||||
* i.e. no explicit INVVPID is necessary.
|
||||
*/
|
||||
vpid_sync_context(to_vmx(vcpu)->vpid);
|
||||
vpid_sync_context(vmx_get_current_vpid(vcpu));
|
||||
}
|
||||
|
||||
void vmx_ept_load_pdptrs(struct kvm_vcpu *vcpu)
|
||||
@ -6262,9 +6267,9 @@ static int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vcpu_vmx *vmx = to_vmx(vcpu);
|
||||
int max_irr;
|
||||
bool max_irr_updated;
|
||||
bool got_posted_interrupt;
|
||||
|
||||
if (KVM_BUG_ON(!vcpu->arch.apicv_active, vcpu->kvm))
|
||||
if (KVM_BUG_ON(!enable_apicv, vcpu->kvm))
|
||||
return -EIO;
|
||||
|
||||
if (pi_test_on(&vmx->pi_desc)) {
|
||||
@ -6274,22 +6279,33 @@ static int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu)
|
||||
* But on x86 this is just a compiler barrier anyway.
|
||||
*/
|
||||
smp_mb__after_atomic();
|
||||
max_irr_updated =
|
||||
got_posted_interrupt =
|
||||
kvm_apic_update_irr(vcpu, vmx->pi_desc.pir, &max_irr);
|
||||
|
||||
/*
|
||||
* If we are running L2 and L1 has a new pending interrupt
|
||||
* which can be injected, this may cause a vmexit or it may
|
||||
* be injected into L2. Either way, this interrupt will be
|
||||
* processed via KVM_REQ_EVENT, not RVI, because we do not use
|
||||
* virtual interrupt delivery to inject L1 interrupts into L2.
|
||||
*/
|
||||
if (is_guest_mode(vcpu) && max_irr_updated)
|
||||
kvm_make_request(KVM_REQ_EVENT, vcpu);
|
||||
} else {
|
||||
max_irr = kvm_lapic_find_highest_irr(vcpu);
|
||||
got_posted_interrupt = false;
|
||||
}
|
||||
vmx_hwapic_irr_update(vcpu, max_irr);
|
||||
|
||||
/*
|
||||
* Newly recognized interrupts are injected via either virtual interrupt
|
||||
* delivery (RVI) or KVM_REQ_EVENT. Virtual interrupt delivery is
|
||||
* disabled in two cases:
|
||||
*
|
||||
* 1) If L2 is running and the vCPU has a new pending interrupt. If L1
|
||||
* wants to exit on interrupts, KVM_REQ_EVENT is needed to synthesize a
|
||||
* VM-Exit to L1. If L1 doesn't want to exit, the interrupt is injected
|
||||
* into L2, but KVM doesn't use virtual interrupt delivery to inject
|
||||
* interrupts into L2, and so KVM_REQ_EVENT is again needed.
|
||||
*
|
||||
* 2) If APICv is disabled for this vCPU, assigned devices may still
|
||||
* attempt to post interrupts. The posted interrupt vector will cause
|
||||
* a VM-Exit and the subsequent entry will call sync_pir_to_irr.
|
||||
*/
|
||||
if (!is_guest_mode(vcpu) && kvm_vcpu_apicv_active(vcpu))
|
||||
vmx_set_rvi(max_irr);
|
||||
else if (got_posted_interrupt)
|
||||
kvm_make_request(KVM_REQ_EVENT, vcpu);
|
||||
|
||||
return max_irr;
|
||||
}
|
||||
|
||||
@ -7761,10 +7777,10 @@ static __init int hardware_setup(void)
|
||||
ple_window_shrink = 0;
|
||||
}
|
||||
|
||||
if (!cpu_has_vmx_apicv()) {
|
||||
if (!cpu_has_vmx_apicv())
|
||||
enable_apicv = 0;
|
||||
if (!enable_apicv)
|
||||
vmx_x86_ops.sync_pir_to_irr = NULL;
|
||||
}
|
||||
|
||||
if (cpu_has_vmx_tsc_scaling()) {
|
||||
kvm_has_tsc_control = true;
|
||||
|
@ -3258,6 +3258,29 @@ static void kvm_vcpu_flush_tlb_guest(struct kvm_vcpu *vcpu)
|
||||
static_call(kvm_x86_tlb_flush_guest)(vcpu);
|
||||
}
|
||||
|
||||
|
||||
static inline void kvm_vcpu_flush_tlb_current(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
++vcpu->stat.tlb_flush;
|
||||
static_call(kvm_x86_tlb_flush_current)(vcpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Service "local" TLB flush requests, which are specific to the current MMU
|
||||
* context. In addition to the generic event handling in vcpu_enter_guest(),
|
||||
* TLB flushes that are targeted at an MMU context also need to be serviced
|
||||
* prior before nested VM-Enter/VM-Exit.
|
||||
*/
|
||||
void kvm_service_local_tlb_flush_requests(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (kvm_check_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu))
|
||||
kvm_vcpu_flush_tlb_current(vcpu);
|
||||
|
||||
if (kvm_check_request(KVM_REQ_TLB_FLUSH_GUEST, vcpu))
|
||||
kvm_vcpu_flush_tlb_guest(vcpu);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_service_local_tlb_flush_requests);
|
||||
|
||||
static void record_steal_time(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct gfn_to_hva_cache *ghc = &vcpu->arch.st.cache;
|
||||
@ -4133,6 +4156,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
case KVM_CAP_SGX_ATTRIBUTE:
|
||||
#endif
|
||||
case KVM_CAP_VM_COPY_ENC_CONTEXT_FROM:
|
||||
case KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM:
|
||||
case KVM_CAP_SREGS2:
|
||||
case KVM_CAP_EXIT_ON_EMULATION_FAILURE:
|
||||
case KVM_CAP_VCPU_ATTRIBUTES:
|
||||
@ -4448,8 +4472,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
|
||||
static int kvm_vcpu_ioctl_get_lapic(struct kvm_vcpu *vcpu,
|
||||
struct kvm_lapic_state *s)
|
||||
{
|
||||
if (vcpu->arch.apicv_active)
|
||||
static_call(kvm_x86_sync_pir_to_irr)(vcpu);
|
||||
static_call_cond(kvm_x86_sync_pir_to_irr)(vcpu);
|
||||
|
||||
return kvm_apic_get_state(vcpu, s);
|
||||
}
|
||||
@ -5124,6 +5147,17 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
|
||||
struct kvm_cpuid __user *cpuid_arg = argp;
|
||||
struct kvm_cpuid cpuid;
|
||||
|
||||
/*
|
||||
* KVM does not correctly handle changing guest CPUID after KVM_RUN, as
|
||||
* MAXPHYADDR, GBPAGES support, AMD reserved bit behavior, etc.. aren't
|
||||
* tracked in kvm_mmu_page_role. As a result, KVM may miss guest page
|
||||
* faults due to reusing SPs/SPTEs. In practice no sane VMM mucks with
|
||||
* the core vCPU model on the fly, so fail.
|
||||
*/
|
||||
r = -EINVAL;
|
||||
if (vcpu->arch.last_vmentry_cpu != -1)
|
||||
goto out;
|
||||
|
||||
r = -EFAULT;
|
||||
if (copy_from_user(&cpuid, cpuid_arg, sizeof(cpuid)))
|
||||
goto out;
|
||||
@ -5134,6 +5168,14 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
|
||||
struct kvm_cpuid2 __user *cpuid_arg = argp;
|
||||
struct kvm_cpuid2 cpuid;
|
||||
|
||||
/*
|
||||
* KVM_SET_CPUID{,2} after KVM_RUN is forbidded, see the comment in
|
||||
* KVM_SET_CPUID case above.
|
||||
*/
|
||||
r = -EINVAL;
|
||||
if (vcpu->arch.last_vmentry_cpu != -1)
|
||||
goto out;
|
||||
|
||||
r = -EFAULT;
|
||||
if (copy_from_user(&cpuid, cpuid_arg, sizeof(cpuid)))
|
||||
goto out;
|
||||
@ -9528,8 +9570,7 @@ static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
|
||||
if (irqchip_split(vcpu->kvm))
|
||||
kvm_scan_ioapic_routes(vcpu, vcpu->arch.ioapic_handled_vectors);
|
||||
else {
|
||||
if (vcpu->arch.apicv_active)
|
||||
static_call(kvm_x86_sync_pir_to_irr)(vcpu);
|
||||
static_call_cond(kvm_x86_sync_pir_to_irr)(vcpu);
|
||||
if (ioapic_in_kernel(vcpu->kvm))
|
||||
kvm_ioapic_scan_entry(vcpu, vcpu->arch.ioapic_handled_vectors);
|
||||
}
|
||||
@ -9648,10 +9689,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
|
||||
/* Flushing all ASIDs flushes the current ASID... */
|
||||
kvm_clear_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu);
|
||||
}
|
||||
if (kvm_check_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu))
|
||||
kvm_vcpu_flush_tlb_current(vcpu);
|
||||
if (kvm_check_request(KVM_REQ_TLB_FLUSH_GUEST, vcpu))
|
||||
kvm_vcpu_flush_tlb_guest(vcpu);
|
||||
kvm_service_local_tlb_flush_requests(vcpu);
|
||||
|
||||
if (kvm_check_request(KVM_REQ_REPORT_TPR_ACCESS, vcpu)) {
|
||||
vcpu->run->exit_reason = KVM_EXIT_TPR_ACCESS;
|
||||
@ -9802,10 +9840,12 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
|
||||
|
||||
/*
|
||||
* This handles the case where a posted interrupt was
|
||||
* notified with kvm_vcpu_kick.
|
||||
* notified with kvm_vcpu_kick. Assigned devices can
|
||||
* use the POSTED_INTR_VECTOR even if APICv is disabled,
|
||||
* so do it even if APICv is disabled on this vCPU.
|
||||
*/
|
||||
if (kvm_lapic_enabled(vcpu) && vcpu->arch.apicv_active)
|
||||
static_call(kvm_x86_sync_pir_to_irr)(vcpu);
|
||||
if (kvm_lapic_enabled(vcpu))
|
||||
static_call_cond(kvm_x86_sync_pir_to_irr)(vcpu);
|
||||
|
||||
if (kvm_vcpu_exit_request(vcpu)) {
|
||||
vcpu->mode = OUTSIDE_GUEST_MODE;
|
||||
@ -9849,8 +9889,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
|
||||
if (likely(exit_fastpath != EXIT_FASTPATH_REENTER_GUEST))
|
||||
break;
|
||||
|
||||
if (vcpu->arch.apicv_active)
|
||||
static_call(kvm_x86_sync_pir_to_irr)(vcpu);
|
||||
if (kvm_lapic_enabled(vcpu))
|
||||
static_call_cond(kvm_x86_sync_pir_to_irr)(vcpu);
|
||||
|
||||
if (unlikely(kvm_vcpu_exit_request(vcpu))) {
|
||||
exit_fastpath = EXIT_FASTPATH_EXIT_HANDLED;
|
||||
|
@ -103,6 +103,7 @@ static inline unsigned int __shrink_ple_window(unsigned int val,
|
||||
|
||||
#define MSR_IA32_CR_PAT_DEFAULT 0x0007040600070406ULL
|
||||
|
||||
void kvm_service_local_tlb_flush_requests(struct kvm_vcpu *vcpu);
|
||||
int kvm_check_nested_events(struct kvm_vcpu *vcpu);
|
||||
|
||||
static inline void kvm_clear_exception_queue(struct kvm_vcpu *vcpu)
|
||||
@ -185,12 +186,6 @@ static inline bool mmu_is_nested(struct kvm_vcpu *vcpu)
|
||||
return vcpu->arch.walk_mmu == &vcpu->arch.nested_mmu;
|
||||
}
|
||||
|
||||
static inline void kvm_vcpu_flush_tlb_current(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
++vcpu->stat.tlb_flush;
|
||||
static_call(kvm_x86_tlb_flush_current)(vcpu);
|
||||
}
|
||||
|
||||
static inline int is_pae(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return kvm_read_cr4_bits(vcpu, X86_CR4_PAE);
|
||||
|
@ -860,13 +860,14 @@ void blk_mq_end_request_batch(struct io_comp_batch *iob)
|
||||
if (iob->need_ts)
|
||||
__blk_mq_end_request_acct(rq, now);
|
||||
|
||||
rq_qos_done(rq->q, rq);
|
||||
|
||||
WRITE_ONCE(rq->state, MQ_RQ_IDLE);
|
||||
if (!refcount_dec_and_test(&rq->ref))
|
||||
continue;
|
||||
|
||||
blk_crypto_free_request(rq);
|
||||
blk_pm_mark_last_busy(rq);
|
||||
rq_qos_done(rq->q, rq);
|
||||
|
||||
if (nr_tags == TAG_COMP_BATCH || cur_hctx != rq->mq_hctx) {
|
||||
if (cur_hctx)
|
||||
|
@ -316,7 +316,7 @@ static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
|
||||
struct request *req = bd->rq;
|
||||
struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);
|
||||
unsigned long flags;
|
||||
unsigned int num;
|
||||
int num;
|
||||
int qid = hctx->queue_num;
|
||||
bool notify = false;
|
||||
blk_status_t status;
|
||||
@ -1049,7 +1049,6 @@ static struct virtio_driver virtio_blk = {
|
||||
.feature_table_size = ARRAY_SIZE(features),
|
||||
.feature_table_legacy = features_legacy,
|
||||
.feature_table_size_legacy = ARRAY_SIZE(features_legacy),
|
||||
.suppress_used_validation = true,
|
||||
.driver.name = KBUILD_MODNAME,
|
||||
.driver.owner = THIS_MODULE,
|
||||
.id_table = id_table,
|
||||
|
@ -1853,12 +1853,14 @@ static const struct block_device_operations zram_devops = {
|
||||
.owner = THIS_MODULE
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ZRAM_WRITEBACK
|
||||
static const struct block_device_operations zram_wb_devops = {
|
||||
.open = zram_open,
|
||||
.submit_bio = zram_submit_bio,
|
||||
.swap_slot_free_notify = zram_slot_free_notify,
|
||||
.owner = THIS_MODULE
|
||||
};
|
||||
#endif
|
||||
|
||||
static DEVICE_ATTR_WO(compact);
|
||||
static DEVICE_ATTR_RW(disksize);
|
||||
|
@ -191,6 +191,8 @@ struct ipmi_user {
|
||||
struct work_struct remove_work;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *remove_work_wq;
|
||||
|
||||
static struct ipmi_user *acquire_ipmi_user(struct ipmi_user *user, int *index)
|
||||
__acquires(user->release_barrier)
|
||||
{
|
||||
@ -1297,7 +1299,7 @@ static void free_user(struct kref *ref)
|
||||
struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount);
|
||||
|
||||
/* SRCU cleanup must happen in task context. */
|
||||
schedule_work(&user->remove_work);
|
||||
queue_work(remove_work_wq, &user->remove_work);
|
||||
}
|
||||
|
||||
static void _ipmi_destroy_user(struct ipmi_user *user)
|
||||
@ -3918,9 +3920,11 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
|
||||
/* We didn't find a user, deliver an error response. */
|
||||
ipmi_inc_stat(intf, unhandled_commands);
|
||||
|
||||
msg->data[0] = ((netfn + 1) << 2) | (msg->rsp[4] & 0x3);
|
||||
msg->data[1] = msg->rsp[2];
|
||||
msg->data[2] = msg->rsp[4] & ~0x3;
|
||||
msg->data[0] = (netfn + 1) << 2;
|
||||
msg->data[0] |= msg->rsp[2] & 0x3; /* rqLUN */
|
||||
msg->data[1] = msg->rsp[1]; /* Addr */
|
||||
msg->data[2] = msg->rsp[2] & ~0x3; /* rqSeq */
|
||||
msg->data[2] |= msg->rsp[0] & 0x3; /* rsLUN */
|
||||
msg->data[3] = cmd;
|
||||
msg->data[4] = IPMI_INVALID_CMD_COMPLETION_CODE;
|
||||
msg->data_size = 5;
|
||||
@ -4455,13 +4459,24 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
|
||||
msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
|
||||
msg->rsp_size = 3;
|
||||
} else if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
|
||||
/* commands must have at least 3 bytes, responses 4. */
|
||||
if (is_cmd && (msg->rsp_size < 3)) {
|
||||
/* commands must have at least 4 bytes, responses 5. */
|
||||
if (is_cmd && (msg->rsp_size < 4)) {
|
||||
ipmi_inc_stat(intf, invalid_commands);
|
||||
goto out;
|
||||
}
|
||||
if (!is_cmd && (msg->rsp_size < 4))
|
||||
goto return_unspecified;
|
||||
if (!is_cmd && (msg->rsp_size < 5)) {
|
||||
ipmi_inc_stat(intf, invalid_ipmb_responses);
|
||||
/* Construct a valid error response. */
|
||||
msg->rsp[0] = msg->data[0] & 0xfc; /* NetFN */
|
||||
msg->rsp[0] |= (1 << 2); /* Make it a response */
|
||||
msg->rsp[0] |= msg->data[2] & 3; /* rqLUN */
|
||||
msg->rsp[1] = msg->data[1]; /* Addr */
|
||||
msg->rsp[2] = msg->data[2] & 0xfc; /* rqSeq */
|
||||
msg->rsp[2] |= msg->data[0] & 0x3; /* rsLUN */
|
||||
msg->rsp[3] = msg->data[3]; /* Cmd */
|
||||
msg->rsp[4] = IPMI_ERR_UNSPECIFIED;
|
||||
msg->rsp_size = 5;
|
||||
}
|
||||
} else if ((msg->data_size >= 2)
|
||||
&& (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
|
||||
&& (msg->data[1] == IPMI_SEND_MSG_CMD)
|
||||
@ -5031,6 +5046,7 @@ struct ipmi_smi_msg *ipmi_alloc_smi_msg(void)
|
||||
if (rv) {
|
||||
rv->done = free_smi_msg;
|
||||
rv->user_data = NULL;
|
||||
rv->type = IPMI_SMI_MSG_TYPE_NORMAL;
|
||||
atomic_inc(&smi_msg_inuse_count);
|
||||
}
|
||||
return rv;
|
||||
@ -5383,6 +5399,13 @@ static int ipmi_init_msghandler(void)
|
||||
|
||||
atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
|
||||
|
||||
remove_work_wq = create_singlethread_workqueue("ipmi-msghandler-remove-wq");
|
||||
if (!remove_work_wq) {
|
||||
pr_err("unable to create ipmi-msghandler-remove-wq workqueue");
|
||||
rv = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
||||
out:
|
||||
@ -5408,6 +5431,8 @@ static void __exit cleanup_ipmi(void)
|
||||
int count;
|
||||
|
||||
if (initialized) {
|
||||
destroy_workqueue(remove_work_wq);
|
||||
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||
&panic_block);
|
||||
|
||||
|
@ -929,10 +929,8 @@ static int __init amd_iommu_v2_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_info("AMD IOMMUv2 driver by Joerg Roedel <jroedel@suse.de>\n");
|
||||
|
||||
if (!amd_iommu_v2_supported()) {
|
||||
pr_info("AMD IOMMUv2 functionality not available on this system\n");
|
||||
pr_info("AMD IOMMUv2 functionality not available on this system - This is not a bug.\n");
|
||||
/*
|
||||
* Load anyway to provide the symbols to other modules
|
||||
* which may use AMD IOMMUv2 optionally.
|
||||
@ -947,6 +945,8 @@ static int __init amd_iommu_v2_init(void)
|
||||
|
||||
amd_iommu_register_ppr_notifier(&ppr_nb);
|
||||
|
||||
pr_info("AMD IOMMUv2 loaded and initialized\n");
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
|
@ -144,6 +144,7 @@ static int cap_audit_static(struct intel_iommu *iommu, enum cap_audit_type type)
|
||||
{
|
||||
struct dmar_drhd_unit *d;
|
||||
struct intel_iommu *i;
|
||||
int rc = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
if (list_empty(&dmar_drhd_units))
|
||||
@ -169,11 +170,11 @@ static int cap_audit_static(struct intel_iommu *iommu, enum cap_audit_type type)
|
||||
*/
|
||||
if (intel_cap_smts_sanity() &&
|
||||
!intel_cap_flts_sanity() && !intel_cap_slts_sanity())
|
||||
return -EOPNOTSUPP;
|
||||
rc = -EOPNOTSUPP;
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int intel_cap_audit(enum cap_audit_type type, struct intel_iommu *iommu)
|
||||
|
@ -1339,13 +1339,11 @@ static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level,
|
||||
pte = &pte[pfn_level_offset(pfn, level)];
|
||||
|
||||
do {
|
||||
unsigned long level_pfn;
|
||||
unsigned long level_pfn = pfn & level_mask(level);
|
||||
|
||||
if (!dma_pte_present(pte))
|
||||
goto next;
|
||||
|
||||
level_pfn = pfn & level_mask(level);
|
||||
|
||||
/* If range covers entire pagetable, free it */
|
||||
if (start_pfn <= level_pfn &&
|
||||
last_pfn >= level_pfn + level_size(level) - 1) {
|
||||
@ -1366,7 +1364,7 @@ static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level,
|
||||
freelist);
|
||||
}
|
||||
next:
|
||||
pfn += level_size(level);
|
||||
pfn = level_pfn + level_size(level);
|
||||
} while (!first_pte_in_page(++pte) && pfn <= last_pfn);
|
||||
|
||||
if (first_pte)
|
||||
|
@ -200,8 +200,8 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
|
||||
#define DTE_HI_MASK2 GENMASK(7, 4)
|
||||
#define DTE_HI_SHIFT1 24 /* shift bit 8 to bit 32 */
|
||||
#define DTE_HI_SHIFT2 32 /* shift bit 4 to bit 36 */
|
||||
#define PAGE_DESC_HI_MASK1 GENMASK_ULL(39, 36)
|
||||
#define PAGE_DESC_HI_MASK2 GENMASK_ULL(35, 32)
|
||||
#define PAGE_DESC_HI_MASK1 GENMASK_ULL(35, 32)
|
||||
#define PAGE_DESC_HI_MASK2 GENMASK_ULL(39, 36)
|
||||
|
||||
static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
|
||||
{
|
||||
|
@ -567,9 +567,7 @@ config XEN_NETDEV_BACKEND
|
||||
config VMXNET3
|
||||
tristate "VMware VMXNET3 ethernet driver"
|
||||
depends on PCI && INET
|
||||
depends on !(PAGE_SIZE_64KB || ARM64_64K_PAGES || \
|
||||
IA64_PAGE_SIZE_64KB || PARISC_PAGE_SIZE_64KB || \
|
||||
PPC_64K_PAGES)
|
||||
depends on PAGE_SIZE_LESS_THAN_64KB
|
||||
help
|
||||
This driver supports VMware's vmxnet3 virtual ethernet NIC.
|
||||
To compile this driver as a module, choose M here: the
|
||||
|
@ -349,6 +349,19 @@ static const struct of_device_id b53_spi_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, b53_spi_of_match);
|
||||
|
||||
static const struct spi_device_id b53_spi_ids[] = {
|
||||
{ .name = "bcm5325" },
|
||||
{ .name = "bcm5365" },
|
||||
{ .name = "bcm5395" },
|
||||
{ .name = "bcm5397" },
|
||||
{ .name = "bcm5398" },
|
||||
{ .name = "bcm53115" },
|
||||
{ .name = "bcm53125" },
|
||||
{ .name = "bcm53128" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, b53_spi_ids);
|
||||
|
||||
static struct spi_driver b53_spi_driver = {
|
||||
.driver = {
|
||||
.name = "b53-switch",
|
||||
@ -357,6 +370,7 @@ static struct spi_driver b53_spi_driver = {
|
||||
.probe = b53_spi_probe,
|
||||
.remove = b53_spi_remove,
|
||||
.shutdown = b53_spi_shutdown,
|
||||
.id_table = b53_spi_ids,
|
||||
};
|
||||
|
||||
module_spi_driver(b53_spi_driver);
|
||||
|
@ -50,11 +50,22 @@ static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip,
|
||||
}
|
||||
|
||||
static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
|
||||
u16 status, u16 lpa,
|
||||
u16 ctrl, u16 status, u16 lpa,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
|
||||
state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
|
||||
|
||||
if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
|
||||
/* The Spped and Duplex Resolved register is 1 if AN is enabled
|
||||
* and complete, or if AN is disabled. So with disabled AN we
|
||||
* still get here on link up. But we want to set an_complete
|
||||
* only if AN was enabled, thus we look at BMCR_ANENABLE.
|
||||
* (According to 802.3-2008 section 22.2.4.2.10, we should be
|
||||
* able to get this same value from BMSR_ANEGCAPABLE, but tests
|
||||
* show that these Marvell PHYs don't conform to this part of
|
||||
* the specificaion - BMSR_ANEGCAPABLE is simply always 1.)
|
||||
*/
|
||||
state->an_complete = !!(ctrl & BMCR_ANENABLE);
|
||||
state->duplex = status &
|
||||
MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
|
||||
DUPLEX_FULL : DUPLEX_HALF;
|
||||
@ -81,6 +92,18 @@ static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
|
||||
dev_err(chip->dev, "invalid PHY speed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (state->link &&
|
||||
state->interface != PHY_INTERFACE_MODE_SGMII) {
|
||||
/* If Speed and Duplex Resolved register is 0 and link is up, it
|
||||
* means that AN was enabled, but link partner had it disabled
|
||||
* and the PHY invoked the Auto-Negotiation Bypass feature and
|
||||
* linked anyway.
|
||||
*/
|
||||
state->duplex = DUPLEX_FULL;
|
||||
if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
|
||||
state->speed = SPEED_2500;
|
||||
else
|
||||
state->speed = SPEED_1000;
|
||||
} else {
|
||||
state->link = false;
|
||||
}
|
||||
@ -168,9 +191,15 @@ int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
|
||||
int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
|
||||
int lane, struct phylink_link_state *state)
|
||||
{
|
||||
u16 lpa, status;
|
||||
u16 lpa, status, ctrl;
|
||||
int err;
|
||||
|
||||
err = mv88e6352_serdes_read(chip, MII_BMCR, &ctrl);
|
||||
if (err) {
|
||||
dev_err(chip->dev, "can't read Serdes PHY control: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mv88e6352_serdes_read(chip, 0x11, &status);
|
||||
if (err) {
|
||||
dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
|
||||
@ -183,7 +212,7 @@ int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
|
||||
return err;
|
||||
}
|
||||
|
||||
return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
|
||||
return mv88e6xxx_serdes_pcs_get_state(chip, ctrl, status, lpa, state);
|
||||
}
|
||||
|
||||
int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
|
||||
@ -883,9 +912,16 @@ int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
|
||||
static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip,
|
||||
int port, int lane, struct phylink_link_state *state)
|
||||
{
|
||||
u16 lpa, status;
|
||||
u16 lpa, status, ctrl;
|
||||
int err;
|
||||
|
||||
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
|
||||
MV88E6390_SGMII_BMCR, &ctrl);
|
||||
if (err) {
|
||||
dev_err(chip->dev, "can't read Serdes PHY control: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
|
||||
MV88E6390_SGMII_PHY_STATUS, &status);
|
||||
if (err) {
|
||||
@ -900,7 +936,7 @@ static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip,
|
||||
return err;
|
||||
}
|
||||
|
||||
return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
|
||||
return mv88e6xxx_serdes_pcs_get_state(chip, ctrl, status, lpa, state);
|
||||
}
|
||||
|
||||
static int mv88e6390_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip,
|
||||
@ -1271,9 +1307,31 @@ void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
|
||||
}
|
||||
}
|
||||
|
||||
static int mv88e6393x_serdes_port_errata(struct mv88e6xxx_chip *chip, int lane)
|
||||
static int mv88e6393x_serdes_power_lane(struct mv88e6xxx_chip *chip, int lane,
|
||||
bool on)
|
||||
{
|
||||
u16 reg, pcs;
|
||||
u16 reg;
|
||||
int err;
|
||||
|
||||
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
|
||||
MV88E6393X_SERDES_CTRL1, ®);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (on)
|
||||
reg &= ~(MV88E6393X_SERDES_CTRL1_TX_PDOWN |
|
||||
MV88E6393X_SERDES_CTRL1_RX_PDOWN);
|
||||
else
|
||||
reg |= MV88E6393X_SERDES_CTRL1_TX_PDOWN |
|
||||
MV88E6393X_SERDES_CTRL1_RX_PDOWN;
|
||||
|
||||
return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
|
||||
MV88E6393X_SERDES_CTRL1, reg);
|
||||
}
|
||||
|
||||
static int mv88e6393x_serdes_erratum_4_6(struct mv88e6xxx_chip *chip, int lane)
|
||||
{
|
||||
u16 reg;
|
||||
int err;
|
||||
|
||||
/* mv88e6393x family errata 4.6:
|
||||
@ -1284,10 +1342,7 @@ static int mv88e6393x_serdes_port_errata(struct mv88e6xxx_chip *chip, int lane)
|
||||
* It seems that after this workaround the SERDES is automatically
|
||||
* powered up (the bit is cleared), so power it down.
|
||||
*/
|
||||
if (lane == MV88E6393X_PORT0_LANE || lane == MV88E6393X_PORT9_LANE ||
|
||||
lane == MV88E6393X_PORT10_LANE) {
|
||||
err = mv88e6390_serdes_read(chip, lane,
|
||||
MDIO_MMD_PHYXS,
|
||||
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
|
||||
MV88E6393X_SERDES_POC, ®);
|
||||
if (err)
|
||||
return err;
|
||||
@ -1303,8 +1358,30 @@ static int mv88e6393x_serdes_port_errata(struct mv88e6xxx_chip *chip, int lane)
|
||||
err = mv88e6390_serdes_power_sgmii(chip, lane, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return mv88e6393x_serdes_power_lane(chip, lane, false);
|
||||
}
|
||||
|
||||
int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = mv88e6393x_serdes_erratum_4_6(chip, MV88E6393X_PORT0_LANE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = mv88e6393x_serdes_erratum_4_6(chip, MV88E6393X_PORT9_LANE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return mv88e6393x_serdes_erratum_4_6(chip, MV88E6393X_PORT10_LANE);
|
||||
}
|
||||
|
||||
static int mv88e6393x_serdes_erratum_4_8(struct mv88e6xxx_chip *chip, int lane)
|
||||
{
|
||||
u16 reg, pcs;
|
||||
int err;
|
||||
|
||||
/* mv88e6393x family errata 4.8:
|
||||
* When a SERDES port is operating in 1000BASE-X or SGMII mode link may
|
||||
* not come up after hardware reset or software reset of SERDES core.
|
||||
@ -1334,38 +1411,149 @@ static int mv88e6393x_serdes_port_errata(struct mv88e6xxx_chip *chip, int lane)
|
||||
MV88E6393X_ERRATA_4_8_REG, reg);
|
||||
}
|
||||
|
||||
int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip)
|
||||
static int mv88e6393x_serdes_erratum_5_2(struct mv88e6xxx_chip *chip, int lane,
|
||||
u8 cmode)
|
||||
{
|
||||
static const struct {
|
||||
u16 dev, reg, val, mask;
|
||||
} fixes[] = {
|
||||
{ MDIO_MMD_VEND1, 0x8093, 0xcb5a, 0xffff },
|
||||
{ MDIO_MMD_VEND1, 0x8171, 0x7088, 0xffff },
|
||||
{ MDIO_MMD_VEND1, 0x80c9, 0x311a, 0xffff },
|
||||
{ MDIO_MMD_VEND1, 0x80a2, 0x8000, 0xff7f },
|
||||
{ MDIO_MMD_VEND1, 0x80a9, 0x0000, 0xfff0 },
|
||||
{ MDIO_MMD_VEND1, 0x80a3, 0x0000, 0xf8ff },
|
||||
{ MDIO_MMD_PHYXS, MV88E6393X_SERDES_POC,
|
||||
MV88E6393X_SERDES_POC_RESET, MV88E6393X_SERDES_POC_RESET },
|
||||
};
|
||||
int err, i;
|
||||
u16 reg;
|
||||
|
||||
/* mv88e6393x family errata 5.2:
|
||||
* For optimal signal integrity the following sequence should be applied
|
||||
* to SERDES operating in 10G mode. These registers only apply to 10G
|
||||
* operation and have no effect on other speeds.
|
||||
*/
|
||||
if (cmode != MV88E6393X_PORT_STS_CMODE_10GBASER)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fixes); ++i) {
|
||||
err = mv88e6390_serdes_read(chip, lane, fixes[i].dev,
|
||||
fixes[i].reg, ®);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
reg &= ~fixes[i].mask;
|
||||
reg |= fixes[i].val;
|
||||
|
||||
err = mv88e6390_serdes_write(chip, lane, fixes[i].dev,
|
||||
fixes[i].reg, reg);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv88e6393x_serdes_fix_2500basex_an(struct mv88e6xxx_chip *chip,
|
||||
int lane, u8 cmode, bool on)
|
||||
{
|
||||
u16 reg;
|
||||
int err;
|
||||
|
||||
err = mv88e6393x_serdes_port_errata(chip, MV88E6393X_PORT0_LANE);
|
||||
if (cmode != MV88E6XXX_PORT_STS_CMODE_2500BASEX)
|
||||
return 0;
|
||||
|
||||
/* Inband AN is broken on Amethyst in 2500base-x mode when set by
|
||||
* standard mechanism (via cmode).
|
||||
* We can get around this by configuring the PCS mode to 1000base-x
|
||||
* and then writing value 0x58 to register 1e.8000. (This must be done
|
||||
* while SerDes receiver and transmitter are disabled, which is, when
|
||||
* this function is called.)
|
||||
* It seem that when we do this configuration to 2500base-x mode (by
|
||||
* changing PCS mode to 1000base-x and frequency to 3.125 GHz from
|
||||
* 1.25 GHz) and then configure to sgmii or 1000base-x, the device
|
||||
* thinks that it already has SerDes at 1.25 GHz and does not change
|
||||
* the 1e.8000 register, leaving SerDes at 3.125 GHz.
|
||||
* To avoid this, change PCS mode back to 2500base-x when disabling
|
||||
* SerDes from 2500base-x mode.
|
||||
*/
|
||||
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
|
||||
MV88E6393X_SERDES_POC, ®);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = mv88e6393x_serdes_port_errata(chip, MV88E6393X_PORT9_LANE);
|
||||
reg &= ~(MV88E6393X_SERDES_POC_PCS_MASK | MV88E6393X_SERDES_POC_AN);
|
||||
if (on)
|
||||
reg |= MV88E6393X_SERDES_POC_PCS_1000BASEX |
|
||||
MV88E6393X_SERDES_POC_AN;
|
||||
else
|
||||
reg |= MV88E6393X_SERDES_POC_PCS_2500BASEX;
|
||||
reg |= MV88E6393X_SERDES_POC_RESET;
|
||||
|
||||
err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
|
||||
MV88E6393X_SERDES_POC, reg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return mv88e6393x_serdes_port_errata(chip, MV88E6393X_PORT10_LANE);
|
||||
err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_VEND1, 0x8000, 0x58);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
|
||||
bool on)
|
||||
{
|
||||
u8 cmode = chip->ports[port].cmode;
|
||||
int err;
|
||||
|
||||
if (port != 0 && port != 9 && port != 10)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (on) {
|
||||
err = mv88e6393x_serdes_erratum_4_8(chip, lane);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = mv88e6393x_serdes_erratum_5_2(chip, lane, cmode);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = mv88e6393x_serdes_fix_2500basex_an(chip, lane, cmode,
|
||||
true);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = mv88e6393x_serdes_power_lane(chip, lane, true);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (cmode) {
|
||||
case MV88E6XXX_PORT_STS_CMODE_SGMII:
|
||||
case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
|
||||
case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
|
||||
return mv88e6390_serdes_power_sgmii(chip, lane, on);
|
||||
err = mv88e6390_serdes_power_sgmii(chip, lane, on);
|
||||
break;
|
||||
case MV88E6393X_PORT_STS_CMODE_5GBASER:
|
||||
case MV88E6393X_PORT_STS_CMODE_10GBASER:
|
||||
return mv88e6390_serdes_power_10g(chip, lane, on);
|
||||
err = mv88e6390_serdes_power_10g(chip, lane, on);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!on) {
|
||||
err = mv88e6393x_serdes_power_lane(chip, lane, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = mv88e6393x_serdes_fix_2500basex_an(chip, lane, cmode,
|
||||
false);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -93,6 +93,10 @@
|
||||
#define MV88E6393X_SERDES_POC_PCS_MASK 0x0007
|
||||
#define MV88E6393X_SERDES_POC_RESET BIT(15)
|
||||
#define MV88E6393X_SERDES_POC_PDOWN BIT(5)
|
||||
#define MV88E6393X_SERDES_POC_AN BIT(3)
|
||||
#define MV88E6393X_SERDES_CTRL1 0xf003
|
||||
#define MV88E6393X_SERDES_CTRL1_TX_PDOWN BIT(9)
|
||||
#define MV88E6393X_SERDES_CTRL1_RX_PDOWN BIT(8)
|
||||
|
||||
#define MV88E6393X_ERRATA_4_8_REG 0xF074
|
||||
#define MV88E6393X_ERRATA_4_8_BIT BIT(14)
|
||||
|
@ -107,6 +107,7 @@
|
||||
#define RTL8365MB_LEARN_LIMIT_MAX_8365MB_VC 2112
|
||||
|
||||
/* Family-specific data and limits */
|
||||
#define RTL8365MB_PHYADDRMAX 7
|
||||
#define RTL8365MB_NUM_PHYREGS 32
|
||||
#define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1)
|
||||
#define RTL8365MB_MAX_NUM_PORTS (RTL8365MB_CPU_PORT_NUM_8365MB_VC + 1)
|
||||
@ -176,7 +177,7 @@
|
||||
#define RTL8365MB_INDIRECT_ACCESS_STATUS_REG 0x1F01
|
||||
#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG 0x1F02
|
||||
#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK GENMASK(4, 0)
|
||||
#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK GENMASK(6, 5)
|
||||
#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK GENMASK(7, 5)
|
||||
#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK GENMASK(11, 8)
|
||||
#define RTL8365MB_PHY_BASE 0x2000
|
||||
#define RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG 0x1F03
|
||||
@ -679,6 +680,9 @@ static int rtl8365mb_phy_read(struct realtek_smi *smi, int phy, int regnum)
|
||||
u16 val;
|
||||
int ret;
|
||||
|
||||
if (phy > RTL8365MB_PHYADDRMAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (regnum > RTL8365MB_PHYREGMAX)
|
||||
return -EINVAL;
|
||||
|
||||
@ -704,6 +708,9 @@ static int rtl8365mb_phy_write(struct realtek_smi *smi, int phy, int regnum,
|
||||
u32 ocp_addr;
|
||||
int ret;
|
||||
|
||||
if (phy > RTL8365MB_PHYADDRMAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (regnum > RTL8365MB_PHYREGMAX)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -40,10 +40,12 @@
|
||||
|
||||
#define AQ_DEVICE_ID_AQC113DEV 0x00C0
|
||||
#define AQ_DEVICE_ID_AQC113CS 0x94C0
|
||||
#define AQ_DEVICE_ID_AQC113CA 0x34C0
|
||||
#define AQ_DEVICE_ID_AQC114CS 0x93C0
|
||||
#define AQ_DEVICE_ID_AQC113 0x04C0
|
||||
#define AQ_DEVICE_ID_AQC113C 0x14C0
|
||||
#define AQ_DEVICE_ID_AQC115C 0x12C0
|
||||
#define AQ_DEVICE_ID_AQC116C 0x11C0
|
||||
|
||||
#define HW_ATL_NIC_NAME "Marvell (aQuantia) AQtion 10Gbit Network Adapter"
|
||||
|
||||
@ -53,20 +55,19 @@
|
||||
|
||||
#define AQ_NIC_RATE_10G BIT(0)
|
||||
#define AQ_NIC_RATE_5G BIT(1)
|
||||
#define AQ_NIC_RATE_5GSR BIT(2)
|
||||
#define AQ_NIC_RATE_2G5 BIT(3)
|
||||
#define AQ_NIC_RATE_1G BIT(4)
|
||||
#define AQ_NIC_RATE_100M BIT(5)
|
||||
#define AQ_NIC_RATE_10M BIT(6)
|
||||
#define AQ_NIC_RATE_1G_HALF BIT(7)
|
||||
#define AQ_NIC_RATE_100M_HALF BIT(8)
|
||||
#define AQ_NIC_RATE_10M_HALF BIT(9)
|
||||
#define AQ_NIC_RATE_2G5 BIT(2)
|
||||
#define AQ_NIC_RATE_1G BIT(3)
|
||||
#define AQ_NIC_RATE_100M BIT(4)
|
||||
#define AQ_NIC_RATE_10M BIT(5)
|
||||
#define AQ_NIC_RATE_1G_HALF BIT(6)
|
||||
#define AQ_NIC_RATE_100M_HALF BIT(7)
|
||||
#define AQ_NIC_RATE_10M_HALF BIT(8)
|
||||
|
||||
#define AQ_NIC_RATE_EEE_10G BIT(10)
|
||||
#define AQ_NIC_RATE_EEE_5G BIT(11)
|
||||
#define AQ_NIC_RATE_EEE_2G5 BIT(12)
|
||||
#define AQ_NIC_RATE_EEE_1G BIT(13)
|
||||
#define AQ_NIC_RATE_EEE_100M BIT(14)
|
||||
#define AQ_NIC_RATE_EEE_10G BIT(9)
|
||||
#define AQ_NIC_RATE_EEE_5G BIT(10)
|
||||
#define AQ_NIC_RATE_EEE_2G5 BIT(11)
|
||||
#define AQ_NIC_RATE_EEE_1G BIT(12)
|
||||
#define AQ_NIC_RATE_EEE_100M BIT(13)
|
||||
#define AQ_NIC_RATE_EEE_MSK (AQ_NIC_RATE_EEE_10G |\
|
||||
AQ_NIC_RATE_EEE_5G |\
|
||||
AQ_NIC_RATE_EEE_2G5 |\
|
||||
|
@ -80,6 +80,8 @@ struct aq_hw_link_status_s {
|
||||
};
|
||||
|
||||
struct aq_stats_s {
|
||||
u64 brc;
|
||||
u64 btc;
|
||||
u64 uprc;
|
||||
u64 mprc;
|
||||
u64 bprc;
|
||||
|
@ -316,19 +316,23 @@ int aq_nic_ndev_register(struct aq_nic_s *self)
|
||||
aq_macsec_init(self);
|
||||
#endif
|
||||
|
||||
if (platform_get_ethdev_address(&self->pdev->dev, self->ndev) != 0) {
|
||||
// If DT has none or an invalid one, ask device for MAC address
|
||||
mutex_lock(&self->fwreq_mutex);
|
||||
err = self->aq_fw_ops->get_mac_permanent(self->aq_hw, addr);
|
||||
mutex_unlock(&self->fwreq_mutex);
|
||||
|
||||
if (err)
|
||||
goto err_exit;
|
||||
|
||||
if (is_valid_ether_addr(addr) &&
|
||||
aq_nic_is_valid_ether_addr(addr)) {
|
||||
eth_hw_addr_set(self->ndev, addr);
|
||||
|
||||
if (!is_valid_ether_addr(self->ndev->dev_addr) ||
|
||||
!aq_nic_is_valid_ether_addr(self->ndev->dev_addr)) {
|
||||
} else {
|
||||
netdev_warn(self->ndev, "MAC is invalid, will use random.");
|
||||
eth_hw_addr_random(self->ndev);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(AQ_CFG_MAC_ADDR_PERMANENT)
|
||||
{
|
||||
@ -905,7 +909,13 @@ u64 *aq_nic_get_stats(struct aq_nic_s *self, u64 *data)
|
||||
data[++i] = stats->mbtc;
|
||||
data[++i] = stats->bbrc;
|
||||
data[++i] = stats->bbtc;
|
||||
if (stats->brc)
|
||||
data[++i] = stats->brc;
|
||||
else
|
||||
data[++i] = stats->ubrc + stats->mbrc + stats->bbrc;
|
||||
if (stats->btc)
|
||||
data[++i] = stats->btc;
|
||||
else
|
||||
data[++i] = stats->ubtc + stats->mbtc + stats->bbtc;
|
||||
data[++i] = stats->dma_pkt_rc;
|
||||
data[++i] = stats->dma_pkt_tc;
|
||||
|
@ -49,6 +49,8 @@ static const struct pci_device_id aq_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC113), },
|
||||
{ PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC113C), },
|
||||
{ PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC115C), },
|
||||
{ PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC113CA), },
|
||||
{ PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC116C), },
|
||||
|
||||
{}
|
||||
};
|
||||
@ -85,7 +87,10 @@ static const struct aq_board_revision_s hw_atl_boards[] = {
|
||||
{ AQ_DEVICE_ID_AQC113CS, AQ_HWREV_ANY, &hw_atl2_ops, &hw_atl2_caps_aqc113, },
|
||||
{ AQ_DEVICE_ID_AQC114CS, AQ_HWREV_ANY, &hw_atl2_ops, &hw_atl2_caps_aqc113, },
|
||||
{ AQ_DEVICE_ID_AQC113C, AQ_HWREV_ANY, &hw_atl2_ops, &hw_atl2_caps_aqc113, },
|
||||
{ AQ_DEVICE_ID_AQC115C, AQ_HWREV_ANY, &hw_atl2_ops, &hw_atl2_caps_aqc113, },
|
||||
{ AQ_DEVICE_ID_AQC115C, AQ_HWREV_ANY, &hw_atl2_ops, &hw_atl2_caps_aqc115c, },
|
||||
{ AQ_DEVICE_ID_AQC113CA, AQ_HWREV_ANY, &hw_atl2_ops, &hw_atl2_caps_aqc113, },
|
||||
{ AQ_DEVICE_ID_AQC116C, AQ_HWREV_ANY, &hw_atl2_ops, &hw_atl2_caps_aqc116c, },
|
||||
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, aq_pci_tbl);
|
||||
|
@ -362,9 +362,6 @@ unsigned int aq_vec_get_sw_stats(struct aq_vec_s *self, const unsigned int tc, u
|
||||
{
|
||||
unsigned int count;
|
||||
|
||||
WARN_ONCE(!aq_vec_is_valid_tc(self, tc),
|
||||
"Invalid tc %u (#rx=%u, #tx=%u)\n",
|
||||
tc, self->rx_rings, self->tx_rings);
|
||||
if (!aq_vec_is_valid_tc(self, tc))
|
||||
return 0;
|
||||
|
||||
|
@ -867,12 +867,20 @@ static int hw_atl_fw1x_deinit(struct aq_hw_s *self)
|
||||
int hw_atl_utils_update_stats(struct aq_hw_s *self)
|
||||
{
|
||||
struct aq_stats_s *cs = &self->curr_stats;
|
||||
struct aq_stats_s curr_stats = *cs;
|
||||
struct hw_atl_utils_mbox mbox;
|
||||
bool corrupted_stats = false;
|
||||
|
||||
hw_atl_utils_mpi_read_stats(self, &mbox);
|
||||
|
||||
#define AQ_SDELTA(_N_) (self->curr_stats._N_ += \
|
||||
mbox.stats._N_ - self->last_stats._N_)
|
||||
#define AQ_SDELTA(_N_) \
|
||||
do { \
|
||||
if (!corrupted_stats && \
|
||||
((s64)(mbox.stats._N_ - self->last_stats._N_)) >= 0) \
|
||||
curr_stats._N_ += mbox.stats._N_ - self->last_stats._N_; \
|
||||
else \
|
||||
corrupted_stats = true; \
|
||||
} while (0)
|
||||
|
||||
if (self->aq_link_status.mbps) {
|
||||
AQ_SDELTA(uprc);
|
||||
@ -892,6 +900,9 @@ int hw_atl_utils_update_stats(struct aq_hw_s *self)
|
||||
AQ_SDELTA(bbrc);
|
||||
AQ_SDELTA(bbtc);
|
||||
AQ_SDELTA(dpc);
|
||||
|
||||
if (!corrupted_stats)
|
||||
*cs = curr_stats;
|
||||
}
|
||||
#undef AQ_SDELTA
|
||||
|
||||
|
@ -132,9 +132,6 @@ static enum hw_atl_fw2x_rate link_speed_mask_2fw2x_ratemask(u32 speed)
|
||||
if (speed & AQ_NIC_RATE_5G)
|
||||
rate |= FW2X_RATE_5G;
|
||||
|
||||
if (speed & AQ_NIC_RATE_5GSR)
|
||||
rate |= FW2X_RATE_5G;
|
||||
|
||||
if (speed & AQ_NIC_RATE_2G5)
|
||||
rate |= FW2X_RATE_2G5;
|
||||
|
||||
|
@ -65,11 +65,25 @@ const struct aq_hw_caps_s hw_atl2_caps_aqc113 = {
|
||||
AQ_NIC_RATE_5G |
|
||||
AQ_NIC_RATE_2G5 |
|
||||
AQ_NIC_RATE_1G |
|
||||
AQ_NIC_RATE_1G_HALF |
|
||||
AQ_NIC_RATE_100M |
|
||||
AQ_NIC_RATE_100M_HALF |
|
||||
AQ_NIC_RATE_10M |
|
||||
AQ_NIC_RATE_10M_HALF,
|
||||
AQ_NIC_RATE_10M,
|
||||
};
|
||||
|
||||
const struct aq_hw_caps_s hw_atl2_caps_aqc115c = {
|
||||
DEFAULT_BOARD_BASIC_CAPABILITIES,
|
||||
.media_type = AQ_HW_MEDIA_TYPE_TP,
|
||||
.link_speed_msk = AQ_NIC_RATE_2G5 |
|
||||
AQ_NIC_RATE_1G |
|
||||
AQ_NIC_RATE_100M |
|
||||
AQ_NIC_RATE_10M,
|
||||
};
|
||||
|
||||
const struct aq_hw_caps_s hw_atl2_caps_aqc116c = {
|
||||
DEFAULT_BOARD_BASIC_CAPABILITIES,
|
||||
.media_type = AQ_HW_MEDIA_TYPE_TP,
|
||||
.link_speed_msk = AQ_NIC_RATE_1G |
|
||||
AQ_NIC_RATE_100M |
|
||||
AQ_NIC_RATE_10M,
|
||||
};
|
||||
|
||||
static u32 hw_atl2_sem_act_rslvr_get(struct aq_hw_s *self)
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "aq_common.h"
|
||||
|
||||
extern const struct aq_hw_caps_s hw_atl2_caps_aqc113;
|
||||
extern const struct aq_hw_caps_s hw_atl2_caps_aqc115c;
|
||||
extern const struct aq_hw_caps_s hw_atl2_caps_aqc116c;
|
||||
extern const struct aq_hw_ops hw_atl2_ops;
|
||||
|
||||
#endif /* HW_ATL2_H */
|
||||
|
@ -239,7 +239,8 @@ struct version_s {
|
||||
u8 minor;
|
||||
u16 build;
|
||||
} phy;
|
||||
u32 rsvd;
|
||||
u32 drv_iface_ver:4;
|
||||
u32 rsvd:28;
|
||||
};
|
||||
|
||||
struct link_status_s {
|
||||
@ -424,7 +425,7 @@ struct cable_diag_status_s {
|
||||
u16 rsvd2;
|
||||
};
|
||||
|
||||
struct statistics_s {
|
||||
struct statistics_a0_s {
|
||||
struct {
|
||||
u32 link_up;
|
||||
u32 link_down;
|
||||
@ -457,6 +458,33 @@ struct statistics_s {
|
||||
u32 reserve_fw_gap;
|
||||
};
|
||||
|
||||
struct __packed statistics_b0_s {
|
||||
u64 rx_good_octets;
|
||||
u64 rx_pause_frames;
|
||||
u64 rx_good_frames;
|
||||
u64 rx_errors;
|
||||
u64 rx_unicast_frames;
|
||||
u64 rx_multicast_frames;
|
||||
u64 rx_broadcast_frames;
|
||||
|
||||
u64 tx_good_octets;
|
||||
u64 tx_pause_frames;
|
||||
u64 tx_good_frames;
|
||||
u64 tx_errors;
|
||||
u64 tx_unicast_frames;
|
||||
u64 tx_multicast_frames;
|
||||
u64 tx_broadcast_frames;
|
||||
|
||||
u32 main_loop_cycles;
|
||||
};
|
||||
|
||||
struct __packed statistics_s {
|
||||
union __packed {
|
||||
struct statistics_a0_s a0;
|
||||
struct statistics_b0_s b0;
|
||||
};
|
||||
};
|
||||
|
||||
struct filter_caps_s {
|
||||
u8 l2_filters_base_index:6;
|
||||
u8 flexible_filter_mask:2;
|
||||
@ -545,7 +573,7 @@ struct management_status_s {
|
||||
u32 rsvd5;
|
||||
};
|
||||
|
||||
struct fw_interface_out {
|
||||
struct __packed fw_interface_out {
|
||||
struct transaction_counter_s transaction_id;
|
||||
struct version_s version;
|
||||
struct link_status_s link_status;
|
||||
@ -569,7 +597,6 @@ struct fw_interface_out {
|
||||
struct core_dump_s core_dump;
|
||||
u32 rsvd11;
|
||||
struct statistics_s stats;
|
||||
u32 rsvd12;
|
||||
struct filter_caps_s filter_caps;
|
||||
struct device_caps_s device_caps;
|
||||
u32 rsvd13;
|
||||
@ -592,6 +619,9 @@ struct fw_interface_out {
|
||||
#define AQ_HOST_MODE_LOW_POWER 3U
|
||||
#define AQ_HOST_MODE_SHUTDOWN 4U
|
||||
|
||||
#define AQ_A2_FW_INTERFACE_A0 0
|
||||
#define AQ_A2_FW_INTERFACE_B0 1
|
||||
|
||||
int hw_atl2_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops);
|
||||
|
||||
int hw_atl2_utils_soft_reset(struct aq_hw_s *self);
|
||||
|
@ -84,7 +84,7 @@ static int hw_atl2_shared_buffer_read_block(struct aq_hw_s *self,
|
||||
if (cnt > AQ_A2_FW_READ_TRY_MAX)
|
||||
return -ETIME;
|
||||
if (tid1.transaction_cnt_a != tid1.transaction_cnt_b)
|
||||
udelay(1);
|
||||
mdelay(1);
|
||||
} while (tid1.transaction_cnt_a != tid1.transaction_cnt_b);
|
||||
|
||||
hw_atl2_mif_shared_buf_read(self, offset, (u32 *)data, dwords);
|
||||
@ -154,7 +154,7 @@ static void a2_link_speed_mask2fw(u32 speed,
|
||||
{
|
||||
link_options->rate_10G = !!(speed & AQ_NIC_RATE_10G);
|
||||
link_options->rate_5G = !!(speed & AQ_NIC_RATE_5G);
|
||||
link_options->rate_N5G = !!(speed & AQ_NIC_RATE_5GSR);
|
||||
link_options->rate_N5G = link_options->rate_5G;
|
||||
link_options->rate_2P5G = !!(speed & AQ_NIC_RATE_2G5);
|
||||
link_options->rate_N2P5G = link_options->rate_2P5G;
|
||||
link_options->rate_1G = !!(speed & AQ_NIC_RATE_1G);
|
||||
@ -192,8 +192,6 @@ static u32 a2_fw_lkp_to_mask(struct lkp_link_caps_s *lkp_link_caps)
|
||||
rate |= AQ_NIC_RATE_10G;
|
||||
if (lkp_link_caps->rate_5G)
|
||||
rate |= AQ_NIC_RATE_5G;
|
||||
if (lkp_link_caps->rate_N5G)
|
||||
rate |= AQ_NIC_RATE_5GSR;
|
||||
if (lkp_link_caps->rate_2P5G)
|
||||
rate |= AQ_NIC_RATE_2G5;
|
||||
if (lkp_link_caps->rate_1G)
|
||||
@ -335,15 +333,22 @@ static int aq_a2_fw_get_mac_permanent(struct aq_hw_s *self, u8 *mac)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aq_a2_fw_update_stats(struct aq_hw_s *self)
|
||||
static void aq_a2_fill_a0_stats(struct aq_hw_s *self,
|
||||
struct statistics_s *stats)
|
||||
{
|
||||
struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
|
||||
struct statistics_s stats;
|
||||
struct aq_stats_s *cs = &self->curr_stats;
|
||||
struct aq_stats_s curr_stats = *cs;
|
||||
bool corrupted_stats = false;
|
||||
|
||||
hw_atl2_shared_buffer_read_safe(self, stats, &stats);
|
||||
|
||||
#define AQ_SDELTA(_N_, _F_) (self->curr_stats._N_ += \
|
||||
stats.msm._F_ - priv->last_stats.msm._F_)
|
||||
#define AQ_SDELTA(_N, _F) \
|
||||
do { \
|
||||
if (!corrupted_stats && \
|
||||
((s64)(stats->a0.msm._F - priv->last_stats.a0.msm._F)) >= 0) \
|
||||
curr_stats._N += stats->a0.msm._F - priv->last_stats.a0.msm._F;\
|
||||
else \
|
||||
corrupted_stats = true; \
|
||||
} while (0)
|
||||
|
||||
if (self->aq_link_status.mbps) {
|
||||
AQ_SDELTA(uprc, rx_unicast_frames);
|
||||
@ -362,17 +367,76 @@ static int aq_a2_fw_update_stats(struct aq_hw_s *self)
|
||||
AQ_SDELTA(mbtc, tx_multicast_octets);
|
||||
AQ_SDELTA(bbrc, rx_broadcast_octets);
|
||||
AQ_SDELTA(bbtc, tx_broadcast_octets);
|
||||
|
||||
if (!corrupted_stats)
|
||||
*cs = curr_stats;
|
||||
}
|
||||
#undef AQ_SDELTA
|
||||
self->curr_stats.dma_pkt_rc =
|
||||
hw_atl_stats_rx_dma_good_pkt_counter_get(self);
|
||||
self->curr_stats.dma_pkt_tc =
|
||||
hw_atl_stats_tx_dma_good_pkt_counter_get(self);
|
||||
self->curr_stats.dma_oct_rc =
|
||||
hw_atl_stats_rx_dma_good_octet_counter_get(self);
|
||||
self->curr_stats.dma_oct_tc =
|
||||
hw_atl_stats_tx_dma_good_octet_counter_get(self);
|
||||
self->curr_stats.dpc = hw_atl_rpb_rx_dma_drop_pkt_cnt_get(self);
|
||||
|
||||
}
|
||||
|
||||
static void aq_a2_fill_b0_stats(struct aq_hw_s *self,
|
||||
struct statistics_s *stats)
|
||||
{
|
||||
struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
|
||||
struct aq_stats_s *cs = &self->curr_stats;
|
||||
struct aq_stats_s curr_stats = *cs;
|
||||
bool corrupted_stats = false;
|
||||
|
||||
#define AQ_SDELTA(_N, _F) \
|
||||
do { \
|
||||
if (!corrupted_stats && \
|
||||
((s64)(stats->b0._F - priv->last_stats.b0._F)) >= 0) \
|
||||
curr_stats._N += stats->b0._F - priv->last_stats.b0._F; \
|
||||
else \
|
||||
corrupted_stats = true; \
|
||||
} while (0)
|
||||
|
||||
if (self->aq_link_status.mbps) {
|
||||
AQ_SDELTA(uprc, rx_unicast_frames);
|
||||
AQ_SDELTA(mprc, rx_multicast_frames);
|
||||
AQ_SDELTA(bprc, rx_broadcast_frames);
|
||||
AQ_SDELTA(erpr, rx_errors);
|
||||
AQ_SDELTA(brc, rx_good_octets);
|
||||
|
||||
AQ_SDELTA(uptc, tx_unicast_frames);
|
||||
AQ_SDELTA(mptc, tx_multicast_frames);
|
||||
AQ_SDELTA(bptc, tx_broadcast_frames);
|
||||
AQ_SDELTA(erpt, tx_errors);
|
||||
AQ_SDELTA(btc, tx_good_octets);
|
||||
|
||||
if (!corrupted_stats)
|
||||
*cs = curr_stats;
|
||||
}
|
||||
#undef AQ_SDELTA
|
||||
}
|
||||
|
||||
static int aq_a2_fw_update_stats(struct aq_hw_s *self)
|
||||
{
|
||||
struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
|
||||
struct aq_stats_s *cs = &self->curr_stats;
|
||||
struct statistics_s stats;
|
||||
struct version_s version;
|
||||
int err;
|
||||
|
||||
err = hw_atl2_shared_buffer_read_safe(self, version, &version);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = hw_atl2_shared_buffer_read_safe(self, stats, &stats);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (version.drv_iface_ver == AQ_A2_FW_INTERFACE_A0)
|
||||
aq_a2_fill_a0_stats(self, &stats);
|
||||
else
|
||||
aq_a2_fill_b0_stats(self, &stats);
|
||||
|
||||
cs->dma_pkt_rc = hw_atl_stats_rx_dma_good_pkt_counter_get(self);
|
||||
cs->dma_pkt_tc = hw_atl_stats_tx_dma_good_pkt_counter_get(self);
|
||||
cs->dma_oct_rc = hw_atl_stats_rx_dma_good_octet_counter_get(self);
|
||||
cs->dma_oct_tc = hw_atl_stats_tx_dma_good_octet_counter_get(self);
|
||||
cs->dpc = hw_atl_rpb_rx_dma_drop_pkt_cnt_get(self);
|
||||
|
||||
memcpy(&priv->last_stats, &stats, sizeof(stats));
|
||||
|
||||
@ -499,9 +563,9 @@ u32 hw_atl2_utils_get_fw_version(struct aq_hw_s *self)
|
||||
hw_atl2_shared_buffer_read_safe(self, version, &version);
|
||||
|
||||
/* A2 FW version is stored in reverse order */
|
||||
return version.mac.major << 24 |
|
||||
version.mac.minor << 16 |
|
||||
version.mac.build;
|
||||
return version.bundle.major << 24 |
|
||||
version.bundle.minor << 16 |
|
||||
version.bundle.build;
|
||||
}
|
||||
|
||||
int hw_atl2_utils_get_action_resolve_table_caps(struct aq_hw_s *self,
|
||||
|
@ -4550,6 +4550,8 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev)
|
||||
|
||||
fsl_mc_portal_free(priv->mc_io);
|
||||
|
||||
destroy_workqueue(priv->dpaa2_ptp_wq);
|
||||
|
||||
dev_dbg(net_dev->dev.parent, "Removed interface %s\n", net_dev->name);
|
||||
|
||||
free_netdev(net_dev);
|
||||
|
@ -628,17 +628,9 @@ static bool reuse_rx_pools(struct ibmvnic_adapter *adapter)
|
||||
old_buff_size = adapter->prev_rx_buf_sz;
|
||||
new_buff_size = adapter->cur_rx_buf_sz;
|
||||
|
||||
/* Require buff size to be exactly same for now */
|
||||
if (old_buff_size != new_buff_size)
|
||||
return false;
|
||||
|
||||
if (old_num_pools == new_num_pools && old_pool_size == new_pool_size)
|
||||
return true;
|
||||
|
||||
if (old_num_pools < adapter->min_rx_queues ||
|
||||
old_num_pools > adapter->max_rx_queues ||
|
||||
old_pool_size < adapter->min_rx_add_entries_per_subcrq ||
|
||||
old_pool_size > adapter->max_rx_add_entries_per_subcrq)
|
||||
if (old_buff_size != new_buff_size ||
|
||||
old_num_pools != new_num_pools ||
|
||||
old_pool_size != new_pool_size)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -874,17 +866,9 @@ static bool reuse_tx_pools(struct ibmvnic_adapter *adapter)
|
||||
old_mtu = adapter->prev_mtu;
|
||||
new_mtu = adapter->req_mtu;
|
||||
|
||||
/* Require MTU to be exactly same to reuse pools for now */
|
||||
if (old_mtu != new_mtu)
|
||||
return false;
|
||||
|
||||
if (old_num_pools == new_num_pools && old_pool_size == new_pool_size)
|
||||
return true;
|
||||
|
||||
if (old_num_pools < adapter->min_tx_queues ||
|
||||
old_num_pools > adapter->max_tx_queues ||
|
||||
old_pool_size < adapter->min_tx_entries_per_subcrq ||
|
||||
old_pool_size > adapter->max_tx_entries_per_subcrq)
|
||||
if (old_mtu != new_mtu ||
|
||||
old_num_pools != new_num_pools ||
|
||||
old_pool_size != new_pool_size)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -383,6 +383,7 @@ bool ice_alloc_rx_bufs_zc(struct ice_rx_ring *rx_ring, u16 count)
|
||||
while (i--) {
|
||||
dma = xsk_buff_xdp_get_dma(*xdp);
|
||||
rx_desc->read.pkt_addr = cpu_to_le64(dma);
|
||||
rx_desc->wb.status_error0 = 0;
|
||||
|
||||
rx_desc++;
|
||||
xdp++;
|
||||
|
@ -7454,7 +7454,7 @@ static int mvpp2_probe(struct platform_device *pdev)
|
||||
|
||||
shared = num_present_cpus() - priv->nthreads;
|
||||
if (shared > 0)
|
||||
bitmap_fill(&priv->lock_map,
|
||||
bitmap_set(&priv->lock_map, 0,
|
||||
min_t(int, shared, MVPP2_MAX_THREADS));
|
||||
|
||||
for (i = 0; i < MVPP2_MAX_THREADS; i++) {
|
||||
|
@ -2341,7 +2341,7 @@ static int rvu_mbox_init(struct rvu *rvu, struct mbox_wq_info *mw,
|
||||
goto free_regions;
|
||||
break;
|
||||
default:
|
||||
return err;
|
||||
goto free_regions;
|
||||
}
|
||||
|
||||
mw->mbox_wq = alloc_workqueue(name,
|
||||
|
@ -670,7 +670,7 @@ void __init mlx4_en_init_ptys2ethtool_map(void)
|
||||
MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_1000BASE_T, SPEED_1000,
|
||||
ETHTOOL_LINK_MODE_1000baseT_Full_BIT);
|
||||
MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_1000BASE_CX_SGMII, SPEED_1000,
|
||||
ETHTOOL_LINK_MODE_1000baseKX_Full_BIT);
|
||||
ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
|
||||
MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_1000BASE_KX, SPEED_1000,
|
||||
ETHTOOL_LINK_MODE_1000baseKX_Full_BIT);
|
||||
MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_T, SPEED_10000,
|
||||
@ -682,9 +682,9 @@ void __init mlx4_en_init_ptys2ethtool_map(void)
|
||||
MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_KR, SPEED_10000,
|
||||
ETHTOOL_LINK_MODE_10000baseKR_Full_BIT);
|
||||
MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_CR, SPEED_10000,
|
||||
ETHTOOL_LINK_MODE_10000baseKR_Full_BIT);
|
||||
ETHTOOL_LINK_MODE_10000baseCR_Full_BIT);
|
||||
MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_SR, SPEED_10000,
|
||||
ETHTOOL_LINK_MODE_10000baseKR_Full_BIT);
|
||||
ETHTOOL_LINK_MODE_10000baseSR_Full_BIT);
|
||||
MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_20GBASE_KR2, SPEED_20000,
|
||||
ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT,
|
||||
ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT);
|
||||
|
@ -2286,9 +2286,14 @@ int mlx4_en_try_alloc_resources(struct mlx4_en_priv *priv,
|
||||
bool carry_xdp_prog)
|
||||
{
|
||||
struct bpf_prog *xdp_prog;
|
||||
int i, t;
|
||||
int i, t, ret;
|
||||
|
||||
mlx4_en_copy_priv(tmp, priv, prof);
|
||||
ret = mlx4_en_copy_priv(tmp, priv, prof);
|
||||
if (ret) {
|
||||
en_warn(priv, "%s: mlx4_en_copy_priv() failed, return\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mlx4_en_alloc_resources(tmp)) {
|
||||
en_warn(priv,
|
||||
|
@ -341,6 +341,7 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
|
||||
case MLX5_CMD_OP_DEALLOC_SF:
|
||||
case MLX5_CMD_OP_DESTROY_UCTX:
|
||||
case MLX5_CMD_OP_DESTROY_UMEM:
|
||||
case MLX5_CMD_OP_MODIFY_RQT:
|
||||
return MLX5_CMD_STAT_OK;
|
||||
|
||||
case MLX5_CMD_OP_QUERY_HCA_CAP:
|
||||
@ -446,7 +447,6 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
|
||||
case MLX5_CMD_OP_MODIFY_TIS:
|
||||
case MLX5_CMD_OP_QUERY_TIS:
|
||||
case MLX5_CMD_OP_CREATE_RQT:
|
||||
case MLX5_CMD_OP_MODIFY_RQT:
|
||||
case MLX5_CMD_OP_QUERY_RQT:
|
||||
|
||||
case MLX5_CMD_OP_CREATE_FLOW_TABLE:
|
||||
|
@ -13,6 +13,9 @@ struct mlx5e_rx_res {
|
||||
unsigned int max_nch;
|
||||
u32 drop_rqn;
|
||||
|
||||
struct mlx5e_packet_merge_param pkt_merge_param;
|
||||
struct rw_semaphore pkt_merge_param_sem;
|
||||
|
||||
struct mlx5e_rss *rss[MLX5E_MAX_NUM_RSS];
|
||||
bool rss_active;
|
||||
u32 rss_rqns[MLX5E_INDIR_RQT_SIZE];
|
||||
@ -392,6 +395,7 @@ static int mlx5e_rx_res_ptp_init(struct mlx5e_rx_res *res)
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Separated from the channels RQs, does not share pkt_merge state with them */
|
||||
mlx5e_tir_builder_build_rqt(builder, res->mdev->mlx5e_res.hw_objs.td.tdn,
|
||||
mlx5e_rqt_get_rqtn(&res->ptp.rqt),
|
||||
inner_ft_support);
|
||||
@ -447,6 +451,9 @@ int mlx5e_rx_res_init(struct mlx5e_rx_res *res, struct mlx5_core_dev *mdev,
|
||||
res->max_nch = max_nch;
|
||||
res->drop_rqn = drop_rqn;
|
||||
|
||||
res->pkt_merge_param = *init_pkt_merge_param;
|
||||
init_rwsem(&res->pkt_merge_param_sem);
|
||||
|
||||
err = mlx5e_rx_res_rss_init_def(res, init_pkt_merge_param, init_nch);
|
||||
if (err)
|
||||
goto err_out;
|
||||
@ -513,7 +520,7 @@ u32 mlx5e_rx_res_get_tirn_ptp(struct mlx5e_rx_res *res)
|
||||
return mlx5e_tir_get_tirn(&res->ptp.tir);
|
||||
}
|
||||
|
||||
u32 mlx5e_rx_res_get_rqtn_direct(struct mlx5e_rx_res *res, unsigned int ix)
|
||||
static u32 mlx5e_rx_res_get_rqtn_direct(struct mlx5e_rx_res *res, unsigned int ix)
|
||||
{
|
||||
return mlx5e_rqt_get_rqtn(&res->channels[ix].direct_rqt);
|
||||
}
|
||||
@ -656,6 +663,9 @@ int mlx5e_rx_res_packet_merge_set_param(struct mlx5e_rx_res *res,
|
||||
if (!builder)
|
||||
return -ENOMEM;
|
||||
|
||||
down_write(&res->pkt_merge_param_sem);
|
||||
res->pkt_merge_param = *pkt_merge_param;
|
||||
|
||||
mlx5e_tir_builder_build_packet_merge(builder, pkt_merge_param);
|
||||
|
||||
final_err = 0;
|
||||
@ -681,6 +691,7 @@ int mlx5e_rx_res_packet_merge_set_param(struct mlx5e_rx_res *res,
|
||||
}
|
||||
}
|
||||
|
||||
up_write(&res->pkt_merge_param_sem);
|
||||
mlx5e_tir_builder_free(builder);
|
||||
return final_err;
|
||||
}
|
||||
@ -689,3 +700,31 @@ struct mlx5e_rss_params_hash mlx5e_rx_res_get_current_hash(struct mlx5e_rx_res *
|
||||
{
|
||||
return mlx5e_rss_get_hash(res->rss[0]);
|
||||
}
|
||||
|
||||
int mlx5e_rx_res_tls_tir_create(struct mlx5e_rx_res *res, unsigned int rxq,
|
||||
struct mlx5e_tir *tir)
|
||||
{
|
||||
bool inner_ft_support = res->features & MLX5E_RX_RES_FEATURE_INNER_FT;
|
||||
struct mlx5e_tir_builder *builder;
|
||||
u32 rqtn;
|
||||
int err;
|
||||
|
||||
builder = mlx5e_tir_builder_alloc(false);
|
||||
if (!builder)
|
||||
return -ENOMEM;
|
||||
|
||||
rqtn = mlx5e_rx_res_get_rqtn_direct(res, rxq);
|
||||
|
||||
mlx5e_tir_builder_build_rqt(builder, res->mdev->mlx5e_res.hw_objs.td.tdn, rqtn,
|
||||
inner_ft_support);
|
||||
mlx5e_tir_builder_build_direct(builder);
|
||||
mlx5e_tir_builder_build_tls(builder);
|
||||
down_read(&res->pkt_merge_param_sem);
|
||||
mlx5e_tir_builder_build_packet_merge(builder, &res->pkt_merge_param);
|
||||
err = mlx5e_tir_init(tir, builder, res->mdev, false);
|
||||
up_read(&res->pkt_merge_param_sem);
|
||||
|
||||
mlx5e_tir_builder_free(builder);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -37,9 +37,6 @@ u32 mlx5e_rx_res_get_tirn_rss(struct mlx5e_rx_res *res, enum mlx5_traffic_types
|
||||
u32 mlx5e_rx_res_get_tirn_rss_inner(struct mlx5e_rx_res *res, enum mlx5_traffic_types tt);
|
||||
u32 mlx5e_rx_res_get_tirn_ptp(struct mlx5e_rx_res *res);
|
||||
|
||||
/* RQTN getters for modules that create their own TIRs */
|
||||
u32 mlx5e_rx_res_get_rqtn_direct(struct mlx5e_rx_res *res, unsigned int ix);
|
||||
|
||||
/* Activate/deactivate API */
|
||||
void mlx5e_rx_res_channels_activate(struct mlx5e_rx_res *res, struct mlx5e_channels *chs);
|
||||
void mlx5e_rx_res_channels_deactivate(struct mlx5e_rx_res *res);
|
||||
@ -69,4 +66,7 @@ struct mlx5e_rss *mlx5e_rx_res_rss_get(struct mlx5e_rx_res *res, u32 rss_idx);
|
||||
/* Workaround for hairpin */
|
||||
struct mlx5e_rss_params_hash mlx5e_rx_res_get_current_hash(struct mlx5e_rx_res *res);
|
||||
|
||||
/* Accel TIRs */
|
||||
int mlx5e_rx_res_tls_tir_create(struct mlx5e_rx_res *res, unsigned int rxq,
|
||||
struct mlx5e_tir *tir);
|
||||
#endif /* __MLX5_EN_RX_RES_H__ */
|
||||
|
@ -191,7 +191,7 @@ static void mlx5e_ipsec_set_swp(struct sk_buff *skb,
|
||||
eseg->swp_inner_l3_offset = skb_inner_network_offset(skb) / 2;
|
||||
eseg->swp_inner_l4_offset =
|
||||
(skb->csum_start + skb->head - skb->data) / 2;
|
||||
if (skb->protocol == htons(ETH_P_IPV6))
|
||||
if (inner_ip_hdr(skb)->version == 6)
|
||||
eseg->swp_flags |= MLX5_ETH_WQE_SWP_INNER_L3_IPV6;
|
||||
break;
|
||||
default:
|
||||
|
@ -100,25 +100,6 @@ mlx5e_ktls_rx_resync_create_resp_list(void)
|
||||
return resp_list;
|
||||
}
|
||||
|
||||
static int mlx5e_ktls_create_tir(struct mlx5_core_dev *mdev, struct mlx5e_tir *tir, u32 rqtn)
|
||||
{
|
||||
struct mlx5e_tir_builder *builder;
|
||||
int err;
|
||||
|
||||
builder = mlx5e_tir_builder_alloc(false);
|
||||
if (!builder)
|
||||
return -ENOMEM;
|
||||
|
||||
mlx5e_tir_builder_build_rqt(builder, mdev->mlx5e_res.hw_objs.td.tdn, rqtn, false);
|
||||
mlx5e_tir_builder_build_direct(builder);
|
||||
mlx5e_tir_builder_build_tls(builder);
|
||||
err = mlx5e_tir_init(tir, builder, mdev, false);
|
||||
|
||||
mlx5e_tir_builder_free(builder);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void accel_rule_handle_work(struct work_struct *work)
|
||||
{
|
||||
struct mlx5e_ktls_offload_context_rx *priv_rx;
|
||||
@ -609,7 +590,6 @@ int mlx5e_ktls_add_rx(struct net_device *netdev, struct sock *sk,
|
||||
struct mlx5_core_dev *mdev;
|
||||
struct mlx5e_priv *priv;
|
||||
int rxq, err;
|
||||
u32 rqtn;
|
||||
|
||||
tls_ctx = tls_get_ctx(sk);
|
||||
priv = netdev_priv(netdev);
|
||||
@ -635,9 +615,7 @@ int mlx5e_ktls_add_rx(struct net_device *netdev, struct sock *sk,
|
||||
priv_rx->sw_stats = &priv->tls->sw_stats;
|
||||
mlx5e_set_ktls_rx_priv_ctx(tls_ctx, priv_rx);
|
||||
|
||||
rqtn = mlx5e_rx_res_get_rqtn_direct(priv->rx_res, rxq);
|
||||
|
||||
err = mlx5e_ktls_create_tir(mdev, &priv_rx->tir, rqtn);
|
||||
err = mlx5e_rx_res_tls_tir_create(priv->rx_res, rxq, &priv_rx->tir);
|
||||
if (err)
|
||||
goto err_create_tir;
|
||||
|
||||
|
@ -1086,6 +1086,10 @@ static mlx5e_stats_grp_t mlx5e_ul_rep_stats_grps[] = {
|
||||
&MLX5E_STATS_GRP(pme),
|
||||
&MLX5E_STATS_GRP(channels),
|
||||
&MLX5E_STATS_GRP(per_port_buff_congest),
|
||||
#ifdef CONFIG_MLX5_EN_IPSEC
|
||||
&MLX5E_STATS_GRP(ipsec_sw),
|
||||
&MLX5E_STATS_GRP(ipsec_hw),
|
||||
#endif
|
||||
};
|
||||
|
||||
static unsigned int mlx5e_ul_rep_stats_grps_num(struct mlx5e_priv *priv)
|
||||
|
@ -544,13 +544,13 @@ static int mlx5e_build_shampo_hd_umr(struct mlx5e_rq *rq,
|
||||
u16 klm_entries, u16 index)
|
||||
{
|
||||
struct mlx5e_shampo_hd *shampo = rq->mpwqe.shampo;
|
||||
u16 entries, pi, i, header_offset, err, wqe_bbs, new_entries;
|
||||
u16 entries, pi, header_offset, err, wqe_bbs, new_entries;
|
||||
u32 lkey = rq->mdev->mlx5e_res.hw_objs.mkey;
|
||||
struct page *page = shampo->last_page;
|
||||
u64 addr = shampo->last_addr;
|
||||
struct mlx5e_dma_info *dma_info;
|
||||
struct mlx5e_umr_wqe *umr_wqe;
|
||||
int headroom;
|
||||
int headroom, i;
|
||||
|
||||
headroom = rq->buff.headroom;
|
||||
new_entries = klm_entries - (shampo->pi & (MLX5_UMR_KLM_ALIGNMENT - 1));
|
||||
@ -602,9 +602,7 @@ static int mlx5e_build_shampo_hd_umr(struct mlx5e_rq *rq,
|
||||
|
||||
err_unmap:
|
||||
while (--i >= 0) {
|
||||
if (--index < 0)
|
||||
index = shampo->hd_per_wq - 1;
|
||||
dma_info = &shampo->info[index];
|
||||
dma_info = &shampo->info[--index];
|
||||
if (!(i & (MLX5E_SHAMPO_WQ_HEADER_PER_PAGE - 1))) {
|
||||
dma_info->addr = ALIGN_DOWN(dma_info->addr, PAGE_SIZE);
|
||||
mlx5e_page_release(rq, dma_info, true);
|
||||
|
@ -130,7 +130,7 @@ static u32 esw_qos_calculate_min_rate_divider(struct mlx5_eswitch *esw,
|
||||
/* If vports min rate divider is 0 but their group has bw_share configured, then
|
||||
* need to set bw_share for vports to minimal value.
|
||||
*/
|
||||
if (!group_level && !max_guarantee && group->bw_share)
|
||||
if (!group_level && !max_guarantee && group && group->bw_share)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@ -419,7 +419,7 @@ static int esw_qos_vport_update_group(struct mlx5_eswitch *esw,
|
||||
return err;
|
||||
|
||||
/* Recalculate bw share weights of old and new groups */
|
||||
if (vport->qos.bw_share) {
|
||||
if (vport->qos.bw_share || new_group->bw_share) {
|
||||
esw_qos_normalize_vports_min_rate(esw, curr_group, extack);
|
||||
esw_qos_normalize_vports_min_rate(esw, new_group, extack);
|
||||
}
|
||||
|
@ -329,14 +329,25 @@ static bool
|
||||
esw_is_indir_table(struct mlx5_eswitch *esw, struct mlx5_flow_attr *attr)
|
||||
{
|
||||
struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
|
||||
bool result = false;
|
||||
int i;
|
||||
|
||||
for (i = esw_attr->split_count; i < esw_attr->out_count; i++)
|
||||
/* Indirect table is supported only for flows with in_port uplink
|
||||
* and the destination is vport on the same eswitch as the uplink,
|
||||
* return false in case at least one of destinations doesn't meet
|
||||
* this criteria.
|
||||
*/
|
||||
for (i = esw_attr->split_count; i < esw_attr->out_count; i++) {
|
||||
if (esw_attr->dests[i].rep &&
|
||||
mlx5_esw_indir_table_needed(esw, attr, esw_attr->dests[i].rep->vport,
|
||||
esw_attr->dests[i].mdev))
|
||||
return true;
|
||||
return false;
|
||||
esw_attr->dests[i].mdev)) {
|
||||
result = true;
|
||||
} else {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -2512,6 +2523,7 @@ static int esw_set_master_egress_rule(struct mlx5_core_dev *master,
|
||||
struct mlx5_eswitch *esw = master->priv.eswitch;
|
||||
struct mlx5_flow_table_attr ft_attr = {
|
||||
.max_fte = 1, .prio = 0, .level = 0,
|
||||
.flags = MLX5_FLOW_TABLE_OTHER_VPORT,
|
||||
};
|
||||
struct mlx5_flow_namespace *egress_ns;
|
||||
struct mlx5_flow_table *acl;
|
||||
|
@ -840,6 +840,9 @@ void mlx5_start_health_poll(struct mlx5_core_dev *dev)
|
||||
|
||||
health->timer.expires = jiffies + msecs_to_jiffies(poll_interval_ms);
|
||||
add_timer(&health->timer);
|
||||
|
||||
if (mlx5_core_is_pf(dev) && MLX5_CAP_MCAM_REG(dev, mrtc))
|
||||
queue_delayed_work(health->wq, &health->update_fw_log_ts_work, 0);
|
||||
}
|
||||
|
||||
void mlx5_stop_health_poll(struct mlx5_core_dev *dev, bool disable_health)
|
||||
@ -907,8 +910,6 @@ int mlx5_health_init(struct mlx5_core_dev *dev)
|
||||
INIT_WORK(&health->fatal_report_work, mlx5_fw_fatal_reporter_err_work);
|
||||
INIT_WORK(&health->report_work, mlx5_fw_reporter_err_work);
|
||||
INIT_DELAYED_WORK(&health->update_fw_log_ts_work, mlx5_health_log_ts_update);
|
||||
if (mlx5_core_is_pf(dev))
|
||||
queue_delayed_work(health->wq, &health->update_fw_log_ts_work, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -608,4 +608,5 @@ void mlx5_lag_port_sel_destroy(struct mlx5_lag *ldev)
|
||||
if (port_sel->tunnel)
|
||||
mlx5_destroy_ttc_table(port_sel->inner.ttc);
|
||||
mlx5_lag_destroy_definers(ldev);
|
||||
memset(port_sel, 0, sizeof(*port_sel));
|
||||
}
|
||||
|
@ -31,11 +31,11 @@ static void tout_set(struct mlx5_core_dev *dev, u64 val, enum mlx5_timeouts_type
|
||||
dev->timeouts->to[type] = val;
|
||||
}
|
||||
|
||||
static void tout_set_def_val(struct mlx5_core_dev *dev)
|
||||
void mlx5_tout_set_def_val(struct mlx5_core_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = MLX5_TO_FW_PRE_INIT_TIMEOUT_MS; i < MAX_TIMEOUT_TYPES; i++)
|
||||
for (i = 0; i < MAX_TIMEOUT_TYPES; i++)
|
||||
tout_set(dev, tout_def_sw_val[i], i);
|
||||
}
|
||||
|
||||
@ -45,7 +45,6 @@ int mlx5_tout_init(struct mlx5_core_dev *dev)
|
||||
if (!dev->timeouts)
|
||||
return -ENOMEM;
|
||||
|
||||
tout_set_def_val(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ int mlx5_tout_init(struct mlx5_core_dev *dev);
|
||||
void mlx5_tout_cleanup(struct mlx5_core_dev *dev);
|
||||
void mlx5_tout_query_iseg(struct mlx5_core_dev *dev);
|
||||
int mlx5_tout_query_dtor(struct mlx5_core_dev *dev);
|
||||
void mlx5_tout_set_def_val(struct mlx5_core_dev *dev);
|
||||
u64 _mlx5_tout_ms(struct mlx5_core_dev *dev, enum mlx5_timeouts_types type);
|
||||
|
||||
#define mlx5_tout_ms(dev, type) _mlx5_tout_ms(dev, MLX5_TO_##type##_MS)
|
||||
|
@ -992,11 +992,7 @@ static int mlx5_function_setup(struct mlx5_core_dev *dev, bool boot)
|
||||
if (mlx5_core_is_pf(dev))
|
||||
pcie_print_link_status(dev->pdev);
|
||||
|
||||
err = mlx5_tout_init(dev);
|
||||
if (err) {
|
||||
mlx5_core_err(dev, "Failed initializing timeouts, aborting\n");
|
||||
return err;
|
||||
}
|
||||
mlx5_tout_set_def_val(dev);
|
||||
|
||||
/* wait for firmware to accept initialization segments configurations
|
||||
*/
|
||||
@ -1005,13 +1001,13 @@ static int mlx5_function_setup(struct mlx5_core_dev *dev, bool boot)
|
||||
if (err) {
|
||||
mlx5_core_err(dev, "Firmware over %llu MS in pre-initializing state, aborting\n",
|
||||
mlx5_tout_ms(dev, FW_PRE_INIT_TIMEOUT));
|
||||
goto err_tout_cleanup;
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mlx5_cmd_init(dev);
|
||||
if (err) {
|
||||
mlx5_core_err(dev, "Failed initializing command interface, aborting\n");
|
||||
goto err_tout_cleanup;
|
||||
return err;
|
||||
}
|
||||
|
||||
mlx5_tout_query_iseg(dev);
|
||||
@ -1075,18 +1071,16 @@ static int mlx5_function_setup(struct mlx5_core_dev *dev, bool boot)
|
||||
|
||||
mlx5_set_driver_version(dev);
|
||||
|
||||
mlx5_start_health_poll(dev);
|
||||
|
||||
err = mlx5_query_hca_caps(dev);
|
||||
if (err) {
|
||||
mlx5_core_err(dev, "query hca failed\n");
|
||||
goto stop_health;
|
||||
goto reclaim_boot_pages;
|
||||
}
|
||||
|
||||
mlx5_start_health_poll(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
stop_health:
|
||||
mlx5_stop_health_poll(dev, boot);
|
||||
reclaim_boot_pages:
|
||||
mlx5_reclaim_startup_pages(dev);
|
||||
err_disable_hca:
|
||||
@ -1094,8 +1088,6 @@ static int mlx5_function_setup(struct mlx5_core_dev *dev, bool boot)
|
||||
err_cmd_cleanup:
|
||||
mlx5_cmd_set_state(dev, MLX5_CMDIF_STATE_DOWN);
|
||||
mlx5_cmd_cleanup(dev);
|
||||
err_tout_cleanup:
|
||||
mlx5_tout_cleanup(dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -1114,7 +1106,6 @@ static int mlx5_function_teardown(struct mlx5_core_dev *dev, bool boot)
|
||||
mlx5_core_disable_hca(dev, 0);
|
||||
mlx5_cmd_set_state(dev, MLX5_CMDIF_STATE_DOWN);
|
||||
mlx5_cmd_cleanup(dev);
|
||||
mlx5_tout_cleanup(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1476,6 +1467,12 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
|
||||
mlx5_debugfs_root);
|
||||
INIT_LIST_HEAD(&priv->traps);
|
||||
|
||||
err = mlx5_tout_init(dev);
|
||||
if (err) {
|
||||
mlx5_core_err(dev, "Failed initializing timeouts, aborting\n");
|
||||
goto err_timeout_init;
|
||||
}
|
||||
|
||||
err = mlx5_health_init(dev);
|
||||
if (err)
|
||||
goto err_health_init;
|
||||
@ -1501,6 +1498,8 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
|
||||
err_pagealloc_init:
|
||||
mlx5_health_cleanup(dev);
|
||||
err_health_init:
|
||||
mlx5_tout_cleanup(dev);
|
||||
err_timeout_init:
|
||||
debugfs_remove(dev->priv.dbg_root);
|
||||
mutex_destroy(&priv->pgdir_mutex);
|
||||
mutex_destroy(&priv->alloc_mutex);
|
||||
@ -1518,6 +1517,7 @@ void mlx5_mdev_uninit(struct mlx5_core_dev *dev)
|
||||
mlx5_adev_cleanup(dev);
|
||||
mlx5_pagealloc_cleanup(dev);
|
||||
mlx5_health_cleanup(dev);
|
||||
mlx5_tout_cleanup(dev);
|
||||
debugfs_remove_recursive(dev->priv.dbg_root);
|
||||
mutex_destroy(&priv->pgdir_mutex);
|
||||
mutex_destroy(&priv->alloc_mutex);
|
||||
|
@ -120,7 +120,7 @@ static const struct net_device_ops xtsonic_netdev_ops = {
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
};
|
||||
|
||||
static int __init sonic_probe1(struct net_device *dev)
|
||||
static int sonic_probe1(struct net_device *dev)
|
||||
{
|
||||
unsigned int silicon_revision;
|
||||
struct sonic_local *lp = netdev_priv(dev);
|
||||
|
@ -1077,8 +1077,14 @@ static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter)
|
||||
sds_mbx_size = sizeof(struct qlcnic_sds_mbx);
|
||||
context_id = recv_ctx->context_id;
|
||||
num_sds = adapter->drv_sds_rings - QLCNIC_MAX_SDS_RINGS;
|
||||
ahw->hw_ops->alloc_mbx_args(&cmd, adapter,
|
||||
err = ahw->hw_ops->alloc_mbx_args(&cmd, adapter,
|
||||
QLCNIC_CMD_ADD_RCV_RINGS);
|
||||
if (err) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"Failed to alloc mbx args %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
cmd.req.arg[1] = 0 | (num_sds << 8) | (context_id << 16);
|
||||
|
||||
/* set up status rings, mbx 2-81 */
|
||||
|
@ -5532,8 +5532,6 @@ static int stmmac_set_features(struct net_device *netdev,
|
||||
netdev_features_t features)
|
||||
{
|
||||
struct stmmac_priv *priv = netdev_priv(netdev);
|
||||
bool sph_en;
|
||||
u32 chan;
|
||||
|
||||
/* Keep the COE Type in case of csum is supporting */
|
||||
if (features & NETIF_F_RXCSUM)
|
||||
@ -5545,10 +5543,13 @@ static int stmmac_set_features(struct net_device *netdev,
|
||||
*/
|
||||
stmmac_rx_ipc(priv, priv->hw);
|
||||
|
||||
sph_en = (priv->hw->rx_csum > 0) && priv->sph;
|
||||
if (priv->sph_cap) {
|
||||
bool sph_en = (priv->hw->rx_csum > 0) && priv->sph;
|
||||
u32 chan;
|
||||
|
||||
for (chan = 0; chan < priv->plat->rx_queues_to_use; chan++)
|
||||
stmmac_enable_sph(priv, priv->ioaddr, sph_en, chan);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2398,7 +2398,7 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
|
||||
if (dev->domain_data.phyirq > 0)
|
||||
phydev->irq = dev->domain_data.phyirq;
|
||||
else
|
||||
phydev->irq = 0;
|
||||
phydev->irq = PHY_POLL;
|
||||
netdev_dbg(dev->net, "phydev->irq = %d\n", phydev->irq);
|
||||
|
||||
/* set to AUTOMDIX */
|
||||
|
@ -3425,7 +3425,6 @@ static struct virtio_driver virtio_net_driver = {
|
||||
.feature_table_size = ARRAY_SIZE(features),
|
||||
.feature_table_legacy = features_legacy,
|
||||
.feature_table_size_legacy = ARRAY_SIZE(features_legacy),
|
||||
.suppress_used_validation = true,
|
||||
.driver.name = KBUILD_MODNAME,
|
||||
.driver.owner = THIS_MODULE,
|
||||
.id_table = id_table,
|
||||
|
@ -497,6 +497,7 @@ static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb,
|
||||
/* strip the ethernet header added for pass through VRF device */
|
||||
__skb_pull(skb, skb_network_offset(skb));
|
||||
|
||||
memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
|
||||
ret = vrf_ip6_local_out(net, skb->sk, skb);
|
||||
if (unlikely(net_xmit_eval(ret)))
|
||||
dev->stats.tx_errors++;
|
||||
@ -579,6 +580,7 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb,
|
||||
RT_SCOPE_LINK);
|
||||
}
|
||||
|
||||
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
|
||||
ret = vrf_ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb);
|
||||
if (unlikely(net_xmit_eval(ret)))
|
||||
vrf_dev->stats.tx_errors++;
|
||||
|
@ -163,7 +163,7 @@ static bool node_placement(struct allowedips_node __rcu *trie, const u8 *key,
|
||||
return exact;
|
||||
}
|
||||
|
||||
static inline void connect_node(struct allowedips_node **parent, u8 bit, struct allowedips_node *node)
|
||||
static inline void connect_node(struct allowedips_node __rcu **parent, u8 bit, struct allowedips_node *node)
|
||||
{
|
||||
node->parent_bit_packed = (unsigned long)parent | bit;
|
||||
rcu_assign_pointer(*parent, node);
|
||||
|
@ -98,6 +98,7 @@ static int wg_stop(struct net_device *dev)
|
||||
{
|
||||
struct wg_device *wg = netdev_priv(dev);
|
||||
struct wg_peer *peer;
|
||||
struct sk_buff *skb;
|
||||
|
||||
mutex_lock(&wg->device_update_lock);
|
||||
list_for_each_entry(peer, &wg->peer_list, peer_list) {
|
||||
@ -108,7 +109,9 @@ static int wg_stop(struct net_device *dev)
|
||||
wg_noise_reset_last_sent_handshake(&peer->last_sent_handshake);
|
||||
}
|
||||
mutex_unlock(&wg->device_update_lock);
|
||||
skb_queue_purge(&wg->incoming_handshakes);
|
||||
while ((skb = ptr_ring_consume(&wg->handshake_queue.ring)) != NULL)
|
||||
kfree_skb(skb);
|
||||
atomic_set(&wg->handshake_queue_len, 0);
|
||||
wg_socket_reinit(wg, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
@ -235,14 +238,13 @@ static void wg_destruct(struct net_device *dev)
|
||||
destroy_workqueue(wg->handshake_receive_wq);
|
||||
destroy_workqueue(wg->handshake_send_wq);
|
||||
destroy_workqueue(wg->packet_crypt_wq);
|
||||
wg_packet_queue_free(&wg->decrypt_queue);
|
||||
wg_packet_queue_free(&wg->encrypt_queue);
|
||||
wg_packet_queue_free(&wg->handshake_queue, true);
|
||||
wg_packet_queue_free(&wg->decrypt_queue, false);
|
||||
wg_packet_queue_free(&wg->encrypt_queue, false);
|
||||
rcu_barrier(); /* Wait for all the peers to be actually freed. */
|
||||
wg_ratelimiter_uninit();
|
||||
memzero_explicit(&wg->static_identity, sizeof(wg->static_identity));
|
||||
skb_queue_purge(&wg->incoming_handshakes);
|
||||
free_percpu(dev->tstats);
|
||||
free_percpu(wg->incoming_handshakes_worker);
|
||||
kvfree(wg->index_hashtable);
|
||||
kvfree(wg->peer_hashtable);
|
||||
mutex_unlock(&wg->device_update_lock);
|
||||
@ -298,7 +300,6 @@ static int wg_newlink(struct net *src_net, struct net_device *dev,
|
||||
init_rwsem(&wg->static_identity.lock);
|
||||
mutex_init(&wg->socket_update_lock);
|
||||
mutex_init(&wg->device_update_lock);
|
||||
skb_queue_head_init(&wg->incoming_handshakes);
|
||||
wg_allowedips_init(&wg->peer_allowedips);
|
||||
wg_cookie_checker_init(&wg->cookie_checker, wg);
|
||||
INIT_LIST_HEAD(&wg->peer_list);
|
||||
@ -316,16 +317,10 @@ static int wg_newlink(struct net *src_net, struct net_device *dev,
|
||||
if (!dev->tstats)
|
||||
goto err_free_index_hashtable;
|
||||
|
||||
wg->incoming_handshakes_worker =
|
||||
wg_packet_percpu_multicore_worker_alloc(
|
||||
wg_packet_handshake_receive_worker, wg);
|
||||
if (!wg->incoming_handshakes_worker)
|
||||
goto err_free_tstats;
|
||||
|
||||
wg->handshake_receive_wq = alloc_workqueue("wg-kex-%s",
|
||||
WQ_CPU_INTENSIVE | WQ_FREEZABLE, 0, dev->name);
|
||||
if (!wg->handshake_receive_wq)
|
||||
goto err_free_incoming_handshakes;
|
||||
goto err_free_tstats;
|
||||
|
||||
wg->handshake_send_wq = alloc_workqueue("wg-kex-%s",
|
||||
WQ_UNBOUND | WQ_FREEZABLE, 0, dev->name);
|
||||
@ -347,10 +342,15 @@ static int wg_newlink(struct net *src_net, struct net_device *dev,
|
||||
if (ret < 0)
|
||||
goto err_free_encrypt_queue;
|
||||
|
||||
ret = wg_ratelimiter_init();
|
||||
ret = wg_packet_queue_init(&wg->handshake_queue, wg_packet_handshake_receive_worker,
|
||||
MAX_QUEUED_INCOMING_HANDSHAKES);
|
||||
if (ret < 0)
|
||||
goto err_free_decrypt_queue;
|
||||
|
||||
ret = wg_ratelimiter_init();
|
||||
if (ret < 0)
|
||||
goto err_free_handshake_queue;
|
||||
|
||||
ret = register_netdevice(dev);
|
||||
if (ret < 0)
|
||||
goto err_uninit_ratelimiter;
|
||||
@ -367,18 +367,18 @@ static int wg_newlink(struct net *src_net, struct net_device *dev,
|
||||
|
||||
err_uninit_ratelimiter:
|
||||
wg_ratelimiter_uninit();
|
||||
err_free_handshake_queue:
|
||||
wg_packet_queue_free(&wg->handshake_queue, false);
|
||||
err_free_decrypt_queue:
|
||||
wg_packet_queue_free(&wg->decrypt_queue);
|
||||
wg_packet_queue_free(&wg->decrypt_queue, false);
|
||||
err_free_encrypt_queue:
|
||||
wg_packet_queue_free(&wg->encrypt_queue);
|
||||
wg_packet_queue_free(&wg->encrypt_queue, false);
|
||||
err_destroy_packet_crypt:
|
||||
destroy_workqueue(wg->packet_crypt_wq);
|
||||
err_destroy_handshake_send:
|
||||
destroy_workqueue(wg->handshake_send_wq);
|
||||
err_destroy_handshake_receive:
|
||||
destroy_workqueue(wg->handshake_receive_wq);
|
||||
err_free_incoming_handshakes:
|
||||
free_percpu(wg->incoming_handshakes_worker);
|
||||
err_free_tstats:
|
||||
free_percpu(dev->tstats);
|
||||
err_free_index_hashtable:
|
||||
@ -398,6 +398,7 @@ static struct rtnl_link_ops link_ops __read_mostly = {
|
||||
static void wg_netns_pre_exit(struct net *net)
|
||||
{
|
||||
struct wg_device *wg;
|
||||
struct wg_peer *peer;
|
||||
|
||||
rtnl_lock();
|
||||
list_for_each_entry(wg, &device_list, device_list) {
|
||||
@ -407,6 +408,8 @@ static void wg_netns_pre_exit(struct net *net)
|
||||
mutex_lock(&wg->device_update_lock);
|
||||
rcu_assign_pointer(wg->creating_net, NULL);
|
||||
wg_socket_reinit(wg, NULL, NULL);
|
||||
list_for_each_entry(peer, &wg->peer_list, peer_list)
|
||||
wg_socket_clear_peer_endpoint_src(peer);
|
||||
mutex_unlock(&wg->device_update_lock);
|
||||
}
|
||||
}
|
||||
|
@ -39,21 +39,18 @@ struct prev_queue {
|
||||
|
||||
struct wg_device {
|
||||
struct net_device *dev;
|
||||
struct crypt_queue encrypt_queue, decrypt_queue;
|
||||
struct crypt_queue encrypt_queue, decrypt_queue, handshake_queue;
|
||||
struct sock __rcu *sock4, *sock6;
|
||||
struct net __rcu *creating_net;
|
||||
struct noise_static_identity static_identity;
|
||||
struct workqueue_struct *handshake_receive_wq, *handshake_send_wq;
|
||||
struct workqueue_struct *packet_crypt_wq;
|
||||
struct sk_buff_head incoming_handshakes;
|
||||
int incoming_handshake_cpu;
|
||||
struct multicore_worker __percpu *incoming_handshakes_worker;
|
||||
struct workqueue_struct *packet_crypt_wq,*handshake_receive_wq, *handshake_send_wq;
|
||||
struct cookie_checker cookie_checker;
|
||||
struct pubkey_hashtable *peer_hashtable;
|
||||
struct index_hashtable *index_hashtable;
|
||||
struct allowedips peer_allowedips;
|
||||
struct mutex device_update_lock, socket_update_lock;
|
||||
struct list_head device_list, peer_list;
|
||||
atomic_t handshake_queue_len;
|
||||
unsigned int num_peers, device_update_gen;
|
||||
u32 fwmark;
|
||||
u16 incoming_port;
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <linux/genetlink.h>
|
||||
#include <net/rtnetlink.h>
|
||||
|
||||
static int __init mod_init(void)
|
||||
static int __init wg_mod_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -60,7 +60,7 @@ static int __init mod_init(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit mod_exit(void)
|
||||
static void __exit wg_mod_exit(void)
|
||||
{
|
||||
wg_genetlink_uninit();
|
||||
wg_device_uninit();
|
||||
@ -68,8 +68,8 @@ static void __exit mod_exit(void)
|
||||
wg_allowedips_slab_uninit();
|
||||
}
|
||||
|
||||
module_init(mod_init);
|
||||
module_exit(mod_exit);
|
||||
module_init(wg_mod_init);
|
||||
module_exit(wg_mod_exit);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("WireGuard secure network tunnel");
|
||||
MODULE_AUTHOR("Jason A. Donenfeld <Jason@zx2c4.com>");
|
||||
|
@ -38,11 +38,11 @@ int wg_packet_queue_init(struct crypt_queue *queue, work_func_t function,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wg_packet_queue_free(struct crypt_queue *queue)
|
||||
void wg_packet_queue_free(struct crypt_queue *queue, bool purge)
|
||||
{
|
||||
free_percpu(queue->worker);
|
||||
WARN_ON(!__ptr_ring_empty(&queue->ring));
|
||||
ptr_ring_cleanup(&queue->ring, NULL);
|
||||
WARN_ON(!purge && !__ptr_ring_empty(&queue->ring));
|
||||
ptr_ring_cleanup(&queue->ring, purge ? (void(*)(void*))kfree_skb : NULL);
|
||||
}
|
||||
|
||||
#define NEXT(skb) ((skb)->prev)
|
||||
|
@ -23,7 +23,7 @@ struct sk_buff;
|
||||
/* queueing.c APIs: */
|
||||
int wg_packet_queue_init(struct crypt_queue *queue, work_func_t function,
|
||||
unsigned int len);
|
||||
void wg_packet_queue_free(struct crypt_queue *queue);
|
||||
void wg_packet_queue_free(struct crypt_queue *queue, bool purge);
|
||||
struct multicore_worker __percpu *
|
||||
wg_packet_percpu_multicore_worker_alloc(work_func_t function, void *ptr);
|
||||
|
||||
|
@ -176,12 +176,12 @@ int wg_ratelimiter_init(void)
|
||||
(1U << 14) / sizeof(struct hlist_head)));
|
||||
max_entries = table_size * 8;
|
||||
|
||||
table_v4 = kvzalloc(table_size * sizeof(*table_v4), GFP_KERNEL);
|
||||
table_v4 = kvcalloc(table_size, sizeof(*table_v4), GFP_KERNEL);
|
||||
if (unlikely(!table_v4))
|
||||
goto err_kmemcache;
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
table_v6 = kvzalloc(table_size * sizeof(*table_v6), GFP_KERNEL);
|
||||
table_v6 = kvcalloc(table_size, sizeof(*table_v6), GFP_KERNEL);
|
||||
if (unlikely(!table_v6)) {
|
||||
kvfree(table_v4);
|
||||
goto err_kmemcache;
|
||||
|
@ -116,7 +116,7 @@ static void wg_receive_handshake_packet(struct wg_device *wg,
|
||||
return;
|
||||
}
|
||||
|
||||
under_load = skb_queue_len(&wg->incoming_handshakes) >=
|
||||
under_load = atomic_read(&wg->handshake_queue_len) >=
|
||||
MAX_QUEUED_INCOMING_HANDSHAKES / 8;
|
||||
if (under_load) {
|
||||
last_under_load = ktime_get_coarse_boottime_ns();
|
||||
@ -212,13 +212,14 @@ static void wg_receive_handshake_packet(struct wg_device *wg,
|
||||
|
||||
void wg_packet_handshake_receive_worker(struct work_struct *work)
|
||||
{
|
||||
struct wg_device *wg = container_of(work, struct multicore_worker,
|
||||
work)->ptr;
|
||||
struct crypt_queue *queue = container_of(work, struct multicore_worker, work)->ptr;
|
||||
struct wg_device *wg = container_of(queue, struct wg_device, handshake_queue);
|
||||
struct sk_buff *skb;
|
||||
|
||||
while ((skb = skb_dequeue(&wg->incoming_handshakes)) != NULL) {
|
||||
while ((skb = ptr_ring_consume_bh(&queue->ring)) != NULL) {
|
||||
wg_receive_handshake_packet(wg, skb);
|
||||
dev_kfree_skb(skb);
|
||||
atomic_dec(&wg->handshake_queue_len);
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
@ -553,22 +554,28 @@ void wg_packet_receive(struct wg_device *wg, struct sk_buff *skb)
|
||||
case cpu_to_le32(MESSAGE_HANDSHAKE_INITIATION):
|
||||
case cpu_to_le32(MESSAGE_HANDSHAKE_RESPONSE):
|
||||
case cpu_to_le32(MESSAGE_HANDSHAKE_COOKIE): {
|
||||
int cpu;
|
||||
int cpu, ret = -EBUSY;
|
||||
|
||||
if (skb_queue_len(&wg->incoming_handshakes) >
|
||||
MAX_QUEUED_INCOMING_HANDSHAKES ||
|
||||
unlikely(!rng_is_initialized())) {
|
||||
if (unlikely(!rng_is_initialized()))
|
||||
goto drop;
|
||||
if (atomic_read(&wg->handshake_queue_len) > MAX_QUEUED_INCOMING_HANDSHAKES / 2) {
|
||||
if (spin_trylock_bh(&wg->handshake_queue.ring.producer_lock)) {
|
||||
ret = __ptr_ring_produce(&wg->handshake_queue.ring, skb);
|
||||
spin_unlock_bh(&wg->handshake_queue.ring.producer_lock);
|
||||
}
|
||||
} else
|
||||
ret = ptr_ring_produce_bh(&wg->handshake_queue.ring, skb);
|
||||
if (ret) {
|
||||
drop:
|
||||
net_dbg_skb_ratelimited("%s: Dropping handshake packet from %pISpfsc\n",
|
||||
wg->dev->name, skb);
|
||||
goto err;
|
||||
}
|
||||
skb_queue_tail(&wg->incoming_handshakes, skb);
|
||||
/* Queues up a call to packet_process_queued_handshake_
|
||||
* packets(skb):
|
||||
*/
|
||||
cpu = wg_cpumask_next_online(&wg->incoming_handshake_cpu);
|
||||
atomic_inc(&wg->handshake_queue_len);
|
||||
cpu = wg_cpumask_next_online(&wg->handshake_queue.last_cpu);
|
||||
/* Queues up a call to packet_process_queued_handshake_packets(skb): */
|
||||
queue_work_on(cpu, wg->handshake_receive_wq,
|
||||
&per_cpu_ptr(wg->incoming_handshakes_worker, cpu)->work);
|
||||
&per_cpu_ptr(wg->handshake_queue.worker, cpu)->work);
|
||||
break;
|
||||
}
|
||||
case cpu_to_le32(MESSAGE_DATA):
|
||||
|
@ -308,7 +308,7 @@ void wg_socket_clear_peer_endpoint_src(struct wg_peer *peer)
|
||||
{
|
||||
write_lock_bh(&peer->endpoint_lock);
|
||||
memset(&peer->endpoint.src6, 0, sizeof(peer->endpoint.src6));
|
||||
dst_cache_reset(&peer->endpoint_cache);
|
||||
dst_cache_reset_now(&peer->endpoint_cache);
|
||||
write_unlock_bh(&peer->endpoint_lock);
|
||||
}
|
||||
|
||||
|
@ -86,6 +86,7 @@ static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans,
|
||||
if (len < tlv_len) {
|
||||
IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
|
||||
len, tlv_len);
|
||||
kfree(reduce_power_data);
|
||||
reduce_power_data = ERR_PTR(-EINVAL);
|
||||
goto out;
|
||||
}
|
||||
@ -105,6 +106,7 @@ static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans,
|
||||
IWL_DEBUG_FW(trans,
|
||||
"Couldn't allocate (more) reduce_power_data\n");
|
||||
|
||||
kfree(reduce_power_data);
|
||||
reduce_power_data = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
@ -134,6 +136,10 @@ static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans,
|
||||
done:
|
||||
if (!size) {
|
||||
IWL_DEBUG_FW(trans, "Empty REDUCE_POWER, skipping.\n");
|
||||
/* Better safe than sorry, but 'reduce_power_data' should
|
||||
* always be NULL if !size.
|
||||
*/
|
||||
kfree(reduce_power_data);
|
||||
reduce_power_data = ERR_PTR(-ENOENT);
|
||||
goto out;
|
||||
}
|
||||
|
@ -1313,6 +1313,9 @@ _iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op)
|
||||
const struct iwl_op_mode_ops *ops = op->ops;
|
||||
struct dentry *dbgfs_dir = NULL;
|
||||
struct iwl_op_mode *op_mode = NULL;
|
||||
int retry, max_retry = !!iwlwifi_mod_params.fw_restart * IWL_MAX_INIT_RETRY;
|
||||
|
||||
for (retry = 0; retry <= max_retry; retry++) {
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
drv->dbgfs_op_mode = debugfs_create_dir(op->name,
|
||||
@ -1320,16 +1323,21 @@ _iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op)
|
||||
dbgfs_dir = drv->dbgfs_op_mode;
|
||||
#endif
|
||||
|
||||
op_mode = ops->start(drv->trans, drv->trans->cfg, &drv->fw, dbgfs_dir);
|
||||
op_mode = ops->start(drv->trans, drv->trans->cfg,
|
||||
&drv->fw, dbgfs_dir);
|
||||
|
||||
if (op_mode)
|
||||
return op_mode;
|
||||
|
||||
IWL_ERR(drv, "retry init count %d\n", retry);
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
if (!op_mode) {
|
||||
debugfs_remove_recursive(drv->dbgfs_op_mode);
|
||||
drv->dbgfs_op_mode = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return op_mode;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _iwl_op_mode_stop(struct iwl_drv *drv)
|
||||
|
@ -89,4 +89,7 @@ void iwl_drv_stop(struct iwl_drv *drv);
|
||||
#define IWL_EXPORT_SYMBOL(sym)
|
||||
#endif
|
||||
|
||||
/* max retry for init flow */
|
||||
#define IWL_MAX_INIT_RETRY 2
|
||||
|
||||
#endif /* __iwl_drv_h__ */
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <net/ieee80211_radiotap.h>
|
||||
#include <net/tcp.h>
|
||||
|
||||
#include "iwl-drv.h"
|
||||
#include "iwl-op-mode.h"
|
||||
#include "iwl-io.h"
|
||||
#include "mvm.h"
|
||||
@ -1117,9 +1118,30 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
int ret;
|
||||
int retry, max_retry = 0;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
||||
/* we are starting the mac not in error flow, and restart is enabled */
|
||||
if (!test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status) &&
|
||||
iwlwifi_mod_params.fw_restart) {
|
||||
max_retry = IWL_MAX_INIT_RETRY;
|
||||
/*
|
||||
* This will prevent mac80211 recovery flows to trigger during
|
||||
* init failures
|
||||
*/
|
||||
set_bit(IWL_MVM_STATUS_STARTING, &mvm->status);
|
||||
}
|
||||
|
||||
for (retry = 0; retry <= max_retry; retry++) {
|
||||
ret = __iwl_mvm_mac_start(mvm);
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
IWL_ERR(mvm, "mac start retry %d\n", retry);
|
||||
}
|
||||
clear_bit(IWL_MVM_STATUS_STARTING, &mvm->status);
|
||||
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
return ret;
|
||||
|
@ -1123,6 +1123,8 @@ struct iwl_mvm {
|
||||
* @IWL_MVM_STATUS_FIRMWARE_RUNNING: firmware is running
|
||||
* @IWL_MVM_STATUS_NEED_FLUSH_P2P: need to flush P2P bcast STA
|
||||
* @IWL_MVM_STATUS_IN_D3: in D3 (or at least about to go into it)
|
||||
* @IWL_MVM_STATUS_STARTING: starting mac,
|
||||
* used to disable restart flow while in STARTING state
|
||||
*/
|
||||
enum iwl_mvm_status {
|
||||
IWL_MVM_STATUS_HW_RFKILL,
|
||||
@ -1134,6 +1136,7 @@ enum iwl_mvm_status {
|
||||
IWL_MVM_STATUS_FIRMWARE_RUNNING,
|
||||
IWL_MVM_STATUS_NEED_FLUSH_P2P,
|
||||
IWL_MVM_STATUS_IN_D3,
|
||||
IWL_MVM_STATUS_STARTING,
|
||||
};
|
||||
|
||||
/* Keep track of completed init configuration */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user