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].
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.
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.
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:
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].
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:
xauth
)./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.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:
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.
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.
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.
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.
int drmCloseDRM(int fd)
drmCloseDRM
closes the file descriptor returned by
drmOpenDRM
.
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
.
void drmFreeVersion(drmVersionPtr)
drmFreeVersion
frees the drmVersion
structure
returned by drmGetVersion
.
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
.
void drmFreeVersionList(drmListPtr)
drmFreeVersionList
frees the drmList
structure
returned by drmGetVersionList
.
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.
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.
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
.
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
.
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
.
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.
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.
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.
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(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
.
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
.
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
.
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
.
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
.
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.
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.
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.
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.
void drmFreeReservedContextList(drmContextPtr)
This call frees the pointer returned by
drmGetReservedContextList
.
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
.
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
.
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
.
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 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.
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
.
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
.
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
.
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
.
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
.
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]:
/dev/drm?
.ioctl
that obtains a magic number from the kernel.ioctl
telling the kernel to authenticate the
currently open-but-unauthenticated connection associated
with that magic number.
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.
int drmCloseSub(int fd)
drmCloseSub
closes the file descriptor obtained from
drmOpenSub
.
Returns 0 on success, and a negative value on failure.
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.
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.
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.
void drmFreeBufInfo(drmBufInfoPtr)
Frees a structure allocated by drmGetBufInfo
.
NOTE/FIXME: This function is not yet implemented.
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.
int drmUnmapBufs(drmBufMapPtr bufs)
Unmaps buffers allocated with drmMapBufs
.
Returns 0 on success, and a negative value on failure.
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.
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.
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.
int drmUnlock(int fd, drmContext context)
drmUnlock
will release the hardware lock.
Returns 0 on success, and a negative value on failure.
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.
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.
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);
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 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);
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.
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).
[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.