Problems using Semaphore in SMP, need help

I’m now porting FreeRTOS V11.1.0 with SMP to my RISC-V and encountering some problems when using semaphores. Now I’m running with two cores.

The tasks are created and binded to core-0 and core-1 and the semaphore is initialized as a mutex.

void smp_example(void)
{
    xMutex = xSemaphoreCreateMutex();
    /* create a task and pin them each cpu */
    for (UBaseType_t i = 0; i < CONFIG_NR_CPUS; i++)
    {
        char name[32];
        TaskHandle_t xHandle = NULL;
        snprintf(name, sizeof(name), "thread_pinned_%u", (unsigned int)i);
        UBaseType_t uxCoreAffinityMask = (1 << i); // core id
        BaseType_t ret = xTaskCreate(task_entry, name, 8192 / sizeof(StackType_t), NULL, tskIDLE_PRIORITY + 5 + i, &xHandle);
        task_created[i] = xHandle;
        if (ret != pdPASS)
        {
            printf("create task failed\r\n");
            vTaskResume(xHandle);
        }
        vTaskCoreAffinitySet(xHandle, uxCoreAffinityMask );
    }
}

in task_entry, they take the semaphores and print the line to the COM.

void task_entry(void *parameter)
{
    while (1)
    {
        int id = csi_get_cpu_id();
        TaskHandle_t tcb_cur = xTaskGetCurrentTaskHandle();
        if( xMutex == NULL )
        {
            printf("mutex is null\r\n");
        }
        if (xSemaphoreTake(xMutex, 0) == pdTRUE)
        {
            printf("[%s] in %lu core, tid = %p, count: %d, MIE = 0x%x, MIP = 0x%x\r\n", pcTaskGetName(NULL), (unsigned long)id, tcb_cur, g_count++, __get_MIE(), __get_MIP());
            xSemaphoreGive(xMutex);
            
        }
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

but it hits the assert in xTaskPriorityDisinherit()

          /* A task can only have an inherited priority if it holds the mutex.
           * If the mutex is held by a task then it cannot be given from an
           * interrupt, and if a mutex is given by the holding task then it must
           * be the running state task. */
          configASSERT( pxTCB == pxCurrentTCB );

it’s because both of the two tasks acquire the mutex and access the protected resource at the same time. So we can see the mutex is locked by a task and released by the other.

Does the functions xQueueSemaphoreTake() goes wrong in SMP? taskENTER_CRITICAL() only disables Interrupt of the current core.
I think these code should be execute within the context of a spinlock, but it doesn’t.

       // line 1681 in queue.c, xQueueSemaphoreTake()
        taskENTER_CRITICAL();
        {
            /* Semaphores are queues with an item size of 0, and where the
             * number of messages in the queue is the semaphore's count value. */
            const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;

            /* Is there data in the queue now?  To be running the calling task
             * must be the highest priority task wanting to access the queue. */
            if( uxSemaphoreCount > ( UBaseType_t ) 0 )
            {
                traceQUEUE_RECEIVE( pxQueue );

                /* Semaphores are queues with a data size of zero and where the
                 * messages waiting is the semaphore's count.  Reduce the count. */
                pxQueue->uxMessagesWaiting = ( UBaseType_t ) ( uxSemaphoreCount - ( UBaseType_t ) 1 );

                #if ( configUSE_MUTEXES == 1 )
                {
...

Thanks and appreciate for your help!

Where did you the SMP port for RISC-V or are you writing a new one?

I don’t think that is the proper behavior for it under SMP, as two different cores aren’t supposed to be running in the critical section at once, so there needs to be the use of the spinlocks too.

That is what I suspect that @DylanPoon is trying to run unicore port in SMP environment.

He said he is building a port, and I think he assumes the functions already defined in the single core port are usable and only adding the new ones. As I remember, the instructions for building a SMP port are still a bit sparse, so the task is a challange.

You are right. I hadn’t noticed that at first. The problem was solved by defining portENTER_CRITICAL() as vTaskEnterCritical(). Thank you for your time!

Thank you for sharing your solution!