2010-08-15 17:02:45 +02:00
|
|
|
/****************************************************************************
|
2014-08-08 23:08:07 +02:00
|
|
|
* sched/paging/pg_worker.c
|
2010-08-15 17:02:45 +02:00
|
|
|
* Page fill worker thread implementation.
|
|
|
|
*
|
2018-01-30 18:07:36 +01:00
|
|
|
* Copyright (C) 2010-2011, 2017-2018 Gregory Nutt. All rights reserved.
|
2012-07-14 21:30:31 +02:00
|
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
2010-08-15 17:02:45 +02:00
|
|
|
*
|
|
|
|
* 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>
|
2010-08-15 18:23:18 +02:00
|
|
|
#include <stdbool.h>
|
2010-08-15 17:02:45 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <queue.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <debug.h>
|
|
|
|
|
2018-01-30 18:07:36 +01:00
|
|
|
#include <nuttx/sched.h>
|
2010-08-15 17:02:45 +02:00
|
|
|
#include <nuttx/arch.h>
|
2017-10-06 18:15:01 +02:00
|
|
|
#include <nuttx/signal.h>
|
2010-08-15 17:02:45 +02:00
|
|
|
#include <nuttx/page.h>
|
|
|
|
#include <nuttx/clock.h>
|
2017-10-07 16:22:18 +02:00
|
|
|
#include <nuttx/signal.h>
|
2010-08-15 17:02:45 +02:00
|
|
|
|
2014-08-09 01:53:55 +02:00
|
|
|
#include "sched/sched.h"
|
2014-08-08 23:08:07 +02:00
|
|
|
#include "paging/paging.h"
|
2010-08-15 17:02:45 +02:00
|
|
|
|
|
|
|
#ifdef CONFIG_PAGING
|
|
|
|
|
|
|
|
/****************************************************************************
|
2015-10-03 00:30:35 +02:00
|
|
|
* Public Data
|
2010-08-15 17:02:45 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/* This is the task ID of the page fill worker thread. This value was set in
|
2019-02-04 23:20:35 +01:00
|
|
|
* nx_start when the page fill worker thread was started.
|
2010-08-15 17:02:45 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
pid_t g_pgworker;
|
|
|
|
|
2012-07-14 21:30:31 +02:00
|
|
|
/* The page fill worker thread maintains a static variable called g_pftcb.
|
|
|
|
* If no fill is in progress, g_pftcb will be NULL. Otherwise, g_pftcb will
|
|
|
|
* point to the TCB of the task which is receiving the fill that is in progess.
|
2010-08-15 17:02:45 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2013-02-04 19:46:28 +01:00
|
|
|
FAR struct tcb_s *g_pftcb;
|
2010-08-15 17:02:45 +02:00
|
|
|
|
|
|
|
/****************************************************************************
|
2016-02-22 01:08:58 +01:00
|
|
|
* Private Data
|
2010-08-15 17:02:45 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#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
|
2018-06-16 20:16:13 +02:00
|
|
|
static clock_t g_starttime;
|
2010-08-15 17:02:45 +02:00
|
|
|
#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:
|
2014-04-13 22:32:20 +02:00
|
|
|
*
|
2010-08-15 18:50:25 +02:00
|
|
|
* - Verify that g_pftcb is non-NULL.
|
2010-08-15 17:02:45 +02:00
|
|
|
* - Find the higher priority between the task waiting for the fill to
|
2010-08-15 18:50:25 +02:00
|
|
|
* complete in g_pftcb and the task waiting at the head of the
|
2010-08-15 17:02:45 +02:00
|
|
|
* 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.
|
|
|
|
*
|
2018-02-01 17:00:02 +01:00
|
|
|
* Input Parameters:
|
2010-08-15 17:02:45 +02:00
|
|
|
* 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
|
2013-02-04 19:46:28 +01:00
|
|
|
static void pg_callback(FAR struct tcb_s *tcb, int result)
|
2010-08-15 17:02:45 +02:00
|
|
|
{
|
2010-08-15 18:50:25 +02:00
|
|
|
/* Verify that g_pftcb is non-NULL */
|
2010-08-15 17:02:45 +02:00
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("g_pftcb: %p\n", g_pftcb);
|
2010-08-15 18:50:25 +02:00
|
|
|
if (g_pftcb)
|
2010-08-15 17:02:45 +02:00
|
|
|
{
|
2013-02-04 19:46:28 +01:00
|
|
|
FAR struct tcb_s *htcb = (FAR struct tcb_s *)g_waitingforfill.head;
|
|
|
|
FAR struct tcb_s *wtcb = sched_gettcb(g_pgworker);
|
2010-08-15 17:02:45 +02:00
|
|
|
|
|
|
|
/* Find the higher priority between the task waiting for the fill to
|
2010-08-15 18:50:25 +02:00
|
|
|
* complete in g_pftcb and the task waiting at the head of the
|
2010-08-15 17:02:45 +02:00
|
|
|
* g_waitingforfill list. That will be the priority of he highest
|
|
|
|
* priority task waiting for a fill.
|
|
|
|
*/
|
|
|
|
|
2010-08-15 18:50:25 +02:00
|
|
|
int priority = g_pftcb->sched_priority;
|
2010-08-15 17:02:45 +02:00
|
|
|
if (htcb && priority < htcb->sched_priority)
|
|
|
|
{
|
|
|
|
priority = htcb->sched_priority;
|
|
|
|
}
|
2014-04-13 22:32:20 +02:00
|
|
|
|
2012-07-14 21:30:31 +02:00
|
|
|
/* 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)
|
|
|
|
{
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("New worker priority. %d->%d\n",
|
|
|
|
wtcb->sched_priority, priority);
|
2020-01-02 17:49:34 +01:00
|
|
|
nxsched_setpriority(wtcb, priority);
|
2012-07-14 21:30:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Save the page fill result (don't permit the value -EBUSY) */
|
|
|
|
|
|
|
|
if (result == -EBUSY)
|
|
|
|
{
|
|
|
|
result = -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_fillresult = result;
|
2010-08-15 17:02:45 +02:00
|
|
|
}
|
|
|
|
|
2010-08-31 04:21:12 +02:00
|
|
|
/* Signal the page fill worker thread (in any event) */
|
2014-04-13 22:32:20 +02:00
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Signaling worker. PID: %d\n", g_pgworker);
|
2020-01-02 17:49:34 +01:00
|
|
|
nxsig_kill(g_pgworker, SIGWORK);
|
2010-08-15 17:02:45 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-08-15 18:23:18 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: pg_dequeue
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Dequeue the next, highest priority TCB from the g_waitingforfill task
|
|
|
|
* list. Call up_checkmapping() see if the still needs to be performed
|
|
|
|
* for that task. In certain conditions, the page fault may occur on
|
|
|
|
* several threads for the same page and be queued multiple times. In this
|
|
|
|
* corner case, the blocked task will simply be restarted.
|
|
|
|
*
|
|
|
|
* This function will continue to examine g_waitingforfill until either
|
|
|
|
* (1) a task is found that still needs the page fill, or (2) the
|
|
|
|
* g_waitingforfill task list becomes empty.
|
|
|
|
*
|
|
|
|
* The result (NULL or a TCB pointer) will be returned in the global
|
2010-08-15 18:50:25 +02:00
|
|
|
* variable, g_pftcb.
|
2014-04-13 22:32:20 +02:00
|
|
|
*
|
2018-02-01 17:00:02 +01:00
|
|
|
* Input Parameters:
|
2010-08-15 18:23:18 +02:00
|
|
|
* None
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* If there are no further queue page fill operations to be performed,
|
2010-08-15 18:50:25 +02:00
|
|
|
* pg_dequeue() will return false. Otherwise, it will return
|
2010-08-15 18:23:18 +02:00
|
|
|
* true to that the full is in process (any errors will result in
|
|
|
|
* assertions and this function will not return).
|
|
|
|
*
|
|
|
|
* Assumptions:
|
2014-04-13 22:32:20 +02:00
|
|
|
* Executing in the context of the page fill worker thread with all
|
2010-08-15 18:23:18 +02:00
|
|
|
* interrupts disabled.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static inline bool pg_dequeue(void)
|
|
|
|
{
|
|
|
|
/* Loop until either (1) the TCB of a task that requires a fill is found, OR
|
|
|
|
* (2) the g_watingforfill list becomes empty.
|
|
|
|
*/
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* Remove the TCB from the head of the list (if any) */
|
|
|
|
|
2015-10-08 03:59:14 +02:00
|
|
|
g_pftcb = (FAR struct tcb_s *)dq_remfirst((dq_queue_t *)&g_waitingforfill);
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("g_pftcb: %p\n", g_pftcb);
|
2010-08-15 18:50:25 +02:00
|
|
|
if (g_pftcb != NULL)
|
2010-08-15 18:23:18 +02:00
|
|
|
{
|
|
|
|
/* 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 for the same page and
|
|
|
|
* be queues multiple times. In this corner case, the blocked task will
|
|
|
|
* simply be restarted.
|
|
|
|
*/
|
|
|
|
|
2010-08-15 18:50:25 +02:00
|
|
|
if (!up_checkmapping(g_pftcb))
|
2010-08-15 18:23:18 +02:00
|
|
|
{
|
2012-07-14 21:30:31 +02:00
|
|
|
/* This page needs to be filled. pg_miss bumps up
|
|
|
|
* the priority of the page fill worker thread as each
|
|
|
|
* TCB is added to the g_waitingforfill list. So we
|
|
|
|
* may need to also drop the priority of the worker
|
|
|
|
* thread as the next TCB comes off of the list.
|
|
|
|
*
|
|
|
|
* If wtcb->sched_priority > CONFIG_PAGING_DEFPRIO,
|
|
|
|
* then the page fill worker thread is executing at
|
|
|
|
* an elevated priority that may be reduced.
|
|
|
|
*
|
|
|
|
* If wtcb->sched_priority > g_pftcb->sched_priority
|
|
|
|
* then the page fill worker thread is executing at
|
|
|
|
* a higher priority than is appropriate for this
|
|
|
|
* fill (this priority can get re-boosted by pg_miss()
|
|
|
|
* if a new higher priority fill is required).
|
|
|
|
*/
|
2014-04-13 22:32:20 +02:00
|
|
|
|
2016-02-07 00:44:41 +01:00
|
|
|
FAR struct tcb_s *wtcb = this_task();
|
2012-07-14 21:30:31 +02:00
|
|
|
if (wtcb->sched_priority > CONFIG_PAGING_DEFPRIO &&
|
|
|
|
wtcb->sched_priority > g_pftcb->sched_priority)
|
|
|
|
{
|
|
|
|
/* Don't reduce the priority of the page fill
|
|
|
|
* worker thread lower than the configured
|
|
|
|
* minimum.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int priority = g_pftcb->sched_priority;
|
|
|
|
if (priority < CONFIG_PAGING_DEFPRIO)
|
|
|
|
{
|
|
|
|
priority = CONFIG_PAGING_DEFPRIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reduce the priority of the page fill worker thread */
|
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("New worker priority. %d->%d\n",
|
|
|
|
wtcb->sched_priority, priority);
|
2020-01-02 17:49:34 +01:00
|
|
|
nxsched_setpriority(wtcb, priority);
|
2012-07-14 21:30:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return with g_pftcb holding the pointer to
|
|
|
|
* the TCB associated with task that requires the page fill.
|
|
|
|
*/
|
|
|
|
|
|
|
|
return true;
|
2010-08-15 18:23:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The page need by this task has already been mapped into the
|
|
|
|
* virtual address space -- just restart it.
|
|
|
|
*/
|
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Restarting TCB: %p\n", g_pftcb);
|
2010-08-15 18:50:25 +02:00
|
|
|
up_unblock_task(g_pftcb);
|
2010-08-15 18:23:18 +02:00
|
|
|
}
|
|
|
|
}
|
2010-08-15 18:50:25 +02:00
|
|
|
while (g_pftcb != NULL);
|
2012-07-14 21:30:31 +02:00
|
|
|
|
2010-08-15 18:23:18 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-08-15 17:02:45 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: pg_startfill
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Start a page fill operation on the thread whose TCB is at the head of
|
2010-08-15 18:23:18 +02:00
|
|
|
* 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).
|
2010-08-15 17:02:45 +02:00
|
|
|
*
|
|
|
|
* 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.
|
2014-04-13 22:32:20 +02:00
|
|
|
*
|
2018-02-01 17:00:02 +01:00
|
|
|
* Input Parameters:
|
2010-08-15 17:02:45 +02:00
|
|
|
* None
|
|
|
|
*
|
|
|
|
* Returned Value:
|
2010-08-15 18:23:18 +02:00
|
|
|
* If there are no further queue page fill operations to be performed,
|
|
|
|
* pg_startfill() will return false. Otherwise, it will return
|
|
|
|
* true to that the full is in process (any errors will result in
|
|
|
|
* assertions and this function will not return).
|
2010-08-15 17:02:45 +02:00
|
|
|
*
|
|
|
|
* Assumptions:
|
2014-04-13 22:32:20 +02:00
|
|
|
* Executing in the context of the page fill worker thread with all
|
2010-08-15 17:02:45 +02:00
|
|
|
* interrupts disabled.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2010-08-15 18:23:18 +02:00
|
|
|
static inline bool pg_startfill(void)
|
2010-08-15 17:02:45 +02:00
|
|
|
{
|
|
|
|
FAR void *vpage;
|
|
|
|
int result;
|
|
|
|
|
2010-08-15 18:23:18 +02:00
|
|
|
/* Remove the TCB at the head of the g_waitfor fill list and check if there
|
|
|
|
* is any task waiting for a page fill. pg_dequeue will handle this (plus
|
2018-08-19 19:19:43 +02:00
|
|
|
* some corner cases) and will true if the next page TCB was successfully
|
2010-08-15 18:23:18 +02:00
|
|
|
* dequeued.
|
|
|
|
*/
|
2010-08-15 17:02:45 +02:00
|
|
|
|
2010-08-15 18:23:18 +02:00
|
|
|
if (pg_dequeue())
|
2010-08-15 17:02:45 +02:00
|
|
|
{
|
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Call up_allocpage(%p)\n", g_pftcb);
|
2010-08-15 18:50:25 +02:00
|
|
|
result = up_allocpage(g_pftcb, &vpage);
|
2010-08-15 17:02:45 +02:00
|
|
|
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?
|
|
|
|
*/
|
2019-11-09 15:15:12 +01:00
|
|
|
|
2010-08-15 17:02:45 +02:00
|
|
|
#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().
|
|
|
|
*/
|
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Call up_fillpage(%p)\n", g_pftcb);
|
2010-08-15 18:50:25 +02:00
|
|
|
result = up_fillpage(g_pftcb, vpage);
|
2010-08-15 17:02:45 +02:00
|
|
|
DEBUGASSERT(result == OK);
|
|
|
|
#else
|
|
|
|
/* If CONFIG_PAGING_BLOCKINGFILL is defined, then up_fillpage is non-blocking
|
2020-01-12 17:07:54 +01:00
|
|
|
* call. In this case up_fillpage() will accept an additional argument: The
|
|
|
|
* page fill worker thread will provide a callback function, pg_callback.
|
2010-08-15 17:02:45 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Call up_fillpage(%p)\n", g_pftcb);
|
2010-08-15 18:50:25 +02:00
|
|
|
result = up_fillpage(g_pftcb, vpage, pg_callback);
|
2010-08-15 17:02:45 +02:00
|
|
|
DEBUGASSERT(result == OK);
|
2014-04-13 22:32:20 +02:00
|
|
|
|
2010-08-15 17:02:45 +02:00
|
|
|
/* Save the time that the fill was started. These will be used to check for
|
|
|
|
* timeouts.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef CONFIG_PAGING_TIMEOUT_TICKS
|
2011-04-13 23:44:24 +02:00
|
|
|
g_starttime = clock_systimer();
|
2010-08-15 17:02:45 +02:00
|
|
|
#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.
|
|
|
|
*/
|
2019-11-09 15:15:12 +01:00
|
|
|
|
2010-08-15 17:02:45 +02:00
|
|
|
#endif /* CONFIG_PAGING_BLOCKINGFILL */
|
2012-07-14 21:30:31 +02:00
|
|
|
|
2010-08-15 18:23:18 +02:00
|
|
|
return true;
|
2010-08-15 17:02:45 +02:00
|
|
|
}
|
2010-08-31 04:21:12 +02:00
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Queue empty\n");
|
2010-08-15 18:23:18 +02:00
|
|
|
return false;
|
2010-08-15 17:02:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* 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.
|
|
|
|
*
|
2018-08-19 19:19:43 +02:00
|
|
|
* This function will perform the following operations:
|
2010-08-15 17:02:45 +02:00
|
|
|
*
|
2010-08-15 18:50:25 +02:00
|
|
|
* - Set g_pftcb to NULL.
|
2010-08-15 17:02:45 +02:00
|
|
|
* - Restore the default priority of the page fill worker thread.
|
|
|
|
*
|
2018-02-01 17:00:02 +01:00
|
|
|
* Input Parameters:
|
2010-08-15 17:02:45 +02:00
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* None
|
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
* Executing in the context of the page fill worker thread with interrupts
|
|
|
|
* disabled.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static inline void pg_alldone(void)
|
|
|
|
{
|
2016-02-07 00:44:41 +01:00
|
|
|
FAR struct tcb_s *wtcb = this_task();
|
2010-08-15 18:50:25 +02:00
|
|
|
g_pftcb = NULL;
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("New worker priority. %d->%d\n",
|
|
|
|
wtcb->sched_priority, CONFIG_PAGING_DEFPRIO);
|
2020-01-02 17:49:34 +01:00
|
|
|
nxsched_setpriority(wtcb, CONFIG_PAGING_DEFPRIO);
|
2010-08-15 17:02:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* 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
|
2018-08-19 19:19:43 +02:00
|
|
|
* thread, or (2) after the blocking up_fillpage() returns (when
|
2010-08-15 17:02:45 +02:00
|
|
|
* 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.
|
|
|
|
*
|
2018-02-01 17:00:02 +01:00
|
|
|
* Input Parameters:
|
2010-08-15 17:02:45 +02:00
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* None
|
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
* Executing in the context of the page fill worker thread with interrupts
|
|
|
|
* disabled.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static inline void pg_fillcomplete(void)
|
|
|
|
{
|
2010-08-15 18:50:25 +02:00
|
|
|
/* Call up_unblocktask(g_pftcb) to make the task that just
|
2010-08-15 17:02:45 +02:00
|
|
|
* received the fill ready-to-run.
|
|
|
|
*/
|
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Restarting TCB: %p\n", g_pftcb);
|
2010-08-15 18:50:25 +02:00
|
|
|
up_unblock_task(g_pftcb);
|
2010-08-15 17:02:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Public Functions
|
|
|
|
****************************************************************************/
|
2018-08-19 19:19:43 +02:00
|
|
|
|
2010-08-15 17:02:45 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* 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
|
2018-08-19 19:19:43 +02:00
|
|
|
* awakened, or
|
2010-08-15 18:23:18 +02:00
|
|
|
* - if CONFIG_PAGING_BLOCKINGFILL is not defined, from pg_callback()
|
2010-08-15 17:02:45 +02:00
|
|
|
* after completing a page fill.
|
|
|
|
* - A configurable timeout with no activity.
|
|
|
|
*
|
2018-02-01 17:00:02 +01:00
|
|
|
* Input Parameters:
|
2010-08-15 17:02:45 +02:00
|
|
|
* argc, argv (not used)
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* Does not return
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
int pg_worker(int argc, char *argv[])
|
|
|
|
{
|
2014-11-25 21:25:02 +01:00
|
|
|
/* Loop forever -- Notice that interrupts will be disabled at all times that
|
|
|
|
* this thread runs. That is so that we can't lose signals or have
|
2010-08-15 17:02:45 +02:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Started\n");
|
2020-01-02 17:49:34 +01:00
|
|
|
up_irq_save();
|
2015-10-08 03:59:14 +02:00
|
|
|
for (; ; )
|
2010-08-15 17:02:45 +02:00
|
|
|
{
|
|
|
|
/* Wait awhile. We will wait here until either the configurable timeout
|
|
|
|
* elapses or until we are awakened by a signal (which terminates the
|
2018-08-19 19:19:43 +02:00
|
|
|
* nxsig_usleep with an EINTR error). Note that interrupts will be re-
|
|
|
|
* enabled while this task sleeps.
|
2010-08-15 17:02:45 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2017-10-06 18:15:01 +02:00
|
|
|
nxsig_usleep(CONFIG_PAGING_WORKPERIOD);
|
2010-08-15 17:02:45 +02:00
|
|
|
|
|
|
|
/* The page fill worker thread will be awakened on one of three conditions:
|
|
|
|
*
|
2018-08-19 19:19:43 +02:00
|
|
|
* - When signaled by pg_miss(), the page fill worker thread will be
|
|
|
|
* awakened,
|
2010-08-15 17:02:45 +02:00
|
|
|
* - 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.
|
|
|
|
*/
|
|
|
|
|
2010-08-22 05:44:18 +02:00
|
|
|
#ifndef CONFIG_PAGING_BLOCKINGFILL
|
2010-08-15 17:02:45 +02:00
|
|
|
/* For the non-blocking up_fillpage(), the page fill worker thread will detect
|
2010-08-15 18:50:25 +02:00
|
|
|
* that the page fill is complete when it is awakened with g_pftcb non-NULL
|
2010-08-15 17:02:45 +02:00
|
|
|
* and fill completion status from pg_callback.
|
|
|
|
*/
|
|
|
|
|
2010-08-15 18:50:25 +02:00
|
|
|
if (g_pftcb != NULL)
|
2010-08-15 17:02:45 +02:00
|
|
|
{
|
|
|
|
/* 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 */
|
|
|
|
|
2018-08-24 14:58:30 +02:00
|
|
|
DEBUGASSERT(g_fillresult == OK);
|
2010-08-15 17:02:45 +02:00
|
|
|
|
2010-08-15 18:23:18 +02:00
|
|
|
/* Handle the successful page fill complete event by restarting the
|
|
|
|
* task that was blocked waiting for this page fill.
|
|
|
|
*/
|
2010-08-15 17:02:45 +02:00
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Restarting TCB: %p\n", g_pftcb);
|
2015-10-08 03:59:14 +02:00
|
|
|
up_unblock_task(g_pftcb);
|
2010-08-15 17:02:45 +02:00
|
|
|
|
2010-08-15 18:23:18 +02:00
|
|
|
/* Yes .. Start the next asynchronous fill. Check the return
|
|
|
|
* value to see a fill was actually started (false means that
|
|
|
|
* no fill was started).
|
|
|
|
*/
|
2010-08-15 17:02:45 +02:00
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Calling pg_startfill\n");
|
2010-08-15 18:23:18 +02:00
|
|
|
if (!pg_startfill())
|
2010-08-15 17:02:45 +02:00
|
|
|
{
|
2010-08-15 18:23:18 +02:00
|
|
|
/* No fill was started. This can mean only that all queued
|
|
|
|
* page fill actions have and been completed and there is
|
|
|
|
* nothing more to do.
|
|
|
|
*/
|
2010-08-15 17:02:45 +02:00
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Call pg_alldone()\n");
|
2010-08-15 17:02:45 +02:00
|
|
|
pg_alldone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If a configurable timeout period expires with no page fill completion
|
|
|
|
* event, then declare a failure.
|
|
|
|
*/
|
|
|
|
|
2010-08-22 05:44:18 +02:00
|
|
|
#ifdef CONFIG_PAGING_TIMEOUT_TICKS
|
2010-08-15 17:02:45 +02:00
|
|
|
else
|
|
|
|
{
|
2016-06-20 19:59:15 +02:00
|
|
|
pgerr("ERROR: Timeout!\n");
|
2019-12-01 20:01:16 +01:00
|
|
|
DEBUGASSERT(clock_systimer() - g_starttime <
|
|
|
|
CONFIG_PAGING_TIMEOUT_TICKS);
|
2010-08-15 17:02:45 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise, this might be a page fill initiation event. When
|
|
|
|
* awakened from pg_miss(), no fill will be in progress and
|
2010-08-15 18:50:25 +02:00
|
|
|
* g_pftcb will be NULL.
|
2010-08-15 17:02:45 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
2010-08-15 18:23:18 +02:00
|
|
|
/* Are there tasks blocked and waiting for a fill? If so,
|
|
|
|
* pg_startfill() will start the asynchronous fill (and set
|
2010-08-15 18:50:25 +02:00
|
|
|
* g_pftcb).
|
2010-08-15 18:23:18 +02:00
|
|
|
*/
|
2010-08-15 17:02:45 +02:00
|
|
|
|
2017-08-15 01:19:27 +02:00
|
|
|
pginfo("Calling pg_startfill\n");
|
2020-01-02 17:49:34 +01:00
|
|
|
pg_startfill();
|
2010-08-15 17:02:45 +02:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
/* Are there tasks blocked and waiting for a fill? Loop until all
|
2010-08-15 18:23:18 +02:00
|
|
|
* pending fills have been processed.
|
2010-08-15 17:02:45 +02:00
|
|
|
*/
|
|
|
|
|
2015-10-08 03:59:14 +02:00
|
|
|
for (; ; )
|
2010-08-15 17:02:45 +02:00
|
|
|
{
|
2010-08-15 18:23:18 +02:00
|
|
|
/* Yes .. Start the fill and block until the fill completes.
|
|
|
|
* Check the return value to see a fill was actually performed.
|
2018-08-19 19:19:43 +02:00
|
|
|
* (false means that no fill was performed).
|
2010-08-15 18:23:18 +02:00
|
|
|
*/
|
2010-08-15 17:02:45 +02:00
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Calling pg_startfill\n");
|
2010-08-15 18:23:18 +02:00
|
|
|
if (!pg_startfill())
|
|
|
|
{
|
2015-10-08 03:59:14 +02:00
|
|
|
/* Break out of the loop -- there is nothing more to do */
|
2010-08-15 18:23:18 +02:00
|
|
|
|
2015-10-08 03:59:14 +02:00
|
|
|
break;
|
2010-08-15 18:23:18 +02:00
|
|
|
}
|
2010-08-15 17:02:45 +02:00
|
|
|
|
2010-08-15 18:23:18 +02:00
|
|
|
/* Handle the page fill complete event by restarting the
|
|
|
|
* task that was blocked waiting for this page fill. In the
|
|
|
|
* non-blocking fill case, the page fill worker thread will
|
|
|
|
* know that the page fill is complete when pg_startfill()
|
|
|
|
* returns true.
|
2010-08-15 17:02:45 +02:00
|
|
|
*/
|
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Restarting TCB: %p\n", g_pftcb);
|
2015-10-08 03:59:14 +02:00
|
|
|
up_unblock_task(g_pftcb);
|
2010-08-15 17:02:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* All queued fills have been processed */
|
|
|
|
|
2016-06-20 19:59:15 +02:00
|
|
|
pginfo("Call pg_alldone()\n");
|
2010-08-15 17:02:45 +02:00
|
|
|
pg_alldone();
|
|
|
|
#endif
|
|
|
|
}
|
2012-07-14 21:30:31 +02:00
|
|
|
|
2010-08-15 17:02:45 +02:00
|
|
|
return OK; /* To keep some compilers happy */
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PAGING */
|