* [PATCH net-next v8 0/2] net: sfp: extend SMBus support
@ 2026-05-16 13:54 Jonas Jelonek
2026-05-16 13:54 ` [PATCH net-next v8 1/2] net: sfp: apply I2C adapter quirks to limit block size Jonas Jelonek
2026-05-16 13:54 ` [PATCH net-next v8 2/2] net: sfp: extend SMBus support Jonas Jelonek
0 siblings, 2 replies; 6+ messages in thread
From: Jonas Jelonek @ 2026-05-16 13:54 UTC (permalink / raw)
To: Russell King, Andrew Lunn, Heiner Kallweit, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Maxime Chevallier
Cc: netdev, linux-kernel, Bjørn Mork, Simon Horman,
Jonas Jelonek
Today, the SFP driver only drives I2C adapters that advertise full
I2C_FUNC_I2C, or SMBus-only adapters via single-byte transfers (with
hwmon disabled). Several SoCs ship I2C/SMBus-only controllers that
support more than just byte access -- e.g. word and I2C block -- and
have SFP cages wired to them. Today, those adapters either work
poorly or not at all.
This series teaches the SFP driver to use the larger SMBus access
modes when the adapter advertises them, and along the way starts
honoring i2c_adapter quirks on read/write length so adapters that
cap below the SFP block size are handled correctly. Patch 1 is a
small prep doing only the quirks handling; patch 2 extends the
SMBus path itself.
Capability matrix supported by patch 2:
- BYTE only: single-byte access (unchanged).
- BYTE + WORD: word for >=2-byte chunks, byte tail.
- I2C_BLOCK present: block as the universal transport.
- WORD only (no BYTE/BLOCK): accepted with WARN_ONCE; works for
even-length transfers, odd-length
transfers will error at xfer time.
Adapters with asymmetric R/W capabilities (e.g. only READ_I2C_BLOCK
without WRITE_I2C_BLOCK) remain functionally correct but use the
worse-supported direction's max for both directions, since
i2c_max_block_size is a single field. No mainline I2C driver was
seen advertising such asymmetry; per-direction sizes can be added
later if needed.
---
v7 -> v8:
- avoid leaking uninitialized memory on short reads by
zero-initializing i2c_smbus_data variable (Simon)
- theoretical issue without practical impact raised at [1]
not addressed but acknowledged at [2]
v7: https://lore.kernel.org/netdev/20260507093301.1144740-1-jelonek.jonas@gmail.com/
v6 -> v7:
- use i2c_block_size instead of i2c_max_block_size (Maxime)
- move WARN_ONCE into 'else if ()' (Maxime)
- reword comments
- included Maxime's Reviewed-by for patch 1
v6: https://lore.kernel.org/netdev/20260505200647.1125311-1-jelonek.jonas@gmail.com/
v5 -> v6:
- Split adapter-quirks handling into a separate prep patch (1/2).
- Use I2C_SMBUS_I2C_BLOCK_DATA in the block-write branch (was
I2C_SMBUS_WORD_DATA), so block writes actually transfer this_len
bytes (also flagged by Jakub's AI bot review).
- In sfp_smbus_read/write, check i2c_smbus_xfer() return before
copying smbus_data into the caller's buffer.
- Use I2C_BLOCK as the universal transport when available (carries
any length 1..32); drop the this_len > 2 guard on the block
branches.
- Broaden the SMBus gate to also accept BLOCK-only adapters
(Russell).
- Accept word-only adapters with WARN_ONCE rather than rejecting
them (Andrew).
- Add a short comment in sfp_i2c_configure() explaining the access
hierarchy (Maxime).
- Use the all-bits-set form via i2c_check_functionality() for the
composite I2C_FUNC_SMBUS_* checks (Russell).
v5: https://lore.kernel.org/netdev/20260116113105.244592-1-jelonek.jonas@gmail.com/
v4 -> v5:
- made a more general approach, also covering word access
v4: https://lore.kernel.org/netdev/20260109101321.2804-1-jelonek.jonas@gmail.com/
v3 -> v4:
- fix formal issues
v3: https://lore.kernel.org/netdev/20260105161242.578487-1-jelonek.jonas@gmail.com/
v2 -> v3:
- fix previous attempt of v2 to fix return value
v2: https://lore.kernel.org/netdev/20260105154653.575397-1-jelonek.jonas@gmail.com/
v1 -> v2:
- return number of written bytes instead of zero
v1: https://lore.kernel.org/netdev/20251228213331.472887-1-jelonek.jonas@gmail.com/
[1] https://lore.kernel.org/netdev/20260510164726.1401317-1-horms@kernel.org/
[2] https://lore.kernel.org/netdev/5129a58d-8852-4395-85e1-8991934810b8@gmail.com/
---
Jonas Jelonek (2):
net: sfp: apply I2C adapter quirks to limit block size
net: sfp: extend SMBus support
drivers/net/phy/sfp.c | 146 +++++++++++++++++++++++++++++++++---------
1 file changed, 117 insertions(+), 29 deletions(-)
base-commit: 822d4a8e390a08ccfaf2abb347ae670b230b196f
--
2.51.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH net-next v8 1/2] net: sfp: apply I2C adapter quirks to limit block size 2026-05-16 13:54 [PATCH net-next v8 0/2] net: sfp: extend SMBus support Jonas Jelonek @ 2026-05-16 13:54 ` Jonas Jelonek 2026-05-20 23:42 ` Jakub Kicinski 2026-05-16 13:54 ` [PATCH net-next v8 2/2] net: sfp: extend SMBus support Jonas Jelonek 1 sibling, 1 reply; 6+ messages in thread From: Jonas Jelonek @ 2026-05-16 13:54 UTC (permalink / raw) To: Russell King, Andrew Lunn, Heiner Kallweit, David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Maxime Chevallier Cc: netdev, linux-kernel, Bjørn Mork, Simon Horman, Jonas Jelonek The SFP driver assumes all I2C adapters support reading and writing the pre-defined block size SFP_EEPROM_BLOCK_SIZE of 16 bytes. This constant was probably chosen based on good guesses and known limitations of a range of I2C adapters and SFP modules. However, I2C adapters may even support less and usually need to specify this via I2C quirks. Theoretically, such an adapter may provide full functionality but only support a read and write length of e.g. 8 bytes. Currently, the SFP driver doesn't account for that. Add handling for I2C quirks in SFP I2C configuration taking the fields max_read_len and max_write_len in struct i2c_adapter_quirks into account to further limit the maximum block size if needed. Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com> --- drivers/net/phy/sfp.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index bd970f753beb..e58e29a1e8d2 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -807,21 +807,29 @@ static int sfp_smbus_byte_write(struct sfp *sfp, bool a2, u8 dev_addr, static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) { + size_t max_block_size; + sfp->i2c = i2c; if (i2c_check_functionality(i2c, I2C_FUNC_I2C)) { sfp->read = sfp_i2c_read; sfp->write = sfp_i2c_write; - sfp->i2c_max_block_size = SFP_EEPROM_BLOCK_SIZE; + max_block_size = SFP_EEPROM_BLOCK_SIZE; } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) { sfp->read = sfp_smbus_byte_read; sfp->write = sfp_smbus_byte_write; - sfp->i2c_max_block_size = 1; + max_block_size = 1; } else { sfp->i2c = NULL; return -EINVAL; } + if (i2c->quirks && i2c->quirks->max_read_len) + max_block_size = min(max_block_size, i2c->quirks->max_read_len); + if (i2c->quirks && i2c->quirks->max_write_len) + max_block_size = min(max_block_size, i2c->quirks->max_write_len); + + sfp->i2c_max_block_size = max_block_size; return 0; } -- 2.51.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v8 1/2] net: sfp: apply I2C adapter quirks to limit block size 2026-05-16 13:54 ` [PATCH net-next v8 1/2] net: sfp: apply I2C adapter quirks to limit block size Jonas Jelonek @ 2026-05-20 23:42 ` Jakub Kicinski 0 siblings, 0 replies; 6+ messages in thread From: Jakub Kicinski @ 2026-05-20 23:42 UTC (permalink / raw) To: jelonek.jonas Cc: Jakub Kicinski, linux, andrew, hkallweit1, davem, edumazet, pabeni, maxime.chevallier, netdev, linux-kernel, bjorn, horms This is an AI-generated review of your patch. The human sending this email has considered the AI review valid, or at least plausible. --- net: sfp: apply I2C adapter quirks to limit block size This patch modifies the SFP driver to respect I2C adapter quirks when configuring the I2C block size. It restricts the maximum block size based on the adapter's max_read_len and max_write_len fields. > diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c > index bd970f753beb6..e58e29a1e8d27 100644 > --- a/drivers/net/phy/sfp.c > +++ b/drivers/net/phy/sfp.c > @@ -807,21 +807,29 @@ static int sfp_smbus_byte_write(struct sfp *sfp, bool a2, u8 dev_addr, > > static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) > { [ ... ] > } else { > sfp->i2c = NULL; > return -EINVAL; > } > > + if (i2c->quirks && i2c->quirks->max_read_len) > + max_block_size = min(max_block_size, i2c->quirks->max_read_len); > + if (i2c->quirks && i2c->quirks->max_write_len) > + max_block_size = min(max_block_size, i2c->quirks->max_write_len); Does this limit the read block size incorrectly based on the write limit? The write portion of an I2C read transfer is always 1 byte for the EEPROM offset, regardless of the read block size. If an adapter has a max_write_len of 1, max_block_size will drop to 1. Since sfp_hwmon_probe() checks if sfp->i2c_max_block_size < 2 to ensure atomic 16-bit reads, does this inadvertently disable hardware monitoring for adapters that have a small write limit but sufficient read capabilities? Also, for actual write transfers, sfp_i2c_write() prepends a 1-byte register address to the payload. If max_block_size is set exactly to max_write_len, won't the resulting message length be 1 + max_write_len, which still exceeds the adapter's quirk limit? Additionally, looking at sfp_i2c_write(), it does not appear to loop or chunk the buffer using sfp->i2c_max_block_size. Does this mean the new limit will be ignored during writes, and the function will still attempt to transfer the entire buffer in a single operation? > + > + sfp->i2c_max_block_size = max_block_size; > return 0; > } -- pw-bot: cr ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH net-next v8 2/2] net: sfp: extend SMBus support 2026-05-16 13:54 [PATCH net-next v8 0/2] net: sfp: extend SMBus support Jonas Jelonek 2026-05-16 13:54 ` [PATCH net-next v8 1/2] net: sfp: apply I2C adapter quirks to limit block size Jonas Jelonek @ 2026-05-16 13:54 ` Jonas Jelonek 2026-05-19 8:05 ` Maxime Chevallier 2026-05-20 23:42 ` Jakub Kicinski 1 sibling, 2 replies; 6+ messages in thread From: Jonas Jelonek @ 2026-05-16 13:54 UTC (permalink / raw) To: Russell King, Andrew Lunn, Heiner Kallweit, David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Maxime Chevallier Cc: netdev, linux-kernel, Bjørn Mork, Simon Horman, Jonas Jelonek Commit 7662abf4db94 ("net: phy: sfp: Add support for SMBus module access") added SMBus access for SFP modules, but limited it to single-byte transfers. As a side effect, hwmon is disabled (16-bit reads cannot be guaranteed atomic) and a warning is printed. Many SMBus-only I2C controllers in the wild support more than just byte access, and SFP cages are often wired to such controllers rather than to a full-featured I2C controller -- e.g. the SMBus controllers in the Realtek longan and mango SoCs, which advertise word access and I2C block reads. Today, they cannot drive an SFP at all without falling back to the byte-only path. Extend sfp_smbus_read()/sfp_smbus_write() so that, in addition to the existing byte access, they also use SMBus word access and SMBus I2C block access whenever the adapter advertises them. Both directions are handled in a single read and a single write helper that pick the largest supported transfer per chunk and fall back as needed. I2C-block is preferred unconditionally when available: the protocol carries any length 1..32, so it can serve every chunk -- including the 1- and 2-byte tails -- without help from word or byte access. Note that this requires I2C_FUNC_SMBUS_I2C_BLOCK, which reads a caller-specified number of bytes. This deviates from the official SMBus Block Read (length is supplied by the slave) but is widely supported by Linux I2C controllers/drivers. Capability matrix this implementation supports: - BYTE only: works (unchanged behaviour); 1-byte xfers, hwmon disabled. - BYTE + WORD: word for >=2-byte chunks, byte for trailing odd byte. - I2C_BLOCK present (with or without BYTE/WORD): block as the universal transport for every chunk. - WORD only (no BYTE/BLOCK): accepted with WARN_ONCE. Even-length transfers work; odd-length transfers (e.g. the 3-byte cotsworks fixup write) hit the BYTE branch which the adapter does not implement, so the xfer returns an error and the operation is aborted. No mainline I2C driver was found to advertise WORD without BYTE; the warning lets us learn about it if it ever shows up. Adapters with asymmetric R/W capabilities (e.g. only READ_I2C_BLOCK but not WRITE_I2C_BLOCK) remain functionally correct -- the per-iteration fallback uses the direction-specific bits -- but the shared i2c_max_block_size is sized by the all-bits-set check, so a transfer in the better-supported direction is not upgraded. None of the mainline I2C bus drivers surveyed during review advertise such asymmetry; promoting i2c_max_block_size to per-direction sizes can be revisited if needed. Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com> --- drivers/net/phy/sfp.c | 136 +++++++++++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 28 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index e58e29a1e8d2..85eb13cf5df2 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -14,6 +14,7 @@ #include <linux/platform_device.h> #include <linux/rtnetlink.h> #include <linux/slab.h> +#include <linux/unaligned.h> #include <linux/workqueue.h> #include "sfp.h" @@ -756,50 +757,110 @@ static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, return ret == ARRAY_SIZE(msgs) ? len : 0; } -static int sfp_smbus_byte_read(struct sfp *sfp, bool a2, u8 dev_addr, - void *buf, size_t len) +static int sfp_smbus_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, + size_t len) { - union i2c_smbus_data smbus_data; + union i2c_smbus_data smbus_data = {0}; u8 bus_addr = a2 ? 0x51 : 0x50; + size_t this_len, transferred; + u32 functionality; u8 *data = buf; int ret; - while (len) { - ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, - I2C_SMBUS_READ, dev_addr, - I2C_SMBUS_BYTE_DATA, &smbus_data); - if (ret < 0) - return ret; + functionality = i2c_get_functionality(sfp->i2c); - *data = smbus_data.byte; + while (len) { + this_len = min(len, sfp->i2c_block_size); + + if (functionality & I2C_FUNC_SMBUS_READ_I2C_BLOCK) { + smbus_data.block[0] = this_len; + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_READ, dev_addr, + I2C_SMBUS_I2C_BLOCK_DATA, &smbus_data); + if (ret < 0) + return ret; + + memcpy(data, &smbus_data.block[1], this_len); + transferred = this_len; + } else if (this_len >= 2 && + (functionality & I2C_FUNC_SMBUS_READ_WORD_DATA)) { + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_READ, dev_addr, + I2C_SMBUS_WORD_DATA, &smbus_data); + if (ret < 0) + return ret; + + put_unaligned_le16(smbus_data.word, data); + transferred = 2; + } else { + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_READ, dev_addr, + I2C_SMBUS_BYTE_DATA, &smbus_data); + if (ret < 0) + return ret; + + *data = smbus_data.byte; + transferred = 1; + } - len--; - data++; - dev_addr++; + data += transferred; + len -= transferred; + dev_addr += transferred; } return data - (u8 *)buf; } -static int sfp_smbus_byte_write(struct sfp *sfp, bool a2, u8 dev_addr, - void *buf, size_t len) +static int sfp_smbus_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, + size_t len) { union i2c_smbus_data smbus_data; u8 bus_addr = a2 ? 0x51 : 0x50; + size_t this_len, transferred; + u32 functionality; u8 *data = buf; int ret; + functionality = i2c_get_functionality(sfp->i2c); + while (len) { - smbus_data.byte = *data; - ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, - I2C_SMBUS_WRITE, dev_addr, - I2C_SMBUS_BYTE_DATA, &smbus_data); - if (ret) - return ret; + this_len = min(len, sfp->i2c_block_size); + + if (functionality & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) { + smbus_data.block[0] = this_len; + memcpy(&smbus_data.block[1], data, this_len); + + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_WRITE, dev_addr, + I2C_SMBUS_I2C_BLOCK_DATA, &smbus_data); + if (ret < 0) + return ret; + + transferred = this_len; + } else if (this_len >= 2 && + (functionality & I2C_FUNC_SMBUS_WRITE_WORD_DATA)) { + smbus_data.word = get_unaligned_le16(data); + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_WRITE, dev_addr, + I2C_SMBUS_WORD_DATA, &smbus_data); + if (ret < 0) + return ret; + + transferred = 2; + } else { + smbus_data.byte = *data; + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_WRITE, dev_addr, + I2C_SMBUS_BYTE_DATA, &smbus_data); + if (ret < 0) + return ret; + + transferred = 1; + } - len--; - data++; - dev_addr++; + data += transferred; + len -= transferred; + dev_addr += transferred; } return data - (u8 *)buf; @@ -815,10 +876,29 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) sfp->read = sfp_i2c_read; sfp->write = sfp_i2c_write; max_block_size = SFP_EEPROM_BLOCK_SIZE; - } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) { - sfp->read = sfp_smbus_byte_read; - sfp->write = sfp_smbus_byte_write; - max_block_size = 1; + } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA) || + i2c_check_functionality(i2c, I2C_FUNC_SMBUS_I2C_BLOCK)) { + /* Either protocol alone covers any length: I2C-block carries + * 1..32 bytes per xfer, byte iterates one byte at a time. + */ + sfp->read = sfp_smbus_read; + sfp->write = sfp_smbus_write; + + if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_I2C_BLOCK)) + max_block_size = SFP_EEPROM_BLOCK_SIZE; + else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_WORD_DATA)) + max_block_size = 2; + else + max_block_size = 1; + } else if (WARN_ONCE(i2c_check_functionality(i2c, I2C_FUNC_SMBUS_WORD_DATA), + "SMBus word-only adapter; odd-length transfers will fail\n")) { + /* Word-only: even-length xfers work; odd-length xfers fall + * to BYTE, which the adapter does not advertise and will + * likely fail. + */ + sfp->read = sfp_smbus_read; + sfp->write = sfp_smbus_write; + max_block_size = 2; } else { sfp->i2c = NULL; return -EINVAL; -- 2.51.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v8 2/2] net: sfp: extend SMBus support 2026-05-16 13:54 ` [PATCH net-next v8 2/2] net: sfp: extend SMBus support Jonas Jelonek @ 2026-05-19 8:05 ` Maxime Chevallier 2026-05-20 23:42 ` Jakub Kicinski 1 sibling, 0 replies; 6+ messages in thread From: Maxime Chevallier @ 2026-05-19 8:05 UTC (permalink / raw) To: Jonas Jelonek, Russell King, Andrew Lunn, Heiner Kallweit, David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni Cc: netdev, linux-kernel, Bjørn Mork, Simon Horman Hi Jonas, On 5/16/26 15:54, Jonas Jelonek wrote: > Commit 7662abf4db94 ("net: phy: sfp: Add support for SMBus module access") > added SMBus access for SFP modules, but limited it to single-byte > transfers. As a side effect, hwmon is disabled (16-bit reads cannot be > guaranteed atomic) and a warning is printed. > > Many SMBus-only I2C controllers in the wild support more than just > byte access, and SFP cages are often wired to such controllers > rather than to a full-featured I2C controller -- e.g. the SMBus > controllers in the Realtek longan and mango SoCs, which advertise > word access and I2C block reads. Today, they cannot drive an SFP at > all without falling back to the byte-only path. > > Extend sfp_smbus_read()/sfp_smbus_write() so that, in addition to > the existing byte access, they also use SMBus word access and SMBus > I2C block access whenever the adapter advertises them. Both > directions are handled in a single read and a single write helper > that pick the largest supported transfer per chunk and fall back as > needed. > > I2C-block is preferred unconditionally when available: the protocol > carries any length 1..32, so it can serve every chunk -- including > the 1- and 2-byte tails -- without help from word or byte access. > Note that this requires I2C_FUNC_SMBUS_I2C_BLOCK, which reads a > caller-specified number of bytes. This deviates from the official > SMBus Block Read (length is supplied by the slave) but is widely > supported by Linux I2C controllers/drivers. > > Capability matrix this implementation supports: > > - BYTE only: works (unchanged behaviour); 1-byte > xfers, hwmon disabled. > - BYTE + WORD: word for >=2-byte chunks, byte for > trailing odd byte. > - I2C_BLOCK present (with or > without BYTE/WORD): block as the universal transport for > every chunk. > - WORD only (no BYTE/BLOCK): accepted with WARN_ONCE. Even-length > transfers work; odd-length transfers > (e.g. the 3-byte cotsworks fixup > write) hit the BYTE branch which the > adapter does not implement, so the > xfer returns an error and the > operation is aborted. No mainline > I2C driver was found to advertise > WORD without BYTE; the warning lets > us learn about it if it ever shows > up. > > Adapters with asymmetric R/W capabilities (e.g. only READ_I2C_BLOCK > but not WRITE_I2C_BLOCK) remain functionally correct -- the > per-iteration fallback uses the direction-specific bits -- but the > shared i2c_max_block_size is sized by the all-bits-set check, so a > transfer in the better-supported direction is not upgraded. None of > the mainline I2C bus drivers surveyed during review advertise such > asymmetry; promoting i2c_max_block_size to per-direction sizes can > be revisited if needed. > > Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com> Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com> Thanks ! Maxime > --- > drivers/net/phy/sfp.c | 136 +++++++++++++++++++++++++++++++++--------- > 1 file changed, 108 insertions(+), 28 deletions(-) > > diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c > index e58e29a1e8d2..85eb13cf5df2 100644 > --- a/drivers/net/phy/sfp.c > +++ b/drivers/net/phy/sfp.c > @@ -14,6 +14,7 @@ > #include <linux/platform_device.h> > #include <linux/rtnetlink.h> > #include <linux/slab.h> > +#include <linux/unaligned.h> > #include <linux/workqueue.h> > > #include "sfp.h" > @@ -756,50 +757,110 @@ static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, > return ret == ARRAY_SIZE(msgs) ? len : 0; > } > > -static int sfp_smbus_byte_read(struct sfp *sfp, bool a2, u8 dev_addr, > - void *buf, size_t len) > +static int sfp_smbus_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, > + size_t len) > { > - union i2c_smbus_data smbus_data; > + union i2c_smbus_data smbus_data = {0}; > u8 bus_addr = a2 ? 0x51 : 0x50; > + size_t this_len, transferred; > + u32 functionality; > u8 *data = buf; > int ret; > > - while (len) { > - ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, > - I2C_SMBUS_READ, dev_addr, > - I2C_SMBUS_BYTE_DATA, &smbus_data); > - if (ret < 0) > - return ret; > + functionality = i2c_get_functionality(sfp->i2c); > > - *data = smbus_data.byte; > + while (len) { > + this_len = min(len, sfp->i2c_block_size); > + > + if (functionality & I2C_FUNC_SMBUS_READ_I2C_BLOCK) { > + smbus_data.block[0] = this_len; > + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, > + I2C_SMBUS_READ, dev_addr, > + I2C_SMBUS_I2C_BLOCK_DATA, &smbus_data); > + if (ret < 0) > + return ret; > + > + memcpy(data, &smbus_data.block[1], this_len); > + transferred = this_len; > + } else if (this_len >= 2 && > + (functionality & I2C_FUNC_SMBUS_READ_WORD_DATA)) { > + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, > + I2C_SMBUS_READ, dev_addr, > + I2C_SMBUS_WORD_DATA, &smbus_data); > + if (ret < 0) > + return ret; > + > + put_unaligned_le16(smbus_data.word, data); > + transferred = 2; > + } else { > + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, > + I2C_SMBUS_READ, dev_addr, > + I2C_SMBUS_BYTE_DATA, &smbus_data); > + if (ret < 0) > + return ret; > + > + *data = smbus_data.byte; > + transferred = 1; > + } > > - len--; > - data++; > - dev_addr++; > + data += transferred; > + len -= transferred; > + dev_addr += transferred; > } > > return data - (u8 *)buf; > } > > -static int sfp_smbus_byte_write(struct sfp *sfp, bool a2, u8 dev_addr, > - void *buf, size_t len) > +static int sfp_smbus_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, > + size_t len) > { > union i2c_smbus_data smbus_data; > u8 bus_addr = a2 ? 0x51 : 0x50; > + size_t this_len, transferred; > + u32 functionality; > u8 *data = buf; > int ret; > > + functionality = i2c_get_functionality(sfp->i2c); > + > while (len) { > - smbus_data.byte = *data; > - ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, > - I2C_SMBUS_WRITE, dev_addr, > - I2C_SMBUS_BYTE_DATA, &smbus_data); > - if (ret) > - return ret; > + this_len = min(len, sfp->i2c_block_size); > + > + if (functionality & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) { > + smbus_data.block[0] = this_len; > + memcpy(&smbus_data.block[1], data, this_len); > + > + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, > + I2C_SMBUS_WRITE, dev_addr, > + I2C_SMBUS_I2C_BLOCK_DATA, &smbus_data); > + if (ret < 0) > + return ret; > + > + transferred = this_len; > + } else if (this_len >= 2 && > + (functionality & I2C_FUNC_SMBUS_WRITE_WORD_DATA)) { > + smbus_data.word = get_unaligned_le16(data); > + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, > + I2C_SMBUS_WRITE, dev_addr, > + I2C_SMBUS_WORD_DATA, &smbus_data); > + if (ret < 0) > + return ret; > + > + transferred = 2; > + } else { > + smbus_data.byte = *data; > + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, > + I2C_SMBUS_WRITE, dev_addr, > + I2C_SMBUS_BYTE_DATA, &smbus_data); > + if (ret < 0) > + return ret; > + > + transferred = 1; > + } > > - len--; > - data++; > - dev_addr++; > + data += transferred; > + len -= transferred; > + dev_addr += transferred; > } > > return data - (u8 *)buf; > @@ -815,10 +876,29 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) > sfp->read = sfp_i2c_read; > sfp->write = sfp_i2c_write; > max_block_size = SFP_EEPROM_BLOCK_SIZE; > - } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) { > - sfp->read = sfp_smbus_byte_read; > - sfp->write = sfp_smbus_byte_write; > - max_block_size = 1; > + } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA) || > + i2c_check_functionality(i2c, I2C_FUNC_SMBUS_I2C_BLOCK)) { > + /* Either protocol alone covers any length: I2C-block carries > + * 1..32 bytes per xfer, byte iterates one byte at a time. > + */ > + sfp->read = sfp_smbus_read; > + sfp->write = sfp_smbus_write; > + > + if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_I2C_BLOCK)) > + max_block_size = SFP_EEPROM_BLOCK_SIZE; > + else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_WORD_DATA)) > + max_block_size = 2; > + else > + max_block_size = 1; > + } else if (WARN_ONCE(i2c_check_functionality(i2c, I2C_FUNC_SMBUS_WORD_DATA), > + "SMBus word-only adapter; odd-length transfers will fail\n")) { > + /* Word-only: even-length xfers work; odd-length xfers fall > + * to BYTE, which the adapter does not advertise and will > + * likely fail. > + */ > + sfp->read = sfp_smbus_read; > + sfp->write = sfp_smbus_write; > + max_block_size = 2; > } else { > sfp->i2c = NULL; > return -EINVAL; ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v8 2/2] net: sfp: extend SMBus support 2026-05-16 13:54 ` [PATCH net-next v8 2/2] net: sfp: extend SMBus support Jonas Jelonek 2026-05-19 8:05 ` Maxime Chevallier @ 2026-05-20 23:42 ` Jakub Kicinski 1 sibling, 0 replies; 6+ messages in thread From: Jakub Kicinski @ 2026-05-20 23:42 UTC (permalink / raw) To: jelonek.jonas Cc: Jakub Kicinski, linux, andrew, hkallweit1, davem, edumazet, pabeni, maxime.chevallier, netdev, linux-kernel, bjorn, horms This is an AI-generated review of your patch. The human sending this email has considered the AI review valid, or at least plausible. --- net: sfp: extend SMBus support Extend SFP SMBus read/write helpers to use SMBus word access and I2C block access when advertised by the adapter, picking the largest supported transfer per chunk. > diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c > --- a/drivers/net/phy/sfp.c > +++ b/drivers/net/phy/sfp.c > @@ -756,50 +757,110 @@ static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, > [ ... ] > -static int sfp_smbus_byte_read(struct sfp *sfp, bool a2, u8 dev_addr, > - void *buf, size_t len) > +static int sfp_smbus_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, > + size_t len) > { > - union i2c_smbus_data smbus_data; > + union i2c_smbus_data smbus_data = {0}; > u8 bus_addr = a2 ? 0x51 : 0x50; > + size_t this_len, transferred; > + u32 functionality; > u8 *data = buf; > int ret; > > - while (len) { > - ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, > - I2C_SMBUS_READ, dev_addr, > - I2C_SMBUS_BYTE_DATA, &smbus_data); > - if (ret < 0) > - return ret; > + functionality = i2c_get_functionality(sfp->i2c); > > - *data = smbus_data.byte; > + while (len) { > + this_len = min(len, sfp->i2c_block_size); > + Does this introduce an -EINVAL regression for ethtool -m during the initial 300ms module insertion window? When an SFP module is inserted, sfp_check_state() sets the SFP_F_PRESENT flag and schedules sfp_sm_mod_probe() to run after a 300ms delay. Because sfp->i2c_block_size isn't initialized until sfp_sm_mod_probe() runs, it remains 0 during this window. If ethtool -m is run during this 300ms window, sfp_module_eeprom() proceeds because SFP_F_PRESENT is set. This means this_len will evaluate to 0. For adapters supporting I2C_FUNC_SMBUS_READ_I2C_BLOCK, the code attempts a 0-length I2C block read, which the I2C subsystem core explicitly rejects with -EINVAL. Prior to this patch, sfp_smbus_byte_read() bypassed the block size entirely and safely executed 1-byte transfers during the 300ms window. This also seems to expose a pre-existing deadlock in the pure I2C path. For pure I2C controllers, sfp_i2c_read() is used. If this_len evaluates to 0, i2c_transfer() is called with a 0-length read message. If the I2C adapter returns success, the loop variables remain unchanged, causing an infinite loop while holding rtnl_lock, which deadlocks the networking stack. Is there a way to prevent reads before sfp->i2c_block_size is properly initialized, or fall back to 1-byte reads when the block size is 0? > + if (functionality & I2C_FUNC_SMBUS_READ_I2C_BLOCK) { > + smbus_data.block[0] = this_len; > + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, > + I2C_SMBUS_READ, dev_addr, > + I2C_SMBUS_I2C_BLOCK_DATA, &smbus_data); > + if (ret < 0) > + return ret; > + > + memcpy(data, &smbus_data.block[1], this_len); > + transferred = this_len; ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-05-20 23:42 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-16 13:54 [PATCH net-next v8 0/2] net: sfp: extend SMBus support Jonas Jelonek 2026-05-16 13:54 ` [PATCH net-next v8 1/2] net: sfp: apply I2C adapter quirks to limit block size Jonas Jelonek 2026-05-20 23:42 ` Jakub Kicinski 2026-05-16 13:54 ` [PATCH net-next v8 2/2] net: sfp: extend SMBus support Jonas Jelonek 2026-05-19 8:05 ` Maxime Chevallier 2026-05-20 23:42 ` Jakub Kicinski
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox