Skip to content

net: Add timeout to various send functions #86127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/zephyr/net/net_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -1199,7 +1199,7 @@ int net_context_send(struct net_context *context,
* @param dst_addr Destination address.
* @param addrlen Length of the address.
* @param cb Caller-supplied callback function.
* @param timeout Currently this value is not used.
* @param timeout Timeout for the send attempt.
* @param user_data Caller-supplied user data.
*
* @return numbers of bytes sent on success, a negative errno otherwise
Expand Down
24 changes: 22 additions & 2 deletions include/zephyr/net/net_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,38 @@ enum net_verdict {
int net_recv_data(struct net_if *iface, struct net_pkt *pkt);

/**
* @brief Send data to network.
* @brief Try sending data to network.
*
* @details Send data to network. This should not be used normally by
* applications as it requires that the network packet is properly
* constructed.
*
* @param pkt Network packet.
* @param timeout Timeout for send.
*
* @return 0 if ok, <0 if error. If <0 is returned, then the caller needs
* to unref the pkt in order to avoid memory leak.
*/
int net_send_data(struct net_pkt *pkt);
int net_try_send_data(struct net_pkt *pkt, k_timeout_t timeout);

/**
* @brief Send data to network.
*
* @details Send data to network. This should not be used normally by
* applications as it requires that the network packet is properly
* constructed. Equivalent to net_try_send_data with infinite timeout.
*
* @param pkt Network packet.
*
* @return 0 if ok, <0 if error. If <0 is returned, then the caller needs
* to unref the pkt in order to avoid memory leak.
*/
static inline int net_send_data(struct net_pkt *pkt)
{
k_timeout_t timeout = k_is_in_isr() ? K_NO_WAIT : K_FOREVER;

return net_try_send_data(pkt, timeout);
}

/** @cond INTERNAL_HIDDEN */

Expand Down
40 changes: 37 additions & 3 deletions include/zephyr/net/net_if.h
Original file line number Diff line number Diff line change
Expand Up @@ -911,15 +911,34 @@ static inline enum net_if_oper_state net_if_oper_state(struct net_if *iface)
return iface->if_dev->oper_state;
}

/**
* @brief Try sending a packet through a net iface
*
* @param iface Pointer to a network interface structure
* @param pkt Pointer to a net packet to send
* @param timeout timeout for attempting to send
*
* @return verdict about the packet
*/
enum net_verdict net_if_try_send_data(struct net_if *iface,
struct net_pkt *pkt, k_timeout_t timeout);

/**
* @brief Send a packet through a net iface
*
* This is equivalent to net_if_try_queue_tx with an infinite timeout
* @param iface Pointer to a network interface structure
* @param pkt Pointer to a net packet to send
*
* return verdict about the packet
* @return verdict about the packet
*/
enum net_verdict net_if_send_data(struct net_if *iface, struct net_pkt *pkt);
static inline enum net_verdict net_if_send_data(struct net_if *iface,
struct net_pkt *pkt)
{
k_timeout_t timeout = k_is_in_isr() ? K_NO_WAIT : K_FOREVER;

return net_if_try_send_data(iface, pkt, timeout);
}

/**
* @brief Get a pointer to the interface L2
Expand Down Expand Up @@ -979,13 +998,28 @@ static inline const struct device *net_if_get_device(struct net_if *iface)
return iface->if_dev->dev;
}

/**
* @brief Try enqueuing a packet to the net interface TX queue
*
* @param iface Pointer to a network interface structure
* @param pkt Pointer to a net packet to queue
* @param timeout Timeout for the enqueuing attempt
*/
void net_if_try_queue_tx(struct net_if *iface, struct net_pkt *pkt, k_timeout_t timeout);

/**
* @brief Queue a packet to the net interface TX queue
*
* This is equivalent to net_if_try_queue_tx with an infinite timeout
* @param iface Pointer to a network interface structure
* @param pkt Pointer to a net packet to queue
*/
void net_if_queue_tx(struct net_if *iface, struct net_pkt *pkt);
static inline void net_if_queue_tx(struct net_if *iface, struct net_pkt *pkt)
{
k_timeout_t timeout = k_is_in_isr() ? K_NO_WAIT : K_FOREVER;

net_if_try_queue_tx(iface, pkt, timeout);
}

