[Nouveau] Busy poll during commit

Greg Depoire--Ferrer greg at gregdf.com
Mon Aug 8 20:57:30 UTC 2022


Hi,

Moving the cursor in circles on Linux 5.18 with a GTX 1070 mobile and a 120 Hz
display uses ~30% CPU.

Using perf shows most of the instructions are spent polling in
base507c_ntfy_wait_begun in drivers/gpu/drm/nouveau/dispnv50/base507c.c:

int
base507c_ntfy_wait_begun(struct nouveau_bo *bo, u32 offset,
			 struct nvif_device *device)
{
	s64 time = nvif_msec(device, 2000ULL,
		if (NVBO_TD32(bo, offset, NV_DISP_BASE_NOTIFIER_1, _0, STATUS, ==, BEGUN))
			break;
		usleep_range(1, 2);
	);
	return time < 0 ? time : 0;
}

That function is called from drivers/gpu/drm/nouveau/dispnv50/wndw.c:

int
nv50_wndw_wait_armed(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
{
	struct nv50_disp *disp = nv50_disp(wndw->plane.dev);
	if (asyw->set.ntfy) {
		return wndw->func->ntfy_wait_begun(disp->sync,
						   asyw->ntfy.offset,
						   wndw->wndw.base.device);
	}
	return 0;
}

Which in turn is called from nv50_disp_atomic_commit_tail in
drivers/gpu/drm/nouveau/dispnv50/disp.c:

	/* Wait for HW to signal completion. */
	for_each_new_plane_in_state(state, plane, new_plane_state, i) {
		struct nv50_wndw_atom *asyw = nv50_wndw_atom(new_plane_state);
		struct nv50_wndw *wndw = nv50_wndw(plane);
		int ret = nv50_wndw_wait_armed(wndw, asyw);
		if (ret)
			NV_ERROR(drm, "%s: timeout\n", plane->name);
	}

I haven't found many resources on how commits work. Could busy polling be
replaced by something else?

Additional information that might be helpful: with printks I found that
nv50_head_vblank_handler in drivers/gpu/drm/nouveau/dispnv50/head.c is always
called right before base507c_ntfy_wait_begun finished.

So for instance this patch drops the CPU usage by making sure that
base507c_ntfy_wait_begun does not even enter the loop using
drm_atomic_helper_wait_for_vblanks to wait for the vblank.

diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index 5863a689818c..f535ded86b2f 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -2186,6 +2186,8 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
	if (atom->lock_core)
		mutex_unlock(&disp->mutex);

+	drm_atomic_helper_wait_for_vblanks(dev, state);
+
	/* Wait for HW to signal completion. */
	for_each_new_plane_in_state(state, plane, new_plane_state, i) {
		struct nv50_wndw_atom *asyw = nv50_wndw_atom(new_plane_state);

Regards,
Greg


More information about the Nouveau mailing list