mirror of
https://github.com/apache/nuttx.git
synced 2026-10-06 05:47:54 +00:00
Completes demand paging core implementation
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2853 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
parent
03f1e72ad8
commit
b1c00675cb
9 changed files with 830 additions and 86 deletions
|
|
@ -72,7 +72,7 @@
|
|||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<a href="#Fillnitiation">Fill Initiation</a>
|
||||
<a href="#FillInitiation">Fill Initiation</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
@ -160,14 +160,14 @@
|
|||
<dl>
|
||||
<dt><code>g_waitingforfill</code></dt>
|
||||
<dd>An OS list that is used to hold the TCBs of tasks that are waiting for a page fill.</dd>
|
||||
<dt><code>g_pendingfill</code></dt>
|
||||
<dt><code>g_pendingfilltcb</code></dt>
|
||||
<dd>A variable that holds a reference to the TCB of the thread that is currently be re-filled.</dd>
|
||||
<dt><code>g_pgworker</code></dt>
|
||||
<dd>The <i>process</i> ID of of the thread that will perform the page fills.</dd>
|
||||
<dt><code>pg_callback()</code></dt>
|
||||
<dd>The callback function that is invoked from a driver when the fill is complete.</dd>
|
||||
<dt><code>pg_miss()</code></dt>
|
||||
<dd>The function that is called from chip-specific code to handle a page fault.</dd>
|
||||
<dd>The function that is called from architecture-specific code to handle a page fault.</dd>
|
||||
<dt><code>TCB</code></dt>
|
||||
<dd>Task Control Block</dd>
|
||||
</dl>
|
||||
|
|
@ -215,8 +215,10 @@
|
|||
<p>
|
||||
Declarations for <code>g_waitingforfill</code>, <code>g_pgworker</code>, and other
|
||||
internal, private definitions will be provided in <code>sched/pg_internal.h</code>.
|
||||
All public definitions that should be used by the chip-specific code will be available
|
||||
in <code>include/nuttx/page.h</code> and <code>include/nuttx/arch.h</code>.
|
||||
All public definitions that should be used by the architecture-specific code will be available
|
||||
in <code>include/nuttx/page.h</code>.
|
||||
Most architecture-specific functions are declared in <code>include/nuttx/arch.h</code>,
|
||||
but for the case of this paging logic, those architecture specific functions are instead declared in <code>include/nuttx/page.h</code>.
|
||||
</p>
|
||||
|
||||
<a name="PageFaults"><h2>Page Faults</h2></a>
|
||||
|
|
@ -244,6 +246,7 @@
|
|||
<b>Boost the page fill worker thread priority</b>.
|
||||
Check the priority of the task at the head of the <code>g_waitingforfill</code> list.
|
||||
If the priority of that task is higher than the current priority of the page fill worker thread, then boost the priority of the page fill worker thread to that priority.
|
||||
Thus, the page fill worker thread will always run at the priority of the highest priority task that is waiting for a fill.
|
||||
</li>
|
||||
<li>
|
||||
<b>Signal the page fill worker thread</b>.
|
||||
|
|
@ -277,51 +280,46 @@
|
|||
Interrupt handling logic must always be available and "<a href="#MemoryOrg">locked</a>" into memory so that page faults never come from interrupt handling.
|
||||
</li>
|
||||
<li>
|
||||
The chip-specific page fault exception handling has already verified that the exception did not occur from interrupt/exception handling logic.
|
||||
The architecture-specific page fault exception handling has already verified that the exception did not occur from interrupt/exception handling logic.
|
||||
</li>
|
||||
<li>
|
||||
As mentioned above, the task causing the page fault must not be the page fill worker thread because that is the only way to complete the page fill.
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
<b>Locking code in Memory</b>.
|
||||
One way to accomplish this would be a two phase link:
|
||||
<ul>
|
||||
<li>
|
||||
In the first phase, create a partially linked objected containing all interrupt/exception handling logic, the page fill worker thread plus all parts of the IDLE thread (which must always be available for execution).
|
||||
</li>
|
||||
<li>
|
||||
All of the <code>.text</code> and <code>.rodata</code> sections of this partial link should be collected into a single section.
|
||||
</li>
|
||||
<li>
|
||||
The second link would link the partially linked object along with the remaining object to produce the final binary.
|
||||
The linker script should position the "special" section so that it lies in a reserved, "non-swappable" region.
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<a name="FillInitiation"><h2>Fill Initiation</h2></a>
|
||||
|
||||
<p>
|
||||
The page fill worker thread will be awakened on one of two conditions:
|
||||
The page fill worker thread will be awakened on one of three conditions:
|
||||
<ul>
|
||||
<li>
|
||||
When signaled by <code>pg_miss()</code>, the page fill worker thread will be awakenend (see above), or
|
||||
When signaled by <code>pg_miss()</code>, the page fill worker thread will be awakenend (see above),
|
||||
</li>
|
||||
<li>
|
||||
From <code>pg_fillcomplete()</code> after completing last fill (see below).
|
||||
From <code>pg_callback()</code> after completing last fill (when <code>CONFIG_PAGING_BLOCKINGFILL</code> is defined... see below), or
|
||||
</li>
|
||||
<li>
|
||||
A configurable timeout expires with no activity.
|
||||
This timeout can be used to detect failure conditions such things as fills that never complete.
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The page fill worker thread will maintain a static variable called <code>_TCB *g_pendingfill</code>.
|
||||
If not fill is in progress, <code>g_pendingfill</code> will be NULL.
|
||||
Otherwise, will point to the TCB of the task which is receiving the fill that is in progess.
|
||||
The page fill worker thread will maintain a static variable called <code>_TCB *g_pendingfilltcb</code>.
|
||||
If no fill is in progress, <code>g_pendingfilltcb</code> will be NULL.
|
||||
Otherwise, it will point to the TCB of the task which is receiving the fill that is in progess.
|
||||
</p>
|
||||
<ul><small>
|
||||
<b>NOTE</b>:
|
||||
I think that this is the only state in which a TCB does not reside in some list.
|
||||
Here is it in limbo, outside of the normally queuing while the page file is in progress.
|
||||
While here, it will be marked with TSTATE_TASK_INVALID.
|
||||
</small></ul>
|
||||
|
||||
<p>
|
||||
When awakened from <code>pg_miss()</code>, no fill will be in progress and <code>g_pendingfill</code> will be NULL.
|
||||
When awakened from <code>pg_miss()</code>, no fill will be in progress and <code>g_pendingfilltcb</code> will be NULL.
|
||||
In this case, the page fill worker thread will call <code>pg_startfill()</code>.
|
||||
That function will perform the following operations:
|
||||
<ul>
|
||||
|
|
@ -334,50 +332,73 @@
|
|||
</li>
|
||||
<li>
|
||||
Call <code>up_allocpage(tcb, &vpage)</code>.
|
||||
This chip-specific function will set aside page in memory and map to virtual address (vpage).
|
||||
If all pages available pages are in-use (the typical case),
|
||||
This architecture-specific function will set aside page in memory and map to virtual address (vpage).
|
||||
If all available pages are in-use (the typical case),
|
||||
this function will select a page in-use, un-map it, and make it available.
|
||||
</li>
|
||||
<li>
|
||||
Call the chip-specific function <code>up_fillpage(page, pg_callback)</code>.
|
||||
This will start asynchronous page fill.
|
||||
The page fill worker thread will provide a callback function, <code>pg_callback</code>,
|
||||
that will be called when the page fill is finished (or an error occurs).
|
||||
This callback will probably from interrupt level.
|
||||
</li>
|
||||
<li>
|
||||
Restore default priority of the page fill worker thread's default priority and wait to be signaled for the next event -- the fill completion event.
|
||||
Call the architecture-specific function <code>up_fillpage()</code>.
|
||||
Two versions of the up_fillpage function are supported -- a blocking and a non-blocking version based upon the configuratin setting <code>CONFIG_PAGING_BLOCKINGFILL</code>.
|
||||
<ul>
|
||||
<li>
|
||||
If <code>CONFIG_PAGING_BLOCKINGFILL</code> is defined, then up_fillpage is blocking call.
|
||||
In this case, <code>up_fillpage()</code> will accept only (1) a reference to the TCB that requires the fill.
|
||||
Architecture-specific context information within the TCB will be sufficient to perform the fill.
|
||||
And (2) the (virtual) address of the allocated page to be filled.
|
||||
The resulting status of the fill will be provided by return value from <code>up_fillpage()</code>.
|
||||
</li>
|
||||
<li>
|
||||
If <code>CONFIG_PAGING_BLOCKINGFILL</code> is defined, then up_fillpage is non-blocking call.
|
||||
In this case <code>up_fillpage()</code> will accept an additional argument:
|
||||
The page fill worker thread will provide a callback function, <code>pg_callback</code>.
|
||||
This function is non-blocking, it will start an asynchronous page fill.
|
||||
After calling the non-blocking <code>up_fillpage()</code>, the page fill worker thread will wait to be signaled for the next event -- the fill completion event.
|
||||
The callback function will be called when the page fill is finished (or an error occurs).
|
||||
The resulting status of the fill will be providing as an argument to the callback functions.
|
||||
This callback will probably occur from interrupt level.
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
While the fill is in progress, other tasks may execute.
|
||||
If another page fault occurs during this time, the faulting task will be blocked and its TCB will be added (in priority order) to <code>g_waitingforfill</code>.
|
||||
In any case, while the fill is in progress, other tasks may execute.
|
||||
If another page fault occurs during this time, the faulting task will be blocked, its TCB will be added (in priority order) to <code>g_waitingforfill</code>, and the priority of the page worker task may be boosted.
|
||||
But no action will be taken until the current page fill completes.
|
||||
NOTE: The IDLE task must also be fully <a href="#MemoryOrg">locked</a> in memory.
|
||||
The IDLE task cannot be blocked.
|
||||
It the case where all tasks are blocked waiting for a page fill, the IDLE task must still be available to run.
|
||||
<p>
|
||||
The chip-specific functions, <code>up_allocpage(tcb, &vpage)</code> and <code>up_fillpage(page, pg_callback)</code>
|
||||
The architecture-specific functions, <code>up_checkmapping()</code>, <code>up_allocpage(tcb, &vpage)</code> and <code>up_fillpage(page, pg_callback)</code>
|
||||
will be prototyped in <code>include/nuttx/arch.h</code>
|
||||
</p>
|
||||
|
||||
<a name="FillComplete"><h2>Fill Complete</h2></a>
|
||||
|
||||
<p>
|
||||
When the chip-specific driver completes the page fill, it will call the <code>pg_callback()</code> that was provided to <code>up_fillpage</code>.
|
||||
<code>pg_callback()</code> will probably be called from driver interrupt-level logic.
|
||||
The driver ill provide the result of the fill as an argument.
|
||||
For the blocking <code>up_fillpage()</code>, the result of the fill will be returned directly from the call to <code>up_fillpage</code>.
|
||||
</p>
|
||||
<p>
|
||||
For the non-blocking <code>up_fillpage()</code>, the architecture-specific driver call the <code>pg_callback()</code> that was provided to <code>up_fillpage()</code> when the fill completes.
|
||||
In this case, the <code>pg_callback()</code> will probably be called from driver interrupt-level logic.
|
||||
The driver will provide the result of the fill as an argument to the callback function.
|
||||
NOTE: <code>pg_callback()</code> must also be <a href="#MemoryOrg">locked</a> in memory.
|
||||
</p>
|
||||
<p>
|
||||
When <code>pg_callback()</code> is called, it will perform the following operations:
|
||||
In this non-blocking case, the callback <code>pg_callback()</code> will perform the following operations when it is notified that the fill has completed:
|
||||
<ul>
|
||||
<li>
|
||||
Verify that <code>g_pendingfill</code> is non-NULL.
|
||||
Verify that <code>g_pendingfilltcb</code> is non-NULL.
|
||||
</li>
|
||||
<li>
|
||||
If the priority of thread in <code>g_pendingfill</code> is higher than page fill worker thread, boost work thread to that level.
|
||||
Find the higher priority between the task waiting for the fill to complete in <code>g_pendingfilltcb</code> and the task waiting at the head of the <code>g_waitingforfill</code> list.
|
||||
That will be the priority of he highest priority task waiting for a fill.
|
||||
</li>
|
||||
<li>
|
||||
If this higher priority is higher than current page fill worker thread, then boost worker thread's priority to that level.
|
||||
Thus, the page fill worker thread will always run at the priority of the highest priority task that is waiting for a fill.
|
||||
</li>
|
||||
<li>
|
||||
Save the result of the fill operation.
|
||||
</li>
|
||||
<li>
|
||||
Signal the page fill worker thread.
|
||||
|
|
@ -388,18 +409,20 @@
|
|||
<a name="TaskResumption"><h2>Task Resumption</h2></a>
|
||||
|
||||
<p>
|
||||
When the page fill worker thread is awakened and <code>g_pendingfill</code> is non-NULL (and other state variables are in concurrence),
|
||||
the page fill thread will know that is was awakened because of a page fill completion event.
|
||||
In this case, the page fill worker thread will:
|
||||
For the non-blocking <code>up_fillpage()</code>, the page fill worker thread will detect that the page fill is complete when it is awakened with <code>g_pendingfilltcb</code> non-NULL and fill completion status from <code>pg_callback</code>.
|
||||
In the non-blocking case, the page fill worker thread will know that the page fill is complete when <code>up_fillpage()</code> returns.
|
||||
</p>
|
||||
<p>
|
||||
In this either, the page fill worker thread will:
|
||||
<ul>
|
||||
<li>
|
||||
Verify consistency of state information and <code>g_pendingfill</code>.
|
||||
Verify consistency of state information and <code>g_pendingfilltcb</code>.
|
||||
</li>
|
||||
<li>
|
||||
Verify that the page fill completed successfully, and if so,
|
||||
</li>
|
||||
<li>
|
||||
Call <code>up_unblocktask(g_pendingfill)</code> to make the task that just received the fill ready-to-run.
|
||||
Call <code>up_unblocktask(g_pendingfilltcb)</code> to make the task that just received the fill ready-to-run.
|
||||
</li>
|
||||
<li>
|
||||
Check if the <code>g_waitingforfill</code> list is empty.
|
||||
|
|
@ -409,10 +432,10 @@
|
|||
Remove the highest priority task waiting for a page fill from <code>g_waitingforfill</code>,
|
||||
</li>
|
||||
<li>
|
||||
Save the task's TCB in <code>g_pendingfill</code>,
|
||||
Save the task's TCB in <code>g_pendingfilltcb</code>,
|
||||
</li>
|
||||
<li>
|
||||
If the priority of the thread in <code>g_pendingfill</code>, is higher in priority than the default priority of the page fill worker thread, then set the priority of the page fill worker thread to that priority.
|
||||
If the priority of the thread in <code>g_pendingfilltcb</code>, is higher in priority than the default priority of the page fill worker thread, then set the priority of the page fill worker thread to that priority.
|
||||
</li>
|
||||
<li>
|
||||
Call <code>pg_startfill()</code> which will start the next fill (as described above).
|
||||
|
|
@ -423,7 +446,7 @@
|
|||
Otherwise,
|
||||
<ul>
|
||||
<li>
|
||||
Set <code>g_pendingfill</code> to NULL.
|
||||
Set <code>g_pendingfilltcb</code> to NULL.
|
||||
</li>
|
||||
<li>
|
||||
Restore the default priority of the page fill worker thread.
|
||||
|
|
@ -447,6 +470,7 @@
|
|||
<a name="MemoryOrg"><h2>Memory Organization</h2></a>
|
||||
|
||||
<p>
|
||||
<b>Memory Regions</b>.
|
||||
Chip specific logic will map the virtual and physical address spaces into three general regions:
|
||||
<ol>
|
||||
<li>
|
||||
|
|
@ -459,7 +483,7 @@
|
|||
All interrupt logic must be locked in memory because the design present here will not support page faults from interrupt handlers.
|
||||
This includes the page fault handling logic and <a href="#PageFaults"><code>pg_miss()</code></a> that is called from the page fault handler.
|
||||
It also includes the <a href="#FillComplete"><code>pg_callback()</code></a> function that wakes up the page fill worker thread
|
||||
and whatever chip-specific logic that calls <code>pg_callback()</code>.
|
||||
and whatever architecture-specific logic that calls <code>pg_callback()</code>.
|
||||
</li>
|
||||
<li>
|
||||
All logic for the IDLE thread.
|
||||
|
|
@ -547,6 +571,7 @@
|
|||
</table></center>
|
||||
|
||||
<p>
|
||||
<b>Example</b>.
|
||||
As an example, suppose that the size of the SRAM is 192Kb (as in the NXP LPC3131). And suppose further that:
|
||||
<ul>
|
||||
<li>
|
||||
|
|
@ -563,16 +588,35 @@
|
|||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Then, the size of the locked, memory resident code is 32Kb (32 pages).
|
||||
Then, the size of the locked, memory resident code is 32Kb (<i>m</i>=32 pages).
|
||||
The size of the physical page region is 96Kb (96 pages), and the
|
||||
size of the data region is 64 pages.
|
||||
And the size of the virtual paged region must then be greater than or equal to (1024-32) or 992 pages (<i>m</i>).
|
||||
And the size of the virtual paged region must then be greater than or equal to (1024-32) or 992 pages (<i>n</i>).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Building the Locked, In-Memory Image</b>.
|
||||
One way to accomplish this would be a two phase link:
|
||||
<ul>
|
||||
<li>
|
||||
In the first phase, create a partially linked objected containing all interrupt/exception handling logic, the page fill worker thread plus all parts of the IDLE thread (which must always be available for execution).
|
||||
</li>
|
||||
<li>
|
||||
All of the <code>.text</code> and <code>.rodata</code> sections of this partial link should be collected into a single section.
|
||||
</li>
|
||||
<li>
|
||||
The second link would link the partially linked object along with the remaining object to produce the final binary.
|
||||
The linker script should position the "special" section so that it lies in a reserved, "non-swappable" region.
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<a name="ArchFuncs"><h2>Architecture-Specific Functions</h2></a>
|
||||
|
||||
<p>
|
||||
Standard functions that should already be provided in the architecture port:
|
||||
Most standard, architecture-specific functions are declared in <code>include/nuttx/arch.h</code>.
|
||||
However, for the case of this paging logic, the architecture specific functions are declared in <code>include/nuttx/page.h</code>.
|
||||
Standard, architecture-specific functions that should already be provided in the architecture port.
|
||||
The following are used by the common paging logic:
|
||||
</p>
|
||||
<ul><dl>
|
||||
<dt>
|
||||
|
|
@ -603,7 +647,7 @@
|
|||
<code>int up_checkmapping(FAR _TCB *tcb);</code>
|
||||
</dt>
|
||||
<dd>
|
||||
The function <code>up_checkmapping()</code> returns an indication that checks if the page fill still needs to performed or not.
|
||||
The function <code>up_checkmapping()</code> returns an indication if the page fill still needs to performed or not.
|
||||
In certain conditions, the page fault may occur on several threads and be queued multiple times.
|
||||
This function will prevent the same page from be filled multiple times.
|
||||
</dd>
|
||||
|
|
@ -611,18 +655,18 @@
|
|||
<code>int up_allocpage(FAR _TCB *tcb, FAR void *vpage);</code>
|
||||
</dt>
|
||||
<dd>
|
||||
This chip-specific function will set aside page in memory and map to its correct virtual address.
|
||||
Architecture-specific context information saved within the TCB will provide the function with the information need to identify the virtual miss address.
|
||||
This function will return the allocated physical page address in <code>paddr</code>.
|
||||
The size of a physical page is determined by the configuration setting <code>CONFIG_PAGING_PAGESIZE</code>.
|
||||
This architecture-specific function will set aside page in memory and map to its correct virtual address.
|
||||
Architecture-specific context information saved within the TCB will provide the function with the information needed to identify the virtual miss address.
|
||||
This function will return the allocated physical page address in <code>vpage</code>.
|
||||
The size of the underlying physical page is determined by the configuration setting <code>CONFIG_PAGING_PAGESIZE</code>.
|
||||
NOTE: This function must <i>always</i> return a page allocation.
|
||||
If all pages available pages are in-use (the typical case), then this function will select a page in-use, un-map it, and make it available.
|
||||
If all available pages are in-use (the typical case), then this function will select a page in-use, un-map it, and make it available.
|
||||
</dd>
|
||||
<dt><code>int up_fillpage(FAR _TCB *tcb, FAR const void *vpage, void (*pg_callback)(FAR _TCB *tcb, int result));</code>
|
||||
</dt>
|
||||
The actual filling of the page with data from the non-volatile, be performed by a separate call to the architecture-specific function, <code>up_fillpage()</code>.
|
||||
The actual filling of the page with data from the non-volatile, must be performed by a separate call to the architecture-specific function, <code>up_fillpage()</code>.
|
||||
This will start asynchronous page fill.
|
||||
The common logic will provide a callback function, <code>pg_callback</code>, that will be called when the page fill is finished (or an error occurs).
|
||||
The common paging logic will provide a callback function, <code>pg_callback</code>, that will be called when the page fill is finished (or an error occurs).
|
||||
This callback is assumed to occur from an interrupt level when the device driver completes the fill operation.
|
||||
</dt>
|
||||
</dl></ul>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,8 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
* Public Functions -- Provided by common paging logic to architecture-
|
||||
* specific logic.
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -140,6 +141,129 @@ extern "C" {
|
|||
|
||||
EXTERN void pg_miss(void);
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions -- Provided by architecture-specific logic to common
|
||||
* paging logic.
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: up_checkmapping()
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* The function up_checkmapping() returns an indication if the page fill
|
||||
* still needs to performed or not. In certain conditions, the page fault
|
||||
* may occur on several threads and be queued multiple times. This function
|
||||
* will prevent the same page from be filled multiple times.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - A reference to the task control block of the task that we believe
|
||||
* needs to have a page fill. Architecture-specific logic can
|
||||
* retrieve page fault information from the architecture-specific
|
||||
* context information in this TCB and can consult processor resources
|
||||
* (page tables or TLBs or ???) to determine if the fill still needs
|
||||
* to be performed or not.
|
||||
*
|
||||
* Returned Value:
|
||||
* This function will return zero (OK) if the mapping is in place and
|
||||
* the negated errn ENXIO if the mapping is still needed. Other errors
|
||||
* may also be returned but these will be interpreted as fatal error
|
||||
* conditions.
|
||||
*
|
||||
* Assumptions:
|
||||
* - This function is called from the normal tasking context (but with
|
||||
* interrupts disabled). The implementation must take whatever actions
|
||||
* are necessary to assure that the operation is safe within this
|
||||
* context.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
EXTERN int up_checkmapping(FAR _TCB *tcb);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: up_allocpage()
|
||||
*
|
||||
* Description:
|
||||
* This architecture-specific function will set aside page in memory and map
|
||||
* the page to its correct virtual address. Architecture-specific context
|
||||
* information saved within the TCB will provide the function with the
|
||||
* information needed to identify the virtual miss address.
|
||||
*
|
||||
* This function will return the allocated physical page address in vpage.
|
||||
* The size of the underlying physical page is determined by the
|
||||
* configuration setting CONFIG_PAGING_PAGESIZE.
|
||||
*
|
||||
* NOTE: This function must always return a page allocation. If all
|
||||
* available pages are in-use (the typical case), then this function will
|
||||
* select a page in-use, un-map it, and make it available.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - A reference to the task control block of the task that needs to
|
||||
* have a page fill. Architecture-specific logic can retrieve page
|
||||
* fault information from the architecture-specific context
|
||||
* information in this TCB to perform the mapping.
|
||||
*
|
||||
* Returned Value:
|
||||
* This function will return zero (OK) if the allocation was successful.
|
||||
* A negated errno value may be returned if an error occurs. All errors,
|
||||
* however, are fatal.
|
||||
*
|
||||
* Assumptions:
|
||||
* - This function is called from the normal tasking context (but with
|
||||
* interrupts disabled). The implementation must take whatever actions
|
||||
* are necessary to assure that the operation is safe within this
|
||||
* context.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
EXTERN int up_allocpage(FAR _TCB *tcb, FAR void **vpage);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: up_fillpage()
|
||||
*
|
||||
* Description:
|
||||
* After a page is allocated and mapped by up_allocpage(), the actual
|
||||
* filling of the page with data from the non-volatile, must be performed
|
||||
* by a separate call to the architecture-specific function, up_fillpage().
|
||||
* This function is non-blocking, it will start an asynchronous page fill.
|
||||
* The common paging logic will provide a callback function, pg_callback,
|
||||
* that will be called when the page fill is finished (or an error occurs).
|
||||
* This callback is assumed to occur from an interrupt level when the
|
||||
* device driver completes the fill operation.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - A reference to the task control block of the task that needs to
|
||||
* have a page fill. Architecture-specific logic can retrieve page
|
||||
* fault information from the architecture-specific context
|
||||
* information in this TCB to perform the fill.
|
||||
* pg_callbck - The function to be called when the page fill is complete.
|
||||
*
|
||||
* Returned Value:
|
||||
* This function will return zero (OK) if the page fill was successfully
|
||||
* started (the result of the page fill is passed to the callback function
|
||||
* as the result argument). A negated errno value may be returned if an
|
||||
* error occurs. All errors, however, are fatal.
|
||||
*
|
||||
* NOTE: -EBUSY has a special meaning. It is used internally to mean that
|
||||
* the callback function has not executed. Therefore, -EBUSY should
|
||||
* never be provided in the result argument of pg_callback.
|
||||
*
|
||||
* Assumptions:
|
||||
* - This function is called from the normal tasking context (but
|
||||
* interrupts siabled). The implementation must take whatever actions
|
||||
* are necessary to assure that the operation is safe within this context.
|
||||
* - Upon return, the caller will sleep waiting for the page fill callback
|
||||
* to occur. The callback function will perform the wakeup.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_PAGING_BLOCKINGFILL
|
||||
EXTERN int up_fillpage(FAR _TCB *tcb, FAR void *vpage);
|
||||
#else
|
||||
typedef void (*up_pgcallback_t)(FAR _TCB *tcb, int result);
|
||||
EXTERN int up_fillpage(FAR _TCB *tcb, FAR void *vpage, up_pgcallback_t pg_callback);
|
||||
#endif
|
||||
|
||||
#undef EXTERN
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
############################################################################
|
||||
# sched/Makefile
|
||||
#
|
||||
# Copyright (C) 2007-2009 Gregory Nutt. All rights reserved.
|
||||
# Copyright (C) 2007-2010 Gregory Nutt. All rights reserved.
|
||||
# Author: Gregory Nutt <spudmonkey@racsa.co.cr>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
|
|
@ -140,7 +140,7 @@ WORK_SRCS = work_thread.c work_queue.c work_cancel.c
|
|||
endif
|
||||
|
||||
ifeq ($(CONFIG_PAGING),y)
|
||||
PGFILL_SRCS = pg_miss.c
|
||||
PGFILL_SRCS = pg_miss.c pg_worker.c
|
||||
endif
|
||||
|
||||
IRQ_SRCS = irq_initialize.c irq_attach.c irq_dispatch.c irq_unexpectedisr.c
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/****************************************************************************
|
||||
* sched/os_internal.h
|
||||
*
|
||||
* Copyright (C) 2007-2009 Gregory Nutt. All rights reserved.
|
||||
* Copyright (C) 2007-2010 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
|
@ -199,6 +199,12 @@ extern volatile dq_queue_t g_waitingformqnotempty;
|
|||
extern volatile dq_queue_t g_waitingformqnotfull;
|
||||
#endif
|
||||
|
||||
/* This is the list of all tasks that are blocking waiting for a page fill */
|
||||
|
||||
#ifdef CONFIG_PAGING
|
||||
extern volatile dq_queue_t g_waitingforfill;
|
||||
#endif
|
||||
|
||||
/* This the list of all tasks that have been initialized, but not yet
|
||||
* activated. NOTE: This is the only list that is not prioritized.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@
|
|||
#include "clock_internal.h"
|
||||
#include "timer_internal.h"
|
||||
#include "irq_internal.h"
|
||||
#ifdef CONFIG_PAGING
|
||||
# include "pg_internal.h"
|
||||
#endif
|
||||
#ifdef CONFIG_SCHED_WORKQUEUE
|
||||
# include "work_internal.h"
|
||||
#endif
|
||||
|
|
@ -172,12 +175,6 @@ pidhash_t g_pidhash[CONFIG_MAX_TASKS];
|
|||
pid_t g_worker;
|
||||
#endif
|
||||
|
||||
/* The task ID of the page fill worker thread */
|
||||
|
||||
#ifdef CONFIG_PAGING
|
||||
pid_t g_pgworker;
|
||||
#endif
|
||||
|
||||
/* This is a table of task lists. This table is indexed by
|
||||
* the task state enumeration type (tstate_t) and provides
|
||||
* a pointer to the associated static task list (if there
|
||||
|
|
@ -451,6 +448,23 @@ void os_start(void)
|
|||
|
||||
(void)sched_setupidlefiles(&g_idletcb);
|
||||
|
||||
/* Start the page fill worker thread that will resolve page faults.
|
||||
* This should always be the first thread started because it may
|
||||
* have to resolve page faults in other threads
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PAGING
|
||||
#ifndef CONFIG_CUSTOM_STACK
|
||||
g_pgworker = task_create("pgfill", CONFIG_PAGING_DEFPRIO,
|
||||
CONFIG_PAGING_STACKSIZE,
|
||||
(main_t)pg_worker, (const char **)NULL);
|
||||
#else
|
||||
g_pgworker = task_create("pgfill", CONFIG_PAGING_DEFPRIO,
|
||||
(main_t)pg_worker, (const char **)NULL);
|
||||
#endif
|
||||
ASSERT(g_pgworker != ERROR);
|
||||
#endif
|
||||
|
||||
/* Start the worker thread that will perform misc garbage clean-up */
|
||||
|
||||
#ifdef CONFIG_SCHED_WORKQUEUE
|
||||
|
|
|
|||
|
|
@ -51,12 +51,20 @@
|
|||
|
||||
/* Configuration ************************************************************/
|
||||
|
||||
#ifndef CONFIG_PAGING_WORKPRIORITY
|
||||
# define CONFIG_PAGING_WORKPRIORITY 50
|
||||
/* Supply reasonable (but probably non-optimal) default settings if
|
||||
* configuration items are omitted.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_PAGING_DEFPRIO
|
||||
# define CONFIG_PAGING_DEFPRIO 50
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PAGING_WORKSTACKSIZE
|
||||
# define CONFIG_PAGING_WORKSTACKSIZE CONFIG_IDLETHREAD_STACKSIZE
|
||||
#ifndef CONFIG_PAGING_WORKPERIOD
|
||||
# define CONFIG_PAGING_WORKPERIOD (500*1000) /* 1/2 second */
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PAGING_STACKSIZE
|
||||
# define CONFIG_PAGING_STACKSIZE CONFIG_IDLETHREAD_STACKSIZE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DISABLE_SIGNALS
|
||||
|
|
@ -73,13 +81,24 @@
|
|||
|
||||
#ifndef __ASSEMBLY
|
||||
|
||||
/* If is the PID of the page fill worker thread */
|
||||
/* This is the task IDof the page fill worker thread. This value was set in
|
||||
* os_start when the page fill worker thread was started.
|
||||
*/
|
||||
|
||||
extern pid_t g_pgworker;
|
||||
|
||||
/* If non-NULL, this points to the TCB of the task currently being filled */
|
||||
/* The page fill worker thread maintains a static variable called
|
||||
* g_pendingfilltcb. If no fill is in progress, g_pendingfilltcb will be NULL.
|
||||
* Otherwise, g_pendingfile will point to the TCB of the task which is
|
||||
* receiving the fill that is in progess.
|
||||
*
|
||||
* NOTE: I think that this is the only state in which a TCB does not reside
|
||||
* in some list. Here is it in limbo, outside of the normally queuing while
|
||||
* the page file is in progress. Where here, it will be marked with
|
||||
* TSTATE_TASK_INVALID.
|
||||
*/
|
||||
|
||||
extern FAR _TCB *g_filltcb;
|
||||
extern FAR _TCB *g_pendingfilltcb;
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ void pg_miss(void)
|
|||
* thread to start working on the queued page fill requests.
|
||||
*/
|
||||
|
||||
if (!g_filltcb)
|
||||
if (!g_pendingfilltcb)
|
||||
{
|
||||
kill(g_pgworker, SIGWORK);
|
||||
}
|
||||
|
|
|
|||
537
sched/pg_worker.c
Executable file
537
sched/pg_worker.c
Executable file
|
|
@ -0,0 +1,537 @@
|
|||
/****************************************************************************
|
||||
* sched/pg_worker.c
|
||||
* Page fill worker thread implementation.
|
||||
*
|
||||
* Copyright (C) 2010 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <queue.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/page.h>
|
||||
#include <nuttx/clock.h>
|
||||
|
||||
#include "os_internal.h"
|
||||
#include "pg_internal.h"
|
||||
|
||||
#ifdef CONFIG_PAGING
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* Configuration ************************************************************/
|
||||
|
||||
#ifdef CONFIG_DISABLE_SIGNALS
|
||||
# warning "Signals needed by this function (CONFIG_DISABLE_SIGNALS=n)"
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Type Declarations
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Public Variables
|
||||
****************************************************************************/
|
||||
|
||||
/* This is the task ID of the page fill worker thread. This value was set in
|
||||
* os_start when the page fill worker thread was started.
|
||||
*/
|
||||
|
||||
pid_t g_pgworker;
|
||||
|
||||
/* The page fill worker thread maintains a static variable called
|
||||
* g_pendingfilltcb. If no fill is in progress, g_pendingfilltcb will be NULL.
|
||||
* Otherwise, g_pendingfile will point to the TCB of the task which is
|
||||
* receiving the fill that is in progess.
|
||||
*
|
||||
* NOTE: I think that this is the only state in which a TCB does not reside
|
||||
* in some list. Here is it in limbo, outside of the normally queuing while
|
||||
* the page file is in progress. Where here, it will be marked with
|
||||
* TSTATE_TASK_INVALID.
|
||||
*/
|
||||
|
||||
FAR _TCB *g_pendingfilltcb;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Variables
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_PAGING_BLOCKINGFILL
|
||||
|
||||
/* When a page fill completes, the result of the fill is stored here. The
|
||||
* value -EBUSY means that the page fill callback has not yet been received.
|
||||
*/
|
||||
|
||||
static int g_fillresult;
|
||||
|
||||
/* A configurable timeout period (in clock ticks) may be select to detect
|
||||
* page fill failures.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PAGING_TIMEOUT_TICKS
|
||||
status uint32_t g_starttime;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pg_callback
|
||||
*
|
||||
* Description:
|
||||
* This function is called from the architecture-specific, page fill logic
|
||||
* when the page fill completes (with or without an error). A reference to
|
||||
* this function was provided to up_fillpage(). The driver will provide
|
||||
* the result of the fill as an argument.
|
||||
*
|
||||
* NOTE: pg_callback() must also be locked in memory.
|
||||
*
|
||||
* When pg_callback() is called, it will perform the following operations:
|
||||
*
|
||||
* - Verify that g_pendingfilltcb is non-NULL.
|
||||
* - Find the higher priority between the task waiting for the fill to
|
||||
* complete in g_pendingfilltcb and the task waiting at the head of the
|
||||
* g_waitingforfill list. That will be the priority of he highest priority
|
||||
* task waiting for a fill.
|
||||
* - If this higher priority is higher than current page fill worker thread,
|
||||
* then boost worker thread's priority to that level. Thus, the page fill
|
||||
* worker thread will always run at the priority of the highest priority
|
||||
* task that is waiting for a fill.
|
||||
* - Signal the page fill worker thread.
|
||||
*
|
||||
* Input parameters:
|
||||
* tcb - The TCB of the task that just received the fill.
|
||||
* result - The result of the page fill operation.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* Possibly executing in the context of a driver interrupt handler???
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_PAGING_BLOCKINGFILL
|
||||
static void pg_callback(FAR _TCB *tcb, int result)
|
||||
{
|
||||
/* Verify that g_pendingfilltcb is non-NULL */
|
||||
|
||||
if (g_pendingfilltcb)
|
||||
{
|
||||
FAR _TCB *htcb = (FAR _TCB *)g_waitingforfill.head;
|
||||
FAR _TCB *wtcb = sched_gettcb(g_pgworker);
|
||||
|
||||
/* Find the higher priority between the task waiting for the fill to
|
||||
* complete in g_pendingfilltcb and the task waiting at the head of the
|
||||
* g_waitingforfill list. That will be the priority of he highest
|
||||
* priority task waiting for a fill.
|
||||
*/
|
||||
|
||||
int priority = g_pendingfilltcb->sched_priority;
|
||||
if (htcb && priority < htcb->sched_priority)
|
||||
{
|
||||
priority = htcb->sched_priority;
|
||||
}
|
||||
|
||||
/* If this higher priority is higher than current page fill worker
|
||||
* thread, then boost worker thread's priority to that level. Thus,
|
||||
* the page fill worker thread will always run at the priority of
|
||||
* the highest priority task that is waiting for a fill.
|
||||
*/
|
||||
|
||||
if (priority > wtcb->sched_priority)
|
||||
{
|
||||
sched_setpriority(wtcb, priority);
|
||||
}
|
||||
|
||||
/* Save the page fill result (don't permit the value -EBUSY) */
|
||||
|
||||
if (result == -EBUSY)
|
||||
{
|
||||
result = -ENOSYS;
|
||||
}
|
||||
g_fillresult = result;
|
||||
}
|
||||
|
||||
/* Signal the page fill worker thread (in any event) */
|
||||
|
||||
kill(g_pgworker, SIGWORK);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pg_startfill
|
||||
*
|
||||
* Description:
|
||||
* Start a page fill operation on the thread whose TCB is at the head of
|
||||
* of the g_waitingforfill task list. That is a prioritized list so that will
|
||||
* be the highest priority task waiting for a page fill (in the event that
|
||||
* are multiple tasks waiting for a page fill).
|
||||
*
|
||||
* This function may be called either (1) when the page fill worker thread
|
||||
* is notified that there is a new page fill TCB in the g_waitingforfill
|
||||
* prioritized list, or (2) when a page fill completes and there are more
|
||||
* pages to be filled in g_waitingforfill list.
|
||||
*
|
||||
* Input parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* Executing in the context of the page fill worker thread with all
|
||||
* interrupts disabled.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline void pg_startfill(void)
|
||||
{
|
||||
FAR void *vpage;
|
||||
int result;
|
||||
|
||||
/* Remove the TCB at the head of the g_waitfor fill list */
|
||||
|
||||
g_pendingfilltcb = (FAR _TCB *)dq_remfirst((dq_queue_t*)&g_waitingforfill);
|
||||
if (g_pendingfilltcb != NULL)
|
||||
{
|
||||
/* Call the architecture-specific function up_checkmapping() to see if the
|
||||
* page fill still needs to be performed. In certain conditions, the page
|
||||
* fault may occur on several threads and be queued multiple times. In this
|
||||
* corner case, the blocked task will simply be restarted.
|
||||
*/
|
||||
|
||||
result = up_checkmapping(g_pendingfilltcb);
|
||||
if (result == OK)
|
||||
{
|
||||
up_unblock_task(g_pendingfilltcb);
|
||||
g_pendingfilltcb = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Call up_allocpage(tcb, &vpage). This architecture-specific function will
|
||||
* set aside page in memory and map to virtual address (vpage). If all
|
||||
* available pages are in-use (the typical case), this function will select
|
||||
* a page in-use, un-map it, and make it available.
|
||||
*/
|
||||
|
||||
result = up_allocpage(g_pendingfilltcb, &vpage);
|
||||
DEBUGASSERT(result == OK);
|
||||
|
||||
/* Start the fill. The exact way that the fill is started depends upon
|
||||
* the nature of the architecture-specific up_fillpage() function -- Is it
|
||||
* a blocking or a non-blocking call?
|
||||
*/
|
||||
#ifdef CONFIG_PAGING_BLOCKINGFILL
|
||||
/* If CONFIG_PAGING_BLOCKINGFILL is defined, then up_fillpage is blocking
|
||||
* call. In this case, up_fillpage() will accept only (1) a reference to
|
||||
* the TCB that requires the fill. Architecture-specific context information
|
||||
* within the TCB will be sufficient to perform the fill. And (2) the
|
||||
* (virtual) address of the allocated page to be filled. The resulting
|
||||
* status of the fill will be provided by return value from up_fillpage().
|
||||
*/
|
||||
|
||||
result = up_fillpage(g_pendingfilltcb, vpage);
|
||||
DEBUGASSERT(result == OK);
|
||||
#else
|
||||
/* If CONFIG_PAGING_BLOCKINGFILL is defined, then up_fillpage is non-blocking
|
||||
* call. In this case up_fillpage() will accept an additional argument: The page
|
||||
* fill worker thread will provide a callback function, pg_callback.
|
||||
*
|
||||
* Calling up_fillpage will start an asynchronous page fill. pg_callback
|
||||
* ill be called when the page fill is finished (or an error occurs). This
|
||||
* This callback will probably from interrupt level.
|
||||
*/
|
||||
|
||||
result = up_fillpage(g_pendingfilltcb, vpage, pg_callback);
|
||||
DEBUGASSERT(result == OK);
|
||||
|
||||
/* Save the time that the fill was started. These will be used to check for
|
||||
* timeouts.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PAGING_TIMEOUT_TICKS
|
||||
g_starttime = g_system_timer;
|
||||
#endif
|
||||
|
||||
/* Return and wait to be signaled for the next event -- the fill completion
|
||||
* event. While the fill is in progress, other tasks may execute. If
|
||||
* another page fault occurs during this time, the faulting task will be
|
||||
* blocked, its TCB will be added (in priority order) to g_waitingforfill
|
||||
* and the priority of the page worker task may be boosted. But no action
|
||||
* will be taken until the current page fill completes. NOTE: The IDLE task
|
||||
* must also be fully locked in memory. The IDLE task cannot be blocked. It
|
||||
* the case where all tasks are blocked waiting for a page fill, the IDLE
|
||||
* task must still be available to run.
|
||||
*/
|
||||
#endif /* CONFIG_PAGING_BLOCKINGFILL */
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pg_alldone
|
||||
*
|
||||
* Description:
|
||||
* Called by the page fill worker thread when all pending page fill
|
||||
* operations have been completed and the g_waitingforfill list is empty.
|
||||
*
|
||||
* This functin will perform the following operations:
|
||||
*
|
||||
* - Set g_pendingfilltcb to NULL.
|
||||
* - Restore the default priority of the page fill worker thread.
|
||||
*
|
||||
* Input parameters:
|
||||
* None.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* Executing in the context of the page fill worker thread with interrupts
|
||||
* disabled.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline void pg_alldone(void)
|
||||
{
|
||||
FAR _TCB *wtcb = (FAR _TCB *)g_readytorun.head;
|
||||
g_pendingfilltcb = NULL;
|
||||
sched_setpriority(wtcb, CONFIG_PAGING_DEFPRIO);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pg_fillcomplete
|
||||
*
|
||||
* Description:
|
||||
* Called by the page fill worker thread when a page fill completes.
|
||||
* Either (1) in the non-blocking up_fillpage(), after the architecture-
|
||||
* specific driver call the pg_callback() to wake up the page fill worker
|
||||
* thread, or (2) after the blocking up_fillpage() returens (when
|
||||
* CONFIG_PAGING_BLOCKINGFILL is defined).
|
||||
*
|
||||
* This function is just a dumb wrapper around up_unblocktask(). This
|
||||
* function simply makes the task that just received the fill ready-to-run.
|
||||
*
|
||||
* Input parameters:
|
||||
* None.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* Executing in the context of the page fill worker thread with interrupts
|
||||
* disabled.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline void pg_fillcomplete(void)
|
||||
{
|
||||
/* Call up_unblocktask(g_pendingfilltcb) to make the task that just
|
||||
* received the fill ready-to-run.
|
||||
*/
|
||||
|
||||
up_unblock_task(g_pendingfilltcb);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
/****************************************************************************
|
||||
* Name: pg_worker
|
||||
*
|
||||
* Description:
|
||||
* This is the page fill worker thread that performs pages fills for tasks
|
||||
* that have received a pag fault and are blocked in the g_waitingforfill
|
||||
* task queue.
|
||||
*
|
||||
* The page fill worker thread will be awakened on one of three conditions:
|
||||
* - When signaled by pg_miss(), the page fill worker thread will be
|
||||
* awakenend, or
|
||||
* - if CONFIG_PAGING_BLOCKINGFILL is not defined, from pg_fillcomplete()
|
||||
* after completing a page fill.
|
||||
* - A configurable timeout with no activity.
|
||||
*
|
||||
* Input parameters:
|
||||
* argc, argv (not used)
|
||||
*
|
||||
* Returned Value:
|
||||
* Does not return
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int pg_worker(int argc, char *argv[])
|
||||
{
|
||||
irqstate_t flags;
|
||||
|
||||
/* Loop forever -- Notice that interrupts will be disable at all times that
|
||||
* this thread runs. That is so that we con't lose signals or have
|
||||
* asynchronous page faults.
|
||||
*
|
||||
* All interrupt logic as well as all page fill worker thread logic must
|
||||
* be locked in memory. Therefore, keeping interrupts disabled here
|
||||
* should prevent any concurrent page faults. Any page faults or page
|
||||
* fill completions should occur while this thread sleeps.
|
||||
*/
|
||||
|
||||
flags = irqsave();
|
||||
for (;;)
|
||||
{
|
||||
/* Wait awhile. We will wait here until either the configurable timeout
|
||||
* elapses or until we are awakened by a signal (which terminates the
|
||||
* usleep with an EINTR error). Note that interrupts will be re-enabled
|
||||
* while this task sleeps.
|
||||
*
|
||||
* The timeout is a failsafe that will handle any cases where a single
|
||||
* is lost (that would really be a bug and shouldn't happen!) and also
|
||||
* supports timeouts for case of non-blocking, asynchronous fills.
|
||||
*/
|
||||
|
||||
usleep(CONFIG_PAGING_WORKPERIOD);
|
||||
|
||||
/* The page fill worker thread will be awakened on one of three conditions:
|
||||
*
|
||||
* - When signaled by pg_miss(), the page fill worker thread will be awakenend,
|
||||
* - if CONFIG_PAGING_BLOCKINGFILL is not defined, from pg_callback()
|
||||
* after completing a page fill, or
|
||||
* - On a configurable timeout expires with no activity.
|
||||
*
|
||||
* Interrupts are still disabled.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PAGING_BLOCKINGFILL
|
||||
/* For the non-blocking up_fillpage(), the page fill worker thread will detect
|
||||
* that the page fill is complete when it is awakened with g_pendingfilltcb non-NULL
|
||||
* and fill completion status from pg_callback.
|
||||
*/
|
||||
|
||||
if (g_pendingfilltcb != NULL)
|
||||
{
|
||||
/* If it is a real page fill completion event, then the result of the page
|
||||
* fill will be in g_fillresult and will not be equal to -EBUSY.
|
||||
*/
|
||||
|
||||
if (g_fillresult != -EBUSY)
|
||||
{
|
||||
/* Any value other than OK, brings the system down */
|
||||
|
||||
ASSERT(g_fillresult == OK);
|
||||
|
||||
/* Handle the page fill complete event */
|
||||
|
||||
pg_fillcomplete();
|
||||
|
||||
/* Check if there are are more pending page fills */
|
||||
|
||||
if (g_waitingforfill.head != NULL)
|
||||
{
|
||||
/* Yes .. Start the next asynchronous fill */
|
||||
|
||||
pg_startfill();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, there is nothing more to do */
|
||||
|
||||
pg_alldone();
|
||||
}
|
||||
}
|
||||
|
||||
/* If a configurable timeout period expires with no page fill completion
|
||||
* event, then declare a failure.
|
||||
*/
|
||||
|
||||
#if defined() && defined(CONFIG_PAGING_TIMEOUT_TICKS)
|
||||
else
|
||||
{
|
||||
dbg("Timeout!\n");
|
||||
ASSERT(g_system_timer - g_starttime < CONFIG_PAGING_TIMEOUT_TICKS);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Otherwise, this might be a page fill initiation event. When
|
||||
* awakened from pg_miss(), no fill will be in progress and
|
||||
* g_pendingfilltcb will be NULL.
|
||||
*/
|
||||
|
||||
else
|
||||
{
|
||||
/* Are there tasks blocked and waiting for a fill? */
|
||||
|
||||
if (g_waitingforfill.head != NULL)
|
||||
{
|
||||
/* Yes .. Start the asynchronous fill */
|
||||
|
||||
pg_startfill();
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Are there tasks blocked and waiting for a fill? Loop until all
|
||||
* pending fills have been processed
|
||||
*/
|
||||
|
||||
while (g_waitingforfill.head != NULL)
|
||||
{
|
||||
/* Yes .. Start the fill and block until the fill completes */
|
||||
|
||||
pg_startfill();
|
||||
|
||||
/* Handle the page fill complete event. In the non-blocking case,
|
||||
* the page fill worker thread will know that the page fill is
|
||||
* complete when pg_startfill() returns.
|
||||
*/
|
||||
|
||||
pg_fillcomplete();
|
||||
}
|
||||
|
||||
/* All queued fills have been processed */
|
||||
|
||||
pg_alldone();
|
||||
#endif
|
||||
}
|
||||
return OK; /* To keep some compilers happy */
|
||||
}
|
||||
#endif /* CONFIG_PAGING */
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/****************************************************************************
|
||||
* sched/work_internal.h
|
||||
*
|
||||
* Copyright (C) 2009 Gregory Nutt. All rights reserved.
|
||||
* Copyright (C) 2009-2010 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue