Commit ec681c53 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

Pull networking fixes from Jakub Kicinski:
 "Quite calm.

  The noisy DSA driver (embedded switches) changes, and adjustment to
  IPv6 IOAM behavior add to diffstat's bottom line but are not scary.

  Current release - regressions:

   - af_unix: rename UNIX-DGRAM to UNIX to maintain backwards
     compatibility

   - procfs: revert "add seq_puts() statement for dev_mcast", minor
     format change broke user space

  Current release - new code bugs:

   - dsa: fix bridge_num not getting cleared after ports leaving the
     bridge, resource leak

   - dsa: tag_dsa: send packets with TX fwd offload from VLAN-unaware
     bridges using VID 0, prevent packet drops if pvid is removed

   - dsa: mv88e6xxx: keep the pvid at 0 when VLAN-unaware, prevent HW
     getting confused about station to VLAN mapping

  Previous releases - regressions:

   - virtio-net: fix for skb_over_panic inside big mode

   - phy: do not shutdown PHYs in READY state

   - dsa: mv88e6xxx: don't use PHY_DETECT on internal PHY's, fix link
     LED staying lit after ifdown

   - mptcp: fix possible infinite wait on recvmsg(MSG_WAITALL)

   - mqprio: Correct stats in mqprio_dump_class_stats()

   - ice: fix deadlock for Tx timestamp tracking flush

   - stmmac: fix feature detection on old hardware

  Previous releases - always broken:

   - sctp: account stream padding length for reconf chunk

   - icmp: fix icmp_ext_echo_iio parsing in icmp_build_probe()

   - isdn: cpai: check ctr->cnr to avoid array index out of bound

   - isdn: mISDN: fix sleeping function called from invalid context

   - nfc: nci: fix potential UAF of rf_conn_info object

   - dsa: microchip: prevent ksz_mib_read_work from kicking back in
     after it's canceled in .remove and crashing

   - dsa: mv88e6xxx: isolate the ATU databases of standalone and bridged
     ports

   - dsa: sja1105, ocelot: break circular dependency between switch and
     tag drivers

   - dsa: felix: improve timestamping in presence of packe loss

   - mlxsw: thermal: fix out-of-bounds memory accesses

  Misc:

   - ipv6: ioam: move the check for undefined bits to improve
     interoperability"

* tag 'net-5.15-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (60 commits)
  icmp: fix icmp_ext_echo_iio parsing in icmp_build_probe
  MAINTAINERS: Update the devicetree documentation path of imx fec driver
  sctp: account stream padding length for reconf chunk
  mlxsw: thermal: Fix out-of-bounds memory accesses
  ethernet: s2io: fix setting mac address during resume
  NFC: digital: fix possible memory leak in digital_in_send_sdd_req()
  NFC: digital: fix possible memory leak in digital_tg_listen_mdaa()
  nfc: fix error handling of nfc_proto_register()
  Revert "net: procfs: add seq_puts() statement for dev_mcast"
  net: encx24j600: check error in devm_regmap_init_encx24j600
  net: korina: select CRC32
  net: arc: select CRC32
  net: dsa: felix: break at first CPU port during init and teardown
  net: dsa: tag_ocelot_8021q: fix inability to inject STP BPDUs into BLOCKING ports
  net: dsa: felix: purge skb from TX timestamping queue if it cannot be sent
  net: dsa: tag_ocelot_8021q: break circular dependency with ocelot switch lib
  net: dsa: tag_ocelot: break circular dependency with ocelot switch lib driver
  net: mscc: ocelot: cross-check the sequence id from the timestamp FIFO with the skb PTP header
  net: mscc: ocelot: deny TX timestamping of non-PTP packets
  net: mscc: ocelot: warn when a PTP IRQ is raised for an unknown skb
  ...
