Why xv6 scheduler calls sti() in the begining of every loop? - operating-system

The companion book says
The reason to enable interrupts periodically on an idling CPU is that
there might be no RUNNABLE process because processes (e.g., the shell)
are waiting for I/O; if the scheduler left interrupts disabled all the
time, the I/O would never arrive.
But I think we just need to call sti() once before the outter for-loop, since everytime we release ptable.lock, the interrupts are enabled again.

It's possible that schedule() is called with interrupts disabled, in which case releasing the ptable spinlock will not reenable them.

If you look at the code for releasing a lock, you will see that it does not explicitly enable interrupts. Instead it uses the function popcli.
void release ( struct spinlock* lk )
{
...
popcli(); // enable interrupts
}
The function popcli does not always enable interrupts. It is used in tandem with pushcli to track nesting level. "Pushcli/popcli are like cli/sti except that they are matched: it takes two popcli to undo two pushcli". 1
void popcli ( void )
{
// If interrupts are enabled, panic...
if ( readeflags() & FL_IF )
{
panic( "popcli: interruptible" );
}
// Track depth of cli nesting
mycpu()->ncli -= 1;
// Popped more than were pushed...
if ( mycpu()->ncli < 0 )
{
panic( "popcli" );
}
// Reached outermost, so restore interrupt state
if ( mycpu()->ncli == 0 && mycpu()->intena )
{
sti(); // enable interrupts
}
}
Whereas popcli sometimes enables interrupts, pushcli always disables interrupts.
void pushcli ( void )
{
int eflags;
eflags = readeflags();
// Disable interrupts
cli();
// Save interrupt state at start of outermost
if ( mycpu()->ncli == 0 )
{
mycpu()->intena = eflags & FL_IF;
}
// Track depth of cli nesting
mycpu()->ncli += 1;
}
By explicitly calling sti, the scheduler overrides whatever the current push/popcli state is. I think this provides the brief window desired to allow an IO interrupt to occur. I.e. the period of time between the call to sti and the call to cli (via acquire -> pushcli -> cli).
void scheduler ( void )
{
...
for ( ;; )
{
// Enable interrupts on this processor.
sti();
// Acquire process table lock
acquire( &ptable.lock );
// Loop over process table looking for process to run.
for ( p = ptable.proc; p < &ptable.proc[ NPROC ]; p += 1 )
{
...
}
// Release process table lock
release( &ptable.lock );
}
}

Related

osdelay stucked after I called HAL_FLASHEx_Erase and HAL_FLASH_Prog in task

I am porting LittleFS on STM32 G431Rb internal Flash. Every thing is OK when I read and write file on main function. But when I write some thing in Task, System will be stuck on osdlelay.
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
int i = 0;
for(;;)
{
ULOG_TRACE("Trace count = %d",i);
i++;
osDelay(5);
}
/* USER CODE END 5 */
}
In ULOG_TRACE function, I called lfs_fs_write function.
lfs_fs_write function call HAL_FLASHEx_Erase and HAL_FLASH_Prog.
static FLASH_EraseInitTypeDef EraseInitStruct;
int stm32_interl_flash_block_erase(const struct lfs_config *c, lfs_block_t block)
{
uint32_t PageError;
__disable_irq();
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP |
FLASH_FLAG_OPERR |
FLASH_FLAG_PROGERR |
FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR |
FLASH_FLAG_SIZERR |
FLASH_FLAG_PGSERR |
FLASH_FLAG_MISERR );
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.Banks = FLASH_BANK_1;
EraseInitStruct.Page = FS_BASE_PAGE_START + block;
EraseInitStruct.NbPages = 1;
if (HAL_FLASHEx_Erase(&EraseInitStruct,&PageError)!= HAL_OK){
__enable_irq();
return HAL_FLASH_GetError();
}
HAL_FLASH_Lock();
__enable_irq();
return 0;
}
int stm32_interl_flash_block_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size)
{
__disable_irq();
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP |
FLASH_FLAG_OPERR |
FLASH_FLAG_PROGERR |
FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR |
FLASH_FLAG_SIZERR |
FLASH_FLAG_PGSERR |
FLASH_FLAG_MISERR );
uint32_t dest_addr = FS_BASE_ADDR + c->block_size*block +off;
uint64_t *pSrc = (uint64_t*)buffer;
uint32_t write_size = 0;
while(write_size < size){
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,dest_addr,*(pSrc)) != HAL_OK){
HAL_FLASH_Lock();
__enable_irq();
return HAL_FLASH_GetError();
}
pSrc++;
dest_addr += 8;
write_size += 8;
}
HAL_FLASH_Lock();
__enable_irq();
return 0;
}
I googled these problem, some guys said the problem is that the task schedule interrupt occur when I erase or prog internal flash.But I add disable_irq, it also have the problem.
Do not disable all interrupts. At least the HAL tick should run further. The HAL_FLASH_* methods depend on them.
Use osKernelSuspend() and osKernelResume() to stop and continue thread scheduling.
Further more check all return codes, also from HAL_FLASH_Unlock.
The flash peripheral is a little nasty on the STM32 controller family.
If something going wrong, either you access addresses unaligned, out of bounds or simple not in the correct order the peripheral set its error bits and the HAL functions do not operate anymore. You already clear the error flags.
I've experienced that checking and clearing the error flags also after HAL_FLASH_Program is always a good idea.
PS: If your code really stuck in osDelay then your cmsis os scheduler interrupt is not running or the scheduler is disabled/stoped.
You haven't specified how long the code gets stuck for or why you think it is stuck in osDelay, but here is one guess:
If you are writing or erasing in the same flash bank as you are executing code from, your code will stop until the operation has completed. This will block all tasks and all interrupts whether they are turned on or off. This is because no instructions can be fetched from flash while it is writing or erasing. A few instructions might still execute if they have already been prefetched.
If you are able to use dual-bank mode you can write one bank while executing from the other, but I think the 128kB parts might only support single bank flash, and others may have dual-bank disabled by default.

