diff --git a/.mailmap b/.mailmap index 66937cfc09f3..59c3fb7372a2 100644 --- a/.mailmap +++ b/.mailmap @@ -641,6 +641,7 @@ Mayuresh Janorkar Md Sadre Alam Miaoqing Pan Michael Buesch +Michael Chan Michael Grzeschik Michael Grzeschik Michael Riesch diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index b00bd77363d7..4b19c8d3efda 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -3825,5 +3825,6 @@ l3mdev_accept - BOOLEAN max_dgram_qlen - INTEGER The maximum length of dgram socket receive queue - Default: 10 + Possible values: 0 - INT_MAX + Default: 10 diff --git a/MAINTAINERS b/MAINTAINERS index a069b893f4d4..7bfbb50e8695 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5624,7 +5624,7 @@ F: drivers/net/ethernet/broadcom/unimac.h BROADCOM TG3 GIGABIT ETHERNET DRIVER M: Pavan Chebbi -M: Michael Chan +M: Michael Chan L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/broadcom/tg3.* @@ -28875,6 +28875,7 @@ F: virt/lib/ VIRTIO AND VHOST VSOCK DRIVER M: Stefan Hajnoczi M: Stefano Garzarella +R: Bobby Eshleman L: kvm@vger.kernel.org L: virtualization@lists.linux.dev L: netdev@vger.kernel.org @@ -29180,6 +29181,7 @@ F: drivers/media/test-drivers/vivid/* VM SOCKETS (AF_VSOCK) M: Stefano Garzarella +R: Bobby Eshleman L: virtualization@lists.linux.dev L: netdev@vger.kernel.org S: Maintained diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp.c b/drivers/net/ethernet/broadcom/asp2/bcmasp.c index 3393b5dcbc9a..1da0b04f59d7 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp.c +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp.c @@ -511,6 +511,13 @@ static int bcmasp_netfilt_wr_to_hw(struct bcmasp_priv *priv, return 0; } +static bool bcmasp_netfilt_is_companion(struct bcmasp_priv *priv, int i) +{ + return i > 0 && (i % 2) && + priv->net_filters[i].wake_filter && + priv->net_filters[i - 1].wake_filter; +} + void bcmasp_netfilt_suspend(struct bcmasp_intf *intf) { struct bcmasp_priv *priv = intf->parent; @@ -524,9 +531,7 @@ void bcmasp_netfilt_suspend(struct bcmasp_intf *intf) priv->net_filters[i].port != intf->port) continue; - if (i > 0 && (i % 2) && - priv->net_filters[i].wake_filter && - priv->net_filters[i - 1].wake_filter) + if (bcmasp_netfilt_is_companion(priv, i)) continue; ret = bcmasp_netfilt_wr_to_hw(priv, &priv->net_filters[i]); @@ -556,9 +561,7 @@ int bcmasp_netfilt_get_all_active(struct bcmasp_intf *intf, u32 *rule_locs, priv->net_filters[i].port != intf->port) continue; - if (i > 0 && (i % 2) && - priv->net_filters[i].wake_filter && - priv->net_filters[i - 1].wake_filter) + if (bcmasp_netfilt_is_companion(priv, i)) continue; if (j == *rule_cnt) @@ -583,9 +586,7 @@ int bcmasp_netfilt_get_active(struct bcmasp_intf *intf) continue; /* Skip over a wake filter pair */ - if (i > 0 && (i % 2) && - priv->net_filters[i].wake_filter && - priv->net_filters[i - 1].wake_filter) + if (bcmasp_netfilt_is_companion(priv, i)) continue; cnt++; @@ -607,6 +608,9 @@ bool bcmasp_netfilt_check_dup(struct bcmasp_intf *intf, priv->net_filters[i].port != intf->port) continue; + if (bcmasp_netfilt_is_companion(priv, i)) + continue; + cur = &priv->net_filters[i].fs; if (cur->flow_type != fs->flow_type || @@ -659,7 +663,7 @@ bool bcmasp_netfilt_check_dup(struct bcmasp_intf *intf, } /* If no network filter found, return open filter. - * If no more open filters return NULL + * If no more open filters return error. */ struct bcmasp_net_filter *bcmasp_netfilt_get_init(struct bcmasp_intf *intf, u32 loc, bool wake_filter, @@ -669,45 +673,55 @@ struct bcmasp_net_filter *bcmasp_netfilt_get_init(struct bcmasp_intf *intf, struct bcmasp_priv *priv = intf->parent; int i, open_index = -1; - /* Check whether we exceed the filter table capacity */ + if (!init) { + if (loc == RX_CLS_LOC_ANY || loc >= priv->num_net_filters) + return ERR_PTR(-EINVAL); + + if (priv->net_filters[loc].claimed && + priv->net_filters[loc].port == intf->port && + !bcmasp_netfilt_is_companion(priv, loc)) + return &priv->net_filters[loc]; + + return ERR_PTR(-ENOENT); + } + if (loc != RX_CLS_LOC_ANY && loc >= priv->num_net_filters) return ERR_PTR(-EINVAL); /* If the filter location is busy (already claimed) and we are initializing * the filter (insertion), return a busy error code. */ - if (loc != RX_CLS_LOC_ANY && init && priv->net_filters[loc].claimed) - return ERR_PTR(-EBUSY); + if (loc != RX_CLS_LOC_ANY) { + if (priv->net_filters[loc].claimed) + return ERR_PTR(-EBUSY); - /* We need two filters for wake-up, so we cannot use an odd filter */ - if (wake_filter && loc != RX_CLS_LOC_ANY && (loc % 2)) - return ERR_PTR(-EINVAL); - - /* Initialize the loop index based on the desired location or from 0 */ - i = loc == RX_CLS_LOC_ANY ? 0 : loc; - - for ( ; i < priv->num_net_filters; i++) { - /* Found matching network filter */ - if (!init && - priv->net_filters[i].claimed && - priv->net_filters[i].hw_index == i && - priv->net_filters[i].port == intf->port) - return &priv->net_filters[i]; - - /* If we don't need a new filter or new filter already found */ - if (!init || open_index >= 0) - continue; - - /* Wake filter conslidates two filters to cover more bytes - * Wake filter is open if... - * 1. It is an even filter - * 2. The current and next filter is not claimed - */ - if (wake_filter && !(i % 2) && !priv->net_filters[i].claimed && - !priv->net_filters[i + 1].claimed) - open_index = i; - else if (!priv->net_filters[i].claimed) - open_index = i; + /* We need two filters for wake-up, so we cannot use an odd filter */ + if (wake_filter) { + if ((loc % 2) || loc + 1 >= priv->num_net_filters) + return ERR_PTR(-EINVAL); + if (priv->net_filters[loc + 1].claimed) + return ERR_PTR(-EBUSY); + } + open_index = loc; + } else { + for (i = 0; i < priv->num_net_filters; i++) { + /* Wake filter consolidates two filters to cover more bytes. + * Wake filter is open if: + * 1. It is an even filter + * 2. The current and next filter is not claimed + */ + if (wake_filter) { + if (!(i % 2) && (i + 1 < priv->num_net_filters) && + !priv->net_filters[i].claimed && + !priv->net_filters[i + 1].claimed) { + open_index = i; + break; + } + } else if (!priv->net_filters[i].claimed) { + open_index = i; + break; + } + } } if (open_index >= 0) { @@ -716,16 +730,20 @@ struct bcmasp_net_filter *bcmasp_netfilt_get_init(struct bcmasp_intf *intf, nfilter->port = intf->port; nfilter->ch = intf->channel + priv->tx_chan_offset; nfilter->hw_index = open_index; + + if (wake_filter) { + /* Claim next filter */ + priv->net_filters[open_index + 1].claimed = true; + priv->net_filters[open_index + 1].wake_filter = true; + priv->net_filters[open_index + 1].hw_index = open_index + 1; + priv->net_filters[open_index + 1].port = intf->port; + priv->net_filters[open_index + 1].ch = intf->channel + + priv->tx_chan_offset; + nfilter->wake_filter = true; + } } - if (wake_filter && open_index >= 0) { - /* Claim next filter */ - priv->net_filters[open_index + 1].claimed = true; - priv->net_filters[open_index + 1].wake_filter = true; - nfilter->wake_filter = true; - } - - return nfilter ? nfilter : ERR_PTR(-EINVAL); + return nfilter ? nfilter : ERR_PTR(-ENOSPC); } void bcmasp_netfilt_release(struct bcmasp_intf *intf, @@ -733,7 +751,8 @@ void bcmasp_netfilt_release(struct bcmasp_intf *intf, { struct bcmasp_priv *priv = intf->parent; - if (nfilt->wake_filter) { + if (nfilt->wake_filter && !(nfilt->hw_index % 2) && + nfilt->hw_index + 1 < priv->num_net_filters) { memset(&priv->net_filters[nfilt->hw_index + 1], 0, sizeof(struct bcmasp_net_filter)); } diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp.h b/drivers/net/ethernet/broadcom/asp2/bcmasp.h index b08a7ddc4c64..659d422174e2 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp.h +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp.h @@ -245,10 +245,6 @@ struct bcmasp_intf_stats64 { }; struct bcmasp_mib_counters { - u32 edpkt_ts; - u32 edpkt_rx_pkt_cnt; - u32 edpkt_hdr_ext_cnt; - u32 edpkt_hdr_out_cnt; u32 umac_frm_cnt; u32 fb_frm_cnt; u32 fb_rx_fifo_depth; diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c index 3369c45b49d5..f09054a02feb 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c @@ -234,7 +234,7 @@ static struct sk_buff *bcmasp_csum_offload(struct net_device *dev, static netdev_tx_t bcmasp_xmit(struct sk_buff *skb, struct net_device *dev) { struct bcmasp_intf *intf = netdev_priv(dev); - unsigned int total_bytes, size; + unsigned int total_bytes, size, min_size; int spb_index, nr_frags, i, j; struct bcmasp_tx_cb *txcb; dma_addr_t mapping, valid; @@ -263,16 +263,21 @@ static netdev_tx_t bcmasp_xmit(struct sk_buff *skb, struct net_device *dev) if (!skb) return NETDEV_TX_OK; + min_size = ETH_ZLEN + ETH_FCS_LEN; + if (csum_hw) + min_size += sizeof(struct bcmasp_pkt_offload); + + if (skb->len < min_size) { + if (skb_put_padto(skb, min_size)) + return NETDEV_TX_OK; + nr_frags = skb_shinfo(skb)->nr_frags; + } + spb_index = intf->tx_spb_index; valid = intf->tx_spb_dma_valid; for (i = 0; i <= nr_frags; i++) { if (!i) { size = skb_headlen(skb); - if (!nr_frags && size < (ETH_ZLEN + ETH_FCS_LEN)) { - if (skb_put_padto(skb, ETH_ZLEN + ETH_FCS_LEN)) - return NETDEV_TX_OK; - size = skb->len; - } mapping = dma_map_single(kdev, skb->data, size, DMA_TO_DEVICE); } else { @@ -286,6 +291,11 @@ static netdev_tx_t bcmasp_xmit(struct sk_buff *skb, struct net_device *dev) intf->mib.tx_dma_failed++; spb_index = intf->tx_spb_index; for (j = 0; j < i; j++) { + txcb = &intf->tx_cbs[spb_index]; + dma_unmap_single(kdev, + dma_unmap_addr(txcb, dma_addr), + dma_unmap_len(txcb, dma_len), + DMA_TO_DEVICE); bcmasp_clean_txcb(intf, spb_index); spb_index = incr_ring(spb_index, DESC_RING_COUNT); @@ -530,6 +540,12 @@ static int bcmasp_rx_poll(struct napi_struct *napi, int budget) DMA_FROM_DEVICE); len = desc->size; + if (unlikely(len < 2 + ETH_HLEN + (intf->crc_fwd ? ETH_FCS_LEN : 0))) { + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->rx_dropped); + u64_stats_update_end(&stats->syncp); + goto next; + } /* Allocate a page pool page as the SKB data area so the * kernel can recycle it efficiently after the packet is @@ -1300,7 +1316,7 @@ struct bcmasp_intf *bcmasp_interface_create(struct bcmasp_priv *priv, ndev_dn->name); goto err_free_netdev; } - intf->phy_dn = ndev_dn; + intf->phy_dn = of_node_get(ndev_dn); } /* Map resource */ @@ -1340,6 +1356,7 @@ struct bcmasp_intf *bcmasp_interface_create(struct bcmasp_priv *priv, err_deregister_fixed_link: if (of_phy_is_fixed_link(ndev_dn)) of_phy_deregister_fixed_link(ndev_dn); + of_node_put(intf->phy_dn); err_free_netdev: free_netdev(ndev); err: @@ -1352,6 +1369,7 @@ void bcmasp_interface_destroy(struct bcmasp_intf *intf) unregister_netdev(intf->ndev); if (of_phy_is_fixed_link(intf->ndev_dn)) of_phy_deregister_fixed_link(intf->ndev_dn); + of_node_put(intf->phy_dn); free_netdev(intf->ndev); } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 5a9742fd3ddf..6fb0619731ae 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -3666,7 +3666,8 @@ static void bnx2x_update_pbds_gso_enc(struct sk_buff *skb, u8 outerip_off, outerip_len = 0; /* from outer IP to transport */ - hlen_w = skb_inner_transport_offset(skb) >> 1; + hlen_w = (skb_inner_transport_offset(skb) - + skb_network_offset(skb)) >> 1; /* transport len */ hlen_w += inner_tcp_hdrlen(skb) >> 1; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index 9e5009be8e98..96845c8646e0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -381,11 +381,31 @@ int bnxt_xdp_xmit(struct net_device *dev, int num_frames, return nxmit; } +static void bnxt_xdp_apply_cfg(struct bnxt *bp, int tx_xdp) +{ + struct net_device *dev = bp->dev; + int tc = bp->num_tc ? : 1; + + if (bp->xdp_prog) { + bnxt_set_rx_skb_mode(bp, true); + xdp_features_set_redirect_target_locked(dev, true); + } else { + xdp_features_clear_redirect_target_locked(dev); + bnxt_set_rx_skb_mode(bp, false); + } + bp->tx_nr_rings_xdp = tx_xdp; + bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tc + tx_xdp; + bnxt_set_cp_rings(bp, true); + bnxt_set_tpa_flags(bp); + bnxt_set_ring_params(bp); +} + static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog) { struct net_device *dev = bp->dev; int tx_xdp = 0, rc, tc; struct bpf_prog *old; + int old_tx_xdp; netdev_assert_locked(dev); @@ -418,26 +438,23 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog) if (netif_running(dev)) bnxt_close_nic(bp, true, false); + old_tx_xdp = bp->tx_nr_rings_xdp; old = xchg(&bp->xdp_prog, prog); + bnxt_xdp_apply_cfg(bp, tx_xdp); + + if (netif_running(dev)) { + rc = bnxt_open_nic(bp, true, false); + /* a detach cannot be undone, only an attach */ + if (rc && prog) { + WRITE_ONCE(bp->xdp_prog, old); + bnxt_xdp_apply_cfg(bp, old_tx_xdp); + return rc; + } + } + if (old) bpf_prog_put(old); - if (prog) { - bnxt_set_rx_skb_mode(bp, true); - xdp_features_set_redirect_target_locked(dev, true); - } else { - xdp_features_clear_redirect_target_locked(dev); - bnxt_set_rx_skb_mode(bp, false); - } - bp->tx_nr_rings_xdp = tx_xdp; - bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tc + tx_xdp; - bnxt_set_cp_rings(bp, true); - bnxt_set_tpa_flags(bp); - bnxt_set_ring_params(bp); - - if (netif_running(dev)) - return bnxt_open_nic(bp, true, false); - return 0; } diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index cc618f8beb3f..ce108705bbaf 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -18297,6 +18297,12 @@ static const struct dmi_system_id tg3_restart_aer_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge R750"), }, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "datto"), + DMI_MATCH(DMI_PRODUCT_NAME, "SIRIS 5 1U Server"), + }, + }, {} }; diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c index 440319a1e775..a28ddc0542bf 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c @@ -78,6 +78,7 @@ static const struct ptp_clock_info dpaa2_ptp_caps = { .n_per_out = 3, .n_pins = 0, .pps = 1, + .supported_extts_flags = PTP_RISING_EDGE | PTP_STRICT_FLAGS, .adjfine = ptp_qoriq_adjfine, .adjtime = ptp_qoriq_adjtime, .gettime64 = ptp_qoriq_gettime, diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ptp.c b/drivers/net/ethernet/freescale/enetc/enetc_ptp.c index 162cbc801730..5ffee882d624 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ptp.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ptp.c @@ -16,6 +16,7 @@ static struct ptp_clock_info enetc_ptp_caps = { .n_per_out = 0, .n_pins = 0, .pps = 1, + .supported_extts_flags = PTP_RISING_EDGE | PTP_STRICT_FLAGS, .adjfine = ptp_qoriq_adjfine, .adjtime = ptp_qoriq_adjtime, .gettime64 = ptp_qoriq_gettime, diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c index 66792e2b0fad..a518dc66d9ef 100644 --- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c +++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c @@ -448,6 +448,7 @@ static int hbg_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) return ret; + netdev->netdev_ops = &hbg_netdev_ops; ret = hbg_init(priv); if (ret) return ret; @@ -460,7 +461,6 @@ static int hbg_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; netdev->max_mtu = priv->dev_specs.max_mtu; netdev->min_mtu = priv->dev_specs.min_mtu; - netdev->netdev_ops = &hbg_netdev_ops; netdev->watchdog_timeo = 5 * HZ; hbg_hw_set_mtu(priv, ETH_DATA_LEN); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index d04540420bca..f4e232a73de8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -1244,13 +1244,13 @@ static int rvu_nix_blk_aq_enq_inst(struct rvu *rvu, struct nix_hw *nix_hw, __assign_bit(req->qidx, pfvf->rq_bmap, ena); } if (req->ctype == NIX_AQ_CTYPE_SQ) { - ena = (req->rq.ena & req->sq_mask.ena) | + ena = (req->sq.ena & req->sq_mask.ena) | (test_bit(req->qidx, pfvf->sq_bmap) & ~req->sq_mask.ena); __assign_bit(req->qidx, pfvf->sq_bmap, ena); } if (req->ctype == NIX_AQ_CTYPE_CQ) { - ena = (req->rq.ena & req->cq_mask.ena) | + ena = (req->cq.ena & req->cq_mask.ena) | (test_bit(req->qidx, pfvf->cq_bmap) & ~req->cq_mask.ena); __assign_bit(req->qidx, pfvf->cq_bmap, ena); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c index 7c69d9029bfa..54ba7c330d66 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c @@ -138,7 +138,7 @@ mlx5_dpll_pin_ffo_get(struct mlx5_dpll_synce_status *synce_status, { if (!synce_status->oper_freq_measure) return -ENODATA; - *ffo = 1000000LL * synce_status->frequency_diff; + *ffo = synce_status->frequency_diff; return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c index 17084d76209e..2edb55afff1d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c @@ -98,7 +98,7 @@ static void mlx5e_skb_cb_hwtstamp_tx(struct sk_buff *skb, ptpsq->cq_stats->abort_abs_diff_ns += diff; if (diff > (NSEC_PER_SEC >> 1) && !test_and_set_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state)) { - netdev_warn(sq->channel->netdev, + netdev_warn(sq->netdev, "PTP TX timestamp difference between CQE and port exceeds threshold: %lld ns, recovering SQ %u\n", (s64)diff, sq->sqn); queue_work(sq->priv->wq, &ptpsq->report_unhealthy_work); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 261c466a4d36..1f534051cdfe 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -403,11 +403,6 @@ static bool mlx5e_ethtool_set_tcp_data_split(struct mlx5e_priv *priv, return false; } - /* Might need to disable HW-GRO if it was kept on due to hds. */ - if (tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_DISABLED && - dev->cfg->hds_config == ETHTOOL_TCP_DATA_SPLIT_ENABLED) - netdev_update_features(priv->netdev); - return true; } @@ -468,13 +463,23 @@ static int mlx5e_set_ringparam(struct net_device *dev, struct netlink_ext_ack *extack) { struct mlx5e_priv *priv = netdev_priv(dev); + int err; if (!mlx5e_ethtool_set_tcp_data_split(priv, kernel_param->tcp_data_split, extack)) return -EINVAL; - return mlx5e_ethtool_set_ringparam(priv, param, extack); + err = mlx5e_ethtool_set_ringparam(priv, param, extack); + if (err) + return err; + + /* Might need to disable HW-GRO if it was kept on due to hds. */ + if (kernel_param->tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_DISABLED && + dev->cfg->hds_config == ETHTOOL_TCP_DATA_SPLIT_ENABLED) + netdev_update_features(priv->netdev); + + return 0; } void mlx5e_ethtool_get_channels(struct mlx5e_priv *priv, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 478b5956044b..c3c2c5720c6f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3075,7 +3075,6 @@ void mlx5e_close_channels(struct mlx5e_channels *chs) { int i; - ASSERT_RTNL(); if (chs->ptp) { mlx5e_ptp_close(chs->ptp); chs->ptp = NULL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c index c5ab0e0f7d26..812b2f3cddcf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c @@ -63,6 +63,7 @@ static int mlx5_fs_init_hws_actions_pool(struct mlx5_core_dev *dev, xa_init(&hws_pool->el2tol3tnl_pools); xa_init(&hws_pool->el2tol2tnl_pools); xa_init(&hws_pool->mh_pools); + mutex_init(&hws_pool->mh_pools_lock); xa_init(&hws_pool->table_dests); xa_init(&hws_pool->vport_dests); xa_init(&hws_pool->vport_vhca_dests); @@ -111,6 +112,7 @@ static void mlx5_fs_cleanup_hws_actions_pool(struct mlx5_fs_hws_context *fs_ctx) xa_for_each(&hws_pool->mh_pools, i, pool) mlx5_fs_destroy_mh_pool(pool, &hws_pool->mh_pools, i); xa_destroy(&hws_pool->mh_pools); + mutex_destroy(&hws_pool->mh_pools_lock); xa_for_each(&hws_pool->el2tol2tnl_pools, i, pool) mlx5_fs_destroy_pr_pool(pool, &hws_pool->el2tol2tnl_pools, i); xa_destroy(&hws_pool->el2tol2tnl_pools); @@ -1237,7 +1239,7 @@ static struct mlx5_fs_pool * mlx5_fs_get_pr_encap_pool(struct mlx5_core_dev *dev, struct xarray *pr_pools, enum mlx5hws_action_type reformat_type, size_t size) { - struct mlx5_fs_pool *pr_pool; + struct mlx5_fs_pool *pr_pool, *old; unsigned long index = size; int err; @@ -1251,13 +1253,14 @@ mlx5_fs_get_pr_encap_pool(struct mlx5_core_dev *dev, struct xarray *pr_pools, err = mlx5_fs_hws_pr_pool_init(pr_pool, dev, size, reformat_type); if (err) goto free_pr_pool; - err = xa_insert(pr_pools, index, pr_pool, GFP_KERNEL); - if (err) - goto cleanup_pr_pool; - return pr_pool; + old = xa_cmpxchg(pr_pools, index, NULL, pr_pool, GFP_KERNEL); + if (!old) + return pr_pool; -cleanup_pr_pool: mlx5_fs_hws_pr_pool_cleanup(pr_pool); + kfree(pr_pool); + return xa_is_err(old) ? ERR_PTR(xa_err(old)) : old; + free_pr_pool: kfree(pr_pool); return ERR_PTR(err); @@ -1465,6 +1468,23 @@ mlx5_fs_destroy_mh_pool(struct mlx5_fs_pool *pool, struct xarray *mh_pools, kfree(pool); } +static struct mlx5_fs_pool * +mlx5_fs_find_mh_pool(struct xarray *mh_pools, + struct mlx5hws_action_mh_pattern *pattern, + unsigned long *cnt) +{ + struct mlx5_fs_pool *pool; + unsigned long i; + + *cnt = 0; + xa_for_each(mh_pools, i, pool) { + if (mlx5_fs_hws_mh_pool_match(pool, pattern)) + return pool; + (*cnt)++; + } + return NULL; +} + static int mlx5_cmd_hws_modify_header_alloc(struct mlx5_flow_root_namespace *ns, u8 namespace, u8 num_actions, void *modify_actions, @@ -1475,33 +1495,28 @@ static int mlx5_cmd_hws_modify_header_alloc(struct mlx5_flow_root_namespace *ns, struct mlx5_fs_hws_mh *mh_data = NULL; struct mlx5hws_action *hws_action; struct mlx5_fs_pool *pool; - unsigned long i, cnt = 0; - bool known_pattern; + unsigned long cnt; int err; pattern.sz = MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto) * num_actions; pattern.data = modify_actions; - known_pattern = false; - xa_for_each(&hws_pool->mh_pools, i, pool) { - if (mlx5_fs_hws_mh_pool_match(pool, &pattern)) { - known_pattern = true; - break; - } - cnt++; - } - - if (!known_pattern) { - pool = mlx5_fs_create_mh_pool(ns->dev, &pattern, - &hws_pool->mh_pools, cnt); + pool = mlx5_fs_find_mh_pool(&hws_pool->mh_pools, &pattern, &cnt); + if (!pool) { + mutex_lock(&hws_pool->mh_pools_lock); + pool = mlx5_fs_find_mh_pool(&hws_pool->mh_pools, &pattern, + &cnt); + if (!pool) + pool = mlx5_fs_create_mh_pool(ns->dev, &pattern, + &hws_pool->mh_pools, cnt); + mutex_unlock(&hws_pool->mh_pools_lock); if (IS_ERR(pool)) return PTR_ERR(pool); } + mh_data = mlx5_fs_hws_mh_pool_acquire_mh(pool); - if (IS_ERR(mh_data)) { - err = PTR_ERR(mh_data); - goto destroy_pool; - } + if (IS_ERR(mh_data)) + return PTR_ERR(mh_data); hws_action = mh_data->bulk->hws_action; mh_data->data = kmemdup(pattern.data, pattern.sz, GFP_KERNEL); if (!mh_data->data) { @@ -1517,9 +1532,6 @@ static int mlx5_cmd_hws_modify_header_alloc(struct mlx5_flow_root_namespace *ns, release_mh: mlx5_fs_hws_mh_pool_release_mh(pool, mh_data); -destroy_pool: - if (!known_pattern) - mlx5_fs_destroy_mh_pool(pool, &hws_pool->mh_pools, cnt); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.h index 20cdacd8f12e..cf068fb5bdbf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.h @@ -19,6 +19,7 @@ struct mlx5_fs_hws_actions_pool { struct xarray el2tol3tnl_pools; struct xarray el2tol2tnl_pools; struct xarray mh_pools; + struct mutex mh_pools_lock; /* serializes pool creation */ struct xarray table_dests; struct xarray vport_vhca_dests; struct xarray vport_dests; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws_pools.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws_pools.c index 86828e329187..05aa143fa088 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws_pools.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws_pools.c @@ -3,6 +3,7 @@ #include #include "fs_hws_pools.h" +#include "internal.h" #define MLX5_FS_HWS_DEFAULT_BULK_LEN 65536 #define MLX5_FS_HWS_POOL_MAX_THRESHOLD BIT(18) @@ -389,7 +390,7 @@ bool mlx5_fs_hws_mh_pool_match(struct mlx5_fs_pool *mh_pool, struct mlx5hws_action_mh_pattern *pattern) { struct mlx5hws_action_mh_pattern *pool_pattern; - int num_actions, i; + int num_actions; pool_pattern = mh_pool->pool_ctx; if (WARN_ON_ONCE(!pool_pattern)) @@ -398,12 +399,8 @@ bool mlx5_fs_hws_mh_pool_match(struct mlx5_fs_pool *mh_pool, if (pattern->sz != pool_pattern->sz) return false; num_actions = pattern->sz / MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto); - for (i = 0; i < num_actions; i++) { - if ((__force __be32)pattern->data[i] != - (__force __be32)pool_pattern->data[i]) - return false; - } - return true; + return mlx5hws_pat_compare_pattern(num_actions, pool_pattern->data, + num_actions, pattern->data); } struct mlx5hws_action *mlx5_fc_get_hws_action(struct mlx5hws_context *ctx, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/pat_arg.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/pat_arg.c index 9af449c2b4d5..a49115f04025 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/pat_arg.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/pat_arg.c @@ -96,10 +96,10 @@ void mlx5hws_pat_uninit_pattern_cache(struct mlx5hws_pattern_cache *cache) kfree(cache); } -static bool mlx5hws_pat_compare_pattern(int cur_num_of_actions, - __be64 cur_actions[], - int num_of_actions, - __be64 actions[]) +bool mlx5hws_pat_compare_pattern(int cur_num_of_actions, + __be64 cur_actions[], + int num_of_actions, + __be64 actions[]) { int i; @@ -116,8 +116,8 @@ static bool mlx5hws_pat_compare_pattern(int cur_num_of_actions, return false; } else { /* Compare just the control, not the values */ - if ((__force __be32)actions[i] != - (__force __be32)cur_actions[i]) + if (*(__be32 *)&actions[i] != + *(__be32 *)&cur_actions[i]) return false; } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/pat_arg.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/pat_arg.h index 7fbd8dc7aa18..2be7787104e1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/pat_arg.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/pat_arg.h @@ -49,6 +49,11 @@ int mlx5hws_pat_init_pattern_cache(struct mlx5hws_pattern_cache **cache); void mlx5hws_pat_uninit_pattern_cache(struct mlx5hws_pattern_cache *cache); +bool mlx5hws_pat_compare_pattern(int cur_num_of_actions, + __be64 cur_actions[], + int num_of_actions, + __be64 actions[]); + bool mlx5hws_pat_verify_actions(struct mlx5hws_context *ctx, __be64 pattern[], size_t sz); int mlx5hws_arg_create(struct mlx5hws_context *ctx, diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 308d8a94865f..42e9689d7f35 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -2001,7 +2001,7 @@ mlxsw_core_health_fw_fatal_test(struct devlink_health_reporter *reporter, struct netlink_ext_ack *extack) { struct mlxsw_core *mlxsw_core = devlink_health_reporter_priv(reporter); - char mfgd_pl[MLXSW_REG_MFGD_LEN]; + char mfgd_pl[MLXSW_REG_MFGD_LEN] = {0}; int err; /* Read the register first to make sure no other bits are changed. */ @@ -2022,7 +2022,7 @@ mlxsw_core_health_fw_fatal_ops = { static int mlxsw_core_health_fw_fatal_config(struct mlxsw_core *mlxsw_core, bool enable) { - char mfgd_pl[MLXSW_REG_MFGD_LEN]; + char mfgd_pl[MLXSW_REG_MFGD_LEN] = {0}; int err; /* Read the register first to make sure no other bits are changed. */ diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c index 30f172ce822c..a5e7aee730d6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c @@ -120,7 +120,7 @@ static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev, container_of(attr, struct mlxsw_hwmon_attr, dev_attr); struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; - char mtmp_pl[MLXSW_REG_MTMP_LEN]; + char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0}; unsigned long val; int index; int err; @@ -177,7 +177,7 @@ static ssize_t mlxsw_hwmon_fan_fault_show(struct device *dev, container_of(attr, struct mlxsw_hwmon_attr, dev_attr); struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; - char fore_pl[MLXSW_REG_FORE_LEN]; + char fore_pl[MLXSW_REG_FORE_LEN] = {0}; bool fault; int err; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c index 0b84a25e2ea8..659537460e0d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c @@ -1734,7 +1734,7 @@ static int mlxsw_sp2_span_init(struct mlxsw_sp *mlxsw_sp) static int mlxsw_sp2_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp, u16 policer_id_base) { - char mogcr_pl[MLXSW_REG_MOGCR_LEN]; + char mogcr_pl[MLXSW_REG_MOGCR_LEN] = {0}; int err; err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl); diff --git a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_calendar.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_calendar.c index e857640df185..4442c0c1a957 100644 --- a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_calendar.c +++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_calendar.c @@ -82,6 +82,9 @@ int lan969x_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi, struct lan969x_dsm_cal_dev_speed *speed; int err; + for (u32 i = 0; i < SPX5_DSM_CAL_LEN; i++) + data->schedule[i] = SPX5_DSM_CAL_EMPTY; + /* Maximum bandwidth for this taxi */ taxi_bw = (128 * 1000000) / sparx5_clk_period(sparx5->coreclock); @@ -125,7 +128,7 @@ int lan969x_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi, } if (n_devs == 0) { - data->schedule[0] = SPX5_DSM_CAL_EMPTY; + data->schedule[0] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; return 0; } @@ -167,9 +170,6 @@ int lan969x_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi, return -EINVAL; } - for (u32 i = 0; i < SPX5_DSM_CAL_LEN; i++) - data->schedule[i] = SPX5_DSM_CAL_EMPTY; - /* Place the remaining devices */ for (u32 i = 0; i < DSM_CAL_DEV_MAX; i++) { speed = &dev_speeds[i]; diff --git a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c index c7e0e58ff0f7..053d125eda65 100644 --- a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c +++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c @@ -54,10 +54,9 @@ static void lan969x_fdma_tx_clear_buf(struct sparx5 *sparx5, int weight) { struct fdma *fdma = &sparx5->tx.fdma; struct sparx5_tx_buf *db; - unsigned long flags; int i; - spin_lock_irqsave(&sparx5->tx_lock, flags); + spin_lock(&sparx5->tx_lock); for (i = 0; i < fdma->n_dcbs; ++i) { db = &sparx5->tx.dbs[i]; @@ -83,7 +82,7 @@ static void lan969x_fdma_tx_clear_buf(struct sparx5 *sparx5, int weight) db->used = false; } - spin_unlock_irqrestore(&sparx5->tx_lock, flags); + spin_unlock(&sparx5->tx_lock); } static void lan969x_fdma_free_pages(struct sparx5_rx *rx) diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index ab8b818588e2..5bf32f1034e0 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -1991,8 +1991,8 @@ static int qede_flow_spec_to_rule(struct qede_dev *edev, struct ethtool_rx_flow_spec *fs) { struct ethtool_rx_flow_spec_input input = {}; + struct netlink_ext_ack extack = {}; struct ethtool_rx_flow_rule *flow; - struct netlink_ext_ack extack; __be16 proto; int err; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c index ff83858ebc1f..0a6e82a155c0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c @@ -346,7 +346,7 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv, u64_stats_update_end(&stats->syncp); ret |= handle_rx; } - if (likely(intr_status & (XGMAC_TI | XGMAC_TBU))) { + if (likely(intr_status & XGMAC_TI)) { u64_stats_update_begin(&stats->syncp); u64_stats_inc(&stats->tx_normal_irq_n[chan]); u64_stats_update_end(&stats->syncp); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index fb53b1f17714..b20e5038eead 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1773,8 +1773,10 @@ static void stmmac_free_tx_buffer(struct stmmac_priv *priv, tx_q->xdpf[i] = NULL; } - if (tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_XSK_TX) + if (tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_XSK_TX) { tx_q->xsk_frames_done++; + tx_q->tx_skbuff_dma[i].buf_type = STMMAC_TXBUF_T_SKB; + } if (tx_q->tx_skbuff[i] && tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_SKB) { @@ -2956,8 +2958,11 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, tx_q->xdpf[entry] = NULL; } - if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_XSK_TX) + if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_XSK_TX) { tx_q->xsk_frames_done++; + /* Teardown must not count this slot again. */ + tx_q->tx_skbuff_dma[entry].buf_type = STMMAC_TXBUF_T_SKB; + } if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_SKB) { if (likely(skb)) { diff --git a/drivers/net/netdevsim/bpf.c b/drivers/net/netdevsim/bpf.c index 16aa88278398..f45c1c8f0584 100644 --- a/drivers/net/netdevsim/bpf.c +++ b/drivers/net/netdevsim/bpf.c @@ -144,6 +144,17 @@ int nsim_bpf_setup_tc_block_cb(enum tc_setup_type type, return -EOPNOTSUPP; } + /* A dev-bound program can be replayed here when a netdev joins + * a shared block, after the core attach-time check has run; + * enforce the device match as nfp does. + */ + if (prog && bpf_prog_is_dev_bound(prog->aux) && + !bpf_offload_dev_match(prog, ns->netdev)) { + NSIM_EA(cls_bpf->common.extack, + "program bound to a different device"); + return -EINVAL; + } + if (cls_bpf->command != TC_CLSBPF_OFFLOAD) return -EOPNOTSUPP; diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 244454f54417..2c688d661297 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -55,6 +55,8 @@ #define MII_M1011_IMASK 0x12 #define MII_M1011_IMASK_INIT 0x6400 #define MII_M1011_IMASK_CLEAR 0x0000 +/* WOL Event Interrupt Enable, 88E1318S and 88E1510 only */ +#define MII_M1011_IMASK_WOL BIT(7) #define MII_M1011_PHY_SCR 0x10 #define MII_M1011_PHY_SCR_DOWNSHIFT_EN BIT(11) @@ -139,11 +141,6 @@ #define MII_88E1318S_PHY_MSCR1_REG 16 #define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6) -/* Copper Specific Interrupt Enable Register */ -#define MII_88E1318S_PHY_CSIER 0x12 -/* WOL Event Interrupt Enable */ -#define MII_88E1318S_PHY_CSIER_WOL_EIE BIT(7) - #define MII_88E1318S_PHY_LED_FUNC 0x10 #define MII_88E1318S_PHY_LED_FUNC_OFF (0x8) #define MII_88E1318S_PHY_LED_FUNC_ON (0x9) @@ -354,6 +351,7 @@ struct marvell_priv { u32 step; s8 pair; u8 vct_phase; + bool wol_enabled; }; static int marvell_read_page(struct phy_device *phydev) @@ -407,7 +405,8 @@ static int marvell_config_intr(struct phy_device *phydev) return err; } -static irqreturn_t marvell_handle_interrupt(struct phy_device *phydev) +static irqreturn_t __marvell_handle_interrupt(struct phy_device *phydev, + u16 mask) { int irq_status; @@ -417,7 +416,7 @@ static irqreturn_t marvell_handle_interrupt(struct phy_device *phydev) return IRQ_NONE; } - if (!(irq_status & MII_M1011_IMASK_INIT)) + if (!(irq_status & mask)) return IRQ_NONE; phy_trigger_machine(phydev); @@ -425,6 +424,57 @@ static irqreturn_t marvell_handle_interrupt(struct phy_device *phydev) return IRQ_HANDLED; } +static irqreturn_t marvell_handle_interrupt(struct phy_device *phydev) +{ + return __marvell_handle_interrupt(phydev, MII_M1011_IMASK_INIT); +} + +/* WOL_EIE also routes WoL events to INTn. Keep it set while WoL is enabled, + * unless INTn is the PHY interrupt and that interrupt is being disabled: + * nothing would read IEVENT after free_irq() to release the line. + */ +static u16 m88e1318_wol_imask(struct phy_device *phydev) +{ + struct marvell_priv *priv = phydev->priv; + + if (priv->wol_enabled && + (phydev->interrupts == PHY_INTERRUPT_ENABLED || + !phy_interrupt_is_valid(phydev))) + return MII_M1011_IMASK_WOL; + + return 0; +} + +static int m88e1318_config_intr(struct phy_device *phydev) +{ + bool enable = phydev->interrupts == PHY_INTERRUPT_ENABLED; + u16 imask; + int err; + + if (enable) { + err = marvell_ack_interrupt(phydev); + if (err) + return err; + } + + /* Serialise against m88e1318_set_wol() updating wol_enabled */ + phy_lock_mdio_bus(phydev); + imask = enable ? MII_M1011_IMASK_INIT : MII_M1011_IMASK_CLEAR; + imask |= m88e1318_wol_imask(phydev); + err = __phy_write(phydev, MII_M1011_IMASK, imask); + phy_unlock_mdio_bus(phydev); + if (err || enable) + return err; + + return marvell_ack_interrupt(phydev); +} + +static irqreturn_t m88e1318_handle_interrupt(struct phy_device *phydev) +{ + return __marvell_handle_interrupt(phydev, MII_M1011_IMASK_INIT | + MII_M1011_IMASK_WOL); +} + static int marvell_set_polarity(struct phy_device *phydev, int polarity) { u16 val; @@ -1969,18 +2019,27 @@ static void m88e1318_get_wol(struct phy_device *phydev, static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { + struct marvell_priv *priv = phydev->priv; int err = 0, oldpage; oldpage = phy_save_page(phydev); if (oldpage < 0) goto error; - if (wol->wolopts & (WAKE_MAGIC | WAKE_PHY)) { - /* Explicitly switch to page 0x00, just to be sure */ - err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE); - if (err < 0) - goto error; + priv->wol_enabled = !!(wol->wolopts & (WAKE_MAGIC | WAKE_PHY)); + /* Explicitly switch to page 0x00, just to be sure */ + err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE); + if (err < 0) + goto error; + + /* Update the WOL interrupt enable */ + err = __phy_modify(phydev, MII_M1011_IMASK, MII_M1011_IMASK_WOL, + m88e1318_wol_imask(phydev)); + if (err < 0) + goto error; + + if (priv->wol_enabled) { /* If WOL event happened once, the LED[2] interrupt pin * will not be cleared unless we reading the interrupt status * register. If interrupts are in use, the normal interrupt @@ -1990,12 +2049,6 @@ static int m88e1318_set_wol(struct phy_device *phydev, if (!phy_interrupt_is_valid(phydev)) __phy_read(phydev, MII_M1011_IEVENT); - /* Enable the WOL interrupt */ - err = __phy_set_bits(phydev, MII_88E1318S_PHY_CSIER, - MII_88E1318S_PHY_CSIER_WOL_EIE); - if (err < 0) - goto error; - err = marvell_write_page(phydev, MII_MARVELL_LED_PAGE); if (err < 0) goto error; @@ -3817,8 +3870,8 @@ static struct phy_driver marvell_drivers[] = { .config_init = m88e1318_config_init, .config_aneg = m88e1318_config_aneg, .read_status = marvell_read_status, - .config_intr = marvell_config_intr, - .handle_interrupt = marvell_handle_interrupt, + .config_intr = m88e1318_config_intr, + .handle_interrupt = m88e1318_handle_interrupt, .get_wol = m88e1318_get_wol, .set_wol = m88e1318_set_wol, .resume = genphy_resume, @@ -3930,8 +3983,8 @@ static struct phy_driver marvell_drivers[] = { .config_init = m88e1510_config_init, .config_aneg = m88e1510_config_aneg, .read_status = marvell_read_status, - .config_intr = marvell_config_intr, - .handle_interrupt = marvell_handle_interrupt, + .config_intr = m88e1318_config_intr, + .handle_interrupt = m88e1318_handle_interrupt, .get_wol = m88e1318_get_wol, .set_wol = m88e1318_set_wol, .resume = m88e1510_resume, diff --git a/drivers/net/phy/qcom/qca83xx.c b/drivers/net/phy/qcom/qca83xx.c index bc70ed8efd86..2c4d085bacdb 100644 --- a/drivers/net/phy/qcom/qca83xx.c +++ b/drivers/net/phy/qcom/qca83xx.c @@ -92,6 +92,63 @@ static int qca83xx_probe(struct phy_device *phydev) return 0; } +static int qca8337_read_status(struct phy_device *phydev) +{ + struct at803x_ss_mask ss_mask = { + .speed_mask = AT803X_SS_SPEED_MASK, + .speed_shift = __bf_shf(AT803X_SS_SPEED_MASK), + }; + int ret, old_link = phydev->link; + + ret = genphy_read_status(phydev); + if (ret) + return ret; + + /* Keep the state resolved at link-up while the link stays up */ + if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) + goto out; + + phydev->mdix = ETH_TP_MDI_INVALID; + + /* genphy_read_status() derives speed and duplex from the + * advertisements, which is wrong after a SmartSpeed downshift. + * Use what the PHY resolved instead. + */ + if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete && + phydev->link) { + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = false; + phydev->asym_pause = false; + + ret = at803x_read_specific_status(phydev, ss_mask); + if (ret) + return ret; + + /* Report the link down until speed and duplex are resolved, + * so that the next poll reads them again. Also clear what + * genphy_read_status() read for an up link, as the + * PHY_HALTED path of the state machine does. + */ + if (phydev->speed == SPEED_UNKNOWN || + phydev->duplex == DUPLEX_UNKNOWN) { + phydev->link = 0; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->mdix = ETH_TP_MDI_INVALID; + phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN; + linkmode_zero(phydev->lp_advertising); + } else { + phy_resolve_aneg_pause(phydev); + } + } + +out: + /* MDI-X configuration is not implemented */ + phydev->mdix_ctrl = ETH_TP_MDI_INVALID; + + return 0; +} + static int qca83xx_config_init(struct phy_device *phydev) { u8 switch_revision; @@ -220,6 +277,7 @@ static struct phy_driver qca83xx_driver[] = { .flags = PHY_IS_INTERNAL, .config_init = qca83xx_config_init, .soft_reset = genphy_soft_reset, + .read_status = qca8337_read_status, .get_sset_count = qca83xx_get_sset_count, .get_strings = qca83xx_get_strings, .get_stats = qca83xx_get_stats, diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index bf7414b46a26..ee0e318d6a2a 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -194,18 +194,16 @@ static int __set_item(struct pppoe_net *pn, struct pppox_sock *po) return 0; } -static void __delete_item(struct pppoe_net *pn, __be16 sid, - char *addr, int ifindex) +static void __delete_item(struct pppoe_net *pn, const struct pppox_sock *po) { - int hash = hash_item(sid, addr); + int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote); struct pppox_sock *ret, __rcu **src; ret = rcu_dereference_protected(pn->hash_table[hash], lockdep_is_held(&pn->hash_lock)); src = &pn->hash_table[hash]; while (ret) { - if (cmp_addr(&ret->pppoe_pa, sid, addr) && - ret->pppoe_ifindex == ifindex) { + if (ret == po) { struct pppox_sock *next; next = rcu_dereference_protected(ret->next, @@ -236,11 +234,10 @@ static inline struct pppox_sock *get_item(struct pppoe_net *pn, __be16 sid, return po; } -static inline void delete_item(struct pppoe_net *pn, __be16 sid, - char *addr, int ifindex) +static void delete_item(struct pppoe_net *pn, const struct pppox_sock *po) { spin_lock(&pn->hash_lock); - __delete_item(pn, sid, addr, ifindex); + __delete_item(pn, po); spin_unlock(&pn->hash_lock); } @@ -570,8 +567,7 @@ static int pppoe_release(struct socket *sock) * protect "po" from concurrent updates * on pppoe_flush_dev */ - delete_item(pn, po->pppoe_pa.sid, po->pppoe_pa.remote, - po->pppoe_ifindex); + delete_item(pn, po); sock_orphan(sk); sock->sk = NULL; @@ -621,8 +617,7 @@ static int pppoe_connect(struct socket *sock, struct sockaddr_unsized *uservaddr if (stage_session(po->pppoe_pa.sid)) { pppox_unbind_sock(sk); pn = pppoe_pernet(sock_net(sk)); - delete_item(pn, po->pppoe_pa.sid, - po->pppoe_pa.remote, po->pppoe_ifindex); + delete_item(pn, po); if (po->pppoe_dev) { dev_put(po->pppoe_dev); po->pppoe_dev = NULL; @@ -672,8 +667,7 @@ static int pppoe_connect(struct socket *sock, struct sockaddr_unsized *uservaddr error = ppp_register_net_channel(dev_net(dev), &po->chan); if (error) { - delete_item(pn, po->pppoe_pa.sid, - po->pppoe_pa.remote, po->pppoe_ifindex); + delete_item(pn, po); goto err_put; } diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c index ca75f08e3c59..957cc7885d2f 100644 --- a/drivers/net/team/team_core.c +++ b/drivers/net/team/team_core.c @@ -1225,6 +1225,17 @@ static int team_port_add(struct team *team, struct net_device *port_dev, return -EINVAL; } + if (port_dev->type == ARPHRD_CAN || + port_dev->type == ARPHRD_IEEE802154 || + port_dev->type == ARPHRD_IEEE802154_MONITOR || + port_dev->type == ARPHRD_6LOWPAN) { + NL_SET_ERR_MSG(extack, + "CAN, IEEE 802.15.4 and 6LoWPAN devices can't be added as a team port"); + netdev_err(dev, "Device %s is CAN, IEEE 802.15.4 or 6LoWPAN. These device types can't be added as a team port\n", + portname); + return -EINVAL; + } + if (netif_is_team_port(port_dev)) { NL_SET_ERR_MSG(extack, "Device is already a port of a team device"); netdev_err(dev, "Device %s is already a port " diff --git a/drivers/net/tun.c b/drivers/net/tun.c index d4152c9ba8df..439565a264ee 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -513,14 +513,18 @@ static u16 tun_select_queue(struct net_device *dev, struct sk_buff *skb, return ret; } -static inline bool tun_not_capable(struct tun_struct *tun) +static inline bool tun_permitted(struct tun_struct *tun) { const struct cred *cred = current_cred(); struct net *net = dev_net(tun->dev); - return ((uid_valid(tun->owner) && !uid_eq(cred->euid, tun->owner)) || - (gid_valid(tun->group) && !in_egroup_p(tun->group))) && - !ns_capable(net->user_ns, CAP_NET_ADMIN); + /* If both uid and gid are set, *both* must match */ + if ((uid_valid(tun->owner) || gid_valid(tun->group)) && + (!uid_valid(tun->owner) || uid_eq(cred->euid, tun->owner)) && + (!gid_valid(tun->group) || in_egroup_p(tun->group))) + return true; + + return ns_capable(net->user_ns, CAP_NET_ADMIN); } static void tun_set_real_num_queues(struct tun_struct *tun) @@ -2879,7 +2883,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) !!(tun->flags & IFF_MULTI_QUEUE)) return -EINVAL; - if (tun_not_capable(tun)) + if (!tun_permitted(tun)) return -EPERM; err = security_tun_dev_open(tun->security); if (err < 0) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 54fceb64f426..6205742f88d9 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -617,7 +617,7 @@ static void veth_xdp_flush(struct veth_rq *rq, struct veth_xdp_tx_bq *bq) rcv_priv = netdev_priv(rcv); rcv_rq = &rcv_priv->rq[veth_select_rxq(rcv)]; /* xdp_ring is initialized on receive side? */ - if (unlikely(!rcu_access_pointer(rcv_rq->xdp_prog))) + if (unlikely(!rcu_access_pointer(rcv_rq->napi))) goto out; __veth_xdp_flush(rcv_rq); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 7ed64da7fce1..dd2c41ef6fbe 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -443,9 +443,6 @@ struct virtnet_info { /* Work struct for setting rx mode */ struct work_struct rx_mode_work; - /* OK to queue work setting RX mode? */ - bool rx_mode_work_enabled; - /* Does the affinity hint is set for virtqueues? */ bool affinity_hint_set; @@ -712,16 +709,13 @@ static void virtnet_rq_free_buf(struct virtnet_info *vi, static void enable_rx_mode_work(struct virtnet_info *vi) { - rtnl_lock(); - vi->rx_mode_work_enabled = true; - rtnl_unlock(); + if (enable_work(&vi->rx_mode_work) && netif_running(vi->dev)) + schedule_work(&vi->rx_mode_work); } static void disable_rx_mode_work(struct virtnet_info *vi) { - rtnl_lock(); - vi->rx_mode_work_enabled = false; - rtnl_unlock(); + disable_work_sync(&vi->rx_mode_work); } static void virtqueue_napi_schedule(struct napi_struct *napi, @@ -908,6 +902,15 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi, } BUG_ON(offset >= PAGE_SIZE); + + /* The first frag starts at offset, each next frag is a full page. */ + if (unlikely(DIV_ROUND_UP(offset + len, PAGE_SIZE) > MAX_SKB_FRAGS)) { + pr_debug("%s: rx error: len %u needs more than %d frags\n", + vi->dev->name, len, MAX_SKB_FRAGS); + kfree_skb(skb); + return NULL; + } + while (len) { unsigned int frag_size = min((unsigned)PAGE_SIZE - offset, len); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, offset, @@ -3941,8 +3944,7 @@ static void virtnet_set_rx_mode(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); - if (vi->rx_mode_work_enabled) - schedule_work(&vi->rx_mode_work); + schedule_work(&vi->rx_mode_work); } static int virtnet_vlan_rx_add_vid(struct net_device *dev, @@ -5759,7 +5761,6 @@ static void virtnet_freeze_down(struct virtio_device *vdev) /* Make sure no work handler is accessing the device */ flush_work(&vi->config_work); disable_rx_mode_work(vi); - flush_work(&vi->rx_mode_work); if (netif_running(vi->dev)) { rtnl_lock(); @@ -5789,8 +5790,6 @@ static int virtnet_restore_up(struct virtio_device *vdev) virtio_device_ready(vdev); - enable_rx_mode_work(vi); - if (netif_running(vi->dev)) { rtnl_lock(); err = virtnet_open(vi->dev); @@ -5802,6 +5801,8 @@ static int virtnet_restore_up(struct virtio_device *vdev) netif_tx_lock_bh(vi->dev); netif_device_attach(vi->dev); netif_tx_unlock_bh(vi->dev); + + enable_rx_mode_work(vi); return 0; err_destroy_pools: @@ -6734,7 +6735,8 @@ static void virtnet_set_big_packets(struct virtnet_info *vi, const int mtu) */ if (mtu > ETH_DATA_LEN || guest_gso) { vi->big_packets = true; - vi->big_packets_num_skbfrags = guest_gso ? MAX_SKB_FRAGS : DIV_ROUND_UP(mtu, PAGE_SIZE); + vi->big_packets_num_skbfrags = guest_gso ? MAX_SKB_FRAGS - 1 : + DIV_ROUND_UP(mtu, PAGE_SIZE); } } @@ -6897,6 +6899,7 @@ static int virtnet_probe(struct virtio_device *vdev) INIT_WORK(&vi->config_work, virtnet_config_changed_work); INIT_WORK(&vi->rx_mode_work, virtnet_rx_mode_work); + disable_work(&vi->rx_mode_work); if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) { vi->mergeable_rx_bufs = true; @@ -7055,8 +7058,6 @@ static int virtnet_probe(struct virtio_device *vdev) if (vi->has_rss || vi->has_rss_hash_report) virtnet_init_default_rss(vi); - enable_rx_mode_work(vi); - for (i = 0; i < ARRAY_SIZE(guest_offloads); i++) { unsigned int fbit; @@ -7160,6 +7161,8 @@ static int virtnet_probe(struct virtio_device *vdev) goto free_unregister_netdev; } + enable_rx_mode_work(vi); + pr_debug("virtnet: registered device %s with %d RX and TX vq's\n", dev->name, max_queue_pairs); @@ -7212,7 +7215,6 @@ static void virtnet_remove(struct virtio_device *vdev) /* Make sure no work handler is accessing the device. */ flush_work(&vi->config_work); disable_rx_mode_work(vi); - flush_work(&vi->rx_mode_work); virtnet_free_irq_moder(vi); diff --git a/drivers/ptp/ptp_qoriq.c b/drivers/ptp/ptp_qoriq.c index 76272e5ac5b8..bcd29104effe 100644 --- a/drivers/ptp/ptp_qoriq.c +++ b/drivers/ptp/ptp_qoriq.c @@ -355,6 +355,7 @@ static const struct ptp_clock_info ptp_qoriq_caps = { .n_per_out = 0, .n_pins = 0, .pps = 1, + .supported_extts_flags = PTP_RISING_EDGE | PTP_STRICT_FLAGS, .adjfine = ptp_qoriq_adjfine, .adjtime = ptp_qoriq_adjtime, .gettime64 = ptp_qoriq_gettime, diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 79f9ee0cf799..c0ae442c4344 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2552,6 +2552,11 @@ struct net_device { /* protected by rtnl_lock */ struct bpf_xdp_entity xdp_state[__MAX_XDP_MODE]; +#ifdef CONFIG_XDP_SOCKETS + /** @xsk_pools: assigned AF_XDP pools, ops protected */ + struct hlist_head xsk_pools; +#endif + u8 dev_addr_shadow[MAX_ADDR_LEN]; netdevice_tracker linkwatch_dev_tracker; netdevice_tracker watchdog_dev_tracker; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 5079335f7aae..6787fcf6ac3e 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -639,9 +639,9 @@ struct skb_shared_info { * * Transport layers send out clones of payload skbs they hold for * retransmissions. To allow lower layers of the stack to prepend their headers - * we split &skb_shared_info.dataref into two halves. - * The lower 16 bits count the overall number of references. - * The higher 16 bits indicate how many of the references are payload-only. + * we split &skb_shared_info.dataref into two parts. + * The lower 30 bits count the overall number of references. + * Bit 30 indicates whether one of the references is payload-only. * skb_header_cloned() checks if skb is allowed to add / write the headers. * * The creator of the skb (e.g. TCP) marks its skb as &sk_buff.nohdr @@ -661,7 +661,7 @@ struct skb_shared_info { * Having multiple payload-only skbs with different lengths of hdr_len is not * possible. The payload-only skbs should never leave their owner. */ -#define SKB_DATAREF_SHIFT 16 +#define SKB_DATAREF_SHIFT 30 #define SKB_DATAREF_MASK ((1 << SKB_DATAREF_SHIFT) - 1) @@ -1042,8 +1042,6 @@ struct sk_buff { __u16 tc_index; /* traffic control index */ #endif - u16 alloc_cpu; - union { __wsum csum; struct { @@ -1094,6 +1092,8 @@ struct sk_buff { ); /* end headers group */ + u16 alloc_cpu; + /* These elements must be at the end, see alloc_skb() for details. */ sk_buff_data_t tail; sk_buff_data_t end; diff --git a/include/net/addrconf.h b/include/net/addrconf.h index e6764245995f..6818057cf727 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -81,6 +81,9 @@ struct ifa6_config { u8 ifa_proto; const struct in6_addr *peer_pfx; + struct inet6_ifaddr *ifpub; + unsigned long cstamp; + unsigned long tstamp; u32 rt_priority; u32 ifa_flags; diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 32fde731bceb..9a83bff32569 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -36,9 +36,6 @@ #define IP_VS_HDR_INVERSE 1 #define IP_VS_HDR_ICMP 2 -/* Destination Server Flags */ -#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */ - /* Destination Server Config Flags */ #define IP_VS_DEST_CF_AVAILABLE 0x0001 /* server is available */ @@ -973,33 +970,44 @@ struct ip_vs_dest_dst { * and so on. */ struct ip_vs_dest { + /* Cacheline for hash table nodes - read-mostly */ + struct list_head n_list; /* for the dests in the service */ struct hlist_node d_list; /* for table with all the dests */ - u16 af; /* address family */ - __be16 port; /* port number of the server */ - union nf_inet_addr addr; /* IP address of the server */ - volatile unsigned int flags; /* dest status flags */ - atomic_t conn_flags; /* flags to copy to conn */ atomic_t weight; /* server weight */ - unsigned long cflags; /* config flags */ atomic_t last_weight; /* server latest weight */ + + /* connection thresholds */ + u32 l_threshold_val;/* used lower threshold */ + u32 u_threshold_val;/* used upper threshold */ + /* 32/48 */ + u32 l_threshold; /* lower threshold */ + u32 u_threshold; /* upper threshold */ + + unsigned long cflags; /* config flags */ + + /* 44/64 */ + atomic_t conn_flags; /* flags to copy to conn */ + __u16 tun_type; /* tunnel type */ __be16 tun_port; /* tunnel port */ __u16 tun_flags; /* tunnel flags */ + u16 af; /* address family */ + __be16 port; /* port number of the server */ + /* 60/80 */ + union nf_inet_addr addr; /* IP address of the server */ + + /* connection counters */ + atomic_t totalconns; /* total connections */ + atomic_t activeconns; /* active connections */ + atomic_t persistconns; /* persistent connections */ + refcount_t refcnt; /* reference counter */ struct ip_vs_stats stats; /* statistics */ unsigned long idle_start; /* start time, jiffies */ - /* connection counters and thresholds */ - atomic_t activeconns; /* active connections */ - atomic_t totalconns; /* total connections */ - atomic_t persistconns; /* persistent connections */ - __u32 u_threshold; /* upper threshold */ - __u32 l_threshold; /* lower threshold */ - __u32 l_threshold_val;/* used lower threshold */ - /* for destination cache */ spinlock_t dst_lock; /* lock of dst_cache */ struct ip_vs_dest_dst __rcu *dest_dst; /* cached dst info */ @@ -1915,7 +1923,13 @@ static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest) kfree(dest); } -void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode); +void ip_vs_dest_update_overload(struct ip_vs_dest *dest, bool overload); + +/* Check if dest is in overloaded state */ +static inline bool ip_vs_dest_is_overloaded(const struct ip_vs_dest *dest) +{ + return READ_ONCE(dest->l_threshold_val); +} /* IPVS sync daemon data and function prototypes * (from ip_vs_sync.c) diff --git a/include/net/mptcp.h b/include/net/mptcp.h index 485d55b66ea6..ad7005b574bb 100644 --- a/include/net/mptcp.h +++ b/include/net/mptcp.h @@ -219,6 +219,9 @@ int mptcp_subflow_init_cookie_req(struct request_sock *req, struct request_sock *mptcp_subflow_reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener, bool attach_listener); +/* Caller must hold an RCU read-side lock. */ +void mptcp_subflow_reqsk_clone(struct request_sock *req, + struct request_sock *new_req); __be32 mptcp_get_reset_option(const struct sk_buff *skb); @@ -305,6 +308,12 @@ static inline struct request_sock *mptcp_subflow_reqsk_alloc(const struct reques return NULL; } +/* Caller must hold an RCU read-side lock. */ +static inline void mptcp_subflow_reqsk_clone(struct request_sock *req, + struct request_sock *new_req) +{ +} + static inline __be32 mptcp_reset_option(const struct sk_buff *skb) { return htonl(0u); } static inline void mptcp_active_detect_blackhole(struct sock *sk, bool expired) { } diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 3bfd261a53cc..76605d1ee839 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -252,6 +252,7 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc, struct sctp_fwdtsn_skip *skiplist); struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc, __u16 key_id); +void sctp_control_set_owner_w(struct sctp_chunk *chunk); struct sctp_chunk *sctp_make_strreset_req(const struct sctp_association *asoc, __u16 stream_num, __be16 *stream_list, bool out, bool in); diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h index a7df573784fd..02e89350faf9 100644 --- a/include/net/xsk_buff_pool.h +++ b/include/net/xsk_buff_pool.h @@ -48,6 +48,8 @@ struct xsk_buff_pool { struct device *dev; struct net_device *netdev; struct list_head xsk_tx_list; + /* Assigned pools, including deferred releases; ops protected. */ + struct hlist_node dev_node; /* Protects modifications to the xsk_tx_list */ spinlock_t xsk_tx_list_lock; refcount_t users; @@ -119,6 +121,7 @@ bool xp_put_pool(struct xsk_buff_pool *pool); void xp_clear_dev(struct xsk_buff_pool *pool); void xp_add_xsk(struct xsk_buff_pool *pool, struct xdp_sock *xs); void xp_del_xsk(struct xsk_buff_pool *pool, struct xdp_sock *xs); +void xp_clear_dev_all(struct net_device *dev); /* AF_XDP, and XDP core. */ void xp_free(struct xdp_buff_xsk *xskb); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index cb8f3cdbf1f7..bca0a3693636 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -110,6 +110,8 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, unsigned int len; int ret; + len = skb->len; + /* Handle non-VLAN frames if they are sent to us, for example by DHCP. * * NOTE: THIS ASSUMES DIX ETHERNET, SPECIFICALLY NOT SUPPORTING @@ -121,10 +123,39 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, vlan_tci = vlan->vlan_id; vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority); __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci); + } else if (skb_headlen(skb) >= VLAN_ETH_HLEN + sizeof(__be16) && + !skb_shared(skb) && !skb_vlan_tag_present(skb) && + !skb_metadata_len(skb) && + (skb->ip_summed != CHECKSUM_PARTIAL || + skb_checksum_start_offset(skb) >= VLAN_ETH_HLEN) && + vlan_hw_offload_capable(READ_ONCE(vlan->real_dev->features), + vlan->vlan_proto)) { + u16 vlan_tci; + + /* The frame carries its outer tag in-band, either inserted + * by vlan_dev_hard_header() or provided by the sender. + * Let the real device insert it: some NICs can not offload + * checksum or TSO for frames carrying an in-band tag. + * Not all ndo_start_xmit() callers set the mac header. + * vlan_set_encap_proto() may read two bytes after the vlan + * header, so frames with a shorter linear part are sent + * unchanged, as are shared skbs (e.g. from pktgen), skbs + * already carrying a hwaccel tag (e.g. from generic XDP + * redirect), skbs carrying XDP metadata, and CHECKSUM_PARTIAL + * skbs whose checksum starts within the header to be popped. + * On allocation failure, drop the frame rather than handing + * it to the real device with an in-band tag. + */ + skb_reset_mac_header(skb); + if (unlikely(__skb_vlan_pop(skb, &vlan_tci))) { + dev_kfree_skb_any(skb); + this_cpu_inc(vlan->vlan_pcpu_stats->tx_dropped); + return NETDEV_TX_OK; + } + __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci); } skb->dev = vlan->real_dev; - len = skb->len; if (unlikely(netpoll_tx_running(dev))) return vlan_netpoll_send_skb(vlan, skb); diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index a01bd280c722..0adcd1524ab2 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -819,6 +819,7 @@ static int br_mdb_add_group_src_fwd(const struct br_mdb_config *cfg, struct br_mdb_config sg_cfg; struct br_ip sg_ip; u8 flags = 0; + int err; sg_ip = cfg->group; sg_ip.src = src_ip->src; @@ -842,7 +843,11 @@ static int br_mdb_add_group_src_fwd(const struct br_mdb_config *cfg, sg_cfg.filter_mode = MCAST_INCLUDE; sg_cfg.rt_protocol = cfg->rt_protocol; sg_cfg.nlflags = cfg->nlflags; - return br_mdb_add_group_sg(&sg_cfg, sgmp, brmctx, flags, extack); + err = br_mdb_add_group_sg(&sg_cfg, sgmp, brmctx, flags, extack); + if (err) + br_multicast_del_group(sgmp); + + return err; } static int br_mdb_add_group_src(const struct br_mdb_config *cfg, @@ -1047,6 +1052,7 @@ static int br_mdb_add_group(const struct br_mdb_config *cfg, struct net_bridge_mcast *brmctx; struct br_ip group = cfg->group; unsigned char flags = 0; + int err; brmctx = __br_mdb_choose_context(br, entry, extack); if (!brmctx) @@ -1073,9 +1079,13 @@ static int br_mdb_add_group(const struct br_mdb_config *cfg, flags |= MDB_PG_FLAGS_PERMANENT; if (br_multicast_is_star_g(&group)) - return br_mdb_add_group_star_g(cfg, mp, brmctx, flags, extack); + err = br_mdb_add_group_star_g(cfg, mp, brmctx, flags, extack); else - return br_mdb_add_group_sg(cfg, mp, brmctx, flags, extack); + err = br_mdb_add_group_sg(cfg, mp, brmctx, flags, extack); + if (err) + br_multicast_del_group(mp); + + return err; } static int __br_mdb_add(const struct br_mdb_config *cfg, diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 59f2278019e8..ae4fa050be97 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -647,6 +647,14 @@ static void br_multicast_del_mdb_entry(struct net_bridge_mdb_entry *mp) queue_work(system_long_wq, &br->mcast_gc_work); } +void br_multicast_del_group(struct net_bridge_mdb_entry *mp) +{ + if (mp->ports || mp->host_joined) + return; + + br_multicast_del_mdb_entry(mp); +} + static void br_multicast_group_expired(struct timer_list *t) { struct net_bridge_mdb_entry *mp = timer_container_of(mp, t, timer); @@ -1554,6 +1562,7 @@ __br_multicast_add_group(struct net_bridge_mcast *brmctx, p = br_multicast_new_port_group(pmctx->port, group, *pp, 0, src, filter_mode, RTPROT_KERNEL, NULL); if (unlikely(!p)) { + br_multicast_del_group(mp); p = ERR_PTR(-ENOMEM); goto out; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 05921192ebda..014d0b8db8c7 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -1143,6 +1143,7 @@ struct net_bridge_mdb_entry * br_mdb_ip_get(struct net_bridge *br, struct br_ip *dst); struct net_bridge_mdb_entry * br_multicast_new_group(struct net_bridge *br, struct br_ip *group); +void br_multicast_del_group(struct net_bridge_mdb_entry *mp); struct net_bridge_port_group * br_multicast_new_port_group(struct net_bridge_port *port, const struct br_ip *group, diff --git a/net/core/gro.c b/net/core/gro.c index 29b4d02bf519..ff828a3dce3c 100644 --- a/net/core/gro.c +++ b/net/core/gro.c @@ -187,6 +187,10 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb) } merge: + /* skb_segment() cannot split the linear part of a frag_list member. */ + if (unlikely(headlen > offset + skb_shinfo(p)->gso_size)) + return -E2BIG; + /* sk ownership - if any - completely transferred to the aggregated packet */ skb->destructor = NULL; skb->sk = NULL; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index a0f2d930ebf5..eb9fcc4b1fd1 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1584,7 +1584,6 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) #ifdef CONFIG_NET_RX_BUSY_POLL CHECK_SKB_FIELD(napi_id); #endif - CHECK_SKB_FIELD(alloc_cpu); #ifdef CONFIG_XPS CHECK_SKB_FIELD(sender_cpu); #endif @@ -1616,6 +1615,7 @@ static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb) C(pfmemalloc); C(pp_recycle); n->destructor = NULL; + C(alloc_cpu); C(tail); C(end); C(head); diff --git a/net/core/sock.c b/net/core/sock.c index ee81c62be517..11537a136e30 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -770,6 +770,7 @@ bool sk_mc_loop(const struct sock *sk) { if (dev_recursion_level()) return false; + sk = sk_const_to_full_sk(sk); if (!sk) return true; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 2ed4c706ca9e..1e9d988849c9 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -2369,8 +2369,8 @@ static int inet_netconf_get_devconf(struct sk_buff *in_skb, struct net *net = sock_net(in_skb->sk); struct nlattr *tb[NETCONFA_MAX + 1]; const struct ipv4_devconf *devconf; - struct in_device *in_dev = NULL; - struct net_device *dev = NULL; + struct in_device *in_dev; + struct net_device *dev; struct sk_buff *skb; int ifindex; int err; @@ -2382,7 +2382,13 @@ static int inet_netconf_get_devconf(struct sk_buff *in_skb, if (!tb[NETCONFA_IFINDEX]) return -EINVAL; + skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL); + if (!skb) + return -ENOBUFS; + ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); + + rcu_read_lock(); switch (ifindex) { case NETCONFA_IFINDEX_ALL: devconf = net->ipv4.devconf_all; @@ -2392,36 +2398,29 @@ static int inet_netconf_get_devconf(struct sk_buff *in_skb, break; default: err = -ENODEV; - dev = dev_get_by_index(net, ifindex); - if (dev) - in_dev = in_dev_get(dev); + dev = dev_get_by_index_rcu(net, ifindex); + if (!dev) + goto out_unlock; + in_dev = __in_dev_get_rcu(dev); if (!in_dev) - goto errout; + goto out_unlock; devconf = &in_dev->cnf; break; } - err = -ENOBUFS; - skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL); - if (!skb) - goto errout; - err = inet_netconf_fill_devconf(skb, ifindex, devconf, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, RTM_NEWNETCONF, 0, NETCONFA_ALL); +out_unlock: + rcu_read_unlock(); if (err < 0) { /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ WARN_ON(err == -EMSGSIZE); kfree_skb(skb); - goto errout; + return err; } - err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); -errout: - if (in_dev) - in_dev_put(in_dev); - dev_put(dev); - return err; + return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); } static int inet_netconf_dump_devconf(struct sk_buff *skb, diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c index 5028c72d494a..c8129782d39d 100644 --- a/net/ipv4/gre_offload.c +++ b/net/ipv4/gre_offload.c @@ -242,7 +242,7 @@ static int gre_gro_complete(struct sk_buff *skb, int nhoff) __be16 type; skb->encapsulation = 1; - skb_shinfo(skb)->gso_type = SKB_GSO_GRE; + skb_shinfo(skb)->gso_type |= SKB_GSO_GRE; type = greh->protocol; if (greh->flags & GRE_KEY) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index c688c454422a..8151c638a24c 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -961,6 +962,9 @@ static struct request_sock *inet_reqsk_clone(struct request_sock *req, rcu_assign_pointer(tcp_sk(nreq->sk)->fastopen_rsk, nreq); } + if (rsk_is_mptcp(req)) + mptcp_subflow_reqsk_clone(req, nreq); + return nreq; } diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 3ee437fa3eae..5485586fa0e6 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1367,6 +1367,7 @@ static int erspan_tunnel_init(struct net_device *dev) dev->features |= GRE_FEATURES; dev->hw_features |= GRE_FEATURES; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + dev->lltx = true; netif_keep_dst(dev); return ip_tunnel_init(dev); diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index 59ec465a9df9..7cb33af671e2 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -357,6 +357,11 @@ void nf_send_unreach(struct sk_buff *skb_in, int code, int hook) if (!skb_valid_dst(skb_in) && nf_reject_fill_skb_dst(skb_in) < 0) return; + if (hook == NF_INET_INGRESS) { + memset(IPCB(skb_in), 0, sizeof(*IPCB(skb_in))); + IPCB(skb_in)->iif = skb_in->skb_iif; + } + if (skb_csum_unnecessary(skb_in) || !nf_reject_verify_csum(skb_in, dataoff, proto)) { icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 73e129768184..8c9691569e01 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -404,12 +404,12 @@ static struct request_sock *cookie_tcp_check(struct net *net, struct sock *sk, */ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) { - struct ip_options *opt = &TCP_SKB_CB(skb)->header.h4.opt; const struct tcphdr *th = tcp_hdr(skb); struct tcp_sock *tp = tcp_sk(sk); struct inet_request_sock *ireq; struct net *net = sock_net(sk); struct tcp_request_sock *treq; + struct ip_options_rcu *opt; struct request_sock *req; struct sock *ret = sk; struct flowi4 fl4; @@ -449,6 +449,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) SKB_DR_SET(reason, SECURITY_HOOK); goto out_free; } + /* Load ireq->ireq_opt after the LSM hook, which can replace it. */ + opt = rcu_dereference_protected(ireq->ireq_opt, true); tcp_ao_syncookie(sk, skb, req, AF_INET); @@ -461,7 +463,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) flowi4_init_output(&fl4, ireq->ir_iif, ireq->ir_mark, ip_sock_rt_tos(sk), ip_sock_rt_scope(sk), IPPROTO_TCP, inet_sk_flowi_flags(sk), - opt->srr ? opt->faddr : ireq->ir_rmt_addr, + (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr, ireq->ir_loc_addr, th->source, th->dest, sk_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi_common(&fl4)); @@ -470,6 +472,12 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) SKB_DR_SET(reason, IP_OUTNOROUTES); goto out_free; } + /* Same check as in inet_csk_route_req(). */ + if (opt && opt->opt.is_strictroute && rt->rt_uses_gateway) { + ip_rt_put(rt); + SKB_DR_SET(reason, IP_OUTNOROUTES); + goto out_free; + } /* Try to redo what tcp_v4_send_synack did. */ req->rsk_window_clamp = READ_ONCE(tp->window_clamp) ? : diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 9dfa25347615..c2b024a90bbd 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -579,6 +579,7 @@ static int tcp_bpf_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) copy); if (ret < 0) { sk_msg_trim(sk, msg_tx, osize); + err = ret; goto out_err; } diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index dc0c081fc1f3..d58f99c67d4d 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -161,16 +161,26 @@ static void tcpm_check_stamp(struct tcp_metrics_block *tm, rcu_dereference_protected(p, lockdep_is_held(&tcp_metrics_lock)) static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, + struct net *net, struct inetpeer_addr *saddr, struct inetpeer_addr *daddr, unsigned int hash) { struct tcp_metrics_block *tm; bool reclaim = false; - struct net *net; spin_lock_bh(&tcp_metrics_lock); - net = dst_dev_net_rcu(dst); + + /* Do not add entries to a dismantling netns: they would not be + * flushed again. Callers are in an RCU read-side section, and + * cleanup_net() waits for a grace period after the netns refcount + * reached zero and before tcp_metrics_flush_all(NULL) runs, so an + * entry added after a successful check_net() is seen by the flush. + */ + if (unlikely(!check_net(net))) { + tm = NULL; + goto out_unlock; + } /* While waiting for the spin-lock the cache might have been populated * with this entry and so we have to check again. @@ -273,7 +283,7 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, return NULL; } - net = dst_dev_net_rcu(dst); + net = sock_net(req_to_sk(req)); hash ^= net_hash_mix(net); hash = hash_32(hash, tcp_metrics_hash_log); @@ -318,7 +328,7 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, else return NULL; - net = dst_dev_net_rcu(dst); + net = sock_net(sk); hash ^= net_hash_mix(net); hash = hash_32(hash, tcp_metrics_hash_log); @@ -326,7 +336,7 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, if (tm == TCP_METRICS_RECLAIM_PTR) tm = NULL; if (!tm && create) - tm = tcpm_new(dst, &saddr, &daddr, hash); + tm = tcpm_new(dst, net, &saddr, &daddr, hash); else tcpm_check_stamp(tm, dst); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 7bd0e8820abb..c8c0dd04982d 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1158,10 +1158,19 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg, ifa->flags |= IFA_F_TENTATIVE; ifa->valid_lft = cfg->valid_lft; ifa->prefered_lft = cfg->preferred_lft; - ifa->cstamp = ifa->tstamp = jiffies; ifa->tokenized = false; ifa->rt = f6i; + ifa->ifpub = cfg->ifpub; + if (cfg->ifpub) { + ifa->cstamp = cfg->cstamp; + ifa->tstamp = cfg->tstamp; + } else { + unsigned long now = jiffies; + + ifa->cstamp = now; + ifa->tstamp = now; + } ifa->idev = idev; in6_dev_hold(idev); @@ -1171,14 +1180,18 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg, rcu_read_lock(); - err = ipv6_add_addr_hash(idev->dev, ifa); + write_lock_bh(&idev->lock); + + if (idev->dead) + err = -ENODEV; + else + err = ipv6_add_addr_hash(idev->dev, ifa); if (err < 0) { + write_unlock_bh(&idev->lock); rcu_read_unlock(); goto out; } - write_lock_bh(&idev->lock); - /* Add to inet6_dev unicast addr list. */ ipv6_link_dev_addr(idev, ifa); @@ -1490,6 +1503,9 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block) cfg.pfx = &addr; cfg.scope = ipv6_addr_scope(cfg.pfx); + cfg.ifpub = ifp; + cfg.cstamp = now; + cfg.tstamp = tmp_tstamp; ift = ipv6_add_addr(idev, &cfg, block, NULL); if (IS_ERR(ift)) { @@ -1500,12 +1516,6 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block) goto retry; } - spin_lock_bh(&ift->lock); - ift->ifpub = ifp; - ift->cstamp = now; - ift->tstamp = tmp_tstamp; - spin_unlock_bh(&ift->lock); - addrconf_dad_start(ift); in6_ifa_put(ift); in6_dev_put(idev); @@ -2191,6 +2201,7 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp) ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source); if (ifp->flags & IFA_F_STABLE_PRIVACY) { + bool start_dad = false; struct in6_addr new_addr; struct inet6_ifaddr *ifp2; int retries = ifp->stable_privacy_retry + 1; @@ -2229,11 +2240,14 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp) goto lock_errdad; spin_lock_bh(&ifp2->lock); - ifp2->stable_privacy_retry = retries; - ifp2->state = INET6_IFADDR_STATE_PREDAD; + if (ifp2->state != INET6_IFADDR_STATE_DEAD) { + ifp2->stable_privacy_retry = retries; + start_dad = true; + } spin_unlock_bh(&ifp2->lock); - addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay); + if (start_dad) + addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay); in6_ifa_put(ifp2); lock_errdad: spin_lock_bh(&ifp->lock); @@ -3900,7 +3914,9 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister) * Do not dev_put! */ if (unregister) { + write_lock_bh(&idev->lock); WRITE_ONCE(idev->dead, 1); + write_unlock_bh(&idev->lock); /* protected by rtnl_lock */ RCU_INIT_POINTER(dev->ip6_ptr, NULL); @@ -4022,6 +4038,10 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister) } if (!keep) { + spin_lock_bh(&net->ipv6.addrconf_hash_lock); + hlist_del_init_rcu(&ifa->addr_lst); + spin_unlock_bh(&net->ipv6.addrconf_hash_lock); + write_lock_bh(&idev->lock); list_del_rcu(&ifa->if_list); write_unlock_bh(&idev->lock); diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index 3017a456d289..e399a42e3083 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c @@ -1428,6 +1428,10 @@ static int calipso_skbuff_delattr(struct sk_buff *skb) skb_pull(skb, delta); memmove((char *)ip6_hdr + delta, ip6_hdr, size); skb_reset_network_header(skb); + ip6_hdr = ipv6_hdr(skb); + /* BIG TCP packets have payload_len == 0 */ + if (ip6_hdr->payload_len) + be16_add_cpu(&ip6_hdr->payload_len, -delta); } return 0; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 3d08240ea00c..b5d6ed8f0c36 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1889,6 +1889,7 @@ static int ip6erspan_tap_init(struct net_device *dev) dev->mtu -= 8; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + dev->lltx = true; ip6erspan_tnl_link_config(tunnel, 1); netdev_hold(dev, &tunnel->dev_tracker, GFP_KERNEL); diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 599c49bf0a0a..be72c4346f8b 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -398,7 +398,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) { u8 nexthdr = ipv6_hdr(skb)->nexthdr; const int netoff = skb_network_offset(skb); - u8 prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr); + int prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr); int start = netoff + sizeof(struct ipv6hdr); int len = skb->len - start; u8 prevhdr = NEXTHDR_IPV6; diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c index 07cdaa10da0d..5e08d8529cc8 100644 --- a/net/ipv6/netfilter/nf_reject_ipv6.c +++ b/net/ipv6/netfilter/nf_reject_ipv6.c @@ -444,6 +444,11 @@ void nf_send_unreach6(struct net *net, struct sk_buff *skb_in, if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL) skb_in->dev = net->loopback_dev; + if (hooknum == NF_INET_INGRESS) { + memset(IP6CB(skb_in), 0, sizeof(*IP6CB(skb_in))); + IP6CB(skb_in)->iif = skb_in->skb_iif; + } + if (!skb_valid_dst(skb_in) && nf_reject6_fill_skb_dst(skb_in) < 0) return; diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 05c51f669754..a7964125c764 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -232,6 +232,7 @@ static int ip6_frag_queue(struct net *net, } skb_dst_drop(skb); + skb_orphan(skb); return -EINPROGRESS; insert_error: diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 34ab7f330540..94e5ef1c4f83 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1511,6 +1511,15 @@ static struct rt6_info *rt6_make_pcpu_route(struct net *net, */ static DEFINE_SPINLOCK(rt6_exception_lock); +/* Exceptions are accounted to the netns their dst was allocated in. + * Unlike dev_net(dst->dev), it does not change when the device moves to + * another netns or when dst_dev_put() points the dst to blackhole_netdev. + */ +static struct net *rt6_exception_net(const struct rt6_info *rt) +{ + return container_of(rt->dst.ops, struct net, ipv6.ip6_dst_ops); +} + /* Remove rt6_ex from hash table and free the memory * Caller must hold rt6_exception_lock */ @@ -1522,7 +1531,7 @@ static void rt6_remove_exception(struct rt6_exception_bucket *bucket, if (!bucket || !rt6_ex) return; - net = dev_net(rt6_ex->rt6i->dst.dev); + net = rt6_exception_net(rt6_ex->rt6i); net->ipv6.rt6_stats->fib_rt_cache--; /* purge completely the exception to allow releasing the held resources: @@ -1750,7 +1759,7 @@ static int rt6_insert_exception(struct rt6_info *nrt, rt6_ex->stamp = jiffies; hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain); bucket->depth++; - net->ipv6.rt6_stats->fib_rt_cache++; + rt6_exception_net(nrt)->ipv6.rt6_stats->fib_rt_cache++; /* Randomize max depth to avoid some side channels attacks. */ max_depth = FIB6_MAX_DEPTH + get_random_u32_below(FIB6_MAX_DEPTH); @@ -6468,7 +6477,9 @@ void fib6_info_hw_flags_set(struct net *net, struct fib6_info *f6i, if (!fib_notify_on_flag_change) return; - skb = nlmsg_new(rt6_nlmsg_size(f6i), GFP_KERNEL); + rcu_read_lock(); + + skb = nlmsg_new(rt6_nlmsg_size(f6i), GFP_ATOMIC); if (!skb) { err = -ENOBUFS; goto errout; @@ -6481,10 +6492,13 @@ void fib6_info_hw_flags_set(struct net *net, struct fib6_info *f6i, goto errout; } + rcu_read_unlock(); + rtnl_notify(skb, net, 0, RTNLGRP_IPV6_ROUTE, NULL, GFP_KERNEL); return; errout: + rcu_read_unlock(); rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); } EXPORT_SYMBOL(fib6_info_hw_flags_set); diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 4712cc41881a..7e042a3e648c 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -915,11 +915,9 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, * check if we sre sending sequence numbers and if not, * configure it so. */ - if (!session->lns_mode && !session->send_seq) { + if (!session->lns_mode && !READ_ONCE(session->send_seq)) { trace_session_seqnum_lns_enable(session); - session->send_seq = 1; - l2tp_session_set_header_len(session, tunnel->version, - tunnel->encap); + WRITE_ONCE(session->send_seq, true); } } else { /* No sequence numbers. @@ -937,12 +935,10 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, * If we're the LNS and we're sending sequence numbers, the * LAC is broken. Discard the frame. */ - if (!session->lns_mode && session->send_seq) { + if (!session->lns_mode && READ_ONCE(session->send_seq)) { trace_session_seqnum_lns_disable(session); - session->send_seq = 0; - l2tp_session_set_header_len(session, tunnel->version, - tunnel->encap); - } else if (session->send_seq) { + WRITE_ONCE(session->send_seq, false); + } else if (READ_ONCE(session->send_seq)) { pr_debug_ratelimited("%s: recv data has no seq numbers when required. Discarding.\n", session->name); atomic_long_inc(&session->stats.rx_seq_discards); @@ -1138,7 +1134,8 @@ static void l2tp_udp_encap_err_recv(struct sock *sk, struct sk_buff *skb, int er /* Build an L2TP header for the session into the buffer provided. */ -static int l2tp_build_l2tpv2_header(struct l2tp_session *session, void *buf) +static int l2tp_build_l2tpv2_header(struct l2tp_session *session, void *buf, + bool send_seq) { struct l2tp_tunnel *tunnel = session->tunnel; __be16 *bufp = buf; @@ -1147,14 +1144,14 @@ static int l2tp_build_l2tpv2_header(struct l2tp_session *session, void *buf) u32 tunnel_id = tunnel->peer_tunnel_id; u32 session_id = session->peer_session_id; - if (session->send_seq) + if (send_seq) flags |= L2TP_HDRFLAG_S; /* Setup L2TP header. */ *bufp++ = htons(flags); *bufp++ = htons(tunnel_id); *bufp++ = htons(session_id); - if (session->send_seq) { + if (send_seq) { *bufp++ = htons(session->ns); *bufp++ = 0; session->ns++; @@ -1165,7 +1162,8 @@ static int l2tp_build_l2tpv2_header(struct l2tp_session *session, void *buf) return bufp - optr; } -static int l2tp_build_l2tpv3_header(struct l2tp_session *session, void *buf) +static int l2tp_build_l2tpv3_header(struct l2tp_session *session, void *buf, + bool send_seq) { struct l2tp_tunnel *tunnel = session->tunnel; char *bufp = buf; @@ -1191,7 +1189,7 @@ static int l2tp_build_l2tpv3_header(struct l2tp_session *session, void *buf) if (session->l2specific_type == L2TP_L2SPECTYPE_DEFAULT) { u32 l2h = 0; - if (session->send_seq) { + if (send_seq) { l2h = 0x40000000 | session->ns; session->ns++; session->ns &= 0xffffff; @@ -1225,19 +1223,24 @@ static int l2tp_xmit_queue(struct l2tp_tunnel *tunnel, struct sk_buff *skb, stru static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, unsigned int *len) { struct l2tp_tunnel *tunnel = session->tunnel; + bool send_seq = READ_ONCE(session->send_seq); unsigned int data_len = skb->len; struct sock *sk = tunnel->sock; + u16 hdr_len = session->hdr_len; int headroom, uhlen, udp_len; int ret = NET_XMIT_SUCCESS; struct inet_sock *inet; struct udphdr *uh; + if (tunnel->version == L2TP_HDR_VER_2 && !send_seq) + hdr_len -= 4; + /* Check that there's enough headroom in the skb to insert IP, * UDP and L2TP headers. If not enough, expand it to * make room. Adjust truesize. */ uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(*uh) : 0; - headroom = NET_SKB_PAD + sizeof(struct iphdr) + uhlen + session->hdr_len; + headroom = NET_SKB_PAD + sizeof(struct iphdr) + uhlen + hdr_len; if (skb_cow_head(skb, headroom)) { kfree_skb(skb); return NET_XMIT_DROP; @@ -1245,9 +1248,9 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, uns /* Setup L2TP header */ if (tunnel->version == L2TP_HDR_VER_2) - l2tp_build_l2tpv2_header(session, __skb_push(skb, session->hdr_len)); + l2tp_build_l2tpv2_header(session, __skb_push(skb, hdr_len), send_seq); else - l2tp_build_l2tpv3_header(session, __skb_push(skb, session->hdr_len)); + l2tp_build_l2tpv3_header(session, __skb_push(skb, hdr_len), send_seq); /* Reset control buffer */ memset(skb->cb, 0, sizeof(skb->cb)); @@ -1290,7 +1293,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, uns uh = udp_hdr(skb); uh->source = inet->inet_sport; uh->dest = inet->inet_dport; - udp_len = uhlen + session->hdr_len + data_len; + udp_len = uhlen + hdr_len + data_len; if (udp_len > U16_MAX) { kfree_skb(skb); ret = NET_XMIT_DROP; @@ -1759,16 +1762,11 @@ static void l2tp_session_del_work(struct work_struct *work) l2tp_session_put(session); } -/* We come here whenever a session's send_seq, cookie_len or - * l2specific_type parameters are set. - */ -void l2tp_session_set_header_len(struct l2tp_session *session, int version, - enum l2tp_encap_type encap) +static void l2tp_session_set_header_len(struct l2tp_session *session, int version, + enum l2tp_encap_type encap) { if (version == L2TP_HDR_VER_2) { - session->hdr_len = 6; - if (session->send_seq) - session->hdr_len += 4; + session->hdr_len = 10; } else { session->hdr_len = 4 + session->cookie_len; session->hdr_len += l2tp_get_l2specific_len(session); @@ -1776,7 +1774,6 @@ void l2tp_session_set_header_len(struct l2tp_session *session, int version, session->hdr_len += 4; } } -EXPORT_SYMBOL_GPL(l2tp_session_set_header_len); struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index ffd8ced3a51f..25ae4c25d3ef 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -76,7 +76,7 @@ struct l2tp_session { u8 peer_cookie[8]; int peer_cookie_len; u16 l2specific_type; - u16 hdr_len; + u16 hdr_len; /* maximum L2TP header length */ u32 nr; /* session NR state (receive) */ u32 ns; /* session NR state (send) */ struct sk_buff_head reorder_q; /* receive reorder queue */ @@ -94,8 +94,8 @@ struct l2tp_session { char name[L2TP_SESSION_NAME_MAX]; /* for logging */ char ifname[IFNAMSIZ]; + bool send_seq; /* send packets with sequence numbers? */ unsigned int recv_seq:1; /* expect receive packets with sequence numbers? */ - unsigned int send_seq:1; /* send packets with sequence numbers? */ unsigned int lns_mode:1; /* behave as LNS? * LAC enables sequence numbers under LNS control. */ @@ -257,8 +257,6 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb); /* Transmit path helpers for sending packets over the tunnel socket. */ -void l2tp_session_set_header_len(struct l2tp_session *session, int version, - enum l2tp_encap_type encap); int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb); /* Pseudowire management. diff --git a/net/l2tp/l2tp_debugfs.c b/net/l2tp/l2tp_debugfs.c index c12d5e1ee550..58d1d6a7768e 100644 --- a/net/l2tp/l2tp_debugfs.c +++ b/net/l2tp/l2tp_debugfs.c @@ -182,12 +182,12 @@ static void l2tp_dfs_seq_session_show(struct seq_file *m, void *v) session->pwtype == L2TP_PWTYPE_ETH ? "ETH" : session->pwtype == L2TP_PWTYPE_PPP ? "PPP" : ""); - if (session->send_seq || session->recv_seq) + if (READ_ONCE(session->send_seq) || session->recv_seq) seq_printf(m, " nr %u, ns %u\n", session->nr, session->ns); seq_printf(m, " refcnt %d\n", refcount_read(&session->ref_count)); seq_printf(m, " config 0/0/%c/%c/-/%s %08x %u\n", session->recv_seq ? 'R' : '-', - session->send_seq ? 'S' : '-', + READ_ONCE(session->send_seq) ? 'S' : '-', session->lns_mode ? "LNS" : "LAC", 0, jiffies_to_msecs(session->reorder_timeout)); diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index 38aac59d052c..89ef04c78787 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -700,12 +700,9 @@ static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *inf if (info->attrs[L2TP_ATTR_RECV_SEQ]) session->recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]); - if (info->attrs[L2TP_ATTR_SEND_SEQ]) { - struct l2tp_tunnel *tunnel = session->tunnel; - - session->send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]); - l2tp_session_set_header_len(session, tunnel->version, tunnel->encap); - } + if (info->attrs[L2TP_ATTR_SEND_SEQ]) + WRITE_ONCE(session->send_seq, + nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]) & 1); if (info->attrs[L2TP_ATTR_LNS_MODE]) session->lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]); @@ -748,7 +745,7 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int fl (session->peer_cookie_len && nla_put(skb, L2TP_ATTR_PEER_COOKIE, session->peer_cookie_len, session->peer_cookie)) || nla_put_u8(skb, L2TP_ATTR_RECV_SEQ, session->recv_seq) || - nla_put_u8(skb, L2TP_ATTR_SEND_SEQ, session->send_seq) || + nla_put_u8(skb, L2TP_ATTR_SEND_SEQ, READ_ONCE(session->send_seq)) || nla_put_u8(skb, L2TP_ATTR_LNS_MODE, session->lns_mode) || (l2tp_tunnel_uses_xfrm(tunnel) && nla_put_u8(skb, L2TP_ATTR_USING_IPSEC, 1)) || diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index ef8fdfaf051d..4f311d473a64 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1195,9 +1195,7 @@ static int pppol2tp_session_setsockopt(struct sock *sk, err = -EINVAL; break; } - session->send_seq = !!val; - l2tp_session_set_header_len(session, session->tunnel->version, - session->tunnel->encap); + WRITE_ONCE(session->send_seq, !!val); break; case PPPOL2TP_SO_LNSMODE: @@ -1307,7 +1305,7 @@ static int pppol2tp_session_getsockopt(struct sock *sk, break; case PPPOL2TP_SO_SENDSEQ: - *val = session->send_seq; + *val = READ_ONCE(session->send_seq); break; case PPPOL2TP_SO_LNSMODE: @@ -1546,7 +1544,7 @@ static void pppol2tp_seq_session_show(struct seq_file *m, void *v) state, user_data_ok); seq_printf(m, " 0/0/%c/%c/%s %08x %u\n", session->recv_seq ? 'R' : '-', - session->send_seq ? 'S' : '-', + READ_ONCE(session->send_seq) ? 'S' : '-', session->lns_mode ? "LNS" : "LAC", 0, jiffies_to_msecs(session->reorder_timeout)); diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 6ca5717ce009..7a22e9388e0e 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3705,6 +3705,24 @@ static void mptcp_copy_ip_options(struct sock *newsk, const struct sock *sk) rcu_read_unlock(); } +static void mptcp_sk_clone_destroy(struct sock *nsk) +{ + struct mptcp_sock *msk = mptcp_sk(nsk); + + mptcp_release_sched(msk); + mptcp_set_state(nsk, TCP_CLOSE); + /* inet_csk_prepare_forced_close() clears TCP sock_ops state via + * tcp_sk(), but nsk is an MPTCP master socket; keep the inet-level + * destroy preparation here. + */ + bh_unlock_sock(nsk); + sock_put(nsk); + sock_set_flag(nsk, SOCK_DEAD); + tcp_orphan_count_inc(); + inet_sk(nsk)->inet_num = 0; + inet_csk_destroy_sock(nsk); +} + struct sock *mptcp_sk_clone_init(const struct sock *sk, const struct mptcp_options_received *mp_opt, struct sock *ssk, @@ -3764,11 +3782,6 @@ struct sock *mptcp_sk_clone_init(const struct sock *sk, list_add(&subflow->node, &msk->conn_list); sock_hold(ssk); - /* new mpc subflow takes ownership of the newly - * created mptcp socket - */ - mptcp_token_accept(subflow_req, msk); - /* set msk addresses early to ensure mptcp_pm_get_local_id() * uses the correct data */ @@ -3777,6 +3790,14 @@ struct sock *mptcp_sk_clone_init(const struct sock *sk, mptcp_rcv_space_init(msk, ssk); msk->rcvq_space.time = mptcp_stamp(); + if (!mptcp_token_accept(subflow_req, msk)) { + list_del_init(&subflow->node); + WRITE_ONCE(msk->first, NULL); + sock_put(ssk); + mptcp_sk_clone_destroy(nsk); + return NULL; + } + if (mp_opt->suboptions & OPTION_MPTCP_MPC_ACK) __mptcp_subflow_fully_established(msk, subflow, mp_opt); bh_unlock_sock(nsk); @@ -4211,9 +4232,16 @@ static int mptcp_connect(struct sock *sk, struct sockaddr_unsized *uaddr, * subflow_finish_connect() */ if (unlikely(err)) { - /* avoid leaving a dangling token in an unconnected socket */ mptcp_token_destroy(msk); mptcp_set_state(sk, TCP_CLOSE); + + spin_lock_bh(&msk->fallback_lock); + msk->allow_subflows = true; + msk->allow_infinite_fallback = true; + clear_bit(MPTCP_FALLBACK_DONE, &msk->flags); + spin_unlock_bh(&msk->fallback_lock); + + mptcp_subflow_ctx_reset(mptcp_subflow_ctx(ssk)); return err; } diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 29b03275a59e..011e73d04ee6 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -597,14 +597,16 @@ struct mptcp_subflow_context { closing : 1, /* must not pass rx data to msk anymore */ valid_csum_seen : 1, /* at least one csum validated */ is_mptfo : 1, /* subflow is doing TFO */ - close_event_done : 1, /* has done the post-closed part */ mpc_drop : 1, /* the MPC option has been dropped in a rtx */ resetting : 1, /* subflow is resetting */ - __unused : 8; + __unused : 9; bool data_avail; bool scheduled; bool pm_listener; /* a listener managed by the kernel PM? */ bool fully_established; /* path validated */ + bool close_event_done; /* has done the post-closed part, + * protected by msk socket lock + */ u32 lent_mem_frag; u32 remote_nonce; u32 local_nonce; @@ -1094,9 +1096,11 @@ static inline void mptcp_token_init_request(struct request_sock *req) } int mptcp_token_new_request(struct request_sock *req); +void mptcp_token_move_request(struct request_sock *req, + struct request_sock *new_req); void mptcp_token_destroy_request(struct request_sock *req); int mptcp_token_new_connect(struct sock *ssk); -void mptcp_token_accept(struct mptcp_subflow_request_sock *r, +bool mptcp_token_accept(struct mptcp_subflow_request_sock *r, struct mptcp_sock *msk); bool mptcp_token_exists(u32 token); struct mptcp_sock *mptcp_token_get_sock(struct net *net, u32 token); diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 3d5f3f68a33a..78e886b20258 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -47,6 +47,41 @@ static void subflow_req_destructor(struct request_sock *req) mptcp_token_destroy_request(req); } +void mptcp_subflow_reqsk_clone(struct request_sock *req, + struct request_sock *new_req) +{ + struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req); + struct mptcp_subflow_request_sock *new_subflow_req; + struct mptcp_sock *msk; + + new_subflow_req = mptcp_subflow_rsk(new_req); + RCU_LOCKDEP_WARN(!rcu_read_lock_any_held(), + "MPTCP reqsk clone called without RCU"); + + /* A non-NULL ->msk means the request owns one reference. The clone + * copied only the pointer, while the original request can concurrently + * transfer its reference to the child. Acquire a reference for the + * clone, then verify that the original request still owns the same msk. + * MPTCP sockets use SLAB_TYPESAFE_BY_RCU and all clone callers run in + * an RCU read-side critical section, keeping the memory stable here. + */ + msk = READ_ONCE(subflow_req->msk); + if (msk) { + struct sock *sk = (struct sock *)msk; + + if (!refcount_inc_not_zero_acquire(&sk->sk_refcnt)) { + msk = NULL; + } else if (READ_ONCE(subflow_req->msk) != msk) { + sock_put(sk); + msk = NULL; + } + } + + new_subflow_req->msk = msk; + + mptcp_token_move_request(req, new_req); +} + static void subflow_generate_hmac(u64 key1, u64 key2, u32 nonce1, u32 nonce2, void *hmac) { @@ -889,8 +924,10 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk, if (ctx->mp_capable) { ctx->conn = mptcp_sk_clone_init(listener->conn, &mp_opt, child, req); - if (!ctx->conn) + if (!ctx->conn) { + fallback = true; goto fallback; + } ctx->subflow_id = 1; owner = mptcp_sk(ctx->conn); @@ -927,7 +964,7 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk, } /* move the msk reference ownership to the subflow */ - subflow_req->msk = NULL; + WRITE_ONCE(subflow_req->msk, NULL); ctx->conn = (struct sock *)owner; if (subflow_use_different_sport(owner, sk)) { diff --git a/net/mptcp/token.c b/net/mptcp/token.c index f1a50f367add..e0f2823f92ef 100644 --- a/net/mptcp/token.c +++ b/net/mptcp/token.c @@ -180,6 +180,43 @@ int mptcp_token_new_connect(struct sock *ssk) return 0; } +/** + * mptcp_token_move_request - move request token ownership to a clone + * @req: original request socket + * @new_req: cloned request socket + * + * Move the token hash entry from the original request to its clone. + */ +void mptcp_token_move_request(struct request_sock *req, + struct request_sock *new_req) +{ + struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req); + struct mptcp_subflow_request_sock *new_subflow_req; + struct mptcp_subflow_request_sock *pos; + struct token_bucket *bucket; + + new_subflow_req = mptcp_subflow_rsk(new_req); + + if (hlist_nulls_unhashed_lockless(&subflow_req->token_node)) { + mptcp_token_init_request(new_req); + return; + } + + bucket = token_bucket(subflow_req->token); + spin_lock_bh(&bucket->lock); + if (hlist_nulls_unhashed(&subflow_req->token_node)) { + mptcp_token_init_request(new_req); + } else { + pos = __token_lookup_req(bucket, subflow_req->token); + if (pos == subflow_req) + hlist_nulls_replace_init_rcu(&subflow_req->token_node, + &new_subflow_req->token_node); + else + mptcp_token_init_request(new_req); + } + spin_unlock_bh(&bucket->lock); +} + /** * mptcp_token_accept - replace a req sk with full sock in token hash * @req: the request socket to be removed @@ -187,24 +224,36 @@ int mptcp_token_new_connect(struct sock *ssk) * * Called when a SYN packet creates a new logical connection, i.e. * is not a join request. + * + * Return: true on success. */ -void mptcp_token_accept(struct mptcp_subflow_request_sock *req, +bool mptcp_token_accept(struct mptcp_subflow_request_sock *req, struct mptcp_sock *msk) { struct mptcp_subflow_request_sock *pos; struct sock *sk = (struct sock *)msk; struct token_bucket *bucket; + bool ret = false; - sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); bucket = token_bucket(req->token); spin_lock_bh(&bucket->lock); + if (hlist_nulls_unhashed(&req->token_node)) + goto unlock; - /* pedantic lookup check for the moved token */ pos = __token_lookup_req(bucket, req->token); - if (!WARN_ON_ONCE(pos != req)) - hlist_nulls_del_init_rcu(&req->token_node); + if (pos != req) + goto unlock; + + hlist_nulls_del_init_rcu(&req->token_node); __sk_nulls_add_node_rcu((struct sock *)msk, &bucket->msk_chain); + ret = true; + +unlock: spin_unlock_bh(&bucket->lock); + if (ret) + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); + + return ret; } bool mptcp_token_exists(u32 token) @@ -355,16 +404,21 @@ void mptcp_token_destroy_request(struct request_sock *req) struct mptcp_subflow_request_sock *pos; struct token_bucket *bucket; - if (hlist_nulls_unhashed(&subflow_req->token_node)) + if (hlist_nulls_unhashed_lockless(&subflow_req->token_node)) return; bucket = token_bucket(subflow_req->token); spin_lock_bh(&bucket->lock); + if (hlist_nulls_unhashed(&subflow_req->token_node)) + goto unlock; + pos = __token_lookup_req(bucket, subflow_req->token); - if (!WARN_ON_ONCE(pos != subflow_req)) { + if (pos == subflow_req) { hlist_nulls_del_init_rcu(&pos->token_node); bucket->chain_len--; } + +unlock: spin_unlock_bh(&bucket->lock); } diff --git a/net/mptcp/token_test.c b/net/mptcp/token_test.c index 4fc39fa2e262..be9acce8a567 100644 --- a/net/mptcp/token_test.c +++ b/net/mptcp/token_test.c @@ -99,7 +99,7 @@ static void mptcp_token_test_accept(struct kunit *test) KUNIT_ASSERT_EQ(test, 0, mptcp_token_new_request((struct request_sock *)req)); msk->token = req->token; - mptcp_token_accept(req, msk); + KUNIT_EXPECT_TRUE(test, mptcp_token_accept(req, msk)); KUNIT_EXPECT_PTR_EQ(test, msk, mptcp_token_get_sock(&init_net, msk->token)); /* this is now a no-op */ @@ -122,7 +122,7 @@ static void mptcp_token_test_destroyed(struct kunit *test) KUNIT_ASSERT_EQ(test, 0, mptcp_token_new_request((struct request_sock *)req)); msk->token = req->token; - mptcp_token_accept(req, msk); + KUNIT_EXPECT_TRUE(test, mptcp_token_accept(req, msk)); /* simulate race on removal */ refcount_set(&sk->sk_refcnt, 0); diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index cb009208826f..94f25fb7d87e 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1153,8 +1153,8 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) if (!(flags & IP_VS_CONN_F_INACTIVE)) atomic_inc(&dest->activeconns); tc = atomic_inc_return(&dest->totalconns); - if (tc == READ_ONCE(dest->u_threshold)) - ip_vs_dest_update_overload(dest, 1); + if (tc >= READ_ONCE(dest->u_threshold_val)) + ip_vs_dest_update_overload(dest, true); } else { /* It is a persistent connection/template, so increase the persistent connection counter */ @@ -1246,8 +1246,8 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp) if (!(cp->flags & IP_VS_CONN_F_INACTIVE)) atomic_dec(&dest->activeconns); tc = atomic_fetch_dec(&dest->totalconns); - if (tc == READ_ONCE(dest->l_threshold_val)) - ip_vs_dest_update_overload(dest, -1); + if (tc <= READ_ONCE(dest->l_threshold_val)) + ip_vs_dest_update_overload(dest, false); } else { /* It is a persistent connection/template, so decrease the persistent connection counter */ diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 4c1c739446b7..ce9fe20d6106 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1309,32 +1309,32 @@ void ip_vs_stats_free(struct ip_vs_stats *stats) * - conns reach u_threshold and exceed it: set the flag * - conns go below l_threshold (or 75% of u_threshold): clear the flag */ -static void __ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode) +static void __ip_vs_dest_update_overload(struct ip_vs_dest *dest, bool overload) { int conns; u32 l, u; lockdep_assert_held(&dest->dst_lock); u = READ_ONCE(dest->u_threshold); - if (!u) - goto unset; - l = READ_ONCE(dest->l_threshold_val); - conns = atomic_read(&dest->totalconns); - if (conns >= (mode > 0 ? l : u)) { - dest->flags |= IP_VS_DEST_F_OVERLOAD; - return; + if (u) { + /* Low threshold defaults to 75% of upper threshold */ + l = READ_ONCE(dest->l_threshold) ? : (u - (u >> 2)); + conns = atomic_read(&dest->totalconns); + if (conns >= (overload ? l : u)) { + WRITE_ONCE(dest->u_threshold_val, INT_MAX); + WRITE_ONCE(dest->l_threshold_val, l); + return; + } } - if (conns >= (mode < 0 ? u : l)) - return; -unset: - dest->flags &= ~IP_VS_DEST_F_OVERLOAD; + WRITE_ONCE(dest->u_threshold_val, u ? : INT_MAX); + WRITE_ONCE(dest->l_threshold_val, 0); } -void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode) +void ip_vs_dest_update_overload(struct ip_vs_dest *dest, bool overload) { spin_lock_bh(&dest->dst_lock); - __ip_vs_dest_update_overload(dest, mode); + __ip_vs_dest_update_overload(dest, overload); spin_unlock_bh(&dest->dst_lock); } @@ -1406,15 +1406,13 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, if (READ_ONCE(dest->u_threshold) != udest->u_threshold || READ_ONCE(dest->l_threshold) != udest->l_threshold) { + bool overload; + spin_lock_bh(&dest->dst_lock); WRITE_ONCE(dest->u_threshold, udest->u_threshold); WRITE_ONCE(dest->l_threshold, udest->l_threshold); - /* Low threshold defaults to 75% of upper threshold */ - WRITE_ONCE(dest->l_threshold_val, - udest->l_threshold ? : - (udest->u_threshold - - (udest->u_threshold >> 2))); - __ip_vs_dest_update_overload(dest, 0); + overload = ip_vs_dest_is_overloaded(dest); + __ip_vs_dest_update_overload(dest, overload); spin_unlock_bh(&dest->dst_lock); } @@ -2454,6 +2452,8 @@ proc_do_sync_threshold(const struct ctl_table *table, int write, mutex_lock(&ipvs->sync_mutex); memcpy(val, valp, sizeof(val)); rc = proc_dointvec(&tmp, write, buffer, lenp, ppos); + if (rc) + goto out; if (write) { if (val[0] < 0 || val[1] < 0 || (val[0] >= val[1] && val[1])) @@ -2461,6 +2461,7 @@ proc_do_sync_threshold(const struct ctl_table *table, int write, else memcpy(valp, val, sizeof(val)); } +out: mutex_unlock(&ipvs->sync_mutex); return rc; } diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c index 43abed7a26a6..95e52950adbb 100644 --- a/net/netfilter/ipvs/ip_vs_dh.c +++ b/net/netfilter/ipvs/ip_vs_dh.c @@ -195,16 +195,6 @@ static int ip_vs_dh_dest_changed(struct ip_vs_service *svc, } -/* - * If the dest flags is set with IP_VS_DEST_F_OVERLOAD, - * consider that the server is overloaded here. - */ -static inline int is_overloaded(struct ip_vs_dest *dest) -{ - return dest->flags & IP_VS_DEST_F_OVERLOAD; -} - - /* * Destination hashing scheduling */ @@ -219,10 +209,8 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, s = (struct ip_vs_dh_state *) svc->sched_data; dest = ip_vs_dh_get(svc->af, s, &iph->daddr); - if (!dest || - !(dest->cflags & IP_VS_DEST_CF_AVAILABLE) - || atomic_read(&dest->weight) <= 0 - || is_overloaded(dest)) { + if (!dest || atomic_read(&dest->weight) <= 0 || + ip_vs_dest_is_overloaded(dest)) { ip_vs_scheduler_err(svc, "no destination available"); return NULL; } diff --git a/net/netfilter/ipvs/ip_vs_fo.c b/net/netfilter/ipvs/ip_vs_fo.c index d657b47c6511..e07fa33f6d52 100644 --- a/net/netfilter/ipvs/ip_vs_fo.c +++ b/net/netfilter/ipvs/ip_vs_fo.c @@ -29,7 +29,7 @@ ip_vs_fo_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, * Find virtual server with highest weight and send it traffic */ list_for_each_entry_rcu(dest, &svc->destinations, n_list) { - if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) && + if (!ip_vs_dest_is_overloaded(dest) && atomic_read(&dest->weight) > hw) { hweight = dest; hw = atomic_read(&dest->weight); diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 55af77a56929..e1111c3b2721 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -418,7 +418,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc) * new connection. */ list_for_each_entry_rcu(dest, &svc->destinations, n_list) { - if (dest->flags & IP_VS_DEST_F_OVERLOAD) + if (ip_vs_dest_is_overloaded(dest)) continue; if (atomic_read(&dest->weight) > 0) { least = dest; @@ -433,7 +433,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc) */ nextstage: list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) { - if (dest->flags & IP_VS_DEST_F_OVERLOAD) + if (ip_vs_dest_is_overloaded(dest)) continue; doh = ip_vs_dest_conn_overhead(dest); diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 858393b1d2d1..dfb5bf83750d 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -166,7 +166,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) /* select the first destination server, whose weight > 0 */ list_for_each_entry_rcu(e, &set->list, list) { least = e->dest; - if (least->flags & IP_VS_DEST_F_OVERLOAD) + if (ip_vs_dest_is_overloaded(least)) continue; if ((atomic_read(&least->weight) > 0) && @@ -181,7 +181,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) nextstage: list_for_each_entry_continue_rcu(e, &set->list, list) { dest = e->dest; - if (dest->flags & IP_VS_DEST_F_OVERLOAD) + if (ip_vs_dest_is_overloaded(dest)) continue; doh = ip_vs_dest_conn_overhead(dest); @@ -580,7 +580,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc) * new connection. */ list_for_each_entry_rcu(dest, &svc->destinations, n_list) { - if (dest->flags & IP_VS_DEST_F_OVERLOAD) + if (ip_vs_dest_is_overloaded(dest)) continue; if (atomic_read(&dest->weight) > 0) { @@ -596,7 +596,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc) */ nextstage: list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) { - if (dest->flags & IP_VS_DEST_F_OVERLOAD) + if (ip_vs_dest_is_overloaded(dest)) continue; doh = ip_vs_dest_conn_overhead(dest); diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c index 18b976a7c4d2..9002f491bff6 100644 --- a/net/netfilter/ipvs/ip_vs_lc.c +++ b/net/netfilter/ipvs/ip_vs_lc.c @@ -38,7 +38,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, */ list_for_each_entry_rcu(dest, &svc->destinations, n_list) { - if ((dest->flags & IP_VS_DEST_F_OVERLOAD) || + if (ip_vs_dest_is_overloaded(dest) || atomic_read(&dest->weight) == 0) continue; doh = ip_vs_dest_conn_overhead(dest); diff --git a/net/netfilter/ipvs/ip_vs_mh.c b/net/netfilter/ipvs/ip_vs_mh.c index 020863047562..d70b23aec5fe 100644 --- a/net/netfilter/ipvs/ip_vs_mh.c +++ b/net/netfilter/ipvs/ip_vs_mh.c @@ -80,7 +80,7 @@ static inline void generate_hash_secret(hsiphash_key_t *hash1, static inline bool is_unavailable(struct ip_vs_dest *dest) { return atomic_read(&dest->weight) <= 0 || - dest->flags & IP_VS_DEST_F_OVERLOAD; + ip_vs_dest_is_overloaded(dest); } /* Returns hash value for IPVS MH entry */ diff --git a/net/netfilter/ipvs/ip_vs_nq.c b/net/netfilter/ipvs/ip_vs_nq.c index ada158c610ce..7cc28902086e 100644 --- a/net/netfilter/ipvs/ip_vs_nq.c +++ b/net/netfilter/ipvs/ip_vs_nq.c @@ -72,7 +72,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, list_for_each_entry_rcu(dest, &svc->destinations, n_list) { - if (dest->flags & IP_VS_DEST_F_OVERLOAD || + if (ip_vs_dest_is_overloaded(dest) || !atomic_read(&dest->weight)) continue; diff --git a/net/netfilter/ipvs/ip_vs_ovf.c b/net/netfilter/ipvs/ip_vs_ovf.c index c5c67df80a0b..eaa3a7ae6efa 100644 --- a/net/netfilter/ipvs/ip_vs_ovf.c +++ b/net/netfilter/ipvs/ip_vs_ovf.c @@ -33,7 +33,7 @@ ip_vs_ovf_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, */ list_for_each_entry_rcu(dest, &svc->destinations, n_list) { w = atomic_read(&dest->weight); - if ((dest->flags & IP_VS_DEST_F_OVERLOAD) || + if (ip_vs_dest_is_overloaded(dest) || atomic_read(&dest->activeconns) > w || w == 0) continue; diff --git a/net/netfilter/ipvs/ip_vs_rr.c b/net/netfilter/ipvs/ip_vs_rr.c index 4125ee561cdc..bd17cdd092f5 100644 --- a/net/netfilter/ipvs/ip_vs_rr.c +++ b/net/netfilter/ipvs/ip_vs_rr.c @@ -66,7 +66,7 @@ ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) { - if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) && + if (!ip_vs_dest_is_overloaded(dest) && atomic_read(&dest->weight) > 0) /* HIT */ goto out; diff --git a/net/netfilter/ipvs/ip_vs_sed.c b/net/netfilter/ipvs/ip_vs_sed.c index 245a323c84cd..7925f4d28fce 100644 --- a/net/netfilter/ipvs/ip_vs_sed.c +++ b/net/netfilter/ipvs/ip_vs_sed.c @@ -75,7 +75,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, */ list_for_each_entry_rcu(dest, &svc->destinations, n_list) { - if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) && + if (!ip_vs_dest_is_overloaded(dest) && atomic_read(&dest->weight) > 0) { least = dest; loh = ip_vs_sed_dest_overhead(least); @@ -90,7 +90,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, */ nextstage: list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) { - if (dest->flags & IP_VS_DEST_F_OVERLOAD) + if (ip_vs_dest_is_overloaded(dest)) continue; doh = ip_vs_sed_dest_overhead(dest); if ((__s64)loh * atomic_read(&dest->weight) > diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c index cd67066e3b26..c24dd5cb7e47 100644 --- a/net/netfilter/ipvs/ip_vs_sh.c +++ b/net/netfilter/ipvs/ip_vs_sh.c @@ -73,7 +73,7 @@ struct ip_vs_sh_state { static inline bool is_unavailable(struct ip_vs_dest *dest) { return atomic_read(&dest->weight) <= 0 || - dest->flags & IP_VS_DEST_F_OVERLOAD; + ip_vs_dest_is_overloaded(dest); } /* diff --git a/net/netfilter/ipvs/ip_vs_twos.c b/net/netfilter/ipvs/ip_vs_twos.c index dbb7f5fd4688..86186122a1f0 100644 --- a/net/netfilter/ipvs/ip_vs_twos.c +++ b/net/netfilter/ipvs/ip_vs_twos.c @@ -52,7 +52,7 @@ static struct ip_vs_dest *ip_vs_twos_schedule(struct ip_vs_service *svc, /* Generate a random weight between [0,sum of all weights) */ list_for_each_entry_rcu(dest, &svc->destinations, n_list) { - if (!(dest->flags & IP_VS_DEST_F_OVERLOAD)) { + if (!ip_vs_dest_is_overloaded(dest)) { weight = atomic_read(&dest->weight); if (weight > 0) { total_weight += weight; @@ -75,7 +75,7 @@ static struct ip_vs_dest *ip_vs_twos_schedule(struct ip_vs_service *svc, /* Pick two weighted servers */ list_for_each_entry_rcu(dest, &svc->destinations, n_list) { - if (dest->flags & IP_VS_DEST_F_OVERLOAD) + if (ip_vs_dest_is_overloaded(dest)) continue; weight = atomic_read(&dest->weight); diff --git a/net/netfilter/ipvs/ip_vs_wlc.c b/net/netfilter/ipvs/ip_vs_wlc.c index 9da445ca09a1..08e9e22f72bf 100644 --- a/net/netfilter/ipvs/ip_vs_wlc.c +++ b/net/netfilter/ipvs/ip_vs_wlc.c @@ -47,7 +47,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, */ list_for_each_entry_rcu(dest, &svc->destinations, n_list) { - if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) && + if (!ip_vs_dest_is_overloaded(dest) && atomic_read(&dest->weight) > 0) { least = dest; loh = ip_vs_dest_conn_overhead(least); @@ -62,7 +62,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, */ nextstage: list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) { - if (dest->flags & IP_VS_DEST_F_OVERLOAD) + if (ip_vs_dest_is_overloaded(dest)) continue; doh = ip_vs_dest_conn_overhead(dest); if ((__s64)loh * atomic_read(&dest->weight) > diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c index 2dcff1040da5..fad69bae5a22 100644 --- a/net/netfilter/ipvs/ip_vs_wrr.c +++ b/net/netfilter/ipvs/ip_vs_wrr.c @@ -176,7 +176,7 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) { - if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) && + if (!ip_vs_dest_is_overloaded(dest) && atomic_read(&dest->weight) >= mark->cw) goto found; if (dest == stop) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 4b6abe36028e..1f8d94a05240 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1109,13 +1109,13 @@ static int ctnetlink_filter_match_tuple(struct nf_conntrack_tuple *filter_tuple, break; case NFPROTO_IPV6: if ((flags & CTA_FILTER_FLAG(CTA_IP_SRC)) && - !ipv6_addr_equal(&filter_tuple->src.u3.in6, - &ct_tuple->src.u3.in6)) + ipv6_addr_cmp(&filter_tuple->src.u3.in6, + &ct_tuple->src.u3.in6)) return 0; if ((flags & CTA_FILTER_FLAG(CTA_IP_DST)) && - !ipv6_addr_equal(&filter_tuple->dst.u3.in6, - &ct_tuple->dst.u3.in6)) + ipv6_addr_cmp(&filter_tuple->dst.u3.in6, + &ct_tuple->dst.u3.in6)) return 0; break; } diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index e13f9397e8cb..19cafae748ab 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -170,14 +170,14 @@ static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx, int i = 0; if (skb_vlan_tag_present(skb)) { - tuple->encap[i].id = skb_vlan_tag_get(skb); + tuple->encap[i].id = skb_vlan_tag_get_id(skb); tuple->encap[i].proto = skb->vlan_proto; i++; } switch (skb->protocol) { case htons(ETH_P_8021Q): veth = (struct vlan_ethhdr *)skb_mac_header(skb); - tuple->encap[i].id = ntohs(veth->h_vlan_TCI); + tuple->encap[i].id = ntohs(veth->h_vlan_TCI) & VLAN_VID_MASK; tuple->encap[i].proto = skb->protocol; offset += VLAN_HLEN; break; @@ -509,6 +509,7 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx, nf_flow_encap_pop(ctx, skb, tuplehash); thoff -= ctx->offset; + skb_set_transport_header(skb, thoff); iph = ip_hdr(skb); nf_flow_nat_ip(flow, skb, thoff, dir, iph); @@ -1136,6 +1137,7 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx, flow_offload_refresh(flow_table, flow, false); nf_flow_encap_pop(ctx, skb, tuplehash); + skb_set_transport_header(skb, sizeof(*ip6h)); ip6h = ipv6_hdr(skb); nf_flow_nat_ipv6(flow, skb, dir, ip6h); diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index e315d35f73d4..5b76af68b704 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1008,11 +1009,14 @@ static bool nft_payload_validate_inet_csum_offset(const struct nft_ctx *ctx, if (priv->csum_flags) /* makes no sense, asks for "re-update" of L4 checksum */ return false; - /* no further check here; offset can't be negative so bogus - * offsets can corrupt L4 or payload but not l3 headers. - * We already allow arbitrary l4/inner payload writes. - */ - return true; + /* Validate csum_offset is one of the supported transport header checksums */ + if (priv->csum_offset == offsetof(struct tcphdr, check) || + priv->csum_offset == offsetof(struct udphdr, check) || + priv->csum_offset == offsetof(struct icmphdr, checksum) || + priv->csum_offset == offsetof(struct icmp6hdr, icmp6_cksum)) + return true; + + return false; case NFT_PAYLOAD_INNER_HEADER: return true; case NFT_PAYLOAD_TUN_HEADER: @@ -1046,6 +1050,27 @@ static bool nft_payload_csum_nh_write_ok(const struct nft_payload_set *priv, return false; } +static bool nft_payload_csum_th_write_ok(const struct nft_payload_set *priv, + const struct nft_pktinfo *pkt) +{ + if (!(pkt->flags & NFT_PKTINFO_L4PROTO)) + return false; + + switch (pkt->tprot) { + case IPPROTO_TCP: + return priv->csum_offset == offsetof(struct tcphdr, check); + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + return priv->csum_offset == offsetof(struct udphdr, check); + case IPPROTO_ICMPV6: + return priv->csum_offset == offsetof(struct icmp6hdr, icmp6_cksum); + case IPPROTO_ICMP: + return priv->csum_offset == offsetof(struct icmphdr, checksum); + } + + return false; +} + static bool nft_payload_csum_write_ok(const struct nft_pktinfo *pkt, const struct nft_payload_set *priv) { @@ -1055,9 +1080,10 @@ static bool nft_payload_csum_write_ok(const struct nft_pktinfo *pkt, case NFT_PAYLOAD_NETWORK_HEADER: return nft_payload_csum_nh_write_ok(priv, pkt); case NFT_PAYLOAD_TRANSPORT_HEADER: + return nft_payload_csum_th_write_ok(priv, pkt); case NFT_PAYLOAD_INNER_HEADER: - /* neither offsets are validated, offsets cannot be - * negative so real l3 headers cannot be mangled. + /* offset is not validated, offset cannot be + * negative so real l3/l4 headers cannot be mangled. */ return true; case NFT_PAYLOAD_TUN_HEADER: diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 12431b55752f..fbe1750f1ca2 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -1091,7 +1091,8 @@ static void nft_rbtree_commit(struct nft_set *set) * representation. Use next start element to complete this * interval. */ - if (nft_rbtree_interval_start(rbe) && + if (nft_set_is_anonymous(set) && + nft_rbtree_interval_start(rbe) && nft_rbtree_interval_start(prev_rbe) && priv->array_next->intervals[num_intervals].from) priv->array_next->intervals[num_intervals++].to = &prev_rbe->ext; diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c index 3db81e041af9..23a4e3e8df7d 100644 --- a/net/netfilter/xt_dccp.c +++ b/net/netfilter/xt_dccp.c @@ -62,7 +62,7 @@ dccp_find_option(u_int8_t option, return true; } - if (op[i] < 2 || i == optlen - 1) + if (op[i] <= DCCPO_MAX_RESERVED || i == optlen - 1) i++; else i += op[i + 1] ? : 1; diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c index bd6cb3ffaa57..aa50b85b0f99 100644 --- a/net/rds/ib_recv.c +++ b/net/rds/ib_recv.c @@ -949,6 +949,12 @@ static void rds_ib_process_recv(struct rds_connection *conn, } } + if (data_len < min_t(u32, ic->i_recv_data_rem, RDS_FRAG_SIZE)) { + rds_ib_conn_error(conn, + "fragment shorter than header-declared length; forcing reconnect\n"); + goto done; + } + list_add_tail(&recv->r_frag->f_item, &ibinc->ii_frags); recv->r_frag = NULL; @@ -1004,7 +1010,12 @@ void rds_ib_recv_cqe_handler(struct rds_ib_connection *ic, * event is processed. */ if (wc->status == IB_WC_SUCCESS) { - rds_ib_process_recv(conn, recv, wc->byte_len, state); + if (wc->opcode != IB_WC_RECV) + rds_ib_conn_error(conn, + "recv completion had unexpected opcode %u, disconnecting and reconnecting\n", + wc->opcode); + else + rds_ib_process_recv(conn, recv, wc->byte_len, state); } else { /* We expect errors as the qp is drained during shutdown */ if (rds_conn_up(conn) || rds_conn_connecting(conn)) diff --git a/net/rds/recv.c b/net/rds/recv.c index 6204e577a90a..93ee4546ae36 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -379,7 +379,8 @@ void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr, rds_stats_inc(s_recv_ping); rds_send_pong(cp, inc->i_hdr.h_sport); /* if this is a handshake ping, start multipath if necessary */ - if (RDS_HS_PROBE(be16_to_cpu(inc->i_hdr.h_sport), + if (conn->c_trans->t_mp_capable && + RDS_HS_PROBE(be16_to_cpu(inc->i_hdr.h_sport), be16_to_cpu(inc->i_hdr.h_dport))) { rds_recv_hs_exthdrs(&inc->i_hdr, cp->cp_conn); rds_start_mprds(cp->cp_conn); @@ -387,7 +388,8 @@ void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr, goto out; } - if (be16_to_cpu(inc->i_hdr.h_dport) == RDS_FLAG_PROBE_PORT && + if (conn->c_trans->t_mp_capable && + be16_to_cpu(inc->i_hdr.h_dport) == RDS_FLAG_PROBE_PORT && inc->i_hdr.h_sport == 0) { rds_recv_hs_exthdrs(&inc->i_hdr, cp->cp_conn); /* if this is a handshake pong, start multipath if necessary */ diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 3295f4cf3c19..841e4584f0fe 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -2069,7 +2069,7 @@ static int tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, u32 portid, int event, struct netlink_ext_ack *extack) { - int i, ret; + int i, j, ret; struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; struct tc_action *act; size_t attr_size = 0; @@ -2094,6 +2094,25 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, ret = PTR_ERR(act); goto err; } + + /* A delete consumes two references per slot (tcf_action_put() + * and tcf_idr_delete_index()) but each entry takes one, so a + * repeated action would hit zero mid-walk. GET balances its + * own references and keeps accepting duplicates. + */ + if (event == RTM_DELACTION) { + for (j = 0; j < i - 1; j++) { + if (actions[j] != act) + continue; + + tcf_action_put(act); + NL_SET_ERR_MSG(extack, + "Duplicate TC action in a delete batch"); + ret = -EINVAL; + goto err; + } + } + attr_size += tcf_action_fill_size(act); actions[i - 1] = act; } diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 232796d3a51f..3aa9978bd050 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -392,11 +392,31 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog, if (bpf_prog_is_dev_bound(fp->aux)) { struct tcf_block *block = tp->chain->block; struct net_device *dev; + unsigned long ifindex; + bool match = false; - dev = block->q ? qdisc_dev(block->q) : NULL; - if (!dev || !bpf_offload_dev_match(fp, dev)) { - NL_SET_ERR_MSG(extack, - "Program is bound to a different device"); + /* A shared block has no qdisc of its own, but it may bind + * several netdevs and the program is offloaded to all of them; + * require a match with every member. + */ + if (!tcf_block_shared(block)) { + dev = qdisc_dev(tcf_block_q(block)); + match = bpf_offload_dev_match(fp, dev); + if (!match) + NL_SET_ERR_MSG(extack, + "Program is bound to a different device"); + } else { + xa_for_each(&block->ports, ifindex, dev) { + match = bpf_offload_dev_match(fp, dev); + if (!match) + break; + } + if (!match) + NL_SET_ERR_MSG(extack, + "Program is not bound to every device in the block"); + } + + if (!match) { bpf_prog_put(fp); return -EINVAL; } diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 299b1493d74b..23eba4a00a94 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -518,15 +518,22 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f, tcf_exts_hw_stats_update(&f->exts, &cls_flower.stats, cls_flower.use_act_stats); } -static void __fl_put(struct cls_fl_filter *f) +static bool __fl_put(struct tcf_proto *tp, struct cls_fl_filter *f) { + bool last = false; + if (!refcount_dec_and_test(&f->refcnt)) - return; + return false; + + if (f->mask) + last = fl_mask_put(fl_head_dereference(tp), f->mask); if (tcf_exts_get_net(&f->exts)) tcf_queue_work(&f->rwork, fl_destroy_filter_work); else __fl_destroy_filter(f); + + return last; } static struct cls_fl_filter *__fl_get(struct cls_fl_head *head, u32 handle) @@ -572,11 +579,10 @@ static int __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f, list_del_rcu(&f->list); spin_unlock(&tp->lock); - *last = fl_mask_put(head, f->mask); if (!tc_skip_hw(f->flags)) fl_hw_destroy_filter(tp, f, rtnl_held, extack); tcf_unbind_filter(tp, &f->res); - __fl_put(f); + *last = __fl_put(tp, f); return 0; } @@ -617,7 +623,7 @@ static void fl_put(struct tcf_proto *tp, void *arg) { struct cls_fl_filter *f = arg; - __fl_put(f); + __fl_put(tp, f); } static void *fl_get(struct tcf_proto *tp, u32 handle) @@ -2517,12 +2523,14 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, } err = fl_check_assign_mask(head, fnew, fold, mask); - if (err) + if (err) { + fnew->mask = NULL; goto unbind_filter; + } err = fl_ht_insert_unique(fnew, fold, &in_ht); if (err) - goto errout_mask; + goto unbind_filter; if (!tc_skip_hw(fnew->flags)) { err = fl_hw_replace_filter(tp, fnew, rtnl_held, extack); @@ -2576,7 +2584,6 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, spin_unlock(&tp->lock); - fl_mask_put(head, fold->mask); if (!tc_skip_hw(fold->flags)) fl_hw_destroy_filter(tp, fold, rtnl_held, NULL); tcf_unbind_filter(tp, &fold->res); @@ -2584,7 +2591,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, * after this. */ refcount_dec(&fold->refcnt); - __fl_put(fold); + __fl_put(tp, fold); } else { idr_replace(&head->handle_idr, fnew, fnew->handle); @@ -2609,9 +2616,6 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, if (in_ht) rhashtable_remove_fast(&fnew->mask->ht, &fnew->ht_node, fnew->mask->filter_ht_params); -errout_mask: - fl_mask_put(head, fnew->mask); - unbind_filter: if (bound_to_filter) { if (flags & TCA_ACT_FLAGS_NO_RTNL) @@ -2627,14 +2631,14 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, idr_remove(&head->handle_idr, fnew->handle); spin_unlock(&tp->lock); } - __fl_put(fnew); + __fl_put(tp, fnew); errout_tb: kfree(tb); errout_mask_alloc: tcf_queue_work(&mask->rwork, fl_uninit_mask_free_work); errout_fold: if (fold) - __fl_put(fold); + __fl_put(tp, fold); return err; } @@ -2647,8 +2651,10 @@ static int fl_delete(struct tcf_proto *tp, void *arg, bool *last, int err = 0; err = __fl_delete(tp, f, &last_on_mask, rtnl_held, extack); - *last = list_empty(&head->masks); - __fl_put(f); + spin_lock(&tp->lock); + *last = idr_is_empty(&head->handle_idr); + spin_unlock(&tp->lock); + __fl_put(tp, f); return err; } @@ -2670,12 +2676,12 @@ static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg, rcu_read_unlock(); if (arg->fn(tp, f, arg) < 0) { - __fl_put(f); + __fl_put(tp, f); arg->stop = 1; rcu_read_lock(); break; } - __fl_put(f); + __fl_put(tp, f); arg->count++; rcu_read_lock(); } @@ -2726,7 +2732,7 @@ static int fl_reoffload(struct tcf_proto *tp, bool add, flow_setup_cb_t *cb, cls_flower.rule = flow_rule_alloc(tcf_exts_num_actions(&f->exts)); if (!cls_flower.rule) { - __fl_put(f); + __fl_put(tp, f); return -ENOMEM; } @@ -2744,7 +2750,7 @@ static int fl_reoffload(struct tcf_proto *tp, bool add, flow_setup_cb_t *cb, if (err) { kfree(cls_flower.rule); if (tc_skip_sw(f->flags)) { - __fl_put(f); + __fl_put(tp, f); return err; } goto next_flow; @@ -2760,11 +2766,11 @@ static int fl_reoffload(struct tcf_proto *tp, bool add, flow_setup_cb_t *cb, kfree(cls_flower.rule); if (err) { - __fl_put(f); + __fl_put(tp, f); return err; } next_flow: - __fl_put(f); + __fl_put(tp, f); } return 0; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 84a4c97d0f75..ddc31a5dbad3 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -94,7 +94,7 @@ static void sctp_control_release_owner(struct sk_buff *skb) } } -static void sctp_control_set_owner_w(struct sctp_chunk *chunk) +void sctp_control_set_owner_w(struct sctp_chunk *chunk) { struct sctp_association *asoc = chunk->asoc; struct sk_buff *skb = chunk->skb; @@ -107,7 +107,8 @@ static void sctp_control_set_owner_w(struct sctp_chunk *chunk) * For now don't do anything for now. */ if (chunk->auth) { - chunk->shkey = asoc->shkey; + if (!chunk->shkey) + chunk->shkey = asoc->shkey; sctp_auth_shkey_hold(chunk->shkey); } skb->sk = asoc ? asoc->base.sk : NULL; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 321898f22d80..b5a57f61ee6d 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -75,7 +75,8 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, struct sctp_transport *transport, long *timeo_p, size_t msg_len); static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p); -static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p); +static int sctp_wait_for_connect(struct sctp_association *, + struct sctp_transport *, long *timeo_p); static int sctp_wait_for_accept(struct sock *sk, long timeo); static void sctp_wait_for_close(struct sock *sk, long timeo); static void sctp_destruct_sock(struct sock *sk); @@ -147,25 +148,47 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk) static void sctp_clear_owner_w(struct sctp_chunk *chunk) { + /* Keep the shkey alive until the new owner takes its reference. */ + if (chunk->shkey) + sctp_auth_shkey_hold(chunk->shkey); skb_orphan(chunk->skb); } +static void sctp_set_owner_w_migrate(struct sctp_chunk *chunk) +{ + struct sctp_shared_key *shkey = chunk->shkey; + + if (chunk->msg) + sctp_set_owner_w(chunk); + else + sctp_control_set_owner_w(chunk); + + if (shkey) + sctp_auth_shkey_release(shkey); +} + +static void sctp_process_tx_chunk(struct sctp_association *asoc, + struct sctp_chunk *chunk, bool clear, + void (*cb)(struct sctp_chunk *)) +{ + if ((clear && asoc->base.sk == chunk->skb->sk) || + (!clear && asoc->base.sk != chunk->skb->sk)) + cb(chunk); +} + #define traverse_and_process() \ do { \ msg = chunk->msg; \ if (msg == prev_msg) \ continue; \ - list_for_each_entry(c, &msg->chunks, frag_list) { \ - if ((clear && asoc->base.sk == c->skb->sk) || \ - (!clear && asoc->base.sk != c->skb->sk)) \ - cb(c); \ - } \ + list_for_each_entry(c, &msg->chunks, frag_list) \ + sctp_process_tx_chunk(asoc, c, clear, cb); \ prev_msg = msg; \ } while (0) -static void sctp_for_each_tx_datachunk(struct sctp_association *asoc, - bool clear, - void (*cb)(struct sctp_chunk *)) +static void sctp_for_each_tx_chunk(struct sctp_association *asoc, + bool clear, + void (*cb)(struct sctp_chunk *)) { struct sctp_datamsg *msg, *prev_msg = NULL; @@ -188,6 +211,21 @@ static void sctp_for_each_tx_datachunk(struct sctp_association *asoc, list_for_each_entry(chunk, &q->out_chunk_list, list) traverse_and_process(); + + list_for_each_entry(chunk, &q->control_chunk_list, list) + sctp_process_tx_chunk(asoc, chunk, clear, cb); + + list_for_each_entry(chunk, &asoc->asconf_ack_list, transmitted_list) + sctp_process_tx_chunk(asoc, chunk, clear, cb); + + list_for_each_entry(chunk, &asoc->addip_chunk_list, list) + sctp_process_tx_chunk(asoc, chunk, clear, cb); + + if (asoc->strreset_chunk) + sctp_process_tx_chunk(asoc, asoc->strreset_chunk, clear, cb); + + if (asoc->addip_last_asconf) + sctp_process_tx_chunk(asoc, asoc->addip_last_asconf, clear, cb); } static void sctp_for_each_rx_skb(struct sctp_association *asoc, struct sock *sk, @@ -1245,7 +1283,7 @@ static int __sctp_connect(struct sock *sk, struct sockaddr *kaddrs, *assoc_id = asoc->assoc_id; timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); - return sctp_wait_for_connect(asoc, &timeo); + return sctp_wait_for_connect(asoc, NULL, &timeo); out_free: pr_debug("%s: took out_free path with asoc:%p kaddrs:%p err:%d\n", @@ -1847,7 +1885,7 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc, if (asoc->ep->intl_enable) { timeo = sock_sndtimeo(sk, 0); - err = sctp_wait_for_connect(asoc, &timeo); + err = sctp_wait_for_connect(asoc, transport, &timeo); if (err || sk->sk_shutdown & RCV_SHUTDOWN) { err = -ESRCH; goto err; @@ -1890,7 +1928,7 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc, if (unlikely(wait_connect)) { timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); - sctp_wait_for_connect(asoc, &timeo); + sctp_wait_for_connect(asoc, NULL, &timeo); } err = msg_len; @@ -9390,7 +9428,9 @@ static bool sctp_writeable(const struct sock *sk) /* Wait for an association to go into ESTABLISHED state. If timeout is 0, * returns immediately with EINPROGRESS. */ -static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p) +static int sctp_wait_for_connect(struct sctp_association *asoc, + struct sctp_transport *transport, + long *timeo_p) { struct sock *sk = asoc->base.sk; int err = 0; @@ -9399,12 +9439,16 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p) pr_debug("%s: asoc:%p, timeo:%ld\n", __func__, asoc, *timeo_p); - /* Increment the association's refcnt. */ + /* Hold references while the socket lock is dropped. */ + if (transport) + sctp_transport_hold(transport); sctp_association_hold(asoc); for (;;) { prepare_to_wait_exclusive(&asoc->wait, &wait, TASK_INTERRUPTIBLE); + if (transport && transport->dead) + goto do_transport_dead; if (!*timeo_p) goto do_nonblock; if (sk->sk_shutdown & RCV_SHUTDOWN) @@ -9433,7 +9477,8 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p) out: finish_wait(&asoc->wait, &wait); - /* Release the association's refcnt. */ + if (transport) + sctp_transport_put(transport); sctp_association_put(asoc); return err; @@ -9452,6 +9497,10 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p) do_nonblock: err = -EINPROGRESS; goto out; + +do_transport_dead: + err = -EAGAIN; + goto out; } static int sctp_wait_for_accept(struct sock *sk, long timeo) @@ -9635,9 +9684,9 @@ static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, * paths won't try to lock it and then oldsk. */ lock_sock_nested(newsk, SINGLE_DEPTH_NESTING); - sctp_for_each_tx_datachunk(assoc, true, sctp_clear_owner_w); + sctp_for_each_tx_chunk(assoc, true, sctp_clear_owner_w); sctp_assoc_migrate(assoc, newsk); - sctp_for_each_tx_datachunk(assoc, false, sctp_set_owner_w); + sctp_for_each_tx_chunk(assoc, false, sctp_set_owner_w_migrate); /* If the association on the newsk is already closed before accept() * is called, set RCV_SHUTDOWN flag. diff --git a/net/smc/smc_tx.c b/net/smc/smc_tx.c index 9e22a1983475..0f0c6df3bf2c 100644 --- a/net/smc/smc_tx.c +++ b/net/smc/smc_tx.c @@ -249,6 +249,13 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len) rc = memcpy_from_msg(sndbuf_base + chunk_off, msg, chunk_len); if (rc) { + if (chunk) { + /* send_done counts the first chunk: + * queue it and retry the rest + */ + copylen -= chunk_len; + break; + } smc_sndbuf_sync_sg_for_device(conn); if (send_done) return send_done; diff --git a/net/tipc/group.c b/net/tipc/group.c index 74f6d3dac078..0c6a762000b0 100644 --- a/net/tipc/group.c +++ b/net/tipc/group.c @@ -74,7 +74,7 @@ struct tipc_member { u16 window; u16 bc_rcv_nxt; u16 bc_syncpt; - u16 bc_acked; + bool bc_pending; }; struct tipc_group { @@ -316,7 +316,6 @@ static struct tipc_member *tipc_group_create_member(struct tipc_group *grp, m->node = node; m->port = port; m->instance = instance; - m->bc_acked = grp->bc_snd_nxt - 1; ret = tipc_group_add_to_tree(grp, m); if (ret < 0) { kfree(m); @@ -340,9 +339,12 @@ static void tipc_group_delete_member(struct tipc_group *grp, rb_erase(&m->tree_node, &grp->members); grp->member_cnt--; - /* Check if we were waiting for replicast ack from this member */ - if (grp->bc_ackers && less(m->bc_acked, grp->bc_snd_nxt - 1)) - grp->bc_ackers--; + /* If this member owed an ack for the current replicast and it + * was the last one, restore the open state so blocked broadcast + * senders can proceed. + */ + if (grp->bc_ackers && m->bc_pending && !--grp->bc_ackers) + *grp->open = true; list_del_init(&m->list); list_del_init(&m->small_win); @@ -394,7 +396,6 @@ void tipc_group_update_member(struct tipc_member *m, int len) void tipc_group_update_bc_members(struct tipc_group *grp, int len, bool ack) { - u16 prev = grp->bc_snd_nxt - 1; struct tipc_member *m; struct rb_node *n; u16 ackers = 0; @@ -403,8 +404,8 @@ void tipc_group_update_bc_members(struct tipc_group *grp, int len, bool ack) m = container_of(n, struct tipc_member, tree_node); if (tipc_group_is_receiver(m)) { tipc_group_update_member(m, len); - m->bc_acked = prev; ackers++; + m->bc_pending = ack; } } @@ -800,9 +801,9 @@ void tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup, if (!m || !grp->bc_ackers) return; acked = msg_grp_bc_acked(hdr); - if (acked != grp->bc_snd_nxt || m->bc_acked == acked) + if (acked != grp->bc_snd_nxt || !m->bc_pending) return; - m->bc_acked = acked; + m->bc_pending = false; if (--grp->bc_ackers) return; list_del_init(&m->small_win); diff --git a/net/tipc/node.c b/net/tipc/node.c index d7cbfa786c13..53b0993c2c23 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -303,6 +303,7 @@ static void tipc_node_free(struct rcu_head *rp) #ifdef CONFIG_TIPC_CRYPTO tipc_crypto_stop(&n->crypto_rx); #endif + kfree(n->bc_entry.link); kfree(n); } @@ -310,7 +311,6 @@ static void tipc_node_kref_release(struct kref *kref) { struct tipc_node *n = container_of(kref, struct tipc_node, kref); - kfree(n->bc_entry.link); call_rcu(&n->rcu, tipc_node_free); } diff --git a/net/tipc/socket.c b/net/tipc/socket.c index d5d70eb230b5..65cc0c4964b3 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -2356,8 +2356,18 @@ static void tipc_sk_filter_rcv(struct sock *sk, struct sk_buff *skb, if (unlikely(!msg_isdata(hdr))) tipc_sk_proto_rcv(sk, &inputq, xmitq); - if (unlikely(grp)) + if (unlikely(grp)) { + bool was_open = tsk->group_is_open; + tipc_group_filter_msg(grp, &inputq, xmitq); + /* A departing member may have held the last outstanding + * replicast ack, which can restore the open state inside + * tipc_group_filter_msg(); wake up senders blocked in + * poll()/write() if the group just became writable. + */ + if (!was_open && tsk->group_is_open) + sk->sk_write_space(sk); + } if (unlikely(!grp) && mtyp == TIPC_MCAST_MSG) tipc_mcast_filter_msg(net, &tsk->mc_method.deferredq, &inputq); diff --git a/net/unix/sysctl_net_unix.c b/net/unix/sysctl_net_unix.c index 47660d5726bb..fa1fbaec3aaf 100644 --- a/net/unix/sysctl_net_unix.c +++ b/net/unix/sysctl_net_unix.c @@ -19,7 +19,9 @@ static const struct ctl_table unix_table[] = { .data = &init_net.unx.sysctl_max_dgram_qlen, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_INT_MAX, }, }; diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 33475b180ea6..781ba46eb152 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -2145,6 +2145,11 @@ static int xsk_notifier(struct notifier_block *this, mutex_unlock(&xs->mutex); } mutex_unlock(&net->xdp.lock); + /* + * A released socket is no longer on xdp.list. Its pool can still + * hold a device reference on the frozen release workqueue. + */ + xp_clear_dev_all(dev); break; } return NOTIFY_DONE; diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index 9d2d94f1fb75..0ebd4d46f7ab 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -172,7 +172,7 @@ int xp_assign_dev(struct xsk_buff_pool *pool, u32 frame_size; int err = 0; - ASSERT_RTNL(); + netdev_assert_locked_ops_compat(netdev); force_zc = flags & XDP_ZEROCOPY; force_copy = flags & XDP_COPY; @@ -204,6 +204,11 @@ int xp_assign_dev(struct xsk_buff_pool *pool, pool->cached_need_wakeup = XDP_WAKEUP_TX; dev_hold(netdev); + /* + * Socket removal precedes the final pool put. Keep the pool visible to + * NETDEV_UNREGISTER even while release work is waiting for process thaw. + */ + hlist_add_head(&pool->dev_node, &netdev->xsk_pools); if (force_copy) /* For copy-mode, we are done. */ @@ -244,7 +249,6 @@ int xp_assign_dev(struct xsk_buff_pool *pool, bpf.xsk.pool = pool; bpf.xsk.queue_id = queue_id; - netdev_assert_locked_ops_compat(netdev); err = netdev->netdev_ops->ndo_bpf(netdev, &bpf); if (err) goto err_unreg_pool; @@ -265,6 +269,8 @@ int xp_assign_dev(struct xsk_buff_pool *pool, err = 0; /* fallback to copy mode */ if (err) { xsk_clear_pool_at_qid(netdev, queue_id); + hlist_del(&pool->dev_node); + pool->netdev = NULL; dev_put(netdev); } return err; @@ -287,19 +293,39 @@ int xp_assign_dev_shared(struct xsk_buff_pool *pool, struct xdp_sock *umem_xs, return xp_assign_dev(pool, dev, queue_id, flags); } +static void __xp_clear_dev(struct xsk_buff_pool *pool) +{ + struct net_device *netdev = pool->netdev; + + netdev_assert_locked_ops_compat(netdev); + xp_disable_drv_zc(pool); + xsk_clear_pool_at_qid(netdev, pool->queue_id); + hlist_del(&pool->dev_node); + pool->netdev = NULL; + dev_put(netdev); +} + void xp_clear_dev(struct xsk_buff_pool *pool) { struct net_device *netdev = pool->netdev; - if (!pool->netdev) + if (!netdev) return; netdev_lock_ops(netdev); - xp_disable_drv_zc(pool); - xsk_clear_pool_at_qid(pool->netdev, pool->queue_id); - pool->netdev = NULL; + __xp_clear_dev(pool); netdev_unlock_ops(netdev); - dev_put(netdev); +} + +void xp_clear_dev_all(struct net_device *dev) +{ + struct xsk_buff_pool *pool; + struct hlist_node *next; + + netdev_lock_ops(dev); + hlist_for_each_entry_safe(pool, next, &dev->xsk_pools, dev_node) + __xp_clear_dev(pool); + netdev_unlock_ops(dev); } static void xp_release_deferred(struct work_struct *work) @@ -337,7 +363,11 @@ bool xp_put_pool(struct xsk_buff_pool *pool) if (refcount_dec_and_test(&pool->users)) { INIT_WORK(&pool->work, xp_release_deferred); - schedule_work(&pool->work); + /* + * Teardown calls ndo_bpf(), which may need powered hardware. + * RTNL alone does not exclude the device's system PM callbacks. + */ + queue_work(system_freezable_wq, &pool->work); return true; } diff --git a/tools/testing/selftests/drivers/net/netdevsim/fib_notifications.sh b/tools/testing/selftests/drivers/net/netdevsim/fib_notifications.sh index 9896580c3d85..c49d6deb621a 100755 --- a/tools/testing/selftests/drivers/net/netdevsim/fib_notifications.sh +++ b/tools/testing/selftests/drivers/net/netdevsim/fib_notifications.sh @@ -8,10 +8,12 @@ ALL_TESTS=" ipv4_route_deletion_test ipv4_route_replacement_test ipv4_route_offload_failed_test + ipv4_route_nexthop_test ipv6_route_addition_test ipv6_route_deletion_test ipv6_route_replacement_test ipv6_route_offload_failed_test + ipv6_route_nexthop_test " NETDEVSIM_PATH=/sys/bus/netdevsim/ @@ -269,6 +271,50 @@ ipv4_route_offload_failed_test() log_test "IPv4 route offload failed" } +route_nexthop_check() +{ + local ip=$1; shift + local route=$1; shift + local nhid=$1; shift + local expected_num_notifications=$1; shift + local outfile + + ip netns exec testns1 sysctl -qw net.$ip.fib_notify_on_flag_change=1 + + outfile=$(mktemp) + + $IP monitor route &> $outfile & + sleep 1 + $IP route add $route nhid $nhid + sleep 1 + kill_process %% + + route_notify_check $outfile $expected_num_notifications + rm -f $outfile + + $IP route del $route nhid $nhid +} + +ipv4_route_nexthop_test() +{ + RET=0 + + local ip="ipv4" + local route=192.0.2.0/24 + local nhid=1 + + $IP -4 nexthop add id $nhid dev dummy1 + + # Make sure two notifications will be emitted for a programmed route + # that uses a nexthop object. + local expected_num_notifications=2 + route_nexthop_check $ip $route $nhid $expected_num_notifications + + $IP nexthop del id $nhid + + log_test "IPv4 route with nexthop object" +} + ipv6_route_addition_test() { RET=0 @@ -385,6 +431,26 @@ ipv6_route_offload_failed_test() log_test "IPv6 route offload failed" } +ipv6_route_nexthop_test() +{ + RET=0 + + local ip="ipv6" + local route=2001:db8:1::/64 + local nhid=1 + + $IP -6 nexthop add id $nhid dev dummy1 + + # Make sure two notifications will be emitted for a programmed route + # that uses a nexthop object. + local expected_num_notifications=2 + route_nexthop_check $ip $route $nhid $expected_num_notifications + + $IP nexthop del id $nhid + + log_test "IPv6 route with nexthop object" +} + setup_prepare() { modprobe netdevsim &> /dev/null diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index df5bcc55ea8e..df215e1a6262 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -42,6 +42,7 @@ TEST_PROGS := \ fib_rule_tests.sh \ fib_tests.sh \ fin_ack_lat.sh \ + fou_gre_gro.sh \ fou_mcast_encap.sh \ fq_band_pktlimit.sh \ gre_gso.sh \ diff --git a/tools/testing/selftests/net/bpf_offload.py b/tools/testing/selftests/net/bpf_offload.py index c856d266c8f3..8c1da9b51be5 100755 --- a/tools/testing/selftests/net/bpf_offload.py +++ b/tools/testing/selftests/net/bpf_offload.py @@ -452,6 +452,38 @@ class BpfNetdevSim(NetdevSim): self.tc_add_ingress() self.wait_for_flush(bound=bound, total=total) + def tc_add_block(self, block, fail=True, include_stderr=False): + return tc("qdisc add dev %s ingress_block %d clsact" % + (self['ifname'], block), fail=fail, + include_stderr=include_stderr) + + def tc_del_block(self, block): + tc("qdisc del dev %s clsact" % (self['ifname']), fail=False) + + def tc_add_block_filter(self, block, bpf, op="add", prio=None, handle=None, + da=False, verbose=False, skip_sw=False, + skip_hw=False, fail=True, include_stderr=False): + spec = "" + if prio is not None: + spec += " prio %d" % (prio) + if handle: + spec += " handle %s" % (handle) + + params = "" + if da: + params += " da" + if verbose: + params += " verbose" + if skip_sw: + params += " skip_sw" + if skip_hw: + params += " skip_hw" + + return tc("filter {op} block {block} {spec} bpf {bpf} {params}" + .format(op=op, block=block, spec=spec, bpf=bpf, + params=params), + fail=fail, include_stderr=include_stderr) + def tc_show_ingress(self, expected=None): # No JSON support, oh well... flags = ["skip_sw", "skip_hw", "in_hw"] @@ -629,6 +661,111 @@ def test_spurios_extack(sim, obj, skip_hw, needle): include_stderr=True) check_no_extack(res, needle) +def test_tc_shared_block(): + """ + Device-bound cls_bpf program attached through a shared block. + """ + # A shared block has no qdisc, so the target device is derived from the + # block's bound netdevs and the program must match every member, since + # the offload path broadcasts it to every callback on the block. A + # netdev joining a populated block later is replayed the stored filter + # before it is inserted into block->ports, so the driver callback must + # enforce the device match as well. + simdev = BpfNetdevSimDev() + sim, = simdev.nsims + sim.set_ethtool_tc_offloads(True) + + start_test("Test TC shared-block load dev-bound program...") + bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/tc_shared", + prog_type="classifier", dev=sim['ifname']) + + start_test("Test TC shared-block dev-bound attach...") + # The pinned program is dev-bound to this device; attaching it through + # a shared block must succeed even though block->q is NULL. + sim.tc_add_block(22) + # err is kept (include_stderr) for post-mortem value on a failure. + ret, _, err = sim.tc_add_block_filter(22, bpf_pinned("/sys/fs/bpf/tc_shared"), + da=True, skip_sw=True, + fail=False, include_stderr=True) + fail(ret != 0, "dev-bound program rejected on a matching shared block") + fail(sim.dfs_read("bpf_offloaded_id") == "0", + "dev-bound program not offloaded on a matching shared block") + sim.tc_del_block(22) + + start_test("Test TC shared-block broadcast to a foreign member...") + # Add an independently backed device to the SAME block before the + # attach. The program is bound to sim's device only, and the offload + # path fans it out to every member, so the attach must be rejected. + simdev2 = BpfNetdevSimDev() + sim2, = simdev2.nsims + sim2.set_ethtool_tc_offloads(True) + sim.tc_add_block(40) + sim2.tc_add_block(40) + ret, _, err = sim.tc_add_block_filter(40, bpf_pinned("/sys/fs/bpf/tc_shared"), + da=True, skip_sw=True, + fail=False, include_stderr=True) + fail(ret == 0, "dev-bound program broadcast to a foreign shared-block member") + check_extack(err, "Program is not bound to every device in the block.", args) + sim2.tc_del_block(40) + sim.tc_del_block(40) + simdev2.remove() + + start_test("Test TC shared-block late join replay...") + # Build a block owned by sim's device with the matching program + # installed, then let an independently backed device join it. The + # join replays the stored filter to the joining device's callback + # before the device is recorded in block->ports; the driver must + # reject the replay so the foreign device is never loaded. + # sim2 is created here (not inherited) so the TC-offload state it + # depends on is explicit. + simdev2 = BpfNetdevSimDev() + sim2, = simdev2.nsims + sim2.set_ethtool_tc_offloads(True) + sim.tc_add_block(30) + ret, _, err = sim.tc_add_block_filter(30, bpf_pinned("/sys/fs/bpf/tc_shared"), + da=True, skip_sw=True, + fail=False, include_stderr=True) + fail(ret != 0, "dev-bound program rejected on a matching shared block") + ret, _, err = sim2.tc_add_block(30, fail=False, include_stderr=True) + fail(ret == 0, "foreign device joined a populated dev-bound shared block") + check_extack_nsim(err, "program bound to a different device.", args) + fail(sim2.dfs_read("bpf_offloaded_id") != "0", + "replayed program loaded onto a foreign device") + # The add above was expected to fail, so the qdisc may not exist; the + # delete is harmless and only cleans up when it does. + sim2.tc_del_block(30) + sim.tc_del_block(30) + simdev2.remove() + + start_test("Test TC shared-block same-offload-device member...") + # A second port of the same netdevsim device shares the offload device, + # so a program bound to one port may legitimately be offloaded to both. + simdev3 = BpfNetdevSimDev(2) + sim3, sim3b = simdev3.nsims + sim3.set_ethtool_tc_offloads(True) + sim3b.set_ethtool_tc_offloads(True) + bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/tc_shared_multi", + prog_type="classifier", dev=sim3['ifname']) + sim3.tc_add_block(50) + sim3b.tc_add_block(50) + # err kept for post-mortem value on a failure. + ret, _, err = sim3.tc_add_block_filter(50, bpf_pinned("/sys/fs/bpf/tc_shared_multi"), + da=True, skip_sw=True, + fail=False, include_stderr=True) + fail(ret != 0, "same-offload-device member rejected on a shared block") + fail(sim3.dfs_read("bpf_offloaded_id") == "0", + "program not offloaded to the bound port") + fail(sim3b.dfs_read("bpf_offloaded_id") == "0", + "program not offloaded to the same-offload-device port") + sim3b.tc_del_block(50) + sim3.tc_del_block(50) + + simdev3.remove() + simdev.remove() + rm("/sys/fs/bpf/tc_shared_multi") + rm("/sys/fs/bpf/tc_shared") + bpftool_prog_list_wait(expected=0) + def test_multi_prog(simdev, sim, obj, modename, modeid): start_test("Test multi-attachment XDP - %s + offload..." % (modename or "default", )) @@ -1349,6 +1486,8 @@ try: ret, out = bpftool("prog show %s" % (progB), fail=False) fail(ret != 0, "couldn't get information about orphaned program") + test_tc_shared_block() + print("%s: OK" % (os.path.basename(__file__))) finally: diff --git a/tools/testing/selftests/net/forwarding/bridge_mdb_max.sh b/tools/testing/selftests/net/forwarding/bridge_mdb_max.sh index 625162fd7e8b..4ebffe4588a2 100755 --- a/tools/testing/selftests/net/forwarding/bridge_mdb_max.sh +++ b/tools/testing/selftests/net/forwarding/bridge_mdb_max.sh @@ -399,6 +399,24 @@ bridge_port_vlan_maxgroups_set() bridge vlan set $locus mcast_max_groups $max } +bridge_mcast_snooping_get() +{ + ip -j -d link show dev br0 | + jq '.[].linkinfo.info_data.mcast_snooping' +} + +bridge_mcast_hash_max_get() +{ + ip -j -d link show dev br0 | + jq '.[].linkinfo.info_data.mcast_hash_max' +} + +bridge_mdb_entries_count() +{ + bridge -j mdb show dev br0 | + jq '[.[].mdb[] | [.grp, .src, .vid]] | unique | length' +} + test_ngroups_reporting() { local CFG=$1; shift @@ -938,6 +956,114 @@ test_8021qvs_maxgroups_too_many_entries_ctl6() test_maxgroups_too_many_entries ctl6 port_vlan "dev $swp1 vid 10" } +test_maxgroups_hash_max() +{ + local CFG=$1; shift + local context=$1; shift + local locus=$1; shift + + local hash_max n i + + RET=0 + + hash_max=$(bridge_mcast_hash_max_get) + + # Make sure the port has entries, so that max == n is not 0 (no limit). + ${CFG}_entries_add "$locus" temp 2 120 + check_err $? "$locus: Couldn't add MDB entries" + n=$(bridge_${context}_ngroups_get "$locus") + + # Leave room for three more MDB entries. + ip link set dev br0 type bridge \ + mcast_hash_max $(($(bridge_mdb_entries_count) + 3)) + check_err $? "Couldn't set mcast_hash_max" + + # Port at its maximum: (*, G) entries are rejected. + bridge_${context}_maxgroups_set "$locus" "$n" + check_err $? "$locus: Couldn't set maximum" + for i in $(seq 121 124); do + ${CFG}_entries_add "$locus" temp 2 "$i" &> /dev/null + done + + # One more slot: (*, G) is accepted, (S, G) is rejected. + bridge_${context}_maxgroups_set "$locus" $((n + 1)) + check_err $? "$locus: Couldn't set maximum" + for i in $(seq 125 128); do + ${CFG}_entries_add "$locus" temp 2 "$i" &> /dev/null + done + + [[ $(bridge_mcast_snooping_get) == 1 ]] + check_err $? "Multicast snooping disabled by rejected MDB entries" + + bridge mdb flush dev br0 + ip link set dev br0 type bridge mcast_snooping 1 + ip link set dev br0 type bridge mcast_hash_max "$hash_max" + bridge_${context}_maxgroups_set "$locus" 0 + check_err $? "$locus: Couldn't set maximum to 0" + + log_test "$CFG: $context maxgroups: rejected entries do not exhaust MDB" +} + +test_8021d_maxgroups_hash_max_cfg4() +{ + test_maxgroups_hash_max cfg4 port "dev $swp1" +} + +test_8021d_maxgroups_hash_max_ctl4() +{ + test_maxgroups_hash_max ctl4 port "dev $swp1" +} + +test_8021d_maxgroups_hash_max_cfg6() +{ + test_maxgroups_hash_max cfg6 port "dev $swp1" +} + +test_8021d_maxgroups_hash_max_ctl6() +{ + test_maxgroups_hash_max ctl6 port "dev $swp1" +} + +test_8021q_maxgroups_hash_max_cfg4() +{ + test_maxgroups_hash_max cfg4 port "dev $swp1 vid 10" +} + +test_8021q_maxgroups_hash_max_ctl4() +{ + test_maxgroups_hash_max ctl4 port "dev $swp1 vid 10" +} + +test_8021q_maxgroups_hash_max_cfg6() +{ + test_maxgroups_hash_max cfg6 port "dev $swp1 vid 10" +} + +test_8021q_maxgroups_hash_max_ctl6() +{ + test_maxgroups_hash_max ctl6 port "dev $swp1 vid 10" +} + +test_8021qvs_maxgroups_hash_max_cfg4() +{ + test_maxgroups_hash_max cfg4 port_vlan "dev $swp1 vid 10" +} + +test_8021qvs_maxgroups_hash_max_ctl4() +{ + test_maxgroups_hash_max ctl4 port_vlan "dev $swp1 vid 10" +} + +test_8021qvs_maxgroups_hash_max_cfg6() +{ + test_maxgroups_hash_max cfg6 port_vlan "dev $swp1 vid 10" +} + +test_8021qvs_maxgroups_hash_max_ctl6() +{ + test_maxgroups_hash_max ctl6 port_vlan "dev $swp1 vid 10" +} + test_maxgroups_too_many_cross_vlan() { local CFG=$1; shift @@ -1177,6 +1303,7 @@ test_8021d_maxgroups_cfg4() test_8021d_maxgroups_zero_cfg4 test_8021d_maxgroups_too_low_cfg4 test_8021d_maxgroups_too_many_entries_cfg4 + test_8021d_maxgroups_hash_max_cfg4 } test_8021d_maxgroups_ctl4() @@ -1184,6 +1311,7 @@ test_8021d_maxgroups_ctl4() test_8021d_maxgroups_zero_ctl4 test_8021d_maxgroups_too_low_ctl4 test_8021d_maxgroups_too_many_entries_ctl4 + test_8021d_maxgroups_hash_max_ctl4 } test_8021d_maxgroups_cfg6() @@ -1191,6 +1319,7 @@ test_8021d_maxgroups_cfg6() test_8021d_maxgroups_zero_cfg6 test_8021d_maxgroups_too_low_cfg6 test_8021d_maxgroups_too_many_entries_cfg6 + test_8021d_maxgroups_hash_max_cfg6 } test_8021d_maxgroups_ctl6() @@ -1198,6 +1327,7 @@ test_8021d_maxgroups_ctl6() test_8021d_maxgroups_zero_ctl6 test_8021d_maxgroups_too_low_ctl6 test_8021d_maxgroups_too_many_entries_ctl6 + test_8021d_maxgroups_hash_max_ctl6 } test_8021q_maxgroups_cfg4() @@ -1205,6 +1335,7 @@ test_8021q_maxgroups_cfg4() test_8021q_maxgroups_zero_cfg4 test_8021q_maxgroups_too_low_cfg4 test_8021q_maxgroups_too_many_entries_cfg4 + test_8021q_maxgroups_hash_max_cfg4 } test_8021q_maxgroups_ctl4() @@ -1212,6 +1343,7 @@ test_8021q_maxgroups_ctl4() test_8021q_maxgroups_zero_ctl4 test_8021q_maxgroups_too_low_ctl4 test_8021q_maxgroups_too_many_entries_ctl4 + test_8021q_maxgroups_hash_max_ctl4 } test_8021q_maxgroups_cfg6() @@ -1219,6 +1351,7 @@ test_8021q_maxgroups_cfg6() test_8021q_maxgroups_zero_cfg6 test_8021q_maxgroups_too_low_cfg6 test_8021q_maxgroups_too_many_entries_cfg6 + test_8021q_maxgroups_hash_max_cfg6 } test_8021q_maxgroups_ctl6() @@ -1226,6 +1359,7 @@ test_8021q_maxgroups_ctl6() test_8021q_maxgroups_zero_ctl6 test_8021q_maxgroups_too_low_ctl6 test_8021q_maxgroups_too_many_entries_ctl6 + test_8021q_maxgroups_hash_max_ctl6 } test_8021qvs_maxgroups_cfg4() @@ -1234,6 +1368,7 @@ test_8021qvs_maxgroups_cfg4() test_8021qvs_maxgroups_zero_cross_vlan_cfg4 test_8021qvs_maxgroups_too_low_cfg4 test_8021qvs_maxgroups_too_many_entries_cfg4 + test_8021qvs_maxgroups_hash_max_cfg4 test_8021qvs_maxgroups_too_many_cross_vlan_cfg4 } @@ -1243,6 +1378,7 @@ test_8021qvs_maxgroups_ctl4() test_8021qvs_maxgroups_zero_cross_vlan_ctl4 test_8021qvs_maxgroups_too_low_ctl4 test_8021qvs_maxgroups_too_many_entries_ctl4 + test_8021qvs_maxgroups_hash_max_ctl4 test_8021qvs_maxgroups_too_many_cross_vlan_ctl4 } @@ -1252,6 +1388,7 @@ test_8021qvs_maxgroups_cfg6() test_8021qvs_maxgroups_zero_cross_vlan_cfg6 test_8021qvs_maxgroups_too_low_cfg6 test_8021qvs_maxgroups_too_many_entries_cfg6 + test_8021qvs_maxgroups_hash_max_cfg6 test_8021qvs_maxgroups_too_many_cross_vlan_cfg6 } @@ -1261,6 +1398,7 @@ test_8021qvs_maxgroups_ctl6() test_8021qvs_maxgroups_zero_cross_vlan_ctl6 test_8021qvs_maxgroups_too_low_ctl6 test_8021qvs_maxgroups_too_many_entries_ctl6 + test_8021qvs_maxgroups_hash_max_ctl6 test_8021qvs_maxgroups_too_many_cross_vlan_ctl6 } diff --git a/tools/testing/selftests/net/fou_gre_gro.sh b/tools/testing/selftests/net/fou_gre_gro.sh new file mode 100755 index 000000000000..21ed50f647ae --- /dev/null +++ b/tools/testing/selftests/net/fou_gre_gro.sh @@ -0,0 +1,164 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Test that GRE packets carried over UDP (FOU or GUE) get through when the +# receiver merges them with GRO. +# +# Send bulk TCP through such tunnels with GRO on at the receiver, and +# segmentation off at the sender so the receiver gets wire-sized packets +# to merge, and check that the receiver counts no UDP errors (UdpInErrors +# or Udp6InErrors). Also check that GRO really merged packets, by comparing +# the UDP datagrams the receiver delivered with the packets its veth +# received, so that a setup where GRO doesn't engage skips rather than +# passes. + +source lib.sh + +NSEND="" +NRECV="" + +FOU_PORT=5555 +TCP_PORT=9000 +SIZE_MB=4 + +TUN_S=192.168.99.1 +TUN_R=192.168.99.2 + +cleanup() { + cleanup_all_ns +} + +trap cleanup EXIT + +# setup_tunnel creates the namespaces, the veth between them and a GRE +# tunnel over UDP encapsulation $1 (fou or gue) over IPv$2. It returns +# $ksft_skip if the kernel or iproute2 can't set it up. +setup_tunnel() { + local encap=$1 + local family=$2 + local type=gre + local addr_s=10.0.0.1 + local addr_r=10.0.0.2 + local fou_args="ipproto 47" + + if [ "$encap" = gue ]; then + fou_args=gue + fi + if [ "$family" = 6 ]; then + type=ip6gre + addr_s=2001:db8::1 + addr_r=2001:db8::2 + fou_args="$fou_args -6" + fi + + setup_ns NSEND NRECV || return "$ksft_skip" + + ip link add veth_s netns "$NSEND" type veth \ + peer name veth_r netns "$NRECV" || return "$ksft_skip" + ip -n "$NSEND" addr add 10.0.0.1/24 dev veth_s + ip -n "$NRECV" addr add 10.0.0.2/24 dev veth_r + ip -n "$NSEND" addr add 2001:db8::1/64 dev veth_s nodad + ip -n "$NRECV" addr add 2001:db8::2/64 dev veth_r nodad + ip -n "$NSEND" link set veth_s up + ip -n "$NRECV" link set veth_r up + + ip netns exec "$NRECV" ethtool -K veth_r gro on || return "$ksft_skip" + ip netns exec "$NSEND" ethtool -K veth_s tso off gso off \ + tx-gre-segmentation off tx-gre-csum-segmentation off \ + tx-udp_tnl-segmentation off tx-udp_tnl-csum-segmentation off || + return "$ksft_skip" + + # shellcheck disable=SC2086 # $fou_args is several arguments + ip netns exec "$NSEND" ip fou add port "$FOU_PORT" $fou_args || + return "$ksft_skip" + # shellcheck disable=SC2086 + ip netns exec "$NRECV" ip fou add port "$FOU_PORT" $fou_args || + return "$ksft_skip" + ip -n "$NSEND" link add gre_t type "$type" \ + local "$addr_s" remote "$addr_r" \ + encap "$encap" encap-sport auto encap-dport "$FOU_PORT" || + return "$ksft_skip" + ip -n "$NRECV" link add gre_t type "$type" \ + local "$addr_r" remote "$addr_s" \ + encap "$encap" encap-sport auto encap-dport "$FOU_PORT" || + return "$ksft_skip" + + ip -n "$NSEND" addr add "$TUN_S/30" dev gre_t + ip -n "$NRECV" addr add "$TUN_R/30" dev gre_t + ip -n "$NSEND" link set gre_t up + ip -n "$NRECV" link set gre_t up +} + +# recv_counter prints the sum of the receiver's nstat counters "$@". +recv_counter() { + ip netns exec "$NRECV" nstat -az "$@" | + awk '!/^#/ { n += $2 } END { print n + 0 }' +} + +run_test() { + local encap=$1 + local family=$2 + local name="GRO of GRE in ${encap^^} over IPv$family" + local datagrams + local packets + local errors + local pid + local rc + + RET=0 + + if ! setup_tunnel "$encap" "$family"; then + log_test_skip "$name" + cleanup_all_ns + return + fi + + ip netns exec "$NRECV" socat -u \ + TCP4-LISTEN:"$TCP_PORT",bind="$TUN_R",reuseaddr OPEN:/dev/null & + pid=$! + wait_local_port_listen "$NRECV" "$TCP_PORT" tcp + + # The timeout only bounds failing runs; passing ones take under a second. + dd if=/dev/zero bs=1M count="$SIZE_MB" 2>/dev/null | + ip netns exec "$NSEND" timeout 20 \ + socat -u STDIN TCP4:"$TUN_R":"$TCP_PORT" + rc=$? + + # Check the UDP error count rather than the duration, which depends + # on the machine. + errors=$(recv_counter UdpInErrors Udp6InErrors) + check_err "$errors" "$errors UDP errors at the receiver" + check_err "$rc" "transfer failed" + + # Without errors, a pass only means something if GRO merged packets: + # then the encapsulation socket sees far fewer datagrams than the + # veth received packets. + datagrams=$(recv_counter UdpInDatagrams Udp6InDatagrams) + packets=$(ip netns exec "$NRECV" \ + cat /sys/class/net/veth_r/statistics/rx_packets) + if [ "$RET" -eq 0 ] && [ $((datagrams * 2)) -gt "$packets" ]; then + ret_set_ksft_status "$ksft_skip" \ + "GRO didn't merge: $datagrams UDP datagrams from $packets packets" + fi + + log_test "$name" + kill "$pid" 2>/dev/null + wait "$pid" 2>/dev/null + cleanup_all_ns +} + +require_command socat +require_command ethtool +require_command nstat + +modprobe -q fou +modprobe -q ip_gre +modprobe -q fou6 +modprobe -q ip6_gre + +run_test fou 4 +run_test gue 4 +run_test fou 6 +run_test gue 6 + +exit "$EXIT_STATUS" diff --git a/tools/testing/selftests/net/lib/xdp_dummy.bpf.c b/tools/testing/selftests/net/lib/xdp_dummy.bpf.c index e73fab3edd9f..95abd67cc184 100644 --- a/tools/testing/selftests/net/lib/xdp_dummy.bpf.c +++ b/tools/testing/selftests/net/lib/xdp_dummy.bpf.c @@ -16,4 +16,10 @@ int xdp_dummy_prog_frags(struct xdp_md *ctx) return XDP_PASS; } +SEC("xdp_tx") +int xdp_dummy_tx(struct xdp_md *ctx) +{ + return XDP_TX; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/net/veth.sh b/tools/testing/selftests/net/veth.sh index 60d0754928bf..17cfe412a266 100755 --- a/tools/testing/selftests/net/veth.sh +++ b/tools/testing/selftests/net/veth.sh @@ -339,6 +339,31 @@ ip netns exec $NS_DST ethtool -K veth$DST rx-udp-gro-forwarding on chk_gro " - aggregation with TSO off" 1 cleanup +create_ns +ip netns exec $NS_SRC sysctl -qw net.ipv6.conf.veth$SRC.disable_ipv6=1 +ip netns exec $NS_DST sysctl -qw net.ipv6.conf.veth$DST.disable_ipv6=1 +ip netns exec $NS_SRC ethtool -K veth$SRC gro on +ip -n $NS_DST link set dev veth$DST xdp object ${BPF_FILE} section xdp_tx +# veth$DST sends every frame back, so neither ARP nor ping gets an answer +mac=`ip netns exec $NS_DST cat /sys/class/net/veth$DST/address` +ip -n $NS_SRC neigh replace $BM_NET_V4$DST lladdr $mac dev veth$SRC +ip netns exec $NS_SRC ping -qc 5 -i 0.01 -W 1 $BM_NET_V4$DST >/dev/null +tx=`ip netns exec $NS_DST ethtool -S veth$DST | \ + awk '/rx_queue_[0-9]+_xdp_tx:/ {s += $2} END {print s + 0}'` +rx=`ip netns exec $NS_SRC ethtool -S veth$SRC | \ + awk '/rx_queue_[0-9]+_xdp_packets:/ {s += $2} END {print s + 0}'` +printf "%-60s" "xdp_tx to peer with gro on" +if [ $tx -ne 5 ]; then + echo " fail - $tx frames sent back, expected 5" + ret=1 +elif [ $rx -ne 5 ]; then + echo " fail - $rx frames received by the peer NAPI, expected 5" + ret=1 +else + echo " ok " +fi +cleanup + create_ns CUR_TX=1 diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/batch-delete.json b/tools/testing/selftests/tc-testing/tc-tests/actions/batch-delete.json index ef7ca4a6775b..7620e927ae2c 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/batch-delete.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/batch-delete.json @@ -111,5 +111,38 @@ 255 ] ] + }, + { + "id": "8135", + "name": "Reject duplicate action in a delete batch", + "category": [ + "actions", + "gact" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action pass index 1" + ], + "cmdUnderTest": "$TC actions del action gact index 1 action gact index 1 action gact index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions ls action gact", + "matchPattern": "total acts 1\\b.*index 1 ref 1 bind 0\\b", + "matchCount": "1", + "teardown": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hfsc.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hfsc.json index 4f6bbb8b57f9..557730eef055 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hfsc.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hfsc.json @@ -181,7 +181,7 @@ "requires": "nsPlugin" }, "setup": [ - "ip link set lo up", + "$IP link set lo up", "$TC qdisc add dev lo handle 1: root hfsc default 30", "$TC class add dev lo parent 1: classid 1:1 hfsc rt m2 100kbit", "$TC class add dev lo parent 1:1 classid 1:10 hfsc rt m2 50kbit",