Commit eab1e53c authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/kraxel/tags/pull-vga-20170424-1' into staging



fix display update races, part one.
add xres + yres properties to qxl and virtio.
misc fixes and cleanups.

# gpg: Signature made Mon 24 Apr 2017 13:14:49 BST
# gpg:                using RSA key 0x4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>"
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>"
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>"
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* remotes/kraxel/tags/pull-vga-20170424-1:
  virtio-gpu: add xres and yres properties
  qxl: add xres and yres properties
  vmsvga: fix vmsvga_update_display
  g364fb: make display updates thread safe
  exynos: make display updates thread safe
  framebuffer: make display updates thread safe
  vga: make display updates thread safe.
  vga: add vga_scanline_invalidated helper
  memory: add support getting and using a dirty bitmap copy.
  bitmap: add bitmap_copy_and_clear_atomic
  virtio-gpu: replace PIXMAN_* by PIXMAN_BE_*
  console: add same displaychangelistener registration pre-condition
  console: add same surface replace pre-condition

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 4c55b1d0 729abb6a
Loading
Loading
Loading
Loading
+75 −0
Original line number Diff line number Diff line
@@ -223,6 +223,12 @@ struct CPUAddressSpace {
    MemoryListener tcg_as_listener;
};

struct DirtyBitmapSnapshot {
    ram_addr_t start;
    ram_addr_t end;
    unsigned long dirty[];
};

#endif

#if !defined(CONFIG_USER_ONLY)
@@ -1061,6 +1067,75 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start,
    return dirty;
}

DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty
     (ram_addr_t start, ram_addr_t length, unsigned client)
{
    DirtyMemoryBlocks *blocks;
    unsigned long align = 1UL << (TARGET_PAGE_BITS + BITS_PER_LEVEL);
    ram_addr_t first = QEMU_ALIGN_DOWN(start, align);
    ram_addr_t last  = QEMU_ALIGN_UP(start + length, align);
    DirtyBitmapSnapshot *snap;
    unsigned long page, end, dest;

    snap = g_malloc0(sizeof(*snap) +
                     ((last - first) >> (TARGET_PAGE_BITS + 3)));
    snap->start = first;
    snap->end   = last;

    page = first >> TARGET_PAGE_BITS;
    end  = last  >> TARGET_PAGE_BITS;
    dest = 0;

    rcu_read_lock();

    blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);

    while (page < end) {
        unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
        unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE;
        unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset);

        assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL)));
        assert(QEMU_IS_ALIGNED(num,    (1 << BITS_PER_LEVEL)));
        offset >>= BITS_PER_LEVEL;

        bitmap_copy_and_clear_atomic(snap->dirty + dest,
                                     blocks->blocks[idx] + offset,
                                     num);
        page += num;
        dest += num >> BITS_PER_LEVEL;
    }

    rcu_read_unlock();

    if (tcg_enabled()) {
        tlb_reset_dirty_range_all(start, length);
    }

    return snap;
}

bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap,
                                            ram_addr_t start,
                                            ram_addr_t length)
{
    unsigned long page, end;

    assert(start >= snap->start);
    assert(start + length <= snap->end);

    end = TARGET_PAGE_ALIGN(start + length - snap->start) >> TARGET_PAGE_BITS;
    page = (start - snap->start) >> TARGET_PAGE_BITS;

    while (page < end) {
        if (test_bit(page, snap->dirty)) {
            return true;
        }
        page++;
    }
    return false;
}

