What is the correct use of the WAIT instruction in order to avoid race conditions?

Tagged: ,

This topic contains 1 reply, has 1 voice, and was last updated by  ChrisImgtec 3 years, 11 months ago.

Viewing 2 posts - 1 through 2 (of 2 total)
  • Author
    Posts
  • #31571

    ChrisImgtec
    Moderator

    The wait instruction causes the CPU to enter a low-power sleep mode until awakened by an interrupt. Most of the core logic is stopped, but the Count register, in particular, continues to run. The wait instruction is commonly used in the OS idle loop when there is nothing for the CPU to do.

    Improper use of the WAIT instruction can cause a race condition that could cause the system to hang. Here is an example of a common race condition involving the wait instruction:

    if (!pending )
    {
    wait();
    }

    In the above example, if an interrupt occurs between the pending check and the WAIT instruction, the WAIT will wait forever if there are no more interrupts.

    The code example below ensures proper functioning of the WAIT instruction by checking to see if a race condition has occurred. This code is used in Linux, but it should be adaptable for any OS. The code uses an assembler wait function to execute the wait sequence. The interrupt routines checks to see if an interrupt occurred within the wait function by checking the EPC value against the address range of the wait function. If it finds the EPC is in the address range, the interrupt routine reprograms the EPC register so execution will resume at the beginning of the wait function after the interrupt returns, instead of somewhere in the middle of the wait function. This makes sure that the pending flag, which might have been updated by the interrupt routine, will be assessed again before the WAIT instruction is executed. For this code to work, the wait function needs to be aligned on a 32-byte boundary, and there must be exactly 8 instructions from the beginning of the function through the WAIT instruction — this alignment makes it easy to determine the address range of the wait function. We must also use “.set noreorder” so the assembler will not rearrange the code or eliminate NOP instructions we have placed to control the alignment of the code.

    .align 5 /* align to a 32 byte address */

    LEAF(r4k_wait)
    .set push
    .set noreorder
    /* start of rollback region */
    LONG_L t0, TI_FLAGS($28) # get flag bits
    nop
    andi t0, _TIF_NEED_RESCHED # isolate rescheduling flag
    bnez t0, 1f # branch around wait if
    # there is something to do
    nop # nops need for padding
    nop
    nop
    wait
    /* end of rollback region */
    .set pop
    1:
    jr ra
    END(r4k_wait)

    The first thing an interrupt routine should do is execute the following code:

    .set push
    .set noat
    MFC0 k0, CP0_EPC # get the EPC
    PTR_LA k1, r4k_wait # get the address of the wait function
    ori k0, 0x1f # align the EPC to a 32 byte boundary
    xori k0, 0x1f
    bne k0, k1, 9f # See if the EPC is in the wait
    # function’s address range
    MTC0 k0, CP0_EPC # It is in range so reset the EPC
    # to the beginning of the wait function
    9:
    .set pop

    A new feature has been added to some MIPS Technologies’ cores that permits termination of the WAIT instruction by an active interrupt signal, even if that signal is prevented from causing an interrupt by Status[IE] being clear. (The IE bit is the global interrupt enable, clearing it disables all interrupts.) The presence of this core feature can be determined by reading the WII (Wait IE/IXMT Ignore) bit in the Config7 register, where 1 = core feature present. This feature only changes the behavior of the IE bit when the core is in a wait state. Interrupts are still prevented in the wait state if the interrupt line is masked.

    The correct use of the WAIT instruction is simpler if the WII bit is set, as shown in the code below. Because the wait condition can be terminated even if interrupts are disabled, all that is needed is to disable interrupts prior to the check for pending things to do. This avoids the possibility of an interrupt occuring between the pending check and the WAIT instruction.

    Disable_interrupts(); /* by clearing the IE bit in Status. */
    if ( !pending )
    {
    wait();
    }
    Enable_interrupts();

    The assembly code is:

    LEAF(wait_interrupts_disabled)

    .set push
    .set noreorder
    mfc0 t4, C0_STATUS
    move t3, t4 # preserve status value in t4
    ins t3, zero, 0, 1 # clear IE bit
    mtc0 t3, C0_STATUS # disable
    LONG_L t0, TI_FLAGS($28) # get flag bits
    nop
    andi t0, _TIF_NEED_RESCHED # isolate rescheduling flag
    bnez t0, 1f # branch around wait if
    # there is something to do
    wait
    mtc0 t4, C0_STATUS # restore status
    .set pop
    1: jr ra
    END(r4k_wait)

    Portable code that can be used whether or not WII is set is shown below.

    mfc0 t4, C0_CONFIG7
    ext t3, t4, 31, 1 # get WII bit
    bnez t3, 1f
    jal wait_interrupts_enabled
    1:
    jal wait_interrupts_disable

    #38261

    ChrisImgtec
    Moderator

    Imagination MIPS Support

Viewing 2 posts - 1 through 2 (of 2 total)
You must be logged in to reply to this topic.