parents 26d65741 1fcd7945
......@@ -21,6 +21,7 @@ select:
contains:
enum:
- snps,dwmac
- snps,dwmac-3.40a
- snps,dwmac-3.50a
- snps,dwmac-3.610
- snps,dwmac-3.70a
......@@ -76,6 +77,7 @@ properties:
- rockchip,rk3399-gmac
- rockchip,rv1108-gmac
- snps,dwmac
- snps,dwmac-3.40a
- snps,dwmac-3.50a
- snps,dwmac-3.610
- snps,dwmac-3.70a
......
......@@ -7440,7 +7440,7 @@ FREESCALE IMX / MXC FEC DRIVER
M: Joakim Zhang <qiangqing.zhang@nxp.com>
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/fsl-fec.txt
F: Documentation/devicetree/bindings/net/fsl,fec.yaml
F: drivers/net/ethernet/freescale/fec.h
F: drivers/net/ethernet/freescale/fec_main.c
F: drivers/net/ethernet/freescale/fec_ptp.c
......@@ -11153,6 +11153,7 @@ S: Maintained
F: Documentation/devicetree/bindings/net/dsa/marvell.txt
F: Documentation/networking/devlink/mv88e6xxx.rst
F: drivers/net/dsa/mv88e6xxx/
F: include/linux/dsa/mv88e6xxx.h
F: include/linux/platform_data/mv88e6xxx.h
 
