162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. 562306a36Sopenharmony_ci * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 662306a36Sopenharmony_ci * All Rights Reserved. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author Rickard E. (Rik) Faith <faith@valinux.com> 962306a36Sopenharmony_ci * Author Gareth Hughes <gareth@valinux.com> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 1262306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 1362306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 1462306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 1562306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 1662306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next 1962306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 2062306a36Sopenharmony_ci * Software. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2362306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2462306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 2562306a36Sopenharmony_ci * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 2662306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 2762306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2862306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <linux/slab.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <drm/drm_auth.h> 3462306a36Sopenharmony_ci#include <drm/drm_drv.h> 3562306a36Sopenharmony_ci#include <drm/drm_file.h> 3662306a36Sopenharmony_ci#include <drm/drm_lease.h> 3762306a36Sopenharmony_ci#include <drm/drm_print.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include "drm_internal.h" 4062306a36Sopenharmony_ci#include "drm_legacy.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/** 4362306a36Sopenharmony_ci * DOC: master and authentication 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * &struct drm_master is used to track groups of clients with open 4662306a36Sopenharmony_ci * primary/legacy device nodes. For every &struct drm_file which has had at 4762306a36Sopenharmony_ci * least once successfully became the device master (either through the 4862306a36Sopenharmony_ci * SET_MASTER IOCTL, or implicitly through opening the primary device node when 4962306a36Sopenharmony_ci * no one else is the current master that time) there exists one &drm_master. 5062306a36Sopenharmony_ci * This is noted in &drm_file.is_master. All other clients have just a pointer 5162306a36Sopenharmony_ci * to the &drm_master they are associated with. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * In addition only one &drm_master can be the current master for a &drm_device. 5462306a36Sopenharmony_ci * It can be switched through the DROP_MASTER and SET_MASTER IOCTL, or 5562306a36Sopenharmony_ci * implicitly through closing/opening the primary device node. See also 5662306a36Sopenharmony_ci * drm_is_current_master(). 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * Clients can authenticate against the current master (if it matches their own) 5962306a36Sopenharmony_ci * using the GETMAGIC and AUTHMAGIC IOCTLs. Together with exchanging masters, 6062306a36Sopenharmony_ci * this allows controlled access to the device for an entire group of mutually 6162306a36Sopenharmony_ci * trusted clients. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic bool drm_is_current_master_locked(struct drm_file *fpriv) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci lockdep_assert_once(lockdep_is_held(&fpriv->master_lookup_lock) || 6762306a36Sopenharmony_ci lockdep_is_held(&fpriv->minor->dev->master_mutex)); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/** 7362306a36Sopenharmony_ci * drm_is_current_master - checks whether @priv is the current master 7462306a36Sopenharmony_ci * @fpriv: DRM file private 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * Checks whether @fpriv is current master on its device. This decides whether a 7762306a36Sopenharmony_ci * client is allowed to run DRM_MASTER IOCTLs. 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * Most of the modern IOCTL which require DRM_MASTER are for kernel modesetting 8062306a36Sopenharmony_ci * - the current master is assumed to own the non-shareable display hardware. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cibool drm_is_current_master(struct drm_file *fpriv) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci bool ret; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci spin_lock(&fpriv->master_lookup_lock); 8762306a36Sopenharmony_ci ret = drm_is_current_master_locked(fpriv); 8862306a36Sopenharmony_ci spin_unlock(&fpriv->master_lookup_lock); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return ret; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_is_current_master); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ciint drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct drm_auth *auth = data; 9762306a36Sopenharmony_ci int ret = 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci mutex_lock(&dev->master_mutex); 10062306a36Sopenharmony_ci if (!file_priv->magic) { 10162306a36Sopenharmony_ci ret = idr_alloc(&file_priv->master->magic_map, file_priv, 10262306a36Sopenharmony_ci 1, 0, GFP_KERNEL); 10362306a36Sopenharmony_ci if (ret >= 0) 10462306a36Sopenharmony_ci file_priv->magic = ret; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci auth->magic = file_priv->magic; 10762306a36Sopenharmony_ci mutex_unlock(&dev->master_mutex); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci drm_dbg_core(dev, "%u\n", auth->magic); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return ret < 0 ? ret : 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ciint drm_authmagic(struct drm_device *dev, void *data, 11562306a36Sopenharmony_ci struct drm_file *file_priv) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct drm_auth *auth = data; 11862306a36Sopenharmony_ci struct drm_file *file; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci drm_dbg_core(dev, "%u\n", auth->magic); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci mutex_lock(&dev->master_mutex); 12362306a36Sopenharmony_ci file = idr_find(&file_priv->master->magic_map, auth->magic); 12462306a36Sopenharmony_ci if (file) { 12562306a36Sopenharmony_ci file->authenticated = 1; 12662306a36Sopenharmony_ci idr_replace(&file_priv->master->magic_map, NULL, auth->magic); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci mutex_unlock(&dev->master_mutex); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return file ? 0 : -EINVAL; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistruct drm_master *drm_master_create(struct drm_device *dev) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct drm_master *master; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci master = kzalloc(sizeof(*master), GFP_KERNEL); 13862306a36Sopenharmony_ci if (!master) 13962306a36Sopenharmony_ci return NULL; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci kref_init(&master->refcount); 14262306a36Sopenharmony_ci drm_master_legacy_init(master); 14362306a36Sopenharmony_ci idr_init_base(&master->magic_map, 1); 14462306a36Sopenharmony_ci master->dev = dev; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* initialize the tree of output resource lessees */ 14762306a36Sopenharmony_ci INIT_LIST_HEAD(&master->lessees); 14862306a36Sopenharmony_ci INIT_LIST_HEAD(&master->lessee_list); 14962306a36Sopenharmony_ci idr_init(&master->leases); 15062306a36Sopenharmony_ci idr_init_base(&master->lessee_idr, 1); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return master; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void drm_set_master(struct drm_device *dev, struct drm_file *fpriv, 15662306a36Sopenharmony_ci bool new_master) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci dev->master = drm_master_get(fpriv->master); 15962306a36Sopenharmony_ci if (dev->driver->master_set) 16062306a36Sopenharmony_ci dev->driver->master_set(dev, fpriv, new_master); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci fpriv->was_master = true; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct drm_master *old_master; 16862306a36Sopenharmony_ci struct drm_master *new_master; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci lockdep_assert_held_once(&dev->master_mutex); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci WARN_ON(fpriv->is_master); 17362306a36Sopenharmony_ci old_master = fpriv->master; 17462306a36Sopenharmony_ci new_master = drm_master_create(dev); 17562306a36Sopenharmony_ci if (!new_master) 17662306a36Sopenharmony_ci return -ENOMEM; 17762306a36Sopenharmony_ci spin_lock(&fpriv->master_lookup_lock); 17862306a36Sopenharmony_ci fpriv->master = new_master; 17962306a36Sopenharmony_ci spin_unlock(&fpriv->master_lookup_lock); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci fpriv->is_master = 1; 18262306a36Sopenharmony_ci fpriv->authenticated = 1; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci drm_set_master(dev, fpriv, true); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (old_master) 18762306a36Sopenharmony_ci drm_master_put(&old_master); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* 19362306a36Sopenharmony_ci * In the olden days the SET/DROP_MASTER ioctls used to return EACCES when 19462306a36Sopenharmony_ci * CAP_SYS_ADMIN was not set. This was used to prevent rogue applications 19562306a36Sopenharmony_ci * from becoming master and/or failing to release it. 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * At the same time, the first client (for a given VT) is _always_ master. 19862306a36Sopenharmony_ci * Thus in order for the ioctls to succeed, one had to _explicitly_ run the 19962306a36Sopenharmony_ci * application as root or flip the setuid bit. 20062306a36Sopenharmony_ci * 20162306a36Sopenharmony_ci * If the CAP_SYS_ADMIN was missing, no other client could become master... 20262306a36Sopenharmony_ci * EVER :-( Leading to a) the graphics session dying badly or b) a completely 20362306a36Sopenharmony_ci * locked session. 20462306a36Sopenharmony_ci * 20562306a36Sopenharmony_ci * 20662306a36Sopenharmony_ci * As some point systemd-logind was introduced to orchestrate and delegate 20762306a36Sopenharmony_ci * master as applicable. It does so by opening the fd and passing it to users 20862306a36Sopenharmony_ci * while in itself logind a) does the set/drop master per users' request and 20962306a36Sopenharmony_ci * b) * implicitly drops master on VT switch. 21062306a36Sopenharmony_ci * 21162306a36Sopenharmony_ci * Even though logind looks like the future, there are a few issues: 21262306a36Sopenharmony_ci * - some platforms don't have equivalent (Android, CrOS, some BSDs) so 21362306a36Sopenharmony_ci * root is required _solely_ for SET/DROP MASTER. 21462306a36Sopenharmony_ci * - applications may not be updated to use it, 21562306a36Sopenharmony_ci * - any client which fails to drop master* can DoS the application using 21662306a36Sopenharmony_ci * logind, to a varying degree. 21762306a36Sopenharmony_ci * 21862306a36Sopenharmony_ci * * Either due missing CAP_SYS_ADMIN or simply not calling DROP_MASTER. 21962306a36Sopenharmony_ci * 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * Here we implement the next best thing: 22262306a36Sopenharmony_ci * - ensure the logind style of fd passing works unchanged, and 22362306a36Sopenharmony_ci * - allow a client to drop/set master, iff it is/was master at a given point 22462306a36Sopenharmony_ci * in time. 22562306a36Sopenharmony_ci * 22662306a36Sopenharmony_ci * Note: DROP_MASTER cannot be free for all, as an arbitrator user could: 22762306a36Sopenharmony_ci * - DoS/crash the arbitrator - details would be implementation specific 22862306a36Sopenharmony_ci * - open the node, become master implicitly and cause issues 22962306a36Sopenharmony_ci * 23062306a36Sopenharmony_ci * As a result this fixes the following when using root-less build w/o logind 23162306a36Sopenharmony_ci * - startx 23262306a36Sopenharmony_ci * - weston 23362306a36Sopenharmony_ci * - various compositors based on wlroots 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_cistatic int 23662306a36Sopenharmony_cidrm_master_check_perm(struct drm_device *dev, struct drm_file *file_priv) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci if (file_priv->was_master && 23962306a36Sopenharmony_ci rcu_access_pointer(file_priv->pid) == task_tgid(current)) 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 24362306a36Sopenharmony_ci return -EACCES; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ciint drm_setmaster_ioctl(struct drm_device *dev, void *data, 24962306a36Sopenharmony_ci struct drm_file *file_priv) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int ret; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci mutex_lock(&dev->master_mutex); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci ret = drm_master_check_perm(dev, file_priv); 25662306a36Sopenharmony_ci if (ret) 25762306a36Sopenharmony_ci goto out_unlock; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (drm_is_current_master_locked(file_priv)) 26062306a36Sopenharmony_ci goto out_unlock; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (dev->master) { 26362306a36Sopenharmony_ci ret = -EBUSY; 26462306a36Sopenharmony_ci goto out_unlock; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (!file_priv->master) { 26862306a36Sopenharmony_ci ret = -EINVAL; 26962306a36Sopenharmony_ci goto out_unlock; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (!file_priv->is_master) { 27362306a36Sopenharmony_ci ret = drm_new_set_master(dev, file_priv); 27462306a36Sopenharmony_ci goto out_unlock; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (file_priv->master->lessor != NULL) { 27862306a36Sopenharmony_ci drm_dbg_lease(dev, 27962306a36Sopenharmony_ci "Attempt to set lessee %d as master\n", 28062306a36Sopenharmony_ci file_priv->master->lessee_id); 28162306a36Sopenharmony_ci ret = -EINVAL; 28262306a36Sopenharmony_ci goto out_unlock; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci drm_set_master(dev, file_priv, false); 28662306a36Sopenharmony_ciout_unlock: 28762306a36Sopenharmony_ci mutex_unlock(&dev->master_mutex); 28862306a36Sopenharmony_ci return ret; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic void drm_drop_master(struct drm_device *dev, 29262306a36Sopenharmony_ci struct drm_file *fpriv) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci if (dev->driver->master_drop) 29562306a36Sopenharmony_ci dev->driver->master_drop(dev, fpriv); 29662306a36Sopenharmony_ci drm_master_put(&dev->master); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ciint drm_dropmaster_ioctl(struct drm_device *dev, void *data, 30062306a36Sopenharmony_ci struct drm_file *file_priv) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci int ret; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci mutex_lock(&dev->master_mutex); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci ret = drm_master_check_perm(dev, file_priv); 30762306a36Sopenharmony_ci if (ret) 30862306a36Sopenharmony_ci goto out_unlock; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (!drm_is_current_master_locked(file_priv)) { 31162306a36Sopenharmony_ci ret = -EINVAL; 31262306a36Sopenharmony_ci goto out_unlock; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (!dev->master) { 31662306a36Sopenharmony_ci ret = -EINVAL; 31762306a36Sopenharmony_ci goto out_unlock; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (file_priv->master->lessor != NULL) { 32162306a36Sopenharmony_ci drm_dbg_lease(dev, 32262306a36Sopenharmony_ci "Attempt to drop lessee %d as master\n", 32362306a36Sopenharmony_ci file_priv->master->lessee_id); 32462306a36Sopenharmony_ci ret = -EINVAL; 32562306a36Sopenharmony_ci goto out_unlock; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci drm_drop_master(dev, file_priv); 32962306a36Sopenharmony_ciout_unlock: 33062306a36Sopenharmony_ci mutex_unlock(&dev->master_mutex); 33162306a36Sopenharmony_ci return ret; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ciint drm_master_open(struct drm_file *file_priv) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct drm_device *dev = file_priv->minor->dev; 33762306a36Sopenharmony_ci int ret = 0; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* if there is no current master make this fd it, but do not create 34062306a36Sopenharmony_ci * any master object for render clients 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci mutex_lock(&dev->master_mutex); 34362306a36Sopenharmony_ci if (!dev->master) { 34462306a36Sopenharmony_ci ret = drm_new_set_master(dev, file_priv); 34562306a36Sopenharmony_ci } else { 34662306a36Sopenharmony_ci spin_lock(&file_priv->master_lookup_lock); 34762306a36Sopenharmony_ci file_priv->master = drm_master_get(dev->master); 34862306a36Sopenharmony_ci spin_unlock(&file_priv->master_lookup_lock); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci mutex_unlock(&dev->master_mutex); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return ret; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_civoid drm_master_release(struct drm_file *file_priv) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct drm_device *dev = file_priv->minor->dev; 35862306a36Sopenharmony_ci struct drm_master *master; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci mutex_lock(&dev->master_mutex); 36162306a36Sopenharmony_ci master = file_priv->master; 36262306a36Sopenharmony_ci if (file_priv->magic) 36362306a36Sopenharmony_ci idr_remove(&file_priv->master->magic_map, file_priv->magic); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (!drm_is_current_master_locked(file_priv)) 36662306a36Sopenharmony_ci goto out; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci drm_legacy_lock_master_cleanup(dev, master); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (dev->master == file_priv->master) 37162306a36Sopenharmony_ci drm_drop_master(dev, file_priv); 37262306a36Sopenharmony_ciout: 37362306a36Sopenharmony_ci if (drm_core_check_feature(dev, DRIVER_MODESET) && file_priv->is_master) { 37462306a36Sopenharmony_ci /* Revoke any leases held by this or lessees, but only if 37562306a36Sopenharmony_ci * this is the "real" master 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci drm_lease_revoke(master); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* drop the master reference held by the file priv */ 38162306a36Sopenharmony_ci if (file_priv->master) 38262306a36Sopenharmony_ci drm_master_put(&file_priv->master); 38362306a36Sopenharmony_ci mutex_unlock(&dev->master_mutex); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * drm_master_get - reference a master pointer 38862306a36Sopenharmony_ci * @master: &struct drm_master 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * Increments the reference count of @master and returns a pointer to @master. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_cistruct drm_master *drm_master_get(struct drm_master *master) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci kref_get(&master->refcount); 39562306a36Sopenharmony_ci return master; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_master_get); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/** 40062306a36Sopenharmony_ci * drm_file_get_master - reference &drm_file.master of @file_priv 40162306a36Sopenharmony_ci * @file_priv: DRM file private 40262306a36Sopenharmony_ci * 40362306a36Sopenharmony_ci * Increments the reference count of @file_priv's &drm_file.master and returns 40462306a36Sopenharmony_ci * the &drm_file.master. If @file_priv has no &drm_file.master, returns NULL. 40562306a36Sopenharmony_ci * 40662306a36Sopenharmony_ci * Master pointers returned from this function should be unreferenced using 40762306a36Sopenharmony_ci * drm_master_put(). 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_cistruct drm_master *drm_file_get_master(struct drm_file *file_priv) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct drm_master *master = NULL; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci spin_lock(&file_priv->master_lookup_lock); 41462306a36Sopenharmony_ci if (!file_priv->master) 41562306a36Sopenharmony_ci goto unlock; 41662306a36Sopenharmony_ci master = drm_master_get(file_priv->master); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ciunlock: 41962306a36Sopenharmony_ci spin_unlock(&file_priv->master_lookup_lock); 42062306a36Sopenharmony_ci return master; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_file_get_master); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic void drm_master_destroy(struct kref *kref) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct drm_master *master = container_of(kref, struct drm_master, refcount); 42762306a36Sopenharmony_ci struct drm_device *dev = master->dev; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (drm_core_check_feature(dev, DRIVER_MODESET)) 43062306a36Sopenharmony_ci drm_lease_destroy(master); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci drm_legacy_master_rmmaps(dev, master); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci idr_destroy(&master->magic_map); 43562306a36Sopenharmony_ci idr_destroy(&master->leases); 43662306a36Sopenharmony_ci idr_destroy(&master->lessee_idr); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci kfree(master->unique); 43962306a36Sopenharmony_ci kfree(master); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci/** 44362306a36Sopenharmony_ci * drm_master_put - unreference and clear a master pointer 44462306a36Sopenharmony_ci * @master: pointer to a pointer of &struct drm_master 44562306a36Sopenharmony_ci * 44662306a36Sopenharmony_ci * This decrements the &drm_master behind @master and sets it to NULL. 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_civoid drm_master_put(struct drm_master **master) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci kref_put(&(*master)->refcount, drm_master_destroy); 45162306a36Sopenharmony_ci *master = NULL; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_master_put); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/* Used by drm_client and drm_fb_helper */ 45662306a36Sopenharmony_cibool drm_master_internal_acquire(struct drm_device *dev) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci mutex_lock(&dev->master_mutex); 45962306a36Sopenharmony_ci if (dev->master) { 46062306a36Sopenharmony_ci mutex_unlock(&dev->master_mutex); 46162306a36Sopenharmony_ci return false; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return true; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_master_internal_acquire); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci/* Used by drm_client and drm_fb_helper */ 46962306a36Sopenharmony_civoid drm_master_internal_release(struct drm_device *dev) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci mutex_unlock(&dev->master_mutex); 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_master_internal_release); 474