2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00

Including fixes from wireless. The ath12k fix to avoid FW crashes

requires adding support for a number of new FW commands so
 it's quite large in terms of LoC. The rest is relatively small.
 
 Current release - fix to a fix:
 
  - ptp: fix breakage after ptp_vclock_in_use() rework
 
 Current release - regressions:
 
  - openvswitch: allocate struct ovs_pcpu_storage dynamically, static
    allocation may exhaust module loader limit on smaller systems
 
 Previous releases - regressions:
 
  - tcp: fix tcp_packet_delayed() for peers with no selective ACK support
 
 Previous releases - always broken:
 
  - wifi: ath12k: don't activate more links than firmware supports
 
  - tcp: make sure sockets open via passive TFO have valid NAPI ID
 
  - eth: bnxt_en: update MRU and RSS table of RSS contexts on queue reset,
    prevent Rx queues from silently hanging after queue reset
 
  - NFC: uart: set tty->disc_data only in success path
 
 Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE6jPA+I1ugmIBA4hXMUZtbf5SIrsFAmhUPLgACgkQMUZtbf5S
 IrvrXxAAvREuPy2BhCrQW77TeVW2ikBlGYYp+dCIL4r4kfqquHU3C+R/GZ5yRbcA
 fiRMkRv7J4xhHOT8AnFmFiWGz6QEJfESy3jSQUfcLssHeandEjJbcZ0qVunEpE4e
 rt0leygfY+axEP3ewoGQEP7Ds2Ku+L3BrIetwsUZPFo9pVAtGJ4XOV56SRYPP7vL
 7toaHcxXtx5vL9Wlb9vulom9kfZU4/hfDMS9/FES+aE4QnRDB+sh0v6wL4pocLrj
 7swSqWCY7vjd7vJck4Ta/O9+bL6kz1wG4Qvv5enXGJlksiBgCpShfbzGMZTbegeP
 8gCj4heAATCRmzCmfkRk5sWzPqedtLI5rzb7jUYYF4wiZpJwjwLzrDxopazFogXs
 kKDCD3lxDmUTV4AkT/5hn8Rlf4CApMrhPs+zhzQQ620Sj/G3k2lyepKmKjOUSAZ4
 fEFKmP9Mzt3fKt2sOi5ETlrpuiSv5CCAZfnCg7UoBXrQM1Rxyn+utLMeGM6oHXRa
 iH9GwVN2cycEXl16y8HVCui8lzBYyZKVbyiRBM6k3Pl6ZozDuHvCRmHgs8g9pTja
 3hGe2wjYcsi+HKrF1zDQrTJbhKzsNMpJej0KIZ06xjyXAgr1VV0noEq/iQsPEj4z
 033DotqhyoFXc6ERmOaca8LfE2kHolwT+0leO+i0sbdmVlSFxB8=
 =2mAa
 -----END PGP SIGNATURE-----

Merge tag 'net-6.16-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net

Pull networking fixes from Jakub Kicinski:
 "Including fixes from wireless.

  The ath12k fix to avoid FW crashes requires adding support for a
  number of new FW commands so it's quite large in terms of LoC. The
  rest is relatively small.

  Current release - fix to a fix:

   - ptp: fix breakage after ptp_vclock_in_use() rework

  Current release - regressions:

   - openvswitch: allocate struct ovs_pcpu_storage dynamically, static
     allocation may exhaust module loader limit on smaller systems

  Previous releases - regressions:

   - tcp: fix tcp_packet_delayed() for peers with no selective ACK
     support

  Previous releases - always broken:

   - wifi: ath12k: don't activate more links than firmware supports

   - tcp: make sure sockets open via passive TFO have valid NAPI ID

   - eth: bnxt_en: update MRU and RSS table of RSS contexts on queue
     reset, prevent Rx queues from silently hanging after queue reset

   - NFC: uart: set tty->disc_data only in success path"

* tag 'net-6.16-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (59 commits)
  net: airoha: Differentiate hwfd buffer size for QDMA0 and QDMA1
  net: airoha: Compute number of descriptors according to reserved memory size
  tools: ynl: fix mixing ops and notifications on one socket
  net: atm: fix /proc/net/atm/lec handling
  net: atm: add lec_mutex
  mlxbf_gige: return EPROBE_DEFER if PHY IRQ is not available
  net: airoha: Always check return value from airoha_ppe_foe_get_entry()
  NFC: nci: uart: Set tty->disc_data only in success path
  calipso: Fix null-ptr-deref in calipso_req_{set,del}attr().
  MAINTAINERS: Remove Shannon Nelson from MAINTAINERS file
  net: lan743x: fix potential out-of-bounds write in lan743x_ptp_io_event_clock_get()
  eth: fbnic: avoid double free when failing to DMA-map FW msg
  tcp: fix passive TFO socket having invalid NAPI ID
  selftests: net: add test for passive TFO socket NAPI ID
  selftests: net: add passive TFO test binary
  selftests: netdevsim: improve lib.sh include in peer.sh
  tipc: fix null-ptr-deref when acquiring remote ip of ethernet bearer
  Octeontx2-pf: Fix Backpresure configuration
  net: ftgmac100: select FIXED_PHY
  net: ethtool: remove duplicate defines for family info
  ...
This commit is contained in:
Linus Torvalds 2025-06-19 10:21:32 -07:00
commit 5c8013ae2e
64 changed files with 2090 additions and 287 deletions

View File

@ -693,9 +693,10 @@ Serge Hallyn <sergeh@kernel.org> <serge.hallyn@canonical.com>
Serge Hallyn <sergeh@kernel.org> <serue@us.ibm.com> Serge Hallyn <sergeh@kernel.org> <serue@us.ibm.com>
Seth Forshee <sforshee@kernel.org> <seth.forshee@canonical.com> Seth Forshee <sforshee@kernel.org> <seth.forshee@canonical.com>
Shakeel Butt <shakeel.butt@linux.dev> <shakeelb@google.com> Shakeel Butt <shakeel.butt@linux.dev> <shakeelb@google.com>
Shannon Nelson <shannon.nelson@amd.com> <snelson@pensando.io> Shannon Nelson <sln@onemain.com> <shannon.nelson@amd.com>
Shannon Nelson <shannon.nelson@amd.com> <shannon.nelson@intel.com> Shannon Nelson <sln@onemain.com> <snelson@pensando.io>
Shannon Nelson <shannon.nelson@amd.com> <shannon.nelson@oracle.com> Shannon Nelson <sln@onemain.com> <shannon.nelson@intel.com>
Shannon Nelson <sln@onemain.com> <shannon.nelson@oracle.com>
Sharath Chandra Vurukala <quic_sharathv@quicinc.com> <sharathv@codeaurora.org> Sharath Chandra Vurukala <quic_sharathv@quicinc.com> <sharathv@codeaurora.org>
Shiraz Hashim <shiraz.linux.kernel@gmail.com> <shiraz.hashim@st.com> Shiraz Hashim <shiraz.linux.kernel@gmail.com> <shiraz.hashim@st.com>
Shuah Khan <shuah@kernel.org> <shuahkhan@gmail.com> Shuah Khan <shuah@kernel.org> <shuahkhan@gmail.com>

View File

@ -7,6 +7,9 @@ protocol: genetlink-legacy
doc: Partial family for Ethtool Netlink. doc: Partial family for Ethtool Netlink.
uapi-header: linux/ethtool_netlink_generated.h uapi-header: linux/ethtool_netlink_generated.h
c-family-name: ethtool-genl-name
c-version-name: ethtool-genl-version
definitions: definitions:
- -
name: udp-tunnel-type name: udp-tunnel-type

View File

@ -1157,7 +1157,6 @@ F: arch/x86/include/asm/amd/node.h
F: arch/x86/kernel/amd_node.c F: arch/x86/kernel/amd_node.c
AMD PDS CORE DRIVER AMD PDS CORE DRIVER
M: Shannon Nelson <shannon.nelson@amd.com>
M: Brett Creeley <brett.creeley@amd.com> M: Brett Creeley <brett.creeley@amd.com>
L: netdev@vger.kernel.org L: netdev@vger.kernel.org
S: Maintained S: Maintained
@ -9942,7 +9941,6 @@ F: drivers/fwctl/mlx5/
FWCTL PDS DRIVER FWCTL PDS DRIVER
M: Brett Creeley <brett.creeley@amd.com> M: Brett Creeley <brett.creeley@amd.com>
R: Shannon Nelson <shannon.nelson@amd.com>
L: linux-kernel@vger.kernel.org L: linux-kernel@vger.kernel.org
S: Maintained S: Maintained
F: drivers/fwctl/pds/ F: drivers/fwctl/pds/
@ -19379,7 +19377,7 @@ F: crypto/pcrypt.c
F: include/crypto/pcrypt.h F: include/crypto/pcrypt.h
PDS DSC VIRTIO DATA PATH ACCELERATOR PDS DSC VIRTIO DATA PATH ACCELERATOR
R: Shannon Nelson <shannon.nelson@amd.com> R: Brett Creeley <brett.creeley@amd.com>
F: drivers/vdpa/pds/ F: drivers/vdpa/pds/
PECI HARDWARE MONITORING DRIVERS PECI HARDWARE MONITORING DRIVERS
@ -19401,7 +19399,6 @@ F: include/linux/peci-cpu.h
F: include/linux/peci.h F: include/linux/peci.h
PENSANDO ETHERNET DRIVERS PENSANDO ETHERNET DRIVERS
M: Shannon Nelson <shannon.nelson@amd.com>
M: Brett Creeley <brett.creeley@amd.com> M: Brett Creeley <brett.creeley@amd.com>
L: netdev@vger.kernel.org L: netdev@vger.kernel.org
S: Maintained S: Maintained

View File

@ -288,7 +288,9 @@ static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb)
struct sk_buff *new_skb; struct sk_buff *new_skb;
int result = 0; int result = 0;
if (!skb->len) return 0; if (skb->len < sizeof(struct atmtcp_hdr))
goto done;
dev = vcc->dev_data; dev = vcc->dev_data;
hdr = (struct atmtcp_hdr *) skb->data; hdr = (struct atmtcp_hdr *) skb->data;
if (hdr->length == ATMTCP_HDR_MAGIC) { if (hdr->length == ATMTCP_HDR_MAGIC) {

View File

@ -411,10 +411,11 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
priv = cdev_to_priv(mcan_class); priv = cdev_to_priv(mcan_class);
priv->power = devm_regulator_get_optional(&spi->dev, "vsup"); priv->power = devm_regulator_get_optional(&spi->dev, "vsup");
if (IS_ERR(priv->power)) {
if (PTR_ERR(priv->power) == -EPROBE_DEFER) { if (PTR_ERR(priv->power) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER; ret = -EPROBE_DEFER;
goto out_m_can_class_free_dev; goto out_m_can_class_free_dev;
} else { }
priv->power = NULL; priv->power = NULL;
} }

View File

@ -1065,23 +1065,18 @@ static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q)
static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma)
{ {
int size, index, num_desc = HW_DSCP_NUM;
struct airoha_eth *eth = qdma->eth; struct airoha_eth *eth = qdma->eth;
int id = qdma - &eth->qdma[0]; int id = qdma - &eth->qdma[0];
u32 status, buf_size;
dma_addr_t dma_addr; dma_addr_t dma_addr;
const char *name; const char *name;
int size, index;
u32 status;
size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc);
if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL))
return -ENOMEM;
airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr);
name = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d-buf", id); name = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d-buf", id);
if (!name) if (!name)
return -ENOMEM; return -ENOMEM;
buf_size = id ? AIROHA_MAX_PACKET_SIZE / 2 : AIROHA_MAX_PACKET_SIZE;
index = of_property_match_string(eth->dev->of_node, index = of_property_match_string(eth->dev->of_node,
"memory-region-names", name); "memory-region-names", name);
if (index >= 0) { if (index >= 0) {
@ -1099,8 +1094,12 @@ static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma)
rmem = of_reserved_mem_lookup(np); rmem = of_reserved_mem_lookup(np);
of_node_put(np); of_node_put(np);
dma_addr = rmem->base; dma_addr = rmem->base;
/* Compute the number of hw descriptors according to the
* reserved memory size and the payload buffer size
*/
num_desc = div_u64(rmem->size, buf_size);
} else { } else {
size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; size = buf_size * num_desc;
if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, if (!dmam_alloc_coherent(eth->dev, size, &dma_addr,
GFP_KERNEL)) GFP_KERNEL))
return -ENOMEM; return -ENOMEM;
@ -1108,15 +1107,21 @@ static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma)
airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr);
size = num_desc * sizeof(struct airoha_qdma_fwd_desc);
if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL))
return -ENOMEM;
airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr);
/* QDMA0: 2KB. QDMA1: 1KB */
airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG,
HW_FWD_DSCP_PAYLOAD_SIZE_MASK, HW_FWD_DSCP_PAYLOAD_SIZE_MASK,
FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0)); FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, !!id));
airoha_qdma_rmw(qdma, REG_FWD_DSCP_LOW_THR, FWD_DSCP_LOW_THR_MASK, airoha_qdma_rmw(qdma, REG_FWD_DSCP_LOW_THR, FWD_DSCP_LOW_THR_MASK,
FIELD_PREP(FWD_DSCP_LOW_THR_MASK, 128)); FIELD_PREP(FWD_DSCP_LOW_THR_MASK, 128));
airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG, airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG,
LMGR_INIT_START | LMGR_SRAM_MODE_MASK | LMGR_INIT_START | LMGR_SRAM_MODE_MASK |
HW_FWD_DESC_NUM_MASK, HW_FWD_DESC_NUM_MASK,
FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) | FIELD_PREP(HW_FWD_DESC_NUM_MASK, num_desc) |
LMGR_INIT_START | LMGR_SRAM_MODE_MASK); LMGR_INIT_START | LMGR_SRAM_MODE_MASK);
return read_poll_timeout(airoha_qdma_rr, status, return read_poll_timeout(airoha_qdma_rr, status,

View File

@ -809,8 +809,10 @@ airoha_ppe_foe_flow_l2_entry_update(struct airoha_ppe *ppe,
int idle; int idle;
hwe = airoha_ppe_foe_get_entry(ppe, iter->hash); hwe = airoha_ppe_foe_get_entry(ppe, iter->hash);
ib1 = READ_ONCE(hwe->ib1); if (!hwe)
continue;
ib1 = READ_ONCE(hwe->ib1);
state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1); state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1);
if (state != AIROHA_FOE_STATE_BIND) { if (state != AIROHA_FOE_STATE_BIND) {
iter->hash = 0xffff; iter->hash = 0xffff;

View File

@ -10780,6 +10780,72 @@ void bnxt_del_one_rss_ctx(struct bnxt *bp, struct bnxt_rss_ctx *rss_ctx,
bp->num_rss_ctx--; bp->num_rss_ctx--;
} }
static bool bnxt_vnic_has_rx_ring(struct bnxt *bp, struct bnxt_vnic_info *vnic,
int rxr_id)
{
u16 tbl_size = bnxt_get_rxfh_indir_size(bp->dev);
int i, vnic_rx;
/* Ntuple VNIC always has all the rx rings. Any change of ring id
* must be updated because a future filter may use it.
*/
if (vnic->flags & BNXT_VNIC_NTUPLE_FLAG)
return true;
for (i = 0; i < tbl_size; i++) {
if (vnic->flags & BNXT_VNIC_RSSCTX_FLAG)
vnic_rx = ethtool_rxfh_context_indir(vnic->rss_ctx)[i];
else
vnic_rx = bp->rss_indir_tbl[i];
if (rxr_id == vnic_rx)
return true;
}
return false;
}
static int bnxt_set_vnic_mru_p5(struct bnxt *bp, struct bnxt_vnic_info *vnic,
u16 mru, int rxr_id)
{
int rc;
if (!bnxt_vnic_has_rx_ring(bp, vnic, rxr_id))
return 0;
if (mru) {
rc = bnxt_hwrm_vnic_set_rss_p5(bp, vnic, true);
if (rc) {
netdev_err(bp->dev, "hwrm vnic %d set rss failure rc: %d\n",
vnic->vnic_id, rc);
return rc;
}
}
vnic->mru = mru;
bnxt_hwrm_vnic_update(bp, vnic,
VNIC_UPDATE_REQ_ENABLES_MRU_VALID);
return 0;
}
static int bnxt_set_rss_ctx_vnic_mru(struct bnxt *bp, u16 mru, int rxr_id)
{
struct ethtool_rxfh_context *ctx;
unsigned long context;
int rc;
xa_for_each(&bp->dev->ethtool->rss_ctx, context, ctx) {
struct bnxt_rss_ctx *rss_ctx = ethtool_rxfh_context_priv(ctx);
struct bnxt_vnic_info *vnic = &rss_ctx->vnic;
rc = bnxt_set_vnic_mru_p5(bp, vnic, mru, rxr_id);
if (rc)
return rc;
}
return 0;
}
static void bnxt_hwrm_realloc_rss_ctx_vnic(struct bnxt *bp) static void bnxt_hwrm_realloc_rss_ctx_vnic(struct bnxt *bp)
{ {
bool set_tpa = !!(bp->flags & BNXT_FLAG_TPA); bool set_tpa = !!(bp->flags & BNXT_FLAG_TPA);
@ -15927,6 +15993,7 @@ static int bnxt_queue_start(struct net_device *dev, void *qmem, int idx)
struct bnxt_vnic_info *vnic; struct bnxt_vnic_info *vnic;
struct bnxt_napi *bnapi; struct bnxt_napi *bnapi;
int i, rc; int i, rc;
u16 mru;
rxr = &bp->rx_ring[idx]; rxr = &bp->rx_ring[idx];
clone = qmem; clone = qmem;
@ -15977,21 +16044,15 @@ static int bnxt_queue_start(struct net_device *dev, void *qmem, int idx)
napi_enable_locked(&bnapi->napi); napi_enable_locked(&bnapi->napi);
bnxt_db_nq_arm(bp, &cpr->cp_db, cpr->cp_raw_cons); bnxt_db_nq_arm(bp, &cpr->cp_db, cpr->cp_raw_cons);
mru = bp->dev->mtu + ETH_HLEN + VLAN_HLEN;
for (i = 0; i < bp->nr_vnics; i++) { for (i = 0; i < bp->nr_vnics; i++) {
vnic = &bp->vnic_info[i]; vnic = &bp->vnic_info[i];
rc = bnxt_hwrm_vnic_set_rss_p5(bp, vnic, true); rc = bnxt_set_vnic_mru_p5(bp, vnic, mru, idx);
if (rc) { if (rc)
netdev_err(bp->dev, "hwrm vnic %d set rss failure rc: %d\n",
vnic->vnic_id, rc);
return rc; return rc;
} }
vnic->mru = bp->dev->mtu + ETH_HLEN + VLAN_HLEN; return bnxt_set_rss_ctx_vnic_mru(bp, mru, idx);
bnxt_hwrm_vnic_update(bp, vnic,
VNIC_UPDATE_REQ_ENABLES_MRU_VALID);
}
return 0;
err_reset: err_reset:
netdev_err(bp->dev, "Unexpected HWRM error during queue start rc: %d\n", netdev_err(bp->dev, "Unexpected HWRM error during queue start rc: %d\n",
@ -16013,10 +16074,10 @@ static int bnxt_queue_stop(struct net_device *dev, void *qmem, int idx)
for (i = 0; i < bp->nr_vnics; i++) { for (i = 0; i < bp->nr_vnics; i++) {
vnic = &bp->vnic_info[i]; vnic = &bp->vnic_info[i];
vnic->mru = 0;
bnxt_hwrm_vnic_update(bp, vnic, bnxt_set_vnic_mru_p5(bp, vnic, 0, idx);
VNIC_UPDATE_REQ_ENABLES_MRU_VALID);
} }
bnxt_set_rss_ctx_vnic_mru(bp, 0, idx);
/* Make sure NAPI sees that the VNIC is disabled */ /* Make sure NAPI sees that the VNIC is disabled */
synchronize_net(); synchronize_net();
rxr = &bp->rx_ring[idx]; rxr = &bp->rx_ring[idx];

View File

@ -231,10 +231,9 @@ void bnxt_ulp_stop(struct bnxt *bp)
return; return;
mutex_lock(&edev->en_dev_lock); mutex_lock(&edev->en_dev_lock);
if (!bnxt_ulp_registered(edev)) { if (!bnxt_ulp_registered(edev) ||
mutex_unlock(&edev->en_dev_lock); (edev->flags & BNXT_EN_FLAG_ULP_STOPPED))
return; goto ulp_stop_exit;
}
edev->flags |= BNXT_EN_FLAG_ULP_STOPPED; edev->flags |= BNXT_EN_FLAG_ULP_STOPPED;
if (aux_priv) { if (aux_priv) {
@ -250,6 +249,7 @@ void bnxt_ulp_stop(struct bnxt *bp)
adrv->suspend(adev, pm); adrv->suspend(adev, pm);
} }
} }
ulp_stop_exit:
mutex_unlock(&edev->en_dev_lock); mutex_unlock(&edev->en_dev_lock);
} }
@ -258,19 +258,13 @@ void bnxt_ulp_start(struct bnxt *bp, int err)
struct bnxt_aux_priv *aux_priv = bp->aux_priv; struct bnxt_aux_priv *aux_priv = bp->aux_priv;
struct bnxt_en_dev *edev = bp->edev; struct bnxt_en_dev *edev = bp->edev;
if (!edev) if (!edev || err)
return;
edev->flags &= ~BNXT_EN_FLAG_ULP_STOPPED;
if (err)
return; return;
mutex_lock(&edev->en_dev_lock); mutex_lock(&edev->en_dev_lock);
if (!bnxt_ulp_registered(edev)) { if (!bnxt_ulp_registered(edev) ||
mutex_unlock(&edev->en_dev_lock); !(edev->flags & BNXT_EN_FLAG_ULP_STOPPED))
return; goto ulp_start_exit;
}
if (edev->ulp_tbl->msix_requested) if (edev->ulp_tbl->msix_requested)
bnxt_fill_msix_vecs(bp, edev->msix_entries); bnxt_fill_msix_vecs(bp, edev->msix_entries);
@ -287,6 +281,8 @@ void bnxt_ulp_start(struct bnxt *bp, int err)
adrv->resume(adev); adrv->resume(adev);
} }
} }
ulp_start_exit:
edev->flags &= ~BNXT_EN_FLAG_ULP_STOPPED;
mutex_unlock(&edev->en_dev_lock); mutex_unlock(&edev->en_dev_lock);
} }

