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

 

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,

  • offset will be the physical address of the start of the frame buffer,
  • size will be the size of the frame buffer in bytes, and
  • type will be DRM_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,

  • offset will be the physical address of the start of the register area,
  • size will be the size of the register area bytes, and
  • type will be DRM_REGISTERS.

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,

  • offset will be ignored and should be set to zero,
  • size will be the desired size of the SAREA in bytes,
  • type will be DRM_SHM.

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:

  • DRM_RESTRICT will prevent the area from being mapped into client space by a non-root user. The area will still be mapped into kernel-space, so this is useful to allow the kernel to have access to registers that control DMA while preventing the client from having such access.
  • DRM_READ_ONLY will force the area to be mapped as a read-only region in client space. This is useful if the client requires read access to registers that are in an area that also contains, for example, DMA control registers. (The client must not be allowed to initiate DMA to arbitrary locations in memory, since this opens up a significant security risk [FM99]).
  • DRM_LOCKED will force SAREA pages (of type DRM_SHM) to be locked into kernel memory. This flag will be ignored for the SI, since all shared memory will be locked.
  • DRM_KERNEL will require that the kernel has access to the mapped region. This flag will be ignored in the SI and the kernel will have read/write access to all mapped regions.
  • DRM_WRITE_COMBINING will specify that, if possible, the mapped region will be set to write-combining. (This is the default for DRM_FRAME_BUFFER but may be useful on some (non-PC-class) hardware for DRM_REGISTERS.)
  • DRM_CONTAINS_LOCK will specify that this SAREA region (of type DRM_SHM) contains the hardware lock at the beginning of the region. This flag must be specified for one of the SAREA regions.

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:

  • DRM_CONTEXT_PRESERVED means that this context guarantees that it will preserve the hardware state (i.e., the GLXContext stored in the hardware) whenever it is used. The kernel will never perform a hardware context switch to or away from this context. This flag is useful in the X server and in some portions of the direct-rendering client library.
  • DRM_CONTEXT_2DONLY means that this context is a 2D rendering context. When the hardware context switches are performed in the kernel device driver, this flag will alert the kernel that only a portion of the full 3D rendering context needs to be saved. In the SI, this flag is ignored by the kernel implementation since it does not perform hardware context switches (the X server performs these switches as a proxy for the kernel, and tracks 2D contexts in user space).

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:

  • DRM_IH_PRE_INST will be executed before the interrupt handler is installed.
  • DRM_IH_POST_INST will be executed after the interrupt handler is installed.
  • DRM_IH_SERVICE is the body of the interrupt handler, and will be executed whenever an interruption occurs.
  • DRM_IH_PRE_UNINST will be executed before the interrupt handler is uninstalled.
  • DRM_IH_POST_UNINST will be executed after the interrupt handler is uninstalled.
  • DRM_DMA_DISPATCH will wait until the hardware is ready for a DMA dispatch, and will dispatch a DMA buffer using a specified physical address and size.
  • DRM_DMA_READY will wait until the hardware is ready for a DMA dispatch.
  • DRM_DMA_IS_READY will look at the hardware and return a value indicating that the hardware is or is not ready for another DMA dispatch.
  • DRM_DMA_QUIESCENT will wait until the hardware is no longer processing DMA or graphics commands.

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:

  • DRM_M_WRITE will write to a specified mapped region and offset.
  • DRM_M_WHILE will read from a specified mapped region and offset while a condition is met.
  • DRM_M_IF will read from a specified mapped region and offset and will jump to another instruction if a condition is met.
  • DRM_M_GOTO will unconditionally jump to another instruction.
  • DRM_M_NOOP will no nothing.
  • DRM_M_RETURN will halt byte-code interpretation before the end of the command stream, and can be used to specify a return value for DRM_DMA_IS_READY.
  • DRM_M_DO will execute special functions, such returning the previously sent buffer to the free list and dispatching a new buffer.
  • DRM_M_READ will read from a specified mapped region and offset and will store the value in the byte-code accumulator.
  • DRM_M_TEST will test the accumulator and will jump to another instruction if a condition is met.

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:

  • context is the handle for the kernel-level DMA queue to use for the request. It was obtained from drmCreateContext by the X server and transmitted to the client via the XFree86-DRI protocol stream.
  • send_count is the number of buffers to send.
  • send_list is a vector of integers at least send_count long containing indices into the list field of the drmBufInfo structure. These are identical to the idx field of the drmBuf structure.
  • send_sizes is a vector of integers at least send_count long containing the size, in bytes, of each partially filled buffer. This value must be equal to or less than the total field in the drmBuf structure.
  • request_count is the number of buffers requested.
  • request_size is the desired size of the buffers.
  • request_list is a vector of integers at least request_count long. This vector will be filled with the index values of the buffer that are being reserved by this request.
  • request_sizes is a vector of integers at least request_count long. This vectore will be filled with the actual maximum size of the buffers returned. This value is identical to the total field in the drmBuf structure and is provided here for convenience only.
  • granted_count is the number of buffers actually reserved. This number is less than or equal to request_count.
  • flags specifies flags that modify the behavior of the drmDMA call:
    • DRM_DMA_BLOCK will cause the drmDMA call to wait until all of the DMA buffers have been dispatched and completely copied to the graphics hardware (although the commands in the DMA buffers may not yet have been processed by the graphics hardware). Without DRM_DMA_BLOCK or DRM_DMA_PRIORITY, drmDMA will not wait for DMA dispatch to occur.
    • DRM_DMA_PRIORITY an experimental high-priority blocking dispatch mode. NOTE/FIXME: This may not be supported in the final SI.
    • DRM_DMA_WHILE_LOCKED places the request on a special high-priority kernel-level DMA queue and dispatches the buffers ahead of all other buffers. This may be used, for example, when the direct-rendering client holds the hardware lock while processing a software fallback and requires that a DMA buffer be processed while the lock is being held. This may also be used when the X server is switching hardware contexts. This probably doesn't make sense without DRM_DMA_BLOCK.
    • DRM_DMA_WAIT will cause the drmDMA call to wait until request_count buffers are available. Otherwise, only buffers immediately available will be returned.
    • DRM_DMA_SMALLER_OK will allow buffers smaller than request_size to be returned.
    • DRM_DMA_LARGER_OK will allow buffers larger then request_size to be returned.

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.

  • DRM_LOCK_READY means that the hardware has completed all DMA dispatches and is ready to receive another DMA buffer (although the hardware may still be processing queued commands from the previous dispatch).
  • DRM_LOCK_QUIESCENT means that the hardware has completed all DMA dispatches and has completely finished processing the commands in those DMA buffers.
  • DRM_LOCK_FLUSH means that all of the pending DMA buffers for this context have been dispatched (combine with the DRM_LOCK_QUIESCENT to dispatch the buffers and wait for them to be processed).
  • DRM_LOCK_FLUSH_ALL means that all of the pending DMA buffers for all contexts have been dispatched.

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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值