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