View File

@ -31,6 +31,7 @@ config FTGMAC100
depends on ARM || COMPILE_TEST depends on ARM || COMPILE_TEST
depends on !64BIT || BROKEN depends on !64BIT || BROKEN
select PHYLIB select PHYLIB
select FIXED_PHY
select MDIO_ASPEED if MACH_ASPEED_G6 select MDIO_ASPEED if MACH_ASPEED_G6
select CRC32 select CRC32
help help

View File

@ -3534,9 +3534,6 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca)
case e1000_pch_cnp: case e1000_pch_cnp:
case e1000_pch_tgp: case e1000_pch_tgp:
case e1000_pch_adp: case e1000_pch_adp:
case e1000_pch_mtp:
case e1000_pch_lnp:
case e1000_pch_ptp:
case e1000_pch_nvp: case e1000_pch_nvp:
if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) { if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) {
/* Stable 24MHz frequency */ /* Stable 24MHz frequency */
@ -3552,6 +3549,17 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca)
adapter->cc.shift = shift; adapter->cc.shift = shift;
} }
break; break;
case e1000_pch_mtp:
case e1000_pch_lnp:
case e1000_pch_ptp:
/* System firmware can misreport this value, so set it to a
* stable 38400KHz frequency.
*/
incperiod = INCPERIOD_38400KHZ;
incvalue = INCVALUE_38400KHZ;
shift = INCVALUE_SHIFT_38400KHZ;
adapter->cc.shift = shift;
break;
case e1000_82574: case e1000_82574:
case e1000_82583: case e1000_82583:
/* Stable 25MHz frequency */ /* Stable 25MHz frequency */

View File

@ -295,15 +295,17 @@ void e1000e_ptp_init(struct e1000_adapter *adapter)
case e1000_pch_cnp: case e1000_pch_cnp:
case e1000_pch_tgp: case e1000_pch_tgp:
case e1000_pch_adp: case e1000_pch_adp:
case e1000_pch_mtp:
case e1000_pch_lnp:
case e1000_pch_ptp:
case e1000_pch_nvp: case e1000_pch_nvp:
if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI)
adapter->ptp_clock_info.max_adj = MAX_PPB_24MHZ; adapter->ptp_clock_info.max_adj = MAX_PPB_24MHZ;
else else
adapter->ptp_clock_info.max_adj = MAX_PPB_38400KHZ; adapter->ptp_clock_info.max_adj = MAX_PPB_38400KHZ;
break; break;
case e1000_pch_mtp:
case e1000_pch_lnp:
case e1000_pch_ptp:
adapter->ptp_clock_info.max_adj = MAX_PPB_38400KHZ;
break;
case e1000_82574: case e1000_82574:
case e1000_82583: case e1000_82583:
adapter->ptp_clock_info.max_adj = MAX_PPB_25MHZ; adapter->ptp_clock_info.max_adj = MAX_PPB_25MHZ;

View File

@ -377,6 +377,50 @@ ice_arfs_is_perfect_flow_set(struct ice_hw *hw, __be16 l3_proto, u8 l4_proto)
return false; return false;
} }
/**
* ice_arfs_cmp - Check if aRFS filter matches this flow.
* @fltr_info: filter info of the saved ARFS entry.
* @fk: flow dissector keys.
* @n_proto: One of htons(ETH_P_IP) or htons(ETH_P_IPV6).
* @ip_proto: One of IPPROTO_TCP or IPPROTO_UDP.
*
* Since this function assumes limited values for n_proto and ip_proto, it
* is meant to be called only from ice_rx_flow_steer().
*
* Return:
* * true - fltr_info refers to the same flow as fk.
* * false - fltr_info and fk refer to different flows.
*/
static bool
ice_arfs_cmp(const struct ice_fdir_fltr *fltr_info, const struct flow_keys *fk,
__be16 n_proto, u8 ip_proto)
{
/* Determine if the filter is for IPv4 or IPv6 based on flow_type,
* which is one of ICE_FLTR_PTYPE_NONF_IPV{4,6}_{TCP,UDP}.
*/
bool is_v4 = fltr_info->flow_type == ICE_FLTR_PTYPE_NONF_IPV4_TCP ||
fltr_info->flow_type == ICE_FLTR_PTYPE_NONF_IPV4_UDP;
/* Following checks are arranged in the quickest and most discriminative
* fields first for early failure.
*/
if (is_v4)
return n_proto == htons(ETH_P_IP) &&
fltr_info->ip.v4.src_port == fk->ports.src &&
fltr_info->ip.v4.dst_port == fk->ports.dst &&
fltr_info->ip.v4.src_ip == fk->addrs.v4addrs.src &&
fltr_info->ip.v4.dst_ip == fk->addrs.v4addrs.dst &&
fltr_info->ip.v4.proto == ip_proto;
return fltr_info->ip.v6.src_port == fk->ports.src &&
fltr_info->ip.v6.dst_port == fk->ports.dst &&
fltr_info->ip.v6.proto == ip_proto &&
!memcmp(&fltr_info->ip.v6.src_ip, &fk->addrs.v6addrs.src,
sizeof(struct in6_addr)) &&
!memcmp(&fltr_info->ip.v6.dst_ip, &fk->addrs.v6addrs.dst,
sizeof(struct in6_addr));
}
/** /**
* ice_rx_flow_steer - steer the Rx flow to where application is being run * ice_rx_flow_steer - steer the Rx flow to where application is being run
* @netdev: ptr to the netdev being adjusted * @netdev: ptr to the netdev being adjusted
@ -448,6 +492,10 @@ ice_rx_flow_steer(struct net_device *netdev, const struct sk_buff *skb,
continue; continue;
fltr_info = &arfs_entry->fltr_info; fltr_info = &arfs_entry->fltr_info;
if (!ice_arfs_cmp(fltr_info, &fk, n_proto, ip_proto))
continue;
ret = fltr_info->fltr_id; ret = fltr_info->fltr_id;
if (fltr_info->q_index == rxq_idx || if (fltr_info->q_index == rxq_idx ||

View File

@ -508,10 +508,14 @@ err_create_repr:
*/ */
int ice_eswitch_attach_vf(struct ice_pf *pf, struct ice_vf *vf) int ice_eswitch_attach_vf(struct ice_pf *pf, struct ice_vf *vf)
{ {
struct ice_repr *repr = ice_repr_create_vf(vf);
struct devlink *devlink = priv_to_devlink(pf); struct devlink *devlink = priv_to_devlink(pf);
struct ice_repr *repr;
int err; int err;
if (!ice_is_eswitch_mode_switchdev(pf))
return 0;
repr = ice_repr_create_vf(vf);
if (IS_ERR(repr)) if (IS_ERR(repr))
return PTR_ERR(repr); return PTR_ERR(repr);

View File

@ -1822,7 +1822,7 @@ int otx2_nix_config_bp(struct otx2_nic *pfvf, bool enable)
req->chan_cnt = IEEE_8021QAZ_MAX_TCS; req->chan_cnt = IEEE_8021QAZ_MAX_TCS;
req->bpid_per_chan = 1; req->bpid_per_chan = 1;
} else { } else {
req->chan_cnt = 1; req->chan_cnt = pfvf->hw.rx_chan_cnt;
req->bpid_per_chan = 0; req->bpid_per_chan = 0;
} }
@ -1847,7 +1847,7 @@ int otx2_nix_cpt_config_bp(struct otx2_nic *pfvf, bool enable)
req->chan_cnt = IEEE_8021QAZ_MAX_TCS; req->chan_cnt = IEEE_8021QAZ_MAX_TCS;
req->bpid_per_chan = 1; req->bpid_per_chan = 1;
} else { } else {
req->chan_cnt = 1; req->chan_cnt = pfvf->hw.rx_chan_cnt;
req->bpid_per_chan = 0; req->bpid_per_chan = 0;
} }

View File

@ -447,8 +447,10 @@ static int mlxbf_gige_probe(struct platform_device *pdev)
priv->llu_plu_irq = platform_get_irq(pdev, MLXBF_GIGE_LLU_PLU_INTR_IDX); priv->llu_plu_irq = platform_get_irq(pdev, MLXBF_GIGE_LLU_PLU_INTR_IDX);
phy_irq = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(&pdev->dev), "phy", 0); phy_irq = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(&pdev->dev), "phy", 0);
if (phy_irq < 0) { if (phy_irq == -EPROBE_DEFER) {
dev_err(&pdev->dev, "Error getting PHY irq. Use polling instead"); err = -EPROBE_DEFER;
goto out;
} else if (phy_irq < 0) {
phy_irq = PHY_POLL; phy_irq = PHY_POLL;
} }

View File

@ -127,11 +127,8 @@ static int fbnic_mbx_map_msg(struct fbnic_dev *fbd, int mbx_idx,
return -EBUSY; return -EBUSY;
addr = dma_map_single(fbd->dev, msg, PAGE_SIZE, direction); addr = dma_map_single(fbd->dev, msg, PAGE_SIZE, direction);
if (dma_mapping_error(fbd->dev, addr)) { if (dma_mapping_error(fbd->dev, addr))
free_page((unsigned long)msg);
return -ENOSPC; return -ENOSPC;
}
mbx->buf_info[tail].msg = msg; mbx->buf_info[tail].msg = msg;
mbx->buf_info[tail].addr = addr; mbx->buf_info[tail].addr = addr;

View File

@ -18,9 +18,9 @@
*/ */
#define LAN743X_PTP_N_EVENT_CHAN 2 #define LAN743X_PTP_N_EVENT_CHAN 2
#define LAN743X_PTP_N_PEROUT LAN743X_PTP_N_EVENT_CHAN #define LAN743X_PTP_N_PEROUT LAN743X_PTP_N_EVENT_CHAN
#define LAN743X_PTP_N_EXTTS 4
#define LAN743X_PTP_N_PPS 0
#define PCI11X1X_PTP_IO_MAX_CHANNELS 8 #define PCI11X1X_PTP_IO_MAX_CHANNELS 8
#define LAN743X_PTP_N_EXTTS PCI11X1X_PTP_IO_MAX_CHANNELS
#define LAN743X_PTP_N_PPS 0
#define PTP_CMD_CTL_TIMEOUT_CNT 50 #define PTP_CMD_CTL_TIMEOUT_CNT 50
struct lan743x_adapter; struct lan743x_adapter;

View File

@ -516,9 +516,9 @@ static int __ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds,
unsigned long start_time; unsigned long start_time;
unsigned long max_wait; unsigned long max_wait;
unsigned long duration; unsigned long duration;
int done = 0;
bool fw_up; bool fw_up;
int opcode; int opcode;
bool done;
int err; int err;
/* Wait for dev cmd to complete, retrying if we get EAGAIN, /* Wait for dev cmd to complete, retrying if we get EAGAIN,
@ -526,6 +526,7 @@ static int __ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds,
*/ */
max_wait = jiffies + (max_seconds * HZ); max_wait = jiffies + (max_seconds * HZ);
try_again: try_again:
done = false;
opcode = idev->opcode; opcode = idev->opcode;
start_time = jiffies; start_time = jiffies;
for (fw_up = ionic_is_fw_running(idev); for (fw_up = ionic_is_fw_running(idev);

View File

@ -98,20 +98,11 @@ void prueth_xmit_free(struct prueth_tx_chn *tx_chn,
{ {
struct cppi5_host_desc_t *first_desc, *next_desc; struct cppi5_host_desc_t *first_desc, *next_desc;
dma_addr_t buf_dma, next_desc_dma; dma_addr_t buf_dma, next_desc_dma;
struct prueth_swdata *swdata;
struct page *page;
u32 buf_dma_len; u32 buf_dma_len;
first_desc = desc; first_desc = desc;
next_desc = first_desc; next_desc = first_desc;
swdata = cppi5_hdesc_get_swdata(desc);
if (swdata->type == PRUETH_SWDATA_PAGE) {
page = swdata->data.page;
page_pool_recycle_direct(page->pp, swdata->data.page);
goto free_desc;
}
cppi5_hdesc_get_obuf(first_desc, &buf_dma, &buf_dma_len); cppi5_hdesc_get_obuf(first_desc, &buf_dma, &buf_dma_len);
k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &buf_dma); k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &buf_dma);
@ -135,7 +126,6 @@ void prueth_xmit_free(struct prueth_tx_chn *tx_chn,
k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc); k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc);
} }
free_desc:
k3_cppi_desc_pool_free(tx_chn->desc_pool, first_desc); k3_cppi_desc_pool_free(tx_chn->desc_pool, first_desc);
} }
EXPORT_SYMBOL_GPL(prueth_xmit_free); EXPORT_SYMBOL_GPL(prueth_xmit_free);
@ -612,13 +602,8 @@ u32 emac_xmit_xdp_frame(struct prueth_emac *emac,
k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma); k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma);
cppi5_hdesc_attach_buf(first_desc, buf_dma, xdpf->len, buf_dma, xdpf->len); cppi5_hdesc_attach_buf(first_desc, buf_dma, xdpf->len, buf_dma, xdpf->len);
swdata = cppi5_hdesc_get_swdata(first_desc); swdata = cppi5_hdesc_get_swdata(first_desc);
if (page) {
swdata->type = PRUETH_SWDATA_PAGE;
swdata->data.page = page;
} else {
swdata->type = PRUETH_SWDATA_XDPF; swdata->type = PRUETH_SWDATA_XDPF;
swdata->data.xdpf = xdpf; swdata->data.xdpf = xdpf;
}
/* Report BQL before sending the packet */ /* Report BQL before sending the packet */
netif_txq = netdev_get_tx_queue(ndev, tx_chn->id); netif_txq = netdev_get_tx_queue(ndev, tx_chn->id);

View File

@ -1216,6 +1216,7 @@ void ath12k_fw_stats_init(struct ath12k *ar)
INIT_LIST_HEAD(&ar->fw_stats.pdevs); INIT_LIST_HEAD(&ar->fw_stats.pdevs);
INIT_LIST_HEAD(&ar->fw_stats.bcn); INIT_LIST_HEAD(&ar->fw_stats.bcn);
init_completion(&ar->fw_stats_complete); init_completion(&ar->fw_stats_complete);
init_completion(&ar->fw_stats_done);
} }
void ath12k_fw_stats_free(struct ath12k_fw_stats *stats) void ath12k_fw_stats_free(struct ath12k_fw_stats *stats)
@ -1228,8 +1229,9 @@ void ath12k_fw_stats_free(struct ath12k_fw_stats *stats)
void ath12k_fw_stats_reset(struct ath12k *ar) void ath12k_fw_stats_reset(struct ath12k *ar)
{ {
spin_lock_bh(&ar->data_lock); spin_lock_bh(&ar->data_lock);
ar->fw_stats.fw_stats_done = false;
ath12k_fw_stats_free(&ar->fw_stats); ath12k_fw_stats_free(&ar->fw_stats);
ar->fw_stats.num_vdev_recvd = 0;
ar->fw_stats.num_bcn_recvd = 0;
spin_unlock_bh(&ar->data_lock); spin_unlock_bh(&ar->data_lock);
} }

View File

