diff mbox

[12/14] drm: Move master pointer from drm_minor to drm_device

Message ID 1465930269-7883-13-git-send-email-daniel.vetter@ffwll.ch (mailing list archive)
State New, archived
Headers show

Commit Message

Daniel Vetter June 14, 2016, 6:51 p.m. UTC
There can only be one current master, and it's for the overall device.
Render/control minors don't support master-based auth at all.

This simplifies the master logic a lot, at least in my eyes: All these
additional pointer chases are just confusing.

While doing the conversion I spotted some locking fail:
- drm_lock/drm_auth check dev->master without holding the
  master_mutex. This is fallout from

  commit c996fd0b956450563454e7ccc97a82ca31f9d043
  Author: Thomas Hellstrom <thellstrom@vmware.com>
  Date:   Tue Feb 25 19:57:44 2014 +0100

      drm: Protect the master management with a drm_device::master_mutex v3

  but I honestly don't care one bit about those old legacy drivers
  using this.

- debugfs name info should just grab master_mutex.

- And the fbdev helper looked at it to figure out whether someone is
  using KMS. We just need a consistent value, so READ_ONCE. Aside: We
  should probably check if anyone has opened a control node too, but I
  guess current userspace doesn't really do that yet.

v2: Balance locking, reported by Julia.

Cc: Julia Lawall <julia.lawall@lip6.fr>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/drm_auth.c      | 26 +++++++++++++-------------
 drivers/gpu/drm/drm_bufs.c      |  8 ++++----
 drivers/gpu/drm/drm_fb_helper.c |  2 +-
 drivers/gpu/drm/drm_info.c      | 10 ++++++++--
 drivers/gpu/drm/drm_lock.c      |  2 +-
 drivers/gpu/drm/sis/sis_mm.c    |  2 +-
 drivers/gpu/drm/via/via_mm.c    |  2 +-
 include/drm/drmP.h              |  9 +++++----
 8 files changed, 34 insertions(+), 27 deletions(-)

Comments

Chris Wilson June 15, 2016, 12:10 p.m. UTC | #1
On Tue, Jun 14, 2016 at 08:51:07PM +0200, Daniel Vetter wrote:
> There can only be one current master, and it's for the overall device.
> Render/control minors don't support master-based auth at all.
> 
> This simplifies the master logic a lot, at least in my eyes: All these
> additional pointer chases are just confusing.

One master for the device, on the struct drm_device, as opposed to hidden
behind the first of three minors, makes sense.

