The Direct Rendering Manager: Kernel Support for the Direct Rendering Infrastructure

Rickard E. Faith Precision Insight, Inc.

$Date: 1999/05/11 22:45:24 $, $Revision: 1.4 $


This paper contains two major sections. The first section describes and discusses the requirements of the Direct Rendering Manager (DRM), a kernel-level device driver that provides support for the Direct Rendering Infrastructure (DRI). The next section describes the xf86drm API exported to the X server and client-side X applications. The low-level ioctl API exported by the kernel to the xf86drm library is not discussed here. Familiarity with the DRI design documents is assumed [OM1998, MFOA1999].

1. Preamble

1.1 Copyright

Copyright © 1999 by Precision Insight, Inc., Cedar Park, Texas. All Rights Reserved.

Permission is granted to make and distribute verbatim copies of this document provided the copyright notice and this permission notice are preserved on all copies.

1.2 Trademarks

OpenGL is a registered trademark of Silicon Graphics, Inc. Unix is a registered trademark of The Open Group. The `X' device and X Window System are trademarks of The Open Group. XFree86 is a trademark of The XFree86 Project. Linux is a registered trademark of Linus Torvalds. Intel is a registered trademark of Intel Corporation. All other trademarks mentioned are the property of their respective owners.

2. The Direct Rendering Manager

The Direct Rendering Manager (DRM) is a kernel-level device driver that loads into the Linux kernel via the standard module interface. The functionality of the DRM can be implemented for other operating systems at a later date. There are a few issues that may require kernel changes outside the module interface -- these issues will be discussed in the future work section.

The DRM supports the Direct Rendering Infrastructure (DRI) in three major ways:

  1. The DRM provides synchronized access to the graphics hardware.

    The direct rendering system has multiple entities (i.e., the X server, multiple direct-rendering clients, and the kernel) competing for direct access to the graphics hardware. Hardware that is currently available for PC-class machines will lock up if more than one entity is accessing the hardware (e.g., if two clients intermingle requests in the command FIFO or (on some hardware) if one client reads the framebuffer while another writes the command FIFO).

    The DRM provides a single per-device hardware lock to synchronize access to the hardware. The hardware lock may be required when the X server performs 2D rendering, when a direct-rendering client is performing a software fallback that must read or write the frame buffer, or when the kernel is dispatching DMA buffers.

    This hardware lock may not be required for all hardware (e.g., high-end hardware may be able to intermingle command requests from multiple clients) or for all implementations (e.g., one that uses a page fault mechanism instead of an explicit lock). In the later case, the DRM would be extended to provide support for this mechanism.

    For more details on the hardware lock requirements and a discussion of the performance implications and implementation details, please see [FOM99].

  2. The DRM enforces the DRI security policy for access to the graphics hardware.

    The X server, running as root, usually obtains access to the frame buffer and MMIO regions on the graphics hardware by mapping these regions using /dev/mem. The direct-rendering clients, however, do not run as root, but still require similar mappings. Like /dev/mem, the DRM device interface allows clients to create these mappings, but with the following restrictions:

    1. The client may only map regions if it has a current connection to the X server. This forces direct-rendering clients to obey the normal X server security policy (e.g., using xauth).
    2. The client may only map regions if it can open /dev/drm?, which is only accessible by root and by a group specified in the XF86Config file (a file that only root can edit). This allows the system administrator to restrict direct rendering access to a group of trusted users.
    3. The client may only map regions that the X server allows to be mapped. The X server may also restrict those mappings to be read-only. This allows regions with security implications (e.g., those containing registers that can start DMA) to be restricted.
  3. The DRM provides a generic DMA engine.

    Most modern PC-class graphics hardware provides for DMA access to the command FIFO. Often, DMA access has been optimized so that it provides significantly better throughput than does MMIO access. For these cards, the DRM provides a DMA engine with the following features:

    1. The X server can specify multiple pools of different sized buffers which are allocated and locked down.
    2. The direct-rendering client maps these buffers into its virtual address space, using the DRM API.
    3. The direct-rendering client reserves some of these buffers from the DRM, fills the buffers with commands, and requests that the DRM send the buffers to the graphics hardware. Small buffers are used to ensure that the X server can get the lock between buffer dispatches, thereby providing X server interactivity. Typical 40MB/s PCI transfer rates may require 10000 4kB buffer dispatches per second.
    4. The DRM manages a queue of DMA buffers for each OpenGL GLXContext, and detects when a GLXContext switch is necessary. Hooks are provided so that a device-specific driver can perform the GLXContext switch in kernel-space, and a callback to the X server is provided when a device-specific driver is not available (for the SI, the callback mechanism is used because it provides an example of the most generic method for GLXContext switching). The DRM also performs simple scheduling of DMA buffer requests to prevent GLXContext thrashing. When a GLXContext is swapped a significant amount of data must be read from and/or written to the graphics device (between 4kB and 64kB for typical hardware).
    5. The DMA engine is generic in the sense that the X server provides information at run-time on how to perform DMA operations for the specific hardware installed on the machine. The X server does all of the hardware detection and setup. This allows easy bootstrapping for new graphics hardware under the DRI, while providing for later performance and capability enhancements through the use of a device-specific kernel driver.
  4. The DRM is extensible.

    The DMA engine is extensible via the use of a device-specific kernel module that can hook-out some or all of the generic functionality. Because DRM exports a well-known API with well-known entry points, the author of the device-specific driver can use as much of the existing DRM functionality as is applicable, and only hook out one or two pieces of functionality that is required by the hardware.

    For example, the device-specific driver can use all of the existing security, memory mapping, and DMA buffer management features of the DRM and hook out only the ioctl that performs the DMA. This is important for graphics hardware that permits multiple-outstanding DMA requests or that provides for ``nested DMA'' (i.e., a DMA request that is initiated within another DMA request).

The next section documents the library-level API that provides wrappers for the kernel-level ioctl API. Currently, there is nearly a one-to-one mapping between the library API and the ioctl API, with the library providing convenience and abstraction.

3. Library API

The library API is available for use within the X server and the direct-rendering clients. This API wraps the low-level ioctl calls that are made to the kernel.

3.1 Obtaining information about DRM

drmAvailable

int drmAvailable(void)
            

drmAvailable is used to determine if the DRM kernel driver has been loaded.

Returns 1 if the DRM driver is loaded, 0 otherwise.

drmOpenDRM

int drmOpenDRM(void)
            

drmOpenDRM is used to open the main /dev/drm control device. If running as root, this device is automatically created in /dev with the appropriate mode.

Returns a file descriptor for the main /dev/drm control device, or a negative value on error.

drmCloseDRM

int drmCloseDRM(int fd)
            

drmCloseDRM closes the file descriptor returned by drmOpenDRM.

drmGetVersion

drmVersionPtr drmGetVersion(int fd)
            

drmGetVersion queries the driver specified by the file descriptor and returns version information about that specific driver. The drmVersion structure, shown below, is automatically created and should be freed with drmFreeVersion.

Returns a new drmVersion structure, or NULL on error.

typedef struct _drmVersion {
    int     version_major;        // Major version of driver
    int     version_minor;        // Minor version of driver
    int     version_patchlevel;   // Patch level of driver
    char    *name;                // Driver name
    char    *date;                // Driver date
    char    *desc;                // Driver description
} drmVersion, *drmVersionPtr;
            

Similar information is available from /proc/drm/drivers.

drmFreeVersion

void drmFreeVersion(drmVersionPtr)
            

drmFreeVersion frees the drmVersion structure returned by drmGetVersion.

drmGetVersionList

drmListPtr drmGetVersionList(int fd)
            

drmGetVersionList returns information about all DRM drivers currently loaded on the system. The drmList structure, shown below, is automatically created and should be freed with drmFreeVersionList.

Returns a new drmList strucutre, or NULL on error.

typedef struct _drmList {
    int              count;       // Length of version
    drmVersionPtr    version;     // List of versions 
} drmList, *drmListPtr;
            

Similar information is available from /proc/drm/drivers.

drmFreeVersionList

void drmFreeVersionList(drmListPtr)
            

drmFreeVersionList frees the drmList structure returned by drmGetVersionList.

drmGetInterruptFromBusID

int drmGetInterruptFromBusID(int fd, int busnum,
                             int devnum, int funcnum)
            

The X server is responsible for determining the IRQ that the graphics hardware will use for DMA operations. XFree86 provides complete user-space support for querying PCI devices, and this support is used for determining graphics hardware capabilities, including which regions of memory should be mapped as the frame buffer and the MMIO regions. This user-space support can also be used to query the IRQ for the PCI device. However, the OS is free to remap the IRQ, and Linux will remap the IRQ whenever IO-APIC support is compiled into the kernel, making it impossible for the user-space routines to determine which IRQ should be used for the device.

The drmGetInterruptFromBusID call uses the bus, device, and function description of the hardware to determine the IRQ mapping.

Returns the IRQ being used by the hardware, or a negative value on error.

3.2 Installing and Configuring the DRM sub-driver

The /dev/drm device is the main control device for the DRM. This device can be used to query the availability of sub-drivers and to install these sub-drivers. The X server will query the hardware and determine which sub-driver should be used for that particular hardware. Then the X server will install and configure that sub-driver. For hardware without a device-specific sub-driver, the ``generic'' sub-driver will be used.

drmCreateSub

int drmCreateSub(int fd, const char *name, const char *busid)
            

drmCreateSub will create an instance of the sub-driver called name. To support multi-head X servers (or more than one X server running simultaneously on different hardware), each sub-driver may be instantiated more than once. However, the busid must be unique across all sub-driver. Usually the busid will be a string describing the unique PCI bus identifier (e.g., a bus, device, function tuple). For OSs that do not support this notion of bus identifier, any unique string may be used.

A device of the appropriate ownership and mode will be created in /dev. Sub-driver instantiations will use /dev/drm0, /dev/drm1, /dev/drm2, etc., and will be referred to in this document at /dev/drm?.

NOTE/FIXME: For the Linux Expo demo, the ability to instantiate more than one sub-driver is not supported. This functionality will be supported for the SI.

Returns 0 on success and a negative value on error. May only be called by root.

drmDestroySub

int drmDestroySub(int fd, const char *busid)
            

drmDestroySub is used to destroy the sub-driver with the specified busid. If the sub-driver is currently in use by other processes, it will be marked as destroyed, and will return errors for all subsequent uses. When the last process disconnects from the sub-driver, then all of its resources will be reclaimed.

NOTE/FIXME: For the Linux Expo demo, this call will fail unless all processes have disconnected from the sub-driver. The ability to mark a pending destruction will be supported for the SI.

Returns 0 on success and a negative value on error. May only be called by root.

drmAddMap

int drmAddMap(int fd, drmHandle offset, drmSize size,
              drmMapType type, drmMapFlags flags,
              drmHandlePtr handle)
            

drmAddMap specifies a range of memory that is available for mapping by a non-root process using mmap(2) on /dev/drm?.

Returns 0 on success and a negative value on error. The handle will be set to a value that may be used as the offset parameter for mmap(2). May only be called by root.

Mapping the frame buffer

For the frame buffer,

The area mapped will be uncached. If MTRR support is available in the kernel, the frame buffer area will be set to write combining.

Mapping the MMIO register area

For the MMIO register area,

The area mapped will be uncached.

Mapping the SAREA

The SAREA is a shared memory segment that is used for communication between the X server, the direct-rendering clients, and the kernel. Standard shm* calls provide only uid/gid-based access restrictions and do not provide the ability to enforce the DRI's security policy, so this area is created and mapped via the DRM interface.

For the SAREA,

A shared memory area of the requested size will be created and locked in kernel memory. This area may be mapped into client-space by using the handle returned.

Flags

Several flags are provided to modify the actions of drmAddMap:

drmAddBufs

drmAddBufs(int fd, int count, int size)
            

drmAddBufs is used to request that count buffers of size bytes be allocated for DMA transfers. More than one size of buffer can be allocated by using drmAddBufs multiple times.

Returns the number of buffers actually allocated, or a negative value on error. May only be called by root.

drmMarkBufs

int drmMarkBufs(int fd, double low, double high)
            

drmMarkBufs specifies a low and high water mark for buffer allocation. low and high should be values between 0 and 1.

Returns 0 on success and a negative value on error. May only be called by root.

drmCreateContext

int drmCreateContext(int fd, drmContextPtr handle)
            

drmCreateContext is used by the X server during GLXContext initialization. The handle returned is used by the client when requesting DMA dispatch with drmDMA.

This call causes kernel-level resources to be allocated for the kernel-level DMA queue associated with the context.

Returns 0 on success and a negative value on error. On success, the handle parameter is set. May only be called by root.

drmDestroyContext

int drmDestroyContext(int fd, drmContext handle)
            

drmDestroyContext frees any kernel-level resources allocated using drmCreateContext. Any DMA buffers that are waiting on the kernel-level DMA queue that have not yet been dispatched to the hardware are removed.

Returns 0 on success and a negative value on error. May only be called by root.

drmSetContextFlags

int drmSetContextFlags(int fd, drmContext context,
                       drmContextFlags flags)
            

drmSetContextFlags sets kernel-level flags on a particular context. Currently, two flags are available:

Returns 0 on success and a negative value on error. May only be called by root.

drmGetContextFlags

int drmGetContextFlags(int fd, drmContext context,
                       drmContextFlagsPtr flags)
            

drmGetContextFlags obtains the flags that were set with drmSetContextFlags.

Returns 0 on success and a negative value on error.

drmAddContextTag

int drmAddContextTag(int fd, drmContext context, void *tag)
            

The X server or the direct-rendering client library may want to associate a private data structure with the drmContext. Because the drmContext is opaque, the drmAddContextTag function is provided as a library-level helper-function to associate a context with a tag. The tag can be retrieved with drmGetContextTag.

This function is implemented in user-space, does not depend on kernel-level support, and is not required for proper kernel-level functionality.

Returns 0 on success.

drmGetContextTag

void *drmGetContextTag(int fd, drmContext context)
            

drmGetContextTag returns the tag associated with the context using drmSetContextTag.

Returns the tag on success, and NULL on failure.

drmGetReservedContextList

drmContextPtr drmGetReservedContextList(int fd, int *count)
            

The X server may want to tag contexts that the kernel reserves for itself. In order to do this, drmGetReservedContextList is provided to get a list of reserved contexts from the kernel. These contexts will never be returned from a drmCreateContext call, but may appear in a context switch request.

Returns a list of length count on success, and NULL on error.

drmFreeReservedContextList

void drmFreeReservedContextList(drmContextPtr)
            

This call frees the pointer returned by drmGetReservedContextList.

drmCreateDrawable

int drmCreateDrawable(int fd, drmDrawablePtr handle)
            

drmCreateDrawable creates a handle for each drawable. This is a hook for future expansion and is not currently used.

Returns 0 on success and a negative value on error. May only be called by root.

drmDestrooyDrawable

int drmDestroyDrawable(int fd, drmDrawable handle)
            

drmDestroyDrawable destroys a drawable handle created with drmCreateDrawable. This is a hook for future expansion and is not currently used.

Returns 0 on success and a negative value on error. May only be called by root.

drmCtlAddCommand

int drmCtlAddCommand(int fd, drmCtlDesc desc, int count, int *inst)
            

The generic version of the DRM contains no device-specific code. However, the DRM must dispatch DMA, which is a device-specific process. drmCtlAddCommand is used by the X server to specify how to perform common device-specific functions using a generic device-independent byte code. For more efficient execution of these functions, a device-specific driver can hook out all of the commands.

Returns 0 on success and a negative value on failure. May only be called by root.

Commands

Several commands can be specified:

Instructions

Instructions are 5 integers long and can be constructed in an array using macros. Instructions are fully documented in the ioctl section of this paper, and are outlined here:

drmCtlRemoveCommands

int drmCtlRemoveCommands(int fd)
            

drmCtlRemoveCommands will remove all of the commands added with drmCtlAddCommand.

Returns 0 on success and a negative value on failure. May only be called by root.

drmCtlInstHandler

int drmCtlInstHandler(int fd, int irq)
            

drmCtlInstHandler installs an interrupt handler for the specified irq.

Returns 0 on success and a negative value on failure. May only be called by root.

drmCtlUninstHandler

int drmCtlUninstHandler(int fd)
            

drmCtlUninstHandler uninstalls the interrupt handler installed with drmCtlInstHandler.

Returns 0 on success and a negative value on failure. May only be called by root.

drmInstallSIGIOHandler

int drmInstallSIGIOHandler(int fd,
                           void (*f)(int fd, void *oldctx, void *newctx))
            

drmInstallSIGIOHandler sets up a signal handler for the X server to be notified when drmContext swaps are required. This function sets up the specified file descriptor for non-blocking reads and installs a handler for the SIGIO signal. When a SIGIO is received, the file descriptor will be read. If commands are read from the file descriptor they are parsed into a pair of drmContext handles. These handles are translated to tags with drmGetContextTag and the specified function is called. When the function returns, the kernel will be notified using the DRM_IOCTL_NEW_CTX ioctl.

Returns 0 on success and a negative value on failure. May only be called by root.

drmRemoveSIGIOHander

int drmRemoveSIGIOHandler(int fd)
            

drmRemoveSIGIOHandler will remove the SIGIO handler installed using drmInstallSIGIOHandler.

Returns 0 on success and a negative value on failure. May only be called by root.

3.3 Using the DRM sub-driver

After the DRM sub-driver is installed and configured by the X server, both the X server and the 3D direct-rendering clients may use the facilities provided.

NOTE/FIXME: The client-size authentication API is not documented here. For the Linux Expo demo, we will use the current magic-cookie algorithm. However, for the SI, we will move to a different algorithm, suggested by Colin Plumb [Plumb99]:

  1. The client opens an unauthenticated connection with /dev/drm?.
  2. The client makes a library call that performs an ioctl that obtains a magic number from the kernel.
  3. The client sends the magic number to the X server, via the XFree86-DRI protocol stream, requesting authentication.
  4. The X server makes a library call that performs an ioctl telling the kernel to authenticate the currently open-but-unauthenticated connection associated with that magic number.

drmOpenSub

int drmOpenSub(const char *busid)
            

drmOpenSub returns a file descriptor for the sub-driver specified by the busid. The busid is send from the X server to the direct-rending client via the XFree86-DRI protocol.

Returns a file descriptor on success, and a negative value on failure.

drmCloseSub

int drmCloseSub(int fd)
            

drmCloseSub closes the file descriptor obtained from drmOpenSub.

Returns 0 on success, and a negative value on failure.

drmMap

int drmMap(int fd, drmHandle handle, drmSize size,
           drmAddressPtr address)
            

drmMap (implemented as a wrapper for mmap(2)) maps a region of memory previously made mappable by the X server via the drmAddMap call. The handle for drmMap is the handle returned by the drmAddMap. The size must match that used by drmAddMap.

Returns 0 on success, and a negative value on failure. On success, address contains the user-space virtual address where the mapping begins.

drmUnmap

int drmUnmap(drmAddress address, drmSize size)
            

drmUnmap (implemented as a wrapper for munmap(2)) is used to unmap mappings obtained with drmMap.

Returns 0 on success, and a negative value on failure.

drmGetBufInfo

drmBufInfoPtr drmGetBufInfo(int fd)
            

drmGetBufInfo is used to get information about the buffer mapping. This can be used for debugging purposes, or by a sophisticated client library to determine how best to use the available buffers (e.g., large buffers can be used for image transfer).

typedef struct _drmBufDesc {
    int              count;       // Number of buffers of this size 
    int              size;        // Size in bytes                  
    int              low_mark;    // Low water mark                 
    int              high_mark;   // High water mark                
} drmBufDesc, *drmBufDescPtr;

typedef struct _drmBufInfo {
    int              count;       // Number of buffers described in list 
    drmBufDescPtr    list;        // List of buffer descriptions         
} drmBufInfo, *drmBufInfoPtr;
            

Returns a pointer to a newly allocated drmBufInfo structure on success, and NULL on error.

drmFreeBufInfo

void drmFreeBufInfo(drmBufInfoPtr)
            

Frees a structure allocated by drmGetBufInfo.

NOTE/FIXME: This function is not yet implemented.

drmMapBufs

drmBufMapPtr drmMapBufs(int fd)
            

Maps all of the the DMA buffers into client-virtual space, creating and returning a drmBufMap structure. This structure and the associated mappings, can be freed using drmUnmapBufs.

Note that the client may not use these buffers until obtaining buffer indices with drmDMA.

typedef struct _drmBuf {
    int              idx;         // Index into master buflist         
    int              total;       // Buffer size                       
    int              used;        // Amount of buffer in use (for DMA) 
    drmAddress       address;     // Address                           
} drmBuf, *drmBufPtr;

typedef struct _drmBufMap {
    int              count;       // Number of buffers mapped
    drmBufPtr        list;        // Buffers                 
} drmBufMap, *drmBufMapPtr;
            

Returns a pointer to the newly allocated drmBufMap on success, and NULL on error.

drmUnmapBufs

int drmUnmapBufs(drmBufMapPtr bufs)
            

Unmaps buffers allocated with drmMapBufs.

Returns 0 on success, and a negative value on failure.

drmDMA

int drmDMA(int fd, drmDMAReqPtr request)
            

drmDMA can be used to reserve DMA buffers and to dispatch previously reserved DMA buffers.

Returns 0 on success, and a negative value on failure.

typedef struct _drmDMAReq {
                                  // Indices here refer to the offset into
                                  // list in drmBufInfo               
    drmContext    context;        // Context handle                   
    int           send_count;     // Number of buffers to send        
    int           *send_list;     // List of handles to buffers       
    int           *send_sizes;    // Lengths of data to send, in bytes
    drmDMAFlags   flags;          // Flags                            
    int           request_count;  // Number of buffers requested      
    int           request_size;   // Desired size of buffers requested
    int           *request_list;  // Buffer information               
    int           *request_sizes; // Minimum acceptable sizes         
    int           granted_count;  // Number of buffers granted at this size
} drmDMAReq, *drmDMAReqPtr;
            

The fields must be filled in before the call as follows:

drmFreeBufs

int drmFreeBufs(int fd, int count, int *list)
            

drmFreeBufs will unreserve the buffers in list, previously reserved using drmDMA. This function is primarily used for debugging.

Returns 0 on success, and a negative value on failure.

drmGetLock

int drmGetLock(int fd, drmContext context, drmLockFlags flags)
            

drmGetLock will obtain the heavyweight hardware lock and will return 0. The hardware lock is obtained whenever the hardware is touched, and holding the lock implies that no other entity in the system will touch the hardware. For a complete discussion of the locking algorithms, please see [FOM99].

Several flags are available that determine the state of the hardware when drmGetLock returns:

NOTE/FIXME None of these flags are currently implemented, but they will be before the Linux Expo demo.

drmUnlock

int drmUnlock(int fd, drmContext context)
            

drmUnlock will release the hardware lock.

Returns 0 on success, and a negative value on failure.

drmFinish

int drmFinish(int fd, drmContext context, drmLockFlags flags)
            

drmFinish takes the same flags as drmGetLock and does the same processing, but returns without the lock held.

NOTE/FIXME drmFinish is not currently implemented, but they will be before the Linux Expo demo.

Returns 0 on success, and a negative value on failure.

3.4 Helper Functions

Several helper functions are provided for use by the X server and the direct-rendering client. These functions are implemented purely in user-space and do not use the kernel-level ioctl interface.

Hash Table Support

Simple hash tables are provided that map an unsigned long key to an unsigned long value. The hash table is currently not dynamically extensible, but should be sufficient to store approximately 100-200 object. The hash function uses a table of random integers, as described by [Hanson97], and collisions are resolved using a self-modifying linked list [Knuth73]. Suggestions for future enhancements and references are included in the source code.

The interface is described briefly:

void *drmHashCreate(void);
int  drmHashDestroy(void *t);
int  drmHashLookup(void *t, unsigned long key, unsigned long *value);
int  drmHashInsert(void *t, unsigned long key, unsigned long value);
int  drmHashDelete(void *t, unsigned long key);
int  drmHashFirst(void *t, unsigned long *key, unsigned long *value);
int  drmHashNext(void *t, unsigned long *key, unsigned long *value);
            

Pseudo-Random Number Generator (PRNG) Support

A simple, straightforward implementation of the Park and Miller ``Minimal Standard'' PRNG [PM88, PMS93], which is a Lehmer multiplicative linear congruential generator (MLCG) with a period of 2^31-1. This implementation is intended to provide a reliable, portable PRNG that is suitable for testing a hash table implementation and for implementing skip lists (see below). Suggestions for future enhancements and references are included in the source code.

The interface is described briefly:

void          *drmRandomCreate(unsigned long seed);
int           drmRandomDestroy(void *state);
unsigned long drmRandom(void *state);
double        drmRandomDouble(void *state);
            

Skip List Support

Skip lists [Pugh90] are a probabilistic alternative to balanced trees. An implementation is included to support maintenance of ordered lists for texture memory management. Suggestions for future enhancements and references are included in the source code.

The interface is described briefly:

void *drmSLCreate(void);
int  drmSLDestroy(void *l);
int  drmSLLookup(void *l, unsigned long key, void **value);
int  drmSLInsert(void *l, unsigned long key, void *value);
int  drmSLDelete(void *l, unsigned long key);
int  drmSLNext(void *l, unsigned long *key, void **value);
int  drmSLFirst(void *l, unsigned long *key, void **value);
void drmSLDump(void *l);
            

4. Future Work

4.1 Delaying Signal Delivery

If the direct-rendering client holds the hardware lock and receives a SIGKILL signal, deadlock may result. If the SIGKILL is detected and the hardware lock is immediately taken from the client, the typical PC-class graphics hardware may be left in an unknown state and may lock up (this kind of hardware usually does not deal well with intermingling of command streams).

Similarly, if the direct-rendering client holds the hardware lock and receives a SIGSTOP, the X server and all other direct-rendering clients will block until the process releases the lock.

Ideally, the client should be permitted to complete the rendering operation that is currently in progress and have the SIGKILL or SIGSTOP signals delivered as soon as the hardware lock is released (or, after some reasonable timeout, so that buggy clients can be halted). This delay of signal delivery appears to require kernel-level changes that cannot be performed by a loadable module using the standard module interface.

4.2 Performance and Debugging Support

Currently, the /proc/drm interface exports some performance counters that can be used for debugging and for gathering performance metrics. The counters currently available were selected on an ad hoc basis as needed for debugging the initial SI. Counter selection and presentation needs to be revisited so that counter that are useful to both the driver implementor and to the OpenGL application author are available, together with a user-space application that computes useful statistics from the counters (e.g., computing rate from two counter snapshots).

5. References

[FM99] Rickard E. Faith and Kevin E. Martin. A Security Analysis of the Direct Rendering Infrastructure. Cedar Park, Texas: Precision Insight, Inc., 1999.

[FOM99] Rickard E. Faith, Jens Owen, and Kevin E. Martin. Hardware Locking for the Direct Rendering Infrastructure. Cedar Park, Texas: Precision Insight, Inc., 1999.

[Hanson97] David R. Hanson. C Interfaces and Implementations: Techniques for Creating Reusable Software. Reading, Massachusetts: Addison-Wesley, 1997.

[Knuth73] Donald E. Knuth. The Art of Computer Programming. Volume 3: Sorting and Searching. Reading, Massachusetts: Addison-Wesley, 1973.

[MFOA99] Kevin E. Martin, Rickard E. Faith, Jens Owen, Allen Akin. Direct Rendering Infrastructure, Low-Level Design Document. Cedar Park, Texas: Precision Insight, Inc., 1999.

[OM98] Jens Owen and Kevin E. Martin. A Multipipe Direct Rendering Architecture for 3D. Cedar Park, Texas: Precision Insight, Inc., 15 September 1998. Available from http://www.precisioninsight.com/dr/dr.html.

[Plumb99] Colin Plumb, personal communication, 20 March 1999.

[PM88] Stephen K. Park and Keith W. Miller. ``Random Number Generators: Good Ones are Hard to Find.'' CACM 31(10), October 1988, pp. 1192-1201.

[PMS93] Stephen K. Park, Keith W. Miller, and Paul K. Stockmeyer. In ``Technical Correspondence: Remarks on Choosing and Implementing Random Number Generators.'' CACM 36(7), July 1993, pp. 105-110.

[Pugh90] William Pugh. ``Skip Lists: A Probabilistic Alternative to Balanced Trees.'' CACM 33(6), June 1990, pp. 668-676.