DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] ethdev: fix SFF-8472 calibration overflow
@ 2026-10-02 12:52 Roman Khromenok
  2026-10-02 12:52 ` [PATCH 1/3] " Roman Khromenok
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Roman Khromenok @ 2026-10-02 12:52 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Thomas Monjalon, Andrew Rybchenko

This follows up on the review of the SFF-8472 Rx power calibration fix,
where the automated review pointed out the remaining issues in
sff_8472_calibration().

Calibration formula 1 (temperature, voltage, Tx bias, Tx power) scales
the 16-bit fields in place by a slope of up to 255.996 and adds a signed
16-bit offset. A result out of the field range is undefined behavior
for the float to integer conversion; a negative sum wraps around.
UBSan reports float-cast-overflow on randomly generated externally
calibrated SFP dumps.

Patch 1 computes formula 1 in double precision and saturates the result
to the field range, as already done for the Rx power. It is intended
for stable.
Patch 2 rounds the calibrated values to the nearest integer instead
of truncating them: RX_PWR(1) = 0.7 is stored as 0.69999999 and
a raw reading of 1000 gave 699 instead of 700.
Patch 3 adds unit tests for saturation at both ends and for rounding.

Based on dpdk-next-net for-main.

Depends-on: series-39455 ("ethdev: fix SFF-8472 external Rx power calibration")

Roman Khromenok (3):
  ethdev: fix SFF-8472 calibration overflow
  ethdev: round SFF-8472 calibrated values
  test: check SFF-8472 calibration saturation and rounding

 app/test/test_ethdev_module_eeprom.c | 96 ++++++++++++++++++++++++++++
 lib/ethdev/sff_8472.c                | 55 +++++++++++-----
 2 files changed, 134 insertions(+), 17 deletions(-)

-- 
2.47.3


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/3] ethdev: fix SFF-8472 calibration overflow
  2026-10-02 12:52 [PATCH 0/3] ethdev: fix SFF-8472 calibration overflow Roman Khromenok
@ 2026-10-02 12:52 ` Roman Khromenok
  2026-10-02 12:52 ` [PATCH 2/3] ethdev: round SFF-8472 calibrated values Roman Khromenok
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Roman Khromenok @ 2026-10-02 12:52 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Thomas Monjalon, Andrew Rybchenko, stable

With external calibration, SFF-8472 computes temperature, voltage,
TX bias and TX power as slope * x + offset, where the slope goes up
to 255.996 and the offset is a signed 16-bit integer.

The decoder multiplied the 16-bit fields in place by the slope and
then added the offset. A result out of the field range is undefined
behavior for the float to integer conversion, and an integer sum
below zero wraps around: a bias reading of 100 with an offset of
-1000 was reported as 129.272 mA.

Compute the formula in double precision and saturate the result
to the range of the field, as already done for the RX power.

Fixes: 0caf7f376b08 ("ethdev: support SFF-8472 module telemetry")
Cc: stable@dpdk.org

Signed-off-by: Roman Khromenok <roma55592@yandex.ru>
---
 lib/ethdev/sff_8472.c | 51 ++++++++++++++++++++++++++++---------------
 1 file changed, 34 insertions(+), 17 deletions(-)

diff --git a/lib/ethdev/sff_8472.c b/lib/ethdev/sff_8472.c
index 3c73a829a8..8cd41161e3 100644
--- a/lib/ethdev/sff_8472.c
+++ b/lib/ethdev/sff_8472.c
@@ -186,6 +186,25 @@ static float befloattoh(const uint8_t *source)
 	return converter.dst;
 }
 
+/* Calibrated values are stored in 16-bit fields, saturate the out of range ones */
+static uint16_t sff_8472_cal_to_u16(double value)
+{
+	if (!(value > 0))
+		return 0;
+	if (value >= UINT16_MAX)
+		return UINT16_MAX;
+	return value;
+}
+
+static int16_t sff_8472_cal_to_s16(double value)
+{
+	if (value <= INT16_MIN)
+		return INT16_MIN;
+	if (value >= INT16_MAX)
+		return INT16_MAX;
+	return value;
+}
+
 static void sff_8472_calibration(const uint8_t *data, struct sff_diags *sd)
 {
 	unsigned long i;
@@ -195,17 +214,21 @@ static void sff_8472_calibration(const uint8_t *data, struct sff_diags *sd)
 	/* Calibration should occur for all values (threshold and current) */
 	for (i = 0; i < RTE_DIM(sd->bias_cur); ++i) {
 		/*
-		 * Apply calibration formula 1 (Temp., Voltage, Bias, Tx Power)
+		 * Apply calibration formula 1 (Temp., Voltage, Bias, Tx Power):
+		 * slope * x + offset
 		 */
-		sd->bias_cur[i]    *= A2_OFFSET_TO_SLP(SFF_A2_CAL_TXI_SLP);
-		sd->tx_power[i]    *= A2_OFFSET_TO_SLP(SFF_A2_CAL_TXPWR_SLP);
-		sd->sfp_voltage[i] *= A2_OFFSET_TO_SLP(SFF_A2_CAL_V_SLP);
-		sd->sfp_temp[i]    *= A2_OFFSET_TO_SLP(SFF_A2_CAL_T_SLP);
-
-		sd->bias_cur[i]    += A2_OFFSET_TO_OFF(SFF_A2_CAL_TXI_OFF);
-		sd->tx_power[i]    += A2_OFFSET_TO_OFF(SFF_A2_CAL_TXPWR_OFF);
-		sd->sfp_voltage[i] += A2_OFFSET_TO_OFF(SFF_A2_CAL_V_OFF);
-		sd->sfp_temp[i]    += A2_OFFSET_TO_OFF(SFF_A2_CAL_T_OFF);
+		sd->bias_cur[i] = sff_8472_cal_to_u16(sd->bias_cur[i] *
+			A2_OFFSET_TO_SLP(SFF_A2_CAL_TXI_SLP) +
+			A2_OFFSET_TO_OFF(SFF_A2_CAL_TXI_OFF));
+		sd->tx_power[i] = sff_8472_cal_to_u16(sd->tx_power[i] *
+			A2_OFFSET_TO_SLP(SFF_A2_CAL_TXPWR_SLP) +
+			A2_OFFSET_TO_OFF(SFF_A2_CAL_TXPWR_OFF));
+		sd->sfp_voltage[i] = sff_8472_cal_to_u16(sd->sfp_voltage[i] *
+			A2_OFFSET_TO_SLP(SFF_A2_CAL_V_SLP) +
+			A2_OFFSET_TO_OFF(SFF_A2_CAL_V_OFF));
+		sd->sfp_temp[i] = sff_8472_cal_to_s16(sd->sfp_temp[i] *
+			A2_OFFSET_TO_SLP(SFF_A2_CAL_T_SLP) +
+			A2_OFFSET_TO_OFF(SFF_A2_CAL_T_OFF));
 
 		/*
 		 * Apply calibration formula 2 (Rx Power only):
@@ -219,13 +242,7 @@ static void sff_8472_calibration(const uint8_t *data, struct sff_diags *sd)
 		rx_power = rx_power * rx_reading + A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR1);
 		rx_power = rx_power * rx_reading + A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR0);
 
-		/* the result is stored in 0.1 uW units, out of range is not representable */
-		if (!(rx_power > 0))
-			sd->rx_power[i] = 0;
-		else if (rx_power >= UINT16_MAX)
-			sd->rx_power[i] = UINT16_MAX;
-		else
-			sd->rx_power[i] = rx_power;
+		sd->rx_power[i] = sff_8472_cal_to_u16(rx_power);
 	}
 }
 
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/3] ethdev: round SFF-8472 calibrated values
  2026-10-02 12:52 [PATCH 0/3] ethdev: fix SFF-8472 calibration overflow Roman Khromenok
  2026-10-02 12:52 ` [PATCH 1/3] " Roman Khromenok
