mirror of
https://github.com/torvalds/linux.git
synced 2026-09-29 12:24:02 +02:00
Including fixes from Bluetooth, NFC and Netfilter.
Every week in this release is record-setting for number of posted
patches. It doesn't seem like we're creating any regressions with
all these fixes, 3 Fixes tags here point to 7.2 commits but none
are true regression fixes. We're trying to keep the count down,
nonetheless.
Previous releases - regressions:
- net: don't require the hwtstamp NDOs when a PHY provides timestamping
- ipv6: fix dst leak for uncached routes
- vrf: stop corrupting skb->csum when capturing CHECKSUM_COMPLETE
packets
Previous releases - always broken:
- packet: use ubuf_info completion for TX_RING packets
- arp: terminate device name before lookup
- ipv6: do not let ipv6_find_hdr() return an offset past the packet end
- udp: remove a disconnected socket from the 4-tuple hash table
- sctp: discard the rest of the packet on a stale-cookie error
- eth: mlx5: Bridge, fix remaining switchdev ownership gaps on merged
eswitch
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEE6jPA+I1ugmIBA4hXMUZtbf5SIrsFAmq1bCQACgkQMUZtbf5S
IrsgCBAAiHOxV3pR0pNw+K/dO5maYhuUV4w+Xcd6CbjRnxzYtImVC1q2ieez5dEe
JrQrVK76t2wuEgGSyiWcckIufPL8xYg5nYaOUbkYMC2QRtDaqhCo+u2jXu26/Lr8
O3sI2cj86XDZN+xKTvmLld1zIRyD6MhYxWz+gIicArdBqTsr5JsWkwhJE7SH013a
iVGu0sIxTgCNu/AusD6SdtwFte26dqvadelI8yuDhBR6rM9pi4EWXChleg7ohCsu
DoZTlaEjEiFavat+6ni99oz8MAT0WVBX0IzTqZa6TCnbCw4qUEbqN8G+CNgC/VPe
Sq5FRoo6vJjlFviwgWPbpje2FoOYtk6CX1eTavx8gZ4pPMTzGkT62crS+ETwLO5H
mRbE+ZjG8UDTUKpIY5P/IQYdznHSc9Ny/pHNlZhFmDdntJOp1lnegx5CmZkyuZxC
Qc0hoFxKdjUrpQ1n0ygT3/P6MT9vXMwbGP52VLIxaB1oaYCKFV71NOp0ET5fCnYb
CBK8cdN0kcaSLrpi3MnPamyQoNZfH78BiuKsLDu/TWg3i2HGmFTlp0/LJ0nkQcCU
4KlCmiJNOkHjbQKHNtYMUOZQcZu5witEv33kb7gr581b0RR+n0lZTaXcnRIyXYdb
ORqIa1Khg5mW3DLaIc9IrzEGGAW5zXUHc5OX7t8J2rVRQWhBEh4=
=YiiK
-----END PGP SIGNATURE-----
Merge tag 'net-7.3-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Pull networking fixes from Jakub Kicinski:
"Including fixes from Bluetooth, NFC and Netfilter.
Every week in this release is record-setting for number of posted
patches. It doesn't seem like we're creating any regressions with all
these fixes, three 'Fixes' tags here point to 7.2 commits but none are
true regression fixes. We're trying to keep the count down,
nonetheless.
Previous releases - regressions:
- net: don't require the hwtstamp NDOs when a PHY provides
timestamping
- ipv6: fix dst leak for uncached routes
- vrf: stop corrupting skb->csum when capturing CHECKSUM_COMPLETE
packets
Previous releases - always broken:
- packet: use ubuf_info completion for TX_RING packets
- arp: terminate device name before lookup
- ipv6: do not let ipv6_find_hdr() return an offset past the packet
end
- udp: remove a disconnected socket from the 4-tuple hash table
- sctp: discard the rest of the packet on a stale-cookie error
- eth: mlx5: Bridge, fix remaining switchdev ownership gaps on merged
eswitch"
[ And lots of other random network driver fixes ]
* tag 'net-7.3-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (189 commits)
tcp: prevent collapsing skbs across boundary in rtx queue
vlan: ensure sufficient headroom in vlan_dev_hard_header()
net/sched: sch_teql: fix shadowed err in __teql_resolve()
bridge: check llc_mac_hdr_init() return value in br_send_bpdu()
llc: fix skb UAF and leaks on llc_mac_hdr_init() failure
llc: reserve device headroom for allocated frames
gve: DQO: reject TSO packets with an out of range MSS
gve: fix TX drop when GSO MSS is too small for hw
gve: DQO: fix header length used by gve_can_send_tso() for UDP GSO
net: flush skb_defer_nodes in dev_cpu_dead()
net: ethernet: stmmac: dwmac-rk: fix bulk clock leak when the PHY clock fails
af_packet: fix integer overflow in prb_calc_retire_blk_tmo()
tipc: Fix a data race on mon->peer_cnt in mon_timeout()
net: phy: intel-xway: workaround 100BASE-TX Link-Up issue
net/smc: fix UAF on lgr list traversal in smcr_port_err()
net/rds: size a connection's path set by the transport it ends up with
nfp: hold IPsec RX state under the XArray lock
net: ena: fix MMIO read buffer leak on probe failure
net: ena: fix PHC cleanup on probe failure
net/sched: act_ct: fix helper UAF due to extensions realloc
...
This commit is contained in:
commit
f2c53ea949
1
.mailmap
1
.mailmap
|
|
@ -977,6 +977,7 @@ Vladimir Davydov <vdavydov.dev@gmail.com> <vdavydov@virtuozzo.com>
|
|||
Vlastimil Babka <vbabka@kernel.org> <vbabka@suse.cz>
|
||||
WangYuli <wangyuli@aosc.io> <wangyl5933@chinaunicom.cn>
|
||||
WangYuli <wangyuli@aosc.io> <wangyuli@deepin.org>
|
||||
Wei Wang <weiwan@google.com> <weibunny@fb.com>
|
||||
Weiwen Hu <huweiwen@linux.alibaba.com> <sehuww@mail.scut.edu.cn>
|
||||
WeiXiong Liao <gmpy.liaowx@gmail.com> <liaoweixiong@allwinnertech.com>
|
||||
Wen Gong <quic_wgong@quicinc.com> <wgong@codeaurora.org>
|
||||
|
|
|
|||
|
|
@ -851,6 +851,8 @@ operations:
|
|||
name: bind-tx
|
||||
doc: Bind dmabuf to netdev for TX
|
||||
attribute-set: dmabuf
|
||||
# Intentionally unprivileged (no admin-perm / uns-admin-perm); see
|
||||
# comment above netdev_nl_bind_tx_doit().
|
||||
do:
|
||||
request:
|
||||
attributes:
|
||||
|
|
|
|||
|
|
@ -5454,6 +5454,7 @@ F: include/linux/brcmphy.h
|
|||
BROADCOM GENET ETHERNET DRIVER
|
||||
M: Doug Berger <opendmb@gmail.com>
|
||||
M: Florian Fainelli <florian.fainelli@broadcom.com>
|
||||
M: Nicolai Buchwitz <nb@tipi-net.de>
|
||||
R: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
|
|
|
|||
|
|
@ -1099,6 +1099,11 @@ static void btintel_pcie_msix_tx_handle(struct btintel_pcie_data *data)
|
|||
|
||||
txq = &data->txq;
|
||||
|
||||
if (cr_hia >= txq->count) {
|
||||
bt_dev_err(data->hdev, "TXQ: invalid cr_hia %u", cr_hia);
|
||||
return;
|
||||
}
|
||||
|
||||
while (cr_tia != cr_hia) {
|
||||
data->tx_wait_done = true;
|
||||
wake_up(&data->tx_wait_q);
|
||||
|
|
@ -1650,6 +1655,11 @@ static void btintel_pcie_msix_rx_handle(struct btintel_pcie_data *data)
|
|||
|
||||
rxq = &data->rxq;
|
||||
|
||||
if (cr_hia >= rxq->count) {
|
||||
bt_dev_err(hdev, "RXQ: invalid cr_hia %u", cr_hia);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The firmware sends multiple CD in a single MSI-X and it needs to
|
||||
* process all received CDs in this interrupt.
|
||||
*/
|
||||
|
|
@ -1657,6 +1667,12 @@ static void btintel_pcie_msix_rx_handle(struct btintel_pcie_data *data)
|
|||
urbd1 = &rxq->urbd1s[cr_tia];
|
||||
ipc_print_urbd1(data->hdev, urbd1, cr_tia);
|
||||
|
||||
if (urbd1->frbd_tag >= rxq->count) {
|
||||
bt_dev_err(hdev, "RXQ: invalid frbd_tag %u",
|
||||
urbd1->frbd_tag);
|
||||
return;
|
||||
}
|
||||
|
||||
buf = &rxq->bufs[urbd1->frbd_tag];
|
||||
if (!buf) {
|
||||
bt_dev_err(hdev, "RXQ: failed to get the DMA buffer for %d",
|
||||
|
|
|
|||
|
|
@ -1388,9 +1388,11 @@ static int nxp_process_fw_dump(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
msecs_to_jiffies(20000));
|
||||
}
|
||||
|
||||
err = hci_devcd_append(hdev, skb_clone(skb, GFP_ATOMIC));
|
||||
if (err < 0)
|
||||
goto free_skb;
|
||||
if (IS_ENABLED(CONFIG_DEV_COREDUMP)) {
|
||||
err = hci_devcd_append(hdev, skb_clone(skb, GFP_ATOMIC));
|
||||
if (err < 0)
|
||||
goto free_skb;
|
||||
}
|
||||
|
||||
if (buf_len == 0) {
|
||||
bt_dev_warn(hdev, "==== FW dump complete ===");
|
||||
|
|
|
|||
|
|
@ -1210,8 +1210,7 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
|
|||
struct dpll_device *dpll;
|
||||
int ret;
|
||||
|
||||
ref_sync_pin = xa_find(&pin->ref_sync_pins, &ref_sync_pin_idx,
|
||||
ULONG_MAX, XA_PRESENT);
|
||||
ref_sync_pin = xa_load(&pin->ref_sync_pins, ref_sync_pin_idx);
|
||||
if (!ref_sync_pin) {
|
||||
NL_SET_ERR_MSG(extack, "reference sync pin not found");
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -490,7 +490,7 @@ static int bond_ipsec_add_sa(struct net_device *bond_dev,
|
|||
!real_dev->xfrmdev_ops->xdo_dev_state_add ||
|
||||
netif_is_bond_master(real_dev)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Slave does not support ipsec offload");
|
||||
err = -EINVAL;
|
||||
err = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -227,15 +227,17 @@ mt7530_remove(struct mdio_device *mdiodev)
|
|||
if (!priv)
|
||||
return;
|
||||
|
||||
ret = regulator_disable(priv->core_pwr);
|
||||
if (ret < 0)
|
||||
dev_err(priv->dev,
|
||||
"Failed to disable core power: %d\n", ret);
|
||||
if (priv->id == ID_MT7530) {
|
||||
ret = regulator_disable(priv->core_pwr);
|
||||
if (ret < 0)
|
||||
dev_err(priv->dev,
|
||||
"Failed to disable core power: %d\n", ret);
|
||||
|
||||
ret = regulator_disable(priv->io_pwr);
|
||||
if (ret < 0)
|
||||
dev_err(priv->dev, "Failed to disable io pwr: %d\n",
|
||||
ret);
|
||||
ret = regulator_disable(priv->io_pwr);
|
||||
if (ret < 0)
|
||||
dev_err(priv->dev, "Failed to disable io pwr: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
mt7530_remove_common(priv);
|
||||
|
||||
|
|
|
|||
|
|
@ -3593,9 +3593,6 @@ EXPORT_SYMBOL_GPL(mt7530_probe_common);
|
|||
void
|
||||
mt7530_remove_common(struct mt7530_priv *priv)
|
||||
{
|
||||
if (priv->irq_domain)
|
||||
mt7530_free_mdio_irq(priv);
|
||||
|
||||
dsa_unregister_switch(priv->ds);
|
||||
|
||||
mutex_destroy(&priv->reg_mutex);
|
||||
|
|
|
|||
|
|
@ -5639,6 +5639,68 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
|
|||
.pcs_ops = &mv88e6390_pcs_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6191x_ops = {
|
||||
/* MV88E6XXX_FAMILY_6393 without AVB and PTP: 6191X and 6193X */
|
||||
.irl_init_all = mv88e6390_g2_irl_init_all,
|
||||
.get_eeprom = mv88e6xxx_g2_get_eeprom8,
|
||||
.set_eeprom = mv88e6xxx_g2_set_eeprom8,
|
||||
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
||||
.phy_read = mv88e6xxx_g2_smi_phy_read_c22,
|
||||
.phy_write = mv88e6xxx_g2_smi_phy_write_c22,
|
||||
.phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45,
|
||||
.phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45,
|
||||
.port_set_link = mv88e6xxx_port_set_link,
|
||||
.port_sync_link = mv88e6xxx_port_sync_link,
|
||||
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
|
||||
.port_set_speed_duplex = mv88e6393x_port_set_speed_duplex,
|
||||
.port_tag_remap = mv88e6390_port_tag_remap,
|
||||
.port_set_policy = mv88e6393x_port_set_policy,
|
||||
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
|
||||
.port_set_ucast_flood = mv88e6352_port_set_ucast_flood,
|
||||
.port_set_mcast_flood = mv88e6352_port_set_mcast_flood,
|
||||
.port_set_ether_type = mv88e6393x_port_set_ether_type,
|
||||
.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
|
||||
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
|
||||
.port_pause_limit = mv88e6390_port_pause_limit,
|
||||
.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
|
||||
.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
|
||||
.port_get_cmode = mv88e6352_port_get_cmode,
|
||||
.port_set_cmode = mv88e6393x_port_set_cmode,
|
||||
.port_setup_message_port = mv88e6xxx_setup_message_port,
|
||||
.port_set_upstream_port = mv88e6393x_port_set_upstream_port,
|
||||
.port_enable_tcam = mv88e6xxx_port_enable_tcam,
|
||||
.stats_snapshot = mv88e6390_g1_stats_snapshot,
|
||||
.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
|
||||
.stats_get_sset_count = mv88e6320_stats_get_sset_count,
|
||||
.stats_get_strings = mv88e6320_stats_get_strings,
|
||||
.stats_get_stat = mv88e6390_stats_get_stat,
|
||||
/* .set_cpu_port is missing because this family does not support a global
|
||||
* CPU port, only per port CPU port which is set via
|
||||
* .port_set_upstream_port method.
|
||||
*/
|
||||
.set_egress_port = mv88e6393x_set_egress_port,
|
||||
.watchdog_ops = &mv88e6393x_watchdog_ops,
|
||||
.mgmt_rsvd2cpu = mv88e6393x_port_mgmt_rsvd2cpu,
|
||||
.pot_clear = mv88e6xxx_g2_pot_clear,
|
||||
.hardware_reset_pre = mv88e6xxx_g2_eeprom_wait,
|
||||
.hardware_reset_post = mv88e6xxx_g2_eeprom_wait,
|
||||
.reset = mv88e6352_g1_reset,
|
||||
.rmu_disable = mv88e6390_g1_rmu_disable,
|
||||
.atu_get_hash = mv88e6165_g1_atu_get_hash,
|
||||
.atu_set_hash = mv88e6165_g1_atu_set_hash,
|
||||
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||
.stu_getnext = mv88e6390_g1_stu_getnext,
|
||||
.stu_loadpurge = mv88e6390_g1_stu_loadpurge,
|
||||
.serdes_get_lane = mv88e6393x_serdes_get_lane,
|
||||
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
|
||||
/* TODO: serdes stats */
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.phylink_get_caps = mv88e6393x_phylink_get_caps,
|
||||
.pcs_ops = &mv88e6393x_pcs_ops,
|
||||
.tcam_ops = &mv88e6393_tcam_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6393x_ops = {
|
||||
/* MV88E6XXX_FAMILY_6393 */
|
||||
.irl_init_all = mv88e6390_g2_irl_init_all,
|
||||
|
|
@ -6163,8 +6225,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|||
.atu_move_port_mask = 0x1f,
|
||||
.pvt = true,
|
||||
.multi_chip = true,
|
||||
.ptp_support = true,
|
||||
.ops = &mv88e6393x_ops,
|
||||
.ops = &mv88e6191x_ops,
|
||||
},
|
||||
|
||||
[MV88E6193X] = {
|
||||
|
|
@ -6190,8 +6251,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|||
.atu_move_port_mask = 0x1f,
|
||||
.pvt = true,
|
||||
.multi_chip = true,
|
||||
.ptp_support = true,
|
||||
.ops = &mv88e6393x_ops,
|
||||
.ops = &mv88e6191x_ops,
|
||||
},
|
||||
|
||||
[MV88E6220] = {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/devcoredump.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_net.h>
|
||||
|
|
@ -751,12 +752,15 @@ static int airoha_npu_probe(struct platform_device *pdev)
|
|||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
err = devm_work_autocancel(dev, &core->wdt_work,
|
||||
airoha_npu_wdt_work);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = devm_request_irq(dev, irq, airoha_npu_wdt_handler,
|
||||
IRQF_SHARED, "airoha-npu-wdt", core);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
INIT_WORK(&core->wdt_work, airoha_npu_wdt_work);
|
||||
}
|
||||
|
||||
/* wlan IRQ lines */
|
||||
|
|
@ -803,18 +807,8 @@ static int airoha_npu_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void airoha_npu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct airoha_npu *npu = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(npu->cores); i++)
|
||||
cancel_work_sync(&npu->cores[i].wdt_work);
|
||||
}
|
||||
|
||||
static struct platform_driver airoha_npu_driver = {
|
||||
.probe = airoha_npu_probe,
|
||||
.remove = airoha_npu_remove,
|
||||
.driver = {
|
||||
.name = "airoha-npu",
|
||||
.of_match_table = of_airoha_npu_match,
|
||||
|
|
|
|||
|
|
@ -4122,6 +4122,8 @@ static int ena_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
err_device_destroy:
|
||||
ena_com_delete_host_info(ena_dev);
|
||||
ena_com_admin_destroy(ena_dev);
|
||||
ena_phc_destroy(adapter);
|
||||
ena_com_mmio_reg_read_request_destroy(ena_dev);
|
||||
ena_devlink_destroy:
|
||||
ena_devlink_free(devlink);
|
||||
err_metrics_destroy:
|
||||
|
|
|
|||
|
|
@ -1602,6 +1602,9 @@ static int atl1c_clean_tx(struct napi_struct *napi, int budget)
|
|||
AT_READ_REGW(&adapter->hw, atl1c_qregs[tpd_ring->num].tpd_cons,
|
||||
&hw_next_to_clean);
|
||||
|
||||
if (unlikely(hw_next_to_clean >= tpd_ring->count))
|
||||
hw_next_to_clean = next_to_clean;
|
||||
|
||||
while (next_to_clean != hw_next_to_clean) {
|
||||
buffer_info = &tpd_ring->buffer_info[next_to_clean];
|
||||
if (buffer_info->skb) {
|
||||
|
|
|
|||
|
|
@ -1234,6 +1234,9 @@ static bool atl1e_clean_tx_irq(struct atl1e_adapter *adapter)
|
|||
u16 hw_next_to_clean = AT_READ_REGW(&adapter->hw, REG_TPD_CONS_IDX);
|
||||
u16 next_to_clean = atomic_read(&tx_ring->next_to_clean);
|
||||
|
||||
if (unlikely(hw_next_to_clean >= tx_ring->count))
|
||||
hw_next_to_clean = next_to_clean;
|
||||
|
||||
while (next_to_clean != hw_next_to_clean) {
|
||||
tx_buffer = &tx_ring->tx_buffer[next_to_clean];
|
||||
if (tx_buffer->dma) {
|
||||
|
|
|
|||
|
|
@ -2066,6 +2066,9 @@ static int atl1_intr_tx(struct atl1_adapter *adapter)
|
|||
sw_tpd_next_to_clean = atomic_read(&tpd_ring->next_to_clean);
|
||||
cmb_tpd_next_to_clean = le16_to_cpu(adapter->cmb.cmb->tpd_cons_idx);
|
||||
|
||||
if (unlikely(cmb_tpd_next_to_clean >= tpd_ring->count))
|
||||
cmb_tpd_next_to_clean = sw_tpd_next_to_clean;
|
||||
|
||||
while (cmb_tpd_next_to_clean != sw_tpd_next_to_clean) {
|
||||
buffer_info = &tpd_ring->buffer_info[sw_tpd_next_to_clean];
|
||||
if (buffer_info->dma) {
|
||||
|
|
|
|||
|
|
@ -5733,7 +5733,8 @@ const struct ethtool_ops bnxt_ethtool_ops = {
|
|||
.op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS |
|
||||
ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM |
|
||||
ETHTOOL_OP_NEEDS_RTNL_SCOALESCE |
|
||||
ETHTOOL_OP_NEEDS_RTNL_RSS,
|
||||
ETHTOOL_OP_NEEDS_RTNL_RSS |
|
||||
ETHTOOL_OP_NEEDS_RTNL_TEST,
|
||||
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
|
||||
ETHTOOL_COALESCE_MAX_FRAMES |
|
||||
ETHTOOL_COALESCE_USECS_IRQ |
|
||||
|
|
|
|||
|
|
@ -852,7 +852,8 @@ static int bcmgenet_get_coalesce(struct net_device *dev,
|
|||
ec->rx_max_coalesced_frames =
|
||||
bcmgenet_rdma_ring_readl(priv, 0, DMA_MBUF_DONE_THRESH);
|
||||
ec->rx_coalesce_usecs =
|
||||
bcmgenet_rdma_readl(priv, DMA_RING0_TIMEOUT) * 8192 / 1000;
|
||||
(bcmgenet_rdma_readl(priv, DMA_RING0_TIMEOUT) &
|
||||
DMA_TIMEOUT_MASK) * 8192 / 1000;
|
||||
|
||||
for (i = 0; i <= priv->hw_params->rx_queues; i++) {
|
||||
ring = &priv->rx_rings[i];
|
||||
|
|
@ -1346,9 +1347,8 @@ static void bcmgenet_get_ethtool_stats(struct net_device *dev,
|
|||
p = (char *)&stats64;
|
||||
|
||||
p += s->stat_offset;
|
||||
if (sizeof(unsigned long) != sizeof(u32) &&
|
||||
s->stat_sizeof == sizeof(unsigned long))
|
||||
data[i] = *(unsigned long *)p;
|
||||
if (s->stat_sizeof == sizeof(u64))
|
||||
data[i] = *(u64 *)p;
|
||||
else
|
||||
data[i] = *(u32 *)p;
|
||||
}
|
||||
|
|
@ -1763,13 +1763,12 @@ static int bcmgenet_power_up(struct bcmgenet_priv *priv,
|
|||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
if (!bcmgenet_has_ext(priv))
|
||||
return ret;
|
||||
|
||||
reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
|
||||
|
||||
switch (mode) {
|
||||
case GENET_POWER_PASSIVE:
|
||||
if (!bcmgenet_has_ext(priv))
|
||||
break;
|
||||
|
||||
reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
|
||||
reg &= ~(EXT_PWR_DOWN_DLL | EXT_PWR_DOWN_BIAS |
|
||||
EXT_ENERGY_DET_MASK);
|
||||
if (GENET_IS_V5(priv) && !bcmgenet_has_ephy_16nm(priv)) {
|
||||
|
|
@ -1793,8 +1792,12 @@ static int bcmgenet_power_up(struct bcmgenet_priv *priv,
|
|||
break;
|
||||
|
||||
case GENET_POWER_CABLE_SENSE:
|
||||
if (!bcmgenet_has_ext(priv))
|
||||
break;
|
||||
|
||||
/* enable APD */
|
||||
if (!GENET_IS_V5(priv)) {
|
||||
reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
|
||||
reg |= EXT_PWR_DN_EN_LD;
|
||||
bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
|
||||
}
|
||||
|
|
@ -3441,6 +3444,8 @@ static void bcmgenet_netif_stop(struct net_device *dev, bool stop_phy)
|
|||
{
|
||||
struct bcmgenet_priv *priv = netdev_priv(dev);
|
||||
|
||||
/* Stop completion polling before it can wake a stopped queue */
|
||||
bcmgenet_disable_tx_napi(priv);
|
||||
netif_tx_disable(dev);
|
||||
|
||||
/* Disable MAC receive */
|
||||
|
|
@ -3455,7 +3460,6 @@ static void bcmgenet_netif_stop(struct net_device *dev, bool stop_phy)
|
|||
/* Disable MAC transmit. TX DMA disabled must be done before this */
|
||||
umac_enable_set(priv, CMD_TX_EN, false);
|
||||
|
||||
bcmgenet_disable_tx_napi(priv);
|
||||
bcmgenet_disable_rx_napi(priv);
|
||||
bcmgenet_intr_disable(priv);
|
||||
|
||||
|
|
@ -3632,6 +3636,9 @@ static int bcmgenet_set_mac_addr(struct net_device *dev, void *p)
|
|||
if (netif_running(dev))
|
||||
return -EBUSY;
|
||||
|
||||
if (!is_valid_ether_addr(addr->sa_data))
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
eth_hw_addr_set(dev, addr->sa_data);
|
||||
|
||||
return 0;
|
||||
|
|
@ -4135,10 +4142,10 @@ static int bcmgenet_probe(struct platform_device *pdev)
|
|||
priv->rx_rings[i].rx_max_coalesced_frames = 1;
|
||||
|
||||
/* Initialize u64 stats seq counter for 32bit machines */
|
||||
for (i = 0; i <= priv->hw_params->rx_queues; i++)
|
||||
for (i = 0; i <= GENET_MAX_MQ_CNT; i++) {
|
||||
u64_stats_init(&priv->rx_rings[i].stats64.syncp);
|
||||
for (i = 0; i <= priv->hw_params->tx_queues; i++)
|
||||
u64_stats_init(&priv->tx_rings[i].stats64.syncp);
|
||||
}
|
||||
|
||||
/* libphy will determine the link state */
|
||||
netif_carrier_off(dev);
|
||||
|
|
@ -4320,6 +4327,8 @@ static int bcmgenet_suspend(struct device *d)
|
|||
netif_device_detach(dev);
|
||||
|
||||
if (device_may_wakeup(d) && priv->wolopts) {
|
||||
/* Stop completion polling before it can wake a stopped queue */
|
||||
bcmgenet_disable_tx_napi(priv);
|
||||
netif_tx_disable(dev);
|
||||
|
||||
/* Suspend non-wake Rx data flows */
|
||||
|
|
@ -4348,7 +4357,6 @@ static int bcmgenet_suspend(struct device *d)
|
|||
netdev_warn(priv->dev,
|
||||
"Timed out while disabling TX DMA\n");
|
||||
|
||||
bcmgenet_disable_tx_napi(priv);
|
||||
bcmgenet_disable_rx_napi(priv);
|
||||
disable_irq(priv->irq1);
|
||||
bcmgenet_tx_reclaim_all(dev);
|
||||
|
|
|
|||
|
|
@ -17915,11 +17915,14 @@ static int tg3_init_one(struct pci_dev *pdev,
|
|||
|
||||
err = tg3_get_device_address(tp, addr);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Could not obtain valid ethernet address, aborting\n");
|
||||
goto err_out_apeunmap;
|
||||
dev_warn_probe(&pdev->dev, err,
|
||||
"Could not obtain a valid ethernet address\n");
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_out_apeunmap;
|
||||
eth_hw_addr_random(dev);
|
||||
} else {
|
||||
eth_hw_addr_set(dev, addr);
|
||||
}
|
||||
eth_hw_addr_set(dev, addr);
|
||||
|
||||
intmbx = MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW;
|
||||
rcvmbx = MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW;
|
||||
|
|
@ -18047,6 +18050,10 @@ static int tg3_init_one(struct pci_dev *pdev,
|
|||
return 0;
|
||||
|
||||
err_out_apeunmap:
|
||||
if (tg3_flag(tp, USE_PHYLIB))
|
||||
tg3_phy_fini(tp);
|
||||
tg3_mdio_fini(tp);
|
||||
|
||||
if (tp->aperegs) {
|
||||
iounmap(tp->aperegs);
|
||||
tp->aperegs = NULL;
|
||||
|
|
|
|||
|
|
@ -2571,6 +2571,22 @@ bnad_ioceth_disable(struct bnad *bnad)
|
|||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* The IOC timers rearm one another, so deleting one cannot stop a
|
||||
* sibling callback from arming it again. Shut them down so a later
|
||||
* mod_timer() is ignored.
|
||||
*/
|
||||
static void
|
||||
bnad_ioc_timers_shutdown(struct bnad *bnad)
|
||||
{
|
||||
struct bfa_ioc *ioc = &bnad->bna.ioceth.ioc;
|
||||
|
||||
timer_shutdown_sync(&ioc->ioc_timer);
|
||||
timer_shutdown_sync(&ioc->sem_timer);
|
||||
timer_shutdown_sync(&ioc->hb_timer);
|
||||
timer_shutdown_sync(&ioc->iocpf_timer);
|
||||
}
|
||||
|
||||
static int
|
||||
bnad_ioceth_enable(struct bnad *bnad)
|
||||
{
|
||||
|
|
@ -3727,9 +3743,7 @@ bnad_pci_probe(struct pci_dev *pdev,
|
|||
bnad_res_free(bnad, &bnad->mod_res_info[0], BNA_MOD_RES_T_MAX);
|
||||
disable_ioceth:
|
||||
bnad_ioceth_disable(bnad);
|
||||
timer_delete_sync(&bnad->bna.ioceth.ioc.ioc_timer);
|
||||
timer_delete_sync(&bnad->bna.ioceth.ioc.sem_timer);
|
||||
timer_delete_sync(&bnad->bna.ioceth.ioc.hb_timer);
|
||||
bnad_ioc_timers_shutdown(bnad);
|
||||
spin_lock_irqsave(&bnad->bna_lock, flags);
|
||||
bna_uninit(bna);
|
||||
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
||||
|
|
@ -3770,9 +3784,7 @@ bnad_pci_remove(struct pci_dev *pdev)
|
|||
|
||||
mutex_lock(&bnad->conf_mutex);
|
||||
bnad_ioceth_disable(bnad);
|
||||
timer_delete_sync(&bnad->bna.ioceth.ioc.ioc_timer);
|
||||
timer_delete_sync(&bnad->bna.ioceth.ioc.sem_timer);
|
||||
timer_delete_sync(&bnad->bna.ioceth.ioc.hb_timer);
|
||||
bnad_ioc_timers_shutdown(bnad);
|
||||
spin_lock_irqsave(&bnad->bna_lock, flags);
|
||||
bna_uninit(bna);
|
||||
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
||||
|
|
|
|||
|
|
@ -2749,14 +2749,24 @@ static int macb_alloc(struct macb *bp)
|
|||
|
||||
size = bp->num_queues * macb_tx_ring_size_per_queue(bp);
|
||||
tx = dma_alloc_coherent(dev, size, &tx_dma, GFP_KERNEL);
|
||||
if (!tx || upper_32_bits(tx_dma) != upper_32_bits(tx_dma + size - 1))
|
||||
if (!tx)
|
||||
goto out_err;
|
||||
/* Record the buffer so that the error path frees it. */
|
||||
bp->queues[0].tx_ring = tx;
|
||||
bp->queues[0].tx_ring_dma = tx_dma;
|
||||
if (upper_32_bits(tx_dma) != upper_32_bits(tx_dma + size - 1))
|
||||
goto out_err;
|
||||
netdev_dbg(bp->netdev, "Allocated %zu bytes for %u TX rings at %08lx (mapped %p)\n",
|
||||
size, bp->num_queues, (unsigned long)tx_dma, tx);
|
||||
|
||||
size = bp->num_queues * macb_rx_ring_size_per_queue(bp);
|
||||
rx = dma_alloc_coherent(dev, size, &rx_dma, GFP_KERNEL);
|
||||
if (!rx || upper_32_bits(rx_dma) != upper_32_bits(rx_dma + size - 1))
|
||||
if (!rx)
|
||||
goto out_err;
|
||||
/* Record the buffer so that the error path frees it. */
|
||||
bp->queues[0].rx_ring = rx;
|
||||
bp->queues[0].rx_ring_dma = rx_dma;
|
||||
if (upper_32_bits(rx_dma) != upper_32_bits(rx_dma + size - 1))
|
||||
goto out_err;
|
||||
netdev_dbg(bp->netdev, "Allocated %zu bytes for %u RX rings at %08lx (mapped %p)\n",
|
||||
size, bp->num_queues, (unsigned long)rx_dma, rx);
|
||||
|
|
|
|||
|
|
@ -2734,6 +2734,7 @@ static struct fman *read_dts_node(struct platform_device *of_dev)
|
|||
}
|
||||
|
||||
clk_rate = clk_get_rate(clk);
|
||||
clk_put(clk);
|
||||
if (!clk_rate) {
|
||||
err = -EINVAL;
|
||||
dev_err(&of_dev->dev, "%s: Failed to determine FM%d clock rate\n",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@
|
|||
#define GVE_TX_MAX_HDR_SIZE_DQO 255
|
||||
#define GVE_TX_MIN_TSO_MSS_DQO 88
|
||||
|
||||
/* HW limit. This also has to fit in the 14 bits of the mss field of
|
||||
* struct gve_tx_tso_context_desc_dqo.
|
||||
*/
|
||||
#define GVE_TX_MAX_TSO_MSS_DQO 9728
|
||||
|
||||
#ifndef __LITTLE_ENDIAN_BITFIELD
|
||||
#error "Only little endian supported"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -577,15 +577,18 @@ static int gve_prep_tso(struct sk_buff *skb)
|
|||
int header_len;
|
||||
int err;
|
||||
|
||||
/* Note: HW requires MSS (gso_size) to be <= 9728 and the total length
|
||||
* of the TSO to be <= 262143.
|
||||
/* Note: HW requires the total length of the TSO to be <= 262143,
|
||||
* this is enforced by netif_set_tso_max_size().
|
||||
*
|
||||
* However, we don't validate these because:
|
||||
* - Hypervisor enforces a limit of 9K MTU
|
||||
* - Kernel will not produce a TSO larger than 64k
|
||||
* MSS (gso_size) can not be trusted: packets forwarded from a tap or
|
||||
* injected by a packet socket can carry an arbitrary value, while the
|
||||
* mss field of the TSO context descriptor is only 14 bits wide.
|
||||
*
|
||||
* A too big MSS is dropped here instead of being rejected from
|
||||
* gve_features_check_dqo(), because software segmentation would
|
||||
* produce packets larger than the device can send.
|
||||
*/
|
||||
|
||||
if (unlikely(shinfo->gso_size < GVE_TX_MIN_TSO_MSS_DQO))
|
||||
if (unlikely(shinfo->gso_size > GVE_TX_MAX_TSO_MSS_DQO))
|
||||
return -1;
|
||||
|
||||
/* Needed because we will modify header. */
|
||||
|
|
@ -918,13 +921,22 @@ static bool gve_can_send_tso(const struct sk_buff *skb)
|
|||
{
|
||||
const int max_bufs_per_seg = GVE_TX_MAX_DATA_DESCS - 1;
|
||||
const struct skb_shared_info *shinfo = skb_shinfo(skb);
|
||||
const int header_len = skb_tcp_all_headers(skb);
|
||||
const int gso_size = shinfo->gso_size;
|
||||
int cur_seg_num_bufs;
|
||||
int prev_frag_size;
|
||||
int cur_seg_size;
|
||||
int header_len;
|
||||
int i;
|
||||
|
||||
if (unlikely(gso_size < GVE_TX_MIN_TSO_MSS_DQO))
|
||||
return false;
|
||||
|
||||
/* Must match the header length programmed by gve_prep_tso(). */
|
||||
if (skb_is_gso_tcp(skb))
|
||||
header_len = skb_tcp_all_headers(skb);
|
||||
else
|
||||
header_len = skb_transport_offset(skb) + sizeof(struct udphdr);
|
||||
|
||||
cur_seg_size = skb_headlen(skb) - header_len;
|
||||
prev_frag_size = skb_headlen(skb);
|
||||
cur_seg_num_bufs = cur_seg_size > 0;
|
||||
|
|
@ -966,7 +978,17 @@ netdev_features_t gve_features_check_dqo(struct sk_buff *skb,
|
|||
struct net_device *dev,
|
||||
netdev_features_t features)
|
||||
{
|
||||
if (skb_is_gso(skb) && !gve_can_send_tso(skb))
|
||||
if (!skb_is_gso(skb))
|
||||
return features;
|
||||
|
||||
/* Keep the GSO bits for a too big MSS, so that gve_prep_tso() drops
|
||||
* the packet: software segmentation would give packets larger than
|
||||
* the device can send.
|
||||
*/
|
||||
if (skb_shinfo(skb)->gso_size > GVE_TX_MAX_TSO_MSS_DQO)
|
||||
return features;
|
||||
|
||||
if (!gve_can_send_tso(skb))
|
||||
return features & ~NETIF_F_GSO_MASK;
|
||||
|
||||
return features;
|
||||
|
|
|
|||
|
|
@ -793,6 +793,7 @@ static int hns_mac_register_phy(struct hns_mac_cb *mac_cb)
|
|||
dev_err(mac_cb->dev,
|
||||
"mac%d mdio is NULL, dsaf will probe again later\n",
|
||||
mac_cb->mac_id);
|
||||
put_device(&pdev->dev);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
|
|
@ -801,6 +802,8 @@ static int hns_mac_register_phy(struct hns_mac_cb *mac_cb)
|
|||
dev_dbg(mac_cb->dev, "mac%d register phy addr:%d\n",
|
||||
mac_cb->mac_id, addr);
|
||||
|
||||
put_device(&pdev->dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3044,6 +3044,15 @@ static int emac_probe(struct platform_device *ofdev)
|
|||
if (err)
|
||||
goto err_gone;
|
||||
|
||||
if (emac_phy_supports_gige(dev->phy_mode)) {
|
||||
ndev->netdev_ops = &emac_gige_netdev_ops;
|
||||
dev->commac.ops = &emac_commac_sg_ops;
|
||||
} else {
|
||||
ndev->netdev_ops = &emac_netdev_ops;
|
||||
dev->commac.ops = &emac_commac_ops;
|
||||
}
|
||||
ndev->ethtool_ops = &emac_ethtool_ops;
|
||||
|
||||
dev->emacp = devm_platform_ioremap_resource(ofdev, 0);
|
||||
if (IS_ERR(dev->emacp)) {
|
||||
err = PTR_ERR(dev->emacp);
|
||||
|
|
@ -3076,7 +3085,6 @@ static int emac_probe(struct platform_device *ofdev)
|
|||
dev->mdio_instance = platform_get_drvdata(dev->mdio_dev);
|
||||
|
||||
/* Register with MAL */
|
||||
dev->commac.ops = &emac_commac_ops;
|
||||
dev->commac.dev = dev;
|
||||
dev->commac.tx_chan_mask = MAL_CHAN_MASK(dev->mal_tx_chan);
|
||||
dev->commac.rx_chan_mask = MAL_CHAN_MASK(dev->mal_rx_chan);
|
||||
|
|
@ -3144,12 +3152,6 @@ static int emac_probe(struct platform_device *ofdev)
|
|||
ndev->features |= ndev->hw_features | NETIF_F_RXCSUM;
|
||||
}
|
||||
ndev->watchdog_timeo = 5 * HZ;
|
||||
if (emac_phy_supports_gige(dev->phy_mode)) {
|
||||
ndev->netdev_ops = &emac_gige_netdev_ops;
|
||||
dev->commac.ops = &emac_commac_sg_ops;
|
||||
} else
|
||||
ndev->netdev_ops = &emac_netdev_ops;
|
||||
ndev->ethtool_ops = &emac_ethtool_ops;
|
||||
|
||||
/* MTU range: 46 - 1500 or whatever is in OF */
|
||||
ndev->min_mtu = EMAC_MIN_MTU;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include "rvu_struct.h"
|
||||
|
||||
#define OTX2_ALIGN 128 /* Align to cacheline */
|
||||
|
|
@ -44,6 +48,33 @@ struct qmem {
|
|||
u32 qsize;
|
||||
};
|
||||
|
||||
static inline void *otx2_dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle)
|
||||
{
|
||||
dma_addr_t dma_addr;
|
||||
void *vaddr;
|
||||
|
||||
vaddr = kzalloc(size, GFP_KERNEL);
|
||||
if (!vaddr)
|
||||
return NULL;
|
||||
|
||||
dma_addr = dma_map_single(dev, vaddr, size, DMA_BIDIRECTIONAL);
|
||||
if (dma_mapping_error(dev, dma_addr)) {
|
||||
kfree(vaddr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*dma_handle = dma_addr;
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
static inline void otx2_dma_free_coherent(struct device *dev, size_t size,
|
||||
void *vaddr, dma_addr_t dma_handle)
|
||||
{
|
||||
dma_unmap_single(dev, dma_handle, size, DMA_BIDIRECTIONAL);
|
||||
kfree(vaddr);
|
||||
}
|
||||
|
||||
static inline int qmem_alloc(struct device *dev, struct qmem **q,
|
||||
int qsize, int entry_sz)
|
||||
{
|
||||
|
|
@ -60,8 +91,11 @@ static inline int qmem_alloc(struct device *dev, struct qmem **q,
|
|||
|
||||
qmem->entry_sz = entry_sz;
|
||||
qmem->alloc_sz = (qsize * entry_sz) + OTX2_ALIGN;
|
||||
qmem->base = dma_alloc_attrs(dev, qmem->alloc_sz, &qmem->iova,
|
||||
GFP_KERNEL, DMA_ATTR_FORCE_CONTIGUOUS);
|
||||
|
||||
if (get_order(PAGE_ALIGN(qmem->alloc_sz)) > MAX_PAGE_ORDER)
|
||||
return -ENOMEM;
|
||||
|
||||
qmem->base = otx2_dma_alloc_coherent(dev, qmem->alloc_sz, &qmem->iova);
|
||||
if (!qmem->base)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -80,10 +114,9 @@ static inline void qmem_free(struct device *dev, struct qmem *qmem)
|
|||
return;
|
||||
|
||||
if (qmem->base)
|
||||
dma_free_attrs(dev, qmem->alloc_sz,
|
||||
qmem->base - qmem->align,
|
||||
qmem->iova - qmem->align,
|
||||
DMA_ATTR_FORCE_CONTIGUOUS);
|
||||
otx2_dma_free_coherent(dev, qmem->alloc_sz,
|
||||
qmem->base - qmem->align,
|
||||
qmem->iova - qmem->align);
|
||||
devm_kfree(dev, qmem);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -714,110 +714,72 @@ static int get_max_column_width(struct rvu *rvu)
|
|||
}
|
||||
|
||||
/* Dumps current provisioning status of all RVU block LFs */
|
||||
static ssize_t rvu_dbg_rsrc_attach_status(struct file *filp,
|
||||
char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
static int rvu_dbg_rsrc_attach_status(struct seq_file *filp, void *unused)
|
||||
{
|
||||
int index, off = 0, flag = 0, len = 0, i = 0;
|
||||
struct rvu *rvu = filp->private_data;
|
||||
int bytes_not_copied = 0;
|
||||
struct rvu *rvu = filp->private;
|
||||
int index, pf, vf, pcifunc;
|
||||
struct rvu_block block;
|
||||
int pf, vf, pcifunc;
|
||||
int buf_size = 2048;
|
||||
int lf_str_size;
|
||||
char *lfs;
|
||||
char *buf;
|
||||
|
||||
/* don't allow partial reads */
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
|
||||
buf = kzalloc(buf_size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get the maximum width of a column */
|
||||
lf_str_size = get_max_column_width(rvu);
|
||||
if (lf_str_size < 0)
|
||||
return lf_str_size;
|
||||
|
||||
lfs = kzalloc(lf_str_size, GFP_KERNEL);
|
||||
if (!lfs) {
|
||||
kfree(buf);
|
||||
if (!lfs)
|
||||
return -ENOMEM;
|
||||
}
|
||||
off += scnprintf(&buf[off], buf_size - 1 - off, "%-*s", lf_str_size,
|
||||
"pcifunc");
|
||||
|
||||
seq_printf(filp, "%-*s", lf_str_size, "pcifunc");
|
||||
for (index = 0; index < BLK_COUNT; index++)
|
||||
if (strlen(rvu->hw->block[index].name)) {
|
||||
off += scnprintf(&buf[off], buf_size - 1 - off,
|
||||
"%-*s", lf_str_size,
|
||||
rvu->hw->block[index].name);
|
||||
}
|
||||
if (strlen(rvu->hw->block[index].name))
|
||||
seq_printf(filp, "%-*s", lf_str_size,
|
||||
rvu->hw->block[index].name);
|
||||
|
||||
off += scnprintf(&buf[off], buf_size - 1 - off, "\n");
|
||||
bytes_not_copied = copy_to_user(buffer + (i * off), buf, off);
|
||||
if (bytes_not_copied)
|
||||
goto out;
|
||||
|
||||
i++;
|
||||
*ppos += off;
|
||||
seq_putc(filp, '\n');
|
||||
for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
|
||||
for (vf = 0; vf <= rvu->hw->total_vfs; vf++) {
|
||||
off = 0;
|
||||
flag = 0;
|
||||
pcifunc = rvu_make_pcifunc(rvu->pdev, pf, vf);
|
||||
if (!pcifunc)
|
||||
continue;
|
||||
|
||||
if (vf) {
|
||||
sprintf(lfs, "PF%d:VF%d", pf, vf - 1);
|
||||
off = scnprintf(&buf[off],
|
||||
buf_size - 1 - off,
|
||||
"%-*s", lf_str_size, lfs);
|
||||
} else {
|
||||
sprintf(lfs, "PF%d", pf);
|
||||
off = scnprintf(&buf[off],
|
||||
buf_size - 1 - off,
|
||||
"%-*s", lf_str_size, lfs);
|
||||
}
|
||||
|
||||
for (index = 0; index < BLK_COUNT; index++) {
|
||||
block = rvu->hw->block[index];
|
||||
if (!strlen(block.name))
|
||||
continue;
|
||||
len = 0;
|
||||
lfs[len] = '\0';
|
||||
lfs[0] = '\0';
|
||||
get_lf_str_list(&block, pcifunc, lfs);
|
||||
if (strlen(lfs))
|
||||
flag = 1;
|
||||
|
||||
off += scnprintf(&buf[off], buf_size - 1 - off,
|
||||
"%-*s", lf_str_size, lfs);
|
||||
break;
|
||||
}
|
||||
if (flag) {
|
||||
off += scnprintf(&buf[off],
|
||||
buf_size - 1 - off, "\n");
|
||||
bytes_not_copied = copy_to_user(buffer +
|
||||
(i * off),
|
||||
buf, off);
|
||||
if (bytes_not_copied)
|
||||
goto out;
|
||||
if (index == BLK_COUNT)
|
||||
continue;
|
||||
|
||||
i++;
|
||||
*ppos += off;
|
||||
if (vf)
|
||||
sprintf(lfs, "PF%d:VF%d", pf, vf - 1);
|
||||
else
|
||||
sprintf(lfs, "PF%d", pf);
|
||||
seq_printf(filp, "%-*s", lf_str_size, lfs);
|
||||
|
||||
for (index = 0; index < BLK_COUNT; index++) {
|
||||
block = rvu->hw->block[index];
|
||||
if (!strlen(block.name))
|
||||
continue;
|
||||
|
||||
lfs[0] = '\0';
|
||||
get_lf_str_list(&block, pcifunc, lfs);
|
||||
seq_printf(filp, "%-*s", lf_str_size, lfs);
|
||||
}
|
||||
seq_putc(filp, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(lfs);
|
||||
kfree(buf);
|
||||
if (bytes_not_copied)
|
||||
return -EFAULT;
|
||||
|
||||
return *ppos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
RVU_DEBUG_FOPS(rsrc_status, rsrc_attach_status, NULL);
|
||||
RVU_DEBUG_SEQ_FOPS(rsrc_status, rsrc_attach_status, NULL);
|
||||
|
||||
static int rvu_dbg_rvu_pf_cgx_map_display(struct seq_file *filp, void *unused)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4509,6 +4509,10 @@ static int mtk_unreg_dev(struct mtk_eth *eth)
|
|||
mac = netdev_priv(eth->netdev[i]);
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
|
||||
unregister_netdevice_notifier(&mac->device_notifier);
|
||||
|
||||
if (eth->netdev[i]->reg_state != NETREG_REGISTERED)
|
||||
continue;
|
||||
|
||||
unregister_netdev(eth->netdev[i]);
|
||||
}
|
||||
|
||||
|
|
@ -5344,7 +5348,7 @@ static int mtk_probe(struct platform_device *pdev)
|
|||
err = register_netdev(eth->netdev[i]);
|
||||
if (err) {
|
||||
dev_err(eth->dev, "error bringing up device\n");
|
||||
goto err_deinit_ppe;
|
||||
goto err_unreg_netdev;
|
||||
} else
|
||||
netif_info(eth, probe, eth->netdev[i],
|
||||
"mediatek frame engine at 0x%08lx, irq %d\n",
|
||||
|
|
|
|||
|
|
@ -85,9 +85,16 @@ mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(struct net_device *dev, struct m
|
|||
struct net_device *lower_dev;
|
||||
struct list_head *iter;
|
||||
|
||||
if (netif_is_lag_master(dev) || mlx5e_eswitch_rep(dev))
|
||||
return mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, esw, vport_num,
|
||||
esw_owner_vhca_id);
|
||||
if (netif_is_lag_master(dev) || mlx5e_eswitch_rep(dev)) {
|
||||
struct net_device *rep;
|
||||
|
||||
rep = mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, esw, vport_num,
|
||||
esw_owner_vhca_id);
|
||||
if (rep && !mlx5_esw_bridge_port_exists(*vport_num, *esw_owner_vhca_id,
|
||||
esw->br_offloads))
|
||||
return NULL;
|
||||
return rep;
|
||||
}
|
||||
|
||||
netdev_for_each_lower_dev(dev, lower_dev, iter) {
|
||||
struct net_device *rep;
|
||||
|
|
@ -104,6 +111,28 @@ mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(struct net_device *dev, struct m
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static bool mlx5_esw_bridge_rep_port_lookup(struct net_device *dev,
|
||||
struct mlx5_esw_bridge_offloads *br_offloads,
|
||||
u16 *vport_num, u16 *esw_owner_vhca_id)
|
||||
{
|
||||
if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, vport_num,
|
||||
esw_owner_vhca_id))
|
||||
return false;
|
||||
|
||||
return mlx5_esw_bridge_port_exists(*vport_num, *esw_owner_vhca_id, br_offloads);
|
||||
}
|
||||
|
||||
static bool mlx5_esw_bridge_lower_rep_port_lookup(struct net_device *dev,
|
||||
struct mlx5_esw_bridge_offloads *br_offloads,
|
||||
u16 *vport_num, u16 *esw_owner_vhca_id)
|
||||
{
|
||||
if (!mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(dev, br_offloads->esw, vport_num,
|
||||
esw_owner_vhca_id))
|
||||
return false;
|
||||
|
||||
return mlx5_esw_bridge_port_exists(*vport_num, *esw_owner_vhca_id, br_offloads);
|
||||
}
|
||||
|
||||
static bool mlx5_esw_bridge_is_local(struct net_device *dev, struct net_device *rep,
|
||||
struct mlx5_eswitch *esw)
|
||||
{
|
||||
|
|
@ -218,8 +247,7 @@ mlx5_esw_bridge_port_obj_add(struct net_device *dev,
|
|||
u16 vport_num, esw_owner_vhca_id;
|
||||
int err;
|
||||
|
||||
if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
|
||||
&esw_owner_vhca_id))
|
||||
if (!mlx5_esw_bridge_rep_port_lookup(dev, br_offloads, &vport_num, &esw_owner_vhca_id))
|
||||
return 0;
|
||||
|
||||
port_obj_info->handled = true;
|
||||
|
|
@ -251,8 +279,7 @@ mlx5_esw_bridge_port_obj_del(struct net_device *dev,
|
|||
const struct switchdev_obj_port_mdb *mdb;
|
||||
u16 vport_num, esw_owner_vhca_id;
|
||||
|
||||
if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
|
||||
&esw_owner_vhca_id))
|
||||
if (!mlx5_esw_bridge_rep_port_lookup(dev, br_offloads, &vport_num, &esw_owner_vhca_id))
|
||||
return 0;
|
||||
|
||||
port_obj_info->handled = true;
|
||||
|
|
@ -283,8 +310,8 @@ mlx5_esw_bridge_port_obj_attr_set(struct net_device *dev,
|
|||
u16 vport_num, esw_owner_vhca_id;
|
||||
int err = 0;
|
||||
|
||||
if (!mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
|
||||
&esw_owner_vhca_id))
|
||||
if (!mlx5_esw_bridge_lower_rep_port_lookup(dev, br_offloads, &vport_num,
|
||||
&esw_owner_vhca_id))
|
||||
return 0;
|
||||
|
||||
port_attr_info->handled = true;
|
||||
|
|
|
|||
|
|
@ -1082,6 +1082,9 @@ mlx5_tc_ct_shared_counter_get(struct mlx5_tc_ct_priv *ct_priv,
|
|||
|
||||
spin_unlock_bh(&ct_priv->ht_lock);
|
||||
|
||||
if (rev_entry)
|
||||
mlx5_tc_ct_entry_put(rev_entry);
|
||||
|
||||
create_counter:
|
||||
|
||||
shared_counter = mlx5_tc_ct_counter_create(ct_priv);
|
||||
|
|
|
|||
|
|
@ -1564,14 +1564,14 @@ static void setup_fte_addr6(struct mlx5_flow_spec *spec,
|
|||
memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value,
|
||||
outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6), saddr, 16);
|
||||
memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
|
||||
outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6), dmask, 16);
|
||||
outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6), smask, 16);
|
||||
}
|
||||
|
||||
if (!addr6_all_zero(daddr)) {
|
||||
memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value,
|
||||
outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), daddr, 16);
|
||||
memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
|
||||
outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), smask, 16);
|
||||
outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), dmask, 16);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1724,6 +1724,8 @@ void mlx5e_macsec_build_netdev(struct mlx5e_priv *priv)
|
|||
mlx5_core_dbg(priv->mdev, "mlx5e: MACsec acceleration enabled\n");
|
||||
netdev->macsec_ops = &macsec_offload_ops;
|
||||
netdev->features |= NETIF_F_HW_MACSEC;
|
||||
netdev->hw_features |= NETIF_F_HW_MACSEC;
|
||||
netdev->vlan_features |= NETIF_F_HW_MACSEC;
|
||||
netif_keep_dst(netdev);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5851,7 +5851,6 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev)
|
|||
|
||||
netdev->vlan_features |= NETIF_F_SG;
|
||||
netdev->vlan_features |= NETIF_F_HW_CSUM;
|
||||
netdev->vlan_features |= NETIF_F_HW_MACSEC;
|
||||
netdev->vlan_features |= NETIF_F_GRO;
|
||||
netdev->vlan_features |= NETIF_F_TSO;
|
||||
netdev->vlan_features |= NETIF_F_TSO6;
|
||||
|
|
|
|||
|
|
@ -1649,10 +1649,8 @@ int mlx5_esw_bridge_vport_unlink(struct net_device *br_netdev, u16 vport_num,
|
|||
int err;
|
||||
|
||||
port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
|
||||
if (!port) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Port is not attached to any bridge");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!port)
|
||||
return 0;
|
||||
if (port->bridge->ifindex != br_netdev->ifindex) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Port is attached to another bridge");
|
||||
return -EINVAL;
|
||||
|
|
@ -1682,10 +1680,19 @@ int mlx5_esw_bridge_vport_peer_unlink(struct net_device *br_netdev, u16 vport_nu
|
|||
struct mlx5_esw_bridge_offloads *br_offloads,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
if (!MLX5_CAP_ESW(br_offloads->esw->dev, merged_eswitch))
|
||||
return 0;
|
||||
|
||||
return mlx5_esw_bridge_vport_unlink(br_netdev, vport_num, esw_owner_vhca_id, br_offloads,
|
||||
extack);
|
||||
}
|
||||
|
||||
bool mlx5_esw_bridge_port_exists(u16 vport_num, u16 esw_owner_vhca_id,
|
||||
struct mlx5_esw_bridge_offloads *br_offloads)
|
||||
{
|
||||
return mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
|
||||
}
|
||||
|
||||
int mlx5_esw_bridge_port_vlan_add(u16 vport_num, u16 esw_owner_vhca_id, u16 vid, u16 flags,
|
||||
struct mlx5_esw_bridge_offloads *br_offloads,
|
||||
struct netlink_ext_ack *extack)
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ int mlx5_esw_bridge_vlan_proto_set(u16 vport_num, u16 esw_owner_vhca_id, u16 pro
|
|||
struct mlx5_esw_bridge_offloads *br_offloads);
|
||||
int mlx5_esw_bridge_mcast_set(u16 vport_num, u16 esw_owner_vhca_id, bool enable,
|
||||
struct mlx5_esw_bridge_offloads *br_offloads);
|
||||
bool mlx5_esw_bridge_port_exists(u16 vport_num, u16 esw_owner_vhca_id,
|
||||
struct mlx5_esw_bridge_offloads *br_offloads);
|
||||
int mlx5_esw_bridge_port_vlan_add(u16 vport_num, u16 esw_owner_vhca_id, u16 vid, u16 flags,
|
||||
struct mlx5_esw_bridge_offloads *br_offloads,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
|
|
|||
|
|
@ -1266,25 +1266,45 @@ void mlx5_lag_remove_devices(struct mlx5_lag *ldev)
|
|||
mlx5_lag_remove_devices_filter(ldev, MLX5_LAG_FILTER_PORTS);
|
||||
}
|
||||
|
||||
static int mlx5_lag_reload_ib_reps_idx(struct mlx5_lag *ldev, int idx,
|
||||
u32 flags)
|
||||
{
|
||||
struct lag_func *pf = mlx5_lag_pf(ldev, idx);
|
||||
struct mlx5_eswitch *esw;
|
||||
int ret;
|
||||
|
||||
if (pf->dev->priv.flags & flags)
|
||||
return 0;
|
||||
|
||||
esw = pf->dev->priv.eswitch;
|
||||
mlx5_esw_reps_block(esw);
|
||||
ret = mlx5_eswitch_reload_ib_reps(esw);
|
||||
mlx5_esw_reps_unblock(esw);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mlx5_lag_reload_ib_reps_unlocked(struct mlx5_lag *ldev, u32 flags,
|
||||
u32 filter, bool cont_on_fail)
|
||||
{
|
||||
struct lag_func *pf;
|
||||
int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1,
|
||||
filter);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
mlx5_lag_for_each(i, 0, ldev, filter) {
|
||||
pf = mlx5_lag_pf(ldev, i);
|
||||
if (!(pf->dev->priv.flags & flags)) {
|
||||
struct mlx5_eswitch *esw;
|
||||
if (master_idx < 0)
|
||||
return -EINVAL;
|
||||
|
||||
esw = pf->dev->priv.eswitch;
|
||||
mlx5_esw_reps_block(esw);
|
||||
ret = mlx5_eswitch_reload_ib_reps(esw);
|
||||
mlx5_esw_reps_unblock(esw);
|
||||
if (ret && !cont_on_fail)
|
||||
return ret;
|
||||
}
|
||||
ret = mlx5_lag_reload_ib_reps_idx(ldev, master_idx, flags);
|
||||
if (ret && !cont_on_fail)
|
||||
return ret;
|
||||
|
||||
mlx5_lag_for_each(i, 0, ldev, filter) {
|
||||
if (i == master_idx)
|
||||
continue;
|
||||
ret = mlx5_lag_reload_ib_reps_idx(ldev, i, flags);
|
||||
if (ret && !cont_on_fail)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -270,6 +270,7 @@ int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,
|
|||
pf->sd_fdb_active = false;
|
||||
}
|
||||
mlx5_lag_destroy_single_fdb_filter(ldev, group_id);
|
||||
mlx5_lag_unload_reps_from_locked(ldev, filter);
|
||||
}
|
||||
err_add_devices:
|
||||
mlx5_lag_add_devices_filter(ldev, filter);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ struct mlx5_devcom_comp {
|
|||
struct mlx5_devcom_key key;
|
||||
mlx5_devcom_event_handler_t handler;
|
||||
struct kref ref;
|
||||
int nr_devs;
|
||||
bool ready;
|
||||
struct rw_semaphore sem;
|
||||
struct lock_class_key lock_key;
|
||||
|
|
@ -170,6 +171,7 @@ devcom_alloc_comp_dev(struct mlx5_devcom_dev *devc,
|
|||
|
||||
down_write(&comp->sem);
|
||||
list_add_tail(&devcom->list, &comp->comp_dev_list_head);
|
||||
WRITE_ONCE(comp->nr_devs, comp->nr_devs + 1);
|
||||
up_write(&comp->sem);
|
||||
|
||||
return devcom;
|
||||
|
|
@ -182,6 +184,7 @@ devcom_free_comp_dev(struct mlx5_devcom_comp_dev *devcom)
|
|||
|
||||
down_write(&comp->sem);
|
||||
list_del(&devcom->list);
|
||||
WRITE_ONCE(comp->nr_devs, comp->nr_devs - 1);
|
||||
up_write(&comp->sem);
|
||||
|
||||
kref_put(&devcom->devc->ref, mlx5_devcom_dev_release);
|
||||
|
|
@ -284,7 +287,7 @@ int mlx5_devcom_comp_get_size(struct mlx5_devcom_comp_dev *devcom)
|
|||
{
|
||||
struct mlx5_devcom_comp *comp = devcom->comp;
|
||||
|
||||
return kref_read(&comp->ref);
|
||||
return READ_ONCE(comp->nr_devs);
|
||||
}
|
||||
|
||||
int mlx5_devcom_locked_send_event(struct mlx5_devcom_comp_dev *devcom,
|
||||
|
|
|
|||
|
|
@ -974,6 +974,7 @@ enum {
|
|||
/* PUL User Registers */
|
||||
#define FBNIC_CSR_START_PUL_USER 0x31000 /* CSR section delimiter */
|
||||
#define FBNIC_PUL_OB_TLP_HDR_AW_CFG 0x3103d /* 0xc40f4 */
|
||||
#define FBNIC_PUL_OB_TLP_HDR_AW_CFG_FLUSH_MODE CSR_BIT(20)
|
||||
#define FBNIC_PUL_OB_TLP_HDR_AW_CFG_FLUSH CSR_BIT(19)
|
||||
#define FBNIC_PUL_OB_TLP_HDR_AW_CFG_BME CSR_BIT(18)
|
||||
#define FBNIC_PUL_OB_TLP_HDR_AW_CFG_RDE_ATTR CSR_GENMASK(17, 15)
|
||||
|
|
@ -1215,6 +1216,10 @@ enum {
|
|||
#define FBNIC_IPC_MBX_DESC_LEN_MASK DESC_GENMASK(63, 48)
|
||||
#define FBNIC_IPC_MBX_DESC_EOM DESC_BIT(46)
|
||||
#define FBNIC_IPC_MBX_DESC_ADDR_MASK DESC_GENMASK(45, 3)
|
||||
/* Set with FW_CMPL when the FW completed a descriptor without successfully
|
||||
* processing it (e.g. a mailbox DMA error); the completion has no valid data.
|
||||
*/
|
||||
#define FBNIC_IPC_MBX_DESC_FW_ERR DESC_BIT(2)
|
||||
#define FBNIC_IPC_MBX_DESC_FW_CMPL DESC_BIT(1)
|
||||
#define FBNIC_IPC_MBX_DESC_HOST_CMPL DESC_BIT(0)
|
||||
|
||||
|
|
|
|||
|
|
@ -539,8 +539,8 @@ static void fbnic_dbg_fw_mbx_display(struct seq_file *s,
|
|||
/* Generate header */
|
||||
seq_puts(s, mbx_idx == FBNIC_IPC_MBX_RX_IDX ? "Rx\n" : "Tx\n");
|
||||
|
||||
seq_printf(s, "Rdy: %d Head: %d Tail: %d\n",
|
||||
mbx->ready, mbx->head, mbx->tail);
|
||||
seq_printf(s, "Rdy: %d Head: %d Tail: %d resp_error: %llu\n",
|
||||
mbx->ready, mbx->head, mbx->tail, mbx->resp_error);
|
||||
|
||||
snprintf(hdr, sizeof(hdr), "%3s %-4s %s %-12s %s %-3s %-16s\n",
|
||||
"Idx", "Len", "E", "Addr", "F", "H", "Raw");
|
||||
|
|
|
|||
|
|
@ -313,6 +313,11 @@ fbnic_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
|
|||
kernel_ring->hds_thresh = fbn->hds_thresh;
|
||||
}
|
||||
|
||||
static u32 fbnic_ring_size_pow2(u32 size)
|
||||
{
|
||||
return size ? roundup_pow_of_two(size) : 0;
|
||||
}
|
||||
|
||||
static void fbnic_set_rings(struct fbnic_net *fbn,
|
||||
struct ethtool_ringparam *ring,
|
||||
struct kernel_ethtool_ringparam *kernel_ring)
|
||||
|
|
@ -334,10 +339,10 @@ fbnic_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
|
|||
struct fbnic_net *clone;
|
||||
int err;
|
||||
|
||||
ring->rx_pending = roundup_pow_of_two(ring->rx_pending);
|
||||
ring->rx_mini_pending = roundup_pow_of_two(ring->rx_mini_pending);
|
||||
ring->rx_jumbo_pending = roundup_pow_of_two(ring->rx_jumbo_pending);
|
||||
ring->tx_pending = roundup_pow_of_two(ring->tx_pending);
|
||||
ring->rx_pending = fbnic_ring_size_pow2(ring->rx_pending);
|
||||
ring->rx_mini_pending = fbnic_ring_size_pow2(ring->rx_mini_pending);
|
||||
ring->rx_jumbo_pending = fbnic_ring_size_pow2(ring->rx_jumbo_pending);
|
||||
ring->tx_pending = fbnic_ring_size_pow2(ring->tx_pending);
|
||||
|
||||
/* These are absolute minimums allowing the device and driver to operate
|
||||
* but not necessarily guarantee reasonable performance. Settings below
|
||||
|
|
@ -2025,7 +2030,8 @@ static const struct ethtool_ops fbnic_ethtool_ops = {
|
|||
ETHTOOL_OP_NEEDS_RTNL_SPAUSEPARAM |
|
||||
ETHTOOL_OP_NEEDS_RTNL_SCHANNELS |
|
||||
ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM |
|
||||
ETHTOOL_OP_NEEDS_RTNL_GLINK,
|
||||
ETHTOOL_OP_NEEDS_RTNL_GLINK |
|
||||
ETHTOOL_OP_NEEDS_RTNL_TEST,
|
||||
.get_drvinfo = fbnic_get_drvinfo,
|
||||
.get_regs_len = fbnic_get_regs_len,
|
||||
.get_regs = fbnic_get_regs,
|
||||
|
|
|
|||
|
|
@ -60,8 +60,15 @@ static void fbnic_mbx_reset_desc_ring(struct fbnic_dev *fbd, int mbx_idx)
|
|||
*/
|
||||
switch (mbx_idx) {
|
||||
case FBNIC_IPC_MBX_RX_IDX:
|
||||
/* Clearing BME blocks the device from writing to the host
|
||||
* but leaves the requests parked in the write pipeline. The
|
||||
* write path only clears outstanding requests when both FLUSH
|
||||
* and FLUSH_MODE are set; FLUSH_MODE lets them drain without
|
||||
* landing on the host.
|
||||
*/
|
||||
wr32(fbd, FBNIC_PUL_OB_TLP_HDR_AW_CFG,
|
||||
FBNIC_PUL_OB_TLP_HDR_AW_CFG_FLUSH);
|
||||
FBNIC_PUL_OB_TLP_HDR_AW_CFG_FLUSH |
|
||||
FBNIC_PUL_OB_TLP_HDR_AW_CFG_FLUSH_MODE);
|
||||
break;
|
||||
case FBNIC_IPC_MBX_TX_IDX:
|
||||
wr32(fbd, FBNIC_PUL_OB_TLP_HDR_AR_CFG,
|
||||
|
|
@ -285,6 +292,12 @@ static void fbnic_mbx_process_tx_msgs(struct fbnic_dev *fbd)
|
|||
if (!(desc & FBNIC_IPC_MBX_DESC_FW_CMPL))
|
||||
break;
|
||||
|
||||
if (desc & FBNIC_IPC_MBX_DESC_FW_ERR) {
|
||||
tx_mbx->resp_error++;
|
||||
dev_warn_ratelimited(fbd->dev,
|
||||
"FW completed a Tx mailbox request with an error\n");
|
||||
}
|
||||
|
||||
fbnic_mbx_unmap_and_free_msg(fbd, FBNIC_IPC_MBX_TX_IDX, head);
|
||||
|
||||
head++;
|
||||
|
|
@ -1666,6 +1679,13 @@ static void fbnic_mbx_process_rx_msgs(struct fbnic_dev *fbd)
|
|||
if (!(desc & FBNIC_IPC_MBX_DESC_FW_CMPL))
|
||||
break;
|
||||
|
||||
if (desc & FBNIC_IPC_MBX_DESC_FW_ERR) {
|
||||
rx_mbx->resp_error++;
|
||||
dev_warn_ratelimited(fbd->dev,
|
||||
"FW reported an error on an Rx mailbox message; dropping\n");
|
||||
goto next_page;
|
||||
}
|
||||
|
||||
dma_sync_single_for_cpu(fbd->dev, rx_mbx->buf_info[head].addr,
|
||||
FBNIC_RX_PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
|
||||
|
|
@ -1733,7 +1753,9 @@ void fbnic_mbx_poll(struct fbnic_dev *fbd)
|
|||
int fbnic_mbx_poll_tx_ready(struct fbnic_dev *fbd)
|
||||
{
|
||||
struct fbnic_fw_mbx *tx_mbx = &fbd->mbx[FBNIC_IPC_MBX_TX_IDX];
|
||||
struct fbnic_fw_mbx *rx_mbx = &fbd->mbx[FBNIC_IPC_MBX_RX_IDX];
|
||||
unsigned long timeout = jiffies + 10 * HZ + 1;
|
||||
u64 tx_resp_error, rx_resp_error;
|
||||
int err, i;
|
||||
|
||||
do {
|
||||
|
|
@ -1764,6 +1786,9 @@ int fbnic_mbx_poll_tx_ready(struct fbnic_dev *fbd)
|
|||
* mgmt.version once we get the actual version from the firmware
|
||||
* in the capabilities request message.
|
||||
*/
|
||||
send_cap_req:
|
||||
tx_resp_error = tx_mbx->resp_error;
|
||||
rx_resp_error = rx_mbx->resp_error;
|
||||
err = fbnic_fw_xmit_simple_msg(fbd, FBNIC_TLV_MSG_ID_HOST_CAP_REQ);
|
||||
if (err)
|
||||
goto clean_mbx;
|
||||
|
|
@ -1781,9 +1806,27 @@ int fbnic_mbx_poll_tx_ready(struct fbnic_dev *fbd)
|
|||
msleep(20);
|
||||
fbnic_mbx_poll(fbd);
|
||||
|
||||
/* A valid capabilities response ends the poll. Check it
|
||||
* before the FW_ERR retry below so a response parsed in the
|
||||
* same poll as an unrelated FW_ERR is not discarded.
|
||||
*/
|
||||
if (fbd->fw_cap.running.mgmt.version >= MIN_FW_VER_CODE)
|
||||
break;
|
||||
|
||||
/* set err, but wait till mgmt.version check to report it */
|
||||
if (!time_is_after_jiffies(timeout))
|
||||
if (!time_is_after_jiffies(timeout)) {
|
||||
err = -ETIMEDOUT;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The FW can flag our capabilities request (Tx) or its
|
||||
* response (Rx) with FW_ERR, in which case it produced no
|
||||
* usable response. The ring is not wedged, so re-issue the
|
||||
* request instead of spinning until the timeout.
|
||||
*/
|
||||
if (tx_mbx->resp_error != tx_resp_error ||
|
||||
rx_mbx->resp_error != rx_resp_error)
|
||||
goto send_cap_req;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ struct fbnic_tlv_msg;
|
|||
|
||||
struct fbnic_fw_mbx {
|
||||
u8 ready, head, tail;
|
||||
u64 resp_error;
|
||||
struct {
|
||||
struct fbnic_tlv_msg *msg;
|
||||
dma_addr_t addr;
|
||||
|
|
|
|||
|
|
@ -434,6 +434,7 @@ static int fbnic_pm_suspend(struct device *dev)
|
|||
{
|
||||
struct fbnic_dev *fbd = dev_get_drvdata(dev);
|
||||
struct net_device *netdev = fbd->netdev;
|
||||
struct fbnic_net *fbn;
|
||||
|
||||
if (fbnic_init_failure(fbd))
|
||||
goto null_uc_addr;
|
||||
|
|
@ -441,11 +442,16 @@ static int fbnic_pm_suspend(struct device *dev)
|
|||
rtnl_lock();
|
||||
netdev_lock(netdev);
|
||||
|
||||
fbn = netdev_priv(netdev);
|
||||
|
||||
netif_device_detach(netdev);
|
||||
|
||||
if (netif_running(netdev))
|
||||
netdev->netdev_ops->ndo_stop(netdev);
|
||||
|
||||
/* The IRQs are about to be freed, so drop the napi vector count */
|
||||
fbn->num_napi = 0;
|
||||
|
||||
netdev_unlock(netdev);
|
||||
rtnl_unlock();
|
||||
|
||||
|
|
@ -508,16 +514,20 @@ static int __fbnic_pm_resume(struct device *dev)
|
|||
if (fbnic_init_failure(fbd))
|
||||
return 0;
|
||||
|
||||
rtnl_lock();
|
||||
netdev_lock(netdev);
|
||||
|
||||
fbn = netdev_priv(netdev);
|
||||
|
||||
/* Reset the queues if needed */
|
||||
fbnic_reset_queues(fbn, fbn->num_tx_queues, fbn->num_rx_queues);
|
||||
|
||||
rtnl_lock();
|
||||
netdev_lock(netdev);
|
||||
|
||||
if (netif_running(netdev))
|
||||
if (netif_running(netdev)) {
|
||||
err = __fbnic_open(fbn);
|
||||
/* On failure the vectors are freed, so drop the count */
|
||||
if (err)
|
||||
fbn->num_napi = 0;
|
||||
}
|
||||
|
||||
netdev_unlock(netdev);
|
||||
rtnl_unlock();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/iopoll.h>
|
||||
#include <linux/pci.h>
|
||||
#include <net/netdev_queues.h>
|
||||
#include <net/netdev_rx_queue.h>
|
||||
#include <net/page_pool/helpers.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/xdp.h>
|
||||
|
|
@ -1622,7 +1623,7 @@ fbnic_alloc_qt_page_pools(struct fbnic_net *fbn, struct fbnic_q_triad *qt,
|
|||
return 0;
|
||||
|
||||
err_destroy_sub0:
|
||||
page_pool_destroy(pp);
|
||||
page_pool_destroy(qt->sub0.page_pool);
|
||||
return PTR_ERR(pp);
|
||||
}
|
||||
|
||||
|
|
@ -1791,7 +1792,7 @@ int fbnic_alloc_napi_vectors(struct fbnic_net *fbn)
|
|||
int err;
|
||||
|
||||
/* Allocate 1 Tx queue per napi vector */
|
||||
if (num_napi < FBNIC_MAX_TXQS && num_napi == num_tx + num_rx) {
|
||||
if (num_napi <= FBNIC_MAX_TXQS && num_napi == num_tx + num_rx) {
|
||||
while (num_tx) {
|
||||
err = fbnic_alloc_napi_vector(fbd, fbn,
|
||||
num_napi, v_idx,
|
||||
|
|
@ -2853,6 +2854,17 @@ void fbnic_napi_depletion_check(struct net_device *netdev)
|
|||
fbnic_wrfl(fbd);
|
||||
}
|
||||
|
||||
/* Returns the napi vector servicing an Rx queue, or NULL if the datapath
|
||||
* is torn down. The association is published by fbnic_set_netif_napi()
|
||||
* and cleared by fbnic_reset_netif_napi(), both under the instance lock.
|
||||
*/
|
||||
static struct fbnic_napi_vector *fbnic_rxq_nv(struct net_device *dev, int idx)
|
||||
{
|
||||
struct napi_struct *napi = __netif_get_rx_queue(dev, idx)->napi;
|
||||
|
||||
return napi ? container_of(napi, struct fbnic_napi_vector, napi) : NULL;
|
||||
}
|
||||
|
||||
static int fbnic_queue_mem_alloc(struct net_device *dev,
|
||||
struct netdev_queue_config *qcfg,
|
||||
void *qmem, int idx)
|
||||
|
|
@ -2865,8 +2877,16 @@ static int fbnic_queue_mem_alloc(struct net_device *dev,
|
|||
if (!netif_running(dev))
|
||||
return fbnic_alloc_qt_page_pools(fbn, qt, idx);
|
||||
|
||||
/* A failed PCIe recovery or resume can leave the datapath torn down
|
||||
* while netif_running() is still true. This ndo runs before
|
||||
* netdev_rx_queue_restart() checks netif_running(), so bail out
|
||||
* rather than touching rings and vectors that are already freed.
|
||||
*/
|
||||
nv = fbnic_rxq_nv(dev, idx);
|
||||
if (!nv)
|
||||
return -ENETDOWN;
|
||||
|
||||
real = container_of(fbn->rx[idx], struct fbnic_q_triad, cmpl);
|
||||
nv = fbn->napi[idx % fbn->num_napi];
|
||||
|
||||
fbnic_ring_init(&qt->sub0, real->sub0.doorbell, real->sub0.q_idx,
|
||||
real->sub0.flags);
|
||||
|
|
@ -2917,7 +2937,7 @@ static int fbnic_queue_start(struct net_device *dev,
|
|||
struct fbnic_q_triad *real;
|
||||
|
||||
real = container_of(fbn->rx[idx], struct fbnic_q_triad, cmpl);
|
||||
nv = fbn->napi[idx % fbn->num_napi];
|
||||
nv = fbnic_rxq_nv(dev, idx);
|
||||
|
||||
fbnic_aggregate_ring_bdq_counters(fbn, &real->sub0);
|
||||
fbnic_aggregate_ring_bdq_counters(fbn, &real->sub1);
|
||||
|
|
@ -2939,7 +2959,7 @@ static int fbnic_queue_stop(struct net_device *dev, void *qmem, int idx)
|
|||
int err;
|
||||
|
||||
real = container_of(fbn->rx[idx], struct fbnic_q_triad, cmpl);
|
||||
nv = fbn->napi[idx % fbn->num_napi];
|
||||
nv = fbnic_rxq_nv(dev, idx);
|
||||
fbnic_dbg_nv_exit(nv);
|
||||
|
||||
napi_disable_locked(&nv->napi);
|
||||
|
|
|
|||
|
|
@ -625,11 +625,12 @@ int nfp_net_ipsec_rx(struct nfp_meta_parsed *meta, struct sk_buff *skb)
|
|||
|
||||
xa_lock(&nn->xa_ipsec);
|
||||
x = xa_load(&nn->xa_ipsec, saidx);
|
||||
if (x)
|
||||
xfrm_state_hold(x);
|
||||
xa_unlock(&nn->xa_ipsec);
|
||||
if (!x)
|
||||
return -EINVAL;
|
||||
|
||||
xfrm_state_hold(x);
|
||||
sp->xvec[sp->len++] = x;
|
||||
sp->olen++;
|
||||
xo = xfrm_offload(skb);
|
||||
|
|
|
|||
|
|
@ -904,6 +904,10 @@ static const struct pci_device_id efx_pci_table[] = {
|
|||
.driver_data = (unsigned long)&efx_x4_nic_type},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x2c03), /* X4 PF (FF only) */
|
||||
.driver_data = (unsigned long)&efx_x4_nic_type},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x8c03), /* X4D PF (FF/LL) */
|
||||
.driver_data = (unsigned long)&efx_x4_nic_type},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0xac03), /* X4D PF (FF only) */
|
||||
.driver_data = (unsigned long)&efx_x4_nic_type},
|
||||
{0} /* end of list */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -803,6 +803,9 @@ static void emac_tx_mem_map(struct emac_priv *priv, struct sk_buff *skb)
|
|||
while (i != head) {
|
||||
emac_free_tx_buf(priv, i);
|
||||
|
||||
tx_desc_addr = &((struct emac_desc *)tx_ring->desc_addr)[i];
|
||||
memset(tx_desc_addr, 0, sizeof(*tx_desc_addr));
|
||||
|
||||
if (++i == tx_ring->total_cnt)
|
||||
i = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1162,8 +1162,11 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable)
|
|||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(bsp_priv->clk_phy);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
clk_bulk_disable_unprepare(bsp_priv->num_clks,
|
||||
bsp_priv->clks);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rk_configure_io_clksel(bsp_priv);
|
||||
rk_ungate_rmii_clock(bsp_priv);
|
||||
|
|
|
|||
|
|
@ -474,11 +474,11 @@ static void dwmac4_set_sarc(struct dma_desc *p, u32 sarc_type)
|
|||
sarc_type));
|
||||
}
|
||||
|
||||
static int set_16kib_bfsize(int mtu)
|
||||
static int set_16kib_bfsize(int len)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (unlikely(mtu >= BUF_SIZE_8KiB))
|
||||
if (unlikely(len > BUF_SIZE_8KiB))
|
||||
ret = BUF_SIZE_16KiB;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -540,7 +540,7 @@ struct stmmac_mode_ops {
|
|||
bool (*is_jumbo_frm)(unsigned int len, bool enh_desc);
|
||||
int (*jumbo_frm)(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
|
||||
int csum);
|
||||
int (*set_16kib_bfsize)(int mtu);
|
||||
int (*set_16kib_bfsize)(int len);
|
||||
void (*init_desc3)(struct dma_desc *p);
|
||||
void (*refill_desc3)(struct stmmac_rx_queue *rx_q, struct dma_desc *p);
|
||||
void (*clean_desc3)(struct stmmac_tx_queue *tx_q, struct dma_desc *p);
|
||||
|
|
|
|||
|
|
@ -124,10 +124,10 @@ static void clean_desc3(struct stmmac_tx_queue *tx_q, struct dma_desc *p)
|
|||
p->des3 = 0;
|
||||
}
|
||||
|
||||
static int set_16kib_bfsize(int mtu)
|
||||
static int set_16kib_bfsize(int len)
|
||||
{
|
||||
int ret = 0;
|
||||
if (unlikely(mtu > BUF_SIZE_8KiB))
|
||||
if (unlikely(len > BUF_SIZE_8KiB))
|
||||
ret = BUF_SIZE_16KiB;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1536,17 +1536,17 @@ static unsigned int stmmac_rx_offset(struct stmmac_priv *priv)
|
|||
return NET_SKB_PAD + NET_IP_ALIGN;
|
||||
}
|
||||
|
||||
static int stmmac_set_bfsize(int mtu)
|
||||
static int stmmac_set_bfsize(int len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (mtu >= BUF_SIZE_8KiB)
|
||||
if (len > BUF_SIZE_8KiB)
|
||||
ret = BUF_SIZE_16KiB;
|
||||
else if (mtu >= BUF_SIZE_4KiB)
|
||||
else if (len > BUF_SIZE_4KiB)
|
||||
ret = BUF_SIZE_8KiB;
|
||||
else if (mtu >= BUF_SIZE_2KiB)
|
||||
else if (len > BUF_SIZE_2KiB)
|
||||
ret = BUF_SIZE_4KiB;
|
||||
else if (mtu > DEFAULT_BUFSIZE)
|
||||
else if (len > DEFAULT_BUFSIZE)
|
||||
ret = BUF_SIZE_2KiB;
|
||||
else
|
||||
ret = DEFAULT_BUFSIZE;
|
||||
|
|
@ -4063,7 +4063,7 @@ static struct stmmac_dma_conf *
|
|||
stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
|
||||
{
|
||||
struct stmmac_dma_conf *dma_conf;
|
||||
int bfsize, ret;
|
||||
int bfsize, len, ret;
|
||||
u8 chan;
|
||||
|
||||
dma_conf = kzalloc_obj(*dma_conf);
|
||||
|
|
@ -4073,13 +4073,15 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
|
|||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/* Returns 0 or BUF_SIZE_16KiB if mtu > 8KiB and dwmac4 or ring mode */
|
||||
bfsize = stmmac_set_16kib_bfsize(priv, mtu);
|
||||
len = mtu + ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN;
|
||||
|
||||
/* Returns 0 or BUF_SIZE_16KiB if len > 8KiB and dwmac4 or ring mode */
|
||||
bfsize = stmmac_set_16kib_bfsize(priv, len);
|
||||
if (bfsize < 0)
|
||||
bfsize = 0;
|
||||
|
||||
if (bfsize < BUF_SIZE_16KiB)
|
||||
bfsize = stmmac_set_bfsize(mtu);
|
||||
bfsize = stmmac_set_bfsize(len);
|
||||
|
||||
dma_conf->dma_buf_sz = bfsize;
|
||||
/* Chose the tx/rx size from the already defined one in the
|
||||
|
|
@ -5887,6 +5889,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
if (!skb) {
|
||||
page_pool_recycle_direct(rx_q->page_pool,
|
||||
buf->page);
|
||||
buf->page = NULL;
|
||||
rx_dropped++;
|
||||
count++;
|
||||
goto drain_data;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/ethtool.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/udp.h>
|
||||
#include <net/dsa.h>
|
||||
#include <net/pkt_cls.h>
|
||||
#include <net/pkt_sched.h>
|
||||
#include <net/tcp.h>
|
||||
|
|
@ -29,6 +30,7 @@ struct stmmachdr {
|
|||
sizeof(struct stmmachdr))
|
||||
#define STMMAC_TEST_PKT_MAGIC 0xdeadcafecafedeadULL
|
||||
#define STMMAC_LB_TIMEOUT msecs_to_jiffies(200)
|
||||
#define STMMAC_SFT_MAX_LPI (5 * USEC_PER_SEC)
|
||||
|
||||
struct stmmac_packet_attrs {
|
||||
int vlan;
|
||||
|
|
@ -237,6 +239,10 @@ struct stmmac_test_priv {
|
|||
struct stmmac_packet_attrs *packet;
|
||||
struct packet_type pt;
|
||||
struct completion comp;
|
||||
__be16 packet_type;
|
||||
int (*func)(struct sk_buff *skb, struct net_device *ndev,
|
||||
struct packet_type *pt, struct net_device *orig_ndev);
|
||||
bool capture_all;
|
||||
int double_vlan;
|
||||
int vlan_id;
|
||||
int ok;
|
||||
|
|
@ -316,6 +322,52 @@ static int stmmac_test_loopback_validate(struct sk_buff *skb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int stmmac_sft_filter(struct sk_buff *skb, struct net_device *ndev,
|
||||
struct packet_type *pt,
|
||||
struct net_device *orig_ndev)
|
||||
{
|
||||
struct stmmac_test_priv *tpriv = pt->af_packet_priv;
|
||||
struct ethhdr *hdr = eth_hdr(skb);
|
||||
int ret = 0;
|
||||
|
||||
if (hdr->h_proto == tpriv->packet_type) {
|
||||
struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
|
||||
|
||||
if (nskb)
|
||||
ret = tpriv->func(nskb, ndev, pt, orig_ndev);
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void stmmac_sft_add_pack(struct packet_type *pt)
|
||||
{
|
||||
struct stmmac_test_priv *tpriv = pt->af_packet_priv;
|
||||
|
||||
if (netdev_uses_dsa(tpriv->pt.dev) || tpriv->capture_all) {
|
||||
tpriv->packet_type = tpriv->pt.type;
|
||||
tpriv->func = tpriv->pt.func;
|
||||
|
||||
/* DSA conduit will report ETH_P_XDSA, so our packet handler
|
||||
* won't match. Let's register a ETH_P_ALL match and filter
|
||||
* manually in stmmac_sft_filter. This is also useful for
|
||||
* VLAN tests, to capture packets otherwise marked as
|
||||
* OTHERHOST.
|
||||
*/
|
||||
tpriv->pt.type = htons(ETH_P_ALL);
|
||||
tpriv->pt.func = stmmac_sft_filter;
|
||||
tpriv->pt.ignore_outgoing = true;
|
||||
}
|
||||
|
||||
dev_add_pack(pt);
|
||||
}
|
||||
|
||||
static void stmmac_sft_remove_pack(struct packet_type *pt)
|
||||
{
|
||||
dev_remove_pack(pt);
|
||||
}
|
||||
|
||||
static int __stmmac_test_loopback(struct stmmac_priv *priv,
|
||||
struct stmmac_packet_attrs *attr)
|
||||
{
|
||||
|
|
@ -337,7 +389,7 @@ static int __stmmac_test_loopback(struct stmmac_priv *priv,
|
|||
tpriv->packet = attr;
|
||||
|
||||
if (!attr->dont_wait)
|
||||
dev_add_pack(&tpriv->pt);
|
||||
stmmac_sft_add_pack(&tpriv->pt);
|
||||
|
||||
skb = stmmac_test_get_udp_skb(priv, attr);
|
||||
if (!skb) {
|
||||
|
|
@ -360,7 +412,7 @@ static int __stmmac_test_loopback(struct stmmac_priv *priv,
|
|||
|
||||
cleanup:
|
||||
if (!attr->dont_wait)
|
||||
dev_remove_pack(&tpriv->pt);
|
||||
stmmac_sft_remove_pack(&tpriv->pt);
|
||||
kfree(tpriv);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -414,12 +466,16 @@ static int stmmac_test_mmc(struct stmmac_priv *priv)
|
|||
static int stmmac_test_eee(struct stmmac_priv *priv)
|
||||
{
|
||||
struct stmmac_extra_stats *initial, *final;
|
||||
int retries = 10;
|
||||
unsigned long timeout, max_duration;
|
||||
int ret;
|
||||
|
||||
if (!priv->dma_cap.eee || !priv->eee_active)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Bail out if the configured LPI timer is too long */
|
||||
if (priv->tx_lpi_timer > STMMAC_SFT_MAX_LPI)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
initial = kzalloc_obj(*initial);
|
||||
if (!initial)
|
||||
return -ENOMEM;
|
||||
|
|
@ -430,14 +486,21 @@ static int stmmac_test_eee(struct stmmac_priv *priv)
|
|||
goto out_free_initial;
|
||||
}
|
||||
|
||||
/* Snapshot stats, we want to count the in_lpi events. We may enter
|
||||
* LPI just after the packet was sent.
|
||||
*/
|
||||
memcpy(initial, &priv->xstats, sizeof(*initial));
|
||||
|
||||
/* Send a frame, then wait to enter LPI */
|
||||
ret = stmmac_test_mac_loopback(priv);
|
||||
if (ret)
|
||||
goto out_free_final;
|
||||
|
||||
max_duration = usecs_to_jiffies(2 * priv->tx_lpi_timer);
|
||||
|
||||
/* We have no traffic in the line so, sooner or later it will go LPI */
|
||||
while (--retries) {
|
||||
timeout = jiffies + max_duration;
|
||||
while (!time_after(jiffies, timeout)) {
|
||||
memcpy(final, &priv->xstats, sizeof(*final));
|
||||
|
||||
if (final->irq_tx_path_in_lpi_mode_n >
|
||||
|
|
@ -446,20 +509,38 @@ static int stmmac_test_eee(struct stmmac_priv *priv)
|
|||
msleep(100);
|
||||
}
|
||||
|
||||
if (!retries) {
|
||||
memcpy(final, &priv->xstats, sizeof(*final));
|
||||
if (final->irq_tx_path_in_lpi_mode_n <=
|
||||
initial->irq_tx_path_in_lpi_mode_n) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto out_free_final;
|
||||
}
|
||||
|
||||
if (final->irq_tx_path_in_lpi_mode_n <=
|
||||
initial->irq_tx_path_in_lpi_mode_n) {
|
||||
ret = -EINVAL;
|
||||
/* Re-snapshot, as we want to measure exit_lpi events. We should be
|
||||
* in LPI right now.
|
||||
*/
|
||||
memcpy(initial, &priv->xstats, sizeof(*initial));
|
||||
|
||||
/* TX something so we go out of LPI */
|
||||
ret = stmmac_test_mac_loopback(priv);
|
||||
if (ret)
|
||||
goto out_free_final;
|
||||
|
||||
/* Wait for the exit LPI interrupt */
|
||||
timeout = jiffies + max_duration;
|
||||
while (!time_after(jiffies, timeout)) {
|
||||
memcpy(final, &priv->xstats, sizeof(*final));
|
||||
|
||||
if (final->irq_tx_path_exit_lpi_mode_n >
|
||||
initial->irq_tx_path_exit_lpi_mode_n)
|
||||
break;
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
memcpy(final, &priv->xstats, sizeof(*final));
|
||||
if (final->irq_tx_path_exit_lpi_mode_n <=
|
||||
initial->irq_tx_path_exit_lpi_mode_n) {
|
||||
ret = -EINVAL;
|
||||
ret = -ETIMEDOUT;
|
||||
goto out_free_final;
|
||||
}
|
||||
|
||||
|
|
@ -767,7 +848,7 @@ static int stmmac_test_flowctrl(struct stmmac_priv *priv)
|
|||
tpriv->pt.func = stmmac_test_flowctrl_validate;
|
||||
tpriv->pt.dev = priv->dev;
|
||||
tpriv->pt.af_packet_priv = tpriv;
|
||||
dev_add_pack(&tpriv->pt);
|
||||
stmmac_sft_add_pack(&tpriv->pt);
|
||||
|
||||
/* Compute minimum number of packets to make FIFO full */
|
||||
pkt_count = rx_fifo_size;
|
||||
|
|
@ -823,7 +904,7 @@ static int stmmac_test_flowctrl(struct stmmac_priv *priv)
|
|||
cleanup:
|
||||
dev_mc_del(priv->dev, paddr);
|
||||
dev_set_promiscuity(priv->dev, -1);
|
||||
dev_remove_pack(&tpriv->pt);
|
||||
stmmac_sft_remove_pack(&tpriv->pt);
|
||||
kfree(tpriv);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -865,6 +946,11 @@ static int stmmac_test_vlan_validate(struct sk_buff *skb,
|
|||
goto out;
|
||||
if (skb_headlen(skb) < (STMMAC_TEST_PKT_SIZE - ETH_HLEN))
|
||||
goto out;
|
||||
|
||||
ehdr = (struct ethhdr *)skb_mac_header(skb);
|
||||
if (!ether_addr_equal_unaligned(ehdr->h_dest, tpriv->packet->dst))
|
||||
goto out;
|
||||
|
||||
if (tpriv->vlan_id) {
|
||||
if (skb->vlan_proto != htons(proto))
|
||||
goto out;
|
||||
|
|
@ -876,10 +962,6 @@ static int stmmac_test_vlan_validate(struct sk_buff *skb,
|
|||
}
|
||||
}
|
||||
|
||||
ehdr = (struct ethhdr *)skb_mac_header(skb);
|
||||
if (!ether_addr_equal_unaligned(ehdr->h_dest, tpriv->packet->dst))
|
||||
goto out;
|
||||
|
||||
ihdr = ip_hdr(skb);
|
||||
if (tpriv->double_vlan)
|
||||
ihdr = (struct iphdr *)(skb_network_header(skb) + 4);
|
||||
|
|
@ -921,6 +1003,7 @@ static int __stmmac_test_vlanfilt(struct stmmac_priv *priv)
|
|||
tpriv->pt.dev = priv->dev;
|
||||
tpriv->pt.af_packet_priv = tpriv;
|
||||
tpriv->packet = &attr;
|
||||
tpriv->capture_all = true;
|
||||
|
||||
/*
|
||||
* As we use HASH filtering, false positives may appear. This is a
|
||||
|
|
@ -928,18 +1011,20 @@ static int __stmmac_test_vlanfilt(struct stmmac_priv *priv)
|
|||
* HASH values.
|
||||
*/
|
||||
tpriv->vlan_id = 0x123;
|
||||
dev_add_pack(&tpriv->pt);
|
||||
|
||||
ret = vlan_vid_add(priv->dev, htons(ETH_P_8021Q), tpriv->vlan_id);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
attr.vlan = 1;
|
||||
attr.dst = priv->dev->dev_addr;
|
||||
attr.sport = 9;
|
||||
attr.dport = 9;
|
||||
|
||||
stmmac_sft_add_pack(&tpriv->pt);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
attr.vlan = 1;
|
||||
attr.vlan_id_out = tpriv->vlan_id + i;
|
||||
attr.dst = priv->dev->dev_addr;
|
||||
attr.sport = 9;
|
||||
attr.dport = 9;
|
||||
|
||||
skb = stmmac_test_get_udp_skb(priv, &attr);
|
||||
if (!skb) {
|
||||
|
|
@ -966,9 +1051,9 @@ static int __stmmac_test_vlanfilt(struct stmmac_priv *priv)
|
|||
}
|
||||
|
||||
vlan_del:
|
||||
stmmac_sft_remove_pack(&tpriv->pt);
|
||||
vlan_vid_del(priv->dev, htons(ETH_P_8021Q), tpriv->vlan_id);
|
||||
cleanup:
|
||||
dev_remove_pack(&tpriv->pt);
|
||||
kfree(tpriv);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1015,6 +1100,7 @@ static int __stmmac_test_dvlanfilt(struct stmmac_priv *priv)
|
|||
tpriv->pt.dev = priv->dev;
|
||||
tpriv->pt.af_packet_priv = tpriv;
|
||||
tpriv->packet = &attr;
|
||||
tpriv->capture_all = true;
|
||||
|
||||
/*
|
||||
* As we use HASH filtering, false positives may appear. This is a
|
||||
|
|
@ -1022,18 +1108,20 @@ static int __stmmac_test_dvlanfilt(struct stmmac_priv *priv)
|
|||
* HASH values.
|
||||
*/
|
||||
tpriv->vlan_id = 0x123;
|
||||
dev_add_pack(&tpriv->pt);
|
||||
|
||||
ret = vlan_vid_add(priv->dev, htons(ETH_P_8021AD), tpriv->vlan_id);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
attr.vlan = 2;
|
||||
attr.dst = priv->dev->dev_addr;
|
||||
attr.sport = 9;
|
||||
attr.dport = 9;
|
||||
|
||||
stmmac_sft_add_pack(&tpriv->pt);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
attr.vlan = 2;
|
||||
attr.vlan_id_out = tpriv->vlan_id + i;
|
||||
attr.dst = priv->dev->dev_addr;
|
||||
attr.sport = 9;
|
||||
attr.dport = 9;
|
||||
|
||||
skb = stmmac_test_get_udp_skb(priv, &attr);
|
||||
if (!skb) {
|
||||
|
|
@ -1060,9 +1148,9 @@ static int __stmmac_test_dvlanfilt(struct stmmac_priv *priv)
|
|||
}
|
||||
|
||||
vlan_del:
|
||||
stmmac_sft_remove_pack(&tpriv->pt);
|
||||
vlan_vid_del(priv->dev, htons(ETH_P_8021AD), tpriv->vlan_id);
|
||||
cleanup:
|
||||
dev_remove_pack(&tpriv->pt);
|
||||
kfree(tpriv);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1293,7 +1381,7 @@ static int stmmac_test_vlanoff_common(struct stmmac_priv *priv, bool svlan)
|
|||
tpriv->pt.af_packet_priv = tpriv;
|
||||
tpriv->packet = &attr;
|
||||
tpriv->vlan_id = 0x123;
|
||||
dev_add_pack(&tpriv->pt);
|
||||
tpriv->capture_all = true;
|
||||
|
||||
ret = vlan_vid_add(priv->dev, htons(proto), tpriv->vlan_id);
|
||||
if (ret)
|
||||
|
|
@ -1301,6 +1389,8 @@ static int stmmac_test_vlanoff_common(struct stmmac_priv *priv, bool svlan)
|
|||
|
||||
attr.dst = priv->dev->dev_addr;
|
||||
|
||||
stmmac_sft_add_pack(&tpriv->pt);
|
||||
|
||||
skb = stmmac_test_get_udp_skb(priv, &attr);
|
||||
if (!skb) {
|
||||
ret = -ENOMEM;
|
||||
|
|
@ -1318,9 +1408,9 @@ static int stmmac_test_vlanoff_common(struct stmmac_priv *priv, bool svlan)
|
|||
ret = tpriv->ok ? 0 : -ETIMEDOUT;
|
||||
|
||||
vlan_del:
|
||||
stmmac_sft_remove_pack(&tpriv->pt);
|
||||
vlan_vid_del(priv->dev, htons(proto), tpriv->vlan_id);
|
||||
cleanup:
|
||||
dev_remove_pack(&tpriv->pt);
|
||||
kfree(tpriv);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1332,7 +1422,7 @@ static int stmmac_test_vlanoff(struct stmmac_priv *priv)
|
|||
|
||||
static int stmmac_test_svlanoff(struct stmmac_priv *priv)
|
||||
{
|
||||
if (!priv->dma_cap.dvlan)
|
||||
if (!(priv->dev->features & NETIF_F_HW_VLAN_STAG_TX))
|
||||
return -EOPNOTSUPP;
|
||||
return stmmac_test_vlanoff_common(priv, true);
|
||||
}
|
||||
|
|
@ -1699,6 +1789,9 @@ static int __stmmac_test_jumbo(struct stmmac_priv *priv, u16 queue)
|
|||
struct stmmac_packet_attrs attr = { };
|
||||
int size = priv->dma_conf.dma_buf_sz;
|
||||
|
||||
if (!dwmac_is_xmac(priv->plat->core_type))
|
||||
size -= NET_IP_ALIGN;
|
||||
|
||||
attr.dst = priv->dev->dev_addr;
|
||||
attr.max_size = size - ETH_FCS_LEN;
|
||||
attr.queue_mapping = queue;
|
||||
|
|
|
|||
|
|
@ -2225,7 +2225,7 @@ static int netcp_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to enable NETCP power-domain\n");
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
|
|
|||
|
|
@ -2518,6 +2518,7 @@ int wx_sw_init(struct wx *wx)
|
|||
}
|
||||
|
||||
spin_lock_init(&wx->hw_stats_lock);
|
||||
spin_lock_init(&wx->ptp_tx_lock);
|
||||
mutex_init(&wx->reset_lock);
|
||||
bitmap_zero(wx->state, WX_STATE_NBITS);
|
||||
bitmap_zero(wx->flags, WX_PF_FLAGS_NBITS);
|
||||
|
|
|
|||
|
|
@ -1200,9 +1200,11 @@ static int wx_tx_map(struct wx_ring *tx_ring,
|
|||
i--;
|
||||
}
|
||||
|
||||
dev_kfree_skb_any(first->skb);
|
||||
first->skb = NULL;
|
||||
|
||||
/* first->skb is released by the caller, which keeps a reference on it
|
||||
* until the PTP cleanup has compared it against wx->ptp_tx_skb. That
|
||||
* prevents the address from being reused by a newer request while the
|
||||
* comparison is pending.
|
||||
*/
|
||||
tx_ring->next_to_use = i;
|
||||
|
||||
return -ENOMEM;
|
||||
|
|
@ -1649,9 +1651,11 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
|
|||
|
||||
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
|
||||
wx->ptp_clock) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&wx->ptp_tx_lock, flags);
|
||||
if (wx->tstamp_config.tx_type == HWTSTAMP_TX_ON &&
|
||||
!test_and_set_bit_lock(WX_STATE_PTP_TX_IN_PROGRESS,
|
||||
wx->state)) {
|
||||
!test_and_set_bit(WX_STATE_PTP_TX_IN_PROGRESS, wx->state)) {
|
||||
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
|
||||
tx_flags |= WX_TX_FLAGS_TSTAMP;
|
||||
wx->ptp_tx_skb = skb_get(skb);
|
||||
|
|
@ -1659,6 +1663,7 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
|
|||
} else {
|
||||
wx->tx_hwtstamp_skipped++;
|
||||
}
|
||||
spin_unlock_irqrestore(&wx->ptp_tx_lock, flags);
|
||||
}
|
||||
|
||||
/* record initial flags and protocol */
|
||||
|
|
@ -1677,19 +1682,35 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
|
|||
wx->atr(tx_ring, first, ptype);
|
||||
|
||||
if (wx_tx_map(tx_ring, first, hdr_len))
|
||||
goto cleanup_tx_tstamp;
|
||||
goto out_drop;
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
out_drop:
|
||||
/* The frame never reached the hardware, so no timestamp will ever be
|
||||
* reported for it and the request has to be cancelled. The slot is
|
||||
* shared, though: wx_ptp_clear_tx_timestamp() or wx_ptp_tx_hang() may
|
||||
* have dropped our request already, and a transmit on another queue
|
||||
* can have claimed the slot since. Only cancel it while it is still
|
||||
* ours, otherwise we would free somebody else's skb and release their
|
||||
* in-progress bit.
|
||||
*/
|
||||
if (unlikely(tx_flags & WX_TX_FLAGS_TSTAMP)) {
|
||||
struct sk_buff *ptp_tx_skb = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&wx->ptp_tx_lock, flags);
|
||||
if (wx->ptp_tx_skb == skb) {
|
||||
ptp_tx_skb = wx->ptp_tx_skb;
|
||||
wx->ptp_tx_skb = NULL;
|
||||
clear_bit(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
|
||||
wx->tx_hwtstamp_errors++;
|
||||
}
|
||||
spin_unlock_irqrestore(&wx->ptp_tx_lock, flags);
|
||||
|
||||
dev_kfree_skb_any(ptp_tx_skb);
|
||||
}
|
||||
dev_kfree_skb_any(first->skb);
|
||||
first->skb = NULL;
|
||||
cleanup_tx_tstamp:
|
||||
if (unlikely(tx_flags & WX_TX_FLAGS_TSTAMP)) {
|
||||
dev_kfree_skb_any(wx->ptp_tx_skb);
|
||||
wx->ptp_tx_skb = NULL;
|
||||
wx->tx_hwtstamp_errors++;
|
||||
clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
|
||||
}
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,6 +129,34 @@ static int wx_ptp_settime64(struct ptp_clock_info *ptp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __wx_ptp_detach_tx_skb - detach the skb tracking the Tx timestamp request
|
||||
* @wx: the private board structure
|
||||
*
|
||||
* Detach the skb of the outstanding request and release the in-progress bit,
|
||||
* so that a new request can be submitted.
|
||||
*
|
||||
* This performs no register access. Callers that need a timestamp the hardware
|
||||
* may have left latched must unlatch it themselves, while the device is known
|
||||
* to be alive. wx_ptp_quiesce() runs during PCIe error recovery, where MMIO is
|
||||
* not reliable, and therefore deliberately skips the unlatch.
|
||||
*
|
||||
* Context: Expects wx->ptp_tx_lock to be held by the caller.
|
||||
* Return: the detached skb, or NULL if no request was outstanding. The caller
|
||||
* owns the returned reference and must release it once the lock is dropped.
|
||||
*/
|
||||
static struct sk_buff *__wx_ptp_detach_tx_skb(struct wx *wx)
|
||||
{
|
||||
struct sk_buff *skb = wx->ptp_tx_skb;
|
||||
|
||||
lockdep_assert_held(&wx->ptp_tx_lock);
|
||||
|
||||
wx->ptp_tx_skb = NULL;
|
||||
clear_bit(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
/**
|
||||
* wx_ptp_clear_tx_timestamp - utility function to clear Tx timestamp state
|
||||
* @wx: the private board structure
|
||||
|
|
@ -139,12 +167,16 @@ static int wx_ptp_settime64(struct ptp_clock_info *ptp,
|
|||
*/
|
||||
static void wx_ptp_clear_tx_timestamp(struct wx *wx)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&wx->ptp_tx_lock, flags);
|
||||
/* Unlatch a timestamp the hardware may have left pending. */
|
||||
rd32ptp(wx, WX_TSC_1588_STMPH);
|
||||
if (wx->ptp_tx_skb) {
|
||||
dev_kfree_skb_any(wx->ptp_tx_skb);
|
||||
wx->ptp_tx_skb = NULL;
|
||||
}
|
||||
clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
|
||||
skb = __wx_ptp_detach_tx_skb(wx);
|
||||
spin_unlock_irqrestore(&wx->ptp_tx_lock, flags);
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -175,49 +207,54 @@ static void wx_ptp_convert_to_hwtstamp(struct wx *wx,
|
|||
}
|
||||
|
||||
/**
|
||||
* wx_ptp_tx_hwtstamp - utility function which checks for TX time stamp
|
||||
* wx_ptp_tx_hwtstamp_work - check for a pending Tx time stamp
|
||||
* @wx: the private board struct
|
||||
*
|
||||
* if the timestamp is valid, we convert it into the timecounter ns
|
||||
* value, then store that result into the shhwtstamps structure which
|
||||
* is passed up the network stack
|
||||
* If a Tx timestamp request is outstanding and the hardware has latched a
|
||||
* valid value, we convert it into the timecounter ns value, then store that
|
||||
* result into the shhwtstamps structure which is passed up the network stack.
|
||||
*
|
||||
* Return: 0 when there is nothing left to poll for, -1 when the timestamp is
|
||||
* not available yet and the caller should poll again.
|
||||
*/
|
||||
static void wx_ptp_tx_hwtstamp(struct wx *wx)
|
||||
{
|
||||
struct skb_shared_hwtstamps shhwtstamps;
|
||||
struct sk_buff *skb = wx->ptp_tx_skb;
|
||||
u64 regval = 0;
|
||||
|
||||
regval |= (u64)rd32ptp(wx, WX_TSC_1588_STMPL);
|
||||
regval |= (u64)rd32ptp(wx, WX_TSC_1588_STMPH) << 32;
|
||||
|
||||
wx_ptp_convert_to_hwtstamp(wx, &shhwtstamps, regval);
|
||||
|
||||
wx->ptp_tx_skb = NULL;
|
||||
clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
|
||||
skb_tstamp_tx(skb, &shhwtstamps);
|
||||
dev_kfree_skb_any(skb);
|
||||
wx->tx_hwtstamp_pkts++;
|
||||
}
|
||||
|
||||
static int wx_ptp_tx_hwtstamp_work(struct wx *wx)
|
||||
{
|
||||
struct skb_shared_hwtstamps shhwtstamps;
|
||||
unsigned long flags;
|
||||
struct sk_buff *skb;
|
||||
u32 tsynctxctl;
|
||||
u64 regval = 0;
|
||||
|
||||
spin_lock_irqsave(&wx->ptp_tx_lock, flags);
|
||||
|
||||
/* we have to have a valid skb to poll for a timestamp */
|
||||
if (!wx->ptp_tx_skb) {
|
||||
wx_ptp_clear_tx_timestamp(wx);
|
||||
rd32ptp(wx, WX_TSC_1588_STMPH);
|
||||
__wx_ptp_detach_tx_skb(wx);
|
||||
spin_unlock_irqrestore(&wx->ptp_tx_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* stop polling once we have a valid timestamp */
|
||||
tsynctxctl = rd32ptp(wx, WX_TSC_1588_CTL);
|
||||
if (tsynctxctl & WX_TSC_1588_CTL_VALID) {
|
||||
wx_ptp_tx_hwtstamp(wx);
|
||||
return 0;
|
||||
if (!(tsynctxctl & WX_TSC_1588_CTL_VALID)) {
|
||||
spin_unlock_irqrestore(&wx->ptp_tx_lock, flags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
regval |= (u64)rd32ptp(wx, WX_TSC_1588_STMPL);
|
||||
regval |= (u64)rd32ptp(wx, WX_TSC_1588_STMPH) << 32;
|
||||
skb = wx->ptp_tx_skb;
|
||||
wx->ptp_tx_skb = NULL;
|
||||
clear_bit(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
|
||||
spin_unlock_irqrestore(&wx->ptp_tx_lock, flags);
|
||||
|
||||
wx_ptp_convert_to_hwtstamp(wx, &shhwtstamps, regval);
|
||||
skb_tstamp_tx(skb, &shhwtstamps);
|
||||
dev_kfree_skb_any(skb);
|
||||
wx->tx_hwtstamp_pkts++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -296,24 +333,29 @@ static void wx_ptp_rx_hang(struct wx *wx)
|
|||
*/
|
||||
static void wx_ptp_tx_hang(struct wx *wx)
|
||||
{
|
||||
bool timeout = time_is_before_jiffies(wx->ptp_tx_start +
|
||||
WX_PTP_TX_TIMEOUT);
|
||||
struct sk_buff *skb = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
if (!wx->ptp_tx_skb)
|
||||
return;
|
||||
|
||||
if (!test_bit(WX_STATE_PTP_TX_IN_PROGRESS, wx->state))
|
||||
return;
|
||||
spin_lock_irqsave(&wx->ptp_tx_lock, flags);
|
||||
|
||||
/* If we haven't received a timestamp within the timeout, it is
|
||||
* reasonable to assume that it will never occur, so we can unlock the
|
||||
* timestamp bit when this occurs.
|
||||
*/
|
||||
if (timeout) {
|
||||
wx_ptp_clear_tx_timestamp(wx);
|
||||
wx->tx_hwtstamp_timeouts++;
|
||||
dev_warn(&wx->pdev->dev, "clearing Tx timestamp hang\n");
|
||||
if (wx->ptp_tx_skb &&
|
||||
test_bit(WX_STATE_PTP_TX_IN_PROGRESS, wx->state) &&
|
||||
time_is_before_jiffies(wx->ptp_tx_start + WX_PTP_TX_TIMEOUT)) {
|
||||
rd32ptp(wx, WX_TSC_1588_STMPH);
|
||||
skb = __wx_ptp_detach_tx_skb(wx);
|
||||
}
|
||||
spin_unlock_irqrestore(&wx->ptp_tx_lock, flags);
|
||||
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
wx->tx_hwtstamp_timeouts++;
|
||||
dev_warn(&wx->pdev->dev, "clearing Tx timestamp hang\n");
|
||||
}
|
||||
|
||||
static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp)
|
||||
|
|
@ -841,6 +883,9 @@ EXPORT_SYMBOL(wx_ptp_stop);
|
|||
|
||||
void wx_ptp_quiesce(struct wx *wx)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
|
||||
if (!test_and_clear_bit(WX_STATE_PTP_RUNNING, wx->state))
|
||||
return;
|
||||
|
||||
|
|
@ -849,11 +894,14 @@ void wx_ptp_quiesce(struct wx *wx)
|
|||
if (wx->ptp_clock)
|
||||
ptp_cancel_worker_sync(wx->ptp_clock);
|
||||
|
||||
if (wx->ptp_tx_skb) {
|
||||
dev_kfree_skb_any(wx->ptp_tx_skb);
|
||||
wx->ptp_tx_skb = NULL;
|
||||
}
|
||||
clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
|
||||
/* Drop a pending Tx timestamp request. Do not touch the registers
|
||||
* here: quiesce runs during PCIe error recovery, where the device may
|
||||
* already be gone and MMIO is not reliable.
|
||||
*/
|
||||
spin_lock_irqsave(&wx->ptp_tx_lock, flags);
|
||||
skb = __wx_ptp_detach_tx_skb(wx);
|
||||
spin_unlock_irqrestore(&wx->ptp_tx_lock, flags);
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
if (wx->ptp_clock) {
|
||||
ptp_clock_unregister(wx->ptp_clock);
|
||||
|
|
|
|||
|
|
@ -1430,6 +1430,8 @@ struct wx {
|
|||
unsigned long last_overflow_check;
|
||||
unsigned long last_rx_ptp_check;
|
||||
unsigned long ptp_tx_start;
|
||||
/* protects ptp_tx_skb, ptp_tx_start and the in-progress state bit */
|
||||
spinlock_t ptp_tx_lock;
|
||||
seqlock_t hw_tc_lock; /* seqlock for ptp */
|
||||
struct cyclecounter hw_cc;
|
||||
struct timecounter hw_tc;
|
||||
|
|
|
|||
|
|
@ -591,15 +591,24 @@ static void txgbe_fdir_filter_restore(struct wx *wx)
|
|||
queue = TXGBE_RDB_FDIR_DROP_QUEUE;
|
||||
} else {
|
||||
u32 ring = ethtool_get_flow_spec_ring(filter->action);
|
||||
u8 vf = ethtool_get_flow_spec_ring_vf(filter->action);
|
||||
|
||||
if (ring >= wx->num_rx_queues) {
|
||||
if (!vf && ring >= wx->num_rx_queues) {
|
||||
wx_err(wx, "FDIR restore failed, ring:%u\n",
|
||||
ring);
|
||||
continue;
|
||||
} else if (vf && (vf > wx->num_vfs ||
|
||||
ring >= wx->num_rx_queues_per_pool)) {
|
||||
wx_err(wx, "FDIR restore failed, vf:%u, ring:%u\n",
|
||||
vf, ring);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Map the ring onto the absolute queue index */
|
||||
queue = wx->rx_ring[ring]->reg_idx;
|
||||
if (!vf)
|
||||
queue = wx->rx_ring[ring]->reg_idx;
|
||||
else
|
||||
queue = ((vf - 1) * wx->num_rx_queues_per_pool) + ring;
|
||||
}
|
||||
|
||||
ret = txgbe_fdir_write_perfect_filter(wx,
|
||||
|
|
|
|||
|
|
@ -3539,6 +3539,22 @@ static int macsec_dev_init(struct net_device *dev)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
err = -ENOMEM;
|
||||
macsec->stats = netdev_alloc_pcpu_stats(struct pcpu_secy_stats);
|
||||
if (!macsec->stats)
|
||||
goto destroy_gro_cells;
|
||||
|
||||
macsec->secy.tx_sc.stats =
|
||||
netdev_alloc_pcpu_stats(struct pcpu_tx_sc_stats);
|
||||
if (!macsec->secy.tx_sc.stats)
|
||||
goto free_secy_stats;
|
||||
|
||||
macsec->secy.tx_sc.md_dst = metadata_dst_alloc(0, METADATA_MACSEC,
|
||||
GFP_KERNEL);
|
||||
if (!macsec->secy.tx_sc.md_dst)
|
||||
goto free_tx_sc_stats;
|
||||
macsec->secy.tx_sc.md_dst->u.macsec_info.sci = macsec->secy.sci;
|
||||
|
||||
macsec_inherit_tso_max(dev);
|
||||
|
||||
dev->hw_features = real_dev->hw_features & MACSEC_OFFLOAD_FEATURES;
|
||||
|
|
@ -3551,8 +3567,6 @@ static int macsec_dev_init(struct net_device *dev)
|
|||
|
||||
macsec_set_head_tail_room(dev);
|
||||
|
||||
if (is_zero_ether_addr(dev->dev_addr))
|
||||
eth_hw_addr_inherit(dev, real_dev);
|
||||
if (is_zero_ether_addr(dev->broadcast))
|
||||
memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len);
|
||||
|
||||
|
|
@ -3560,6 +3574,14 @@ static int macsec_dev_init(struct net_device *dev)
|
|||
netdev_hold(real_dev, &macsec->dev_tracker, GFP_KERNEL);
|
||||
|
||||
return 0;
|
||||
|
||||
free_tx_sc_stats:
|
||||
free_percpu(macsec->secy.tx_sc.stats);
|
||||
free_secy_stats:
|
||||
free_percpu(macsec->stats);
|
||||
destroy_gro_cells:
|
||||
gro_cells_destroy(&macsec->gro_cells);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void macsec_dev_uninit(struct net_device *dev)
|
||||
|
|
@ -4116,26 +4138,11 @@ static sci_t dev_to_sci(struct net_device *dev, __be16 port)
|
|||
return make_sci(dev->dev_addr, port);
|
||||
}
|
||||
|
||||
static int macsec_add_dev(struct net_device *dev, sci_t sci, u8 icv_len)
|
||||
static void macsec_init_secy(struct net_device *dev, sci_t sci, u8 icv_len)
|
||||
{
|
||||
struct macsec_dev *macsec = macsec_priv(dev);
|
||||
struct macsec_secy *secy = &macsec->secy;
|
||||
|
||||
macsec->stats = netdev_alloc_pcpu_stats(struct pcpu_secy_stats);
|
||||
if (!macsec->stats)
|
||||
return -ENOMEM;
|
||||
|
||||
secy->tx_sc.stats = netdev_alloc_pcpu_stats(struct pcpu_tx_sc_stats);
|
||||
if (!secy->tx_sc.stats)
|
||||
return -ENOMEM;
|
||||
|
||||
secy->tx_sc.md_dst = metadata_dst_alloc(0, METADATA_MACSEC, GFP_KERNEL);
|
||||
if (!secy->tx_sc.md_dst)
|
||||
/* macsec and secy percpu stats will be freed when unregistering
|
||||
* net_device in macsec_free_netdev()
|
||||
*/
|
||||
return -ENOMEM;
|
||||
|
||||
if (sci == MACSEC_UNDEF_SCI)
|
||||
sci = dev_to_sci(dev, MACSEC_PORT_ES);
|
||||
|
||||
|
|
@ -4149,15 +4156,12 @@ static int macsec_add_dev(struct net_device *dev, sci_t sci, u8 icv_len)
|
|||
secy->xpn = DEFAULT_XPN;
|
||||
|
||||
secy->sci = sci;
|
||||
secy->tx_sc.md_dst->u.macsec_info.sci = sci;
|
||||
secy->tx_sc.active = true;
|
||||
secy->tx_sc.encoding_sa = DEFAULT_ENCODING_SA;
|
||||
secy->tx_sc.encrypt = DEFAULT_ENCRYPT;
|
||||
secy->tx_sc.send_sci = DEFAULT_SEND_SCI;
|
||||
secy->tx_sc.end_station = false;
|
||||
secy->tx_sc.scb = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct lock_class_key macsec_netdev_addr_lock_key;
|
||||
|
|
@ -4220,6 +4224,24 @@ static int macsec_newlink(struct net_device *dev,
|
|||
if (rx_handler && rx_handler != macsec_handle_frame)
|
||||
return -EBUSY;
|
||||
|
||||
if (is_zero_ether_addr(dev->dev_addr))
|
||||
eth_hw_addr_inherit(dev, real_dev);
|
||||
|
||||
if (data && data[IFLA_MACSEC_SCI])
|
||||
sci = nla_get_sci(data[IFLA_MACSEC_SCI]);
|
||||
else if (data && data[IFLA_MACSEC_PORT])
|
||||
sci = dev_to_sci(dev, nla_get_be16(data[IFLA_MACSEC_PORT]));
|
||||
else
|
||||
sci = dev_to_sci(dev, MACSEC_PORT_ES);
|
||||
|
||||
/* Registration can notify listeners before returning. */
|
||||
macsec_init_secy(dev, sci, icv_len);
|
||||
if (data) {
|
||||
err = macsec_changelink_common(dev, data);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = register_netdevice(dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
|
@ -4232,31 +4254,11 @@ static int macsec_newlink(struct net_device *dev,
|
|||
if (err < 0)
|
||||
goto unregister;
|
||||
|
||||
/* need to be already registered so that ->init has run and
|
||||
* the MAC addr is set
|
||||
*/
|
||||
if (data && data[IFLA_MACSEC_SCI])
|
||||
sci = nla_get_sci(data[IFLA_MACSEC_SCI]);
|
||||
else if (data && data[IFLA_MACSEC_PORT])
|
||||
sci = dev_to_sci(dev, nla_get_be16(data[IFLA_MACSEC_PORT]));
|
||||
else
|
||||
sci = dev_to_sci(dev, MACSEC_PORT_ES);
|
||||
|
||||
if (rx_handler && sci_exists(real_dev, sci)) {
|
||||
err = -EBUSY;
|
||||
goto unlink;
|
||||
}
|
||||
|
||||
err = macsec_add_dev(dev, sci, icv_len);
|
||||
if (err)
|
||||
goto unlink;
|
||||
|
||||
if (data) {
|
||||
err = macsec_changelink_common(dev, data);
|
||||
if (err)
|
||||
goto del_dev;
|
||||
}
|
||||
|
||||
/* If h/w offloading is available, propagate to the device */
|
||||
if (macsec_is_offloaded(macsec)) {
|
||||
const struct macsec_ops *ops;
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@
|
|||
#define RTL9310_SMI_INDRT_ACCESS_BC_PHYID_CTRL 0x0c14
|
||||
#define RTL9310_BC_PORT_ID GENMASK(10, 5)
|
||||
#define RTL9310_SMI_INDRT_ACCESS_CTRL_1 0x0c04
|
||||
#define RTL9310_SMI_INDRT_EXT_PAGE GENMASK(8, 0)
|
||||
#define RTL9310_SMI_INDRT_EXT_PAGE_NO_CHANGE 0x1ff
|
||||
#define RTL9310_SMI_INDRT_ACCESS_CTRL_2_LOW 0x0c08
|
||||
#define RTL9310_SMI_INDRT_ACCESS_CTRL_2_HIGH 0x0c0c
|
||||
#define RTL9310_SMI_INDRT_ACCESS_CTRL_3 0x0c10 /* I/O fields flipped */
|
||||
|
|
@ -325,6 +327,8 @@ static int otto_emdio_9310_read_c22(struct mii_bus *bus, int port, int regnum, u
|
|||
.broadcast = FIELD_PREP(RTL9310_BC_PORT_ID, port),
|
||||
.c22_data = FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) |
|
||||
FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
|
||||
.ext_page = FIELD_PREP(RTL9310_SMI_INDRT_EXT_PAGE,
|
||||
RTL9310_SMI_INDRT_EXT_PAGE_NO_CHANGE),
|
||||
};
|
||||
|
||||
return otto_emdio_read_cmd(bus, RTL9310_PHY_CTRL_TYPE_C22, &cmd_data,
|
||||
|
|
@ -337,6 +341,8 @@ static int otto_emdio_9310_write_c22(struct mii_bus *bus, int port, int regnum,
|
|||
struct otto_emdio_cmd_regs cmd_data = {
|
||||
.c22_data = FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) |
|
||||
FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
|
||||
.ext_page = FIELD_PREP(RTL9310_SMI_INDRT_EXT_PAGE,
|
||||
RTL9310_SMI_INDRT_EXT_PAGE_NO_CHANGE),
|
||||
.io_data = FIELD_PREP(RTL9310_PHY_CTRL_INDATA, value),
|
||||
.port_mask_high = (u32)(BIT_ULL(port) >> 32),
|
||||
.port_mask_low = (u32)(BIT_ULL(port)),
|
||||
|
|
|
|||
|
|
@ -100,6 +100,8 @@ static bool ovpn_nl_attr_sockaddr_remote(struct nlattr **attrs,
|
|||
struct sockaddr_in6 *sin6;
|
||||
struct sockaddr_in *sin;
|
||||
struct in6_addr *in6;
|
||||
struct nlattr *scope;
|
||||
u32 scope_id = 0;
|
||||
__be16 port = 0;
|
||||
__be32 *in;
|
||||
|
||||
|
|
@ -114,6 +116,9 @@ static bool ovpn_nl_attr_sockaddr_remote(struct nlattr **attrs,
|
|||
} else if (attrs[OVPN_A_PEER_REMOTE_IPV6]) {
|
||||
ss->ss_family = AF_INET6;
|
||||
in6 = nla_data(attrs[OVPN_A_PEER_REMOTE_IPV6]);
|
||||
scope = attrs[OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID];
|
||||
if (scope)
|
||||
scope_id = nla_get_u32(scope);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -126,6 +131,7 @@ static bool ovpn_nl_attr_sockaddr_remote(struct nlattr **attrs,
|
|||
if (!ipv6_addr_v4mapped(in6)) {
|
||||
sin6 = (struct sockaddr_in6 *)ss;
|
||||
sin6->sin6_port = port;
|
||||
sin6->sin6_scope_id = scope_id;
|
||||
memcpy(&sin6->sin6_addr, in6, sizeof(*in6));
|
||||
break;
|
||||
}
|
||||
|
|
@ -179,6 +185,39 @@ static sa_family_t ovpn_nl_family_get(struct nlattr *addr4,
|
|||
return AF_UNSPEC;
|
||||
}
|
||||
|
||||
static int ovpn_nl_peer_check_vpn_addrs(const struct in_addr *addr4,
|
||||
const struct in6_addr *addr6,
|
||||
struct genl_info *info)
|
||||
{
|
||||
int addr6_type;
|
||||
|
||||
if (addr4->s_addr == htonl(INADDR_ANY) && ipv6_addr_any(addr6)) {
|
||||
NL_SET_ERR_MSG_MOD(info->extack,
|
||||
"at least one VPN IP must be configured in MP mode");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ipv4_is_multicast(addr4->s_addr) || ipv4_is_lbcast(addr4->s_addr) ||
|
||||
ipv4_is_loopback(addr4->s_addr)) {
|
||||
NL_SET_ERR_MSG_MOD(info->extack,
|
||||
"VPN IPv4 address must be valid unicast or any");
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
if (!ipv6_addr_any(addr6)) {
|
||||
addr6_type = ipv6_addr_type(addr6);
|
||||
|
||||
if (!(addr6_type & IPV6_ADDR_UNICAST) ||
|
||||
(addr6_type & (IPV6_ADDR_LOOPBACK | IPV6_ADDR_COMPATv4))) {
|
||||
NL_SET_ERR_MSG_MOD(info->extack,
|
||||
"VPN IPv6 address must be valid unicast or any");
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ovpn_nl_peer_precheck(struct ovpn_priv *ovpn,
|
||||
struct genl_info *info,
|
||||
struct nlattr **attrs)
|
||||
|
|
@ -346,8 +385,10 @@ static int ovpn_nl_peer_modify(struct ovpn_peer *peer, struct genl_info *info,
|
|||
|
||||
int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct nlattr *attrs[OVPN_A_PEER_MAX + 1];
|
||||
struct in_addr vpn_addr4 = { .s_addr = htonl(INADDR_ANY) };
|
||||
struct in6_addr vpn_addr6 = IN6ADDR_ANY_INIT;
|
||||
struct ovpn_priv *ovpn = info->user_ptr[0];
|
||||
struct nlattr *attrs[OVPN_A_PEER_MAX + 1];
|
||||
struct ovpn_socket *ovpn_sock;
|
||||
struct socket *sock = NULL;
|
||||
struct ovpn_peer *peer;
|
||||
|
|
@ -371,11 +412,18 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
return -EINVAL;
|
||||
|
||||
/* in MP mode VPN IPs are required for selecting the right peer */
|
||||
if (ovpn->mode == OVPN_MODE_MP && !attrs[OVPN_A_PEER_VPN_IPV4] &&
|
||||
!attrs[OVPN_A_PEER_VPN_IPV6]) {
|
||||
NL_SET_ERR_MSG_FMT_MOD(info->extack,
|
||||
"VPN IP must be provided in MP mode");
|
||||
return -EINVAL;
|
||||
if (ovpn->mode == OVPN_MODE_MP) {
|
||||
if (attrs[OVPN_A_PEER_VPN_IPV4])
|
||||
vpn_addr4.s_addr =
|
||||
nla_get_in_addr(attrs[OVPN_A_PEER_VPN_IPV4]);
|
||||
if (attrs[OVPN_A_PEER_VPN_IPV6])
|
||||
vpn_addr6 =
|
||||
nla_get_in6_addr(attrs[OVPN_A_PEER_VPN_IPV6]);
|
||||
|
||||
ret = ovpn_nl_peer_check_vpn_addrs(&vpn_addr4, &vpn_addr6,
|
||||
info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
peer_id = nla_get_u32(attrs[OVPN_A_PEER_ID]);
|
||||
|
|
@ -474,8 +522,10 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct nlattr *attrs[OVPN_A_PEER_MAX + 1];
|
||||
struct ovpn_priv *ovpn = info->user_ptr[0];
|
||||
struct nlattr *attrs[OVPN_A_PEER_MAX + 1];
|
||||
struct in6_addr vpn_addr6;
|
||||
struct in_addr vpn_addr4;
|
||||
struct ovpn_socket *sock;
|
||||
struct ovpn_peer *peer;
|
||||
u32 peer_id;
|
||||
|
|
@ -522,28 +572,58 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
rcu_read_unlock();
|
||||
|
||||
spin_lock_bh(&ovpn->lock);
|
||||
ret = ovpn_nl_peer_modify(peer, info, attrs);
|
||||
if (ret < 0) {
|
||||
spin_unlock_bh(&ovpn->lock);
|
||||
ovpn_peer_put(peer);
|
||||
return ret;
|
||||
|
||||
vpn_addr4 = peer->vpn_addrs.ipv4;
|
||||
vpn_addr6 = peer->vpn_addrs.ipv6;
|
||||
|
||||
/* reject peer with conflicting VPN address */
|
||||
if (attrs[OVPN_A_PEER_VPN_IPV4]) {
|
||||
vpn_addr4.s_addr = nla_get_in_addr(attrs[OVPN_A_PEER_VPN_IPV4]);
|
||||
if (ovpn_peer_vpn_addr_conflict4(ovpn, peer, &vpn_addr4))
|
||||
goto addr_conflict;
|
||||
}
|
||||
if (attrs[OVPN_A_PEER_VPN_IPV6]) {
|
||||
vpn_addr6 = nla_get_in6_addr(attrs[OVPN_A_PEER_VPN_IPV6]);
|
||||
if (ovpn_peer_vpn_addr_conflict6(ovpn, peer, &vpn_addr6))
|
||||
goto addr_conflict;
|
||||
}
|
||||
|
||||
/* in MP mode VPN IPs are required for selecting the right peer */
|
||||
if (ovpn->mode == OVPN_MODE_MP) {
|
||||
ret = ovpn_nl_peer_check_vpn_addrs(&vpn_addr4, &vpn_addr6,
|
||||
info);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = ovpn_nl_peer_modify(peer, info, attrs);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
/* ret == 1 means that VPN IPv4/6 has been modified and rehashing
|
||||
* is required
|
||||
*/
|
||||
if (ret > 0)
|
||||
if (ret > 0) {
|
||||
ovpn_peer_hash_vpn_ip(peer);
|
||||
ret = 0;
|
||||
}
|
||||
/* if the remote endpoint was updated, the by_transp_addr hash bucket
|
||||
* also needs to be refreshed, otherwise incoming packets from the new
|
||||
* remote address would fail the lockless lookup
|
||||
*/
|
||||
if (attrs[OVPN_A_PEER_REMOTE_IPV4] || attrs[OVPN_A_PEER_REMOTE_IPV6])
|
||||
ovpn_peer_hash_transp_addr(peer);
|
||||
|
||||
unlock:
|
||||
spin_unlock_bh(&ovpn->lock);
|
||||
ovpn_peer_put(peer);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
addr_conflict:
|
||||
NL_SET_ERR_MSG_FMT_MOD(info->extack,
|
||||
"VPN IP is already assigned to another peer");
|
||||
ret = -EADDRINUSE;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
static int ovpn_nl_send_peer(struct sk_buff *skb, const struct genl_info *info,
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ struct ovpn_peer *ovpn_peer_new(struct ovpn_priv *ovpn, u32 id)
|
|||
RCU_INIT_POINTER(peer->bind, NULL);
|
||||
ovpn_crypto_state_init(&peer->crypto);
|
||||
spin_lock_init(&peer->lock);
|
||||
seqcount_spinlock_init(&peer->route_key_seq, &peer->lock);
|
||||
kref_init(&peer->refcount);
|
||||
ovpn_peer_stats_init(&peer->vpn_stats);
|
||||
ovpn_peer_stats_init(&peer->link_stats);
|
||||
|
|
@ -199,13 +200,12 @@ static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer,
|
|||
*/
|
||||
void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
|
||||
{
|
||||
const void *local_ip = NULL;
|
||||
struct sockaddr_storage ss;
|
||||
struct sockaddr_in6 *sa6;
|
||||
bool reset_cache = false;
|
||||
struct sockaddr_in *sa;
|
||||
struct ovpn_bind *bind;
|
||||
const void *local_ip;
|
||||
size_t salen = 0;
|
||||
bool floated = false;
|
||||
|
||||
spin_lock_bh(&peer->lock);
|
||||
bind = rcu_dereference_protected(peer->bind,
|
||||
|
|
@ -232,8 +232,7 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
|
|||
.sin_addr.s_addr = ip_hdr(skb)->saddr,
|
||||
.sin_port = udp_hdr(skb)->source,
|
||||
};
|
||||
salen = sizeof(*sa);
|
||||
reset_cache = true;
|
||||
floated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -245,10 +244,12 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
|
|||
netdev_name(peer->ovpn->dev),
|
||||
peer->id, &bind->local.ipv4.s_addr,
|
||||
&ip_hdr(skb)->daddr);
|
||||
bind->local.ipv4.s_addr = ip_hdr(skb)->daddr;
|
||||
reset_cache = true;
|
||||
local_ip = &ip_hdr(skb)->daddr;
|
||||
memcpy(&ss, &bind->remote, sizeof(struct sockaddr_in));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* nothing changed */
|
||||
goto unlock;
|
||||
case htons(ETH_P_IPV6):
|
||||
/* float check */
|
||||
if (unlikely(!ovpn_bind_skb_src_match(bind, skb))) {
|
||||
|
|
@ -270,8 +271,7 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
|
|||
ipv6_iface_scope_id(&ipv6_hdr(skb)->saddr,
|
||||
skb->skb_iif),
|
||||
};
|
||||
salen = sizeof(*sa6);
|
||||
reset_cache = true;
|
||||
floated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -284,26 +284,30 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
|
|||
netdev_name(peer->ovpn->dev),
|
||||
peer->id, &bind->local.ipv6,
|
||||
&ipv6_hdr(skb)->daddr);
|
||||
bind->local.ipv6 = ipv6_hdr(skb)->daddr;
|
||||
reset_cache = true;
|
||||
local_ip = &ipv6_hdr(skb)->daddr;
|
||||
memcpy(&ss, &bind->remote, sizeof(struct sockaddr_in6));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* nothing changed */
|
||||
goto unlock;
|
||||
default:
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (unlikely(reset_cache))
|
||||
dst_cache_reset(&peer->dst_cache);
|
||||
|
||||
/* if the peer did not float, we can bail out now */
|
||||
if (likely(!salen))
|
||||
goto unlock;
|
||||
|
||||
if (unlikely(ovpn_peer_reset_sockaddr(peer,
|
||||
(struct sockaddr_storage *)&ss,
|
||||
local_ip) < 0))
|
||||
goto unlock;
|
||||
|
||||
/* reset the cache only after a successful bind update to avoid useless
|
||||
* cache misses on concurrent TX
|
||||
*/
|
||||
dst_cache_reset(&peer->dst_cache);
|
||||
|
||||
/* if only the local address changed, bail out now */
|
||||
if (!floated)
|
||||
goto unlock;
|
||||
|
||||
net_dbg_ratelimited("%s: peer %d floated to %pIScp",
|
||||
netdev_name(peer->ovpn->dev), peer->id, &ss);
|
||||
|
||||
|
|
@ -484,7 +488,7 @@ static struct ovpn_peer *ovpn_peer_get_by_vpn_addr4(struct ovpn_priv *ovpn,
|
|||
* Return: the peer if found or NULL otherwise
|
||||
*/
|
||||
static struct ovpn_peer *ovpn_peer_get_by_vpn_addr6(struct ovpn_priv *ovpn,
|
||||
struct in6_addr *addr)
|
||||
const struct in6_addr *addr)
|
||||
{
|
||||
struct hlist_nulls_head *nhead;
|
||||
struct hlist_nulls_node *ntmp;
|
||||
|
|
@ -509,6 +513,64 @@ static struct ovpn_peer *ovpn_peer_get_by_vpn_addr6(struct ovpn_priv *ovpn,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_peer_vpn_addr_conflict4 - check if the VPN v4 address is already in use
|
||||
* @ovpn: the openvpn instance to search
|
||||
* @peer: peer being added or updated, or NULL
|
||||
* @addr: VPN IPv4 address to check
|
||||
*
|
||||
* Check whether @addr is already assigned to another peer. @peer is ignored
|
||||
* when found, allowing peer updates that keep an existing address.
|
||||
* Unspecified addresses are ignored.
|
||||
*
|
||||
* Note: the caller must hold @ovpn->lock.
|
||||
*
|
||||
* Return: true on conflict, false otherwise.
|
||||
*/
|
||||
bool ovpn_peer_vpn_addr_conflict4(struct ovpn_priv *ovpn,
|
||||
const struct ovpn_peer *peer,
|
||||
const struct in_addr *addr)
|
||||
{
|
||||
struct ovpn_peer *tmp = NULL;
|
||||
|
||||
lockdep_assert_held(&ovpn->lock);
|
||||
|
||||
/* we don't hash INADDR_ANY, no conflict in that case */
|
||||
if (addr->s_addr != htonl(INADDR_ANY))
|
||||
tmp = ovpn_peer_get_by_vpn_addr4(ovpn, addr->s_addr);
|
||||
|
||||
return tmp && tmp != peer;
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_peer_vpn_addr_conflict6 - check if the VPN v6 address is already in use
|
||||
* @ovpn: the openvpn instance to search
|
||||
* @peer: peer being added or updated, or NULL
|
||||
* @addr: VPN IPv6 address to check
|
||||
*
|
||||
* Check whether @addr is already assigned to another peer. @peer is ignored
|
||||
* when found, allowing peer updates that keep an existing address.
|
||||
* Unspecified addresses are ignored.
|
||||
*
|
||||
* Note: the caller must hold @ovpn->lock.
|
||||
*
|
||||
* Return: true on conflict, false otherwise.
|
||||
*/
|
||||
bool ovpn_peer_vpn_addr_conflict6(struct ovpn_priv *ovpn,
|
||||
const struct ovpn_peer *peer,
|
||||
const struct in6_addr *addr)
|
||||
{
|
||||
struct ovpn_peer *tmp = NULL;
|
||||
|
||||
lockdep_assert_held(&ovpn->lock);
|
||||
|
||||
/* we don't hash ::, no conflict in that case */
|
||||
if (!ipv6_addr_any(addr))
|
||||
tmp = ovpn_peer_get_by_vpn_addr6(ovpn, addr);
|
||||
|
||||
return tmp && tmp != peer;
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_peer_transp_match - check if sockaddr and peer binding match
|
||||
* @peer: the peer to get the binding from
|
||||
|
|
@ -990,10 +1052,11 @@ void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer)
|
|||
if (hlist_unhashed(&peer->hash_entry_id))
|
||||
return;
|
||||
|
||||
if (peer->vpn_addrs.ipv4.s_addr != htonl(INADDR_ANY)) {
|
||||
/* remove potential old hashing */
|
||||
hlist_nulls_del_init_rcu(&peer->hash_entry_addr4);
|
||||
/* remove potential old hashing */
|
||||
hlist_nulls_del_init_rcu(&peer->hash_entry_addr4);
|
||||
hlist_nulls_del_init_rcu(&peer->hash_entry_addr6);
|
||||
|
||||
if (peer->vpn_addrs.ipv4.s_addr != htonl(INADDR_ANY)) {
|
||||
nhead = ovpn_get_hash_head(peer->ovpn->peers->by_vpn_addr4,
|
||||
&peer->vpn_addrs.ipv4,
|
||||
sizeof(peer->vpn_addrs.ipv4));
|
||||
|
|
@ -1001,9 +1064,6 @@ void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer)
|
|||
}
|
||||
|
||||
if (!ipv6_addr_any(&peer->vpn_addrs.ipv6)) {
|
||||
/* remove potential old hashing */
|
||||
hlist_nulls_del_init_rcu(&peer->hash_entry_addr6);
|
||||
|
||||
nhead = ovpn_get_hash_head(peer->ovpn->peers->by_vpn_addr6,
|
||||
&peer->vpn_addrs.ipv6,
|
||||
sizeof(peer->vpn_addrs.ipv6));
|
||||
|
|
@ -1038,6 +1098,13 @@ static int ovpn_peer_add_mp(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* reject peer with conflicting VPN address */
|
||||
if (ovpn_peer_vpn_addr_conflict4(ovpn, NULL, &peer->vpn_addrs.ipv4) ||
|
||||
ovpn_peer_vpn_addr_conflict6(ovpn, NULL, &peer->vpn_addrs.ipv6)) {
|
||||
ret = -EADDRINUSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bind = rcu_dereference_protected(peer->bind, true);
|
||||
/* peers connected via TCP have bind == NULL */
|
||||
if (bind) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#ifndef _NET_OVPN_OVPNPEER_H_
|
||||
#define _NET_OVPN_OVPNPEER_H_
|
||||
|
||||
#include <linux/seqlock.h>
|
||||
#include <net/dst_cache.h>
|
||||
#include <net/strparser.h>
|
||||
|
||||
|
|
@ -17,6 +18,16 @@
|
|||
#include "socket.h"
|
||||
#include "stats.h"
|
||||
|
||||
/**
|
||||
* struct ovpn_route_key - route key used for the peer dst cache
|
||||
* @mark: fwmark used for route lookup
|
||||
* @sport: UDP source port used for route lookup
|
||||
*/
|
||||
struct ovpn_route_key {
|
||||
u32 mark;
|
||||
__be16 sport;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ovpn_peer - the main remote peer object
|
||||
* @ovpn: main openvpn instance this peer belongs to
|
||||
|
|
@ -45,6 +56,8 @@
|
|||
* @tcp.sk_cb.ops: pointer to the original prot_ops object (TCP only)
|
||||
* @crypto: the crypto configuration (ciphers, keys, etc..)
|
||||
* @dst_cache: cache for dst_entry used to send to peer
|
||||
* @route_key: route key matching the current dst cache contents
|
||||
* @route_key_seq: seqcount protecting lockless route_key reads
|
||||
* @bind: remote peer binding
|
||||
* @keepalive_interval: seconds after which a new keepalive should be sent
|
||||
* @keepalive_xmit_exp: future timestamp when next keepalive should be sent
|
||||
|
|
@ -55,7 +68,7 @@
|
|||
* @vpn_stats: per-peer in-VPN TX/RX stats
|
||||
* @link_stats: per-peer link/transport TX/RX stats
|
||||
* @delete_reason: why peer was deleted (i.e. timeout, transport error, ..)
|
||||
* @lock: protects binding to peer (bind) and keepalive* fields
|
||||
* @lock: protects binding to peer (bind), route_key and keepalive* fields
|
||||
* @refcount: reference counter
|
||||
* @rcu: used to free peer in an RCU safe way
|
||||
* @release_entry: entry for the socket release list
|
||||
|
|
@ -99,6 +112,8 @@ struct ovpn_peer {
|
|||
} tcp;
|
||||
struct ovpn_crypto_state crypto;
|
||||
struct dst_cache dst_cache;
|
||||
struct ovpn_route_key route_key;
|
||||
seqcount_spinlock_t route_key_seq;
|
||||
struct ovpn_bind __rcu *bind;
|
||||
unsigned long keepalive_interval;
|
||||
unsigned long keepalive_xmit_exp;
|
||||
|
|
@ -109,7 +124,7 @@ struct ovpn_peer {
|
|||
struct ovpn_peer_stats vpn_stats;
|
||||
struct ovpn_peer_stats link_stats;
|
||||
enum ovpn_del_peer_reason delete_reason;
|
||||
spinlock_t lock; /* protects bind and keepalive* */
|
||||
spinlock_t lock; /* protects bind, route_key and keepalive* */
|
||||
struct kref refcount;
|
||||
struct rcu_head rcu;
|
||||
struct llist_node release_entry;
|
||||
|
|
@ -149,6 +164,12 @@ struct ovpn_peer *ovpn_peer_get_by_transp_addr(struct ovpn_priv *ovpn,
|
|||
struct ovpn_peer *ovpn_peer_get_by_id(struct ovpn_priv *ovpn, u32 peer_id);
|
||||
struct ovpn_peer *ovpn_peer_get_by_dst(struct ovpn_priv *ovpn,
|
||||
struct sk_buff *skb);
|
||||
bool ovpn_peer_vpn_addr_conflict4(struct ovpn_priv *ovpn,
|
||||
const struct ovpn_peer *peer,
|
||||
const struct in_addr *addr);
|
||||
bool ovpn_peer_vpn_addr_conflict6(struct ovpn_priv *ovpn,
|
||||
const struct ovpn_peer *peer,
|
||||
const struct in6_addr *addr);
|
||||
void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer);
|
||||
void ovpn_peer_hash_transp_addr(struct ovpn_peer *peer);
|
||||
bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb,
|
||||
|
|
|
|||
|
|
@ -131,6 +131,77 @@ static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool ovpn_route_key_equal(const struct ovpn_route_key *a,
|
||||
const struct ovpn_route_key *b)
|
||||
{
|
||||
return a->mark == b->mark && a->sport == b->sport;
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_dst_cache_check_key - reset peer dst cache after key changes
|
||||
* @peer: the peer owning the dst cache
|
||||
* @cache: the cache that might need to be reset
|
||||
* @key: the route key for the packet being transmitted
|
||||
*
|
||||
* Reset the peer dst cache if it was populated for a different route key.
|
||||
*/
|
||||
static void ovpn_dst_cache_check_key(struct ovpn_peer *peer,
|
||||
struct dst_cache *cache,
|
||||
const struct ovpn_route_key *key)
|
||||
{
|
||||
struct ovpn_route_key old_key;
|
||||
unsigned int seq;
|
||||
|
||||
/* snapshot the saved key before deciding whether the cache matches */
|
||||
do {
|
||||
seq = read_seqcount_begin(&peer->route_key_seq);
|
||||
old_key = peer->route_key;
|
||||
} while (read_seqcount_retry(&peer->route_key_seq, seq));
|
||||
|
||||
/* nothing changed: the current cache can be reused */
|
||||
if (likely(ovpn_route_key_equal(&old_key, key)))
|
||||
return;
|
||||
|
||||
/* recheck under lock because another path may have updated the key */
|
||||
spin_lock_bh(&peer->lock);
|
||||
if (!ovpn_route_key_equal(&peer->route_key, key)) {
|
||||
write_seqcount_begin(&peer->route_key_seq);
|
||||
peer->route_key = *key;
|
||||
dst_cache_reset(cache);
|
||||
write_seqcount_end(&peer->route_key_seq);
|
||||
}
|
||||
spin_unlock_bh(&peer->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_dst_cache_current - check whether a route lookup matches peer state
|
||||
* @peer: the peer owning the bind and dst cache
|
||||
* @bind: the RCU bind used for the route lookup
|
||||
* @key: the route key used for the route lookup
|
||||
*
|
||||
* Check that @bind is still the current peer bind and that @key still matches
|
||||
* the peer route key. The caller must hold @peer->lock. The TX path keeps
|
||||
* @bind inside an RCU read-side critical section, so pointer identity is enough
|
||||
* to detect whether the bind was replaced while the route lookup was running.
|
||||
*
|
||||
* Return: true if the lookup result still matches the current peer state and
|
||||
* may update the dst cache or replace the bind.
|
||||
*/
|
||||
static bool ovpn_dst_cache_current(const struct ovpn_peer *peer,
|
||||
const struct ovpn_bind *bind,
|
||||
const struct ovpn_route_key *key)
|
||||
{
|
||||
const struct ovpn_bind *curr_bind;
|
||||
|
||||
lockdep_assert_held(&peer->lock);
|
||||
|
||||
curr_bind = rcu_dereference_protected(peer->bind,
|
||||
lockdep_is_held(&peer->lock));
|
||||
|
||||
return curr_bind == bind &&
|
||||
ovpn_route_key_equal(key, &peer->route_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_udp4_output - send IPv4 packet over udp socket
|
||||
* @peer: the destination peer
|
||||
|
|
@ -138,21 +209,26 @@ static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
|
|||
* @cache: dst cache
|
||||
* @sk: the socket to send the packet over
|
||||
* @skb: the packet to send
|
||||
* @key: the route key snapshot used for cache validation and flow lookup
|
||||
*
|
||||
* Return: 0 on success or a negative error code otherwise
|
||||
*/
|
||||
static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
||||
struct dst_cache *cache, struct sock *sk,
|
||||
struct sk_buff *skb)
|
||||
struct sk_buff *skb,
|
||||
const struct ovpn_route_key *key)
|
||||
{
|
||||
struct sockaddr_storage remote;
|
||||
struct in_addr local = {};
|
||||
bool reset_local = false;
|
||||
struct rtable *rt;
|
||||
struct flowi4 fl = {
|
||||
.saddr = bind->local.ipv4.s_addr,
|
||||
.daddr = bind->remote.in4.sin_addr.s_addr,
|
||||
.fl4_sport = inet_sk(sk)->inet_sport,
|
||||
.fl4_sport = key->sport,
|
||||
.fl4_dport = bind->remote.in4.sin_port,
|
||||
.flowi4_proto = sk->sk_protocol,
|
||||
.flowi4_mark = sk->sk_mark,
|
||||
.flowi4_mark = key->mark,
|
||||
};
|
||||
int ret;
|
||||
|
||||
|
|
@ -161,26 +237,19 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
if (rt)
|
||||
goto transmit;
|
||||
|
||||
if (unlikely(!inet_confirm_addr(sock_net(sk), NULL, 0, fl.saddr,
|
||||
RT_SCOPE_HOST))) {
|
||||
/* we may end up here when the cached address is not usable
|
||||
* anymore. In this case we reset address/cache and perform a
|
||||
* new look up
|
||||
if (fl.saddr && unlikely(!inet_confirm_addr(sock_net(sk), NULL, 0,
|
||||
fl.saddr, RT_SCOPE_HOST))) {
|
||||
/* The learned local address is not usable anymore.
|
||||
* Retry with source address autoselection.
|
||||
*/
|
||||
fl.saddr = 0;
|
||||
spin_lock_bh(&peer->lock);
|
||||
bind->local.ipv4.s_addr = 0;
|
||||
spin_unlock_bh(&peer->lock);
|
||||
dst_cache_reset(cache);
|
||||
reset_local = true;
|
||||
}
|
||||
|
||||
rt = ip_route_output_flow(sock_net(sk), &fl, sk);
|
||||
if (IS_ERR(rt) && PTR_ERR(rt) == -EINVAL) {
|
||||
fl.saddr = 0;
|
||||
spin_lock_bh(&peer->lock);
|
||||
bind->local.ipv4.s_addr = 0;
|
||||
spin_unlock_bh(&peer->lock);
|
||||
dst_cache_reset(cache);
|
||||
reset_local = true;
|
||||
|
||||
rt = ip_route_output_flow(sock_net(sk), &fl, sk);
|
||||
}
|
||||
|
|
@ -193,7 +262,30 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
ret);
|
||||
goto err;
|
||||
}
|
||||
dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
|
||||
|
||||
/* avoid storing a stale cache or local address */
|
||||
spin_lock_bh(&peer->lock);
|
||||
if (likely(ovpn_dst_cache_current(peer, bind, key))) {
|
||||
if (!reset_local) {
|
||||
dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
|
||||
spin_unlock_bh(&peer->lock);
|
||||
goto transmit;
|
||||
}
|
||||
|
||||
/* invalidate per-CPU dst entries that may still carry
|
||||
* the stale source
|
||||
*/
|
||||
dst_cache_reset(cache);
|
||||
|
||||
/* preserve the current remote */
|
||||
memcpy(&remote, &bind->remote, sizeof(struct sockaddr_in));
|
||||
/* The current packet already has a valid wildcard-source route.
|
||||
* If replacing the bind fails, leave the stale local in place;
|
||||
* a later cache miss will retry the repair.
|
||||
*/
|
||||
ovpn_peer_reset_sockaddr(peer, &remote, &local);
|
||||
}
|
||||
spin_unlock_bh(&peer->lock);
|
||||
|
||||
transmit:
|
||||
udp_tunnel_xmit_skb(rt, sk, skb, fl.saddr, fl.daddr, 0,
|
||||
|
|
@ -213,23 +305,28 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
* @cache: dst cache
|
||||
* @sk: the socket to send the packet over
|
||||
* @skb: the packet to send
|
||||
* @key: the route key snapshot used for cache validation and flow lookup
|
||||
*
|
||||
* Return: 0 on success or a negative error code otherwise
|
||||
*/
|
||||
static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
||||
struct dst_cache *cache, struct sock *sk,
|
||||
struct sk_buff *skb)
|
||||
struct sk_buff *skb,
|
||||
const struct ovpn_route_key *key)
|
||||
{
|
||||
struct in6_addr local = in6addr_any;
|
||||
struct sockaddr_storage remote;
|
||||
bool reset_local = false;
|
||||
struct dst_entry *dst;
|
||||
int ret;
|
||||
|
||||
struct flowi6 fl = {
|
||||
.saddr = bind->local.ipv6,
|
||||
.daddr = bind->remote.in6.sin6_addr,
|
||||
.fl6_sport = inet_sk(sk)->inet_sport,
|
||||
.fl6_sport = key->sport,
|
||||
.fl6_dport = bind->remote.in6.sin6_port,
|
||||
.flowi6_proto = sk->sk_protocol,
|
||||
.flowi6_mark = sk->sk_mark,
|
||||
.flowi6_mark = key->mark,
|
||||
.flowi6_oif = bind->remote.in6.sin6_scope_id,
|
||||
};
|
||||
|
||||
|
|
@ -238,16 +335,13 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
if (dst)
|
||||
goto transmit;
|
||||
|
||||
if (unlikely(!ipv6_chk_addr(sock_net(sk), &fl.saddr, NULL, 0))) {
|
||||
/* we may end up here when the cached address is not usable
|
||||
* anymore. In this case we reset address/cache and perform a
|
||||
* new look up
|
||||
if (!ipv6_addr_any(&fl.saddr) &&
|
||||
unlikely(!ipv6_chk_addr(sock_net(sk), &fl.saddr, NULL, 0))) {
|
||||
/* The learned local address is not usable anymore.
|
||||
* Retry with source address autoselection.
|
||||
*/
|
||||
fl.saddr = in6addr_any;
|
||||
spin_lock_bh(&peer->lock);
|
||||
bind->local.ipv6 = in6addr_any;
|
||||
spin_unlock_bh(&peer->lock);
|
||||
dst_cache_reset(cache);
|
||||
reset_local = true;
|
||||
}
|
||||
|
||||
dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl, NULL);
|
||||
|
|
@ -258,7 +352,30 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
&bind->remote.in6, ret);
|
||||
goto err;
|
||||
}
|
||||
dst_cache_set_ip6(cache, dst, &fl.saddr);
|
||||
|
||||
/* avoid storing a stale cache or local address */
|
||||
spin_lock_bh(&peer->lock);
|
||||
if (likely(ovpn_dst_cache_current(peer, bind, key))) {
|
||||
if (!reset_local) {
|
||||
dst_cache_set_ip6(cache, dst, &fl.saddr);
|
||||
spin_unlock_bh(&peer->lock);
|
||||
goto transmit;
|
||||
}
|
||||
|
||||
/* invalidate per-CPU dst entries that may still carry
|
||||
* the stale source
|
||||
*/
|
||||
dst_cache_reset(cache);
|
||||
|
||||
/* preserve the current remote */
|
||||
memcpy(&remote, &bind->remote, sizeof(struct sockaddr_in6));
|
||||
/* The current packet already has a valid wildcard-source route.
|
||||
* If replacing the bind fails, leave the stale local in place;
|
||||
* a later cache miss will retry the repair.
|
||||
*/
|
||||
ovpn_peer_reset_sockaddr(peer, &remote, &local);
|
||||
}
|
||||
spin_unlock_bh(&peer->lock);
|
||||
|
||||
transmit:
|
||||
/* user IPv6 packets may be larger than the transport interface
|
||||
|
|
@ -287,6 +404,7 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
* @cache: dst cache
|
||||
* @sk: the socket to send the packet over
|
||||
* @skb: the packet to send
|
||||
* @key: route key snapshot used for cache validation and flow lookup
|
||||
*
|
||||
* rcu_read_lock should be held on entry.
|
||||
* On return, the skb is consumed.
|
||||
|
|
@ -294,7 +412,8 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
* Return: 0 on success or a negative error code otherwise
|
||||
*/
|
||||
static int ovpn_udp_output(struct ovpn_peer *peer, struct dst_cache *cache,
|
||||
struct sock *sk, struct sk_buff *skb)
|
||||
struct sock *sk, struct sk_buff *skb,
|
||||
struct ovpn_route_key *key)
|
||||
{
|
||||
struct ovpn_bind *bind;
|
||||
int ret;
|
||||
|
|
@ -314,11 +433,11 @@ static int ovpn_udp_output(struct ovpn_peer *peer, struct dst_cache *cache,
|
|||
|
||||
switch (bind->remote.in4.sin_family) {
|
||||
case AF_INET:
|
||||
ret = ovpn_udp4_output(peer, bind, cache, sk, skb);
|
||||
ret = ovpn_udp4_output(peer, bind, cache, sk, skb, key);
|
||||
break;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
case AF_INET6:
|
||||
ret = ovpn_udp6_output(peer, bind, cache, sk, skb);
|
||||
ret = ovpn_udp6_output(peer, bind, cache, sk, skb, key);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
|
|
@ -340,15 +459,21 @@ static int ovpn_udp_output(struct ovpn_peer *peer, struct dst_cache *cache,
|
|||
void ovpn_udp_send_skb(struct ovpn_peer *peer, struct sock *sk,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ovpn_route_key key = {
|
||||
.mark = READ_ONCE(sk->sk_mark),
|
||||
.sport = READ_ONCE(inet_sk(sk)->inet_sport),
|
||||
};
|
||||
int ret;
|
||||
|
||||
skb->dev = peer->ovpn->dev;
|
||||
skb->mark = READ_ONCE(sk->sk_mark);
|
||||
skb->mark = key.mark;
|
||||
/* no checksum performed at this layer */
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
||||
ovpn_dst_cache_check_key(peer, &peer->dst_cache, &key);
|
||||
|
||||
/* crypto layer -> transport (UDP) */
|
||||
ret = ovpn_udp_output(peer, &peer->dst_cache, sk, skb);
|
||||
ret = ovpn_udp_output(peer, &peer->dst_cache, sk, skb, &key);
|
||||
if (unlikely(ret < 0))
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -683,7 +683,8 @@ static int miic_parse_dt(struct miic *miic, u32 *mode_cfg)
|
|||
if (!dt_val)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(dt_val, MIIC_MODCTRL_CONF_NONE, sizeof(*dt_val));
|
||||
memset(dt_val, MIIC_MODCTRL_CONF_NONE,
|
||||
sizeof(*dt_val) * miic->of_data->conf_conv_count);
|
||||
|
||||
if (of_property_read_u32(np, "renesas,miic-switch-portin", &conf) == 0)
|
||||
dt_val[0] = conf;
|
||||
|
|
|
|||
|
|
@ -1545,8 +1545,10 @@ static int xpcs_init_clks(struct dw_xpcs *xpcs)
|
|||
return dev_err_probe(dev, ret, "Failed to get clocks\n");
|
||||
|
||||
ret = clk_bulk_prepare_enable(DW_XPCS_NUM_CLKS, xpcs->clks);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
clk_bulk_put(DW_XPCS_NUM_CLKS, xpcs->clks);
|
||||
return dev_err_probe(dev, ret, "Failed to enable clocks\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@
|
|||
#define XWAY_MDIO_ISTAT 0x1A /* interrupt status */
|
||||
#define XWAY_MDIO_LED 0x1B /* led control */
|
||||
|
||||
#define XWAY_MDIO_GCTRL_TM_MASK GENMASK(15, 13)
|
||||
#define XWAY_MDIO_GCTRL_TM(mode) FIELD_PREP(XWAY_MDIO_GCTRL_TM_MASK, (mode))
|
||||
#define XWAY_MDIO_GCTRL_TM_NOP XWAY_MDIO_GCTRL_TM(0) /* Normal operation */
|
||||
#define XWAY_MDIO_GCTRL_TM_CDIAG XWAY_MDIO_GCTRL_TM(6) /* Cable diagnostics */
|
||||
|
||||
#define XWAY_MDIO_ERRCNT_SEL GENMASK(11, 8)
|
||||
#define XWAY_MDIO_ERRCNT_COUNT GENMASK(7, 0)
|
||||
#define XWAY_MDIO_ERRCNT_SEL_RXERR 0
|
||||
|
|
@ -326,6 +331,28 @@ static int xway_gphy_probe(struct phy_device *phydev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int xway_11g_int_config_init(struct phy_device *phydev)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* An issue has been sporadically observed after device power-on on the
|
||||
* first link-up attempt in 100BASE-TX mode resulting in either the
|
||||
* link-up taking a long time, or failing to link-up altogether.
|
||||
*
|
||||
* Workaround:
|
||||
* After power-on, enable Cable Diagnostic Mode for all ports and
|
||||
* disable it.
|
||||
*/
|
||||
err = phy_modify(phydev, MII_CTRL1000, XWAY_MDIO_GCTRL_TM_MASK, XWAY_MDIO_GCTRL_TM_CDIAG);
|
||||
if (err)
|
||||
return err;
|
||||
err = phy_modify(phydev, MII_CTRL1000, XWAY_MDIO_GCTRL_TM_MASK, XWAY_MDIO_GCTRL_TM_NOP);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return xway_gphy_config_init(phydev);
|
||||
}
|
||||
|
||||
static int xway_gphy14_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
int reg, err;
|
||||
|
|
@ -735,7 +762,7 @@ static struct phy_driver xway_gphy[] = {
|
|||
.phy_id_mask = 0xffffffff,
|
||||
.name = "Intel XWAY PHY11G (xRX v1.2 integrated)",
|
||||
/* PHY_GBIT_FEATURES */
|
||||
.config_init = xway_gphy_config_init,
|
||||
.config_init = xway_11g_int_config_init,
|
||||
.probe = xway_gphy_probe,
|
||||
.handle_interrupt = xway_gphy_handle_interrupt,
|
||||
.config_intr = xway_gphy_config_intr,
|
||||
|
|
|
|||
|
|
@ -6344,6 +6344,7 @@ static int lanphy_write_reg_data(struct phy_device *phydev,
|
|||
data->val);
|
||||
if (ret)
|
||||
break;
|
||||
data++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -2129,7 +2129,6 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
|
|||
mutex_lock(&pl->phydev_mutex);
|
||||
mutex_lock(&phy->lock);
|
||||
mutex_lock(&pl->state_mutex);
|
||||
pl->phydev = phy;
|
||||
pl->phy_state.interface = interface;
|
||||
pl->phy_state.pause = MLO_PAUSE_NONE;
|
||||
pl->phy_state.speed = SPEED_UNKNOWN;
|
||||
|
|
@ -2196,10 +2195,25 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
|
|||
ret = 0;
|
||||
}
|
||||
|
||||
if (ret == 0 && phy_interrupt_is_valid(phy))
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Nothing below can fail, so the PHY can be recorded now. Doing it
|
||||
* here rather than above keeps a failed bringup from leaving
|
||||
* pl->phydev pointing at a PHY the caller is about to detach.
|
||||
*/
|
||||
mutex_lock(&pl->phydev_mutex);
|
||||
mutex_lock(&phy->lock);
|
||||
mutex_lock(&pl->state_mutex);
|
||||
pl->phydev = phy;
|
||||
mutex_unlock(&pl->state_mutex);
|
||||
mutex_unlock(&phy->lock);
|
||||
mutex_unlock(&pl->phydev_mutex);
|
||||
|
||||
if (phy_interrupt_is_valid(phy))
|
||||
phy_request_interrupt(phy);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,
|
||||
|
|
|
|||
|
|
@ -233,17 +233,26 @@ static void catc_rx_done(struct urb *urb)
|
|||
}
|
||||
|
||||
do {
|
||||
if(!catc->is_f5u011) {
|
||||
pkt_len = le16_to_cpup((__le16*)pkt_start);
|
||||
if (pkt_len > urb->actual_length) {
|
||||
int remaining = urb->actual_length -
|
||||
(pkt_start - (u8 *)urb->transfer_buffer);
|
||||
|
||||
if (!catc->is_f5u011) {
|
||||
if (remaining < pkt_offset) {
|
||||
catc->netdev->stats.rx_length_errors++;
|
||||
catc->netdev->stats.rx_errors++;
|
||||
break;
|
||||
}
|
||||
pkt_len = le16_to_cpup((__le16 *)pkt_start);
|
||||
} else {
|
||||
pkt_len = urb->actual_length;
|
||||
}
|
||||
|
||||
if (pkt_len < ETH_HLEN || pkt_len + pkt_offset > remaining) {
|
||||
catc->netdev->stats.rx_length_errors++;
|
||||
catc->netdev->stats.rx_errors++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(skb = dev_alloc_skb(pkt_len)))
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -635,6 +635,11 @@ static const struct usb_device_id mbim_devs[] = {
|
|||
.driver_info = (unsigned long)&cdc_mbim_info,
|
||||
},
|
||||
|
||||
/* MeiG Smart SRM821 ZLP conformance */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d53, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
|
||||
.driver_info = (unsigned long)&cdc_mbim_info,
|
||||
},
|
||||
|
||||
/* Some Huawei devices, ME906s-158 (12d1:15c1) and E3372
|
||||
* (12d1:157d), are known to fail unless the NDP is placed
|
||||
* after the IP packets. Applying the quirk to all Huawei
|
||||
|
|
|
|||
|
|
@ -5239,10 +5239,12 @@ static bool lan78xx_submit_deferred_urbs(struct lan78xx_net *dev)
|
|||
!netif_carrier_ok(dev->net) ||
|
||||
pipe_halted) {
|
||||
lan78xx_release_tx_buf(dev, skb);
|
||||
usb_put_urb(urb);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
usb_put_urb(urb);
|
||||
|
||||
if (ret == 0) {
|
||||
netif_trans_update(dev->net);
|
||||
|
|
|
|||
|
|
@ -1086,6 +1086,7 @@ static const struct usb_device_id products[] = {
|
|||
{QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */
|
||||
{QMI_MATCH_FF_FF_FF(0x2c7c, 0x013d)}, /* Quectel RG660QB */
|
||||
{QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)}, /* Quectel EP06/EG06/EM06 */
|
||||
{QMI_MATCH_FF_FF_FF(0x2c7c, 0x030b)}, /* Quectel EM060K/EG120K-EA */
|
||||
{QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */
|
||||
{QMI_MATCH_FF_FF_FF(0x2c7c, 0x0620)}, /* Quectel EM160R-GL */
|
||||
{QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)}, /* Quectel RM500Q-GL */
|
||||
|
|
|
|||
|
|
@ -355,7 +355,8 @@ static int sr9700_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
|||
/* ignore the CRC length */
|
||||
len = (skb->data[1] | (skb->data[2] << 8)) - 4;
|
||||
|
||||
if (len > ETH_FRAME_LEN || len > skb->len || len < 0)
|
||||
if (len > ETH_FRAME_LEN || len < 0 ||
|
||||
len > skb->len - SR_RX_OVERHEAD)
|
||||
return 0;
|
||||
|
||||
/* the last packet of current skb */
|
||||
|
|
|
|||
|
|
@ -1054,6 +1054,7 @@ static int __veth_napi_enable_range(struct net_device *dev, int start, int end)
|
|||
for (i = start; i < end; i++) {
|
||||
struct veth_rq *rq = &priv->rq[i];
|
||||
|
||||
rcu_assign_pointer(rq->xdp_prog, priv->_xdp_prog);
|
||||
napi_enable(&rq->xdp_napi);
|
||||
rcu_assign_pointer(priv->rq[i].napi, &priv->rq[i].xdp_napi);
|
||||
}
|
||||
|
|
@ -1088,6 +1089,7 @@ static void veth_napi_del_range(struct net_device *dev, int start, int end)
|
|||
|
||||
rcu_assign_pointer(priv->rq[i].napi, NULL);
|
||||
napi_disable(&rq->xdp_napi);
|
||||
rcu_assign_pointer(rq->xdp_prog, NULL);
|
||||
__netif_napi_del(&rq->xdp_napi);
|
||||
}
|
||||
synchronize_net();
|
||||
|
|
|
|||
|
|
@ -3349,6 +3349,14 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
else
|
||||
virtqueue_disable_cb(sq->vq);
|
||||
|
||||
if (!use_napi &&
|
||||
unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) {
|
||||
DEV_STATS_INC(dev, tx_dropped);
|
||||
dev_kfree_skb_any(skb);
|
||||
kick = !xmit_more || netif_xmit_stopped(txq);
|
||||
goto kick_vq;
|
||||
}
|
||||
|
||||
/* timestamp packet in software */
|
||||
skb_tx_timestamp(skb);
|
||||
|
||||
|
|
@ -3381,6 +3389,7 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
|
||||
kick = use_napi ? __netdev_tx_sent_queue(txq, skb->len, xmit_more) :
|
||||
!xmit_more || netif_xmit_stopped(txq);
|
||||
kick_vq:
|
||||
if (kick) {
|
||||
if (virtqueue_kick_prepare(sq->vq) && virtqueue_notify(sq->vq)) {
|
||||
u64_stats_update_begin(&sq->stats.syncp);
|
||||
|
|
|
|||
|
|
@ -1175,8 +1175,6 @@ static int vrf_prepare_mac_header(struct sk_buff *skb,
|
|||
skb->protocol = eth->h_proto;
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
|
||||
skb_postpush_rcsum(skb, skb->data, ETH_HLEN);
|
||||
|
||||
skb_pull_inline(skb, ETH_HLEN);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1958,13 +1958,15 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,
|
|||
struct ipv6hdr *pip6;
|
||||
u8 *daddr;
|
||||
int na_olen = 8; /* opt hdr + ETH_ALEN for target */
|
||||
int headroom;
|
||||
int ns_olen;
|
||||
int i, len;
|
||||
|
||||
if (dev == NULL || !pskb_may_pull(request, request->len))
|
||||
return NULL;
|
||||
|
||||
len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) +
|
||||
headroom = LL_RESERVED_SPACE(dev);
|
||||
len = headroom + sizeof(struct ipv6hdr) +
|
||||
sizeof(*na) + na_olen + dev->needed_tailroom;
|
||||
reply = alloc_skb(len, GFP_ATOMIC);
|
||||
if (reply == NULL)
|
||||
|
|
@ -1972,7 +1974,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,
|
|||
|
||||
reply->protocol = htons(ETH_P_IPV6);
|
||||
reply->dev = dev;
|
||||
skb_reserve(reply, LL_RESERVED_SPACE(request->dev));
|
||||
skb_reserve(reply, headroom);
|
||||
skb_push(reply, sizeof(struct ethhdr));
|
||||
skb_reset_mac_header(reply);
|
||||
|
||||
|
|
|
|||
|
|
@ -251,9 +251,9 @@ static int microread_start_poll(struct nfc_hci_dev *hdev,
|
|||
param[1] |= (1 << 1);
|
||||
|
||||
if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
|
||||
hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
|
||||
&hdev->gb_len);
|
||||
if (hdev->gb == NULL || hdev->gb_len == 0) {
|
||||
nfc_get_local_general_bytes(hdev->ndev, hdev->gb,
|
||||
sizeof(hdev->gb), &hdev->gb_len);
|
||||
if (hdev->gb_len == 0) {
|
||||
im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
|
||||
tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -263,9 +263,14 @@ static int process_state_fw_dnld(struct nfcmrvl_private *priv,
|
|||
* B8..N: payload
|
||||
*/
|
||||
|
||||
/* Remove NCI HDR */
|
||||
skb_pull(skb, 3);
|
||||
if (skb->data[0] != HELPER_CMD_PACKET_FORMAT || skb->len != 5) {
|
||||
if (skb->len != NCI_DATA_HDR_SIZE + 5) {
|
||||
nfc_err(priv->dev, "bad command");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Remove NCI header */
|
||||
skb_pull(skb, NCI_DATA_HDR_SIZE);
|
||||
if (skb->data[0] != HELPER_CMD_PACKET_FORMAT) {
|
||||
nfc_err(priv->dev, "bad command");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1357,10 +1357,11 @@ static int pn533_poll_dep(struct nfc_dev *nfc_dev)
|
|||
u8 *next, nfcid3[NFC_NFCID3_MAXSIZE];
|
||||
u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
|
||||
|
||||
if (!dev->gb) {
|
||||
dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
|
||||
|
||||
if (!dev->gb || !dev->gb_len) {
|
||||
if (!dev->gb_len) {
|
||||
nfc_get_local_general_bytes(nfc_dev, dev->gb,
|
||||
sizeof(dev->gb),
|
||||
&dev->gb_len);
|
||||
if (!dev->gb_len) {
|
||||
dev->poll_dep = 0;
|
||||
queue_work(dev->wq, &dev->rf_work);
|
||||
}
|
||||
|
|
@ -1658,8 +1659,9 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev,
|
|||
}
|
||||
|
||||
if (tm_protocols) {
|
||||
dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
|
||||
if (dev->gb == NULL)
|
||||
nfc_get_local_general_bytes(nfc_dev, dev->gb,
|
||||
sizeof(dev->gb), &dev->gb_len);
|
||||
if (dev->gb_len == 0)
|
||||
tm_protocols = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
* Copyright (C) 2012-2013 Tieto Poland
|
||||
*/
|
||||
|
||||
#include <net/nfc/nfc.h>
|
||||
|
||||
#define PN533_DEVICE_STD 0x1
|
||||
#define PN533_DEVICE_PASORI 0x2
|
||||
#define PN533_DEVICE_ACR122U 0x3
|
||||
|
|
@ -166,7 +168,7 @@ struct pn533 {
|
|||
struct timer_list listen_timer;
|
||||
int cancel_listen;
|
||||
|
||||
u8 *gb;
|
||||
u8 gb[NFC_MAX_GT_LEN];
|
||||
size_t gb_len;
|
||||
|
||||
u8 tgt_available_prots;
|
||||
|
|
|
|||
|
|
@ -319,7 +319,9 @@ static bool pn533_acr122_is_rx_frame_valid(void *_frame, struct pn533 *dev)
|
|||
if (frame->ccid.type != 0x83)
|
||||
return false;
|
||||
|
||||
if (!frame->ccid.datalen)
|
||||
if (frame->ccid.datalen < 2 ||
|
||||
frame->ccid.datalen > PN533_ACR122_FRAME_MAX_PAYLOAD_LEN +
|
||||
PN533_ACR122_RX_FRAME_TAIL_LEN)
|
||||
return false;
|
||||
|
||||
if (frame->data[frame->ccid.datalen - 2] == 0x63)
|
||||
|
|
|
|||
|
|
@ -377,10 +377,9 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
|
|||
return r;
|
||||
|
||||
if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
|
||||
hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
|
||||
&hdev->gb_len);
|
||||
pr_debug("generate local bytes %p\n", hdev->gb);
|
||||
if (hdev->gb == NULL || hdev->gb_len == 0) {
|
||||
nfc_get_local_general_bytes(hdev->ndev, hdev->gb,
|
||||
sizeof(hdev->gb), &hdev->gb_len);
|
||||
if (hdev->gb_len == 0) {
|
||||
im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
|
||||
tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -636,6 +636,13 @@ static void port100_recv_response(struct urb *urb)
|
|||
|
||||
in_frame = dev->in_urb->transfer_buffer;
|
||||
|
||||
if (urb->actual_length < PORT100_FRAME_HEADER_LEN ||
|
||||
urb->actual_length < port100_rx_frame_size(in_frame)) {
|
||||
nfc_err(&dev->interface->dev, "Received a truncated frame\n");
|
||||
cmd->status = -EIO;
|
||||
goto sched_wq;
|
||||
}
|
||||
|
||||
if (!port100_rx_frame_is_valid(in_frame)) {
|
||||
nfc_err(&dev->interface->dev, "Received an invalid frame\n");
|
||||
cmd->status = -EIO;
|
||||
|
|
|
|||
|
|
@ -351,10 +351,10 @@ static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev,
|
|||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
|
||||
&hdev->gb_len);
|
||||
|
||||
if (hdev->gb == NULL || hdev->gb_len == 0) {
|
||||
nfc_get_local_general_bytes(hdev->ndev, hdev->gb,
|
||||
sizeof(hdev->gb),
|
||||
&hdev->gb_len);
|
||||
if (hdev->gb_len == 0) {
|
||||
im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
|
||||
tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
|
||||
}
|
||||
|
|
@ -577,9 +577,7 @@ static int st21nfca_get_iso15693_inventory(struct nfc_hci_dev *hdev,
|
|||
if (r < 0)
|
||||
goto exit;
|
||||
|
||||
skb_pull(inventory_skb, 2);
|
||||
|
||||
if (inventory_skb->len == 0 ||
|
||||
if (!skb_pull(inventory_skb, 2) || inventory_skb->len < 2 ||
|
||||
inventory_skb->len > NFC_ISO15693_UID_MAXSIZE) {
|
||||
r = -EPROTO;
|
||||
goto exit;
|
||||
|
|
|
|||
|
|
@ -289,27 +289,36 @@ static int check_crc(u8 *buf, int buflen)
|
|||
*/
|
||||
static int st21nfca_hci_i2c_repack(struct sk_buff *skb)
|
||||
{
|
||||
int i, j, r, size;
|
||||
int read, write, r, size;
|
||||
|
||||
if (skb->len < 1 || (skb->len > 1 && skb->data[1] != 0))
|
||||
if (skb->len < ST21NFCA_FRAME_HEADROOM ||
|
||||
!IS_START_OF_FRAME(skb->data))
|
||||
return -EBADMSG;
|
||||
|
||||
size = get_frame_size(skb->data, skb->len);
|
||||
if (size > 0) {
|
||||
if (size < ST21NFCA_FRAME_HEADROOM + 2)
|
||||
return -EBADMSG;
|
||||
|
||||
skb_trim(skb, size);
|
||||
/* remove ST21NFCA byte stuffing for upper layer */
|
||||
for (i = 1, j = 0; i < skb->len; i++) {
|
||||
if (skb->data[i + j] ==
|
||||
for (read = 1, write = 1; read < skb->len;) {
|
||||
if (skb->data[read] ==
|
||||
(u8) ST21NFCA_ESCAPE_BYTE_STUFFING) {
|
||||
skb->data[i] = skb->data[i + j + 1]
|
||||
| ST21NFCA_BYTE_STUFFING_MASK;
|
||||
i++;
|
||||
j++;
|
||||
if (read + 1 == skb->len)
|
||||
return -EBADMSG;
|
||||
|
||||
skb->data[write++] = skb->data[read + 1]
|
||||
| ST21NFCA_BYTE_STUFFING_MASK;
|
||||
read += 2;
|
||||
} else {
|
||||
skb->data[write++] = skb->data[read++];
|
||||
}
|
||||
skb->data[i] = skb->data[i + j];
|
||||
}
|
||||
/* remove byte stuffing useless byte */
|
||||
skb_trim(skb, i - j);
|
||||
skb_trim(skb, write);
|
||||
if (skb->len < ST21NFCA_FRAME_HEADROOM + 2)
|
||||
return -EBADMSG;
|
||||
/* remove ST21NFCA_SOF_EOF from head */
|
||||
skb_pull(skb, 1);
|
||||
|
||||
|
|
|
|||
|
|
@ -1997,8 +1997,10 @@ static int trf7970a_startup(struct trf7970a *trf)
|
|||
return ret;
|
||||
|
||||
ret = trf7970a_update_rx_gain_reduction(trf);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
trf7970a_power_down(trf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_set_active(trf->dev);
|
||||
pm_runtime_enable(trf->dev);
|
||||
|
|
|
|||
|
|
@ -195,7 +195,8 @@ static const struct file_operations virtual_ncidev_fops = {
|
|||
.write = virtual_ncidev_write,
|
||||
.open = virtual_ncidev_open,
|
||||
.release = virtual_ncidev_close,
|
||||
.unlocked_ioctl = virtual_ncidev_ioctl
|
||||
.unlocked_ioctl = virtual_ncidev_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
};
|
||||
|
||||
static struct miscdevice miscdev = {
|
||||
|
|
|
|||
|
|
@ -944,6 +944,7 @@ struct kernel_ethtool_ts_info {
|
|||
#define ETHTOOL_OP_NEEDS_RTNL_SPAUSEPARAM BIT(6)
|
||||
#define ETHTOOL_OP_NEEDS_RTNL_RSS BIT(7)
|
||||
#define ETHTOOL_OP_NEEDS_RTNL_GLINK BIT(8)
|
||||
#define ETHTOOL_OP_NEEDS_RTNL_TEST BIT(9)
|
||||
|
||||
/**
|
||||
* struct ethtool_ops - optional netdev operations
|
||||
|
|
@ -981,6 +982,7 @@ struct kernel_ethtool_ts_info {
|
|||
* - netdev_update_features()
|
||||
* - netif_set_real_num_tx_queues()
|
||||
* - ethtool_op_get_link() (syncs link watch under rtnl_lock)
|
||||
* - netif_open() / netif_close() (used by @self_test)
|
||||
*
|
||||
* @get_drvinfo: Report driver/device information. Modern drivers no
|
||||
* longer have to implement this callback. Most fields are
|
||||
|
|
|
|||
|
|
@ -365,6 +365,9 @@ static inline int __vlan_insert_inner_tag(struct sk_buff *skb,
|
|||
const u8 meta_len = mac_len > ETH_TLEN ? skb_metadata_len(skb) : 0;
|
||||
struct vlan_ethhdr *veth;
|
||||
|
||||
if (unlikely(!pskb_may_pull(skb, mac_len)))
|
||||
return -EINVAL;
|
||||
|
||||
if (skb_cow_head(skb, meta_len + VLAN_HLEN) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
|||
|
|
@ -1834,22 +1834,6 @@ static inline void skb_zcopy_set(struct sk_buff *skb, struct ubuf_info *uarg,
|
|||
}
|
||||
}
|
||||
|
||||
static inline void skb_zcopy_set_nouarg(struct sk_buff *skb, void *val)
|
||||
{
|
||||
skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t) val | 0x1UL);
|
||||
skb_shinfo(skb)->flags |= SKBFL_ZEROCOPY_FRAG;
|
||||
}
|
||||
|
||||
static inline bool skb_zcopy_is_nouarg(struct sk_buff *skb)
|
||||
{
|
||||
return (uintptr_t) skb_shinfo(skb)->destructor_arg & 0x1UL;
|
||||
}
|
||||
|
||||
static inline void *skb_zcopy_get_nouarg(struct sk_buff *skb)
|
||||
{
|
||||
return (void *)((uintptr_t) skb_shinfo(skb)->destructor_arg & ~0x1UL);
|
||||
}
|
||||
|
||||
static inline void net_zcopy_put(struct ubuf_info *uarg)
|
||||
{
|
||||
if (uarg)
|
||||
|
|
@ -1872,8 +1856,7 @@ static inline void skb_zcopy_clear(struct sk_buff *skb, bool zerocopy_success)
|
|||
struct ubuf_info *uarg = skb_zcopy(skb);
|
||||
|
||||
if (uarg) {
|
||||
if (!skb_zcopy_is_nouarg(skb))
|
||||
uarg->ops->complete(skb, uarg, zerocopy_success);
|
||||
uarg->ops->complete(skb, uarg, zerocopy_success);
|
||||
|
||||
skb_shinfo(skb)->flags &= ~SKBFL_ALL_ZEROCOPY;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -455,7 +455,8 @@ static inline unsigned int dst_dev_overhead(struct dst_entry *dst,
|
|||
struct sk_buff *skb)
|
||||
{
|
||||
if (likely(dst))
|
||||
return LL_RESERVED_SPACE(dst->dev);
|
||||
return max_t(unsigned int, skb->mac_len,
|
||||
LL_RESERVED_SPACE(dst->dev));
|
||||
|
||||
return skb->mac_len;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,8 +84,9 @@ static inline size_t guehdr_priv_flags_len(__be32 flags)
|
|||
}
|
||||
|
||||
/* Validate standard and private flags. Returns non-zero (meaning invalid)
|
||||
* if there is an unknown standard or private flags, or the options length for
|
||||
* the flags exceeds the options length specific in hlen of the GUE header.
|
||||
* if there is an unknown standard or private flags, if the options length for
|
||||
* the flags exceeds the options length specified in hlen of the GUE header, or
|
||||
* if a private option contains invalid data.
|
||||
*/
|
||||
static inline int validate_gue_flags(struct guehdr *guehdr, size_t optlen)
|
||||
{
|
||||
|
|
@ -103,8 +104,8 @@ static inline int validate_gue_flags(struct guehdr *guehdr, size_t optlen)
|
|||
/* Private flags are last four bytes accounted in
|
||||
* guehdr_flags_len
|
||||
*/
|
||||
__be32 pflags = *(__be32 *)((void *)&guehdr[1] +
|
||||
len - GUE_LEN_PRIV);
|
||||
void *data = (void *)&guehdr[1] + len;
|
||||
__be32 pflags = *(__be32 *)(data - GUE_LEN_PRIV);
|
||||
|
||||
if (pflags & ~GUE_PFLAGS_ALL)
|
||||
return 1;
|
||||
|
|
@ -112,6 +113,16 @@ static inline int validate_gue_flags(struct guehdr *guehdr, size_t optlen)
|
|||
len += guehdr_priv_flags_len(pflags);
|
||||
if (len > optlen)
|
||||
return 1;
|
||||
|
||||
if (pflags & GUE_PFLAG_REMCSUM) {
|
||||
__be16 *pd = data;
|
||||
|
||||
/* The field offset pd[1] must not be less
|
||||
* than the start pd[0].
|
||||
*/
|
||||
if (ntohs(pd[1]) < ntohs(pd[0]))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -101,12 +101,12 @@ static inline struct dst_entry *ip6_route_output(struct net *net,
|
|||
}
|
||||
|
||||
/* Only conditionally release dst if flags indicates
|
||||
* !RT6_LOOKUP_F_DST_NOREF or dst is in uncached_list.
|
||||
* !RT6_LOOKUP_F_DST_NOREF or dst is uncached.
|
||||
*/
|
||||
static inline void ip6_rt_put_flags(struct rt6_info *rt, int flags)
|
||||
{
|
||||
if (!(flags & RT6_LOOKUP_F_DST_NOREF) ||
|
||||
!list_empty(&rt->dst.rt_uncached))
|
||||
rt->dst.rt_uncached_list)
|
||||
ip6_rt_put(rt);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -185,6 +185,11 @@ static inline void nf_ct_put(struct nf_conn *ct)
|
|||
nf_ct_destroy(&ct->ct_general);
|
||||
}
|
||||
|
||||
static inline bool nf_ct_shared(const struct nf_conn *ct)
|
||||
{
|
||||
return refcount_read(&ct->ct_general.use) > 1;
|
||||
}
|
||||
|
||||
/* load module; enable/disable conntrack in this namespace */
|
||||
int nf_ct_netns_get(struct net *net, u8 nfproto);
|
||||
void nf_ct_netns_put(struct net *net, u8 nfproto);
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ struct nfc_hci_dev {
|
|||
data_exchange_cb_t async_cb;
|
||||
void *async_cb_context;
|
||||
|
||||
u8 *gb;
|
||||
u8 gb[NFC_MAX_GT_LEN];
|
||||
size_t gb_len;
|
||||
|
||||
unsigned long quirks;
|
||||
|
|
|
|||
|
|
@ -273,7 +273,8 @@ struct sk_buff *nfc_alloc_recv_skb(unsigned int size, gfp_t gfp);
|
|||
|
||||
int nfc_set_remote_general_bytes(struct nfc_dev *dev,
|
||||
const u8 *gt, u8 gt_len);
|
||||
u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len);
|
||||
u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, u8 *out_gb,
|
||||
size_t gb_max_len, size_t *gb_len);
|
||||
|
||||
int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
|
||||
u32 result);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user