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