aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/susb/dwc_common_port/dwc_mem.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/susb/dwc_common_port/dwc_mem.c')
-rw-r--r--drivers/usb/susb/dwc_common_port/dwc_mem.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/drivers/usb/susb/dwc_common_port/dwc_mem.c b/drivers/usb/susb/dwc_common_port/dwc_mem.c
new file mode 100644
index 00000000000..afdd9d73bfa
--- /dev/null
+++ b/drivers/usb/susb/dwc_common_port/dwc_mem.c
@@ -0,0 +1,244 @@
+/* Memory Debugging */
+#ifdef DWC_DEBUG_MEMORY
+
+#include "dwc_os.h"
+#include "dwc_list.h"
+
+struct allocation {
+ void *addr;
+ void *ctx;
+ char *func;
+ int line;
+ uint32_t size;
+ int dma;
+ DWC_CIRCLEQ_ENTRY(allocation) entry;
+};
+
+DWC_CIRCLEQ_HEAD(allocation_queue, allocation);
+
+struct allocation_manager {
+ void *mem_ctx;
+ struct allocation_queue allocations;
+
+ /* statistics */
+ int num;
+ int num_freed;
+ int num_active;
+ uint32_t total;
+ uint32_t current;
+ uint32_t max;
+};
+
+static struct allocation_manager *manager = NULL;
+
+static int add_allocation(void *ctx, uint32_t size, char const *func, int line, void *addr,
+ int dma)
+{
+ struct allocation *a;
+
+ DWC_ASSERT(manager != NULL, "manager not allocated");
+
+ a = __DWC_ALLOC_ATOMIC(manager->mem_ctx, sizeof(*a));
+ if (!a) {
+ return -DWC_E_NO_MEMORY;
+ }
+
+ a->func = __DWC_ALLOC_ATOMIC(manager->mem_ctx, DWC_STRLEN(func) + 1);
+ if (!a->func) {
+ __DWC_FREE(manager->mem_ctx, a);
+ return -DWC_E_NO_MEMORY;
+ }
+
+ DWC_MEMCPY(a->func, func, DWC_STRLEN(func) + 1);
+ a->addr = addr;
+ a->ctx = ctx;
+ a->line = line;
+ a->size = size;
+ a->dma = dma;
+ DWC_CIRCLEQ_INSERT_TAIL(&manager->allocations, a, entry);
+
+ /* Update stats */
+ manager->num++;
+ manager->num_active++;
+ manager->total += size;
+ manager->current += size;
+
+ if (manager->max < manager->current) {
+ manager->max = manager->current;
+ }
+
+ return 0;
+}
+
+static struct allocation *find_allocation(void *ctx, void *addr)
+{
+ struct allocation *a;
+
+ DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) {
+ if (a->ctx == ctx && a->addr == addr) {
+ return a;
+ }
+ }
+
+ return NULL;
+}
+
+static void free_allocation(void *ctx, void *addr, char const *func, int line)
+{
+ struct allocation *a = find_allocation(ctx, addr);
+
+ if (!a) {
+ DWC_ASSERT(0,
+ "Free of address %p that was never allocated or already freed %s:%d",
+ addr, func, line);
+ return;
+ }
+
+ DWC_CIRCLEQ_REMOVE(&manager->allocations, a, entry);
+
+ manager->num_active--;
+ manager->num_freed++;
+ manager->current -= a->size;
+ __DWC_FREE(manager->mem_ctx, a->func);
+ __DWC_FREE(manager->mem_ctx, a);
+}
+
+int dwc_memory_debug_start(void *mem_ctx)
+{
+ DWC_ASSERT(manager == NULL, "Memory debugging has already started\n");
+
+ if (manager) {
+ return -DWC_E_BUSY;
+ }
+
+ manager = __DWC_ALLOC(mem_ctx, sizeof(*manager));
+ if (!manager) {
+ return -DWC_E_NO_MEMORY;
+ }
+
+ DWC_CIRCLEQ_INIT(&manager->allocations);
+ manager->mem_ctx = mem_ctx;
+ manager->num = 0;
+ manager->num_freed = 0;
+ manager->num_active = 0;
+ manager->total = 0;
+ manager->current = 0;
+ manager->max = 0;
+
+ return 0;
+}
+
+void dwc_memory_debug_stop(void)
+{
+ struct allocation *a;
+
+ dwc_memory_debug_report();
+
+ DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) {
+ DWC_ERROR("Memory leaked from %s:%d\n", a->func, a->line);
+ free_allocation(a->ctx, a->addr, NULL, -1);
+ }
+
+ __DWC_FREE(manager->mem_ctx, manager);
+}
+
+void dwc_memory_debug_report(void)
+{
+ struct allocation *a;
+
+ DWC_PRINTF("\n\n\n----------------- Memory Debugging Report -----------------\n\n");
+ DWC_PRINTF("Num Allocations = %d\n", manager->num);
+ DWC_PRINTF("Freed = %d\n", manager->num_freed);
+ DWC_PRINTF("Active = %d\n", manager->num_active);
+ DWC_PRINTF("Current Memory Used = %d\n", manager->current);
+ DWC_PRINTF("Total Memory Used = %d\n", manager->total);
+ DWC_PRINTF("Maximum Memory Used at Once = %d\n", manager->max);
+ DWC_PRINTF("Unfreed allocations:\n");
+
+ DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) {
+ DWC_PRINTF(" addr=%p, size=%d from %s:%d, DMA=%d\n",
+ a->addr, a->size, a->func, a->line, a->dma);
+ }
+}
+
+/* The replacement functions */
+void *dwc_alloc_debug(void *mem_ctx, uint32_t size, char const *func, int line)
+{
+ void *addr = __DWC_ALLOC(mem_ctx, size);
+
+ if (!addr) {
+ return NULL;
+ }
+
+ if (add_allocation(mem_ctx, size, func, line, addr, 0)) {
+ __DWC_FREE(mem_ctx, addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+void *dwc_alloc_atomic_debug(void *mem_ctx, uint32_t size, char const *func,
+ int line)
+{
+ void *addr = __DWC_ALLOC_ATOMIC(mem_ctx, size);
+
+ if (!addr) {
+ return NULL;
+ }
+
+ if (add_allocation(mem_ctx, size, func, line, addr, 0)) {
+ __DWC_FREE(mem_ctx, addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+void dwc_free_debug(void *mem_ctx, void *addr, char const *func, int line)
+{
+ free_allocation(mem_ctx, addr, func, line);
+ __DWC_FREE(mem_ctx, addr);
+}
+
+void *dwc_dma_alloc_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr,
+ char const *func, int line)
+{
+ void *addr = __DWC_DMA_ALLOC(dma_ctx, size, dma_addr);
+
+ if (!addr) {
+ return NULL;
+ }
+
+ if (add_allocation(dma_ctx, size, func, line, addr, 1)) {
+ __DWC_DMA_FREE(dma_ctx, size, addr, *dma_addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+void *dwc_dma_alloc_atomic_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr, char const *func, int line)
+{
+ void *addr = __DWC_DMA_ALLOC_ATOMIC(dma_ctx, size, dma_addr);
+
+ if (!addr) {
+ return NULL;
+ }
+
+ if (add_allocation(dma_ctx, size, func, line, addr, 1)) {
+ __DWC_DMA_FREE(dma_ctx, size, addr, *dma_addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+void dwc_dma_free_debug(void *dma_ctx, uint32_t size, void *virt_addr,
+ dwc_dma_t dma_addr, char const *func, int line)
+{
+ free_allocation(dma_ctx, virt_addr, func, line);
+ __DWC_DMA_FREE(dma_ctx, size, virt_addr, dma_addr);
+}
+
+#endif /* DWC_DEBUG_MEMORY */