@ -601,6 +601,12 @@ struct ath12k_sta {
#define ATH12K_NUM_CHANS 101 #define ATH12K_NUM_CHANS 101
#define ATH12K_MAX_5GHZ_CHAN 173 #define ATH12K_MAX_5GHZ_CHAN 173
static inline bool ath12k_is_2ghz_channel_freq(u32 freq)
{
return freq >= ATH12K_MIN_2GHZ_FREQ &&
freq <= ATH12K_MAX_2GHZ_FREQ;
}
enum ath12k_hw_state { enum ath12k_hw_state {
ATH12K_HW_STATE_OFF, ATH12K_HW_STATE_OFF,
ATH12K_HW_STATE_ON, ATH12K_HW_STATE_ON,
@ -626,7 +632,8 @@ struct ath12k_fw_stats {
struct list_head pdevs; struct list_head pdevs;
struct list_head vdevs; struct list_head vdevs;
struct list_head bcn; struct list_head bcn;
bool fw_stats_done; u32 num_vdev_recvd;
u32 num_bcn_recvd;
}; };
struct ath12k_dbg_htt_stats { struct ath12k_dbg_htt_stats {
@ -806,6 +813,7 @@ struct ath12k {
bool regdom_set_by_user; bool regdom_set_by_user;
struct completion fw_stats_complete; struct completion fw_stats_complete;
struct completion fw_stats_done;
struct completion mlo_setup_done; struct completion mlo_setup_done;
u32 mlo_setup_status; u32 mlo_setup_status;

View File

@ -1251,64 +1251,6 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab)
*/ */
} }
void
ath12k_debugfs_fw_stats_process(struct ath12k *ar,
struct ath12k_fw_stats *stats)
{
struct ath12k_base *ab = ar->ab;
struct ath12k_pdev *pdev;
bool is_end;
static unsigned int num_vdev, num_bcn;
size_t total_vdevs_started = 0;
int i;
if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {
if (list_empty(&stats->vdevs)) {
ath12k_warn(ab, "empty vdev stats");
return;
}
/* FW sends all the active VDEV stats irrespective of PDEV,
* hence limit until the count of all VDEVs started
*/
rcu_read_lock();
for (i = 0; i < ab->num_radios; i++) {
pdev = rcu_dereference(ab->pdevs_active[i]);
if (pdev && pdev->ar)
total_vdevs_started += pdev->ar->num_started_vdevs;
}
rcu_read_unlock();
is_end = ((++num_vdev) == total_vdevs_started);
list_splice_tail_init(&stats->vdevs,
&ar->fw_stats.vdevs);
if (is_end) {
ar->fw_stats.fw_stats_done = true;
num_vdev = 0;
}
return;
}
if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
if (list_empty(&stats->bcn)) {
ath12k_warn(ab, "empty beacon stats");
return;
}
/* Mark end until we reached the count of all started VDEVs
* within the PDEV
*/
is_end = ((++num_bcn) == ar->num_started_vdevs);
list_splice_tail_init(&stats->bcn,
&ar->fw_stats.bcn);
if (is_end) {
ar->fw_stats.fw_stats_done = true;
num_bcn = 0;
}
}
}
static int ath12k_open_vdev_stats(struct inode *inode, struct file *file) static int ath12k_open_vdev_stats(struct inode *inode, struct file *file)
{ {
struct ath12k *ar = inode->i_private; struct ath12k *ar = inode->i_private;

View File

@ -12,8 +12,6 @@ void ath12k_debugfs_soc_create(struct ath12k_base *ab);
void ath12k_debugfs_soc_destroy(struct ath12k_base *ab); void ath12k_debugfs_soc_destroy(struct ath12k_base *ab);
void ath12k_debugfs_register(struct ath12k *ar); void ath12k_debugfs_register(struct ath12k *ar);
void ath12k_debugfs_unregister(struct ath12k *ar); void ath12k_debugfs_unregister(struct ath12k *ar);
void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
struct ath12k_fw_stats *stats);
void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw, void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw,
struct ieee80211_vif *vif); struct ieee80211_vif *vif);
void ath12k_debugfs_pdev_create(struct ath12k_base *ab); void ath12k_debugfs_pdev_create(struct ath12k_base *ab);
@ -126,11 +124,6 @@ static inline void ath12k_debugfs_unregister(struct ath12k *ar)
{ {
} }
static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
struct ath12k_fw_stats *stats)
{
}
static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar) static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar)
{ {
return false; return false;

View File

@ -4360,7 +4360,7 @@ int ath12k_mac_get_fw_stats(struct ath12k *ar,
{ {
struct ath12k_base *ab = ar->ab; struct ath12k_base *ab = ar->ab;
struct ath12k_hw *ah = ath12k_ar_to_ah(ar); struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
unsigned long timeout, time_left; unsigned long time_left;
int ret; int ret;
guard(mutex)(&ah->hw_mutex); guard(mutex)(&ah->hw_mutex);
@ -4368,19 +4368,13 @@ int ath12k_mac_get_fw_stats(struct ath12k *ar,
if (ah->state != ATH12K_HW_STATE_ON) if (ah->state != ATH12K_HW_STATE_ON)
return -ENETDOWN; return -ENETDOWN;
/* FW stats can get split when exceeding the stats data buffer limit.
* In that case, since there is no end marking for the back-to-back
* received 'update stats' event, we keep a 3 seconds timeout in case,
* fw_stats_done is not marked yet
*/
timeout = jiffies + msecs_to_jiffies(3 * 1000);
ath12k_fw_stats_reset(ar); ath12k_fw_stats_reset(ar);
reinit_completion(&ar->fw_stats_complete); reinit_completion(&ar->fw_stats_complete);
reinit_completion(&ar->fw_stats_done);
ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id, ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id,
param->vdev_id, param->pdev_id); param->vdev_id, param->pdev_id);
if (ret) { if (ret) {
ath12k_warn(ab, "failed to request fw stats: %d\n", ret); ath12k_warn(ab, "failed to request fw stats: %d\n", ret);
return ret; return ret;
@ -4391,7 +4385,6 @@ int ath12k_mac_get_fw_stats(struct ath12k *ar,
param->pdev_id, param->vdev_id, param->stats_id); param->pdev_id, param->vdev_id, param->stats_id);
time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ); time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
if (!time_left) { if (!time_left) {
ath12k_warn(ab, "time out while waiting for get fw stats\n"); ath12k_warn(ab, "time out while waiting for get fw stats\n");
return -ETIMEDOUT; return -ETIMEDOUT;
@ -4400,20 +4393,15 @@ int ath12k_mac_get_fw_stats(struct ath12k *ar,
/* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back /* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back
* when stats data buffer limit is reached. fw_stats_complete * when stats data buffer limit is reached. fw_stats_complete
* is completed once host receives first event from firmware, but * is completed once host receives first event from firmware, but
* still end might not be marked in the TLV. * still there could be more events following. Below is to wait
* Below loop is to confirm that firmware completed sending all the event * until firmware completes sending all the events.
* and fw_stats_done is marked true when end is marked in the TLV.
*/ */
for (;;) { time_left = wait_for_completion_timeout(&ar->fw_stats_done, 3 * HZ);
if (time_after(jiffies, timeout)) if (!time_left) {
break; ath12k_warn(ab, "time out while waiting for fw stats done\n");
spin_lock_bh(&ar->data_lock); return -ETIMEDOUT;
if (ar->fw_stats.fw_stats_done) {
spin_unlock_bh(&ar->data_lock);
break;
}
spin_unlock_bh(&ar->data_lock);
} }
return 0; return 0;
} }
@ -5890,6 +5878,327 @@ exit:
return ret; return ret;
} }
static bool ath12k_mac_is_freq_on_mac(struct ath12k_hw_mode_freq_range_arg *freq_range,
u32 freq, u8 mac_id)
{
return (freq >= freq_range[mac_id].low_2ghz_freq &&
freq <= freq_range[mac_id].high_2ghz_freq) ||
(freq >= freq_range[mac_id].low_5ghz_freq &&
freq <= freq_range[mac_id].high_5ghz_freq);
}
static bool
ath12k_mac_2_freq_same_mac_in_freq_range(struct ath12k_base *ab,
struct ath12k_hw_mode_freq_range_arg *freq_range,
u32 freq_link1, u32 freq_link2)
{
u8 i;
for (i = 0; i < MAX_RADIOS; i++) {
if (ath12k_mac_is_freq_on_mac(freq_range, freq_link1, i) &&
ath12k_mac_is_freq_on_mac(freq_range, freq_link2, i))
return true;
}
return false;
}
static bool ath12k_mac_is_hw_dbs_capable(struct ath12k_base *ab)
{
return test_bit(WMI_TLV_SERVICE_DUAL_BAND_SIMULTANEOUS_SUPPORT,
ab->wmi_ab.svc_map) &&
ab->wmi_ab.hw_mode_info.support_dbs;
}
static bool ath12k_mac_2_freq_same_mac_in_dbs(struct ath12k_base *ab,
u32 freq_link1, u32 freq_link2)
{
struct ath12k_hw_mode_freq_range_arg *freq_range;
if (!ath12k_mac_is_hw_dbs_capable(ab))
return true;
freq_range = ab->wmi_ab.hw_mode_info.freq_range_caps[ATH12K_HW_MODE_DBS];
return ath12k_mac_2_freq_same_mac_in_freq_range(ab, freq_range,
freq_link1, freq_link2);
}
static bool ath12k_mac_is_hw_sbs_capable(struct ath12k_base *ab)
{
return test_bit(WMI_TLV_SERVICE_DUAL_BAND_SIMULTANEOUS_SUPPORT,
ab->wmi_ab.svc_map) &&
ab->wmi_ab.hw_mode_info.support_sbs;
}
static bool ath12k_mac_2_freq_same_mac_in_sbs(struct ath12k_base *ab,
u32 freq_link1, u32 freq_link2)
{
struct ath12k_hw_mode_info *info = &ab->wmi_ab.hw_mode_info;
struct ath12k_hw_mode_freq_range_arg *sbs_uppr_share;
struct ath12k_hw_mode_freq_range_arg *sbs_low_share;
struct ath12k_hw_mode_freq_range_arg *sbs_range;
if (!ath12k_mac_is_hw_sbs_capable(ab))
return true;
if (ab->wmi_ab.sbs_lower_band_end_freq) {
sbs_uppr_share = info->freq_range_caps[ATH12K_HW_MODE_SBS_UPPER_SHARE];
sbs_low_share = info->freq_range_caps[ATH12K_HW_MODE_SBS_LOWER_SHARE];
return ath12k_mac_2_freq_same_mac_in_freq_range(ab, sbs_low_share,
freq_link1, freq_link2) ||
ath12k_mac_2_freq_same_mac_in_freq_range(ab, sbs_uppr_share,
freq_link1, freq_link2);
}
sbs_range = info->freq_range_caps[ATH12K_HW_MODE_SBS];
return ath12k_mac_2_freq_same_mac_in_freq_range(ab, sbs_range,
freq_link1, freq_link2);
}
static bool ath12k_mac_freqs_on_same_mac(struct ath12k_base *ab,
u32 freq_link1, u32 freq_link2)
{
return ath12k_mac_2_freq_same_mac_in_dbs(ab, freq_link1, freq_link2) &&
ath12k_mac_2_freq_same_mac_in_sbs(ab, freq_link1, freq_link2);
}
static int ath12k_mac_mlo_sta_set_link_active(struct ath12k_base *ab,
enum wmi_mlo_link_force_reason reason,
enum wmi_mlo_link_force_mode mode,
u8 *mlo_vdev_id_lst,
u8 num_mlo_vdev,
u8 *mlo_inactive_vdev_lst,
u8 num_mlo_inactive_vdev)
{
struct wmi_mlo_link_set_active_arg param = {0};
u32 entry_idx, entry_offset, vdev_idx;
u8 vdev_id;
param.reason = reason;
param.force_mode = mode;
for (vdev_idx = 0; vdev_idx < num_mlo_vdev; vdev_idx++) {
vdev_id = mlo_vdev_id_lst[vdev_idx];
entry_idx = vdev_id / 32;
entry_offset = vdev_id % 32;
if (entry_idx >= WMI_MLO_LINK_NUM_SZ) {
ath12k_warn(ab, "Invalid entry_idx %d num_mlo_vdev %d vdev %d",
entry_idx, num_mlo_vdev, vdev_id);
return -EINVAL;
}
param.vdev_bitmap[entry_idx] |= 1 << entry_offset;
/* update entry number if entry index changed */
if (param.num_vdev_bitmap < entry_idx + 1)
param.num_vdev_bitmap = entry_idx + 1;
}
ath12k_dbg(ab, ATH12K_DBG_MAC,
"num_vdev_bitmap %d vdev_bitmap[0] = 0x%x, vdev_bitmap[1] = 0x%x",
param.num_vdev_bitmap, param.vdev_bitmap[0], param.vdev_bitmap[1]);
if (mode == WMI_MLO_LINK_FORCE_MODE_ACTIVE_INACTIVE) {
for (vdev_idx = 0; vdev_idx < num_mlo_inactive_vdev; vdev_idx++) {
vdev_id = mlo_inactive_vdev_lst[vdev_idx];
entry_idx = vdev_id / 32;
entry_offset = vdev_id % 32;
if (entry_idx >= WMI_MLO_LINK_NUM_SZ) {
ath12k_warn(ab, "Invalid entry_idx %d num_mlo_vdev %d vdev %d",
entry_idx, num_mlo_inactive_vdev, vdev_id);
return -EINVAL;
}
param.inactive_vdev_bitmap[entry_idx] |= 1 << entry_offset;
/* update entry number if entry index changed */
if (param.num_inactive_vdev_bitmap < entry_idx + 1)
param.num_inactive_vdev_bitmap = entry_idx + 1;
}
ath12k_dbg(ab, ATH12K_DBG_MAC,
"num_vdev_bitmap %d inactive_vdev_bitmap[0] = 0x%x, inactive_vdev_bitmap[1] = 0x%x",
param.num_inactive_vdev_bitmap,
param.inactive_vdev_bitmap[0],
param.inactive_vdev_bitmap[1]);
}
if (mode == WMI_MLO_LINK_FORCE_MODE_ACTIVE_LINK_NUM ||
mode == WMI_MLO_LINK_FORCE_MODE_INACTIVE_LINK_NUM) {
param.num_link_entry = 1;
param.link_num[0].num_of_link = num_mlo_vdev - 1;
}
return ath12k_wmi_send_mlo_link_set_active_cmd(ab, &param);
}
static int ath12k_mac_mlo_sta_update_link_active(struct ath12k_base *ab,
struct ieee80211_hw *hw,
struct ath12k_vif *ahvif)
{
u8 mlo_vdev_id_lst[IEEE80211_MLD_MAX_NUM_LINKS] = {0};
u32 mlo_freq_list[IEEE80211_MLD_MAX_NUM_LINKS] = {0};
unsigned long links = ahvif->links_map;
enum wmi_mlo_link_force_reason reason;
struct ieee80211_chanctx_conf *conf;
enum wmi_mlo_link_force_mode mode;
struct ieee80211_bss_conf *info;
struct ath12k_link_vif *arvif;
u8 num_mlo_vdev = 0;
u8 link_id;
for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]);
/* make sure vdev is created on this device */
if (!arvif || !arvif->is_created || arvif->ar->ab != ab)
continue;
info = ath12k_mac_get_link_bss_conf(arvif);
conf = wiphy_dereference(hw->wiphy, info->chanctx_conf);
mlo_freq_list[num_mlo_vdev] = conf->def.chan->center_freq;
mlo_vdev_id_lst[num_mlo_vdev] = arvif->vdev_id;
num_mlo_vdev++;
}
/* It is not allowed to activate more links than a single device
* supported. Something goes wrong if we reach here.
*/
if (num_mlo_vdev > ATH12K_NUM_MAX_ACTIVE_LINKS_PER_DEVICE) {
WARN_ON_ONCE(1);
return -EINVAL;
}
/* if 2 links are established and both link channels fall on the
* same hardware MAC, send command to firmware to deactivate one
* of them.
*/
if (num_mlo_vdev == 2 &&
ath12k_mac_freqs_on_same_mac(ab, mlo_freq_list[0],
mlo_freq_list[1])) {
mode = WMI_MLO_LINK_FORCE_MODE_INACTIVE_LINK_NUM;
reason = WMI_MLO_LINK_FORCE_REASON_NEW_CONNECT;
return ath12k_mac_mlo_sta_set_link_active(ab, reason, mode,
mlo_vdev_id_lst, num_mlo_vdev,
NULL, 0);
}
return 0;
}
static bool ath12k_mac_are_sbs_chan(struct ath12k_base *ab, u32 freq_1, u32 freq_2)
{
if (!ath12k_mac_is_hw_sbs_capable(ab))
return false;
if (ath12k_is_2ghz_channel_freq(freq_1) ||
ath12k_is_2ghz_channel_freq(freq_2))
return false;
return !ath12k_mac_2_freq_same_mac_in_sbs(ab, freq_1, freq_2);
}
static bool ath12k_mac_are_dbs_chan(struct ath12k_base *ab, u32 freq_1, u32 freq_2)
{
if (!ath12k_mac_is_hw_dbs_capable(ab))
return false;
return !ath12k_mac_2_freq_same_mac_in_dbs(ab, freq_1, freq_2);
}
static int ath12k_mac_select_links(struct ath12k_base *ab,
struct ieee80211_vif *vif,
struct ieee80211_hw *hw,
u16 *selected_links)
{
unsigned long useful_links = ieee80211_vif_usable_links(vif);
struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
u8 num_useful_links = hweight_long(useful_links);
struct ieee80211_chanctx_conf *chanctx;
struct ath12k_link_vif *assoc_arvif;
u32 assoc_link_freq, partner_freq;
u16 sbs_links = 0, dbs_links = 0;
struct ieee80211_bss_conf *info;
struct ieee80211_channel *chan;
struct ieee80211_sta *sta;
struct ath12k_sta *ahsta;
u8 link_id;
/* activate all useful links if less than max supported */
if (num_useful_links <= ATH12K_NUM_MAX_ACTIVE_LINKS_PER_DEVICE) {
*selected_links = useful_links;
return 0;
}
/* only in station mode we can get here, so it's safe
* to use ap_addr
*/
rcu_read_lock();
sta = ieee80211_find_sta(vif, vif->cfg.ap_addr);
if (!sta) {
rcu_read_unlock();
ath12k_warn(ab, "failed to find sta with addr %pM\n", vif->cfg.ap_addr);
return -EINVAL;
}
ahsta = ath12k_sta_to_ahsta(sta);
assoc_arvif = wiphy_dereference(hw->wiphy, ahvif->link[ahsta->assoc_link_id]);
info = ath12k_mac_get_link_bss_conf(assoc_arvif);
chanctx = rcu_dereference(info->chanctx_conf);
assoc_link_freq = chanctx->def.chan->center_freq;
rcu_read_unlock();
ath12k_dbg(ab, ATH12K_DBG_MAC, "assoc link %u freq %u\n",
assoc_arvif->link_id, assoc_link_freq);
/* assoc link is already activated and has to be kept active,
* only need to select a partner link from others.
*/
useful_links &= ~BIT(assoc_arvif->link_id);
for_each_set_bit(link_id, &useful_links, IEEE80211_MLD_MAX_NUM_LINKS) {
info = wiphy_dereference(hw->wiphy, vif->link_conf[link_id]);
if (!info) {
ath12k_warn(ab, "failed to get link info for link: %u\n",
link_id);
return -ENOLINK;
}
chan = info->chanreq.oper.chan;
if (!chan) {
ath12k_warn(ab, "failed to get chan for link: %u\n", link_id);
return -EINVAL;
}
partner_freq = chan->center_freq;
if (ath12k_mac_are_sbs_chan(ab, assoc_link_freq, partner_freq)) {
sbs_links |= BIT(link_id);
ath12k_dbg(ab, ATH12K_DBG_MAC, "new SBS link %u freq %u\n",
link_id, partner_freq);
continue;
}
if (ath12k_mac_are_dbs_chan(ab, assoc_link_freq, partner_freq)) {
dbs_links |= BIT(link_id);
ath12k_dbg(ab, ATH12K_DBG_MAC, "new DBS link %u freq %u\n",
link_id, partner_freq);
continue;
}
ath12k_dbg(ab, ATH12K_DBG_MAC, "non DBS/SBS link %u freq %u\n",
link_id, partner_freq);
}
/* choose the first candidate no matter how many is in the list */
if (sbs_links)
link_id = __ffs(sbs_links);
else if (dbs_links)
link_id = __ffs(dbs_links);
else
link_id = ffs(useful_links) - 1;
ath12k_dbg(ab, ATH12K_DBG_MAC, "select partner link %u\n", link_id);
*selected_links = BIT(assoc_arvif->link_id) | BIT(link_id);
return 0;
}
static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
@ -5899,10 +6208,13 @@ static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta);
struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
struct ath12k_base *prev_ab = NULL, *ab;
struct ath12k_link_vif *arvif; struct ath12k_link_vif *arvif;
struct ath12k_link_sta *arsta; struct ath12k_link_sta *arsta;
unsigned long valid_links; unsigned long valid_links;
u8 link_id = 0; u16 selected_links = 0;
u8 link_id = 0, i;
struct ath12k *ar;
int ret; int ret;
lockdep_assert_wiphy(hw->wiphy); lockdep_assert_wiphy(hw->wiphy);
@ -5972,8 +6284,24 @@ static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
* about to move to the associated state. * about to move to the associated state.
*/ */
if (ieee80211_vif_is_mld(vif) && vif->type == NL80211_IFTYPE_STATION && if (ieee80211_vif_is_mld(vif) && vif->type == NL80211_IFTYPE_STATION &&
old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) {
ieee80211_set_active_links(vif, ieee80211_vif_usable_links(vif)); /* TODO: for now only do link selection for single device
* MLO case. Other cases would be handled in the future.
*/
ab = ah->radio[0].ab;
if (ab->ag->num_devices == 1) {
ret = ath12k_mac_select_links(ab, vif, hw, &selected_links);
if (ret) {
ath12k_warn(ab,
"failed to get selected links: %d\n", ret);
goto exit;
}
} else {
selected_links = ieee80211_vif_usable_links(vif);
}
ieee80211_set_active_links(vif, selected_links);
}
/* Handle all the other state transitions in generic way */ /* Handle all the other state transitions in generic way */
valid_links = ahsta->links_map; valid_links = ahsta->links_map;
@ -5997,6 +6325,24 @@ static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
} }
} }
if (ieee80211_vif_is_mld(vif) && vif->type == NL80211_IFTYPE_STATION &&
old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) {
for_each_ar(ah, ar, i) {
ab = ar->ab;
if (prev_ab == ab)
continue;
ret = ath12k_mac_mlo_sta_update_link_active(ab, hw, ahvif);
if (ret) {
ath12k_warn(ab,
"failed to update link active state on connect %d\n",
ret);
goto exit;
}
prev_ab = ab;
}
}
/* IEEE80211_STA_NONE -> IEEE80211_STA_NOTEXIST: /* IEEE80211_STA_NONE -> IEEE80211_STA_NOTEXIST:
* Remove the station from driver (handle ML sta here since that * Remove the station from driver (handle ML sta here since that
* needs special handling. Normal sta will be handled in generic * needs special handling. Normal sta will be handled in generic

View File

@ -54,6 +54,8 @@ struct ath12k_generic_iter {
#define ATH12K_DEFAULT_SCAN_LINK IEEE80211_MLD_MAX_NUM_LINKS #define ATH12K_DEFAULT_SCAN_LINK IEEE80211_MLD_MAX_NUM_LINKS
#define ATH12K_NUM_MAX_LINKS (IEEE80211_MLD_MAX_NUM_LINKS + 1) #define ATH12K_NUM_MAX_LINKS (IEEE80211_MLD_MAX_NUM_LINKS + 1)
#define ATH12K_NUM_MAX_ACTIVE_LINKS_PER_DEVICE 2
enum ath12k_supported_bw { enum ath12k_supported_bw {
ATH12K_BW_20 = 0, ATH12K_BW_20 = 0,
ATH12K_BW_40 = 1, ATH12K_BW_40 = 1,

View File

@ -91,6 +91,11 @@ struct ath12k_wmi_svc_rdy_ext2_parse {
bool dma_ring_cap_done; bool dma_ring_cap_done;
bool spectral_bin_scaling_done; bool spectral_bin_scaling_done;
bool mac_phy_caps_ext_done; bool mac_phy_caps_ext_done;
bool hal_reg_caps_ext2_done;
bool scan_radio_caps_ext2_done;
bool twt_caps_done;
bool htt_msdu_idx_to_qtype_map_done;
bool dbs_or_sbs_cap_ext_done;
}; };
struct ath12k_wmi_rdy_parse { struct ath12k_wmi_rdy_parse {
@ -4395,6 +4400,7 @@ static int ath12k_wmi_hw_mode_caps_parse(struct ath12k_base *soc,
static int ath12k_wmi_hw_mode_caps(struct ath12k_base *soc, static int ath12k_wmi_hw_mode_caps(struct ath12k_base *soc,
u16 len, const void *ptr, void *data) u16 len, const void *ptr, void *data)
{ {
struct ath12k_svc_ext_info *svc_ext_info = &soc->wmi_ab.svc_ext_info;
struct ath12k_wmi_svc_rdy_ext_parse *svc_rdy_ext = data; struct ath12k_wmi_svc_rdy_ext_parse *svc_rdy_ext = data;
const struct ath12k_wmi_hw_mode_cap_params *hw_mode_caps; const struct ath12k_wmi_hw_mode_cap_params *hw_mode_caps;
enum wmi_host_hw_mode_config_type mode, pref; enum wmi_host_hw_mode_config_type mode, pref;
@ -4427,8 +4433,11 @@ static int ath12k_wmi_hw_mode_caps(struct ath12k_base *soc,
} }
} }
ath12k_dbg(soc, ATH12K_DBG_WMI, "preferred_hw_mode:%d\n", svc_ext_info->num_hw_modes = svc_rdy_ext->n_hw_mode_caps;
soc->wmi_ab.preferred_hw_mode);
ath12k_dbg(soc, ATH12K_DBG_WMI, "num hw modes %u preferred_hw_mode %d\n",
svc_ext_info->num_hw_modes, soc->wmi_ab.preferred_hw_mode);
if (soc->wmi_ab.preferred_hw_mode == WMI_HOST_HW_MODE_MAX) if (soc->wmi_ab.preferred_hw_mode == WMI_HOST_HW_MODE_MAX)
return -EINVAL; return -EINVAL;
@ -4658,6 +4667,65 @@ free_dir_buff:
return ret; return ret;
} }
static void
ath12k_wmi_save_mac_phy_info(struct ath12k_base *ab,
const struct ath12k_wmi_mac_phy_caps_params *mac_phy_cap,
struct ath12k_svc_ext_mac_phy_info *mac_phy_info)
{
mac_phy_info->phy_id = __le32_to_cpu(mac_phy_cap->phy_id);
mac_phy_info->supported_bands = __le32_to_cpu(mac_phy_cap->supported_bands);
mac_phy_info->hw_freq_range.low_2ghz_freq =
__le32_to_cpu(mac_phy_cap->low_2ghz_chan_freq);
mac_phy_info->hw_freq_range.high_2ghz_freq =
__le32_to_cpu(mac_phy_cap->high_2ghz_chan_freq);
mac_phy_info->hw_freq_range.low_5ghz_freq =
__le32_to_cpu(mac_phy_cap->low_5ghz_chan_freq);
mac_phy_info->hw_freq_range.high_5ghz_freq =
__le32_to_cpu(mac_phy_cap->high_5ghz_chan_freq);
}
static void
ath12k_wmi_save_all_mac_phy_info(struct ath12k_base *ab,
struct ath12k_wmi_svc_rdy_ext_parse *svc_rdy_ext)
{
struct ath12k_svc_ext_info *svc_ext_info = &ab->wmi_ab.svc_ext_info;
const struct ath12k_wmi_mac_phy_caps_params *mac_phy_cap;
const struct ath12k_wmi_hw_mode_cap_params *hw_mode_cap;
struct ath12k_svc_ext_mac_phy_info *mac_phy_info;
u32 hw_mode_id, phy_bit_map;
u8 hw_idx;
mac_phy_info = &svc_ext_info->mac_phy_info[0];
mac_phy_cap = svc_rdy_ext->mac_phy_caps;
for (hw_idx = 0; hw_idx < svc_ext_info->num_hw_modes; hw_idx++) {
hw_mode_cap = &svc_rdy_ext->hw_mode_caps[hw_idx];
hw_mode_id = __le32_to_cpu(hw_mode_cap->hw_mode_id);
phy_bit_map = __le32_to_cpu(hw_mode_cap->phy_id_map);
while (phy_bit_map) {
ath12k_wmi_save_mac_phy_info(ab, mac_phy_cap, mac_phy_info);
mac_phy_info->hw_mode_config_type =
le32_get_bits(hw_mode_cap->hw_mode_config_type,
WMI_HW_MODE_CAP_CFG_TYPE);
ath12k_dbg(ab, ATH12K_DBG_WMI,
"hw_idx %u hw_mode_id %u hw_mode_config_type %u supported_bands %u phy_id %u 2 GHz [%u - %u] 5 GHz [%u - %u]\n",
hw_idx, hw_mode_id,
mac_phy_info->hw_mode_config_type,
mac_phy_info->supported_bands, mac_phy_info->phy_id,
mac_phy_info->hw_freq_range.low_2ghz_freq,
mac_phy_info->hw_freq_range.high_2ghz_freq,
mac_phy_info->hw_freq_range.low_5ghz_freq,
mac_phy_info->hw_freq_range.high_5ghz_freq);
mac_phy_cap++;
mac_phy_info++;
phy_bit_map >>= 1;
}
}
}
static int ath12k_wmi_svc_rdy_ext_parse(struct ath12k_base *ab, static int ath12k_wmi_svc_rdy_ext_parse(struct ath12k_base *ab,
u16 tag, u16 len, u16 tag, u16 len,
const void *ptr, void *data) const void *ptr, void *data)
@ -4706,6 +4774,8 @@ static int ath12k_wmi_svc_rdy_ext_parse(struct ath12k_base *ab,
return ret; return ret;
} }
ath12k_wmi_save_all_mac_phy_info(ab, svc_rdy_ext);
svc_rdy_ext->mac_phy_done = true; svc_rdy_ext->mac_phy_done = true;
} else if (!svc_rdy_ext->ext_hal_reg_done) { } else if (!svc_rdy_ext->ext_hal_reg_done) {
ret = ath12k_wmi_ext_hal_reg_caps(ab, len, ptr, svc_rdy_ext); ret = ath12k_wmi_ext_hal_reg_caps(ab, len, ptr, svc_rdy_ext);
@ -4922,10 +4992,449 @@ static int ath12k_wmi_tlv_mac_phy_caps_ext(struct ath12k_base *ab, u16 tag,
return 0; return 0;
} }
static void
ath12k_wmi_update_freq_info(struct ath12k_base *ab,
struct ath12k_svc_ext_mac_phy_info *mac_cap,
enum ath12k_hw_mode mode,
u32 phy_id)
{
struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info;
struct ath12k_hw_mode_freq_range_arg *mac_range;
mac_range = &hw_mode_info->freq_range_caps[mode][phy_id];
if (mac_cap->supported_bands & WMI_HOST_WLAN_2GHZ_CAP) {
mac_range->low_2ghz_freq = max_t(u32,
mac_cap->hw_freq_range.low_2ghz_freq,
ATH12K_MIN_2GHZ_FREQ);
mac_range->high_2ghz_freq = mac_cap->hw_freq_range.high_2ghz_freq ?
min_t(u32,
mac_cap->hw_freq_range.high_2ghz_freq,
ATH12K_MAX_2GHZ_FREQ) :
ATH12K_MAX_2GHZ_FREQ;
}
if (mac_cap->supported_bands & WMI_HOST_WLAN_5GHZ_CAP) {
mac_range->low_5ghz_freq = max_t(u32,
mac_cap->hw_freq_range.low_5ghz_freq,
ATH12K_MIN_5GHZ_FREQ);
mac_range->high_5ghz_freq = mac_cap->hw_freq_range.high_5ghz_freq ?
min_t(u32,
mac_cap->hw_freq_range.high_5ghz_freq,
ATH12K_MAX_6GHZ_FREQ) :
ATH12K_MAX_6GHZ_FREQ;
}
}
static bool
ath12k_wmi_all_phy_range_updated(struct ath12k_base *ab,
enum ath12k_hw_mode hwmode)
{
struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info;
struct ath12k_hw_mode_freq_range_arg *mac_range;
u8 phy_id;
for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) {
mac_range = &hw_mode_info->freq_range_caps[hwmode][phy_id];
/* modify SBS/DBS range only when both phy for DBS are filled */
if (!mac_range->low_2ghz_freq && !mac_range->low_5ghz_freq)
return false;
}
return true;
}
static void ath12k_wmi_update_dbs_freq_info(struct ath12k_base *ab)
{
struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info;
struct ath12k_hw_mode_freq_range_arg *mac_range;
u8 phy_id;
mac_range = hw_mode_info->freq_range_caps[ATH12K_HW_MODE_DBS];
/* Reset 5 GHz range for shared mac for DBS */
for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) {
if (mac_range[phy_id].low_2ghz_freq &&
mac_range[phy_id].low_5ghz_freq) {
mac_range[phy_id].low_5ghz_freq = 0;
mac_range[phy_id].high_5ghz_freq = 0;
}
}
}
static u32
ath12k_wmi_get_highest_5ghz_freq_from_range(struct ath12k_hw_mode_freq_range_arg *range)
{
u32 highest_freq = 0;
u8 phy_id;
for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) {
if (range[phy_id].high_5ghz_freq > highest_freq)
highest_freq = range[phy_id].high_5ghz_freq;
}
return highest_freq ? highest_freq : ATH12K_MAX_6GHZ_FREQ;
}
static u32
ath12k_wmi_get_lowest_5ghz_freq_from_range(struct ath12k_hw_mode_freq_range_arg *range)
{
u32 lowest_freq = 0;
u8 phy_id;
for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) {
if ((!lowest_freq && range[phy_id].low_5ghz_freq) ||
range[phy_id].low_5ghz_freq < lowest_freq)
lowest_freq = range[phy_id].low_5ghz_freq;
}
return lowest_freq ? lowest_freq : ATH12K_MIN_5GHZ_FREQ;
}
static void
ath12k_wmi_fill_upper_share_sbs_freq(struct ath12k_base *ab,
u16 sbs_range_sep,
struct ath12k_hw_mode_freq_range_arg *ref_freq)
{
struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info;
struct ath12k_hw_mode_freq_range_arg *upper_sbs_freq_range;
u8 phy_id;
upper_sbs_freq_range =
hw_mode_info->freq_range_caps[ATH12K_HW_MODE_SBS_UPPER_SHARE];
for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) {
upper_sbs_freq_range[phy_id].low_2ghz_freq =
ref_freq[phy_id].low_2ghz_freq;
upper_sbs_freq_range[phy_id].high_2ghz_freq =
ref_freq[phy_id].high_2ghz_freq;
/* update for shared mac */
if (upper_sbs_freq_range[phy_id].low_2ghz_freq) {
upper_sbs_freq_range[phy_id].low_5ghz_freq = sbs_range_sep + 10;
upper_sbs_freq_range[phy_id].high_5ghz_freq =
ath12k_wmi_get_highest_5ghz_freq_from_range(ref_freq);
} else {
upper_sbs_freq_range[phy_id].low_5ghz_freq =
ath12k_wmi_get_lowest_5ghz_freq_from_range(ref_freq);
upper_sbs_freq_range[phy_id].high_5ghz_freq = sbs_range_sep;
}
}
}
static void
ath12k_wmi_fill_lower_share_sbs_freq(struct ath12k_base *ab,
u16 sbs_range_sep,
struct ath12k_hw_mode_freq_range_arg *ref_freq)
{
struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info;
struct ath12k_hw_mode_freq_range_arg *lower_sbs_freq_range;
u8 phy_id;
lower_sbs_freq_range =
hw_mode_info->freq_range_caps[ATH12K_HW_MODE_SBS_LOWER_SHARE];
for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) {
lower_sbs_freq_range[phy_id].low_2ghz_freq =
ref_freq[phy_id].low_2ghz_freq;
lower_sbs_freq_range[phy_id].high_2ghz_freq =
ref_freq[phy_id].high_2ghz_freq;
/* update for shared mac */
if (lower_sbs_freq_range[phy_id].low_2ghz_freq) {
lower_sbs_freq_range[phy_id].low_5ghz_freq =
ath12k_wmi_get_lowest_5ghz_freq_from_range(ref_freq);
lower_sbs_freq_range[phy_id].high_5ghz_freq = sbs_range_sep;
} else {
lower_sbs_freq_range[phy_id].low_5ghz_freq = sbs_range_sep + 10;
lower_sbs_freq_range[phy_id].high_5ghz_freq =
ath12k_wmi_get_highest_5ghz_freq_from_range(ref_freq);
}
}
}
static const char *ath12k_wmi_hw_mode_to_str(enum ath12k_hw_mode hw_mode)
{
static const char * const mode_str[] = {
[ATH12K_HW_MODE_SMM] = "SMM",
[ATH12K_HW_MODE_DBS] = "DBS",
[ATH12K_HW_MODE_SBS] = "SBS",
[ATH12K_HW_MODE_SBS_UPPER_SHARE] = "SBS_UPPER_SHARE",
[ATH12K_HW_MODE_SBS_LOWER_SHARE] = "SBS_LOWER_SHARE",
};
if (hw_mode >= ARRAY_SIZE(mode_str))
return "Unknown";
return mode_str[hw_mode];
}
static void
ath12k_wmi_dump_freq_range_per_mac(struct ath12k_base *ab,
struct ath12k_hw_mode_freq_range_arg *freq_range,
enum ath12k_hw_mode hw_mode)
{
u8 i;
for (i = 0; i < MAX_RADIOS; i++)
if (freq_range[i].low_2ghz_freq || freq_range[i].low_5ghz_freq)
ath12k_dbg(ab, ATH12K_DBG_WMI,
"frequency range: %s(%d) mac %d 2 GHz [%d - %d] 5 GHz [%d - %d]",
ath12k_wmi_hw_mode_to_str(hw_mode),
hw_mode, i,
freq_range[i].low_2ghz_freq,
freq_range[i].high_2ghz_freq,
freq_range[i].low_5ghz_freq,
freq_range[i].high_5ghz_freq);
}
static void ath12k_wmi_dump_freq_range(struct ath12k_base *ab)
{
struct ath12k_hw_mode_freq_range_arg *freq_range;
u8 i;
for (i = ATH12K_HW_MODE_SMM; i < ATH12K_HW_MODE_MAX; i++) {
freq_range = ab->wmi_ab.hw_mode_info.freq_range_caps[i];
ath12k_wmi_dump_freq_range_per_mac(ab, freq_range, i);
}
}
static int ath12k_wmi_modify_sbs_freq(struct ath12k_base *ab, u8 phy_id)
{
struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info;
struct ath12k_hw_mode_freq_range_arg *sbs_mac_range, *shared_mac_range;
struct ath12k_hw_mode_freq_range_arg *non_shared_range;
u8 shared_phy_id;
sbs_mac_range = &hw_mode_info->freq_range_caps[ATH12K_HW_MODE_SBS][phy_id];
/* if SBS mac range has both 2.4 and 5 GHz ranges, i.e. shared phy_id
* keep the range as it is in SBS
*/
if (sbs_mac_range->low_2ghz_freq && sbs_mac_range->low_5ghz_freq)
return 0;
if (sbs_mac_range->low_2ghz_freq && !sbs_mac_range->low_5ghz_freq) {
ath12k_err(ab, "Invalid DBS/SBS mode with only 2.4Ghz");
ath12k_wmi_dump_freq_range_per_mac(ab, sbs_mac_range, ATH12K_HW_MODE_SBS);
return -EINVAL;
}
non_shared_range = sbs_mac_range;
/* if SBS mac range has only 5 GHz then it's the non-shared phy, so
* modify the range as per the shared mac.
*/
shared_phy_id = phy_id ? 0 : 1;
shared_mac_range =
&hw_mode_info->freq_range_caps[ATH12K_HW_MODE_SBS][shared_phy_id];
if (shared_mac_range->low_5ghz_freq > non_shared_range->low_5ghz_freq) {
ath12k_dbg(ab, ATH12K_DBG_WMI, "high 5 GHz shared");
/* If the shared mac lower 5 GHz frequency is greater than
* non-shared mac lower 5 GHz frequency then the shared mac has
* high 5 GHz shared with 2.4 GHz. So non-shared mac's 5 GHz high
* freq should be less than the shared mac's low 5 GHz freq.
*/
if (non_shared_range->high_5ghz_freq >=
shared_mac_range->low_5ghz_freq)
non_shared_range->high_5ghz_freq =
max_t(u32, shared_mac_range->low_5ghz_freq - 10,
non_shared_range->low_5ghz_freq);
} else if (shared_mac_range->high_5ghz_freq <
non_shared_range->high_5ghz_freq) {
ath12k_dbg(ab, ATH12K_DBG_WMI, "low 5 GHz shared");
/* If the shared mac high 5 GHz frequency is less than
* non-shared mac high 5 GHz frequency then the shared mac has
* low 5 GHz shared with 2.4 GHz. So non-shared mac's 5 GHz low
* freq should be greater than the shared mac's high 5 GHz freq.
*/
if (shared_mac_range->high_5ghz_freq >=
non_shared_range->low_5ghz_freq)
non_shared_range->low_5ghz_freq =
min_t(u32, shared_mac_range->high_5ghz_freq + 10,
non_shared_range->high_5ghz_freq);
} else {
ath12k_warn(ab, "invalid SBS range with all 5 GHz shared");
return -EINVAL;
}
return 0;
}
static void ath12k_wmi_update_sbs_freq_info(struct ath12k_base *ab)
{
struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info;
struct ath12k_hw_mode_freq_range_arg *mac_range;
u16 sbs_range_sep;
u8 phy_id;
int ret;
mac_range = hw_mode_info->freq_range_caps[ATH12K_HW_MODE_SBS];
/* If sbs_lower_band_end_freq has a value, then the frequency range
* will be split using that value.
*/
sbs_range_sep = ab->wmi_ab.sbs_lower_band_end_freq;
if (sbs_range_sep) {
ath12k_wmi_fill_upper_share_sbs_freq(ab, sbs_range_sep,
mac_range);
ath12k_wmi_fill_lower_share_sbs_freq(ab, sbs_range_sep,
mac_range);
/* Hardware specifies the range boundary with sbs_range_sep,
* (i.e. the boundary between 5 GHz high and 5 GHz low),
* reset the original one to make sure it will not get used.
*/
memset(mac_range, 0, sizeof(*mac_range) * MAX_RADIOS);
return;
}
/* If sbs_lower_band_end_freq is not set that means firmware will send one
* shared mac range and one non-shared mac range. so update that freq.
*/
for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) {
ret = ath12k_wmi_modify_sbs_freq(ab, phy_id);
if (ret) {
memset(mac_range, 0, sizeof(*mac_range) * MAX_RADIOS);
break;
}
}
}
static void
ath12k_wmi_update_mac_freq_info(struct ath12k_base *ab,
enum wmi_host_hw_mode_config_type hw_config_type,
u32 phy_id,
struct ath12k_svc_ext_mac_phy_info *mac_cap)
{
if (phy_id >= MAX_RADIOS) {
ath12k_err(ab, "mac more than two not supported: %d", phy_id);
return;
}
ath12k_dbg(ab, ATH12K_DBG_WMI,
"hw_mode_cfg %d mac %d band 0x%x SBS cutoff freq %d 2 GHz [%d - %d] 5 GHz [%d - %d]",
hw_config_type, phy_id, mac_cap->supported_bands,
ab->wmi_ab.sbs_lower_band_end_freq,
mac_cap->hw_freq_range.low_2ghz_freq,
mac_cap->hw_freq_range.high_2ghz_freq,
mac_cap->hw_freq_range.low_5ghz_freq,
mac_cap->hw_freq_range.high_5ghz_freq);
switch (hw_config_type) {
case WMI_HOST_HW_MODE_SINGLE:
if (phy_id) {
ath12k_dbg(ab, ATH12K_DBG_WMI, "mac phy 1 is not supported");
break;
}
ath12k_wmi_update_freq_info(ab, mac_cap, ATH12K_HW_MODE_SMM, phy_id);
break;
case WMI_HOST_HW_MODE_DBS:
if (!ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_DBS))
ath12k_wmi_update_freq_info(ab, mac_cap,
ATH12K_HW_MODE_DBS, phy_id);
break;
case WMI_HOST_HW_MODE_DBS_SBS:
case WMI_HOST_HW_MODE_DBS_OR_SBS:
ath12k_wmi_update_freq_info(ab, mac_cap, ATH12K_HW_MODE_DBS, phy_id);
if (ab->wmi_ab.sbs_lower_band_end_freq ||
mac_cap->hw_freq_range.low_5ghz_freq ||
mac_cap->hw_freq_range.low_2ghz_freq)
ath12k_wmi_update_freq_info(ab, mac_cap, ATH12K_HW_MODE_SBS,
phy_id);
if (ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_DBS))
ath12k_wmi_update_dbs_freq_info(ab);
if (ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_SBS))
ath12k_wmi_update_sbs_freq_info(ab);
break;
case WMI_HOST_HW_MODE_SBS:
case WMI_HOST_HW_MODE_SBS_PASSIVE:
ath12k_wmi_update_freq_info(ab, mac_cap, ATH12K_HW_MODE_SBS, phy_id);
if (ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_SBS))
ath12k_wmi_update_sbs_freq_info(ab);
break;
default:
break;
}
}
static bool ath12k_wmi_sbs_range_present(struct ath12k_base *ab)
{
if (ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_SBS) ||
(ab->wmi_ab.sbs_lower_band_end_freq &&
ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_SBS_LOWER_SHARE) &&
ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_SBS_UPPER_SHARE)))
return true;
return false;
}
static int ath12k_wmi_update_hw_mode_list(struct ath12k_base *ab)
{
struct ath12k_svc_ext_info *svc_ext_info = &ab->wmi_ab.svc_ext_info;
struct ath12k_hw_mode_info *info = &ab->wmi_ab.hw_mode_info;
enum wmi_host_hw_mode_config_type hw_config_type;
struct ath12k_svc_ext_mac_phy_info *tmp;
bool dbs_mode = false, sbs_mode = false;
u32 i, j = 0;
if (!svc_ext_info->num_hw_modes) {
ath12k_err(ab, "invalid number of hw modes");
return -EINVAL;
}
ath12k_dbg(ab, ATH12K_DBG_WMI, "updated HW mode list: num modes %d",
svc_ext_info->num_hw_modes);
memset(info->freq_range_caps, 0, sizeof(info->freq_range_caps));
for (i = 0; i < svc_ext_info->num_hw_modes; i++) {
if (j >= ATH12K_MAX_MAC_PHY_CAP)
return -EINVAL;
/* Update for MAC0 */
tmp = &svc_ext_info->mac_phy_info[j++];
hw_config_type = tmp->hw_mode_config_type;
ath12k_wmi_update_mac_freq_info(ab, hw_config_type, tmp->phy_id, tmp);
/* SBS and DBS have dual MAC. Up to 2 MACs are considered. */
if (hw_config_type == WMI_HOST_HW_MODE_DBS ||
hw_config_type == WMI_HOST_HW_MODE_SBS_PASSIVE ||
hw_config_type == WMI_HOST_HW_MODE_SBS ||
hw_config_type == WMI_HOST_HW_MODE_DBS_OR_SBS) {
if (j >= ATH12K_MAX_MAC_PHY_CAP)
return -EINVAL;
/* Update for MAC1 */
tmp = &svc_ext_info->mac_phy_info[j++];
ath12k_wmi_update_mac_freq_info(ab, hw_config_type,
tmp->phy_id, tmp);
if (hw_config_type == WMI_HOST_HW_MODE_DBS ||
hw_config_type == WMI_HOST_HW_MODE_DBS_OR_SBS)
dbs_mode = true;
if (ath12k_wmi_sbs_range_present(ab) &&
(hw_config_type == WMI_HOST_HW_MODE_SBS_PASSIVE ||
hw_config_type == WMI_HOST_HW_MODE_SBS ||
hw_config_type == WMI_HOST_HW_MODE_DBS_OR_SBS))
sbs_mode = true;
}
}
info->support_dbs = dbs_mode;
info->support_sbs = sbs_mode;
ath12k_wmi_dump_freq_range(ab);
return 0;
}
static int ath12k_wmi_svc_rdy_ext2_parse(struct ath12k_base *ab, static int ath12k_wmi_svc_rdy_ext2_parse(struct ath12k_base *ab,
u16 tag, u16 len, u16 tag, u16 len,
const void *ptr, void *data) const void *ptr, void *data)
{ {
const struct ath12k_wmi_dbs_or_sbs_cap_params *dbs_or_sbs_caps;
struct ath12k_wmi_pdev *wmi_handle = &ab->wmi_ab.wmi[0]; struct ath12k_wmi_pdev *wmi_handle = &ab->wmi_ab.wmi[0];
struct ath12k_wmi_svc_rdy_ext2_parse *parse = data; struct ath12k_wmi_svc_rdy_ext2_parse *parse = data;
int ret; int ret;
@ -4967,7 +5476,32 @@ static int ath12k_wmi_svc_rdy_ext2_parse(struct ath12k_base *ab,
} }
parse->mac_phy_caps_ext_done = true; parse->mac_phy_caps_ext_done = true;
} else if (!parse->hal_reg_caps_ext2_done) {
parse->hal_reg_caps_ext2_done = true;
} else if (!parse->scan_radio_caps_ext2_done) {
parse->scan_radio_caps_ext2_done = true;
} else if (!parse->twt_caps_done) {
parse->twt_caps_done = true;
} else if (!parse->htt_msdu_idx_to_qtype_map_done) {
parse->htt_msdu_idx_to_qtype_map_done = true;
} else if (!parse->dbs_or_sbs_cap_ext_done) {
dbs_or_sbs_caps = ptr;
ab->wmi_ab.sbs_lower_band_end_freq =
__le32_to_cpu(dbs_or_sbs_caps->sbs_lower_band_end_freq);
ath12k_dbg(ab, ATH12K_DBG_WMI, "sbs_lower_band_end_freq %u\n",
ab->wmi_ab.sbs_lower_band_end_freq);
ret = ath12k_wmi_update_hw_mode_list(ab);
if (ret) {
ath12k_warn(ab, "failed to update hw mode list: %d\n",
ret);
return ret;
} }
parse->dbs_or_sbs_cap_ext_done = true;
}
break; break;
default: default:
break; break;
@ -7626,6 +8160,64 @@ static int ath12k_wmi_pull_fw_stats(struct ath12k_base *ab, struct sk_buff *skb,
&parse); &parse);
} }
static void ath12k_wmi_fw_stats_process(struct ath12k *ar,
struct ath12k_fw_stats *stats)
{
struct ath12k_base *ab = ar->ab;
struct ath12k_pdev *pdev;
bool is_end = true;
size_t total_vdevs_started = 0;
int i;
if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {
if (list_empty(&stats->vdevs)) {
ath12k_warn(ab, "empty vdev stats");
return;
}
/* FW sends all the active VDEV stats irrespective of PDEV,
* hence limit until the count of all VDEVs started
*/
rcu_read_lock();
for (i = 0; i < ab->num_radios; i++) {
pdev = rcu_dereference(ab->pdevs_active[i]);
if (pdev && pdev->ar)
total_vdevs_started += pdev->ar->num_started_vdevs;
}
rcu_read_unlock();
if (total_vdevs_started)
is_end = ((++ar->fw_stats.num_vdev_recvd) ==
total_vdevs_started);
list_splice_tail_init(&stats->vdevs,
&ar->fw_stats.vdevs);
if (is_end)
complete(&ar->fw_stats_done);
return;
}
if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
if (list_empty(&stats->bcn)) {
ath12k_warn(ab, "empty beacon stats");
return;
}
/* Mark end until we reached the count of all started VDEVs
* within the PDEV
*/
if (ar->num_started_vdevs)
is_end = ((++ar->fw_stats.num_bcn_recvd) ==
ar->num_started_vdevs);
list_splice_tail_init(&stats->bcn,
&ar->fw_stats.bcn);
if (is_end)
complete(&ar->fw_stats_done);
}
}
static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *skb) static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *skb)
{ {
struct ath12k_fw_stats stats = {}; struct ath12k_fw_stats stats = {};
@ -7655,19 +8247,15 @@ static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *sk
spin_lock_bh(&ar->data_lock); spin_lock_bh(&ar->data_lock);
/* WMI_REQUEST_PDEV_STAT can be requested via .get_txpower mac ops or via /* Handle WMI_REQUEST_PDEV_STAT status update */
* debugfs fw stats. Therefore, processing it separately.
*/
if (stats.stats_id == WMI_REQUEST_PDEV_STAT) { if (stats.stats_id == WMI_REQUEST_PDEV_STAT) {
list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs); list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs);
ar->fw_stats.fw_stats_done = true; complete(&ar->fw_stats_done);
goto complete; goto complete;
} }
/* WMI_REQUEST_VDEV_STAT and WMI_REQUEST_BCN_STAT are currently requested only /* Handle WMI_REQUEST_VDEV_STAT and WMI_REQUEST_BCN_STAT updates. */
* via debugfs fw stats. Hence, processing these in debugfs context. ath12k_wmi_fw_stats_process(ar, &stats);
*/
ath12k_debugfs_fw_stats_process(ar, &stats);
complete: complete:
complete(&ar->fw_stats_complete); complete(&ar->fw_stats_complete);
@ -9911,3 +10499,224 @@ int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar,
return 0; return 0;
} }
static int
ath12k_wmi_fill_disallowed_bmap(struct ath12k_base *ab,
struct wmi_disallowed_mlo_mode_bitmap_params *dislw_bmap,
struct wmi_mlo_link_set_active_arg *arg)
{
struct wmi_ml_disallow_mode_bmap_arg *dislw_bmap_arg;
u8 i;
if (arg->num_disallow_mode_comb >
ARRAY_SIZE(arg->disallow_bmap)) {
ath12k_warn(ab, "invalid num_disallow_mode_comb: %d",
arg->num_disallow_mode_comb);
return -EINVAL;
}
dislw_bmap_arg = &arg->disallow_bmap[0];
for (i = 0; i < arg->num_disallow_mode_comb; i++) {
dislw_bmap->tlv_header =
ath12k_wmi_tlv_cmd_hdr(0, sizeof(*dislw_bmap));
dislw_bmap->disallowed_mode_bitmap =
cpu_to_le32(dislw_bmap_arg->disallowed_mode);
dislw_bmap->ieee_link_id_comb =
le32_encode_bits(dislw_bmap_arg->ieee_link_id[0],
WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_1) |
le32_encode_bits(dislw_bmap_arg->ieee_link_id[1],
WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_2) |
le32_encode_bits(dislw_bmap_arg->ieee_link_id[2],
WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_3) |
le32_encode_bits(dislw_bmap_arg->ieee_link_id[3],
WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_4);
ath12k_dbg(ab, ATH12K_DBG_WMI,
"entry %d disallowed_mode %d ieee_link_id_comb 0x%x",
i, dislw_bmap_arg->disallowed_mode,
dislw_bmap_arg->ieee_link_id_comb);
dislw_bmap++;
dislw_bmap_arg++;
}
return 0;
}
int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab,
struct wmi_mlo_link_set_active_arg *arg)
{
struct wmi_disallowed_mlo_mode_bitmap_params *disallowed_mode_bmap;
struct wmi_mlo_set_active_link_number_params *link_num_param;
u32 num_link_num_param = 0, num_vdev_bitmap = 0;
struct ath12k_wmi_base *wmi_ab = &ab->wmi_ab;
struct wmi_mlo_link_set_active_cmd *cmd;
u32 num_inactive_vdev_bitmap = 0;
u32 num_disallow_mode_comb = 0;
struct wmi_tlv *tlv;
struct sk_buff *skb;
__le32 *vdev_bitmap;
void *buf_ptr;
int i, ret;
u32 len;
if (!arg->num_vdev_bitmap && !arg->num_link_entry) {
ath12k_warn(ab, "Invalid num_vdev_bitmap and num_link_entry");
return -EINVAL;
}
switch (arg->force_mode) {
case WMI_MLO_LINK_FORCE_MODE_ACTIVE_LINK_NUM:
case WMI_MLO_LINK_FORCE_MODE_INACTIVE_LINK_NUM:
num_link_num_param = arg->num_link_entry;
fallthrough;
case WMI_MLO_LINK_FORCE_MODE_ACTIVE:
case WMI_MLO_LINK_FORCE_MODE_INACTIVE:
case WMI_MLO_LINK_FORCE_MODE_NO_FORCE:
num_vdev_bitmap = arg->num_vdev_bitmap;
break;
case WMI_MLO_LINK_FORCE_MODE_ACTIVE_INACTIVE:
num_vdev_bitmap = arg->num_vdev_bitmap;
num_inactive_vdev_bitmap = arg->num_inactive_vdev_bitmap;
break;
default:
ath12k_warn(ab, "Invalid force mode: %u", arg->force_mode);
return -EINVAL;
}
num_disallow_mode_comb = arg->num_disallow_mode_comb;
len = sizeof(*cmd) +
TLV_HDR_SIZE + sizeof(*link_num_param) * num_link_num_param +
TLV_HDR_SIZE + sizeof(*vdev_bitmap) * num_vdev_bitmap +
TLV_HDR_SIZE + TLV_HDR_SIZE + TLV_HDR_SIZE +
TLV_HDR_SIZE + sizeof(*disallowed_mode_bmap) * num_disallow_mode_comb;
if (arg->force_mode == WMI_MLO_LINK_FORCE_MODE_ACTIVE_INACTIVE)
len += sizeof(*vdev_bitmap) * num_inactive_vdev_bitmap;
skb = ath12k_wmi_alloc_skb(wmi_ab, len);
if (!skb)
return -ENOMEM;
cmd = (struct wmi_mlo_link_set_active_cmd *)skb->data;
cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_LINK_SET_ACTIVE_CMD,
sizeof(*cmd));
cmd->force_mode = cpu_to_le32(arg->force_mode);
cmd->reason = cpu_to_le32(arg->reason);
ath12k_dbg(ab, ATH12K_DBG_WMI,
"mode %d reason %d num_link_num_param %d num_vdev_bitmap %d inactive %d num_disallow_mode_comb %d",
arg->force_mode, arg->reason, num_link_num_param,
num_vdev_bitmap, num_inactive_vdev_bitmap,
num_disallow_mode_comb);
buf_ptr = skb->data + sizeof(*cmd);
tlv = buf_ptr;
tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT,
sizeof(*link_num_param) * num_link_num_param);
buf_ptr += TLV_HDR_SIZE;
if (num_link_num_param) {
cmd->ctrl_flags =
le32_encode_bits(arg->ctrl_flags.dync_force_link_num ? 1 : 0,
CRTL_F_DYNC_FORCE_LINK_NUM);
link_num_param = buf_ptr;
for (i = 0; i < num_link_num_param; i++) {
link_num_param->tlv_header =
ath12k_wmi_tlv_cmd_hdr(0, sizeof(*link_num_param));
link_num_param->num_of_link =
cpu_to_le32(arg->link_num[i].num_of_link);
link_num_param->vdev_type =
cpu_to_le32(arg->link_num[i].vdev_type);
link_num_param->vdev_subtype =
cpu_to_le32(arg->link_num[i].vdev_subtype);
link_num_param->home_freq =
cpu_to_le32(arg->link_num[i].home_freq);
ath12k_dbg(ab, ATH12K_DBG_WMI,
"entry %d num_of_link %d vdev type %d subtype %d freq %d control_flags %d",
i, arg->link_num[i].num_of_link,
arg->link_num[i].vdev_type,
arg->link_num[i].vdev_subtype,
arg->link_num[i].home_freq,
__le32_to_cpu(cmd->ctrl_flags));
link_num_param++;
}
buf_ptr += sizeof(*link_num_param) * num_link_num_param;
}
tlv = buf_ptr;
tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32,
sizeof(*vdev_bitmap) * num_vdev_bitmap);
buf_ptr += TLV_HDR_SIZE;
if (num_vdev_bitmap) {
vdev_bitmap = buf_ptr;
for (i = 0; i < num_vdev_bitmap; i++) {
vdev_bitmap[i] = cpu_to_le32(arg->vdev_bitmap[i]);
ath12k_dbg(ab, ATH12K_DBG_WMI, "entry %d vdev_id_bitmap 0x%x",
i, arg->vdev_bitmap[i]);
}
buf_ptr += sizeof(*vdev_bitmap) * num_vdev_bitmap;
}
if (arg->force_mode == WMI_MLO_LINK_FORCE_MODE_ACTIVE_INACTIVE) {
tlv = buf_ptr;
tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32,
sizeof(*vdev_bitmap) *
num_inactive_vdev_bitmap);
buf_ptr += TLV_HDR_SIZE;
if (num_inactive_vdev_bitmap) {
vdev_bitmap = buf_ptr;
for (i = 0; i < num_inactive_vdev_bitmap; i++) {
vdev_bitmap[i] =
cpu_to_le32(arg->inactive_vdev_bitmap[i]);
ath12k_dbg(ab, ATH12K_DBG_WMI,
"entry %d inactive_vdev_id_bitmap 0x%x",
i, arg->inactive_vdev_bitmap[i]);
}
buf_ptr += sizeof(*vdev_bitmap) * num_inactive_vdev_bitmap;
}
} else {
/* add empty vdev bitmap2 tlv */
tlv = buf_ptr;
tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0);
buf_ptr += TLV_HDR_SIZE;
}
/* add empty ieee_link_id_bitmap tlv */
tlv = buf_ptr;
tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0);
buf_ptr += TLV_HDR_SIZE;
/* add empty ieee_link_id_bitmap2 tlv */
tlv = buf_ptr;
tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0);
buf_ptr += TLV_HDR_SIZE;
tlv = buf_ptr;
tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT,
sizeof(*disallowed_mode_bmap) *
arg->num_disallow_mode_comb);
buf_ptr += TLV_HDR_SIZE;
ret = ath12k_wmi_fill_disallowed_bmap(ab, buf_ptr, arg);
if (ret)
goto free_skb;
ret = ath12k_wmi_cmd_send(&wmi_ab->wmi[0], skb, WMI_MLO_LINK_SET_ACTIVE_CMDID);
if (ret) {
ath12k_warn(ab,
"failed to send WMI_MLO_LINK_SET_ACTIVE_CMDID: %d\n", ret);
goto free_skb;
}
ath12k_dbg(ab, ATH12K_DBG_WMI, "WMI mlo link set active cmd");
return ret;
free_skb:
dev_kfree_skb(skb);
return ret;
}

