| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/05 00:17 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch series only modifies the Motorcomm YT9215 DSA Ethernet switch driver (drivers/net/dsa/motorcomm/), which interacts with a specific physical MDIO-connected switch ASIC. This hardware is not emulated in standard QEMU/GCE fuzzing environments, making the code structurally unreachable during fuzzing.",
"WorthFuzzing": false
} |
| 1/1 | 2026/09/05 00:17 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 7217a26274e7591624ca7a0893dd0606389a1fd2\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sat Sep 5 00:17:51 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile\nindex aeb12cb91f935..5c2d34ec05fc0 100644\n--- a/drivers/net/dsa/motorcomm/Makefile\n+++ b/drivers/net/dsa/motorcomm/Makefile\n@@ -2,4 +2,5 @@\n obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o\n yt921x-objs := chip.o\n yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o\n+yt921x-objs += mib.o\n yt921x-objs += smi.o\ndiff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c\nindex d663af010f431..b4059053ef152 100644\n--- a/drivers/net/dsa/motorcomm/chip.c\n+++ b/drivers/net/dsa/motorcomm/chip.c\n@@ -19,6 +19,7 @@\n #include \u003clinux/of_mdio.h\u003e\n #include \u003clinux/of_net.h\u003e\n #include \u003clinux/sort.h\u003e\n+#include \u003clinux/u64_stats_sync.h\u003e\n \n #include \u003cnet/dsa.h\u003e\n #include \u003cnet/dscp.h\u003e\n@@ -27,75 +28,9 @@\n \n #include \"chip.h\"\n #include \"leds.h\"\n+#include \"mib.h\"\n #include \"smi.h\"\n \n-struct yt921x_mib_desc {\n-\tunsigned int size;\n-\tunsigned int offset;\n-\tconst char *name;\n-};\n-\n-#define MIB_DESC(_size, _offset, _name) \\\n-\t{_size, _offset, _name}\n-\n-/* Must agree with yt921x_mib\n- *\n- * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),\n- * structured go to their *_stats() methods, but we need their sizes and offsets\n- * to perform 32bit MIB overflow wraparound.\n- */\n-static const struct yt921x_mib_desc yt921x_mib_descs[] = {\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),\n-\tMIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),\n-\n-\tMIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, \"RxBadBytes\"),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),\n-\n-\tMIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_OAM, \"RxOAM\"),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_OAM, \"TxOAM\"),\n-};\n-\n struct yt921x_info {\n \tconst char *name;\n \tu16 major;\n@@ -154,14 +89,6 @@ static const struct yt921x_info yt921x_infos[] = {\n \n #define YT921X_VID_UNWARE\t4095\n \n-/* The interval should be small enough to avoid overflow of 32bit MIBs.\n- *\n- * Until we can read MIBs from stats64 call directly (i.e. sleep\n- * there), we have to poll stats more frequently then it is actually needed.\n- * For overflow protection, normally, 100 sec interval should have been OK.\n- */\n-#define YT921X_STATS_INTERVAL_JIFFIES\t(3 * HZ)\n-\n struct yt921x_reg_mdio {\n \tstruct mii_bus *bus;\n \tint addr;\n@@ -544,294 +471,6 @@ yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)\n \treturn 0;\n }\n \n-/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */\n-static int yt921x_read_mib(struct yt921x_priv *priv, int port)\n-{\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct device *dev = to_device(priv);\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\tint res = 0;\n-\n-\t/* Reading of yt921x_port::mib is not protected by a lock and it's vain\n-\t * to keep its consistency, since we have to read registers one by one\n-\t * and there is no way to make a snapshot of MIB stats.\n-\t *\n-\t * Writing (by this function only) is and should be protected by\n-\t * reg_lock.\n-\t */\n-\n-\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n-\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n-\t\tu32 reg = YT921X_MIBn_DATA0(port) + desc-\u003eoffset;\n-\t\tu64 *valp = \u0026((u64 *)mib)[i];\n-\t\tu32 val0;\n-\t\tu64 val;\n-\n-\t\tres = yt921x_reg_read(priv, reg, \u0026val0);\n-\t\tif (res)\n-\t\t\tbreak;\n-\n-\t\tif (desc-\u003esize \u003c= 1) {\n-\t\t\tu64 old_val = *valp;\n-\n-\t\t\tval = (old_val \u0026 ~(u64)U32_MAX) | val0;\n-\t\t\tif (val \u003c old_val)\n-\t\t\t\tval += 1ull \u003c\u003c 32;\n-\t\t} else {\n-\t\t\tu32 val1;\n-\n-\t\t\tres = yt921x_reg_read(priv, reg + 4, \u0026val1);\n-\t\t\tif (res)\n-\t\t\t\tbreak;\n-\t\t\tval = ((u64)val1 \u003c\u003c 32) | val0;\n-\t\t}\n-\n-\t\tWRITE_ONCE(*valp, val);\n-\t}\n-\n-\tpp-\u003erx_frames = mib-\u003erx_64byte + mib-\u003erx_65_127byte +\n-\t\t\tmib-\u003erx_128_255byte + mib-\u003erx_256_511byte +\n-\t\t\tmib-\u003erx_512_1023byte + mib-\u003erx_1024_1518byte +\n-\t\t\tmib-\u003erx_jumbo;\n-\tpp-\u003etx_frames = mib-\u003etx_64byte + mib-\u003etx_65_127byte +\n-\t\t\tmib-\u003etx_128_255byte + mib-\u003etx_256_511byte +\n-\t\t\tmib-\u003etx_512_1023byte + mib-\u003etx_1024_1518byte +\n-\t\t\tmib-\u003etx_jumbo;\n-\n-\tif (res)\n-\t\tdev_err(dev, \"Failed to %s port %d: %i\\n\", \"read stats for\",\n-\t\t\tport, res);\n-\treturn res;\n-}\n-\n-static void yt921x_poll_mib(struct work_struct *work)\n-{\n-\tstruct yt921x_port *pp = container_of_const(work, struct yt921x_port,\n-\t\t\t\t\t\t mib_read.work);\n-\tstruct yt921x_priv *priv = (void *)(pp - pp-\u003eindex) -\n-\t\t\t\t offsetof(struct yt921x_priv, ports);\n-\tunsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;\n-\tint port = pp-\u003eindex;\n-\tint res;\n-\n-\tmutex_lock(\u0026priv-\u003ereg_lock);\n-\tres = yt921x_read_mib(priv, port);\n-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n-\tif (res)\n-\t\tdelay *= 4;\n-\n-\tschedule_delayed_work(\u0026pp-\u003emib_read, delay);\n-}\n-\n-static void\n-yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,\n-\t\t uint8_t *data)\n-{\n-\tif (stringset != ETH_SS_STATS)\n-\t\treturn;\n-\n-\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n-\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n-\n-\t\tif (desc-\u003ename)\n-\t\t\tethtool_puts(\u0026data, desc-\u003ename);\n-\t}\n-}\n-\n-static void\n-yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)\n-{\n-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\tsize_t j;\n-\n-\tmutex_lock(\u0026priv-\u003ereg_lock);\n-\tyt921x_read_mib(priv, port);\n-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n-\n-\tj = 0;\n-\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n-\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n-\n-\t\tif (!desc-\u003ename)\n-\t\t\tcontinue;\n-\n-\t\tdata[j] = ((u64 *)mib)[i];\n-\t\tj++;\n-\t}\n-}\n-\n-static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)\n-{\n-\tint cnt = 0;\n-\n-\tif (sset != ETH_SS_STATS)\n-\t\treturn 0;\n-\n-\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n-\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n-\n-\t\tif (desc-\u003ename)\n-\t\t\tcnt++;\n-\t}\n-\n-\treturn cnt;\n-}\n-\n-static void\n-yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,\n-\t\t\t struct ethtool_eth_mac_stats *mac_stats)\n-{\n-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\n-\tmutex_lock(\u0026priv-\u003ereg_lock);\n-\tyt921x_read_mib(priv, port);\n-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n-\n-\tmac_stats-\u003eFramesTransmittedOK = pp-\u003etx_frames;\n-\tmac_stats-\u003eSingleCollisionFrames = mib-\u003etx_single_collisions;\n-\tmac_stats-\u003eMultipleCollisionFrames = mib-\u003etx_multiple_collisions;\n-\tmac_stats-\u003eFramesReceivedOK = pp-\u003erx_frames;\n-\tmac_stats-\u003eFrameCheckSequenceErrors = mib-\u003erx_crc_errors;\n-\tmac_stats-\u003eAlignmentErrors = mib-\u003erx_alignment_errors;\n-\tmac_stats-\u003eOctetsTransmittedOK = mib-\u003etx_good_bytes;\n-\tmac_stats-\u003eFramesWithDeferredXmissions = mib-\u003etx_deferred;\n-\tmac_stats-\u003eLateCollisions = mib-\u003etx_late_collisions;\n-\tmac_stats-\u003eFramesAbortedDueToXSColls = mib-\u003etx_aborted_errors;\n-\t/* mac_stats-\u003eFramesLostDueToIntMACXmitError */\n-\t/* mac_stats-\u003eCarrierSenseErrors */\n-\tmac_stats-\u003eOctetsReceivedOK = mib-\u003erx_good_bytes;\n-\t/* mac_stats-\u003eFramesLostDueToIntMACRcvError */\n-\tmac_stats-\u003eMulticastFramesXmittedOK = mib-\u003etx_multicast;\n-\tmac_stats-\u003eBroadcastFramesXmittedOK = mib-\u003etx_broadcast;\n-\t/* mac_stats-\u003eFramesWithExcessiveDeferral */\n-\tmac_stats-\u003eMulticastFramesReceivedOK = mib-\u003erx_multicast;\n-\tmac_stats-\u003eBroadcastFramesReceivedOK = mib-\u003erx_broadcast;\n-\t/* mac_stats-\u003eInRangeLengthErrors */\n-\t/* mac_stats-\u003eOutOfRangeLengthField */\n-\tmac_stats-\u003eFrameTooLongErrors = mib-\u003erx_oversize_errors;\n-}\n-\n-static void\n-yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,\n-\t\t\t struct ethtool_eth_ctrl_stats *ctrl_stats)\n-{\n-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\n-\tmutex_lock(\u0026priv-\u003ereg_lock);\n-\tyt921x_read_mib(priv, port);\n-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n-\n-\tctrl_stats-\u003eMACControlFramesTransmitted = mib-\u003etx_pause;\n-\tctrl_stats-\u003eMACControlFramesReceived = mib-\u003erx_pause;\n-\t/* ctrl_stats-\u003eUnsupportedOpcodesReceived */\n-}\n-\n-static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {\n-\t{ 0, 64 },\n-\t{ 65, 127 },\n-\t{ 128, 255 },\n-\t{ 256, 511 },\n-\t{ 512, 1023 },\n-\t{ 1024, 1518 },\n-\t{ 1519, YT921X_FRAME_SIZE_MAX },\n-\t{}\n-};\n-\n-static void\n-yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,\n-\t\t\t struct ethtool_rmon_stats *rmon_stats,\n-\t\t\t const struct ethtool_rmon_hist_range **ranges)\n-{\n-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\n-\tmutex_lock(\u0026priv-\u003ereg_lock);\n-\tyt921x_read_mib(priv, port);\n-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n-\n-\t*ranges = yt921x_rmon_ranges;\n-\n-\trmon_stats-\u003eundersize_pkts = mib-\u003erx_undersize_errors;\n-\trmon_stats-\u003eoversize_pkts = mib-\u003erx_oversize_errors;\n-\trmon_stats-\u003efragments = mib-\u003erx_alignment_errors;\n-\t/* rmon_stats-\u003ejabbers */\n-\n-\trmon_stats-\u003ehist[0] = mib-\u003erx_64byte;\n-\trmon_stats-\u003ehist[1] = mib-\u003erx_65_127byte;\n-\trmon_stats-\u003ehist[2] = mib-\u003erx_128_255byte;\n-\trmon_stats-\u003ehist[3] = mib-\u003erx_256_511byte;\n-\trmon_stats-\u003ehist[4] = mib-\u003erx_512_1023byte;\n-\trmon_stats-\u003ehist[5] = mib-\u003erx_1024_1518byte;\n-\trmon_stats-\u003ehist[6] = mib-\u003erx_jumbo;\n-\n-\trmon_stats-\u003ehist_tx[0] = mib-\u003etx_64byte;\n-\trmon_stats-\u003ehist_tx[1] = mib-\u003etx_65_127byte;\n-\trmon_stats-\u003ehist_tx[2] = mib-\u003etx_128_255byte;\n-\trmon_stats-\u003ehist_tx[3] = mib-\u003etx_256_511byte;\n-\trmon_stats-\u003ehist_tx[4] = mib-\u003etx_512_1023byte;\n-\trmon_stats-\u003ehist_tx[5] = mib-\u003etx_1024_1518byte;\n-\trmon_stats-\u003ehist_tx[6] = mib-\u003etx_jumbo;\n-}\n-\n-static void\n-yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,\n-\t\t struct rtnl_link_stats64 *stats)\n-{\n-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\n-\tstats-\u003erx_length_errors = mib-\u003erx_undersize_errors +\n-\t\t\t\t mib-\u003erx_fragment_errors;\n-\tstats-\u003erx_over_errors = mib-\u003erx_oversize_errors;\n-\tstats-\u003erx_crc_errors = mib-\u003erx_crc_errors;\n-\tstats-\u003erx_frame_errors = mib-\u003erx_alignment_errors;\n-\t/* stats-\u003erx_fifo_errors */\n-\t/* stats-\u003erx_missed_errors */\n-\n-\tstats-\u003etx_aborted_errors = mib-\u003etx_aborted_errors;\n-\t/* stats-\u003etx_carrier_errors */\n-\tstats-\u003etx_fifo_errors = mib-\u003etx_undersize_errors;\n-\t/* stats-\u003etx_heartbeat_errors */\n-\tstats-\u003etx_window_errors = mib-\u003etx_late_collisions;\n-\n-\tstats-\u003erx_packets = pp-\u003erx_frames;\n-\tstats-\u003etx_packets = pp-\u003etx_frames;\n-\tstats-\u003erx_bytes = mib-\u003erx_good_bytes - ETH_FCS_LEN * stats-\u003erx_packets;\n-\tstats-\u003etx_bytes = mib-\u003etx_good_bytes - ETH_FCS_LEN * stats-\u003etx_packets;\n-\tstats-\u003erx_errors = stats-\u003erx_length_errors + stats-\u003erx_over_errors +\n-\t\t\t stats-\u003erx_crc_errors + stats-\u003erx_frame_errors;\n-\tstats-\u003etx_errors = stats-\u003etx_aborted_errors + stats-\u003etx_fifo_errors +\n-\t\t\t stats-\u003etx_window_errors;\n-\tstats-\u003erx_dropped = mib-\u003erx_dropped;\n-\t/* stats-\u003etx_dropped */\n-\tstats-\u003emulticast = mib-\u003erx_multicast;\n-\tstats-\u003ecollisions = mib-\u003etx_collisions;\n-}\n-\n-static void\n-yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,\n-\t\t\t struct ethtool_pause_stats *pause_stats)\n-{\n-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\n-\tmutex_lock(\u0026priv-\u003ereg_lock);\n-\tyt921x_read_mib(priv, port);\n-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n-\n-\tpause_stats-\u003etx_pause_frames = mib-\u003etx_pause;\n-\tpause_stats-\u003erx_pause_frames = mib-\u003erx_pause;\n-}\n-\n static int\n yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)\n {\n@@ -3953,11 +3592,14 @@ yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,\n {\n \tstruct dsa_port *dp = dsa_phylink_to_port(config);\n \tstruct yt921x_priv *priv = to_yt921x_priv(dp-\u003eds);\n+\tstruct yt921x_mib *pm;\n \tint port = dp-\u003eindex;\n \tint res;\n \n \t/* No need to sync; port control block is hold until device remove */\n-\tcancel_delayed_work(\u0026priv-\u003eports[port].mib_read);\n+\tpm = priv-\u003eports[port].mib;\n+\tif (pm)\n+\t\tcancel_delayed_work(\u0026pm-\u003ework);\n \n \tmutex_lock(\u0026priv-\u003ereg_lock);\n \tres = yt921x_port_down(priv, port);\n@@ -3976,6 +3618,7 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,\n {\n \tstruct dsa_port *dp = dsa_phylink_to_port(config);\n \tstruct yt921x_priv *priv = to_yt921x_priv(dp-\u003eds);\n+\tstruct yt921x_mib *pm;\n \tint port = dp-\u003eindex;\n \tint res;\n \n@@ -3988,7 +3631,9 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,\n \t\tdev_err(dp-\u003eds-\u003edev, \"Failed to %s port %d: %i\\n\", \"bring up\",\n \t\t\tport, res);\n \n-\tschedule_delayed_work(\u0026priv-\u003eports[port].mib_read, 0);\n+\tpm = priv-\u003eports[port].mib;\n+\tif (pm)\n+\t\tschedule_delayed_work(\u0026pm-\u003ework, 0);\n }\n \n static void\n@@ -4108,11 +3753,36 @@ yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port,\n \treturn DSA_TAG_PROTO_YT921X;\n }\n \n+static void yt921x_dsa_port_teardown(struct dsa_switch *ds, int port)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\n+\tif (pm)\n+\t\tdisable_delayed_work_sync(\u0026pm-\u003ework);\n+}\n+\n static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port)\n {\n \tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct device *dev = to_device(priv);\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n \tint res;\n \n+\tif (!pm \u0026\u0026 !(BIT(port) \u0026 (priv-\u003einfo-\u003einternal_mask |\n+\t\t priv-\u003einfo-\u003eexternal_mask))) {\n+\t\tpm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL);\n+\t\tif (!pm)\n+\t\t\treturn -ENOMEM;\n+\t\tpp-\u003emib = pm;\n+\n+\t\tpm-\u003eport = pp;\n+\t\tu64_stats_init(\u0026pm-\u003esyncp);\n+\t\tINIT_DELAYED_WORK(\u0026pm-\u003ework, yt921x_mib_poll);\n+\t}\n+\n \tmutex_lock(\u0026priv-\u003ereg_lock);\n \tres = yt921x_port_setup(priv, port);\n \tmutex_unlock(\u0026priv-\u003ereg_lock);\n@@ -4667,6 +4337,7 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = {\n \t/* port */\n \t.get_tag_protocol\t= yt921x_dsa_get_tag_protocol,\n \t.phylink_get_caps\t= yt921x_dsa_phylink_get_caps,\n+\t.port_teardown\t\t= yt921x_dsa_port_teardown,\n \t.port_setup\t\t= yt921x_dsa_port_setup,\n #if IS_ENABLED(CONFIG_DCB)\n \t/* dscp */\n@@ -4696,12 +4367,6 @@ static void yt921x_mdio_remove(struct mdio_device *mdiodev)\n \tif (!priv)\n \t\treturn;\n \n-\tfor (size_t i = ARRAY_SIZE(priv-\u003eports); i-- \u003e 0; ) {\n-\t\tstruct yt921x_port *pp = \u0026priv-\u003eports[i];\n-\n-\t\tdisable_delayed_work_sync(\u0026pp-\u003emib_read);\n-\t}\n-\n \tdsa_unregister_switch(\u0026priv-\u003eds);\n \n \tfor (unsigned int i = 0; i \u003c ARRAY_SIZE(priv-\u003eacl_blks); i++) {\n@@ -4749,7 +4414,6 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev)\n \t\tstruct yt921x_port *pp = \u0026priv-\u003eports[i];\n \n \t\tpp-\u003eindex = i;\n-\t\tINIT_DELAYED_WORK(\u0026pp-\u003emib_read, yt921x_poll_mib);\n \t}\n \n \tds = \u0026priv-\u003eds;\ndiff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h\nindex 83cd454955ddb..ce2015026849a 100644\n--- a/drivers/net/dsa/motorcomm/chip.h\n+++ b/drivers/net/dsa/motorcomm/chip.h\n@@ -167,69 +167,6 @@\n #define YT921X_EEE_CTRL\t\t\t0xb0000\n #define YT921X_EEE_CTRL_ENn(port)\t\tBIT(port)\n \n-#define YT921X_MIB_CTRL\t\t\t0xc0004\n-#define YT921X_MIB_CTRL_CLEAN\t\t\tBIT(30)\n-#define YT921X_MIB_CTRL_PORT_M\t\t\tGENMASK(6, 3)\n-#define YT921X_MIB_CTRL_PORT(x)\t\t\tFIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))\n-#define YT921X_MIB_CTRL_ONE_PORT\t\tBIT(1)\n-#define YT921X_MIB_CTRL_ALL_PORT\t\tBIT(0)\n-#define YT921X_MIBn_DATA0(port)\t\t(0xc0100 + 0x100 * (port))\n-#define YT921X_MIBn_DATAm(port, x)\t(YT921X_MIBn_DATA0(port) + 4 * (x))\n-#define YT921X_MIB_DATA_RX_BROADCAST\t\t0x00\n-#define YT921X_MIB_DATA_RX_PAUSE\t\t0x04\n-#define YT921X_MIB_DATA_RX_MULTICAST\t\t0x08\n-#define YT921X_MIB_DATA_RX_CRC_ERR\t\t0x0c\n-\n-#define YT921X_MIB_DATA_RX_ALIGN_ERR\t\t0x10\n-#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR\t0x14\n-#define YT921X_MIB_DATA_RX_FRAG_ERR\t\t0x18\n-#define YT921X_MIB_DATA_RX_PKT_SZ_64\t\t0x1c\n-\n-#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127\t0x20\n-#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255\t0x24\n-#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511\t0x28\n-#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023\t0x2c\n-\n-#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518\t0x30\n-#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX\t0x34\n-/* 0x38: unused */\n-#define YT921X_MIB_DATA_RX_GOOD_BYTES\t\t0x3c\n-\n-/* 0x40: 64 bytes */\n-#define YT921X_MIB_DATA_RX_BAD_BYTES\t\t0x44\n-/* 0x48: 64 bytes */\n-#define YT921X_MIB_DATA_RX_OVERSIZE_ERR\t0x4c\n-\n-#define YT921X_MIB_DATA_RX_DROPPED\t\t0x50\n-#define YT921X_MIB_DATA_TX_BROADCAST\t\t0x54\n-#define YT921X_MIB_DATA_TX_PAUSE\t\t0x58\n-#define YT921X_MIB_DATA_TX_MULTICAST\t\t0x5c\n-\n-#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR\t0x60\n-#define YT921X_MIB_DATA_TX_PKT_SZ_64\t\t0x64\n-#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127\t0x68\n-#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255\t0x6c\n-\n-#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511\t0x70\n-#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023\t0x74\n-#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518\t0x78\n-#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX\t0x7c\n-\n-/* 0x80: unused */\n-#define YT921X_MIB_DATA_TX_GOOD_BYTES\t\t0x84\n-/* 0x88: 64 bytes */\n-#define YT921X_MIB_DATA_TX_COLLISION\t\t0x8c\n-\n-#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION\t0x90\n-#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION\t0x94\n-#define YT921X_MIB_DATA_TX_SINGLE_COLLISION\t0x98\n-#define YT921X_MIB_DATA_TX_PKT\t\t\t0x9c\n-\n-#define YT921X_MIB_DATA_TX_DEFERRED\t\t0xa0\n-#define YT921X_MIB_DATA_TX_LATE_COLLISION\t0xa4\n-#define YT921X_MIB_DATA_RX_OAM\t\t\t0xa8\n-#define YT921X_MIB_DATA_TX_OAM\t\t\t0xac\n-\n #define YT921X_EDATA_CTRL\t\t0xe0000\n #define YT921X_EDATA_CTRL_ADDR_M\t\tGENMASK(15, 8)\n #define YT921X_EDATA_CTRL_ADDR(x)\t\t\tFIELD_PREP(YT921X_EDATA_CTRL_ADDR_M, (x))\n@@ -861,58 +798,6 @@ enum yt921x_fdb_entry_status {\n #define yt921x_port_is_internal(port) ((port) \u003c 8)\n #define yt921x_port_is_external(port) ((port) == 8 || (port) == 9)\n \n-struct yt921x_mib {\n-\tu64 rx_broadcast;\n-\tu64 rx_pause;\n-\tu64 rx_multicast;\n-\tu64 rx_crc_errors;\n-\n-\tu64 rx_alignment_errors;\n-\tu64 rx_undersize_errors;\n-\tu64 rx_fragment_errors;\n-\tu64 rx_64byte;\n-\n-\tu64 rx_65_127byte;\n-\tu64 rx_128_255byte;\n-\tu64 rx_256_511byte;\n-\tu64 rx_512_1023byte;\n-\n-\tu64 rx_1024_1518byte;\n-\tu64 rx_jumbo;\n-\tu64 rx_good_bytes;\n-\n-\tu64 rx_bad_bytes;\n-\tu64 rx_oversize_errors;\n-\n-\tu64 rx_dropped;\n-\tu64 tx_broadcast;\n-\tu64 tx_pause;\n-\tu64 tx_multicast;\n-\n-\tu64 tx_undersize_errors;\n-\tu64 tx_64byte;\n-\tu64 tx_65_127byte;\n-\tu64 tx_128_255byte;\n-\n-\tu64 tx_256_511byte;\n-\tu64 tx_512_1023byte;\n-\tu64 tx_1024_1518byte;\n-\tu64 tx_jumbo;\n-\n-\tu64 tx_good_bytes;\n-\tu64 tx_collisions;\n-\n-\tu64 tx_aborted_errors;\n-\tu64 tx_multiple_collisions;\n-\tu64 tx_single_collisions;\n-\tu64 tx_good;\n-\n-\tu64 tx_deferred;\n-\tu64 tx_late_collisions;\n-\tu64 rx_oam;\n-\tu64 tx_oam;\n-};\n-\n struct yt921x_acl_entry {\n \tu32 key[2];\n \tu32 mask[2];\n@@ -936,13 +821,8 @@ struct yt921x_acl_blk {\n struct yt921x_port {\n \tunsigned char index;\n \n-\tbool hairpin;\n-\tbool isolated;\n-\n-\tstruct delayed_work mib_read;\n-\tstruct yt921x_mib mib;\n-\tu64 rx_frames;\n-\tu64 tx_frames;\n+\tbool hairpin:1;\n+\tbool isolated:1;\n \n #if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\n \tunsigned char led_duty;\n@@ -953,6 +833,8 @@ struct yt921x_port {\n \n \tstruct yt921x_led *leds[YT921X_LED_GROUP_NUM];\n #endif\n+\n+\tstruct yt921x_mib *mib;\n };\n \n struct yt921x_reg_ops {\ndiff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c\nnew file mode 100644\nindex 0000000000000..30478ce71c105\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/mib.c\n@@ -0,0 +1,430 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/*\n+ * Copyright (c) 2026 David Yang\n+ */\n+\n+#include \"chip.h\"\n+#include \"mib.h\"\n+#include \"smi.h\"\n+\n+struct yt921x_mib_desc {\n+\tunsigned char size;\n+\tunsigned char offset;\n+\tconst char *name;\n+};\n+\n+#define MIB_DESC(_size, _offset, _name) {_size, _offset, _name}\n+\n+/* Must agree with yt921x_mib\n+ *\n+ * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),\n+ * structured go to their *_stats() methods, but we need their sizes and offsets\n+ * to perform 32bit MIB overflow wraparound.\n+ */\n+static const struct yt921x_mib_desc yt921x_mib_descs[] = {\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),\n+\tMIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),\n+\n+\tMIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, \"RxBadBytes\"),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),\n+\n+\tMIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_OAM, \"RxOAM\"),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_OAM, \"TxOAM\"),\n+};\n+\n+/* The interval should be small enough to avoid overflow of 32bit MIBs.\n+ *\n+ * Until we can read MIBs from stats64 call directly (i.e. sleep\n+ * there), we have to poll stats more frequently then it is actually needed.\n+ * For overflow protection, normally, 100 sec interval should have been OK.\n+ */\n+#define YT921X_STATS_INTERVAL_JIFFIES\t(3 * HZ)\n+\n+#define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds)\n+#define to_device(priv) ((priv)-\u003eds.dev)\n+\n+/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */\n+static int yt921x_mib_read(struct yt921x_priv *priv, int port)\n+{\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct device *dev = to_device(priv);\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\tu64 *buf = pm-\u003edata;\n+\tu64 rx_frames;\n+\tu64 tx_frames;\n+\tint res = 0;\n+\n+\tmib = \u0026pm-\u003estats;\n+\n+\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n+\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n+\t\tu32 reg = YT921X_MIBn_DATA0(port) + desc-\u003eoffset;\n+\n+\t\tif (desc-\u003esize \u003c= 1) {\n+\t\t\tu64 old_val = buf[i];\n+\t\t\tu32 val0;\n+\t\t\tu64 val;\n+\n+\t\t\tres = yt921x_reg_read(priv, reg, \u0026val0);\n+\t\t\tif (res)\n+\t\t\t\tbreak;\n+\n+\t\t\tval = (old_val \u0026 ~(u64)U32_MAX) | val0;\n+\t\t\tif (val \u003c old_val)\n+\t\t\t\tval += 1ull \u003c\u003c 32;\n+\n+\t\t\tbuf[i] = val;\n+\t\t} else {\n+\t\t\tres = yt921x_counter_read(priv, reg, \u0026buf[i]);\n+\t\t\tif (res)\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tu64_stats_update_begin(\u0026pm-\u003esyncp);\n+\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++)\n+\t\tu64_stats_set(\u0026((u64_stats_t *)mib)[i], buf[i]);\n+\trx_frames = u64_stats_read(\u0026mib-\u003erx_64byte) +\n+\t\t u64_stats_read(\u0026mib-\u003erx_65_127byte) +\n+\t\t u64_stats_read(\u0026mib-\u003erx_128_255byte) +\n+\t\t u64_stats_read(\u0026mib-\u003erx_256_511byte) +\n+\t\t u64_stats_read(\u0026mib-\u003erx_512_1023byte) +\n+\t\t u64_stats_read(\u0026mib-\u003erx_1024_1518byte) +\n+\t\t u64_stats_read(\u0026mib-\u003erx_jumbo);\n+\ttx_frames = u64_stats_read(\u0026mib-\u003etx_64byte) +\n+\t\t u64_stats_read(\u0026mib-\u003etx_65_127byte) +\n+\t\t u64_stats_read(\u0026mib-\u003etx_128_255byte) +\n+\t\t u64_stats_read(\u0026mib-\u003etx_256_511byte) +\n+\t\t u64_stats_read(\u0026mib-\u003etx_512_1023byte) +\n+\t\t u64_stats_read(\u0026mib-\u003etx_1024_1518byte) +\n+\t\t u64_stats_read(\u0026mib-\u003etx_jumbo);\n+\tu64_stats_set(\u0026pm-\u003erx_frames, rx_frames);\n+\tu64_stats_set(\u0026pm-\u003etx_frames, tx_frames);\n+\tu64_stats_update_end(\u0026pm-\u003esyncp);\n+\n+\tif (res)\n+\t\tdev_err(dev, \"Failed to %s port %d: %i\\n\", \"read stats for\",\n+\t\t\tport, res);\n+\treturn res;\n+}\n+\n+void yt921x_mib_poll(struct work_struct *work)\n+{\n+\tstruct yt921x_mib *pm = container_of_const(work, struct yt921x_mib,\n+\t\t\t\t\t\t work.work);\n+\tstruct yt921x_port *pp = pm-\u003eport;\n+\tstruct yt921x_priv *priv = container_of_const(pp, struct yt921x_priv,\n+\t\t\t\t\t\t ports[pp-\u003eindex]);\n+\tunsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;\n+\tint port = pp-\u003eindex;\n+\tint res;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\tres = yt921x_mib_read(priv, port);\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+\tif (res)\n+\t\tdelay *= 4;\n+\n+\tschedule_delayed_work(\u0026pm-\u003ework, delay);\n+}\n+\n+void\n+yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,\n+\t\t uint8_t *data)\n+{\n+\tif (stringset != ETH_SS_STATS)\n+\t\treturn;\n+\n+\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n+\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n+\n+\t\tif (desc-\u003ename)\n+\t\t\tethtool_puts(\u0026data, desc-\u003ename);\n+\t}\n+}\n+\n+void\n+yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\tu64 *buf;\n+\tsize_t j;\n+\n+\tif (!pm)\n+\t\treturn;\n+\tmib = \u0026pm-\u003estats;\n+\tbuf = pm-\u003edata;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\n+\tyt921x_mib_read(priv, port);\n+\n+\tj = 0;\n+\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n+\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n+\n+\t\tif (!desc-\u003ename)\n+\t\t\tcontinue;\n+\n+\t\tdata[j] = buf[i];\n+\t\tj++;\n+\t}\n+\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+}\n+\n+int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)\n+{\n+\tint cnt = 0;\n+\n+\tif (sset != ETH_SS_STATS)\n+\t\treturn 0;\n+\n+\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n+\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n+\n+\t\tif (desc-\u003ename)\n+\t\t\tcnt++;\n+\t}\n+\n+\treturn cnt;\n+}\n+\n+void\n+yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_eth_mac_stats *mac_stats)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\n+\tif (!pm)\n+\t\treturn;\n+\tmib = \u0026pm-\u003estats;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\n+\tyt921x_mib_read(priv, port);\n+\n+\tmac_stats-\u003eFramesTransmittedOK = u64_stats_read(\u0026pm-\u003etx_frames);\n+\tmac_stats-\u003eSingleCollisionFrames = u64_stats_read(\u0026mib-\u003etx_single_collisions);\n+\tmac_stats-\u003eMultipleCollisionFrames = u64_stats_read(\u0026mib-\u003etx_multiple_collisions);\n+\tmac_stats-\u003eFramesReceivedOK = u64_stats_read(\u0026pm-\u003erx_frames);\n+\tmac_stats-\u003eFrameCheckSequenceErrors = u64_stats_read(\u0026mib-\u003erx_crc_errors);\n+\tmac_stats-\u003eAlignmentErrors = u64_stats_read(\u0026mib-\u003erx_alignment_errors);\n+\tmac_stats-\u003eOctetsTransmittedOK = u64_stats_read(\u0026mib-\u003etx_good_bytes);\n+\tmac_stats-\u003eFramesWithDeferredXmissions = u64_stats_read(\u0026mib-\u003etx_deferred);\n+\tmac_stats-\u003eLateCollisions = u64_stats_read(\u0026mib-\u003etx_late_collisions);\n+\tmac_stats-\u003eFramesAbortedDueToXSColls = u64_stats_read(\u0026mib-\u003etx_aborted_errors);\n+\t/* mac_stats-\u003eFramesLostDueToIntMACXmitError */\n+\t/* mac_stats-\u003eCarrierSenseErrors */\n+\tmac_stats-\u003eOctetsReceivedOK = u64_stats_read(\u0026mib-\u003erx_good_bytes);\n+\t/* mac_stats-\u003eFramesLostDueToIntMACRcvError */\n+\tmac_stats-\u003eMulticastFramesXmittedOK = u64_stats_read(\u0026mib-\u003etx_multicast);\n+\tmac_stats-\u003eBroadcastFramesXmittedOK = u64_stats_read(\u0026mib-\u003etx_broadcast);\n+\t/* mac_stats-\u003eFramesWithExcessiveDeferral */\n+\tmac_stats-\u003eMulticastFramesReceivedOK = u64_stats_read(\u0026mib-\u003erx_multicast);\n+\tmac_stats-\u003eBroadcastFramesReceivedOK = u64_stats_read(\u0026mib-\u003erx_broadcast);\n+\t/* mac_stats-\u003eInRangeLengthErrors */\n+\t/* mac_stats-\u003eOutOfRangeLengthField */\n+\tmac_stats-\u003eFrameTooLongErrors = u64_stats_read(\u0026mib-\u003erx_oversize_errors);\n+\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+}\n+\n+void\n+yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_eth_ctrl_stats *ctrl_stats)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\n+\tif (!pm)\n+\t\treturn;\n+\tmib = \u0026pm-\u003estats;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\n+\tyt921x_mib_read(priv, port);\n+\n+\tctrl_stats-\u003eMACControlFramesTransmitted = u64_stats_read(\u0026mib-\u003etx_pause);\n+\tctrl_stats-\u003eMACControlFramesReceived = u64_stats_read(\u0026mib-\u003erx_pause);\n+\t/* ctrl_stats-\u003eUnsupportedOpcodesReceived */\n+\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+}\n+\n+static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {\n+\t{ 0, 64 },\n+\t{ 65, 127 },\n+\t{ 128, 255 },\n+\t{ 256, 511 },\n+\t{ 512, 1023 },\n+\t{ 1024, 1518 },\n+\t{ 1519, YT921X_FRAME_SIZE_MAX },\n+\t{}\n+};\n+\n+void\n+yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_rmon_stats *rmon_stats,\n+\t\t\t const struct ethtool_rmon_hist_range **ranges)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\n+\tif (!pm)\n+\t\treturn;\n+\tmib = \u0026pm-\u003estats;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\n+\tyt921x_mib_read(priv, port);\n+\n+\t*ranges = yt921x_rmon_ranges;\n+\n+\trmon_stats-\u003eundersize_pkts = u64_stats_read(\u0026mib-\u003erx_undersize_errors);\n+\trmon_stats-\u003eoversize_pkts = u64_stats_read(\u0026mib-\u003erx_oversize_errors);\n+\trmon_stats-\u003efragments = u64_stats_read(\u0026mib-\u003erx_alignment_errors);\n+\t/* rmon_stats-\u003ejabbers */\n+\n+\trmon_stats-\u003ehist[0] = u64_stats_read(\u0026mib-\u003erx_64byte);\n+\trmon_stats-\u003ehist[1] = u64_stats_read(\u0026mib-\u003erx_65_127byte);\n+\trmon_stats-\u003ehist[2] = u64_stats_read(\u0026mib-\u003erx_128_255byte);\n+\trmon_stats-\u003ehist[3] = u64_stats_read(\u0026mib-\u003erx_256_511byte);\n+\trmon_stats-\u003ehist[4] = u64_stats_read(\u0026mib-\u003erx_512_1023byte);\n+\trmon_stats-\u003ehist[5] = u64_stats_read(\u0026mib-\u003erx_1024_1518byte);\n+\trmon_stats-\u003ehist[6] = u64_stats_read(\u0026mib-\u003erx_jumbo);\n+\n+\trmon_stats-\u003ehist_tx[0] = u64_stats_read(\u0026mib-\u003etx_64byte);\n+\trmon_stats-\u003ehist_tx[1] = u64_stats_read(\u0026mib-\u003etx_65_127byte);\n+\trmon_stats-\u003ehist_tx[2] = u64_stats_read(\u0026mib-\u003etx_128_255byte);\n+\trmon_stats-\u003ehist_tx[3] = u64_stats_read(\u0026mib-\u003etx_256_511byte);\n+\trmon_stats-\u003ehist_tx[4] = u64_stats_read(\u0026mib-\u003etx_512_1023byte);\n+\trmon_stats-\u003ehist_tx[5] = u64_stats_read(\u0026mib-\u003etx_1024_1518byte);\n+\trmon_stats-\u003ehist_tx[6] = u64_stats_read(\u0026mib-\u003etx_jumbo);\n+\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+}\n+\n+void\n+yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,\n+\t\t struct rtnl_link_stats64 *stats)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\tunsigned int start;\n+\n+\tif (!pm)\n+\t\treturn;\n+\tmib = \u0026pm-\u003estats;\n+\n+\tdo {\n+\t\tstart = u64_stats_fetch_begin(\u0026pm-\u003esyncp);\n+\n+\t\tstats-\u003erx_length_errors = u64_stats_read(\u0026mib-\u003erx_undersize_errors) +\n+\t\t\t\t\t u64_stats_read(\u0026mib-\u003erx_fragment_errors);\n+\t\tstats-\u003erx_over_errors = u64_stats_read(\u0026mib-\u003erx_oversize_errors);\n+\t\tstats-\u003erx_crc_errors = u64_stats_read(\u0026mib-\u003erx_crc_errors);\n+\t\tstats-\u003erx_frame_errors = u64_stats_read(\u0026mib-\u003erx_alignment_errors);\n+\t\t/* stats-\u003erx_fifo_errors */\n+\t\t/* stats-\u003erx_missed_errors */\n+\n+\t\tstats-\u003etx_aborted_errors = u64_stats_read(\u0026mib-\u003etx_aborted_errors);\n+\t\t/* stats-\u003etx_carrier_errors */\n+\t\tstats-\u003etx_fifo_errors = u64_stats_read(\u0026mib-\u003etx_undersize_errors);\n+\t\t/* stats-\u003etx_heartbeat_errors */\n+\t\tstats-\u003etx_window_errors = u64_stats_read(\u0026mib-\u003etx_late_collisions);\n+\n+\t\tstats-\u003erx_packets = u64_stats_read(\u0026pm-\u003erx_frames);\n+\t\tstats-\u003etx_packets = u64_stats_read(\u0026pm-\u003etx_frames);\n+\t\tstats-\u003erx_bytes = u64_stats_read(\u0026mib-\u003erx_good_bytes) -\n+\t\t\t\t ETH_FCS_LEN * stats-\u003erx_packets;\n+\t\tstats-\u003etx_bytes = u64_stats_read(\u0026mib-\u003etx_good_bytes) -\n+\t\t\t\t ETH_FCS_LEN * stats-\u003etx_packets;\n+\t\tstats-\u003erx_dropped = u64_stats_read(\u0026mib-\u003erx_dropped);\n+\t\t/* stats-\u003etx_dropped */\n+\t\tstats-\u003emulticast = u64_stats_read(\u0026mib-\u003erx_multicast);\n+\t\tstats-\u003ecollisions = u64_stats_read(\u0026mib-\u003etx_collisions);\n+\t} while (u64_stats_fetch_retry(\u0026pm-\u003esyncp, start));\n+\n+\tstats-\u003erx_errors = stats-\u003erx_length_errors + stats-\u003erx_over_errors +\n+\t\t\t stats-\u003erx_crc_errors + stats-\u003erx_frame_errors;\n+\tstats-\u003etx_errors = stats-\u003etx_aborted_errors + stats-\u003etx_fifo_errors +\n+\t\t\t stats-\u003etx_window_errors;\n+}\n+\n+void\n+yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_pause_stats *pause_stats)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\n+\tif (!pm)\n+\t\treturn;\n+\tmib = \u0026pm-\u003estats;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\n+\tyt921x_mib_read(priv, port);\n+\n+\tpause_stats-\u003etx_pause_frames = u64_stats_read(\u0026mib-\u003etx_pause);\n+\tpause_stats-\u003erx_pause_frames = u64_stats_read(\u0026mib-\u003erx_pause);\n+\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+}\ndiff --git a/drivers/net/dsa/motorcomm/mib.h b/drivers/net/dsa/motorcomm/mib.h\nnew file mode 100644\nindex 0000000000000..33992a5f9bdc1\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/mib.h\n@@ -0,0 +1,167 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (c) 2026 David Yang\n+ */\n+\n+#ifndef _YT_MIB_H\n+#define _YT_MIB_H\n+\n+#include \u003clinux/u64_stats_sync.h\u003e\n+\n+#include \u003cnet/dsa.h\u003e\n+\n+#define YT921X_MIB_CTRL\t\t\t0xc0004\n+#define YT921X_MIB_CTRL_CLEAN\t\t\tBIT(30)\n+#define YT921X_MIB_CTRL_PORT_M\t\t\tGENMASK(6, 3)\n+#define YT921X_MIB_CTRL_PORT(x)\t\t\tFIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))\n+#define YT921X_MIB_CTRL_ONE_PORT\t\tBIT(1)\n+#define YT921X_MIB_CTRL_ALL_PORT\t\tBIT(0)\n+#define YT921X_MIBn_DATA0(port)\t\t(0xc0100 + 0x100 * (port))\n+#define YT921X_MIBn_DATAm(port, x)\t(YT921X_MIBn_DATA0(port) + 4 * (x))\n+#define YT921X_MIB_DATA_RX_BROADCAST\t\t0x00\n+#define YT921X_MIB_DATA_RX_PAUSE\t\t0x04\n+#define YT921X_MIB_DATA_RX_MULTICAST\t\t0x08\n+#define YT921X_MIB_DATA_RX_CRC_ERR\t\t0x0c\n+\n+#define YT921X_MIB_DATA_RX_ALIGN_ERR\t\t0x10\n+#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR\t0x14\n+#define YT921X_MIB_DATA_RX_FRAG_ERR\t\t0x18\n+#define YT921X_MIB_DATA_RX_PKT_SZ_64\t\t0x1c\n+\n+#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127\t0x20\n+#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255\t0x24\n+#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511\t0x28\n+#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023\t0x2c\n+\n+#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518\t0x30\n+#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX\t0x34\n+/* 0x38: unused */\n+#define YT921X_MIB_DATA_RX_GOOD_BYTES\t\t0x3c\n+\n+/* 0x40: 64 bytes */\n+#define YT921X_MIB_DATA_RX_BAD_BYTES\t\t0x44\n+/* 0x48: 64 bytes */\n+#define YT921X_MIB_DATA_RX_OVERSIZE_ERR\t0x4c\n+\n+#define YT921X_MIB_DATA_RX_DROPPED\t\t0x50\n+#define YT921X_MIB_DATA_TX_BROADCAST\t\t0x54\n+#define YT921X_MIB_DATA_TX_PAUSE\t\t0x58\n+#define YT921X_MIB_DATA_TX_MULTICAST\t\t0x5c\n+\n+#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR\t0x60\n+#define YT921X_MIB_DATA_TX_PKT_SZ_64\t\t0x64\n+#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127\t0x68\n+#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255\t0x6c\n+\n+#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511\t0x70\n+#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023\t0x74\n+#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518\t0x78\n+#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX\t0x7c\n+\n+/* 0x80: unused */\n+#define YT921X_MIB_DATA_TX_GOOD_BYTES\t\t0x84\n+/* 0x88: 64 bytes */\n+#define YT921X_MIB_DATA_TX_COLLISION\t\t0x8c\n+\n+#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION\t0x90\n+#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION\t0x94\n+#define YT921X_MIB_DATA_TX_SINGLE_COLLISION\t0x98\n+#define YT921X_MIB_DATA_TX_PKT\t\t\t0x9c\n+\n+#define YT921X_MIB_DATA_TX_DEFERRED\t\t0xa0\n+#define YT921X_MIB_DATA_TX_LATE_COLLISION\t0xa4\n+#define YT921X_MIB_DATA_RX_OAM\t\t\t0xa8\n+#define YT921X_MIB_DATA_TX_OAM\t\t\t0xac\n+\n+struct yt921x_mib_stats {\n+\tu64_stats_t rx_broadcast;\n+\tu64_stats_t rx_pause;\n+\tu64_stats_t rx_multicast;\n+\tu64_stats_t rx_crc_errors;\n+\n+\tu64_stats_t rx_alignment_errors;\n+\tu64_stats_t rx_undersize_errors;\n+\tu64_stats_t rx_fragment_errors;\n+\tu64_stats_t rx_64byte;\n+\n+\tu64_stats_t rx_65_127byte;\n+\tu64_stats_t rx_128_255byte;\n+\tu64_stats_t rx_256_511byte;\n+\tu64_stats_t rx_512_1023byte;\n+\n+\tu64_stats_t rx_1024_1518byte;\n+\tu64_stats_t rx_jumbo;\n+\tu64_stats_t rx_good_bytes;\n+\n+\tu64_stats_t rx_bad_bytes;\n+\tu64_stats_t rx_oversize_errors;\n+\n+\tu64_stats_t rx_dropped;\n+\tu64_stats_t tx_broadcast;\n+\tu64_stats_t tx_pause;\n+\tu64_stats_t tx_multicast;\n+\n+\tu64_stats_t tx_undersize_errors;\n+\tu64_stats_t tx_64byte;\n+\tu64_stats_t tx_65_127byte;\n+\tu64_stats_t tx_128_255byte;\n+\n+\tu64_stats_t tx_256_511byte;\n+\tu64_stats_t tx_512_1023byte;\n+\tu64_stats_t tx_1024_1518byte;\n+\tu64_stats_t tx_jumbo;\n+\n+\tu64_stats_t tx_good_bytes;\n+\tu64_stats_t tx_collisions;\n+\n+\tu64_stats_t tx_aborted_errors;\n+\tu64_stats_t tx_multiple_collisions;\n+\tu64_stats_t tx_single_collisions;\n+\tu64_stats_t tx_good;\n+\n+\tu64_stats_t tx_deferred;\n+\tu64_stats_t tx_late_collisions;\n+\tu64_stats_t rx_oam;\n+\tu64_stats_t tx_oam;\n+};\n+\n+#define YT921X_MIB_NUM\t(sizeof(struct yt921x_mib_stats) / sizeof(u64_stats_t))\n+\n+struct yt921x_mib {\n+\tstruct yt921x_port *port;\n+\n+\tstruct delayed_work work;\n+\tstruct u64_stats_sync syncp;\n+\t/* protected by syncp OR priv-\u003ereg_lock */\n+\tstruct yt921x_mib_stats stats;\n+\tu64_stats_t rx_frames;\n+\tu64_stats_t tx_frames;\n+\t/* protected by priv-\u003ereg_lock */\n+\tu64 data[YT921X_MIB_NUM];\n+};\n+\n+void yt921x_mib_poll(struct work_struct *work);\n+void\n+yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,\n+\t\t uint8_t *data);\n+void\n+yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);\n+int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset);\n+void\n+yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_eth_mac_stats *mac_stats);\n+void\n+yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_eth_ctrl_stats *ctrl_stats);\n+void\n+yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_rmon_stats *rmon_stats,\n+\t\t\t const struct ethtool_rmon_hist_range **ranges);\n+void\n+yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,\n+\t\t struct rtnl_link_stats64 *stats);\n+void\n+yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_pause_stats *pause_stats);\n+\n+#endif\ndiff --git a/drivers/net/dsa/motorcomm/smi.c b/drivers/net/dsa/motorcomm/smi.c\nindex bf3adfd64165a..27086602997bb 100644\n--- a/drivers/net/dsa/motorcomm/smi.c\n+++ b/drivers/net/dsa/motorcomm/smi.c\n@@ -63,6 +63,36 @@ int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)\n \treturn yt921x_reg_write(priv, reg, u);\n }\n \n+/* Reliably read a 64bit counter */\n+int yt921x_counter_read(struct yt921x_priv *priv, u32 reg, u64 *valp)\n+{\n+\tu32 old_lo;\n+\tint res;\n+\tu32 hi;\n+\tu32 lo;\n+\n+\tres = yt921x_reg_read(priv, reg, \u0026old_lo);\n+\tif (res)\n+\t\treturn res;\n+\n+\tfor (int i = 0; i \u003c 16; i++) {\n+\t\tres = yt921x_reg_read(priv, reg + 4, \u0026hi);\n+\t\tif (res)\n+\t\t\treturn res;\n+\t\tres = yt921x_reg_read(priv, reg, \u0026lo);\n+\t\tif (res)\n+\t\t\treturn res;\n+\n+\t\tif (lo \u003e= old_lo) {\n+\t\t\t*valp = ((u64)hi \u003c\u003c 32) | lo;\n+\t\t\treturn 0;\n+\t\t}\n+\t\told_lo = lo;\n+\t}\n+\n+\treturn -ETIMEDOUT;\n+}\n+\n static int\n yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,\n \t\t unsigned int num_regs)\ndiff --git a/drivers/net/dsa/motorcomm/smi.h b/drivers/net/dsa/motorcomm/smi.h\nindex 212e20f71d806..d34240548af7e 100644\n--- a/drivers/net/dsa/motorcomm/smi.h\n+++ b/drivers/net/dsa/motorcomm/smi.h\n@@ -35,6 +35,8 @@ yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)\n \treturn yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);\n }\n \n+int yt921x_counter_read(struct yt921x_priv *priv, u32 reg, u64 *valp);\n+\n /* Some multi-word registers, like VLANn_CTRL, should be treated as a single\n * long register. More specifically, writes to parts of its words won't become\n * visible, until the last word is written.\n"
} |
| 2/1 | 2026/09/05 00:17 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch series only modifies the Motorcomm YT9215 DSA Ethernet switch driver (drivers/net/dsa/motorcomm/), which interacts with a specific physical MDIO-connected switch ASIC. This hardware is not emulated in standard QEMU/GCE fuzzing environments, making the code structurally unreachable during fuzzing.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 7217a26274e7591624ca7a0893dd0606389a1fd2
Author: syz-cluster <triage@syzkaller.com>
Date: Sat Sep 5 00:17:51 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile
index aeb12cb91f935..5c2d34ec05fc0 100644
--- a/drivers/net/dsa/motorcomm/Makefile
+++ b/drivers/net/dsa/motorcomm/Makefile
@@ -2,4 +2,5 @@
obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
yt921x-objs := chip.o
yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o
+yt921x-objs += mib.o
yt921x-objs += smi.o
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index d663af010f431..b4059053ef152 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -19,6 +19,7 @@
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/sort.h>
+#include <linux/u64_stats_sync.h>
#include <net/dsa.h>
#include <net/dscp.h>
@@ -27,75 +28,9 @@
#include "chip.h"
#include "leds.h"
+#include "mib.h"
#include "smi.h"
-struct yt921x_mib_desc {
- unsigned int size;
- unsigned int offset;
- const char *name;
-};
-
-#define MIB_DESC(_size, _offset, _name) \
- {_size, _offset, _name}
-
-/* Must agree with yt921x_mib
- *
- * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),
- * structured go to their *_stats() methods, but we need their sizes and offsets
- * to perform 32bit MIB overflow wraparound.
- */
-static const struct yt921x_mib_desc yt921x_mib_descs[] = {
- MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),
- MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),
-
- MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"),
- MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),
-
- MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"),
- MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"),
-};
-
struct yt921x_info {
const char *name;
u16 major;
@@ -154,14 +89,6 @@ static const struct yt921x_info yt921x_infos[] = {
#define YT921X_VID_UNWARE 4095
-/* The interval should be small enough to avoid overflow of 32bit MIBs.
- *
- * Until we can read MIBs from stats64 call directly (i.e. sleep
- * there), we have to poll stats more frequently then it is actually needed.
- * For overflow protection, normally, 100 sec interval should have been OK.
- */
-#define YT921X_STATS_INTERVAL_JIFFIES (3 * HZ)
-
struct yt921x_reg_mdio {
struct mii_bus *bus;
int addr;
@@ -544,294 +471,6 @@ yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
return 0;
}
-/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
-static int yt921x_read_mib(struct yt921x_priv *priv, int port)
-{
- struct yt921x_port *pp = &priv->ports[port];
- struct device *dev = to_device(priv);
- struct yt921x_mib *mib = &pp->mib;
- int res = 0;
-
- /* Reading of yt921x_port::mib is not protected by a lock and it's vain
- * to keep its consistency, since we have to read registers one by one
- * and there is no way to make a snapshot of MIB stats.
- *
- * Writing (by this function only) is and should be protected by
- * reg_lock.
- */
-
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
- u32 reg = YT921X_MIBn_DATA0(port) + desc->offset;
- u64 *valp = &((u64 *)mib)[i];
- u32 val0;
- u64 val;
-
- res = yt921x_reg_read(priv, reg, &val0);
- if (res)
- break;
-
- if (desc->size <= 1) {
- u64 old_val = *valp;
-
- val = (old_val & ~(u64)U32_MAX) | val0;
- if (val < old_val)
- val += 1ull << 32;
- } else {
- u32 val1;
-
- res = yt921x_reg_read(priv, reg + 4, &val1);
- if (res)
- break;
- val = ((u64)val1 << 32) | val0;
- }
-
- WRITE_ONCE(*valp, val);
- }
-
- pp->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
- mib->rx_128_255byte + mib->rx_256_511byte +
- mib->rx_512_1023byte + mib->rx_1024_1518byte +
- mib->rx_jumbo;
- pp->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
- mib->tx_128_255byte + mib->tx_256_511byte +
- mib->tx_512_1023byte + mib->tx_1024_1518byte +
- mib->tx_jumbo;
-
- if (res)
- dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
- port, res);
- return res;
-}
-
-static void yt921x_poll_mib(struct work_struct *work)
-{
- struct yt921x_port *pp = container_of_const(work, struct yt921x_port,
- mib_read.work);
- struct yt921x_priv *priv = (void *)(pp - pp->index) -
- offsetof(struct yt921x_priv, ports);
- unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
- int port = pp->index;
- int res;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
- if (res)
- delay *= 4;
-
- schedule_delayed_work(&pp->mib_read, delay);
-}
-
-static void
-yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
- uint8_t *data)
-{
- if (stringset != ETH_SS_STATS)
- return;
-
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-
- if (desc->name)
- ethtool_puts(&data, desc->name);
- }
-}
-
-static void
-yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
- size_t j;
-
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- j = 0;
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-
- if (!desc->name)
- continue;
-
- data[j] = ((u64 *)mib)[i];
- j++;
- }
-}
-
-static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
-{
- int cnt = 0;
-
- if (sset != ETH_SS_STATS)
- return 0;
-
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-
- if (desc->name)
- cnt++;
- }
-
- return cnt;
-}
-
-static void
-yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
- struct ethtool_eth_mac_stats *mac_stats)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
-
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- mac_stats->FramesTransmittedOK = pp->tx_frames;
- mac_stats->SingleCollisionFrames = mib->tx_single_collisions;
- mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions;
- mac_stats->FramesReceivedOK = pp->rx_frames;
- mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors;
- mac_stats->AlignmentErrors = mib->rx_alignment_errors;
- mac_stats->OctetsTransmittedOK = mib->tx_good_bytes;
- mac_stats->FramesWithDeferredXmissions = mib->tx_deferred;
- mac_stats->LateCollisions = mib->tx_late_collisions;
- mac_stats->FramesAbortedDueToXSColls = mib->tx_aborted_errors;
- /* mac_stats->FramesLostDueToIntMACXmitError */
- /* mac_stats->CarrierSenseErrors */
- mac_stats->OctetsReceivedOK = mib->rx_good_bytes;
- /* mac_stats->FramesLostDueToIntMACRcvError */
- mac_stats->MulticastFramesXmittedOK = mib->tx_multicast;
- mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast;
- /* mac_stats->FramesWithExcessiveDeferral */
- mac_stats->MulticastFramesReceivedOK = mib->rx_multicast;
- mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast;
- /* mac_stats->InRangeLengthErrors */
- /* mac_stats->OutOfRangeLengthField */
- mac_stats->FrameTooLongErrors = mib->rx_oversize_errors;
-}
-
-static void
-yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
- struct ethtool_eth_ctrl_stats *ctrl_stats)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
-
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- ctrl_stats->MACControlFramesTransmitted = mib->tx_pause;
- ctrl_stats->MACControlFramesReceived = mib->rx_pause;
- /* ctrl_stats->UnsupportedOpcodesReceived */
-}
-
-static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {
- { 0, 64 },
- { 65, 127 },
- { 128, 255 },
- { 256, 511 },
- { 512, 1023 },
- { 1024, 1518 },
- { 1519, YT921X_FRAME_SIZE_MAX },
- {}
-};
-
-static void
-yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
- struct ethtool_rmon_stats *rmon_stats,
- const struct ethtool_rmon_hist_range **ranges)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
-
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- *ranges = yt921x_rmon_ranges;
-
- rmon_stats->undersize_pkts = mib->rx_undersize_errors;
- rmon_stats->oversize_pkts = mib->rx_oversize_errors;
- rmon_stats->fragments = mib->rx_alignment_errors;
- /* rmon_stats->jabbers */
-
- rmon_stats->hist[0] = mib->rx_64byte;
- rmon_stats->hist[1] = mib->rx_65_127byte;
- rmon_stats->hist[2] = mib->rx_128_255byte;
- rmon_stats->hist[3] = mib->rx_256_511byte;
- rmon_stats->hist[4] = mib->rx_512_1023byte;
- rmon_stats->hist[5] = mib->rx_1024_1518byte;
- rmon_stats->hist[6] = mib->rx_jumbo;
-
- rmon_stats->hist_tx[0] = mib->tx_64byte;
- rmon_stats->hist_tx[1] = mib->tx_65_127byte;
- rmon_stats->hist_tx[2] = mib->tx_128_255byte;
- rmon_stats->hist_tx[3] = mib->tx_256_511byte;
- rmon_stats->hist_tx[4] = mib->tx_512_1023byte;
- rmon_stats->hist_tx[5] = mib->tx_1024_1518byte;
- rmon_stats->hist_tx[6] = mib->tx_jumbo;
-}
-
-static void
-yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
- struct rtnl_link_stats64 *stats)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
-
- stats->rx_length_errors = mib->rx_undersize_errors +
- mib->rx_fragment_errors;
- stats->rx_over_errors = mib->rx_oversize_errors;
- stats->rx_crc_errors = mib->rx_crc_errors;
- stats->rx_frame_errors = mib->rx_alignment_errors;
- /* stats->rx_fifo_errors */
- /* stats->rx_missed_errors */
-
- stats->tx_aborted_errors = mib->tx_aborted_errors;
- /* stats->tx_carrier_errors */
- stats->tx_fifo_errors = mib->tx_undersize_errors;
- /* stats->tx_heartbeat_errors */
- stats->tx_window_errors = mib->tx_late_collisions;
-
- stats->rx_packets = pp->rx_frames;
- stats->tx_packets = pp->tx_frames;
- stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets;
- stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets;
- stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
- stats->rx_crc_errors + stats->rx_frame_errors;
- stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors +
- stats->tx_window_errors;
- stats->rx_dropped = mib->rx_dropped;
- /* stats->tx_dropped */
- stats->multicast = mib->rx_multicast;
- stats->collisions = mib->tx_collisions;
-}
-
-static void
-yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
- struct ethtool_pause_stats *pause_stats)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
-
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- pause_stats->tx_pause_frames = mib->tx_pause;
- pause_stats->rx_pause_frames = mib->rx_pause;
-}
-
static int
yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)
{
@@ -3953,11 +3592,14 @@ yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
+ struct yt921x_mib *pm;
int port = dp->index;
int res;
/* No need to sync; port control block is hold until device remove */
- cancel_delayed_work(&priv->ports[port].mib_read);
+ pm = priv->ports[port].mib;
+ if (pm)
+ cancel_delayed_work(&pm->work);
mutex_lock(&priv->reg_lock);
res = yt921x_port_down(priv, port);
@@ -3976,6 +3618,7 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
+ struct yt921x_mib *pm;
int port = dp->index;
int res;
@@ -3988,7 +3631,9 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,
dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up",
port, res);
- schedule_delayed_work(&priv->ports[port].mib_read, 0);
+ pm = priv->ports[port].mib;
+ if (pm)
+ schedule_delayed_work(&pm->work, 0);
}
static void
@@ -4108,11 +3753,36 @@ yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port,
return DSA_TAG_PROTO_YT921X;
}
+static void yt921x_dsa_port_teardown(struct dsa_switch *ds, int port)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+
+ if (pm)
+ disable_delayed_work_sync(&pm->work);
+}
+
static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct device *dev = to_device(priv);
+ struct yt921x_mib *pm = pp->mib;
int res;
+ if (!pm && !(BIT(port) & (priv->info->internal_mask |
+ priv->info->external_mask))) {
+ pm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL);
+ if (!pm)
+ return -ENOMEM;
+ pp->mib = pm;
+
+ pm->port = pp;
+ u64_stats_init(&pm->syncp);
+ INIT_DELAYED_WORK(&pm->work, yt921x_mib_poll);
+ }
+
mutex_lock(&priv->reg_lock);
res = yt921x_port_setup(priv, port);
mutex_unlock(&priv->reg_lock);
@@ -4667,6 +4337,7 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
/* port */
.get_tag_protocol = yt921x_dsa_get_tag_protocol,
.phylink_get_caps = yt921x_dsa_phylink_get_caps,
+ .port_teardown = yt921x_dsa_port_teardown,
.port_setup = yt921x_dsa_port_setup,
#if IS_ENABLED(CONFIG_DCB)
/* dscp */
@@ -4696,12 +4367,6 @@ static void yt921x_mdio_remove(struct mdio_device *mdiodev)
if (!priv)
return;
- for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0; ) {
- struct yt921x_port *pp = &priv->ports[i];
-
- disable_delayed_work_sync(&pp->mib_read);
- }
-
dsa_unregister_switch(&priv->ds);
for (unsigned int i = 0; i < ARRAY_SIZE(priv->acl_blks); i++) {
@@ -4749,7 +4414,6 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev)
struct yt921x_port *pp = &priv->ports[i];
pp->index = i;
- INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib);
}
ds = &priv->ds;
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 83cd454955ddb..ce2015026849a 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -167,69 +167,6 @@
#define YT921X_EEE_CTRL 0xb0000
#define YT921X_EEE_CTRL_ENn(port) BIT(port)
-#define YT921X_MIB_CTRL 0xc0004
-#define YT921X_MIB_CTRL_CLEAN BIT(30)
-#define YT921X_MIB_CTRL_PORT_M GENMASK(6, 3)
-#define YT921X_MIB_CTRL_PORT(x) FIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))
-#define YT921X_MIB_CTRL_ONE_PORT BIT(1)
-#define YT921X_MIB_CTRL_ALL_PORT BIT(0)
-#define YT921X_MIBn_DATA0(port) (0xc0100 + 0x100 * (port))
-#define YT921X_MIBn_DATAm(port, x) (YT921X_MIBn_DATA0(port) + 4 * (x))
-#define YT921X_MIB_DATA_RX_BROADCAST 0x00
-#define YT921X_MIB_DATA_RX_PAUSE 0x04
-#define YT921X_MIB_DATA_RX_MULTICAST 0x08
-#define YT921X_MIB_DATA_RX_CRC_ERR 0x0c
-
-#define YT921X_MIB_DATA_RX_ALIGN_ERR 0x10
-#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR 0x14
-#define YT921X_MIB_DATA_RX_FRAG_ERR 0x18
-#define YT921X_MIB_DATA_RX_PKT_SZ_64 0x1c
-
-#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127 0x20
-#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255 0x24
-#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511 0x28
-#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023 0x2c
-
-#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518 0x30
-#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX 0x34
-/* 0x38: unused */
-#define YT921X_MIB_DATA_RX_GOOD_BYTES 0x3c
-
-/* 0x40: 64 bytes */
-#define YT921X_MIB_DATA_RX_BAD_BYTES 0x44
-/* 0x48: 64 bytes */
-#define YT921X_MIB_DATA_RX_OVERSIZE_ERR 0x4c
-
-#define YT921X_MIB_DATA_RX_DROPPED 0x50
-#define YT921X_MIB_DATA_TX_BROADCAST 0x54
-#define YT921X_MIB_DATA_TX_PAUSE 0x58
-#define YT921X_MIB_DATA_TX_MULTICAST 0x5c
-
-#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR 0x60
-#define YT921X_MIB_DATA_TX_PKT_SZ_64 0x64
-#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127 0x68
-#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255 0x6c
-
-#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511 0x70
-#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023 0x74
-#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518 0x78
-#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX 0x7c
-
-/* 0x80: unused */
-#define YT921X_MIB_DATA_TX_GOOD_BYTES 0x84
-/* 0x88: 64 bytes */
-#define YT921X_MIB_DATA_TX_COLLISION 0x8c
-
-#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION 0x90
-#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION 0x94
-#define YT921X_MIB_DATA_TX_SINGLE_COLLISION 0x98
-#define YT921X_MIB_DATA_TX_PKT 0x9c
-
-#define YT921X_MIB_DATA_TX_DEFERRED 0xa0
-#define YT921X_MIB_DATA_TX_LATE_COLLISION 0xa4
-#define YT921X_MIB_DATA_RX_OAM 0xa8
-#define YT921X_MIB_DATA_TX_OAM 0xac
-
#define YT921X_EDATA_CTRL 0xe0000
#define YT921X_EDATA_CTRL_ADDR_M GENMASK(15, 8)
#define YT921X_EDATA_CTRL_ADDR(x) FIELD_PREP(YT921X_EDATA_CTRL_ADDR_M, (x))
@@ -861,58 +798,6 @@ enum yt921x_fdb_entry_status {
#define yt921x_port_is_internal(port) ((port) < 8)
#define yt921x_port_is_external(port) ((port) == 8 || (port) == 9)
-struct yt921x_mib {
- u64 rx_broadcast;
- u64 rx_pause;
- u64 rx_multicast;
- u64 rx_crc_errors;
-
- u64 rx_alignment_errors;
- u64 rx_undersize_errors;
- u64 rx_fragment_errors;
- u64 rx_64byte;
-
- u64 rx_65_127byte;
- u64 rx_128_255byte;
- u64 rx_256_511byte;
- u64 rx_512_1023byte;
-
- u64 rx_1024_1518byte;
- u64 rx_jumbo;
- u64 rx_good_bytes;
-
- u64 rx_bad_bytes;
- u64 rx_oversize_errors;
-
- u64 rx_dropped;
- u64 tx_broadcast;
- u64 tx_pause;
- u64 tx_multicast;
-
- u64 tx_undersize_errors;
- u64 tx_64byte;
- u64 tx_65_127byte;
- u64 tx_128_255byte;
-
- u64 tx_256_511byte;
- u64 tx_512_1023byte;
- u64 tx_1024_1518byte;
- u64 tx_jumbo;
-
- u64 tx_good_bytes;
- u64 tx_collisions;
-
- u64 tx_aborted_errors;
- u64 tx_multiple_collisions;
- u64 tx_single_collisions;
- u64 tx_good;
-
- u64 tx_deferred;
- u64 tx_late_collisions;
- u64 rx_oam;
- u64 tx_oam;
-};
-
struct yt921x_acl_entry {
u32 key[2];
u32 mask[2];
@@ -936,13 +821,8 @@ struct yt921x_acl_blk {
struct yt921x_port {
unsigned char index;
- bool hairpin;
- bool isolated;
-
- struct delayed_work mib_read;
- struct yt921x_mib mib;
- u64 rx_frames;
- u64 tx_frames;
+ bool hairpin:1;
+ bool isolated:1;
#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
unsigned char led_duty;
@@ -953,6 +833,8 @@ struct yt921x_port {
struct yt921x_led *leds[YT921X_LED_GROUP_NUM];
#endif
+
+ struct yt921x_mib *mib;
};
struct yt921x_reg_ops {
diff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c
new file mode 100644
index 0000000000000..30478ce71c105
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/mib.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#include "chip.h"
+#include "mib.h"
+#include "smi.h"
+
+struct yt921x_mib_desc {
+ unsigned char size;
+ unsigned char offset;
+ const char *name;
+};
+
+#define MIB_DESC(_size, _offset, _name) {_size, _offset, _name}
+
+/* Must agree with yt921x_mib
+ *
+ * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),
+ * structured go to their *_stats() methods, but we need their sizes and offsets
+ * to perform 32bit MIB overflow wraparound.
+ */
+static const struct yt921x_mib_desc yt921x_mib_descs[] = {
+ MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),
+ MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),
+
+ MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),
+
+ MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"),
+};
+
+/* The interval should be small enough to avoid overflow of 32bit MIBs.
+ *
+ * Until we can read MIBs from stats64 call directly (i.e. sleep
+ * there), we have to poll stats more frequently then it is actually needed.
+ * For overflow protection, normally, 100 sec interval should have been OK.
+ */
+#define YT921X_STATS_INTERVAL_JIFFIES (3 * HZ)
+
+#define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds)
+#define to_device(priv) ((priv)->ds.dev)
+
+/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
+static int yt921x_mib_read(struct yt921x_priv *priv, int port)
+{
+ struct yt921x_port *pp = &priv->ports[port];
+ struct device *dev = to_device(priv);
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+ u64 *buf = pm->data;
+ u64 rx_frames;
+ u64 tx_frames;
+ int res = 0;
+
+ mib = &pm->stats;
+
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+ u32 reg = YT921X_MIBn_DATA0(port) + desc->offset;
+
+ if (desc->size <= 1) {
+ u64 old_val = buf[i];
+ u32 val0;
+ u64 val;
+
+ res = yt921x_reg_read(priv, reg, &val0);
+ if (res)
+ break;
+
+ val = (old_val & ~(u64)U32_MAX) | val0;
+ if (val < old_val)
+ val += 1ull << 32;
+
+ buf[i] = val;
+ } else {
+ res = yt921x_counter_read(priv, reg, &buf[i]);
+ if (res)
+ break;
+ }
+ }
+
+ u64_stats_update_begin(&pm->syncp);
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++)
+ u64_stats_set(&((u64_stats_t *)mib)[i], buf[i]);
+ rx_frames = u64_stats_read(&mib->rx_64byte) +
+ u64_stats_read(&mib->rx_65_127byte) +
+ u64_stats_read(&mib->rx_128_255byte) +
+ u64_stats_read(&mib->rx_256_511byte) +
+ u64_stats_read(&mib->rx_512_1023byte) +
+ u64_stats_read(&mib->rx_1024_1518byte) +
+ u64_stats_read(&mib->rx_jumbo);
+ tx_frames = u64_stats_read(&mib->tx_64byte) +
+ u64_stats_read(&mib->tx_65_127byte) +
+ u64_stats_read(&mib->tx_128_255byte) +
+ u64_stats_read(&mib->tx_256_511byte) +
+ u64_stats_read(&mib->tx_512_1023byte) +
+ u64_stats_read(&mib->tx_1024_1518byte) +
+ u64_stats_read(&mib->tx_jumbo);
+ u64_stats_set(&pm->rx_frames, rx_frames);
+ u64_stats_set(&pm->tx_frames, tx_frames);
+ u64_stats_update_end(&pm->syncp);
+
+ if (res)
+ dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
+ port, res);
+ return res;
+}
+
+void yt921x_mib_poll(struct work_struct *work)
+{
+ struct yt921x_mib *pm = container_of_const(work, struct yt921x_mib,
+ work.work);
+ struct yt921x_port *pp = pm->port;
+ struct yt921x_priv *priv = container_of_const(pp, struct yt921x_priv,
+ ports[pp->index]);
+ unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
+ int port = pp->index;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_mib_read(priv, port);
+ mutex_unlock(&priv->reg_lock);
+ if (res)
+ delay *= 4;
+
+ schedule_delayed_work(&pm->work, delay);
+}
+
+void
+yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+ uint8_t *data)
+{
+ if (stringset != ETH_SS_STATS)
+ return;
+
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+
+ if (desc->name)
+ ethtool_puts(&data, desc->name);
+ }
+}
+
+void
+yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+ u64 *buf;
+ size_t j;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+ buf = pm->data;
+
+ mutex_lock(&priv->reg_lock);
+
+ yt921x_mib_read(priv, port);
+
+ j = 0;
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+
+ if (!desc->name)
+ continue;
+
+ data[j] = buf[i];
+ j++;
+ }
+
+ mutex_unlock(&priv->reg_lock);
+}
+
+int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+ int cnt = 0;
+
+ if (sset != ETH_SS_STATS)
+ return 0;
+
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+
+ if (desc->name)
+ cnt++;
+ }
+
+ return cnt;
+}
+
+void
+yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+
+ yt921x_mib_read(priv, port);
+
+ mac_stats->FramesTransmittedOK = u64_stats_read(&pm->tx_frames);
+ mac_stats->SingleCollisionFrames = u64_stats_read(&mib->tx_single_collisions);
+ mac_stats->MultipleCollisionFrames = u64_stats_read(&mib->tx_multiple_collisions);
+ mac_stats->FramesReceivedOK = u64_stats_read(&pm->rx_frames);
+ mac_stats->FrameCheckSequenceErrors = u64_stats_read(&mib->rx_crc_errors);
+ mac_stats->AlignmentErrors = u64_stats_read(&mib->rx_alignment_errors);
+ mac_stats->OctetsTransmittedOK = u64_stats_read(&mib->tx_good_bytes);
+ mac_stats->FramesWithDeferredXmissions = u64_stats_read(&mib->tx_deferred);
+ mac_stats->LateCollisions = u64_stats_read(&mib->tx_late_collisions);
+ mac_stats->FramesAbortedDueToXSColls = u64_stats_read(&mib->tx_aborted_errors);
+ /* mac_stats->FramesLostDueToIntMACXmitError */
+ /* mac_stats->CarrierSenseErrors */
+ mac_stats->OctetsReceivedOK = u64_stats_read(&mib->rx_good_bytes);
+ /* mac_stats->FramesLostDueToIntMACRcvError */
+ mac_stats->MulticastFramesXmittedOK = u64_stats_read(&mib->tx_multicast);
+ mac_stats->BroadcastFramesXmittedOK = u64_stats_read(&mib->tx_broadcast);
+ /* mac_stats->FramesWithExcessiveDeferral */
+ mac_stats->MulticastFramesReceivedOK = u64_stats_read(&mib->rx_multicast);
+ mac_stats->BroadcastFramesReceivedOK = u64_stats_read(&mib->rx_broadcast);
+ /* mac_stats->InRangeLengthErrors */
+ /* mac_stats->OutOfRangeLengthField */
+ mac_stats->FrameTooLongErrors = u64_stats_read(&mib->rx_oversize_errors);
+
+ mutex_unlock(&priv->reg_lock);
+}
+
+void
+yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+
+ yt921x_mib_read(priv, port);
+
+ ctrl_stats->MACControlFramesTransmitted = u64_stats_read(&mib->tx_pause);
+ ctrl_stats->MACControlFramesReceived = u64_stats_read(&mib->rx_pause);
+ /* ctrl_stats->UnsupportedOpcodesReceived */
+
+ mutex_unlock(&priv->reg_lock);
+}
+
+static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {
+ { 0, 64 },
+ { 65, 127 },
+ { 128, 255 },
+ { 256, 511 },
+ { 512, 1023 },
+ { 1024, 1518 },
+ { 1519, YT921X_FRAME_SIZE_MAX },
+ {}
+};
+
+void
+yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+
+ yt921x_mib_read(priv, port);
+
+ *ranges = yt921x_rmon_ranges;
+
+ rmon_stats->undersize_pkts = u64_stats_read(&mib->rx_undersize_errors);
+ rmon_stats->oversize_pkts = u64_stats_read(&mib->rx_oversize_errors);
+ rmon_stats->fragments = u64_stats_read(&mib->rx_alignment_errors);
+ /* rmon_stats->jabbers */
+
+ rmon_stats->hist[0] = u64_stats_read(&mib->rx_64byte);
+ rmon_stats->hist[1] = u64_stats_read(&mib->rx_65_127byte);
+ rmon_stats->hist[2] = u64_stats_read(&mib->rx_128_255byte);
+ rmon_stats->hist[3] = u64_stats_read(&mib->rx_256_511byte);
+ rmon_stats->hist[4] = u64_stats_read(&mib->rx_512_1023byte);
+ rmon_stats->hist[5] = u64_stats_read(&mib->rx_1024_1518byte);
+ rmon_stats->hist[6] = u64_stats_read(&mib->rx_jumbo);
+
+ rmon_stats->hist_tx[0] = u64_stats_read(&mib->tx_64byte);
+ rmon_stats->hist_tx[1] = u64_stats_read(&mib->tx_65_127byte);
+ rmon_stats->hist_tx[2] = u64_stats_read(&mib->tx_128_255byte);
+ rmon_stats->hist_tx[3] = u64_stats_read(&mib->tx_256_511byte);
+ rmon_stats->hist_tx[4] = u64_stats_read(&mib->tx_512_1023byte);
+ rmon_stats->hist_tx[5] = u64_stats_read(&mib->tx_1024_1518byte);
+ rmon_stats->hist_tx[6] = u64_stats_read(&mib->tx_jumbo);
+
+ mutex_unlock(&priv->reg_lock);
+}
+
+void
+yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
+ struct rtnl_link_stats64 *stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+ unsigned int start;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ do {
+ start = u64_stats_fetch_begin(&pm->syncp);
+
+ stats->rx_length_errors = u64_stats_read(&mib->rx_undersize_errors) +
+ u64_stats_read(&mib->rx_fragment_errors);
+ stats->rx_over_errors = u64_stats_read(&mib->rx_oversize_errors);
+ stats->rx_crc_errors = u64_stats_read(&mib->rx_crc_errors);
+ stats->rx_frame_errors = u64_stats_read(&mib->rx_alignment_errors);
+ /* stats->rx_fifo_errors */
+ /* stats->rx_missed_errors */
+
+ stats->tx_aborted_errors = u64_stats_read(&mib->tx_aborted_errors);
+ /* stats->tx_carrier_errors */
+ stats->tx_fifo_errors = u64_stats_read(&mib->tx_undersize_errors);
+ /* stats->tx_heartbeat_errors */
+ stats->tx_window_errors = u64_stats_read(&mib->tx_late_collisions);
+
+ stats->rx_packets = u64_stats_read(&pm->rx_frames);
+ stats->tx_packets = u64_stats_read(&pm->tx_frames);
+ stats->rx_bytes = u64_stats_read(&mib->rx_good_bytes) -
+ ETH_FCS_LEN * stats->rx_packets;
+ stats->tx_bytes = u64_stats_read(&mib->tx_good_bytes) -
+ ETH_FCS_LEN * stats->tx_packets;
+ stats->rx_dropped = u64_stats_read(&mib->rx_dropped);
+ /* stats->tx_dropped */
+ stats->multicast = u64_stats_read(&mib->rx_multicast);
+ stats->collisions = u64_stats_read(&mib->tx_collisions);
+ } while (u64_stats_fetch_retry(&pm->syncp, start));
+
+ stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
+ stats->rx_crc_errors + stats->rx_frame_errors;
+ stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors +
+ stats->tx_window_errors;
+}
+
+void
+yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
+ struct ethtool_pause_stats *pause_stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+
+ yt921x_mib_read(priv, port);
+
+ pause_stats->tx_pause_frames = u64_stats_read(&mib->tx_pause);
+ pause_stats->rx_pause_frames = u64_stats_read(&mib->rx_pause);
+
+ mutex_unlock(&priv->reg_lock);
+}
diff --git a/drivers/net/dsa/motorcomm/mib.h b/drivers/net/dsa/motorcomm/mib.h
new file mode 100644
index 0000000000000..33992a5f9bdc1
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/mib.h
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#ifndef _YT_MIB_H
+#define _YT_MIB_H
+
+#include <linux/u64_stats_sync.h>
+
+#include <net/dsa.h>
+
+#define YT921X_MIB_CTRL 0xc0004
+#define YT921X_MIB_CTRL_CLEAN BIT(30)
+#define YT921X_MIB_CTRL_PORT_M GENMASK(6, 3)
+#define YT921X_MIB_CTRL_PORT(x) FIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))
+#define YT921X_MIB_CTRL_ONE_PORT BIT(1)
+#define YT921X_MIB_CTRL_ALL_PORT BIT(0)
+#define YT921X_MIBn_DATA0(port) (0xc0100 + 0x100 * (port))
+#define YT921X_MIBn_DATAm(port, x) (YT921X_MIBn_DATA0(port) + 4 * (x))
+#define YT921X_MIB_DATA_RX_BROADCAST 0x00
+#define YT921X_MIB_DATA_RX_PAUSE 0x04
+#define YT921X_MIB_DATA_RX_MULTICAST 0x08
+#define YT921X_MIB_DATA_RX_CRC_ERR 0x0c
+
+#define YT921X_MIB_DATA_RX_ALIGN_ERR 0x10
+#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR 0x14
+#define YT921X_MIB_DATA_RX_FRAG_ERR 0x18
+#define YT921X_MIB_DATA_RX_PKT_SZ_64 0x1c
+
+#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127 0x20
+#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255 0x24
+#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511 0x28
+#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023 0x2c
+
+#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518 0x30
+#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX 0x34
+/* 0x38: unused */
+#define YT921X_MIB_DATA_RX_GOOD_BYTES 0x3c
+
+/* 0x40: 64 bytes */
+#define YT921X_MIB_DATA_RX_BAD_BYTES 0x44
+/* 0x48: 64 bytes */
+#define YT921X_MIB_DATA_RX_OVERSIZE_ERR 0x4c
+
+#define YT921X_MIB_DATA_RX_DROPPED 0x50
+#define YT921X_MIB_DATA_TX_BROADCAST 0x54
+#define YT921X_MIB_DATA_TX_PAUSE 0x58
+#define YT921X_MIB_DATA_TX_MULTICAST 0x5c
+
+#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR 0x60
+#define YT921X_MIB_DATA_TX_PKT_SZ_64 0x64
+#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127 0x68
+#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255 0x6c
+
+#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511 0x70
+#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023 0x74
+#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518 0x78
+#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX 0x7c
+
+/* 0x80: unused */
+#define YT921X_MIB_DATA_TX_GOOD_BYTES 0x84
+/* 0x88: 64 bytes */
+#define YT921X_MIB_DATA_TX_COLLISION 0x8c
+
+#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION 0x90
+#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION 0x94
+#define YT921X_MIB_DATA_TX_SINGLE_COLLISION 0x98
+#define YT921X_MIB_DATA_TX_PKT 0x9c
+
+#define YT921X_MIB_DATA_TX_DEFERRED 0xa0
+#define YT921X_MIB_DATA_TX_LATE_COLLISION 0xa4
+#define YT921X_MIB_DATA_RX_OAM 0xa8
+#define YT921X_MIB_DATA_TX_OAM 0xac
+
+struct yt921x_mib_stats {
+ u64_stats_t rx_broadcast;
+ u64_stats_t rx_pause;
+ u64_stats_t rx_multicast;
+ u64_stats_t rx_crc_errors;
+
+ u64_stats_t rx_alignment_errors;
+ u64_stats_t rx_undersize_errors;
+ u64_stats_t rx_fragment_errors;
+ u64_stats_t rx_64byte;
+
+ u64_stats_t rx_65_127byte;
+ u64_stats_t rx_128_255byte;
+ u64_stats_t rx_256_511byte;
+ u64_stats_t rx_512_1023byte;
+
+ u64_stats_t rx_1024_1518byte;
+ u64_stats_t rx_jumbo;
+ u64_stats_t rx_good_bytes;
+
+ u64_stats_t rx_bad_bytes;
+ u64_stats_t rx_oversize_errors;
+
+ u64_stats_t rx_dropped;
+ u64_stats_t tx_broadcast;
+ u64_stats_t tx_pause;
+ u64_stats_t tx_multicast;
+
+ u64_stats_t tx_undersize_errors;
+ u64_stats_t tx_64byte;
+ u64_stats_t tx_65_127byte;
+ u64_stats_t tx_128_255byte;
+
+ u64_stats_t tx_256_511byte;
+ u64_stats_t tx_512_1023byte;
+ u64_stats_t tx_1024_1518byte;
+ u64_stats_t tx_jumbo;
+
+ u64_stats_t tx_good_bytes;
+ u64_stats_t tx_collisions;
+
+ u64_stats_t tx_aborted_errors;
+ u64_stats_t tx_multiple_collisions;
+ u64_stats_t tx_single_collisions;
+ u64_stats_t tx_good;
+
+ u64_stats_t tx_deferred;
+ u64_stats_t tx_late_collisions;
+ u64_stats_t rx_oam;
+ u64_stats_t tx_oam;
+};
+
+#define YT921X_MIB_NUM (sizeof(struct yt921x_mib_stats) / sizeof(u64_stats_t))
+
+struct yt921x_mib {
+ struct yt921x_port *port;
+
+ struct delayed_work work;
+ struct u64_stats_sync syncp;
+ /* protected by syncp OR priv->reg_lock */
+ struct yt921x_mib_stats stats;
+ u64_stats_t rx_frames;
+ u64_stats_t tx_frames;
+ /* protected by priv->reg_lock */
+ u64 data[YT921X_MIB_NUM];
+};
+
+void yt921x_mib_poll(struct work_struct *work);
+void
+yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+ uint8_t *data);
+void
+yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);
+int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset);
+void
+yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_mac_stats *mac_stats);
+void
+yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_ctrl_stats *ctrl_stats);
+void
+yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges);
+void
+yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
+ struct rtnl_link_stats64 *stats);
+void
+yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
+ struct ethtool_pause_stats *pause_stats);
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/smi.c b/drivers/net/dsa/motorcomm/smi.c
index bf3adfd64165a..27086602997bb 100644
--- a/drivers/net/dsa/motorcomm/smi.c
+++ b/drivers/net/dsa/motorcomm/smi.c
@@ -63,6 +63,36 @@ int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)
return yt921x_reg_write(priv, reg, u);
}
+/* Reliably read a 64bit counter */
+int yt921x_counter_read(struct yt921x_priv *priv, u32 reg, u64 *valp)
+{
+ u32 old_lo;
+ int res;
+ u32 hi;
+ u32 lo;
+
+ res = yt921x_reg_read(priv, reg, &old_lo);
+ if (res)
+ return res;
+
+ for (int i = 0; i < 16; i++) {
+ res = yt921x_reg_read(priv, reg + 4, &hi);
+ if (res)
+ return res;
+ res = yt921x_reg_read(priv, reg, &lo);
+ if (res)
+ return res;
+
+ if (lo >= old_lo) {
+ *valp = ((u64)hi << 32) | lo;
+ return 0;
+ }
+ old_lo = lo;
+ }
+
+ return -ETIMEDOUT;
+}
+
static int
yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
unsigned int num_regs)
diff --git a/drivers/net/dsa/motorcomm/smi.h b/drivers/net/dsa/motorcomm/smi.h
index 212e20f71d806..d34240548af7e 100644
--- a/drivers/net/dsa/motorcomm/smi.h
+++ b/drivers/net/dsa/motorcomm/smi.h
@@ -35,6 +35,8 @@ yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);
}
+int yt921x_counter_read(struct yt921x_priv *priv, u32 reg, u64 *valp);
+
/* Some multi-word registers, like VLANn_CTRL, should be treated as a single
* long register. More specifically, writes to parts of its words won't become
* visible, until the last word is written.
|
| 3/2 | 2026/09/05 00:17 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 23283 output: 25 thoughts: 231 Thoughts: **Assessing the Fuzzing Potential of This Patch Series** Okay, so the question is whether this patch series is a good candidate for syzkaller fuzzing. My immediate thought is, let's break this down. First, I need to analyze the changes themselves. It looks like we're touching files within `drivers/net/dsa/motorcomm/`. Specifically, it looks like these files are being modified: * `drivers/net/dsa/motorcomm/Makefile` * `drivers/net/dsa/motorcomm/chip.c` * `drivers/net/dsa/motorcomm/chip.h` * `drivers/net/dsa/motorcomm/mib.c` * `drivers/net/dsa/motorcomm/mib.h` * `drivers/net/dsa/motorcomm/smi.c` * `drivers/net/dsa/motorcomm/smi.h` Alright, the location gives me a good start. I can see that this is a DSA (Distributed Switch Architecture) switch driver, specifically for the YT921X, which is a Motorcomm device. My next step is to understand the hardware setup. I need to figure out how this particular switch is probed and what hardware or bus it's bound to. Is it I2C, SPI, MDIO, something else? Knowing this will significantly inform whether it's viable for syzkaller. I'll probably use `grepper` or scan through the relevant files to figure out the probing mechanism and understand its dependencies. |
| 4/2 | 2026/09/05 00:17 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_NET_DSA_YT921X"
}Results: {
"Output": "drivers/net/dsa/motorcomm/Makefile-1-# SPDX-License-Identifier: GPL-2.0\ndrivers/net/dsa/motorcomm/Makefile:2:obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o\ndrivers/net/dsa/motorcomm/Makefile-3-yt921x-objs := chip.o\ndrivers/net/dsa/motorcomm/Makefile:4:yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o\ndrivers/net/dsa/motorcomm/Makefile-5-yt921x-objs += mib.o\n--\ndrivers/net/dsa/motorcomm/chip.c=3897=static void yt921x_dsa_teardown(struct dsa_switch *ds)\n--\ndrivers/net/dsa/motorcomm/chip.c-3900-\ndrivers/net/dsa/motorcomm/chip.c:3901:#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/chip.c-3902-\tyt921x_leds_remove(priv);\n--\ndrivers/net/dsa/motorcomm/chip.c=4216=static int yt921x_dsa_setup(struct dsa_switch *ds)\n--\ndrivers/net/dsa/motorcomm/chip.c-4261-\ndrivers/net/dsa/motorcomm/chip.c:4262:#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/chip.c-4263-\tres = yt921x_leds_setup(priv);\n--\ndrivers/net/dsa/motorcomm/chip.h=821=struct yt921x_port {\n--\ndrivers/net/dsa/motorcomm/chip.h-826-\ndrivers/net/dsa/motorcomm/chip.h:827:#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/chip.h-828-\tunsigned char led_duty;\n--\ndrivers/net/dsa/motorcomm/leds.h=96=struct yt921x_led {\n--\ndrivers/net/dsa/motorcomm/leds.h-101-\ndrivers/net/dsa/motorcomm/leds.h:102:#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/leds.h-103-\n"
} |
| 5/2 | 2026/09/05 00:17 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 24568 output: 35 thoughts: 30 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating Kconfig Files for DSA Driver Configuration** Right, so I need to find the configuration options for a network driver, specifically within the Distributed Switch Architecture (DSA) framework. My initial thought is to zero in on the relevant Kconfig files. Considering the potential vendor-specific nature of the driver, I'm going to check the `drivers/net/dsa/motorcomm/Kconfig` file first. MotorComm is a vendor, and it's likely they have their own specific configurations and dependencies defined within a dedicated subdirectory. If, however, the driver is a more generic DSA implementation, or if the MotorComm-specific file is missing or doesn't yield the information I need, I'll then immediately check the main DSA Kconfig file, which is `drivers/net/dsa/Kconfig`. That file should provide a more general overview of the DSA framework's configurable aspects and potentially point me to other relevant sub-configurations. Either way, this is the logical place to begin digging into the build system configuration. |
| 6/2 | 2026/09/05 00:18 | tool | grepper |
0mArgs:{
"Expression": "NET_DSA_YT921X",
"PathPrefix": "drivers/net/dsa"
}Results: {
"Output": "drivers/net/dsa/motorcomm/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\ndrivers/net/dsa/motorcomm/Kconfig:2:config NET_DSA_YT921X\ndrivers/net/dsa/motorcomm/Kconfig-3-\ttristate \"Motorcomm YT9215 ethernet switch chip support\"\n--\ndrivers/net/dsa/motorcomm/Kconfig-9-\ndrivers/net/dsa/motorcomm/Kconfig:10:config NET_DSA_YT921X_LEDS\ndrivers/net/dsa/motorcomm/Kconfig-11-\tbool \"LED support for Motorcomm YT9215\"\ndrivers/net/dsa/motorcomm/Kconfig-12-\tdefault y\ndrivers/net/dsa/motorcomm/Kconfig:13:\tdepends on NET_DSA_YT921X\ndrivers/net/dsa/motorcomm/Kconfig:14:\tdepends on LEDS_CLASS=y || LEDS_CLASS=NET_DSA_YT921X\ndrivers/net/dsa/motorcomm/Kconfig-15-\thelp\n--\ndrivers/net/dsa/motorcomm/Makefile-1-# SPDX-License-Identifier: GPL-2.0\ndrivers/net/dsa/motorcomm/Makefile:2:obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o\ndrivers/net/dsa/motorcomm/Makefile-3-yt921x-objs := chip.o\ndrivers/net/dsa/motorcomm/Makefile:4:yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o\ndrivers/net/dsa/motorcomm/Makefile-5-yt921x-objs += mib.o\n--\ndrivers/net/dsa/motorcomm/chip.c=3897=static void yt921x_dsa_teardown(struct dsa_switch *ds)\n--\ndrivers/net/dsa/motorcomm/chip.c-3900-\ndrivers/net/dsa/motorcomm/chip.c:3901:#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/chip.c-3902-\tyt921x_leds_remove(priv);\n--\ndrivers/net/dsa/motorcomm/chip.c=4216=static int yt921x_dsa_setup(struct dsa_switch *ds)\n--\ndrivers/net/dsa/motorcomm/chip.c-4261-\ndrivers/net/dsa/motorcomm/chip.c:4262:#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/chip.c-4263-\tres = yt921x_leds_setup(priv);\n--\ndrivers/net/dsa/motorcomm/chip.h=821=struct yt921x_port {\n--\ndrivers/net/dsa/motorcomm/chip.h-826-\ndrivers/net/dsa/motorcomm/chip.h:827:#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/chip.h-828-\tunsigned char led_duty;\n--\ndrivers/net/dsa/motorcomm/leds.h=96=struct yt921x_led {\n--\ndrivers/net/dsa/motorcomm/leds.h-101-\ndrivers/net/dsa/motorcomm/leds.h:102:#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/leds.h-103-\n"
} |
| 7/2 | 2026/09/05 00:18 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 25829 output: 38 thoughts: 39 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating Motorcomm DSA Driver Registration** Right, let's dive into this Motorcomm DSA driver situation. First, I need to verify how it's being handled at the Kconfig level – particularly the dependencies and build options in `drivers/net/dsa/motorcomm/Kconfig`. This helps me understand the overall configuration and if the driver is correctly enabled for the target platform. Then, the real meat of it: I'll jump into `drivers/net/dsa/motorcomm/chip.c`. I need to zero in on the probe function, the driver registration itself, and the associated module loading sequence. I need to make sure the probe is correctly identifying the hardware and binding to it. I want to see if they're using a proper device tree binding, or some other method, and check for any potential issues with resource allocation, interrupt handling, and clock management within the probe function. The registration part is critical; I'll check how it's linked into the core DSA framework, how the driver's operations are being hooked in, and any potential conflict points. This is where the magic happens – or where things can go wrong. I'm looking for robustness, good error handling, and a clean interface to the DSA core. Let's see what we've got. |
| 8/2 | 2026/09/05 00:18 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/Kconfig",
"FirstLine": 1,
"LineCount": 30
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\tconfig NET_DSA_YT921X\n 3:\t\ttristate \"Motorcomm YT9215 ethernet switch chip support\"\n 4:\t\tselect NET_DSA_TAG_YT921X\n 5:\t\tselect NET_IEEE8021Q_HELPERS if DCB\n 6:\t\thelp\n 7:\t\t This enables support for the Motorcomm YT9215 ethernet switch\n 8:\t\t chip.\n 9:\t\n 10:\tconfig NET_DSA_YT921X_LEDS\n 11:\t\tbool \"LED support for Motorcomm YT9215\"\n 12:\t\tdefault y\n 13:\t\tdepends on NET_DSA_YT921X\n 14:\t\tdepends on LEDS_CLASS=y || LEDS_CLASS=NET_DSA_YT921X\n 15:\t\thelp\n 16:\t\t This enables support for controlling the LEDs attached to the\n 17:\t\t Motorcomm YT9215 switch chips.\n"
} |
| 9/2 | 2026/09/05 00:18 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 26477 output: 42 thoughts: 51 Thoughts: Okay, here's my take on those thoughts, framed as my own: **Initial Assessment of Device Integration** Alright, so a new `yt921x` device... let's get down to brass tacks. First thing, my mind immediately jumps to how it interfaces. Is it riding the MDIO bus, is it I2C, or is it SPI? I need to quickly identify the connection protocol to know how I'm going to interact with it. Then, since this seems to be networking related, I'm thinking I should start digging into the motorcomm drivers within the networking directory, specifically looking at the `chip.c` file. Chances are, whatever architecture we're talking about has implemented it there. Might be a good place to start my deep dive and work backwards from there. |
| 10/2 | 2026/09/05 00:18 | tool | read-file |
0mError:file drivers/net/dsa/motorcomm/chip.c does not have line 4740, it has only 4456 lines Args: {
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4740,
"LineCount": 80
}Results: {
"error": "file drivers/net/dsa/motorcomm/chip.c does not have line 4740, it has only 4456 lines"
} |
| 11/2 | 2026/09/05 00:18 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 26788 output: 42 thoughts: 12 Thoughts: Here's my take on the process, imagining myself as an expert diving into `chip.c`: **Diving into the Deep End: The Tail End of `chip.c`** Alright, let's skip the preliminaries and get right to the meaty bits. I'm heading straight for the end of `chip.c`. No need to rehash the obvious; I'm assuming the groundwork and core functionality are already understood. This is where the magic, or the trouble, usually resides: the finalization routines, the interrupt handlers, the debug interfaces, or maybe just some clever optimization trickery. I'm looking for the "so what?" – what's the ultimate outcome of all this code? What are the edge cases being handled, or, more interestingly, the *un*handled ones? Are there any obvious resource leaks or concurrency issues just waiting to bite us? My experience tells me the end often ties everything together, reveals the true architecture, and exposes those little warts and workarounds that make a system truly fascinating. Time to start digging! |
| 12/2 | 2026/09/05 00:18 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4400,
"LineCount": 60
}Results: {
"Contents": "4400:\t\tmdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL);\n4401:\t\tif (!mdio)\n4402:\t\t\treturn -ENOMEM;\n4403:\t\n4404:\t\tmdio-\u003ebus = mdiodev-\u003ebus;\n4405:\t\tmdio-\u003eaddr = mdiodev-\u003eaddr;\n4406:\t\tmdio-\u003eswitchid = 0;\n4407:\t\n4408:\t\tmutex_init(\u0026priv-\u003ereg_lock);\n4409:\t\n4410:\t\tpriv-\u003ereg_ops = \u0026yt921x_reg_ops_mdio;\n4411:\t\tpriv-\u003ereg_ctx = mdio;\n4412:\t\n4413:\t\tfor (size_t i = 0; i \u003c ARRAY_SIZE(priv-\u003eports); i++) {\n4414:\t\t\tstruct yt921x_port *pp = \u0026priv-\u003eports[i];\n4415:\t\n4416:\t\t\tpp-\u003eindex = i;\n4417:\t\t}\n4418:\t\n4419:\t\tds = \u0026priv-\u003eds;\n4420:\t\tds-\u003edev = dev;\n4421:\t\tds-\u003eassisted_learning_on_cpu_port = true;\n4422:\t\tds-\u003edscp_prio_mapping_is_global = true;\n4423:\t\tds-\u003epriv = priv;\n4424:\t\tds-\u003eops = \u0026yt921x_dsa_switch_ops;\n4425:\t\tds-\u003eageing_time_min = 1 * 5000;\n4426:\t\tds-\u003eageing_time_max = U16_MAX * 5000;\n4427:\t\tds-\u003ephylink_mac_ops = \u0026yt921x_phylink_mac_ops;\n4428:\t\tds-\u003enum_lag_ids = YT921X_LAG_NUM;\n4429:\t\tds-\u003enum_ports = YT921X_PORT_NUM;\n4430:\t\n4431:\t\tmdiodev_set_drvdata(mdiodev, priv);\n4432:\t\n4433:\t\treturn dsa_register_switch(ds);\n4434:\t}\n4435:\t\n4436:\tstatic const struct of_device_id yt921x_of_match[] = {\n4437:\t\t{ .compatible = \"motorcomm,yt9215\" },\n4438:\t\t{}\n4439:\t};\n4440:\tMODULE_DEVICE_TABLE(of, yt921x_of_match);\n4441:\t\n4442:\tstatic struct mdio_driver yt921x_mdio_driver = {\n4443:\t\t.probe = yt921x_mdio_probe,\n4444:\t\t.remove = yt921x_mdio_remove,\n4445:\t\t.shutdown = yt921x_mdio_shutdown,\n4446:\t\t.mdiodrv.driver = {\n4447:\t\t\t.name = YT921X_NAME,\n4448:\t\t\t.of_match_table = yt921x_of_match,\n4449:\t\t},\n4450:\t};\n4451:\t\n4452:\tmdio_module_driver(yt921x_mdio_driver);\n4453:\t\n4454:\tMODULE_AUTHOR(\"David Yang \u003cmmyangfl@gmail.com\u003e\");\n4455:\tMODULE_DESCRIPTION(\"Driver for Motorcomm YT921x Switch\");\n4456:\tMODULE_LICENSE(\"GPL\");\n"
} |
| 13/2 | 2026/09/05 00:18 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 28025 output: 22 thoughts: 290 Thoughts: **Reachability Analysis: Can `yt921x_mdio_driver` Be Fuzzed?** Okay, let's break this down. My goal is to determine if I can fuzz the `yt921x_mdio_driver`. It's an MDIO device driver, a `mdio_driver`, and it's matched via Device Tree with the `yt921x_of_match` entry, which tells me it's compatible with "motorcomm,yt9215." The key question is whether I can instantiate and fuzz an MDIO switch driver like this in a standard QEMU/GCE syzkaller environment. Can I do this without physically interacting with the hardware? I need to apply the "WorthFuzzing=false" criteria to rule it out. The rule specifically mentions structurally unreachable hardware. It flags things like vendor-specific PCIe switches, SmartNICs, or GPU drivers that require physical ASICs or PCIe cards not emulated in standard QEMU. Virtual buses, like virtio, USB gadget, loopback, netlink, binder, and sockets, are exceptions. The big question now: can MDIO devices or DSA switches, in general, be instantiated without an actual physical OF device tree or a physical MDIO bus? Or is there a way to emulate or instantiate them from userspace? Perhaps something using netlink, a "fake mdio," or some kind of mock implementation? I really need to check other DSA drivers within the kernel and see if I can find some clues in the syzkaller descriptions. I need to know if there's any precedent here. |
| 14/2 | 2026/09/05 00:18 | tool | grepper |
0mArgs:{
"Expression": "motorcomm,yt9215"
}Results: {
"Output": "Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml=27=properties:\nDocumentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml-28- compatible:\nDocumentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml:29: const: motorcomm,yt9215\nDocumentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml-30-\n--\nDocumentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml=62=examples:\n--\nDocumentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml-71- switch@1d {\nDocumentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml:72: compatible = \"motorcomm,yt9215\";\nDocumentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml-73- /* default 0x1d, alternate 0x0 */\n--\ndrivers/net/dsa/motorcomm/chip.c=4436=static const struct of_device_id yt921x_of_match[] = {\ndrivers/net/dsa/motorcomm/chip.c:4437:\t{ .compatible = \"motorcomm,yt9215\" },\ndrivers/net/dsa/motorcomm/chip.c-4438-\t{}\n"
} |
| 15/2 | 2026/09/05 00:18 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 29083 output: 20 thoughts: 58 Thoughts: **MDIO Device Creation Investigation** Okay, so I need to figure out how MDIO devices are actually brought into existence within the kernel. My first thought is to determine if we can sidestep the typical hardware-centric approach – do we have the ability to create MDIO devices purely through sysfs, ioctl calls, or netlink mechanisms? Can a user space application instantiate these devices, much like what we see with I2C (using the `/dev/new_device` approach)? The fundamental question is whether MDIO switch drivers are *required*, and what role physical or OF hardware plays, or if there's a more software-defined path. I need to understand the full spectrum of instantiation methods to get a complete picture. |
| 16/2 | 2026/09/05 00:18 | tool | grepper |
0mArgs:{
"Expression": "mdio_device_create"
}Results: {
"Output": "drivers/net/dsa/dsa_loop.c=446=static int __init dsa_loop_create_switch_mdiodev(void)\n--\ndrivers/net/dsa/dsa_loop.c-466-\ndrivers/net/dsa/dsa_loop.c:467:\tswitch_mdiodev = mdio_device_create(bus, 31);\ndrivers/net/dsa/dsa_loop.c-468-\tif (IS_ERR(switch_mdiodev))\n--\ndrivers/net/mdio/of_mdio.c=52=static int of_mdiobus_register_device(struct mii_bus *mdio,\n--\ndrivers/net/mdio/of_mdio.c-58-\ndrivers/net/mdio/of_mdio.c:59:\tmdiodev = mdio_device_create(mdio, addr);\ndrivers/net/mdio/of_mdio.c-60-\tif (IS_ERR(mdiodev))\n--\ndrivers/net/pcs/pcs-lynx.c=310=struct phylink_pcs *lynx_pcs_create_mdiodev(struct mii_bus *bus, int addr)\n--\ndrivers/net/pcs/pcs-lynx.c-314-\ndrivers/net/pcs/pcs-lynx.c:315:\tmdio = mdio_device_create(bus, addr);\ndrivers/net/pcs/pcs-lynx.c-316-\tif (IS_ERR(mdio))\n--\ndrivers/net/pcs/pcs-xpcs-plat.c=337=static int xpcs_plat_init_dev(struct dw_xpcs_plat *pxpcs)\n--\ndrivers/net/pcs/pcs-xpcs-plat.c-343-\t/* There is a single memory-mapped DW XPCS device */\ndrivers/net/pcs/pcs-xpcs-plat.c:344:\tmdiodev = mdio_device_create(pxpcs-\u003ebus, 0);\ndrivers/net/pcs/pcs-xpcs-plat.c-345-\tif (IS_ERR(mdiodev))\n--\ndrivers/net/pcs/pcs-xpcs.c=1621=struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr)\n--\ndrivers/net/pcs/pcs-xpcs.c-1625-\ndrivers/net/pcs/pcs-xpcs.c:1626:\tmdiodev = mdio_device_create(bus, addr);\ndrivers/net/pcs/pcs-xpcs.c-1627-\tif (IS_ERR(mdiodev))\n--\ndrivers/net/phy/air_en8811h.c=1074=static int an8811hb_probe(struct phy_device *phydev)\n--\ndrivers/net/phy/air_en8811h.c-1092-\t */\ndrivers/net/phy/air_en8811h.c:1093:\tmdiodev = mdio_device_create(phydev-\u003emdio.bus,\ndrivers/net/phy/air_en8811h.c-1094-\t\t\t\t phydev-\u003emdio.addr + EN8811H_PBUS_ADDR_OFFS);\n--\ndrivers/net/phy/mdio_device.c=113=static void mdio_device_release(struct device *dev)\n--\ndrivers/net/phy/mdio_device.c-118-\ndrivers/net/phy/mdio_device.c:119:struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)\ndrivers/net/phy/mdio_device.c-120-{\n--\ndrivers/net/phy/mdio_device.c-142-}\ndrivers/net/phy/mdio_device.c:143:EXPORT_SYMBOL(mdio_device_create);\ndrivers/net/phy/mdio_device.c-144-\n--\ninclude/linux/mdio.h=94=void mdio_device_free(struct mdio_device *mdiodev);\ninclude/linux/mdio.h:95:struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr);\ninclude/linux/mdio.h-96-int mdio_device_register(struct mdio_device *mdiodev);\n"
} |
| 17/2 | 2026/09/05 00:18 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 30437 output: 99 thoughts: 926 Thoughts: **Analysis of Motorcomm YT9215 Driver Patch for Fuzzing Suitability** Okay, let's break this down. My initial thought is to determine if this patch, which seems to focus on the Motorcomm YT9215 Ethernet switch driver, is even *fuzzable* within the constraints of our standard fuzzing environment. The key here is reachability. The `of_mdiobus_register_device` function call seems to be the entry point when dealing with OpenFirmware Device Tree nodes, and I need to figure out if the `yt921x_mdio_driver` is triggered in any other way. Quickly checking, the driver uses an `of_match_table` ( `yt921x_of_match`), but no `id_table` or anything else. Now, my experience tells me that Motorcomm YT9215 is a *physical* switch chip, an ASIC controlled via MDIO. It's usually found on embedded systems, specifically ones using device trees to describe the hardware. I need to confirm whether QEMU/GCE, our standard fuzzing platforms, emulate this. I know they don't; it's a dedicated piece of hardware. Right, time to consult the reachability guidelines. This is where it gets crucial. The guidelines clearly state that fuzzing can *only* discover bugs in code that's executable in standard virtualized environments. Code that's structurally unreachable should *not* be fuzzed. The "negative criteria" section further clarifies this with examples. Vendor-specific hardware like this, requiring physical cards or ASICs, are explicitly excluded. The guidelines specifically call out SmartNICs, GPU drivers and PCIe switches. Now, let's look at the patch itself. It's modifying files within `drivers/net/dsa/motorcomm/`, which is the Motorcomm YT9215 Ethernet switch driver. This is a very strong indicator. This confirms my suspicion that it requires physical hardware to function. It touches the Makefile, chip.c, chip.h, mib.c, mib.h, smi.c and smi.h. So the driver's code is not reachable in standard QEMU/GCE environment, I need to confirm I'm not missing anything. I need to be *absolutely* sure. A careful diff confirms this: the changes add a new MIB handling, but everything is contained within that specific driver directory. It's moving MIB handling to mib.c/h, adding some new `yt921x_counter_read` functions. All within the hardware switch. Therefore, because this driver requires a physical MDIO-attached Ethernet switch ASIC, which is not emulated, the code is structurally unreachable within our standard fuzzing setup. I'm confident in saying that this patch *should not* be fuzzed. My decision: * `WorthFuzzing: false` * `FocusSymbols: []` * `EnableConfigs: []` The reasoning here is solid: The patch series modifies code exclusively within the Motorcomm YT9215 DSA switch driver, a hardware driver for an external MDIO-attached Ethernet switch ASIC. Standard fuzzing environments, QEMU and GCE, do not emulate this hardware, making the modified code structurally unreachable. Let's call `set-results`. |
| 18/2 | 2026/09/05 00:18 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch series only modifies the Motorcomm YT9215 DSA Ethernet switch driver (drivers/net/dsa/motorcomm/), which interacts with a specific physical MDIO-connected switch ASIC. This hardware is not emulated in standard QEMU/GCE fuzzing environments, making the code structurally unreachable during fuzzing.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch series only modifies the Motorcomm YT9215 DSA Ethernet switch driver (drivers/net/dsa/motorcomm/), which interacts with a specific physical MDIO-connected switch ASIC. This hardware is not emulated in standard QEMU/GCE fuzzing environments, making the code structurally unreachable during fuzzing.",
"WorthFuzzing": false
} |
| 19/1 | 2026/09/05 00:18 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|