Re: [PATCH v3 3/3] thunderbolt: Refactor DROM reading

From: Mika Westerberg
Date: Fri Feb 17 2023 - 08:36:00 EST


Hi Mario,

On Thu, Feb 16, 2023 at 02:19:10PM -0600, Mario Limonciello wrote:
> The NVM reading code has a series of gotos that potentially introduce
> unexpected behaviors with retries if something unexpected has failed
> to parse.
>
> Additionally the retry logic introduced in commit f022ff7bf377
> ("thunderbolt: Retry DROM read once if parsing fails") was added from
> failures in bit banging, which aren't expected to be present when the
> DROM is fetched directly from the NVM.

Okay that's why you remove the EILSEQ returns below, right?

> Refactor the code to remove the gotos and drop the retry logic.

Thanks for doing this. Few minor stylistic comments below. I can also
fix these myself when applying if you prefer.

Note I will be on vacation next week but will be picking up patches once
v6.3-rc1 is released.

> Signed-off-by: Mario Limonciello <mario.limonciello@xxxxxxx>
> ---
> v2->v3:
> * Split out refactor
> ---
> drivers/thunderbolt/eeprom.c | 147 +++++++++++++++++++----------------
> 1 file changed, 79 insertions(+), 68 deletions(-)
>
> diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c
> index a326cf16ca3d..2a078c69f0d2 100644
> --- a/drivers/thunderbolt/eeprom.c
> +++ b/drivers/thunderbolt/eeprom.c
> @@ -416,7 +416,7 @@ static int tb_drom_parse_entries(struct tb_switch *sw, size_t header_size)
> if (pos + 1 == drom_size || pos + entry->len > drom_size
> || !entry->len) {
> tb_sw_warn(sw, "DROM buffer overrun\n");
> - return -EILSEQ;
> + return -EIO;
> }
>
> switch (entry->type) {
> @@ -543,7 +543,37 @@ static int tb_drom_read_n(struct tb_switch *sw, u16 offset, u8 *val,
> return tb_eeprom_read_n(sw, offset, val, count);
> }
>
> -static int tb_drom_parse(struct tb_switch *sw)
> +static int tb_drom_bit_bang(struct tb_switch *sw, u16 *size)
> +{
> + int res;

ret

> +
> + res = tb_drom_read_n(sw, 14, (u8 *) size, 2);
> + if (res)
> + return res;

empty line here.

> + *size &= 0x3ff;
> + *size += TB_DROM_DATA_START;

here too.

> + tb_sw_dbg(sw, "reading drom (length: %#x)\n", *size);
> + if (*size < sizeof(struct tb_drom_header)) {
> + tb_sw_warn(sw, "drom too small, aborting\n");

DROM

> + return -EIO;
> + }
> +
> + sw->drom = kzalloc(*size, GFP_KERNEL);
> + if (!sw->drom)
> + return -ENOMEM;
> +
> + res = tb_drom_read_n(sw, 0, sw->drom, *size);
> + if (res)
> + goto err;
> +
> + return 0;

empty line

> +err:
> + kfree(sw->drom);
> + sw->drom = NULL;

empty line

> + return res;
> +}
> +
> +static int tb_drom_parse_v1(struct tb_switch *sw)
> {
> const struct tb_drom_header *header =
> (const struct tb_drom_header *)sw->drom;
> @@ -554,7 +584,7 @@ static int tb_drom_parse(struct tb_switch *sw)
> tb_sw_warn(sw,
> "DROM UID CRC8 mismatch (expected: %#x, got: %#x)\n",
> header->uid_crc8, crc);
> - return -EILSEQ;
> + return -EIO;
> }
> if (!sw->uid)
> sw->uid = header->uid;
> @@ -588,6 +618,43 @@ static int usb4_drom_parse(struct tb_switch *sw)
> return tb_drom_parse_entries(sw, USB4_DROM_HEADER_SIZE);
> }
>
> +static int tb_drom_parse(struct tb_switch *sw, u16 *size)
> +{
> + struct tb_drom_header *header = (void *) sw->drom;
> + int res;

ret

> +
> + if (header->data_len + TB_DROM_DATA_START != *size) {
> + tb_sw_warn(sw, "drom size mismatch\n");

DROM

> + goto err;
> + }
> +
> + tb_sw_dbg(sw, "DROM version: %d\n", header->device_rom_revision);
> +
> + switch (header->device_rom_revision) {
> + case 3:
> + res = usb4_drom_parse(sw);
> + break;
> + default:
> + tb_sw_warn(sw, "DROM device_rom_revision %#x unknown\n",
> + header->device_rom_revision);
> + fallthrough;
> + case 1:
> + res = tb_drom_parse_v1(sw);
> + break;
> + }
> +
> + if (res) {
> + tb_sw_warn(sw, "parsing DROM failed\n");
> + goto err;
> + }
> +
> + return 0;

empty line

> +err:
> + kfree(sw->drom);
> + sw->drom = NULL;

empty line

> + return -EIO;
> +}
> +
> /**
> * tb_drom_read() - Copy DROM to sw->drom and parse it
> * @sw: Router whose DROM to read and parse
> @@ -601,8 +668,7 @@ static int usb4_drom_parse(struct tb_switch *sw)
> int tb_drom_read(struct tb_switch *sw)
> {
> u16 size;
> - struct tb_drom_header *header;
> - int res, retries = 1;
> + int res;
>
> if (sw->drom)
> return 0;
> @@ -613,11 +679,11 @@ int tb_drom_read(struct tb_switch *sw)
> * in a device property. Use it if available.
> */
> if (tb_drom_copy_efi(sw, &size) == 0)
> - goto parse;
> + return tb_drom_parse(sw, &size);
>
> /* Non-Apple hardware has the DROM as part of NVM */
> if (tb_drom_copy_nvm(sw, &size) == 0)
> - goto parse;
> + return tb_drom_parse(sw, &size);
>
> /*
> * USB4 hosts may support reading DROM through router
> @@ -626,7 +692,7 @@ int tb_drom_read(struct tb_switch *sw)
> if (tb_switch_is_usb4(sw)) {
> usb4_switch_read_uid(sw, &sw->uid);
> if (!usb4_copy_host_drom(sw, &size))
> - goto parse;
> + return tb_drom_parse(sw, &size);
> } else {
> /*
> * The root switch contains only a dummy drom
> @@ -640,67 +706,12 @@ int tb_drom_read(struct tb_switch *sw)
> }
>
> /* We can use LC to get UUID later */
> - if (sw->cap_lc && tb_drom_copy_nvm(sw, &size) == 0)
> - goto parse;
> -
> - res = tb_drom_read_n(sw, 14, (u8 *) &size, 2);
> + if (sw->cap_lc)
> + res = tb_drom_copy_nvm(sw, &size);
> + else
> + res = tb_drom_bit_bang(sw, &size);
> if (res)
> return res;
> - size &= 0x3ff;
> - size += TB_DROM_DATA_START;
> - tb_sw_dbg(sw, "reading drom (length: %#x)\n", size);
> - if (size < sizeof(*header)) {
> - tb_sw_warn(sw, "drom too small, aborting\n");
> - return -EIO;
> - }
> -
> - sw->drom = kzalloc(size, GFP_KERNEL);
> - if (!sw->drom)
> - return -ENOMEM;
> -read:
> - res = tb_drom_read_n(sw, 0, sw->drom, size);
> - if (res)
> - goto err;
> -
> -parse:
> - header = (void *) sw->drom;
> -
> - if (header->data_len + TB_DROM_DATA_START != size) {
> - tb_sw_warn(sw, "drom size mismatch\n");
> - if (retries--) {
> - msleep(100);
> - goto read;
> - }
> - goto err;
> - }
> -
> - tb_sw_dbg(sw, "DROM version: %d\n", header->device_rom_revision);
> -
> - switch (header->device_rom_revision) {
> - case 3:
> - res = usb4_drom_parse(sw);
> - break;
> - default:
> - tb_sw_warn(sw, "DROM device_rom_revision %#x unknown\n",
> - header->device_rom_revision);
> - fallthrough;
> - case 1:
> - res = tb_drom_parse(sw);
> - break;
> - }
> -
> - /* If the DROM parsing fails, wait a moment and retry once */
> - if (res == -EILSEQ && retries--) {
> - tb_sw_warn(sw, "parsing DROM failed\n");
> - msleep(100);
> - goto read;
> - }
>
> - if (!res)
> - return 0;
> -
> -err:
> - kfree(sw->drom);
> - sw->drom = NULL;
> - return -EIO;
> + return tb_drom_parse(sw, &size);
> }
> --
> 2.34.1