Skip to content

Commit b45b29a

Browse files
committed
Merge branch 'fix/fix_mmu_map_concurrent_issue' into 'master'
mmu: fix mmu map concurrent issue Closes IDFGH-14841 See merge request espressif/esp-idf!37780
2 parents ccb4384 + 1abe57c commit b45b29a

File tree

10 files changed

+98
-30
lines changed

10 files changed

+98
-30
lines changed

components/esp_driver_spi/test_apps/master/main/test_app_main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include "esp_newlib.h"
1111

1212
// iterator to load partition tables in `test spi bus lock, with flash` will lead memory not free
13-
#define TEST_MEMORY_LEAK_THRESHOLD (350)
13+
#define TEST_MEMORY_LEAK_THRESHOLD (450)
1414

1515
void setUp(void)
1616
{

components/esp_hw_support/test_apps/mspi/main/test_app_main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include "esp_newlib.h"
1111

1212
// load partition table in tests will use memory
13-
#define TEST_MEMORY_LEAK_THRESHOLD (450)
13+
#define TEST_MEMORY_LEAK_THRESHOLD (600)
1414

1515
void setUp(void)
1616
{

components/esp_mm/esp_mmu_map.c

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <sys/param.h>
1010
#include <sys/queue.h>
1111
#include <inttypes.h>
12+
#include <sys/lock.h>
1213
#include "sdkconfig.h"
1314
#include "esp_attr.h"
1415
#include "esp_log.h"
@@ -106,6 +107,10 @@ typedef struct {
106107
* Only the first `num_regions` items are valid
107108
*/
108109
mem_region_t mem_regions[SOC_MMU_LINEAR_ADDRESS_REGION_NUM];
110+
/**
111+
* driver layer mutex lock
112+
*/
113+
_lock_t mutex;
109114
} mmu_ctx_t;
110115

111116
static mmu_ctx_t s_mmu_ctx;
@@ -443,17 +448,15 @@ esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target,
443448
ESP_RETURN_ON_FALSE((paddr_start % CONFIG_MMU_PAGE_SIZE == 0), ESP_ERR_INVALID_ARG, TAG, "paddr must be rounded up to the nearest multiple of CONFIG_MMU_PAGE_SIZE");
444449
ESP_RETURN_ON_ERROR(s_mem_caps_check(caps), TAG, "invalid caps");
445450

451+
_lock_acquire(&s_mmu_ctx.mutex);
452+
mem_block_t *dummy_head = NULL;
453+
mem_block_t *dummy_tail = NULL;
446454
size_t aligned_size = ALIGN_UP_BY(size, CONFIG_MMU_PAGE_SIZE);
447455
int32_t found_region_id = s_find_available_region(s_mmu_ctx.mem_regions, s_mmu_ctx.num_regions, aligned_size, caps, target);
448-
if (found_region_id == -1) {
449-
ESP_EARLY_LOGE(TAG, "no such vaddr range");
450-
return ESP_ERR_NOT_FOUND;
451-
}
456+
ESP_GOTO_ON_FALSE(found_region_id != -1, ESP_ERR_NOT_FOUND, err, TAG, "no such vaddr range");
452457

453458
//Now we're sure we can find an available block inside a certain region
454459
mem_region_t *found_region = &s_mmu_ctx.mem_regions[found_region_id];
455-
mem_block_t *dummy_head = NULL;
456-
mem_block_t *dummy_tail = NULL;
457460
mem_block_t *new_block = NULL;
458461

459462
if (TAILQ_EMPTY(&found_region->mem_block_head)) {
@@ -502,7 +505,6 @@ esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target,
502505
}
503506

504507
if (is_enclosed) {
505-
ESP_LOGW(TAG, "paddr block is mapped already, vaddr_start: %p, size: 0x%x", (void *)mem_block->vaddr_start, mem_block->size);
506508
/*
507509
* This condition is triggered when `s_is_enclosed` is true and hence
508510
* we are sure that `paddr_start` >= `mem_block->paddr_start`.
@@ -512,12 +514,11 @@ esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target,
512514
*/
513515
const uint32_t new_paddr_offset = paddr_start - mem_block->paddr_start;
514516
*out_ptr = (void *)mem_block->vaddr_start + new_paddr_offset;
515-
return ESP_ERR_INVALID_STATE;
517+
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_STATE, err, TAG, "paddr block is mapped already, vaddr_start: %p, size: 0x%x", (void *)mem_block->vaddr_start, mem_block->size);
516518
}
517519

518520
if (!allow_overlap && is_overlapped) {
519-
ESP_LOGE(TAG, "paddr block is overlapped with an already mapped paddr block");
520-
return ESP_ERR_INVALID_ARG;
521+
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "paddr block is overlapped with an already mapped paddr block");
521522
}
522523
#endif //#if ENABLE_PADDR_CHECK
523524

@@ -552,7 +553,6 @@ esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target,
552553
assert(found);
553554
//insert the to-be-mapped new block to the list
554555
TAILQ_INSERT_BEFORE(found_block, new_block, entries);
555-
556556
//Finally, we update the max_slot_size
557557
found_region->max_slot_size = max_slot_len;
558558

