qcacmn: Add APIs to defragment elem/subelem fragment sequence

Add APIs to defragment a sequence of 802.11 element/subelement
fragments. Add an inline mode to carry out the defragmentation within
the source buffer in order to reduce buffer requirements and to
eliminate memory copies/moves for the lead element/sublement.
Conversely, add a non-inline mode to copy the defragmented payload to
a separate buffer.

Add common helper functionality shared between these APIs.

CRs-Fixed: 3124132
Change-Id: Ia68584e918ddcf626b748f2b0c3a5da6492b40b6
This commit is contained in:
Krishna Rao
2022-01-27 23:33:57 +05:30
committed by Gerrit - the friendly Code Review server
parent 94df448341
commit 3f38db23d4
2 changed files with 377 additions and 0 deletions

View File

@@ -414,6 +414,78 @@ QDF_STATUS wlan_get_elem_fragseq_info(uint8_t *elembuff,
qdf_size_t *fragseq_totallen,
qdf_size_t *fragseq_payloadlen);
/**
* wlan_defrag_elem_fragseq() - Defragment sequence of element fragments
*
* @inline_defrag: Whether to use inline defragmentation, wherein the
* defragmentation is carried out inline within the source buffer and no
* memmoves/memcopy would be required for the lead element.
* @fragbuff: Source buffer containing the element fragment sequence starting
* with the Element ID of the lead element. The buffer should not contain any
* material other than elements. If inline defragmentation is enabled, the
* corresponding defragmented payload will be generated inline into this buffer
* and the defragmented payload will start after the location of the lead
* element's element ID, element length, and (if the lead element's element ID
* is WLAN_ELEMID_EXTN_ELEM), the element ID extension. This defragmented
* payload will not contain the headers of any of the other fragments in the
* fragment sequence.
* @fragbuff_maxsize: Maximum size of fragbuff. This should be greater than or
* equal to the total size of the element fragment sequence, inclusive of the
* header and payload of the leading element and the headers and payloads of all
* subsequent fragments applicable to that element.
* @defragbuff: The destination buffer into which the defragmented payload
* should be copied. This is inapplicable and ignored if inline_defrag is true.
* The defragmented payload will be copied to the start of the destination
* buffer without including the headers of the lead element and the subsequent
* fragment elements, and (if the lead element's element ID is
* WLAN_ELEMID_EXTN_ELEM), without including the element ID extension.
* @defragbuff_maxsize: Maximum size of defragbuff. This is inapplicable and
* ignored if inline_defrag is true. The size should be large enough to contain
* the entire defragmented payload, otherwise an error will be returned.
* @defragpayload_len: Pointer to the location where the length of the
* defragmented payload should be updated. Irrespective of whether inline_defrag
* is true or false, this will not include the sizes of the headers of the lead
* element and subsequent fragments, and (if the lead element's element ID is
* WLAN_ELEMID_EXTN_ELEM), it will not include the size of the lead element's
* element ID extension. Please note standards related limitation given in
* function description below.
*
* Defragment a sequence of element fragments. If the source buffer does not
* contain an element fragment sequence (in the beginning), an error is
* returned. An inline mode is available to carry out the defragmentation within
* the source buffer in order to reduce buffer requirements and to eliminate
* memory copies/moves for the lead element. In the inline mode, the buffer
* content (if any) after the fragments is moved as well. The contents of the
* defragmented payload are intended for end consumption by control path
* protocol processing code within the driver in a manner uniform with other
* protocol data in byte buffers, and not for onward forwarding to other
* subsystems or for intrusive specialized processing different from other
* protocol data. Hence zero copy methods such as network buffer fragment
* processing, etc. are not used in this use case. Additionally, this API is
* intended for use cases where the nature of the payload is complex and it is
* infeasible for the caller to skip the (un-defragmented) fragment boundaries
* on its own in a scalable and maintainable manner. Separately, please note a
* limitation arising from the standard wherein if the caller passes a truncated
* maximum buffer size such that the buffer ends prematurely just at the end of
* a fragment element with length 255, the function will have to conclude that
* the last successfully parsed fragment element is the final one in the
* fragment sequence, and return results accordingly. If another fragment
* actually exists beyond the given buffer, this function cannot detect the
* condition since there is no provision in the standard to indicate a total
* fragment sequence size in one place in the beginning or anywhere else. Hence
* the caller should take care to provide the complete buffer with the max size
* set accordingly.
*
* Return: QDF_STATUS_SUCCESS in the case of success, QDF_STATUS value giving
* the reason for error in the case of failure
*/
QDF_STATUS wlan_defrag_elem_fragseq(bool inline_defrag,
uint8_t *fragbuff,
qdf_size_t fragbuff_maxsize,
uint8_t *defragbuff,
qdf_size_t defragbuff_maxsize,
qdf_size_t *defragpayload_len);
/**
* wlan_get_subelem_fragseq_info() - Get information about subelement fragment
* sequence
@@ -481,6 +553,78 @@ QDF_STATUS wlan_get_subelem_fragseq_info(uint8_t subelemfragid,
qdf_size_t *fragseq_totallen,
qdf_size_t *fragseq_payloadlen);
/**
* wlan_defrag_subelem_fragseq() - Defragment sequence of subelement fragments
*
* @inline_defrag: Whether to use inline defragmentation, wherein the
* defragmentation is carried out inline within the source buffer and no
* memmoves/memcopy would be required for the lead subelement.
* @subelemid: Fragment ID applicable for the subelement (this can potentially
* vary across protocol areas)
* @fragbuff: Source buffer containing the subelement fragment sequence starting
* with the subelement ID of the lead subelement. The containing element is
* required to have already been defragmented (if applicable). If inline
* defragmentation is enabled, the corresponding defragmented payload will be
* generated inline into this buffer and the defragmented payload will start
* after the location of the lead subelement's subelement ID and subelement
* length. This defragmented payload will not contain the headers of any of the
* other fragments in the fragment sequence.
* @fragbuff_maxsize: Maximum size of fragbuff. This should be greater than or
* equal to the total size of the subelement fragment sequence, inclusive of the
* header and payload of the leading subelement and the headers and payloads of
* all subsequent fragments applicable to that subelement.
* @defragbuff: The destination buffer into which the defragmented payload
* should be copied. This is inapplicable and ignored if inline_defrag is true.
* The defragmented payload will be copied to the start of the destination
* buffer without including the headers of the lead subelement and the
* subsequent fragment subelements.
* @defragbuff_maxsize: Maximum size of defragbuff. This is inapplicable and
* ignored if inline_defrag is true. The size should be large enough to contain
* the entire defragmented payload, otherwise an error will be returned.
* @defragpayload_len: Pointer to the location where the length of the
* defragmented payload should be updated. Irrespective of whether inline_defrag
* is true or false, this will not include the sizes of the headers of the lead
* subelement and subsequent fragments. Please note standards related limitation
* given in function description below.
*
* Defragment a sequence of subelement fragments. If the source buffer does not
* contain a subelement fragment sequence (in the beginning), the function
* returns an error. The containing element is required to have already been
* defragmented. An inline mode is available to carry out the defragmentation
* within the source buffer in order to reduce buffer requirements and to
* eliminate memory copies/moves for the lead subelement. In the inline mode,
* the buffer content (if any) after the fragments is moved as well. The
* contents of the defragmented payload are intended for end consumption by
* control path protocol processing code within the driver in a manner uniform
* with other protocol data in byte buffers, and not for onward forwarding to
* other subsystems or for intrusive specialized processing different from other
* protocol data. Hence zero copy methods such as network buffer fragment
* processing, etc. are not used in this use case. Additionally, this API is
* intended for use cases where the nature of the payload is complex and it is
* infeasible for the caller to skip the (un-defragmented) fragment boundaries
* on its own in a scalable and maintainable manner. Separately, please note a
* limitation arising from the standard wherein if the caller passes a truncated
* maximum buffer size such that the buffer ends prematurely just at the end of
* a fragment subelement with length 255, the function will have to conclude
* that the last successfully parsed fragment subelement is the final one in the
* fragment sequence, and return results accordingly. If another fragment
* actually exists beyond the given buffer, this function cannot detect the
* condition since there is no provision in the standard to indicate a total
* fragment sequence size in one place in the beginning or anywhere else. Hence
* the caller should take care to provide the complete buffer with the max size
* set accordingly.
*
* Return: QDF_STATUS_SUCCESS in the case of success, QDF_STATUS value giving
* the reason for error in the case of failure
*/
QDF_STATUS wlan_defrag_subelem_fragseq(bool inline_defrag,
uint8_t subelemfragid,
uint8_t *fragbuff,
qdf_size_t fragbuff_maxsize,
uint8_t *defragbuff,
qdf_size_t defragbuff_maxsize,
qdf_size_t *defragpayload_len);
/**
* wlan_is_emulation_platform() - check if platform is emulation based
* @phy_version - psoc nif phy_version

View File

@@ -1123,6 +1123,204 @@ wlan_get_elemsubelem_fragseq_info(bool is_subelem,
return QDF_STATUS_SUCCESS;
}
static QDF_STATUS wlan_defrag_elemsubelem_fragseq(bool inline_defrag,
bool is_subelem,
uint8_t subelemfragid,
uint8_t *fragbuff,
qdf_size_t fragbuff_maxsize,
uint8_t *defragbuff,
qdf_size_t defragbuff_maxsize,
qdf_size_t *defragpayload_len)
{
/* elemunit, i.e. 'element unit' here refers to either an 802.11 element
* or a 802.11 subelement.
*/
uint8_t elemunit_fragid;
qdf_size_t elemunit_hdrlen;
int elemunit_id_pos;
int elemunit_len_pos;
int elemunit_idext_pos;
bool is_fragseq;
qdf_size_t fragseq_totallen;
qdf_size_t fragseq_payloadlen;
uint8_t *curr_elemunit_ptr;
qdf_size_t curr_elemunit_payloadlen;
qdf_size_t curr_elemunit_totallen;
uint8_t *src;
uint8_t *dst;
/* Current length of the defragmented payload */
qdf_size_t defragpayload_currlen;
/* Remaining length available in the source buffer containing the
* fragment sequence, after element units processed so far.
*/
qdf_size_t fragbuff_remlen;
QDF_STATUS ret;
/* Helper function to de-fragment element or subelement fragment
* sequence. Refer to the documentation of the public APIs which call
* this helper, for more information. Those APIs are mainly wrappers
* over this helper.
*/
ret = wlan_get_elemunit_info(is_subelem,
subelemfragid,
&elemunit_fragid,
&elemunit_hdrlen,
NULL,
&elemunit_id_pos,
&elemunit_len_pos,
&elemunit_idext_pos);
if (QDF_IS_STATUS_ERROR(ret)) {
qdf_rl_nofl_err("Investigate error %d when trying to get element unit info",
ret);
return QDF_STATUS_E_FAILURE;
}
if (!fragbuff) {
qdf_nofl_err("Source buffer for fragment sequence is NULL");
return QDF_STATUS_E_NULL_VALUE;
}
if (fragbuff_maxsize == 0) {
qdf_nofl_err("Size of source buffer for fragment sequence is 0");
return QDF_STATUS_E_INVAL;
}
if (!inline_defrag) {
if (!defragbuff) {
qdf_nofl_err("Destination buffer for defragmented payload is NULL");
return QDF_STATUS_E_NULL_VALUE;
}
if (defragbuff_maxsize == 0) {
qdf_nofl_err("Size of destination buffer for defragmented payload is 0");
return QDF_STATUS_E_INVAL;
}
}
if (!defragpayload_len) {
qdf_nofl_err("Pointer to location for length of defragmented payload is NULL");
return QDF_STATUS_E_NULL_VALUE;
}
ret = wlan_get_elemsubelem_fragseq_info(is_subelem,
subelemfragid,
fragbuff,
fragbuff_maxsize,
&is_fragseq,
&fragseq_totallen,
&fragseq_payloadlen);
if (QDF_IS_STATUS_ERROR(ret))
return ret;
if (!is_fragseq) {
/* We treat this as an error since the caller is supposed to
* check this.
*/
qdf_rl_nofl_err("Fragment sequence not found at start of source buffer for fragment sequence");
return QDF_STATUS_E_INVAL;
}
/* fragseq_totallen is known to be smaller than or equal to
* fragbuff_maxsize since wlan_get_elemsubelem_fragseq_info() is bound
* by fragbuff_maxsize in the search for a fragment sequence and it's
* total length.
*/
if (!inline_defrag && (defragbuff_maxsize < fragseq_payloadlen)) {
qdf_rl_nofl_err("Size of destination buffer for defragmented payload %zu octets is smaller than the size of fragment sequence payload %zu octets",
defragbuff_maxsize, fragseq_payloadlen);
return QDF_STATUS_E_INVAL;
}
defragpayload_currlen = 0;
fragbuff_remlen = fragbuff_maxsize;
/* We have already validated through wlan_get_elemsubelem_fragseq_info()
* that the elements we are about to access below are within the bounds
* of fragbuff.
*/
curr_elemunit_ptr = fragbuff;
if (!is_subelem &&
(curr_elemunit_ptr[elemunit_id_pos] == WLAN_ELEMID_EXTN_ELEM)) {
curr_elemunit_payloadlen =
curr_elemunit_ptr[elemunit_len_pos] - 1;
src = curr_elemunit_ptr + elemunit_hdrlen + 1;
} else {
curr_elemunit_payloadlen = curr_elemunit_ptr[elemunit_len_pos];
src = curr_elemunit_ptr + elemunit_hdrlen;
}
curr_elemunit_totallen =
elemunit_hdrlen + curr_elemunit_ptr[elemunit_len_pos];
if (inline_defrag) {
/* There is no need to move any bytes in the lead element. Set
* dst=src so that the next update for dst can happen in a
* manner uniform with the non-inlined defrag case.
*/
dst = src;
} else {
dst = defragbuff;
qdf_mem_copy(dst, src, curr_elemunit_payloadlen);
}
defragpayload_currlen += curr_elemunit_payloadlen;
fragbuff_remlen -= curr_elemunit_totallen;
dst += curr_elemunit_payloadlen;
curr_elemunit_ptr += curr_elemunit_totallen;
/* We have already validated through wlan_get_elemsubelem_fragseq_info()
* that at least one non-lead fragment element is present as required in
* the standard.
*/
while (curr_elemunit_ptr[elemunit_id_pos] == elemunit_fragid) {
curr_elemunit_payloadlen = curr_elemunit_ptr[elemunit_len_pos];
curr_elemunit_totallen =
elemunit_hdrlen + curr_elemunit_ptr[elemunit_len_pos];
src = curr_elemunit_ptr + elemunit_hdrlen;
if (inline_defrag)
qdf_mem_move(dst, src, curr_elemunit_payloadlen);
else
qdf_mem_copy(dst, src, curr_elemunit_payloadlen);
defragpayload_currlen += curr_elemunit_payloadlen;
fragbuff_remlen -= curr_elemunit_totallen;
if (fragbuff_remlen == 0)
break;
dst += curr_elemunit_payloadlen;
curr_elemunit_ptr += curr_elemunit_totallen;
}
if (inline_defrag && (fragbuff_remlen != 0)) {
/* Move the residual content after the fragments, in the source
* buffer.
*/
src = curr_elemunit_ptr;
qdf_mem_move(dst, src, fragbuff_remlen);
}
*defragpayload_len = defragpayload_currlen;
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
wlan_get_elem_fragseq_requirements(uint8_t elemid,
qdf_size_t payloadlen,
@@ -1218,6 +1416,23 @@ QDF_STATUS wlan_get_elem_fragseq_info(uint8_t *elembuff,
fragseq_payloadlen);
}
QDF_STATUS wlan_defrag_elem_fragseq(bool inline_defrag,
uint8_t *fragbuff,
qdf_size_t fragbuff_maxsize,
uint8_t *defragbuff,
qdf_size_t defragbuff_maxsize,
qdf_size_t *defragpayload_len)
{
return wlan_defrag_elemsubelem_fragseq(inline_defrag,
false,
0,
fragbuff,
fragbuff_maxsize,
defragbuff,
defragbuff_maxsize,
defragpayload_len);
}
QDF_STATUS wlan_get_subelem_fragseq_info(uint8_t subelemfragid,
uint8_t *subelembuff,
qdf_size_t subelembuff_maxsize,
@@ -1234,6 +1449,24 @@ QDF_STATUS wlan_get_subelem_fragseq_info(uint8_t subelemfragid,
fragseq_payloadlen);
}
QDF_STATUS wlan_defrag_subelem_fragseq(bool inline_defrag,
uint8_t subelemfragid,
uint8_t *fragbuff,
qdf_size_t fragbuff_maxsize,
uint8_t *defragbuff,
qdf_size_t defragbuff_maxsize,
qdf_size_t *defragpayload_len)
{
return wlan_defrag_elemsubelem_fragseq(inline_defrag,
true,
subelemfragid,
fragbuff,
fragbuff_maxsize,
defragbuff,
defragbuff_maxsize,
defragpayload_len);
}
bool wlan_is_emulation_platform(uint32_t phy_version)
{
if ((phy_version == 0xABC0) || (phy_version == 0xABC1) ||