net: buf: Support copying of user data

This functionality is useful on the following scenario:

1) The first buffer in a net_pkt contains user data which is relevant
   for the (whole) net_pkt.
2) When inserting a new buffer in front of the net_pkt, the (previously)
   first buffer (and its user data) are no longer accessible via
   net_pkt->buffer.
3) net_buf_user_data_copy() allows to simply copy the user data from the
   old to the new first  buffer.

Signed-off-by: Reto Schneider <reto.schneider@husqvarnagroup.com>
This commit is contained in:
Reto Schneider 2024-03-20 16:41:25 +01:00 committed by Alberto Escolar
parent 9bf0a24f2e
commit 9dd87a7194
3 changed files with 62 additions and 0 deletions

View file

@ -1467,6 +1467,17 @@ static inline void * __must_check net_buf_user_data(const struct net_buf *buf)
return (void *)buf->user_data; return (void *)buf->user_data;
} }
/**
* @brief Copy user data from one to another buffer.
*
* @param dst A valid pointer to a buffer gettings its user data overwritten.
* @param src A valid pointer to a buffer gettings its user data copied. User data size must be
* equal to or exceed @a dst.
*
* @return 0 on success or negative error number on failure.
*/
int net_buf_user_data_copy(struct net_buf *dst, const struct net_buf *src);
/** /**
* @brief Initialize buffer with the given headroom. * @brief Initialize buffer with the given headroom.
* *

View file

@ -542,6 +542,24 @@ struct net_buf *net_buf_clone(struct net_buf *buf, k_timeout_t timeout)
return clone; return clone;
} }
int net_buf_user_data_copy(struct net_buf *dst, const struct net_buf *src)
{
__ASSERT_NO_MSG(dst);
__ASSERT_NO_MSG(src);
if (dst == src) {
return 0;
}
if (dst->user_data_size < src->user_data_size) {
return -EINVAL;
}
memcpy(dst->user_data, src->user_data, src->user_data_size);
return 0;
}
struct net_buf *net_buf_frag_last(struct net_buf *buf) struct net_buf *net_buf_frag_last(struct net_buf *buf)
{ {
__ASSERT_NO_MSG(buf); __ASSERT_NO_MSG(buf);

View file

@ -756,6 +756,39 @@ ZTEST(net_buf_tests, test_net_buf_user_data)
net_buf_unref(buf); net_buf_unref(buf);
} }
ZTEST(net_buf_tests, test_net_buf_user_data_copy)
{
struct net_buf *buf_user_data_small, *buf_user_data_big;
uint32_t *src_user_data, *dst_user_data;
buf_user_data_small = net_buf_alloc_len(&bufs_pool, 1, K_NO_WAIT);
zassert_not_null(buf_user_data_small, "Failed to get buffer");
src_user_data = net_buf_user_data(buf_user_data_small);
*src_user_data = 0xAABBCCDD;
/* Happy case: Size of user data in destination buf is bigger than the source buf one */
buf_user_data_big = net_buf_alloc_len(&var_pool, 1, K_NO_WAIT);
zassert_not_null(buf_user_data_big, "Failed to get buffer");
dst_user_data = net_buf_user_data(buf_user_data_big);
*dst_user_data = 0x11223344;
zassert_ok(net_buf_user_data_copy(buf_user_data_big, buf_user_data_small));
zassert_equal(*src_user_data, 0xAABBCCDD);
/* Error case: User data size of destination buffer is too small */
zassert_not_ok(net_buf_user_data_copy(buf_user_data_small, buf_user_data_big),
"User data size in destination buffer too small");
net_buf_unref(buf_user_data_big);
/* Corner case: Same buffer used as source and target */
zassert_ok(net_buf_user_data_copy(buf_user_data_small, buf_user_data_small),
"No-op is tolerated");
zassert_equal(*src_user_data, 0xAABBCCDD, "User data remains the same");
net_buf_unref(buf_user_data_small);
}
ZTEST(net_buf_tests, test_net_buf_comparison) ZTEST(net_buf_tests, test_net_buf_comparison)
{ {
struct net_buf *buf; struct net_buf *buf;