瀏覽代碼

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
Krishna Rao 3 年之前
父節點
當前提交
3f38db23d4
共有 2 個文件被更改,包括 377 次插入0 次删除
  1. 144 0
      umac/cmn_services/utils/inc/wlan_utility.h
  2. 233 0
      umac/cmn_services/utils/src/wlan_utility.c

+ 144 - 0
umac/cmn_services/utils/inc/wlan_utility.h

@@ -414,6 +414,78 @@ QDF_STATUS wlan_get_elem_fragseq_info(uint8_t *elembuff,
 				      qdf_size_t *fragseq_totallen,
 				      qdf_size_t *fragseq_totallen,
 				      qdf_size_t *fragseq_payloadlen);
 				      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
  * wlan_get_subelem_fragseq_info() - Get information about subelement fragment
  * sequence
  * sequence
@@ -481,6 +553,78 @@ QDF_STATUS wlan_get_subelem_fragseq_info(uint8_t subelemfragid,
 					 qdf_size_t *fragseq_totallen,
 					 qdf_size_t *fragseq_totallen,
 					 qdf_size_t *fragseq_payloadlen);
 					 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
  * wlan_is_emulation_platform() - check if platform is emulation based
  * @phy_version - psoc nif phy_version
  * @phy_version - psoc nif phy_version

+ 233 - 0
umac/cmn_services/utils/src/wlan_utility.c

@@ -1123,6 +1123,204 @@ wlan_get_elemsubelem_fragseq_info(bool is_subelem,
 	return QDF_STATUS_SUCCESS;
 	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
 QDF_STATUS
 wlan_get_elem_fragseq_requirements(uint8_t elemid,
 wlan_get_elem_fragseq_requirements(uint8_t elemid,
 				   qdf_size_t payloadlen,
 				   qdf_size_t payloadlen,
@@ -1218,6 +1416,23 @@ QDF_STATUS wlan_get_elem_fragseq_info(uint8_t *elembuff,
 						 fragseq_payloadlen);
 						 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,
 QDF_STATUS wlan_get_subelem_fragseq_info(uint8_t subelemfragid,
 					 uint8_t *subelembuff,
 					 uint8_t *subelembuff,
 					 qdf_size_t subelembuff_maxsize,
 					 qdf_size_t subelembuff_maxsize,
@@ -1234,6 +1449,24 @@ QDF_STATUS wlan_get_subelem_fragseq_info(uint8_t subelemfragid,
 						 fragseq_payloadlen);
 						 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)
 bool wlan_is_emulation_platform(uint32_t phy_version)
 {
 {
 	if ((phy_version == 0xABC0) || (phy_version == 0xABC1) ||
 	if ((phy_version == 0xABC0) || (phy_version == 0xABC1) ||