View File

@ -1974,6 +1974,7 @@ enum wmi_tlv_tag {
WMI_TAG_TPC_STATS_CTL_PWR_TABLE_EVENT, WMI_TAG_TPC_STATS_CTL_PWR_TABLE_EVENT,
WMI_TAG_VDEV_SET_TPC_POWER_CMD = 0x3B5, WMI_TAG_VDEV_SET_TPC_POWER_CMD = 0x3B5,
WMI_TAG_VDEV_CH_POWER_INFO, WMI_TAG_VDEV_CH_POWER_INFO,
WMI_TAG_MLO_LINK_SET_ACTIVE_CMD = 0x3BE,
WMI_TAG_EHT_RATE_SET = 0x3C4, WMI_TAG_EHT_RATE_SET = 0x3C4,
WMI_TAG_DCS_AWGN_INT_TYPE = 0x3C5, WMI_TAG_DCS_AWGN_INT_TYPE = 0x3C5,
WMI_TAG_MLO_TX_SEND_PARAMS, WMI_TAG_MLO_TX_SEND_PARAMS,
@ -2617,6 +2618,8 @@ struct ath12k_wmi_soc_mac_phy_hw_mode_caps_params {
__le32 num_chainmask_tables; __le32 num_chainmask_tables;
} __packed; } __packed;
#define WMI_HW_MODE_CAP_CFG_TYPE GENMASK(27, 0)
struct ath12k_wmi_hw_mode_cap_params { struct ath12k_wmi_hw_mode_cap_params {
__le32 tlv_header; __le32 tlv_header;
__le32 hw_mode_id; __le32 hw_mode_id;
@ -2666,6 +2669,12 @@ struct ath12k_wmi_mac_phy_caps_params {
__le32 he_cap_info_2g_ext; __le32 he_cap_info_2g_ext;
__le32 he_cap_info_5g_ext; __le32 he_cap_info_5g_ext;
__le32 he_cap_info_internal; __le32 he_cap_info_internal;
__le32 wireless_modes;
__le32 low_2ghz_chan_freq;
__le32 high_2ghz_chan_freq;
__le32 low_5ghz_chan_freq;
__le32 high_5ghz_chan_freq;
__le32 nss_ratio;
} __packed; } __packed;
struct ath12k_wmi_hal_reg_caps_ext_params { struct ath12k_wmi_hal_reg_caps_ext_params {
@ -2739,6 +2748,11 @@ struct wmi_service_ready_ext2_event {
__le32 default_num_msduq_supported_per_tid; __le32 default_num_msduq_supported_per_tid;
} __packed; } __packed;
struct ath12k_wmi_dbs_or_sbs_cap_params {
__le32 hw_mode_id;
__le32 sbs_lower_band_end_freq;
} __packed;
struct ath12k_wmi_caps_ext_params { struct ath12k_wmi_caps_ext_params {
__le32 hw_mode_id; __le32 hw_mode_id;
__le32 pdev_and_hw_link_ids; __le32 pdev_and_hw_link_ids;
@ -5049,6 +5063,53 @@ struct ath12k_wmi_pdev {
u32 rx_decap_mode; u32 rx_decap_mode;
}; };
struct ath12k_hw_mode_freq_range_arg {
u32 low_2ghz_freq;
u32 high_2ghz_freq;
u32 low_5ghz_freq;
u32 high_5ghz_freq;
};
struct ath12k_svc_ext_mac_phy_info {
enum wmi_host_hw_mode_config_type hw_mode_config_type;
u32 phy_id;
u32 supported_bands;
struct ath12k_hw_mode_freq_range_arg hw_freq_range;
};
#define ATH12K_MAX_MAC_PHY_CAP 8
struct ath12k_svc_ext_info {
u32 num_hw_modes;
struct ath12k_svc_ext_mac_phy_info mac_phy_info[ATH12K_MAX_MAC_PHY_CAP];
};
/**
* enum ath12k_hw_mode - enum for host mode
* @ATH12K_HW_MODE_SMM: Single mac mode
* @ATH12K_HW_MODE_DBS: DBS mode
* @ATH12K_HW_MODE_SBS: SBS mode with either high share or low share
* @ATH12K_HW_MODE_SBS_UPPER_SHARE: Higher 5 GHz shared with 2.4 GHz
* @ATH12K_HW_MODE_SBS_LOWER_SHARE: Lower 5 GHz shared with 2.4 GHz
* @ATH12K_HW_MODE_MAX: Max, used to indicate invalid mode
*/
enum ath12k_hw_mode {
ATH12K_HW_MODE_SMM,
ATH12K_HW_MODE_DBS,
ATH12K_HW_MODE_SBS,
ATH12K_HW_MODE_SBS_UPPER_SHARE,
ATH12K_HW_MODE_SBS_LOWER_SHARE,
ATH12K_HW_MODE_MAX,
};
struct ath12k_hw_mode_info {
bool support_dbs:1;
bool support_sbs:1;
struct ath12k_hw_mode_freq_range_arg freq_range_caps[ATH12K_HW_MODE_MAX]
[MAX_RADIOS];
};
struct ath12k_wmi_base { struct ath12k_wmi_base {
struct ath12k_base *ab; struct ath12k_base *ab;
struct ath12k_wmi_pdev wmi[MAX_RADIOS]; struct ath12k_wmi_pdev wmi[MAX_RADIOS];
@ -5066,6 +5127,10 @@ struct ath12k_wmi_base {
enum wmi_host_hw_mode_config_type preferred_hw_mode; enum wmi_host_hw_mode_config_type preferred_hw_mode;
struct ath12k_wmi_target_cap_arg *targ_cap; struct ath12k_wmi_target_cap_arg *targ_cap;
struct ath12k_svc_ext_info svc_ext_info;
u32 sbs_lower_band_end_freq;
struct ath12k_hw_mode_info hw_mode_info;
}; };
struct wmi_pdev_set_bios_interface_cmd { struct wmi_pdev_set_bios_interface_cmd {
@ -5997,6 +6062,118 @@ struct wmi_vdev_set_tpc_power_cmd {
*/ */
} __packed; } __packed;
#define CRTL_F_DYNC_FORCE_LINK_NUM GENMASK(3, 2)
struct wmi_mlo_link_set_active_cmd {
__le32 tlv_header;
__le32 force_mode;
__le32 reason;
__le32 use_ieee_link_id_bitmap;
struct ath12k_wmi_mac_addr_params ap_mld_mac_addr;
__le32 ctrl_flags;
} __packed;
struct wmi_mlo_set_active_link_number_params {
__le32 tlv_header;
__le32 num_of_link;
__le32 vdev_type;
__le32 vdev_subtype;
__le32 home_freq;
} __packed;
#define WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_1 GENMASK(7, 0)
#define WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_2 GENMASK(15, 8)
#define WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_3 GENMASK(23, 16)
#define WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_4 GENMASK(31, 24)
struct wmi_disallowed_mlo_mode_bitmap_params {
__le32 tlv_header;
__le32 disallowed_mode_bitmap;
__le32 ieee_link_id_comb;
} __packed;
enum wmi_mlo_link_force_mode {
WMI_MLO_LINK_FORCE_MODE_ACTIVE = 1,
WMI_MLO_LINK_FORCE_MODE_INACTIVE = 2,
WMI_MLO_LINK_FORCE_MODE_ACTIVE_LINK_NUM = 3,
WMI_MLO_LINK_FORCE_MODE_INACTIVE_LINK_NUM = 4,
WMI_MLO_LINK_FORCE_MODE_NO_FORCE = 5,
WMI_MLO_LINK_FORCE_MODE_ACTIVE_INACTIVE = 6,
WMI_MLO_LINK_FORCE_MODE_NON_FORCE_UPDATE = 7,
};
enum wmi_mlo_link_force_reason {
WMI_MLO_LINK_FORCE_REASON_NEW_CONNECT = 1,
WMI_MLO_LINK_FORCE_REASON_NEW_DISCONNECT = 2,
WMI_MLO_LINK_FORCE_REASON_LINK_REMOVAL = 3,
WMI_MLO_LINK_FORCE_REASON_TDLS = 4,
WMI_MLO_LINK_FORCE_REASON_REVERT_FAILURE = 5,
WMI_MLO_LINK_FORCE_REASON_LINK_DELETE = 6,
WMI_MLO_LINK_FORCE_REASON_SINGLE_LINK_EMLSR_OP = 7,
};
struct wmi_mlo_link_num_arg {
u32 num_of_link;
u32 vdev_type;
u32 vdev_subtype;
u32 home_freq;
};
struct wmi_mlo_control_flags_arg {
bool overwrite_force_active_bitmap;
bool overwrite_force_inactive_bitmap;
bool dync_force_link_num;
bool post_re_evaluate;
u8 post_re_evaluate_loops;
bool dont_reschedule_workqueue;
};
struct wmi_ml_link_force_cmd_arg {
u8 ap_mld_mac_addr[ETH_ALEN];
u16 ieee_link_id_bitmap;
u16 ieee_link_id_bitmap2;
u8 link_num;
};
struct wmi_ml_disallow_mode_bmap_arg {
u32 disallowed_mode;
union {
u32 ieee_link_id_comb;
u8 ieee_link_id[4];
};
};
/* maximum size of link number param array
* for MLO link set active command
*/
#define WMI_MLO_LINK_NUM_SZ 2
/* maximum size of vdev bitmap array for
* MLO link set active command
*/
#define WMI_MLO_VDEV_BITMAP_SZ 2
/* Max number of disallowed bitmap combination
* sent to firmware
*/
#define WMI_ML_MAX_DISALLOW_BMAP_COMB 4
struct wmi_mlo_link_set_active_arg {
enum wmi_mlo_link_force_mode force_mode;
enum wmi_mlo_link_force_reason reason;
u32 num_link_entry;
u32 num_vdev_bitmap;
u32 num_inactive_vdev_bitmap;
struct wmi_mlo_link_num_arg link_num[WMI_MLO_LINK_NUM_SZ];
u32 vdev_bitmap[WMI_MLO_VDEV_BITMAP_SZ];
u32 inactive_vdev_bitmap[WMI_MLO_VDEV_BITMAP_SZ];
struct wmi_mlo_control_flags_arg ctrl_flags;
bool use_ieee_link_id;
struct wmi_ml_link_force_cmd_arg force_cmd;
u32 num_disallow_mode_comb;
struct wmi_ml_disallow_mode_bmap_arg disallow_bmap[WMI_ML_MAX_DISALLOW_BMAP_COMB];
};
void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, void ath12k_wmi_init_qcn9274(struct ath12k_base *ab,
struct ath12k_wmi_resource_config_arg *config); struct ath12k_wmi_resource_config_arg *config);
void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, void ath12k_wmi_init_wcn7850(struct ath12k_base *ab,
@ -6195,5 +6372,6 @@ bool ath12k_wmi_supports_6ghz_cc_ext(struct ath12k *ar);
int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar, int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar,
u32 vdev_id, u32 vdev_id,
struct ath12k_reg_tpc_power_info *param); struct ath12k_reg_tpc_power_info *param);
int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab,
struct wmi_mlo_link_set_active_arg *param);
#endif #endif

View File

@ -87,7 +87,9 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar,
* We need to do some backwards compatibility to make this work. * We need to do some backwards compatibility to make this work.
*/ */
if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) { if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) {
WARN_ON(1); ath6kl_err("mismatched byte count %d vs. expected %zd\n",
le32_to_cpu(targ_info->byte_count),
sizeof(*targ_info));
return -EINVAL; return -EINVAL;
} }

