ethdev: prefer offload names in logs

Message ID 20230309081633.780438-1-david.marchand@redhat.com (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers
Series ethdev: prefer offload names in logs |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/Intel-compilation success Compilation OK
ci/github-robot: build success github build: passed
ci/intel-Testing success Testing PASS
ci/iol-mellanox-Performance success Performance Testing PASS
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-aarch64-unit-testing success Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-x86_64-compile-testing success Testing PASS
ci/iol-aarch64-compile-testing fail Testing issues
ci/iol-testing success Testing PASS
ci/iol-x86_64-unit-testing fail Testing issues
ci/iol-abi-testing success Testing PASS
ci/intel-Functional success Functional PASS

Commit Message

David Marchand March 9, 2023, 8:16 a.m. UTC
  Displaying a bitmask is terrible for users.
Prefer offload names when refusing some offloads in
rte_eth_dev_configure.

Before:
Ethdev port_id=0 requested Rx offloads 0x621 doesn't match Rx offloads
	capabilities 0x0 in rte_eth_dev_configure()

After:
Ethdev port_id=0 requested Rx offloads 'VLAN_STRIP,QINQ_STRIP,VLAN_FILTER,
	VLAN_EXTEND' in rte_eth_dev_configure(). Device supports '' Rx
	offloads but does not support 'VLAN_STRIP,QINQ_STRIP,VLAN_FILTER,
	VLAN_EXTEND'.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 lib/ethdev/rte_ethdev.c | 80 ++++++++++++++++++++++++++++++++++-------
 1 file changed, 68 insertions(+), 12 deletions(-)
  

Comments

Stephen Hemminger March 9, 2023, 4:21 p.m. UTC | #1
On Thu,  9 Mar 2023 09:16:33 +0100
David Marchand <david.marchand@redhat.com> wrote:

> +		RTE_ETHDEV_LOG(ERR, "Ethdev port_id=%u requested Rx offloads '%s' in %s(). "
> +			"Device supports '%s' Rx offloads but does not support '%s'.\n",
> +			port_id, requested != NULL ? requested : "N/A", __func__,
> +			available != NULL ? available : "N/A",
> +			unavailable != NULL ? unavailable : "N/A");
> +		free(requested);

Please shorten message and make sure it does not cross line boundaries.
Best to allow users to do simple search for message.
  
Ferruh Yigit May 17, 2023, 2:52 p.m. UTC | #2
On 3/9/2023 4:21 PM, Stephen Hemminger wrote:
> On Thu,  9 Mar 2023 09:16:33 +0100
> David Marchand <david.marchand@redhat.com> wrote:
> 
>> +		RTE_ETHDEV_LOG(ERR, "Ethdev port_id=%u requested Rx offloads '%s' in %s(). "
>> +			"Device supports '%s' Rx offloads but does not support '%s'.\n",
>> +			port_id, requested != NULL ? requested : "N/A", __func__,
>> +			available != NULL ? available : "N/A",
>> +			unavailable != NULL ? unavailable : "N/A");
>> +		free(requested);
> 
> Please shorten message and make sure it does not cross line boundaries.
> Best to allow users to do simple search for message.

Agree that using offload names are more user friendly.

To keep the log more reasonable length, what would you think to split
into two, one in ERR level other is in info/debug:
ERR, "Ethdev port_id=%u does not support '%s'.\n", unavailable
DEBUG, "Ethdev port_id=%u requested Rx offloads '%s', device supports
'%s'.\n", requested, available

And I think we can drop __func__, we don't use in many other logs anyway.



Other option can be to provide APIs to print all offloads (similar to
'rte_eth_dev_tx_offload_name()'), so application does its own logging,
and ethdev just prints 'unavailable' part of the log.
  
Ferruh Yigit May 17, 2023, 2:52 p.m. UTC | #3
On 3/9/2023 8:16 AM, David Marchand wrote:
> +static char *
> +eth_dev_offload_names(uint64_t bitmask, const char *(*offload_name)(uint64_t))
> +{
> +	uint64_t offload;
> +	char *names;
> +
> +	if (bitmask == 0) {
> +		names = strdup("");
> +		goto out;
> +	}
> +
> +	offload = RTE_BIT64(__builtin_ctzll(bitmask));
> +	names = strdup(offload_name(offload));
> +	if (names == NULL)
> +		goto out;
> +
> +	bitmask &= ~offload;
> +	while (bitmask != 0) {
> +		char *old = names;
> +		int ret;
> +
> +		offload = RTE_BIT64(__builtin_ctzll(bitmask));
> +		ret = asprintf(&names, "%s,%s", old, offload_name(offload));
> +		free(old);
> +		if (ret == -1) {
> +			names = NULL;
> +			goto out;
> +		}
> +
> +		bitmask &= ~offload;
> +	}
> +
> +out:
> +	return names;
> +}
> +

Above works fine, not a strong opinion but just a comment,
this function is just for logging and output string will be short lived,
but it does lots of memory alloc and free, and expose lot of failure points.

To simplify, why not just alloc a big enough buffer, fill it in a loop
and never return NULL?
(Can always append "%s," and drop final ',' at the end.)
  
David Marchand June 13, 2023, 1:07 p.m. UTC | #4
On Wed, May 17, 2023 at 4:53 PM Ferruh Yigit <ferruh.yigit@amd.com> wrote:
>
> On 3/9/2023 4:21 PM, Stephen Hemminger wrote:
> > On Thu,  9 Mar 2023 09:16:33 +0100
> > David Marchand <david.marchand@redhat.com> wrote:
> >
> >> +            RTE_ETHDEV_LOG(ERR, "Ethdev port_id=%u requested Rx offloads '%s' in %s(). "
> >> +                    "Device supports '%s' Rx offloads but does not support '%s'.\n",
> >> +                    port_id, requested != NULL ? requested : "N/A", __func__,
> >> +                    available != NULL ? available : "N/A",
> >> +                    unavailable != NULL ? unavailable : "N/A");
> >> +            free(requested);
> >
> > Please shorten message and make sure it does not cross line boundaries.
> > Best to allow users to do simple search for message.
>
> Agree that using offload names are more user friendly.
>
> To keep the log more reasonable length, what would you think to split
> into two, one in ERR level other is in info/debug:
> ERR, "Ethdev port_id=%u does not support '%s'.\n", unavailable
> DEBUG, "Ethdev port_id=%u requested Rx offloads '%s', device supports
> '%s'.\n", requested, available
>
> And I think we can drop __func__, we don't use in many other logs anyway.

Splitting seems the simpler and won't require an application involvement.
I would even split the debug message in two (after all, if we have two
logs, why not three :-)).