@@ -574,6 +574,7 @@ esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target,
574574
//do mapping
575575
s_do_mapping(target, new_block->vaddr_start, paddr_start, aligned_size);
576576
*out_ptr = (void *)new_block->vaddr_start;
577+
_lock_release(&s_mmu_ctx.mutex);
577578

578579
return ESP_OK;
579580

@@ -584,6 +585,7 @@ esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target,
584585
if (dummy_head) {
585586
free(dummy_head);
586587
}
588+
_lock_release(&s_mmu_ctx.mutex);
587589

588590
return ret;
589591
}
@@ -626,19 +628,21 @@ esp_err_t esp_mmu_unmap(void *ptr)
626628
{
627629
ESP_RETURN_ON_FALSE(ptr, ESP_ERR_INVALID_ARG, TAG, "null pointer");
628630

631+
esp_err_t ret = ESP_FAIL;
629632
mem_region_t *region = NULL;
630633
mem_block_t *mem_block = NULL;
631634
uint32_t ptr_laddr = mmu_ll_vaddr_to_laddr((uint32_t)ptr);
632635
size_t slot_len = 0;
633636

637+
_lock_acquire(&s_mmu_ctx.mutex);
634638
for (int i = 0; i < s_mmu_ctx.num_regions; i++) {
635639
ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-out-of-bounds")
636640
if (ptr_laddr >= s_mmu_ctx.mem_regions[i].free_head && ptr_laddr < s_mmu_ctx.mem_regions[i].end) {
637641
region = &s_mmu_ctx.mem_regions[i];
638642
}
639643
ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-out-of-bounds")
640644
}
641-
ESP_RETURN_ON_FALSE(region, ESP_ERR_NOT_FOUND, TAG, "munmap target pointer is outside external memory regions");
645+
ESP_GOTO_ON_FALSE(region, ESP_ERR_NOT_FOUND, err, TAG, "munmap target pointer is outside external memory regions");
642646

643647
bool found = false;
644648
mem_block_t *found_block = NULL;
@@ -659,15 +663,23 @@ esp_err_t esp_mmu_unmap(void *ptr)
659663
}
660664
}
661665

662-
ESP_RETURN_ON_FALSE(found, ESP_ERR_NOT_FOUND, TAG, "munmap target pointer isn't mapped yet");
666+
if (found) {
667+
TAILQ_REMOVE(&region->mem_block_head, found_block, entries);
668+
}
669+
ESP_GOTO_ON_FALSE(found, ESP_ERR_NOT_FOUND, err, TAG, "munmap target pointer isn't mapped yet");
663670

664671
//do unmap
665672
s_do_unmapping(mem_block->vaddr_start, mem_block->size);
666-
//remove the already unmapped block from the list
667-
TAILQ_REMOVE(&region->mem_block_head, found_block, entries);
668673
free(found_block);
669674

675+
_lock_release(&s_mmu_ctx.mutex);
676+
670677
return ESP_OK;
678+
679+
err:
680+
681+
_lock_release(&s_mmu_ctx.mutex);
682+
return ret;
671683
}
672684

673685
esp_err_t esp_mmu_map_dump_mapped_blocks(FILE* stream)