@ 2026-10-02 12:52 ` Roman Khromenok
  2026-10-02 12:52 ` [PATCH 3/3] test: check SFF-8472 calibration saturation and rounding Roman Khromenok
  2026-10-02 17:18 ` [PATCH 0/3] ethdev: fix SFF-8472 calibration overflow Stephen Hemminger
  3 siblings, 0 replies; 5+ messages in thread
From: Roman Khromenok @ 2026-10-02 12:52 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Thomas Monjalon, Andrew Rybchenko

The externally calibrated SFF-8472 values were truncated when stored
in their 16-bit fields. Coefficients that are not exact in binary
floating point lose one unit this way: RX_PWR(1) = 0.7 is stored as
0.69999999, and a raw reading of 1000 gave 699 instead of 700.

Round the calibrated values to the nearest integer instead.

Signed-off-by: Roman Khromenok <roma55592@yandex.ru>
---
 lib/ethdev/sff_8472.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/lib/ethdev/sff_8472.c b/lib/ethdev/sff_8472.c
index 8cd41161e3..88d1f4a0a5 100644
--- a/lib/ethdev/sff_8472.c
+++ b/lib/ethdev/sff_8472.c
@@ -3,6 +3,7 @@
  * Implements SFF-8472 optics diagnostics.
  */
 
+#include <math.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -186,14 +187,17 @@ static float befloattoh(const uint8_t *source)
 	return converter.dst;
 }
 
-/* Calibrated values are stored in 16-bit fields, saturate the out of range ones */
+/*
+ * Calibrated values are stored in 16-bit fields:
+ * round to the nearest integer and saturate the out of range ones.
+ */
 static uint16_t sff_8472_cal_to_u16(double value)
 {
 	if (!(value > 0))
 		return 0;
 	if (value >= UINT16_MAX)
 		return UINT16_MAX;
-	return value;
+	return lround(value);
 }
 
 static int16_t sff_8472_cal_to_s16(double value)
@@ -202,7 +206,7 @@ static int16_t sff_8472_cal_to_s16(double value)
 		return INT16_MIN;
 	if (value >= INT16_MAX)
 		return INT16_MAX;
-	return value;
+	return lround(value);
 }
 
 static void sff_8472_calibration(const uint8_t *data, struct sff_diags *sd)
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/3] test: check SFF-8472 calibration saturation and rounding
  2026-10-02 12:52 [PATCH 0/3] ethdev: fix SFF-8472 calibration overflow Roman Khromenok
  2026-10-02 12:52 ` [PATCH 1/3] " Roman Khromenok
  2026-10-02 12:52 ` [PATCH 2/3] ethdev: round SFF-8472 calibrated values Roman Khromenok
@ 2026-10-02 12:52 ` Roman Khromenok
  2026-10-02 17:18 ` [PATCH 0/3] ethdev: fix SFF-8472 calibration overflow Stephen Hemminger
  3 siblings, 0 replies; 5+ messages in thread
From: Roman Khromenok @ 2026-10-02 12:52 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Thomas Monjalon, Andrew Rybchenko

Check that externally calibrated SFF-8472 values out of the range
of their 16-bit fields saturate at both ends, for formula 1
(temperature, voltage, TX bias and TX power) and for the RX power
polynomial, and that calibrated values are rounded to the nearest.

Signed-off-by: Roman Khromenok <roma55592@yandex.ru>
---
 app/test/test_ethdev_module_eeprom.c | 96 ++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/app/test/test_ethdev_module_eeprom.c b/app/test/test_ethdev_module_eeprom.c
index b07a18aa06..1956fdd440 100644
--- a/app/test/test_ethdev_module_eeprom.c
+++ b/app/test/test_ethdev_module_eeprom.c
@@ -305,6 +305,99 @@ test_module_eeprom_sfp_8472_rx_power_polynomial(void)
 	return TEST_SUCCESS;
 }
 
+/* SFF-8472 module externally calibrated with unit slopes and zero offsets */
+static void
+fill_sfp_ext_cal(uint8_t *data)
+{
+	uint8_t *a2 = data + RTE_ETH_MODULE_SFF_8079_LEN;
+
+	fill_sfp(data);
+	/* diagnostics implemented, externally calibrated, average RX power */
+	data[92] = 0x58;
+	a2[76] = 1;	/* TX bias slope */
+	a2[80] = 1;	/* TX power slope */
+	a2[84] = 1;	/* temperature slope */
+	a2[88] = 1;	/* voltage slope */
+	put_be_float(a2, 68, 0x3f800000);	/* RX_PWR(1) = 1.0 */
+}
+
+static int
+test_module_eeprom_sfp_8472_cal_saturate_max(void)
+{
+	uint8_t data[RTE_ETH_MODULE_SFF_8472_LEN];
+	uint8_t *a2 = data + RTE_ETH_MODULE_SFF_8079_LEN;
+
+	fill_sfp_ext_cal(data);
+	/* results above the 16-bit range must saturate */
+	put_u16(a2, 100, 65535);	/* raw TX bias */
+	put_u16(a2, 76, 0xffff);	/* TX bias slope = 255.996 */
+	put_u16(a2, 102, 60000);	/* raw TX power */
+	put_u16(a2, 82, 10000);		/* TX power offset */
+	put_u16(a2, 96, 0x7f00);	/* raw temperature: 127 C */
+	put_u16(a2, 84, 0x0200);	/* temperature slope = 2.0 */
+	put_u16(a2, 98, 60000);		/* raw voltage */
+	put_u16(a2, 90, 10000);		/* voltage offset */
+	put_u16(a2, 104, 65535);	/* raw RX power */
+	put_be_float(a2, 56, 0x3f800000);	/* RX_PWR(4) = 1.0 */
+
+	TEST_ASSERT_SUCCESS(parse(RTE_ETH_MODULE_SFF_8472, data, sizeof(data)),
+		"Failed to parse externally calibrated SFF-8472 data");
+	CHECK_FIELD("Laser bias current", "131.070 mA");
+	CHECK_FIELD("Laser output power", "6.5535 mW / 8.16 dBm");
+	CHECK_FIELD("Module temperature", "128.00 degrees C / 262.39 degrees F");
+	CHECK_FIELD("Module voltage", "6.5535 V");
+	CHECK_FIELD("Receiver signal average optical power", "6.5535 mW / 8.16 dBm");
+	return TEST_SUCCESS;
+}
+
+static int
+test_module_eeprom_sfp_8472_cal_saturate_min(void)
+{
+	uint8_t data[RTE_ETH_MODULE_SFF_8472_LEN];
+	uint8_t *a2 = data + RTE_ETH_MODULE_SFF_8079_LEN;
+
+	fill_sfp_ext_cal(data);
+	/* results below the 16-bit range must saturate */
+	put_u16(a2, 100, 100);		/* raw TX bias */
+	put_u16(a2, 78, -1000);		/* TX bias offset */
+	put_u16(a2, 102, 100);		/* raw TX power */
+	put_u16(a2, 82, -1000);		/* TX power offset */
+	put_u16(a2, 96, 0x8100);	/* raw temperature: -127 C */
+	put_u16(a2, 84, 0x0200);	/* temperature slope = 2.0 */
+	put_u16(a2, 98, 100);		/* raw voltage */
+	put_u16(a2, 90, -1000);		/* voltage offset */
+	put_be_float(a2, 68, 0xbf800000);	/* RX_PWR(1) = -1.0 */
+
+	TEST_ASSERT_SUCCESS(parse(RTE_ETH_MODULE_SFF_8472, data, sizeof(data)),
+		"Failed to parse externally calibrated SFF-8472 data");
+	CHECK_FIELD("Laser bias current", "0.000 mA");
+	CHECK_FIELD("Laser output power", "0.0000 mW / -inf dBm");
+	CHECK_FIELD("Module temperature", "-128.00 degrees C / -198.40 degrees F");
+	CHECK_FIELD("Module voltage", "0.0000 V");
+	CHECK_FIELD("Receiver signal average optical power", "0.0000 mW / -inf dBm");
+	return TEST_SUCCESS;
+}
+
+static int
+test_module_eeprom_sfp_8472_cal_round(void)
+{
+	uint8_t data[RTE_ETH_MODULE_SFF_8472_LEN];
+	uint8_t *a2 = data + RTE_ETH_MODULE_SFF_8079_LEN;
+
+	fill_sfp_ext_cal(data);
+	/* calibrated values must be rounded to the nearest, not truncated */
+	put_u16(a2, 100, 3);		/* raw TX bias */
+	put_u16(a2, 76, 0x0180);	/* TX bias slope = 1.5 */
+	put_u16(a2, 104, 1000);		/* raw RX power */
+	put_be_float(a2, 68, 0x3f333333);	/* RX_PWR(1) = 0.7, stored as 0.69999999 */
+
+	TEST_ASSERT_SUCCESS(parse(RTE_ETH_MODULE_SFF_8472, data, sizeof(data)),
+		"Failed to parse externally calibrated SFF-8472 data");
+	CHECK_FIELD("Laser bias current", "0.010 mA");
+	CHECK_FIELD("Receiver signal average optical power", "0.0700 mW / -11.55 dBm");
+	return TEST_SUCCESS;
+}
+
 static int
 test_module_eeprom_invalid(void)
 {
@@ -355,6 +448,9 @@ static struct unit_test_suite module_eeprom_testsuite = {
 		TEST_CASE(test_module_eeprom_sfp_8472_short),
 		TEST_CASE(test_module_eeprom_sfp_8472_ext_cal_unaligned),
 		TEST_CASE(test_module_eeprom_sfp_8472_rx_power_polynomial),
+		TEST_CASE(test_module_eeprom_sfp_8472_cal_saturate_max),
+		TEST_CASE(test_module_eeprom_sfp_8472_cal_saturate_min),
+		TEST_CASE(test_module_eeprom_sfp_8472_cal_round),
 		TEST_CASE(test_module_eeprom_qsfp_8636),
 		TEST_CASE(test_module_eeprom_qsfp_8636_thresholds),
 		TEST_CASE(test_module_eeprom_invalid),
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/3] ethdev: fix SFF-8472 calibration overflow
  2026-10-02 12:52 [PATCH 0/3] ethdev: fix SFF-8472 calibration overflow Roman Khromenok
                   ` (2 preceding siblings ...)
  2026-10-02 12:52 ` [PATCH 3/3] test: check SFF-8472 calibration saturation and rounding Roman Khromenok
@ 2026-10-02 17:18 ` Stephen Hemminger
  3 siblings, 0 replies; 5+ messages in thread