I'll also revisit the patch wrt allocations.

>
>
>
> Other option can be to provide APIs to print all offloads (similar to
> 'rte_eth_dev_tx_offload_name()'), so application does its own logging,
> and ethdev just prints 'unavailable' part of the log.
>
  

Patch

diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index 4d03255683..ba5cfeecd9 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -1026,6 +1026,42 @@  rte_eth_dev_tx_offload_name(uint64_t offload)
 	return name;
 }
 
+static char *
+eth_dev_offload_names(uint64_t bitmask, const char *(*offload_name)(uint64_t))
+{
+	uint64_t offload;
+	char *names;
+
+	if (bitmask == 0) {
+		names = strdup("");
+		goto out;
+	}
+
+	offload = RTE_BIT64(__builtin_ctzll(bitmask));
+	names = strdup(offload_name(offload));
+	if (names == NULL)
+		goto out;
+
+	bitmask &= ~offload;
+	while (bitmask != 0) {
+		char *old = names;
+		int ret;
+
+		offload = RTE_BIT64(__builtin_ctzll(bitmask));
+		ret = asprintf(&names, "%s,%s", old, offload_name(offload));
+		free(old);
+		if (ret == -1) {
+			names = NULL;
+			goto out;
+		}
+
+		bitmask &= ~offload;
+	}
+
+out:
+	return names;
+}
+
 const char *
 rte_eth_dev_capability_name(uint64_t capability)
 {
@@ -1332,23 +1368,43 @@  rte_eth_dev_configure(uint16_t port_id, uint16_t nb_rx_q, uint16_t nb_tx_q,
 	/* Any requested offloading must be within its device capabilities */
 	if ((dev_conf->rxmode.offloads & dev_info.rx_offload_capa) !=
 	     dev_conf->rxmode.offloads) {
-		RTE_ETHDEV_LOG(ERR,
-			"Ethdev port_id=%u requested Rx offloads 0x%"PRIx64" doesn't match Rx offloads "
-			"capabilities 0x%"PRIx64" in %s()\n",
-			port_id, dev_conf->rxmode.offloads,
-			dev_info.rx_offload_capa,
-			__func__);
+		char *requested = eth_dev_offload_names(dev_conf->rxmode.offloads,
+			rte_eth_dev_rx_offload_name);
+		char *available = eth_dev_offload_names(dev_info.rx_offload_capa,
+			rte_eth_dev_rx_offload_name);
+		char *unavailable = eth_dev_offload_names(
+			dev_conf->rxmode.offloads & ~dev_info.rx_offload_capa,
+			rte_eth_dev_rx_offload_name);
+
+		RTE_ETHDEV_LOG(ERR, "Ethdev port_id=%u requested Rx offloads '%s' in %s(). "
+			"Device supports '%s' Rx offloads but does not support '%s'.\n",
+			port_id, requested != NULL ? requested : "N/A", __func__,
+			available != NULL ? available : "N/A",
+			unavailable != NULL ? unavailable : "N/A");
+		free(requested);
+		free(available);
+		free(unavailable);
 		ret = -EINVAL;
 		goto rollback;
 	}
 	if ((dev_conf->txmode.offloads & dev_info.tx_offload_capa) !=
 	     dev_conf->txmode.offloads) {
-		RTE_ETHDEV_LOG(ERR,
-			"Ethdev port_id=%u requested Tx offloads 0x%"PRIx64" doesn't match Tx offloads "
-			"capabilities 0x%"PRIx64" in %s()\n",
-			port_id, dev_conf->txmode.offloads,
-			dev_info.tx_offload_capa,
-			__func__);
+		char *requested = eth_dev_offload_names(dev_conf->txmode.offloads,
+			rte_eth_dev_tx_offload_name);
+		char *available = eth_dev_offload_names(dev_info.tx_offload_capa,
+			rte_eth_dev_tx_offload_name);
+		char *unavailable = eth_dev_offload_names(
+			dev_conf->txmode.offloads & ~dev_info.tx_offload_capa,
+			rte_eth_dev_tx_offload_name);
+
+		RTE_ETHDEV_LOG(ERR, "Ethdev port_id=%u requested Tx offloads '%s' in %s(). "
+			"Device supports '%s' Tx offloads but does not support '%s'.\n",
+			port_id, requested != NULL ? requested : "N/A", __func__,
+			available != NULL ? available : "N/A",
+			unavailable != NULL ? unavailable : "N/A");
+		free(requested);
+		free(available);
+		free(unavailable);
 		ret = -EINVAL;
 		goto rollback;
 	}