eclipse-threadx / usbx

Eclipse ThreadX - USBX is a high-performance USB host, device, and on-the-go (OTG) embedded stack, that is fully integrated with Eclipse ThreadX RTOS
https://github.com/eclipse-threadx/rtos-docs/blob/main/rtos-docs/usbx/index.md
MIT License
148 stars 88 forks source link

FileX 读写 USBX 大容量存储设备中的文件,必须使用 CACHESAFE 缓存 #107

Closed zhangjinxing closed 1 year ago

zhangjinxing commented 1 year ago

我在 i.MX RT1052 项目中使用 FileX 和 USBX 读取 U 盘中的文件,发现 fx_file_read 读取文件时,必须提供 CACHESAFE 内存才能读取到正确的数据(使用 SCB_InvalidateDCache 不能正常工作)。如果挂载两个 FileX 文件系统,一个必须提供 CACHESAFE 内存才能工作 ,而另一个文件系统无需 CACHESAFE 内存就可以工作。这会导致程序不统一和混乱。

// CACHESAFE buff AT_NONCACHEABLE_SECTION(ULONG local_buffer[DEMO_BUFFER_SIZE / sizeof(ULONG)]); status = fx_file_read(&my_file, local_buffer, bufferSize, &requested_length);

我看到 FileX 使用了直接 IO 的方式读取数据,调用 _ux_host_class_storage_transport 读取数据。

UINT _ux_host_class_storage_transport(UX_HOST_CLASS_STORAGE storage, UCHAR data_pointer) { UINT status; UINT csw_status;

/* Reset the sense code.  */
storage -> ux_host_class_storage_sense_code =  UX_SUCCESS;

// call this function read filex data
status =  storage -> ux_host_class_storage_transport(storage, data_pointer);
 ......

}

该函数最终调用 _ux_host_class_storage_transport_bo 完成传输请求,但是这种硬件传输请求在 ehci 中不是 cache 安全的。 能否根据 _ux_system -> ux_system_cache_safe_memory_pool_base 不为 NULL 增加额外的数据拷贝,为上层提供 cache 安全的操作接口:

// 分配 cache safe 缓存
UCHAR *cache_safe_data_buff = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_STORAGE_MAX_TRANSFER_SIZE);

/* Perform the data stage - if there is any.  */
while (data_phase_requested_length != 0)
{
    /* Check if we can finish the transaction with one data phase.  */
    if (data_phase_requested_length > UX_HOST_CLASS_STORAGE_MAX_TRANSFER_SIZE)
        /* We have too much data to send in one phase. Split into smaller chunks.  */
        data_phase_transfer_size =  UX_HOST_CLASS_STORAGE_MAX_TRANSFER_SIZE;
    else
        /* The transfer size can be the requested length.  */
        data_phase_transfer_size =  data_phase_requested_length;

    /* Check the direction and determine which endpoint to use.  */
    if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_IN)
        transfer_request =  &storage -> ux_host_class_storage_bulk_in_endpoint -> ux_endpoint_transfer_request;
    else
        _ux_utility_memory_copy(cache_safe_data_buff, data_pointer, data_phase_transfer_size);

    // 使用 cache 安全内存
    transfer_request -> ux_transfer_request_data_pointer =  cache_safe_data_buff;

    /* Store the requested length in the transfer request.  */
    transfer_request -> ux_transfer_request_requested_length =  data_phase_transfer_size;

    /* Perform data payload transfer (in or out).  */
    status =  _ux_host_stack_transfer_request(transfer_request);

    /* Check the status of the data payload.  */
    if (status == UX_SUCCESS)

        /* Wait for the completion of the transfer request.  */
        status =  _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT));

    /* If the semaphore did not succeed we may have a time out or if we had a problem during the preparation of the transaction
       we should abort the SCSI transaction.  */
    if (status != UX_SUCCESS)
    {
        /* All transfers pending need to abort. There may have been a partial transfer.  */
        _ux_host_stack_transfer_request_abort(transfer_request);

        /* Set the completion code.  */
        transfer_request -> ux_transfer_request_completion_code =  UX_TRANSFER_TIMEOUT;

        /* Error trap. */
        _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT);

        /* If trace is enabled, insert this event into the trace buffer.  */
        UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)

        _ux_utility_memory_free(cache_safe_data_buff);

        /* There was an error that cannot be recovered, return to the caller.  */
        return(UX_TRANSFER_TIMEOUT);
    }
    ......
    /* Adjust the total size that was requested.  */
    data_phase_requested_length -=  data_phase_transfer_size;

    // 拷贝到外部内存
    if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_IN)
        _ux_utility_memory_copy(data_pointer, cache_safe_data_buff, data_phase_transfer_size);

    /* And the data pointer.  */
    data_pointer +=  data_phase_transfer_size;
}

_ux_utility_memory_free(cache_safe_data_buff);
xiaocq2001 commented 1 year ago

Thanks for the feedback.

I notice that you mentioned "SCB_InvalidateDCache is not working". If you are adding cache validating, I would recommend add caching solutions (invalidating or copy to cache safe buffer) to host controller driver (HCD) so upper level can directly use regular memory instead of cache safe memory.

For operation with FileX, the FileX operation buffer is allocated in cache safe memory (in ux_host_class_storage_media_open.c:152), the FileX operations are expected to use the buffer, so the buffer passed to "_transport" is expected to be cache safe, please check if there is some issue to let the buffer "skipped" to cause the issue.

zhangjinxing commented 1 year ago

谢谢! 关于 FileX ,我看到 fx_file_read.c 281 行对 1 扇区读取使用了 fx_media_memory_buffer CACHESAFE 缓存再通过 _fx_utility_memory_copy 拷贝到目标内存。但是在 307 行对多个山区读取时使用 CACHESAFE 缓存,直接进行 USB IO 读取。

// 306 行 media_ptr -> fx_media_disable_burst_cache = file_ptr -> fx_file_disable_burst_cache; status = _fx_utility_logical_sector_read(media_ptr, file_ptr -> fx_file_current_logical_sector, destination_ptr, (ULONG) sectors, FX_DATA_SECTOR); media_ptr -> fx_media_disable_burst_cache = FX_FALSE;

这样 fx_file_read 读取的文件时,一部分使用 fx_media_memory_buffer 再 copy 到内存,另一部分使用 _fx_utility_logical_sector_read 直接读取数据,外面使用 SCB_InvalidateDCache() 函数由于读取的数据没有按 cache line 对齐而出错。

xiaocq2001 commented 1 year ago

Thanks for the feedback. I think for cache safe copying, it's better in HCD. Note that the data copying decreases the performance, so from system side it's better considering to put appliation data in cache page aligned area or cache safe area to avoid re-copying of data.