/* Called from RCU critical section */
hwaddr memory_region_section_get_iotlb(CPUState *cpu,
                                       MemoryRegionSection *section,
+6 −5
Original line number Diff line number Diff line
@@ -1263,6 +1263,7 @@ static void exynos4210_fimd_update(void *opaque)
    Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
    DisplaySurface *surface;
    Exynos4210fimdWindow *w;
    DirtyBitmapSnapshot *snap;
    int i, line;
    hwaddr fb_line_addr, inc_size;
    int scrn_height;
@@ -1291,10 +1292,12 @@ static void exynos4210_fimd_update(void *opaque)
            memory_region_sync_dirty_bitmap(w->mem_section.mr);
            host_fb_addr = w->host_fb_addr;
            fb_line_addr = w->mem_section.offset_within_region;
            snap = memory_region_snapshot_and_clear_dirty(w->mem_section.mr,
                    fb_line_addr, inc_size * scrn_height, DIRTY_MEMORY_VGA);

            for (line = 0; line < scrn_height; line++) {
                is_dirty = memory_region_get_dirty(w->mem_section.mr,
                            fb_line_addr, scrn_width, DIRTY_MEMORY_VGA);
                is_dirty = memory_region_snapshot_get_dirty(w->mem_section.mr,
                            snap, fb_line_addr, scrn_width);

                if (s->invalidate || is_dirty) {
                    if (first_line == -1) {
@@ -1309,9 +1312,7 @@ static void exynos4210_fimd_update(void *opaque)
                fb_line_addr += inc_size;
                is_dirty = false;
            }
            memory_region_reset_dirty(w->mem_section.mr,
                w->mem_section.offset_within_region,
                w->fb_len, DIRTY_MEMORY_VGA);
            g_free(snap);
            blend = true;
        }
    }
+5 −6
Original line number Diff line number Diff line
@@ -67,7 +67,7 @@ void framebuffer_update_display(
    int *first_row, /* Input and output.  */
    int *last_row /* Output only */)
{
    hwaddr src_len;
    DirtyBitmapSnapshot *snap;
    uint8_t *dest;
    uint8_t *src;
    int first, last = 0;
@@ -78,7 +78,6 @@ void framebuffer_update_display(

    i = *first_row;
    *first_row = -1;
    src_len = (hwaddr)src_width * rows;

    mem = mem_section->mr;
    if (!mem) {
@@ -102,9 +101,10 @@ void framebuffer_update_display(
    src += i * src_width;
    dest += i * dest_row_pitch;

    for (; i < rows; i++) {
        dirty = memory_region_get_dirty(mem, addr, src_width,
    snap = memory_region_snapshot_and_clear_dirty(mem, addr, src_width * rows,
                                                  DIRTY_MEMORY_VGA);
    for (; i < rows; i++) {
        dirty = memory_region_snapshot_get_dirty(mem, snap, addr, src_width);
        if (dirty || invalidate) {
            fn(opaque, dest, src, cols, dest_col_pitch);
            if (first == -1)
@@ -115,11 +115,10 @@ void framebuffer_update_display(
        src += src_width;
        dest += dest_row_pitch;
    }
    g_free(snap);
    if (first < 0) {
        return;
    }
    memory_region_reset_dirty(mem, mem_section->offset_within_region, src_len,
                              DIRTY_MEMORY_VGA);
    *first_row = first;
    *last_row = last;
}
+5 −23
Original line number Diff line number Diff line
@@ -64,16 +64,7 @@ typedef struct G364State {

static inline int check_dirty(G364State *s, ram_addr_t page)
{
    return memory_region_get_dirty(&s->mem_vram, page, G364_PAGE_SIZE,
                                   DIRTY_MEMORY_VGA);
}

static inline void reset_dirty(G364State *s,
                               ram_addr_t page_min, ram_addr_t page_max)
{
    memory_region_reset_dirty(&s->mem_vram,
                              page_min,
                              page_max + G364_PAGE_SIZE - page_min - 1,
    return memory_region_test_and_clear_dirty(&s->mem_vram, page, G364_PAGE_SIZE,
                                              DIRTY_MEMORY_VGA);
}

@@ -83,7 +74,7 @@ static void g364fb_draw_graphic8(G364State *s)
    int i, w;
    uint8_t *vram;
    uint8_t *data_display, *dd;
    ram_addr_t page, page_min, page_max;
    ram_addr_t page;
    int x, y;
    int xmin, xmax;
    int ymin, ymax;
@@ -114,8 +105,6 @@ static void g364fb_draw_graphic8(G364State *s)
    }

    page = 0;
    page_min = (ram_addr_t)-1;
    page_max = 0;

    x = y = 0;
    xmin = s->width;
@@ -137,9 +126,6 @@ static void g364fb_draw_graphic8(G364State *s)
        if (check_dirty(s, page)) {
            if (y < ymin)
                ymin = ymax = y;
            if (page_min == (ram_addr_t)-1)
                page_min = page;
            page_max = page;
            if (x < xmin)
                xmin = x;
            for (i = 0; i < G364_PAGE_SIZE; i++) {
@@ -196,10 +182,7 @@ static void g364fb_draw_graphic8(G364State *s)
                ymax = y;
        } else {
            int dy;
            if (page_min != (ram_addr_t)-1) {
                reset_dirty(s, page_min, page_max);
                page_min = (ram_addr_t)-1;
                page_max = 0;
            if (xmax || ymax) {
                dpy_gfx_update(s->con, xmin, ymin,
                               xmax - xmin + 1, ymax - ymin + 1);
                xmin = s->width;
@@ -219,9 +202,8 @@ static void g364fb_draw_graphic8(G364State *s)
    }

done:
    if (page_min != (ram_addr_t)-1) {
    if (xmax || ymax) {
        dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
        reset_dirty(s, page_min, page_max);
    }
}

+24 −10
Original line number Diff line number Diff line
@@ -305,6 +305,16 @@ void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
    qxl->ssd.cursor = cursor_builtin_hidden();
}

static uint32_t qxl_crc32(const uint8_t *p, unsigned len)
{
    /*
     * zlib xors the seed with 0xffffffff, and xors the result
     * again with 0xffffffff; Both are not done with linux's crc32,
     * which we want to be compatible with, so undo that.
     */
    return crc32(0xffffffff, p, len) ^ 0xffffffff;
}

static ram_addr_t qxl_rom_size(void)
{
#define QXL_REQUIRED_SZ (sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes))
@@ -369,6 +379,18 @@ static void init_qxl_rom(PCIQXLDevice *d)
    rom->num_pages          = cpu_to_le32(num_pages);
    rom->ram_header_offset  = cpu_to_le32(d->vga.vram_size - ram_header_size);

    if (d->xres && d->yres) {
        /* needs linux kernel 4.12+ to work */
        rom->client_monitors_config.count = 1;
        rom->client_monitors_config.heads[0].left = 0;
        rom->client_monitors_config.heads[0].top = 0;
        rom->client_monitors_config.heads[0].right = cpu_to_le32(d->xres);
        rom->client_monitors_config.heads[0].bottom = cpu_to_le32(d->yres);
        rom->client_monitors_config_crc = qxl_crc32(
            (const uint8_t *)&rom->client_monitors_config,
            sizeof(rom->client_monitors_config));
    }

    d->shadow_rom = *rom;
    d->rom        = rom;
    d->modes      = modes;
@@ -1011,16 +1033,6 @@ static void interface_set_client_capabilities(QXLInstance *sin,
    qxl_send_events(qxl, QXL_INTERRUPT_CLIENT);
}

static uint32_t qxl_crc32(const uint8_t *p, unsigned len)
{
    /*
     * zlib xors the seed with 0xffffffff, and xors the result
     * again with 0xffffffff; Both are not done with linux's crc32,
     * which we want to be compatible with, so undo that.
     */
    return crc32(0xffffffff, p, len) ^ 0xffffffff;
}

static bool qxl_rom_monitors_config_changed(QXLRom *rom,
        VDAgentMonitorsConfig *monitors_config,
        unsigned int max_outputs)
@@ -2397,6 +2409,8 @@ static Property qxl_properties[] = {
#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
        DEFINE_PROP_UINT16("max_outputs", PCIQXLDevice, max_outputs, 0),
#endif
        DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0),
        DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0),
        DEFINE_PROP_END_OF_LIST(),
};

Loading