MARVELL ARMADA 3700 PHY DRIVERS
......
......@@ -47,7 +47,7 @@ dma@fc400000 {
};
gmac: eth@e0800000 {
compatible = "st,spear600-gmac";
compatible = "snps,dwmac-3.40a";
reg = <0xe0800000 0x8000>;
interrupts = <23 22>;
interrupt-names = "macirq", "eth_wake_irq";
......
......@@ -480,6 +480,11 @@ int detach_capi_ctr(struct capi_ctr *ctr)
ctr_down(ctr, CAPI_CTR_DETACHED);
if (ctr->cnr < 1 || ctr->cnr - 1 >= CAPI_MAXCONTR) {
err = -EINVAL;
goto unlock_out;
}
if (capi_controller[ctr->cnr - 1] != ctr) {
err = -EINVAL;
goto unlock_out;
......
......@@ -949,8 +949,8 @@ nj_release(struct tiger_hw *card)
nj_disable_hwirq(card);
mode_tiger(&card->bc[0], ISDN_P_NONE);
mode_tiger(&card->bc[1], ISDN_P_NONE);
card->isac.release(&card->isac);
spin_unlock_irqrestore(&card->lock, flags);
card->isac.release(&card->isac);
release_region(card->base, card->base_s);
card->base_s = 0;
}
......
......@@ -449,8 +449,10 @@ EXPORT_SYMBOL(ksz_switch_register);
void ksz_switch_remove(struct ksz_device *dev)
{
/* timer started */
if (dev->mib_read_interval)
if (dev->mib_read_interval) {
dev->mib_read_interval = 0;
cancel_delayed_work_sync(&dev->mib_read);
}
dev->dev_ops->exit(dev);
dsa_unregister_switch(dev->ds);
......
......@@ -12,6 +12,7 @@
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/dsa/mv88e6xxx.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/if_bridge.h>
......@@ -749,7 +750,11 @@ static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
ops = chip->info->ops;
mv88e6xxx_reg_lock(chip);
if ((!mv88e6xxx_port_ppu_updates(chip, port) ||
/* Internal PHYs propagate their configuration directly to the MAC.
* External PHYs depend on whether the PPU is enabled for this port.
*/
if (((!mv88e6xxx_phy_is_internal(ds, port) &&
!mv88e6xxx_port_ppu_updates(chip, port)) ||
mode == MLO_AN_FIXED) && ops->port_sync_link)
err = ops->port_sync_link(chip, port, mode, false);
mv88e6xxx_reg_unlock(chip);
......@@ -772,7 +777,12 @@ static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
ops = chip->info->ops;
mv88e6xxx_reg_lock(chip);
if (!mv88e6xxx_port_ppu_updates(chip, port) || mode == MLO_AN_FIXED) {
/* Internal PHYs propagate their configuration directly to the MAC.
* External PHYs depend on whether the PPU is enabled for this port.
*/
if ((!mv88e6xxx_phy_is_internal(ds, port) &&
!mv88e6xxx_port_ppu_updates(chip, port)) ||
mode == MLO_AN_FIXED) {
/* FIXME: for an automedia port, should we force the link
* down here - what if the link comes up due to "other" media
* while we're bringing the port up, how is the exclusivity
......@@ -1677,6 +1687,30 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
return 0;
}
static int mv88e6xxx_port_commit_pvid(struct mv88e6xxx_chip *chip, int port)
{
struct dsa_port *dp = dsa_to_port(chip->ds, port);
struct mv88e6xxx_port *p = &chip->ports[port];
u16 pvid = MV88E6XXX_VID_STANDALONE;
bool drop_untagged = false;
int err;
if (dp->bridge_dev) {
if (br_vlan_enabled(dp->bridge_dev)) {
pvid = p->bridge_pvid.vid;
drop_untagged = !p->bridge_pvid.valid;
} else {
pvid = MV88E6XXX_VID_BRIDGED;
}
}
err = mv88e6xxx_port_set_pvid(chip, port, pvid);
if (err)
return err;
return mv88e6xxx_port_drop_untagged(chip, port, drop_untagged);
}
static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
bool vlan_filtering,
struct netlink_ext_ack *extack)
......@@ -1690,7 +1724,16 @@ static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
return -EOPNOTSUPP;
mv88e6xxx_reg_lock(chip);
err = mv88e6xxx_port_set_8021q_mode(chip, port, mode);
if (err)
goto unlock;
err = mv88e6xxx_port_commit_pvid(chip, port);
if (err)
goto unlock;
unlock:
mv88e6xxx_reg_unlock(chip);
return err;
......@@ -1725,11 +1768,15 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
u16 fid;
int err;
/* Null VLAN ID corresponds to the port private database */
/* Ports have two private address databases: one for when the port is
* standalone and one for when the port is under a bridge and the
* 802.1Q mode is disabled. When the port is standalone, DSA wants its
* address database to remain 100% empty, so we never load an ATU entry
* into a standalone port's database. Therefore, translate the null
* VLAN ID into the port's database used for VLAN-unaware bridging.
*/
if (vid == 0) {
err = mv88e6xxx_port_get_fid(chip, port, &fid);
if (err)
return err;
fid = MV88E6XXX_FID_BRIDGED;
} else {
err = mv88e6xxx_vtu_get(chip, vid, &vlan);
if (err)
......@@ -2123,6 +2170,7 @@ static int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
struct mv88e6xxx_chip *chip = ds->priv;
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
struct mv88e6xxx_port *p = &chip->ports[port];
bool warn;
u8 member;
int err;
......@@ -2156,13 +2204,21 @@ static int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
}
if (pvid) {
err = mv88e6xxx_port_set_pvid(chip, port, vlan->vid);
if (err) {
dev_err(ds->dev, "p%d: failed to set PVID %d\n",
port, vlan->vid);
p->bridge_pvid.vid = vlan->vid;
p->bridge_pvid.valid = true;
err = mv88e6xxx_port_commit_pvid(chip, port);
if (err)
goto out;
} else if (vlan->vid && p->bridge_pvid.vid == vlan->vid) {
/* The old pvid was reinstalled as a non-pvid VLAN */
p->bridge_pvid.valid = false;
err = mv88e6xxx_port_commit_pvid(chip, port);
if (err)
goto out;
}
}
out:
mv88e6xxx_reg_unlock(chip);
......@@ -2212,6 +2268,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan)
{
struct mv88e6xxx_chip *chip = ds->priv;
struct mv88e6xxx_port *p = &chip->ports[port];
int err = 0;
u16 pvid;
......@@ -2229,7 +2286,9 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
goto unlock;
if (vlan->vid == pvid) {
err = mv88e6xxx_port_set_pvid(chip, port, 0);
p->bridge_pvid.valid = false;
err = mv88e6xxx_port_commit_pvid(chip, port);
if (err)
goto unlock;
}
......@@ -2393,7 +2452,16 @@ static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
int err;
mv88e6xxx_reg_lock(chip);
err = mv88e6xxx_bridge_map(chip, br);
if (err)
goto unlock;
err = mv88e6xxx_port_commit_pvid(chip, port);
if (err)
goto unlock;
unlock:
mv88e6xxx_reg_unlock(chip);
return err;
......@@ -2403,11 +2471,20 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,
struct net_device *br)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err;
mv88e6xxx_reg_lock(chip);
if (mv88e6xxx_bridge_map(chip, br) ||
mv88e6xxx_port_vlan_map(chip, port))
dev_err(ds->dev, "failed to remap in-chip Port VLAN\n");
err = mv88e6xxx_port_commit_pvid(chip, port);
if (err)
dev_err(ds->dev,
"port %d failed to restore standalone pvid: %pe\n",
port, ERR_PTR(err));
mv88e6xxx_reg_unlock(chip);
}
......@@ -2853,6 +2930,20 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
if (err)
return err;
/* Associate MV88E6XXX_VID_BRIDGED with MV88E6XXX_FID_BRIDGED in the
* ATU by virtue of the fact that mv88e6xxx_atu_new() will pick it as
* the first free FID after MV88E6XXX_FID_STANDALONE. This will be used
* as the private PVID on ports under a VLAN-unaware bridge.
* Shared (DSA and CPU) ports must also be members of it, to translate
* the VID from the DSA tag into MV88E6XXX_FID_BRIDGED, instead of
* relying on their port default FID.
*/
err = mv88e6xxx_port_vlan_join(chip, port, MV88E6XXX_VID_BRIDGED,
MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED,
false);
if (err)
return err;
if (chip->info->ops->port_set_jumbo_size) {
err = chip->info->ops->port_set_jumbo_size(chip, port, 10218);
if (err)
......@@ -2925,7 +3016,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
* database, and allow bidirectional communication between the
* CPU and DSA port(s), and the other ports.
*/
err = mv88e6xxx_port_set_fid(chip, port, 0);
err = mv88e6xxx_port_set_fid(chip, port, MV88E6XXX_FID_STANDALONE);
if (err)
return err;
......@@ -3115,6 +3206,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
}
}
err = mv88e6xxx_vtu_setup(chip);
if (err)
goto unlock;
/* Setup Switch Port Registers */
for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
if (dsa_is_unused_port(ds, i))
......@@ -3144,10 +3239,6 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
if (err)
goto unlock;
err = mv88e6xxx_vtu_setup(chip);
if (err)
goto unlock;
err = mv88e6xxx_pvt_setup(chip);
if (err)
goto unlock;
......
......@@ -21,6 +21,9 @@
#define EDSA_HLEN 8
#define MV88E6XXX_N_FID 4096
#define MV88E6XXX_FID_STANDALONE 0
#define MV88E6XXX_FID_BRIDGED 1
/* PVT limits for 4-bit port and 5-bit switch */
#define MV88E6XXX_MAX_PVT_SWITCHES 32
#define MV88E6XXX_MAX_PVT_PORTS 16
......@@ -246,9 +249,15 @@ struct mv88e6xxx_policy {
u16 vid;
};
struct mv88e6xxx_vlan {
u16 vid;
bool valid;
};
struct mv88e6xxx_port {
struct mv88e6xxx_chip *chip;
int port;
struct mv88e6xxx_vlan bridge_pvid;
u64 serdes_stats[2];
u64 atu_member_violation;
u64 atu_miss_violation;
......
......@@ -1257,6 +1257,27 @@ int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
return 0;
}
int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port,
bool drop_untagged)
{
u16 old, new;
int err;
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &old);
if (err)
return err;
if (drop_untagged)
new = old | MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED;
else
new = old & ~MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED;
if (new == old)
return 0;
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, new);
}
int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port)
{
u16 reg;
......
......@@ -423,6 +423,8 @@ int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode);
int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port,
bool drop_untagged);
int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);
int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
int upstream_port);
......
......@@ -266,12 +266,12 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port)
*/
static int felix_setup_mmio_filtering(struct felix *felix)
{
unsigned long user_ports = 0, cpu_ports = 0;
unsigned long user_ports = dsa_user_ports(felix->ds);
struct ocelot_vcap_filter *redirect_rule;
struct ocelot_vcap_filter *tagging_rule;
struct ocelot *ocelot = &felix->ocelot;
struct dsa_switch *ds = felix->ds;
int port, ret;
int cpu = -1, port, ret;
tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL);
if (!tagging_rule)
......@@ -284,12 +284,15 @@ static int felix_setup_mmio_filtering(struct felix *felix)
}
for (port = 0; port < ocelot->num_phys_ports; port++) {
if (dsa_is_user_port(ds, port))
user_ports |= BIT(port);
if (dsa_is_cpu_port(ds, port))
cpu_ports |= BIT(port);
if (dsa_is_cpu_port(ds, port)) {
cpu = port;
break;
}
}
if (cpu < 0)
return -EINVAL;
tagging_rule->key_type = OCELOT_VCAP_KEY_ETYPE;
*(__be16 *)tagging_rule->key.etype.etype.value = htons(ETH_P_1588);
*(__be16 *)tagging_rule->key.etype.etype.mask = htons(0xffff);
......@@ -325,7 +328,7 @@ static int felix_setup_mmio_filtering(struct felix *felix)
* the CPU port module
*/
redirect_rule->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
redirect_rule->action.port_mask = cpu_ports;
redirect_rule->action.port_mask = BIT(cpu);
} else {
/* Trap PTP packets only to the CPU port module (which is
* redirected to the NPI port)
......@@ -1074,6 +1077,101 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
return 0;
}
static void ocelot_port_purge_txtstamp_skb(struct ocelot *ocelot, int port,
struct sk_buff *skb)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct sk_buff *clone = OCELOT_SKB_CB(skb)->clone;
struct sk_buff *skb_match = NULL, *skb_tmp;
unsigned long flags;
if (!clone)
return;
spin_lock_irqsave(&ocelot_port->tx_skbs.lock, flags);
skb_queue_walk_safe(&ocelot_port->tx_skbs, skb, skb_tmp) {
if (skb != clone)
continue;
__skb_unlink(skb, &ocelot_port->tx_skbs);
skb_match = skb;
break;
}
spin_unlock_irqrestore(&ocelot_port->tx_skbs.lock, flags);
WARN_ONCE(!skb_match,
"Could not find skb clone in TX timestamping list\n");
}
#define work_to_xmit_work(w) \
container_of((w), struct felix_deferred_xmit_work, work)
static void felix_port_deferred_xmit(struct kthread_work *work)
{
struct felix_deferred_xmit_work *xmit_work = work_to_xmit_work(work);
struct dsa_switch *ds = xmit_work->dp->ds;
struct sk_buff *skb = xmit_work->skb;
u32 rew_op = ocelot_ptp_rew_op(skb);
struct ocelot *ocelot = ds->priv;
int port = xmit_work->dp->index;
int retries = 10;
do {
if (ocelot_can_inject(ocelot, 0))
break;
cpu_relax();
} while (--retries);
if (!retries) {
dev_err(ocelot->dev, "port %d failed to inject skb\n",
port);
ocelot_port_purge_txtstamp_skb(ocelot, port, skb);
kfree_skb(skb);
return;
}
ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
consume_skb(skb);
kfree(xmit_work);
}
static int felix_port_setup_tagger_data(struct dsa_switch *ds, int port)
{
struct dsa_port *dp = dsa_to_port(ds, port);
struct ocelot *ocelot = ds->priv;
struct felix *felix = ocelot_to_felix(ocelot);
struct felix_port *felix_port;
if (!dsa_port_is_user(dp))
return 0;
felix_port = kzalloc(sizeof(*felix_port), GFP_KERNEL);
if (!felix_port)
return -ENOMEM;
felix_port->xmit_worker = felix->xmit_worker;
felix_port->xmit_work_fn = felix_port_deferred_xmit;
dp->priv = felix_port;
return 0;
}
static void felix_port_teardown_tagger_data(struct dsa_switch *ds, int port)
{
struct dsa_port *dp = dsa_to_port(ds, port);
struct felix_port *felix_port = dp->priv;
if (!felix_port)
return;
dp->priv = NULL;
kfree(felix_port);
}
/* Hardware initialization done here so that we can allocate structures with
* devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing
* us to allocate structures twice (leak memory) and map PCI memory twice
......@@ -1102,6 +1200,12 @@ static int felix_setup(struct dsa_switch *ds)
}
}
felix->xmit_worker = kthread_create_worker(0, "felix_xmit");
if (IS_ERR(felix->xmit_worker)) {
err = PTR_ERR(felix->xmit_worker);
goto out_deinit_timestamp;
}
for (port = 0; port < ds->num_ports; port++) {
if (dsa_is_unused_port(ds, port))
continue;
......@@ -1112,6 +1216,14 @@ static int felix_setup(struct dsa_switch *ds)
* bits of vlan tag.
*/
felix_port_qos_map_init(ocelot, port);
err = felix_port_setup_tagger_data(ds, port);
if (err) {
dev_err(ds->dev,
"port %d failed to set up tagger data: %pe\n",
port, ERR_PTR(err));
goto out_deinit_ports;
}
}
err = ocelot_devlink_sb_register(ocelot);
......@@ -1126,6 +1238,7 @@ static int felix_setup(struct dsa_switch *ds)
* there's no real point in checking for errors.
*/
felix_set_tag_protocol(ds, port, felix->tag_proto);
break;
}
ds->mtu_enforcement_ingress = true;
......@@ -1138,9 +1251,13 @@ static int felix_setup(struct dsa_switch *ds)
if (dsa_is_unused_port(ds, port))
continue;
felix_port_teardown_tagger_data(ds, port);
ocelot_deinit_port(ocelot, port);
}
kthread_destroy_worker(felix->xmit_worker);
out_deinit_timestamp:
ocelot_deinit_timestamp(ocelot);
ocelot_deinit(ocelot);
......@@ -1162,19 +1279,23 @@ static void felix_teardown(struct dsa_switch *ds)
continue;
felix_del_tag_protocol(ds, port, felix->tag_proto);
break;
}
ocelot_devlink_sb_unregister(ocelot);
ocelot_deinit_timestamp(ocelot);
ocelot_deinit(ocelot);
for (port = 0; port < ocelot->num_phys_ports; port++) {
if (dsa_is_unused_port(ds, port))
continue;
felix_port_teardown_tagger_data(ds, port);
ocelot_deinit_port(ocelot, port);
}
kthread_destroy_worker(felix->xmit_worker);
ocelot_devlink_sb_unregister(ocelot);
ocelot_deinit_timestamp(ocelot);
ocelot_deinit(ocelot);
if (felix->info->mdio_bus_free)
felix->info->mdio_bus_free(ocelot);
}
......@@ -1291,8 +1412,12 @@ static void felix_txtstamp(struct dsa_switch *ds, int port,
if (!ocelot->ptp)
return;
if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone))
if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) {
dev_err_ratelimited(ds->dev,
"port %d delivering skb without TX timestamp\n",
port);
return;
}
if (clone)