Skip to content

Commit 27536e0

Browse files
committed
drm/i915/huc: track delayed HuC load with a fence
Given that HuC load is delayed on DG2, this patch adds support for a fence that can be used to wait for load completion. No waiters are added in this patch (they're coming up in the next one), to keep the focus of the patch on the tracking logic. The full HuC loading flow on boot DG2 is as follows: 1) i915 exports the GSC as an aux device; 2) the mei-gsc driver is loaded on the aux device; 3) the mei-pxp component is loaded; 4) mei-pxp calls back into i915 and we load the HuC. Between steps 1 and 2 there can be several seconds of gap, mainly due to the kernel doing other work during the boot. The resume flow is slightly different, because we don't need to re-expose or re-probe the aux device, so we go directly to step 3 once i915 and mei-gsc have completed their resume flow. Here's an example of the boot timing, captured with some logs added to i915: [ 17.908307] [drm] adding GSC device [ 17.915717] [drm] i915 probe done [ 22.282917] [drm] mei-gsc bound [ 22.938153] [drm] HuC authenticated Also to note is that if something goes wrong during GSC HW init the mei-gsc driver will still bind, but steps 3 and 4 will not happen. The status tracking is done by registering a bus_notifier to receive a callback when the mei-gsc driver binds, with a large enough timeout to account for delays. Once mei-gsc is bound, we switch to a smaller timeout to wait for the mei-pxp component to load. The fence is signalled on HuC load complete or if anything goes wrong in any of the tracking steps. Timeout are enforced via hrtimer callbacks. v2: fix includes (Jani) v5: gsc_notifier() remove unneeded () Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com> Reviewed-by: Alan Previn <alan.previn.teres.alexis@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20220928004145.745803-12-daniele.ceraolospurio@intel.com
1 parent 087b681 commit 27536e0

File tree

3 files changed

+241
-3
lines changed

3 files changed

+241
-3
lines changed

drivers/gpu/drm/i915/gt/intel_gsc.c

+19-3
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,14 @@ static void gsc_destroy_one(struct drm_i915_private *i915,
143143
struct intel_gsc_intf *intf = &gsc->intf[intf_id];
144144

145145
if (intf->adev) {
146-
auxiliary_device_delete(&intf->adev->aux_dev);
147-
auxiliary_device_uninit(&intf->adev->aux_dev);
146+
struct auxiliary_device *aux_dev = &intf->adev->aux_dev;
147+
148+
if (intf_id == 0)
149+
intel_huc_unregister_gsc_notifier(&gsc_to_gt(gsc)->uc.huc,
150+
aux_dev->dev.bus);
151+
152+
auxiliary_device_delete(aux_dev);
153+
auxiliary_device_uninit(aux_dev);
148154
intf->adev = NULL;
149155
}
150156

@@ -243,14 +249,24 @@ static void gsc_init_one(struct drm_i915_private *i915, struct intel_gsc *gsc,
243249
goto fail;
244250
}
245251

252+
intf->adev = adev; /* needed by the notifier */
253+
254+
if (intf_id == 0)
255+
intel_huc_register_gsc_notifier(&gsc_to_gt(gsc)->uc.huc,
256+
aux_dev->dev.bus);
257+
246258
ret = auxiliary_device_add(aux_dev);
247259
if (ret < 0) {
248260
drm_err(&i915->drm, "gsc aux add failed %d\n", ret);
261+
if (intf_id == 0)
262+
intel_huc_unregister_gsc_notifier(&gsc_to_gt(gsc)->uc.huc,
263+
aux_dev->dev.bus);
264+
intf->adev = NULL;
265+
249266
/* adev will be freed with the put_device() and .release sequence */
250267
auxiliary_device_uninit(aux_dev);
251268
goto fail;
252269
}
253-
intf->adev = adev;
254270

255271
return;
256272
fail:

drivers/gpu/drm/i915/gt/uc/intel_huc.c

+199
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#include "intel_huc.h"
1111
#include "i915_drv.h"
1212

13+
#include <linux/device/bus.h>
14+
#include <linux/mei_aux.h>
15+
1316
/**
1417
* DOC: HuC
1518
*
@@ -42,6 +45,164 @@
4245
* HuC-specific commands.
4346
*/
4447

48+
/*
49+
* MEI-GSC load is an async process. The probing of the exposed aux device
50+
* (see intel_gsc.c) usually happens a few seconds after i915 probe, depending
51+
* on when the kernel schedules it. Unless something goes terribly wrong, we're
52+
* guaranteed for this to happen during boot, so the big timeout is a safety net
53+
* that we never expect to need.
54+
* MEI-PXP + HuC load usually takes ~300ms, but if the GSC needs to be resumed
55+
* and/or reset, this can take longer.
56+
*/
57+
#define GSC_INIT_TIMEOUT_MS 10000
58+
#define PXP_INIT_TIMEOUT_MS 2000
59+
60+
static int sw_fence_dummy_notify(struct i915_sw_fence *sf,
61+
enum i915_sw_fence_notify state)
62+
{
63+
return NOTIFY_DONE;
64+
}
65+
66+
static void __delayed_huc_load_complete(struct intel_huc *huc)
67+
{
68+
if (!i915_sw_fence_done(&huc->delayed_load.fence))
69+
i915_sw_fence_complete(&huc->delayed_load.fence);
70+
}
71+
72+
static void delayed_huc_load_complete(struct intel_huc *huc)
73+
{
74+
hrtimer_cancel(&huc->delayed_load.timer);
75+
__delayed_huc_load_complete(huc);
76+
}
77+
78+
static void __gsc_init_error(struct intel_huc *huc)
79+
{
80+
huc->delayed_load.status = INTEL_HUC_DELAYED_LOAD_ERROR;
81+
__delayed_huc_load_complete(huc);
82+
}
83+
84+
static void gsc_init_error(struct intel_huc *huc)
85+
{
86+
hrtimer_cancel(&huc->delayed_load.timer);
87+
__gsc_init_error(huc);
88+
}
89+
90+
static void gsc_init_done(struct intel_huc *huc)
91+
{
92+
hrtimer_cancel(&huc->delayed_load.timer);
93+
94+
/* MEI-GSC init is done, now we wait for MEI-PXP to bind */
95+
huc->delayed_load.status = INTEL_HUC_WAITING_ON_PXP;
96+
if (!i915_sw_fence_done(&huc->delayed_load.fence))
97+
hrtimer_start(&huc->delayed_load.timer,
98+
ms_to_ktime(PXP_INIT_TIMEOUT_MS),
99+
HRTIMER_MODE_REL);
100+
}
101+
102+
static enum hrtimer_restart huc_delayed_load_timer_callback(struct hrtimer *hrtimer)
103+
{
104+
struct intel_huc *huc = container_of(hrtimer, struct intel_huc, delayed_load.timer);
105+
106+
if (!intel_huc_is_authenticated(huc)) {
107+
drm_err(&huc_to_gt(huc)->i915->drm,
108+
"timed out waiting for GSC init to load HuC\n");
109+
110+
__gsc_init_error(huc);
111+
}
112+
113+
return HRTIMER_NORESTART;
114+
}
115+
116+
static void huc_delayed_load_start(struct intel_huc *huc)
117+
{
118+
ktime_t delay;
119+
120+
GEM_BUG_ON(intel_huc_is_authenticated(huc));
121+
122+
/*
123+
* On resume we don't have to wait for MEI-GSC to be re-probed, but we
124+
* do need to wait for MEI-PXP to reset & re-bind
125+
*/
126+
switch (huc->delayed_load.status) {
127+
case INTEL_HUC_WAITING_ON_GSC:
128+
delay = ms_to_ktime(GSC_INIT_TIMEOUT_MS);
129+
break;
130+
case INTEL_HUC_WAITING_ON_PXP:
131+
delay = ms_to_ktime(PXP_INIT_TIMEOUT_MS);
132+
break;
133+
default:
134+
gsc_init_error(huc);
135+
return;
136+
}
137+
138+
/*
139+
* This fence is always complete unless we're waiting for the
140+
* GSC device to come up to load the HuC. We arm the fence here
141+
* and complete it when we confirm that the HuC is loaded from
142+
* the PXP bind callback.
143+
*/
144+
GEM_BUG_ON(!i915_sw_fence_done(&huc->delayed_load.fence));
145+
i915_sw_fence_fini(&huc->delayed_load.fence);
146+
i915_sw_fence_reinit(&huc->delayed_load.fence);
147+
i915_sw_fence_await(&huc->delayed_load.fence);
148+
i915_sw_fence_commit(&huc->delayed_load.fence);
149+
150+
hrtimer_start(&huc->delayed_load.timer, delay, HRTIMER_MODE_REL);
151+
}
152+
153+
static int gsc_notifier(struct notifier_block *nb, unsigned long action, void *data)
154+
{
155+
struct device *dev = data;
156+
struct intel_huc *huc = container_of(nb, struct intel_huc, delayed_load.nb);
157+
struct intel_gsc_intf *intf = &huc_to_gt(huc)->gsc.intf[0];
158+
159+
if (!intf->adev || &intf->adev->aux_dev.dev != dev)
160+
return 0;
161+
162+
switch (action) {
163+
case BUS_NOTIFY_BOUND_DRIVER: /* mei driver bound to aux device */
164+
gsc_init_done(huc);
165+
break;
166+
167+
case BUS_NOTIFY_DRIVER_NOT_BOUND: /* mei driver fails to be bound */
168+
case BUS_NOTIFY_UNBIND_DRIVER: /* mei driver about to be unbound */
169+
drm_info(&huc_to_gt(huc)->i915->drm,
170+
"mei driver not bound, disabling HuC load\n");
171+
gsc_init_error(huc);
172+
break;
173+
}
174+
175+
return 0;
176+
}
177+
178+
void intel_huc_register_gsc_notifier(struct intel_huc *huc, struct bus_type *bus)
179+
{
180+
int ret;
181+
182+
if (!intel_huc_is_loaded_by_gsc(huc))
183+
return;
184+
185+
huc->delayed_load.nb.notifier_call = gsc_notifier;
186+
ret = bus_register_notifier(bus, &huc->delayed_load.nb);
187+
if (ret) {
188+
drm_err(&huc_to_gt(huc)->i915->drm,
189+
"failed to register GSC notifier\n");
190+
huc->delayed_load.nb.notifier_call = NULL;
191+
gsc_init_error(huc);
192+
}
193+
}
194+
195+
void intel_huc_unregister_gsc_notifier(struct intel_huc *huc, struct bus_type *bus)
196+
{
197+
if (!huc->delayed_load.nb.notifier_call)
198+
return;
199+
200+
delayed_huc_load_complete(huc);
201+
202+
bus_unregister_notifier(bus, &huc->delayed_load.nb);
203+
huc->delayed_load.nb.notifier_call = NULL;
204+
}
205+
45206
void intel_huc_init_early(struct intel_huc *huc)
46207
{
47208
struct drm_i915_private *i915 = huc_to_gt(huc)->i915;
@@ -57,6 +218,17 @@ void intel_huc_init_early(struct intel_huc *huc)
57218
huc->status.mask = HUC_FW_VERIFIED;
58219
huc->status.value = HUC_FW_VERIFIED;
59220
}
221+
222+
/*
223+
* Initialize fence to be complete as this is expected to be complete
224+
* unless there is a delayed HuC reload in progress.
225+
*/
226+
i915_sw_fence_init(&huc->delayed_load.fence,
227+
sw_fence_dummy_notify);
228+
i915_sw_fence_commit(&huc->delayed_load.fence);
229+
230+
hrtimer_init(&huc->delayed_load.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
231+
huc->delayed_load.timer.function = huc_delayed_load_timer_callback;
60232
}
61233

62234
#define HUC_LOAD_MODE_STRING(x) (x ? "GSC" : "legacy")
@@ -122,9 +294,25 @@ void intel_huc_fini(struct intel_huc *huc)
122294
if (!intel_uc_fw_is_loadable(&huc->fw))
123295
return;
124296

297+
delayed_huc_load_complete(huc);
298+
299+
i915_sw_fence_fini(&huc->delayed_load.fence);
125300
intel_uc_fw_fini(&huc->fw);
126301
}
127302

303+
void intel_huc_suspend(struct intel_huc *huc)
304+
{
305+
if (!intel_uc_fw_is_loadable(&huc->fw))
306+
return;
307+
308+
/*
309+
* in the unlikely case that we're suspending before the GSC has
310+
* completed its loading sequence, just stop waiting. We'll restart
311+
* on resume.
312+
*/
313+
delayed_huc_load_complete(huc);
314+
}
315+
128316
int intel_huc_wait_for_auth_complete(struct intel_huc *huc)
129317
{
130318
struct intel_gt *gt = huc_to_gt(huc);
@@ -136,6 +324,9 @@ int intel_huc_wait_for_auth_complete(struct intel_huc *huc)
136324
huc->status.value,
137325
2, 50, NULL);
138326

327+
/* mark the load process as complete even if the wait failed */
328+
delayed_huc_load_complete(huc);
329+
139330
if (ret) {
140331
drm_err(&gt->i915->drm, "HuC: Firmware not verified %d\n", ret);
141332
intel_uc_fw_change_status(&huc->fw, INTEL_UC_FIRMWARE_LOAD_FAIL);
@@ -239,6 +430,12 @@ int intel_huc_check_status(struct intel_huc *huc)
239430
return intel_huc_is_authenticated(huc);
240431
}
241432

433+
static bool huc_has_delayed_load(struct intel_huc *huc)
434+
{
435+
return intel_huc_is_loaded_by_gsc(huc) &&
436+
(huc->delayed_load.status != INTEL_HUC_DELAYED_LOAD_ERROR);
437+
}
438+
242439
void intel_huc_update_auth_status(struct intel_huc *huc)
243440
{
244441
if (!intel_uc_fw_is_loadable(&huc->fw))
@@ -247,6 +444,8 @@ void intel_huc_update_auth_status(struct intel_huc *huc)
247444
if (intel_huc_is_authenticated(huc))
248445
intel_uc_fw_change_status(&huc->fw,
249446
INTEL_UC_FIRMWARE_RUNNING);
447+
else if (huc_has_delayed_load(huc))
448+
huc_delayed_load_start(huc);
250449
}
251450

252451
/**

drivers/gpu/drm/i915/gt/uc/intel_huc.h

+23
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,21 @@
77
#define _INTEL_HUC_H_
88

99
#include "i915_reg_defs.h"
10+
#include "i915_sw_fence.h"
1011
#include "intel_uc_fw.h"
1112
#include "intel_huc_fw.h"
1213

14+
#include <linux/notifier.h>
15+
#include <linux/hrtimer.h>
16+
17+
struct bus_type;
18+
19+
enum intel_huc_delayed_load_status {
20+
INTEL_HUC_WAITING_ON_GSC = 0,
21+
INTEL_HUC_WAITING_ON_PXP,
22+
INTEL_HUC_DELAYED_LOAD_ERROR,
23+
};
24+
1325
struct intel_huc {
1426
/* Generic uC firmware management */
1527
struct intel_uc_fw fw;
@@ -20,17 +32,28 @@ struct intel_huc {
2032
u32 mask;
2133
u32 value;
2234
} status;
35+
36+
struct {
37+
struct i915_sw_fence fence;
38+
struct hrtimer timer;
39+
struct notifier_block nb;
40+
enum intel_huc_delayed_load_status status;
41+
} delayed_load;
2342
};
2443

2544
void intel_huc_init_early(struct intel_huc *huc);
2645
int intel_huc_init(struct intel_huc *huc);
2746
void intel_huc_fini(struct intel_huc *huc);
47+
void intel_huc_suspend(struct intel_huc *huc);
2848
int intel_huc_auth(struct intel_huc *huc);
2949
int intel_huc_wait_for_auth_complete(struct intel_huc *huc);
3050
int intel_huc_check_status(struct intel_huc *huc);
3151
void intel_huc_update_auth_status(struct intel_huc *huc);
3252
bool intel_huc_is_authenticated(struct intel_huc *huc);
3353

54+
void intel_huc_register_gsc_notifier(struct intel_huc *huc, struct bus_type *bus);
55+
void intel_huc_unregister_gsc_notifier(struct intel_huc *huc, struct bus_type *bus);
56+
3457
static inline int intel_huc_sanitize(struct intel_huc *huc)
3558
{
3659
intel_uc_fw_sanitize(&huc->fw);

0 commit comments

Comments
 (0)