瀏覽代碼

qcacmn: Avoid using small buffer address

On some third-party platforms, we observe the memory physical
address below 0x2000 is allocated will cause HW/FW NOC error,
so this region memory should not use by host.

This change will hold the memory if the allocated memory physical
address below 0x2000 until driver unload.

Change-Id: I37b6abc98033230dc4f572dafb849101497f6e93
CRs-Fixed: 3419648
Zhiwei Yang 2 年之前
父節點
當前提交
dcc5bcfd68
共有 2 個文件被更改,包括 202 次插入0 次删除
  1. 41 0
      qdf/inc/qdf_mem.h
  2. 161 0
      qdf/linux/src/qdf_mem.c

+ 41 - 0
qdf/inc/qdf_mem.h

@@ -1487,4 +1487,45 @@ int
 qdf_iommu_domain_get_attr(qdf_iommu_domain_t *domain,
 			  enum qdf_iommu_attr attr, void *data);
 #endif
+
+#define DEFAULT_DEBUG_DOMAIN_INIT 0
+#ifdef QCA_DMA_PADDR_CHECK
+/**
+ * qdf_dma_invalid_buf_list_init() - Initialize dma invalid buffer list
+ *
+ * Return: none
+ */
+void qdf_dma_invalid_buf_list_init(void);
+
+/**
+ * qdf_dma_invalid_buf_list_deinit() - Deinitialize dma invalid buffer list
+ *
+ * Return: none
+ */
+void qdf_dma_invalid_buf_list_deinit(void);
+
+/**
+ * qdf_dma_invalid_buf_free() - Free dma invalid buffer
+ * @dev: Pointer to device handle
+ * @domain: Debug domain
+ *
+ * Return: none
+ */
+void qdf_dma_invalid_buf_free(void *dev, uint8_t domain);
+#else
+static inline void
+qdf_dma_invalid_buf_list_init(void)
+{
+}
+
+static inline void
+qdf_dma_invalid_buf_list_deinit(void)
+{
+}
+
+static inline void
+qdf_dma_invalid_buf_free(void *dev, uint8_t domain)
+{
+}
+#endif /* QCA_DMA_PADDR_CHECK */
 #endif /* __QDF_MEMORY_H */

+ 161 - 0
qdf/linux/src/qdf_mem.c

@@ -2410,7 +2410,106 @@ void *qdf_mem_dma_alloc(qdf_device_t osdev, void *dev, qdf_size_t size,
 
 	return NULL;
 }
+#elif defined(QCA_DMA_PADDR_CHECK)
+#ifdef CONFIG_LEAK_DETECTION
+#define MAX_DEBUG_DOMAIN_COUNT QDF_DEBUG_DOMAIN_COUNT
+#define debug_domain_get() qdf_debug_domain_get()
+#else
+#define MAX_DEBUG_DOMAIN_COUNT 1
+#define debug_domain_get() DEFAULT_DEBUG_DOMAIN_INIT
+#endif
+/**
+ * struct qdf_dma_buf_entry - DMA invalid buffer list entry
+ * @node: QDF list node member
+ * @size: DMA buffer size
+ * @phy_addr: DMA buffer physical address
+ * @vaddr: DMA buffer virtual address. if DMA buffer size is larger than entry
+ *         size, we use the DMA buffer to save entry info and the starting
+ *         address of the entry is the DMA buffer vaddr, in this way, we can
+ *         reduce unnecessary memory consumption. if DMA buffer size is smaller
+ *         than entry size, we need alloc another buffer, and vaddr will be set
+ *         to the invalid dma buffer virtual address.
+ */
+struct qdf_dma_buf_entry {
+	qdf_list_node_t node;
+	qdf_size_t size;
+	qdf_dma_addr_t phy_addr;
+	void *vaddr;
+};
+
+#define DMA_PHY_ADDR_RESERVED 0x2000
+#define QDF_DMA_MEM_ALLOC_MAX_RETRIES 10
+#define QDF_DMA_INVALID_BUF_LIST_SIZE 128
+static qdf_list_t qdf_invalid_buf_list[MAX_DEBUG_DOMAIN_COUNT];
+static bool qdf_invalid_buf_list_init[MAX_DEBUG_DOMAIN_COUNT];
+static qdf_spinlock_t qdf_invalid_buf_list_lock;
+
+static inline void *qdf_mem_dma_alloc(qdf_device_t osdev, void *dev,
+				      qdf_size_t size, qdf_dma_addr_t *paddr)
+{
+	void *vaddr;
+	uint32_t retry;
+	QDF_STATUS status;
+	bool is_separate;
+	qdf_list_t *cur_buf_list;
+	struct qdf_dma_buf_entry *entry;
+	uint8_t current_domain;
+
+	for (retry = 0; retry < QDF_DMA_MEM_ALLOC_MAX_RETRIES; retry++) {
+		vaddr = dma_alloc_coherent(dev, size, paddr,
+					   qdf_mem_malloc_flags());
+		if (!vaddr)
+			return NULL;
+
+		if (qdf_likely(*paddr > DMA_PHY_ADDR_RESERVED))
+			return vaddr;
+
+		current_domain = debug_domain_get();
+
+		/* if qdf_invalid_buf_list not init, so we can't store memory
+		 * info and can't hold it. let's free the invalid memory and
+		 * try to get memory with phy address greater than
+		 * DMA_PHY_ADDR_RESERVED
+		 */
+		if (current_domain >= MAX_DEBUG_DOMAIN_COUNT ||
+		    !qdf_invalid_buf_list_init[current_domain]) {
+			qdf_debug("physical address below 0x%x, re-alloc",
+				  DMA_PHY_ADDR_RESERVED);
+			dma_free_coherent(dev, size, vaddr, *paddr);
+			continue;
+		}
+
+		cur_buf_list = &qdf_invalid_buf_list[current_domain];
+		if (size >= sizeof(*entry)) {
+			entry = vaddr;
+			entry->vaddr = NULL;
+		} else {
+			entry = qdf_mem_malloc(sizeof(*entry));
+			if (!entry) {
+				dma_free_coherent(dev, size, vaddr, *paddr);
+				qdf_err("qdf_mem_malloc entry failed!");
+				continue;
+			}
+			entry->vaddr = vaddr;
+		}
+
+		entry->phy_addr = *paddr;
+		entry->size = size;
+		qdf_spin_lock_irqsave(&qdf_invalid_buf_list_lock);
+		status = qdf_list_insert_back(cur_buf_list,
+					      &entry->node);
+		qdf_spin_unlock_irqrestore(&qdf_invalid_buf_list_lock);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			qdf_err("insert buf entry fail, status %d", status);
+			is_separate = !entry->vaddr ? false : true;
+			dma_free_coherent(dev, size, vaddr, *paddr);
+			if (is_separate)
+				qdf_mem_free(entry);
+		}
+	}
 
