aboutsummaryrefslogtreecommitdiff
path: root/nuttx/sched/pg_worker.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/sched/pg_worker.c')
-rw-r--r--nuttx/sched/pg_worker.c678
1 files changed, 0 insertions, 678 deletions
diff --git a/nuttx/sched/pg_worker.c b/nuttx/sched/pg_worker.c
deleted file mode 100644
index 4a18ec9a3..000000000
--- a/nuttx/sched/pg_worker.c
+++ /dev/null
@@ -1,678 +0,0 @@
-/****************************************************************************
- * sched/pg_worker.c
- * Page fill worker thread implementation.
- *
- * Copyright (C) 2010-2011 Gregory Nutt. All rights reserved.
- * Author: Gregory Nutt <gnutt@nuttx.org>
- *
- * 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 <stdbool.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_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.
- *
- * 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_pftcb;
-
-/****************************************************************************
- * 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_pftcb is non-NULL.
- * - Find the higher priority between the task waiting for the fill to
- * complete in g_pftcb 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_pftcb is non-NULL */
-
- pgllvdbg("g_pftcb: %p\n", g_pftcb);
- if (g_pftcb)
- {
- 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_pftcb 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_pftcb->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)
- {
- pgllvdbg("New worker priority. %d->%d\n",
- wtcb->sched_priority, 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) */
-
- pglldbg("Signaling worker. PID: %d\n", g_pgworker);
- kill(g_pgworker, SIGWORK);
-}
-#endif
-
-/****************************************************************************
- * 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
- * variable, g_pftcb.
- *
- * Input parameters:
- * None
- *
- * Returned Value:
- * If there are no further queue page fill operations to be performed,
- * pg_dequeue() 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).
- *
- * Assumptions:
- * Executing in the context of the page fill worker thread with all
- * 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) */
-
- g_pftcb = (FAR _TCB *)dq_remfirst((dq_queue_t*)&g_waitingforfill);
- pgllvdbg("g_pftcb: %p\n", g_pftcb);
- if (g_pftcb != 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 for the same page and
- * be queues multiple times. In this corner case, the blocked task will
- * simply be restarted.
- */
-
- if (!up_checkmapping(g_pftcb))
- {
- /* 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).
- */
-
- FAR _TCB *wtcb = (FAR _TCB *)g_readytorun.head;
- 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 */
-
- pgllvdbg("New worker priority. %d->%d\n",
- wtcb->sched_priority, priority);
- sched_setpriority(wtcb, priority);
- }
-
- /* Return with g_pftcb holding the pointer to
- * the TCB associated with task that requires the page fill.
- */
-
- return true;
- }
-
- /* The page need by this task has already been mapped into the
- * virtual address space -- just restart it.
- */
-
- pglldbg("Restarting TCB: %p\n", g_pftcb);
- up_unblock_task(g_pftcb);
- }
- }
- while (g_pftcb != NULL);
-
- return false;
-}
-
-/****************************************************************************
- * 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:
- * 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).
- *
- * Assumptions:
- * Executing in the context of the page fill worker thread with all
- * interrupts disabled.
- *
- ****************************************************************************/
-
-static inline bool pg_startfill(void)
-{
- FAR void *vpage;
- int result;
-
- /* 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
- * some cornercases) and will true if the next page TCB was successfully
- * dequeued.
- */
-
- if (pg_dequeue())
- {
- /* 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.
- */
-
- pgllvdbg("Call up_allocpage(%p)\n", g_pftcb);
- result = up_allocpage(g_pftcb, &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().
- */
-
- pgllvdbg("Call up_fillpage(%p)\n", g_pftcb);
- result = up_fillpage(g_pftcb, 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.
- */
-
- pgllvdbg("Call up_fillpage(%p)\n", g_pftcb);
- result = up_fillpage(g_pftcb, 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 = clock_systimer();
-#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 */
-
- return true;
- }
-
- pglldbg("Queue empty\n");
- return false;
-}
-
-/****************************************************************************
- * 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_pftcb 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_pftcb = NULL;
- pgllvdbg("New worker priority. %d->%d\n",
- wtcb->sched_priority, CONFIG_PAGING_DEFPRIO);
- 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_pftcb) to make the task that just
- * received the fill ready-to-run.
- */
-
- pglldbg("Restarting TCB: %p\n", g_pftcb);
- up_unblock_task(g_pftcb);
-}
-
-/****************************************************************************
- * 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_callback()
- * 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.
- */
-
- pglldbg("Started\n");
- 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.
- */
-
-#ifndef 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_pftcb non-NULL
- * and fill completion status from pg_callback.
- */
-
- if (g_pftcb != 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 successful page fill complete event by restarting the
- * task that was blocked waiting for this page fill.
- */
-
- pglldbg("Restarting TCB: %p\n", g_pftcb);
- up_unblock_task(g_pftcb);;
-
- /* Yes .. Start the next asynchronous fill. Check the return
- * value to see a fill was actually started (false means that
- * no fill was started).
- */
-
- pgllvdbg("Calling pg_startfill\n");
- if (!pg_startfill())
- {
- /* 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.
- */
-
- pgllvdbg("Call pg_alldone()\n");
- pg_alldone();
- }
- }
-
- /* If a configurable timeout period expires with no page fill completion
- * event, then declare a failure.
- */
-
-#ifdef CONFIG_PAGING_TIMEOUT_TICKS
- else
- {
- lldbg("Timeout!\n");
- ASSERT(clock_systimer() - 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_pftcb will be NULL.
- */
-
- else
- {
- /* Are there tasks blocked and waiting for a fill? If so,
- * pg_startfill() will start the asynchronous fill (and set
- * g_pftcb).
- */
-
- pgllvdbg("Calling pg_startfill\n");
- (void)pg_startfill();
- }
-#else
- /* Are there tasks blocked and waiting for a fill? Loop until all
- * pending fills have been processed.
- */
-
- for (;;)
- {
- /* Yes .. Start the fill and block until the fill completes.
- * Check the return value to see a fill was actually performed.
- * (false means that no fill was perforemd).
- */
-
- pgllvdbg("Calling pg_startfill\n");
- if (!pg_startfill())
- {
- /* Break out of the loop -- there is nothing more to do */
-
- break;
- }
-
- /* 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.
- */
-
- pgllvdbg("Restarting TCB: %p\n", g_pftcb);
- up_unblock_task(g_pftcb);;
- }
-
- /* All queued fills have been processed */
-
- pgllvdbg("Call pg_alldone()\n");
- pg_alldone();
-#endif
- }
-
- return OK; /* To keep some compilers happy */
-}
-#endif /* CONFIG_PAGING */