diff options
Diffstat (limited to 'drivers/gpu/drm/drm_file.c')
| -rw-r--r-- | drivers/gpu/drm/drm_file.c | 677 |
1 files changed, 482 insertions, 195 deletions
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 46f48f245eb5..be5e617ceb9f 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -31,21 +31,43 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#include <linux/anon_inodes.h> +#include <linux/dma-fence.h> +#include <linux/export.h> +#include <linux/file.h> +#include <linux/module.h> +#include <linux/pci.h> #include <linux/poll.h> #include <linux/slab.h> -#include <linux/module.h> +#include <linux/vga_switcheroo.h> -#include <drm/drm_client.h> +#include <drm/drm_client_event.h> +#include <drm/drm_drv.h> #include <drm/drm_file.h> -#include <drm/drmP.h> +#include <drm/drm_gem.h> +#include <drm/drm_print.h> +#include <drm/drm_debugfs.h> -#include "drm_legacy.h" -#include "drm_internal.h" #include "drm_crtc_internal.h" +#include "drm_internal.h" /* from BKL pushdown */ DEFINE_MUTEX(drm_global_mutex); +bool drm_dev_needs_global_mutex(struct drm_device *dev) +{ + /* + * The deprecated ->load callback must be called after the driver is + * already registered. This means such drivers rely on the BKL to make + * sure an open can't proceed until the driver is actually fully set up. + * Similar hilarity holds for the unload callback. + */ + if (dev->driver->load || dev->driver->unload) + return true; + + return false; +} + /** * DOC: file operations * @@ -70,10 +92,7 @@ DEFINE_MUTEX(drm_global_mutex); * 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(), and - * for drivers which use the CMA GEM helpers it's drm_gem_cma_mmap(). + * manages memory. For GEM-based drivers this is drm_gem_mmap(). * * No other file operations are supported by the DRM userspace API. Overall the * following is an example &file_operations structure:: @@ -86,12 +105,11 @@ DEFINE_MUTEX(drm_global_mutex); * .compat_ioctl = drm_compat_ioctl, // NULL if CONFIG_COMPAT=n * .poll = drm_poll, * .read = drm_read, - * .llseek = no_llseek, * .mmap = drm_gem_mmap, * }; * * For plain GEM based drivers there is the DEFINE_DRM_GEM_FOPS() macro, and for - * CMA based drivers there is the DEFINE_DRM_GEM_CMA_FOPS() macro to make this + * DMA based drivers there is the DEFINE_DRM_GEM_DMA_FOPS() macro to make this * simpler. * * The driver's &file_operations must be stored in &drm_driver.fops. @@ -100,8 +118,6 @@ DEFINE_MUTEX(drm_global_mutex); * :ref:`IOCTL support in the userland interfaces chapter<drm_driver_ioctl>`. */ -static int drm_open_helper(struct file *filp, struct drm_minor *minor); - /** * drm_file_alloc - allocate file context * @minor: minor to allocate on @@ -115,6 +131,7 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor); */ struct drm_file *drm_file_alloc(struct drm_minor *minor) { + static atomic64_t ident = ATOMIC64_INIT(0); struct drm_device *dev = minor->dev; struct drm_file *file; int ret; @@ -123,12 +140,13 @@ struct drm_file *drm_file_alloc(struct drm_minor *minor) if (!file) return ERR_PTR(-ENOMEM); - file->pid = get_pid(task_pid(current)); + /* Get a unique identifier for fdinfo: */ + file->client_id = atomic64_inc_return(&ident); + rcu_assign_pointer(file->pid, get_pid(task_tgid(current))); file->minor = minor; /* for compatibility root is always authenticated */ file->authenticated = capable(CAP_SYS_ADMIN); - file->lock_count = 0; INIT_LIST_HEAD(&file->lhead); INIT_LIST_HEAD(&file->fbs); @@ -139,7 +157,9 @@ struct drm_file *drm_file_alloc(struct drm_minor *minor) init_waitqueue_head(&file->event_wait); file->event_space = 4096; /* set aside 4k for event buffer */ + spin_lock_init(&file->master_lookup_lock); mutex_init(&file->event_read_lock); + mutex_init(&file->client_name_lock); if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_open(dev, file); @@ -147,8 +167,10 @@ struct drm_file *drm_file_alloc(struct drm_minor *minor) if (drm_core_check_feature(dev, DRIVER_SYNCOBJ)) drm_syncobj_open(file); - if (drm_core_check_feature(dev, DRIVER_PRIME)) - drm_prime_init_file_private(&file->prime); + drm_prime_init_file_private(&file->prime); + + if (!drm_core_check_feature(dev, DRIVER_COMPUTE_ACCEL)) + drm_debugfs_clients_add(file); if (dev->driver->open) { ret = dev->driver->open(dev, file); @@ -159,13 +181,16 @@ struct drm_file *drm_file_alloc(struct drm_minor *minor) return file; out_prime_destroy: - if (drm_core_check_feature(dev, DRIVER_PRIME)) - drm_prime_destroy_file_private(&file->prime); + drm_prime_destroy_file_private(&file->prime); if (drm_core_check_feature(dev, DRIVER_SYNCOBJ)) drm_syncobj_release(file); if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_release(dev, file); - put_pid(file->pid); + + if (!drm_core_check_feature(dev, DRIVER_COMPUTE_ACCEL)) + drm_debugfs_clients_remove(file); + + put_pid(rcu_access_pointer(file->pid)); kfree(file); return ERR_PTR(ret); @@ -204,9 +229,6 @@ static void drm_events_release(struct drm_file *file_priv) * before calling this. * * If NULL is passed, this is a no-op. - * - * RETURNS: - * 0 on success, or error code on failure. */ void drm_file_free(struct drm_file *file) { @@ -217,20 +239,13 @@ void drm_file_free(struct drm_file *file) dev = file->minor->dev; - DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", - task_pid_nr(current), - (long)old_encode_dev(file->minor->kdev->devt), - dev->open_count); - - if (drm_core_check_feature(dev, DRIVER_LEGACY) && - dev->driver->preclose) - dev->driver->preclose(dev, file); - - if (drm_core_check_feature(dev, DRIVER_LEGACY)) - drm_legacy_lock_release(dev, file->filp); + drm_dbg_core(dev, "comm=\"%s\", pid=%d, dev=0x%lx, open_count=%d\n", + current->comm, task_pid_nr(current), + (long)old_encode_dev(file->minor->kdev->devt), + atomic_read(&dev->open_count)); - if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) - drm_legacy_reclaim_buffers(dev, file); + if (!drm_core_check_feature(dev, DRIVER_COMPUTE_ACCEL)) + drm_debugfs_clients_remove(file); drm_events_release(file); @@ -245,90 +260,35 @@ void drm_file_free(struct drm_file *file) if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_release(dev, file); - drm_legacy_ctxbitmap_flush(dev, file); - if (drm_is_primary_client(file)) drm_master_release(file); if (dev->driver->postclose) dev->driver->postclose(dev, file); - if (drm_core_check_feature(dev, DRIVER_PRIME)) - drm_prime_destroy_file_private(&file->prime); + drm_prime_destroy_file_private(&file->prime); WARN_ON(!list_empty(&file->event_list)); - put_pid(file->pid); - kfree(file); -} - -static int drm_setup(struct drm_device * dev) -{ - int ret; - - if (dev->driver->firstopen && - drm_core_check_feature(dev, DRIVER_LEGACY)) { - ret = dev->driver->firstopen(dev); - if (ret != 0) - return ret; - } - - ret = drm_legacy_dma_setup(dev); - if (ret < 0) - return ret; + put_pid(rcu_access_pointer(file->pid)); + mutex_destroy(&file->client_name_lock); + kfree(file->client_name); - DRM_DEBUG("\n"); - return 0; + kfree(file); } -/** - * drm_open - open method for DRM file - * @inode: device inode - * @filp: file pointer. - * - * 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: - * - * 0 on success or negative errno value on falure. - */ -int drm_open(struct inode *inode, struct file *filp) +static void drm_close_helper(struct file *filp) { - struct drm_device *dev; - struct drm_minor *minor; - int retcode; - int need_setup = 0; - - minor = drm_minor_acquire(iminor(inode)); - if (IS_ERR(minor)) - return PTR_ERR(minor); - - dev = minor->dev; - if (!dev->open_count++) - need_setup = 1; - - /* share address_space across all char-devs of a single device */ - filp->f_mapping = dev->anon_inode->i_mapping; + struct drm_file *file_priv = filp->private_data; + struct drm_device *dev = file_priv->minor->dev; - retcode = drm_open_helper(filp, minor); - if (retcode) - goto err_undo; - if (need_setup) { - retcode = drm_setup(dev); - if (retcode) - goto err_undo; - } - return 0; + mutex_lock(&dev->filelist_mutex); + list_del(&file_priv->lhead); + mutex_unlock(&dev->filelist_mutex); -err_undo: - dev->open_count--; - drm_minor_release(minor); - return retcode; + drm_file_free(file_priv); } -EXPORT_SYMBOL(drm_open); /* * Check whether DRI will run on this CPU. @@ -344,7 +304,7 @@ static int drm_cpu_valid(void) } /* - * Called whenever a process opens /dev/drm. + * Called whenever a process opens a drm node * * \param filp file pointer. * \param minor acquired minor-object. @@ -353,7 +313,7 @@ static int drm_cpu_valid(void) * Creates and initializes a drm_file structure for the file private data in \p * filp and add it into the double linked list in \p dev. */ -static int drm_open_helper(struct file *filp, struct drm_minor *minor) +int drm_open_helper(struct file *filp, struct drm_minor *minor) { struct drm_device *dev = minor->dev; struct drm_file *priv; @@ -363,10 +323,14 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) return -EBUSY; /* No exclusive opens */ if (!drm_cpu_valid()) return -EINVAL; - if (dev->switch_power_state != DRM_SWITCH_POWER_ON && dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF) + if (dev->switch_power_state != DRM_SWITCH_POWER_ON && + dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF) + return -EINVAL; + if (WARN_ON_ONCE(!(filp->f_op->fop_flags & FOP_UNSIGNED_OFFSET))) return -EINVAL; - DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor->index); + drm_dbg_core(dev, "comm=\"%s\", pid=%d, minor=%d\n", + current->comm, task_pid_nr(current), minor->index); priv = drm_file_alloc(minor); if (IS_ERR(priv)) @@ -381,72 +345,70 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) } filp->private_data = priv; - filp->f_mode |= FMODE_UNSIGNED_OFFSET; priv->filp = filp; mutex_lock(&dev->filelist_mutex); list_add(&priv->lhead, &dev->filelist); mutex_unlock(&dev->filelist_mutex); -#ifdef __alpha__ - /* - * Default the hose - */ - if (!dev->hose) { - struct pci_dev *pci_dev; - pci_dev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL); - if (pci_dev) { - dev->hose = pci_dev->sysdata; - pci_dev_put(pci_dev); - } - if (!dev->hose) { - struct pci_bus *b = list_entry(pci_root_buses.next, - struct pci_bus, node); - if (b) - dev->hose = b->sysdata; - } - } -#endif - return 0; } -static void drm_legacy_dev_reinit(struct drm_device *dev) +/** + * drm_open - open method for DRM file + * @inode: device inode + * @filp: file pointer. + * + * 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: + * 0 on success or negative errno value on failure. + */ +int drm_open(struct inode *inode, struct file *filp) { - if (dev->irq_enabled) - drm_irq_uninstall(dev); + struct drm_device *dev; + struct drm_minor *minor; + int retcode; - mutex_lock(&dev->struct_mutex); + minor = drm_minor_acquire(&drm_minors_xa, iminor(inode)); + if (IS_ERR(minor)) + return PTR_ERR(minor); - drm_legacy_agp_clear(dev); + dev = minor->dev; + if (drm_dev_needs_global_mutex(dev)) + mutex_lock(&drm_global_mutex); + + atomic_fetch_inc(&dev->open_count); - drm_legacy_sg_cleanup(dev); - drm_legacy_vma_flush(dev); - drm_legacy_dma_takedown(dev); + /* share address_space across all char-devs of a single device */ + filp->f_mapping = dev->anon_inode->i_mapping; - mutex_unlock(&dev->struct_mutex); + retcode = drm_open_helper(filp, minor); + if (retcode) + goto err_undo; - dev->sigdata.lock = NULL; + if (drm_dev_needs_global_mutex(dev)) + mutex_unlock(&drm_global_mutex); - dev->context_flag = 0; - dev->last_context = 0; - dev->if_version = 0; + return 0; - DRM_DEBUG("lastclose completed\n"); +err_undo: + atomic_dec(&dev->open_count); + if (drm_dev_needs_global_mutex(dev)) + mutex_unlock(&drm_global_mutex); + drm_minor_release(minor); + return retcode; } +EXPORT_SYMBOL(drm_open); -void drm_lastclose(struct drm_device * dev) +static void drm_lastclose(struct drm_device *dev) { - DRM_DEBUG("\n"); - - if (dev->driver->lastclose) - dev->driver->lastclose(dev); - DRM_DEBUG("driver lastclose completed\n"); + drm_client_dev_restore(dev, false); - if (drm_core_check_feature(dev, DRIVER_LEGACY)) - drm_legacy_dev_reinit(dev); - - drm_client_dev_restore(dev); + if (dev_is_pci(dev->dev)) + vga_switcheroo_process_delayed_switch(); } /** @@ -455,12 +417,11 @@ void drm_lastclose(struct drm_device * dev) * @filp: file pointer. * * 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.postclose driver callback. If this is the last open file for the - * DRM device also proceeds to call the &drm_driver.lastclose driver callback. + * method. It frees any resources associated with the open file. If this + * is the last open file for the DRM device, it also restores the active + * in-kernel DRM client. * * RETURNS: - * * Always succeeds and returns 0. */ int drm_release(struct inode *inode, struct file *filp) @@ -469,28 +430,88 @@ int drm_release(struct inode *inode, struct file *filp) struct drm_minor *minor = file_priv->minor; struct drm_device *dev = minor->dev; - mutex_lock(&drm_global_mutex); + if (drm_dev_needs_global_mutex(dev)) + mutex_lock(&drm_global_mutex); + + drm_dbg_core(dev, "open_count = %d\n", atomic_read(&dev->open_count)); + + drm_close_helper(filp); + + if (atomic_dec_and_test(&dev->open_count)) + drm_lastclose(dev); + + if (drm_dev_needs_global_mutex(dev)) + mutex_unlock(&drm_global_mutex); + + drm_minor_release(minor); + + return 0; +} +EXPORT_SYMBOL(drm_release); - DRM_DEBUG("open_count = %d\n", dev->open_count); +void drm_file_update_pid(struct drm_file *filp) +{ + struct drm_device *dev; + struct pid *pid, *old; + /* + * Master nodes need to keep the original ownership in order for + * drm_master_check_perm to keep working correctly. (See comment in + * drm_auth.c.) + */ + if (filp->was_master) + return; + + pid = task_tgid(current); + + /* + * Quick unlocked check since the model is a single handover followed by + * exclusive repeated use. + */ + if (pid == rcu_access_pointer(filp->pid)) + return; + + dev = filp->minor->dev; mutex_lock(&dev->filelist_mutex); - list_del(&file_priv->lhead); + get_pid(pid); + old = rcu_replace_pointer(filp->pid, pid, 1); mutex_unlock(&dev->filelist_mutex); - drm_file_free(file_priv); + synchronize_rcu(); + put_pid(old); +} + +/** + * drm_release_noglobal - release method for DRM file + * @inode: device inode + * @filp: file pointer. + * + * This function may be used by drivers as their &file_operations.release + * method. It frees any resources associated with the open file prior to taking + * the drm_global_mutex. If this is the last open file for the DRM device, it + * then restores the active in-kernel DRM client. + * + * RETURNS: + * Always succeeds and returns 0. + */ +int drm_release_noglobal(struct inode *inode, struct file *filp) +{ + struct drm_file *file_priv = filp->private_data; + struct drm_minor *minor = file_priv->minor; + struct drm_device *dev = minor->dev; - if (!--dev->open_count) { + drm_close_helper(filp); + + if (atomic_dec_and_mutex_lock(&dev->open_count, &drm_global_mutex)) { drm_lastclose(dev); - if (drm_dev_is_unplugged(dev)) - drm_put_dev(dev); + mutex_unlock(&drm_global_mutex); } - mutex_unlock(&drm_global_mutex); drm_minor_release(minor); return 0; } -EXPORT_SYMBOL(drm_release); +EXPORT_SYMBOL(drm_release_noglobal); /** * drm_read - read method for DRM file @@ -500,12 +521,11 @@ EXPORT_SYMBOL(drm_release); * @offset: offset to read * * This function must be used by drivers as their &file_operations.read - * method iff they use DRM events for asynchronous signalling to userspace. + * method if 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 ignored, DRM events are read like a pipe. Therefore drivers also - * must set the &file_operation.llseek to no_llseek(). Polling support is + * @offset is ignored, DRM events are read like a pipe. Polling support is * provided by drm_poll(). * * This function will only ever read a full event. Therefore userspace must @@ -514,7 +534,6 @@ EXPORT_SYMBOL(drm_release); * safety. * * RETURNS: - * * Number of bytes read (always aligned to full events, and can be 0) or a * negative error code on failure. */ @@ -525,9 +544,6 @@ ssize_t drm_read(struct file *filp, char __user *buffer, struct drm_device *dev = file_priv->minor->dev; ssize_t ret; - if (!access_ok(buffer, count)) - return -EFAULT; - ret = mutex_lock_interruptible(&file_priv->event_read_lock); if (ret) return ret; @@ -569,6 +585,8 @@ put_back_event: file_priv->event_space -= length; list_add(&e->link, &file_priv->event_list); spin_unlock_irq(&dev->event_lock); + wake_up_interruptible_poll(&file_priv->event_wait, + EPOLLIN | EPOLLRDNORM); break; } @@ -594,14 +612,13 @@ EXPORT_SYMBOL(drm_read); * @wait: poll waiter table * * This function must be used by drivers as their &file_operations.read method - * iff they use DRM events for asynchronous signalling to userspace. Since + * if 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(). * * RETURNS: - * * Mask of POLL flags indicating the current status of the file. */ __poll_t drm_poll(struct file *filp, struct poll_table_struct *wait) @@ -639,7 +656,6 @@ EXPORT_SYMBOL(drm_poll); * already hold &drm_device.event_lock. * * RETURNS: - * * 0 on success or a negative error code on failure. */ int drm_event_reserve_init_locked(struct drm_device *dev, @@ -681,7 +697,6 @@ EXPORT_SYMBOL(drm_event_reserve_init_locked); * drm_event_reserve_init_locked() instead. * * RETURNS: - * * 0 on success or a negative error code on failure. */ int drm_event_reserve_init(struct drm_device *dev, @@ -701,7 +716,7 @@ int drm_event_reserve_init(struct drm_device *dev, EXPORT_SYMBOL(drm_event_reserve_init); /** - * drm_event_cancel_free - free a DRM event and release it's space + * drm_event_cancel_free - free a DRM event and release its space * @dev: DRM device * @p: tracking structure for the pending event * @@ -713,6 +728,7 @@ void drm_event_cancel_free(struct drm_device *dev, struct drm_pending_event *p) { unsigned long flags; + spin_lock_irqsave(&dev->event_lock, flags); if (p->file_priv) { p->file_priv->event_space += p->event->length; @@ -727,21 +743,8 @@ void drm_event_cancel_free(struct drm_device *dev, } EXPORT_SYMBOL(drm_event_cancel_free); -/** - * drm_send_event_locked - send DRM event to file descriptor - * @dev: DRM device - * @e: DRM event to deliver - * - * This function sends the event @e, initialized with drm_event_reserve_init(), - * to its associated userspace DRM file. Callers must already hold - * &drm_device.event_lock, see drm_send_event() for the unlocked version. - * - * Note that the core will take care of unlinking and disarming events when the - * corresponding DRM file is closed. Drivers need not worry about whether the - * DRM file for this event still exists and can call this function upon - * completion of the asynchronous work unconditionally. - */ -void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e) +static void drm_send_event_helper(struct drm_device *dev, + struct drm_pending_event *e, ktime_t timestamp) { assert_spin_locked(&dev->event_lock); @@ -752,7 +755,10 @@ void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e) } if (e->fence) { - dma_fence_signal(e->fence); + if (timestamp) + dma_fence_signal_timestamp(e->fence, timestamp); + else + dma_fence_signal(e->fence); dma_fence_put(e->fence); } @@ -764,7 +770,50 @@ void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e) list_del(&e->pending_link); list_add_tail(&e->link, &e->file_priv->event_list); - wake_up_interruptible(&e->file_priv->event_wait); + wake_up_interruptible_poll(&e->file_priv->event_wait, + EPOLLIN | EPOLLRDNORM); +} + +/** + * drm_send_event_timestamp_locked - send DRM event to file descriptor + * @dev: DRM device + * @e: DRM event to deliver + * @timestamp: timestamp to set for the fence event in kernel's CLOCK_MONOTONIC + * time domain + * + * This function sends the event @e, initialized with drm_event_reserve_init(), + * to its associated userspace DRM file. Callers must already hold + * &drm_device.event_lock. + * + * Note that the core will take care of unlinking and disarming events when the + * corresponding DRM file is closed. Drivers need not worry about whether the + * DRM file for this event still exists and can call this function upon + * completion of the asynchronous work unconditionally. + */ +void drm_send_event_timestamp_locked(struct drm_device *dev, + struct drm_pending_event *e, ktime_t timestamp) +{ + drm_send_event_helper(dev, e, timestamp); +} +EXPORT_SYMBOL(drm_send_event_timestamp_locked); + +/** + * drm_send_event_locked - send DRM event to file descriptor + * @dev: DRM device + * @e: DRM event to deliver + * + * This function sends the event @e, initialized with drm_event_reserve_init(), + * to its associated userspace DRM file. Callers must already hold + * &drm_device.event_lock, see drm_send_event() for the unlocked version. + * + * Note that the core will take care of unlinking and disarming events when the + * corresponding DRM file is closed. Drivers need not worry about whether the + * DRM file for this event still exists and can call this function upon + * completion of the asynchronous work unconditionally. + */ +void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e) +{ + drm_send_event_helper(dev, e, 0); } EXPORT_SYMBOL(drm_send_event_locked); @@ -788,7 +837,245 @@ void drm_send_event(struct drm_device *dev, struct drm_pending_event *e) unsigned long irqflags; spin_lock_irqsave(&dev->event_lock, irqflags); - drm_send_event_locked(dev, e); + drm_send_event_helper(dev, e, 0); spin_unlock_irqrestore(&dev->event_lock, irqflags); } EXPORT_SYMBOL(drm_send_event); + +void drm_fdinfo_print_size(struct drm_printer *p, + const char *prefix, + const char *stat, + const char *region, + u64 sz) +{ + const char *units[] = {"", " KiB", " MiB"}; + unsigned u; + + for (u = 0; u < ARRAY_SIZE(units) - 1; u++) { + if (sz == 0 || !IS_ALIGNED(sz, SZ_1K)) + break; + sz = div_u64(sz, SZ_1K); + } + + drm_printf(p, "%s-%s-%s:\t%llu%s\n", + prefix, stat, region, sz, units[u]); +} +EXPORT_SYMBOL(drm_fdinfo_print_size); + +int drm_memory_stats_is_zero(const struct drm_memory_stats *stats) +{ + return (stats->shared == 0 && + stats->private == 0 && + stats->resident == 0 && + stats->purgeable == 0 && + stats->active == 0); +} +EXPORT_SYMBOL(drm_memory_stats_is_zero); + +/** + * drm_print_memory_stats - A helper to print memory stats + * @p: The printer to print output to + * @stats: The collected memory stats + * @supported_status: Bitmask of optional stats which are available + * @region: The memory region + * + */ +void drm_print_memory_stats(struct drm_printer *p, + const struct drm_memory_stats *stats, + enum drm_gem_object_status supported_status, + const char *region) +{ + const char *prefix = "drm"; + + drm_fdinfo_print_size(p, prefix, "total", region, + stats->private + stats->shared); + drm_fdinfo_print_size(p, prefix, "shared", region, stats->shared); + + if (supported_status & DRM_GEM_OBJECT_ACTIVE) + drm_fdinfo_print_size(p, prefix, "active", region, stats->active); + + if (supported_status & DRM_GEM_OBJECT_RESIDENT) + drm_fdinfo_print_size(p, prefix, "resident", region, + stats->resident); + + if (supported_status & DRM_GEM_OBJECT_PURGEABLE) + drm_fdinfo_print_size(p, prefix, "purgeable", region, + stats->purgeable); +} +EXPORT_SYMBOL(drm_print_memory_stats); + +/** + * drm_show_memory_stats - Helper to collect and show standard fdinfo memory stats + * @p: the printer to print output to + * @file: the DRM file + * + * Helper to iterate over GEM objects with a handle allocated in the specified + * file. + */ +void drm_show_memory_stats(struct drm_printer *p, struct drm_file *file) +{ + struct drm_gem_object *obj; + struct drm_memory_stats status = {}; + enum drm_gem_object_status supported_status = 0; + int id; + + spin_lock(&file->table_lock); + idr_for_each_entry (&file->object_idr, obj, id) { + enum drm_gem_object_status s = 0; + size_t add_size = (obj->funcs && obj->funcs->rss) ? + obj->funcs->rss(obj) : obj->size; + + if (obj->funcs && obj->funcs->status) { + s = obj->funcs->status(obj); + supported_status |= s; + } + + if (drm_gem_object_is_shared_for_memory_stats(obj)) + status.shared += obj->size; + else + status.private += obj->size; + + if (s & DRM_GEM_OBJECT_RESIDENT) { + status.resident += add_size; + } else { + /* If already purged or not yet backed by pages, don't + * count it as purgeable: + */ + s &= ~DRM_GEM_OBJECT_PURGEABLE; + } + + if (!dma_resv_test_signaled(obj->resv, dma_resv_usage_rw(true))) { + status.active += add_size; + supported_status |= DRM_GEM_OBJECT_ACTIVE; + + /* If still active, don't count as purgeable: */ + s &= ~DRM_GEM_OBJECT_PURGEABLE; + } + + if (s & DRM_GEM_OBJECT_PURGEABLE) + status.purgeable += add_size; + } + spin_unlock(&file->table_lock); + + drm_print_memory_stats(p, &status, supported_status, "memory"); +} +EXPORT_SYMBOL(drm_show_memory_stats); + +/** + * drm_show_fdinfo - helper for drm file fops + * @m: output stream + * @f: the device file instance + * + * Helper to implement fdinfo, for userspace to query usage stats, etc, of a + * process using the GPU. See also &drm_driver.show_fdinfo. + * + * For text output format description please see Documentation/gpu/drm-usage-stats.rst + */ +void drm_show_fdinfo(struct seq_file *m, struct file *f) +{ + struct drm_file *file = f->private_data; + struct drm_device *dev = file->minor->dev; + struct drm_printer p = drm_seq_file_printer(m); + int idx; + + if (!drm_dev_enter(dev, &idx)) + return; + + drm_printf(&p, "drm-driver:\t%s\n", dev->driver->name); + drm_printf(&p, "drm-client-id:\t%llu\n", file->client_id); + + if (dev_is_pci(dev->dev)) { + struct pci_dev *pdev = to_pci_dev(dev->dev); + + drm_printf(&p, "drm-pdev:\t%04x:%02x:%02x.%d\n", + pci_domain_nr(pdev->bus), pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + } + + mutex_lock(&file->client_name_lock); + if (file->client_name) + drm_printf(&p, "drm-client-name:\t%s\n", file->client_name); + mutex_unlock(&file->client_name_lock); + + if (dev->driver->show_fdinfo) + dev->driver->show_fdinfo(&p, file); + + drm_dev_exit(idx); +} +EXPORT_SYMBOL(drm_show_fdinfo); + +/** + * drm_file_err - log process name, pid and client_name associated with a drm_file + * @file_priv: context of interest for process name and pid + * @fmt: printf() like format string + * + * Helper function for clients which needs to log process details such + * as name and pid etc along with user logs. + */ +void drm_file_err(struct drm_file *file_priv, const char *fmt, ...) +{ + va_list args; + struct va_format vaf; + struct pid *pid; + struct task_struct *task; + struct drm_device *dev = file_priv->minor->dev; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + mutex_lock(&file_priv->client_name_lock); + rcu_read_lock(); + pid = rcu_dereference(file_priv->pid); + task = pid_task(pid, PIDTYPE_TGID); + + drm_err(dev, "comm: %s pid: %d client-id:%llu client: %s ... %pV", + task ? task->comm : "Unset", + task ? task->pid : 0, file_priv->client_id, + file_priv->client_name ?: "Unset", &vaf); + + va_end(args); + rcu_read_unlock(); + mutex_unlock(&file_priv->client_name_lock); +} +EXPORT_SYMBOL(drm_file_err); + +/** + * mock_drm_getfile - Create a new struct file for the drm device + * @minor: drm minor to wrap (e.g. #drm_device.primary) + * @flags: file creation mode (O_RDWR etc) + * + * This create a new struct file that wraps a DRM file context around a + * DRM minor. This mimicks userspace opening e.g. /dev/dri/card0, but without + * invoking userspace. The struct file may be operated on using its f_op + * (the drm_device.driver.fops) to mimick userspace operations, or be supplied + * to userspace facing functions as an internal/anonymous client. + * + * RETURNS: + * Pointer to newly created struct file, ERR_PTR on failure. + */ +struct file *mock_drm_getfile(struct drm_minor *minor, unsigned int flags) +{ + struct drm_device *dev = minor->dev; + struct drm_file *priv; + struct file *file; + + priv = drm_file_alloc(minor); + if (IS_ERR(priv)) + return ERR_CAST(priv); + + file = anon_inode_getfile("drm", dev->driver->fops, priv, flags); + if (IS_ERR(file)) { + drm_file_free(priv); + return file; + } + + /* Everyone shares a single global address space */ + file->f_mapping = dev->anon_inode->i_mapping; + + drm_dev_get(dev); + priv->filp = file; + + return file; +} +EXPORT_SYMBOL_FOR_TESTS_ONLY(mock_drm_getfile); |