Callback parameter in stm32 freeRTOS software timer

I'm trying to use a software timer with cubeMx integration of freeRTOS (basically, it's freeRTOS with a nearly transparent layer above).
I thought I would be able to pass a pointer to a structure as timer parameter and getting it as a parameter in the callback function. Something like this:
typedef struct{
uint8_t a;
uint8_t b;
uint8_t c;
}T;
T t = {1, 2, 3};
osTimerDef(myTimer01, Callback01);
myTimer01Handle = osTimerCreate(osTimer(myTimer01), osTimerPeriodic, (void*) &t);
osTimerStart(myTimer01Handle, 5000);
callback:
void Callback01(void const * argument)
{
T* a = argument;
}
Unfortunately argument does not point to the same address as &t. When I look to freeRTOS code, it appears that the lib passes a structure "Timer_t" casted as void* to the callback function (see end of the code below):
static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow )
{
BaseType_t xResult;
Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList );
/* Remove the timer from the list of active timers. A check has already
been performed to ensure the list is not empty. */
( void ) uxListRemove( &( pxTimer->xTimerListItem ) );
traceTIMER_EXPIRED( pxTimer );
/* If the timer is an auto reload timer then calculate the next
expiry time and re-insert the timer in the list of active timers. */
if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE )
{
/* The timer is inserted into a list using a time relative to anything
other than the current time. It will therefore be inserted into the
correct list relative to the time this task thinks it is now. */
if( prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xNextExpireTime ) != pdFALSE )
{
/* The timer expired before it was added to the active timer
list. Reload it now. */
xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY );
configASSERT( xResult );
( void ) xResult;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Call the timer callback. */
pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );
}
The structure is:
typedef struct tmrTimerControl
{
const char *pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
ListItem_t xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */
TickType_t xTimerPeriodInTicks;/*<< How quickly and often the timer expires. */
UBaseType_t uxAutoReload; /*<< Set to pdTRUE if the timer should be automatically restarted once expired. Set to pdFALSE if the timer is, in effect, a one-shot timer. */
void *pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */
TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */
} xTIMER;
typedef xTIMER Timer_t;
This structure contains the data pointer I passed when I created the timer. It is stored in pvTimerId.
This means that I should cast the callback parameter as Timer_t in order to have access to pvTimerId. Something like this:
void Callback01(void const * argument)
{
T* a =((Timer_t*)argument)->pvTimerID;
}
BUT this Timer_t structure is not public. I don't really understand why the callback is called with this structure as parameter and moreover casted as const void*...
How should I do?
Considering the call to the osTimerCreate function in your version of cmsis stores realy the argument parameter to the pvTimerID of the Timer_t structure then you could use pvTimerGetTimerID to get your passed data back:
void Callback01(void const * argument)
{
T* data = (T*)pvTimerGetTimerID((TimerHandle_t)argument);
}

