Driver implements DRM PRIME buffer sharing.
</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term>DRIVER_RENDER</term>
+ <listitem><para>
+ Driver supports dedicated render nodes.
+ </para></listitem>
+ </varlistentry>
</variablelist>
</sect3>
<sect3>
info, since man pages should cover the rest.
</para>
+ <!-- External: render nodes -->
+
+ <sect1>
+ <title>Render nodes</title>
+ <para>
+ DRM core provides multiple character-devices for user-space to use.
+ Depending on which device is opened, user-space can perform a different
+ set of operations (mainly ioctls). The primary node is always created
+ and called <term>card<num></term>. Additionally, a currently
+ unused control node, called <term>controlD<num></term> is also
+ created. The primary node provides all legacy operations and
+ historically was the only interface used by userspace. With KMS, the
+ control node was introduced. However, the planned KMS control interface
+ has never been written and so the control node stays unused to date.
+ </para>
+ <para>
+ With the increased use of offscreen renderers and GPGPU applications,
+ clients no longer require running compositors or graphics servers to
+ make use of a GPU. But the DRM API required unprivileged clients to
+ authenticate to a DRM-Master prior to getting GPU access. To avoid this
+ step and to grant clients GPU access without authenticating, render
+ nodes were introduced. Render nodes solely serve render clients, that
+ is, no modesetting or privileged ioctls can be issued on render nodes.
+ Only non-global rendering commands are allowed. If a driver supports
+ render nodes, it must advertise it via the <term>DRIVER_RENDER</term>
+ DRM driver capability. If not supported, the primary node must be used
+ for render clients together with the legacy drmAuth authentication
+ procedure.
+ </para>
+ <para>
+ If a driver advertises render node support, DRM core will create a
+ separate render node called <term>renderD<num></term>. There will
+ be one render node per device. No ioctls except PRIME-related ioctls
+ will be allowed on this node. Especially <term>GEM_OPEN</term> will be
+ explicitly prohibited. Render nodes are designed to avoid the
+ buffer-leaks, which occur if clients guess the flink names or mmap
+ offsets on the legacy interface. Additionally to this basic interface,
+ drivers must mark their driver-dependent render-only ioctls as
+ <term>DRM_RENDER_ALLOW</term> so render clients can use them. Driver
+ authors must be careful not to allow any privileged ioctls on render
+ nodes.
+ </para>
+ <para>
+ With render nodes, user-space can now control access to the render node
+ via basic file-system access-modes. A running graphics server which
+ authenticates clients on the privileged primary/legacy node is no longer
+ required. Instead, a client can open the render node and is immediately
+ granted GPU access. Communication between clients (or servers) is done
+ via PRIME. FLINK from render node to legacy node is not supported. New
+ clients must not use the insecure FLINK interface.
+ </para>
+ <para>
+ Besides dropping all modeset/global ioctls, render nodes also drop the
+ DRM-Master concept. There is no reason to associate render clients with
+ a DRM-Master as they are independent of any graphics server. Besides,
+ they must work without any running master, anyway.
+ Drivers must be able to run without a master object if they support
+ render nodes. If, on the other hand, a driver requires shared state
+ between clients which is visible to user-space and accessible beyond
+ open-file boundaries, they cannot support render nodes.
+ </para>
+ </sect1>
+
<!-- External: vblank handling -->
<sect1>
DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER),
DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_DEBUG("no function\n");
retcode = -EINVAL;
} else if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) ||
- ((ioctl->flags & DRM_AUTH) && !file_priv->authenticated) ||
+ ((ioctl->flags & DRM_AUTH) && !drm_is_render_client(file_priv) && !file_priv->authenticated) ||
((ioctl->flags & DRM_MASTER) && !file_priv->is_master) ||
- (!(ioctl->flags & DRM_CONTROL_ALLOW) && (file_priv->minor->type == DRM_MINOR_CONTROL))) {
+ (!(ioctl->flags & DRM_CONTROL_ALLOW) && (file_priv->minor->type == DRM_MINOR_CONTROL)) ||
+ (!(ioctl->flags & DRM_RENDER_ALLOW) && drm_is_render_client(file_priv))) {
retcode = -EACCES;
} else {
if (cmd & (IOC_IN | IOC_OUT)) {
goto out_prime_destroy;
}
-
- /* if there is no current master make this fd it */
+ /* if there is no current master make this fd it, but do not create
+ * any master object for render clients */
mutex_lock(&dev->struct_mutex);
- if (!priv->minor->master) {
+ if (!priv->minor->master && !drm_is_render_client(priv)) {
/* create a new master */
priv->minor->master = drm_master_create(priv->minor);
if (!priv->minor->master) {
goto out_close;
}
}
- mutex_unlock(&dev->struct_mutex);
- } else {
+ } else if (!drm_is_render_client(priv)) {
/* get a reference to the master */
priv->master = drm_master_get(priv->minor->master);
- mutex_unlock(&dev->struct_mutex);
}
+ mutex_unlock(&dev->struct_mutex);
mutex_lock(&dev->struct_mutex);
list_add(&priv->lhead, &dev->filelist);
iput(container_of(dev->dev_mapping, struct inode, i_data));
/* drop the reference held my the file priv */
- drm_master_put(&file_priv->master);
+ if (file_priv->master)
+ drm_master_put(&file_priv->master);
file_priv->is_master = 0;
list_del(&file_priv->lhead);
mutex_unlock(&dev->struct_mutex);
goto err_g2;
}
+ if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
+ ret = drm_get_minor(dev, &dev->render, DRM_MINOR_RENDER);
+ if (ret)
+ goto err_g21;
+ }
+
if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY)))
goto err_g3;
err_g4:
drm_put_minor(&dev->primary);
err_g3:
+ if (dev->render)
+ drm_put_minor(&dev->render);
+err_g21:
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_put_minor(&dev->control);
err_g2:
goto err_g1;
}
+ if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
+ ret = drm_get_minor(dev, &dev->render, DRM_MINOR_RENDER);
+ if (ret)
+ goto err_g11;
+ }
+
ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY);
if (ret)
goto err_g2;
err_g3:
drm_put_minor(&dev->primary);
err_g2:
+ if (dev->render)
+ drm_put_minor(&dev->render);
+err_g11:
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_put_minor(&dev->control);
err_g1:
unsigned int drm_debug = 0; /* 1 to enable debug output */
EXPORT_SYMBOL(drm_debug);
+unsigned int drm_rnodes = 0; /* 1 to enable experimental render nodes API */
+EXPORT_SYMBOL(drm_rnodes);
+
unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */
EXPORT_SYMBOL(drm_vblank_offdelay);
MODULE_DESCRIPTION(CORE_DESC);
MODULE_LICENSE("GPL and additional rights");
MODULE_PARM_DESC(debug, "Enable debug output");
+MODULE_PARM_DESC(rnodes, "Enable experimental render nodes API");
MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]");
MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
module_param_named(debug, drm_debug, int, 0600);
+module_param_named(rnodes, drm_rnodes, int, 0600);
module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_put_minor(&dev->control);
+ if (dev->render)
+ drm_put_minor(&dev->render);
+
if (driver->driver_features & DRIVER_GEM)
drm_gem_destroy(dev);
/* for a USB device */
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_unplug_minor(dev->control);
+ if (dev->render)
+ drm_unplug_minor(dev->render);
drm_unplug_minor(dev->primary);
mutex_lock(&drm_global_mutex);
if (ret)
goto err_g1;
+ if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
+ ret = drm_get_minor(dev, &dev->render, DRM_MINOR_RENDER);
+ if (ret)
+ goto err_g11;
+ }
+
ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY);
if (ret)
goto err_g2;
err_g3:
drm_put_minor(&dev->primary);
err_g2:
+ if (dev->render)
+ drm_put_minor(&dev->render);
+err_g11:
drm_put_minor(&dev->control);
err_g1:
kfree(dev);
#define DRIVER_GEM 0x1000
#define DRIVER_MODESET 0x2000
#define DRIVER_PRIME 0x4000
+#define DRIVER_RENDER 0x8000
#define DRIVER_BUS_PCI 0x1
#define DRIVER_BUS_PLATFORM 0x2
#define DRM_ROOT_ONLY 0x4
#define DRM_CONTROL_ALLOW 0x8
#define DRM_UNLOCKED 0x10
+#define DRM_RENDER_ALLOW 0x20
struct drm_ioctl_desc {
unsigned int cmd;
unsigned int agp_buffer_token;
struct drm_minor *control; /**< Control node for card */
struct drm_minor *primary; /**< render type primary screen head */
+ struct drm_minor *render; /**< render node for card */
struct drm_mode_config mode_config; /**< Current mode config */
return mutex_is_locked(&dev->mode_config.mutex);
}
+static inline bool drm_is_render_client(struct drm_file *file_priv)
+{
+ return file_priv->minor->type == DRM_MINOR_RENDER;
+}
+
/******************************************************************/
/** \name Internal function definitions */
/*@{*/
extern int drm_put_minor(struct drm_minor **minor);
extern void drm_unplug_dev(struct drm_device *dev);
extern unsigned int drm_debug;
+extern unsigned int drm_rnodes;
extern unsigned int drm_vblank_offdelay;
extern unsigned int drm_timestamp_precision;