View File

@ -438,14 +438,21 @@ static void carl9170_usb_rx_complete(struct urb *urb)
if (atomic_read(&ar->rx_anch_urbs) == 0) { if (atomic_read(&ar->rx_anch_urbs) == 0) {
/* /*
* The system is too slow to cope with * At this point, either the system is too slow to
* the enormous workload. We have simply * cope with the enormous workload (so we have simply
* run out of active rx urbs and this * run out of active rx urbs and this unfortunately
* unfortunately leads to an unpredictable * leads to an unpredictable device), or the device
* device. * is not fully functional after an unsuccessful
* firmware loading attempts (so it doesn't pass
* ieee80211_register_hw() and there is no internal
* workqueue at all).
*/ */
if (ar->registered)
ieee80211_queue_work(ar->hw, &ar->ping_work); ieee80211_queue_work(ar->hw, &ar->ping_work);
else
pr_warn_once("device %s is not registered\n",
dev_name(&ar->udev->dev));
} }
} else { } else {
/* /*

View File

@ -1316,6 +1316,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
sizeof(trans->conf.no_reclaim_cmds)); sizeof(trans->conf.no_reclaim_cmds));
memcpy(trans->conf.no_reclaim_cmds, no_reclaim_cmds, memcpy(trans->conf.no_reclaim_cmds, no_reclaim_cmds,
sizeof(no_reclaim_cmds)); sizeof(no_reclaim_cmds));
trans->conf.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
switch (iwlwifi_mod_params.amsdu_size) { switch (iwlwifi_mod_params.amsdu_size) {
case IWL_AMSDU_DEF: case IWL_AMSDU_DEF:

View File

@ -77,6 +77,7 @@ void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans,
/* Setup async RX handling */ /* Setup async RX handling */
spin_lock_init(&mld->async_handlers_lock); spin_lock_init(&mld->async_handlers_lock);
INIT_LIST_HEAD(&mld->async_handlers_list);
wiphy_work_init(&mld->async_handlers_wk, wiphy_work_init(&mld->async_handlers_wk,
iwl_mld_async_handlers_wk); iwl_mld_async_handlers_wk);

View File

@ -34,7 +34,7 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
WIDE_ID(MAC_CONF_GROUP, WIDE_ID(MAC_CONF_GROUP,
MAC_CONFIG_CMD), 0); MAC_CONFIG_CMD), 0);
if (WARN_ON(cmd_ver < 1 && cmd_ver > 3)) if (WARN_ON(cmd_ver < 1 || cmd_ver > 3))
return; return;
cmd->id_and_color = cpu_to_le32(mvmvif->id); cmd->id_and_color = cpu_to_le32(mvmvif->id);

