[Spice-devel] [PATCH qxl-win v3] display/driver: reimplement DrvAssertMode for Suspend+Hibernate (S3+S4) support
Yonit Halperin
yhalperi at redhat.com
Thu Jul 7 07:00:50 PDT 2011
On 07/07/2011 12:43 PM, Alon Levy wrote:
> our old code did a very minimal flow good for resolution change:
> DrvAssertMode False (disable)
> destroy primary surface
> delete vram memslot
> DrvAssertMode True (enable)
> create primary surface (destroyed on disable)
> create vram memslot
>
> Aside: Importantly the flow for resolution change involves two pdevs, so actually the
> enable call is not called, only:
> DrvAssertMode(PDEV#1, FALSE)
> DrvEnableSurface(PDEV#2)
>
> EnableSurface creates a primary surface, so the call to AssertMode must destroy it.
>
> This fails on suspend for many reasons, one of them being that we don't disable
> operations to any driver managed off screen surfaces, and the other is that
> after the qxl reset done via acpi S3 request by windows, we don't reinitialize
> the primary memslot (this is fixed by a previous commit).
>
> The correct (per example drivers from WinDDK, and per msdn) thing to do in
> DrvAssertMode is to not do any further interaction with the device. The
> simplest way to achieve that is to fail any operation. The GDI is designed such
> that it can work completely without any driver, so for any operation there is a
> fallback in case the driver returns a failure error code. A simplification is
> to use EngModifySurface to move a surface to GDI control and so not to get any
> further callback on it (except for the deletion callback when it is deleted).
> This is also done in the 3dlabs example driver.
>
> There is zero synchronization between the miniport, which knows about the power
> state, and the displayport, which knows about the pci device structure, command
> rings, resources.
>
> As a result the easiest and also consistent with the above requirements
> implementation for suspend to ram and to disk is to reset any server side state
> during DrvAssertMode(FALSE), copying all volatile (surface contents only atm)
> memory to guest ram (from there windows will copy it to disk for us if we are
> hibernating).
>
> So the new flow for DriveAssertMode is then:
>
> AssertModeDisable:
> 1. set pdev->enable to False (all further operations will be punted)
> 2. tell server to prepare for sleep, via new QXL_IO_UPDATE_MEM(QXL_UPDATE_MEM_RENDER_ALL):
> server updates all surfaces
> server destroys all surfaces. Since we never sent it a destroy command
> this doesn't trigger any guest side surface destruction, only a release of
> the creation command resources.
> 3. release anything in the release ring.
> 4. tell server to write it's current releasable items list (qxl->last_release)
> to the release_ring (we just made sure the release_ring is empty, and since we
> are not sending anything new to the worker and we already had it render
> anything outstanding it will not fill it) release the last resources (at this
> point there is nothing allocated on devram and vram - we verify that in debug
> mode with DUMP_MSPACE_VRAM and DUMP_MSPACE_DEVRAM).
> 5. Destroy primary surface
> 6. Delete vram memslot
>
> AssertModeEnable:
> 1. Create primary surface
> 2. create vram memslot
> 3. copy surfaces from ram to vram, sending create commands which cause both
> create and surface image commands to be sent to the client.
>
> In suspend there is a single PDEV involved which does exactly those two calls,
> disable before sleep and enable after.
>
> For resolution change this work is excessive but correct. Since
> miniport:SetPowerState is called after DrvAssertMode during suspend (actually
> there is no defined order in the documentation), there is no way to distinguish
> between the two anyway.
>
> Cc: Yonit Halperin<yhalperi at redhat.com>
> ---
> display/driver.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++------
> display/surface.c | 1 +
> 2 files changed, 62 insertions(+), 8 deletions(-)
>
> diff --git a/display/driver.c b/display/driver.c
> index a231bb4..fff462d 100644
> --- a/display/driver.c
> +++ b/display/driver.c
> @@ -939,7 +939,9 @@ VOID DrvDisableSurface(DHPDEV in_pdev)
>
> DEBUG_PRINT((pdev, 1, "%s: 0x%lx\n", __FUNCTION__, pdev));
>
> - DisableQXLPrimarySurface(pdev, 1 /* hide mouse */);
> + // Don't destroy the primary - it's destroyed by destroy_all_surfaces
> + // at AssertModeDisable
I can't understand from msdn if DrvAssertMode(disable) is always called
before DrvDisableSurface
> + pdev->surf_enable = FALSE;
> UnmapFB(pdev);
>
> if (pdev->surf) {
> @@ -953,25 +955,76 @@ VOID DrvDisableSurface(DHPDEV in_pdev)
> pdev->mem_slots = NULL;
> }
>
> - DEBUG_PRINT((NULL, 1, "%s: 0x%lx exit\n", __FUNCTION__, pdev));
> + DEBUG_PRINT((pdev, 1, "%s: 0x%lx exit\n", __FUNCTION__, pdev));
> +}
> +
> +static BOOL AssertModeDisable(PDev *pdev)
> +{
> + DEBUG_PRINT((pdev, 3, "%s entry\n", __FUNCTION__));
> + /* flush command ring and update all surfaces */
> + WRITE_PORT_UCHAR(pdev->notify_cmd_port, 0);
> + WRITE_PORT_UCHAR(pdev->notify_cursor_port, 0);
don't think the above two IOs are needed. flush_surface will do the job.
> + WRITE_PORT_UCHAR(pdev->flush_surfaces_port, 0);
> + WRITE_PORT_UCHAR(pdev->destroy_all_surfaces_port, 0);
> + /* move all surfaces from device to system memory */
> + if (!MoveAllSurfacesToRam(pdev)) {
> + return FALSE;
> + }
> + /* Free release ring contents */
> + ReleaseCacheDeviceMemoryResources(pdev);
> + EmptyReleaseRing(pdev);
> + /* Get the last free list onto the release ring */
> + WRITE_PORT_UCHAR(pdev->flush_release_port, 0);
> + DEBUG_PRINT((pdev, 4, "%s after FLUSH_RELEASE\n", __FUNCTION__));
> + /* And release that. mspace allocators should be clean after. */
> + EmptyReleaseRing(pdev);
> + RemoveVRamSlot(pdev);
> + DebugCountAliveSurfaces(pdev);
> + DEBUG_PRINT((pdev, 4, "%s: [%d,%d] [%d,%d] [%d,%d] %lx\n", __FUNCTION__,
> + pdev->cmd_ring->prod, pdev->cmd_ring->cons,
> + pdev->cursor_ring->prod, pdev->cursor_ring->cons,
> + pdev->release_ring->prod, pdev->release_ring->cons,
> + pdev->Res->free_outputs));
> + DEBUG_PRINT((pdev, 4, "%s exit\n", __FUNCTION__));
> + return TRUE;
> +}
> +
> +static void AssertModeEnable(PDev *pdev)
> +{
> + InitResources(pdev);
> + InitDeviceMemoryResources(pdev);
> + DEBUG_PRINT((pdev, 3, "%s: [%d,%d] [%d,%d] [%d,%d] %lx\n", __FUNCTION__,
> + pdev->cmd_ring->prod, pdev->cmd_ring->cons,
> + pdev->cursor_ring->prod, pdev->cursor_ring->cons,
> + pdev->release_ring->prod, pdev->release_ring->cons,
> + pdev->Res->free_outputs));
> + EnableQXLPrimarySurface(pdev);
> + CreateVRamSlot(pdev);
> + DebugCountAliveSurfaces(pdev);
> + MoveAllSurfacesToVideoRam(pdev);
> + DebugCountAliveSurfaces(pdev);
> }
>
> BOOL DrvAssertMode(DHPDEV in_pdev, BOOL enable)
> {
> PDev* pdev = (PDev*)in_pdev;
> + BOOL ret = TRUE;
>
> DEBUG_PRINT((pdev, 1, "%s: 0x%lx %d\n", __FUNCTION__, pdev, enable));
>
> + if (pdev->enabled == enable) {
> + DEBUG_PRINT((pdev, 1, "%s: called twice with same argument (%d)\n", __FUNCTION__,
> + enable));
> + return TRUE;
> + }
> + pdev->enabled = enable;
> if (enable) {
> - InitResources(pdev);
> - EnableQXLPrimarySurface(pdev);
> - CreateVRamSlot(pdev);
> + AssertModeEnable(pdev);
> } else {
> - DisableQXLPrimarySurface(pdev, 0);
> - RemoveVRamSlot(pdev);
> + ret = AssertModeDisable(pdev);
I'm not sure about changing pdev->enabled if AssertModeDisable fails
(which means part of the surfaces are still on the vram)
> }
> DEBUG_PRINT((pdev, 1, "%s: 0x%lx exit %d\n", __FUNCTION__, pdev, enable));
> - return TRUE;
> + return ret;
> }
>
> ULONG DrvGetModes(HANDLE driver, ULONG dev_modes_size, DEVMODEW *dev_modes)
> diff --git a/display/surface.c b/display/surface.c
> index 8f7aae1..b8628fe 100644
> --- a/display/surface.c
> +++ b/display/surface.c
> @@ -249,6 +249,7 @@ BOOL MoveSurfaceToVideoRam(PDev *pdev, UINT32 surface_id)
> surface_info->phys_mem = phys_mem;
> surface_info->draw_area.base_mem = base_mem;
> surface = SurfaceCmd(pdev, QXL_SURFACE_CMD_CREATE, surface_id);
> + surface->flags |= QXL_SURF_FLAG_KEEP_DATA;
> surface->u.surface_create.format = surface_format;
> surface->u.surface_create.width = cx;
> surface->u.surface_create.height = cy;
More information about the Spice-devel
mailing list