/**
* @brief Return the IP offload status
Expand Down
4 changes: 2 additions & 2 deletions subsys/net/ip/icmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ static int send_icmpv4_echo_request(struct net_icmp_ctx *ctx,
ctx->user_data = user_data;
ctx->iface = iface;

if (net_send_data(pkt) >= 0) {
if (net_try_send_data(pkt, K_NO_WAIT) >= 0) {
net_stats_update_icmp_sent(iface);
return 0;
}
Expand Down Expand Up @@ -330,7 +330,7 @@ static int send_icmpv6_echo_request(struct net_icmp_ctx *ctx,
ctx->user_data = user_data;
ctx->iface = iface;

if (net_send_data(pkt) >= 0) {
if (net_try_send_data(pkt, K_NO_WAIT) >= 0) {
net_stats_update_icmp_sent(iface);
return 0;
}
Expand Down
4 changes: 2 additions & 2 deletions subsys/net/ip/icmpv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ static int icmpv4_handle_echo_request(struct net_icmp_ctx *ctx,
net_sprint_ipv4_addr(src),
net_sprint_ipv4_addr(&ip_hdr->src));

if (net_send_data(reply) < 0) {
if (net_try_send_data(reply, K_NO_WAIT) < 0) {
goto drop;
}

Expand Down Expand Up @@ -588,7 +588,7 @@ int net_icmpv4_send_error(struct net_pkt *orig, uint8_t type, uint8_t code)
net_sprint_ipv4_addr(&ip_hdr->dst),
net_sprint_ipv4_addr(&ip_hdr->src));

if (net_send_data(pkt) >= 0) {
if (net_try_send_data(pkt, K_NO_WAIT) >= 0) {
net_stats_update_icmp_sent(net_pkt_iface(orig));
return 0;
}
Expand Down
4 changes: 2 additions & 2 deletions subsys/net/ip/icmpv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ static int icmpv6_handle_echo_request(struct net_icmp_ctx *ctx,
net_sprint_ipv6_addr(src),
net_sprint_ipv6_addr(&ip_hdr->src));

if (net_send_data(reply) < 0) {
if (net_try_send_data(reply, K_NO_WAIT) < 0) {
goto drop;
}

Expand Down Expand Up @@ -319,7 +319,7 @@ int net_icmpv6_send_error(struct net_pkt *orig, uint8_t type, uint8_t code,
net_sprint_ipv6_addr(src),
net_sprint_ipv6_addr(&ip_hdr->src));

if (net_send_data(pkt) >= 0) {
if (net_try_send_data(pkt, K_NO_WAIT) >= 0) {
net_stats_update_icmp_sent(net_pkt_iface(pkt));
return 0;
}
Expand Down
8 changes: 4 additions & 4 deletions subsys/net/ip/net_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -2544,7 +2544,7 @@ static int context_sendto(struct net_context *context,

context_finalize_packet(context, family, pkt);

ret = net_send_data(pkt);
ret = net_try_send_data(pkt, timeout);
} else if (IS_ENABLED(CONFIG_NET_TCP) &&
net_context_get_proto(context) == IPPROTO_TCP) {

Expand Down Expand Up @@ -2588,7 +2588,7 @@ static int context_sendto(struct net_context *context,
}

/* Pass to L2: */
ret = net_send_data(pkt);
ret = net_try_send_data(pkt, timeout);
} else {
struct sockaddr_ll_ptr *ll_src_addr;
struct sockaddr_ll *ll_dst_addr;
Expand All @@ -2609,7 +2609,7 @@ static int context_sendto(struct net_context *context,
net_pkt_set_ll_proto_type(pkt,
ntohs(ll_dst_addr->sll_protocol));

net_if_queue_tx(net_pkt_iface(pkt), pkt);
net_if_try_queue_tx(net_pkt_iface(pkt), pkt, timeout);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

context_sendto() has actually more places where data is passed to the lower layer (net_send_data() calls above and below).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, will take a look.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

}
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) && family == AF_CAN &&
net_context_get_proto(context) == CAN_RAW) {
Expand All @@ -2620,7 +2620,7 @@ static int context_sendto(struct net_context *context,

net_pkt_cursor_init(pkt);

ret = net_send_data(pkt);
ret = net_try_send_data(pkt, timeout);
} else {
NET_DBG("Unknown protocol while sending packet: %d",
net_context_get_proto(context));
Expand Down
8 changes: 4 additions & 4 deletions subsys/net/ip/net_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,7 @@ static inline int check_ip(struct net_pkt *pkt)
return ret;
}

/* Called when data needs to be sent to network */
int net_send_data(struct net_pkt *pkt)
int net_try_send_data(struct net_pkt *pkt, k_timeout_t timeout)
{
int status;
int ret;
Expand Down Expand Up @@ -407,7 +406,7 @@ int net_send_data(struct net_pkt *pkt)
goto err;
}

if (net_if_send_data(net_pkt_iface(pkt), pkt) == NET_DROP) {
if (net_if_try_send_data(net_pkt_iface(pkt), pkt, timeout) == NET_DROP) {
ret = -EIO;
goto err;
}
Expand Down Expand Up @@ -570,9 +569,10 @@ static inline void l3_init(void)
#else /* CONFIG_NET_NATIVE */
#define l3_init(...)
#define net_post_init(...)
int net_send_data(struct net_pkt *pkt)
int net_try_send_data(struct net_pkt *pkt, k_timeout_t timeout)
{
ARG_UNUSED(pkt);
ARG_UNUSED(timeout);

return -ENOTSUP;
}
Expand Down
9 changes: 5 additions & 4 deletions subsys/net/ip/net_if.c
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ void net_process_tx_packet(struct net_pkt *pkt)
#endif
}

void net_if_queue_tx(struct net_if *iface, struct net_pkt *pkt)
void net_if_try_queue_tx(struct net_if *iface, struct net_pkt *pkt, k_timeout_t timeout)
{
if (!net_pkt_filter_send_ok(pkt)) {
/* silently drop the packet */
Expand All @@ -368,7 +368,7 @@ void net_if_queue_tx(struct net_if *iface, struct net_pkt *pkt)

net_if_tx(net_pkt_iface(pkt), pkt);
} else {
if (net_tc_submit_to_tx_queue(tc, pkt) != NET_OK) {
if (net_tc_try_submit_to_tx_queue(tc, pkt, timeout) != NET_OK) {
goto drop;
}
#if defined(CONFIG_NET_POWER_MANAGEMENT)
Expand Down Expand Up @@ -451,7 +451,8 @@ static inline void init_iface(struct net_if *iface)
}

#if defined(CONFIG_NET_NATIVE)
enum net_verdict net_if_send_data(struct net_if *iface, struct net_pkt *pkt)
enum net_verdict net_if_try_send_data(struct net_if *iface, struct net_pkt *pkt,
k_timeout_t timeout)
{
const struct net_l2 *l2;
struct net_context *context = net_pkt_context(pkt);
Expand Down Expand Up @@ -550,7 +551,7 @@ enum net_verdict net_if_send_data(struct net_if *iface, struct net_pkt *pkt)
}
} else if (verdict == NET_OK) {
/* Packet is ready to be sent by L2, let's queue */
net_if_queue_tx(iface, pkt);
net_if_try_queue_tx(iface, pkt, timeout);
}

return verdict;
Expand Down
3 changes: 2 additions & 1 deletion subsys/net/ip/net_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ static inline enum net_verdict net_ipv6_input(struct net_pkt *pkt,
static inline void net_tc_tx_init(void) { }
static inline void net_tc_rx_init(void) { }
#endif
extern enum net_verdict net_tc_submit_to_tx_queue(uint8_t tc, struct net_pkt *pkt);
enum net_verdict net_tc_try_submit_to_tx_queue(uint8_t tc, struct net_pkt *pkt,
k_timeout_t timeout);
extern enum net_verdict net_tc_submit_to_rx_queue(uint8_t tc, struct net_pkt *pkt);
extern enum net_verdict net_promisc_mode_input(struct net_pkt *pkt);

Expand Down
5 changes: 2 additions & 3 deletions subsys/net/ip/net_tc.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,13 @@ static struct net_traffic_class tx_classes[NET_TC_TX_COUNT];
static struct net_traffic_class rx_classes[NET_TC_RX_COUNT];
#endif

enum net_verdict net_tc_submit_to_tx_queue(uint8_t tc, struct net_pkt *pkt)
enum net_verdict net_tc_try_submit_to_tx_queue(uint8_t tc, struct net_pkt *pkt,
k_timeout_t timeout)
{
#if NET_TC_TX_COUNT > 0
net_pkt_set_tx_stats_tick(pkt, k_cycle_get_32());

#if NET_TC_TX_EFFECTIVE_COUNT > 1
k_timeout_t timeout = k_is_in_isr() ? K_NO_WAIT : K_FOREVER;

if (k_sem_take(&tx_classes[tc].fifo_slot, timeout) != 0) {
return NET_DROP;
}
Expand Down
7 changes: 5 additions & 2 deletions subsys/net/l2/ethernet/arp.c
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,10 @@ static void arp_gratuitous_send(struct net_if *iface,

NET_DBG("Sending gratuitous ARP pkt %p", pkt);

if (net_if_send_data(iface, pkt) == NET_DROP) {
/* send without timeout, so we do not risk being blocked by tx when
* being flooded
*/
if (net_if_try_send_data(iface, pkt, K_NO_WAIT) == NET_DROP) {
net_pkt_unref(pkt);
}
}
Expand Down Expand Up @@ -874,7 +877,7 @@ enum net_verdict net_arp_input(struct net_pkt *pkt,
/* Send reply */
reply = arp_prepare_reply(net_pkt_iface(pkt), pkt, dst_hw_addr);
if (reply) {
net_if_queue_tx(net_pkt_iface(reply), reply);
net_if_try_queue_tx(net_pkt_iface(reply), reply, K_NO_WAIT);
} else {
NET_DBG("Cannot send ARP reply");
}
Expand Down
5 changes: 4 additions & 1 deletion subsys/net/l2/ethernet/lldp/lldp.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,10 @@ static int lldp_send(struct ethernet_lldp *lldp)
net_pkt_lladdr_dst(pkt)->addr = (uint8_t *)lldp_multicast_eth_addr.addr;
net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_eth_addr);

if (net_if_send_data(lldp->iface, pkt) == NET_DROP) {
/* send without timeout, so we do not risk being blocked by tx when
* being flooded
*/
if (net_if_try_send_data(lldp->iface, pkt, K_NO_WAIT) == NET_DROP) {
net_pkt_unref(pkt);
ret = -EIO;
}
Expand Down
Loading