xref: /openbmc/linux/drivers/gpu/drm/drm_auth.c (revision 3e7759b94a0fcfdd6771caa64a37dda7ce825874)
1c0e09200SDave Airlie /*
2c0e09200SDave Airlie  * Created: Tue Feb  2 08:37:54 1999 by faith@valinux.com
3c0e09200SDave Airlie  *
4c0e09200SDave Airlie  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
5c0e09200SDave Airlie  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
6c0e09200SDave Airlie  * All Rights Reserved.
7c0e09200SDave Airlie  *
832e7b94aSDavid Herrmann  * Author Rickard E. (Rik) Faith <faith@valinux.com>
932e7b94aSDavid Herrmann  * Author Gareth Hughes <gareth@valinux.com>
1032e7b94aSDavid Herrmann  *
11c0e09200SDave Airlie  * Permission is hereby granted, free of charge, to any person obtaining a
12c0e09200SDave Airlie  * copy of this software and associated documentation files (the "Software"),
13c0e09200SDave Airlie  * to deal in the Software without restriction, including without limitation
14c0e09200SDave Airlie  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15c0e09200SDave Airlie  * and/or sell copies of the Software, and to permit persons to whom the
16c0e09200SDave Airlie  * Software is furnished to do so, subject to the following conditions:
17c0e09200SDave Airlie  *
18c0e09200SDave Airlie  * The above copyright notice and this permission notice (including the next
19c0e09200SDave Airlie  * paragraph) shall be included in all copies or substantial portions of the
20c0e09200SDave Airlie  * Software.
21c0e09200SDave Airlie  *
22c0e09200SDave Airlie  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23c0e09200SDave Airlie  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24c0e09200SDave Airlie  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
25c0e09200SDave Airlie  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
26c0e09200SDave Airlie  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
27c0e09200SDave Airlie  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28c0e09200SDave Airlie  * OTHER DEALINGS IN THE SOFTWARE.
29c0e09200SDave Airlie  */
30c0e09200SDave Airlie 
310500c04eSSam Ravnborg #include <linux/slab.h>
320500c04eSSam Ravnborg 
330500c04eSSam Ravnborg #include <drm/drm_auth.h>
340500c04eSSam Ravnborg #include <drm/drm_drv.h>
350500c04eSSam Ravnborg #include <drm/drm_file.h>
360500c04eSSam Ravnborg #include <drm/drm_lease.h>
370500c04eSSam Ravnborg #include <drm/drm_print.h>
380500c04eSSam Ravnborg 
3967d0ec4eSDaniel Vetter #include "drm_internal.h"
406548f4e7SDaniel Vetter #include "drm_legacy.h"
41c0e09200SDave Airlie 
42c0e09200SDave Airlie /**
433b96a0b1SDaniel Vetter  * DOC: master and authentication
44c0e09200SDave Airlie  *
45ea0dd85aSDaniel Vetter  * &struct drm_master is used to track groups of clients with open
46ea0dd85aSDaniel Vetter  * primary/legacy device nodes. For every &struct drm_file which has had at
473b96a0b1SDaniel Vetter  * least once successfully became the device master (either through the
483b96a0b1SDaniel Vetter  * SET_MASTER IOCTL, or implicitly through opening the primary device node when
493b96a0b1SDaniel Vetter  * no one else is the current master that time) there exists one &drm_master.
50ef40cbf9SDaniel Vetter  * This is noted in &drm_file.is_master. All other clients have just a pointer
51ef40cbf9SDaniel Vetter  * to the &drm_master they are associated with.
52c0e09200SDave Airlie  *
533b96a0b1SDaniel Vetter  * In addition only one &drm_master can be the current master for a &drm_device.
543b96a0b1SDaniel Vetter  * It can be switched through the DROP_MASTER and SET_MASTER IOCTL, or
550ae865efSCai Huoqing  * implicitly through closing/opening the primary device node. See also
563b96a0b1SDaniel Vetter  * drm_is_current_master().
573b96a0b1SDaniel Vetter  *
583b96a0b1SDaniel Vetter  * Clients can authenticate against the current master (if it matches their own)
593b96a0b1SDaniel Vetter  * using the GETMAGIC and AUTHMAGIC IOCTLs. Together with exchanging masters,
603b96a0b1SDaniel Vetter  * this allows controlled access to the device for an entire group of mutually
613b96a0b1SDaniel Vetter  * trusted clients.
62c0e09200SDave Airlie  */
633b96a0b1SDaniel Vetter 
drm_is_current_master_locked(struct drm_file * fpriv)641f7ef07cSDesmond Cheong Zhi Xi static bool drm_is_current_master_locked(struct drm_file *fpriv)
651f7ef07cSDesmond Cheong Zhi Xi {
66649839d7SDesmond Cheong Zhi Xi 	lockdep_assert_once(lockdep_is_held(&fpriv->master_lookup_lock) ||
67649839d7SDesmond Cheong Zhi Xi 			    lockdep_is_held(&fpriv->minor->dev->master_mutex));
68649839d7SDesmond Cheong Zhi Xi 
691f7ef07cSDesmond Cheong Zhi Xi 	return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master;
701f7ef07cSDesmond Cheong Zhi Xi }
711f7ef07cSDesmond Cheong Zhi Xi 
721f7ef07cSDesmond Cheong Zhi Xi /**
731f7ef07cSDesmond Cheong Zhi Xi  * drm_is_current_master - checks whether @priv is the current master
741f7ef07cSDesmond Cheong Zhi Xi  * @fpriv: DRM file private
751f7ef07cSDesmond Cheong Zhi Xi  *
761f7ef07cSDesmond Cheong Zhi Xi  * Checks whether @fpriv is current master on its device. This decides whether a
771f7ef07cSDesmond Cheong Zhi Xi  * client is allowed to run DRM_MASTER IOCTLs.
781f7ef07cSDesmond Cheong Zhi Xi  *
791f7ef07cSDesmond Cheong Zhi Xi  * Most of the modern IOCTL which require DRM_MASTER are for kernel modesetting
801f7ef07cSDesmond Cheong Zhi Xi  * - the current master is assumed to own the non-shareable display hardware.
811f7ef07cSDesmond Cheong Zhi Xi  */
drm_is_current_master(struct drm_file * fpriv)821f7ef07cSDesmond Cheong Zhi Xi bool drm_is_current_master(struct drm_file *fpriv)
831f7ef07cSDesmond Cheong Zhi Xi {
841f7ef07cSDesmond Cheong Zhi Xi 	bool ret;
851f7ef07cSDesmond Cheong Zhi Xi 
8628be2405SDesmond Cheong Zhi Xi 	spin_lock(&fpriv->master_lookup_lock);
871f7ef07cSDesmond Cheong Zhi Xi 	ret = drm_is_current_master_locked(fpriv);
8828be2405SDesmond Cheong Zhi Xi 	spin_unlock(&fpriv->master_lookup_lock);
891f7ef07cSDesmond Cheong Zhi Xi 
901f7ef07cSDesmond Cheong Zhi Xi 	return ret;
911f7ef07cSDesmond Cheong Zhi Xi }
921f7ef07cSDesmond Cheong Zhi Xi EXPORT_SYMBOL(drm_is_current_master);
931f7ef07cSDesmond Cheong Zhi Xi 
drm_getmagic(struct drm_device * dev,void * data,struct drm_file * file_priv)94c0e09200SDave Airlie int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
95c0e09200SDave Airlie {
96c0e09200SDave Airlie 	struct drm_auth *auth = data;
9732e7b94aSDavid Herrmann 	int ret = 0;
98c0e09200SDave Airlie 
99d2b34ee6SDaniel Vetter 	mutex_lock(&dev->master_mutex);
10032e7b94aSDavid Herrmann 	if (!file_priv->magic) {
10132e7b94aSDavid Herrmann 		ret = idr_alloc(&file_priv->master->magic_map, file_priv,
10232e7b94aSDavid Herrmann 				1, 0, GFP_KERNEL);
10332e7b94aSDavid Herrmann 		if (ret >= 0)
10432e7b94aSDavid Herrmann 			file_priv->magic = ret;
105c0e09200SDave Airlie 	}
10632e7b94aSDavid Herrmann 	auth->magic = file_priv->magic;
107d2b34ee6SDaniel Vetter 	mutex_unlock(&dev->master_mutex);
108c0e09200SDave Airlie 
1096e22dc35SClaudio Suarez 	drm_dbg_core(dev, "%u\n", auth->magic);
110c0e09200SDave Airlie 
11132e7b94aSDavid Herrmann 	return ret < 0 ? ret : 0;
112c0e09200SDave Airlie }
113c0e09200SDave Airlie 
drm_authmagic(struct drm_device * dev,void * data,struct drm_file * file_priv)114c0e09200SDave Airlie int drm_authmagic(struct drm_device *dev, void *data,
115c0e09200SDave Airlie 		  struct drm_file *file_priv)
116c0e09200SDave Airlie {
117c0e09200SDave Airlie 	struct drm_auth *auth = data;
118c0e09200SDave Airlie 	struct drm_file *file;
119c0e09200SDave Airlie 
1206e22dc35SClaudio Suarez 	drm_dbg_core(dev, "%u\n", auth->magic);
12132e7b94aSDavid Herrmann 
122d2b34ee6SDaniel Vetter 	mutex_lock(&dev->master_mutex);
12332e7b94aSDavid Herrmann 	file = idr_find(&file_priv->master->magic_map, auth->magic);
12432e7b94aSDavid Herrmann 	if (file) {
125c0e09200SDave Airlie 		file->authenticated = 1;
12632e7b94aSDavid Herrmann 		idr_replace(&file_priv->master->magic_map, NULL, auth->magic);
127c0e09200SDave Airlie 	}
128d2b34ee6SDaniel Vetter 	mutex_unlock(&dev->master_mutex);
12932e7b94aSDavid Herrmann 
13032e7b94aSDavid Herrmann 	return file ? 0 : -EINVAL;
131c0e09200SDave Airlie }
1326548f4e7SDaniel Vetter 
drm_master_create(struct drm_device * dev)1332ed077e4SKeith Packard struct drm_master *drm_master_create(struct drm_device *dev)
1346548f4e7SDaniel Vetter {
1356548f4e7SDaniel Vetter 	struct drm_master *master;
1366548f4e7SDaniel Vetter 
1376548f4e7SDaniel Vetter 	master = kzalloc(sizeof(*master), GFP_KERNEL);
1386548f4e7SDaniel Vetter 	if (!master)
1396548f4e7SDaniel Vetter 		return NULL;
1406548f4e7SDaniel Vetter 
1416548f4e7SDaniel Vetter 	kref_init(&master->refcount);
142ee22f763SDave Airlie 	drm_master_legacy_init(master);
143a49afeb4SDanilo Krummrich 	idr_init_base(&master->magic_map, 1);
1446548f4e7SDaniel Vetter 	master->dev = dev;
1456548f4e7SDaniel Vetter 
1462ed077e4SKeith Packard 	/* initialize the tree of output resource lessees */
1472ed077e4SKeith Packard 	INIT_LIST_HEAD(&master->lessees);
1482ed077e4SKeith Packard 	INIT_LIST_HEAD(&master->lessee_list);
1492ed077e4SKeith Packard 	idr_init(&master->leases);
1503a6acb7dSDanilo Krummrich 	idr_init_base(&master->lessee_idr, 1);
1512ed077e4SKeith Packard 
1526548f4e7SDaniel Vetter 	return master;
1536548f4e7SDaniel Vetter }
1546548f4e7SDaniel Vetter 
drm_set_master(struct drm_device * dev,struct drm_file * fpriv,bool new_master)155907f5320SEmil Velikov static void drm_set_master(struct drm_device *dev, struct drm_file *fpriv,
156d6ed682eSDaniel Vetter 			   bool new_master)
157d6ed682eSDaniel Vetter {
158d6ed682eSDaniel Vetter 	dev->master = drm_master_get(fpriv->master);
159907f5320SEmil Velikov 	if (dev->driver->master_set)
160907f5320SEmil Velikov 		dev->driver->master_set(dev, fpriv, new_master);
161d6ed682eSDaniel Vetter 
162907f5320SEmil Velikov 	fpriv->was_master = true;
163d6ed682eSDaniel Vetter }
164d6ed682eSDaniel Vetter 
drm_new_set_master(struct drm_device * dev,struct drm_file * fpriv)1652cbae7e6SDaniel Vetter static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
1666548f4e7SDaniel Vetter {
1676548f4e7SDaniel Vetter 	struct drm_master *old_master;
1680b0860a3SDesmond Cheong Zhi Xi 	struct drm_master *new_master;
1696548f4e7SDaniel Vetter 
1706548f4e7SDaniel Vetter 	lockdep_assert_held_once(&dev->master_mutex);
1716548f4e7SDaniel Vetter 
17223a336b3SSergio Correia 	WARN_ON(fpriv->is_master);
1736548f4e7SDaniel Vetter 	old_master = fpriv->master;
1740b0860a3SDesmond Cheong Zhi Xi 	new_master = drm_master_create(dev);
1750b0860a3SDesmond Cheong Zhi Xi 	if (!new_master)
176d6ed682eSDaniel Vetter 		return -ENOMEM;
1770b0860a3SDesmond Cheong Zhi Xi 	spin_lock(&fpriv->master_lookup_lock);
1780b0860a3SDesmond Cheong Zhi Xi 	fpriv->master = new_master;
1790b0860a3SDesmond Cheong Zhi Xi 	spin_unlock(&fpriv->master_lookup_lock);
1806548f4e7SDaniel Vetter 
1810aae5920SDaniel Vetter 	fpriv->is_master = 1;
1826548f4e7SDaniel Vetter 	fpriv->authenticated = 1;
183d6ed682eSDaniel Vetter 
184907f5320SEmil Velikov 	drm_set_master(dev, fpriv, true);
185d6ed682eSDaniel Vetter 
1866548f4e7SDaniel Vetter 	if (old_master)
1876548f4e7SDaniel Vetter 		drm_master_put(&old_master);
1886548f4e7SDaniel Vetter 
1896548f4e7SDaniel Vetter 	return 0;
1906548f4e7SDaniel Vetter }
1916548f4e7SDaniel Vetter 
19245bc3d26SEmil Velikov /*
19345bc3d26SEmil Velikov  * In the olden days the SET/DROP_MASTER ioctls used to return EACCES when
19445bc3d26SEmil Velikov  * CAP_SYS_ADMIN was not set. This was used to prevent rogue applications
19545bc3d26SEmil Velikov  * from becoming master and/or failing to release it.
19645bc3d26SEmil Velikov  *
19745bc3d26SEmil Velikov  * At the same time, the first client (for a given VT) is _always_ master.
19845bc3d26SEmil Velikov  * Thus in order for the ioctls to succeed, one had to _explicitly_ run the
19945bc3d26SEmil Velikov  * application as root or flip the setuid bit.
20045bc3d26SEmil Velikov  *
20145bc3d26SEmil Velikov  * If the CAP_SYS_ADMIN was missing, no other client could become master...
20245bc3d26SEmil Velikov  * EVER :-( Leading to a) the graphics session dying badly or b) a completely
20345bc3d26SEmil Velikov  * locked session.
20445bc3d26SEmil Velikov  *
20545bc3d26SEmil Velikov  *
20645bc3d26SEmil Velikov  * As some point systemd-logind was introduced to orchestrate and delegate
20745bc3d26SEmil Velikov  * master as applicable. It does so by opening the fd and passing it to users
20845bc3d26SEmil Velikov  * while in itself logind a) does the set/drop master per users' request and
20945bc3d26SEmil Velikov  * b)  * implicitly drops master on VT switch.
21045bc3d26SEmil Velikov  *
21145bc3d26SEmil Velikov  * Even though logind looks like the future, there are a few issues:
21245bc3d26SEmil Velikov  *  - some platforms don't have equivalent (Android, CrOS, some BSDs) so
21345bc3d26SEmil Velikov  * root is required _solely_ for SET/DROP MASTER.
21445bc3d26SEmil Velikov  *  - applications may not be updated to use it,
21545bc3d26SEmil Velikov  *  - any client which fails to drop master* can DoS the application using
21645bc3d26SEmil Velikov  * logind, to a varying degree.
21745bc3d26SEmil Velikov  *
21845bc3d26SEmil Velikov  * * Either due missing CAP_SYS_ADMIN or simply not calling DROP_MASTER.
21945bc3d26SEmil Velikov  *
22045bc3d26SEmil Velikov  *
22145bc3d26SEmil Velikov  * Here we implement the next best thing:
22245bc3d26SEmil Velikov  *  - ensure the logind style of fd passing works unchanged, and
22345bc3d26SEmil Velikov  *  - allow a client to drop/set master, iff it is/was master at a given point
22445bc3d26SEmil Velikov  * in time.
22545bc3d26SEmil Velikov  *
22645bc3d26SEmil Velikov  * Note: DROP_MASTER cannot be free for all, as an arbitrator user could:
22745bc3d26SEmil Velikov  *  - DoS/crash the arbitrator - details would be implementation specific
22845bc3d26SEmil Velikov  *  - open the node, become master implicitly and cause issues
22945bc3d26SEmil Velikov  *
23045bc3d26SEmil Velikov  * As a result this fixes the following when using root-less build w/o logind
23145bc3d26SEmil Velikov  * - startx
23245bc3d26SEmil Velikov  * - weston
23345bc3d26SEmil Velikov  * - various compositors based on wlroots
23445bc3d26SEmil Velikov  */
23545bc3d26SEmil Velikov static int
drm_master_check_perm(struct drm_device * dev,struct drm_file * file_priv)23645bc3d26SEmil Velikov drm_master_check_perm(struct drm_device *dev, struct drm_file *file_priv)
23745bc3d26SEmil Velikov {
238031ddd28STvrtko Ursulin 	if (file_priv->was_master &&
239*c9b26d9eSLingkai Dong 	    rcu_access_pointer(file_priv->pid) == task_tgid(current))
24045bc3d26SEmil Velikov 		return 0;
24145bc3d26SEmil Velikov 
24245bc3d26SEmil Velikov 	if (!capable(CAP_SYS_ADMIN))
24345bc3d26SEmil Velikov 		return -EACCES;
24445bc3d26SEmil Velikov 
24545bc3d26SEmil Velikov 	return 0;
24645bc3d26SEmil Velikov }
24745bc3d26SEmil Velikov 
drm_setmaster_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)2486548f4e7SDaniel Vetter int drm_setmaster_ioctl(struct drm_device *dev, void *data,
2496548f4e7SDaniel Vetter 			struct drm_file *file_priv)
2506548f4e7SDaniel Vetter {
251264ddd07SEmil Velikov 	int ret;
2526548f4e7SDaniel Vetter 
2536548f4e7SDaniel Vetter 	mutex_lock(&dev->master_mutex);
25445bc3d26SEmil Velikov 
25545bc3d26SEmil Velikov 	ret = drm_master_check_perm(dev, file_priv);
25645bc3d26SEmil Velikov 	if (ret)
25745bc3d26SEmil Velikov 		goto out_unlock;
25845bc3d26SEmil Velikov 
2591f7ef07cSDesmond Cheong Zhi Xi 	if (drm_is_current_master_locked(file_priv))
2606548f4e7SDaniel Vetter 		goto out_unlock;
2616548f4e7SDaniel Vetter 
26295c081c1SDaniel Vetter 	if (dev->master) {
2632bf99b22SEmil Velikov 		ret = -EBUSY;
2646548f4e7SDaniel Vetter 		goto out_unlock;
2656548f4e7SDaniel Vetter 	}
2666548f4e7SDaniel Vetter 
2676548f4e7SDaniel Vetter 	if (!file_priv->master) {
2686548f4e7SDaniel Vetter 		ret = -EINVAL;
2696548f4e7SDaniel Vetter 		goto out_unlock;
2706548f4e7SDaniel Vetter 	}
2716548f4e7SDaniel Vetter 
2720aae5920SDaniel Vetter 	if (!file_priv->is_master) {
2736548f4e7SDaniel Vetter 		ret = drm_new_set_master(dev, file_priv);
2746548f4e7SDaniel Vetter 		goto out_unlock;
2756548f4e7SDaniel Vetter 	}
2766548f4e7SDaniel Vetter 
2772ed077e4SKeith Packard 	if (file_priv->master->lessor != NULL) {
2786e22dc35SClaudio Suarez 		drm_dbg_lease(dev,
2796e22dc35SClaudio Suarez 			      "Attempt to set lessee %d as master\n",
2806e22dc35SClaudio Suarez 			      file_priv->master->lessee_id);
2812ed077e4SKeith Packard 		ret = -EINVAL;
2822ed077e4SKeith Packard 		goto out_unlock;
2832ed077e4SKeith Packard 	}
2842ed077e4SKeith Packard 
285907f5320SEmil Velikov 	drm_set_master(dev, file_priv, false);
2866548f4e7SDaniel Vetter out_unlock:
2876548f4e7SDaniel Vetter 	mutex_unlock(&dev->master_mutex);
2886548f4e7SDaniel Vetter 	return ret;
2896548f4e7SDaniel Vetter }
2906548f4e7SDaniel Vetter 
drm_drop_master(struct drm_device * dev,struct drm_file * fpriv)291d6ed682eSDaniel Vetter static void drm_drop_master(struct drm_device *dev,
292d6ed682eSDaniel Vetter 			    struct drm_file *fpriv)
293d6ed682eSDaniel Vetter {
294d6ed682eSDaniel Vetter 	if (dev->driver->master_drop)
295d6ed682eSDaniel Vetter 		dev->driver->master_drop(dev, fpriv);
296d6ed682eSDaniel Vetter 	drm_master_put(&dev->master);
297d6ed682eSDaniel Vetter }
298d6ed682eSDaniel Vetter 
drm_dropmaster_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)2996548f4e7SDaniel Vetter int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
3006548f4e7SDaniel Vetter 			 struct drm_file *file_priv)
3016548f4e7SDaniel Vetter {
3022217d3bcSColin Ian King 	int ret;
3036548f4e7SDaniel Vetter 
3046548f4e7SDaniel Vetter 	mutex_lock(&dev->master_mutex);
30545bc3d26SEmil Velikov 
30645bc3d26SEmil Velikov 	ret = drm_master_check_perm(dev, file_priv);
30745bc3d26SEmil Velikov 	if (ret)
30845bc3d26SEmil Velikov 		goto out_unlock;
30945bc3d26SEmil Velikov 
3101f7ef07cSDesmond Cheong Zhi Xi 	if (!drm_is_current_master_locked(file_priv)) {
31145bc3d26SEmil Velikov 		ret = -EINVAL;
3126548f4e7SDaniel Vetter 		goto out_unlock;
313264ddd07SEmil Velikov 	}
3146548f4e7SDaniel Vetter 
315264ddd07SEmil Velikov 	if (!dev->master) {
316264ddd07SEmil Velikov 		ret = -EINVAL;
3176548f4e7SDaniel Vetter 		goto out_unlock;
318264ddd07SEmil Velikov 	}
3196548f4e7SDaniel Vetter 
320761e05a7SKeith Packard 	if (file_priv->master->lessor != NULL) {
3216e22dc35SClaudio Suarez 		drm_dbg_lease(dev,
3226e22dc35SClaudio Suarez 			      "Attempt to drop lessee %d as master\n",
3236e22dc35SClaudio Suarez 			      file_priv->master->lessee_id);
324761e05a7SKeith Packard 		ret = -EINVAL;
325761e05a7SKeith Packard 		goto out_unlock;
326761e05a7SKeith Packard 	}
327761e05a7SKeith Packard 
328d6ed682eSDaniel Vetter 	drm_drop_master(dev, file_priv);
3296548f4e7SDaniel Vetter out_unlock:
3306548f4e7SDaniel Vetter 	mutex_unlock(&dev->master_mutex);
3316548f4e7SDaniel Vetter 	return ret;
3326548f4e7SDaniel Vetter }
3336548f4e7SDaniel Vetter 
drm_master_open(struct drm_file * file_priv)3342cbae7e6SDaniel Vetter int drm_master_open(struct drm_file *file_priv)
3352cbae7e6SDaniel Vetter {
3362cbae7e6SDaniel Vetter 	struct drm_device *dev = file_priv->minor->dev;
3372cbae7e6SDaniel Vetter 	int ret = 0;
3382cbae7e6SDaniel Vetter 
3392cbae7e6SDaniel Vetter 	/* if there is no current master make this fd it, but do not create
340d00e3d9eSBeatriz Martins de Carvalho 	 * any master object for render clients
341d00e3d9eSBeatriz Martins de Carvalho 	 */
3422cbae7e6SDaniel Vetter 	mutex_lock(&dev->master_mutex);
3430b0860a3SDesmond Cheong Zhi Xi 	if (!dev->master) {
3442cbae7e6SDaniel Vetter 		ret = drm_new_set_master(dev, file_priv);
3450b0860a3SDesmond Cheong Zhi Xi 	} else {
3460b0860a3SDesmond Cheong Zhi Xi 		spin_lock(&file_priv->master_lookup_lock);
34795c081c1SDaniel Vetter 		file_priv->master = drm_master_get(dev->master);
3480b0860a3SDesmond Cheong Zhi Xi 		spin_unlock(&file_priv->master_lookup_lock);
3490b0860a3SDesmond Cheong Zhi Xi 	}
3502cbae7e6SDaniel Vetter 	mutex_unlock(&dev->master_mutex);
3512cbae7e6SDaniel Vetter 
3522cbae7e6SDaniel Vetter 	return ret;
3532cbae7e6SDaniel Vetter }
3542cbae7e6SDaniel Vetter 
drm_master_release(struct drm_file * file_priv)35514d71ebdSDaniel Vetter void drm_master_release(struct drm_file *file_priv)
35614d71ebdSDaniel Vetter {
35714d71ebdSDaniel Vetter 	struct drm_device *dev = file_priv->minor->dev;
358c336a5eeSDesmond Cheong Zhi Xi 	struct drm_master *master;
35914d71ebdSDaniel Vetter 
360d2b34ee6SDaniel Vetter 	mutex_lock(&dev->master_mutex);
361c336a5eeSDesmond Cheong Zhi Xi 	master = file_priv->master;
362a77316bfSDaniel Vetter 	if (file_priv->magic)
363a77316bfSDaniel Vetter 		idr_remove(&file_priv->master->magic_map, file_priv->magic);
364a77316bfSDaniel Vetter 
3651f7ef07cSDesmond Cheong Zhi Xi 	if (!drm_is_current_master_locked(file_priv))
3660de4cc99SDaniel Vetter 		goto out;
3670de4cc99SDaniel Vetter 
368058ca50cSDave Airlie 	drm_legacy_lock_master_cleanup(dev, master);
36914d71ebdSDaniel Vetter 
370d6ed682eSDaniel Vetter 	if (dev->master == file_priv->master)
371d6ed682eSDaniel Vetter 		drm_drop_master(dev, file_priv);
3720de4cc99SDaniel Vetter out:
3732ed077e4SKeith Packard 	if (drm_core_check_feature(dev, DRIVER_MODESET) && file_priv->is_master) {
3742ed077e4SKeith Packard 		/* Revoke any leases held by this or lessees, but only if
3752ed077e4SKeith Packard 		 * this is the "real" master
3762ed077e4SKeith Packard 		 */
3772ed077e4SKeith Packard 		drm_lease_revoke(master);
3782ed077e4SKeith Packard 	}
3792ed077e4SKeith Packard 
38014d71ebdSDaniel Vetter 	/* drop the master reference held by the file priv */
38114d71ebdSDaniel Vetter 	if (file_priv->master)
38214d71ebdSDaniel Vetter 		drm_master_put(&file_priv->master);
38314d71ebdSDaniel Vetter 	mutex_unlock(&dev->master_mutex);
38414d71ebdSDaniel Vetter }
38514d71ebdSDaniel Vetter 
3863b96a0b1SDaniel Vetter /**
3873b96a0b1SDaniel Vetter  * drm_master_get - reference a master pointer
388ea0dd85aSDaniel Vetter  * @master: &struct drm_master
3893b96a0b1SDaniel Vetter  *
3903b96a0b1SDaniel Vetter  * Increments the reference count of @master and returns a pointer to @master.
3913b96a0b1SDaniel Vetter  */
drm_master_get(struct drm_master * master)3926548f4e7SDaniel Vetter struct drm_master *drm_master_get(struct drm_master *master)
3936548f4e7SDaniel Vetter {
3946548f4e7SDaniel Vetter 	kref_get(&master->refcount);
3956548f4e7SDaniel Vetter 	return master;
3966548f4e7SDaniel Vetter }
3976548f4e7SDaniel Vetter EXPORT_SYMBOL(drm_master_get);
3986548f4e7SDaniel Vetter 
39956f0729aSDesmond Cheong Zhi Xi /**
40056f0729aSDesmond Cheong Zhi Xi  * drm_file_get_master - reference &drm_file.master of @file_priv
40156f0729aSDesmond Cheong Zhi Xi  * @file_priv: DRM file private
40256f0729aSDesmond Cheong Zhi Xi  *
40356f0729aSDesmond Cheong Zhi Xi  * Increments the reference count of @file_priv's &drm_file.master and returns
40456f0729aSDesmond Cheong Zhi Xi  * the &drm_file.master. If @file_priv has no &drm_file.master, returns NULL.
40556f0729aSDesmond Cheong Zhi Xi  *
40656f0729aSDesmond Cheong Zhi Xi  * Master pointers returned from this function should be unreferenced using
40756f0729aSDesmond Cheong Zhi Xi  * drm_master_put().
40856f0729aSDesmond Cheong Zhi Xi  */
drm_file_get_master(struct drm_file * file_priv)40956f0729aSDesmond Cheong Zhi Xi struct drm_master *drm_file_get_master(struct drm_file *file_priv)
41056f0729aSDesmond Cheong Zhi Xi {
41156f0729aSDesmond Cheong Zhi Xi 	struct drm_master *master = NULL;
41256f0729aSDesmond Cheong Zhi Xi 
41356f0729aSDesmond Cheong Zhi Xi 	spin_lock(&file_priv->master_lookup_lock);
41456f0729aSDesmond Cheong Zhi Xi 	if (!file_priv->master)
41556f0729aSDesmond Cheong Zhi Xi 		goto unlock;
41656f0729aSDesmond Cheong Zhi Xi 	master = drm_master_get(file_priv->master);
41756f0729aSDesmond Cheong Zhi Xi 
41856f0729aSDesmond Cheong Zhi Xi unlock:
41956f0729aSDesmond Cheong Zhi Xi 	spin_unlock(&file_priv->master_lookup_lock);
42056f0729aSDesmond Cheong Zhi Xi 	return master;
42156f0729aSDesmond Cheong Zhi Xi }
42256f0729aSDesmond Cheong Zhi Xi EXPORT_SYMBOL(drm_file_get_master);
42356f0729aSDesmond Cheong Zhi Xi 
drm_master_destroy(struct kref * kref)4246548f4e7SDaniel Vetter static void drm_master_destroy(struct kref *kref)
4256548f4e7SDaniel Vetter {
4266548f4e7SDaniel Vetter 	struct drm_master *master = container_of(kref, struct drm_master, refcount);
4276548f4e7SDaniel Vetter 	struct drm_device *dev = master->dev;
4286548f4e7SDaniel Vetter 
4292ed077e4SKeith Packard 	if (drm_core_check_feature(dev, DRIVER_MODESET))
4302ed077e4SKeith Packard 		drm_lease_destroy(master);
4312ed077e4SKeith Packard 
4326548f4e7SDaniel Vetter 	drm_legacy_master_rmmaps(dev, master);
4336548f4e7SDaniel Vetter 
4346548f4e7SDaniel Vetter 	idr_destroy(&master->magic_map);
4352ed077e4SKeith Packard 	idr_destroy(&master->leases);
4362ed077e4SKeith Packard 	idr_destroy(&master->lessee_idr);
4372ed077e4SKeith Packard 
4386548f4e7SDaniel Vetter 	kfree(master->unique);
4396548f4e7SDaniel Vetter 	kfree(master);
4406548f4e7SDaniel Vetter }
4416548f4e7SDaniel Vetter 
4423b96a0b1SDaniel Vetter /**
4433b96a0b1SDaniel Vetter  * drm_master_put - unreference and clear a master pointer
444ea0dd85aSDaniel Vetter  * @master: pointer to a pointer of &struct drm_master
4453b96a0b1SDaniel Vetter  *
4463b96a0b1SDaniel Vetter  * This decrements the &drm_master behind @master and sets it to NULL.
4473b96a0b1SDaniel Vetter  */
drm_master_put(struct drm_master ** master)4486548f4e7SDaniel Vetter void drm_master_put(struct drm_master **master)
4496548f4e7SDaniel Vetter {
4506548f4e7SDaniel Vetter 	kref_put(&(*master)->refcount, drm_master_destroy);
4516548f4e7SDaniel Vetter 	*master = NULL;
4526548f4e7SDaniel Vetter }
4536548f4e7SDaniel Vetter EXPORT_SYMBOL(drm_master_put);
45403a9606eSNoralf Trønnes 
45503a9606eSNoralf Trønnes /* Used by drm_client and drm_fb_helper */
drm_master_internal_acquire(struct drm_device * dev)45603a9606eSNoralf Trønnes bool drm_master_internal_acquire(struct drm_device *dev)
45703a9606eSNoralf Trønnes {
45803a9606eSNoralf Trønnes 	mutex_lock(&dev->master_mutex);
45903a9606eSNoralf Trønnes 	if (dev->master) {
46003a9606eSNoralf Trønnes 		mutex_unlock(&dev->master_mutex);
46103a9606eSNoralf Trønnes 		return false;
46203a9606eSNoralf Trønnes 	}
46303a9606eSNoralf Trønnes 
46403a9606eSNoralf Trønnes 	return true;
46503a9606eSNoralf Trønnes }
46603a9606eSNoralf Trønnes EXPORT_SYMBOL(drm_master_internal_acquire);
46703a9606eSNoralf Trønnes 
46803a9606eSNoralf Trønnes /* Used by drm_client and drm_fb_helper */
drm_master_internal_release(struct drm_device * dev)46903a9606eSNoralf Trønnes void drm_master_internal_release(struct drm_device *dev)
47003a9606eSNoralf Trønnes {
47103a9606eSNoralf Trønnes 	mutex_unlock(&dev->master_mutex);
47203a9606eSNoralf Trønnes }
47303a9606eSNoralf Trønnes EXPORT_SYMBOL(drm_master_internal_release);
474