View File

@ -166,7 +166,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans,
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_context_info *ctxt_info; struct iwl_context_info *ctxt_info;
struct iwl_context_info_rbd_cfg *rx_cfg; struct iwl_context_info_rbd_cfg *rx_cfg;
u32 control_flags = 0, rb_size; u32 control_flags = 0, rb_size, cb_size;
dma_addr_t phys; dma_addr_t phys;
int ret; int ret;
@ -202,11 +202,12 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans,
rb_size = IWL_CTXT_INFO_RB_SIZE_4K; rb_size = IWL_CTXT_INFO_RB_SIZE_4K;
} }
WARN_ON(RX_QUEUE_CB_SIZE(iwl_trans_get_num_rbds(trans)) > 12); cb_size = RX_QUEUE_CB_SIZE(iwl_trans_get_num_rbds(trans));
if (WARN_ON(cb_size > 12))
cb_size = 12;
control_flags = IWL_CTXT_INFO_TFD_FORMAT_LONG; control_flags = IWL_CTXT_INFO_TFD_FORMAT_LONG;
control_flags |= control_flags |= u32_encode_bits(cb_size, IWL_CTXT_INFO_RB_CB_SIZE);
u32_encode_bits(RX_QUEUE_CB_SIZE(iwl_trans_get_num_rbds(trans)),
IWL_CTXT_INFO_RB_CB_SIZE);
control_flags |= u32_encode_bits(rb_size, IWL_CTXT_INFO_RB_SIZE); control_flags |= u32_encode_bits(rb_size, IWL_CTXT_INFO_RB_SIZE);
ctxt_info->control.control_flags = cpu_to_le32(control_flags); ctxt_info->control.control_flags = cpu_to_le32(control_flags);

View File

@ -121,7 +121,8 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
struct ptp_clock_info *ops; struct ptp_clock_info *ops;
int err = -EOPNOTSUPP; int err = -EOPNOTSUPP;
if (ptp_clock_freerun(ptp)) { if (tx->modes & (ADJ_SETOFFSET | ADJ_FREQUENCY | ADJ_OFFSET) &&
ptp_clock_freerun(ptp)) {
pr_err("ptp: physical clock is free running\n"); pr_err("ptp: physical clock is free running\n");
return -EBUSY; return -EBUSY;
} }

View File

@ -98,7 +98,27 @@ static inline int queue_cnt(const struct timestamp_event_queue *q)
/* Check if ptp virtual clock is in use */ /* Check if ptp virtual clock is in use */
static inline bool ptp_vclock_in_use(struct ptp_clock *ptp) static inline bool ptp_vclock_in_use(struct ptp_clock *ptp)
{ {
return !ptp->is_virtual_clock; bool in_use = false;
/* Virtual clocks can't be stacked on top of virtual clocks.
* Avoid acquiring the n_vclocks_mux on virtual clocks, to allow this
* function to be called from code paths where the n_vclocks_mux of the
* parent physical clock is already held. Functionally that's not an
* issue, but lockdep would complain, because they have the same lock
* class.
*/
if (ptp->is_virtual_clock)
return false;
if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
return true;
if (ptp->n_vclocks)
in_use = true;
mutex_unlock(&ptp->n_vclocks_mux);
return in_use;
} }
/* Check if ptp clock shall be free running */ /* Check if ptp clock shall be free running */

View File

@ -249,6 +249,12 @@ static inline void atm_account_tx(struct atm_vcc *vcc, struct sk_buff *skb)
ATM_SKB(skb)->atm_options = vcc->atm_options; ATM_SKB(skb)->atm_options = vcc->atm_options;
} }
static inline void atm_return_tx(struct atm_vcc *vcc, struct sk_buff *skb)
{
WARN_ON_ONCE(refcount_sub_and_test(ATM_SKB(skb)->acct_truesize,
&sk_atm(vcc)->sk_wmem_alloc));
}
static inline void atm_force_charge(struct atm_vcc *vcc,int truesize) static inline void atm_force_charge(struct atm_vcc *vcc,int truesize)
{ {
atomic_add(truesize, &sk_atm(vcc)->sk_rmem_alloc); atomic_add(truesize, &sk_atm(vcc)->sk_rmem_alloc);

View File

@ -1278,7 +1278,7 @@ struct ieee80211_ext {
u8 sa[ETH_ALEN]; u8 sa[ETH_ALEN];
__le32 timestamp; __le32 timestamp;
u8 change_seq; u8 change_seq;
u8 variable[0]; u8 variable[];
} __packed s1g_beacon; } __packed s1g_beacon;
} u; } u;
} __packed __aligned(2); } __packed __aligned(2);
@ -1536,7 +1536,7 @@ struct ieee80211_mgmt {
u8 action_code; u8 action_code;
u8 dialog_token; u8 dialog_token;
__le16 capability; __le16 capability;
u8 variable[0]; u8 variable[];
} __packed tdls_discover_resp; } __packed tdls_discover_resp;
struct { struct {
u8 action_code; u8 action_code;
@ -1721,35 +1721,35 @@ struct ieee80211_tdls_data {
struct { struct {
u8 dialog_token; u8 dialog_token;
__le16 capability; __le16 capability;
u8 variable[0]; u8 variable[];
} __packed setup_req; } __packed setup_req;
struct { struct {
__le16 status_code; __le16 status_code;
u8 dialog_token; u8 dialog_token;
__le16 capability; __le16 capability;
u8 variable[0]; u8 variable[];
} __packed setup_resp; } __packed setup_resp;
struct { struct {
__le16 status_code; __le16 status_code;
u8 dialog_token; u8 dialog_token;
u8 variable[0]; u8 variable[];
} __packed setup_cfm; } __packed setup_cfm;
struct { struct {
__le16 reason_code; __le16 reason_code;
u8 variable[0]; u8 variable[];
} __packed teardown; } __packed teardown;
struct { struct {
u8 dialog_token; u8 dialog_token;
u8 variable[0]; u8 variable[];
} __packed discover_req; } __packed discover_req;
struct { struct {
u8 target_channel; u8 target_channel;
u8 oper_class; u8 oper_class;
u8 variable[0]; u8 variable[];
} __packed chan_switch_req; } __packed chan_switch_req;
struct { struct {
__le16 status_code; __le16 status_code;
u8 variable[0]; u8 variable[];
} __packed chan_switch_resp; } __packed chan_switch_resp;
} u; } u;
} __packed; } __packed;

View File

@ -208,10 +208,6 @@ enum {
ETHTOOL_A_STATS_PHY_MAX = (__ETHTOOL_A_STATS_PHY_CNT - 1) ETHTOOL_A_STATS_PHY_MAX = (__ETHTOOL_A_STATS_PHY_CNT - 1)
}; };
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
#define ETHTOOL_MCGRP_MONITOR_NAME "monitor" #define ETHTOOL_MCGRP_MONITOR_NAME "monitor"
#endif /* _UAPI_LINUX_ETHTOOL_NETLINK_H_ */ #endif /* _UAPI_LINUX_ETHTOOL_NETLINK_H_ */

View File