+	return NULL;
+}
 #else
 static inline void *qdf_mem_dma_alloc(qdf_device_t osdev, void *dev,
 				      qdf_size_t size, qdf_dma_addr_t *paddr)
@@ -2928,3 +3027,65 @@ __qdf_kmem_cache_free(qdf_kmem_cache_t cache, void *node)
 {
 }
 #endif
+
+#ifdef QCA_DMA_PADDR_CHECK
+void qdf_dma_invalid_buf_list_init(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_DEBUG_DOMAIN_COUNT; i++) {
+		qdf_list_create(&qdf_invalid_buf_list[i],
+				QDF_DMA_INVALID_BUF_LIST_SIZE);
+		qdf_invalid_buf_list_init[i] = true;
+	}
+	qdf_spinlock_create(&qdf_invalid_buf_list_lock);
+}
+
+void qdf_dma_invalid_buf_free(void *dev, uint8_t domain)
+{
+	bool is_separate;
+	qdf_list_t *cur_buf_list;
+	struct qdf_dma_buf_entry *entry;
+	QDF_STATUS status = QDF_STATUS_E_EMPTY;
+
+	if (!dev)
+		return;
+
+	if (domain >= MAX_DEBUG_DOMAIN_COUNT)
+		return;
+
+	if (!qdf_invalid_buf_list_init[domain])
+		return;
+
+	cur_buf_list = &qdf_invalid_buf_list[domain];
+	do {
+		qdf_spin_lock_irqsave(&qdf_invalid_buf_list_lock);
+		status = qdf_list_remove_front(cur_buf_list,
+					       (qdf_list_node_t **)&entry);
+		qdf_spin_unlock_irqrestore(&qdf_invalid_buf_list_lock);
+
+		if (status != QDF_STATUS_SUCCESS)
+			break;
+
+		is_separate = !entry->vaddr ? false : true;
+		if (is_separate) {
+			dma_free_coherent(dev, entry->size, entry->vaddr,
+					  entry->phy_addr);
+			qdf_mem_free(entry);
+		} else
+			dma_free_coherent(dev, entry->size, entry,
+					  entry->phy_addr);
+	} while (!qdf_list_empty(cur_buf_list));
+	qdf_invalid_buf_list_init[domain] = false;
+}
+
+void qdf_dma_invalid_buf_list_deinit(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_DEBUG_DOMAIN_COUNT; i++)
+		qdf_list_destroy(&qdf_invalid_buf_list[i]);
+
+	qdf_spinlock_destroy(&qdf_invalid_buf_list_lock);
+}
+#endif /* QCA_DMA_PADDR_CHECK */