[01/16] api: odp_cpumask.h: odp_cpumask_to_str() return bufsz

Message ID 1422528008-29845-2-git-send-email-ola.liljedahl@linaro.org
State New
Headers show

Commit Message

Ola Liljedahl Jan. 29, 2015, 10:39 a.m.
odp_cpumask_to_str() definition modified to pass output buffer size by
reference and the variable is updated with the number of bytes written on
success or the minimum required buffer size should it be too small.
odp_cpumask_to_str() returns 0 on success and <0 on failure.
Updated the implementation and usages of this call to match.

Signed-off-by: Ola Liljedahl <ola.liljedahl@linaro.org>
---
(This document/code contribution attached is provided under the terms of
agreement LES-LTM-21309)

 example/generator/odp_generator.c                |   5 +-
 example/ipsec/odp_ipsec.c                        |   5 +-
 example/l2fwd/odp_l2fwd.c                        |   5 +-
 example/packet/odp_pktio.c                       |   5 +-
 example/timer/odp_timer_test.c                   |   5 +-
 platform/linux-generic/include/api/odp_cpumask.h | 103 ++++++++++++++---------
 platform/linux-generic/odp_cpumask.c             |  34 +++++---
 test/api_test/odp_common.c                       |   5 +-
 test/performance/odp_scheduling.c                |   5 +-
 9 files changed, 106 insertions(+), 66 deletions(-)

Comments