@ -6,8 +6,8 @@
#ifndef _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H #ifndef _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H
#define _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H #define _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H
#define ETHTOOL_FAMILY_NAME "ethtool" #define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_FAMILY_VERSION 1 #define ETHTOOL_GENL_VERSION 1
enum { enum {
ETHTOOL_UDP_TUNNEL_TYPE_VXLAN, ETHTOOL_UDP_TUNNEL_TYPE_VXLAN,

View File

@ -716,6 +716,7 @@ config GENERIC_LIB_DEVMEM_IS_ALLOWED
config PLDMFW config PLDMFW
bool bool
select CRC32
default n default n
config ASN1_ENCODER config ASN1_ENCODER

View File

@ -635,6 +635,7 @@ int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size)
skb->dev = NULL; /* for paths shared with net_device interfaces */ skb->dev = NULL; /* for paths shared with net_device interfaces */
if (!copy_from_iter_full(skb_put(skb, size), size, &m->msg_iter)) { if (!copy_from_iter_full(skb_put(skb, size), size, &m->msg_iter)) {
atm_return_tx(vcc, skb);
kfree_skb(skb); kfree_skb(skb);
error = -EFAULT; error = -EFAULT;
goto out; goto out;

View File

@ -124,6 +124,7 @@ static unsigned char bus_mac[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
/* Device structures */ /* Device structures */
static struct net_device *dev_lec[MAX_LEC_ITF]; static struct net_device *dev_lec[MAX_LEC_ITF];
static DEFINE_MUTEX(lec_mutex);
#if IS_ENABLED(CONFIG_BRIDGE) #if IS_ENABLED(CONFIG_BRIDGE)
static void lec_handle_bridge(struct sk_buff *skb, struct net_device *dev) static void lec_handle_bridge(struct sk_buff *skb, struct net_device *dev)
@ -685,6 +686,7 @@ static int lec_vcc_attach(struct atm_vcc *vcc, void __user *arg)
int bytes_left; int bytes_left;
struct atmlec_ioc ioc_data; struct atmlec_ioc ioc_data;
lockdep_assert_held(&lec_mutex);
/* Lecd must be up in this case */ /* Lecd must be up in this case */
bytes_left = copy_from_user(&ioc_data, arg, sizeof(struct atmlec_ioc)); bytes_left = copy_from_user(&ioc_data, arg, sizeof(struct atmlec_ioc));
if (bytes_left != 0) if (bytes_left != 0)
@ -710,6 +712,7 @@ static int lec_vcc_attach(struct atm_vcc *vcc, void __user *arg)
static int lec_mcast_attach(struct atm_vcc *vcc, int arg) static int lec_mcast_attach(struct atm_vcc *vcc, int arg)
{ {
lockdep_assert_held(&lec_mutex);
if (arg < 0 || arg >= MAX_LEC_ITF) if (arg < 0 || arg >= MAX_LEC_ITF)
return -EINVAL; return -EINVAL;
arg = array_index_nospec(arg, MAX_LEC_ITF); arg = array_index_nospec(arg, MAX_LEC_ITF);
@ -725,6 +728,7 @@ static int lecd_attach(struct atm_vcc *vcc, int arg)
int i; int i;
struct lec_priv *priv; struct lec_priv *priv;
lockdep_assert_held(&lec_mutex);
if (arg < 0) if (arg < 0)
arg = 0; arg = 0;
if (arg >= MAX_LEC_ITF) if (arg >= MAX_LEC_ITF)
@ -742,6 +746,7 @@ static int lecd_attach(struct atm_vcc *vcc, int arg)
snprintf(dev_lec[i]->name, IFNAMSIZ, "lec%d", i); snprintf(dev_lec[i]->name, IFNAMSIZ, "lec%d", i);
if (register_netdev(dev_lec[i])) { if (register_netdev(dev_lec[i])) {
free_netdev(dev_lec[i]); free_netdev(dev_lec[i]);
dev_lec[i] = NULL;
return -EINVAL; return -EINVAL;
} }
@ -904,7 +909,6 @@ static void *lec_itf_walk(struct lec_state *state, loff_t *l)
v = (dev && netdev_priv(dev)) ? v = (dev && netdev_priv(dev)) ?
lec_priv_walk(state, l, netdev_priv(dev)) : NULL; lec_priv_walk(state, l, netdev_priv(dev)) : NULL;
if (!v && dev) { if (!v && dev) {
dev_put(dev);
/* Partial state reset for the next time we get called */ /* Partial state reset for the next time we get called */
dev = NULL; dev = NULL;
} }
@ -928,6 +932,7 @@ static void *lec_seq_start(struct seq_file *seq, loff_t *pos)
{ {
struct lec_state *state = seq->private; struct lec_state *state = seq->private;
mutex_lock(&lec_mutex);
state->itf = 0; state->itf = 0;
state->dev = NULL; state->dev = NULL;
state->locked = NULL; state->locked = NULL;
@ -945,8 +950,9 @@ static void lec_seq_stop(struct seq_file *seq, void *v)
if (state->dev) { if (state->dev) {
spin_unlock_irqrestore(&state->locked->lec_arp_lock, spin_unlock_irqrestore(&state->locked->lec_arp_lock,
state->flags); state->flags);
dev_put(state->dev); state->dev = NULL;
} }
mutex_unlock(&lec_mutex);
} }
static void *lec_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void *lec_seq_next(struct seq_file *seq, void *v, loff_t *pos)
@ -1003,6 +1009,7 @@ static int lane_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
} }
mutex_lock(&lec_mutex);
switch (cmd) { switch (cmd) {
case ATMLEC_CTRL: case ATMLEC_CTRL:
err = lecd_attach(vcc, (int)arg); err = lecd_attach(vcc, (int)arg);
@ -1017,6 +1024,7 @@ static int lane_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
break; break;
} }
mutex_unlock(&lec_mutex);
return err; return err;
} }

View File

@ -36,7 +36,7 @@ static void atm_pop_raw(struct atm_vcc *vcc, struct sk_buff *skb)
pr_debug("(%d) %d -= %d\n", pr_debug("(%d) %d -= %d\n",
vcc->vci, sk_wmem_alloc_get(sk), ATM_SKB(skb)->acct_truesize); vcc->vci, sk_wmem_alloc_get(sk), ATM_SKB(skb)->acct_truesize);
WARN_ON(refcount_sub_and_test(ATM_SKB(skb)->acct_truesize, &sk->sk_wmem_alloc)); atm_return_tx(vcc, skb);
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
sk->sk_write_space(sk); sk->sk_write_space(sk);
} }

View File

@ -6261,9 +6261,6 @@ int skb_ensure_writable(struct sk_buff *skb, unsigned int write_len)
if (!pskb_may_pull(skb, write_len)) if (!pskb_may_pull(skb, write_len))
return -ENOMEM; return -ENOMEM;
if (!skb_frags_readable(skb))
return -EFAULT;
if (!skb_cloned(skb) || skb_clone_writable(skb, write_len)) if (!skb_cloned(skb) || skb_clone_writable(skb, write_len))
return 0; return 0;

View File

@ -3,6 +3,7 @@
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <net/tcp.h> #include <net/tcp.h>
#include <net/busy_poll.h>
void tcp_fastopen_init_key_once(struct net *net) void tcp_fastopen_init_key_once(struct net *net)
{ {
@ -279,6 +280,8 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk,
refcount_set(&req->rsk_refcnt, 2); refcount_set(&req->rsk_refcnt, 2);
sk_mark_napi_id_set(child, skb);
/* Now finish processing the fastopen child socket. */ /* Now finish processing the fastopen child socket. */
tcp_init_transfer(child, BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB, skb); tcp_init_transfer(child, BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB, skb);

View File

@ -2479,20 +2479,33 @@ static inline bool tcp_packet_delayed(const struct tcp_sock *tp)
{ {
const struct sock *sk = (const struct sock *)tp; const struct sock *sk = (const struct sock *)tp;
if (tp->retrans_stamp && /* Received an echoed timestamp before the first retransmission? */
tcp_tsopt_ecr_before(tp, tp->retrans_stamp)) if (tp->retrans_stamp)
return true; /* got echoed TS before first retransmission */ return tcp_tsopt_ecr_before(tp, tp->retrans_stamp);
/* Check if nothing was retransmitted (retrans_stamp==0), which may /* We set tp->retrans_stamp upon the first retransmission of a loss
* happen in fast recovery due to TSQ. But we ignore zero retrans_stamp * recovery episode, so normally if tp->retrans_stamp is 0 then no
* in TCP_SYN_SENT, since when we set FLAG_SYN_ACKED we also clear * retransmission has happened yet (likely due to TSQ, which can cause
* retrans_stamp even if we had retransmitted the SYN. * fast retransmits to be delayed). So if snd_una advanced while
* (tp->retrans_stamp is 0 then apparently a packet was merely delayed,
* not lost. But there are exceptions where we retransmit but then
* clear tp->retrans_stamp, so we check for those exceptions.
*/ */
if (!tp->retrans_stamp && /* no record of a retransmit/SYN? */
sk->sk_state != TCP_SYN_SENT) /* not the FLAG_SYN_ACKED case? */
return true; /* nothing was retransmitted */
/* (1) For non-SACK connections, tcp_is_non_sack_preventing_reopen()
* clears tp->retrans_stamp when snd_una == high_seq.
*/
if (!tcp_is_sack(tp) && !before(tp->snd_una, tp->high_seq))
return false; return false;
/* (2) In TCP_SYN_SENT tcp_clean_rtx_queue() clears tp->retrans_stamp
* when setting FLAG_SYN_ACKED is set, even if the SYN was
* retransmitted.
*/
if (sk->sk_state == TCP_SYN_SENT)
return false;
return true; /* tp->retrans_stamp is zero; no retransmit yet */
} }
/* Undo procedures. */ /* Undo procedures. */

View File

@ -1207,6 +1207,10 @@ static int calipso_req_setattr(struct request_sock *req,
struct ipv6_opt_hdr *old, *new; struct ipv6_opt_hdr *old, *new;
struct sock *sk = sk_to_full_sk(req_to_sk(req)); struct sock *sk = sk_to_full_sk(req_to_sk(req));
/* sk is NULL for SYN+ACK w/ SYN Cookie */
if (!sk)
return -ENOMEM;
if (req_inet->ipv6_opt && req_inet->ipv6_opt->hopopt) if (req_inet->ipv6_opt && req_inet->ipv6_opt->hopopt)
old = req_inet->ipv6_opt->hopopt; old = req_inet->ipv6_opt->hopopt;
else else
@ -1247,6 +1251,10 @@ static void calipso_req_delattr(struct request_sock *req)
struct ipv6_txoptions *txopts; struct ipv6_txoptions *txopts;
struct sock *sk = sk_to_full_sk(req_to_sk(req)); struct sock *sk = sk_to_full_sk(req_to_sk(req));
/* sk is NULL for SYN+ACK w/ SYN Cookie */
if (!sk)
return;
if (!req_inet->ipv6_opt || !req_inet->ipv6_opt->hopopt) if (!req_inet->ipv6_opt || !req_inet->ipv6_opt->hopopt)
return; return;

View File

@ -1,10 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */ /* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Portions * Portions
* Copyright (C) 2022 - 2024 Intel Corporation * Copyright (C) 2022 - 2025 Intel Corporation
*/ */
#ifndef __MAC80211_DEBUG_H #ifndef __MAC80211_DEBUG_H
#define __MAC80211_DEBUG_H #define __MAC80211_DEBUG_H
#include <linux/once_lite.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>
#ifdef CONFIG_MAC80211_OCB_DEBUG #ifdef CONFIG_MAC80211_OCB_DEBUG
@ -152,6 +153,8 @@ do { \
else \ else \
_sdata_err((link)->sdata, fmt, ##__VA_ARGS__); \ _sdata_err((link)->sdata, fmt, ##__VA_ARGS__); \
} while (0) } while (0)
#define link_err_once(link, fmt, ...) \
DO_ONCE_LITE(link_err, link, fmt, ##__VA_ARGS__)
#define link_id_info(sdata, link_id, fmt, ...) \ #define link_id_info(sdata, link_id, fmt, ...) \
do { \ do { \
if (ieee80211_vif_is_mld(&sdata->vif)) \ if (ieee80211_vif_is_mld(&sdata->vif)) \

View File

@ -4432,6 +4432,10 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
if (!multicast && if (!multicast &&
!ether_addr_equal(sdata->dev->dev_addr, hdr->addr1)) !ether_addr_equal(sdata->dev->dev_addr, hdr->addr1))
return false; return false;
/* reject invalid/our STA address */
if (!is_valid_ether_addr(hdr->addr2) ||
ether_addr_equal(sdata->dev->dev_addr, hdr->addr2))
return false;
if (!rx->sta) { if (!rx->sta) {
int rate_idx; int rate_idx;
if (status->encoding != RX_ENC_LEGACY) if (status->encoding != RX_ENC_LEGACY)

View File

@ -5,7 +5,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net> * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2018-2024 Intel Corporation * Copyright (C) 2018-2025 Intel Corporation
* *
* Transmit and frame generation functions. * Transmit and frame generation functions.
*/ */
@ -5016,12 +5016,25 @@ static void ieee80211_set_beacon_cntdwn(struct ieee80211_sub_if_data *sdata,
} }
} }
static u8 __ieee80211_beacon_update_cntdwn(struct beacon_data *beacon) static u8 __ieee80211_beacon_update_cntdwn(struct ieee80211_link_data *link,
struct beacon_data *beacon)
{ {
beacon->cntdwn_current_counter--; if (beacon->cntdwn_current_counter == 1) {
/*
* Channel switch handling is done by a worker thread while
* beacons get pulled from hardware timers. It's therefore
* possible that software threads are slow enough to not be
* able to complete CSA handling in a single beacon interval,
* in which case we get here. There isn't much to do about
* it, other than letting the user know that the AP isn't
* behaving correctly.
*/
link_err_once(link,
"beacon TX faster than countdown (channel/color switch) completion\n");
return 0;
}
/* the counter should never reach 0 */ beacon->cntdwn_current_counter--;
WARN_ON_ONCE(!beacon->cntdwn_current_counter);
return beacon->cntdwn_current_counter; return beacon->cntdwn_current_counter;
} }
@ -5052,7 +5065,7 @@ u8 ieee80211_beacon_update_cntdwn(struct ieee80211_vif *vif, unsigned int link_i
if (!beacon) if (!beacon)
goto unlock; goto unlock;
count = __ieee80211_beacon_update_cntdwn(beacon); count = __ieee80211_beacon_update_cntdwn(link, beacon);
unlock: unlock:
rcu_read_unlock(); rcu_read_unlock();
@ -5450,7 +5463,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
if (beacon->cntdwn_counter_offsets[0]) { if (beacon->cntdwn_counter_offsets[0]) {
if (!is_template) if (!is_template)
__ieee80211_beacon_update_cntdwn(beacon); __ieee80211_beacon_update_cntdwn(link, beacon);
ieee80211_set_beacon_cntdwn(sdata, beacon, link); ieee80211_set_beacon_cntdwn(sdata, beacon, link);
} }
@ -5482,7 +5495,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
* for now we leave it consistent with overall * for now we leave it consistent with overall
* mac80211's behavior. * mac80211's behavior.
*/ */
__ieee80211_beacon_update_cntdwn(beacon); __ieee80211_beacon_update_cntdwn(link, beacon);
ieee80211_set_beacon_cntdwn(sdata, beacon, link); ieee80211_set_beacon_cntdwn(sdata, beacon, link);
} }

View File

@ -81,8 +81,8 @@ static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index)
if (index < net->mpls.platform_labels) { if (index < net->mpls.platform_labels) {
struct mpls_route __rcu **platform_label = struct mpls_route __rcu **platform_label =
rcu_dereference(net->mpls.platform_label); rcu_dereference_rtnl(net->mpls.platform_label);
rt = rcu_dereference(platform_label[index]); rt = rcu_dereference_rtnl(platform_label[index]);
} }
return rt; return rt;
} }

View File

@ -119,22 +119,22 @@ static int nci_uart_set_driver(struct tty_struct *tty, unsigned int driver)
memcpy(nu, nci_uart_drivers[driver], sizeof(struct nci_uart)); memcpy(nu, nci_uart_drivers[driver], sizeof(struct nci_uart));
nu->tty = tty; nu->tty = tty;
tty->disc_data = nu;
skb_queue_head_init(&nu->tx_q); skb_queue_head_init(&nu->tx_q);
INIT_WORK(&nu->write_work, nci_uart_write_work); INIT_WORK(&nu->write_work, nci_uart_write_work);
spin_lock_init(&nu->rx_lock); spin_lock_init(&nu->rx_lock);
ret = nu->ops.open(nu); ret = nu->ops.open(nu);
if (ret) { if (ret) {
tty->disc_data = NULL;
kfree(nu); kfree(nu);
return ret;
} else if (!try_module_get(nu->owner)) { } else if (!try_module_get(nu->owner)) {
nu->ops.close(nu); nu->ops.close(nu);
tty->disc_data = NULL;
kfree(nu); kfree(nu);
return -ENOENT; return -ENOENT;
} }
return ret; tty->disc_data = nu;
return 0;
} }
/* ------ LDISC part ------ */ /* ------ LDISC part ------ */

View File