components/esp_mm/include/esp_mmu_map.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ typedef uint32_t esp_paddr_t;
6060
/**
6161
* @brief Map a physical memory block to external virtual address block, with given capabilities.
6262
*
63-
* @note This API does not guarantee thread safety
64-
*
6563
* @param[in] paddr_start Start address of the physical memory block
6664
* @param[in] size Size to be mapped. Size will be rounded up by to the nearest multiple of MMU page size
6765
* @param[in] target Physical memory target you're going to map to, see `mmu_target_t`
@@ -88,8 +86,6 @@ esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target,
8886
/**
8987
* @brief Unmap a previously mapped virtual memory block
9088
*
91-
* @note This API does not guarantee thread safety
92-
*
9389
* @param[in] ptr Start address of the virtual memory
9490
*
9591
* @return

components/esp_mm/test_apps/mm/main/test_mmap.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#include <sys/param.h>
99
#include <string.h>
1010
#include "inttypes.h"
11+
#include "freertos/FreeRTOS.h"
12+
#include "freertos/task.h"
13+
#include "freertos/semphr.h"
1114
#include "esp_log.h"
1215
#include "esp_attr.h"
1316
#include "unity.h"
@@ -71,6 +74,59 @@ TEST_CASE("Can find paddr caps by any paddr offset", "[mmu]")
7174
TEST_ESP_OK(esp_mmu_unmap(ptr0));
7275
}
7376

77+
typedef struct {
78+
SemaphoreHandle_t done;
79+
} test_task_arg_t;
80+
81+
void map_task(void *varg)
82+
{
83+
esp_err_t ret = ESP_FAIL;
84+
test_task_arg_t* args = (test_task_arg_t*) varg;
85+
86+
const esp_partition_t *part = s_get_partition();
87+
srand(199);
88+
int cnt = 0;
89+
while (true) {
90+
if (cnt >= 10000) {
91+
break;
92+
}
93+
size_t i = rand() % part->size;
94+
void *ptr0 = NULL;
95+
size_t phys_addr = part->address + i;
96+
size_t region_offset = phys_addr & (CONFIG_MMU_PAGE_SIZE - 1);
97+
size_t mmap_addr = phys_addr & ~(CONFIG_MMU_PAGE_SIZE - 1);
98+
ret = esp_mmu_map(mmap_addr, 1 + region_offset, MMU_TARGET_FLASH0, MMU_MEM_CAP_READ | MMU_MEM_CAP_8BIT, ESP_MMU_MMAP_FLAG_PADDR_SHARED, &ptr0);
99+
if (ret == ESP_OK) {
100+
esp_mmu_unmap(ptr0);
101+
}
102+
cnt++;
103+
vTaskDelay(pdMS_TO_TICKS(1));
104+
}
105+
106+
xSemaphoreGive(args->done);
107+
vTaskDelete(NULL);
108+
}
109+
110+
TEST_CASE("Test mmap concurrency", "[mmu][manual][ignore]")
111+
{
112+
test_task_arg_t args0 = {
113+
.done = xSemaphoreCreateBinary(),
114+
};
115+
116+
test_task_arg_t args1 = {
117+
.done = xSemaphoreCreateBinary(),
118+
};
119+
120+
xTaskCreate(map_task, "map0_task", 8096, &args0, 1, NULL);
121+
xTaskCreate(map_task, "map1_task", 8096, &args1, 1, NULL);
122+
123+
xSemaphoreTake(args0.done, portMAX_DELAY);
124+
xSemaphoreTake(args1.done, portMAX_DELAY);
125+
vTaskDelay(100);
126+
vSemaphoreDelete(args0.done);
127+
vSemaphoreDelete(args1.done);
128+
}
129+
74130
#if CONFIG_SPIRAM
75131
#if !CONFIG_IDF_TARGET_ESP32 //ESP32 doesn't support using `esp_mmu_map` to map to PSRAM
76132
TEST_CASE("Can find paddr when mapping to psram", "[mmu]")

components/esp_mm/test_apps/mmap_hw/main/test_app_main.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -13,11 +13,11 @@
1313
* - traversing all vaddr range to check their attributes
1414
*
1515
* These tests need certain number of internal resources (heap memory), as they uses up the vaddr ranges
16-
* On ESP32, it should be around 450
17-
* On ESP32S2, it should be around 600
18-
* On other chips, it should be around 400
16+
* On ESP32, it should be around 650
17+
* On ESP32S2, it should be around 800
18+
* On other chips, it should be around 600
1919
*/
20-
#define TEST_MEMORY_LEAK_THRESHOLD (-650)
20+
#define TEST_MEMORY_LEAK_THRESHOLD (-800)
2121

2222
static size_t before_free_8bit;
2323
static size_t before_free_32bit;

components/esp_psram/test_apps/psram/main/test_app_main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "unity_test_runner.h"
99
#include "esp_heap_caps.h"
1010

11-
#define TEST_MEMORY_LEAK_THRESHOLD (-700)
11+
#define TEST_MEMORY_LEAK_THRESHOLD (-750)
1212

1313
static size_t before_free_8bit;
1414
static size_t before_free_32bit;

components/esp_system/test_apps/esp_system_unity_tests/main/test_app_main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#include "freertos/task.h"
1212

1313
// Some resources are lazy allocated, the threshold is left for that case
14-
#define TEST_MEMORY_LEAK_THRESHOLD (-900)
14+
#define TEST_MEMORY_LEAK_THRESHOLD (-1100)
1515

1616
static size_t before_free_8bit;
1717
static size_t before_free_32bit;

docs/en/api-reference/system/mm.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,9 @@ SPI flash can be accessed by SPI1 (ESP-IDF ``esp_flash`` driver APIs), or by poi
169169
Thread Safety
170170
=============
171171

172-
APIs in ``esp_mmu_map.h`` are not guaranteed to be thread-safe.
172+
Following APIs in ``esp_mmu_map.h`` are not guaranteed to be thread-safe:
173+
174+
- :cpp:func:`esp_mmu_map_dump_mapped_blocks`
173175

174176
APIs in ``esp_cache.h`` are guaranteed to be thread-safe.
175177

docs/zh_CN/api-reference/system/mm.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,9 @@ SPI flash 可以通过 SPI1(ESP-IDF ``spi_flash`` 驱动 API)或指针进行
169169
线程安全
170170
========
171171

172-
``esp_mmu_map.h`` 中的 API 不能确保线程的安全性。
172+
``esp_mmu_map.h`` 中的以下 API 不能确保线程的安全性:
173+
174+
- :cpp:func:`esp_mmu_map_dump_mapped_blocks`
173175

174176
``esp_cache.h`` 中的 API 能够确保线程的安全性。
175177

0 commit comments

Comments
 (0)