mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 03:27:30 -04:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Cross-merge networking fixes after downstream PR (net-7.1-rc2). No conflicts, or adjacent changes. Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -220,7 +220,7 @@ cgroup v2 currently supports the following mount options.
|
||||
memory_hugetlb_accounting
|
||||
Count HugeTLB memory usage towards the cgroup's overall
|
||||
memory usage for the memory controller (for the purpose of
|
||||
statistics reporting and memory protetion). This is a new
|
||||
statistics reporting and memory protection). This is a new
|
||||
behavior that could regress existing setups, so it must be
|
||||
explicitly opted in with this mount option.
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,eliza-ipcc
|
||||
- qcom,glymur-ipcc
|
||||
- qcom,kaanapali-ipcc
|
||||
- qcom,milos-ipcc
|
||||
|
||||
@@ -57,7 +57,7 @@ Mount options unique to the isofs filesystem.
|
||||
Recommended documents about ISO 9660 standard are located at:
|
||||
|
||||
- http://www.y-adagio.com/
|
||||
- ftp://ftp.ecma.ch/ecma-st/Ecma-119.pdf
|
||||
- https://ecma-international.org/wp-content/uploads/ECMA-119_2nd_edition_december_1987.pdf
|
||||
|
||||
Quoting from the PDF "This 2nd Edition of Standard ECMA-119 is technically
|
||||
identical with ISO 9660.", so it is a valid and gratis substitute of the
|
||||
|
||||
@@ -188,6 +188,7 @@ operations:
|
||||
name: dev-set
|
||||
doc: Set the configuration of a PSP device.
|
||||
attribute-set: dev
|
||||
flags: [admin-perm]
|
||||
do:
|
||||
request:
|
||||
attributes:
|
||||
@@ -207,6 +208,7 @@ operations:
|
||||
name: key-rotate
|
||||
doc: Rotate the device key.
|
||||
attribute-set: dev
|
||||
flags: [admin-perm]
|
||||
do:
|
||||
request:
|
||||
attributes:
|
||||
|
||||
70
MAINTAINERS
70
MAINTAINERS
@@ -18672,19 +18672,59 @@ F: net/xfrm/
|
||||
F: tools/testing/selftests/net/ipsec.c
|
||||
|
||||
NETWORKING [IPv4/IPv6]
|
||||
M: "David S. Miller" <davem@davemloft.net>
|
||||
M: David Ahern <dsahern@kernel.org>
|
||||
M: Ido Schimmel <idosch@nvidia.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git
|
||||
F: arch/x86/net/*
|
||||
F: include/linux/ip.h
|
||||
F: include/linux/ipv6*
|
||||
F: Documentation/netlink/specs/rt-addr.yaml
|
||||
F: Documentation/netlink/specs/rt-neigh.yaml
|
||||
F: Documentation/netlink/specs/rt-route.yaml
|
||||
F: Documentation/netlink/specs/rt-rule.yaml
|
||||
F: include/linux/inetdevice.h
|
||||
F: include/linux/mroute*
|
||||
F: include/net/addrconf.h
|
||||
F: include/net/arp.h
|
||||
F: include/net/fib*
|
||||
F: include/net/if_inet6.h
|
||||
F: include/net/inetpeer.h
|
||||
F: include/net/ip*
|
||||
F: include/net/lwtunnel.h
|
||||
F: include/net/ndisc.h
|
||||
F: include/net/netns/nexthop.h
|
||||
F: include/net/nexthop.h
|
||||
F: include/net/route.h
|
||||
F: net/ipv4/
|
||||
F: net/ipv6/
|
||||
F: include/uapi/linux/fib_rules.h
|
||||
F: include/uapi/linux/in_route.h
|
||||
F: include/uapi/linux/mroute*
|
||||
F: include/uapi/linux/nexthop.h
|
||||
F: net/core/fib*
|
||||
F: net/core/lwtunnel.c
|
||||
F: net/ipv4/arp.c
|
||||
F: net/ipv4/devinet.c
|
||||
F: net/ipv4/fib*
|
||||
F: net/ipv4/icmp.c
|
||||
F: net/ipv4/igmp.c
|
||||
F: net/ipv4/inet_fragment.c
|
||||
F: net/ipv4/inetpeer.c
|
||||
F: net/ipv4/ip*
|
||||
F: net/ipv4/metrics.c
|
||||
F: net/ipv4/netlink.c
|
||||
F: net/ipv4/nexthop.c
|
||||
F: net/ipv4/route.c
|
||||
F: net/ipv6/addr*
|
||||
F: net/ipv6/anycast.c
|
||||
F: net/ipv6/exthdrs.c
|
||||
F: net/ipv6/exthdrs_core.c
|
||||
F: net/ipv6/fib*
|
||||
F: net/ipv6/icmp.c
|
||||
F: net/ipv6/ip*
|
||||
F: net/ipv6/mcast*
|
||||
F: net/ipv6/ndisc.c
|
||||
F: net/ipv6/output_core.c
|
||||
F: net/ipv6/reassembly.c
|
||||
F: net/ipv6/route.c
|
||||
F: tools/testing/selftests/net/fib*
|
||||
F: tools/testing/selftests/net/forwarding/
|
||||
|
||||
NETWORKING [LABELED] (NetLabel, Labeled IPsec, SECMARK)
|
||||
M: Paul Moore <paul@paul-moore.com>
|
||||
@@ -18819,18 +18859,11 @@ F: Documentation/networking/net_failover.rst
|
||||
F: drivers/net/net_failover.c
|
||||
F: include/net/net_failover.h
|
||||
|
||||
NEXTHOP
|
||||
M: David Ahern <dsahern@kernel.org>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: include/net/netns/nexthop.h
|
||||
F: include/net/nexthop.h
|
||||
F: include/uapi/linux/nexthop.h
|
||||
F: net/ipv4/nexthop.c
|
||||
|
||||
NFC SUBSYSTEM
|
||||
L: netdev@vger.kernel.org
|
||||
S: Orphan
|
||||
M: David Heidelberg <david+nfc@ixit.cz>
|
||||
L: oe-linux-nfc@lists.linux.dev
|
||||
S: Maintained
|
||||
T: git https://codeberg.org/linux-nfc/linux.git
|
||||
F: Documentation/devicetree/bindings/net/nfc/
|
||||
F: drivers/nfc/
|
||||
F: include/net/nfc/
|
||||
@@ -20774,6 +20807,7 @@ M: Dominik Brodowski <linux@dominikbrodowski.net>
|
||||
S: Odd Fixes
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/brodo/linux.git
|
||||
F: Documentation/pcmcia/
|
||||
F: drivers/net/ethernet/8390/pcnet_cs.c
|
||||
F: drivers/pcmcia/
|
||||
F: include/pcmcia/
|
||||
F: tools/pcmcia/
|
||||
|
||||
@@ -50,6 +50,9 @@
|
||||
|
||||
#include <linux/mm.h>
|
||||
|
||||
#define MARKER(m) \
|
||||
m, __after_##m = m - 1
|
||||
|
||||
enum __kvm_host_smccc_func {
|
||||
/* Hypercalls that are unavailable once pKVM has finalised. */
|
||||
/* __KVM_HOST_SMCCC_FUNC___kvm_hyp_init */
|
||||
@@ -59,8 +62,10 @@ enum __kvm_host_smccc_func {
|
||||
__KVM_HOST_SMCCC_FUNC___kvm_enable_ssbs,
|
||||
__KVM_HOST_SMCCC_FUNC___vgic_v3_init_lrs,
|
||||
__KVM_HOST_SMCCC_FUNC___vgic_v3_get_gic_config,
|
||||
|
||||
MARKER(__KVM_HOST_SMCCC_FUNC_MIN_PKVM),
|
||||
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_prot_finalize,
|
||||
__KVM_HOST_SMCCC_FUNC_MIN_PKVM = __KVM_HOST_SMCCC_FUNC___pkvm_prot_finalize,
|
||||
|
||||
/* Hypercalls that are always available and common to [nh]VHE/pKVM. */
|
||||
__KVM_HOST_SMCCC_FUNC___kvm_adjust_pc,
|
||||
@@ -72,11 +77,20 @@ enum __kvm_host_smccc_func {
|
||||
__KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_range,
|
||||
__KVM_HOST_SMCCC_FUNC___kvm_flush_cpu_context,
|
||||
__KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_load,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_unload,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_enable,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_swap_reader,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_update_clock,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_reset,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_enable_event,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_write_event,
|
||||
__KVM_HOST_SMCCC_FUNC___vgic_v3_save_aprs,
|
||||
__KVM_HOST_SMCCC_FUNC___vgic_v3_restore_vmcr_aprs,
|
||||
__KVM_HOST_SMCCC_FUNC___vgic_v5_save_apr,
|
||||
__KVM_HOST_SMCCC_FUNC___vgic_v5_restore_vmcr_apr,
|
||||
__KVM_HOST_SMCCC_FUNC_MAX_NO_PKVM = __KVM_HOST_SMCCC_FUNC___vgic_v5_restore_vmcr_apr,
|
||||
|
||||
MARKER(__KVM_HOST_SMCCC_FUNC_PKVM_ONLY),
|
||||
|
||||
/* Hypercalls that are available only when pKVM has finalised. */
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp,
|
||||
@@ -100,14 +114,8 @@ enum __kvm_host_smccc_func {
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_tlb_flush_vmid,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_load,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_unload,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_enable,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_swap_reader,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_update_clock,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_reset,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_enable_event,
|
||||
__KVM_HOST_SMCCC_FUNC___tracing_write_event,
|
||||
|
||||
MARKER(__KVM_HOST_SMCCC_FUNC_MAX)
|
||||
};
|
||||
|
||||
#define DECLARE_KVM_VHE_SYM(sym) extern char sym[]
|
||||
|
||||
@@ -450,9 +450,6 @@ struct kvm_vcpu_fault_info {
|
||||
r = __VNCR_START__ + ((VNCR_ ## r) / 8), \
|
||||
__after_##r = __MAX__(__before_##r - 1, r)
|
||||
|
||||
#define MARKER(m) \
|
||||
m, __after_##m = m - 1
|
||||
|
||||
enum vcpu_sysreg {
|
||||
__INVALID_SYSREG__, /* 0 is reserved as an invalid value */
|
||||
MPIDR_EL1, /* MultiProcessor Affinity Register */
|
||||
@@ -1548,7 +1545,7 @@ static inline bool __vcpu_has_feature(const struct kvm_arch *ka, int feature)
|
||||
#define kvm_vcpu_has_feature(k, f) __vcpu_has_feature(&(k)->arch, (f))
|
||||
#define vcpu_has_feature(v, f) __vcpu_has_feature(&(v)->kvm->arch, (f))
|
||||
|
||||
#define kvm_vcpu_initialized(v) vcpu_get_flag(vcpu, VCPU_INITIALIZED)
|
||||
#define kvm_vcpu_initialized(v) vcpu_get_flag(v, VCPU_INITIALIZED)
|
||||
|
||||
int kvm_trng_call(struct kvm_vcpu *vcpu);
|
||||
#ifdef CONFIG_KVM
|
||||
|
||||
@@ -824,6 +824,10 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
|
||||
{
|
||||
bool irq_lines = *vcpu_hcr(v) & (HCR_VI | HCR_VF | HCR_VSE);
|
||||
|
||||
irq_lines |= (!irqchip_in_kernel(v->kvm) &&
|
||||
(kvm_timer_should_notify_user(v) ||
|
||||
kvm_pmu_should_notify_user(v)));
|
||||
|
||||
return ((irq_lines || kvm_vgic_vcpu_pending_irq(v))
|
||||
&& !kvm_arm_vcpu_stopped(v) && !v->arch.pause);
|
||||
}
|
||||
|
||||
@@ -131,7 +131,6 @@ struct reg_feat_map_desc {
|
||||
}
|
||||
|
||||
#define FEAT_SPE ID_AA64DFR0_EL1, PMSVer, IMP
|
||||
#define FEAT_SPE_FnE ID_AA64DFR0_EL1, PMSVer, V1P2
|
||||
#define FEAT_BRBE ID_AA64DFR0_EL1, BRBE, IMP
|
||||
#define FEAT_TRC_SR ID_AA64DFR0_EL1, TraceVer, IMP
|
||||
#define FEAT_PMUv3 ID_AA64DFR0_EL1, PMUVer, IMP
|
||||
@@ -192,7 +191,7 @@ struct reg_feat_map_desc {
|
||||
#define FEAT_SRMASK ID_AA64MMFR4_EL1, SRMASK, IMP
|
||||
#define FEAT_PoPS ID_AA64MMFR4_EL1, PoPS, IMP
|
||||
#define FEAT_PFAR ID_AA64PFR1_EL1, PFAR, IMP
|
||||
#define FEAT_Debugv8p9 ID_AA64DFR0_EL1, PMUVer, V3P9
|
||||
#define FEAT_Debugv8p9 ID_AA64DFR0_EL1, DebugVer, V8P9
|
||||
#define FEAT_PMUv3_SS ID_AA64DFR0_EL1, PMSS, IMP
|
||||
#define FEAT_SEBEP ID_AA64DFR0_EL1, SEBEP, IMP
|
||||
#define FEAT_EBEP ID_AA64DFR1_EL1, EBEP, IMP
|
||||
@@ -283,7 +282,7 @@ static bool feat_anerr(struct kvm *kvm)
|
||||
static bool feat_sme_smps(struct kvm *kvm)
|
||||
{
|
||||
/*
|
||||
* Revists this if KVM ever supports SME -- this really should
|
||||
* Revisit this if KVM ever supports SME -- this really should
|
||||
* look at the guest's view of SMIDR_EL1. Funnily enough, this
|
||||
* is not captured in the JSON file, but only as a note in the
|
||||
* ARM ARM.
|
||||
@@ -295,17 +294,27 @@ static bool feat_sme_smps(struct kvm *kvm)
|
||||
static bool feat_spe_fds(struct kvm *kvm)
|
||||
{
|
||||
/*
|
||||
* Revists this if KVM ever supports SPE -- this really should
|
||||
* Revisit this if KVM ever supports SPE -- this really should
|
||||
* look at the guest's view of PMSIDR_EL1.
|
||||
*/
|
||||
return (kvm_has_feat(kvm, FEAT_SPEv1p4) &&
|
||||
(read_sysreg_s(SYS_PMSIDR_EL1) & PMSIDR_EL1_FDS));
|
||||
}
|
||||
|
||||
static bool feat_spe_fne(struct kvm *kvm)
|
||||
{
|
||||
/*
|
||||
* Revisit this if KVM ever supports SPE -- this really should
|
||||
* look at the guest's view of PMSIDR_EL1.
|
||||
*/
|
||||
return (kvm_has_feat(kvm, FEAT_SPEv1p2) &&
|
||||
(read_sysreg_s(SYS_PMSIDR_EL1) & PMSIDR_EL1_FnE));
|
||||
}
|
||||
|
||||
static bool feat_trbe_mpam(struct kvm *kvm)
|
||||
{
|
||||
/*
|
||||
* Revists this if KVM ever supports both MPAM and TRBE --
|
||||
* Revisit this if KVM ever supports both MPAM and TRBE --
|
||||
* this really should look at the guest's view of TRBIDR_EL1.
|
||||
*/
|
||||
return (kvm_has_feat(kvm, FEAT_TRBE) &&
|
||||
@@ -537,7 +546,7 @@ static const struct reg_bits_to_feat_map hdfgrtr_feat_map[] = {
|
||||
HDFGRTR_EL2_PMBPTR_EL1 |
|
||||
HDFGRTR_EL2_PMBLIMITR_EL1,
|
||||
FEAT_SPE),
|
||||
NEEDS_FEAT(HDFGRTR_EL2_nPMSNEVFR_EL1, FEAT_SPE_FnE),
|
||||
NEEDS_FEAT(HDFGRTR_EL2_nPMSNEVFR_EL1, feat_spe_fne),
|
||||
NEEDS_FEAT(HDFGRTR_EL2_nBRBDATA |
|
||||
HDFGRTR_EL2_nBRBCTL |
|
||||
HDFGRTR_EL2_nBRBIDR,
|
||||
@@ -605,7 +614,7 @@ static const struct reg_bits_to_feat_map hdfgwtr_feat_map[] = {
|
||||
HDFGWTR_EL2_PMBPTR_EL1 |
|
||||
HDFGWTR_EL2_PMBLIMITR_EL1,
|
||||
FEAT_SPE),
|
||||
NEEDS_FEAT(HDFGWTR_EL2_nPMSNEVFR_EL1, FEAT_SPE_FnE),
|
||||
NEEDS_FEAT(HDFGWTR_EL2_nPMSNEVFR_EL1, feat_spe_fne),
|
||||
NEEDS_FEAT(HDFGWTR_EL2_nBRBDATA |
|
||||
HDFGWTR_EL2_nBRBCTL,
|
||||
FEAT_BRBE),
|
||||
|
||||
@@ -709,6 +709,14 @@ static const hcall_t host_hcall[] = {
|
||||
HANDLE_FUNC(__kvm_tlb_flush_vmid_range),
|
||||
HANDLE_FUNC(__kvm_flush_cpu_context),
|
||||
HANDLE_FUNC(__kvm_timer_set_cntvoff),
|
||||
HANDLE_FUNC(__tracing_load),
|
||||
HANDLE_FUNC(__tracing_unload),
|
||||
HANDLE_FUNC(__tracing_enable),
|
||||
HANDLE_FUNC(__tracing_swap_reader),
|
||||
HANDLE_FUNC(__tracing_update_clock),
|
||||
HANDLE_FUNC(__tracing_reset),
|
||||
HANDLE_FUNC(__tracing_enable_event),
|
||||
HANDLE_FUNC(__tracing_write_event),
|
||||
HANDLE_FUNC(__vgic_v3_save_aprs),
|
||||
HANDLE_FUNC(__vgic_v3_restore_vmcr_aprs),
|
||||
HANDLE_FUNC(__vgic_v5_save_apr),
|
||||
@@ -735,22 +743,16 @@ static const hcall_t host_hcall[] = {
|
||||
HANDLE_FUNC(__pkvm_vcpu_load),
|
||||
HANDLE_FUNC(__pkvm_vcpu_put),
|
||||
HANDLE_FUNC(__pkvm_tlb_flush_vmid),
|
||||
HANDLE_FUNC(__tracing_load),
|
||||
HANDLE_FUNC(__tracing_unload),
|
||||
HANDLE_FUNC(__tracing_enable),
|
||||
HANDLE_FUNC(__tracing_swap_reader),
|
||||
HANDLE_FUNC(__tracing_update_clock),
|
||||
HANDLE_FUNC(__tracing_reset),
|
||||
HANDLE_FUNC(__tracing_enable_event),
|
||||
HANDLE_FUNC(__tracing_write_event),
|
||||
};
|
||||
|
||||
static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(unsigned long, id, host_ctxt, 0);
|
||||
unsigned long hcall_min = 0, hcall_max = -1;
|
||||
unsigned long hcall_min = 0, hcall_max = __KVM_HOST_SMCCC_FUNC_MAX;
|
||||
hcall_t hfn;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(host_hcall) != __KVM_HOST_SMCCC_FUNC_MAX);
|
||||
|
||||
/*
|
||||
* If pKVM has been initialised then reject any calls to the
|
||||
* early "privileged" hypercalls. Note that we cannot reject
|
||||
@@ -763,16 +765,14 @@ static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)
|
||||
if (static_branch_unlikely(&kvm_protected_mode_initialized)) {
|
||||
hcall_min = __KVM_HOST_SMCCC_FUNC_MIN_PKVM;
|
||||
} else {
|
||||
hcall_max = __KVM_HOST_SMCCC_FUNC_MAX_NO_PKVM;
|
||||
hcall_max = __KVM_HOST_SMCCC_FUNC_PKVM_ONLY;
|
||||
}
|
||||
|
||||
id &= ~ARM_SMCCC_CALL_HINTS;
|
||||
id -= KVM_HOST_SMCCC_ID(0);
|
||||
|
||||
if (unlikely(id < hcall_min || id > hcall_max ||
|
||||
id >= ARRAY_SIZE(host_hcall))) {
|
||||
if (unlikely(id < hcall_min || id >= hcall_max))
|
||||
goto inval;
|
||||
}
|
||||
|
||||
hfn = host_hcall[id];
|
||||
if (unlikely(!hfn))
|
||||
@@ -805,6 +805,10 @@ static void handle_host_smc(struct kvm_cpu_context *host_ctxt)
|
||||
}
|
||||
|
||||
func_id &= ~ARM_SMCCC_CALL_HINTS;
|
||||
if (upper_32_bits(func_id)) {
|
||||
cpu_reg(host_ctxt, 0) = SMCCC_RET_NOT_SUPPORTED;
|
||||
goto exit_skip_instr;
|
||||
}
|
||||
|
||||
handled = kvm_host_psci_handler(host_ctxt, func_id);
|
||||
if (!handled)
|
||||
|
||||
@@ -266,7 +266,8 @@ struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
|
||||
if (hyp_vm->kvm.created_vcpus <= vcpu_idx)
|
||||
goto unlock;
|
||||
|
||||
hyp_vcpu = hyp_vm->vcpus[vcpu_idx];
|
||||
/* Pairs with smp_store_release() in register_hyp_vcpu(). */
|
||||
hyp_vcpu = smp_load_acquire(&hyp_vm->vcpus[vcpu_idx]);
|
||||
if (!hyp_vcpu)
|
||||
goto unlock;
|
||||
|
||||
@@ -860,12 +861,30 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
|
||||
* the page-aligned size of 'struct pkvm_hyp_vcpu'.
|
||||
* Return 0 on success, negative error code on failure.
|
||||
*/
|
||||
static int register_hyp_vcpu(struct pkvm_hyp_vm *hyp_vm,
|
||||
struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
{
|
||||
unsigned int idx = hyp_vcpu->vcpu.vcpu_idx;
|
||||
|
||||
if (idx >= hyp_vm->kvm.created_vcpus)
|
||||
return -EINVAL;
|
||||
|
||||
if (hyp_vm->vcpus[idx])
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Ensure the hyp_vcpu is initialised before publishing it to
|
||||
* the vCPU-load path via 'hyp_vm->vcpus[]'.
|
||||
*/
|
||||
smp_store_release(&hyp_vm->vcpus[idx], hyp_vcpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
|
||||
unsigned long vcpu_hva)
|
||||
{
|
||||
struct pkvm_hyp_vcpu *hyp_vcpu;
|
||||
struct pkvm_hyp_vm *hyp_vm;
|
||||
unsigned int idx;
|
||||
int ret;
|
||||
|
||||
hyp_vcpu = map_donated_memory(vcpu_hva, sizeof(*hyp_vcpu));
|
||||
@@ -884,18 +903,11 @@ int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
idx = hyp_vcpu->vcpu.vcpu_idx;
|
||||
if (idx >= hyp_vm->kvm.created_vcpus) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
ret = register_hyp_vcpu(hyp_vm, hyp_vcpu);
|
||||
if (ret) {
|
||||
unpin_host_vcpu(host_vcpu);
|
||||
unpin_host_sve_state(hyp_vcpu);
|
||||
}
|
||||
|
||||
if (hyp_vm->vcpus[idx]) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hyp_vm->vcpus[idx] = hyp_vcpu;
|
||||
unlock:
|
||||
hyp_spin_unlock(&vm_table_lock);
|
||||
|
||||
|
||||
@@ -312,10 +312,6 @@ void __noreturn __pkvm_init_finalise(void)
|
||||
};
|
||||
pkvm_pgtable.mm_ops = &pkvm_pgtable_mm_ops;
|
||||
|
||||
ret = fix_host_ownership();
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = fix_hyp_pgtable_refcnt();
|
||||
if (ret)
|
||||
goto out;
|
||||
@@ -324,6 +320,10 @@ void __noreturn __pkvm_init_finalise(void)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = fix_host_ownership();
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = hyp_ffa_init(ffa_proxy_pages);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
@@ -91,7 +91,7 @@ static int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu,
|
||||
* migration from old kernels to new kernels with legacy
|
||||
* userspace.
|
||||
*/
|
||||
reg = FIELD_GET(GICD_IIDR_REVISION_MASK, reg);
|
||||
reg = FIELD_GET(GICD_IIDR_REVISION_MASK, val);
|
||||
switch (reg) {
|
||||
case KVM_VGIC_IMP_REV_2:
|
||||
case KVM_VGIC_IMP_REV_3:
|
||||
|
||||
@@ -194,7 +194,7 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu,
|
||||
if ((reg ^ val) & ~GICD_IIDR_REVISION_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
reg = FIELD_GET(GICD_IIDR_REVISION_MASK, reg);
|
||||
reg = FIELD_GET(GICD_IIDR_REVISION_MASK, val);
|
||||
switch (reg) {
|
||||
case KVM_VGIC_IMP_REV_2:
|
||||
case KVM_VGIC_IMP_REV_3:
|
||||
|
||||
@@ -459,19 +459,11 @@ static void pata_parport_dev_release(struct device *dev)
|
||||
kfree(pi);
|
||||
}
|
||||
|
||||
static void pata_parport_bus_release(struct device *dev)
|
||||
{
|
||||
/* nothing to do here but required to avoid warning on device removal */
|
||||
}
|
||||
|
||||
static const struct bus_type pata_parport_bus_type = {
|
||||
.name = DRV_NAME,
|
||||
};
|
||||
|
||||
static struct device pata_parport_bus = {
|
||||
.init_name = DRV_NAME,
|
||||
.release = pata_parport_bus_release,
|
||||
};
|
||||
static struct device *pata_parport_bus;
|
||||
|
||||
static const struct scsi_host_template pata_parport_sht = {
|
||||
PATA_PARPORT_SHT("pata_parport")
|
||||
@@ -518,7 +510,7 @@ static struct pi_adapter *pi_init_one(struct parport *parport,
|
||||
}
|
||||
|
||||
/* set up pi->dev before pi_probe_unit() so it can use dev_printk() */
|
||||
pi->dev.parent = &pata_parport_bus;
|
||||
pi->dev.parent = pata_parport_bus;
|
||||
pi->dev.bus = &pata_parport_bus_type;
|
||||
pi->dev.driver = &pr->driver;
|
||||
pi->dev.release = pata_parport_dev_release;
|
||||
@@ -780,8 +772,9 @@ static __init int pata_parport_init(void)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = device_register(&pata_parport_bus);
|
||||
if (error) {
|
||||
pata_parport_bus = root_device_register(DRV_NAME);
|
||||
if (IS_ERR(pata_parport_bus)) {
|
||||
error = PTR_ERR(pata_parport_bus);
|
||||
pr_err("failed to register pata_parport bus, error: %d\n", error);
|
||||
goto out_unregister_bus;
|
||||
}
|
||||
@@ -811,7 +804,7 @@ static __init int pata_parport_init(void)
|
||||
out_remove_new:
|
||||
bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
|
||||
out_unregister_dev:
|
||||
device_unregister(&pata_parport_bus);
|
||||
root_device_unregister(pata_parport_bus);
|
||||
out_unregister_bus:
|
||||
bus_unregister(&pata_parport_bus_type);
|
||||
return error;
|
||||
@@ -822,7 +815,7 @@ static __exit void pata_parport_exit(void)
|
||||
parport_unregister_driver(&pata_parport_driver);
|
||||
bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
|
||||
bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
|
||||
device_unregister(&pata_parport_bus);
|
||||
root_device_unregister(pata_parport_bus);
|
||||
bus_unregister(&pata_parport_bus_type);
|
||||
}
|
||||
|
||||
|
||||
@@ -927,11 +927,21 @@ int dpll_pin_delete_ntf(struct dpll_pin *pin)
|
||||
return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
|
||||
}
|
||||
|
||||
/**
|
||||
* __dpll_pin_change_ntf - notify that the pin has been changed
|
||||
* @pin: registered pin pointer
|
||||
*
|
||||
* Context: caller must hold dpll_lock. Suitable for use inside pin
|
||||
* callbacks which are already invoked under dpll_lock.
|
||||
* Return: 0 if succeeds, error code otherwise.
|
||||
*/
|
||||
int __dpll_pin_change_ntf(struct dpll_pin *pin)
|
||||
{
|
||||
lockdep_assert_held(&dpll_lock);
|
||||
dpll_pin_notify(pin, DPLL_PIN_CHANGED);
|
||||
return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__dpll_pin_change_ntf);
|
||||
|
||||
/**
|
||||
* dpll_pin_change_ntf - notify that the pin has been changed
|
||||
|
||||
@@ -11,5 +11,3 @@ int dpll_device_delete_ntf(struct dpll_device *dpll);
|
||||
int dpll_pin_create_ntf(struct dpll_pin *pin);
|
||||
|
||||
int dpll_pin_delete_ntf(struct dpll_pin *pin);
|
||||
|
||||
int __dpll_pin_change_ntf(struct dpll_pin *pin);
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "mailbox.h"
|
||||
|
||||
/*
|
||||
* The maximum transmission size is 32 words or 128 bytes.
|
||||
*/
|
||||
@@ -405,7 +403,7 @@ static int cix_mbox_startup(struct mbox_chan *chan)
|
||||
int index = cp->index, ret;
|
||||
u32 val;
|
||||
|
||||
ret = request_irq(priv->irq, cix_mbox_isr, 0,
|
||||
ret = request_irq(priv->irq, cix_mbox_isr, IRQF_NO_SUSPEND,
|
||||
dev_name(priv->dev), chan);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Unable to acquire IRQ %d\n", priv->irq);
|
||||
@@ -415,7 +413,7 @@ static int cix_mbox_startup(struct mbox_chan *chan)
|
||||
switch (cp->type) {
|
||||
case CIX_MBOX_TYPE_DB:
|
||||
/* Overwrite txdone_method for DB channel */
|
||||
chan->txdone_method = TXDONE_BY_ACK;
|
||||
chan->txdone_method = MBOX_TXDONE_BY_ACK;
|
||||
fallthrough;
|
||||
case CIX_MBOX_TYPE_REG:
|
||||
if (priv->dir == CIX_MBOX_TX) {
|
||||
|
||||
@@ -99,7 +99,6 @@ static int exynos_mbox_probe(struct platform_device *pdev)
|
||||
struct mbox_controller *mbox;
|
||||
struct mbox_chan *chans;
|
||||
struct clk *pclk;
|
||||
int i;
|
||||
|
||||
exynos_mbox = devm_kzalloc(dev, sizeof(*exynos_mbox), GFP_KERNEL);
|
||||
if (!exynos_mbox)
|
||||
@@ -129,9 +128,6 @@ static int exynos_mbox_probe(struct platform_device *pdev)
|
||||
mbox->ops = &exynos_mbox_chan_ops;
|
||||
mbox->of_xlate = exynos_mbox_of_xlate;
|
||||
|
||||
for (i = 0; i < EXYNOS_MBOX_CHAN_COUNT; i++)
|
||||
chans[i].mbox = mbox;
|
||||
|
||||
exynos_mbox->mbox = mbox;
|
||||
|
||||
platform_set_drvdata(pdev, exynos_mbox);
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "mailbox.h"
|
||||
|
||||
#define MBOX_CHAN_MAX 32
|
||||
|
||||
#define MBOX_RX 0x0
|
||||
|
||||
@@ -79,12 +79,12 @@ struct hi6220_mbox {
|
||||
/* region for mailbox */
|
||||
void __iomem *base;
|
||||
|
||||
unsigned int chan_num;
|
||||
struct hi6220_mbox_chan *mchan;
|
||||
|
||||
void *irq_map_chan[MBOX_CHAN_MAX];
|
||||
struct mbox_chan *chan;
|
||||
struct mbox_controller controller;
|
||||
|
||||
unsigned int chan_num;
|
||||
struct hi6220_mbox_chan mchan[] __counted_by(chan_num);
|
||||
};
|
||||
|
||||
static void mbox_set_state(struct hi6220_mbox *mbox,
|
||||
@@ -267,16 +267,12 @@ static int hi6220_mbox_probe(struct platform_device *pdev)
|
||||
struct hi6220_mbox *mbox;
|
||||
int i, err;
|
||||
|
||||
mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
|
||||
mbox = devm_kzalloc(dev, struct_size(mbox, mchan, MBOX_CHAN_MAX), GFP_KERNEL);
|
||||
if (!mbox)
|
||||
return -ENOMEM;
|
||||
|
||||
mbox->dev = dev;
|
||||
mbox->chan_num = MBOX_CHAN_MAX;
|
||||
mbox->mchan = devm_kcalloc(dev,
|
||||
mbox->chan_num, sizeof(*mbox->mchan), GFP_KERNEL);
|
||||
if (!mbox->mchan)
|
||||
return -ENOMEM;
|
||||
mbox->dev = dev;
|
||||
|
||||
mbox->chan = devm_kcalloc(dev,
|
||||
mbox->chan_num, sizeof(*mbox->chan), GFP_KERNEL);
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "mailbox.h"
|
||||
|
||||
#define IMX_MU_CHANS 24
|
||||
/* TX0/RX0/RXDB[0-3] */
|
||||
#define IMX_MU_SCU_CHANS 6
|
||||
@@ -734,7 +732,7 @@ static struct mbox_chan * imx_mu_xlate(struct mbox_controller *mbox,
|
||||
p_chan = &mbox->chans[chan];
|
||||
|
||||
if (type == IMX_MU_TYPE_TXDB_V2)
|
||||
p_chan->txdone_method = TXDONE_BY_ACK;
|
||||
p_chan->txdone_method = MBOX_TXDONE_BY_ACK;
|
||||
|
||||
return p_chan;
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "mailbox.h"
|
||||
|
||||
#define STI_MBOX_INST_MAX 4 /* RAM saving: Max supported instances */
|
||||
#define STI_MBOX_CHAN_MAX 20 /* RAM saving: Max supported channels */
|
||||
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
#define MBOX_HEXDUMP_MAX_LEN (MBOX_HEXDUMP_LINE_LEN * \
|
||||
(MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE))
|
||||
|
||||
static bool mbox_data_ready;
|
||||
|
||||
struct mbox_test_device {
|
||||
struct device *dev;
|
||||
void __iomem *tx_mmio;
|
||||
@@ -42,6 +40,7 @@ struct mbox_test_device {
|
||||
spinlock_t lock;
|
||||
struct mutex mutex;
|
||||
wait_queue_head_t waitq;
|
||||
bool data_ready;
|
||||
struct fasync_struct *async_queue;
|
||||
struct dentry *root_debugfs_dir;
|
||||
};
|
||||
@@ -162,7 +161,7 @@ static bool mbox_test_message_data_ready(struct mbox_test_device *tdev)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tdev->lock, flags);
|
||||
data_ready = mbox_data_ready;
|
||||
data_ready = tdev->data_ready;
|
||||
spin_unlock_irqrestore(&tdev->lock, flags);
|
||||
|
||||
return data_ready;
|
||||
@@ -227,7 +226,7 @@ static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf,
|
||||
*(touser + l) = '\0';
|
||||
|
||||
memset(tdev->rx_buffer, 0, MBOX_MAX_MSG_LEN);
|
||||
mbox_data_ready = false;
|
||||
tdev->data_ready = false;
|
||||
|
||||
spin_unlock_irqrestore(&tdev->lock, flags);
|
||||
|
||||
@@ -297,7 +296,7 @@ static void mbox_test_receive_message(struct mbox_client *client, void *message)
|
||||
message, MBOX_MAX_MSG_LEN);
|
||||
memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN);
|
||||
}
|
||||
mbox_data_ready = true;
|
||||
tdev->data_ready = true;
|
||||
spin_unlock_irqrestore(&tdev->lock, flags);
|
||||
|
||||
wake_up_interruptible(&tdev->waitq);
|
||||
@@ -336,7 +335,7 @@ mbox_test_request_channel(struct platform_device *pdev, const char *name)
|
||||
|
||||
client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL);
|
||||
if (!client)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return NULL;
|
||||
|
||||
client->dev = &pdev->dev;
|
||||
client->rx_callback = mbox_test_receive_message;
|
||||
@@ -355,67 +354,80 @@ mbox_test_request_channel(struct platform_device *pdev, const char *name)
|
||||
return channel;
|
||||
}
|
||||
|
||||
static void __iomem *mbox_test_ioremap(struct platform_device *pdev, unsigned int res_num)
|
||||
{
|
||||
struct resource *res;
|
||||
void __iomem *mmio;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, res_num);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
mmio = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (PTR_ERR(mmio) == -EBUSY) {
|
||||
dev_info(&pdev->dev, "trying workaround with plain ioremap\n");
|
||||
return devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
}
|
||||
|
||||
return IS_ERR(mmio) ? NULL : mmio;
|
||||
}
|
||||
|
||||
static int mbox_test_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mbox_test_device *tdev;
|
||||
struct resource *res;
|
||||
resource_size_t size;
|
||||
int ret;
|
||||
|
||||
tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL);
|
||||
if (!tdev)
|
||||
return -ENOMEM;
|
||||
|
||||
tdev->dev = &pdev->dev;
|
||||
spin_lock_init(&tdev->lock);
|
||||
mutex_init(&tdev->mutex);
|
||||
init_waitqueue_head(&tdev->waitq);
|
||||
platform_set_drvdata(pdev, tdev);
|
||||
|
||||
/* It's okay for MMIO to be NULL */
|
||||
tdev->tx_mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (PTR_ERR(tdev->tx_mmio) == -EBUSY) {
|
||||
/* if reserved area in SRAM, try just ioremap */
|
||||
size = resource_size(res);
|
||||
tdev->tx_mmio = devm_ioremap(&pdev->dev, res->start, size);
|
||||
} else if (IS_ERR(tdev->tx_mmio)) {
|
||||
tdev->tx_mmio = NULL;
|
||||
}
|
||||
tdev->tx_mmio = mbox_test_ioremap(pdev, 0);
|
||||
|
||||
/* If specified, second reg entry is Rx MMIO */
|
||||
tdev->rx_mmio = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
|
||||
if (PTR_ERR(tdev->rx_mmio) == -EBUSY) {
|
||||
size = resource_size(res);
|
||||
tdev->rx_mmio = devm_ioremap(&pdev->dev, res->start, size);
|
||||
} else if (IS_ERR(tdev->rx_mmio)) {
|
||||
tdev->rx_mmio = mbox_test_ioremap(pdev, 1);
|
||||
if (!tdev->rx_mmio)
|
||||
tdev->rx_mmio = tdev->tx_mmio;
|
||||
}
|
||||
|
||||
tdev->tx_channel = mbox_test_request_channel(pdev, "tx");
|
||||
tdev->rx_channel = mbox_test_request_channel(pdev, "rx");
|
||||
|
||||
if (IS_ERR_OR_NULL(tdev->tx_channel) && IS_ERR_OR_NULL(tdev->rx_channel))
|
||||
if (!tdev->tx_channel && !tdev->rx_channel)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/* If Rx is not specified but has Rx MMIO, then Rx = Tx */
|
||||
if (!tdev->rx_channel && (tdev->rx_mmio != tdev->tx_mmio))
|
||||
tdev->rx_channel = tdev->tx_channel;
|
||||
|
||||
tdev->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, tdev);
|
||||
|
||||
spin_lock_init(&tdev->lock);
|
||||
mutex_init(&tdev->mutex);
|
||||
|
||||
if (tdev->rx_channel) {
|
||||
tdev->rx_buffer = devm_kzalloc(&pdev->dev,
|
||||
MBOX_MAX_MSG_LEN, GFP_KERNEL);
|
||||
if (!tdev->rx_buffer)
|
||||
return -ENOMEM;
|
||||
if (!tdev->rx_buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_chans;
|
||||
}
|
||||
}
|
||||
|
||||
ret = mbox_test_add_debugfs(pdev, tdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_free_chans;
|
||||
|
||||
init_waitqueue_head(&tdev->waitq);
|
||||
dev_info(&pdev->dev, "Successfully registered\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_chans:
|
||||
if (tdev->tx_channel)
|
||||
mbox_free_channel(tdev->tx_channel);
|
||||
if (tdev->rx_channel && tdev->rx_channel != tdev->tx_channel)
|
||||
mbox_free_channel(tdev->rx_channel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mbox_test_remove(struct platform_device *pdev)
|
||||
@@ -426,7 +438,7 @@ static void mbox_test_remove(struct platform_device *pdev)
|
||||
|
||||
if (tdev->tx_channel)
|
||||
mbox_free_channel(tdev->tx_channel);
|
||||
if (tdev->rx_channel)
|
||||
if (tdev->rx_channel && tdev->rx_channel != tdev->tx_channel)
|
||||
mbox_free_channel(tdev->rx_channel);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
#include <linux/property.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "mailbox.h"
|
||||
|
||||
static LIST_HEAD(mbox_cons);
|
||||
static DEFINE_MUTEX(con_mutex);
|
||||
|
||||
@@ -52,7 +50,7 @@ static void msg_submit(struct mbox_chan *chan)
|
||||
int err = -EBUSY;
|
||||
|
||||
scoped_guard(spinlock_irqsave, &chan->lock) {
|
||||
if (!chan->msg_count || chan->active_req)
|
||||
if (!chan->msg_count || chan->active_req != MBOX_NO_MSG)
|
||||
break;
|
||||
|
||||
count = chan->msg_count;
|
||||
@@ -74,7 +72,7 @@ static void msg_submit(struct mbox_chan *chan)
|
||||
}
|
||||
}
|
||||
|
||||
if (!err && (chan->txdone_method & TXDONE_BY_POLL)) {
|
||||
if (!err && (chan->txdone_method & MBOX_TXDONE_BY_POLL)) {
|
||||
/* kick start the timer immediately to avoid delays */
|
||||
scoped_guard(spinlock_irqsave, &chan->mbox->poll_hrt_lock)
|
||||
hrtimer_start(&chan->mbox->poll_hrt, 0, HRTIMER_MODE_REL);
|
||||
@@ -87,13 +85,13 @@ static void tx_tick(struct mbox_chan *chan, int r)
|
||||
|
||||
scoped_guard(spinlock_irqsave, &chan->lock) {
|
||||
mssg = chan->active_req;
|
||||
chan->active_req = NULL;
|
||||
chan->active_req = MBOX_NO_MSG;
|
||||
}
|
||||
|
||||
/* Submit next message */
|
||||
msg_submit(chan);
|
||||
|
||||
if (!mssg)
|
||||
if (mssg == MBOX_NO_MSG)
|
||||
return;
|
||||
|
||||
/* Notify the client */
|
||||
@@ -114,7 +112,7 @@ static enum hrtimer_restart txdone_hrtimer(struct hrtimer *hrtimer)
|
||||
for (i = 0; i < mbox->num_chans; i++) {
|
||||
struct mbox_chan *chan = &mbox->chans[i];
|
||||
|
||||
if (chan->active_req && chan->cl) {
|
||||
if (chan->active_req != MBOX_NO_MSG && chan->cl) {
|
||||
txdone = chan->mbox->ops->last_tx_done(chan);
|
||||
if (txdone)
|
||||
tx_tick(chan, 0);
|
||||
@@ -164,7 +162,7 @@ EXPORT_SYMBOL_GPL(mbox_chan_received_data);
|
||||
*/
|
||||
void mbox_chan_txdone(struct mbox_chan *chan, int r)
|
||||
{
|
||||
if (unlikely(!(chan->txdone_method & TXDONE_BY_IRQ))) {
|
||||
if (unlikely(!(chan->txdone_method & MBOX_TXDONE_BY_IRQ))) {
|
||||
dev_err(chan->mbox->dev,
|
||||
"Controller can't run the TX ticker\n");
|
||||
return;
|
||||
@@ -185,7 +183,7 @@ EXPORT_SYMBOL_GPL(mbox_chan_txdone);
|
||||
*/
|
||||
void mbox_client_txdone(struct mbox_chan *chan, int r)
|
||||
{
|
||||
if (unlikely(!(chan->txdone_method & TXDONE_BY_ACK))) {
|
||||
if (unlikely(!(chan->txdone_method & MBOX_TXDONE_BY_ACK))) {
|
||||
dev_err(chan->mbox->dev, "Client can't run the TX ticker\n");
|
||||
return;
|
||||
}
|
||||
@@ -218,6 +216,29 @@ bool mbox_client_peek_data(struct mbox_chan *chan)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mbox_client_peek_data);
|
||||
|
||||
/**
|
||||
* mbox_chan_tx_slots_available - Query the number of available TX queue slots.
|
||||
* @chan: Mailbox channel to query.
|
||||
*
|
||||
* Clients may call this to check how many messages can be queued via
|
||||
* mbox_send_message() before the channel's TX queue is full. This helps
|
||||
* clients avoid the -ENOBUFS error without needing to increase
|
||||
* MBOX_TX_QUEUE_LEN.
|
||||
* This can be called from atomic context.
|
||||
*
|
||||
* Return: Number of available slots in the channel's TX queue.
|
||||
*/
|
||||
unsigned int mbox_chan_tx_slots_available(struct mbox_chan *chan)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
guard(spinlock_irqsave)(&chan->lock);
|
||||
ret = MBOX_TX_QUEUE_LEN - chan->msg_count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mbox_chan_tx_slots_available);
|
||||
|
||||
/**
|
||||
* mbox_send_message - For client to submit a message to be
|
||||
* sent to the remote.
|
||||
@@ -246,7 +267,7 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
|
||||
{
|
||||
int t;
|
||||
|
||||
if (!chan || !chan->cl)
|
||||
if (!chan || !chan->cl || mssg == MBOX_NO_MSG)
|
||||
return -EINVAL;
|
||||
|
||||
t = add_to_rbuf(chan, mssg);
|
||||
@@ -319,12 +340,12 @@ static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl)
|
||||
scoped_guard(spinlock_irqsave, &chan->lock) {
|
||||
chan->msg_free = 0;
|
||||
chan->msg_count = 0;
|
||||
chan->active_req = NULL;
|
||||
chan->active_req = MBOX_NO_MSG;
|
||||
chan->cl = cl;
|
||||
init_completion(&chan->tx_complete);
|
||||
|
||||
if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone)
|
||||
chan->txdone_method = TXDONE_BY_ACK;
|
||||
if (chan->txdone_method == MBOX_TXDONE_BY_POLL && cl->knows_txdone)
|
||||
chan->txdone_method = MBOX_TXDONE_BY_ACK;
|
||||
}
|
||||
|
||||
if (chan->mbox->ops->startup) {
|
||||
@@ -341,7 +362,7 @@ static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl)
|
||||
}
|
||||
|
||||
/**
|
||||
* mbox_bind_client - Request a mailbox channel.
|
||||
* mbox_bind_client - Bind client to a mailbox channel.
|
||||
* @chan: The mailbox channel to bind the client to.
|
||||
* @cl: Identity of the client requesting the channel.
|
||||
*
|
||||
@@ -477,9 +498,9 @@ void mbox_free_channel(struct mbox_chan *chan)
|
||||
/* The queued TX requests are simply aborted, no callbacks are made */
|
||||
scoped_guard(spinlock_irqsave, &chan->lock) {
|
||||
chan->cl = NULL;
|
||||
chan->active_req = NULL;
|
||||
if (chan->txdone_method == TXDONE_BY_ACK)
|
||||
chan->txdone_method = TXDONE_BY_POLL;
|
||||
chan->active_req = MBOX_NO_MSG;
|
||||
if (chan->txdone_method == MBOX_TXDONE_BY_ACK)
|
||||
chan->txdone_method = MBOX_TXDONE_BY_POLL;
|
||||
}
|
||||
|
||||
module_put(chan->mbox->dev->driver->owner);
|
||||
@@ -505,18 +526,17 @@ int mbox_controller_register(struct mbox_controller *mbox)
|
||||
{
|
||||
int i, txdone;
|
||||
|
||||
/* Sanity check */
|
||||
if (!mbox || !mbox->dev || !mbox->ops || !mbox->num_chans)
|
||||
if (!mbox || !mbox->dev || !mbox->ops || !mbox->chans || !mbox->num_chans)
|
||||
return -EINVAL;
|
||||
|
||||
if (mbox->txdone_irq)
|
||||
txdone = TXDONE_BY_IRQ;
|
||||
txdone = MBOX_TXDONE_BY_IRQ;
|
||||
else if (mbox->txdone_poll)
|
||||
txdone = TXDONE_BY_POLL;
|
||||
txdone = MBOX_TXDONE_BY_POLL;
|
||||
else /* It has to be ACK then */
|
||||
txdone = TXDONE_BY_ACK;
|
||||
txdone = MBOX_TXDONE_BY_ACK;
|
||||
|
||||
if (txdone == TXDONE_BY_POLL) {
|
||||
if (txdone == MBOX_TXDONE_BY_POLL) {
|
||||
|
||||
if (!mbox->ops->last_tx_done) {
|
||||
dev_err(mbox->dev, "last_tx_done method is absent\n");
|
||||
@@ -532,6 +552,7 @@ int mbox_controller_register(struct mbox_controller *mbox)
|
||||
|
||||
chan->cl = NULL;
|
||||
chan->mbox = mbox;
|
||||
chan->active_req = MBOX_NO_MSG;
|
||||
chan->txdone_method = txdone;
|
||||
spin_lock_init(&chan->lock);
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __MAILBOX_H
|
||||
#define __MAILBOX_H
|
||||
|
||||
#include <linux/bits.h>
|
||||
|
||||
#define TXDONE_BY_IRQ BIT(0) /* controller has remote RTR irq */
|
||||
#define TXDONE_BY_POLL BIT(1) /* controller can read status of last TX */
|
||||
#define TXDONE_BY_ACK BIT(2) /* S/W ACK received by Client ticks the TX */
|
||||
|
||||
#endif /* __MAILBOX_H */
|
||||
@@ -493,14 +493,14 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
|
||||
if (curr_pa == end_pa - CMDQ_INST_SIZE ||
|
||||
curr_pa == end_pa) {
|
||||
/* set to this task directly */
|
||||
writel(task->pa_base >> cmdq->pdata->shift,
|
||||
thread->base + CMDQ_THR_CURR_ADDR);
|
||||
gce_addr = cmdq_convert_gce_addr(task->pa_base, cmdq->pdata);
|
||||
writel(gce_addr, thread->base + CMDQ_THR_CURR_ADDR);
|
||||
} else {
|
||||
cmdq_task_insert_into_thread(task);
|
||||
smp_mb(); /* modify jump before enable thread */
|
||||
}
|
||||
writel((task->pa_base + pkt->cmd_buf_size) >> cmdq->pdata->shift,
|
||||
thread->base + CMDQ_THR_END_ADDR);
|
||||
gce_addr = cmdq_convert_gce_addr(task->pa_base + pkt->cmd_buf_size, cmdq->pdata);
|
||||
writel(gce_addr, thread->base + CMDQ_THR_END_ADDR);
|
||||
cmdq_thread_resume(thread);
|
||||
}
|
||||
list_move_tail(&task->list_entry, &thread->task_busy_list);
|
||||
@@ -728,7 +728,7 @@ static int cmdq_probe(struct platform_device *pdev)
|
||||
cmdq->mbox.ops = &cmdq_mbox_chan_ops;
|
||||
cmdq->mbox.of_xlate = cmdq_xlate;
|
||||
|
||||
/* make use of TXDONE_BY_ACK */
|
||||
/* make use of MBOX_TXDONE_BY_ACK */
|
||||
cmdq->mbox.txdone_irq = false;
|
||||
cmdq->mbox.txdone_poll = false;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ static struct mbox_chan *mtk_vcp_mbox_xlate(struct mbox_controller *mbox,
|
||||
const struct of_phandle_args *sp)
|
||||
{
|
||||
if (sp->args_count)
|
||||
return NULL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return &mbox->chans[0];
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/mailbox_controller.h>
|
||||
|
||||
#include "mailbox.h"
|
||||
|
||||
#define MAILBOX_REVISION 0x000
|
||||
#define MAILBOX_MESSAGE(m) (0x040 + 4 * (m))
|
||||
#define MAILBOX_FIFOSTATUS(m) (0x080 + 4 * (m))
|
||||
@@ -240,7 +238,7 @@ static int omap_mbox_startup(struct omap_mbox *mbox)
|
||||
}
|
||||
|
||||
if (mbox->send_no_irq)
|
||||
mbox->chan->txdone_method = TXDONE_BY_ACK;
|
||||
mbox->chan->txdone_method = MBOX_TXDONE_BY_ACK;
|
||||
|
||||
omap_mbox_enable_irq(mbox, IRQ_RX);
|
||||
|
||||
|
||||
@@ -59,8 +59,6 @@
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <acpi/pcc.h>
|
||||
|
||||
#include "mailbox.h"
|
||||
|
||||
#define MBOX_IRQ_NAME "pcc-mbox"
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,7 +46,7 @@ struct rockchip_mbox {
|
||||
/* The maximum size of buf for each channel */
|
||||
u32 buf_size;
|
||||
|
||||
struct rockchip_mbox_chan *chans;
|
||||
struct rockchip_mbox_chan chans[];
|
||||
};
|
||||
|
||||
static int rockchip_mbox_send_data(struct mbox_chan *chan, void *data)
|
||||
@@ -173,15 +173,10 @@ static int rockchip_mbox_probe(struct platform_device *pdev)
|
||||
|
||||
drv_data = (const struct rockchip_mbox_data *) device_get_match_data(&pdev->dev);
|
||||
|
||||
mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL);
|
||||
mb = devm_kzalloc(&pdev->dev, struct_size(mb, chans, drv_data->num_chans), GFP_KERNEL);
|
||||
if (!mb)
|
||||
return -ENOMEM;
|
||||
|
||||
mb->chans = devm_kcalloc(&pdev->dev, drv_data->num_chans,
|
||||
sizeof(*mb->chans), GFP_KERNEL);
|
||||
if (!mb->chans)
|
||||
return -ENOMEM;
|
||||
|
||||
mb->mbox.chans = devm_kcalloc(&pdev->dev, drv_data->num_chans,
|
||||
sizeof(*mb->mbox.chans), GFP_KERNEL);
|
||||
if (!mb->mbox.chans)
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#include <dt-bindings/mailbox/tegra186-hsp.h>
|
||||
|
||||
#include "mailbox.h"
|
||||
|
||||
#define HSP_INT_IE(x) (0x100 + ((x) * 4))
|
||||
#define HSP_INT_IV 0x300
|
||||
#define HSP_INT_IR 0x304
|
||||
@@ -497,7 +495,7 @@ static int tegra_hsp_mailbox_flush(struct mbox_chan *chan,
|
||||
mbox_chan_txdone(chan, 0);
|
||||
|
||||
/* Wait until channel is empty */
|
||||
if (chan->active_req != NULL)
|
||||
if (chan->active_req != MBOX_NO_MSG)
|
||||
continue;
|
||||
|
||||
return 0;
|
||||
@@ -516,7 +514,7 @@ static int tegra_hsp_mailbox_startup(struct mbox_chan *chan)
|
||||
struct tegra_hsp *hsp = mb->channel.hsp;
|
||||
unsigned long flags;
|
||||
|
||||
chan->txdone_method = TXDONE_BY_IRQ;
|
||||
chan->txdone_method = MBOX_TXDONE_BY_IRQ;
|
||||
|
||||
/*
|
||||
* Shared mailboxes start out as consumers by default. FULL and EMPTY
|
||||
|
||||
@@ -490,12 +490,20 @@ static int rebalance_children(struct shadow_spine *s,
|
||||
|
||||
if (le32_to_cpu(n->header.nr_entries) == 1) {
|
||||
struct dm_block *child;
|
||||
int is_shared;
|
||||
dm_block_t b = value64(n, 0);
|
||||
|
||||
r = dm_tm_block_is_shared(info->tm, b, &is_shared);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = dm_tm_read_lock(info->tm, b, &btree_node_validator, &child);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (is_shared)
|
||||
inc_children(info->tm, dm_block_data(child), vt);
|
||||
|
||||
memcpy(n, dm_block_data(child),
|
||||
dm_bm_block_size(dm_tm_get_bm(info->tm)));
|
||||
|
||||
|
||||
@@ -529,6 +529,9 @@ static int bareudp_fill_metadata_dst(struct net_device *dev,
|
||||
struct in6_addr saddr;
|
||||
struct socket *sock = rcu_dereference(bareudp->sock);
|
||||
|
||||
if (!sock)
|
||||
return -ESHUTDOWN;
|
||||
|
||||
dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sock,
|
||||
0, &saddr, &info->key,
|
||||
sport, bareudp->port, info->key.tos,
|
||||
|
||||
@@ -1029,6 +1029,7 @@ static void ad_cond_set_peer_notif(struct port *port)
|
||||
static void ad_mux_machine(struct port *port, bool *update_slave_arr)
|
||||
{
|
||||
struct bonding *bond = __get_bond_by_port(port);
|
||||
struct aggregator *aggregator;
|
||||
mux_states_t last_state;
|
||||
|
||||
/* keep current State Machine state to compare later if it was
|
||||
@@ -1036,6 +1037,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr)
|
||||
*/
|
||||
last_state = port->sm_mux_state;
|
||||
|
||||
aggregator = rcu_dereference(port->aggregator);
|
||||
if (port->sm_vars & AD_PORT_BEGIN) {
|
||||
port->sm_mux_state = AD_MUX_DETACHED;
|
||||
} else {
|
||||
@@ -1055,7 +1057,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr)
|
||||
* cycle to update ready variable, we check
|
||||
* READY_N and update READY here
|
||||
*/
|
||||
__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
|
||||
__set_agg_ports_ready(aggregator, __agg_ports_are_ready(aggregator));
|
||||
port->sm_mux_state = AD_MUX_DETACHED;
|
||||
break;
|
||||
}
|
||||
@@ -1070,7 +1072,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr)
|
||||
* update ready variable, we check READY_N and update
|
||||
* READY here
|
||||
*/
|
||||
__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
|
||||
__set_agg_ports_ready(aggregator, __agg_ports_are_ready(aggregator));
|
||||
|
||||
/* if the wait_while_timer expired, and the port is
|
||||
* in READY state, move to ATTACHED state
|
||||
@@ -1086,7 +1088,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr)
|
||||
if ((port->sm_vars & AD_PORT_SELECTED) &&
|
||||
(port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) &&
|
||||
!__check_agg_selection_timer(port)) {
|
||||
if (port->aggregator->is_active) {
|
||||
if (aggregator->is_active) {
|
||||
int state = AD_MUX_COLLECTING_DISTRIBUTING;
|
||||
|
||||
if (!bond->params.coupled_control)
|
||||
@@ -1102,9 +1104,9 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr)
|
||||
* cycle to update ready variable, we check
|
||||
* READY_N and update READY here
|
||||
*/
|
||||
__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
|
||||
__set_agg_ports_ready(aggregator, __agg_ports_are_ready(aggregator));
|
||||
port->sm_mux_state = AD_MUX_DETACHED;
|
||||
} else if (port->aggregator->is_active) {
|
||||
} else if (aggregator->is_active) {
|
||||
port->actor_oper_port_state |=
|
||||
LACP_STATE_SYNCHRONIZATION;
|
||||
}
|
||||
@@ -1115,7 +1117,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr)
|
||||
* sure that a collecting distributing
|
||||
* port in an active aggregator is enabled
|
||||
*/
|
||||
if (port->aggregator->is_active &&
|
||||
if (aggregator->is_active &&
|
||||
!__port_is_collecting_distributing(port)) {
|
||||
__enable_port(port);
|
||||
*update_slave_arr = true;
|
||||
@@ -1134,7 +1136,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr)
|
||||
*/
|
||||
struct slave *slave = port->slave;
|
||||
|
||||
if (port->aggregator->is_active &&
|
||||
if (aggregator->is_active &&
|
||||
bond_is_slave_rx_disabled(slave)) {
|
||||
ad_enable_collecting(port);
|
||||
*update_slave_arr = true;
|
||||
@@ -1154,8 +1156,8 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr)
|
||||
* sure that a collecting distributing
|
||||
* port in an active aggregator is enabled
|
||||
*/
|
||||
if (port->aggregator &&
|
||||
port->aggregator->is_active &&
|
||||
if (aggregator &&
|
||||
aggregator->is_active &&
|
||||
!__port_is_collecting_distributing(port)) {
|
||||
__enable_port(port);
|
||||
*update_slave_arr = true;
|
||||
@@ -1187,7 +1189,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr)
|
||||
port->sm_mux_timer_counter = __ad_timer_to_ticks(AD_WAIT_WHILE_TIMER, 0);
|
||||
break;
|
||||
case AD_MUX_ATTACHED:
|
||||
if (port->aggregator->is_active)
|
||||
if (aggregator->is_active)
|
||||
port->actor_oper_port_state |=
|
||||
LACP_STATE_SYNCHRONIZATION;
|
||||
else
|
||||
@@ -1561,9 +1563,9 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr)
|
||||
bond = __get_bond_by_port(port);
|
||||
|
||||
/* if the port is connected to other aggregator, detach it */
|
||||
if (port->aggregator) {
|
||||
temp_aggregator = rcu_dereference(port->aggregator);
|
||||
if (temp_aggregator) {
|
||||
/* detach the port from its former aggregator */
|
||||
temp_aggregator = port->aggregator;
|
||||
for (curr_port = temp_aggregator->lag_ports; curr_port;
|
||||
last_port = curr_port,
|
||||
curr_port = curr_port->next_port_in_aggregator) {
|
||||
@@ -1586,7 +1588,7 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr)
|
||||
/* clear the port's relations to this
|
||||
* aggregator
|
||||
*/
|
||||
port->aggregator = NULL;
|
||||
RCU_INIT_POINTER(port->aggregator, NULL);
|
||||
port->next_port_in_aggregator = NULL;
|
||||
port->actor_port_aggregator_identifier = 0;
|
||||
|
||||
@@ -1609,7 +1611,7 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr)
|
||||
port->slave->bond->dev->name,
|
||||
port->slave->dev->name,
|
||||
port->actor_port_number,
|
||||
port->aggregator->aggregator_identifier);
|
||||
temp_aggregator->aggregator_identifier);
|
||||
}
|
||||
}
|
||||
/* search on all aggregators for a suitable aggregator for this port */
|
||||
@@ -1633,15 +1635,15 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr)
|
||||
)
|
||||
) {
|
||||
/* attach to the founded aggregator */
|
||||
port->aggregator = aggregator;
|
||||
rcu_assign_pointer(port->aggregator, aggregator);
|
||||
port->actor_port_aggregator_identifier =
|
||||
port->aggregator->aggregator_identifier;
|
||||
aggregator->aggregator_identifier;
|
||||
port->next_port_in_aggregator = aggregator->lag_ports;
|
||||
port->aggregator->num_of_ports++;
|
||||
aggregator->num_of_ports++;
|
||||
aggregator->lag_ports = port;
|
||||
slave_dbg(bond->dev, slave->dev, "Port %d joined LAG %d (existing LAG)\n",
|
||||
port->actor_port_number,
|
||||
port->aggregator->aggregator_identifier);
|
||||
aggregator->aggregator_identifier);
|
||||
|
||||
/* mark this port as selected */
|
||||
port->sm_vars |= AD_PORT_SELECTED;
|
||||
@@ -1656,39 +1658,40 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr)
|
||||
if (!found) {
|
||||
if (free_aggregator) {
|
||||
/* assign port a new aggregator */
|
||||
port->aggregator = free_aggregator;
|
||||
port->actor_port_aggregator_identifier =
|
||||
port->aggregator->aggregator_identifier;
|
||||
free_aggregator->aggregator_identifier;
|
||||
|
||||
/* update the new aggregator's parameters
|
||||
* if port was responsed from the end-user
|
||||
*/
|
||||
if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS)
|
||||
/* if port is full duplex */
|
||||
port->aggregator->is_individual = false;
|
||||
free_aggregator->is_individual = false;
|
||||
else
|
||||
port->aggregator->is_individual = true;
|
||||
free_aggregator->is_individual = true;
|
||||
|
||||
port->aggregator->actor_admin_aggregator_key =
|
||||
free_aggregator->actor_admin_aggregator_key =
|
||||
port->actor_admin_port_key;
|
||||
port->aggregator->actor_oper_aggregator_key =
|
||||
free_aggregator->actor_oper_aggregator_key =
|
||||
port->actor_oper_port_key;
|
||||
port->aggregator->partner_system =
|
||||
free_aggregator->partner_system =
|
||||
port->partner_oper.system;
|
||||
port->aggregator->partner_system_priority =
|
||||
free_aggregator->partner_system_priority =
|
||||
port->partner_oper.system_priority;
|
||||
port->aggregator->partner_oper_aggregator_key = port->partner_oper.key;
|
||||
port->aggregator->receive_state = 1;
|
||||
port->aggregator->transmit_state = 1;
|
||||
port->aggregator->lag_ports = port;
|
||||
port->aggregator->num_of_ports++;
|
||||
free_aggregator->partner_oper_aggregator_key = port->partner_oper.key;
|
||||
free_aggregator->receive_state = 1;
|
||||
free_aggregator->transmit_state = 1;
|
||||
free_aggregator->lag_ports = port;
|
||||
free_aggregator->num_of_ports++;
|
||||
|
||||
rcu_assign_pointer(port->aggregator, free_aggregator);
|
||||
|
||||
/* mark this port as selected */
|
||||
port->sm_vars |= AD_PORT_SELECTED;
|
||||
|
||||
slave_dbg(bond->dev, port->slave->dev, "Port %d joined LAG %d (new LAG)\n",
|
||||
port->actor_port_number,
|
||||
port->aggregator->aggregator_identifier);
|
||||
free_aggregator->aggregator_identifier);
|
||||
} else {
|
||||
slave_err(bond->dev, port->slave->dev,
|
||||
"Port %d did not find a suitable aggregator\n",
|
||||
@@ -1700,13 +1703,12 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr)
|
||||
* in all aggregator's ports, else set ready=FALSE in all
|
||||
* aggregator's ports
|
||||
*/
|
||||
__set_agg_ports_ready(port->aggregator,
|
||||
__agg_ports_are_ready(port->aggregator));
|
||||
aggregator = rcu_dereference(port->aggregator);
|
||||
__set_agg_ports_ready(aggregator, __agg_ports_are_ready(aggregator));
|
||||
|
||||
aggregator = __get_first_agg(port);
|
||||
ad_agg_selection_logic(aggregator, update_slave_arr);
|
||||
ad_agg_selection_logic(__get_first_agg(port), update_slave_arr);
|
||||
|
||||
if (!port->aggregator->is_active)
|
||||
if (!aggregator->is_active)
|
||||
port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION;
|
||||
}
|
||||
|
||||
@@ -2075,13 +2077,15 @@ static void ad_initialize_port(struct port *port, const struct bond_params *bond
|
||||
*/
|
||||
static void ad_enable_collecting(struct port *port)
|
||||
{
|
||||
if (port->aggregator->is_active) {
|
||||
struct aggregator *aggregator = rcu_dereference(port->aggregator);
|
||||
|
||||
if (aggregator->is_active) {
|
||||
struct slave *slave = port->slave;
|
||||
|
||||
slave_dbg(slave->bond->dev, slave->dev,
|
||||
"Enabling collecting on port %d (LAG %d)\n",
|
||||
port->actor_port_number,
|
||||
port->aggregator->aggregator_identifier);
|
||||
aggregator->aggregator_identifier);
|
||||
__enable_collecting_port(port);
|
||||
}
|
||||
}
|
||||
@@ -2093,11 +2097,13 @@ static void ad_enable_collecting(struct port *port)
|
||||
*/
|
||||
static void ad_disable_distributing(struct port *port, bool *update_slave_arr)
|
||||
{
|
||||
if (port->aggregator && __agg_has_partner(port->aggregator)) {
|
||||
struct aggregator *aggregator = rcu_dereference(port->aggregator);
|
||||
|
||||
if (aggregator && __agg_has_partner(aggregator)) {
|
||||
slave_dbg(port->slave->bond->dev, port->slave->dev,
|
||||
"Disabling distributing on port %d (LAG %d)\n",
|
||||
port->actor_port_number,
|
||||
port->aggregator->aggregator_identifier);
|
||||
aggregator->aggregator_identifier);
|
||||
__disable_distributing_port(port);
|
||||
/* Slave array needs an update */
|
||||
*update_slave_arr = true;
|
||||
@@ -2114,11 +2120,13 @@ static void ad_disable_distributing(struct port *port, bool *update_slave_arr)
|
||||
static void ad_enable_collecting_distributing(struct port *port,
|
||||
bool *update_slave_arr)
|
||||
{
|
||||
if (port->aggregator->is_active) {
|
||||
struct aggregator *aggregator = rcu_dereference(port->aggregator);
|
||||
|
||||
if (aggregator->is_active) {
|
||||
slave_dbg(port->slave->bond->dev, port->slave->dev,
|
||||
"Enabling port %d (LAG %d)\n",
|
||||
port->actor_port_number,
|
||||
port->aggregator->aggregator_identifier);
|
||||
aggregator->aggregator_identifier);
|
||||
__enable_port(port);
|
||||
/* Slave array needs update */
|
||||
*update_slave_arr = true;
|
||||
@@ -2135,11 +2143,13 @@ static void ad_enable_collecting_distributing(struct port *port,
|
||||
static void ad_disable_collecting_distributing(struct port *port,
|
||||
bool *update_slave_arr)
|
||||
{
|
||||
if (port->aggregator && __agg_has_partner(port->aggregator)) {
|
||||
struct aggregator *aggregator = rcu_dereference(port->aggregator);
|
||||
|
||||
if (aggregator && __agg_has_partner(aggregator)) {
|
||||
slave_dbg(port->slave->bond->dev, port->slave->dev,
|
||||
"Disabling port %d (LAG %d)\n",
|
||||
port->actor_port_number,
|
||||
port->aggregator->aggregator_identifier);
|
||||
aggregator->aggregator_identifier);
|
||||
__disable_port(port);
|
||||
/* Slave array needs an update */
|
||||
*update_slave_arr = true;
|
||||
@@ -2379,7 +2389,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
|
||||
*/
|
||||
for (temp_port = aggregator->lag_ports; temp_port;
|
||||
temp_port = temp_port->next_port_in_aggregator) {
|
||||
temp_port->aggregator = new_aggregator;
|
||||
rcu_assign_pointer(temp_port->aggregator, new_aggregator);
|
||||
temp_port->actor_port_aggregator_identifier = new_aggregator->aggregator_identifier;
|
||||
}
|
||||
|
||||
@@ -2848,15 +2858,16 @@ int bond_3ad_set_carrier(struct bonding *bond)
|
||||
int __bond_3ad_get_active_agg_info(struct bonding *bond,
|
||||
struct ad_info *ad_info)
|
||||
{
|
||||
struct aggregator *aggregator = NULL;
|
||||
struct aggregator *aggregator = NULL, *tmp;
|
||||
struct list_head *iter;
|
||||
struct slave *slave;
|
||||
struct port *port;
|
||||
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
port = &(SLAVE_AD_INFO(slave)->port);
|
||||
if (port->aggregator && port->aggregator->is_active) {
|
||||
aggregator = port->aggregator;
|
||||
tmp = rcu_dereference(port->aggregator);
|
||||
if (tmp && tmp->is_active) {
|
||||
aggregator = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1433,7 +1433,7 @@ static void bond_poll_controller(struct net_device *bond_dev)
|
||||
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
struct aggregator *agg =
|
||||
SLAVE_AD_INFO(slave)->port.aggregator;
|
||||
rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator);
|
||||
|
||||
if (agg &&
|
||||
agg->aggregator_identifier != ad_info.aggregator_id)
|
||||
@@ -5179,15 +5179,16 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave)
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
agg_id = ad_info.aggregator_id;
|
||||
}
|
||||
rcu_read_lock();
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
if (skipslave == slave)
|
||||
continue;
|
||||
|
||||
all_slaves->arr[all_slaves->count++] = slave;
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
struct aggregator *agg;
|
||||
const struct aggregator *agg;
|
||||
|
||||
agg = SLAVE_AD_INFO(slave)->port.aggregator;
|
||||
agg = rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator);
|
||||
if (!agg || agg->aggregator_identifier != agg_id)
|
||||
continue;
|
||||
}
|
||||
@@ -5199,6 +5200,7 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave)
|
||||
|
||||
usable_slaves->arr[usable_slaves->count++] = slave;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
bond_set_slave_arr(bond, usable_slaves, all_slaves);
|
||||
return ret;
|
||||
|
||||
@@ -66,27 +66,29 @@ static int bond_fill_slave_info(struct sk_buff *skb,
|
||||
const struct port *ad_port;
|
||||
|
||||
ad_port = &SLAVE_AD_INFO(slave)->port;
|
||||
agg = SLAVE_AD_INFO(slave)->port.aggregator;
|
||||
rcu_read_lock();
|
||||
agg = rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator);
|
||||
if (agg) {
|
||||
if (nla_put_u16(skb, IFLA_BOND_SLAVE_AD_AGGREGATOR_ID,
|
||||
agg->aggregator_identifier))
|
||||
goto nla_put_failure;
|
||||
goto nla_put_failure_rcu;
|
||||
if (nla_put_u8(skb,
|
||||
IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE,
|
||||
ad_port->actor_oper_port_state))
|
||||
goto nla_put_failure;
|
||||
goto nla_put_failure_rcu;
|
||||
if (nla_put_u16(skb,
|
||||
IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE,
|
||||
ad_port->partner_oper.port_state))
|
||||
goto nla_put_failure;
|
||||
goto nla_put_failure_rcu;
|
||||
|
||||
if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_ACTOR_STATE,
|
||||
ad_port->sm_churn_actor_state))
|
||||
goto nla_put_failure;
|
||||
goto nla_put_failure_rcu;
|
||||
if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_PARTNER_STATE,
|
||||
ad_port->sm_churn_partner_state))
|
||||
goto nla_put_failure;
|
||||
goto nla_put_failure_rcu;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (nla_put_u16(skb, IFLA_BOND_SLAVE_ACTOR_PORT_PRIO,
|
||||
SLAVE_AD_INFO(slave)->port_priority))
|
||||
@@ -95,6 +97,8 @@ static int bond_fill_slave_info(struct sk_buff *skb,
|
||||
|
||||
return 0;
|
||||
|
||||
nla_put_failure_rcu:
|
||||
rcu_read_unlock();
|
||||
nla_put_failure:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
@@ -188,6 +188,7 @@ static void bond_info_show_master(struct seq_file *seq)
|
||||
}
|
||||
}
|
||||
|
||||
/* Note: runs under rcu_read_lock() */
|
||||
static void bond_info_show_slave(struct seq_file *seq,
|
||||
const struct slave *slave)
|
||||
{
|
||||
@@ -214,7 +215,7 @@ static void bond_info_show_slave(struct seq_file *seq,
|
||||
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
const struct port *port = &SLAVE_AD_INFO(slave)->port;
|
||||
const struct aggregator *agg = port->aggregator;
|
||||
const struct aggregator *agg = rcu_dereference(port->aggregator);
|
||||
|
||||
if (agg) {
|
||||
seq_printf(seq, "Aggregator ID: %d\n",
|
||||
|
||||
@@ -62,10 +62,15 @@ static ssize_t ad_aggregator_id_show(struct slave *slave, char *buf)
|
||||
const struct aggregator *agg;
|
||||
|
||||
if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) {
|
||||
agg = SLAVE_AD_INFO(slave)->port.aggregator;
|
||||
if (agg)
|
||||
return sysfs_emit(buf, "%d\n",
|
||||
agg->aggregator_identifier);
|
||||
rcu_read_lock();
|
||||
agg = rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator);
|
||||
if (agg) {
|
||||
ssize_t res = sysfs_emit(buf, "%d\n",
|
||||
agg->aggregator_identifier);
|
||||
rcu_read_unlock();
|
||||
return res;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
return sysfs_emit(buf, "N/A\n");
|
||||
@@ -78,7 +83,7 @@ static ssize_t ad_actor_oper_port_state_show(struct slave *slave, char *buf)
|
||||
|
||||
if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) {
|
||||
ad_port = &SLAVE_AD_INFO(slave)->port;
|
||||
if (ad_port->aggregator)
|
||||
if (rcu_access_pointer(ad_port->aggregator))
|
||||
return sysfs_emit(buf, "%u\n",
|
||||
ad_port->actor_oper_port_state);
|
||||
}
|
||||
@@ -93,7 +98,7 @@ static ssize_t ad_partner_oper_port_state_show(struct slave *slave, char *buf)
|
||||
|
||||
if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) {
|
||||
ad_port = &SLAVE_AD_INFO(slave)->port;
|
||||
if (ad_port->aggregator)
|
||||
if (rcu_access_pointer(ad_port->aggregator))
|
||||
return sysfs_emit(buf, "%u\n",
|
||||
ad_port->partner_oper.port_state);
|
||||
}
|
||||
|
||||
@@ -847,13 +847,24 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
|
||||
{
|
||||
struct airoha_qdma *qdma = q->qdma;
|
||||
struct airoha_eth *eth = qdma->eth;
|
||||
int i;
|
||||
int i, qid = q - &qdma->q_tx[0];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
|
||||
struct airoha_gdm_port *port = eth->ports[i];
|
||||
int j;
|
||||
|
||||
if (port && port->qdma == qdma)
|
||||
netif_tx_wake_all_queues(port->dev);
|
||||
if (!port)
|
||||
continue;
|
||||
|
||||
if (port->qdma != qdma)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < port->dev->num_tx_queues; j++) {
|
||||
if (airoha_qdma_get_txq(qdma, j) != qid)
|
||||
continue;
|
||||
|
||||
netif_wake_subqueue(port->dev, j);
|
||||
}
|
||||
}
|
||||
q->txq_stopped = false;
|
||||
}
|
||||
@@ -929,10 +940,9 @@ static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget)
|
||||
q->queued--;
|
||||
|
||||
if (skb) {
|
||||
u16 queue = skb_get_queue_mapping(skb);
|
||||
struct netdev_queue *txq;
|
||||
|
||||
txq = netdev_get_tx_queue(skb->dev, queue);
|
||||
txq = skb_get_tx_queue(skb->dev, skb);
|
||||
netdev_tx_completed_queue(txq, 1, skb->len);
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
@@ -1737,14 +1747,11 @@ static int airoha_dev_stop(struct net_device *dev)
|
||||
{
|
||||
struct airoha_gdm_port *port = netdev_priv(dev);
|
||||
struct airoha_qdma *qdma = port->qdma;
|
||||
int i, err;
|
||||
int i;
|
||||
|
||||
netif_tx_disable(dev);
|
||||
err = airoha_set_vip_for_gdm_port(port, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++)
|
||||
airoha_set_vip_for_gdm_port(port, false);
|
||||
for (i = 0; i < dev->num_tx_queues; i++)
|
||||
netdev_tx_reset_subqueue(dev, i);
|
||||
|
||||
airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
|
||||
@@ -1997,12 +2004,12 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
|
||||
struct netdev_queue *txq;
|
||||
struct airoha_queue *q;
|
||||
LIST_HEAD(tx_list);
|
||||
int i = 0, qid;
|
||||
void *data;
|
||||
int i, qid;
|
||||
u16 index;
|
||||
u8 fport;
|
||||
|
||||
qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx);
|
||||
qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb));
|
||||
tag = airoha_get_dsa_tag(skb, dev);
|
||||
|
||||
msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK,
|
||||
@@ -2039,7 +2046,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
|
||||
|
||||
spin_lock_bh(&q->lock);
|
||||
|
||||
txq = netdev_get_tx_queue(dev, qid);
|
||||
txq = skb_get_tx_queue(dev, skb);
|
||||
nr_frags = 1 + skb_shinfo(skb)->nr_frags;
|
||||
|
||||
if (q->queued + nr_frags >= q->ndesc) {
|
||||
@@ -2057,7 +2064,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
|
||||
list);
|
||||
index = e - q->entry;
|
||||
|
||||
for (i = 0; i < nr_frags; i++) {
|
||||
while (true) {
|
||||
struct airoha_qdma_desc *desc = &q->desc[index];
|
||||
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
|
||||
dma_addr_t addr;
|
||||
@@ -2069,7 +2076,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
|
||||
goto error_unmap;
|
||||
|
||||
list_move_tail(&e->list, &tx_list);
|
||||
e->skb = i ? NULL : skb;
|
||||
e->skb = i == nr_frags - 1 ? skb : NULL;
|
||||
e->dma_addr = addr;
|
||||
e->dma_len = len;
|
||||
|
||||
@@ -2088,6 +2095,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
|
||||
WRITE_ONCE(desc->msg1, cpu_to_le32(msg1));
|
||||
WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff));
|
||||
|
||||
if (++i == nr_frags)
|
||||
break;
|
||||
|
||||
data = skb_frag_address(frag);
|
||||
len = skb_frag_size(frag);
|
||||
}
|
||||
@@ -2095,17 +2105,16 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
|
||||
|
||||
skb_tx_timestamp(skb);
|
||||
netdev_tx_sent_queue(txq, skb->len);
|
||||
if (q->ndesc - q->queued < q->free_thr) {
|
||||
netif_tx_stop_queue(txq);
|
||||
q->txq_stopped = true;
|
||||
}
|
||||
|
||||
if (netif_xmit_stopped(txq) || !netdev_xmit_more())
|
||||
airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid),
|
||||
TX_RING_CPU_IDX_MASK,
|
||||
FIELD_PREP(TX_RING_CPU_IDX_MASK, index));
|
||||
|
||||
if (q->ndesc - q->queued < q->free_thr) {
|
||||
netif_tx_stop_queue(txq);
|
||||
q->txq_stopped = true;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&q->lock);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
@@ -631,6 +631,11 @@ u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val);
|
||||
#define airoha_qdma_clear(qdma, offset, val) \
|
||||
airoha_rmw((qdma)->regs, (offset), (val), 0)
|
||||
|
||||
static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid)
|
||||
{
|
||||
return qid % ARRAY_SIZE(qdma->q_tx);
|
||||
}
|
||||
|
||||
static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port)
|
||||
{
|
||||
/* GDM1 port on EN7581 SoC is connected to the lan dsa switch.
|
||||
|
||||
@@ -1756,6 +1756,27 @@ static int ibmveth_set_mac_addr(struct net_device *dev, void *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static netdev_features_t ibmveth_features_check(struct sk_buff *skb,
|
||||
struct net_device *dev,
|
||||
netdev_features_t features)
|
||||
{
|
||||
/* Some physical adapters do not support segmentation offload with
|
||||
* MSS < 224. Disable GSO for such packets to avoid adapter freeze.
|
||||
* Note: Single-segment packets (gso_segs == 1) don't need this check
|
||||
* as they bypass the LSO path and are transmitted without segmentation.
|
||||
*/
|
||||
if (skb_is_gso(skb)) {
|
||||
if (skb_shinfo(skb)->gso_size < IBMVETH_MIN_LSO_MSS) {
|
||||
netdev_warn_once(dev,
|
||||
"MSS %u too small for LSO, disabling GSO\n",
|
||||
skb_shinfo(skb)->gso_size);
|
||||
features &= ~NETIF_F_GSO_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
return vlan_features_check(skb, features);
|
||||
}
|
||||
|
||||
static const struct net_device_ops ibmveth_netdev_ops = {
|
||||
.ndo_open = ibmveth_open,
|
||||
.ndo_stop = ibmveth_close,
|
||||
@@ -1767,6 +1788,7 @@ static const struct net_device_ops ibmveth_netdev_ops = {
|
||||
.ndo_set_features = ibmveth_set_features,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
.ndo_set_mac_address = ibmveth_set_mac_addr,
|
||||
.ndo_features_check = ibmveth_features_check,
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
.ndo_poll_controller = ibmveth_poll_controller,
|
||||
#endif
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#define IBMVETH_ILLAN_IPV4_TCP_CSUM 0x0000000000000002UL
|
||||
#define IBMVETH_ILLAN_ACTIVE_TRUNK 0x0000000000000001UL
|
||||
|
||||
#define IBMVETH_MIN_LSO_MSS 224 /* Minimum MSS for LSO */
|
||||
/* hcall macros */
|
||||
#define h_register_logical_lan(ua, buflst, rxq, fltlst, mac) \
|
||||
plpar_hcall_norets(H_REGISTER_LOGICAL_LAN, ua, buflst, rxq, fltlst, mac)
|
||||
|
||||
@@ -158,11 +158,10 @@ struct iavf_vlan {
|
||||
enum iavf_vlan_state_t {
|
||||
IAVF_VLAN_INVALID,
|
||||
IAVF_VLAN_ADD, /* filter needs to be added */
|
||||
IAVF_VLAN_IS_NEW, /* filter is new, wait for PF answer */
|
||||
IAVF_VLAN_ACTIVE, /* filter is accepted by PF */
|
||||
IAVF_VLAN_DISABLE, /* filter needs to be deleted by PF, then marked INACTIVE */
|
||||
IAVF_VLAN_INACTIVE, /* filter is inactive, we are in IFF_DOWN */
|
||||
IAVF_VLAN_REMOVE, /* filter needs to be removed from list */
|
||||
IAVF_VLAN_ADDING, /* ADD sent to PF, waiting for response */
|
||||
IAVF_VLAN_ACTIVE, /* PF confirmed, filter is in HW */
|
||||
IAVF_VLAN_REMOVE, /* filter queued for DEL from PF */
|
||||
IAVF_VLAN_REMOVING, /* DEL sent to PF, waiting for response */
|
||||
};
|
||||
|
||||
struct iavf_vlan_filter {
|
||||
|
||||
@@ -757,10 +757,10 @@ iavf_vlan_filter *iavf_add_vlan(struct iavf_adapter *adapter,
|
||||
adapter->num_vlan_filters++;
|
||||
iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_ADD_VLAN_FILTER);
|
||||
} else if (f->state == IAVF_VLAN_REMOVE) {
|
||||
/* Re-add the filter since we cannot tell whether the
|
||||
* pending delete has already been processed by the PF.
|
||||
* A duplicate add is harmless.
|
||||
*/
|
||||
/* DEL not yet sent to PF, cancel it */
|
||||
f->state = IAVF_VLAN_ACTIVE;
|
||||
} else if (f->state == IAVF_VLAN_REMOVING) {
|
||||
/* DEL already sent to PF, re-add after completion */
|
||||
f->state = IAVF_VLAN_ADD;
|
||||
iavf_schedule_aq_request(adapter,
|
||||
IAVF_FLAG_AQ_ADD_VLAN_FILTER);
|
||||
@@ -791,37 +791,19 @@ static void iavf_del_vlan(struct iavf_adapter *adapter, struct iavf_vlan vlan)
|
||||
list_del(&f->list);
|
||||
kfree(f);
|
||||
adapter->num_vlan_filters--;
|
||||
} else {
|
||||
} else if (f->state != IAVF_VLAN_REMOVING) {
|
||||
f->state = IAVF_VLAN_REMOVE;
|
||||
iavf_schedule_aq_request(adapter,
|
||||
IAVF_FLAG_AQ_DEL_VLAN_FILTER);
|
||||
}
|
||||
/* If REMOVING, DEL is already sent to PF; completion
|
||||
* handler will free the filter when PF confirms.
|
||||
*/
|
||||
}
|
||||
|
||||
spin_unlock_bh(&adapter->mac_vlan_list_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* iavf_restore_filters
|
||||
* @adapter: board private structure
|
||||
*
|
||||
* Restore existing non MAC filters when VF netdev comes back up
|
||||
**/
|
||||
static void iavf_restore_filters(struct iavf_adapter *adapter)
|
||||
{
|
||||
struct iavf_vlan_filter *f;
|
||||
|
||||
/* re-add all VLAN filters */
|
||||
spin_lock_bh(&adapter->mac_vlan_list_lock);
|
||||
|
||||
list_for_each_entry(f, &adapter->vlan_filter_list, list) {
|
||||
if (f->state == IAVF_VLAN_INACTIVE)
|
||||
f->state = IAVF_VLAN_ADD;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&adapter->mac_vlan_list_lock);
|
||||
adapter->aq_required |= IAVF_FLAG_AQ_ADD_VLAN_FILTER;
|
||||
}
|
||||
|
||||
/**
|
||||
* iavf_get_num_vlans_added - get number of VLANs added
|
||||
@@ -1246,13 +1228,12 @@ static void iavf_up_complete(struct iavf_adapter *adapter)
|
||||
}
|
||||
|
||||
/**
|
||||
* iavf_clear_mac_vlan_filters - Remove mac and vlan filters not sent to PF
|
||||
* yet and mark other to be removed.
|
||||
* iavf_clear_mac_filters - Remove MAC filters not sent to PF yet and mark
|
||||
* others to be removed.
|
||||
* @adapter: board private structure
|
||||
**/
|
||||
static void iavf_clear_mac_vlan_filters(struct iavf_adapter *adapter)
|
||||
static void iavf_clear_mac_filters(struct iavf_adapter *adapter)
|
||||
{
|
||||
struct iavf_vlan_filter *vlf, *vlftmp;
|
||||
struct iavf_mac_filter *f, *ftmp;
|
||||
|
||||
spin_lock_bh(&adapter->mac_vlan_list_lock);
|
||||
@@ -1271,11 +1252,6 @@ static void iavf_clear_mac_vlan_filters(struct iavf_adapter *adapter)
|
||||
}
|
||||
}
|
||||
|
||||
/* disable all VLAN filters */
|
||||
list_for_each_entry_safe(vlf, vlftmp, &adapter->vlan_filter_list,
|
||||
list)
|
||||
vlf->state = IAVF_VLAN_DISABLE;
|
||||
|
||||
spin_unlock_bh(&adapter->mac_vlan_list_lock);
|
||||
}
|
||||
|
||||
@@ -1371,7 +1347,7 @@ void iavf_down(struct iavf_adapter *adapter)
|
||||
iavf_napi_disable_all(adapter);
|
||||
iavf_irq_disable(adapter);
|
||||
|
||||
iavf_clear_mac_vlan_filters(adapter);
|
||||
iavf_clear_mac_filters(adapter);
|
||||
iavf_clear_cloud_filters(adapter);
|
||||
iavf_clear_fdir_filters(adapter);
|
||||
iavf_clear_adv_rss_conf(adapter);
|
||||
@@ -1388,8 +1364,6 @@ void iavf_down(struct iavf_adapter *adapter)
|
||||
*/
|
||||
if (!list_empty(&adapter->mac_filter_list))
|
||||
adapter->aq_required |= IAVF_FLAG_AQ_DEL_MAC_FILTER;
|
||||
if (!list_empty(&adapter->vlan_filter_list))
|
||||
adapter->aq_required |= IAVF_FLAG_AQ_DEL_VLAN_FILTER;
|
||||
if (!list_empty(&adapter->cloud_filter_list))
|
||||
adapter->aq_required |= IAVF_FLAG_AQ_DEL_CLOUD_FILTER;
|
||||
if (!list_empty(&adapter->fdir_list_head))
|
||||
@@ -4494,8 +4468,6 @@ static int iavf_open(struct net_device *netdev)
|
||||
iavf_add_filter(adapter, adapter->hw.mac.addr);
|
||||
spin_unlock_bh(&adapter->mac_vlan_list_lock);
|
||||
|
||||
/* Restore filters that were removed with IFF_DOWN */
|
||||
iavf_restore_filters(adapter);
|
||||
iavf_restore_fdir_filters(adapter);
|
||||
|
||||
iavf_configure(adapter);
|
||||
|
||||
@@ -746,7 +746,7 @@ static void iavf_vlan_add_reject(struct iavf_adapter *adapter)
|
||||
|
||||
spin_lock_bh(&adapter->mac_vlan_list_lock);
|
||||
list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) {
|
||||
if (f->state == IAVF_VLAN_IS_NEW) {
|
||||
if (f->state == IAVF_VLAN_ADDING) {
|
||||
list_del(&f->list);
|
||||
kfree(f);
|
||||
adapter->num_vlan_filters--;
|
||||
@@ -812,7 +812,7 @@ void iavf_add_vlans(struct iavf_adapter *adapter)
|
||||
if (f->state == IAVF_VLAN_ADD) {
|
||||
vvfl->vlan_id[i] = f->vlan.vid;
|
||||
i++;
|
||||
f->state = IAVF_VLAN_IS_NEW;
|
||||
f->state = IAVF_VLAN_ADDING;
|
||||
if (i == count)
|
||||
break;
|
||||
}
|
||||
@@ -874,7 +874,7 @@ void iavf_add_vlans(struct iavf_adapter *adapter)
|
||||
vlan->tpid = f->vlan.tpid;
|
||||
|
||||
i++;
|
||||
f->state = IAVF_VLAN_IS_NEW;
|
||||
f->state = IAVF_VLAN_ADDING;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -911,22 +911,12 @@ void iavf_del_vlans(struct iavf_adapter *adapter)
|
||||
spin_lock_bh(&adapter->mac_vlan_list_lock);
|
||||
|
||||
list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) {
|
||||
/* since VLAN capabilities are not allowed, we dont want to send
|
||||
* a VLAN delete request because it will most likely fail and
|
||||
* create unnecessary errors/noise, so just free the VLAN
|
||||
* filters marked for removal to enable bailing out before
|
||||
* sending a virtchnl message
|
||||
*/
|
||||
if (f->state == IAVF_VLAN_REMOVE &&
|
||||
!VLAN_FILTERING_ALLOWED(adapter)) {
|
||||
list_del(&f->list);
|
||||
kfree(f);
|
||||
adapter->num_vlan_filters--;
|
||||
} else if (f->state == IAVF_VLAN_DISABLE &&
|
||||
!VLAN_FILTERING_ALLOWED(adapter)) {
|
||||
f->state = IAVF_VLAN_INACTIVE;
|
||||
} else if (f->state == IAVF_VLAN_REMOVE ||
|
||||
f->state == IAVF_VLAN_DISABLE) {
|
||||
} else if (f->state == IAVF_VLAN_REMOVE) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -958,18 +948,10 @@ void iavf_del_vlans(struct iavf_adapter *adapter)
|
||||
|
||||
vvfl->vsi_id = adapter->vsi_res->vsi_id;
|
||||
vvfl->num_elements = count;
|
||||
list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) {
|
||||
if (f->state == IAVF_VLAN_DISABLE) {
|
||||
list_for_each_entry(f, &adapter->vlan_filter_list, list) {
|
||||
if (f->state == IAVF_VLAN_REMOVE) {
|
||||
vvfl->vlan_id[i] = f->vlan.vid;
|
||||
f->state = IAVF_VLAN_INACTIVE;
|
||||
i++;
|
||||
if (i == count)
|
||||
break;
|
||||
} else if (f->state == IAVF_VLAN_REMOVE) {
|
||||
vvfl->vlan_id[i] = f->vlan.vid;
|
||||
list_del(&f->list);
|
||||
kfree(f);
|
||||
adapter->num_vlan_filters--;
|
||||
f->state = IAVF_VLAN_REMOVING;
|
||||
i++;
|
||||
if (i == count)
|
||||
break;
|
||||
@@ -1006,9 +988,8 @@ void iavf_del_vlans(struct iavf_adapter *adapter)
|
||||
|
||||
vvfl_v2->vport_id = adapter->vsi_res->vsi_id;
|
||||
vvfl_v2->num_elements = count;
|
||||
list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) {
|
||||
if (f->state == IAVF_VLAN_DISABLE ||
|
||||
f->state == IAVF_VLAN_REMOVE) {
|
||||
list_for_each_entry(f, &adapter->vlan_filter_list, list) {
|
||||
if (f->state == IAVF_VLAN_REMOVE) {
|
||||
struct virtchnl_vlan_supported_caps *filtering_support =
|
||||
&adapter->vlan_v2_caps.filtering.filtering_support;
|
||||
struct virtchnl_vlan *vlan;
|
||||
@@ -1022,13 +1003,7 @@ void iavf_del_vlans(struct iavf_adapter *adapter)
|
||||
vlan->tci = f->vlan.vid;
|
||||
vlan->tpid = f->vlan.tpid;
|
||||
|
||||
if (f->state == IAVF_VLAN_DISABLE) {
|
||||
f->state = IAVF_VLAN_INACTIVE;
|
||||
} else {
|
||||
list_del(&f->list);
|
||||
kfree(f);
|
||||
adapter->num_vlan_filters--;
|
||||
}
|
||||
f->state = IAVF_VLAN_REMOVING;
|
||||
i++;
|
||||
if (i == count)
|
||||
break;
|
||||
@@ -2391,10 +2366,6 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
|
||||
ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr);
|
||||
wake_up(&adapter->vc_waitqueue);
|
||||
break;
|
||||
case VIRTCHNL_OP_DEL_VLAN:
|
||||
dev_err(&adapter->pdev->dev, "Failed to delete VLAN filter, error %s\n",
|
||||
iavf_stat_str(&adapter->hw, v_retval));
|
||||
break;
|
||||
case VIRTCHNL_OP_DEL_ETH_ADDR:
|
||||
dev_err(&adapter->pdev->dev, "Failed to delete MAC filter, error %s\n",
|
||||
iavf_stat_str(&adapter->hw, v_retval));
|
||||
@@ -2905,17 +2876,42 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
|
||||
spin_unlock_bh(&adapter->adv_rss_lock);
|
||||
}
|
||||
break;
|
||||
case VIRTCHNL_OP_ADD_VLAN:
|
||||
case VIRTCHNL_OP_ADD_VLAN_V2: {
|
||||
struct iavf_vlan_filter *f;
|
||||
|
||||
if (v_retval)
|
||||
break;
|
||||
|
||||
spin_lock_bh(&adapter->mac_vlan_list_lock);
|
||||
list_for_each_entry(f, &adapter->vlan_filter_list, list) {
|
||||
if (f->state == IAVF_VLAN_IS_NEW)
|
||||
if (f->state == IAVF_VLAN_ADDING)
|
||||
f->state = IAVF_VLAN_ACTIVE;
|
||||
}
|
||||
spin_unlock_bh(&adapter->mac_vlan_list_lock);
|
||||
}
|
||||
break;
|
||||
case VIRTCHNL_OP_DEL_VLAN:
|
||||
case VIRTCHNL_OP_DEL_VLAN_V2: {
|
||||
struct iavf_vlan_filter *f, *ftmp;
|
||||
|
||||
spin_lock_bh(&adapter->mac_vlan_list_lock);
|
||||
list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list,
|
||||
list) {
|
||||
if (f->state == IAVF_VLAN_REMOVING) {
|
||||
if (v_retval) {
|
||||
/* PF rejected DEL, keep filter */
|
||||
f->state = IAVF_VLAN_ACTIVE;
|
||||
} else {
|
||||
list_del(&f->list);
|
||||
kfree(f);
|
||||
adapter->num_vlan_filters--;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&adapter->mac_vlan_list_lock);
|
||||
}
|
||||
break;
|
||||
case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
|
||||
/* PF enabled vlan strip on this VF.
|
||||
* Update netdev->features if needed to be in sync with ethtool.
|
||||
|
||||
@@ -1245,6 +1245,8 @@ static int ice_devlink_reinit_up(struct ice_pf *pf)
|
||||
return err;
|
||||
}
|
||||
|
||||
ice_init_dev_hw(pf);
|
||||
|
||||
/* load MSI-X values */
|
||||
ice_set_min_max_msix(pf);
|
||||
|
||||
|
||||
@@ -1126,8 +1126,6 @@ int ice_init_hw(struct ice_hw *hw)
|
||||
if (status)
|
||||
goto err_unroll_fltr_mgmt_struct;
|
||||
|
||||
ice_init_dev_hw(hw->back);
|
||||
|
||||
mutex_init(&hw->tnl_lock);
|
||||
ice_init_chk_recipe_reuse_support(hw);
|
||||
|
||||
|
||||
@@ -1154,6 +1154,32 @@ ice_dpll_input_state_get(const struct dpll_pin *pin, void *pin_priv,
|
||||
extack, ICE_DPLL_PIN_TYPE_INPUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_dpll_sw_pin_notify_peer - notify the paired SW pin after a state change
|
||||
* @d: pointer to dplls struct
|
||||
* @changed: the SW pin that was explicitly changed (already notified by dpll core)
|
||||
*
|
||||
* SMA and U.FL pins share physical signal paths in pairs (SMA1/U.FL1 and
|
||||
* SMA2/U.FL2). When one pin's routing changes via the PCA9575 GPIO
|
||||
* expander, the paired pin's state may also change. Send a change
|
||||
* notification for the peer pin so userspace consumers monitoring the
|
||||
* peer via dpll netlink learn about the update.
|
||||
*
|
||||
* Context: Called from dpll_pin_ops callbacks after pf->dplls.lock is
|
||||
* released. Uses __dpll_pin_change_ntf() because dpll_lock is
|
||||
* still held by the dpll netlink layer.
|
||||
*/
|
||||
static void ice_dpll_sw_pin_notify_peer(struct ice_dplls *d,
|
||||
struct ice_dpll_pin *changed)
|
||||
{
|
||||
struct ice_dpll_pin *peer;
|
||||
|
||||
peer = (changed >= d->sma && changed < d->sma + ICE_DPLL_PIN_SW_NUM) ?
|
||||
&d->ufl[changed->idx] : &d->sma[changed->idx];
|
||||
if (peer->pin)
|
||||
__dpll_pin_change_ntf(peer->pin);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_dpll_sma_direction_set - set direction of SMA pin
|
||||
* @p: pointer to a pin
|
||||
@@ -1171,6 +1197,8 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
|
||||
enum dpll_pin_direction direction,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct ice_dplls *d = &p->pf->dplls;
|
||||
struct ice_dpll_pin *peer;
|
||||
u8 data;
|
||||
int ret;
|
||||
|
||||
@@ -1189,8 +1217,9 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
|
||||
case ICE_DPLL_PIN_SW_2_IDX:
|
||||
if (direction == DPLL_PIN_DIRECTION_INPUT) {
|
||||
data &= ~ICE_SMA2_DIR_EN;
|
||||
data |= ICE_SMA2_UFL2_RX_DIS;
|
||||
} else {
|
||||
data &= ~ICE_SMA2_TX_EN;
|
||||
data &= ~(ICE_SMA2_TX_EN | ICE_SMA2_UFL2_RX_DIS);
|
||||
data |= ICE_SMA2_DIR_EN;
|
||||
}
|
||||
break;
|
||||
@@ -1202,6 +1231,34 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
|
||||
ret = ice_dpll_pin_state_update(p->pf, p,
|
||||
ICE_DPLL_PIN_TYPE_SOFTWARE,
|
||||
extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* When a direction change activates the paired U.FL pin, enable
|
||||
* its backing CGU pin so the pin reports as connected. Without
|
||||
* this the U.FL routing is correct but the CGU pin stays disabled
|
||||
* and userspace sees the pin as disconnected. Do not disable the
|
||||
* backing pin when U.FL becomes inactive because the SMA pin may
|
||||
* still be using it.
|
||||
*/
|
||||
peer = &d->ufl[p->idx];
|
||||
if (peer->active) {
|
||||
struct ice_dpll_pin *target;
|
||||
enum ice_dpll_pin_type type;
|
||||
|
||||
if (peer->output) {
|
||||
target = peer->output;
|
||||
type = ICE_DPLL_PIN_TYPE_OUTPUT;
|
||||
} else {
|
||||
target = peer->input;
|
||||
type = ICE_DPLL_PIN_TYPE_INPUT;
|
||||
}
|
||||
ret = ice_dpll_pin_enable(&p->pf->hw, target,
|
||||
d->eec.dpll_idx, type, extack);
|
||||
if (!ret)
|
||||
ret = ice_dpll_pin_state_update(p->pf, target,
|
||||
type, extack);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1253,6 +1310,14 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
|
||||
data &= ~ICE_SMA1_MASK;
|
||||
enable = true;
|
||||
} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
|
||||
/* Skip if U.FL1 is not active, setting TX_EN
|
||||
* while DIR_EN is set would also deactivate
|
||||
* the paired SMA1 output.
|
||||
*/
|
||||
if (data & (ICE_SMA1_DIR_EN | ICE_SMA1_TX_EN)) {
|
||||
ret = 0;
|
||||
goto unlock;
|
||||
}
|
||||
data |= ICE_SMA1_TX_EN;
|
||||
enable = false;
|
||||
} else {
|
||||
@@ -1267,6 +1332,15 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
|
||||
data &= ~ICE_SMA2_UFL2_RX_DIS;
|
||||
enable = true;
|
||||
} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
|
||||
/* Skip if U.FL2 is not active, setting
|
||||
* UFL2_RX_DIS could also disable the paired
|
||||
* SMA2 input.
|
||||
*/
|
||||
if (!(data & ICE_SMA2_DIR_EN) ||
|
||||
(data & ICE_SMA2_UFL2_RX_DIS)) {
|
||||
ret = 0;
|
||||
goto unlock;
|
||||
}
|
||||
data |= ICE_SMA2_UFL2_RX_DIS;
|
||||
enable = false;
|
||||
} else {
|
||||
@@ -1296,6 +1370,8 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&pf->dplls.lock);
|
||||
if (!ret)
|
||||
ice_dpll_sw_pin_notify_peer(&pf->dplls, p);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1414,6 +1490,8 @@ ice_dpll_sma_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&pf->dplls.lock);
|
||||
if (!ret)
|
||||
ice_dpll_sw_pin_notify_peer(&pf->dplls, sma);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1609,6 +1687,8 @@ ice_dpll_pin_sma_direction_set(const struct dpll_pin *pin, void *pin_priv,
|
||||
mutex_lock(&pf->dplls.lock);
|
||||
ret = ice_dpll_sma_direction_set(p, direction, extack);
|
||||
mutex_unlock(&pf->dplls.lock);
|
||||
if (!ret)
|
||||
ice_dpll_sw_pin_notify_peer(&pf->dplls, p);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1915,7 +1995,10 @@ ice_dpll_phase_offset_get(const struct dpll_pin *pin, void *pin_priv,
|
||||
d->active_input == p->input->pin))
|
||||
*phase_offset = d->phase_offset * ICE_DPLL_PHASE_OFFSET_FACTOR;
|
||||
else if (d->phase_offset_monitor_period)
|
||||
*phase_offset = p->phase_offset * ICE_DPLL_PHASE_OFFSET_FACTOR;
|
||||
*phase_offset = (p->input &&
|
||||
p->direction == DPLL_PIN_DIRECTION_INPUT ?
|
||||
p->input->phase_offset :
|
||||
p->phase_offset) * ICE_DPLL_PHASE_OFFSET_FACTOR;
|
||||
else
|
||||
*phase_offset = 0;
|
||||
mutex_unlock(&pf->dplls.lock);
|
||||
@@ -2609,6 +2692,27 @@ static u64 ice_generate_clock_id(struct ice_pf *pf)
|
||||
return pci_get_dsn(pf->pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_dpll_pin_ntf - notify pin change including any SW pin wrappers
|
||||
* @dplls: pointer to dplls struct
|
||||
* @pin: the dpll_pin that changed
|
||||
*
|
||||
* Send a change notification for @pin and for any registered SMA/U.FL pin
|
||||
* whose backing CGU input matches @pin.
|
||||
*/
|
||||
static void ice_dpll_pin_ntf(struct ice_dplls *dplls, struct dpll_pin *pin)
|
||||
{
|
||||
dpll_pin_change_ntf(pin);
|
||||
for (int i = 0; i < ICE_DPLL_PIN_SW_NUM; i++) {
|
||||
if (dplls->sma[i].pin && dplls->sma[i].input &&
|
||||
dplls->sma[i].input->pin == pin)
|
||||
dpll_pin_change_ntf(dplls->sma[i].pin);
|
||||
if (dplls->ufl[i].pin && dplls->ufl[i].input &&
|
||||
dplls->ufl[i].input->pin == pin)
|
||||
dpll_pin_change_ntf(dplls->ufl[i].pin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_dpll_notify_changes - notify dpll subsystem about changes
|
||||
* @d: pointer do dpll
|
||||
@@ -2617,6 +2721,7 @@ static u64 ice_generate_clock_id(struct ice_pf *pf)
|
||||
*/
|
||||
static void ice_dpll_notify_changes(struct ice_dpll *d)
|
||||
{
|
||||
struct ice_dplls *dplls = &d->pf->dplls;
|
||||
bool pin_notified = false;
|
||||
|
||||
if (d->prev_dpll_state != d->dpll_state) {
|
||||
@@ -2625,17 +2730,17 @@ static void ice_dpll_notify_changes(struct ice_dpll *d)
|
||||
}
|
||||
if (d->prev_input != d->active_input) {
|
||||
if (d->prev_input)
|
||||
dpll_pin_change_ntf(d->prev_input);
|
||||
ice_dpll_pin_ntf(dplls, d->prev_input);
|
||||
d->prev_input = d->active_input;
|
||||
if (d->active_input) {
|
||||
dpll_pin_change_ntf(d->active_input);
|
||||
ice_dpll_pin_ntf(dplls, d->active_input);
|
||||
pin_notified = true;
|
||||
}
|
||||
}
|
||||
if (d->prev_phase_offset != d->phase_offset) {
|
||||
d->prev_phase_offset = d->phase_offset;
|
||||
if (!pin_notified && d->active_input)
|
||||
dpll_pin_change_ntf(d->active_input);
|
||||
ice_dpll_pin_ntf(dplls, d->active_input);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2664,6 +2769,7 @@ static bool ice_dpll_is_pps_phase_monitor(struct ice_pf *pf)
|
||||
|
||||
/**
|
||||
* ice_dpll_pins_notify_mask - notify dpll subsystem about bulk pin changes
|
||||
* @dplls: pointer to dplls struct
|
||||
* @pins: array of ice_dpll_pin pointers registered within dpll subsystem
|
||||
* @pin_num: number of pins
|
||||
* @phase_offset_ntf_mask: bitmask of pin indexes to notify
|
||||
@@ -2673,15 +2779,14 @@ static bool ice_dpll_is_pps_phase_monitor(struct ice_pf *pf)
|
||||
*
|
||||
* Context: Must be called while pf->dplls.lock is released.
|
||||
*/
|
||||
static void ice_dpll_pins_notify_mask(struct ice_dpll_pin *pins,
|
||||
static void ice_dpll_pins_notify_mask(struct ice_dplls *dplls,
|
||||
struct ice_dpll_pin *pins,
|
||||
u8 pin_num,
|
||||
u32 phase_offset_ntf_mask)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < pin_num; i++)
|
||||
if (phase_offset_ntf_mask & (1 << i))
|
||||
dpll_pin_change_ntf(pins[i].pin);
|
||||
for (int i = 0; i < pin_num; i++)
|
||||
if (phase_offset_ntf_mask & BIT(i))
|
||||
ice_dpll_pin_ntf(dplls, pins[i].pin);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2857,7 +2962,7 @@ static void ice_dpll_periodic_work(struct kthread_work *work)
|
||||
ice_dpll_notify_changes(de);
|
||||
ice_dpll_notify_changes(dp);
|
||||
if (phase_offset_ntf)
|
||||
ice_dpll_pins_notify_mask(d->inputs, d->num_inputs,
|
||||
ice_dpll_pins_notify_mask(d, d->inputs, d->num_inputs,
|
||||
phase_offset_ntf);
|
||||
|
||||
resched:
|
||||
@@ -4014,6 +4119,7 @@ static int ice_dpll_init_info_sw_pins(struct ice_pf *pf)
|
||||
struct ice_dpll_pin *pin;
|
||||
u32 phase_adj_max, caps;
|
||||
int i, ret;
|
||||
u8 data;
|
||||
|
||||
if (pf->hw.device_id == ICE_DEV_ID_E810C_QSFP)
|
||||
input_idx_offset = ICE_E810_RCLK_PINS_NUM;
|
||||
@@ -4073,6 +4179,22 @@ static int ice_dpll_init_info_sw_pins(struct ice_pf *pf)
|
||||
}
|
||||
ice_dpll_phase_range_set(&pin->prop.phase_range, phase_adj_max);
|
||||
}
|
||||
|
||||
/* Initialize the SMA control register to a known-good default state.
|
||||
* Without this write the PCA9575 GPIO expander retains its power-on
|
||||
* default (all outputs high) which makes all SW pins appear inactive.
|
||||
* Set SMA1 and SMA2 as active inputs, disable U.FL1 output and
|
||||
* U.FL2 input.
|
||||
*/
|
||||
ret = ice_read_sma_ctrl(&pf->hw, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
data &= ~ICE_ALL_SMA_MASK;
|
||||
data |= ICE_SMA1_TX_EN | ICE_SMA2_TX_EN | ICE_SMA2_UFL2_RX_DIS;
|
||||
ret = ice_write_sma_ctrl(&pf->hw, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ice_dpll_pin_state_update(pf, pin, ICE_DPLL_PIN_TYPE_SOFTWARE,
|
||||
NULL);
|
||||
if (ret)
|
||||
|
||||
@@ -5245,6 +5245,8 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
|
||||
return err;
|
||||
}
|
||||
|
||||
ice_init_dev_hw(pf);
|
||||
|
||||
adapter = ice_adapter_get(pdev);
|
||||
if (IS_ERR(adapter)) {
|
||||
err = PTR_ERR(adapter);
|
||||
|
||||
@@ -804,7 +804,12 @@ void ice_reset_all_vfs(struct ice_pf *pf)
|
||||
ice_vf_ctrl_invalidate_vsi(vf);
|
||||
|
||||
ice_vf_pre_vsi_rebuild(vf);
|
||||
ice_vf_rebuild_vsi(vf);
|
||||
if (ice_vf_rebuild_vsi(vf)) {
|
||||
dev_err(dev, "VF %u VSI rebuild failed, leaving VF disabled\n",
|
||||
vf->vf_id);
|
||||
mutex_unlock(&vf->cfg_lock);
|
||||
continue;
|
||||
}
|
||||
ice_vf_post_vsi_rebuild(vf);
|
||||
|
||||
ice_eswitch_attach_vf(pf, vf);
|
||||
|
||||
@@ -531,7 +531,7 @@ static int efx_devlink_info_running_versions(struct efx_nic *efx,
|
||||
if (rc || outlength < MC_CMD_GET_VERSION_OUT_LEN) {
|
||||
netif_err(efx, drv, efx->net_dev,
|
||||
"mcdi MC_CMD_GET_VERSION failed\n");
|
||||
return rc;
|
||||
return rc ?: -EIO;
|
||||
}
|
||||
|
||||
/* Handle previous output */
|
||||
|
||||
@@ -5549,9 +5549,12 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
|
||||
break;
|
||||
|
||||
/* Prefetch the next RX descriptor */
|
||||
rx_q->cur_rx = STMMAC_NEXT_ENTRY(rx_q->cur_rx,
|
||||
priv->dma_conf.dma_rx_size);
|
||||
next_entry = rx_q->cur_rx;
|
||||
next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx,
|
||||
priv->dma_conf.dma_rx_size);
|
||||
if (unlikely(next_entry == rx_q->dirty_rx))
|
||||
break;
|
||||
|
||||
rx_q->cur_rx = next_entry;
|
||||
|
||||
np = stmmac_get_rx_desc(priv, rx_q, next_entry);
|
||||
|
||||
@@ -5686,7 +5689,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
||||
|
||||
dma_dir = page_pool_get_dma_dir(rx_q->page_pool);
|
||||
bufsz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;
|
||||
limit = min(priv->dma_conf.dma_rx_size - 1, (unsigned int)limit);
|
||||
|
||||
if (netif_msg_rx_status(priv)) {
|
||||
void *rx_head = stmmac_get_rx_desc(priv, rx_q, 0);
|
||||
@@ -5733,9 +5735,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
||||
if (unlikely(status & dma_own))
|
||||
break;
|
||||
|
||||
rx_q->cur_rx = STMMAC_NEXT_ENTRY(rx_q->cur_rx,
|
||||
priv->dma_conf.dma_rx_size);
|
||||
next_entry = rx_q->cur_rx;
|
||||
next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx,
|
||||
priv->dma_conf.dma_rx_size);
|
||||
if (unlikely(next_entry == rx_q->dirty_rx))
|
||||
break;
|
||||
|
||||
rx_q->cur_rx = next_entry;
|
||||
|
||||
np = stmmac_get_rx_desc(priv, rx_q, next_entry);
|
||||
|
||||
|
||||
@@ -496,8 +496,6 @@ static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb)
|
||||
u8 *pecp;
|
||||
int rc;
|
||||
|
||||
fs = mctp_i2c_get_tx_flow_state(midev, skb);
|
||||
|
||||
hdr = (void *)skb_mac_header(skb);
|
||||
/* Sanity check that packet contents matches skb length,
|
||||
* and can't exceed MCTP_I2C_BUFSZ
|
||||
@@ -509,6 +507,8 @@ static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb)
|
||||
return;
|
||||
}
|
||||
|
||||
fs = mctp_i2c_get_tx_flow_state(midev, skb);
|
||||
|
||||
if (skb_tailroom(skb) >= 1) {
|
||||
/* Linear case with space, we can just append the PEC */
|
||||
skb_put(skb, 1);
|
||||
|
||||
@@ -752,7 +752,7 @@ static ssize_t enabled_store(struct config_item *item,
|
||||
unregister_netcons_consoles();
|
||||
}
|
||||
|
||||
ret = strnlen(buf, count);
|
||||
ret = count;
|
||||
/* Deferred cleanup */
|
||||
netconsole_process_cleanups();
|
||||
out_unlock:
|
||||
@@ -781,7 +781,7 @@ static ssize_t release_store(struct config_item *item, const char *buf,
|
||||
|
||||
nt->release = release;
|
||||
|
||||
ret = strnlen(buf, count);
|
||||
ret = count;
|
||||
out_unlock:
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
return ret;
|
||||
@@ -807,7 +807,7 @@ static ssize_t extended_store(struct config_item *item, const char *buf,
|
||||
goto out_unlock;
|
||||
|
||||
nt->extended = extended;
|
||||
ret = strnlen(buf, count);
|
||||
ret = count;
|
||||
out_unlock:
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
return ret;
|
||||
@@ -817,6 +817,13 @@ static ssize_t dev_name_store(struct config_item *item, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct netconsole_target *nt = to_target(item);
|
||||
size_t len = count;
|
||||
|
||||
/* Account for a trailing newline appended by tools like echo */
|
||||
if (len && buf[len - 1] == '\n')
|
||||
len--;
|
||||
if (len >= IFNAMSIZ)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
dynamic_netconsole_mutex_lock();
|
||||
if (nt->state == STATE_ENABLED) {
|
||||
@@ -830,7 +837,7 @@ static ssize_t dev_name_store(struct config_item *item, const char *buf,
|
||||
trim_newline(nt->np.dev_name, IFNAMSIZ);
|
||||
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
return strnlen(buf, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t local_port_store(struct config_item *item, const char *buf,
|
||||
@@ -849,7 +856,7 @@ static ssize_t local_port_store(struct config_item *item, const char *buf,
|
||||
ret = kstrtou16(buf, 10, &nt->np.local_port);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
ret = strnlen(buf, count);
|
||||
ret = count;
|
||||
out_unlock:
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
return ret;
|
||||
@@ -871,7 +878,7 @@ static ssize_t remote_port_store(struct config_item *item,
|
||||
ret = kstrtou16(buf, 10, &nt->np.remote_port);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
ret = strnlen(buf, count);
|
||||
ret = count;
|
||||
out_unlock:
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
return ret;
|
||||
@@ -896,7 +903,7 @@ static ssize_t local_ip_store(struct config_item *item, const char *buf,
|
||||
goto out_unlock;
|
||||
nt->np.ipv6 = !!ipv6;
|
||||
|
||||
ret = strnlen(buf, count);
|
||||
ret = count;
|
||||
out_unlock:
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
return ret;
|
||||
@@ -921,7 +928,7 @@ static ssize_t remote_ip_store(struct config_item *item, const char *buf,
|
||||
goto out_unlock;
|
||||
nt->np.ipv6 = !!ipv6;
|
||||
|
||||
ret = strnlen(buf, count);
|
||||
ret = count;
|
||||
out_unlock:
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
return ret;
|
||||
@@ -957,7 +964,7 @@ static ssize_t remote_mac_store(struct config_item *item, const char *buf,
|
||||
goto out_unlock;
|
||||
memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN);
|
||||
|
||||
ret = strnlen(buf, count);
|
||||
ret = count;
|
||||
out_unlock:
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
return ret;
|
||||
@@ -1072,26 +1079,30 @@ static ssize_t userdatum_value_store(struct config_item *item, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct userdatum *udm = to_userdatum(item);
|
||||
char old_value[MAX_EXTRADATA_VALUE_LEN];
|
||||
struct netconsole_target *nt;
|
||||
struct userdata *ud;
|
||||
ssize_t ret;
|
||||
|
||||
if (count > MAX_EXTRADATA_VALUE_LEN)
|
||||
if (count >= MAX_EXTRADATA_VALUE_LEN)
|
||||
return -EMSGSIZE;
|
||||
|
||||
mutex_lock(&netconsole_subsys.su_mutex);
|
||||
dynamic_netconsole_mutex_lock();
|
||||
|
||||
ret = strscpy(udm->value, buf, sizeof(udm->value));
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
/* Snapshot for rollback if update_userdata() fails below */
|
||||
strscpy(old_value, udm->value, sizeof(old_value));
|
||||
/* count is bounded above, so strscpy() cannot truncate here */
|
||||
strscpy(udm->value, buf, sizeof(udm->value));
|
||||
trim_newline(udm->value, sizeof(udm->value));
|
||||
|
||||
ud = to_userdata(item->ci_parent);
|
||||
nt = userdata_to_target(ud);
|
||||
ret = update_userdata(nt);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
/* Restore the previous value so it matches the live payload */
|
||||
strscpy(udm->value, old_value, sizeof(udm->value));
|
||||
goto out_unlock;
|
||||
}
|
||||
ret = count;
|
||||
out_unlock:
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
@@ -1133,7 +1144,7 @@ static ssize_t sysdata_msgid_enabled_store(struct config_item *item,
|
||||
disable_sysdata_feature(nt, SYSDATA_MSGID);
|
||||
|
||||
unlock_ok:
|
||||
ret = strnlen(buf, count);
|
||||
ret = count;
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
mutex_unlock(&netconsole_subsys.su_mutex);
|
||||
return ret;
|
||||
@@ -1162,7 +1173,7 @@ static ssize_t sysdata_release_enabled_store(struct config_item *item,
|
||||
disable_sysdata_feature(nt, SYSDATA_RELEASE);
|
||||
|
||||
unlock_ok:
|
||||
ret = strnlen(buf, count);
|
||||
ret = count;
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
mutex_unlock(&netconsole_subsys.su_mutex);
|
||||
return ret;
|
||||
@@ -1191,7 +1202,7 @@ static ssize_t sysdata_taskname_enabled_store(struct config_item *item,
|
||||
disable_sysdata_feature(nt, SYSDATA_TASKNAME);
|
||||
|
||||
unlock_ok:
|
||||
ret = strnlen(buf, count);
|
||||
ret = count;
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
mutex_unlock(&netconsole_subsys.su_mutex);
|
||||
return ret;
|
||||
@@ -1225,7 +1236,7 @@ static ssize_t sysdata_cpu_nr_enabled_store(struct config_item *item,
|
||||
disable_sysdata_feature(nt, SYSDATA_CPU_NR);
|
||||
|
||||
unlock_ok:
|
||||
ret = strnlen(buf, count);
|
||||
ret = count;
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
mutex_unlock(&netconsole_subsys.su_mutex);
|
||||
return ret;
|
||||
|
||||
@@ -829,7 +829,7 @@ static struct sk_buff *nsim_dev_trap_skb_build(void)
|
||||
skb->protocol = htons(ETH_P_IP);
|
||||
|
||||
skb_set_network_header(skb, skb->len);
|
||||
iph = skb_put(skb, sizeof(struct iphdr));
|
||||
iph = skb_put_zero(skb, sizeof(struct iphdr));
|
||||
iph->protocol = IPPROTO_UDP;
|
||||
iph->saddr = in_aton("192.0.2.1");
|
||||
iph->daddr = in_aton("198.51.100.1");
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#define DP83869_RGMIICTL 0x0032
|
||||
#define DP83869_STRAP_STS1 0x006e
|
||||
#define DP83869_RGMIIDCTL 0x0086
|
||||
#define DP83869_ANA_PLL_PROG_PI 0x00c6
|
||||
#define DP83869_RXFCFG 0x0134
|
||||
#define DP83869_RXFPMD1 0x0136
|
||||
#define DP83869_RXFPMD2 0x0137
|
||||
@@ -826,12 +827,22 @@ static int dp83869_config_init(struct phy_device *phydev)
|
||||
dp83869_config_port_mirroring(phydev);
|
||||
|
||||
/* Clock output selection if muxing property is set */
|
||||
if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK)
|
||||
if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK) {
|
||||
/*
|
||||
* Table 7-121 in datasheet says we have to set register 0xc6
|
||||
* to value 0x10 before CLK_O_SEL can be modified.
|
||||
*/
|
||||
ret = phy_write_mmd(phydev, DP83869_DEVADDR,
|
||||
DP83869_ANA_PLL_PROG_PI, 0x10);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_modify_mmd(phydev,
|
||||
DP83869_DEVADDR, DP83869_IO_MUX_CFG,
|
||||
DP83869_IO_MUX_CFG_CLK_O_SEL_MASK,
|
||||
dp83869->clk_output_sel <<
|
||||
DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT);
|
||||
}
|
||||
|
||||
if (phy_interface_is_rgmii(phydev)) {
|
||||
ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL,
|
||||
|
||||
@@ -683,6 +683,7 @@ static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *netdev)
|
||||
{
|
||||
rtl8150_t *dev = netdev_priv(netdev);
|
||||
unsigned int skb_len;
|
||||
int count, res;
|
||||
|
||||
/* pad the frame and ensure terminating USB packet, datasheet 9.2.3 */
|
||||
@@ -694,6 +695,8 @@ static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb,
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
skb_len = skb->len;
|
||||
|
||||
netif_stop_queue(netdev);
|
||||
dev->tx_skb = skb;
|
||||
usb_fill_bulk_urb(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2),
|
||||
@@ -707,9 +710,16 @@ static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb,
|
||||
netdev->stats.tx_errors++;
|
||||
netif_start_queue(netdev);
|
||||
}
|
||||
/*
|
||||
* The URB was not submitted, so write_bulk_callback() will
|
||||
* never run to free dev->tx_skb. Drop the skb here and
|
||||
* clear tx_skb to avoid leaving a stale pointer.
|
||||
*/
|
||||
dev->tx_skb = NULL;
|
||||
dev_kfree_skb_any(skb);
|
||||
} else {
|
||||
netdev->stats.tx_packets++;
|
||||
netdev->stats.tx_bytes += skb->len;
|
||||
netdev->stats.tx_bytes += skb_len;
|
||||
netif_trans_update(netdev);
|
||||
}
|
||||
|
||||
|
||||
@@ -1034,6 +1034,7 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev,
|
||||
|
||||
err:
|
||||
port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
|
||||
synchronize_net();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1053,10 +1054,16 @@ static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev,
|
||||
}
|
||||
|
||||
/* inverse of do_vrf_add_slave */
|
||||
static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
|
||||
static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev,
|
||||
bool needs_sync)
|
||||
{
|
||||
netdev_upper_dev_unlink(port_dev, dev);
|
||||
port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
|
||||
/* Make sure that concurrent RCU readers that identified the device
|
||||
* as a VRF port see a VRF master or no master at all.
|
||||
*/
|
||||
if (needs_sync)
|
||||
synchronize_net();
|
||||
|
||||
cycle_netdev(port_dev, NULL);
|
||||
|
||||
@@ -1065,7 +1072,7 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
|
||||
|
||||
static int vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
|
||||
{
|
||||
return do_vrf_del_slave(dev, port_dev);
|
||||
return do_vrf_del_slave(dev, port_dev, true);
|
||||
}
|
||||
|
||||
static void vrf_dev_uninit(struct net_device *dev)
|
||||
@@ -1619,7 +1626,7 @@ static void vrf_dellink(struct net_device *dev, struct list_head *head)
|
||||
struct list_head *iter;
|
||||
|
||||
netdev_for_each_lower_dev(dev, port_dev, iter)
|
||||
vrf_del_slave(dev, port_dev);
|
||||
do_vrf_del_slave(dev, port_dev, false);
|
||||
|
||||
vrf_map_unregister_dev(dev);
|
||||
|
||||
@@ -1751,7 +1758,7 @@ static int vrf_device_event(struct notifier_block *unused,
|
||||
goto out;
|
||||
|
||||
vrf_dev = netdev_master_upper_dev_get(dev);
|
||||
vrf_del_slave(vrf_dev, dev);
|
||||
do_vrf_del_slave(vrf_dev, dev, false);
|
||||
}
|
||||
out:
|
||||
return NOTIFY_DONE;
|
||||
|
||||
@@ -317,6 +317,7 @@
|
||||
#define TRF7970A_RSSI_OSC_STATUS_RSSI_MASK (BIT(2) | BIT(1) | BIT(0))
|
||||
#define TRF7970A_RSSI_OSC_STATUS_RSSI_X_MASK (BIT(5) | BIT(4) | BIT(3))
|
||||
#define TRF7970A_RSSI_OSC_STATUS_RSSI_OSC_OK BIT(6)
|
||||
#define TRF7970A_RSSI_OSC_STATUS_RSSI_NOISE_LEVEL 1
|
||||
|
||||
#define TRF7970A_SPECIAL_FCN_REG1_COL_7_6 BIT(0)
|
||||
#define TRF7970A_SPECIAL_FCN_REG1_14_ANTICOLL BIT(1)
|
||||
@@ -1300,7 +1301,7 @@ static int trf7970a_is_rf_field(struct trf7970a *trf, bool *is_rf_field)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (rssi & TRF7970A_RSSI_OSC_STATUS_RSSI_MASK)
|
||||
if ((rssi & TRF7970A_RSSI_OSC_STATUS_RSSI_MASK) > TRF7970A_RSSI_OSC_STATUS_RSSI_NOISE_LEVEL)
|
||||
*is_rf_field = true;
|
||||
else
|
||||
*is_rf_field = false;
|
||||
|
||||
@@ -1620,6 +1620,12 @@ static void privcmd_close(struct vm_area_struct *vma)
|
||||
kvfree(pages);
|
||||
}
|
||||
|
||||
static int privcmd_may_split(struct vm_area_struct *area, unsigned long addr)
|
||||
{
|
||||
/* Forbid splitting, avoids double free via privcmd_close(). */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static vm_fault_t privcmd_fault(struct vm_fault *vmf)
|
||||
{
|
||||
printk(KERN_DEBUG "privcmd_fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",
|
||||
@@ -1631,6 +1637,7 @@ static vm_fault_t privcmd_fault(struct vm_fault *vmf)
|
||||
|
||||
static const struct vm_operations_struct privcmd_vm_ops = {
|
||||
.close = privcmd_close,
|
||||
.may_split = privcmd_may_split,
|
||||
.fault = privcmd_fault
|
||||
};
|
||||
|
||||
|
||||
@@ -366,6 +366,8 @@ static ssize_t buildid_show(struct hyp_sysfs_attr *attr, char *buffer)
|
||||
ret = sprintf(buffer, "<denied>");
|
||||
return ret;
|
||||
}
|
||||
if (ret > PAGE_SIZE)
|
||||
return -ENOSPC;
|
||||
|
||||
buildid = kmalloc(sizeof(*buildid) + ret, GFP_KERNEL);
|
||||
if (!buildid)
|
||||
@@ -373,8 +375,10 @@ static ssize_t buildid_show(struct hyp_sysfs_attr *attr, char *buffer)
|
||||
|
||||
buildid->len = ret;
|
||||
ret = HYPERVISOR_xen_version(XENVER_build_id, buildid);
|
||||
if (ret > 0)
|
||||
ret = sprintf(buffer, "%s", buildid->buf);
|
||||
if (ret > 0) {
|
||||
/* Build id is binary, not a string. */
|
||||
memcpy(buffer, buildid->buf, ret);
|
||||
}
|
||||
kfree(buildid);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -4641,7 +4641,8 @@ int try_release_extent_buffer(struct folio *folio)
|
||||
* to read the block we will not block on anything.
|
||||
*/
|
||||
void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info,
|
||||
u64 bytenr, u64 owner_root, u64 gen, int level)
|
||||
u64 bytenr, u64 owner_root, u64 gen, int level,
|
||||
const struct btrfs_key *first_key)
|
||||
{
|
||||
struct btrfs_tree_parent_check check = {
|
||||
.level = level,
|
||||
@@ -4650,6 +4651,11 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info,
|
||||
struct extent_buffer *eb;
|
||||
int ret;
|
||||
|
||||
if (first_key) {
|
||||
memcpy(&check.first_key, first_key, sizeof(struct btrfs_key));
|
||||
check.has_first_key = true;
|
||||
}
|
||||
|
||||
eb = btrfs_find_create_tree_block(fs_info, bytenr, owner_root, level);
|
||||
if (IS_ERR(eb))
|
||||
return;
|
||||
@@ -4677,9 +4683,13 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info,
|
||||
*/
|
||||
void btrfs_readahead_node_child(struct extent_buffer *node, int slot)
|
||||
{
|
||||
struct btrfs_key node_key;
|
||||
|
||||
btrfs_node_key_to_cpu(node, &node_key, slot);
|
||||
btrfs_readahead_tree_block(node->fs_info,
|
||||
btrfs_node_blockptr(node, slot),
|
||||
btrfs_header_owner(node),
|
||||
btrfs_node_ptr_generation(node, slot),
|
||||
btrfs_header_level(node) - 1);
|
||||
btrfs_header_level(node) - 1,
|
||||
&node_key);
|
||||
}
|
||||
|
||||
@@ -287,7 +287,8 @@ static inline void wait_on_extent_buffer_writeback(struct extent_buffer *eb)
|
||||
}
|
||||
|
||||
void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info,
|
||||
u64 bytenr, u64 owner_root, u64 gen, int level);
|
||||
u64 bytenr, u64 owner_root, u64 gen, int level,
|
||||
const struct btrfs_key *first_key);
|
||||
void btrfs_readahead_node_child(struct extent_buffer *node, int slot);
|
||||
|
||||
/* Note: this can be used in for loops without caching the value in a variable. */
|
||||
|
||||
@@ -259,7 +259,11 @@ int btrfs_convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
|
||||
nr++;
|
||||
path->slots[0]--;
|
||||
} else {
|
||||
ASSERT(0);
|
||||
btrfs_err(fs_info, "unexpected free space tree key type %u",
|
||||
found_key.type);
|
||||
ret = -EUCLEAN;
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,7 +409,11 @@ int btrfs_convert_free_space_to_extents(struct btrfs_trans_handle *trans,
|
||||
|
||||
nr++;
|
||||
} else {
|
||||
ASSERT(0);
|
||||
btrfs_err(fs_info, "unexpected free space tree key type %u",
|
||||
found_key.type);
|
||||
ret = -EUCLEAN;
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1518,7 +1526,11 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans,
|
||||
nr++;
|
||||
path->slots[0]--;
|
||||
} else {
|
||||
ASSERT(0);
|
||||
btrfs_err(trans->fs_info, "unexpected free space tree key type %u",
|
||||
found_key.type);
|
||||
ret = -EUCLEAN;
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1153,7 +1153,7 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
|
||||
NULL, &cached,
|
||||
EXTENT_LOCKED | EXTENT_DELALLOC |
|
||||
EXTENT_DELALLOC_NEW |
|
||||
EXTENT_DEFRAG | EXTENT_DO_ACCOUNTING,
|
||||
EXTENT_DEFRAG | EXTENT_CLEAR_META_RESV,
|
||||
PAGE_UNLOCK | PAGE_START_WRITEBACK |
|
||||
PAGE_END_WRITEBACK);
|
||||
if (async_extent->cb)
|
||||
@@ -4959,6 +4959,8 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
btrfs_record_unlink_dir(trans, dir, inode, false);
|
||||
|
||||
/* now the directory is empty */
|
||||
ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name);
|
||||
if (!ret)
|
||||
|
||||
@@ -5102,7 +5102,6 @@ static int btrfs_ioctl_subvol_sync(struct btrfs_fs_info *fs_info, void __user *a
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BTRFS_EXPERIMENTAL
|
||||
static int btrfs_ioctl_shutdown(struct btrfs_fs_info *fs_info, unsigned long arg)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -5134,10 +5133,12 @@ static int btrfs_ioctl_shutdown(struct btrfs_fs_info *fs_info, unsigned long arg
|
||||
case BTRFS_SHUTDOWN_FLAGS_NOLOGFLUSH:
|
||||
btrfs_force_shutdown(fs_info);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
long btrfs_ioctl(struct file *file, unsigned int
|
||||
cmd, unsigned long arg)
|
||||
@@ -5294,10 +5295,8 @@ long btrfs_ioctl(struct file *file, unsigned int
|
||||
#endif
|
||||
case BTRFS_IOC_SUBVOL_SYNC_WAIT:
|
||||
return btrfs_ioctl_subvol_sync(fs_info, argp);
|
||||
#ifdef CONFIG_BTRFS_EXPERIMENTAL
|
||||
case BTRFS_IOC_SHUTDOWN:
|
||||
return btrfs_ioctl_shutdown(fs_info, arg);
|
||||
#endif
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
|
||||
@@ -45,8 +45,11 @@ static int btrfs_partially_delete_raid_extent(struct btrfs_trans_handle *trans,
|
||||
|
||||
for (int i = 0; i < btrfs_num_raid_stripes(item_size); i++) {
|
||||
struct btrfs_raid_stride *stride = &extent->strides[i];
|
||||
u64 devid;
|
||||
u64 phys;
|
||||
|
||||
devid = btrfs_raid_stride_devid(leaf, stride);
|
||||
btrfs_set_stack_raid_stride_devid(&newitem->strides[i], devid);
|
||||
phys = btrfs_raid_stride_physical(leaf, stride) + frontpad;
|
||||
btrfs_set_stack_raid_stride_physical(&newitem->strides[i], phys);
|
||||
}
|
||||
@@ -95,14 +98,26 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le
|
||||
while (1) {
|
||||
key.objectid = start;
|
||||
key.type = BTRFS_RAID_STRIPE_KEY;
|
||||
key.offset = 0;
|
||||
key.offset = (u64)-1;
|
||||
|
||||
ret = btrfs_search_slot(trans, stripe_root, &key, path, -1, 1);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
if (path->slots[0] == btrfs_header_nritems(path->nodes[0]))
|
||||
path->slots[0]--;
|
||||
/*
|
||||
* Search with offset=(u64)-1 ensures we land on the correct
|
||||
* leaf even when the target entry is the first item on a leaf.
|
||||
* Since no real entry has offset=(u64)-1, ret is always 1 and
|
||||
* slot points past the last entry with objectid==start (or
|
||||
* past the end of the leaf if that entry is the last item).
|
||||
* Back up one slot to find the actual entry.
|
||||
*/
|
||||
if (path->slots[0] == 0) {
|
||||
/* No entry with objectid <= start exists. */
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
path->slots[0]--;
|
||||
|
||||
leaf = path->nodes[0];
|
||||
slot = path->slots[0];
|
||||
@@ -123,7 +138,7 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le
|
||||
*/
|
||||
if (found_start > start) {
|
||||
if (slot == 0) {
|
||||
ret = btrfs_previous_item(stripe_root, path, start,
|
||||
ret = btrfs_previous_item(stripe_root, path, 0,
|
||||
BTRFS_RAID_STRIPE_KEY);
|
||||
if (ret) {
|
||||
if (ret > 0)
|
||||
@@ -139,7 +154,10 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le
|
||||
btrfs_item_key_to_cpu(leaf, &key, slot);
|
||||
found_start = key.objectid;
|
||||
found_end = found_start + key.offset;
|
||||
ASSERT(found_start <= start);
|
||||
if (found_start > start || found_end <= start) {
|
||||
ret = -ENOENT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (key.type != BTRFS_RAID_STRIPE_KEY)
|
||||
@@ -176,9 +194,19 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le
|
||||
|
||||
/* The "right" item. */
|
||||
ret = btrfs_duplicate_item(trans, stripe_root, path, &newkey);
|
||||
if (ret == -EAGAIN) {
|
||||
btrfs_release_path(path);
|
||||
continue;
|
||||
}
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
/*
|
||||
* btrfs_duplicate_item() may have triggered a leaf
|
||||
* split via setup_leaf_for_split(), so we must refresh
|
||||
* our leaf pointer from the path.
|
||||
*/
|
||||
leaf = path->nodes[0];
|
||||
item_size = btrfs_item_size(leaf, path->slots[0]);
|
||||
extent = btrfs_item_ptr(leaf, path->slots[0],
|
||||
struct btrfs_stripe_extent);
|
||||
@@ -195,8 +223,9 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le
|
||||
/* The "left" item. */
|
||||
path->slots[0]--;
|
||||
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
|
||||
btrfs_partially_delete_raid_extent(trans, path, &key,
|
||||
diff_start, 0);
|
||||
ret = btrfs_partially_delete_raid_extent(trans, path,
|
||||
&key,
|
||||
diff_start, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -212,8 +241,11 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le
|
||||
if (found_start < start) {
|
||||
u64 diff_start = start - found_start;
|
||||
|
||||
btrfs_partially_delete_raid_extent(trans, path, &key,
|
||||
diff_start, 0);
|
||||
ret = btrfs_partially_delete_raid_extent(trans, path,
|
||||
&key,
|
||||
diff_start, 0);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
start += (key.offset - diff_start);
|
||||
length -= (key.offset - diff_start);
|
||||
@@ -236,9 +268,10 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le
|
||||
if (found_end > end) {
|
||||
u64 diff_end = found_end - end;
|
||||
|
||||
btrfs_partially_delete_raid_extent(trans, path, &key,
|
||||
key.offset - length,
|
||||
length);
|
||||
ret = btrfs_partially_delete_raid_extent(trans, path,
|
||||
&key,
|
||||
key.offset - length,
|
||||
length);
|
||||
ASSERT(key.offset - diff_end == length);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2607,7 +2607,7 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans,
|
||||
if (!block->key_ready)
|
||||
btrfs_readahead_tree_block(fs_info, block->bytenr,
|
||||
block->owner, 0,
|
||||
block->level);
|
||||
block->level, NULL);
|
||||
}
|
||||
|
||||
/* Get first keys */
|
||||
@@ -3876,7 +3876,7 @@ static int add_remap_tree_entries(struct btrfs_trans_handle *trans, struct btrfs
|
||||
ret = btrfs_insert_empty_items(trans, fs_info->remap_root, path, &batch);
|
||||
btrfs_release_path(path);
|
||||
|
||||
if (num_entries <= max_items)
|
||||
if (ret || num_entries <= max_items)
|
||||
break;
|
||||
|
||||
num_entries -= max_items;
|
||||
@@ -4174,6 +4174,12 @@ static int move_existing_remap(struct btrfs_fs_info *fs_info,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ins.offset < length) {
|
||||
spin_lock(&sinfo->lock);
|
||||
btrfs_space_info_update_bytes_may_use(sinfo, ins.offset - length);
|
||||
spin_unlock(&sinfo->lock);
|
||||
}
|
||||
|
||||
dest_addr = ins.objectid;
|
||||
dest_length = ins.offset;
|
||||
|
||||
@@ -5000,6 +5006,12 @@ static int do_remap_reloc_trans(struct btrfs_fs_info *fs_info,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ins.offset < remap_length) {
|
||||
spin_lock(&sinfo->lock);
|
||||
btrfs_space_info_update_bytes_may_use(sinfo, ins.offset - remap_length);
|
||||
spin_unlock(&sinfo->lock);
|
||||
}
|
||||
|
||||
made_reservation = true;
|
||||
|
||||
new_addr = ins.objectid;
|
||||
@@ -5023,21 +5035,27 @@ static int do_remap_reloc_trans(struct btrfs_fs_info *fs_info,
|
||||
|
||||
if (bg_needs_free_space) {
|
||||
ret = btrfs_add_block_group_free_space(trans, dest_bg);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = copy_remapped_data(fs_info, start, new_addr, length);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = btrfs_remove_from_free_space_tree(trans, new_addr, length);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = add_remap_entry(trans, path, src_bg, start, new_addr, length);
|
||||
if (ret) {
|
||||
btrfs_add_to_free_space_tree(trans, new_addr, length);
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ isofs_export_iget(struct super_block *sb,
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
if (block == 0)
|
||||
if (block == 0 || block >= ISOFS_SB(sb)->s_nzones)
|
||||
return ERR_PTR(-ESTALE);
|
||||
inode = isofs_iget(sb, block, offset);
|
||||
if (IS_ERR(inode))
|
||||
|
||||
@@ -10,20 +10,13 @@
|
||||
#include <linux/gfp.h>
|
||||
#include "isofs.h"
|
||||
|
||||
/*
|
||||
* ok, we cannot use strncmp, as the name is not in our data space.
|
||||
* Thus we'll have to use isofs_match. No big problem. Match also makes
|
||||
* some sanity tests.
|
||||
*/
|
||||
static int
|
||||
isofs_cmp(struct dentry *dentry, const char *compare, int dlen)
|
||||
{
|
||||
struct qstr qstr;
|
||||
qstr.name = compare;
|
||||
qstr.len = dlen;
|
||||
if (likely(!dentry->d_op))
|
||||
return dentry->d_name.len != dlen || memcmp(dentry->d_name.name, compare, dlen);
|
||||
return dentry->d_op->d_compare(NULL, dentry->d_name.len, dentry->d_name.name, &qstr);
|
||||
return dentry->d_op->d_compare(NULL, dentry->d_name.len, dentry->d_name.name,
|
||||
&QSTR_LEN(compare, dlen));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -101,6 +101,15 @@ static int rock_continue(struct rock_state *rs)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((unsigned)rs->cont_extent >= ISOFS_SB(rs->inode->i_sb)->s_nzones) {
|
||||
printk(KERN_NOTICE "rock: corrupted directory entry. "
|
||||
"extent=%u out of volume (nzones=%lu)\n",
|
||||
(unsigned)rs->cont_extent,
|
||||
ISOFS_SB(rs->inode->i_sb)->s_nzones);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rs->cont_extent) {
|
||||
struct buffer_head *bh;
|
||||
|
||||
|
||||
@@ -457,7 +457,7 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
|
||||
/*
|
||||
* Unlike file_handle, type and len of struct fanotify_fh are u8.
|
||||
* Traditionally, filesystem return handle_type < 0xff, but there
|
||||
* is no enforecement for that in vfs.
|
||||
* is no enforcement for that in vfs.
|
||||
*/
|
||||
BUILD_BUG_ON(MAX_HANDLE_SZ > 0xff || FILEID_INVALID > 0xff);
|
||||
if (type <= 0 || type >= FILEID_INVALID || fh_len != dwords << 2)
|
||||
|
||||
@@ -388,7 +388,7 @@ static struct fsnotify_mark *fsnotify_first_mark(struct fsnotify_mark_connector
|
||||
return hlist_entry_safe(node, struct fsnotify_mark, obj_list);
|
||||
}
|
||||
|
||||
static struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark)
|
||||
struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark)
|
||||
{
|
||||
struct hlist_node *node = NULL;
|
||||
|
||||
|
||||
@@ -238,7 +238,12 @@ static struct inode *fsnotify_update_iref(struct fsnotify_mark_connector *conn,
|
||||
return inode;
|
||||
}
|
||||
|
||||
static void *__fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
|
||||
/*
|
||||
* Calculate mask of events for a list of marks.
|
||||
*
|
||||
* Return true if any of the attached marks want to hold an inode reference.
|
||||
*/
|
||||
static bool __fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
|
||||
{
|
||||
u32 new_mask = 0;
|
||||
bool want_iref = false;
|
||||
@@ -262,6 +267,34 @@ static void *__fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
|
||||
*/
|
||||
WRITE_ONCE(*fsnotify_conn_mask_p(conn), new_mask);
|
||||
|
||||
return want_iref;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate mask of events for a list of marks after attach/modify mark
|
||||
* and get an inode reference for the connector if needed.
|
||||
*
|
||||
* A concurrent add of evictable mark and detach of non-evictable mark can
|
||||
* lead to __fsnotify_recalc_mask() returning false want_iref, but in this
|
||||
* case we defer clearing iref to fsnotify_recalc_mask_clear_iref() called
|
||||
* from fsnotify_put_mark().
|
||||
*/
|
||||
static void fsnotify_recalc_mask_set_iref(struct fsnotify_mark_connector *conn)
|
||||
{
|
||||
bool has_iref = conn->flags & FSNOTIFY_CONN_FLAG_HAS_IREF;
|
||||
bool want_iref = __fsnotify_recalc_mask(conn) || has_iref;
|
||||
|
||||
(void) fsnotify_update_iref(conn, want_iref);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate mask of events for a list of marks after detach mark
|
||||
* and return the inode object if its reference is no longer needed.
|
||||
*/
|
||||
static void *fsnotify_recalc_mask_clear_iref(struct fsnotify_mark_connector *conn)
|
||||
{
|
||||
bool want_iref = __fsnotify_recalc_mask(conn);
|
||||
|
||||
return fsnotify_update_iref(conn, want_iref);
|
||||
}
|
||||
|
||||
@@ -298,7 +331,7 @@ void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
|
||||
|
||||
spin_lock(&conn->lock);
|
||||
update_children = !fsnotify_conn_watches_children(conn);
|
||||
__fsnotify_recalc_mask(conn);
|
||||
fsnotify_recalc_mask_set_iref(conn);
|
||||
update_children &= fsnotify_conn_watches_children(conn);
|
||||
spin_unlock(&conn->lock);
|
||||
/*
|
||||
@@ -419,7 +452,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
|
||||
/* Update watched objects after detaching mark */
|
||||
if (sb)
|
||||
fsnotify_update_sb_watchers(sb, conn);
|
||||
objp = __fsnotify_recalc_mask(conn);
|
||||
objp = fsnotify_recalc_mask_clear_iref(conn);
|
||||
type = conn->type;
|
||||
}
|
||||
WRITE_ONCE(mark->connector, NULL);
|
||||
@@ -457,9 +490,6 @@ EXPORT_SYMBOL_GPL(fsnotify_put_mark);
|
||||
*/
|
||||
static bool fsnotify_get_mark_safe(struct fsnotify_mark *mark)
|
||||
{
|
||||
if (!mark)
|
||||
return true;
|
||||
|
||||
if (refcount_inc_not_zero(&mark->refcnt)) {
|
||||
spin_lock(&mark->lock);
|
||||
if (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED) {
|
||||
@@ -500,15 +530,22 @@ bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info)
|
||||
int type;
|
||||
|
||||
fsnotify_foreach_iter_type(type) {
|
||||
struct fsnotify_mark *mark = iter_info->marks[type];
|
||||
|
||||
/* This can fail if mark is being removed */
|
||||
if (!fsnotify_get_mark_safe(iter_info->marks[type])) {
|
||||
__release(&fsnotify_mark_srcu);
|
||||
goto fail;
|
||||
while (mark && !fsnotify_get_mark_safe(mark)) {
|
||||
if (mark->group == iter_info->current_group) {
|
||||
__release(&fsnotify_mark_srcu);
|
||||
goto fail;
|
||||
}
|
||||
/* This is a mark in an unrelated group, skip */
|
||||
mark = fsnotify_next_mark(mark);
|
||||
iter_info->marks[type] = mark;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that both marks are pinned by refcount in the inode / vfsmount
|
||||
* Now that all marks are pinned by refcount in the inode / vfsmount / etc
|
||||
* lists, we can drop SRCU lock, and safely resume the list iteration
|
||||
* once userspace returns.
|
||||
*/
|
||||
|
||||
@@ -230,8 +230,12 @@ struct buffer_head *udf_read_tagged(struct super_block *sb, uint32_t block,
|
||||
}
|
||||
|
||||
/* Verify the descriptor CRC */
|
||||
if (le16_to_cpu(tag_p->descCRCLength) + sizeof(struct tag) > sb->s_blocksize ||
|
||||
le16_to_cpu(tag_p->descCRC) == crc_itu_t(0,
|
||||
if (le16_to_cpu(tag_p->descCRCLength) + sizeof(struct tag) > sb->s_blocksize) {
|
||||
udf_err(sb, "block %u: CRC length %u exceeds block size\n",
|
||||
block, le16_to_cpu(tag_p->descCRCLength));
|
||||
goto error_out;
|
||||
}
|
||||
if (le16_to_cpu(tag_p->descCRC) == crc_itu_t(0,
|
||||
bh->b_data + sizeof(struct tag),
|
||||
le16_to_cpu(tag_p->descCRCLength)))
|
||||
return bh;
|
||||
|
||||
@@ -292,6 +292,7 @@ int dpll_pin_ref_sync_pair_add(struct dpll_pin *pin,
|
||||
|
||||
int dpll_device_change_ntf(struct dpll_device *dpll);
|
||||
|
||||
int __dpll_pin_change_ntf(struct dpll_pin *pin);
|
||||
int dpll_pin_change_ntf(struct dpll_pin *pin);
|
||||
|
||||
int register_dpll_notifier(struct notifier_block *nb);
|
||||
|
||||
@@ -915,6 +915,7 @@ extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group,
|
||||
unsigned int obj_type);
|
||||
extern void fsnotify_get_mark(struct fsnotify_mark *mark);
|
||||
extern void fsnotify_put_mark(struct fsnotify_mark *mark);
|
||||
struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark);
|
||||
extern void fsnotify_finish_user_wait(struct fsnotify_iter_info *iter_info);
|
||||
extern bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info);
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg);
|
||||
int mbox_flush(struct mbox_chan *chan, unsigned long timeout);
|
||||
void mbox_client_txdone(struct mbox_chan *chan, int r); /* atomic */
|
||||
bool mbox_client_peek_data(struct mbox_chan *chan); /* atomic */
|
||||
unsigned int mbox_chan_tx_slots_available(struct mbox_chan *chan); /* atomic */
|
||||
void mbox_free_channel(struct mbox_chan *chan); /* may sleep */
|
||||
|
||||
#endif /* __MAILBOX_CLIENT_H */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#ifndef __MAILBOX_CONTROLLER_H
|
||||
#define __MAILBOX_CONTROLLER_H
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/hrtimer.h>
|
||||
@@ -11,6 +12,13 @@
|
||||
|
||||
struct mbox_chan;
|
||||
|
||||
/* Sentinel value distinguishing "no active request" from "NULL message data" */
|
||||
#define MBOX_NO_MSG ((void *)-1)
|
||||
|
||||
#define MBOX_TXDONE_BY_IRQ BIT(0) /* controller has remote RTR irq */
|
||||
#define MBOX_TXDONE_BY_POLL BIT(1) /* controller can read status of last TX */
|
||||
#define MBOX_TXDONE_BY_ACK BIT(2) /* S/W ACK received by Client ticks the TX */
|
||||
|
||||
/**
|
||||
* struct mbox_chan_ops - methods to control mailbox channels
|
||||
* @send_data: The API asks the MBOX controller driver, in atomic
|
||||
@@ -54,10 +62,10 @@ struct mbox_chan_ops {
|
||||
|
||||
/**
|
||||
* struct mbox_controller - Controller of a class of communication channels
|
||||
* @dev: Device backing this controller
|
||||
* @ops: Operators that work on each communication chan
|
||||
* @chans: Array of channels
|
||||
* @num_chans: Number of channels in the 'chans' array.
|
||||
* @dev: Device backing this controller. Required.
|
||||
* @ops: Operators that work on each communication chan. Required.
|
||||
* @chans: Array of channels. Required.
|
||||
* @num_chans: Number of channels in the 'chans' array. Required.
|
||||
* @txdone_irq: Indicates if the controller can report to API when
|
||||
* the last transmitted data was read by the remote.
|
||||
* Eg, if it has some TX ACK irq.
|
||||
@@ -70,6 +78,7 @@ struct mbox_chan_ops {
|
||||
* @of_xlate: Controller driver specific mapping of channel via DT
|
||||
* @poll_hrt: API private. hrtimer used to poll for TXDONE on all
|
||||
* channels.
|
||||
* @poll_hrt_lock: API private. Lock protecting access to poll_hrt.
|
||||
* @node: API private. To hook into list of controllers.
|
||||
*/
|
||||
struct mbox_controller {
|
||||
|
||||
@@ -226,6 +226,7 @@ struct mr_table_ops {
|
||||
|
||||
/**
|
||||
* struct mr_table - a multicast routing table
|
||||
* @work: used for table destruction
|
||||
* @list: entry within a list of multicast routing tables
|
||||
* @net: net where this table belongs
|
||||
* @ops: protocol specific operations
|
||||
@@ -243,6 +244,7 @@ struct mr_table_ops {
|
||||
* @mroute_reg_vif_num: PIM-device vif index
|
||||
*/
|
||||
struct mr_table {
|
||||
struct rcu_work work;
|
||||
struct list_head list;
|
||||
possible_net_t net;
|
||||
struct mr_table_ops ops;
|
||||
@@ -274,6 +276,7 @@ void vif_device_init(struct vif_device *v,
|
||||
unsigned short flags,
|
||||
unsigned short get_iflink_mask);
|
||||
|
||||
void mr_table_free(struct mr_table *mrt);
|
||||
struct mr_table *
|
||||
mr_table_alloc(struct net *net, u32 id,
|
||||
struct mr_table_ops *ops,
|
||||
|
||||
@@ -261,6 +261,35 @@ static inline void list_replace_rcu(struct list_head *old,
|
||||
old->prev = LIST_POISON2;
|
||||
}
|
||||
|
||||
static inline void __list_splice_rcu(struct list_head *list,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
struct list_head *first = list->next;
|
||||
struct list_head *last = list->prev;
|
||||
|
||||
last->next = next;
|
||||
first->prev = prev;
|
||||
next->prev = last;
|
||||
rcu_assign_pointer(list_next_rcu(prev), first);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_rcu - splice a non-RCU list into an RCU-protected list,
|
||||
* designed for stacks.
|
||||
* @list: the non RCU-protected list to splice
|
||||
* @head: the place in the existing RCU-protected list to splice
|
||||
*
|
||||
* The list pointed to by @head can be RCU-read traversed concurrently with
|
||||
* this function.
|
||||
*/
|
||||
static inline void list_splice_rcu(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list))
|
||||
__list_splice_rcu(list, head, head->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* __list_splice_init_rcu - join an RCU-protected list into an existing list.
|
||||
* @list: the RCU-protected list to splice
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <linux/alloc_tag.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/irq_work_types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue_types.h>
|
||||
|
||||
@@ -49,6 +50,7 @@ typedef int (*rht_obj_cmpfn_t)(struct rhashtable_compare_arg *arg,
|
||||
* @head_offset: Offset of rhash_head in struct to be hashed
|
||||
* @max_size: Maximum size while expanding
|
||||
* @min_size: Minimum size while shrinking
|
||||
* @insecure_elasticity: Set to true to disable chain length checks
|
||||
* @automatic_shrinking: Enable automatic shrinking of tables
|
||||
* @hashfn: Hash function (default: jhash2 if !(key_len % 4), or jhash)
|
||||
* @obj_hashfn: Function to hash object
|
||||
@@ -61,6 +63,7 @@ struct rhashtable_params {
|
||||
u16 head_offset;
|
||||
unsigned int max_size;
|
||||
u16 min_size;
|
||||
bool insecure_elasticity;
|
||||
bool automatic_shrinking;
|
||||
rht_hashfn_t hashfn;
|
||||
rht_obj_hashfn_t obj_hashfn;
|
||||
@@ -75,6 +78,7 @@ struct rhashtable_params {
|
||||
* @p: Configuration parameters
|
||||
* @rhlist: True if this is an rhltable
|
||||
* @run_work: Deferred worker to expand/shrink asynchronously
|
||||
* @run_irq_work: Bounces the @run_work kick through hard IRQ context.
|
||||
* @mutex: Mutex to protect current/future table swapping
|
||||
* @lock: Spin lock to protect walker list
|
||||
* @nelems: Number of elements in table
|
||||
@@ -86,6 +90,7 @@ struct rhashtable {
|
||||
struct rhashtable_params p;
|
||||
bool rhlist;
|
||||
struct work_struct run_work;
|
||||
struct irq_work run_irq_work;
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
atomic_t nelems;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/list_nulls.h>
|
||||
#include <linux/workqueue.h>
|
||||
@@ -821,14 +822,15 @@ static __always_inline void *__rhashtable_insert_fast(
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (elasticity <= 0)
|
||||
if (elasticity <= 0 && !params.insecure_elasticity)
|
||||
goto slow_path;
|
||||
|
||||
data = ERR_PTR(-E2BIG);
|
||||
if (unlikely(rht_grow_above_max(ht, tbl)))
|
||||
goto out_unlock;
|
||||
|
||||
if (unlikely(rht_grow_above_100(ht, tbl)))
|
||||
if (unlikely(rht_grow_above_100(ht, tbl)) &&
|
||||
!params.insecure_elasticity)
|
||||
goto slow_path;
|
||||
|
||||
/* Inserting at head of list makes unlocking free. */
|
||||
@@ -846,7 +848,7 @@ static __always_inline void *__rhashtable_insert_fast(
|
||||
rht_assign_unlock(tbl, bkt, obj, flags);
|
||||
|
||||
if (rht_grow_above_75(ht, tbl))
|
||||
schedule_work(&ht->run_work);
|
||||
irq_work_queue(&ht->run_irq_work);
|
||||
|
||||
data = NULL;
|
||||
out:
|
||||
|
||||
@@ -202,7 +202,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
|
||||
#define TP_CONDITION(args...) args
|
||||
|
||||
/*
|
||||
* Individual subsystem my have a separate configuration to
|
||||
* Individual subsystem may have a separate configuration to
|
||||
* enable their tracepoints. By default, this file will create
|
||||
* the tracepoints if CONFIG_TRACEPOINTS is defined. If a subsystem
|
||||
* wants to be able to disable its tracepoints from being created
|
||||
|
||||
@@ -243,7 +243,7 @@ typedef struct port {
|
||||
churn_state_t sm_churn_actor_state;
|
||||
churn_state_t sm_churn_partner_state;
|
||||
struct slave *slave; /* pointer to the bond slave that this port belongs to */
|
||||
struct aggregator *aggregator; /* pointer to an aggregator that this port related to */
|
||||
struct aggregator __rcu *aggregator; /* pointer to an aggregator that this port related to */
|
||||
struct port *next_port_in_aggregator; /* Next port on the linked list of the parent aggregator */
|
||||
u32 transaction_id; /* continuous number for identification of Marker PDU's; */
|
||||
struct lacpdu lacpdu; /* the lacpdu that will be sent for this port */
|
||||
|
||||
@@ -1204,12 +1204,15 @@ struct nft_stats {
|
||||
struct u64_stats_sync syncp;
|
||||
};
|
||||
|
||||
#define NFT_HOOK_REMOVE (1 << 0)
|
||||
|
||||
struct nft_hook {
|
||||
struct list_head list;
|
||||
struct list_head ops_list;
|
||||
struct rcu_head rcu;
|
||||
char ifname[IFNAMSIZ];
|
||||
u8 ifnamelen;
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook,
|
||||
@@ -1664,6 +1667,16 @@ struct nft_trans {
|
||||
u8 put_net:1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nft_trans_hook - nf_tables hook update in transaction
|
||||
* @list: used internally
|
||||
* @hook: struct nft_hook with the device hook
|
||||
*/
|
||||
struct nft_trans_hook {
|
||||
struct list_head list;
|
||||
struct nft_hook *hook;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nft_trans_binding - nf_tables object with binding support in transaction
|
||||
* @nft_trans: base structure, MUST be first member
|
||||
|
||||
@@ -127,6 +127,21 @@ static inline unsigned int net_iov_idx(const struct net_iov *niov)
|
||||
return niov - net_iov_owner(niov)->niovs;
|
||||
}
|
||||
|
||||
/* Initialize a niov: stamp the owning area, the memory provider type,
|
||||
* and the page_type "no type" sentinel expected by the page-type API
|
||||
* (see PAGE_TYPE_OPS in <linux/page-flags.h>) so that
|
||||
* page_pool_set_pp_info() can later call __SetPageNetpp() on a niov
|
||||
* cast to struct page.
|
||||
*/
|
||||
static inline void net_iov_init(struct net_iov *niov,
|
||||
struct net_iov_area *owner,
|
||||
enum net_iov_type type)
|
||||
{
|
||||
niov->owner = owner;
|
||||
niov->type = type;
|
||||
niov->page_type = UINT_MAX;
|
||||
}
|
||||
|
||||
/* netmem */
|
||||
|
||||
/**
|
||||
|
||||
@@ -495,10 +495,9 @@ static int io_zcrx_create_area(struct io_zcrx_ifq *ifq,
|
||||
for (i = 0; i < nr_iovs; i++) {
|
||||
struct net_iov *niov = &area->nia.niovs[i];
|
||||
|
||||
niov->owner = &area->nia;
|
||||
net_iov_init(niov, &area->nia, NET_IOV_IOURING);
|
||||
area->freelist[i] = i;
|
||||
atomic_set(&area->user_refs[i], 0);
|
||||
niov->type = NET_IOV_IOURING;
|
||||
}
|
||||
|
||||
if (ifq->dev) {
|
||||
|
||||
@@ -3934,33 +3934,41 @@ static int cgroup_cpu_pressure_show(struct seq_file *seq, void *v)
|
||||
static ssize_t pressure_write(struct kernfs_open_file *of, char *buf,
|
||||
size_t nbytes, enum psi_res res)
|
||||
{
|
||||
struct cgroup_file_ctx *ctx = of->priv;
|
||||
struct cgroup_file_ctx *ctx;
|
||||
struct psi_trigger *new;
|
||||
struct cgroup *cgrp;
|
||||
struct psi_group *psi;
|
||||
ssize_t ret = 0;
|
||||
|
||||
cgrp = cgroup_kn_lock_live(of->kn, false);
|
||||
if (!cgrp)
|
||||
return -ENODEV;
|
||||
|
||||
cgroup_get(cgrp);
|
||||
cgroup_kn_unlock(of->kn);
|
||||
ctx = of->priv;
|
||||
if (!ctx) {
|
||||
ret = -ENODEV;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* Allow only one trigger per file descriptor */
|
||||
if (ctx->psi.trigger) {
|
||||
cgroup_put(cgrp);
|
||||
return -EBUSY;
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
psi = cgroup_psi(cgrp);
|
||||
new = psi_trigger_create(psi, buf, res, of->file, of);
|
||||
if (IS_ERR(new)) {
|
||||
cgroup_put(cgrp);
|
||||
return PTR_ERR(new);
|
||||
ret = PTR_ERR(new);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
smp_store_release(&ctx->psi.trigger, new);
|
||||
cgroup_put(cgrp);
|
||||
|
||||
out_unlock:
|
||||
cgroup_kn_unlock(of->kn);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
@@ -5716,16 +5724,6 @@ static void offline_css(struct cgroup_subsys_state *css)
|
||||
RCU_INIT_POINTER(css->cgroup->subsys[ss->id], NULL);
|
||||
|
||||
wake_up_all(&css->cgroup->offline_waitq);
|
||||
|
||||
css->cgroup->nr_dying_subsys[ss->id]++;
|
||||
/*
|
||||
* Parent css and cgroup cannot be freed until after the freeing
|
||||
* of child css, see css_free_rwork_fn().
|
||||
*/
|
||||
while ((css = css->parent)) {
|
||||
css->nr_descendants--;
|
||||
css->cgroup->nr_dying_subsys[ss->id]++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6038,6 +6036,8 @@ static void css_killed_ref_fn(struct percpu_ref *ref)
|
||||
*/
|
||||
static void kill_css(struct cgroup_subsys_state *css)
|
||||
{
|
||||
struct cgroup_subsys *ss = css->ss;
|
||||
|
||||
lockdep_assert_held(&cgroup_mutex);
|
||||
|
||||
if (css->flags & CSS_DYING)
|
||||
@@ -6074,6 +6074,16 @@ static void kill_css(struct cgroup_subsys_state *css)
|
||||
* css is confirmed to be seen as killed on all CPUs.
|
||||
*/
|
||||
percpu_ref_kill_and_confirm(&css->refcnt, css_killed_ref_fn);
|
||||
|
||||
css->cgroup->nr_dying_subsys[ss->id]++;
|
||||
/*
|
||||
* Parent css and cgroup cannot be freed until after the freeing
|
||||
* of child css, see css_free_rwork_fn().
|
||||
*/
|
||||
while ((css = css->parent)) {
|
||||
css->nr_descendants--;
|
||||
css->cgroup->nr_dying_subsys[ss->id]++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -168,6 +168,11 @@ struct cpuset {
|
||||
int nr_deadline_tasks;
|
||||
int nr_migrate_dl_tasks;
|
||||
u64 sum_migrate_dl_bw;
|
||||
/*
|
||||
* CPU used for temporary DL bandwidth allocation during attach;
|
||||
* -1 if no DL bandwidth was allocated in the current attach.
|
||||
*/
|
||||
int dl_bw_cpu;
|
||||
|
||||
/* Invalid partition error code, not lock protected */
|
||||
enum prs_errcode prs_err;
|
||||
|
||||
@@ -288,6 +288,7 @@ struct cpuset top_cpuset = {
|
||||
.flags = BIT(CS_CPU_EXCLUSIVE) |
|
||||
BIT(CS_MEM_EXCLUSIVE) | BIT(CS_SCHED_LOAD_BALANCE),
|
||||
.partition_root_state = PRS_ROOT,
|
||||
.dl_bw_cpu = -1,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -579,6 +580,8 @@ static struct cpuset *dup_or_alloc_cpuset(struct cpuset *cs)
|
||||
if (!trial)
|
||||
return NULL;
|
||||
|
||||
trial->dl_bw_cpu = -1;
|
||||
|
||||
/* Setup cpumask pointer array */
|
||||
cpumask_var_t *pmask[4] = {
|
||||
&trial->cpus_allowed,
|
||||
@@ -2980,6 +2983,7 @@ static void reset_migrate_dl_data(struct cpuset *cs)
|
||||
{
|
||||
cs->nr_migrate_dl_tasks = 0;
|
||||
cs->sum_migrate_dl_bw = 0;
|
||||
cs->dl_bw_cpu = -1;
|
||||
}
|
||||
|
||||
/* Called by cgroups to determine if a cpuset is usable; cpuset_mutex held */
|
||||
@@ -3056,6 +3060,8 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
|
||||
reset_migrate_dl_data(cs);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
cs->dl_bw_cpu = cpu;
|
||||
}
|
||||
|
||||
out_success:
|
||||
@@ -3080,12 +3086,11 @@ static void cpuset_cancel_attach(struct cgroup_taskset *tset)
|
||||
mutex_lock(&cpuset_mutex);
|
||||
dec_attach_in_progress_locked(cs);
|
||||
|
||||
if (cs->nr_migrate_dl_tasks) {
|
||||
int cpu = cpumask_any(cs->effective_cpus);
|
||||
if (cs->dl_bw_cpu >= 0)
|
||||
dl_bw_free(cs->dl_bw_cpu, cs->sum_migrate_dl_bw);
|
||||
|
||||
dl_bw_free(cpu, cs->sum_migrate_dl_bw);
|
||||
if (cs->nr_migrate_dl_tasks)
|
||||
reset_migrate_dl_data(cs);
|
||||
}
|
||||
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ int rdmacg_try_charge(struct rdma_cgroup **rdmacg,
|
||||
ret = PTR_ERR(rpool);
|
||||
goto err;
|
||||
} else {
|
||||
new = rpool->resources[index].usage + 1;
|
||||
new = (s64)rpool->resources[index].usage + 1;
|
||||
if (new > rpool->resources[index].max) {
|
||||
ret = -EAGAIN;
|
||||
goto err;
|
||||
|
||||
@@ -32,6 +32,7 @@ static const struct rhashtable_params scx_sched_hash_params = {
|
||||
.key_len = sizeof_field(struct scx_sched, ops.sub_cgroup_id),
|
||||
.key_offset = offsetof(struct scx_sched, ops.sub_cgroup_id),
|
||||
.head_offset = offsetof(struct scx_sched, hash_node),
|
||||
.insecure_elasticity = true, /* inserted under scx_sched_lock */
|
||||
};
|
||||
|
||||
static struct rhashtable scx_sched_hash;
|
||||
@@ -52,8 +53,6 @@ DEFINE_STATIC_KEY_FALSE(__scx_enabled);
|
||||
DEFINE_STATIC_PERCPU_RWSEM(scx_fork_rwsem);
|
||||
static atomic_t scx_enable_state_var = ATOMIC_INIT(SCX_DISABLED);
|
||||
static DEFINE_RAW_SPINLOCK(scx_bypass_lock);
|
||||
static cpumask_var_t scx_bypass_lb_donee_cpumask;
|
||||
static cpumask_var_t scx_bypass_lb_resched_cpumask;
|
||||
static bool scx_init_task_enabled;
|
||||
static bool scx_switching_all;
|
||||
DEFINE_STATIC_KEY_FALSE(__scx_switched_all);
|
||||
@@ -469,24 +468,35 @@ static inline void update_locked_rq(struct rq *rq)
|
||||
__this_cpu_write(scx_locked_rq_state, rq);
|
||||
}
|
||||
|
||||
#define SCX_CALL_OP(sch, op, rq, args...) \
|
||||
/*
|
||||
* SCX ops can recurse via scx_bpf_sub_dispatch() - the inner call must not
|
||||
* clobber the outer's scx_locked_rq_state. Save it on entry, restore on exit.
|
||||
*/
|
||||
#define SCX_CALL_OP(sch, op, locked_rq, args...) \
|
||||
do { \
|
||||
if (rq) \
|
||||
update_locked_rq(rq); \
|
||||
struct rq *__prev_locked_rq; \
|
||||
\
|
||||
if (locked_rq) { \
|
||||
__prev_locked_rq = scx_locked_rq(); \
|
||||
update_locked_rq(locked_rq); \
|
||||
} \
|
||||
(sch)->ops.op(args); \
|
||||
if (rq) \
|
||||
update_locked_rq(NULL); \
|
||||
if (locked_rq) \
|
||||
update_locked_rq(__prev_locked_rq); \
|
||||
} while (0)
|
||||
|
||||
#define SCX_CALL_OP_RET(sch, op, rq, args...) \
|
||||
#define SCX_CALL_OP_RET(sch, op, locked_rq, args...) \
|
||||
({ \
|
||||
struct rq *__prev_locked_rq; \
|
||||
__typeof__((sch)->ops.op(args)) __ret; \
|
||||
\
|
||||
if (rq) \
|
||||
update_locked_rq(rq); \
|
||||
if (locked_rq) { \
|
||||
__prev_locked_rq = scx_locked_rq(); \
|
||||
update_locked_rq(locked_rq); \
|
||||
} \
|
||||
__ret = (sch)->ops.op(args); \
|
||||
if (rq) \
|
||||
update_locked_rq(NULL); \
|
||||
if (locked_rq) \
|
||||
update_locked_rq(__prev_locked_rq); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
@@ -498,39 +508,39 @@ do { \
|
||||
* those subject tasks.
|
||||
*
|
||||
* Every SCX_CALL_OP_TASK*() call site invokes its op with @p's rq lock held -
|
||||
* either via the @rq argument here, or (for ops.select_cpu()) via @p's pi_lock
|
||||
* held by try_to_wake_up() with rq tracking via scx_rq.in_select_cpu. So if
|
||||
* kf_tasks[] is set, @p's scheduler-protected fields are stable.
|
||||
* either via the @locked_rq argument here, or (for ops.select_cpu()) via @p's
|
||||
* pi_lock held by try_to_wake_up() with rq tracking via scx_rq.in_select_cpu.
|
||||
* So if kf_tasks[] is set, @p's scheduler-protected fields are stable.
|
||||
*
|
||||
* kf_tasks[] can not stack, so task-based SCX ops must not nest. The
|
||||
* WARN_ON_ONCE() in each macro catches a re-entry of any of the three variants
|
||||
* while a previous one is still in progress.
|
||||
*/
|
||||
#define SCX_CALL_OP_TASK(sch, op, rq, task, args...) \
|
||||
#define SCX_CALL_OP_TASK(sch, op, locked_rq, task, args...) \
|
||||
do { \
|
||||
WARN_ON_ONCE(current->scx.kf_tasks[0]); \
|
||||
current->scx.kf_tasks[0] = task; \
|
||||
SCX_CALL_OP((sch), op, rq, task, ##args); \
|
||||
SCX_CALL_OP((sch), op, locked_rq, task, ##args); \
|
||||
current->scx.kf_tasks[0] = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define SCX_CALL_OP_TASK_RET(sch, op, rq, task, args...) \
|
||||
#define SCX_CALL_OP_TASK_RET(sch, op, locked_rq, task, args...) \
|
||||
({ \
|
||||
__typeof__((sch)->ops.op(task, ##args)) __ret; \
|
||||
WARN_ON_ONCE(current->scx.kf_tasks[0]); \
|
||||
current->scx.kf_tasks[0] = task; \
|
||||
__ret = SCX_CALL_OP_RET((sch), op, rq, task, ##args); \
|
||||
__ret = SCX_CALL_OP_RET((sch), op, locked_rq, task, ##args); \
|
||||
current->scx.kf_tasks[0] = NULL; \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#define SCX_CALL_OP_2TASKS_RET(sch, op, rq, task0, task1, args...) \
|
||||
#define SCX_CALL_OP_2TASKS_RET(sch, op, locked_rq, task0, task1, args...) \
|
||||
({ \
|
||||
__typeof__((sch)->ops.op(task0, task1, ##args)) __ret; \
|
||||
WARN_ON_ONCE(current->scx.kf_tasks[0]); \
|
||||
current->scx.kf_tasks[0] = task0; \
|
||||
current->scx.kf_tasks[1] = task1; \
|
||||
__ret = SCX_CALL_OP_RET((sch), op, rq, task0, task1, ##args); \
|
||||
__ret = SCX_CALL_OP_RET((sch), op, locked_rq, task0, task1, ##args); \
|
||||
current->scx.kf_tasks[0] = NULL; \
|
||||
current->scx.kf_tasks[1] = NULL; \
|
||||
__ret; \
|
||||
@@ -1388,18 +1398,55 @@ static void call_task_dequeue(struct scx_sched *sch, struct rq *rq,
|
||||
p->scx.flags &= ~SCX_TASK_IN_CUSTODY;
|
||||
}
|
||||
|
||||
static void local_dsq_post_enq(struct scx_dispatch_q *dsq, struct task_struct *p,
|
||||
u64 enq_flags)
|
||||
static void local_dsq_post_enq(struct scx_sched *sch, struct scx_dispatch_q *dsq,
|
||||
struct task_struct *p, u64 enq_flags)
|
||||
{
|
||||
struct rq *rq = container_of(dsq, struct rq, scx.local_dsq);
|
||||
bool preempt = false;
|
||||
|
||||
call_task_dequeue(scx_root, rq, p, 0);
|
||||
call_task_dequeue(sch, rq, p, 0);
|
||||
|
||||
/*
|
||||
* Note that @rq's lock may be dropped between this enqueue and @p
|
||||
* actually getting on CPU. This gives higher-class tasks (e.g. RT)
|
||||
* an opportunity to wake up on @rq and prevent @p from running.
|
||||
* Here are some concrete examples:
|
||||
*
|
||||
* Example 1:
|
||||
*
|
||||
* We dispatch two tasks from a single ops.dispatch():
|
||||
* - First, a local task to this CPU's local DSQ;
|
||||
* - Second, a local/remote task to a remote CPU's local DSQ.
|
||||
* We must drop the local rq lock in order to finish the second
|
||||
* dispatch. In that time, an RT task can wake up on the local rq.
|
||||
*
|
||||
* Example 2:
|
||||
*
|
||||
* We dispatch a local/remote task to a remote CPU's local DSQ.
|
||||
* We must drop the remote rq lock before the dispatched task can run,
|
||||
* which gives an RT task an opportunity to wake up on the remote rq.
|
||||
*
|
||||
* Both examples work the same if we replace dispatching with moving
|
||||
* the tasks from a user-created DSQ.
|
||||
*
|
||||
* We must detect these wakeups so that we can re-enqueue IMMED tasks
|
||||
* from @rq's local DSQ. scx_wakeup_preempt() serves exactly this
|
||||
* purpose, but for it to be invoked, we must ensure that we bump
|
||||
* @rq->next_class to &ext_sched_class if it's currently idle.
|
||||
*
|
||||
* wakeup_preempt() does the bumping, and since we only invoke it if
|
||||
* @rq->next_class is below &ext_sched_class, it will also
|
||||
* resched_curr(rq).
|
||||
*/
|
||||
if (sched_class_above(p->sched_class, rq->next_class))
|
||||
wakeup_preempt(rq, p, 0);
|
||||
|
||||
/*
|
||||
* If @rq is in balance, the CPU is already vacant and looking for the
|
||||
* next task to run. No need to preempt or trigger resched after moving
|
||||
* @p into its local DSQ.
|
||||
* Note that the wakeup_preempt() above may have already triggered
|
||||
* a resched if @rq->next_class was idle. It's harmless, since
|
||||
* need_resched is cleared immediately after task pick.
|
||||
*/
|
||||
if (rq->scx.flags & SCX_RQ_IN_BALANCE)
|
||||
return;
|
||||
@@ -1407,11 +1454,8 @@ static void local_dsq_post_enq(struct scx_dispatch_q *dsq, struct task_struct *p
|
||||
if ((enq_flags & SCX_ENQ_PREEMPT) && p != rq->curr &&
|
||||
rq->curr->sched_class == &ext_sched_class) {
|
||||
rq->curr->scx.slice = 0;
|
||||
preempt = true;
|
||||
}
|
||||
|
||||
if (preempt || sched_class_above(&ext_sched_class, rq->curr->sched_class))
|
||||
resched_curr(rq);
|
||||
}
|
||||
}
|
||||
|
||||
static void dispatch_enqueue(struct scx_sched *sch, struct rq *rq,
|
||||
@@ -1494,11 +1538,13 @@ static void dispatch_enqueue(struct scx_sched *sch, struct rq *rq,
|
||||
if (!(dsq->id & SCX_DSQ_FLAG_BUILTIN))
|
||||
rcu_assign_pointer(dsq->first_task, p);
|
||||
} else {
|
||||
bool was_empty;
|
||||
|
||||
was_empty = list_empty(&dsq->list);
|
||||
/*
|
||||
* dsq->list can contain parked BPF iterator cursors, so
|
||||
* list_empty() here isn't a reliable proxy for "no real
|
||||
* task in the DSQ". Test dsq->first_task directly.
|
||||
*/
|
||||
list_add_tail(&p->scx.dsq_list.node, &dsq->list);
|
||||
if (was_empty && !(dsq->id & SCX_DSQ_FLAG_BUILTIN))
|
||||
if (!dsq->first_task && !(dsq->id & SCX_DSQ_FLAG_BUILTIN))
|
||||
rcu_assign_pointer(dsq->first_task, p);
|
||||
}
|
||||
}
|
||||
@@ -1518,7 +1564,7 @@ static void dispatch_enqueue(struct scx_sched *sch, struct rq *rq,
|
||||
* concurrently in a non-atomic way.
|
||||
*/
|
||||
if (is_local) {
|
||||
local_dsq_post_enq(dsq, p, enq_flags);
|
||||
local_dsq_post_enq(sch, dsq, p, enq_flags);
|
||||
} else {
|
||||
/*
|
||||
* Task on global/bypass DSQ: leave custody, task on
|
||||
@@ -2129,7 +2175,8 @@ static void wakeup_preempt_scx(struct rq *rq, struct task_struct *p, int wake_fl
|
||||
schedule_reenq_local(rq, 0);
|
||||
}
|
||||
|
||||
static void move_local_task_to_local_dsq(struct task_struct *p, u64 enq_flags,
|
||||
static void move_local_task_to_local_dsq(struct scx_sched *sch,
|
||||
struct task_struct *p, u64 enq_flags,
|
||||
struct scx_dispatch_q *src_dsq,
|
||||
struct rq *dst_rq)
|
||||
{
|
||||
@@ -2149,7 +2196,7 @@ static void move_local_task_to_local_dsq(struct task_struct *p, u64 enq_flags,
|
||||
dsq_inc_nr(dst_dsq, p, enq_flags);
|
||||
p->scx.dsq = dst_dsq;
|
||||
|
||||
local_dsq_post_enq(dst_dsq, p, enq_flags);
|
||||
local_dsq_post_enq(sch, dst_dsq, p, enq_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2370,7 +2417,7 @@ static struct rq *move_task_between_dsqs(struct scx_sched *sch,
|
||||
/* @p is going from a non-local DSQ to a local DSQ */
|
||||
if (src_rq == dst_rq) {
|
||||
task_unlink_from_dsq(p, src_dsq);
|
||||
move_local_task_to_local_dsq(p, enq_flags,
|
||||
move_local_task_to_local_dsq(sch, p, enq_flags,
|
||||
src_dsq, dst_rq);
|
||||
raw_spin_unlock(&src_dsq->lock);
|
||||
} else {
|
||||
@@ -2423,7 +2470,7 @@ static bool consume_dispatch_q(struct scx_sched *sch, struct rq *rq,
|
||||
|
||||
if (rq == task_rq) {
|
||||
task_unlink_from_dsq(p, dsq);
|
||||
move_local_task_to_local_dsq(p, enq_flags, dsq, rq);
|
||||
move_local_task_to_local_dsq(sch, p, enq_flags, dsq, rq);
|
||||
raw_spin_unlock(&dsq->lock);
|
||||
return true;
|
||||
}
|
||||
@@ -3183,7 +3230,7 @@ bool scx_prio_less(const struct task_struct *a, const struct task_struct *b,
|
||||
if (sch_a == sch_b && SCX_HAS_OP(sch_a, core_sched_before) &&
|
||||
!scx_bypassing(sch_a, task_cpu(a)))
|
||||
return SCX_CALL_OP_2TASKS_RET(sch_a, core_sched_before,
|
||||
NULL,
|
||||
task_rq(a),
|
||||
(struct task_struct *)a,
|
||||
(struct task_struct *)b);
|
||||
else
|
||||
@@ -3631,6 +3678,22 @@ static void __scx_disable_and_exit_task(struct scx_sched *sch,
|
||||
SCX_CALL_OP_TASK(sch, exit_task, task_rq(p), p, &args);
|
||||
}
|
||||
|
||||
/*
|
||||
* Undo a completed __scx_init_task(sch, p, false) when scx_enable_task() never
|
||||
* ran. The task state has not been transitioned, so this mirrors the
|
||||
* SCX_TASK_INIT branch in __scx_disable_and_exit_task().
|
||||
*/
|
||||
static void scx_sub_init_cancel_task(struct scx_sched *sch, struct task_struct *p)
|
||||
{
|
||||
struct scx_exit_task_args args = { .cancelled = true };
|
||||
|
||||
lockdep_assert_held(&p->pi_lock);
|
||||
lockdep_assert_rq_held(task_rq(p));
|
||||
|
||||
if (SCX_HAS_OP(sch, exit_task))
|
||||
SCX_CALL_OP_TASK(sch, exit_task, task_rq(p), p, &args);
|
||||
}
|
||||
|
||||
static void scx_disable_and_exit_task(struct scx_sched *sch,
|
||||
struct task_struct *p)
|
||||
{
|
||||
@@ -3639,11 +3702,12 @@ static void scx_disable_and_exit_task(struct scx_sched *sch,
|
||||
/*
|
||||
* If set, @p exited between __scx_init_task() and scx_enable_task() in
|
||||
* scx_sub_enable() and is initialized for both the associated sched and
|
||||
* its parent. Disable and exit for the child too.
|
||||
* its parent. Exit for the child too - scx_enable_task() never ran for
|
||||
* it, so undo only init_task.
|
||||
*/
|
||||
if ((p->scx.flags & SCX_TASK_SUB_INIT) &&
|
||||
!WARN_ON_ONCE(!scx_enabling_sub_sched)) {
|
||||
__scx_disable_and_exit_task(scx_enabling_sub_sched, p);
|
||||
if (p->scx.flags & SCX_TASK_SUB_INIT) {
|
||||
if (!WARN_ON_ONCE(!scx_enabling_sub_sched))
|
||||
scx_sub_init_cancel_task(scx_enabling_sub_sched, p);
|
||||
p->scx.flags &= ~SCX_TASK_SUB_INIT;
|
||||
}
|
||||
|
||||
@@ -4324,9 +4388,10 @@ void scx_cgroup_cancel_attach(struct cgroup_taskset *tset)
|
||||
|
||||
void scx_group_set_weight(struct task_group *tg, unsigned long weight)
|
||||
{
|
||||
struct scx_sched *sch = scx_root;
|
||||
struct scx_sched *sch;
|
||||
|
||||
percpu_down_read(&scx_cgroup_ops_rwsem);
|
||||
sch = scx_root;
|
||||
|
||||
if (scx_cgroup_enabled && SCX_HAS_OP(sch, cgroup_set_weight) &&
|
||||
tg->scx.weight != weight)
|
||||
@@ -4339,9 +4404,10 @@ void scx_group_set_weight(struct task_group *tg, unsigned long weight)
|
||||
|
||||
void scx_group_set_idle(struct task_group *tg, bool idle)
|
||||
{
|
||||
struct scx_sched *sch = scx_root;
|
||||
struct scx_sched *sch;
|
||||
|
||||
percpu_down_read(&scx_cgroup_ops_rwsem);
|
||||
sch = scx_root;
|
||||
|
||||
if (scx_cgroup_enabled && SCX_HAS_OP(sch, cgroup_set_idle))
|
||||
SCX_CALL_OP(sch, cgroup_set_idle, NULL, tg_cgrp(tg), idle);
|
||||
@@ -4355,9 +4421,10 @@ void scx_group_set_idle(struct task_group *tg, bool idle)
|
||||
void scx_group_set_bandwidth(struct task_group *tg,
|
||||
u64 period_us, u64 quota_us, u64 burst_us)
|
||||
{
|
||||
struct scx_sched *sch = scx_root;
|
||||
struct scx_sched *sch;
|
||||
|
||||
percpu_down_read(&scx_cgroup_ops_rwsem);
|
||||
sch = scx_root;
|
||||
|
||||
if (scx_cgroup_enabled && SCX_HAS_OP(sch, cgroup_set_bandwidth) &&
|
||||
(tg->scx.bw_period_us != period_us ||
|
||||
@@ -4380,21 +4447,6 @@ static struct cgroup *root_cgroup(void)
|
||||
return &cgrp_dfl_root.cgrp;
|
||||
}
|
||||
|
||||
static struct cgroup *sch_cgroup(struct scx_sched *sch)
|
||||
{
|
||||
return sch->cgrp;
|
||||
}
|
||||
|
||||
/* for each descendant of @cgrp including self, set ->scx_sched to @sch */
|
||||
static void set_cgroup_sched(struct cgroup *cgrp, struct scx_sched *sch)
|
||||
{
|
||||
struct cgroup *pos;
|
||||
struct cgroup_subsys_state *css;
|
||||
|
||||
cgroup_for_each_live_descendant_pre(pos, css, cgrp)
|
||||
rcu_assign_pointer(pos->scx_sched, sch);
|
||||
}
|
||||
|
||||
static void scx_cgroup_lock(void)
|
||||
{
|
||||
#ifdef CONFIG_EXT_GROUP_SCHED
|
||||
@@ -4412,12 +4464,30 @@ static void scx_cgroup_unlock(void)
|
||||
}
|
||||
#else /* CONFIG_EXT_GROUP_SCHED || CONFIG_EXT_SUB_SCHED */
|
||||
static struct cgroup *root_cgroup(void) { return NULL; }
|
||||
static struct cgroup *sch_cgroup(struct scx_sched *sch) { return NULL; }
|
||||
static void set_cgroup_sched(struct cgroup *cgrp, struct scx_sched *sch) {}
|
||||
static void scx_cgroup_lock(void) {}
|
||||
static void scx_cgroup_unlock(void) {}
|
||||
#endif /* CONFIG_EXT_GROUP_SCHED || CONFIG_EXT_SUB_SCHED */
|
||||
|
||||
#ifdef CONFIG_EXT_SUB_SCHED
|
||||
static struct cgroup *sch_cgroup(struct scx_sched *sch)
|
||||
{
|
||||
return sch->cgrp;
|
||||
}
|
||||
|
||||
/* for each descendant of @cgrp including self, set ->scx_sched to @sch */
|
||||
static void set_cgroup_sched(struct cgroup *cgrp, struct scx_sched *sch)
|
||||
{
|
||||
struct cgroup *pos;
|
||||
struct cgroup_subsys_state *css;
|
||||
|
||||
cgroup_for_each_live_descendant_pre(pos, css, cgrp)
|
||||
rcu_assign_pointer(pos->scx_sched, sch);
|
||||
}
|
||||
#else /* CONFIG_EXT_SUB_SCHED */
|
||||
static struct cgroup *sch_cgroup(struct scx_sched *sch) { return NULL; }
|
||||
static void set_cgroup_sched(struct cgroup *cgrp, struct scx_sched *sch) {}
|
||||
#endif /* CONFIG_EXT_SUB_SCHED */
|
||||
|
||||
/*
|
||||
* Omitted operations:
|
||||
*
|
||||
@@ -4712,6 +4782,8 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
|
||||
irq_work_sync(&sch->disable_irq_work);
|
||||
kthread_destroy_worker(sch->helper);
|
||||
timer_shutdown_sync(&sch->bypass_lb_timer);
|
||||
free_cpumask_var(sch->bypass_lb_donee_cpumask);
|
||||
free_cpumask_var(sch->bypass_lb_resched_cpumask);
|
||||
|
||||
#ifdef CONFIG_EXT_SUB_SCHED
|
||||
kfree(sch->cgrp_path);
|
||||
@@ -4938,6 +5010,25 @@ void scx_softlockup(u32 dur_s)
|
||||
smp_processor_id(), dur_s);
|
||||
}
|
||||
|
||||
/*
|
||||
* scx_hardlockup() runs from NMI and eventually calls scx_claim_exit(),
|
||||
* which takes scx_sched_lock. scx_sched_lock isn't NMI-safe and grabbing
|
||||
* it from NMI context can lead to deadlocks. Defer via irq_work; the
|
||||
* disable path runs off irq_work anyway.
|
||||
*/
|
||||
static atomic_t scx_hardlockup_cpu = ATOMIC_INIT(-1);
|
||||
|
||||
static void scx_hardlockup_irq_workfn(struct irq_work *work)
|
||||
{
|
||||
int cpu = atomic_xchg(&scx_hardlockup_cpu, -1);
|
||||
|
||||
if (cpu >= 0 && handle_lockup("hard lockup - CPU %d", cpu))
|
||||
printk_deferred(KERN_ERR "sched_ext: Hard lockup - CPU %d, disabling BPF scheduler\n",
|
||||
cpu);
|
||||
}
|
||||
|
||||
static DEFINE_IRQ_WORK(scx_hardlockup_irq_work, scx_hardlockup_irq_workfn);
|
||||
|
||||
/**
|
||||
* scx_hardlockup - sched_ext hardlockup handler
|
||||
*
|
||||
@@ -4946,17 +5037,19 @@ void scx_softlockup(u32 dur_s)
|
||||
* Try kicking out the current scheduler in an attempt to recover the system to
|
||||
* a good state before taking more drastic actions.
|
||||
*
|
||||
* Returns %true if sched_ext is enabled and abort was initiated, which may
|
||||
* resolve the reported hardlockup. %false if sched_ext is not enabled or
|
||||
* someone else already initiated abort.
|
||||
* Queues an irq_work; the handle_lockup() call happens in IRQ context (see
|
||||
* scx_hardlockup_irq_workfn).
|
||||
*
|
||||
* Returns %true if sched_ext is enabled and the work was queued, %false
|
||||
* otherwise.
|
||||
*/
|
||||
bool scx_hardlockup(int cpu)
|
||||
{
|
||||
if (!handle_lockup("hard lockup - CPU %d", cpu))
|
||||
if (!rcu_access_pointer(scx_root))
|
||||
return false;
|
||||
|
||||
printk_deferred(KERN_ERR "sched_ext: Hard lockup - CPU %d, disabling BPF scheduler\n",
|
||||
cpu);
|
||||
atomic_cmpxchg(&scx_hardlockup_cpu, -1, cpu);
|
||||
irq_work_queue(&scx_hardlockup_irq_work);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5000,6 +5093,15 @@ static u32 bypass_lb_cpu(struct scx_sched *sch, s32 donor,
|
||||
if (cpumask_empty(donee_mask))
|
||||
break;
|
||||
|
||||
/*
|
||||
* If an earlier pass placed @p on @donor_dsq from a different
|
||||
* CPU and the donee hasn't consumed it yet, @p is still on the
|
||||
* previous CPU and task_rq(@p) != @donor_rq. @p can't be moved
|
||||
* without its rq locked. Skip.
|
||||
*/
|
||||
if (task_rq(p) != donor_rq)
|
||||
continue;
|
||||
|
||||
donee = cpumask_any_and_distribute(donee_mask, p->cpus_ptr);
|
||||
if (donee >= nr_cpu_ids)
|
||||
continue;
|
||||
@@ -5058,8 +5160,8 @@ static u32 bypass_lb_cpu(struct scx_sched *sch, s32 donor,
|
||||
static void bypass_lb_node(struct scx_sched *sch, int node)
|
||||
{
|
||||
const struct cpumask *node_mask = cpumask_of_node(node);
|
||||
struct cpumask *donee_mask = scx_bypass_lb_donee_cpumask;
|
||||
struct cpumask *resched_mask = scx_bypass_lb_resched_cpumask;
|
||||
struct cpumask *donee_mask = sch->bypass_lb_donee_cpumask;
|
||||
struct cpumask *resched_mask = sch->bypass_lb_resched_cpumask;
|
||||
u32 nr_tasks = 0, nr_cpus = 0, nr_balanced = 0;
|
||||
u32 nr_target, nr_donor_target;
|
||||
u32 before_min = U32_MAX, before_max = 0;
|
||||
@@ -5698,6 +5800,8 @@ static void scx_sub_disable(struct scx_sched *sch)
|
||||
|
||||
if (sch->ops.exit)
|
||||
SCX_CALL_OP(sch, exit, NULL, sch->exit_info);
|
||||
if (sch->sub_kset)
|
||||
kset_unregister(sch->sub_kset);
|
||||
kobject_del(&sch->kobj);
|
||||
}
|
||||
#else /* CONFIG_EXT_SUB_SCHED */
|
||||
@@ -5829,6 +5933,10 @@ static void scx_root_disable(struct scx_sched *sch)
|
||||
* could observe an object of the same name still in the hierarchy when
|
||||
* the next scheduler is loaded.
|
||||
*/
|
||||
#ifdef CONFIG_EXT_SUB_SCHED
|
||||
if (sch->sub_kset)
|
||||
kset_unregister(sch->sub_kset);
|
||||
#endif
|
||||
kobject_del(&sch->kobj);
|
||||
|
||||
free_kick_syncs();
|
||||
@@ -5921,6 +6029,25 @@ static void scx_disable(struct scx_sched *sch, enum scx_exit_kind kind)
|
||||
irq_work_queue(&sch->disable_irq_work);
|
||||
}
|
||||
|
||||
/**
|
||||
* scx_flush_disable_work - flush the disable work and wait for it to finish
|
||||
* @sch: the scheduler
|
||||
*
|
||||
* sch->disable_work might still not queued, causing kthread_flush_work()
|
||||
* as a noop. Syncing the irq_work first is required to guarantee the
|
||||
* kthread work has been queued before waiting for it.
|
||||
*/
|
||||
static void scx_flush_disable_work(struct scx_sched *sch)
|
||||
{
|
||||
int kind;
|
||||
|
||||
do {
|
||||
irq_work_sync(&sch->disable_irq_work);
|
||||
kthread_flush_work(&sch->disable_work);
|
||||
kind = atomic_read(&sch->exit_kind);
|
||||
} while (kind != SCX_EXIT_NONE && kind != SCX_EXIT_DONE);
|
||||
}
|
||||
|
||||
static void dump_newline(struct seq_buf *s)
|
||||
{
|
||||
trace_sched_ext_dump("");
|
||||
@@ -6032,9 +6159,8 @@ static void ops_dump_exit(void)
|
||||
scx_dump_data.cpu = -1;
|
||||
}
|
||||
|
||||
static void scx_dump_task(struct scx_sched *sch,
|
||||
struct seq_buf *s, struct scx_dump_ctx *dctx,
|
||||
struct task_struct *p, char marker)
|
||||
static void scx_dump_task(struct scx_sched *sch, struct seq_buf *s, struct scx_dump_ctx *dctx,
|
||||
struct rq *rq, struct task_struct *p, char marker)
|
||||
{
|
||||
static unsigned long bt[SCX_EXIT_BT_LEN];
|
||||
struct scx_sched *task_sch = scx_task_sched(p);
|
||||
@@ -6075,7 +6201,7 @@ static void scx_dump_task(struct scx_sched *sch,
|
||||
|
||||
if (SCX_HAS_OP(sch, dump_task)) {
|
||||
ops_dump_init(s, " ");
|
||||
SCX_CALL_OP(sch, dump_task, NULL, dctx, p);
|
||||
SCX_CALL_OP(sch, dump_task, rq, dctx, p);
|
||||
ops_dump_exit();
|
||||
}
|
||||
|
||||
@@ -6199,8 +6325,7 @@ static void scx_dump_state(struct scx_sched *sch, struct scx_exit_info *ei,
|
||||
used = seq_buf_used(&ns);
|
||||
if (SCX_HAS_OP(sch, dump_cpu)) {
|
||||
ops_dump_init(&ns, " ");
|
||||
SCX_CALL_OP(sch, dump_cpu, NULL,
|
||||
&dctx, cpu, idle);
|
||||
SCX_CALL_OP(sch, dump_cpu, rq, &dctx, cpu, idle);
|
||||
ops_dump_exit();
|
||||
}
|
||||
|
||||
@@ -6223,11 +6348,11 @@ static void scx_dump_state(struct scx_sched *sch, struct scx_exit_info *ei,
|
||||
|
||||
if (rq->curr->sched_class == &ext_sched_class &&
|
||||
(dump_all_tasks || scx_task_on_sched(sch, rq->curr)))
|
||||
scx_dump_task(sch, &s, &dctx, rq->curr, '*');
|
||||
scx_dump_task(sch, &s, &dctx, rq, rq->curr, '*');
|
||||
|
||||
list_for_each_entry(p, &rq->scx.runnable_list, scx.runnable_node)
|
||||
if (dump_all_tasks || scx_task_on_sched(sch, p))
|
||||
scx_dump_task(sch, &s, &dctx, p, ' ');
|
||||
scx_dump_task(sch, &s, &dctx, rq, p, ' ');
|
||||
next:
|
||||
rq_unlock_irqrestore(rq, &rf);
|
||||
}
|
||||
@@ -6437,6 +6562,15 @@ static struct scx_sched *scx_alloc_and_add_sched(struct sched_ext_ops *ops,
|
||||
init_irq_work(&sch->disable_irq_work, scx_disable_irq_workfn);
|
||||
kthread_init_work(&sch->disable_work, scx_disable_workfn);
|
||||
timer_setup(&sch->bypass_lb_timer, scx_bypass_lb_timerfn, 0);
|
||||
|
||||
if (!alloc_cpumask_var(&sch->bypass_lb_donee_cpumask, GFP_KERNEL)) {
|
||||
ret = -ENOMEM;
|
||||
goto err_stop_helper;
|
||||
}
|
||||
if (!alloc_cpumask_var(&sch->bypass_lb_resched_cpumask, GFP_KERNEL)) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_lb_cpumask;
|
||||
}
|
||||
sch->ops = *ops;
|
||||
rcu_assign_pointer(ops->priv, sch);
|
||||
|
||||
@@ -6446,14 +6580,14 @@ static struct scx_sched *scx_alloc_and_add_sched(struct sched_ext_ops *ops,
|
||||
char *buf = kzalloc(PATH_MAX, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
goto err_stop_helper;
|
||||
goto err_free_lb_resched;
|
||||
}
|
||||
cgroup_path(cgrp, buf, PATH_MAX);
|
||||
sch->cgrp_path = kstrdup(buf, GFP_KERNEL);
|
||||
kfree(buf);
|
||||
if (!sch->cgrp_path) {
|
||||
ret = -ENOMEM;
|
||||
goto err_stop_helper;
|
||||
goto err_free_lb_resched;
|
||||
}
|
||||
|
||||
sch->cgrp = cgrp;
|
||||
@@ -6488,10 +6622,12 @@ static struct scx_sched *scx_alloc_and_add_sched(struct sched_ext_ops *ops,
|
||||
#endif /* CONFIG_EXT_SUB_SCHED */
|
||||
return sch;
|
||||
|
||||
#ifdef CONFIG_EXT_SUB_SCHED
|
||||
err_free_lb_resched:
|
||||
free_cpumask_var(sch->bypass_lb_resched_cpumask);
|
||||
err_free_lb_cpumask:
|
||||
free_cpumask_var(sch->bypass_lb_donee_cpumask);
|
||||
err_stop_helper:
|
||||
kthread_destroy_worker(sch->helper);
|
||||
#endif
|
||||
err_free_pcpu:
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (cpu == bypass_fail_cpu)
|
||||
@@ -6510,7 +6646,7 @@ static struct scx_sched *scx_alloc_and_add_sched(struct sched_ext_ops *ops,
|
||||
err_free_sch:
|
||||
kfree(sch);
|
||||
err_put_cgrp:
|
||||
#if defined(CONFIG_EXT_GROUP_SCHED) || defined(CONFIG_EXT_SUB_SCHED)
|
||||
#ifdef CONFIG_EXT_SUB_SCHED
|
||||
cgroup_put(cgrp);
|
||||
#endif
|
||||
return ERR_PTR(ret);
|
||||
@@ -6601,7 +6737,7 @@ static void scx_root_enable_workfn(struct kthread_work *work)
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
#if defined(CONFIG_EXT_GROUP_SCHED) || defined(CONFIG_EXT_SUB_SCHED)
|
||||
#ifdef CONFIG_EXT_SUB_SCHED
|
||||
cgroup_get(cgrp);
|
||||
#endif
|
||||
sch = scx_alloc_and_add_sched(ops, cgrp, NULL);
|
||||
@@ -6639,8 +6775,10 @@ static void scx_root_enable_workfn(struct kthread_work *work)
|
||||
rcu_assign_pointer(scx_root, sch);
|
||||
|
||||
ret = scx_link_sched(sch);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
cpus_read_unlock();
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
scx_idle_enable(ops);
|
||||
|
||||
@@ -6821,7 +6959,7 @@ static void scx_root_enable_workfn(struct kthread_work *work)
|
||||
* completion. sch's base reference will be put by bpf_scx_unreg().
|
||||
*/
|
||||
scx_error(sch, "scx_root_enable() failed (%d)", ret);
|
||||
kthread_flush_work(&sch->disable_work);
|
||||
scx_flush_disable_work(sch);
|
||||
cmd->ret = 0;
|
||||
}
|
||||
|
||||
@@ -7072,23 +7210,30 @@ static void scx_sub_enable_workfn(struct kthread_work *work)
|
||||
abort:
|
||||
put_task_struct(p);
|
||||
scx_task_iter_stop(&sti);
|
||||
scx_enabling_sub_sched = NULL;
|
||||
|
||||
/*
|
||||
* Undo __scx_init_task() for tasks we marked. scx_enable_task() never
|
||||
* ran for @sch on them, so calling scx_disable_task() here would invoke
|
||||
* ops.disable() without a matching ops.enable(). scx_enabling_sub_sched
|
||||
* must stay set until SUB_INIT is cleared from every marked task -
|
||||
* scx_disable_and_exit_task() reads it when a task exits concurrently.
|
||||
*/
|
||||
scx_task_iter_start(&sti, sch->cgrp);
|
||||
while ((p = scx_task_iter_next_locked(&sti))) {
|
||||
if (p->scx.flags & SCX_TASK_SUB_INIT) {
|
||||
__scx_disable_and_exit_task(sch, p);
|
||||
scx_sub_init_cancel_task(sch, p);
|
||||
p->scx.flags &= ~SCX_TASK_SUB_INIT;
|
||||
}
|
||||
}
|
||||
scx_task_iter_stop(&sti);
|
||||
scx_enabling_sub_sched = NULL;
|
||||
err_unlock_and_disable:
|
||||
/* we'll soon enter disable path, keep bypass on */
|
||||
scx_cgroup_unlock();
|
||||
percpu_up_write(&scx_fork_rwsem);
|
||||
err_disable:
|
||||
mutex_unlock(&scx_enable_mutex);
|
||||
kthread_flush_work(&sch->disable_work);
|
||||
scx_flush_disable_work(sch);
|
||||
cmd->ret = 0;
|
||||
}
|
||||
|
||||
@@ -7349,7 +7494,7 @@ static void bpf_scx_unreg(void *kdata, struct bpf_link *link)
|
||||
struct scx_sched *sch = rcu_dereference_protected(ops->priv, true);
|
||||
|
||||
scx_disable(sch, SCX_EXIT_UNREG);
|
||||
kthread_flush_work(&sch->disable_work);
|
||||
scx_flush_disable_work(sch);
|
||||
RCU_INIT_POINTER(ops->priv, NULL);
|
||||
kobject_put(&sch->kobj);
|
||||
}
|
||||
@@ -8033,12 +8178,22 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
|
||||
struct task_struct *p, u64 dsq_id, u64 enq_flags)
|
||||
{
|
||||
struct scx_dispatch_q *src_dsq = kit->dsq, *dst_dsq;
|
||||
struct scx_sched *sch = src_dsq->sched;
|
||||
struct scx_sched *sch;
|
||||
struct rq *this_rq, *src_rq, *locked_rq;
|
||||
bool dispatched = false;
|
||||
bool in_balance;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* The verifier considers an iterator slot initialized on any
|
||||
* KF_ITER_NEW return, so a BPF program may legally reach here after
|
||||
* bpf_iter_scx_dsq_new() failed and left @kit->dsq NULL.
|
||||
*/
|
||||
if (unlikely(!src_dsq))
|
||||
return false;
|
||||
|
||||
sch = src_dsq->sched;
|
||||
|
||||
if (!scx_vet_enq_flags(sch, dsq_id, &enq_flags))
|
||||
return false;
|
||||
|
||||
@@ -8526,7 +8681,7 @@ __bpf_kfunc bool scx_bpf_task_set_slice(struct task_struct *p, u64 slice,
|
||||
|
||||
guard(rcu)();
|
||||
sch = scx_prog_sched(aux);
|
||||
if (unlikely(!scx_task_on_sched(sch, p)))
|
||||
if (unlikely(!sch || !scx_task_on_sched(sch, p)))
|
||||
return false;
|
||||
|
||||
p->scx.slice = slice;
|
||||
@@ -8549,7 +8704,7 @@ __bpf_kfunc bool scx_bpf_task_set_dsq_vtime(struct task_struct *p, u64 vtime,
|
||||
|
||||
guard(rcu)();
|
||||
sch = scx_prog_sched(aux);
|
||||
if (unlikely(!scx_task_on_sched(sch, p)))
|
||||
if (unlikely(!sch || !scx_task_on_sched(sch, p)))
|
||||
return false;
|
||||
|
||||
p->scx.dsq_vtime = vtime;
|
||||
@@ -8633,11 +8788,12 @@ __bpf_kfunc void scx_bpf_kick_cpu(s32 cpu, u64 flags, const struct bpf_prog_aux
|
||||
/**
|
||||
* scx_bpf_dsq_nr_queued - Return the number of queued tasks
|
||||
* @dsq_id: id of the DSQ
|
||||
* @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
|
||||
*
|
||||
* Return the number of tasks in the DSQ matching @dsq_id. If not found,
|
||||
* -%ENOENT is returned.
|
||||
*/
|
||||
__bpf_kfunc s32 scx_bpf_dsq_nr_queued(u64 dsq_id)
|
||||
__bpf_kfunc s32 scx_bpf_dsq_nr_queued(u64 dsq_id, const struct bpf_prog_aux *aux)
|
||||
{
|
||||
struct scx_sched *sch;
|
||||
struct scx_dispatch_q *dsq;
|
||||
@@ -8645,7 +8801,7 @@ __bpf_kfunc s32 scx_bpf_dsq_nr_queued(u64 dsq_id)
|
||||
|
||||
preempt_disable();
|
||||
|
||||
sch = rcu_dereference_sched(scx_root);
|
||||
sch = scx_prog_sched(aux);
|
||||
if (unlikely(!sch)) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
@@ -8677,21 +8833,21 @@ __bpf_kfunc s32 scx_bpf_dsq_nr_queued(u64 dsq_id)
|
||||
/**
|
||||
* scx_bpf_destroy_dsq - Destroy a custom DSQ
|
||||
* @dsq_id: DSQ to destroy
|
||||
* @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
|
||||
*
|
||||
* Destroy the custom DSQ identified by @dsq_id. Only DSQs created with
|
||||
* scx_bpf_create_dsq() can be destroyed. The caller must ensure that the DSQ is
|
||||
* empty and no further tasks are dispatched to it. Ignored if called on a DSQ
|
||||
* which doesn't exist. Can be called from any online scx_ops operations.
|
||||
*/
|
||||
__bpf_kfunc void scx_bpf_destroy_dsq(u64 dsq_id)
|
||||
__bpf_kfunc void scx_bpf_destroy_dsq(u64 dsq_id, const struct bpf_prog_aux *aux)
|
||||
{
|
||||
struct scx_sched *sch;
|
||||
|
||||
rcu_read_lock();
|
||||
sch = rcu_dereference(scx_root);
|
||||
guard(rcu)();
|
||||
sch = scx_prog_sched(aux);
|
||||
if (sch)
|
||||
destroy_dsq(sch, dsq_id);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9445,8 +9601,8 @@ BTF_KFUNCS_START(scx_kfunc_ids_any)
|
||||
BTF_ID_FLAGS(func, scx_bpf_task_set_slice, KF_IMPLICIT_ARGS | KF_RCU);
|
||||
BTF_ID_FLAGS(func, scx_bpf_task_set_dsq_vtime, KF_IMPLICIT_ARGS | KF_RCU);
|
||||
BTF_ID_FLAGS(func, scx_bpf_kick_cpu, KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_nr_queued)
|
||||
BTF_ID_FLAGS(func, scx_bpf_destroy_dsq)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_nr_queued, KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, scx_bpf_destroy_dsq, KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_peek, KF_IMPLICIT_ARGS | KF_RCU_PROTECTED | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_reenq, KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, scx_bpf_reenqueue_local___v2, KF_IMPLICIT_ARGS)
|
||||
@@ -9479,6 +9635,7 @@ BTF_KFUNCS_END(scx_kfunc_ids_any)
|
||||
static const struct btf_kfunc_id_set scx_kfunc_set_any = {
|
||||
.owner = THIS_MODULE,
|
||||
.set = &scx_kfunc_ids_any,
|
||||
.filter = scx_kfunc_context_filter,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -9526,13 +9683,12 @@ static const u32 scx_kf_allow_flags[] = {
|
||||
};
|
||||
|
||||
/*
|
||||
* Verifier-time filter for context-sensitive SCX kfuncs. Registered via the
|
||||
* .filter field on each per-group btf_kfunc_id_set. The BPF core invokes this
|
||||
* for every kfunc call in the registered hook (BPF_PROG_TYPE_STRUCT_OPS or
|
||||
* Verifier-time filter for SCX kfuncs. Registered via the .filter field on
|
||||
* each per-group btf_kfunc_id_set. The BPF core invokes this for every kfunc
|
||||
* call in the registered hook (BPF_PROG_TYPE_STRUCT_OPS or
|
||||
* BPF_PROG_TYPE_SYSCALL), regardless of which set originally introduced the
|
||||
* kfunc - so the filter must short-circuit on kfuncs it doesn't govern (e.g.
|
||||
* scx_kfunc_ids_any) by falling through to "allow" when none of the
|
||||
* context-sensitive sets contain the kfunc.
|
||||
* kfunc - so the filter must short-circuit on kfuncs it doesn't govern by
|
||||
* falling through to "allow" when none of the SCX sets contain the kfunc.
|
||||
*/
|
||||
int scx_kfunc_context_filter(const struct bpf_prog *prog, u32 kfunc_id)
|
||||
{
|
||||
@@ -9541,18 +9697,21 @@ int scx_kfunc_context_filter(const struct bpf_prog *prog, u32 kfunc_id)
|
||||
bool in_enqueue = btf_id_set8_contains(&scx_kfunc_ids_enqueue_dispatch, kfunc_id);
|
||||
bool in_dispatch = btf_id_set8_contains(&scx_kfunc_ids_dispatch, kfunc_id);
|
||||
bool in_cpu_release = btf_id_set8_contains(&scx_kfunc_ids_cpu_release, kfunc_id);
|
||||
bool in_idle = btf_id_set8_contains(&scx_kfunc_ids_idle, kfunc_id);
|
||||
bool in_any = btf_id_set8_contains(&scx_kfunc_ids_any, kfunc_id);
|
||||
u32 moff, flags;
|
||||
|
||||
/* Not a context-sensitive kfunc (e.g. from scx_kfunc_ids_any) - allow. */
|
||||
if (!(in_unlocked || in_select_cpu || in_enqueue || in_dispatch || in_cpu_release))
|
||||
/* Not an SCX kfunc - allow. */
|
||||
if (!(in_unlocked || in_select_cpu || in_enqueue || in_dispatch ||
|
||||
in_cpu_release || in_idle || in_any))
|
||||
return 0;
|
||||
|
||||
/* SYSCALL progs (e.g. BPF test_run()) may call unlocked and select_cpu kfuncs. */
|
||||
if (prog->type == BPF_PROG_TYPE_SYSCALL)
|
||||
return (in_unlocked || in_select_cpu) ? 0 : -EACCES;
|
||||
return (in_unlocked || in_select_cpu || in_idle || in_any) ? 0 : -EACCES;
|
||||
|
||||
if (prog->type != BPF_PROG_TYPE_STRUCT_OPS)
|
||||
return -EACCES;
|
||||
return (in_any || in_idle) ? 0 : -EACCES;
|
||||
|
||||
/*
|
||||
* add_subprog_and_kfunc() collects all kfunc calls, including dead code
|
||||
@@ -9565,14 +9724,15 @@ int scx_kfunc_context_filter(const struct bpf_prog *prog, u32 kfunc_id)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Non-SCX struct_ops: only unlocked kfuncs are safe. The other
|
||||
* context-sensitive kfuncs assume the rq lock is held by the SCX
|
||||
* dispatch path, which doesn't apply to other struct_ops users.
|
||||
* Non-SCX struct_ops: SCX kfuncs are not permitted.
|
||||
*/
|
||||
if (prog->aux->st_ops != &bpf_sched_ext_ops)
|
||||
return in_unlocked ? 0 : -EACCES;
|
||||
return -EACCES;
|
||||
|
||||
/* SCX struct_ops: check the per-op allow list. */
|
||||
if (in_any || in_idle)
|
||||
return 0;
|
||||
|
||||
moff = prog->aux->attach_st_ops_member_off;
|
||||
flags = scx_kf_allow_flags[SCX_MOFF_IDX(moff)];
|
||||
|
||||
@@ -9656,12 +9816,6 @@ static int __init scx_init(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!alloc_cpumask_var(&scx_bypass_lb_donee_cpumask, GFP_KERNEL) ||
|
||||
!alloc_cpumask_var(&scx_bypass_lb_resched_cpumask, GFP_KERNEL)) {
|
||||
pr_err("sched_ext: Failed to allocate cpumasks\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
__initcall(scx_init);
|
||||
|
||||
@@ -927,14 +927,24 @@ static s32 select_cpu_from_kfunc(struct scx_sched *sch, struct task_struct *p,
|
||||
* Accessing p->cpus_ptr / p->nr_cpus_allowed needs either @p's rq
|
||||
* lock or @p's pi_lock. Three cases:
|
||||
*
|
||||
* - inside ops.select_cpu(): try_to_wake_up() holds @p's pi_lock.
|
||||
* - inside ops.select_cpu(): try_to_wake_up() holds the wake-up
|
||||
* task's pi_lock; the wake-up task is recorded in kf_tasks[0]
|
||||
* by SCX_CALL_OP_TASK_RET().
|
||||
* - other rq-locked SCX op: scx_locked_rq() points at the held rq.
|
||||
* - truly unlocked (UNLOCKED ops, SYSCALL, non-SCX struct_ops):
|
||||
* nothing held, take pi_lock ourselves.
|
||||
*
|
||||
* In the first two cases, BPF schedulers may pass an arbitrary task
|
||||
* that the held lock doesn't cover. Refuse those.
|
||||
*/
|
||||
if (this_rq()->scx.in_select_cpu) {
|
||||
if (!scx_kf_arg_task_ok(sch, p))
|
||||
return -EINVAL;
|
||||
lockdep_assert_held(&p->pi_lock);
|
||||
} else if (!scx_locked_rq()) {
|
||||
} else if (scx_locked_rq()) {
|
||||
if (task_rq(p) != scx_locked_rq())
|
||||
goto cross_task;
|
||||
} else {
|
||||
raw_spin_lock_irqsave(&p->pi_lock, irq_flags);
|
||||
we_locked = true;
|
||||
}
|
||||
@@ -960,6 +970,11 @@ static s32 select_cpu_from_kfunc(struct scx_sched *sch, struct task_struct *p,
|
||||
raw_spin_unlock_irqrestore(&p->pi_lock, irq_flags);
|
||||
|
||||
return cpu;
|
||||
|
||||
cross_task:
|
||||
scx_error(sch, "select_cpu kfunc called cross-task on %s[%d]",
|
||||
p->comm, p->pid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1467,6 +1482,7 @@ BTF_KFUNCS_END(scx_kfunc_ids_idle)
|
||||
static const struct btf_kfunc_id_set scx_kfunc_set_idle = {
|
||||
.owner = THIS_MODULE,
|
||||
.set = &scx_kfunc_ids_idle,
|
||||
.filter = scx_kfunc_context_filter,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
struct sched_ext_ops;
|
||||
|
||||
extern struct btf_id_set8 scx_kfunc_ids_idle;
|
||||
extern struct btf_id_set8 scx_kfunc_ids_select_cpu;
|
||||
|
||||
void scx_idle_update_selcpu_topology(struct sched_ext_ops *ops);
|
||||
|
||||
@@ -1075,6 +1075,8 @@ struct scx_sched {
|
||||
struct irq_work disable_irq_work;
|
||||
struct kthread_work disable_work;
|
||||
struct timer_list bypass_lb_timer;
|
||||
cpumask_var_t bypass_lb_donee_cpumask;
|
||||
cpumask_var_t bypass_lb_resched_cpumask;
|
||||
struct rcu_work rcu_work;
|
||||
|
||||
/* all ancestors including self */
|
||||
|
||||
@@ -373,10 +373,10 @@ __init static int init_annotated_branch_stats(void)
|
||||
int ret;
|
||||
|
||||
ret = register_stat_tracer(&annotated_branch_stats);
|
||||
if (!ret) {
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "Warning: could not register "
|
||||
"annotated branches stats\n");
|
||||
return 1;
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -438,10 +438,10 @@ __init static int all_annotated_branch_stats(void)
|
||||
int ret;
|
||||
|
||||
ret = register_stat_tracer(&all_branch_stats);
|
||||
if (!ret) {
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "Warning: could not register "
|
||||
"all branches stats\n");
|
||||
return 1;
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1523,6 +1523,12 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
|
||||
parg->offset = *size;
|
||||
*size += parg->type->size * (parg->count ?: 1);
|
||||
|
||||
if (*size > MAX_PROBE_EVENT_SIZE) {
|
||||
ret = -E2BIG;
|
||||
trace_probe_log_err(ctx->offset, EVENT_TOO_BIG);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (parg->count) {
|
||||
len = strlen(parg->type->fmttype) + 6;
|
||||
parg->fmt = kmalloc(len, GFP_KERNEL);
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#define MAX_BTF_ARGS_LEN 128
|
||||
#define MAX_DENTRY_ARGS_LEN 256
|
||||
#define MAX_STRING_SIZE PATH_MAX
|
||||
#define MAX_PROBE_EVENT_SIZE 3072
|
||||
|
||||
/* Reserved field names */
|
||||
#define FIELD_STRING_IP "__probe_ip"
|
||||
@@ -561,7 +562,8 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
|
||||
C(BAD_TYPE4STR, "This type does not fit for string."),\
|
||||
C(NEED_STRING_TYPE, "$comm and immediate-string only accepts string type"),\
|
||||
C(TOO_MANY_ARGS, "Too many arguments are specified"), \
|
||||
C(TOO_MANY_EARGS, "Too many entry arguments specified"),
|
||||
C(TOO_MANY_EARGS, "Too many entry arguments specified"), \
|
||||
C(EVENT_TOO_BIG, "Event too big (too many fields?)"),
|
||||
|
||||
#undef C
|
||||
#define C(a, b) TP_ERR_##a
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user