@ -39,16 +39,14 @@
#include "flow_netlink.h" #include "flow_netlink.h"
#include "openvswitch_trace.h" #include "openvswitch_trace.h"
DEFINE_PER_CPU(struct ovs_pcpu_storage, ovs_pcpu_storage) = { struct ovs_pcpu_storage __percpu *ovs_pcpu_storage;
.bh_lock = INIT_LOCAL_LOCK(bh_lock),
};
/* Make a clone of the 'key', using the pre-allocated percpu 'flow_keys' /* Make a clone of the 'key', using the pre-allocated percpu 'flow_keys'
* space. Return NULL if out of key spaces. * space. Return NULL if out of key spaces.
*/ */
static struct sw_flow_key *clone_key(const struct sw_flow_key *key_) static struct sw_flow_key *clone_key(const struct sw_flow_key *key_)
{ {
struct ovs_pcpu_storage *ovs_pcpu = this_cpu_ptr(&ovs_pcpu_storage); struct ovs_pcpu_storage *ovs_pcpu = this_cpu_ptr(ovs_pcpu_storage);
struct action_flow_keys *keys = &ovs_pcpu->flow_keys; struct action_flow_keys *keys = &ovs_pcpu->flow_keys;
int level = ovs_pcpu->exec_level; int level = ovs_pcpu->exec_level;
struct sw_flow_key *key = NULL; struct sw_flow_key *key = NULL;
@ -94,7 +92,7 @@ static struct deferred_action *add_deferred_actions(struct sk_buff *skb,
const struct nlattr *actions, const struct nlattr *actions,
const int actions_len) const int actions_len)
{ {
struct action_fifo *fifo = this_cpu_ptr(&ovs_pcpu_storage.action_fifos); struct action_fifo *fifo = this_cpu_ptr(&ovs_pcpu_storage->action_fifos);
struct deferred_action *da; struct deferred_action *da;
da = action_fifo_put(fifo); da = action_fifo_put(fifo);
@ -755,7 +753,7 @@ static int set_sctp(struct sk_buff *skb, struct sw_flow_key *flow_key,
static int ovs_vport_output(struct net *net, struct sock *sk, static int ovs_vport_output(struct net *net, struct sock *sk,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct ovs_frag_data *data = this_cpu_ptr(&ovs_pcpu_storage.frag_data); struct ovs_frag_data *data = this_cpu_ptr(&ovs_pcpu_storage->frag_data);
struct vport *vport = data->vport; struct vport *vport = data->vport;
if (skb_cow_head(skb, data->l2_len) < 0) { if (skb_cow_head(skb, data->l2_len) < 0) {
@ -807,7 +805,7 @@ static void prepare_frag(struct vport *vport, struct sk_buff *skb,
unsigned int hlen = skb_network_offset(skb); unsigned int hlen = skb_network_offset(skb);
struct ovs_frag_data *data; struct ovs_frag_data *data;
data = this_cpu_ptr(&ovs_pcpu_storage.frag_data); data = this_cpu_ptr(&ovs_pcpu_storage->frag_data);
data->dst = skb->_skb_refdst; data->dst = skb->_skb_refdst;
data->vport = vport; data->vport = vport;
data->cb = *OVS_CB(skb); data->cb = *OVS_CB(skb);
@ -1566,16 +1564,15 @@ static int clone_execute(struct datapath *dp, struct sk_buff *skb,
clone = clone_flow_key ? clone_key(key) : key; clone = clone_flow_key ? clone_key(key) : key;
if (clone) { if (clone) {
int err = 0; int err = 0;
if (actions) { /* Sample action */ if (actions) { /* Sample action */
if (clone_flow_key) if (clone_flow_key)
__this_cpu_inc(ovs_pcpu_storage.exec_level); __this_cpu_inc(ovs_pcpu_storage->exec_level);
err = do_execute_actions(dp, skb, clone, err = do_execute_actions(dp, skb, clone,
actions, len); actions, len);
if (clone_flow_key) if (clone_flow_key)
__this_cpu_dec(ovs_pcpu_storage.exec_level); __this_cpu_dec(ovs_pcpu_storage->exec_level);
} else { /* Recirc action */ } else { /* Recirc action */
clone->recirc_id = recirc_id; clone->recirc_id = recirc_id;
ovs_dp_process_packet(skb, clone); ovs_dp_process_packet(skb, clone);
@ -1611,7 +1608,7 @@ static int clone_execute(struct datapath *dp, struct sk_buff *skb,
static void process_deferred_actions(struct datapath *dp) static void process_deferred_actions(struct datapath *dp)
{ {
struct action_fifo *fifo = this_cpu_ptr(&ovs_pcpu_storage.action_fifos); struct action_fifo *fifo = this_cpu_ptr(&ovs_pcpu_storage->action_fifos);
/* Do not touch the FIFO in case there is no deferred actions. */ /* Do not touch the FIFO in case there is no deferred actions. */
if (action_fifo_is_empty(fifo)) if (action_fifo_is_empty(fifo))
@ -1642,7 +1639,7 @@ int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb,
{ {
int err, level; int err, level;
level = __this_cpu_inc_return(ovs_pcpu_storage.exec_level); level = __this_cpu_inc_return(ovs_pcpu_storage->exec_level);
if (unlikely(level > OVS_RECURSION_LIMIT)) { if (unlikely(level > OVS_RECURSION_LIMIT)) {
net_crit_ratelimited("ovs: recursion limit reached on datapath %s, probable configuration error\n", net_crit_ratelimited("ovs: recursion limit reached on datapath %s, probable configuration error\n",
ovs_dp_name(dp)); ovs_dp_name(dp));
@ -1659,6 +1656,6 @@ int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb,
process_deferred_actions(dp); process_deferred_actions(dp);
out: out:
__this_cpu_dec(ovs_pcpu_storage.exec_level); __this_cpu_dec(ovs_pcpu_storage->exec_level);
return err; return err;
} }

View File

@ -244,7 +244,7 @@ void ovs_dp_detach_port(struct vport *p)
/* Must be called with rcu_read_lock. */ /* Must be called with rcu_read_lock. */
void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
{ {
struct ovs_pcpu_storage *ovs_pcpu = this_cpu_ptr(&ovs_pcpu_storage); struct ovs_pcpu_storage *ovs_pcpu = this_cpu_ptr(ovs_pcpu_storage);
const struct vport *p = OVS_CB(skb)->input_vport; const struct vport *p = OVS_CB(skb)->input_vport;
struct datapath *dp = p->dp; struct datapath *dp = p->dp;
struct sw_flow *flow; struct sw_flow *flow;
@ -299,7 +299,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
* avoided. * avoided.
*/ */
if (IS_ENABLED(CONFIG_PREEMPT_RT) && ovs_pcpu->owner != current) { if (IS_ENABLED(CONFIG_PREEMPT_RT) && ovs_pcpu->owner != current) {
local_lock_nested_bh(&ovs_pcpu_storage.bh_lock); local_lock_nested_bh(&ovs_pcpu_storage->bh_lock);
ovs_pcpu->owner = current; ovs_pcpu->owner = current;
ovs_pcpu_locked = true; ovs_pcpu_locked = true;
} }
@ -310,7 +310,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
ovs_dp_name(dp), error); ovs_dp_name(dp), error);
if (ovs_pcpu_locked) { if (ovs_pcpu_locked) {
ovs_pcpu->owner = NULL; ovs_pcpu->owner = NULL;
local_unlock_nested_bh(&ovs_pcpu_storage.bh_lock); local_unlock_nested_bh(&ovs_pcpu_storage->bh_lock);
} }
stats_counter = &stats->n_hit; stats_counter = &stats->n_hit;
@ -689,13 +689,13 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
sf_acts = rcu_dereference(flow->sf_acts); sf_acts = rcu_dereference(flow->sf_acts);
local_bh_disable(); local_bh_disable();
local_lock_nested_bh(&ovs_pcpu_storage.bh_lock); local_lock_nested_bh(&ovs_pcpu_storage->bh_lock);
if (IS_ENABLED(CONFIG_PREEMPT_RT)) if (IS_ENABLED(CONFIG_PREEMPT_RT))
this_cpu_write(ovs_pcpu_storage.owner, current); this_cpu_write(ovs_pcpu_storage->owner, current);
err = ovs_execute_actions(dp, packet, sf_acts, &flow->key); err = ovs_execute_actions(dp, packet, sf_acts, &flow->key);
if (IS_ENABLED(CONFIG_PREEMPT_RT)) if (IS_ENABLED(CONFIG_PREEMPT_RT))
this_cpu_write(ovs_pcpu_storage.owner, NULL); this_cpu_write(ovs_pcpu_storage->owner, NULL);
local_unlock_nested_bh(&ovs_pcpu_storage.bh_lock); local_unlock_nested_bh(&ovs_pcpu_storage->bh_lock);
local_bh_enable(); local_bh_enable();
rcu_read_unlock(); rcu_read_unlock();
@ -2744,6 +2744,28 @@ static struct drop_reason_list drop_reason_list_ovs = {
.n_reasons = ARRAY_SIZE(ovs_drop_reasons), .n_reasons = ARRAY_SIZE(ovs_drop_reasons),
}; };
static int __init ovs_alloc_percpu_storage(void)
{
unsigned int cpu;
ovs_pcpu_storage = alloc_percpu(*ovs_pcpu_storage);
if (!ovs_pcpu_storage)
return -ENOMEM;
for_each_possible_cpu(cpu) {
struct ovs_pcpu_storage *ovs_pcpu;
ovs_pcpu = per_cpu_ptr(ovs_pcpu_storage, cpu);
local_lock_init(&ovs_pcpu->bh_lock);
}
return 0;
}
static void ovs_free_percpu_storage(void)
{
free_percpu(ovs_pcpu_storage);
}
static int __init dp_init(void) static int __init dp_init(void)
{ {
int err; int err;
@ -2753,6 +2775,10 @@ static int __init dp_init(void)
pr_info("Open vSwitch switching datapath\n"); pr_info("Open vSwitch switching datapath\n");
err = ovs_alloc_percpu_storage();
if (err)
goto error;
err = ovs_internal_dev_rtnl_link_register(); err = ovs_internal_dev_rtnl_link_register();
if (err) if (err)
goto error; goto error;
@ -2799,6 +2825,7 @@ error_flow_exit:
error_unreg_rtnl_link: error_unreg_rtnl_link:
ovs_internal_dev_rtnl_link_unregister(); ovs_internal_dev_rtnl_link_unregister();
error: error:
ovs_free_percpu_storage();
return err; return err;
} }
@ -2813,6 +2840,7 @@ static void dp_cleanup(void)
ovs_vport_exit(); ovs_vport_exit();
ovs_flow_exit(); ovs_flow_exit();
ovs_internal_dev_rtnl_link_unregister(); ovs_internal_dev_rtnl_link_unregister();
ovs_free_percpu_storage();
} }
module_init(dp_init); module_init(dp_init);

View File

@ -220,7 +220,8 @@ struct ovs_pcpu_storage {
struct task_struct *owner; struct task_struct *owner;
local_lock_t bh_lock; local_lock_t bh_lock;
}; };
DECLARE_PER_CPU(struct ovs_pcpu_storage, ovs_pcpu_storage);
extern struct ovs_pcpu_storage __percpu *ovs_pcpu_storage;
/** /**
* enum ovs_pkt_hash_types - hash info to include with a packet * enum ovs_pkt_hash_types - hash info to include with a packet

View File

@ -1328,13 +1328,15 @@ static int taprio_dev_notifier(struct notifier_block *nb, unsigned long event,
stab = rtnl_dereference(q->root->stab); stab = rtnl_dereference(q->root->stab);
oper = rtnl_dereference(q->oper_sched); rcu_read_lock();
oper = rcu_dereference(q->oper_sched);
if (oper) if (oper)
taprio_update_queue_max_sdu(q, oper, stab); taprio_update_queue_max_sdu(q, oper, stab);
admin = rtnl_dereference(q->admin_sched); admin = rcu_dereference(q->admin_sched);
if (admin) if (admin)
taprio_update_queue_max_sdu(q, admin, stab); taprio_update_queue_max_sdu(q, admin, stab);
rcu_read_unlock();
break; break;
} }

View File

@ -489,7 +489,7 @@ int tipc_udp_nl_dump_remoteip(struct sk_buff *skb, struct netlink_callback *cb)
rtnl_lock(); rtnl_lock();
b = tipc_bearer_find(net, bname); b = tipc_bearer_find(net, bname);
if (!b) { if (!b || b->bcast_addr.media_id != TIPC_MEDIA_TYPE_UDP) {
rtnl_unlock(); rtnl_unlock();
return -EINVAL; return -EINVAL;
} }
@ -500,7 +500,7 @@ int tipc_udp_nl_dump_remoteip(struct sk_buff *skb, struct netlink_callback *cb)
rtnl_lock(); rtnl_lock();
b = rtnl_dereference(tn->bearer_list[bid]); b = rtnl_dereference(tn->bearer_list[bid]);
if (!b) { if (!b || b->bcast_addr.media_id != TIPC_MEDIA_TYPE_UDP) {
rtnl_unlock(); rtnl_unlock();
return -EINVAL; return -EINVAL;
} }

View File

@ -231,14 +231,7 @@ class NlMsg:
self.extack['unknown'].append(extack) self.extack['unknown'].append(extack)
if attr_space: if attr_space:
# We don't have the ability to parse nests yet, so only do global self.annotate_extack(attr_space)
if 'miss-type' in self.extack and 'miss-nest' not in self.extack:
miss_type = self.extack['miss-type']
if miss_type in attr_space.attrs_by_val:
spec = attr_space.attrs_by_val[miss_type]
self.extack['miss-type'] = spec['name']
if 'doc' in spec:
self.extack['miss-type-doc'] = spec['doc']
def _decode_policy(self, raw): def _decode_policy(self, raw):
policy = {} policy = {}
@ -264,6 +257,18 @@ class NlMsg:
policy['mask'] = attr.as_scalar('u64') policy['mask'] = attr.as_scalar('u64')
return policy return policy
def annotate_extack(self, attr_space):
""" Make extack more human friendly with attribute information """
# We don't have the ability to parse nests yet, so only do global
if 'miss-type' in self.extack and 'miss-nest' not in self.extack:
miss_type = self.extack['miss-type']
if miss_type in attr_space.attrs_by_val:
spec = attr_space.attrs_by_val[miss_type]
self.extack['miss-type'] = spec['name']
if 'doc' in spec:
self.extack['miss-type-doc'] = spec['doc']
def cmd(self): def cmd(self):
return self.nl_type return self.nl_type
@ -277,12 +282,12 @@ class NlMsg:
class NlMsgs: class NlMsgs:
def __init__(self, data, attr_space=None): def __init__(self, data):
self.msgs = [] self.msgs = []
offset = 0 offset = 0
while offset < len(data): while offset < len(data):
msg = NlMsg(data, offset, attr_space=attr_space) msg = NlMsg(data, offset)
offset += msg.nl_len offset += msg.nl_len
self.msgs.append(msg) self.msgs.append(msg)
@ -1034,12 +1039,13 @@ class YnlFamily(SpecFamily):
op_rsp = [] op_rsp = []
while not done: while not done:
reply = self.sock.recv(self._recv_size) reply = self.sock.recv(self._recv_size)
nms = NlMsgs(reply, attr_space=op.attr_set) nms = NlMsgs(reply)
self._recv_dbg_print(reply, nms) self._recv_dbg_print(reply, nms)
for nl_msg in nms: for nl_msg in nms:
if nl_msg.nl_seq in reqs_by_seq: if nl_msg.nl_seq in reqs_by_seq:
(op, vals, req_msg, req_flags) = reqs_by_seq[nl_msg.nl_seq] (op, vals, req_msg, req_flags) = reqs_by_seq[nl_msg.nl_seq]
if nl_msg.extack: if nl_msg.extack:
nl_msg.annotate_extack(op.attr_set)
self._decode_extack(req_msg, op, nl_msg.extack, vals) self._decode_extack(req_msg, op, nl_msg.extack, vals)
else: else:
op = None op = None

View File

@ -1,7 +1,8 @@
#!/bin/bash #!/bin/bash
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
source ../../../net/lib.sh lib_dir=$(dirname $0)/../../../net
source $lib_dir/lib.sh
NSIM_DEV_1_ID=$((256 + RANDOM % 256)) NSIM_DEV_1_ID=$((256 + RANDOM % 256))
NSIM_DEV_1_SYS=/sys/bus/netdevsim/devices/netdevsim$NSIM_DEV_1_ID NSIM_DEV_1_SYS=/sys/bus/netdevsim/devices/netdevsim$NSIM_DEV_1_ID

View File

@ -50,6 +50,7 @@ tap
tcp_fastopen_backup_key tcp_fastopen_backup_key
tcp_inq tcp_inq
tcp_mmap tcp_mmap
tfo
timestamping timestamping
tls tls
toeplitz toeplitz

View File

@ -110,6 +110,8 @@ TEST_GEN_PROGS += proc_net_pktgen
TEST_PROGS += lwt_dst_cache_ref_loop.sh TEST_PROGS += lwt_dst_cache_ref_loop.sh
TEST_PROGS += skf_net_off.sh TEST_PROGS += skf_net_off.sh
TEST_GEN_FILES += skf_net_off TEST_GEN_FILES += skf_net_off
TEST_GEN_FILES += tfo
TEST_PROGS += tfo_passive.sh
# YNL files, must be before "include ..lib.mk" # YNL files, must be before "include ..lib.mk"
YNL_GEN_FILES := busy_poller netlink-dumps YNL_GEN_FILES := busy_poller netlink-dumps

View File

@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0
#include <error.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <errno.h>
static int cfg_server;
static int cfg_client;
static int cfg_port = 8000;
static struct sockaddr_in6 cfg_addr;
static char *cfg_outfile;
static int parse_address(const char *str, int port, struct sockaddr_in6 *sin6)
{
int ret;
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(port);
ret = inet_pton(sin6->sin6_family, str, &sin6->sin6_addr);
if (ret != 1) {
/* fallback to plain IPv4 */
ret = inet_pton(AF_INET, str, &sin6->sin6_addr.s6_addr32[3]);
if (ret != 1)
return -1;
/* add ::ffff prefix */
sin6->sin6_addr.s6_addr32[0] = 0;
sin6->sin6_addr.s6_addr32[1] = 0;
sin6->sin6_addr.s6_addr16[4] = 0;
sin6->sin6_addr.s6_addr16[5] = 0xffff;
}
return 0;
}
static void run_server(void)
{
unsigned long qlen = 32;
int fd, opt, connfd;
socklen_t len;
char buf[64];
FILE *outfile;
outfile = fopen(cfg_outfile, "w");
if (!outfile)
error(1, errno, "fopen() outfile");
fd = socket(AF_INET6, SOCK_STREAM, 0);
if (fd == -1)
error(1, errno, "socket()");
opt = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
error(1, errno, "setsockopt(SO_REUSEADDR)");
if (setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)) < 0)
error(1, errno, "setsockopt(TCP_FASTOPEN)");
if (bind(fd, (struct sockaddr *)&cfg_addr, sizeof(cfg_addr)) < 0)
error(1, errno, "bind()");
if (listen(fd, 5) < 0)
error(1, errno, "listen()");
len = sizeof(cfg_addr);
connfd = accept(fd, (struct sockaddr *)&cfg_addr, &len);
if (connfd < 0)
error(1, errno, "accept()");
len = sizeof(opt);
if (getsockopt(connfd, SOL_SOCKET, SO_INCOMING_NAPI_ID, &opt, &len) < 0)
error(1, errno, "getsockopt(SO_INCOMING_NAPI_ID)");
read(connfd, buf, 64);
fprintf(outfile, "%d\n", opt);
fclose(outfile);
close(connfd);
close(fd);
}
static void run_client(void)
{
int fd;
char *msg = "Hello, world!";
fd = socket(AF_INET6, SOCK_STREAM, 0);
if (fd == -1)
error(1, errno, "socket()");
sendto(fd, msg, strlen(msg), MSG_FASTOPEN, (struct sockaddr *)&cfg_addr, sizeof(cfg_addr));
close(fd);
}
static void usage(const char *filepath)
{
error(1, 0, "Usage: %s (-s|-c) -h<server_ip> -p<port> -o<outfile> ", filepath);
}
static void parse_opts(int argc, char **argv)
{
struct sockaddr_in6 *addr6 = (void *) &cfg_addr;
char *addr = NULL;
int ret;
int c;
if (argc <= 1)
usage(argv[0]);
while ((c = getopt(argc, argv, "sch:p:o:")) != -1) {
switch (c) {
case 's':
if (cfg_client)
error(1, 0, "Pass one of -s or -c");
cfg_server = 1;
break;
case 'c':
if (cfg_server)
error(1, 0, "Pass one of -s or -c");
cfg_client = 1;
break;
case 'h':
addr = optarg;
break;
case 'p':
cfg_port = strtoul(optarg, NULL, 0);
break;
case 'o':
cfg_outfile = strdup(optarg);
if (!cfg_outfile)
error(1, 0, "outfile invalid");
break;
}
}
if (cfg_server && addr)
error(1, 0, "Server cannot have -h specified");
memset(addr6, 0, sizeof(*addr6));
addr6->sin6_family = AF_INET6;
addr6->sin6_port = htons(cfg_port);
addr6->sin6_addr = in6addr_any;
if (addr) {
ret = parse_address(addr, cfg_port, addr6);
if (ret)
error(1, 0, "Client address parse error: %s", addr);
}
}
int main(int argc, char **argv)
{
parse_opts(argc, argv);
if (cfg_server)
run_server();
else if (cfg_client)
run_client();
return 0;
}

View File

@ -0,0 +1,112 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
source lib.sh
NSIM_SV_ID=$((256 + RANDOM % 256))
NSIM_SV_SYS=/sys/bus/netdevsim/devices/netdevsim$NSIM_SV_ID
NSIM_CL_ID=$((512 + RANDOM % 256))
NSIM_CL_SYS=/sys/bus/netdevsim/devices/netdevsim$NSIM_CL_ID
NSIM_DEV_SYS_NEW=/sys/bus/netdevsim/new_device
NSIM_DEV_SYS_DEL=/sys/bus/netdevsim/del_device
NSIM_DEV_SYS_LINK=/sys/bus/netdevsim/link_device
NSIM_DEV_SYS_UNLINK=/sys/bus/netdevsim/unlink_device
SERVER_IP=192.168.1.1
CLIENT_IP=192.168.1.2
SERVER_PORT=48675
setup_ns()
{
set -e
ip netns add nssv
ip netns add nscl
NSIM_SV_NAME=$(find $NSIM_SV_SYS/net -maxdepth 1 -type d ! \
-path $NSIM_SV_SYS/net -exec basename {} \;)
NSIM_CL_NAME=$(find $NSIM_CL_SYS/net -maxdepth 1 -type d ! \
-path $NSIM_CL_SYS/net -exec basename {} \;)
ip link set $NSIM_SV_NAME netns nssv
ip link set $NSIM_CL_NAME netns nscl
ip netns exec nssv ip addr add "${SERVER_IP}/24" dev $NSIM_SV_NAME
ip netns exec nscl ip addr add "${CLIENT_IP}/24" dev $NSIM_CL_NAME
ip netns exec nssv ip link set dev $NSIM_SV_NAME up
ip netns exec nscl ip link set dev $NSIM_CL_NAME up
# Enable passive TFO
ip netns exec nssv sysctl -w net.ipv4.tcp_fastopen=519 > /dev/null
set +e
}
cleanup_ns()
{
ip netns del nscl
ip netns del nssv
}
###
### Code start
###
modprobe netdevsim
# linking
echo $NSIM_SV_ID > $NSIM_DEV_SYS_NEW
echo $NSIM_CL_ID > $NSIM_DEV_SYS_NEW
udevadm settle
setup_ns
NSIM_SV_FD=$((256 + RANDOM % 256))
exec {NSIM_SV_FD}</var/run/netns/nssv
NSIM_SV_IFIDX=$(ip netns exec nssv cat /sys/class/net/$NSIM_SV_NAME/ifindex)
NSIM_CL_FD=$((256 + RANDOM % 256))
exec {NSIM_CL_FD}</var/run/netns/nscl
NSIM_CL_IFIDX=$(ip netns exec nscl cat /sys/class/net/$NSIM_CL_NAME/ifindex)
echo "$NSIM_SV_FD:$NSIM_SV_IFIDX $NSIM_CL_FD:$NSIM_CL_IFIDX" > \
$NSIM_DEV_SYS_LINK
if [ $? -ne 0 ]; then
echo "linking netdevsim1 with netdevsim2 should succeed"
cleanup_ns
exit 1
fi
out_file=$(mktemp)
timeout -k 1s 30s ip netns exec nssv ./tfo \
-s \
-p ${SERVER_PORT} \
-o ${out_file}&
wait_local_port_listen nssv ${SERVER_PORT} tcp
ip netns exec nscl ./tfo -c -h ${SERVER_IP} -p ${SERVER_PORT}
wait
res=$(cat $out_file)
rm $out_file
if [ $res -eq 0 ]; then
echo "got invalid NAPI ID from passive TFO socket"
cleanup_ns
exit 1
fi
echo "$NSIM_SV_FD:$NSIM_SV_IFIDX" > $NSIM_DEV_SYS_UNLINK
echo $NSIM_CL_ID > $NSIM_DEV_SYS_DEL
cleanup_ns
modprobe -r netdevsim
exit 0