Hi,
This series contains some improvements that Daniel Vetter proposed following a discussion on a recent series: https://lore.kernel.org/lkml/20210712043508.11584-1-desmondcheongzx@gmail.co...
While preparing these patches, I also noticed some unprotected uses of drm_master in the vmwgfx driver that can be addressed by new functions from the previous series.
This series is thus broken up into three patches:
1. Switch from the outer drm_device.master_mutex to the inner drm_file.master_lookup_lock in drm_is_current_master.
2. Update the kerneldoc for lease fields in drm_master to clarify lifetime/locking rules.
3. Prevent potential use-after-free bugs by replacing calls to drm_master_get with drm_file_get_master in vmwgfx_surface.c.
Best wishes, Desmond
Desmond Cheong Zhi Xi (3): drm: use the lookup lock in drm_is_current_master drm: clarify lifetime/locking for drm_master's lease fields drm/vmwgfx: fix potential UAF in vmwgfx_surface.c
drivers/gpu/drm/drm_auth.c | 9 ++-- drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 4 +- include/drm/drm_auth.h | 62 ++++++++++++++++++++----- 3 files changed, 58 insertions(+), 17 deletions(-)
Inside drm_is_current_master, using the outer drm_device.master_mutex to protect reads of drm_file.master makes the function prone to creating lock hierarchy inversions. Instead, we can use the drm_file.master_lookup_lock that sits at the bottom of the lock hierarchy.
Reported-by: Daniel Vetter daniel.vetter@ffwll.ch Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com --- drivers/gpu/drm/drm_auth.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index f00354bec3fb..9c24b8cc8e36 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -63,8 +63,9 @@
static bool drm_is_current_master_locked(struct drm_file *fpriv) { - lockdep_assert_held_once(&fpriv->minor->dev->master_mutex); - + /* Either drm_device.master_mutex or drm_file.master_lookup_lock + * should be held here. + */ return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master; }
@@ -82,9 +83,9 @@ bool drm_is_current_master(struct drm_file *fpriv) { bool ret;
- mutex_lock(&fpriv->minor->dev->master_mutex); + spin_lock(&fpriv->master_lookup_lock); ret = drm_is_current_master_locked(fpriv); - mutex_unlock(&fpriv->minor->dev->master_mutex); + spin_unlock(&fpriv->master_lookup_lock);
return ret; }
On Thu, Jul 22, 2021 at 05:29:27PM +0800, Desmond Cheong Zhi Xi wrote:
Inside drm_is_current_master, using the outer drm_device.master_mutex to protect reads of drm_file.master makes the function prone to creating lock hierarchy inversions. Instead, we can use the drm_file.master_lookup_lock that sits at the bottom of the lock hierarchy.
Reported-by: Daniel Vetter daniel.vetter@ffwll.ch Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
drivers/gpu/drm/drm_auth.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index f00354bec3fb..9c24b8cc8e36 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -63,8 +63,9 @@
static bool drm_is_current_master_locked(struct drm_file *fpriv) {
- lockdep_assert_held_once(&fpriv->minor->dev->master_mutex);
- /* Either drm_device.master_mutex or drm_file.master_lookup_lock
* should be held here.
*/
Disappointing that lockdep can't check or conditions for us, a lockdep_assert_held_either would be really neat in some cases.
Adding lockdep folks, maybe they have ideas.
On the patch:
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master; }
@@ -82,9 +83,9 @@ bool drm_is_current_master(struct drm_file *fpriv) { bool ret;
- mutex_lock(&fpriv->minor->dev->master_mutex);
- spin_lock(&fpriv->master_lookup_lock); ret = drm_is_current_master_locked(fpriv);
- mutex_unlock(&fpriv->minor->dev->master_mutex);
spin_unlock(&fpriv->master_lookup_lock);
return ret;
}
2.25.1
On Thu, Jul 22, 2021 at 12:38:10PM +0200, Daniel Vetter wrote:
On Thu, Jul 22, 2021 at 05:29:27PM +0800, Desmond Cheong Zhi Xi wrote:
Inside drm_is_current_master, using the outer drm_device.master_mutex to protect reads of drm_file.master makes the function prone to creating lock hierarchy inversions. Instead, we can use the drm_file.master_lookup_lock that sits at the bottom of the lock hierarchy.
Reported-by: Daniel Vetter daniel.vetter@ffwll.ch Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
drivers/gpu/drm/drm_auth.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index f00354bec3fb..9c24b8cc8e36 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -63,8 +63,9 @@
static bool drm_is_current_master_locked(struct drm_file *fpriv) {
- lockdep_assert_held_once(&fpriv->minor->dev->master_mutex);
- /* Either drm_device.master_mutex or drm_file.master_lookup_lock
* should be held here.
*/
Disappointing that lockdep can't check or conditions for us, a lockdep_assert_held_either would be really neat in some cases.
The implementation is not hard but I don't understand the usage, for example, if we have a global variable x, and two locks L1 and L2, and the function
void do_something_to_x(void) { lockdep_assert_held_either(L1, L2); x++; }
and two call sites:
void f(void) { lock(L1); do_something_to_x(); unlock(L1); }
void g(void) { lock(L2); do_something_to_x(); unlock(L2); }
, wouldn't it be racy if f() and g() called by two threads at the same time? Usually I would expect there exists a third synchronazition mechanism (say M), which synchronizes the calls to f() and g(), and we put M in the lockdep_assert_held() check inside do_something_to_x() like:
void do_something_to_x(void) { lockdep_assert_held_once(M); x++; }
But of course, M may not be a lock, so we cannot put the assert there.
My cscope failed to find ->master_lookup_lock in -rc2 and seems it's not introduced in the patchset either, could you point me the branch this patchset is based on, so that I could understand this better, and maybe come up with a solution? Thanks ;-)
Regards, Boqun
Adding lockdep folks, maybe they have ideas.
On the patch:
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master; }
@@ -82,9 +83,9 @@ bool drm_is_current_master(struct drm_file *fpriv) { bool ret;
- mutex_lock(&fpriv->minor->dev->master_mutex);
- spin_lock(&fpriv->master_lookup_lock); ret = drm_is_current_master_locked(fpriv);
- mutex_unlock(&fpriv->minor->dev->master_mutex);
spin_unlock(&fpriv->master_lookup_lock);
return ret;
}
2.25.1
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
On Thu, Jul 22, 2021 at 6:00 PM Boqun Feng boqun.feng@gmail.com wrote:
On Thu, Jul 22, 2021 at 12:38:10PM +0200, Daniel Vetter wrote:
On Thu, Jul 22, 2021 at 05:29:27PM +0800, Desmond Cheong Zhi Xi wrote:
Inside drm_is_current_master, using the outer drm_device.master_mutex to protect reads of drm_file.master makes the function prone to creating lock hierarchy inversions. Instead, we can use the drm_file.master_lookup_lock that sits at the bottom of the lock hierarchy.
Reported-by: Daniel Vetter daniel.vetter@ffwll.ch Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
drivers/gpu/drm/drm_auth.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index f00354bec3fb..9c24b8cc8e36 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -63,8 +63,9 @@
static bool drm_is_current_master_locked(struct drm_file *fpriv) {
- lockdep_assert_held_once(&fpriv->minor->dev->master_mutex);
- /* Either drm_device.master_mutex or drm_file.master_lookup_lock
- should be held here.
- */
Disappointing that lockdep can't check or conditions for us, a lockdep_assert_held_either would be really neat in some cases.
The implementation is not hard but I don't understand the usage, for example, if we have a global variable x, and two locks L1 and L2, and the function
void do_something_to_x(void) { lockdep_assert_held_either(L1, L2); x++; }
and two call sites:
void f(void) { lock(L1); do_something_to_x(); unlock(L1); } void g(void) { lock(L2); do_something_to_x(); unlock(L2); }
, wouldn't it be racy if f() and g() called by two threads at the same time? Usually I would expect there exists a third synchronazition mechanism (say M), which synchronizes the calls to f() and g(), and we put M in the lockdep_assert_held() check inside do_something_to_x() like:
void do_something_to_x(void) { lockdep_assert_held_once(M); x++; }
But of course, M may not be a lock, so we cannot put the assert there.
My cscope failed to find ->master_lookup_lock in -rc2 and seems it's not introduced in the patchset either, could you point me the branch this patchset is based on, so that I could understand this better, and maybe come up with a solution? Thanks ;-)
The use case is essentially 2 nesting locks, and only the innermost is used to update a field. So when you only read this field, it's safe if either of these two locks are held. Essentially this is a read/write lock type of thing, except for various reasons the two locks might not be of the same type (like here where the write lock is a mutex, but the read lock is a spinlock).
It's a bit like the rcu_derefence macro where it's ok to either be in a rcu_read_lock() section, or holding the relevant lock that's used to update the value. We do _not_ have two different locks that allow writing to the same X.
Does that make it clearer what's the use-case here?
In an example:
void * interesting_pointer.
do_update_interesting_pointer() { mutex_lock(A); /* do more stuff to prepare things */ spin_lock(B); interesting_pointer = new_value; spin_unlock(B); mutex_unlock(A); }
read_interesting_thing_locked() { lockdep_assert_held_either(A, B);
return interesting_pointer->thing; }
read_interesting_thing() { int thing; spin_lock(B); thing = interesting_pointer->thing; spin_unlock(B);
return B; }
spinlock might also be irqsafe here if this can be called from irq context.
Cheers, Daniel
Regards, Boqun
Adding lockdep folks, maybe they have ideas.
On the patch:
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master;
}
@@ -82,9 +83,9 @@ bool drm_is_current_master(struct drm_file *fpriv) { bool ret;
- mutex_lock(&fpriv->minor->dev->master_mutex);
- spin_lock(&fpriv->master_lookup_lock); ret = drm_is_current_master_locked(fpriv);
- mutex_unlock(&fpriv->minor->dev->master_mutex);
spin_unlock(&fpriv->master_lookup_lock);
return ret;
}
2.25.1
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
On Thu, Jul 22, 2021 at 09:02:41PM +0200, Daniel Vetter wrote:
On Thu, Jul 22, 2021 at 6:00 PM Boqun Feng boqun.feng@gmail.com wrote:
On Thu, Jul 22, 2021 at 12:38:10PM +0200, Daniel Vetter wrote:
On Thu, Jul 22, 2021 at 05:29:27PM +0800, Desmond Cheong Zhi Xi wrote:
Inside drm_is_current_master, using the outer drm_device.master_mutex to protect reads of drm_file.master makes the function prone to creating lock hierarchy inversions. Instead, we can use the drm_file.master_lookup_lock that sits at the bottom of the lock hierarchy.
Reported-by: Daniel Vetter daniel.vetter@ffwll.ch Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
drivers/gpu/drm/drm_auth.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index f00354bec3fb..9c24b8cc8e36 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -63,8 +63,9 @@
static bool drm_is_current_master_locked(struct drm_file *fpriv) {
- lockdep_assert_held_once(&fpriv->minor->dev->master_mutex);
- /* Either drm_device.master_mutex or drm_file.master_lookup_lock
- should be held here.
- */
Disappointing that lockdep can't check or conditions for us, a lockdep_assert_held_either would be really neat in some cases.
The implementation is not hard but I don't understand the usage, for example, if we have a global variable x, and two locks L1 and L2, and the function
void do_something_to_x(void) { lockdep_assert_held_either(L1, L2); x++; }
and two call sites:
void f(void) { lock(L1); do_something_to_x(); unlock(L1); } void g(void) { lock(L2); do_something_to_x(); unlock(L2); }
, wouldn't it be racy if f() and g() called by two threads at the same time? Usually I would expect there exists a third synchronazition mechanism (say M), which synchronizes the calls to f() and g(), and we put M in the lockdep_assert_held() check inside do_something_to_x() like:
void do_something_to_x(void) { lockdep_assert_held_once(M); x++; }
But of course, M may not be a lock, so we cannot put the assert there.
My cscope failed to find ->master_lookup_lock in -rc2 and seems it's not introduced in the patchset either, could you point me the branch this patchset is based on, so that I could understand this better, and maybe come up with a solution? Thanks ;-)
The use case is essentially 2 nesting locks, and only the innermost is used to update a field. So when you only read this field, it's safe if either of these two locks are held. Essentially this is a read/write lock type of thing, except for various reasons the two locks might not be of the same type (like here where the write lock is a mutex, but the read lock is a spinlock).
It's a bit like the rcu_derefence macro where it's ok to either be in a rcu_read_lock() section, or holding the relevant lock that's used to update the value. We do _not_ have two different locks that allow writing to the same X.
Does that make it clearer what's the use-case here?
In an example:
void * interesting_pointer.
do_update_interesting_pointer() { mutex_lock(A); /* do more stuff to prepare things */ spin_lock(B); interesting_pointer = new_value; spin_unlock(B); mutex_unlock(A); }
read_interesting_thing_locked() { lockdep_assert_held_either(A, B);
return interesting_pointer->thing; }
read_interesting_thing() { int thing; spin_lock(B); thing = interesting_pointer->thing; spin_unlock(B);
return B; }
spinlock might also be irqsafe here if this can be called from irq context.
Make sense, so we'd better also provide lockdep_assert_held_both(), I think, to use it at the update side, something as below:
/* * lockdep_assert_held_{both,either}(). * * Sometimes users can use a combination of two locks to * implement a rwlock-like lock, for example, say we have * locks L1 and L2, and we only allow updates when two locks * both held like: * * update() * { * lockdep_assert_held_both(L1, L2); * x++; // update x * } * * while for read-only accesses, either lock suffices (since * holding either lock means others cannot hold both, so readers * serialized with the updaters): * * read() * { * lockdep_assert_held_either(L1, L2); * r = x; // read x * } */
#define lockdep_assert_held_both(l1, l2) do { \ WARN_ON_ONCE(debug_locks && \ (!lockdep_is_held(l1) || \ !lockdep_is_held(l2))); \ } while (0)
#define lockdep_assert_held_either(l1, l2) do { \ WARN_ON_ONCE(debug_locks && \ (!lockdep_is_held(l1) && \ !lockdep_is_held(l2))); \ } while (0)
Still need sometime to think through this (e.g. on whether this it the best implementation).
Regards, Boqun
Cheers, Daniel
Regards, Boqun
Adding lockdep folks, maybe they have ideas.
On the patch:
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master;
}
@@ -82,9 +83,9 @@ bool drm_is_current_master(struct drm_file *fpriv) { bool ret;
- mutex_lock(&fpriv->minor->dev->master_mutex);
- spin_lock(&fpriv->master_lookup_lock); ret = drm_is_current_master_locked(fpriv);
- mutex_unlock(&fpriv->minor->dev->master_mutex);
spin_unlock(&fpriv->master_lookup_lock);
return ret;
}
2.25.1
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
On Thu, Jul 22, 2021 at 12:38:10PM +0200, Daniel Vetter wrote:
On Thu, Jul 22, 2021 at 05:29:27PM +0800, Desmond Cheong Zhi Xi wrote:
Inside drm_is_current_master, using the outer drm_device.master_mutex to protect reads of drm_file.master makes the function prone to creating lock hierarchy inversions. Instead, we can use the drm_file.master_lookup_lock that sits at the bottom of the lock hierarchy.
Reported-by: Daniel Vetter daniel.vetter@ffwll.ch Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
drivers/gpu/drm/drm_auth.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index f00354bec3fb..9c24b8cc8e36 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -63,8 +63,9 @@
static bool drm_is_current_master_locked(struct drm_file *fpriv) {
- lockdep_assert_held_once(&fpriv->minor->dev->master_mutex);
- /* Either drm_device.master_mutex or drm_file.master_lookup_lock
* should be held here.
*/
Disappointing that lockdep can't check or conditions for us, a lockdep_assert_held_either would be really neat in some cases.
Adding lockdep folks, maybe they have ideas.
#ifdef CONFIG_LOCKDEP WARN_ON_ONCE(debug_locks && !(lockdep_is_held(&drm_device.master_mutex) || lockdep_is_held(&drm_file.master_lookup_lock))); #endif
doesn't exactly roll off the tongue, but should do as you want I suppose.
Would something like:
#define lockdep_assert(cond) WARN_ON_ONCE(debug_locks && !(cond))
Such that we can write:
lockdep_assert(lockdep_is_held(&drm_device.master_mutex) || lockdep_is_held(&drm_file.master_lookup_lock));
make it better ?
--- Subject: locking/lockdep: Provide lockdep_assert{,_once}() helpers
Extract lockdep_assert{,_once}() helpers to more easily write composite assertions like, for example:
lockdep_assert(lockdep_is_held(&drm_device.master_mutex) || lockdep_is_held(&drm_file.master_lookup_lock));
Signed-off-by: Peter Zijlstra (Intel) peterz@infradead.org --- diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 5cf387813754..0da67341c1fb 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -306,31 +306,29 @@ extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie);
#define lockdep_depth(tsk) (debug_locks ? (tsk)->lockdep_depth : 0)
-#define lockdep_assert_held(l) do { \ - WARN_ON(debug_locks && \ - lockdep_is_held(l) == LOCK_STATE_NOT_HELD); \ - } while (0) +#define lockdep_assert(cond) \ + do { WARN_ON(debug_locks && !(cond)); } while (0)
-#define lockdep_assert_not_held(l) do { \ - WARN_ON(debug_locks && \ - lockdep_is_held(l) == LOCK_STATE_HELD); \ - } while (0) +#define lockdep_assert_once(cond) \ + do { WARN_ON_ONCE(debug_locks && !(cond)); } while (0)
-#define lockdep_assert_held_write(l) do { \ - WARN_ON(debug_locks && !lockdep_is_held_type(l, 0)); \ - } while (0) +#define lockdep_assert_held(l) \ + lockdep_assert(lockdep_is_held(l) != LOCK_STAT_NOT_HELD)
-#define lockdep_assert_held_read(l) do { \ - WARN_ON(debug_locks && !lockdep_is_held_type(l, 1)); \ - } while (0) +#define lockdep_assert_not_held(l) \ + lockdep_assert(lockdep_is_held(l) != LOCK_STATE_HELD)
-#define lockdep_assert_held_once(l) do { \ - WARN_ON_ONCE(debug_locks && !lockdep_is_held(l)); \ - } while (0) +#define lockdep_assert_held_write(l) \ + lockdep_assert(lockdep_is_held_type(l, 0))
-#define lockdep_assert_none_held_once() do { \ - WARN_ON_ONCE(debug_locks && current->lockdep_depth); \ - } while (0) +#define lockdep_assert_held_read(l) \ + lockdep_assert(lockdep_is_held_type(l, 1)) + +#define lockdep_assert_held_once(l) \ + lockdep_assert_once(lockdep_is_held(l) != LOCK_STAT_NOT_HELD) + +#define lockdep_assert_none_held_once() \ + lockdep_assert_once(!current->lockdep_depth)
#define lockdep_recursing(tsk) ((tsk)->lockdep_recursion)
@@ -407,6 +405,9 @@ extern int lock_is_held(const void *); extern int lockdep_is_held(const void *); #define lockdep_is_held_type(l, r) (1)
+#define lockdep_assert(c) do { } while (0) +#define lockdep_assert_once(c) do { } while (0) + #define lockdep_assert_held(l) do { (void)(l); } while (0) #define lockdep_assert_not_held(l) do { (void)(l); } while (0) #define lockdep_assert_held_write(l) do { (void)(l); } while (0)
On Tue, Jul 27, 2021 at 04:37:22PM +0200, Peter Zijlstra wrote:
On Thu, Jul 22, 2021 at 12:38:10PM +0200, Daniel Vetter wrote:
On Thu, Jul 22, 2021 at 05:29:27PM +0800, Desmond Cheong Zhi Xi wrote:
Inside drm_is_current_master, using the outer drm_device.master_mutex to protect reads of drm_file.master makes the function prone to creating lock hierarchy inversions. Instead, we can use the drm_file.master_lookup_lock that sits at the bottom of the lock hierarchy.
Reported-by: Daniel Vetter daniel.vetter@ffwll.ch Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
drivers/gpu/drm/drm_auth.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index f00354bec3fb..9c24b8cc8e36 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -63,8 +63,9 @@
static bool drm_is_current_master_locked(struct drm_file *fpriv) {
- lockdep_assert_held_once(&fpriv->minor->dev->master_mutex);
- /* Either drm_device.master_mutex or drm_file.master_lookup_lock
* should be held here.
*/
Disappointing that lockdep can't check or conditions for us, a lockdep_assert_held_either would be really neat in some cases.
Adding lockdep folks, maybe they have ideas.
#ifdef CONFIG_LOCKDEP WARN_ON_ONCE(debug_locks && !(lockdep_is_held(&drm_device.master_mutex) || lockdep_is_held(&drm_file.master_lookup_lock))); #endif
doesn't exactly roll off the tongue, but should do as you want I suppose.
Would something like:
#define lockdep_assert(cond) WARN_ON_ONCE(debug_locks && !(cond))
Such that we can write:
lockdep_assert(lockdep_is_held(&drm_device.master_mutex) || lockdep_is_held(&drm_file.master_lookup_lock));
make it better ?
Yeah I think that's pretty tidy and flexible.
Desmond, can you pls give this a shot with Peter's patch below? -Daniel
Subject: locking/lockdep: Provide lockdep_assert{,_once}() helpers
Extract lockdep_assert{,_once}() helpers to more easily write composite assertions like, for example:
lockdep_assert(lockdep_is_held(&drm_device.master_mutex) || lockdep_is_held(&drm_file.master_lookup_lock));
Signed-off-by: Peter Zijlstra (Intel) peterz@infradead.org
diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 5cf387813754..0da67341c1fb 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -306,31 +306,29 @@ extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie);
#define lockdep_depth(tsk) (debug_locks ? (tsk)->lockdep_depth : 0)
-#define lockdep_assert_held(l) do { \
WARN_ON(debug_locks && \
lockdep_is_held(l) == LOCK_STATE_NOT_HELD); \
- } while (0)
+#define lockdep_assert(cond) \
- do { WARN_ON(debug_locks && !(cond)); } while (0)
-#define lockdep_assert_not_held(l) do { \
WARN_ON(debug_locks && \
lockdep_is_held(l) == LOCK_STATE_HELD); \
- } while (0)
+#define lockdep_assert_once(cond) \
- do { WARN_ON_ONCE(debug_locks && !(cond)); } while (0)
-#define lockdep_assert_held_write(l) do { \
WARN_ON(debug_locks && !lockdep_is_held_type(l, 0)); \
- } while (0)
+#define lockdep_assert_held(l) \
- lockdep_assert(lockdep_is_held(l) != LOCK_STAT_NOT_HELD)
-#define lockdep_assert_held_read(l) do { \
WARN_ON(debug_locks && !lockdep_is_held_type(l, 1)); \
- } while (0)
+#define lockdep_assert_not_held(l) \
- lockdep_assert(lockdep_is_held(l) != LOCK_STATE_HELD)
-#define lockdep_assert_held_once(l) do { \
WARN_ON_ONCE(debug_locks && !lockdep_is_held(l)); \
- } while (0)
+#define lockdep_assert_held_write(l) \
- lockdep_assert(lockdep_is_held_type(l, 0))
-#define lockdep_assert_none_held_once() do { \
WARN_ON_ONCE(debug_locks && current->lockdep_depth); \
- } while (0)
+#define lockdep_assert_held_read(l) \
- lockdep_assert(lockdep_is_held_type(l, 1))
+#define lockdep_assert_held_once(l) \
- lockdep_assert_once(lockdep_is_held(l) != LOCK_STAT_NOT_HELD)
+#define lockdep_assert_none_held_once() \
- lockdep_assert_once(!current->lockdep_depth)
#define lockdep_recursing(tsk) ((tsk)->lockdep_recursion)
@@ -407,6 +405,9 @@ extern int lock_is_held(const void *); extern int lockdep_is_held(const void *); #define lockdep_is_held_type(l, r) (1)
+#define lockdep_assert(c) do { } while (0) +#define lockdep_assert_once(c) do { } while (0)
#define lockdep_assert_held(l) do { (void)(l); } while (0) #define lockdep_assert_not_held(l) do { (void)(l); } while (0) #define lockdep_assert_held_write(l) do { (void)(l); } while (0)
On 29/7/21 3:00 pm, Daniel Vetter wrote:
On Tue, Jul 27, 2021 at 04:37:22PM +0200, Peter Zijlstra wrote:
On Thu, Jul 22, 2021 at 12:38:10PM +0200, Daniel Vetter wrote:
On Thu, Jul 22, 2021 at 05:29:27PM +0800, Desmond Cheong Zhi Xi wrote:
Inside drm_is_current_master, using the outer drm_device.master_mutex to protect reads of drm_file.master makes the function prone to creating lock hierarchy inversions. Instead, we can use the drm_file.master_lookup_lock that sits at the bottom of the lock hierarchy.
Reported-by: Daniel Vetter daniel.vetter@ffwll.ch Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
drivers/gpu/drm/drm_auth.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index f00354bec3fb..9c24b8cc8e36 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -63,8 +63,9 @@
static bool drm_is_current_master_locked(struct drm_file *fpriv) {
- lockdep_assert_held_once(&fpriv->minor->dev->master_mutex);
- /* Either drm_device.master_mutex or drm_file.master_lookup_lock
* should be held here.
*/
Disappointing that lockdep can't check or conditions for us, a lockdep_assert_held_either would be really neat in some cases.
Adding lockdep folks, maybe they have ideas.
#ifdef CONFIG_LOCKDEP WARN_ON_ONCE(debug_locks && !(lockdep_is_held(&drm_device.master_mutex) || lockdep_is_held(&drm_file.master_lookup_lock))); #endif
doesn't exactly roll off the tongue, but should do as you want I suppose.
Would something like:
#define lockdep_assert(cond) WARN_ON_ONCE(debug_locks && !(cond))
Such that we can write:
lockdep_assert(lockdep_is_held(&drm_device.master_mutex) || lockdep_is_held(&drm_file.master_lookup_lock));
make it better ?
Yeah I think that's pretty tidy and flexible.
Desmond, can you pls give this a shot with Peter's patch below? -Daniel
Sounds good, will do. Thanks for the patch, Peter.
Just going to make a small edit: s/LOCK_STAT_NOT_HELD/LOCK_STATE_NOT_HELD/
Best wishes, Desmond
Subject: locking/lockdep: Provide lockdep_assert{,_once}() helpers
Extract lockdep_assert{,_once}() helpers to more easily write composite assertions like, for example:
lockdep_assert(lockdep_is_held(&drm_device.master_mutex) || lockdep_is_held(&drm_file.master_lookup_lock));
Signed-off-by: Peter Zijlstra (Intel) peterz@infradead.org
diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 5cf387813754..0da67341c1fb 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -306,31 +306,29 @@ extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie);
#define lockdep_depth(tsk) (debug_locks ? (tsk)->lockdep_depth : 0)
-#define lockdep_assert_held(l) do { \
WARN_ON(debug_locks && \
lockdep_is_held(l) == LOCK_STATE_NOT_HELD); \
- } while (0)
+#define lockdep_assert(cond) \
- do { WARN_ON(debug_locks && !(cond)); } while (0)
-#define lockdep_assert_not_held(l) do { \
WARN_ON(debug_locks && \
lockdep_is_held(l) == LOCK_STATE_HELD); \
- } while (0)
+#define lockdep_assert_once(cond) \
- do { WARN_ON_ONCE(debug_locks && !(cond)); } while (0)
-#define lockdep_assert_held_write(l) do { \
WARN_ON(debug_locks && !lockdep_is_held_type(l, 0)); \
- } while (0)
+#define lockdep_assert_held(l) \
- lockdep_assert(lockdep_is_held(l) != LOCK_STAT_NOT_HELD)
-#define lockdep_assert_held_read(l) do { \
WARN_ON(debug_locks && !lockdep_is_held_type(l, 1)); \
- } while (0)
+#define lockdep_assert_not_held(l) \
- lockdep_assert(lockdep_is_held(l) != LOCK_STATE_HELD)
-#define lockdep_assert_held_once(l) do { \
WARN_ON_ONCE(debug_locks && !lockdep_is_held(l)); \
- } while (0)
+#define lockdep_assert_held_write(l) \
- lockdep_assert(lockdep_is_held_type(l, 0))
-#define lockdep_assert_none_held_once() do { \
WARN_ON_ONCE(debug_locks && current->lockdep_depth); \
- } while (0)
+#define lockdep_assert_held_read(l) \
- lockdep_assert(lockdep_is_held_type(l, 1))
+#define lockdep_assert_held_once(l) \
- lockdep_assert_once(lockdep_is_held(l) != LOCK_STAT_NOT_HELD)
+#define lockdep_assert_none_held_once() \
lockdep_assert_once(!current->lockdep_depth)
#define lockdep_recursing(tsk) ((tsk)->lockdep_recursion)
@@ -407,6 +405,9 @@ extern int lock_is_held(const void *); extern int lockdep_is_held(const void *); #define lockdep_is_held_type(l, r) (1)
+#define lockdep_assert(c) do { } while (0) +#define lockdep_assert_once(c) do { } while (0)
- #define lockdep_assert_held(l) do { (void)(l); } while (0) #define lockdep_assert_not_held(l) do { (void)(l); } while (0) #define lockdep_assert_held_write(l) do { (void)(l); } while (0)
On Thu, Jul 29, 2021 at 10:32:13PM +0800, Desmond Cheong Zhi Xi wrote:
Sounds good, will do. Thanks for the patch, Peter.
Just going to make a small edit: s/LOCK_STAT_NOT_HELD/LOCK_STATE_NOT_HELD/
Bah, I knew I should've compile tested it :-), Thanks!
In particular, we make it clear that &drm_device.mode_config.idr_mutex protects the lease idr and list structures for drm_master. The lessor field itself doesn't need to be protected as it doesn't change after it's set in drm_lease_create.
Additionally, we add descriptions for the lifetime of lessors and leases to make it easier to reason about them.
Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com --- include/drm/drm_auth.h | 62 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 11 deletions(-)
diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h index f99d3417f304..c978c85464fa 100644 --- a/include/drm/drm_auth.h +++ b/include/drm/drm_auth.h @@ -58,12 +58,6 @@ struct drm_lock_data { * @refcount: Refcount for this master object. * @dev: Link back to the DRM device * @driver_priv: Pointer to driver-private information. - * @lessor: Lease holder - * @lessee_id: id for lessees. Owners always have id 0 - * @lessee_list: other lessees of the same master - * @lessees: drm_masters leasing from this one - * @leases: Objects leased to this drm_master. - * @lessee_idr: All lessees under this owner (only used where lessor == NULL) * * Note that master structures are only relevant for the legacy/primary device * nodes, hence there can only be one per device, not one per drm_minor. @@ -88,17 +82,63 @@ struct drm_master { struct idr magic_map; void *driver_priv;
- /* Tree of display resource leases, each of which is a drm_master struct - * All of these get activated simultaneously, so drm_device master points - * at the top of the tree (for which lessor is NULL). Protected by - * &drm_device.mode_config.idr_mutex. + /** + * @lessor: + * + * Lease holder. The lessor does not change once it's set in + * drm_lease_create(). Each lessee holds a reference to its lessor that + * it releases upon being destroyed in drm_lease_destroy(). + * + * Display resource leases form a tree of &struct drm_master. All of + * these get activated simultaneously, so &drm_device.master + * points at the top of the tree (for which lessor is NULL). */ - struct drm_master *lessor; + + /** + * @lessee_id: + * + * ID for lessees. Owners always have ID 0. Protected by + * &drm_device.mode_config's &drm_mode_config.idr_mutex. + */ int lessee_id; + + /** + * @lessee_list: + * + * List of lessees of the same master. Protected by + * &drm_device.mode_config's &drm_mode_config.idr_mutex. + */ struct list_head lessee_list; + + /** + * @lessees: + * + * List of drm_masters leasing from this one. Protected by + * &drm_device.mode_config's &drm_mode_config.idr_mutex. + * + * This master cannot be destroyed unless this list is empty as lessors + * are referenced by all their lessees. + */ struct list_head lessees; + + /** + * @leases: + * + * Objects leased to this drm_master. Protected by + * &drm_device.mode_config's &drm_mode_config.idr_mutex. + * + * Objects are leased all together in drm_lease_create(), and are + * removed all together when the lease is revoked. + */ struct idr leases; + + /** + * @lessee_idr: + * + * All lessees under this owner (only used where lessor is NULL). + * Protected by &drm_device.mode_config's &drm_mode_config.idr_mutex. + */ struct idr lessee_idr; /* private: */ #if IS_ENABLED(CONFIG_DRM_LEGACY)
On Thu, Jul 22, 2021 at 05:29:28PM +0800, Desmond Cheong Zhi Xi wrote:
In particular, we make it clear that &drm_device.mode_config.idr_mutex protects the lease idr and list structures for drm_master. The lessor field itself doesn't need to be protected as it doesn't change after it's set in drm_lease_create.
Additionally, we add descriptions for the lifetime of lessors and leases to make it easier to reason about them.
Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
include/drm/drm_auth.h | 62 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 11 deletions(-)
diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h index f99d3417f304..c978c85464fa 100644 --- a/include/drm/drm_auth.h +++ b/include/drm/drm_auth.h @@ -58,12 +58,6 @@ struct drm_lock_data {
- @refcount: Refcount for this master object.
- @dev: Link back to the DRM device
- @driver_priv: Pointer to driver-private information.
- @lessor: Lease holder
- @lessee_id: id for lessees. Owners always have id 0
- @lessee_list: other lessees of the same master
- @lessees: drm_masters leasing from this one
- @leases: Objects leased to this drm_master.
- @lessee_idr: All lessees under this owner (only used where lessor == NULL)
- Note that master structures are only relevant for the legacy/primary device
- nodes, hence there can only be one per device, not one per drm_minor.
@@ -88,17 +82,63 @@ struct drm_master { struct idr magic_map; void *driver_priv;
- /* Tree of display resource leases, each of which is a drm_master struct
* All of these get activated simultaneously, so drm_device master points
* at the top of the tree (for which lessor is NULL). Protected by
* &drm_device.mode_config.idr_mutex.
- /**
* @lessor:
*
* Lease holder. The lessor does not change once it's set in
Lessor is the lease grantor, lessee is the one receiving the lease. Maybe clarify this with
"Lease grantor, only set if this struct drm_master represents a lessee holding a lease of objects from @lessor. Full owners of the device have this set to NULL."
* drm_lease_create(). Each lessee holds a reference to its lessor that
I also figured it'd be a good idea to link to the drm_lease docs here to explain the concepts, but alas we don't have those :-( Hence at least define what we mean with lessor, lessee, lease and owner.
* it releases upon being destroyed in drm_lease_destroy().
*
* Display resource leases form a tree of &struct drm_master. All of
For now we've limited the tree to a depth of 1, you can't create another lease if yourself are a lease. I guess another reason to update the drm_lease.c docs.
So maybe add "Currently the lease tree depth is limited to 1."
* these get activated simultaneously, so &drm_device.master
*/* points at the top of the tree (for which lessor is NULL).
- struct drm_master *lessor;
- /**
* @lessee_id:
*
* ID for lessees. Owners always have ID 0. Protected by
Maybe clarify to "Owners (i.e. @lessor is NULL) ..."
* &drm_device.mode_config's &drm_mode_config.idr_mutex.
int lessee_id;*/
- /**
* @lessee_list:
*
* List of lessees of the same master. Protected by
We try to distinguis between list entries and the list, even though it's the same struct. So maybe
"List entry of lessees of @lessor, where they are linked to @lessee. Not use for owners."
* &drm_device.mode_config's &drm_mode_config.idr_mutex.
struct list_head lessee_list;*/
- /**
* @lessees:
*
* List of drm_masters leasing from this one. Protected by
* &drm_device.mode_config's &drm_mode_config.idr_mutex.
*
* This master cannot be destroyed unless this list is empty as lessors
* are referenced by all their lessees.
Maybe add "this list is empty of no leases have been granted."
struct list_head lessees;*/
- /**
* @leases:
*
* Objects leased to this drm_master. Protected by
* &drm_device.mode_config's &drm_mode_config.idr_mutex.
*
* Objects are leased all together in drm_lease_create(), and are
* removed all together when the lease is revoked.
struct idr leases;*/
- /**
* @lessee_idr:
*
* All lessees under this owner (only used where lessor is NULL).
@lessor so it's highlighted correctly
* Protected by &drm_device.mode_config's &drm_mode_config.idr_mutex.
struct idr lessee_idr; /* private: */*/
With the nits addressed:
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
Thanks for updating the docs! -Daniel
#if IS_ENABLED(CONFIG_DRM_LEGACY)
2.25.1
On 22/7/21 6:35 pm, Daniel Vetter wrote:
On Thu, Jul 22, 2021 at 05:29:28PM +0800, Desmond Cheong Zhi Xi wrote:
In particular, we make it clear that &drm_device.mode_config.idr_mutex protects the lease idr and list structures for drm_master. The lessor field itself doesn't need to be protected as it doesn't change after it's set in drm_lease_create.
Additionally, we add descriptions for the lifetime of lessors and leases to make it easier to reason about them.
Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
include/drm/drm_auth.h | 62 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 11 deletions(-)
diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h index f99d3417f304..c978c85464fa 100644 --- a/include/drm/drm_auth.h +++ b/include/drm/drm_auth.h @@ -58,12 +58,6 @@ struct drm_lock_data {
- @refcount: Refcount for this master object.
- @dev: Link back to the DRM device
- @driver_priv: Pointer to driver-private information.
- @lessor: Lease holder
- @lessee_id: id for lessees. Owners always have id 0
- @lessee_list: other lessees of the same master
- @lessees: drm_masters leasing from this one
- @leases: Objects leased to this drm_master.
- @lessee_idr: All lessees under this owner (only used where lessor == NULL)
- Note that master structures are only relevant for the legacy/primary device
- nodes, hence there can only be one per device, not one per drm_minor.
@@ -88,17 +82,63 @@ struct drm_master { struct idr magic_map; void *driver_priv;
- /* Tree of display resource leases, each of which is a drm_master struct
* All of these get activated simultaneously, so drm_device master points
* at the top of the tree (for which lessor is NULL). Protected by
* &drm_device.mode_config.idr_mutex.
- /**
* @lessor:
*
* Lease holder. The lessor does not change once it's set in
Lessor is the lease grantor, lessee is the one receiving the lease. Maybe clarify this with
"Lease grantor, only set if this struct drm_master represents a lessee holding a lease of objects from @lessor. Full owners of the device have this set to NULL."
* drm_lease_create(). Each lessee holds a reference to its lessor that
I also figured it'd be a good idea to link to the drm_lease docs here to explain the concepts, but alas we don't have those :-( Hence at least define what we mean with lessor, lessee, lease and owner.
Thanks for the suggestions, Daniel. I'll incorporate them in a v2.
Regarding the missing drm_lease docs... any reason we can't just add it in? Seems like most of the comments in drm_lease.c are almost correctly formatted too. I could clean them up, define the terms in a preamble, then add it to the v2 patch.
* it releases upon being destroyed in drm_lease_destroy().
*
* Display resource leases form a tree of &struct drm_master. All of
For now we've limited the tree to a depth of 1, you can't create another lease if yourself are a lease. I guess another reason to update the drm_lease.c docs.
So maybe add "Currently the lease tree depth is limited to 1."
* these get activated simultaneously, so &drm_device.master
*/* points at the top of the tree (for which lessor is NULL).
- struct drm_master *lessor;
- /**
* @lessee_id:
*
* ID for lessees. Owners always have ID 0. Protected by
Maybe clarify to "Owners (i.e. @lessor is NULL) ..."
* &drm_device.mode_config's &drm_mode_config.idr_mutex.
int lessee_id;*/
- /**
* @lessee_list:
*
* List of lessees of the same master. Protected by
We try to distinguis between list entries and the list, even though it's the same struct. So maybe
"List entry of lessees of @lessor, where they are linked to @lessee. Not use for owners."
* &drm_device.mode_config's &drm_mode_config.idr_mutex.
struct list_head lessee_list;*/
- /**
* @lessees:
*
* List of drm_masters leasing from this one. Protected by
* &drm_device.mode_config's &drm_mode_config.idr_mutex.
*
* This master cannot be destroyed unless this list is empty as lessors
* are referenced by all their lessees.
Maybe add "this list is empty of no leases have been granted."
struct list_head lessees;*/
- /**
* @leases:
*
* Objects leased to this drm_master. Protected by
* &drm_device.mode_config's &drm_mode_config.idr_mutex.
*
* Objects are leased all together in drm_lease_create(), and are
* removed all together when the lease is revoked.
struct idr leases;*/
- /**
* @lessee_idr:
*
* All lessees under this owner (only used where lessor is NULL).
@lessor so it's highlighted correctly
* Protected by &drm_device.mode_config's &drm_mode_config.idr_mutex.
struct idr lessee_idr; /* private: */*/
With the nits addressed:
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
Thanks for updating the docs! -Daniel
#if IS_ENABLED(CONFIG_DRM_LEGACY)
2.25.1
On Thu, Jul 22, 2021 at 3:03 PM Desmond Cheong Zhi Xi desmondcheongzx@gmail.com wrote:
On 22/7/21 6:35 pm, Daniel Vetter wrote:
On Thu, Jul 22, 2021 at 05:29:28PM +0800, Desmond Cheong Zhi Xi wrote:
In particular, we make it clear that &drm_device.mode_config.idr_mutex protects the lease idr and list structures for drm_master. The lessor field itself doesn't need to be protected as it doesn't change after it's set in drm_lease_create.
Additionally, we add descriptions for the lifetime of lessors and leases to make it easier to reason about them.
Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
include/drm/drm_auth.h | 62 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 11 deletions(-)
diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h index f99d3417f304..c978c85464fa 100644 --- a/include/drm/drm_auth.h +++ b/include/drm/drm_auth.h @@ -58,12 +58,6 @@ struct drm_lock_data {
- @refcount: Refcount for this master object.
- @dev: Link back to the DRM device
- @driver_priv: Pointer to driver-private information.
- @lessor: Lease holder
- @lessee_id: id for lessees. Owners always have id 0
- @lessee_list: other lessees of the same master
- @lessees: drm_masters leasing from this one
- @leases: Objects leased to this drm_master.
- @lessee_idr: All lessees under this owner (only used where lessor == NULL)
- Note that master structures are only relevant for the legacy/primary device
- nodes, hence there can only be one per device, not one per drm_minor.
@@ -88,17 +82,63 @@ struct drm_master { struct idr magic_map; void *driver_priv;
- /* Tree of display resource leases, each of which is a drm_master struct
* All of these get activated simultaneously, so drm_device master points
* at the top of the tree (for which lessor is NULL). Protected by
* &drm_device.mode_config.idr_mutex.
- /**
* @lessor:
*
* Lease holder. The lessor does not change once it's set in
Lessor is the lease grantor, lessee is the one receiving the lease. Maybe clarify this with
"Lease grantor, only set if this struct drm_master represents a lessee holding a lease of objects from @lessor. Full owners of the device have this set to NULL."
* drm_lease_create(). Each lessee holds a reference to its lessor that
I also figured it'd be a good idea to link to the drm_lease docs here to explain the concepts, but alas we don't have those :-( Hence at least define what we mean with lessor, lessee, lease and owner.
Thanks for the suggestions, Daniel. I'll incorporate them in a v2.
Regarding the missing drm_lease docs... any reason we can't just add it in? Seems like most of the comments in drm_lease.c are almost correctly formatted too. I could clean them up, define the terms in a preamble, then add it to the v2 patch.
Sure if you want to do that, that would be great. Usual style tips: - We only document driver interfaces, so structs/inline functions in headers and exported symbols in .c files. - Anything else interesting just leave as normal C comments - An overview DOC: section that explains a bit how leases work and why (git blame on the commit that added it should help, otherwise I can type that up) would also be really great.
I think the right place for this is in the drm-uapi.rst section after the section about primary nodes:
https://dri.freedesktop.org/docs/drm/gpu/drm-kms.html#modeset-base-object-ab...
Cheers, Daniel
* it releases upon being destroyed in drm_lease_destroy().
*
* Display resource leases form a tree of &struct drm_master. All of
For now we've limited the tree to a depth of 1, you can't create another lease if yourself are a lease. I guess another reason to update the drm_lease.c docs.
So maybe add "Currently the lease tree depth is limited to 1."
* these get activated simultaneously, so &drm_device.master
* points at the top of the tree (for which lessor is NULL). */
- struct drm_master *lessor;
- /**
* @lessee_id:
*
* ID for lessees. Owners always have ID 0. Protected by
Maybe clarify to "Owners (i.e. @lessor is NULL) ..."
* &drm_device.mode_config's &drm_mode_config.idr_mutex.
int lessee_id;*/
- /**
* @lessee_list:
*
* List of lessees of the same master. Protected by
We try to distinguis between list entries and the list, even though it's the same struct. So maybe
"List entry of lessees of @lessor, where they are linked to @lessee. Not use for owners."
* &drm_device.mode_config's &drm_mode_config.idr_mutex.
struct list_head lessee_list;*/
- /**
* @lessees:
*
* List of drm_masters leasing from this one. Protected by
* &drm_device.mode_config's &drm_mode_config.idr_mutex.
*
* This master cannot be destroyed unless this list is empty as lessors
* are referenced by all their lessees.
Maybe add "this list is empty of no leases have been granted."
struct list_head lessees;*/
- /**
* @leases:
*
* Objects leased to this drm_master. Protected by
* &drm_device.mode_config's &drm_mode_config.idr_mutex.
*
* Objects are leased all together in drm_lease_create(), and are
* removed all together when the lease is revoked.
struct idr leases;*/
- /**
* @lessee_idr:
*
* All lessees under this owner (only used where lessor is NULL).
@lessor so it's highlighted correctly
* Protected by &drm_device.mode_config's &drm_mode_config.idr_mutex.
struct idr lessee_idr; /* private: */*/
With the nits addressed:
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
Thanks for updating the docs! -Daniel
#if IS_ENABLED(CONFIG_DRM_LEGACY)
2.25.1
drm_file.master should be protected by either drm_device.master_mutex or drm_file.master_lookup_lock when being dereferenced. However, drm_master_get is called on unprotected file_priv->master pointers in vmw_surface_define_ioctl and vmw_gb_surface_define_internal.
This is fixed by replacing drm_master_get with drm_file_get_master.
Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com --- drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index 0eba47762bed..5d53a5f9d123 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -865,7 +865,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, user_srf->prime.base.shareable = false; user_srf->prime.base.tfile = NULL; if (drm_is_primary_client(file_priv)) - user_srf->master = drm_master_get(file_priv->master); + user_srf->master = drm_file_get_master(file_priv);
/** * From this point, the generic resource management functions @@ -1534,7 +1534,7 @@ vmw_gb_surface_define_internal(struct drm_device *dev,
user_srf = container_of(srf, struct vmw_user_surface, srf); if (drm_is_primary_client(file_priv)) - user_srf->master = drm_master_get(file_priv->master); + user_srf->master = drm_file_get_master(file_priv);
res = &user_srf->srf.res;
On Thu, Jul 22, 2021 at 05:29:29PM +0800, Desmond Cheong Zhi Xi wrote:
drm_file.master should be protected by either drm_device.master_mutex or drm_file.master_lookup_lock when being dereferenced. However, drm_master_get is called on unprotected file_priv->master pointers in vmw_surface_define_ioctl and vmw_gb_surface_define_internal.
This is fixed by replacing drm_master_get with drm_file_get_master.
Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
I'll let Zack take a look at this and expect him to push this patch to drm-misc.git. -Daniel
drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index 0eba47762bed..5d53a5f9d123 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -865,7 +865,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, user_srf->prime.base.shareable = false; user_srf->prime.base.tfile = NULL; if (drm_is_primary_client(file_priv))
user_srf->master = drm_master_get(file_priv->master);
user_srf->master = drm_file_get_master(file_priv);
/**
- From this point, the generic resource management functions
@@ -1534,7 +1534,7 @@ vmw_gb_surface_define_internal(struct drm_device *dev,
user_srf = container_of(srf, struct vmw_user_surface, srf); if (drm_is_primary_client(file_priv))
user_srf->master = drm_master_get(file_priv->master);
user_srf->master = drm_file_get_master(file_priv);
res = &user_srf->srf.res;
-- 2.25.1
On 7/22/21 5:29 AM, Desmond Cheong Zhi Xi wrote:
drm_file.master should be protected by either drm_device.master_mutex or drm_file.master_lookup_lock when being dereferenced. However, drm_master_get is called on unprotected file_priv->master pointers in vmw_surface_define_ioctl and vmw_gb_surface_define_internal.
This is fixed by replacing drm_master_get with drm_file_get_master.
Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
Reviewed-by: Zack Rusin zackr@vmware.com
Thanks for taking the time to fix this. Apart from the clear logic error, do you happen to know under what circumstances would this be hit? We have someone looking at writing some vmwgfx specific igt tests and I was wondering if I could add this to the list.
z
On 23/7/21 3:17 am, Zack Rusin wrote:
On 7/22/21 5:29 AM, Desmond Cheong Zhi Xi wrote:
drm_file.master should be protected by either drm_device.master_mutex or drm_file.master_lookup_lock when being dereferenced. However, drm_master_get is called on unprotected file_priv->master pointers in vmw_surface_define_ioctl and vmw_gb_surface_define_internal.
This is fixed by replacing drm_master_get with drm_file_get_master.
Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
Reviewed-by: Zack Rusin zackr@vmware.com
Thanks for taking the time to fix this. Apart from the clear logic error, do you happen to know under what circumstances would this be hit? We have someone looking at writing some vmwgfx specific igt tests and I was wondering if I could add this to the list.
z
Hi Zack,
Thanks for the review.
For some context, the use-after-free happens when there's a race between accessing the value of drm_file.master, and a call to drm_setmaster_ioctl. If drm_file is not the creator of master, then the ioctl allocates a new master for drm_file and puts the old master.
Thus for example, the old value of drm_file.master could be freed in between getting the value of file_priv->master, and the call to drm_master_get.
I'm not entirely sure whether this scenario is a good candidate for a test?
For further reference, the issue was originally caught by Syzbot here: https://syzkaller.appspot.com/bug?id=148d2f1dfac64af52ffd27b661981a540724f80...
And from the logs it seems that the reproducer set up a race between DRM_IOCTL_GET_UNIQUE and DRM_IOCTL_SET_MASTER. So possibly a race between VMW_CREATE_SURFACE and DRM_IOCTL_SET_MASTER could trigger the same bug.
Best wishes, Desmond
dri-devel@lists.freedesktop.org