From b93658f83fa7f89cde5cfba7158da2be8cac42b2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:44 +0100 Subject: [PATCH] drm/doc: Document drm_file.[hc] Well, mostly drm_file.h, and clean up all related things: - I didnt' figure out the difference between preclose and postclose. The existing explanation in drm-internals.rst didn't convince me, since it's also really outdated - we clean up pending DRM events in the core nowadays. I put a FIXME in for the future. - Another FIXME is to have a macro for default fops. - Lots of links all around, main areas are to tie the overview in drm_file.c more into the callbacks in struct drm_device, and the other is to link render/primary node code to the right sections in drm-uapi.rst. - Also moved the open/close stuff to drm_drv.h from drm-internals.rst, seems like the better place for that information. Since that section was rather outdated this amounted to full-on rewrite. A big missing piece here is some overview graph, but I think better to wait with that one until drm_device and drm_driver are also fully documented. v2: Nits from Sean. Reviewed-by: Sean Paul Reviewed-by: Liviu Dudau Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-12-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-internals.rst | 52 +----- Documentation/gpu/drm-uapi.rst | 4 + drivers/gpu/drm/drm_file.c | 66 ++++--- include/drm/drm_drv.h | 81 +++++++- include/drm/drm_file.h | 275 ++++++++++++++++++++++++---- 5 files changed, 359 insertions(+), 119 deletions(-) diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index 3bb4d937cdfe..cbf8e52cd9bb 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -243,61 +243,15 @@ drivers. Open/Close, File Operations and IOCTLs ====================================== -Open and Close --------------- - -Open and close handlers. None of those methods are mandatory:: - - int (*firstopen) (struct drm_device *); - void (*lastclose) (struct drm_device *); - int (*open) (struct drm_device *, struct drm_file *); - void (*preclose) (struct drm_device *, struct drm_file *); - void (*postclose) (struct drm_device *, struct drm_file *); - -The firstopen method is called by the DRM core for legacy UMS (User Mode -Setting) drivers only when an application opens a device that has no -other opened file handle. UMS drivers can implement it to acquire device -resources. KMS drivers can't use the method and must acquire resources -in the load method instead. - -Similarly the lastclose method is called when the last application -holding a file handle opened on the device closes it, for both UMS and -KMS drivers. Additionally, the method is also called at module unload -time or, for hot-pluggable devices, when the device is unplugged. The -firstopen and lastclose calls can thus be unbalanced. - -The open method is called every time the device is opened by an -application. Drivers can allocate per-file private data in this method -and store them in the struct :c:type:`struct drm_file -` driver_priv field. Note that the open method is -called before firstopen. - -The close operation is split into preclose and postclose methods. -Drivers must stop and cleanup all per-file operations in the preclose -method. For instance pending vertical blanking and page flip events must -be cancelled. No per-file operation is allowed on the file handle after -returning from the preclose method. - -Finally the postclose method is called as the last step of the close -operation, right before calling the lastclose method if no other open -file handle exists for the device. Drivers that have allocated per-file -private data in the open method should free it here. - -The lastclose method should restore CRTC and plane properties to default -value, so that a subsequent open of the device will not inherit state -from the previous user. It can also be used to execute delayed power -switching state changes, e.g. in conjunction with the :ref:`vga_switcheroo` -infrastructure. Beyond that KMS drivers should not do any -further cleanup. Only legacy UMS drivers might need to clean up device -state so that the vga console or an independent fbdev driver could take -over. - File Operations --------------- .. kernel-doc:: drivers/gpu/drm/drm_file.c :doc: file operations +.. kernel-doc:: include/drm/drm_file.h + :internal: + .. kernel-doc:: drivers/gpu/drm/drm_file.c :export: diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst index fcc228ef5bc4..352652810dab 100644 --- a/Documentation/gpu/drm-uapi.rst +++ b/Documentation/gpu/drm-uapi.rst @@ -21,6 +21,8 @@ libdrm Device Lookup :doc: getunique and setversion story +.. _drm_primary_node: + Primary Nodes, DRM Master and Authentication ============================================ @@ -103,6 +105,8 @@ is already rather painful for the DRM subsystem, with multiple different uAPIs for the same thing co-existing. If we add a few more complete mistakes into the mix every year it would be entirely unmanageable. +.. _drm_render_node: + Render nodes ============ diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index d9e63d73d3ec..92848fe75ea6 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -50,12 +50,14 @@ DEFINE_MUTEX(drm_global_mutex); * * Drivers must define the file operations structure that forms the DRM * userspace API entry point, even though most of those operations are - * implemented in the DRM core. The mandatory functions are drm_open(), + * implemented in the DRM core. The resulting &struct file_operations must be + * stored in the &drm_driver.fops field. The mandatory functions are drm_open(), * drm_read(), drm_ioctl() and drm_compat_ioctl() if CONFIG_COMPAT is enabled - * (note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n). Drivers which - * implement private ioctls that require 32/64 bit compatibility support must - * provide their own .compat_ioctl() handler that processes private ioctls and - * calls drm_compat_ioctl() for core ioctls. + * Note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n, so there's no + * need to sprinkle #ifdef into the code. Drivers which implement private ioctls + * that require 32/64 bit compatibility support must provide their own + * &file_operations.compat_ioctl handler that processes private ioctls and calls + * drm_compat_ioctl() for core ioctls. * * In addition drm_read() and drm_poll() provide support for DRM events. DRM * events are a generic and extensible means to send asynchronous events to @@ -63,10 +65,14 @@ DEFINE_MUTEX(drm_global_mutex); * page flip completions by the KMS API. But drivers can also use it for their * own needs, e.g. to signal completion of rendering. * + * For the driver-side event interface see drm_event_reserve_init() and + * drm_send_event() as the main starting points. + * * The memory mapping implementation will vary depending on how the driver * manages memory. Legacy drivers will use the deprecated drm_legacy_mmap() * function, modern drivers should use one of the provided memory-manager - * specific implementations. For GEM-based drivers this is drm_gem_mmap(). + * specific implementations. For GEM-based drivers this is drm_gem_mmap(), and + * for drivers which use the CMA GEM helpers it's drm_gem_cma_mmap(). * * No other file operations are supported by the DRM userspace API. Overall the * following is an example #file_operations structure:: @@ -82,6 +88,9 @@ DEFINE_MUTEX(drm_global_mutex); * .llseek = no_llseek, * .mmap = drm_gem_mmap, * }; + * + * FIXME: We should have a macro for this (and the CMA version) so that drivers + * don't have to repeat it all the time. */ static int drm_open_helper(struct file *filp, struct drm_minor *minor); @@ -111,9 +120,9 @@ static int drm_setup(struct drm_device * dev) * @inode: device inode * @filp: file pointer. * - * This function must be used by drivers as their .open() #file_operations - * method. It looks up the correct DRM device and instantiates all the per-file - * resources for it. + * This function must be used by drivers as their &file_operations.open method. + * It looks up the correct DRM device and instantiates all the per-file + * resources for it. It also calls the &drm_driver.open driver callback. * * RETURNS: * @@ -298,11 +307,6 @@ static void drm_events_release(struct drm_file *file_priv) spin_unlock_irqrestore(&dev->event_lock, flags); } -/* - * drm_legacy_dev_reinit - * - * Reinitializes a legacy/ums drm device in it's lastclose function. - */ static void drm_legacy_dev_reinit(struct drm_device *dev) { if (dev->irq_enabled) @@ -327,15 +331,6 @@ static void drm_legacy_dev_reinit(struct drm_device *dev) DRM_DEBUG("lastclose completed\n"); } -/* - * Take down the DRM device. - * - * \param dev DRM device structure. - * - * Frees every resource in \p dev. - * - * \sa drm_device - */ void drm_lastclose(struct drm_device * dev) { DRM_DEBUG("\n"); @@ -353,9 +348,11 @@ void drm_lastclose(struct drm_device * dev) * @inode: device inode * @filp: file pointer. * - * This function must be used by drivers as their .release() #file_operations - * method. It frees any resources associated with the open file, and if this is - * the last open file for the DRM device also proceeds to call drm_lastclose(). + * This function must be used by drivers as their &file_operations.release + * method. It frees any resources associated with the open file, and calls the + * &drm_driver.preclose and &drm_driver.lastclose driver callbacks. If this is + * the last open file for the DRM device also proceeds to call the + * &drm_driver.lastclose driver callback. * * RETURNS: * @@ -443,13 +440,13 @@ EXPORT_SYMBOL(drm_release); * @count: count in bytes to read * @offset: offset to read * - * This function must be used by drivers as their .read() #file_operations + * This function must be used by drivers as their &file_operations.read * method iff they use DRM events for asynchronous signalling to userspace. * Since events are used by the KMS API for vblank and page flip completion this * means all modern display drivers must use it. * - * @offset is ignore, DRM events are read like a pipe. Therefore drivers also - * must set the .llseek() #file_operation to no_llseek(). Polling support is + * @offset is ignored, DRM events are read like a pipe. Therefore drivers also + * must set the &file_operation.llseek to no_llseek(). Polling support is * provided by drm_poll(). * * This function will only ever read a full event. Therefore userspace must @@ -537,10 +534,10 @@ EXPORT_SYMBOL(drm_read); * @filp: file pointer * @wait: poll waiter table * - * This function must be used by drivers as their .read() #file_operations - * method iff they use DRM events for asynchronous signalling to userspace. - * Since events are used by the KMS API for vblank and page flip completion this - * means all modern display drivers must use it. + * This function must be used by drivers as their &file_operations.read method + * iff they use DRM events for asynchronous signalling to userspace. Since + * events are used by the KMS API for vblank and page flip completion this means + * all modern display drivers must use it. * * See also drm_read(). * @@ -650,7 +647,8 @@ EXPORT_SYMBOL(drm_event_reserve_init); * @p: tracking structure for the pending event * * This function frees the event @p initialized with drm_event_reserve_init() - * and releases any allocated space. + * and releases any allocated space. It is used to cancel an event when the + * nonblocking operation could not be submitted and needed to be aborted. */ void drm_event_cancel_free(struct drm_device *dev, struct drm_pending_event *p) diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 4321d012c4ba..8f900fb30275 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -64,7 +64,6 @@ struct drm_mode_create_dumb; * structure for GEM drivers. */ struct drm_driver { - /** * @load: * @@ -76,14 +75,94 @@ struct drm_driver { * See drm_dev_init() and drm_dev_register() for proper and * race-free way to set up a &struct drm_device. * + * This is deprecated, do not use! + * * Returns: * * Zero on success, non-zero value on failure. */ int (*load) (struct drm_device *, unsigned long flags); + + /** + * @open: + * + * Driver callback when a new &struct drm_file is opened. Useful for + * setting up driver-private data structures like buffer allocators, + * execution contexts or similar things. Such driver-private resources + * must be released again in @postclose. + * + * Since the display/modeset side of DRM can only be owned by exactly + * one &struct drm_file (see &drm_file.is_master and &drm_device.master) + * there should never be a need to set up any modeset related resources + * in this callback. Doing so would be a driver design bug. + * + * Returns: + * + * 0 on success, a negative error code on failure, which will be + * promoted to userspace as the result of the open() system call. + */ int (*open) (struct drm_device *, struct drm_file *); + + /** + * @preclose: + * + * One of the driver callbacks when a new &struct drm_file is closed. + * Useful for tearing down driver-private data structures allocated in + * @open like buffer allocators, execution contexts or similar things. + * + * Since the display/modeset side of DRM can only be owned by exactly + * one &struct drm_file (see &drm_file.is_master and &drm_device.master) + * there should never be a need to tear down any modeset related + * resources in this callback. Doing so would be a driver design bug. + * + * FIXME: It is not really clear why there's both @preclose and + * @postclose. Without a really good reason, use @postclose only. + */ void (*preclose) (struct drm_device *, struct drm_file *file_priv); + + /** + * @postclose: + * + * One of the driver callbacks when a new &struct drm_file is closed. + * Useful for tearing down driver-private data structures allocated in + * @open like buffer allocators, execution contexts or similar things. + * + * Since the display/modeset side of DRM can only be owned by exactly + * one &struct drm_file (see &drm_file.is_master and &drm_device.master) + * there should never be a need to tear down any modeset related + * resources in this callback. Doing so would be a driver design bug. + * + * FIXME: It is not really clear why there's both @preclose and + * @postclose. Without a really good reason, use @postclose only. + */ void (*postclose) (struct drm_device *, struct drm_file *); + + /** + * @lastclose: + * + * Called when the last &struct drm_file has been closed and there's + * currently no userspace client for the &struct drm_device. + * + * Modern drivers should only use this to force-restore the fbdev + * framebuffer using drm_fb_helper_restore_fbdev_mode_unlocked(). + * Anything else would indicate there's something seriously wrong. + * Modern drivers can also use this to execute delayed power switching + * state changes, e.g. in conjunction with the :ref:`vga_switcheroo` + * infrastructure. + * + * This is called after @preclose and @postclose have been called. + * + * NOTE: + * + * All legacy drivers use this callback to de-initialize the hardware. + * This is purely because of the shadow-attach model, where the DRM + * kernel driver does not really own the hardware. Instead ownershipe is + * handled with the help of userspace through an inheritedly racy dance + * to set/unset the VT into raw mode. + * + * Legacy drivers initialize the hardware in the @firstopen callback, + * which isn't even called for modern drivers. + */ void (*lastclose) (struct drm_device *); /** diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 4e347399a7bd..5dd27ae5c47c 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -45,6 +45,7 @@ struct drm_device; * FIXME: Not sure we want to have drm_minor here in the end, but to avoid * header include loops we need it here for now. */ + enum drm_minor_type { DRM_MINOR_PRIMARY, DRM_MINOR_CONTROL, @@ -52,12 +53,19 @@ enum drm_minor_type { }; /** - * DRM minor structure. This structure represents a drm minor number. + * struct drm_minor - DRM device minor structure + * + * This structure represents a DRM minor number for device nodes in /dev. + * Entirely opaque to drivers and should never be inspected directly by drivers. + * Drivers instead should only interact with &struct drm_file and of course + * &struct drm_device, which is also where driver-private data and resources can + * be attached to. */ struct drm_minor { - int index; /**< Minor device number */ - int type; /**< Control or render */ - struct device *kdev; /**< Linux device */ + /* private: */ + int index; /* Minor device number */ + int type; /* Control or render */ + struct device *kdev; /* Linux device */ struct drm_device *dev; struct dentry *debugfs_root; @@ -66,89 +74,286 @@ struct drm_minor { struct mutex debugfs_lock; /* Protects debugfs_list. */ }; -/* Event queued up for userspace to read */ +/** + * struct drm_pending_event - Event queued up for userspace to read + * + * This represents a DRM event. Drivers can use this as a generic completion + * mechanism, which supports kernel-internal &struct completion, &struct dma_fence + * and also the DRM-specific &struct drm_event delivery mechanism. + */ struct drm_pending_event { + /** + * @completion: + * + * Optional pointer to a kernel internal completion signalled when + * drm_send_event() is called, useful to internally synchronize with + * nonblocking operations. + */ struct completion *completion; + + /** + * @completion_release: + * + * Optional callback currently only used by the atomic modeset helpers + * to clean up the reference count for the structure @completion is + * stored in. + */ void (*completion_release)(struct completion *completion); + + /** + * @event: + * + * Pointer to the actual event that should be sent to userspace to be + * read using drm_read(). Can be optional, since nowadays events are + * also used to signal kernel internal threads with @completion or DMA + * transactions using @fence. + */ struct drm_event *event; + + /** + * @fence: + * + * Optional DMA fence to unblock other hardware transactions which + * depend upon the nonblocking DRM operation this event represents. + */ struct dma_fence *fence; - struct list_head link; - struct list_head pending_link; + + /** + * @file_priv: + * + * &struct drm_file where @event should be delivered to. Only set when + * @event is set. + */ struct drm_file *file_priv; + + /** + * @link: + * + * Double-linked list to keep track of this event. Can be used by the + * driver up to the point when it calls drm_send_event(), after that + * this list entry is owned by the core for its own book-keeping. + */ + struct list_head link; + + /** + * @pending_link: + * + * Entry on &drm_file.pending_event_list, to keep track of all pending + * events for @file_priv, to allow correct unwinding of them when + * userspace closes the file before the event is delivered. + */ + struct list_head pending_link; }; -/** File private data */ +/** + * struct drm_file - DRM file private data + * + * This structure tracks DRM state per open file descriptor. + */ struct drm_file { + /** + * @authenticated: + * + * Whether the client is allowed to submit rendering, which for legacy + * nodes means it must be authenticated. + * + * See also the :ref:`section on primary nodes and authentication + * `. + */ unsigned authenticated :1; - /* true when the client has asked us to expose stereo 3D mode flags */ + + /** + * @stereo_allowed: + * + * True when the client has asked us to expose stereo 3D mode flags. + */ unsigned stereo_allowed :1; - /* - * true if client understands CRTC primary planes and cursor planes - * in the plane list + + /** + * @universal_planes: + * + * True if client understands CRTC primary planes and cursor planes + * in the plane list. Automatically set when @atomic is set. */ unsigned universal_planes:1; - /* true if client understands atomic properties */ + + /** @atomic: True if client understands atomic properties. */ unsigned atomic:1; - /* - * This client is the creator of @master. - * Protected by struct drm_device::master_mutex. + + /** + * @is_master: + * + * This client is the creator of @master. Protected by struct + * &drm_device.master_mutex. + * + * See also the :ref:`section on primary nodes and authentication + * `. */ unsigned is_master:1; - struct pid *pid; - drm_magic_t magic; - struct list_head lhead; - struct drm_minor *minor; - unsigned long lock_count; + /** + * @master: + * + * Master this node is currently associated with. Only relevant if + * drm_is_primary_client() returns true. Note that this only + * matches &drm_device.master if the master is the currently active one. + * + * See also @authentication and @is_master and the :ref:`section on + * primary nodes and authentication `. + */ + struct drm_master *master; - /** Mapping of mm object handles to object pointers. */ + /** @pid: Process that opened this file. */ + struct pid *pid; + + /** @magic: Authentication magic, see @authenticated. */ + drm_magic_t magic; + + /** + * @lhead: + * + * List of all open files of a DRM device, linked into + * &drm_device.filelist. Protected by &drm_device.filelist_mutex. + */ + struct list_head lhead; + + /** @minor: &struct drm_minor for this file. */ + struct drm_minor *minor; + + /** + * @object_idr: + * + * Mapping of mm object handles to object pointers. Used by the GEM + * subsystem. Protected by @table_lock. + */ struct idr object_idr; - /** Lock for synchronization of access to object_idr. */ + + /** @table_lock: Protects @object_idr. */ spinlock_t table_lock; + /** @filp: Pointer to the core file structure. */ struct file *filp; + + /** + * @driver_priv: + * + * Optional pointer for driver private data. Can be allocated in + * &drm_driver.open and should be freed in &drm_driver.postclose. + */ void *driver_priv; - struct drm_master *master; /* master this node is currently associated with - N.B. not always dev->master */ /** - * fbs - List of framebuffers associated with this file. + * @fbs: * - * Protected by fbs_lock. Note that the fbs list holds a reference on - * the fb object to prevent it from untimely disappearing. + * List of &struct drm_framebuffer associated with this file, using the + * &drm_framebuffer.filp_head entry. + * + * Protected by @fbs_lock. Note that the @fbs list holds a reference on + * the framebuffer object to prevent it from untimely disappearing. */ struct list_head fbs; + + /** @fbs_lock: Protects @fbs. */ struct mutex fbs_lock; - /** User-created blob properties; this retains a reference on the - * property. */ + /** + * @blobs: + * + * User-created blob properties; this retains a reference on the + * property. + * + * Protected by @drm_mode_config.blob_lock; + */ struct list_head blobs; + /** @event_wait: Waitqueue for new events added to @event_list. */ wait_queue_head_t event_wait; + + /** + * @pending_event_list: + * + * List of pending &struct drm_pending_event, used to clean up pending + * events in case this file gets closed before the event is signalled. + * Uses the &drm_pending_event.pending_link entry. + * + * Protect by &drm_device.event_lock. + */ struct list_head pending_event_list; + + /** + * @event_list: + * + * List of &struct drm_pending_event, ready for delivery to userspace + * through drm_read(). Uses the &drm_pending_event.link entry. + * + * Protect by &drm_device.event_lock. + */ struct list_head event_list; + + /** + * @event_space: + * + * Available event space to prevent userspace from + * exhausting kernel memory. Currently limited to the fairly arbitrary + * value of 4KB. + */ int event_space; + /** @event_read_lock: Serializes drm_read(). */ struct mutex event_read_lock; + /** + * @prime: + * + * Per-file buffer caches used by the PRIME buffer sharing code. + */ struct drm_prime_file_private prime; + + /* private: */ + unsigned long lock_count; /* DRI1 legacy lock count */ }; +/** + * drm_is_primary_client - is this an open file of the primary node + * @file_priv: DRM file + * + * Returns true if this is an open file of the primary node, i.e. + * &drm_file.minor of @file_priv is a primary minor. + * + * See also the :ref:`section on primary nodes and authentication + * `. + */ +static inline bool drm_is_primary_client(const struct drm_file *file_priv) +{ + return file_priv->minor->type == DRM_MINOR_PRIMARY; +} + +/** + * drm_is_render_client - is this an open file of the render node + * @file_priv: DRM file + * + * Returns true if this is an open file of the render node, i.e. + * &drm_file.minor of @file_priv is a render minor. + * + * See also the :ref:`section on render nodes `. + */ static inline bool drm_is_render_client(const struct drm_file *file_priv) { return file_priv->minor->type == DRM_MINOR_RENDER; } +/** + * drm_is_control_client - is this an open file of the control node + * @file_priv: DRM file + * + * Control nodes are deprecated and in the process of getting removed from the + * DRM userspace API. Do not ever use! + */ static inline bool drm_is_control_client(const struct drm_file *file_priv) { return file_priv->minor->type == DRM_MINOR_CONTROL; } -static inline bool drm_is_primary_client(const struct drm_file *file_priv) -{ - return file_priv->minor->type == DRM_MINOR_PRIMARY; -} - int drm_open(struct inode *inode, struct file *filp); ssize_t drm_read(struct file *filp, char __user *buffer, size_t count, loff_t *offset);