> @@ -128,13 +128,13 @@ static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
>  	lockdep_assert_held_once(&dev->master_mutex);
>  
>  	/* create a new master */
> -	fpriv->minor->master = drm_master_create(fpriv->minor->dev);
> -	if (!fpriv->minor->master)
> +	dev->master = drm_master_create(dev);
> +	if (!dev->master)
>  		return -ENOMEM;
>  
>  	/* take another reference for the copy in the local file priv */
>  	old_master = fpriv->master;
> -	fpriv->master = drm_master_get(fpriv->minor->master);
> +	fpriv->master = drm_master_get(dev->master);
>  
>  	if (dev->driver->master_create) {
>  		ret = dev->driver->master_create(dev, fpriv->master);

> @@ -234,10 +234,10 @@ int drm_master_open(struct drm_file *file_priv)
>  	/* if there is no current master make this fd it, but do not create
>  	 * any master object for render clients */
>  	mutex_lock(&dev->master_mutex);
> -	if (!file_priv->minor->master)
> +	if (!dev->master)
>  		ret = drm_new_set_master(dev, file_priv);
>  	else
> -		file_priv->master = drm_master_get(file_priv->minor->master);
> +		file_priv->master = drm_master_get(dev->master);
>  	mutex_unlock(&dev->master_mutex);

You could take the opportunity to make this a bit simpler:

	if (!READ_ONCE(dev->master)) {
		int ret;

		ret = 0;
		mutex_lock(&dev->master_mutex);
		if (!dev->master)
			ret = drm_new_master(dev);
		mutex_unlock(&dev->master_mutex);
		if (ret)
			return ret;
	}

	file_priv->master = drm_master_get(dev->master);
	return 0;

Just to straighten out the kref dance.

>  
>  	return ret;
> @@ -271,11 +271,11 @@ void drm_master_release(struct drm_file *file_priv)
>  		mutex_unlock(&dev->struct_mutex);
>  	}
>  
> -	if (file_priv->minor->master == file_priv->master) {
> +	if (dev->master == file_priv->master) {
>  		/* drop the reference held my the minor */
>  		if (dev->driver->master_drop)
>  			dev->driver->master_drop(dev, file_priv, true);
> -		drm_master_put(&file_priv->minor->master);
> +		drm_master_put(&dev->master);

This still makes me uneasy. This is not equivalent to dropmaster_ioctl
and subsequent setmaster_ioctl will fail as dev->master is still
assigned (but the owner has gone).
-Chris
Daniel Vetter June 15, 2016, 4:01 p.m. UTC | #2
On Wed, Jun 15, 2016 at 01:10:35PM +0100, Chris Wilson wrote:
> On Tue, Jun 14, 2016 at 08:51:07PM +0200, Daniel Vetter wrote:
> > There can only be one current master, and it's for the overall device.
> > Render/control minors don't support master-based auth at all.
> > 
> > This simplifies the master logic a lot, at least in my eyes: All these
> > additional pointer chases are just confusing.
> 
> One master for the device, on the struct drm_device, as opposed to hidden
> behind the first of three minors, makes sense.
> 
> > @@ -128,13 +128,13 @@ static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
> >  	lockdep_assert_held_once(&dev->master_mutex);
> >  
> >  	/* create a new master */
> > -	fpriv->minor->master = drm_master_create(fpriv->minor->dev);
> > -	if (!fpriv->minor->master)
> > +	dev->master = drm_master_create(dev);
> > +	if (!dev->master)
> >  		return -ENOMEM;
> >  
> >  	/* take another reference for the copy in the local file priv */
> >  	old_master = fpriv->master;
> > -	fpriv->master = drm_master_get(fpriv->minor->master);
> > +	fpriv->master = drm_master_get(dev->master);
> >  
> >  	if (dev->driver->master_create) {
> >  		ret = dev->driver->master_create(dev, fpriv->master);
> 
> > @@ -234,10 +234,10 @@ int drm_master_open(struct drm_file *file_priv)
> >  	/* if there is no current master make this fd it, but do not create
> >  	 * any master object for render clients */
> >  	mutex_lock(&dev->master_mutex);
> > -	if (!file_priv->minor->master)
> > +	if (!dev->master)
> >  		ret = drm_new_set_master(dev, file_priv);
> >  	else
> > -		file_priv->master = drm_master_get(file_priv->minor->master);
> > +		file_priv->master = drm_master_get(dev->master);
> >  	mutex_unlock(&dev->master_mutex);
> 
> You could take the opportunity to make this a bit simpler:
> 
> 	if (!READ_ONCE(dev->master)) {
> 		int ret;
> 
> 		ret = 0;
> 		mutex_lock(&dev->master_mutex);
> 		if (!dev->master)
> 			ret = drm_new_master(dev);
> 		mutex_unlock(&dev->master_mutex);
> 		if (ret)
> 			return ret;
> 	}
> 
> 	file_priv->master = drm_master_get(dev->master);

drm_master_get(dev->master) must be under the master_mutex, without it we
could race with a drm_master_put(&dev->master) and end up doing a kref_get
when the refcount already reached 0.

> 	return 0;
> 
> Just to straighten out the kref dance.
> 
> >  
> >  	return ret;
> > @@ -271,11 +271,11 @@ void drm_master_release(struct drm_file *file_priv)
> >  		mutex_unlock(&dev->struct_mutex);
> >  	}
> >  
> > -	if (file_priv->minor->master == file_priv->master) {
> > +	if (dev->master == file_priv->master) {
> >  		/* drop the reference held my the minor */
> >  		if (dev->driver->master_drop)
> >  			dev->driver->master_drop(dev, file_priv, true);
> > -		drm_master_put(&file_priv->minor->master);
> > +		drm_master_put(&dev->master);
> 
> This still makes me uneasy. This is not equivalent to dropmaster_ioctl
> and subsequent setmaster_ioctl will fail as dev->master is still
> assigned (but the owner has gone).

drm_master_put clears the pointer passed to it, so dev->master will be set
to NULL. And it does the same as drop_master (wrt dev->master at least,
master_release also needs to clean up file_priv->master on top). Not sure
it's worth it to extract those 5 lines into a __drm_drop_master() helper
function? I can respin with that if you want. On the master_open/setmaster
side the shared code is already extracted in drm_new_set_master().
-Daniel
Chris Wilson June 15, 2016, 4:33 p.m. UTC | #3
On Wed, Jun 15, 2016 at 06:01:41PM +0200, Daniel Vetter wrote:
> On Wed, Jun 15, 2016 at 01:10:35PM +0100, Chris Wilson wrote:
> > On Tue, Jun 14, 2016 at 08:51:07PM +0200, Daniel Vetter wrote:
> > > There can only be one current master, and it's for the overall device.
> > > Render/control minors don't support master-based auth at all.
> > > 
> > > This simplifies the master logic a lot, at least in my eyes: All these
> > > additional pointer chases are just confusing.
> > 
> > One master for the device, on the struct drm_device, as opposed to hidden
> > behind the first of three minors, makes sense.
> > 
> > > @@ -128,13 +128,13 @@ static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
> > >  	lockdep_assert_held_once(&dev->master_mutex);
> > >  
> > >  	/* create a new master */
> > > -	fpriv->minor->master = drm_master_create(fpriv->minor->dev);
> > > -	if (!fpriv->minor->master)
> > > +	dev->master = drm_master_create(dev);
> > > +	if (!dev->master)
> > >  		return -ENOMEM;
> > >  
> > >  	/* take another reference for the copy in the local file priv */
> > >  	old_master = fpriv->master;
> > > -	fpriv->master = drm_master_get(fpriv->minor->master);
> > > +	fpriv->master = drm_master_get(dev->master);
> > >  
> > >  	if (dev->driver->master_create) {
> > >  		ret = dev->driver->master_create(dev, fpriv->master);
> > 
> > > @@ -234,10 +234,10 @@ int drm_master_open(struct drm_file *file_priv)
> > >  	/* if there is no current master make this fd it, but do not create
> > >  	 * any master object for render clients */
> > >  	mutex_lock(&dev->master_mutex);
> > > -	if (!file_priv->minor->master)
> > > +	if (!dev->master)
> > >  		ret = drm_new_set_master(dev, file_priv);
> > >  	else
> > > -		file_priv->master = drm_master_get(file_priv->minor->master);
> > > +		file_priv->master = drm_master_get(dev->master);
> > >  	mutex_unlock(&dev->master_mutex);
> > 
> > You could take the opportunity to make this a bit simpler:
> > 
> > 	if (!READ_ONCE(dev->master)) {
> > 		int ret;
> > 
> > 		ret = 0;
> > 		mutex_lock(&dev->master_mutex);
> > 		if (!dev->master)
> > 			ret = drm_new_master(dev);
> > 		mutex_unlock(&dev->master_mutex);
> > 		if (ret)
> > 			return ret;
> > 	}
> > 
> > 	file_priv->master = drm_master_get(dev->master);
> 
> drm_master_get(dev->master) must be under the master_mutex, without it we
> could race with a drm_master_put(&dev->master) and end up doing a kref_get
> when the refcount already reached 0.

Something is very fishy then. The behaviour of drm_new_master() would
appear to be to create a drm_master owned by the device, but really it is
owned by file_priv?

* checks back on drm_master

So drm_master is the authentication structure that needs to be unique to
a hierachy. So drm_new_set_master() and here really do appear backwards.

The old drm_new_set_master() makes more sense, it assigns to the
file_priv, and then performs a setmaster operation. In that ordering,
you could even do the file_priv local operation of creating the new
master structure before taking the lock to perform setmaster.


> > Just to straighten out the kref dance.
> > 
> > >  
> > >  	return ret;
> > > @@ -271,11 +271,11 @@ void drm_master_release(struct drm_file *file_priv)
> > >  		mutex_unlock(&dev->struct_mutex);
> > >  	}
> > >  
> > > -	if (file_priv->minor->master == file_priv->master) {
> > > +	if (dev->master == file_priv->master) {
> > >  		/* drop the reference held my the minor */
> > >  		if (dev->driver->master_drop)
> > >  			dev->driver->master_drop(dev, file_priv, true);
> > > -		drm_master_put(&file_priv->minor->master);
> > > +		drm_master_put(&dev->master);
> > 
> > This still makes me uneasy. This is not equivalent to dropmaster_ioctl
> > and subsequent setmaster_ioctl will fail as dev->master is still
> > assigned (but the owner has gone).
> 
> drm_master_put clears the pointer passed to it, so dev->master will be set
> to NULL. And it does the same as drop_master (wrt dev->master at least,
> master_release also needs to clean up file_priv->master on top). Not sure
> it's worth it to extract those 5 lines into a __drm_drop_master() helper
> function? I can respin with that if you want. On the master_open/setmaster
> side the shared code is already extracted in drm_new_set_master().

drm_master_put() nullifies, didn't expect that.
-Chris
Daniel Vetter June 15, 2016, 7:54 p.m. UTC | #4
On Wed, Jun 15, 2016 at 6:33 PM, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> On Wed, Jun 15, 2016 at 06:01:41PM +0200, Daniel Vetter wrote:
>> On Wed, Jun 15, 2016 at 01:10:35PM +0100, Chris Wilson wrote:
>> > On Tue, Jun 14, 2016 at 08:51:07PM +0200, Daniel Vetter wrote:
>> > > There can only be one current master, and it's for the overall device.
>> > > Render/control minors don't support master-based auth at all.
>> > >
>> > > This simplifies the master logic a lot, at least in my eyes: All these
>> > > additional pointer chases are just confusing.
>> >
>> > One master for the device, on the struct drm_device, as opposed to hidden
>> > behind the first of three minors, makes sense.
>> >
>> > > @@ -128,13 +128,13 @@ static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
>> > >   lockdep_assert_held_once(&dev->master_mutex);
>> > >
>> > >   /* create a new master */
>> > > - fpriv->minor->master = drm_master_create(fpriv->minor->dev);
>> > > - if (!fpriv->minor->master)
>> > > + dev->master = drm_master_create(dev);
>> > > + if (!dev->master)
>> > >           return -ENOMEM;
>> > >
>> > >   /* take another reference for the copy in the local file priv */
>> > >   old_master = fpriv->master;
>> > > - fpriv->master = drm_master_get(fpriv->minor->master);
>> > > + fpriv->master = drm_master_get(dev->master);
>> > >
>> > >   if (dev->driver->master_create) {
>> > >           ret = dev->driver->master_create(dev, fpriv->master);
>> >
>> > > @@ -234,10 +234,10 @@ int drm_master_open(struct drm_file *file_priv)
>> > >   /* if there is no current master make this fd it, but do not create
>> > >    * any master object for render clients */
>> > >   mutex_lock(&dev->master_mutex);
>> > > - if (!file_priv->minor->master)
>> > > + if (!dev->master)
>> > >           ret = drm_new_set_master(dev, file_priv);
>> > >   else
>> > > -         file_priv->master = drm_master_get(file_priv->minor->master);
>> > > +         file_priv->master = drm_master_get(dev->master);
>> > >   mutex_unlock(&dev->master_mutex);
>> >
>> > You could take the opportunity to make this a bit simpler:
>> >
>> >     if (!READ_ONCE(dev->master)) {
>> >             int ret;
>> >
>> >             ret = 0;
>> >             mutex_lock(&dev->master_mutex);
>> >             if (!dev->master)
>> >                     ret = drm_new_master(dev);
>> >             mutex_unlock(&dev->master_mutex);
>> >             if (ret)
>> >                     return ret;
>> >     }
>> >
>> >     file_priv->master = drm_master_get(dev->master);
>>
>> drm_master_get(dev->master) must be under the master_mutex, without it we
>> could race with a drm_master_put(&dev->master) and end up doing a kref_get
>> when the refcount already reached 0.
>
> Something is very fishy then. The behaviour of drm_new_master() would
> appear to be to create a drm_master owned by the device, but really it is
> owned by file_priv?
>
> * checks back on drm_master
>
> So drm_master is the authentication structure that needs to be unique to
> a hierachy. So drm_new_set_master() and here really do appear backwards.
>
> The old drm_new_set_master() makes more sense, it assigns to the
> file_priv, and then performs a setmaster operation. In that ordering,
> you could even do the file_priv local operation of creating the new
> master structure before taking the lock to perform setmaster.

I didn't change the logic, the old new_set_master didn't first create
the master for file_priv, it first created it for file_priv->minor,
and that drm_minor is the device-unique drm_minor for the
primary/legacy node. The change only moves from that drm_minor for the
legacy node to the drm_device.

Wrt refences, every file_priv holds a ref for it's master. On top of
that the current master structure (after this patch stored in
dev->master, before in dev->minors[legacy]->master. The only tricky
bit is that when the current master closes, we do an implicit
dropmaster first.
-Daniel
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
index 94761e4bd1e5..fcadd8e1de28 100644
--- a/drivers/gpu/drm/drm_auth.c
+++ b/drivers/gpu/drm/drm_auth.c
@@ -128,13 +128,13 @@  static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
 	lockdep_assert_held_once(&dev->master_mutex);
 
 	/* create a new master */
-	fpriv->minor->master = drm_master_create(fpriv->minor->dev);
-	if (!fpriv->minor->master)
+	dev->master = drm_master_create(dev);
+	if (!dev->master)
 		return -ENOMEM;
 
 	/* take another reference for the copy in the local file priv */
 	old_master = fpriv->master;
-	fpriv->master = drm_master_get(fpriv->minor->master);
+	fpriv->master = drm_master_get(dev->master);
 
 	if (dev->driver->master_create) {
 		ret = dev->driver->master_create(dev, fpriv->master);
@@ -157,7 +157,7 @@  static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
 
 out_err:
 	/* drop both references and restore old master on failure */
-	drm_master_put(&fpriv->minor->master);
+	drm_master_put(&dev->master);
 	drm_master_put(&fpriv->master);
 	fpriv->master = old_master;
 
@@ -173,7 +173,7 @@  int drm_setmaster_ioctl(struct drm_device *dev, void *data,
 	if (file_priv->is_master)
 		goto out_unlock;
 
-	if (file_priv->minor->master) {
+	if (dev->master) {
 		ret = -EINVAL;
 		goto out_unlock;
 	}
@@ -188,13 +188,13 @@  int drm_setmaster_ioctl(struct drm_device *dev, void *data,
 		goto out_unlock;
 	}
 
-	file_priv->minor->master = drm_master_get(file_priv->master);
+	dev->master = drm_master_get(file_priv->master);
 	file_priv->is_master = 1;
 	if (dev->driver->master_set) {
 		ret = dev->driver->master_set(dev, file_priv, false);
 		if (unlikely(ret != 0)) {
 			file_priv->is_master = 0;
-			drm_master_put(&file_priv->minor->master);
+			drm_master_put(&dev->master);
 		}
 	}
 
@@ -212,13 +212,13 @@  int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
 	if (!file_priv->is_master)
 		goto out_unlock;
 
-	if (!file_priv->minor->master)
+	if (!dev->master)
 		goto out_unlock;
 
 	ret = 0;
 	if (dev->driver->master_drop)
 		dev->driver->master_drop(dev, file_priv, false);
-	drm_master_put(&file_priv->minor->master);
+	drm_master_put(&dev->master);
 	file_priv->is_master = 0;
 
 out_unlock:
@@ -234,10 +234,10 @@  int drm_master_open(struct drm_file *file_priv)
 	/* if there is no current master make this fd it, but do not create
 	 * any master object for render clients */
 	mutex_lock(&dev->master_mutex);
-	if (!file_priv->minor->master)
+	if (!dev->master)
 		ret = drm_new_set_master(dev, file_priv);
 	else
-		file_priv->master = drm_master_get(file_priv->minor->master);
+		file_priv->master = drm_master_get(dev->master);
 	mutex_unlock(&dev->master_mutex);
 
 	return ret;
@@ -271,11 +271,11 @@  void drm_master_release(struct drm_file *file_priv)
 		mutex_unlock(&dev->struct_mutex);
 	}
 
-	if (file_priv->minor->master == file_priv->master) {
+	if (dev->master == file_priv->master) {
 		/* drop the reference held my the minor */
 		if (dev->driver->master_drop)
 			dev->driver->master_drop(dev, file_priv, true);
-		drm_master_put(&file_priv->minor->master);
+		drm_master_put(&dev->master);
 	}
 
 	/* drop the master reference held by the file priv */
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index 9b34158c0f77..c3a12cd8bd0d 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -51,7 +51,7 @@  static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
 		 */
 		if (!entry->map ||
 		    map->type != entry->map->type ||
-		    entry->master != dev->primary->master)
+		    entry->master != dev->master)
 			continue;
 		switch (map->type) {
 		case _DRM_SHM:
@@ -245,12 +245,12 @@  static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
 		map->offset = (unsigned long)map->handle;
 		if (map->flags & _DRM_CONTAINS_LOCK) {
 			/* Prevent a 2nd X Server from creating a 2nd lock */
-			if (dev->primary->master->lock.hw_lock != NULL) {
+			if (dev->master->lock.hw_lock != NULL) {
 				vfree(map->handle);
 				kfree(map);
 				return -EBUSY;
 			}
-			dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle;	/* Pointer to lock */
+			dev->sigdata.lock = dev->master->lock.hw_lock = map->handle;	/* Pointer to lock */
 		}
 		break;
 	case _DRM_AGP: {
@@ -356,7 +356,7 @@  static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
 	mutex_unlock(&dev->struct_mutex);
 
 	if (!(map->flags & _DRM_DRIVER))
-		list->master = dev->primary->master;
+		list->master = dev->master;
 	*maplist = list;
 	return 0;
 }
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 0bac5246e5a7..0a06f9120b5a 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -464,7 +464,7 @@  static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
 
 	/* Sometimes user space wants everything disabled, so don't steal the
 	 * display if there's a master. */
-	if (dev->primary->master)
+	if (READ_ONCE(dev->master))
 		return false;
 
 	drm_for_each_crtc(crtc, dev) {
diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c
index 0090d5987801..24c80dd13837 100644
--- a/drivers/gpu/drm/drm_info.c
+++ b/drivers/gpu/drm/drm_info.c
@@ -50,9 +50,12 @@  int drm_name_info(struct seq_file *m, void *data)
 	struct drm_info_node *node = (struct drm_info_node *) m->private;
 	struct drm_minor *minor = node->minor;
 	struct drm_device *dev = minor->dev;
-	struct drm_master *master = minor->master;
+	struct drm_master *master;
+
+	mutex_lock(&dev->master_mutex);
+	master = dev->master;
 	if (!master)
-		return 0;
+		goto out_unlock;
 
 	if (master->unique) {
 		seq_printf(m, "%s %s %s\n",
@@ -62,6 +65,9 @@  int drm_name_info(struct seq_file *m, void *data)
 		seq_printf(m, "%s %s\n",
 			   dev->driver->name, dev_name(dev->dev));
 	}
+out_unlock:
+	mutex_unlock(&dev->master_mutex);
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c
index 0fb8f9e73486..ae0a4d39deec 100644
--- a/drivers/gpu/drm/drm_lock.c
+++ b/drivers/gpu/drm/drm_lock.c
@@ -334,7 +334,7 @@  void drm_legacy_lock_release(struct drm_device *dev, struct file *filp)
 	struct drm_file *file_priv = filp->private_data;
 
 	/* if the master has gone away we can't do anything with the lock */
-	if (!file_priv->minor->master)
+	if (!dev->master)
 		return;
 
 	if (drm_legacy_i_have_hw_lock(dev, file_priv)) {
diff --git a/drivers/gpu/drm/sis/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c
index 93ad8a5704d1..03defda77766 100644
--- a/drivers/gpu/drm/sis/sis_mm.c
+++ b/drivers/gpu/drm/sis/sis_mm.c
@@ -316,7 +316,7 @@  void sis_reclaim_buffers_locked(struct drm_device *dev,
 	struct sis_file_private *file_priv = file->driver_priv;
 	struct sis_memblock *entry, *next;
 
-	if (!(file->minor->master && file->master->lock.hw_lock))
+	if (!(dev->master && file->master->lock.hw_lock))
 		return;
 
 	drm_legacy_idlelock_take(&file->master->lock);
diff --git a/drivers/gpu/drm/via/via_mm.c b/drivers/gpu/drm/via/via_mm.c
index 4f20742e7788..a04ef1c992d9 100644
--- a/drivers/gpu/drm/via/via_mm.c
+++ b/drivers/gpu/drm/via/via_mm.c
@@ -208,7 +208,7 @@  void via_reclaim_buffers_locked(struct drm_device *dev,
 	struct via_file_private *file_priv = file->driver_priv;
 	struct via_memblock *entry, *next;
 
-	if (!(file->minor->master && file->master->lock.hw_lock))
+	if (!(dev->master && file->master->lock.hw_lock))
 		return;
 
 	drm_legacy_idlelock_take(&file->master->lock);
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index e8788d7b29dc..62e0e70cb5a2 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -336,7 +336,7 @@  struct drm_file {
 	void *driver_priv;
 
 	struct drm_master *master; /* master this node is currently associated with
-				      N.B. not always minor->master */
+				      N.B. not always dev->master */
 	/**
 	 * fbs - List of framebuffers associated with this file.
 	 *
@@ -708,9 +708,6 @@  struct drm_minor {
 
 	struct list_head debugfs_list;
 	struct mutex debugfs_lock; /* Protects debugfs_list. */
-
-	/* currently active master for this node. Protected by master_mutex */
-	struct drm_master *master;
 };
 
 
@@ -759,6 +756,10 @@  struct drm_device {
 	struct drm_minor *control;		/**< Control node */
 	struct drm_minor *primary;		/**< Primary node */
 	struct drm_minor *render;		/**< Render node */
+
+	/* currently active master for this device. Protected by master_mutex */
+	struct drm_master *master;
+
 	atomic_t unplugged;			/**< Flag whether dev is dead */
 	struct inode *anon_inode;		/**< inode for private address-space */
 	char *unique;				/**< unique name of the device */