From: Stephen Hemminger @ 2026-10-02 17:18 UTC (permalink / raw)
  To: Roman Khromenok; +Cc: dev, Thomas Monjalon, Andrew Rybchenko

On Fri,  2 Oct 2026 14:52:34 +0200
Roman Khromenok <roma55592@yandex.ru> wrote:

> This follows up on the review of the SFF-8472 Rx power calibration fix,
> where the automated review pointed out the remaining issues in
> sff_8472_calibration().
> 
> Calibration formula 1 (temperature, voltage, Tx bias, Tx power) scales
> the 16-bit fields in place by a slope of up to 255.996 and adds a signed
> 16-bit offset. A result out of the field range is undefined behavior
> for the float to integer conversion; a negative sum wraps around.
> UBSan reports float-cast-overflow on randomly generated externally
> calibrated SFP dumps.
> 
> Patch 1 computes formula 1 in double precision and saturates the result
> to the field range, as already done for the Rx power. It is intended
> for stable.
> Patch 2 rounds the calibrated values to the nearest integer instead
> of truncating them: RX_PWR(1) = 0.7 is stored as 0.69999999 and
> a raw reading of 1000 gave 699 instead of 700.
> Patch 3 adds unit tests for saturation at both ends and for rounding.
> 
> Based on dpdk-next-net for-main.
> 
> Depends-on: series-39455 ("ethdev: fix SFF-8472 external Rx power calibration")

 Applied to next-net

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-02 17:18 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-02 12:52 [PATCH 0/3] ethdev: fix SFF-8472 calibration overflow Roman Khromenok
2026-10-02 12:52 ` [PATCH 1/3] " Roman Khromenok
2026-10-02 12:52 ` [PATCH 2/3] ethdev: round SFF-8472 calibrated values Roman Khromenok
2026-10-02 12:52 ` [PATCH 3/3] test: check SFF-8472 calibration saturation and rounding Roman Khromenok
2026-10-02 17:18 ` [PATCH 0/3] ethdev: fix SFF-8472 calibration overflow Stephen Hemminger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox