Skip to content

Commit 882f38b

Browse files
vsyrjalajlahtine-intel
authored andcommitted
drm/i915: Fix global state use-after-frees with a refcount
While the current locking/serialization of the global state suffices for protecting the obj->state access and the actual hardware reprogramming, we do have a problem with accessing the old/new states during nonblocking commits. The state computation and swap will be protected by the crtc locks, but the commit_tails can finish out of order, thus also causing the atomic states to be cleaned up out of order. This would mean the commit that started first but finished last has had its new state freed as the no-longer-needed old state by the other commit. To fix this let's just refcount the states. obj->state amounts to one reference, and the intel_atomic_state holds extra references to both its new and old global obj states. Fixes: 0ef1905 ("drm/i915: Introduce better global state handling") Signed-off-by: Ville Syrjälä <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected] Reviewed-by: Stanislav Lisovskiy <[email protected]> (cherry picked from commit f8c86ff) Signed-off-by: Joonas Lahtinen <[email protected]>
1 parent 0e38695 commit 882f38b

File tree

2 files changed

+42
-6
lines changed

2 files changed

+42
-6
lines changed

drivers/gpu/drm/i915/display/intel_global_state.c

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,39 @@
1010
#include "intel_display_types.h"
1111
#include "intel_global_state.h"
1212

13+
static void __intel_atomic_global_state_free(struct kref *kref)
14+
{
15+
struct intel_global_state *obj_state =
16+
container_of(kref, struct intel_global_state, ref);
17+
struct intel_global_obj *obj = obj_state->obj;
18+
19+
obj->funcs->atomic_destroy_state(obj, obj_state);
20+
}
21+
22+
static void intel_atomic_global_state_put(struct intel_global_state *obj_state)
23+
{
24+
kref_put(&obj_state->ref, __intel_atomic_global_state_free);
25+
}
26+
27+
static struct intel_global_state *
28+
intel_atomic_global_state_get(struct intel_global_state *obj_state)
29+
{
30+
kref_get(&obj_state->ref);
31+
32+
return obj_state;
33+
}
34+
1335
void intel_atomic_global_obj_init(struct drm_i915_private *dev_priv,
1436
struct intel_global_obj *obj,
1537
struct intel_global_state *state,
1638
const struct intel_global_state_funcs *funcs)
1739
{
1840
memset(obj, 0, sizeof(*obj));
1941

42+
state->obj = obj;
43+
44+
kref_init(&state->ref);
45+
2046
obj->state = state;
2147
obj->funcs = funcs;
2248
list_add_tail(&obj->head, &dev_priv->global_obj_list);
@@ -28,7 +54,9 @@ void intel_atomic_global_obj_cleanup(struct drm_i915_private *dev_priv)
2854

2955
list_for_each_entry_safe(obj, next, &dev_priv->global_obj_list, head) {
3056
list_del(&obj->head);
31-
obj->funcs->atomic_destroy_state(obj, obj->state);
57+
58+
drm_WARN_ON(&dev_priv->drm, kref_read(&obj->state->ref) != 1);
59+
intel_atomic_global_state_put(obj->state);
3260
}
3361
}
3462

@@ -97,10 +125,14 @@ intel_atomic_get_global_obj_state(struct intel_atomic_state *state,
97125
if (!obj_state)
98126
return ERR_PTR(-ENOMEM);
99127

128+
obj_state->obj = obj;
100129
obj_state->changed = false;
101130

131+
kref_init(&obj_state->ref);
132+
102133
state->global_objs[index].state = obj_state;
103-
state->global_objs[index].old_state = obj->state;
134+
state->global_objs[index].old_state =
135+
intel_atomic_global_state_get(obj->state);
104136
state->global_objs[index].new_state = obj_state;
105137
state->global_objs[index].ptr = obj;
106138
obj_state->state = state;
@@ -163,7 +195,9 @@ void intel_atomic_swap_global_state(struct intel_atomic_state *state)
163195
new_obj_state->state = NULL;
164196

165197
state->global_objs[i].state = old_obj_state;
166-
obj->state = new_obj_state;
198+
199+
intel_atomic_global_state_put(obj->state);
200+
obj->state = intel_atomic_global_state_get(new_obj_state);
167201
}
168202
}
169203

@@ -172,10 +206,9 @@ void intel_atomic_clear_global_state(struct intel_atomic_state *state)
172206
int i;
173207

174208
for (i = 0; i < state->num_global_objs; i++) {
175-
struct intel_global_obj *obj = state->global_objs[i].ptr;
209+
intel_atomic_global_state_put(state->global_objs[i].old_state);
210+
intel_atomic_global_state_put(state->global_objs[i].new_state);
176211

177-
obj->funcs->atomic_destroy_state(obj,
178-
state->global_objs[i].state);
179212
state->global_objs[i].ptr = NULL;
180213
state->global_objs[i].state = NULL;
181214
state->global_objs[i].old_state = NULL;

drivers/gpu/drm/i915/display/intel_global_state.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#ifndef __INTEL_GLOBAL_STATE_H__
77
#define __INTEL_GLOBAL_STATE_H__
88

9+
#include <linux/kref.h>
910
#include <linux/list.h>
1011

1112
struct drm_i915_private;
@@ -54,7 +55,9 @@ struct intel_global_obj {
5455
for_each_if(obj)
5556

5657
struct intel_global_state {
58+
struct intel_global_obj *obj;
5759
struct intel_atomic_state *state;
60+
struct kref ref;
5861
bool changed;
5962
};
6063

0 commit comments

Comments
 (0)