Impact of Data Paging on Kernel-side Code Guide

Describes the kernel-side issues that must be considered when writing device drivers.

Purpose

This document explains the impact of data paging on kernel side code.

Intended Audience:

This document is intended for device driver writers.

Access to user memory

New restrictions on access to user memory.

Access to current thread's address space

Certain exported and internal APIs access the address space of the current thread and are subject to restrictions on their use enforced by assertions in the code. The restrictions are these:

  • The APIs may only be called from thread context.

  • They may not be called while any mutexes are held. There are two particularly important cases when mutexes are often held:

    • When publish and subscribe is writing large binary properties to user space, and

      • When the multiple memory model writes code segments' export directories to user space.

  • The APIs may not be called when the system lock is held. There are two particularly important cases when the system lock is often held:

    • When publish and subscribe is writing large binary properties to user space, and

    • When the multiple memory model uses DThread::FastWrite to write to user space.

The APIs concerned are these:

Access to another thread's address space

Certain exported and internal APIs access the address space of another thread and are subject to restrictions on their use enforced by assertions in the code. The restrictions are these:

  • The APIs may not be called when any mutexes are held. One particularly important case of this is when undertakers are completed and handles written to user space.

The APIs concerned are these:

Client request completion

In non-paged code it is usual for a thread to have an asynchronous request outstanding and to complete it by calling Kern::RequestComplete(). This technique is not safe in data paged code as it involves writing a status value back into the thread's address space, but this memory might be paged out and violate the thread's performance guarantees or cause a mutex order violation.

Instead, you should use the TClientRequest object to write the request status within the context of the client thread in the following way.

  1. Create a TClientRequest object by calling Kern::CreateClientRequest().

  2. Call the SetStatus() function of the TClientRequest object to set the client's request status.

  3. Call Kern::QueueRequestComplete() to signal the client thread that the request has been queued for completion.

  4. When the client thread next runs, the TRequestStatus value is written back to it.

  5. The TRequestStatus can now be reused, or destroyed by a call to Kern::DestroyRequest().

IPC message delivery

In non-paged code it is common for a client thread to send a message to a server and write it into the address space of the server. When data paging is enabled, this creates the same risk of page faults as the completion of asynchronous requests and can be mitigated by the same techniques as above. In addition, descriptor information (type, length and maximum length) is read by temporarily switching to the client's address space, creating additional risk of page faults.

When data paging is enabled, messages to servers must be pre-parsed and their type, length and maximum length stored in the message structure. This involves change to the kernel code but does not impact on user-side code.

Kernel event queue

The kernel maintains a queue of user-input events which is read by the window server. The introduction of data paging involved a change to the kernel code which responds to the user-side API UserSver::RequestEvent(). No change to user-side code is involved.