aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--CREDITS10
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/net/can/usb/ucan.c2
-rw-r--r--drivers/net/ethernet/airoha/airoha_eth.c97
-rw-r--r--drivers/net/ethernet/airoha/airoha_eth.h2
-rw-r--r--drivers/net/ethernet/airoha/airoha_ppe.c14
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_auxr.c1
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c30
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c47
-rw-r--r--drivers/net/ethernet/mediatek/mtk_eth_soc.c22
-rw-r--r--drivers/net/ethernet/mediatek/mtk_ppe.c30
-rw-r--r--drivers/net/ethernet/mediatek/mtk_ppe.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c12
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c8
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_pci.c2
-rw-r--r--drivers/net/ethernet/microsoft/mana/gdma_main.c7
-rw-r--r--drivers/net/ethernet/microsoft/mana/mana_en.c4
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c2
-rw-r--r--drivers/net/hamradio/6pack.c9
-rw-r--r--drivers/net/hamradio/bpqether.c3
-rw-r--r--drivers/net/hamradio/scc.c2
-rw-r--r--drivers/net/ipa/gsi.c1
-rw-r--r--drivers/net/ipa/ipa_main.c6
-rw-r--r--drivers/net/macsec.c71
-rw-r--r--drivers/net/netdevsim/netdev.c65
-rw-r--r--drivers/net/netdevsim/netdevsim.h8
-rw-r--r--drivers/net/phy/phy_device.c4
-rw-r--r--drivers/net/phy/qcom/at803x.c2
-rw-r--r--drivers/net/ppp/ppp_generic.c3
-rw-r--r--drivers/net/usb/cdc-phonet.c7
-rw-r--r--include/net/sch_generic.h16
-rw-r--r--include/uapi/linux/mii.h3
-rw-r--r--net/caif/cfsrvl.c14
-rw-r--r--net/can/raw.c11
-rw-r--r--net/core/filter.c8
-rw-r--r--net/ipv4/syncookies.c2
-rw-r--r--net/nfc/digital_technology.c6
-rw-r--r--net/nfc/llcp_core.c2
-rw-r--r--net/qrtr/ns.c79
-rw-r--r--net/rds/af_rds.c10
-rw-r--r--net/rds/ib.c24
-rw-r--r--net/rds/ib.h1
-rw-r--r--net/rds/ib_rdma.c2
-rw-r--r--net/rose/rose_in.c7
-rw-r--r--net/sched/act_ct.c8
-rw-r--r--net/sched/cls_fw.c6
-rw-r--r--net/sctp/inqueue.c1
-rw-r--r--net/sctp/ipv6.c2
-rw-r--r--net/sctp/protocol.c2
-rw-r--r--net/strparser/strparser.c8
-rw-r--r--net/vmw_vsock/af_vsock.c6
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sock_ops_get_sk.c76
-rw-r--r--tools/testing/selftests/bpf/prog_tests/xdp_bonding.c96
-rw-r--r--tools/testing/selftests/bpf/progs/sock_ops_get_sk.c117
-rw-r--r--tools/testing/selftests/drivers/net/Makefile1
-rw-r--r--tools/testing/selftests/drivers/net/config2
-rw-r--r--tools/testing/selftests/drivers/net/lib/py/env.py9
-rwxr-xr-xtools/testing/selftests/drivers/net/macsec.py343
-rw-r--r--tools/testing/selftests/drivers/net/netdevsim/Makefile1
-rwxr-xr-xtools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh117
60 files changed, 1204 insertions, 250 deletions
diff --git a/CREDITS b/CREDITS
index 9091bac3d2da..a03b00452a1e 100644
--- a/CREDITS
+++ b/CREDITS
@@ -3592,6 +3592,16 @@ E: wsalamon@tislabs.com
E: wsalamon@nai.com
D: portions of the Linux Security Module (LSM) framework and security modules
+N: Salil Mehta
+E: salil.mehta@opnsrc.net
+D: Co-authored Huawei/HiSilicon Kunpeng 920 SoC HNS3 PF and VF 100G
+D: Ethernet driver
+D: Co-authored Huawei/HiSilicon Kunpeng 916 SoC HNS 10G Ethernet
+D: driver enhancements
+D: Maintained Huawei/HiSilicon HNS and HNS3 10G/100G Ethernet drivers
+D: for Kunpeng 916 family, 920 family of SoCs
+S: Cambridge, Cambridgeshire, United Kingdom
+
N: Robert Sanders
E: gt8134b@prism.gatech.edu
D: Dosemu
diff --git a/MAINTAINERS b/MAINTAINERS
index fa1bdb1db73e..0a71d167a22b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11534,7 +11534,6 @@ F: drivers/bus/hisi_lpc.c
HISILICON NETWORK SUBSYSTEM 3 DRIVER (HNS3)
M: Jian Shen <shenjian15@huawei.com>
-M: Salil Mehta <salil.mehta@huawei.com>
M: Jijie Shao <shaojijie@huawei.com>
L: netdev@vger.kernel.org
S: Maintained
@@ -11549,7 +11548,6 @@ F: drivers/net/ethernet/hisilicon/hibmcge/
HISILICON NETWORK SUBSYSTEM DRIVER
M: Jian Shen <shenjian15@huawei.com>
-M: Salil Mehta <salil.mehta@huawei.com>
L: netdev@vger.kernel.org
S: Maintained
W: http://www.hisilicon.com
diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c
index 506d9e74aa79..f72e0644c5fd 100644
--- a/drivers/net/can/usb/ucan.c
+++ b/drivers/net/can/usb/ucan.c
@@ -1379,7 +1379,7 @@ static int ucan_probe(struct usb_interface *intf,
*/
/* Prepare Memory for control transfers */
- ctl_msg_buffer = devm_kzalloc(&udev->dev,
+ ctl_msg_buffer = devm_kzalloc(&intf->dev,
sizeof(union ucan_ctl_payload),
GFP_KERNEL);
if (!ctl_msg_buffer) {
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 2bd79da70934..e1ab15f1ee7d 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -107,19 +107,7 @@ static int airoha_set_vip_for_gdm_port(struct airoha_gdm_port *port,
struct airoha_eth *eth = port->qdma->eth;
u32 vip_port;
- switch (port->id) {
- case AIROHA_GDM3_IDX:
- /* FIXME: handle XSI_PCIE1_PORT */
- vip_port = XSI_PCIE0_VIP_PORT_MASK;
- break;
- case AIROHA_GDM4_IDX:
- /* FIXME: handle XSI_USB_PORT */
- vip_port = XSI_ETH_VIP_PORT_MASK;
- break;
- default:
- return 0;
- }
-
+ vip_port = eth->soc->ops.get_vip_port(port, port->nbq);
if (enable) {
airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port);
airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port);
@@ -293,16 +281,18 @@ static void airoha_fe_pse_ports_init(struct airoha_eth *eth)
[FE_PSE_PORT_GDM4] = 2,
[FE_PSE_PORT_CDM5] = 2,
};
- u32 all_rsv;
int q;
- all_rsv = airoha_fe_get_pse_all_rsv(eth);
if (airoha_ppe_is_enabled(eth, 1)) {
+ u32 all_rsv;
+
/* hw misses PPE2 oq rsv */
+ all_rsv = airoha_fe_get_pse_all_rsv(eth);
all_rsv += PSE_RSV_PAGES *
pse_port_num_queues[FE_PSE_PORT_PPE2];
+ airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK,
+ FIELD_PREP(PSE_ALLRSV_MASK, all_rsv));
}
- airoha_fe_set(eth, REG_FE_PSE_BUF_SET, all_rsv);
/* CMD1 */
for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM1]; q++)
@@ -584,7 +574,7 @@ static int airoha_qdma_fill_rx_queue(struct airoha_queue *q)
static int airoha_qdma_get_gdm_port(struct airoha_eth *eth,
struct airoha_qdma_desc *desc)
{
- u32 port, sport, msg1 = le32_to_cpu(desc->msg1);
+ u32 port, sport, msg1 = le32_to_cpu(READ_ONCE(desc->msg1));
sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1);
switch (sport) {
@@ -612,21 +602,24 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
while (done < budget) {
struct airoha_queue_entry *e = &q->entry[q->tail];
struct airoha_qdma_desc *desc = &q->desc[q->tail];
- u32 hash, reason, msg1 = le32_to_cpu(desc->msg1);
- struct page *page = virt_to_head_page(e->buf);
- u32 desc_ctrl = le32_to_cpu(desc->ctrl);
+ u32 hash, reason, msg1, desc_ctrl;
struct airoha_gdm_port *port;
int data_len, len, p;
+ struct page *page;
+ desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
if (!(desc_ctrl & QDMA_DESC_DONE_MASK))
break;
+ dma_rmb();
+
q->tail = (q->tail + 1) % q->ndesc;
q->queued--;
dma_sync_single_for_cpu(eth->dev, e->dma_addr,
SKB_WITH_OVERHEAD(q->buf_size), dir);
+ page = virt_to_head_page(e->buf);
len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl);
data_len = q->skb ? q->buf_size
: SKB_WITH_OVERHEAD(q->buf_size);
@@ -670,8 +663,8 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
* DMA descriptor. Report DSA tag to the DSA stack
* via skb dst info.
*/
- u32 sptag = FIELD_GET(QDMA_ETH_RXMSG_SPTAG,
- le32_to_cpu(desc->msg0));
+ u32 msg0 = le32_to_cpu(READ_ONCE(desc->msg0));
+ u32 sptag = FIELD_GET(QDMA_ETH_RXMSG_SPTAG, msg0);
if (sptag < ARRAY_SIZE(port->dsa_meta) &&
port->dsa_meta[sptag])
@@ -679,6 +672,7 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
&port->dsa_meta[sptag]->dst);
}
+ msg1 = le32_to_cpu(READ_ONCE(desc->msg1));
hash = FIELD_GET(AIROHA_RXD4_FOE_ENTRY, msg1);
if (hash != AIROHA_RXD4_FOE_ENTRY)
skb_set_hash(q->skb, jhash_1word(hash, 0),
@@ -819,6 +813,11 @@ static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q)
}
q->head = q->tail;
+ /* Set RX_DMA_IDX to RX_CPU_IDX to notify the hw the QDMA RX ring is
+ * empty.
+ */
+ airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK,
+ FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head));
airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK,
FIELD_PREP(RX_RING_DMA_IDX_MASK, q->tail));
}
@@ -1727,7 +1726,7 @@ static int airoha_dev_set_macaddr(struct net_device *dev, void *p)
static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
{
struct airoha_eth *eth = port->qdma->eth;
- u32 val, pse_port, chan, nbq;
+ u32 val, pse_port, chan;
int src_port;
/* Forward the traffic to the proper GDM port */
@@ -1757,9 +1756,7 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
- /* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */
- nbq = port->id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
- src_port = eth->soc->ops.get_src_port_id(port, nbq);
+ src_port = eth->soc->ops.get_src_port_id(port, port->nbq);
if (src_port < 0)
return src_port;
@@ -1773,7 +1770,7 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
__field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2));
if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
- u32 mask = FC_ID_OF_SRC_PORT_MASK(nbq);
+ u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq);
airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
__field_prep(mask, AIROHA_GDM2_IDX));
@@ -2952,6 +2949,8 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth,
port->eth = eth;
port->dev = dev;
port->id = id;
+ /* XXX: Read nbq from DTS */
+ port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
eth->ports[p] = port;
return airoha_metadata_dst_alloc(port);
@@ -3148,6 +3147,28 @@ static int airoha_en7581_get_src_port_id(struct airoha_gdm_port *port, int nbq)
return -EINVAL;
}
+static u32 airoha_en7581_get_vip_port(struct airoha_gdm_port *port, int nbq)
+{
+ switch (port->id) {
+ case AIROHA_GDM3_IDX:
+ if (nbq == 4)
+ return XSI_PCIE0_VIP_PORT_MASK;
+ if (nbq == 5)
+ return XSI_PCIE1_VIP_PORT_MASK;
+ break;
+ case AIROHA_GDM4_IDX:
+ if (!nbq)
+ return XSI_ETH_VIP_PORT_MASK;
+ if (nbq == 1)
+ return XSI_USB_VIP_PORT_MASK;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static const char * const an7583_xsi_rsts_names[] = {
"xsi-mac",
"hsi0-mac",
@@ -3177,6 +3198,26 @@ static int airoha_an7583_get_src_port_id(struct airoha_gdm_port *port, int nbq)
return -EINVAL;
}
+static u32 airoha_an7583_get_vip_port(struct airoha_gdm_port *port, int nbq)
+{
+ switch (port->id) {
+ case AIROHA_GDM3_IDX:
+ if (!nbq)
+ return XSI_ETH_VIP_PORT_MASK;
+ break;
+ case AIROHA_GDM4_IDX:
+ if (!nbq)
+ return XSI_PCIE0_VIP_PORT_MASK;
+ if (nbq == 1)
+ return XSI_USB_VIP_PORT_MASK;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static const struct airoha_eth_soc_data en7581_soc_data = {
.version = 0x7581,
.xsi_rsts_names = en7581_xsi_rsts_names,
@@ -3184,6 +3225,7 @@ static const struct airoha_eth_soc_data en7581_soc_data = {
.num_ppe = 2,
.ops = {
.get_src_port_id = airoha_en7581_get_src_port_id,
+ .get_vip_port = airoha_en7581_get_vip_port,
},
};
@@ -3194,6 +3236,7 @@ static const struct airoha_eth_soc_data an7583_soc_data = {
.num_ppe = 1,
.ops = {
.get_src_port_id = airoha_an7583_get_src_port_id,
+ .get_vip_port = airoha_an7583_get_vip_port,
},
};
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index af29fc74165b..95e557638617 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -537,6 +537,7 @@ struct airoha_gdm_port {
struct airoha_eth *eth;
struct net_device *dev;
int id;
+ int nbq;
struct airoha_hw_stats stats;
@@ -577,6 +578,7 @@ struct airoha_eth_soc_data {
int num_ppe;
struct {
int (*get_src_port_id)(struct airoha_gdm_port *port, int nbq);
+ u32 (*get_vip_port)(struct airoha_gdm_port *port, int nbq);
} ops;
};
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index c057bb2d105d..03115c1c1063 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -125,13 +125,13 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
airoha_fe_rmw(eth, REG_PPE_BND_AGE0(i),
PPE_BIND_AGE0_DELTA_NON_L4 |
PPE_BIND_AGE0_DELTA_UDP,
- FIELD_PREP(PPE_BIND_AGE0_DELTA_NON_L4, 1) |
- FIELD_PREP(PPE_BIND_AGE0_DELTA_UDP, 12));
+ FIELD_PREP(PPE_BIND_AGE0_DELTA_NON_L4, 60) |
+ FIELD_PREP(PPE_BIND_AGE0_DELTA_UDP, 60));
airoha_fe_rmw(eth, REG_PPE_BND_AGE1(i),
PPE_BIND_AGE1_DELTA_TCP_FIN |
PPE_BIND_AGE1_DELTA_TCP,
FIELD_PREP(PPE_BIND_AGE1_DELTA_TCP_FIN, 1) |
- FIELD_PREP(PPE_BIND_AGE1_DELTA_TCP, 7));
+ FIELD_PREP(PPE_BIND_AGE1_DELTA_TCP, 60));
airoha_fe_rmw(eth, REG_PPE_TB_HASH_CFG(i),
PPE_SRAM_TABLE_EN_MASK |
@@ -159,7 +159,15 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK,
dram_num_entries));
+ airoha_fe_rmw(eth, REG_PPE_BIND_RATE(i),
+ PPE_BIND_RATE_L2B_BIND_MASK |
+ PPE_BIND_RATE_BIND_MASK,
+ FIELD_PREP(PPE_BIND_RATE_L2B_BIND_MASK, 0x1e) |
+ FIELD_PREP(PPE_BIND_RATE_BIND_MASK, 0x1e));
+
airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED);
+ airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i),
+ PPE_FLOW_CFG_IP6_6RD_MASK);
for (p = 0; p < ARRAY_SIZE(eth->ports); p++) {
struct airoha_gdm_port *port = eth->ports[p];
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
index b942076762ef..67e93e17d4d9 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
@@ -194,6 +194,7 @@ void bnge_rdma_aux_device_add(struct bnge_dev *bd)
dev_warn(bd->dev, "Failed to add auxiliary device for ROCE\n");
auxiliary_device_uninit(aux_dev);
bd->flags &= ~BNGE_EN_ROCE;
+ return;
}
bd->auxr_dev->net = bd->netdev;
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 482a31e7b72b..54f71b1e85fc 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -1819,15 +1819,15 @@ static struct enet_cb *bcmgenet_put_txcb(struct bcmgenet_priv *priv,
{
struct enet_cb *tx_cb_ptr;
- tx_cb_ptr = ring->cbs;
- tx_cb_ptr += ring->write_ptr - ring->cb_ptr;
-
/* Rewinding local write pointer */
if (ring->write_ptr == ring->cb_ptr)
ring->write_ptr = ring->end_ptr;
else
ring->write_ptr--;
+ tx_cb_ptr = ring->cbs;
+ tx_cb_ptr += ring->write_ptr - ring->cb_ptr;
+
return tx_cb_ptr;
}
@@ -1985,6 +1985,7 @@ static unsigned int bcmgenet_tx_reclaim(struct net_device *dev,
drop = (ring->prod_index - ring->c_index) & DMA_C_INDEX_MASK;
released += drop;
ring->prod_index = ring->c_index & DMA_C_INDEX_MASK;
+ ring->free_bds += drop;
while (drop--) {
cb_ptr = bcmgenet_put_txcb(priv, ring);
skb = cb_ptr->skb;
@@ -1996,6 +1997,7 @@ static unsigned int bcmgenet_tx_reclaim(struct net_device *dev,
}
if (skb)
dev_consume_skb_any(skb);
+ netdev_tx_reset_queue(netdev_get_tx_queue(dev, ring->index));
bcmgenet_tdma_ring_writel(priv, ring->index,
ring->prod_index, TDMA_PROD_INDEX);
wr_ptr = ring->write_ptr * WORDS_PER_BD(priv);
@@ -3475,27 +3477,23 @@ static void bcmgenet_dump_tx_queue(struct bcmgenet_tx_ring *ring)
static void bcmgenet_timeout(struct net_device *dev, unsigned int txqueue)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
- u32 int1_enable = 0;
- unsigned int q;
+ struct bcmgenet_tx_ring *ring = &priv->tx_rings[txqueue];
+ struct netdev_queue *txq = netdev_get_tx_queue(dev, txqueue);
netif_dbg(priv, tx_err, dev, "bcmgenet_timeout\n");
- for (q = 0; q <= priv->hw_params->tx_queues; q++)
- bcmgenet_dump_tx_queue(&priv->tx_rings[q]);
-
- bcmgenet_tx_reclaim_all(dev);
+ bcmgenet_dump_tx_queue(ring);
- for (q = 0; q <= priv->hw_params->tx_queues; q++)
- int1_enable |= (1 << q);
+ bcmgenet_tx_reclaim(dev, ring, true);
- /* Re-enable TX interrupts if disabled */
- bcmgenet_intrl2_1_writel(priv, int1_enable, INTRL2_CPU_MASK_CLEAR);
+ /* Re-enable the TX interrupt for this ring */
+ bcmgenet_intrl2_1_writel(priv, 1 << txqueue, INTRL2_CPU_MASK_CLEAR);
- netif_trans_update(dev);
+ txq_trans_cond_update(txq);
- BCMGENET_STATS64_INC((&priv->tx_rings[txqueue].stats64), errors);
+ BCMGENET_STATS64_INC((&ring->stats64), errors);
- netif_tx_wake_all_queues(dev);
+ netif_tx_wake_queue(txq);
}
#define MAX_MDF_FILTER 17
diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
index b579d5b545c4..d98247408242 100644
--- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
+++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
@@ -352,6 +352,11 @@ static int octep_vf_oq_check_hw_for_pkts(struct octep_vf_device *oct,
return new_pkts;
}
+static inline u32 octep_vf_oq_next_idx(struct octep_vf_oq *oq, u32 idx)
+{
+ return (idx + 1 == oq->max_count) ? 0 : idx + 1;
+}
+
/**
* __octep_vf_oq_process_rx() - Process hardware Rx queue and push to stack.
*
@@ -409,30 +414,52 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
data_offset = OCTEP_VF_OQ_RESP_HW_SIZE;
rx_ol_flags = 0;
}
- rx_bytes += buff_info->len;
-
if (buff_info->len <= oq->max_single_buffer_size) {
skb = napi_build_skb((void *)resp_hw, PAGE_SIZE);
+ if (!skb) {
+ oq->stats->alloc_failures++;
+ desc_used++;
+ read_idx = octep_vf_oq_next_idx(oq, read_idx);
+ continue;
+ }
+ rx_bytes += buff_info->len;
skb_reserve(skb, data_offset);
skb_put(skb, buff_info->len);
- read_idx++;
desc_used++;
- if (read_idx == oq->max_count)
- read_idx = 0;
+ read_idx = octep_vf_oq_next_idx(oq, read_idx);
} else {
struct skb_shared_info *shinfo;
u16 data_len;
skb = napi_build_skb((void *)resp_hw, PAGE_SIZE);
+ if (!skb) {
+ oq->stats->alloc_failures++;
+ desc_used++;
+ read_idx = octep_vf_oq_next_idx(oq, read_idx);
+ data_len = buff_info->len - oq->max_single_buffer_size;
+ while (data_len) {
+ dma_unmap_page(oq->dev, oq->desc_ring[read_idx].buffer_ptr,
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ buff_info = (struct octep_vf_rx_buffer *)
+ &oq->buff_info[read_idx];
+ buff_info->page = NULL;
+ if (data_len < oq->buffer_size)
+ data_len = 0;
+ else
+ data_len -= oq->buffer_size;
+ desc_used++;
+ read_idx = octep_vf_oq_next_idx(oq, read_idx);
+ }
+ continue;
+ }
+ rx_bytes += buff_info->len;
skb_reserve(skb, data_offset);
/* Head fragment includes response header(s);
* subsequent fragments contains only data.
*/
skb_put(skb, oq->max_single_buffer_size);
- read_idx++;
desc_used++;
- if (read_idx == oq->max_count)
- read_idx = 0;
+ read_idx = octep_vf_oq_next_idx(oq, read_idx);
shinfo = skb_shinfo(skb);
data_len = buff_info->len - oq->max_single_buffer_size;
@@ -454,10 +481,8 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
buff_info->len,
buff_info->len);
buff_info->page = NULL;
- read_idx++;
desc_used++;
- if (read_idx == oq->max_count)
- read_idx = 0;
+ read_idx = octep_vf_oq_next_idx(oq, read_idx);
}
}
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index bb8ced22ca3b..8d225bc9f063 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -3588,12 +3588,23 @@ found:
return NOTIFY_DONE;
}
+static int mtk_max_gmac_mtu(struct mtk_eth *eth)
+{
+ int i, max_mtu = ETH_DATA_LEN;
+
+ for (i = 0; i < ARRAY_SIZE(eth->netdev); i++)
+ if (eth->netdev[i] && eth->netdev[i]->mtu > max_mtu)
+ max_mtu = eth->netdev[i]->mtu;
+
+ return max_mtu;
+}
+
static int mtk_open(struct net_device *dev)
{
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
struct mtk_mac *target_mac;
- int i, err, ppe_num;
+ int i, err, ppe_num, mtu;
ppe_num = eth->soc->ppe_num;
@@ -3640,6 +3651,10 @@ static int mtk_open(struct net_device *dev)
mtk_gdm_config(eth, target_mac->id, gdm_config);
}
+ mtu = mtk_max_gmac_mtu(eth);
+ for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
+ mtk_ppe_update_mtu(eth->ppe[i], mtu);
+
napi_enable(&eth->tx_napi);
napi_enable(&eth->rx_napi);
mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
@@ -4333,6 +4348,7 @@ static int mtk_change_mtu(struct net_device *dev, int new_mtu)
int length = new_mtu + MTK_RX_ETH_HLEN;
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
+ int max_mtu, i;
if (rcu_access_pointer(eth->prog) &&
length > MTK_PP_MAX_BUF_SIZE) {
@@ -4343,6 +4359,10 @@ static int mtk_change_mtu(struct net_device *dev, int new_mtu)
mtk_set_mcr_max_rx(mac, length);
WRITE_ONCE(dev->mtu, new_mtu);
+ max_mtu = mtk_max_gmac_mtu(eth);
+ for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
+ mtk_ppe_update_mtu(eth->ppe[i], max_mtu);
+
return 0;
}
diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
index 75f7728fc796..18279e2a7022 100644
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -973,6 +973,36 @@ static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
}
}
+void mtk_ppe_update_mtu(struct mtk_ppe *ppe, int mtu)
+{
+ int base;
+ u32 val;
+
+ if (!ppe)
+ return;
+
+ /* The PPE checks output frame size against per-tag-layer MTU limits,
+ * treating PPPoE and DSA tags just like 802.1Q VLAN tags. The Linux
+ * device MTU already accounts for PPPoE (PPPOE_SES_HLEN) and DSA tag
+ * overhead, but 802.1Q VLAN tags are handled transparently without
+ * being reflected by the lower device MTU being increased by 4.
+ * Use the maximum MTU across all GMAC interfaces so that PPE output
+ * frame limits are sufficiently high regardless of which port a flow
+ * egresses through.
+ */
+ base = ETH_HLEN + mtu;
+
+ val = FIELD_PREP(MTK_PPE_VLAN_MTU0_NONE, base) |
+ FIELD_PREP(MTK_PPE_VLAN_MTU0_1TAG, base + VLAN_HLEN);
+ ppe_w32(ppe, MTK_PPE_VLAN_MTU0, val);
+
+ val = FIELD_PREP(MTK_PPE_VLAN_MTU1_2TAG,
+ base + 2 * VLAN_HLEN) |
+ FIELD_PREP(MTK_PPE_VLAN_MTU1_3TAG,
+ base + 3 * VLAN_HLEN);
+ ppe_w32(ppe, MTK_PPE_VLAN_MTU1, val);
+}
+
void mtk_ppe_start(struct mtk_ppe *ppe)
{
u32 val;
diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
index 223f709e2704..ba85e39a155b 100644
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
@@ -346,6 +346,7 @@ struct mtk_ppe {
struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index);
void mtk_ppe_deinit(struct mtk_eth *eth);
+void mtk_ppe_update_mtu(struct mtk_ppe *ppe, int mtu);
void mtk_ppe_start(struct mtk_ppe *ppe);
int mtk_ppe_stop(struct mtk_ppe *ppe);
int mtk_ppe_prepare_reset(struct mtk_ppe *ppe);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c
index 05faad5083d9..145677ce9640 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2017, Mellanox Technologies inc. All rights reserved. */
+#include <linux/iopoll.h>
+
#include "mlx5_core.h"
#include "en.h"
#include "ipsec.h"
@@ -592,7 +594,6 @@ int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry,
struct mlx5_wqe_aso_ctrl_seg *ctrl;
struct mlx5e_hw_objs *res;
struct mlx5_aso_wqe *wqe;
- unsigned long expires;
u8 ds_cnt;
int ret;
@@ -614,13 +615,8 @@ int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry,
mlx5e_ipsec_aso_copy(ctrl, data);
mlx5_aso_post_wqe(aso->aso, false, &wqe->ctrl);
- expires = jiffies + msecs_to_jiffies(10);
- do {
- ret = mlx5_aso_poll_cq(aso->aso, false);
- if (ret)
- /* We are in atomic context */
- udelay(10);
- } while (ret && time_is_after_jiffies(expires));
+ read_poll_timeout_atomic(mlx5_aso_poll_cq, ret, !ret, 10,
+ 10 * USEC_PER_MSEC, false, aso->aso, false);
if (!ret)
memcpy(sa_entry->ctx, aso->ctx, MLX5_ST_SZ_BYTES(ipsec_aso));
spin_unlock_bh(&aso->lock);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 4ba198fb9d6c..6c4eeb88588c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -6868,6 +6868,14 @@ static int _mlx5e_probe(struct auxiliary_device *adev)
goto err_resume;
}
+ /* mlx5e_fix_features() returns early when the device is not present
+ * to avoid dereferencing cleared priv during profile changes.
+ * This also causes it to be a no-op during register_netdev(), where
+ * the device is not yet present.
+ * Trigger an additional features update that will actually work.
+ */
+ mlx5e_update_features(netdev);
+
mlx5e_dcbnl_init_app(priv);
mlx5_core_uplink_netdev_set(mdev, netdev);
mlx5e_params_print_info(mdev, &priv->channels.params);
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c
index e3aebbe3656d..b7c0b7349d00 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c
@@ -139,7 +139,7 @@ void fbnic_up(struct fbnic_net *fbn)
/* Enable Tx/Rx processing */
fbnic_napi_enable(fbn);
- netif_tx_start_all_queues(fbn->netdev);
+ netif_tx_wake_all_queues(fbn->netdev);
fbnic_service_task_start(fbn);
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index 43741cd35af8..098fbda0d128 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -2065,11 +2065,8 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
gc->dev = &pdev->dev;
xa_init(&gc->irq_contexts);
- if (gc->is_pf)
- gc->mana_pci_debugfs = debugfs_create_dir("0", mana_debugfs_root);
- else
- gc->mana_pci_debugfs = debugfs_create_dir(pci_slot_name(pdev->slot),
- mana_debugfs_root);
+ gc->mana_pci_debugfs = debugfs_create_dir(pci_name(pdev),
+ mana_debugfs_root);
err = mana_gd_setup(pdev);
if (err)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 07630322545f..6302432b9bf6 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -3154,6 +3154,8 @@ static int mana_init_port(struct net_device *ndev)
eth_hw_addr_set(ndev, apc->mac_addr);
sprintf(vport, "vport%d", port_idx);
apc->mana_port_debugfs = debugfs_create_dir(vport, gc->mana_pci_debugfs);
+ debugfs_create_u32("current_speed", 0400, apc->mana_port_debugfs,
+ &apc->speed);
return 0;
reset_apc:
@@ -3432,8 +3434,6 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
netif_carrier_on(ndev);
- debugfs_create_u32("current_speed", 0400, apc->mana_port_debugfs, &apc->speed);
-
return 0;
free_indir:
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
index 8ea7aa07ae4e..dc9f24314658 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -657,7 +657,9 @@ void txgbe_remove_phy(struct txgbe *txgbe)
return;
case wx_mac_sp:
if (txgbe->wx->media_type == wx_media_copper) {
+ rtnl_lock();
phylink_disconnect_phy(txgbe->wx->phylink);
+ rtnl_unlock();
phylink_destroy(txgbe->wx->phylink);
return;
}
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index 885992951e8a..c8b2dc5c1bec 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -391,7 +391,6 @@ static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp,
const u8 *fp, size_t count)
{
struct sixpack *sp;
- size_t count1;
if (!count)
return;
@@ -401,16 +400,16 @@ static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp,
return;
/* Read the characters out of the buffer */
- count1 = count;
- while (count) {
- count--;
+ while (count--) {
if (fp && *fp++) {
if (!test_and_set_bit(SIXPF_ERROR, &sp->flags))
sp->dev->stats.rx_errors++;
+ cp++;
continue;
}
+ sixpack_decode(sp, cp, 1);
+ cp++;
}
- sixpack_decode(sp, cp, count1);
tty_unthrottle(tty);
}
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index 045c5177262e..214fd1f819a1 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -187,6 +187,9 @@ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_ty
len = skb->data[0] + skb->data[1] * 256 - 5;
+ if (len < 0 || len > skb->len - 2)
+ goto drop_unlock;
+
skb_pull(skb, 2); /* Remove the length bytes */
skb_trim(skb, len); /* Set the length of the data */
diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c
index ae5048efde68..8569db4a7140 100644
--- a/drivers/net/hamradio/scc.c
+++ b/drivers/net/hamradio/scc.c
@@ -1909,6 +1909,8 @@ static int scc_net_siocdevprivate(struct net_device *dev,
if (!capable(CAP_SYS_RAWIO)) return -EPERM;
if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg)))
return -EINVAL;
+ if (memcfg.bufsize < 16)
+ return -EINVAL;
scc->stat.bufsize = memcfg.bufsize;
return 0;
diff --git a/drivers/net/ipa/gsi.c b/drivers/net/ipa/gsi.c
index 4c3227e77898..624649484d62 100644
--- a/drivers/net/ipa/gsi.c
+++ b/drivers/net/ipa/gsi.c
@@ -2044,6 +2044,7 @@ static int gsi_ring_setup(struct gsi *gsi)
count = reg_decode(reg, NUM_EV_PER_EE, val);
} else {
reg = gsi_reg(gsi, HW_PARAM_4);
+ val = ioread32(gsi->virt + reg_offset(reg));
count = reg_decode(reg, EV_PER_EE, val);
}
if (!count) {
diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c
index 8e2b4bf7b14e..788dd99af2a4 100644
--- a/drivers/net/ipa/ipa_main.c
+++ b/drivers/net/ipa/ipa_main.c
@@ -361,7 +361,7 @@ static void ipa_qtime_config(struct ipa *ipa)
{
const struct reg *reg;
u32 offset;
- u32 val;
+ u32 val = 0;
/* Timer clock divider must be disabled when we change the rate */
reg = ipa_reg(ipa, TIMERS_XO_CLK_DIV_CFG);
@@ -374,8 +374,8 @@ static void ipa_qtime_config(struct ipa *ipa)
val |= reg_bit(reg, DPL_TIMESTAMP_SEL);
}
/* Configure tag and NAT Qtime timestamp resolution as well */
- val = reg_encode(reg, TAG_TIMESTAMP_LSB, TAG_TIMESTAMP_SHIFT);
- val = reg_encode(reg, NAT_TIMESTAMP_LSB, NAT_TIMESTAMP_SHIFT);
+ val |= reg_encode(reg, TAG_TIMESTAMP_LSB, TAG_TIMESTAMP_SHIFT);
+ val |= reg_encode(reg, NAT_TIMESTAMP_LSB, NAT_TIMESTAMP_SHIFT);
iowrite32(val, ipa->reg_virt + reg_offset(reg));
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index f6cad0746a02..6147ee8b1d78 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -2584,7 +2584,9 @@ static void macsec_inherit_tso_max(struct net_device *dev)
netif_inherit_tso_max(dev, macsec->real_dev);
}
-static int macsec_update_offload(struct net_device *dev, enum macsec_offload offload)
+static int macsec_update_offload(struct net_device *dev,
+ enum macsec_offload offload,
+ struct netlink_ext_ack *extack)
{
enum macsec_offload prev_offload;
const struct macsec_ops *ops;
@@ -2616,14 +2618,35 @@ static int macsec_update_offload(struct net_device *dev, enum macsec_offload off
if (!ops)
return -EOPNOTSUPP;
- macsec->offload = offload;
-
ctx.secy = &macsec->secy;
ret = offload == MACSEC_OFFLOAD_OFF ? macsec_offload(ops->mdo_del_secy, &ctx)
: macsec_offload(ops->mdo_add_secy, &ctx);
- if (ret) {
- macsec->offload = prev_offload;
+ if (ret)
return ret;
+
+ /* Remove VLAN filters when disabling offload. */
+ if (offload == MACSEC_OFFLOAD_OFF) {
+ vlan_drop_rx_ctag_filter_info(dev);
+ vlan_drop_rx_stag_filter_info(dev);
+ }
+ macsec->offload = offload;
+ /* Add VLAN filters when enabling offload. */
+ if (prev_offload == MACSEC_OFFLOAD_OFF) {
+ ret = vlan_get_rx_ctag_filter_info(dev);
+ if (ret) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "adding ctag VLAN filters failed, err %d",
+ ret);
+ goto rollback_offload;
+ }
+ ret = vlan_get_rx_stag_filter_info(dev);
+ if (ret) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "adding stag VLAN filters failed, err %d",
+ ret);
+ vlan_drop_rx_ctag_filter_info(dev);
+ goto rollback_offload;
+ }
}
macsec_set_head_tail_room(dev);
@@ -2633,6 +2656,12 @@ static int macsec_update_offload(struct net_device *dev, enum macsec_offload off
netdev_update_features(dev);
+ return 0;
+
+rollback_offload:
+ macsec->offload = prev_offload;
+ macsec_offload(ops->mdo_del_secy, &ctx);
+
return ret;
}
@@ -2673,7 +2702,7 @@ static int macsec_upd_offload(struct sk_buff *skb, struct genl_info *info)
offload = nla_get_u8(tb_offload[MACSEC_OFFLOAD_ATTR_TYPE]);
if (macsec->offload != offload)
- ret = macsec_update_offload(dev, offload);
+ ret = macsec_update_offload(dev, offload, info->extack);
out:
rtnl_unlock();
return ret;
@@ -3486,7 +3515,8 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb,
}
#define MACSEC_FEATURES \
- (NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST)
+ (NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
+ NETIF_F_HW_VLAN_STAG_FILTER | NETIF_F_HW_VLAN_CTAG_FILTER)
#define MACSEC_OFFLOAD_FEATURES \
(MACSEC_FEATURES | NETIF_F_GSO_SOFTWARE | NETIF_F_SOFT_FEATURES | \
@@ -3707,6 +3737,29 @@ restore_old_addr:
return err;
}
+static int macsec_vlan_rx_add_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct macsec_dev *macsec = netdev_priv(dev);
+
+ if (!macsec_is_offloaded(macsec))
+ return 0;
+
+ return vlan_vid_add(macsec->real_dev, proto, vid);
+}
+
+static int macsec_vlan_rx_kill_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct macsec_dev *macsec = netdev_priv(dev);
+
+ if (!macsec_is_offloaded(macsec))
+ return 0;
+
+ vlan_vid_del(macsec->real_dev, proto, vid);
+ return 0;
+}
+
static int macsec_change_mtu(struct net_device *dev, int new_mtu)
{
struct macsec_dev *macsec = macsec_priv(dev);
@@ -3748,6 +3801,8 @@ static const struct net_device_ops macsec_netdev_ops = {
.ndo_set_rx_mode = macsec_dev_set_rx_mode,
.ndo_change_rx_flags = macsec_dev_change_rx_flags,
.ndo_set_mac_address = macsec_set_mac_address,
+ .ndo_vlan_rx_add_vid = macsec_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = macsec_vlan_rx_kill_vid,
.ndo_start_xmit = macsec_start_xmit,
.ndo_get_stats64 = macsec_get_stats64,
.ndo_get_iflink = macsec_get_iflink,
@@ -3912,7 +3967,7 @@ static int macsec_changelink(struct net_device *dev, struct nlattr *tb[],
offload = nla_get_u8(data[IFLA_MACSEC_OFFLOAD]);
if (macsec->offload != offload) {
macsec_offload_state_change = true;
- ret = macsec_update_offload(dev, offload);
+ ret = macsec_update_offload(dev, offload, extack);
if (ret)
goto cleanup;
}
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index c71b8d116f18..e1541ca76715 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -554,6 +554,36 @@ static int nsim_stop(struct net_device *dev)
return 0;
}
+static int nsim_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+ struct netdevsim *ns = netdev_priv(dev);
+
+ if (vid >= VLAN_N_VID)
+ return -EINVAL;
+
+ if (proto == htons(ETH_P_8021Q))
+ WARN_ON_ONCE(test_and_set_bit(vid, ns->vlan.ctag));
+ else if (proto == htons(ETH_P_8021AD))
+ WARN_ON_ONCE(test_and_set_bit(vid, ns->vlan.stag));
+
+ return 0;
+}
+
+static int nsim_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+ struct netdevsim *ns = netdev_priv(dev);
+
+ if (vid >= VLAN_N_VID)
+ return -EINVAL;
+
+ if (proto == htons(ETH_P_8021Q))
+ WARN_ON_ONCE(!test_and_clear_bit(vid, ns->vlan.ctag));
+ else if (proto == htons(ETH_P_8021AD))
+ WARN_ON_ONCE(!test_and_clear_bit(vid, ns->vlan.stag));
+
+ return 0;
+}
+
static int nsim_shaper_set(struct net_shaper_binding *binding,
const struct net_shaper *shaper,
struct netlink_ext_ack *extack)
@@ -611,6 +641,8 @@ static const struct net_device_ops nsim_netdev_ops = {
.ndo_bpf = nsim_bpf,
.ndo_open = nsim_open,
.ndo_stop = nsim_stop,
+ .ndo_vlan_rx_add_vid = nsim_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = nsim_vlan_rx_kill_vid,
.net_shaper_ops = &nsim_shaper_ops,
};
@@ -622,6 +654,8 @@ static const struct net_device_ops nsim_vf_netdev_ops = {
.ndo_change_mtu = nsim_change_mtu,
.ndo_setup_tc = nsim_setup_tc,
.ndo_set_features = nsim_set_features,
+ .ndo_vlan_rx_add_vid = nsim_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = nsim_vlan_rx_kill_vid,
};
/* We don't have true per-queue stats, yet, so do some random fakery here.
@@ -919,6 +953,20 @@ static const struct file_operations nsim_pp_hold_fops = {
.owner = THIS_MODULE,
};
+static int nsim_vlan_show(struct seq_file *s, void *data)
+{
+ struct netdevsim *ns = s->private;
+ int vid;
+
+ for_each_set_bit(vid, ns->vlan.ctag, VLAN_N_VID)
+ seq_printf(s, "ctag %d\n", vid);
+ for_each_set_bit(vid, ns->vlan.stag, VLAN_N_VID)
+ seq_printf(s, "stag %d\n", vid);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(nsim_vlan);
+
static void nsim_setup(struct net_device *dev)
{
ether_setup(dev);
@@ -931,14 +979,18 @@ static void nsim_setup(struct net_device *dev)
NETIF_F_FRAGLIST |
NETIF_F_HW_CSUM |
NETIF_F_LRO |
- NETIF_F_TSO;
+ NETIF_F_TSO |
+ NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_STAG_FILTER;
dev->hw_features |= NETIF_F_HW_TC |
NETIF_F_SG |
NETIF_F_FRAGLIST |
NETIF_F_HW_CSUM |
NETIF_F_LRO |
NETIF_F_TSO |
- NETIF_F_LOOPBACK;
+ NETIF_F_LOOPBACK |
+ NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_STAG_FILTER;
dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
dev->max_mtu = ETH_MAX_MTU;
dev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_HW_OFFLOAD;
@@ -1105,6 +1157,8 @@ struct netdevsim *nsim_create(struct nsim_dev *nsim_dev,
ns->qr_dfs = debugfs_create_file("queue_reset", 0200,
nsim_dev_port->ddir, ns,
&nsim_qreset_fops);
+ ns->vlan_dfs = debugfs_create_file("vlan", 0400, nsim_dev_port->ddir,
+ ns, &nsim_vlan_fops);
return ns;
err_free_netdev:
@@ -1116,7 +1170,9 @@ void nsim_destroy(struct netdevsim *ns)
{
struct net_device *dev = ns->netdev;
struct netdevsim *peer;
+ u16 vid;
+ debugfs_remove(ns->vlan_dfs);
debugfs_remove(ns->qr_dfs);
debugfs_remove(ns->pp_dfs);
@@ -1142,6 +1198,11 @@ void nsim_destroy(struct netdevsim *ns)
if (nsim_dev_port_is_pf(ns->nsim_dev_port))
nsim_exit_netdevsim(ns);
+ for_each_set_bit(vid, ns->vlan.ctag, VLAN_N_VID)
+ WARN_ON_ONCE(1);
+ for_each_set_bit(vid, ns->vlan.stag, VLAN_N_VID)
+ WARN_ON_ONCE(1);
+
/* Put this intentionally late to exercise the orphaning path */
if (ns->page) {
page_pool_put_full_page(pp_page_to_nmdesc(ns->page)->pp,
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index c7de53706ec4..7e129dddbbe7 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -18,6 +18,7 @@
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
#include <linux/kernel.h>
+#include <linux/if_vlan.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/ptp_mock.h>
@@ -75,6 +76,11 @@ struct nsim_macsec {
u8 nsim_secy_count;
};
+struct nsim_vlan {
+ DECLARE_BITMAP(ctag, VLAN_N_VID);
+ DECLARE_BITMAP(stag, VLAN_N_VID);
+};
+
struct nsim_ethtool_pauseparam {
bool rx;
bool tx;
@@ -135,6 +141,7 @@ struct netdevsim {
bool bpf_map_accept;
struct nsim_ipsec ipsec;
struct nsim_macsec macsec;
+ struct nsim_vlan vlan;
struct {
u32 inject_error;
u32 __ports[2][NSIM_UDP_TUNNEL_N_PORTS];
@@ -146,6 +153,7 @@ struct netdevsim {
struct page *page;
struct dentry *pp_dfs;
struct dentry *qr_dfs;
+ struct dentry *vlan_dfs;
struct nsim_ethtool ethtool;
struct netdevsim __rcu *peer;
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index cda4abf4e68c..c2cdf1ae3542 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -927,8 +927,8 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr,
/* returning -ENODEV doesn't stop bus
* scanning
*/
- return (phy_reg == -EIO ||
- phy_reg == -ENODEV) ? -ENODEV : -EIO;
+ return (ret == -EIO ||
+ ret == -ENODEV) ? -ENODEV : -EIO;
if (!ret)
continue;
diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c
index 2995b08bac96..63726cf98cd4 100644
--- a/drivers/net/phy/qcom/at803x.c
+++ b/drivers/net/phy/qcom/at803x.c
@@ -524,7 +524,7 @@ static int at803x_config_init(struct phy_device *phydev)
* behaviour but we still need to accommodate it. XNP is only needed
* for 10Gbps support, so disable XNP.
*/
- return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0);
+ return phy_modify(phydev, MII_ADVERTISE, ADVERTISE_XNP, 0);
}
static void at803x_link_change_notify(struct phy_device *phydev)
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index b097d1b38ac9..b0d3bc49c685 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -1048,6 +1048,9 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
struct ppp_net *pn;
int __user *p = (int __user *)arg;
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+ return -EPERM;
+
switch (cmd) {
case PPPIOCNEWUNIT:
/* Create a new ppp unit */
diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c
index ad5121e9cf5d..165650ecef64 100644
--- a/drivers/net/usb/cdc-phonet.c
+++ b/drivers/net/usb/cdc-phonet.c
@@ -157,11 +157,16 @@ static void rx_complete(struct urb *req)
PAGE_SIZE);
page = NULL;
}
- } else {
+ } else if (skb_shinfo(skb)->nr_frags < MAX_SKB_FRAGS) {
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
page, 0, req->actual_length,
PAGE_SIZE);
page = NULL;
+ } else {
+ dev_kfree_skb_any(skb);
+ pnd->rx_skb = NULL;
+ skb = NULL;
+ dev->stats.rx_length_errors++;
}
if (req->actual_length < PAGE_SIZE)
pnd->rx_skb = NULL; /* Last fragment */
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index 5af262ec4bbd..11159a50d6a1 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -1187,12 +1187,22 @@ static inline void tcf_kfree_skb_list(struct sk_buff *skb, struct Qdisc *q,
static inline void qdisc_dequeue_drop(struct Qdisc *q, struct sk_buff *skb,
enum qdisc_drop_reason reason)
{
+ struct Qdisc *root;
+
DEBUG_NET_WARN_ON_ONCE(!(q->flags & TCQ_F_DEQUEUE_DROPS));
DEBUG_NET_WARN_ON_ONCE(q->flags & TCQ_F_NOLOCK);
- tcf_set_qdisc_drop_reason(skb, reason);
- skb->next = q->to_free;
- q->to_free = skb;
+ rcu_read_lock();
+ root = qdisc_root_sleeping(q);
+
+ if (root->flags & TCQ_F_DEQUEUE_DROPS) {
+ tcf_set_qdisc_drop_reason(skb, reason);
+ skb->next = root->to_free;
+ root->to_free = skb;
+ } else {
+ kfree_skb_reason(skb, (enum skb_drop_reason)reason);
+ }
+ rcu_read_unlock();
}
/* Instead of calling kfree_skb() while root qdisc lock is held,
diff --git a/include/uapi/linux/mii.h b/include/uapi/linux/mii.h
index 39f7c44baf53..61d6edad4b94 100644
--- a/include/uapi/linux/mii.h
+++ b/include/uapi/linux/mii.h
@@ -82,7 +82,8 @@
#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */
#define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */
#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */
-#define ADVERTISE_RESV 0x1000 /* Unused... */
+#define ADVERTISE_XNP 0x1000 /* Extended Next Page */
+#define ADVERTISE_RESV ADVERTISE_XNP /* Used to be reserved */
#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */
#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */
#define ADVERTISE_NPAGE 0x8000 /* Next page bit */
diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c
index 171fa32ada85..d687fd0b4ed3 100644
--- a/net/caif/cfsrvl.c
+++ b/net/caif/cfsrvl.c
@@ -191,10 +191,20 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid)
void caif_free_client(struct cflayer *adap_layer)
{
+ struct cflayer *serv_layer;
struct cfsrvl *servl;
- if (adap_layer == NULL || adap_layer->dn == NULL)
+
+ if (!adap_layer)
+ return;
+
+ serv_layer = adap_layer->dn;
+ if (!serv_layer)
return;
- servl = container_obj(adap_layer->dn);
+
+ layer_set_dn(adap_layer, NULL);
+ layer_set_up(serv_layer, NULL);
+
+ servl = container_obj(serv_layer);
servl->release(&servl->layer);
}
EXPORT_SYMBOL(caif_free_client);
diff --git a/net/can/raw.c b/net/can/raw.c
index a0c0f5e946d8..a26942e78e68 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -362,6 +362,14 @@ static int raw_notifier(struct notifier_block *nb, unsigned long msg,
return NOTIFY_DONE;
}
+static void raw_sock_destruct(struct sock *sk)
+{
+ struct raw_sock *ro = raw_sk(sk);
+
+ free_percpu(ro->uniq);
+ can_sock_destruct(sk);
+}
+
static int raw_init(struct sock *sk)
{
struct raw_sock *ro = raw_sk(sk);
@@ -388,6 +396,8 @@ static int raw_init(struct sock *sk)
if (unlikely(!ro->uniq))
return -ENOMEM;
+ sk->sk_destruct = raw_sock_destruct;
+
/* set notifier */
spin_lock(&raw_notifier_lock);
list_add_tail(&ro->notifier, &raw_notifier_list);
@@ -437,7 +447,6 @@ static int raw_release(struct socket *sock)
ro->bound = 0;
ro->dev = NULL;
ro->count = 0;
- free_percpu(ro->uniq);
sock_orphan(sk);
sock->sk = NULL;
diff --git a/net/core/filter.c b/net/core/filter.c
index bf9c37b27646..fcfcb72663ca 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -4394,6 +4394,8 @@ u32 xdp_master_redirect(struct xdp_buff *xdp)
struct net_device *master, *slave;
master = netdev_master_upper_dev_get_rcu(xdp->rxq->dev);
+ if (unlikely(!(master->flags & IFF_UP)))
+ return XDP_ABORTED;
slave = master->netdev_ops->ndo_xdp_get_xmit_slave(master, xdp);
if (slave && slave != xdp->rxq->dev) {
/* The target device is different from the receiving device, so
@@ -10570,10 +10572,11 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
si->dst_reg, si->dst_reg, \
offsetof(OBJ, OBJ_FIELD)); \
if (si->dst_reg == si->src_reg) { \
- *insn++ = BPF_JMP_A(1); \
+ *insn++ = BPF_JMP_A(2); \
*insn++ = BPF_LDX_MEM(BPF_DW, reg, si->src_reg, \
offsetof(struct bpf_sock_ops_kern, \
temp)); \
+ *insn++ = BPF_MOV64_IMM(si->dst_reg, 0); \
} \
} while (0)
@@ -10607,10 +10610,11 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
si->dst_reg, si->src_reg, \
offsetof(struct bpf_sock_ops_kern, sk));\
if (si->dst_reg == si->src_reg) { \
- *insn++ = BPF_JMP_A(1); \
+ *insn++ = BPF_JMP_A(2); \
*insn++ = BPF_LDX_MEM(BPF_DW, reg, si->src_reg, \
offsetof(struct bpf_sock_ops_kern, \
temp)); \
+ *insn++ = BPF_MOV64_IMM(si->dst_reg, 0); \
} \
} while (0)
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index f1474598d2c8..df479277fb80 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -284,7 +284,6 @@ static int cookie_tcp_reqsk_init(struct sock *sk, struct sk_buff *skb,
treq->rcv_isn = ntohl(th->seq) - 1;
treq->snt_isn = ntohl(th->ack_seq) - 1;
treq->syn_tos = TCP_SKB_CB(skb)->ip_dsfield;
- treq->req_usec_ts = false;
#if IS_ENABLED(CONFIG_MPTCP)
treq->is_mptcp = sk_is_mptcp(sk);
@@ -346,6 +345,7 @@ struct request_sock *cookie_tcp_reqsk_alloc(const struct request_sock_ops *ops,
ireq->wscale_ok = tcp_opt->wscale_ok;
ireq->ecn_ok = !!(tcp_opt->rcv_tsecr & TS_OPT_ECN);
+ treq->req_usec_ts = false;
treq->ts_off = tsoff;
return req;
diff --git a/net/nfc/digital_technology.c b/net/nfc/digital_technology.c
index 63f1b721c71d..ae63c5eb06fa 100644
--- a/net/nfc/digital_technology.c
+++ b/net/nfc/digital_technology.c
@@ -424,6 +424,12 @@ static void digital_in_recv_sdd_res(struct nfc_digital_dev *ddev, void *arg,
size = 4;
}
+ if (target->nfcid1_len + size > NFC_NFCID1_MAXSIZE) {
+ PROTOCOL_ERR("4.7.2.1");
+ rc = -EPROTO;
+ goto exit;
+ }
+
memcpy(target->nfcid1 + target->nfcid1_len, sdd_res->nfcid1 + offset,
size);
target->nfcid1_len += size;
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index 366d7566308c..db5bc6a878dd 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -1091,6 +1091,7 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local,
if (sk->sk_state == LLCP_CLOSED) {
release_sock(sk);
nfc_llcp_sock_put(llcp_sock);
+ return;
}
/* Pass the payload upstream */
@@ -1182,6 +1183,7 @@ static void nfc_llcp_recv_disc(struct nfc_llcp_local *local,
if (sk->sk_state == LLCP_CLOSED) {
release_sock(sk);
nfc_llcp_sock_put(llcp_sock);
+ return;
}
if (sk->sk_state == LLCP_CONNECTED) {
diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
index 3203b2220860..b3f9bbcf9ab9 100644
--- a/net/qrtr/ns.c
+++ b/net/qrtr/ns.c
@@ -22,8 +22,10 @@ static struct {
struct socket *sock;
struct sockaddr_qrtr bcast_sq;
struct list_head lookups;
+ u32 lookup_count;
struct workqueue_struct *workqueue;
struct work_struct work;
+ void (*saved_data_ready)(struct sock *sk);
int local_node;
} qrtr_ns;
@@ -67,8 +69,19 @@ struct qrtr_server {
struct qrtr_node {
unsigned int id;
struct xarray servers;
+ u32 server_count;
};
+/* Max nodes, server, lookup limits are chosen based on the current platform
+ * requirements. If the requirement changes in the future, these values can be
+ * increased.
+ */
+#define QRTR_NS_MAX_NODES 64
+#define QRTR_NS_MAX_SERVERS 256
+#define QRTR_NS_MAX_LOOKUPS 64
+
+static u8 node_count;
+
static struct qrtr_node *node_get(unsigned int node_id)
{
struct qrtr_node *node;
@@ -77,6 +90,11 @@ static struct qrtr_node *node_get(unsigned int node_id)
if (node)
return node;
+ if (node_count >= QRTR_NS_MAX_NODES) {
+ pr_err_ratelimited("QRTR clients exceed max node limit!\n");
+ return NULL;
+ }
+
/* If node didn't exist, allocate and insert it to the tree */
node = kzalloc_obj(*node);
if (!node)
@@ -90,6 +108,8 @@ static struct qrtr_node *node_get(unsigned int node_id)
return NULL;
}
+ node_count++;
+
return node;
}
@@ -229,6 +249,17 @@ static struct qrtr_server *server_add(unsigned int service,
if (!service || !port)
return NULL;
+ node = node_get(node_id);
+ if (!node)
+ return NULL;
+
+ /* Make sure the new servers per port are capped at the maximum value */
+ old = xa_load(&node->servers, port);
+ if (!old && node->server_count >= QRTR_NS_MAX_SERVERS) {
+ pr_err_ratelimited("QRTR client node %u exceeds max server limit!\n", node_id);
+ return NULL;
+ }
+
srv = kzalloc_obj(*srv);
if (!srv)
return NULL;
@@ -238,10 +269,6 @@ static struct qrtr_server *server_add(unsigned int service,
srv->node = node_id;
srv->port = port;
- node = node_get(node_id);
- if (!node)
- goto err;
-
/* Delete the old server on the same port */
old = xa_store(&node->servers, port, srv, GFP_KERNEL);
if (old) {
@@ -252,6 +279,8 @@ static struct qrtr_server *server_add(unsigned int service,
} else {
kfree(old);
}
+ } else {
+ node->server_count++;
}
trace_qrtr_ns_server_add(srv->service, srv->instance,
@@ -292,6 +321,7 @@ static int server_del(struct qrtr_node *node, unsigned int port, bool bcast)
}
kfree(srv);
+ node->server_count--;
return 0;
}
@@ -341,7 +371,7 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from)
struct qrtr_node *node;
unsigned long index;
struct kvec iv;
- int ret;
+ int ret = 0;
iv.iov_base = &pkt;
iv.iov_len = sizeof(pkt);
@@ -356,8 +386,10 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from)
/* Advertise the removal of this client to all local servers */
local_node = node_get(qrtr_ns.local_node);
- if (!local_node)
- return 0;
+ if (!local_node) {
+ ret = 0;
+ goto delete_node;
+ }
memset(&pkt, 0, sizeof(pkt));
pkt.cmd = cpu_to_le32(QRTR_TYPE_BYE);
@@ -374,10 +406,19 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from)
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
if (ret < 0 && ret != -ENODEV) {
pr_err("failed to send bye cmd\n");
- return ret;
+ goto delete_node;
}
}
- return 0;
+
+ /* Ignore -ENODEV */
+ ret = 0;
+
+delete_node:
+ xa_erase(&nodes, from->sq_node);
+ kfree(node);
+ node_count--;
+
+ return ret;
}
static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
@@ -417,6 +458,7 @@ static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
list_del(&lookup->li);
kfree(lookup);
+ qrtr_ns.lookup_count--;
}
/* Remove the server belonging to this port but don't broadcast
@@ -534,6 +576,11 @@ static int ctrl_cmd_new_lookup(struct sockaddr_qrtr *from,
if (from->sq_node != qrtr_ns.local_node)
return -EINVAL;
+ if (qrtr_ns.lookup_count >= QRTR_NS_MAX_LOOKUPS) {
+ pr_err_ratelimited("QRTR client node exceeds max lookup limit!\n");
+ return -ENOSPC;
+ }
+
lookup = kzalloc_obj(*lookup);
if (!lookup)
return -ENOMEM;
@@ -542,6 +589,7 @@ static int ctrl_cmd_new_lookup(struct sockaddr_qrtr *from,
lookup->service = service;
lookup->instance = instance;
list_add_tail(&lookup->li, &qrtr_ns.lookups);
+ qrtr_ns.lookup_count++;
memset(&filter, 0, sizeof(filter));
filter.service = service;
@@ -582,6 +630,7 @@ static void ctrl_cmd_del_lookup(struct sockaddr_qrtr *from,
list_del(&lookup->li);
kfree(lookup);
+ qrtr_ns.lookup_count--;
}
}
@@ -670,7 +719,7 @@ static void qrtr_ns_worker(struct work_struct *work)
}
if (ret < 0)
- pr_err("failed while handling packet from %d:%d",
+ pr_err_ratelimited("failed while handling packet from %d:%d",
sq.sq_node, sq.sq_port);
}
@@ -709,6 +758,7 @@ int qrtr_ns_init(void)
goto err_sock;
}
+ qrtr_ns.saved_data_ready = qrtr_ns.sock->sk->sk_data_ready;
qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
sq.sq_port = QRTR_PORT_CTRL;
@@ -749,6 +799,10 @@ int qrtr_ns_init(void)
return 0;
err_wq:
+ write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
+ qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
+ write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
+
destroy_workqueue(qrtr_ns.workqueue);
err_sock:
sock_release(qrtr_ns.sock);
@@ -758,7 +812,12 @@ EXPORT_SYMBOL_GPL(qrtr_ns_init);
void qrtr_ns_remove(void)
{
+ write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
+ qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
+ write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
+
cancel_work_sync(&qrtr_ns.work);
+ synchronize_net();
destroy_workqueue(qrtr_ns.workqueue);
/* sock_release() expects the two references that were put during
diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c
index b396c673dfaf..76f625986a7f 100644
--- a/net/rds/af_rds.c
+++ b/net/rds/af_rds.c
@@ -357,7 +357,8 @@ static int rds_cong_monitor(struct rds_sock *rs, sockptr_t optval, int optlen)
return ret;
}
-static int rds_set_transport(struct rds_sock *rs, sockptr_t optval, int optlen)
+static int rds_set_transport(struct net *net, struct rds_sock *rs,
+ sockptr_t optval, int optlen)
{
int t_type;
@@ -373,6 +374,10 @@ static int rds_set_transport(struct rds_sock *rs, sockptr_t optval, int optlen)
if (t_type < 0 || t_type >= RDS_TRANS_COUNT)
return -EINVAL;
+ /* RDS/IB is restricted to the initial network namespace */
+ if (t_type != RDS_TRANS_TCP && !net_eq(net, &init_net))
+ return -EPROTOTYPE;
+
rs->rs_transport = rds_trans_get(t_type);
return rs->rs_transport ? 0 : -ENOPROTOOPT;
@@ -433,6 +438,7 @@ static int rds_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int optlen)
{
struct rds_sock *rs = rds_sk_to_rs(sock->sk);
+ struct net *net = sock_net(sock->sk);
int ret;
if (level != SOL_RDS) {
@@ -461,7 +467,7 @@ static int rds_setsockopt(struct socket *sock, int level, int optname,
break;
case SO_RDS_TRANSPORT:
lock_sock(sock->sk);
- ret = rds_set_transport(rs, optval, optlen);
+ ret = rds_set_transport(net, rs, optval, optlen);
release_sock(sock->sk);
break;
case SO_TIMESTAMP_OLD:
diff --git a/net/rds/ib.c b/net/rds/ib.c
index ac6affa33ce7..39f87272e071 100644
--- a/net/rds/ib.c
+++ b/net/rds/ib.c
@@ -401,8 +401,8 @@ static void rds6_ib_ic_info(struct socket *sock, unsigned int len,
* allowed to influence which paths have priority. We could call userspace
* asserting this policy "routing".
*/
-static int rds_ib_laddr_check(struct net *net, const struct in6_addr *addr,
- __u32 scope_id)
+static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr,
+ __u32 scope_id)
{
int ret;
struct rdma_cm_id *cm_id;
@@ -487,6 +487,26 @@ out:
return ret;
}
+static int rds_ib_laddr_check(struct net *net, const struct in6_addr *addr,
+ __u32 scope_id)
+{
+ struct rds_ib_device *rds_ibdev = NULL;
+
+ /* RDS/IB is restricted to the initial network namespace */
+ if (!net_eq(net, &init_net))
+ return -EPROTOTYPE;
+
+ if (ipv6_addr_v4mapped(addr)) {
+ rds_ibdev = rds_ib_get_device(addr->s6_addr32[3]);
+ if (rds_ibdev) {
+ rds_ib_dev_put(rds_ibdev);
+ return 0;
+ }
+ }
+
+ return rds_ib_laddr_check_cm(net, addr, scope_id);
+}
+
static void rds_ib_unregister_client(void)
{
ib_unregister_client(&rds_ib_client);
diff --git a/net/rds/ib.h b/net/rds/ib.h
index 8ef3178ed4d6..5ff346a1e8ba 100644
--- a/net/rds/ib.h
+++ b/net/rds/ib.h
@@ -381,6 +381,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn,
__rds_ib_conn_error(conn, KERN_WARNING "RDS/IB: " fmt)
/* ib_rdma.c */
+struct rds_ib_device *rds_ib_get_device(__be32 ipaddr);
int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev,
struct in6_addr *ipaddr);
void rds_ib_add_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn);
diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c
index 2cfec252eeac..9594ea245f7f 100644
--- a/net/rds/ib_rdma.c
+++ b/net/rds/ib_rdma.c
@@ -43,7 +43,7 @@ struct workqueue_struct *rds_ib_mr_wq;
static void rds_ib_odp_mr_worker(struct work_struct *work);
-static struct rds_ib_device *rds_ib_get_device(__be32 ipaddr)
+struct rds_ib_device *rds_ib_get_device(__be32 ipaddr)
{
struct rds_ib_device *rds_ibdev;
struct rds_ib_ipaddr *i_ipaddr;
diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c
index 3aff3c2d45a9..ca4f217ef3d3 100644
--- a/net/rose/rose_in.c
+++ b/net/rose/rose_in.c
@@ -270,6 +270,13 @@ int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb)
frametype = rose_decode(skb, &ns, &nr, &q, &d, &m);
+ /*
+ * ROSE_CLEAR_REQUEST carries cause and diagnostic in bytes 3..4.
+ * Reject a malformed frame that is too short to contain them.
+ */
+ if (frametype == ROSE_CLEAR_REQUEST && skb->len < 5)
+ return 0;
+
switch (rose->state) {
case ROSE_STATE_1:
queued = rose_state1_machine(sk, skb, frametype);
diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c
index 7d5e50c921a0..6158e13c98d3 100644
--- a/net/sched/act_ct.c
+++ b/net/sched/act_ct.c
@@ -328,9 +328,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params)
int err = -ENOMEM;
mutex_lock(&zones_mutex);
- ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params);
- if (ct_ft && refcount_inc_not_zero(&ct_ft->ref))
+ rcu_read_lock();
+ ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params);
+ if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) {
+ rcu_read_unlock();
goto out_unlock;
+ }
+ rcu_read_unlock();
ct_ft = kzalloc_obj(*ct_ft);
if (!ct_ft)
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index 23884ef8b80c..646a730dca93 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -74,9 +74,13 @@ TC_INDIRECT_SCOPE int fw_classify(struct sk_buff *skb,
}
}
} else {
- struct Qdisc *q = tcf_block_q(tp->chain->block);
+ struct Qdisc *q;
/* Old method: classify the packet using its skb mark. */
+ if (tcf_block_shared(tp->chain->block))
+ return -1;
+
+ q = tcf_block_q(tp->chain->block);
if (id && (TC_H_MAJ(id) == 0 ||
!(TC_H_MAJ(id ^ q->handle)))) {
res->classid = id;
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index f5a7d5a38755..a024c0843247 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -201,6 +201,7 @@ new_skb:
cb->chunk = head_cb->chunk;
cb->af = head_cb->af;
+ cb->encap_port = head_cb->encap_port;
}
}
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 53a5c027f8e3..cd15b695607e 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -261,9 +261,11 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *t)
skb_set_inner_ipproto(skb, IPPROTO_SCTP);
label = ip6_make_flowlabel(sock_net(sk), skb, fl6->flowlabel, true, fl6);
+ local_bh_disable();
udp_tunnel6_xmit_skb(dst, sk, skb, NULL, &fl6->saddr, &fl6->daddr,
tclass, ip6_dst_hoplimit(dst), label,
sctp_sk(sk)->udp_port, t->encap_port, false, 0);
+ local_bh_enable();
return 0;
}
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 828a59b8e7bf..5800e7ee7ea0 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1070,10 +1070,12 @@ static inline int sctp_v4_xmit(struct sk_buff *skb, struct sctp_transport *t)
skb_reset_inner_mac_header(skb);
skb_reset_inner_transport_header(skb);
skb_set_inner_ipproto(skb, IPPROTO_SCTP);
+ local_bh_disable();
udp_tunnel_xmit_skb(dst_rtable(dst), sk, skb, fl4->saddr,
fl4->daddr, dscp, ip4_dst_hoplimit(dst), df,
sctp_sk(sk)->udp_port, t->encap_port, false, false,
0);
+ local_bh_enable();
return 0;
}
diff --git a/net/strparser/strparser.c b/net/strparser/strparser.c
index fe0e76fdd1f1..a23f4b4dfc67 100644
--- a/net/strparser/strparser.c
+++ b/net/strparser/strparser.c
@@ -45,6 +45,14 @@ static void strp_abort_strp(struct strparser *strp, int err)
strp->stopped = 1;
+ if (strp->skb_head) {
+ kfree_skb(strp->skb_head);
+ strp->skb_head = NULL;
+ }
+
+ strp->skb_nextp = NULL;
+ strp->need_bytes = 0;
+
if (strp->sk) {
struct sock *sk = strp->sk;
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index b8794ea0f01e..44037b066a5f 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -1962,12 +1962,12 @@ static void vsock_update_buffer_size(struct vsock_sock *vsk,
const struct vsock_transport *transport,
u64 val)
{
- if (val > vsk->buffer_max_size)
- val = vsk->buffer_max_size;
-
if (val < vsk->buffer_min_size)
val = vsk->buffer_min_size;
+ if (val > vsk->buffer_max_size)
+ val = vsk->buffer_max_size;
+
if (val != vsk->buffer_size &&
transport && transport->notify_buffer_size)
transport->notify_buffer_size(vsk, &val);
diff --git a/tools/testing/selftests/bpf/prog_tests/sock_ops_get_sk.c b/tools/testing/selftests/bpf/prog_tests/sock_ops_get_sk.c
new file mode 100644
index 000000000000..343d92c4df30
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/sock_ops_get_sk.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+#include "cgroup_helpers.h"
+#include "network_helpers.h"
+#include "sock_ops_get_sk.skel.h"
+
+/* See progs/sock_ops_get_sk.c for the bug description. */
+static void run_sock_ops_test(int cgroup_fd, int prog_fd)
+{
+ int server_fd, client_fd, err;
+
+ err = bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_SOCK_OPS, 0);
+ if (!ASSERT_OK(err, "prog_attach"))
+ return;
+
+ server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0);
+ if (!ASSERT_OK_FD(server_fd, "start_server"))
+ goto detach;
+
+ /* Trigger TCP handshake which causes TCP_NEW_SYN_RECV state where
+ * is_fullsock == 0 and is_locked_tcp_sock == 0.
+ */
+ client_fd = connect_to_fd(server_fd, 0);
+ if (!ASSERT_OK_FD(client_fd, "connect_to_fd"))
+ goto close_server;
+
+ close(client_fd);
+
+close_server:
+ close(server_fd);
+detach:
+ bpf_prog_detach(cgroup_fd, BPF_CGROUP_SOCK_OPS);
+}
+
+void test_ns_sock_ops_get_sk(void)
+{
+ struct sock_ops_get_sk *skel;
+ int cgroup_fd;
+
+ cgroup_fd = test__join_cgroup("/sock_ops_get_sk");
+ if (!ASSERT_OK_FD(cgroup_fd, "join_cgroup"))
+ return;
+
+ skel = sock_ops_get_sk__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel_open_load"))
+ goto close_cgroup;
+
+ /* Test SOCK_OPS_GET_SK with same src/dst register */
+ if (test__start_subtest("get_sk")) {
+ run_sock_ops_test(cgroup_fd,
+ bpf_program__fd(skel->progs.sock_ops_get_sk_same_reg));
+ ASSERT_EQ(skel->bss->null_seen, 1, "null_seen");
+ ASSERT_EQ(skel->bss->bug_detected, 0, "bug_not_detected");
+ }
+
+ /* Test SOCK_OPS_GET_FIELD with same src/dst register */
+ if (test__start_subtest("get_field")) {
+ run_sock_ops_test(cgroup_fd,
+ bpf_program__fd(skel->progs.sock_ops_get_field_same_reg));
+ ASSERT_EQ(skel->bss->field_null_seen, 1, "field_null_seen");
+ ASSERT_EQ(skel->bss->field_bug_detected, 0, "field_bug_not_detected");
+ }
+
+ /* Test SOCK_OPS_GET_SK with different src/dst register */
+ if (test__start_subtest("get_sk_diff_reg")) {
+ run_sock_ops_test(cgroup_fd,
+ bpf_program__fd(skel->progs.sock_ops_get_sk_diff_reg));
+ ASSERT_EQ(skel->bss->diff_reg_null_seen, 1, "diff_reg_null_seen");
+ ASSERT_EQ(skel->bss->diff_reg_bug_detected, 0, "diff_reg_bug_not_detected");
+ }
+
+ sock_ops_get_sk__destroy(skel);
+close_cgroup:
+ close(cgroup_fd);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_bonding.c b/tools/testing/selftests/bpf/prog_tests/xdp_bonding.c
index e8ea26464349..c42488e445c2 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp_bonding.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_bonding.c
@@ -191,13 +191,18 @@ fail:
return -1;
}
-static void bonding_cleanup(struct skeletons *skeletons)
+static void link_cleanup(struct skeletons *skeletons)
{
- restore_root_netns();
while (skeletons->nlinks) {
skeletons->nlinks--;
bpf_link__destroy(skeletons->links[skeletons->nlinks]);
}
+}
+
+static void bonding_cleanup(struct skeletons *skeletons)
+{
+ restore_root_netns();
+ link_cleanup(skeletons);
ASSERT_OK(system("ip link delete bond1"), "delete bond1");
ASSERT_OK(system("ip link delete veth1_1"), "delete veth1_1");
ASSERT_OK(system("ip link delete veth1_2"), "delete veth1_2");
@@ -493,6 +498,90 @@ out:
system("ip link del bond_nest2");
}
+/*
+ * Test that XDP redirect via xdp_master_redirect() does not crash when
+ * the bond master device is not up. When bond is in round-robin mode but
+ * never opened, rr_tx_counter is NULL.
+ */
+static void test_xdp_bonding_redirect_no_up(struct skeletons *skeletons)
+{
+ struct nstoken *nstoken = NULL;
+ int xdp_pass_fd;
+ int veth1_ifindex;
+ int err;
+ char pkt[ETH_HLEN + 1];
+ struct xdp_md ctx_in = {};
+
+ DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
+ .data_in = &pkt,
+ .data_size_in = sizeof(pkt),
+ .ctx_in = &ctx_in,
+ .ctx_size_in = sizeof(ctx_in),
+ .flags = BPF_F_TEST_XDP_LIVE_FRAMES,
+ .repeat = 1,
+ .batch_size = 1,
+ );
+
+ /* We can't use bonding_setup() because bond will be active */
+ SYS(out, "ip netns add ns_rr_no_up");
+ nstoken = open_netns("ns_rr_no_up");
+ if (!ASSERT_OK_PTR(nstoken, "open ns_rr_no_up"))
+ goto out;
+
+ /* bond0: active-backup, UP with slave veth0.
+ * Attaching native XDP to bond0 enables bpf_master_redirect_enabled_key
+ * globally.
+ */
+ SYS(out, "ip link add bond0 type bond mode active-backup");
+ SYS(out, "ip link add veth0 type veth peer name veth0p");
+ SYS(out, "ip link set veth0 master bond0");
+ SYS(out, "ip link set bond0 up");
+ SYS(out, "ip link set veth0p up");
+
+ /* bond1: round-robin, never UP -> rr_tx_counter stays NULL */
+ SYS(out, "ip link add bond1 type bond mode balance-rr");
+ SYS(out, "ip link add veth1 type veth peer name veth1p");
+ SYS(out, "ip link set veth1 master bond1");
+
+ veth1_ifindex = if_nametoindex("veth1");
+ if (!ASSERT_GT(veth1_ifindex, 0, "veth1_ifindex"))
+ goto out;
+
+ /* Attach native XDP to bond0 -> enables global redirect key */
+ if (xdp_attach(skeletons, skeletons->xdp_tx->progs.xdp_tx, "bond0"))
+ goto out;
+
+ /* Attach generic XDP (XDP_TX) to veth1.
+ * When packets arrive at veth1 via netif_receive_skb, do_xdp_generic()
+ * runs this program. XDP_TX + bond slave triggers xdp_master_redirect().
+ */
+ err = bpf_xdp_attach(veth1_ifindex,
+ bpf_program__fd(skeletons->xdp_tx->progs.xdp_tx),
+ XDP_FLAGS_SKB_MODE, NULL);
+ if (!ASSERT_OK(err, "attach generic XDP to veth1"))
+ goto out;
+
+ /* Run BPF_PROG_TEST_RUN with XDP_PASS live frames on veth1.
+ * XDP_PASS frames become SKBs with skb->dev = veth1, entering
+ * netif_receive_skb -> do_xdp_generic -> xdp_master_redirect.
+ * Without the fix, bond_rr_gen_slave_id() dereferences NULL
+ * rr_tx_counter and crashes.
+ */
+ xdp_pass_fd = bpf_program__fd(skeletons->xdp_dummy->progs.xdp_dummy_prog);
+
+ memset(pkt, 0, sizeof(pkt));
+ ctx_in.data_end = sizeof(pkt);
+ ctx_in.ingress_ifindex = veth1_ifindex;
+
+ err = bpf_prog_test_run_opts(xdp_pass_fd, &opts);
+ ASSERT_OK(err, "xdp_pass test_run should not crash");
+
+out:
+ link_cleanup(skeletons);
+ close_netns(nstoken);
+ SYS_NOFAIL("ip netns del ns_rr_no_up");
+}
+
static void test_xdp_bonding_features(struct skeletons *skeletons)
{
LIBBPF_OPTS(bpf_xdp_query_opts, query_opts);
@@ -738,6 +827,9 @@ void serial_test_xdp_bonding(void)
if (test__start_subtest("xdp_bonding_redirect_multi"))
test_xdp_bonding_redirect_multi(&skeletons);
+ if (test__start_subtest("xdp_bonding_redirect_no_up"))
+ test_xdp_bonding_redirect_no_up(&skeletons);
+
out:
xdp_dummy__destroy(skeletons.xdp_dummy);
xdp_tx__destroy(skeletons.xdp_tx);
diff --git a/tools/testing/selftests/bpf/progs/sock_ops_get_sk.c b/tools/testing/selftests/bpf/progs/sock_ops_get_sk.c
new file mode 100644
index 000000000000..3a0689f8ce7c
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/sock_ops_get_sk.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+/*
+ * Test the SOCK_OPS_GET_SK() and SOCK_OPS_GET_FIELD() macros in
+ * sock_ops_convert_ctx_access() when dst_reg == src_reg.
+ *
+ * When dst_reg == src_reg, the macros borrow a temporary register to load
+ * is_fullsock / is_locked_tcp_sock, because dst_reg holds the ctx pointer
+ * and cannot be clobbered before ctx->sk / ctx->field is read. If
+ * is_fullsock == 0 (e.g., TCP_NEW_SYN_RECV with a request_sock), the macro
+ * must still zero dst_reg so the verifier's PTR_TO_SOCKET_OR_NULL /
+ * SCALAR_VALUE type is correct at runtime. A missing clear leaves a stale
+ * ctx pointer in dst_reg that passes NULL checks (GET_SK) or leaks a kernel
+ * address as a scalar (GET_FIELD).
+ *
+ * When dst_reg != src_reg, dst_reg itself is used to load is_fullsock, so
+ * the JEQ (dst_reg == 0) naturally leaves it zeroed on the !fullsock path.
+ */
+
+int bug_detected;
+int null_seen;
+
+SEC("sockops")
+__naked void sock_ops_get_sk_same_reg(void)
+{
+ asm volatile (
+ "r7 = *(u32 *)(r1 + %[is_fullsock_off]);"
+ "r1 = *(u64 *)(r1 + %[sk_off]);"
+ "if r7 != 0 goto 2f;"
+ "if r1 == 0 goto 1f;"
+ "r1 = %[bug_detected] ll;"
+ "r2 = 1;"
+ "*(u32 *)(r1 + 0) = r2;"
+ "goto 2f;"
+ "1:"
+ "r1 = %[null_seen] ll;"
+ "r2 = 1;"
+ "*(u32 *)(r1 + 0) = r2;"
+ "2:"
+ "r0 = 1;"
+ "exit;"
+ :
+ : __imm_const(is_fullsock_off, offsetof(struct bpf_sock_ops, is_fullsock)),
+ __imm_const(sk_off, offsetof(struct bpf_sock_ops, sk)),
+ __imm_addr(bug_detected),
+ __imm_addr(null_seen)
+ : __clobber_all);
+}
+
+/* SOCK_OPS_GET_FIELD: same-register, is_locked_tcp_sock == 0 path. */
+int field_bug_detected;
+int field_null_seen;
+
+SEC("sockops")
+__naked void sock_ops_get_field_same_reg(void)
+{
+ asm volatile (
+ "r7 = *(u32 *)(r1 + %[is_fullsock_off]);"
+ "r1 = *(u32 *)(r1 + %[snd_cwnd_off]);"
+ "if r7 != 0 goto 2f;"
+ "if r1 == 0 goto 1f;"
+ "r1 = %[field_bug_detected] ll;"
+ "r2 = 1;"
+ "*(u32 *)(r1 + 0) = r2;"
+ "goto 2f;"
+ "1:"
+ "r1 = %[field_null_seen] ll;"
+ "r2 = 1;"
+ "*(u32 *)(r1 + 0) = r2;"
+ "2:"
+ "r0 = 1;"
+ "exit;"
+ :
+ : __imm_const(is_fullsock_off, offsetof(struct bpf_sock_ops, is_fullsock)),
+ __imm_const(snd_cwnd_off, offsetof(struct bpf_sock_ops, snd_cwnd)),
+ __imm_addr(field_bug_detected),
+ __imm_addr(field_null_seen)
+ : __clobber_all);
+}
+
+/* SOCK_OPS_GET_SK: different-register, is_fullsock == 0 path. */
+int diff_reg_bug_detected;
+int diff_reg_null_seen;
+
+SEC("sockops")
+__naked void sock_ops_get_sk_diff_reg(void)
+{
+ asm volatile (
+ "r7 = r1;"
+ "r6 = *(u32 *)(r7 + %[is_fullsock_off]);"
+ "r2 = *(u64 *)(r7 + %[sk_off]);"
+ "if r6 != 0 goto 2f;"
+ "if r2 == 0 goto 1f;"
+ "r1 = %[diff_reg_bug_detected] ll;"
+ "r3 = 1;"
+ "*(u32 *)(r1 + 0) = r3;"
+ "goto 2f;"
+ "1:"
+ "r1 = %[diff_reg_null_seen] ll;"
+ "r3 = 1;"
+ "*(u32 *)(r1 + 0) = r3;"
+ "2:"
+ "r0 = 1;"
+ "exit;"
+ :
+ : __imm_const(is_fullsock_off, offsetof(struct bpf_sock_ops, is_fullsock)),
+ __imm_const(sk_off, offsetof(struct bpf_sock_ops, sk)),
+ __imm_addr(diff_reg_bug_detected),
+ __imm_addr(diff_reg_null_seen)
+ : __clobber_all);
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile
index 7c7fa75b80c2..b72080c6d06b 100644
--- a/tools/testing/selftests/drivers/net/Makefile
+++ b/tools/testing/selftests/drivers/net/Makefile
@@ -12,6 +12,7 @@ TEST_GEN_FILES := \
TEST_PROGS := \
gro.py \
hds.py \
+ macsec.py \
napi_id.py \
napi_threaded.py \
netpoll_basic.py \
diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config
index 77ccf83d87e0..fd16994366f4 100644
--- a/tools/testing/selftests/drivers/net/config
+++ b/tools/testing/selftests/drivers/net/config
@@ -3,8 +3,10 @@ CONFIG_DEBUG_INFO_BTF=y
CONFIG_DEBUG_INFO_BTF_MODULES=n
CONFIG_INET_PSP=y
CONFIG_IPV6=y
+CONFIG_MACSEC=m
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_NETCONSOLE_EXTENDED_LOG=y
CONFIG_NETDEVSIM=m
+CONFIG_VLAN_8021Q=m
CONFIG_XDP_SOCKETS=y
diff --git a/tools/testing/selftests/drivers/net/lib/py/env.py b/tools/testing/selftests/drivers/net/lib/py/env.py
index 6a71c7e7f136..24ce122abd9c 100644
--- a/tools/testing/selftests/drivers/net/lib/py/env.py
+++ b/tools/testing/selftests/drivers/net/lib/py/env.py
@@ -258,6 +258,15 @@ class NetDrvEpEnv(NetDrvEnvBase):
if nsim_test is False and self._ns is not None:
raise KsftXfailEx("Test does not work on netdevsim")
+ def get_local_nsim_dev(self):
+ """Returns the local netdevsim device or None.
+ Using this method is discouraged, as it makes tests nsim-specific.
+ Standard interfaces available on all HW should ideally be used.
+ This method is intended for the few cases where nsim-specific
+ assertions need to be verified which cannot be verified otherwise.
+ """
+ return self._ns
+
def _require_cmd(self, comm, key, host=None):
cached = self._required_cmd.get(comm, {})
if cached.get(key) is None:
diff --git a/tools/testing/selftests/drivers/net/macsec.py b/tools/testing/selftests/drivers/net/macsec.py
new file mode 100755
index 000000000000..9a83d9542e04
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/macsec.py
@@ -0,0 +1,343 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+"""MACsec tests."""
+
+import os
+
+from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_raises
+from lib.py import ksft_variants, KsftNamedVariant
+from lib.py import CmdExitFailure, KsftSkipEx
+from lib.py import NetDrvEpEnv
+from lib.py import cmd, ip, defer, ethtool
+
+MACSEC_KEY = "12345678901234567890123456789012"
+MACSEC_VLAN_VID = 10
+
+# Unique prefix per run to avoid collisions in the shared netns.
+# Keep it short: IFNAMSIZ is 16 (incl. NUL), and VLAN names append ".<vid>".
+MACSEC_PFX = f"ms{os.getpid()}_"
+
+
+def _macsec_name(idx=0):
+ return f"{MACSEC_PFX}{idx}"
+
+
+def _get_macsec_offload(dev):
+ """Returns macsec offload mode string from ip -d link show."""
+ info = ip(f"-d link show dev {dev}", json=True)[0]
+ return info.get("linkinfo", {}).get("info_data", {}).get("offload")
+
+
+def _get_features(dev):
+ """Returns ethtool features dict for a device."""
+ return ethtool(f"-k {dev}", json=True)[0]
+
+
+def _require_ip_macsec(cfg):
+ """SKIP if iproute2 on local or remote lacks 'ip macsec' support."""
+ for host in [None, cfg.remote]:
+ out = cmd("ip macsec help", fail=False, host=host)
+ if "Usage" not in out.stdout + out.stderr:
+ where = "remote" if host else "local"
+ raise KsftSkipEx(f"iproute2 too old on {where},"
+ " missing macsec support")
+
+
+def _require_ip_macsec_offload():
+ """SKIP if local iproute2 doesn't understand 'ip macsec offload'."""
+ out = cmd("ip macsec help", fail=False)
+ if "offload" not in out.stdout + out.stderr:
+ raise KsftSkipEx("iproute2 too old, missing macsec offload")
+
+
+def _require_macsec_offload(cfg):
+ """SKIP if local device doesn't support macsec-hw-offload."""
+ _require_ip_macsec_offload()
+ try:
+ feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
+ except (CmdExitFailure, IndexError) as e:
+ raise KsftSkipEx(
+ f"can't query features: {e}") from e
+ if not feat.get("macsec-hw-offload", {}).get("active"):
+ raise KsftSkipEx("macsec-hw-offload not supported")
+
+
+def _get_mac(ifname, host=None):
+ """Gets MAC address of an interface."""
+ dev = ip(f"link show dev {ifname}", json=True, host=host)
+ return dev[0]["address"]
+
+
+def _setup_macsec_sa(cfg, name):
+ """Adds matching TX/RX SAs on both ends."""
+ local_mac = _get_mac(name)
+ remote_mac = _get_mac(name, host=cfg.remote)
+
+ ip(f"macsec add {name} tx sa 0 pn 1 on key 01 {MACSEC_KEY}")
+ ip(f"macsec add {name} rx port 1 address {remote_mac}")
+ ip(f"macsec add {name} rx port 1 address {remote_mac} "
+ f"sa 0 pn 1 on key 02 {MACSEC_KEY}")
+
+ ip(f"macsec add {name} tx sa 0 pn 1 on key 02 {MACSEC_KEY}",
+ host=cfg.remote)
+ ip(f"macsec add {name} rx port 1 address {local_mac}", host=cfg.remote)
+ ip(f"macsec add {name} rx port 1 address {local_mac} "
+ f"sa 0 pn 1 on key 01 {MACSEC_KEY}", host=cfg.remote)
+
+
+def _setup_macsec_devs(cfg, name, offload):
+ """Creates macsec devices on both ends.
+
+ Only the local device gets HW offload; the remote always uses software
+ MACsec since it may not support offload at all.
+ """
+ offload_arg = "mac" if offload else "off"
+
+ ip(f"link add link {cfg.ifname} {name} "
+ f"type macsec encrypt on offload {offload_arg}")
+ defer(ip, f"link del {name}")
+ ip(f"link add link {cfg.remote_ifname} {name} "
+ f"type macsec encrypt on", host=cfg.remote)
+ defer(ip, f"link del {name}", host=cfg.remote)
+
+
+def _set_offload(name, offload):
+ """Sets offload on the local macsec device only."""
+ offload_arg = "mac" if offload else "off"
+
+ ip(f"link set {name} type macsec encrypt on offload {offload_arg}")
+
+
+def _setup_vlans(cfg, name, vid):
+ """Adds VLANs on top of existing macsec devs."""
+ vlan_name = f"{name}.{vid}"
+
+ ip(f"link add link {name} {vlan_name} type vlan id {vid}")
+ defer(ip, f"link del {vlan_name}")
+ ip(f"link add link {name} {vlan_name} type vlan id {vid}", host=cfg.remote)
+ defer(ip, f"link del {vlan_name}", host=cfg.remote)
+
+
+def _setup_vlan_ips(cfg, name, vid):
+ """Adds VLANs and IPs and brings up the macsec + VLAN devices."""
+ local_ip = "198.51.100.1"
+ remote_ip = "198.51.100.2"
+ vlan_name = f"{name}.{vid}"
+
+ ip(f"addr add {local_ip}/24 dev {vlan_name}")
+ ip(f"addr add {remote_ip}/24 dev {vlan_name}", host=cfg.remote)
+ ip(f"link set {name} up")
+ ip(f"link set {name} up", host=cfg.remote)
+ ip(f"link set {vlan_name} up")
+ ip(f"link set {vlan_name} up", host=cfg.remote)
+
+ return vlan_name, remote_ip
+
+
+def test_offload_api(cfg) -> None:
+ """MACsec offload API: create SecY, add SA/rx, toggle offload."""
+
+ _require_macsec_offload(cfg)
+ ms0 = _macsec_name(0)
+ ms1 = _macsec_name(1)
+ ms2 = _macsec_name(2)
+
+ # Create 3 SecY with offload
+ ip(f"link add link {cfg.ifname} {ms0} type macsec "
+ f"port 4 encrypt on offload mac")
+ defer(ip, f"link del {ms0}")
+
+ ip(f"link add link {cfg.ifname} {ms1} type macsec "
+ f"address aa:bb:cc:dd:ee:ff port 5 encrypt on offload mac")
+ defer(ip, f"link del {ms1}")
+
+ ip(f"link add link {cfg.ifname} {ms2} type macsec "
+ f"sci abbacdde01020304 encrypt on offload mac")
+ defer(ip, f"link del {ms2}")
+
+ # Add TX SA
+ ip(f"macsec add {ms0} tx sa 0 pn 1024 on "
+ "key 01 12345678901234567890123456789012")
+
+ # Add RX SC + SA
+ ip(f"macsec add {ms0} rx port 1234 address 1c:ed:de:ad:be:ef")
+ ip(f"macsec add {ms0} rx port 1234 address 1c:ed:de:ad:be:ef "
+ "sa 0 pn 1 on key 00 0123456789abcdef0123456789abcdef")
+
+ # Can't disable offload when SAs are configured
+ with ksft_raises(CmdExitFailure):
+ ip(f"link set {ms0} type macsec offload off")
+ with ksft_raises(CmdExitFailure):
+ ip(f"macsec offload {ms0} off")
+
+ # Toggle offload via rtnetlink on SA-free device
+ ip(f"link set {ms2} type macsec offload off")
+ ip(f"link set {ms2} type macsec encrypt on offload mac")
+
+ # Toggle offload via genetlink
+ ip(f"macsec offload {ms2} off")
+ ip(f"macsec offload {ms2} mac")
+
+
+def test_max_secy(cfg) -> None:
+ """nsim-only test for max number of SecYs."""
+
+ cfg.require_nsim()
+ _require_ip_macsec_offload()
+ ms0 = _macsec_name(0)
+ ms1 = _macsec_name(1)
+ ms2 = _macsec_name(2)
+ ms3 = _macsec_name(3)
+
+ ip(f"link add link {cfg.ifname} {ms0} type macsec "
+ f"port 4 encrypt on offload mac")
+ defer(ip, f"link del {ms0}")
+
+ ip(f"link add link {cfg.ifname} {ms1} type macsec "
+ f"address aa:bb:cc:dd:ee:ff port 5 encrypt on offload mac")
+ defer(ip, f"link del {ms1}")
+
+ ip(f"link add link {cfg.ifname} {ms2} type macsec "
+ f"sci abbacdde01020304 encrypt on offload mac")
+ defer(ip, f"link del {ms2}")
+ with ksft_raises(CmdExitFailure):
+ ip(f"link add link {cfg.ifname} {ms3} "
+ f"type macsec port 8 encrypt on offload mac")
+
+
+def test_max_sc(cfg) -> None:
+ """nsim-only test for max number of SCs."""
+
+ cfg.require_nsim()
+ _require_ip_macsec_offload()
+ ms0 = _macsec_name(0)
+
+ ip(f"link add link {cfg.ifname} {ms0} type macsec "
+ f"port 4 encrypt on offload mac")
+ defer(ip, f"link del {ms0}")
+ ip(f"macsec add {ms0} rx port 1234 address 1c:ed:de:ad:be:ef")
+ with ksft_raises(CmdExitFailure):
+ ip(f"macsec add {ms0} rx port 1235 address 1c:ed:de:ad:be:ef")
+
+
+def test_offload_state(cfg) -> None:
+ """Offload state reflects configuration changes."""
+
+ _require_macsec_offload(cfg)
+ ms0 = _macsec_name(0)
+
+ # Create with offload on
+ ip(f"link add link {cfg.ifname} {ms0} type macsec "
+ f"encrypt on offload mac")
+ cleanup = defer(ip, f"link del {ms0}")
+
+ ksft_eq(_get_macsec_offload(ms0), "mac",
+ "created with offload: should be mac")
+ feats_on_1 = _get_features(ms0)
+
+ ip(f"link set {ms0} type macsec offload off")
+ ksft_eq(_get_macsec_offload(ms0), "off",
+ "offload disabled: should be off")
+ feats_off_1 = _get_features(ms0)
+
+ ip(f"link set {ms0} type macsec encrypt on offload mac")
+ ksft_eq(_get_macsec_offload(ms0), "mac",
+ "offload re-enabled: should be mac")
+ ksft_eq(_get_features(ms0), feats_on_1,
+ "features should match first offload-on snapshot")
+
+ # Delete and recreate without offload
+ cleanup.exec()
+ ip(f"link add link {cfg.ifname} {ms0} type macsec")
+ defer(ip, f"link del {ms0}")
+ ksft_eq(_get_macsec_offload(ms0), "off",
+ "created without offload: should be off")
+ ksft_eq(_get_features(ms0), feats_off_1,
+ "features should match first offload-off snapshot")
+
+ ip(f"link set {ms0} type macsec encrypt on offload mac")
+ ksft_eq(_get_macsec_offload(ms0), "mac",
+ "offload enabled after create: should be mac")
+ ksft_eq(_get_features(ms0), feats_on_1,
+ "features should match first offload-on snapshot")
+
+
+def _check_nsim_vid(cfg, vid, expected) -> None:
+ """Checks if a VLAN is present. Only works on netdevsim."""
+
+ nsim = cfg.get_local_nsim_dev()
+ if not nsim:
+ return
+
+ vlan_path = os.path.join(nsim.nsims[0].dfs_dir, "vlan")
+ with open(vlan_path, encoding="utf-8") as f:
+ vids = f.read()
+ found = f"ctag {vid}\n" in vids
+ ksft_eq(found, expected,
+ f"VLAN {vid} {'expected' if expected else 'not expected'}"
+ f" in debugfs")
+
+
+@ksft_variants([
+ KsftNamedVariant("offloaded", True),
+ KsftNamedVariant("software", False),
+])
+def test_vlan(cfg, offload) -> None:
+ """Ping through VLAN-over-macsec."""
+
+ _require_ip_macsec(cfg)
+ if offload:
+ _require_macsec_offload(cfg)
+ else:
+ _require_ip_macsec_offload()
+ name = _macsec_name()
+ _setup_macsec_devs(cfg, name, offload=offload)
+ _setup_macsec_sa(cfg, name)
+ _setup_vlans(cfg, name, MACSEC_VLAN_VID)
+ vlan_name, remote_ip = _setup_vlan_ips(cfg, name, MACSEC_VLAN_VID)
+ _check_nsim_vid(cfg, MACSEC_VLAN_VID, offload)
+ # nsim doesn't handle the data path for offloaded macsec, so skip
+ # the ping when offloaded on nsim.
+ if not offload or not cfg.get_local_nsim_dev():
+ cmd(f"ping -I {vlan_name} -c 1 -W 5 {remote_ip}")
+
+
+@ksft_variants([
+ KsftNamedVariant("on_to_off", True),
+ KsftNamedVariant("off_to_on", False),
+])
+def test_vlan_toggle(cfg, offload) -> None:
+ """Toggle offload: VLAN filters propagate/remove correctly."""
+
+ _require_ip_macsec(cfg)
+ _require_macsec_offload(cfg)
+ name = _macsec_name()
+ _setup_macsec_devs(cfg, name, offload=offload)
+ _setup_vlans(cfg, name, MACSEC_VLAN_VID)
+ _check_nsim_vid(cfg, MACSEC_VLAN_VID, offload)
+ _set_offload(name, offload=not offload)
+ _check_nsim_vid(cfg, MACSEC_VLAN_VID, not offload)
+ vlan_name, remote_ip = _setup_vlan_ips(cfg, name, MACSEC_VLAN_VID)
+ _setup_macsec_sa(cfg, name)
+ # nsim doesn't handle the data path for offloaded macsec, so skip
+ # the ping when the final state is offloaded on nsim.
+ if offload or not cfg.get_local_nsim_dev():
+ cmd(f"ping -I {vlan_name} -c 1 -W 5 {remote_ip}")
+
+
+def main() -> None:
+ """Main program."""
+ with NetDrvEpEnv(__file__) as cfg:
+ ksft_run([test_offload_api,
+ test_max_secy,
+ test_max_sc,
+ test_offload_state,
+ test_vlan,
+ test_vlan_toggle,
+ ], args=(cfg,))
+ ksft_exit()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/testing/selftests/drivers/net/netdevsim/Makefile b/tools/testing/selftests/drivers/net/netdevsim/Makefile
index 1a228c5430f5..9808c2fbae9e 100644
--- a/tools/testing/selftests/drivers/net/netdevsim/Makefile
+++ b/tools/testing/selftests/drivers/net/netdevsim/Makefile
@@ -11,7 +11,6 @@ TEST_PROGS := \
fib.sh \
fib_notifications.sh \
hw_stats_l3.sh \
- macsec-offload.sh \
nexthop.sh \
peer.sh \
psample.sh \
diff --git a/tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh b/tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh
deleted file mode 100755
index 98033e6667d2..000000000000
--- a/tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0-only
-
-source ethtool-common.sh
-
-NSIM_NETDEV=$(make_netdev)
-MACSEC_NETDEV=macsec_nsim
-
-set -o pipefail
-
-if ! ethtool -k $NSIM_NETDEV | grep -q 'macsec-hw-offload: on'; then
- echo "SKIP: netdevsim doesn't support MACsec offload"
- exit 4
-fi
-
-if ! ip link add link $NSIM_NETDEV $MACSEC_NETDEV type macsec offload mac 2>/dev/null; then
- echo "SKIP: couldn't create macsec device"
- exit 4
-fi
-ip link del $MACSEC_NETDEV
-
-#
-# test macsec offload API
-#
-
-ip link add link $NSIM_NETDEV "${MACSEC_NETDEV}" type macsec port 4 offload mac
-check $?
-
-ip link add link $NSIM_NETDEV "${MACSEC_NETDEV}2" type macsec address "aa:bb:cc:dd:ee:ff" port 5 offload mac
-check $?
-
-ip link add link $NSIM_NETDEV "${MACSEC_NETDEV}3" type macsec sci abbacdde01020304 offload mac
-check $?
-
-ip link add link $NSIM_NETDEV "${MACSEC_NETDEV}4" type macsec port 8 offload mac 2> /dev/null
-check $? '' '' 1
-
-ip macsec add "${MACSEC_NETDEV}" tx sa 0 pn 1024 on key 01 12345678901234567890123456789012
-check $?
-
-ip macsec add "${MACSEC_NETDEV}" rx port 1234 address "1c:ed:de:ad:be:ef"
-check $?
-
-ip macsec add "${MACSEC_NETDEV}" rx port 1234 address "1c:ed:de:ad:be:ef" sa 0 pn 1 on \
- key 00 0123456789abcdef0123456789abcdef
-check $?
-
-ip macsec add "${MACSEC_NETDEV}" rx port 1235 address "1c:ed:de:ad:be:ef" 2> /dev/null
-check $? '' '' 1
-
-# can't disable macsec offload when SAs are configured
-ip link set "${MACSEC_NETDEV}" type macsec offload off 2> /dev/null
-check $? '' '' 1
-
-ip macsec offload "${MACSEC_NETDEV}" off 2> /dev/null
-check $? '' '' 1
-
-# toggle macsec offload via rtnetlink
-ip link set "${MACSEC_NETDEV}2" type macsec offload off
-check $?
-
-ip link set "${MACSEC_NETDEV}2" type macsec offload mac
-check $?
-
-# toggle macsec offload via genetlink
-ip macsec offload "${MACSEC_NETDEV}2" off
-check $?
-
-ip macsec offload "${MACSEC_NETDEV}2" mac
-check $?
-
-for dev in ${MACSEC_NETDEV}{,2,3} ; do
- ip link del $dev
- check $?
-done
-
-
-#
-# test ethtool features when toggling offload
-#
-
-ip link add link $NSIM_NETDEV $MACSEC_NETDEV type macsec offload mac
-TMP_FEATS_ON_1="$(ethtool -k $MACSEC_NETDEV)"
-
-ip link set $MACSEC_NETDEV type macsec offload off
-TMP_FEATS_OFF_1="$(ethtool -k $MACSEC_NETDEV)"
-
-ip link set $MACSEC_NETDEV type macsec offload mac
-TMP_FEATS_ON_2="$(ethtool -k $MACSEC_NETDEV)"
-
-[ "$TMP_FEATS_ON_1" = "$TMP_FEATS_ON_2" ]
-check $?
-
-ip link del $MACSEC_NETDEV
-
-ip link add link $NSIM_NETDEV $MACSEC_NETDEV type macsec
-check $?
-
-TMP_FEATS_OFF_2="$(ethtool -k $MACSEC_NETDEV)"
-[ "$TMP_FEATS_OFF_1" = "$TMP_FEATS_OFF_2" ]
-check $?
-
-ip link set $MACSEC_NETDEV type macsec offload mac
-check $?
-
-TMP_FEATS_ON_3="$(ethtool -k $MACSEC_NETDEV)"
-[ "$TMP_FEATS_ON_1" = "$TMP_FEATS_ON_3" ]
-check $?
-
-
-if [ $num_errors -eq 0 ]; then
- echo "PASSED all $((num_passes)) checks"
- exit 0
-else
- echo "FAILED $num_errors/$((num_errors+num_passes)) checks"
- exit 1
-fi