RTOS STM32F4 discovery

i'm actually working on my final project for graduation. I'm using FreeRTOS on STM32F4 discovery. It all works properly, but the tasks are not ordered as i like. they execute in this cycle : task3 twice, task2 once, then again task3 twice and tas2 once and then task1 once.
I want them to execute iin this order : task1 then task2 then task3. Thank you!
Here is a portion of my code:
/* The period of the example software timer, specified in milliseconds, and
converted to ticks using the portTICK_RATE_MS constant. */
#define mainSOFTWARE_TIMER_PERIOD_MS ( 1000 / portTICK_RATE_MS )
int main(void)
{
/* Configure the system ready to run the demo. The clock configuration
can be done here if it was not done before main() was called. */
prvSetupHardware();
/* Create the queue used by the queue send and queue receive tasks.
http://www.freertos.org/a00116.html */
xQueue = xQueueCreate( mainQUEUE_LENGTH, /* The number of items the queue can hold. */
sizeof( uint32_t ) ); /* The size of each item the queue holds. */
/* Add to the registry, for the benefit of kernel aware debugging. */
vQueueAddToRegistry( xQueue, ( signed char * ) "MainQueue" );
/* Create the semaphore used by the FreeRTOS tick hook function and the
event semaphore task. */
vSemaphoreCreateBinary( xEventSemaphore );
/* Add to the registry, for the benefit of kernel aware debugging. */
vQueueAddToRegistry( xEventSemaphore, ( signed char * ) "xEventSemaphore" );
/* Create the MPXV7002DP task */
xTaskCreate( vMPXV7002DPTask, /* The function that implements the task. */
( signed char * ) "MPXV7002DP", /* Text name for the task, just to help debugging. */
configMINIMAL_STACK_SIZE, /* The size (in words) of the stack that should be created for the task. */
NULL, /* A parameter that can be passed into the task. Not used in this simple demo. */
configMAX_PRIORITIES - 1, /* The priority to assign to the task. tskIDLE_PRIORITY (which is 0) is the lowest priority. configMAX_PRIORITIES - 1 is the highest priority. */
NULL ); /* Used to obtain a handle to the created task. Not used in this simple demo, so set to NULL. */
/* Create the MPU9250 task */
xTaskCreate( vMPU9250Task,
( signed char * ) "MPU9250",
configMINIMAL_STACK_SIZE,
NULL,
configMAX_PRIORITIES - 1,
NULL );
/* Create the MPL3115A2 task */
xTaskCreate( vMPL3115A2Task,
( signed char * ) "MPL3115A2",
configMINIMAL_STACK_SIZE,
NULL,
configMAX_PRIORITIES - 1,
NULL );
/* Create the TOPC task */
//xTaskCreate( vToPcTask,
// ( signed char * ) "ToPc",
// configMINIMAL_STACK_SIZE,
// NULL,
//configMAX_PRIORITIES - 4,
//NULL );
/* Start the tasks and timer running. */
vTaskStartScheduler();
}
static void vMPXV7002DPTask(void *pvParameters)
{
int convertedValue,pressure,v;
for(;;)
{
if(xSemaphoreTake( xEventSemaphore, mainSOFTWARE_TIMER_PERIOD_MS ))
{USART_puts(USART1, "mpxv begin\n\r");
ADC_SoftwareStartConv(ADC1);//Start the conversion
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));//Processing the conversion
convertedValue = ADC_GetConversionValue(ADC1); //Return the converted dat
convertedValue=(5*convertedValue)/255;
pressure=(convertedValue+0.0625*4)-0.5;
v=sqrt((2*pressure)/1.293);
USART_puts(USART1, "mpxv end\n\r");
xSemaphoreGive( xEventSemaphore );
}
vTaskDelay( mainSOFTWARE_TIMER_PERIOD_MS );
}
}
static void vMPU9250Task(void *pvParameters)
{
int16_t xa,ya,za,xg,yg,zg,xm,ym,zm;
uint8_t res[22];
for( ;; )
{
if(xSemaphoreTake( xEventSemaphore, mainSOFTWARE_TIMER_PERIOD_MS ))
{USART_puts(USART1, "mpu begin\n\r");
SPI_Tx(0x25,0x0C|0x80);//read from slv0
SPI_Tx(0x26,0x03);//reg from which start reading
SPI_Tx(0x27,0x87);//read 7 bytes
SPI_Rx_seq(0x3A,res,22);
xa=((int16_t)res[1]<<8)|res[2];
xa/=8192;
ya=((int16_t)res[3]<<8)|res[4];
ya/=8192;
za=((int16_t)res[5]<<8)|res[6];
za/=8192;
xg=((int16_t)res[9]<<8)|res[10];
xg/=131;
yg=((int16_t)res[11]<<8)|res[12];
yg/=131;
zg=((int16_t)res[13]<<8)|res[14];
zg/=131;
//AK8963_Rx_seq( 0x03, mag_asax, 7);
//SPI_Rx_seq(0x49,mag_asax,7);
xm=((int16_t)res[16]<<8)|res[15];
ym=((int16_t)res[18]<<8)|res[17];
zm=((int16_t)res[20]<<8)|res[19];
USART_puts(USART1, "mpu end\n\r");
xSemaphoreGive( xEventSemaphore );
}
vTaskDelay( mainSOFTWARE_TIMER_PERIOD_MS/2 );
}
}
static void vMPL3115A2Task( void *pvParameters )
{
uint8_t altitude[3];
uint32_t x;
char alt[1];
for( ;; )
{
if(xSemaphoreTake( xEventSemaphore, mainSOFTWARE_TIMER_PERIOD_MS ))
{USART_puts(USART1, "mpl begin\n\r");
Read_IIC1_seq(0x60<<1, 0x01, altitude, 3);
x=altitude[0];x<<=8;
x|=altitude[1];x<<=8;
x|=altitude[2];x>>=4;
x/=49920;
USART_puts(USART1, "mpl end\n\r");
xSemaphoreGive( xEventSemaphore );
}
vTaskDelay( mainSOFTWARE_TIMER_PERIOD_MS/4 );
}
}
Look at the call to vTaskDelay() in each task function. One task delays for PERIOD, the next for PERIOD/2, and the third for PERIOD/4. The task that delays for PERIOD/4 will be ready to run four times for every time that the task that delays for PERIOD will be ready to run. This is why you are seeing one task run four times, the next two times, and the third once. Why did you use different delay periods if you want the tasks to run at the same rate?
As for which task runs first at the beginning, that is going to depend on how the FreeRTOS scheduler is implemented. You assigned the same priority (configMAX_PRIORITIES - 1) to each task in the calls to xTaskCreate(). The FreeRTOS scheduler is probably using its round-robin scheduling algorithm for tasks with the same priority. And I'm guessing that the scheduler readies the tasks in the order they were created (or maybe reverse order). So you might be able to affect the ready order by changing the creation order. But I'm just guessing and you should look at the source code for the FreeRTOS scheduler to learn what it does. Or maybe you should give the tasks different priorities. Then the FreeRTOS scheduler should make the task with the highest priority ready to run first.

what's the difference between pending and active event in Libevent?

I'm learning how to use Libevent.While I can't understand the difference between pending and active.In my opinion,when a event is added to a event_base and the event hasn't happened, then it's in pending state,while the event which is waited by the caller is happened,then in the active state,is that right?Hoever,when I read the description of event_pending,see the code blew,it says when the event is pending,it's bad to change the inner state of it,i think the word "pending" here is misunderstand,it should be "event_active"....Am i wrong ?
#include <event2/event.h>
#include <stdio.h>
/* Change the callback and callback_arg of 'ev', which must not be
* pending. */
int replace_callback(struct event *ev, event_callback_fn new_callback,
void *new_callback_arg)
{
struct event_base *base;
evutil_socket_t fd;
short events;
int pending;
pending = event_pending(ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT,
NULL);
if (pending) {
/* We want to catch this here so that we do not re-assign a
* pending event. That would be very very bad. */
fprintf(stderr,
"Error! replace_callback called on a pending event!\n");
return -1;
}
event_get_assignment(ev, &base, &fd, &events,
NULL /* ignore old callback */ ,
NULL /* ignore old callback argument */);
event_assign(ev, base, fd, events, new_callback, new_callback_arg);
return 0;
}
pending event means that some action triggered and execution of event callback is queued in event_base.
active event means that event callback is currently executed current thread or some other(do not forget that you can manipulate events from other thread, that is not running event loop)
Reassigning a pending event is not thread safe operation, you can see it in event.c.
I think if you writing single-threaded application it's safe to reassigning event at any time.
libevent book quote (http://www.wangafu.net/~nickm/libevent-book/Ref4_event.html):
Once you call a Libevent function to set up an event and associate it with an event base, it becomes initialized. At this point, you can add, which makes it pending in the base. When the event is pending, if the conditions that would trigger an event occur (e.g., its file descriptor changes state or its timeout expires), the event becomes active, and its (user-provided) callback function is run.
So "pending" in libevent terms means "just added in reactor". No action triggering need to get pending event. I've checked this behavior with simple program:
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <event.h>
void read_cb( int sock, short which, void *arg )
{
printf( "read_cb() called\n" );
fflush( stdout );
// while our callback is running event is active
}
int main()
{
struct event_base *base = event_base_new();
int fd[ 2 ];
assert( pipe( fd ) == 0 );
struct event ev;
event_set( & ev, fd[ 0 ], EV_READ | EV_PERSIST, read_cb, NULL );
event_base_set( base, & ev );
assert( event_add( & ev, NULL ) != -1 );
// it's pending now, just after adding
printf( "event_pending( ... ) == %d\n", event_pending( & ev, EV_READ, NULL ) );
fflush( stdout );
event_base_loop( base, EVLOOP_ONCE );
return 0;
}
Output:
event_pending( ... ) == 2

Mutual Exclusion Problem

Please take a look on the following pseudo-code:
boolean blocked[2];
int turn;
void P(int id) {
while(true) {
blocked[id] = true;
while(turn != id) {
while(blocked[1-id])
/* do nothing */;
turn = id;
}
/* critical section */
blocked[id] = false;
/* remainder */
}
}
void main() {
blocked[0] = false;
blocked[1] = false;
turn = 0;
parbegin(P(0), P(1)); //RUN P0 and P1 parallel
}
I thought that a could implement a simple Mutual - Exclution solution using the code above. But it's not working. Has anyone got an idea why?
Any help would really be appreciated!
Mutual Exclusion is in this exemple not guaranteed because of the following:
We begin with the following situation:
blocked = {false, false};
turn = 0;
P1 is now executes, and skips
blocked[id] = false; // Not yet executed.
The situation is now:
blocked {false, true}
turn = 0;
Now P0 executes. It passes the second while loop, ready to execute the critical section. And when P1 executes, it sets turn to 1, and is also ready to execute the critical section.
Btw, this method was originally invented by Hyman. He sent it to Communications of the Acm in 1966
Mutual Exclusion is in this exemple not guaranteed because of the following:
We begin with the following situation:
turn= 1;
blocked = {false, false};
The execution runs as follows:
P0: while (true) {
P0: blocked[0] = true;
P0: while (turn != 0) {
P0: while (blocked[1]) {
P0: }
P1: while (true) {
P1: blocked[1] = true;
P1: while (turn != 1) {
P1: }
P1: criticalSection(P1);
P0: turn = 0;
P0: while (turn != 0)
P0: }
P0: critcalSection(P0);
Is this homework, or some embedded platform? Is there any reason why you can't use pthreads or Win32 (as relevant) synchronisation primitives?
Maybe you need to declare blocked and turn as volatile, but without specifying the programming language there is no way to know.
Concurrency can not be implemented like this, especially in a multi-processor (or multi-core) environment: different cores/processors have different caches. Those caches may not be coherent. The pseudo-code below could execute in the order shown, with the results shown:
get blocked[0] -> false // cpu 0
set blocked[0] = true // cpu 1 (stored in CPU 1's L1 cache)
get blocked[0] -> false // cpu 0 (retrieved from CPU 0's L1 cache)
get glocked[0] -> false // cpu 2 (retrieved from main memory)
You need hardware knowledge to implement concurrency.
Compiler might have optimized out the "empty" while loop. Declaring variables as volatile might help, but is not guaranteed to be sufficient on multiprocessor systems.