/*************************************************************************** * Copyright (c) 2024 Microsoft Corporation * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT **************************************************************************/ /**************************************************************************/ /**************************************************************************/ /** */ /** NetX Component */ /** */ /** Packet Pool Management (Packet) */ /** */ /**************************************************************************/ /**************************************************************************/ #define NX_SOURCE_CODE /* Include necessary system files. */ #include "nx_api.h" #include "tx_thread.h" #include "nx_packet.h" /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _nx_packet_allocate PORTABLE C */ /* 6.4.0 */ /* AUTHOR */ /* */ /* Yuxin Zhou, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function allocates a packet from the specified packet pool. */ /* */ /* INPUT */ /* */ /* pool_ptr Pool to allocate packet from */ /* packet_ptr Pointer to place allocated */ /* packet pointer */ /* packet_type Type of packet to allocate */ /* wait_option Suspension option */ /* */ /* OUTPUT */ /* */ /* status Completion status */ /* */ /* CALLS */ /* */ /* _tx_thread_system_suspend Suspend thread */ /* */ /* CALLED BY */ /* */ /* Application Code */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */ /* 09-30-2020 Yuxin Zhou Modified comment(s), */ /* resulting in version 6.1 */ /* 12-31-2023 Yajun Xia Modified comment(s), */ /* supported VLAN and generic */ /* link layer, */ /* resulting in version 6.4.0 */ /* */ /**************************************************************************/ UINT _nx_packet_allocate(NX_PACKET_POOL *pool_ptr, NX_PACKET **packet_ptr, ULONG packet_type, ULONG wait_option) { TX_INTERRUPT_SAVE_AREA UINT status; /* Return status */ TX_THREAD *thread_ptr; /* Working thread pointer */ NX_PACKET *work_ptr; /* Working packet pointer */ #ifdef TX_ENABLE_EVENT_TRACE TX_TRACE_BUFFER_ENTRY *trace_event; ULONG trace_timestamp; #endif /* Make sure the packet_type does not go beyond nx_packet_data_end. */ if (pool_ptr -> nx_packet_pool_payload_size < packet_type) { return(NX_INVALID_PARAMETERS); } /* Set the return pointer to NULL initially. */ *packet_ptr = NX_NULL; /* If trace is enabled, insert this event into the trace buffer. */ NX_TRACE_IN_LINE_INSERT(NX_TRACE_PACKET_ALLOCATE, pool_ptr, 0, packet_type, pool_ptr -> nx_packet_pool_available, NX_TRACE_PACKET_EVENTS, &trace_event, &trace_timestamp); /* Disable interrupts to get a packet from the pool. */ TX_DISABLE /* Determine if there is an available packet. */ if (pool_ptr -> nx_packet_pool_available) { /* Yes, a packet is available. Decrement the available count. */ pool_ptr -> nx_packet_pool_available--; /* Pickup the current packet pointer. */ work_ptr = pool_ptr -> nx_packet_pool_available_list; /* Modify the available list to point at the next packet in the pool. */ pool_ptr -> nx_packet_pool_available_list = work_ptr -> nx_packet_queue_next; /* Setup various fields for this packet. */ work_ptr -> nx_packet_queue_next = NX_NULL; #ifndef NX_DISABLE_PACKET_CHAIN work_ptr -> nx_packet_next = NX_NULL; work_ptr -> nx_packet_last = NX_NULL; #endif /* NX_DISABLE_PACKET_CHAIN */ work_ptr -> nx_packet_length = 0; work_ptr -> nx_packet_prepend_ptr = work_ptr -> nx_packet_data_start + packet_type; work_ptr -> nx_packet_append_ptr = work_ptr -> nx_packet_prepend_ptr; work_ptr -> nx_packet_address.nx_packet_interface_ptr = NX_NULL; #ifdef NX_ENABLE_INTERFACE_CAPABILITY work_ptr -> nx_packet_interface_capability_flag = 0; #endif /* NX_ENABLE_INTERFACE_CAPABILITY */ /* Set the TCP queue to the value that indicates it has been allocated. */ /*lint -e{923} suppress cast of ULONG to pointer. */ work_ptr -> nx_packet_union_next.nx_packet_tcp_queue_next = (NX_PACKET *)NX_PACKET_ALLOCATED; #ifdef FEATURE_NX_IPV6 /* Clear the option state. */ work_ptr -> nx_packet_option_state = 0; #endif /* FEATURE_NX_IPV6 */ #ifdef NX_IPSEC_ENABLE /* Clear the ipsec state. */ work_ptr -> nx_packet_ipsec_state = 0; work_ptr -> nx_packet_ipsec_sa_ptr = NX_NULL; #endif /* NX_IPSEC_ENABLE */ #ifndef NX_DISABLE_IPV4 /* Initialize the IP version field */ work_ptr -> nx_packet_ip_version = NX_IP_VERSION_V4; #endif /* !NX_DISABLE_IPV4 */ /* Initialize the IP identification flag. */ work_ptr -> nx_packet_identical_copy = NX_FALSE; #ifdef NX_ENABLE_VLAN /* Initialize the packet vlan priority */ work_ptr -> nx_packet_vlan_priority = NX_VLAN_PRIORITY_INVALID; #endif /* NX_ENABLE_VLAN */ /* Initialize the IP header length. */ work_ptr -> nx_packet_ip_header_length = 0; #ifdef NX_ENABLE_THREAD work_ptr -> nx_packet_type = 0; #endif /* NX_ENABLE_THREAD */ /* Place the new packet pointer in the return destination. */ *packet_ptr = work_ptr; /* Set status to success. */ status = NX_SUCCESS; /* Add debug information. */ NX_PACKET_DEBUG(__FILE__, __LINE__, work_ptr); } else { #ifndef NX_DISABLE_PACKET_INFO /* Increment the packet pool empty request count. */ pool_ptr -> nx_packet_pool_empty_requests++; #endif /* Determine if the request specifies suspension. */ if (wait_option) { /* Prepare for suspension of this thread. */ #ifndef NX_DISABLE_PACKET_INFO /* Increment the packet pool empty request suspension count. */ pool_ptr -> nx_packet_pool_empty_suspensions++; #endif /* Pickup thread pointer. */ thread_ptr = _tx_thread_current_ptr; /* Setup cleanup routine pointer. */ thread_ptr -> tx_thread_suspend_cleanup = _nx_packet_pool_cleanup; /* Setup cleanup information, i.e. this pool control block. */ thread_ptr -> tx_thread_suspend_control_block = (void *)pool_ptr; /* Save the return packet pointer address as well. */ thread_ptr -> tx_thread_additional_suspend_info = (void *)packet_ptr; /* Save the packet type (or prepend offset) so this can be added after a new packet becomes available. */ thread_ptr -> tx_thread_suspend_info = packet_type; /* Setup suspension list. */ if (pool_ptr -> nx_packet_pool_suspension_list) { /* This list is not NULL, add current thread to the end. */ thread_ptr -> tx_thread_suspended_next = pool_ptr -> nx_packet_pool_suspension_list; thread_ptr -> tx_thread_suspended_previous = (pool_ptr -> nx_packet_pool_suspension_list) -> tx_thread_suspended_previous; ((pool_ptr -> nx_packet_pool_suspension_list) -> tx_thread_suspended_previous) -> tx_thread_suspended_next = thread_ptr; (pool_ptr -> nx_packet_pool_suspension_list) -> tx_thread_suspended_previous = thread_ptr; } else { /* No other threads are suspended. Setup the head pointer and just setup this threads pointers to itself. */ pool_ptr -> nx_packet_pool_suspension_list = thread_ptr; thread_ptr -> tx_thread_suspended_next = thread_ptr; thread_ptr -> tx_thread_suspended_previous = thread_ptr; } /* Increment the suspended thread count. */ pool_ptr -> nx_packet_pool_suspended_count++; /* Set the state to suspended. */ thread_ptr -> tx_thread_state = TX_TCP_IP; /* Set the suspending flag. */ thread_ptr -> tx_thread_suspending = TX_TRUE; /* Temporarily disable preemption. */ _tx_thread_preempt_disable++; /* Save the timeout value. */ thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks = wait_option; /* Restore interrupts. */ TX_RESTORE /* Call actual thread suspension routine. */ _tx_thread_system_suspend(thread_ptr); /* Update the trace event with the status. */ NX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, NX_TRACE_PACKET_ALLOCATE, 0, *packet_ptr, 0, 0); #ifdef NX_ENABLE_PACKET_DEBUG_INFO if (thread_ptr -> tx_thread_suspend_status == NX_SUCCESS) { /* Add debug information. */ NX_PACKET_DEBUG(__FILE__, __LINE__, *packet_ptr); } #endif /* NX_ENABLE_PACKET_DEBUG_INFO */ /* Return the completion status. */ return(thread_ptr -> tx_thread_suspend_status); } else { /* Immediate return, return error completion. */ status = NX_NO_PACKET; } } /* Restore interrupts. */ TX_RESTORE /* Update the trace event with the status. */ NX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, NX_TRACE_PACKET_ALLOCATE, 0, *packet_ptr, 0, 0); /* Return completion status. */ return(status); }