Maxim Uvarov Feb. 2, 2015, 11:05 a.m. | #1
On 01/29/2015 01:39 PM, Ola Liljedahl wrote:
>   	num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
> -	odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
> +	size_t bufsz = sizeof(cpumaskstr);
> +	(void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
>   

why did add this (void)?

Maxim.
Ola Liljedahl Feb. 2, 2015, 11:18 a.m. | #2
On 2 February 2015 at 12:05, Maxim Uvarov <maxim.uvarov@linaro.org> wrote:
> On 01/29/2015 01:39 PM, Ola Liljedahl wrote:
>>
>>         num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
>> -       odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
>> +       size_t bufsz = sizeof(cpumaskstr);
>> +       (void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
>>
>
>
> why did add this (void)?
Because odp_cpumask_to_str() has a return value that you normally
should check. If I think the return value for some reason doesn't
merit checking, then I cast the function call to "(void)".

If you are closing a file/socket descriptor you have written to, you
should check the return value from close() because writes may have
been buffered and close may fail writing out your data.. But if the
file descriptor was e.g. only used for reading, then you don't need to
check the return value from close(). Ideally the compiler should warn
if you are not checking the return value from close() and in the
second case above, you would use "(void)close(fd);".

I think the compiler can warn if return values are not used. Possibly
this is controlled using some GCC attribute.

BTW: This patch series is obsolete, I am currently rebasing it.


>
> Maxim.
>
> _______________________________________________
> lng-odp mailing list
> lng-odp@lists.linaro.org
> http://lists.linaro.org/mailman/listinfo/lng-odp
Ola Liljedahl Feb. 2, 2015, 11:22 a.m. | #3
On 2 February 2015 at 12:18, Ola Liljedahl <ola.liljedahl@linaro.org> wrote:
> On 2 February 2015 at 12:05, Maxim Uvarov <maxim.uvarov@linaro.org> wrote:
>> On 01/29/2015 01:39 PM, Ola Liljedahl wrote:
>>>
>>>         num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
>>> -       odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
>>> +       size_t bufsz = sizeof(cpumaskstr);
>>> +       (void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
>>>
>>
>>
>> why did add this (void)?
> Because odp_cpumask_to_str() has a return value that you normally
> should check. If I think the return value for some reason doesn't
> merit checking, then I cast the function call to "(void)".
>
> If you are closing a file/socket descriptor you have written to, you
> should check the return value from close() because writes may have
> been buffered and close may fail writing out your data.. But if the
> file descriptor was e.g. only used for reading, then you don't need to
> check the return value from close(). Ideally the compiler should warn
> if you are not checking the return value from close() and in the
> second case above, you would use "(void)close(fd);".
>
> I think the compiler can warn if return values are not used. Possibly
> this is controlled using some GCC attribute.
http://stackoverflow.com/questions/2870529/g-how-to-get-warning-on-ignoring-function-return-value

Something for ODP?
>
> BTW: This patch series is obsolete, I am currently rebasing it.
>
>
>>
>> Maxim.
>>
>> _______________________________________________
>> lng-odp mailing list
>> lng-odp@lists.linaro.org
>> http://lists.linaro.org/mailman/listinfo/lng-odp
Maxim Uvarov Feb. 2, 2015, 11:26 a.m. | #4
On 02/02/2015 02:18 PM, Ola Liljedahl wrote:
> On 2 February 2015 at 12:05, Maxim Uvarov <maxim.uvarov@linaro.org> wrote:
>> On 01/29/2015 01:39 PM, Ola Liljedahl wrote:
>>>          num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
>>> -       odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
>>> +       size_t bufsz = sizeof(cpumaskstr);
>>> +       (void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
>>>
>>
>> why did add this (void)?
> Because odp_cpumask_to_str() has a return value that you normally
> should check. If I think the return value for some reason doesn't
> merit checking, then I cast the function call to "(void)".
In current repo it's void:

void odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int len);

>
> If you are closing a file/socket descriptor you have written to, you
> should check the return value from close() because writes may have
> been buffered and close may fail writing out your data.. But if the
> file descriptor was e.g. only used for reading, then you don't need to
> check the return value from close(). Ideally the compiler should warn
> if you are not checking the return value from close() and in the
> second case above, you would use "(void)close(fd);".
>
> I think the compiler can warn if return values are not used. Possibly
> this is controlled using some GCC attribute.
>
> BTW: This patch series is obsolete, I am currently rebasing it.
>
>
>> Maxim.
>>
>> _______________________________________________
>> lng-odp mailing list
>> lng-odp@lists.linaro.org
>> http://lists.linaro.org/mailman/listinfo/lng-odp
Ola Liljedahl Feb. 2, 2015, 11:35 a.m. | #5
On 2 February 2015 at 12:26, Maxim Uvarov <maxim.uvarov@linaro.org> wrote:
> On 02/02/2015 02:18 PM, Ola Liljedahl wrote:
>>
>> On 2 February 2015 at 12:05, Maxim Uvarov <maxim.uvarov@linaro.org> wrote:
>>>
>>> On 01/29/2015 01:39 PM, Ola Liljedahl wrote:
>>>>
>>>>          num_workers = odph_linux_cpumask_default(&cpumask,
>>>> num_workers);
>>>> -       odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
>>>> +       size_t bufsz = sizeof(cpumaskstr);
>>>> +       (void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
>>>>
>>>
>>> why did add this (void)?
>>
>> Because odp_cpumask_to_str() has a return value that you normally
>> should check. If I think the return value for some reason doesn't
>> merit checking, then I cast the function call to "(void)".
>
> In current repo it's void:
>
> void odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int len);
Yes but the patch changed that. The formatting can fail because the
specified output buffer is too small. So the call needs a return
value.

>
>>
>> If you are closing a file/socket descriptor you have written to, you
>> should check the return value from close() because writes may have
>> been buffered and close may fail writing out your data.. But if the
>> file descriptor was e.g. only used for reading, then you don't need to
>> check the return value from close(). Ideally the compiler should warn
>> if you are not checking the return value from close() and in the
>> second case above, you would use "(void)close(fd);".
>>
>> I think the compiler can warn if return values are not used. Possibly
>> this is controlled using some GCC attribute.
>>
>> BTW: This patch series is obsolete, I am currently rebasing it.
>>
>>
>>> Maxim.
>>>
>>> _______________________________________________
>>> lng-odp mailing list
>>> lng-odp@lists.linaro.org
>>> http://lists.linaro.org/mailman/listinfo/lng-odp
>
>
Maxim Uvarov Feb. 2, 2015, 11:48 a.m. | #6
On 02/02/2015 02:35 PM, Ola Liljedahl wrote:
> On 2 February 2015 at 12:26, Maxim Uvarov <maxim.uvarov@linaro.org> wrote:
>> On 02/02/2015 02:18 PM, Ola Liljedahl wrote:
>>> On 2 February 2015 at 12:05, Maxim Uvarov <maxim.uvarov@linaro.org> wrote:
>>>> On 01/29/2015 01:39 PM, Ola Liljedahl wrote:
>>>>>           num_workers = odph_linux_cpumask_default(&cpumask,
>>>>> num_workers);
>>>>> -       odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
>>>>> +       size_t bufsz = sizeof(cpumaskstr);
>>>>> +       (void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
>>>>>
>>>> why did add this (void)?
>>> Because odp_cpumask_to_str() has a return value that you normally
>>> should check. If I think the return value for some reason doesn't
>>> merit checking, then I cast the function call to "(void)".
>> In current repo it's void:
>>
>> void odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int len);
> Yes but the patch changed that. The formatting can fail because the
> specified output buffer is too small. So the call needs a return
> value.

Ah, ok. Somehow I skipped that and was surprised casting from void to void.
Patch looks good.

Maxim.

>
>>> If you are closing a file/socket descriptor you have written to, you
>>> should check the return value from close() because writes may have
>>> been buffered and close may fail writing out your data.. But if the
>>> file descriptor was e.g. only used for reading, then you don't need to
>>> check the return value from close(). Ideally the compiler should warn
>>> if you are not checking the return value from close() and in the
>>> second case above, you would use "(void)close(fd);".
>>>
>>> I think the compiler can warn if return values are not used. Possibly
>>> this is controlled using some GCC attribute.
>>>
>>> BTW: This patch series is obsolete, I am currently rebasing it.
>>>
>>>
>>>> Maxim.
>>>>
>>>> _______________________________________________
>>>> lng-odp mailing list
>>>> lng-odp@lists.linaro.org
>>>> http://lists.linaro.org/mailman/listinfo/lng-odp
>>
Mike Holmes Feb. 2, 2015, 12:07 p.m. | #7
On 2 February 2015 at 06:22, Ola Liljedahl <ola.liljedahl@linaro.org> wrote:

> On 2 February 2015 at 12:18, Ola Liljedahl <ola.liljedahl@linaro.org>
> wrote:
> > On 2 February 2015 at 12:05, Maxim Uvarov <maxim.uvarov@linaro.org>
> wrote:
> >> On 01/29/2015 01:39 PM, Ola Liljedahl wrote:
> >>>
> >>>         num_workers = odph_linux_cpumask_default(&cpumask,
> num_workers);
> >>> -       odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
> >>> +       size_t bufsz = sizeof(cpumaskstr);
> >>> +       (void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
> >>>
> >>
> >>
> >> why did add this (void)?
> > Because odp_cpumask_to_str() has a return value that you normally
> > should check. If I think the return value for some reason doesn't
> > merit checking, then I cast the function call to "(void)".
> >
> > If you are closing a file/socket descriptor you have written to, you
> > should check the return value from close() because writes may have
> > been buffered and close may fail writing out your data.. But if the
> > file descriptor was e.g. only used for reading, then you don't need to
> > check the return value from close(). Ideally the compiler should warn
> > if you are not checking the return value from close() and in the
> > second case above, you would use "(void)close(fd);".
> >
> > I think the compiler can warn if return values are not used. Possibly
> > this is controlled using some GCC attribute.
>
> http://stackoverflow.com/questions/2870529/g-how-to-get-warning-on-ignoring-function-return-value
>
> Something for ODP?
>

Looks reasonable - coverity is currently list two such cases, it is smart
enough to work out that you "usually" check the return for a particular
function rather than just warn every time

CID 85004: Unchecked return value (CHECKED_RETURN)6. check_return: Calling
_odp_packet_copy_to_packet without checking return value (as is done
elsewhere 5 out of 6 times).
369                _odp_packet_copy_to_packet(params->pkt, 0, params->
out_pkt, 0,
370                                           odp_packet_len(params->pkt));



>
> > BTW: This patch series is obsolete, I am currently rebasing it.
> >
> >
> >>
> >> Maxim.
> >>
> >> _______________________________________________
> >> lng-odp mailing list
> >> lng-odp@lists.linaro.org
> >> http://lists.linaro.org/mailman/listinfo/lng-odp
>
> _______________________________________________
> lng-odp mailing list
> lng-odp@lists.linaro.org
> http://lists.linaro.org/mailman/listinfo/lng-odp
>

Patch

diff --git a/example/generator/odp_generator.c b/example/generator/odp_generator.c
index 492664e..635d15d 100644
--- a/example/generator/odp_generator.c
+++ b/example/generator/odp_generator.c
@@ -545,7 +545,7 @@  int main(int argc, char *argv[])
 	int i;
 	odp_shm_t shm;
 	odp_cpumask_t cpumask;
-	char cpumaskstr[64];
+	char cpumaskstr[ODP_CPUMASK_BUFSIZE];
 	odp_pool_param_t params;
 
 	/* Init ODP before calling anything else */
@@ -596,7 +596,8 @@  int main(int argc, char *argv[])
 	 * Start mapping thread from CPU #1
 	 */
 	num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
-	odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+	size_t bufsz = sizeof(cpumaskstr);
+	(void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
 
 	printf("num worker threads: %i\n", num_workers);
 	printf("first CPU:          %i\n", odp_cpumask_first(&cpumask));
diff --git a/example/ipsec/odp_ipsec.c b/example/ipsec/odp_ipsec.c
index f2d2fc7..730491d 100644
--- a/example/ipsec/odp_ipsec.c
+++ b/example/ipsec/odp_ipsec.c
@@ -1175,7 +1175,7 @@  main(int argc, char *argv[])
 	int stream_count;
 	odp_shm_t shm;
 	odp_cpumask_t cpumask;
-	char cpumaskstr[64];
+	char cpumaskstr[ODP_CPUMASK_BUFSIZE];
 	odp_pool_param_t params;
 
 	/* Init ODP before calling anything else */
@@ -1224,7 +1224,8 @@  main(int argc, char *argv[])
 	 * Start mapping thread from CPU #1
 	 */
 	num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
-	odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+	size_t bufsz = sizeof(cpumaskstr);
+	(void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
 
 	printf("num worker threads: %i\n", num_workers);
 	printf("first CPU:          %i\n", odp_cpumask_first(&cpumask));
diff --git a/example/l2fwd/odp_l2fwd.c b/example/l2fwd/odp_l2fwd.c
index 18403da..9a550fc 100644
--- a/example/l2fwd/odp_l2fwd.c
+++ b/example/l2fwd/odp_l2fwd.c
@@ -292,7 +292,7 @@  int main(int argc, char *argv[])
 	int num_workers;
 	odp_shm_t shm;
 	odp_cpumask_t cpumask;
-	char cpumaskstr[64];
+	char cpumaskstr[ODP_CPUMASK_BUFSIZE];
 	odp_pool_param_t params;
 
 	/* Init ODP before calling anything else */
@@ -334,7 +334,8 @@  int main(int argc, char *argv[])
 	 * Start mapping thread from CPU #1
 	 */
 	num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
-	odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+	size_t bufsz = sizeof(cpumaskstr);
+	(void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
 
 	printf("num worker threads: %i\n", num_workers);
 	printf("first CPU:          %i\n", odp_cpumask_first(&cpumask));
diff --git a/example/packet/odp_pktio.c b/example/packet/odp_pktio.c
index c4c720b..924a6c7 100644
--- a/example/packet/odp_pktio.c
+++ b/example/packet/odp_pktio.c
@@ -285,7 +285,7 @@  int main(int argc, char *argv[])
 	int i;
 	int cpu;
 	odp_cpumask_t cpumask;
-	char cpumaskstr[64];
+	char cpumaskstr[ODP_CPUMASK_BUFSIZE];
 	odp_pool_param_t params;
 
 	args = calloc(1, sizeof(args_t));
@@ -322,7 +322,8 @@  int main(int argc, char *argv[])
 	 * Start mapping thread from CPU #1
 	 */
 	num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
-	odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+	size_t bufsz = sizeof(cpumaskstr);
+	(void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
 
 	printf("num worker threads: %i\n", num_workers);
 	printf("first CPU:          %i\n", odp_cpumask_first(&cpumask));
diff --git a/example/timer/odp_timer_test.c b/example/timer/odp_timer_test.c
index fafa0a4..e33468b 100644
--- a/example/timer/odp_timer_test.c
+++ b/example/timer/odp_timer_test.c
@@ -317,7 +317,7 @@  int main(int argc, char *argv[])
 	odp_timer_pool_param_t tparams;
 	odp_timer_pool_info_t tpinfo;
 	odp_cpumask_t cpumask;
-	char cpumaskstr[64];
+	char cpumaskstr[ODP_CPUMASK_BUFSIZE];
 
 	printf("\nODP timer example starts\n");
 
@@ -358,7 +358,8 @@  int main(int argc, char *argv[])
 	 * Start mapping thread from CPU #1
 	 */
 	num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
-	odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+	size_t bufsz = sizeof(cpumaskstr);
+	(void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
 
 	printf("num worker threads: %i\n", num_workers);
 	printf("first CPU:          %i\n", odp_cpumask_first(&cpumask));
diff --git a/platform/linux-generic/include/api/odp_cpumask.h b/platform/linux-generic/include/api/odp_cpumask.h
index b746a4d..1784d93 100644
--- a/platform/linux-generic/include/api/odp_cpumask.h
+++ b/platform/linux-generic/include/api/odp_cpumask.h
@@ -22,6 +22,7 @@  extern "C" {
 #include <sched.h>
 
 #include <odp_std_types.h>
+#include <odp_config.h>
 
 /** @addtogroup odp_scheduler
  *  CPU mask operations.
@@ -29,6 +30,11 @@  extern "C" {
  */
 
 /**
+ * Minimum size of output buffer for odp_cpumask_to_str()
+ */
+#define ODP_CPUMASK_BUFSIZE ((ODP_CONFIG_MAX_THREADS + 3) / 4 + 3)
+
+/**
  * CPU mask
  *
  * Don't access directly, use access functions.
@@ -47,111 +53,127 @@  typedef struct odp_cpumask_t {
 void odp_cpumask_from_str(odp_cpumask_t *mask, const char *str);
 
 /**
- * Write CPU mask as a string of hexadecimal digits
+ * Format CPU mask as a string of hexadecimal digits
+ *
+ * @param mask CPU mask to format
+ * @param[out] buf Output buffer
+ * @param[in,out] bufsz Size of output buffer, updated with size of data
+ * actually written or minimum required buffer size
  *
- * @param mask   CPU mask
- * @param str    String for output
- * @param len    Size of string length (incl. ending zero)
+ * @return 0 on success and '*bufsz' updated with size of data written
+ * @retval <0 on failure (buffer too small) and '*bufsz' updated with minumum
+ * required buffer size
  */
-void odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int len);
+int odp_cpumask_to_str(const odp_cpumask_t *mask, char *buf, size_t *bufsz);
 
 /**
- * Clear entire mask
- * @param mask	CPU mask to flush with zero value
+ * Clear entire CPU mask
+ * @param mask	CPU mask to update
  */
 void odp_cpumask_zero(odp_cpumask_t *mask);
 
 /**
- * Add cpu to mask
- * @param mask  add cpu number in CPU mask
+ * Add CPU to mask
+ * @param mask  CPU mask to update
  * @param cpu   CPU number
  */
 void odp_cpumask_set(odp_cpumask_t *mask, int cpu);
 
 /**
- * Remove cpu from mask
- * @param mask  clear cpu number from CPU mask
+ * Remove CPU from mask
+ * @param mask  CPU mask to update
  * @param cpu   CPU number
  */
 void odp_cpumask_clr(odp_cpumask_t *mask, int cpu);
 
 /**
- * Test if cpu is a member of mask
- * @param mask  CPU mask to check if cpu num set or not
+ * Test if CPU is a member of mask
+ * @param mask  CPU mask to test
  * @param cpu   CPU number
- * @return      non-zero if set otherwise 0
+ * @return      non-zero if set
+ * @retval	0 if not set
  */
 int odp_cpumask_isset(const odp_cpumask_t *mask, int cpu);
 
 /**
- * Count number of cpus in mask
+ * Count number of CPU's in mask
  * @param mask  CPU mask
- * @return cpumask count
+ * @return population count
  */
 int odp_cpumask_count(const odp_cpumask_t *mask);
 
 /**
- * Logical AND over two source masks.
+ * Member-wise AND over two CPU masks
  *
- * @param dest    Destination mask, can be one of the source masks
- * @param src1    Source mask 1
- * @param src2    Source mask 2
+ * @param dest    Destination CPU mask (may be one of the source masks)
+ * @param src1    Source CPU mask 1
+ * @param src2    Source CPU mask 2
  */
 void odp_cpumask_and(odp_cpumask_t *dest, odp_cpumask_t *src1,
 		     odp_cpumask_t *src2);
 
 /**
- * Logical OR over two source masks.
+ * Member-wise OR over two CPU masks
  *
- * @param dest    Destination mask, can be one of the source masks
- * @param src1    Source mask 1
- * @param src2    Source mask 2
+ * @param dest    Destination CPU mask (may be one of the source masks)
+ * @param src1    Source CPU mask 1
+ * @param src2    Source CPU mask 2
  */
 void odp_cpumask_or(odp_cpumask_t *dest, odp_cpumask_t *src1,
 		    odp_cpumask_t *src2);
 
 /**
- * Logical XOR over two source masks.
+ * Member-wise XOR over two CPU masks
  *
- * @param dest    Destination mask, can be one of the source masks
- * @param src1    Source mask 1
- * @param src2    Source mask 2
+ * @param dest    Destination CPU mask (may be one of the source masks)
+ * @param src1    Source CPU mask 1
+ * @param src2    Source CPU mask 2
  */
 void odp_cpumask_xor(odp_cpumask_t *dest, odp_cpumask_t *src1,
 		     odp_cpumask_t *src2);
 
 /**
- * Test if two masks contain the same cpus
+ * Test if two CPU masks contain the same CPU's
+ *
+ * @param mask1    CPU mask 1
+ * @param mask2    CPU mask 2
  */
 int odp_cpumask_equal(const odp_cpumask_t *mask1,
 		      const odp_cpumask_t *mask2);
 
 /**
  * Copy a CPU mask
+ *
+ * @param dest    Destination CPU mask
+ * @param src     Source CPU mask
  */
 void odp_cpumask_copy(odp_cpumask_t *dest, const odp_cpumask_t *src);
 
 /**
- * Find first CPU that is set in the mask
+ * Find first set CPU in mask
  *
- * @param mask is the mask to be searched
- * @return cpu else -1 if no bits set in cpumask
+ * @param mask    CPU mask
+ *
+ * @return cpu number
+ * @retval <0 if no CPU found
  */
 int odp_cpumask_first(const odp_cpumask_t *mask);
 
 /**
- * Find last CPU that is set in the mask
+ * Find last set CPU in mask
+ *
+ * @param mask    CPU mask
  *
- * @param mask is the mask to be searched
- * @return cpu else -1 if no bits set in cpumask
+ * @return cpu number
+ * @retval <0 if no CPU found
  */
 int odp_cpumask_last(const odp_cpumask_t *mask);
 
 /**
- * Find next cpu in mask
+ * Find next set CPU in mask
  *
- * Finds the next cpu in the CPU mask, starting at the cpu passed.
- * Use with odp_cpumask_first to traverse a CPU mask, i.e.
+ * Finds the next CPU in the CPU mask, starting at the CPU passed.
+ * Use with odp_cpumask_first to traverse a CPU mask, e.g.
  *
  * int cpu = odp_cpumask_first(&mask);
  * while (0 <= cpu) {
@@ -160,9 +182,10 @@  int odp_cpumask_last(const odp_cpumask_t *mask);
  *     cpu = odp_cpumask_next(&mask, cpu);
  * }
  *
- * @param mask        CPU mask to find next cpu in
+ * @param mask        CPU mask
  * @param cpu         CPU to start from
- * @return cpu found else -1
+ * @return cpu number
+ * @retval <0 if no CPU found
  *
  * @see odp_cpumask_first()
  */
diff --git a/platform/linux-generic/odp_cpumask.c b/platform/linux-generic/odp_cpumask.c
index d9931b4..5c571d9 100644
--- a/platform/linux-generic/odp_cpumask.c
+++ b/platform/linux-generic/odp_cpumask.c
@@ -8,6 +8,7 @@ 
 #define _GNU_SOURCE
 #endif
 #include <sched.h>
+#include <sys/types.h>
 
 #include <odp_cpumask.h>
 #include <odp_debug_internal.h>
@@ -60,29 +61,36 @@  void odp_cpumask_from_str(odp_cpumask_t *mask, const char *str_in)
 	memcpy(&mask->set, &cpuset, sizeof(cpuset));
 }
 
-void odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int len)
+int odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, size_t *len)
 {
 	char *p = str;
 	int cpu = odp_cpumask_last(mask);
-	int nibbles;
+	unsigned int nibbles;
 	int value;
 
-	/* Quickly handle bad string length or empty mask */
-	if (len <= 0)
-		return;
-	*str = 0;
+	/* Handle bad string length, need at least 4 chars for "0x0" and
+	 * terminating null char */
+	if (*len < 4) {
+		*len = ODP_CPUMASK_BUFSIZE;
+		return -1; /* Failure */
+	}
+
+	/* Handle no CPU found */
 	if (cpu < 0) {
-		if (len >= 4)
-			strcpy(str, "0x0");
-		return;
+		strcpy(str, "0x0");
+		*len = strlen(str) + 1; /* Include terminating null char */
+		return 0; /* Success */
 	}
+	/* CPU was found and cpu >= 0 */
 
-	/* Compute number nibbles in cpumask that have bits set */
+	/* Compute number of nibbles in cpumask that have bits set */
 	nibbles = (cpu / 4) + 1;
 
 	/* Verify minimum space (account for "0x" and termination) */
-	if (len < (3 + nibbles))
-		return;
+	if (*len < (3 + nibbles)) {
+		*len = ODP_CPUMASK_BUFSIZE;
+		return -1; /* Failure */
+	}
 
 	/* Prefix */
 	*p++ = '0';
@@ -110,6 +118,8 @@  void odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int len)
 
 	/* Terminate the string */
 	*p++ = 0;
+	*len = p - str;
+	return 0; /* Success */
 }
 
 void odp_cpumask_zero(odp_cpumask_t *mask)
diff --git a/test/api_test/odp_common.c b/test/api_test/odp_common.c
index bce6f09..c8e9f78 100644
--- a/test/api_test/odp_common.c
+++ b/test/api_test/odp_common.c
@@ -30,14 +30,15 @@  __thread test_shared_data_t *test_shared_data;	    /**< pointer to shared data *
 void odp_print_system_info(void)
 {
 	odp_cpumask_t cpumask;
-	char str[32];
+	char str[ODP_CPUMASK_BUFSIZE];
+	size_t strsz = sizeof(str);
 
 	memset(str, 1, sizeof(str));
 
 	odp_cpumask_zero(&cpumask);
 
 	odp_cpumask_from_str(&cpumask, "0x1");
-	odp_cpumask_to_str(&cpumask, str, sizeof(str));
+	(void)odp_cpumask_to_str(&cpumask, str, &strsz);
 
 	printf("\n");
 	printf("ODP system info\n");
diff --git a/test/performance/odp_scheduling.c b/test/performance/odp_scheduling.c
index 32155bd..fbe22b8 100644
--- a/test/performance/odp_scheduling.c
+++ b/test/performance/odp_scheduling.c
@@ -828,7 +828,8 @@  int main(int argc, char *argv[])
 	int prios;
 	odp_shm_t shm;
 	test_globals_t *globals;
-	char cpumaskstr[64];
+	char cpumaskstr[ODP_CPUMASK_BUFSIZE];
+	size_t bufsz = sizeof(cpumaskstr);
 	odp_pool_param_t params;
 
 	printf("\nODP example starts\n\n");
@@ -879,7 +880,7 @@  int main(int argc, char *argv[])
 	 * Start mapping thread from CPU #1
 	 */
 	num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
-	odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+	(void)odp_cpumask_to_str(&cpumask, cpumaskstr, &bufsz);
 
 	printf("num worker threads: %i\n", num_workers);
 	printf("first CPU:          %i\n", odp_cpumask_first(&cpumask));