diff options
Diffstat (limited to 'drivers/gpu/drm/drm_file.c')
| -rw-r--r-- | drivers/gpu/drm/drm_file.c | 366 |
1 files changed, 269 insertions, 97 deletions
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index a51ff8cee049..be5e617ceb9f 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -33,20 +33,23 @@ #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/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/drm_gem.h> #include <drm/drm_print.h> +#include <drm/drm_debugfs.h> #include "drm_crtc_internal.h" #include "drm_internal.h" -#include "drm_legacy.h" /* from BKL pushdown */ DEFINE_MUTEX(drm_global_mutex); @@ -54,14 +57,6 @@ DEFINE_MUTEX(drm_global_mutex); bool drm_dev_needs_global_mutex(struct drm_device *dev) { /* - * Legacy drivers rely on all kinds of BKL locking semantics, don't - * bother. They also still need BKL locking for their ioctls, so better - * safe than sorry. - */ - if (drm_core_check_feature(dev, DRIVER_LEGACY)) - return true; - - /* * 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. @@ -70,15 +65,6 @@ bool drm_dev_needs_global_mutex(struct drm_device *dev) if (dev->driver->load || dev->driver->unload) return true; - /* - * Drivers with the lastclose callback assume that it's synchronized - * against concurrent opens, which again needs the BKL. The proper fix - * is to use the drm_client infrastructure with proper locking for each - * client. - */ - if (dev->driver->lastclose) - return true; - return false; } @@ -106,9 +92,7 @@ bool drm_dev_needs_global_mutex(struct drm_device *dev) * 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(). + * 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:: @@ -121,7 +105,6 @@ bool drm_dev_needs_global_mutex(struct drm_device *dev) * .compat_ioctl = drm_compat_ioctl, // NULL if CONFIG_COMPAT=n * .poll = drm_poll, * .read = drm_read, - * .llseek = no_llseek, * .mmap = drm_gem_mmap, * }; * @@ -148,6 +131,7 @@ bool drm_dev_needs_global_mutex(struct drm_device *dev) */ 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; @@ -156,7 +140,9 @@ 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 */ @@ -173,6 +159,7 @@ struct drm_file *drm_file_alloc(struct drm_minor *minor) 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); @@ -182,6 +169,9 @@ struct drm_file *drm_file_alloc(struct drm_minor *minor) 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); if (ret < 0) @@ -196,7 +186,11 @@ out_prime_destroy: 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); @@ -250,17 +244,8 @@ void drm_file_free(struct drm_file *file) (long)old_encode_dev(file->minor->kdev->devt), atomic_read(&dev->open_count)); -#ifdef CONFIG_DRM_LEGACY - if (drm_core_check_feature(dev, DRIVER_LEGACY) && - dev->driver->preclose) - dev->driver->preclose(dev, file); -#endif - - if (drm_core_check_feature(dev, DRIVER_LEGACY)) - drm_legacy_lock_release(dev, file->filp); - - 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); @@ -275,8 +260,6 @@ 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); @@ -287,7 +270,11 @@ void drm_file_free(struct drm_file *file) WARN_ON(!list_empty(&file->event_list)); - put_pid(file->pid); + put_pid(rcu_access_pointer(file->pid)); + + mutex_destroy(&file->client_name_lock); + kfree(file->client_name); + kfree(file); } @@ -339,6 +326,8 @@ int drm_open_helper(struct file *filp, struct drm_minor *minor) 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_dbg_core(dev, "comm=\"%s\", pid=%d, minor=%d\n", current->comm, task_pid_nr(current), minor->index); @@ -356,36 +345,12 @@ 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 CONFIG_DRM_LEGACY -#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 -#endif - return 0; } @@ -399,7 +364,6 @@ int drm_open_helper(struct file *filp, struct drm_minor *minor) * 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) @@ -407,9 +371,8 @@ int drm_open(struct inode *inode, struct file *filp) struct drm_device *dev; struct drm_minor *minor; int retcode; - int need_setup = 0; - minor = drm_minor_acquire(iminor(inode)); + minor = drm_minor_acquire(&drm_minors_xa, iminor(inode)); if (IS_ERR(minor)) return PTR_ERR(minor); @@ -417,8 +380,7 @@ int drm_open(struct inode *inode, struct file *filp) if (drm_dev_needs_global_mutex(dev)) mutex_lock(&drm_global_mutex); - if (!atomic_fetch_inc(&dev->open_count)) - need_setup = 1; + atomic_fetch_inc(&dev->open_count); /* share address_space across all char-devs of a single device */ filp->f_mapping = dev->anon_inode->i_mapping; @@ -426,13 +388,6 @@ int drm_open(struct inode *inode, struct file *filp) retcode = drm_open_helper(filp, minor); if (retcode) goto err_undo; - if (need_setup) { - retcode = drm_legacy_setup(dev); - if (retcode) { - drm_close_helper(filp); - goto err_undo; - } - } if (drm_dev_needs_global_mutex(dev)) mutex_unlock(&drm_global_mutex); @@ -448,18 +403,12 @@ err_undo: } EXPORT_SYMBOL(drm_open); -void drm_lastclose(struct drm_device * dev) +static void drm_lastclose(struct drm_device *dev) { - drm_dbg_core(dev, "\n"); + drm_client_dev_restore(dev, false); - if (dev->driver->lastclose) - dev->driver->lastclose(dev); - drm_dbg_core(dev, "driver lastclose completed\n"); - - 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(); } /** @@ -468,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) @@ -501,6 +449,38 @@ int drm_release(struct inode *inode, struct file *filp) } EXPORT_SYMBOL(drm_release); +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); + get_pid(pid); + old = rcu_replace_pointer(filp->pid, pid, 1); + mutex_unlock(&dev->filelist_mutex); + + synchronize_rcu(); + put_pid(old); +} + /** * drm_release_noglobal - release method for DRM file * @inode: device inode @@ -508,12 +488,10 @@ EXPORT_SYMBOL(drm_release); * * 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, which then 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. + * 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) @@ -556,7 +534,6 @@ EXPORT_SYMBOL(drm_release_noglobal); * safety. * * RETURNS: - * * Number of bytes read (always aligned to full events, and can be 0) or a * negative error code on failure. */ @@ -642,7 +619,6 @@ EXPORT_SYMBOL(drm_read); * 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) @@ -680,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, @@ -722,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, @@ -868,6 +842,204 @@ void drm_send_event(struct drm_device *dev, struct drm_pending_event *e) } 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) |
