Page MenuHomeFreeBSD

D55012.id178333.diff
No OneTemporary

D55012.id178333.diff

diff --git a/sys/dev/virtio/gpu/virtio_gpu.c b/sys/dev/virtio/gpu/virtio_gpu.c
--- a/sys/dev/virtio/gpu/virtio_gpu.c
+++ b/sys/dev/virtio/gpu/virtio_gpu.c
@@ -35,9 +35,13 @@
#include <sys/callout.h>
#include <sys/fbio.h>
#include <sys/kernel.h>
+#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
+#include <sys/mutex.h>
#include <sys/sglist.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
#include <machine/atomic.h>
#include <machine/bus.h>
@@ -61,6 +65,14 @@
/* The guest can allocate resource IDs, we only need one */
#define VTGPU_RESOURCE_ID 1
+static int vtgpu_width_override = 0;
+static int vtgpu_height_override = 0;
+static int vtgpu_flush_enabled = 0;
+
+TUNABLE_INT("hw.virtio_gpu.width", &vtgpu_width_override);
+TUNABLE_INT("hw.virtio_gpu.height", &vtgpu_height_override);
+TUNABLE_INT("hw.virtio_gpu.flush_enabled", &vtgpu_flush_enabled);
+
struct vtgpu_softc {
/* Must be first so we can cast from info -> softc */
struct fb_info vtgpu_fb_info;
@@ -70,9 +82,18 @@
uint64_t vtgpu_features;
struct virtqueue *vtgpu_ctrl_vq;
+ struct mtx vtgpu_mtx;
uint64_t vtgpu_next_fence;
+ struct callout vtgpu_flush_callout;
+ struct task vtgpu_flush_task;
+ bool vtgpu_fb_registered;
+ bool vtgpu_detaching;
+ bool vtgpu_flush_enabled; /* periodic flush on/off */
+ int vtgpu_refresh_rate; /* FPS for periodic flush */
+ struct sysctl_ctx_list vtgpu_sysctl_ctx;
+
bool vtgpu_have_fb_info;
};
@@ -81,6 +102,7 @@
static int vtgpu_probe(device_t);
static int vtgpu_attach(device_t);
static int vtgpu_detach(device_t);
+static int vtgpu_shutdown(device_t);
static int vtgpu_negotiate_features(struct vtgpu_softc *);
static int vtgpu_setup_features(struct vtgpu_softc *);
@@ -96,6 +118,13 @@
uint32_t, uint32_t, uint32_t);
static int vtgpu_resource_flush(struct vtgpu_softc *, uint32_t, uint32_t,
uint32_t, uint32_t);
+static void vtgpu_flush_callout_cb(void *);
+static void vtgpu_flush_task_cb(void *, int);
+static int vtgpu_sysctl_refresh_rate(SYSCTL_HANDLER_ARGS);
+
+#define VTGPU_DEFAULT_REFRESH_RATE 30
+#define VTGPU_FLUSH_INTERVAL(sc) \
+ (hz / ((sc)->vtgpu_refresh_rate > 0 ? (sc)->vtgpu_refresh_rate : 1))
static vd_blank_t vtgpu_fb_blank;
static vd_bitblt_text_t vtgpu_fb_bitblt_text;
@@ -103,6 +132,7 @@
static vd_drawrect_t vtgpu_fb_drawrect;
static vd_setpixel_t vtgpu_fb_setpixel;
static vd_bitblt_argb_t vtgpu_fb_bitblt_argb;
+static vd_fb_mmap_t vtgpu_fb_mmap;
static struct vt_driver vtgpu_fb_driver = {
.vd_name = "virtio_gpu",
@@ -118,7 +148,7 @@
.vd_postswitch = vt_fb_postswitch,
.vd_priority = VD_PRIORITY_GENERIC+10,
.vd_fb_ioctl = vt_fb_ioctl,
- .vd_fb_mmap = NULL, /* No mmap as we need to signal the host */
+ .vd_fb_mmap = vtgpu_fb_mmap,
.vd_suspend = vt_fb_suspend,
.vd_resume = vt_fb_resume,
};
@@ -226,6 +256,23 @@
vtgpu_resource_flush(sc, x, y, 1, 1);
}
+static int
+vtgpu_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr,
+ int prot, vm_memattr_t *memattr)
+{
+ struct vtgpu_softc *sc;
+ struct fb_info *info;
+
+ info = vd->vd_softc;
+ sc = (struct vtgpu_softc *)info;
+
+ /* Deny mmap when periodic flush is not active */
+ if (!sc->vtgpu_flush_enabled)
+ return (ENODEV);
+
+ return (vt_fb_mmap(vd, offset, paddr, prot, memattr));
+}
+
static struct virtio_feature_desc vtgpu_feature_desc[] = {
{ VIRTIO_GPU_F_VIRGL, "VirGL" },
{ VIRTIO_GPU_F_EDID, "EDID" },
@@ -240,6 +287,7 @@
DEVMETHOD(device_probe, vtgpu_probe),
DEVMETHOD(device_attach, vtgpu_attach),
DEVMETHOD(device_detach, vtgpu_detach),
+ DEVMETHOD(device_shutdown, vtgpu_shutdown),
DEVMETHOD_END
};
@@ -293,6 +341,13 @@
sc->vtgpu_have_fb_info = false;
sc->vtgpu_dev = dev;
sc->vtgpu_next_fence = 1;
+ sc->vtgpu_detaching = false;
+ sc->vtgpu_fb_registered = false;
+
+ TASK_INIT(&sc->vtgpu_flush_task, 0, vtgpu_flush_task_cb, sc);
+ callout_init(&sc->vtgpu_flush_callout, 1);
+ mtx_init(&sc->vtgpu_mtx, "vtgpu", NULL, MTX_DEF);
+
virtio_set_feature_desc(dev, vtgpu_feature_desc);
error = vtgpu_setup_features(sc);
@@ -317,6 +372,21 @@
goto fail;
}
+ sc->vtgpu_refresh_rate = VTGPU_DEFAULT_REFRESH_RATE;
+ sc->vtgpu_flush_enabled = vtgpu_flush_enabled ? true : false;
+ sysctl_ctx_init(&sc->vtgpu_sysctl_ctx);
+ SYSCTL_ADD_INT(&sc->vtgpu_sysctl_ctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "flush_enabled", CTLFLAG_RD, &vtgpu_flush_enabled, 0,
+ "Periodic framebuffer flush");
+ if (sc->vtgpu_flush_enabled) {
+ SYSCTL_ADD_PROC(&sc->vtgpu_sysctl_ctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "refresh_rate", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, vtgpu_sysctl_refresh_rate, "I",
+ "Framebuffer flush rate in FPS");
+ }
+
/*
* TODO: This doesn't need to be contigmalloc as we
* can use scatter-gather lists.
@@ -325,6 +395,13 @@
sc->vtgpu_fb_info.fb_size, M_DEVBUF, M_WAITOK|M_ZERO, 0, ~0, 4, 0);
sc->vtgpu_fb_info.fb_pbase = pmap_kextract(sc->vtgpu_fb_info.fb_vbase);
+ /*
+ * Since this is a shadow buffer in regular RAM (not actual GPU VRAM),
+ * use VM_MEMATTR_DEFAULT
+ */
+ sc->vtgpu_fb_info.fb_flags |= FB_FLAG_MEMATTR;
+ sc->vtgpu_fb_info.fb_memattr = VM_MEMATTR_DEFAULT;
+
/* Create the 2d resource */
error = vtgpu_create_2d(sc);
if (error != 0) {
@@ -347,12 +424,29 @@
vt_allocate(&vtgpu_fb_driver, &sc->vtgpu_fb_info);
sc->vtgpu_have_fb_info = true;
+ error = fbd_register(&sc->vtgpu_fb_info);
+ if (error != 0 && error != EEXIST) {
+ device_printf(dev, "warning: cannot register /dev/fb device"
+ " (error %d)\n", error);
+ } else {
+ device_printf(dev, "/dev/fb0 created\n");
+ sc->vtgpu_fb_registered = true;
+ }
+
error = vtgpu_transfer_to_host_2d(sc, 0, 0, sc->vtgpu_fb_info.fb_width,
sc->vtgpu_fb_info.fb_height);
if (error != 0)
goto fail;
error = vtgpu_resource_flush(sc, 0, 0, sc->vtgpu_fb_info.fb_width,
sc->vtgpu_fb_info.fb_height);
+ if (error != 0)
+ goto fail;
+
+ /* Start the periodic flush mechanism if enabled */
+ if (sc->vtgpu_fb_registered && sc->vtgpu_flush_enabled) {
+ callout_reset(&sc->vtgpu_flush_callout, VTGPU_FLUSH_INTERVAL(sc),
+ vtgpu_flush_callout_cb, sc);
+ }
fail:
if (error != 0)
@@ -367,19 +461,101 @@
struct vtgpu_softc *sc;
sc = device_get_softc(dev);
- if (sc->vtgpu_have_fb_info)
+
+ /* Stop the periodic flush callout and task */
+ sc->vtgpu_detaching = true;
+ callout_drain(&sc->vtgpu_flush_callout);
+ taskqueue_drain(taskqueue_thread, &sc->vtgpu_flush_task);
+
+ /* Unregister the framebuffer device */
+ if (sc->vtgpu_fb_registered) {
+ fbd_unregister(&sc->vtgpu_fb_info);
+ sc->vtgpu_fb_registered = false;
+ }
+
+ if (sc->vtgpu_have_fb_info) {
vt_deallocate(&vtgpu_fb_driver, &sc->vtgpu_fb_info);
+ }
if (sc->vtgpu_fb_info.fb_vbase != 0) {
MPASS(sc->vtgpu_fb_info.fb_size != 0);
free((void *)sc->vtgpu_fb_info.fb_vbase,
M_DEVBUF);
}
+ mtx_destroy(&sc->vtgpu_mtx);
+
+ sysctl_ctx_free(&sc->vtgpu_sysctl_ctx);
+
/* TODO: Tell the host we are detaching */
return (0);
}
+static int
+vtgpu_shutdown(device_t dev)
+{
+ struct vtgpu_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->vtgpu_detaching = true;
+ callout_drain(&sc->vtgpu_flush_callout);
+ taskqueue_drain(taskqueue_thread, &sc->vtgpu_flush_task);
+
+ return (0);
+}
+
+static int
+vtgpu_sysctl_refresh_rate(SYSCTL_HANDLER_ARGS)
+{
+ struct vtgpu_softc *sc = arg1;
+ int rate = sc->vtgpu_refresh_rate;
+ int error;
+
+ error = sysctl_handle_int(oidp, &rate, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (!sc->vtgpu_flush_enabled)
+ return (EPERM);
+
+ if (rate < 1 || rate > 120)
+ return (EINVAL);
+
+ sc->vtgpu_refresh_rate = rate;
+ return (0);
+}
+
+static void
+vtgpu_flush_callout_cb(void *arg)
+{
+ struct vtgpu_softc *sc = arg;
+
+ if (!sc->vtgpu_detaching && sc->vtgpu_flush_enabled)
+ taskqueue_enqueue(taskqueue_thread, &sc->vtgpu_flush_task);
+}
+
+static void
+vtgpu_flush_task_cb(void *arg, int pending __unused)
+{
+ struct vtgpu_softc *sc = arg;
+
+ if (sc->vtgpu_detaching)
+ return;
+
+ /* Transfer the entire framebuffer to the host and flush */
+ vtgpu_transfer_to_host_2d(sc, 0, 0, sc->vtgpu_fb_info.fb_width,
+ sc->vtgpu_fb_info.fb_height);
+ vtgpu_resource_flush(sc, 0, 0, sc->vtgpu_fb_info.fb_width,
+ sc->vtgpu_fb_info.fb_height);
+
+ /* Reschedule the callout for the next flush */
+ if (!sc->vtgpu_detaching && sc->vtgpu_flush_enabled)
+ callout_reset(&sc->vtgpu_flush_callout,
+ VTGPU_FLUSH_INTERVAL(sc),
+ vtgpu_flush_callout_cb, sc);
+}
+
static int
vtgpu_negotiate_features(struct vtgpu_softc *sc)
{
@@ -452,6 +628,8 @@
struct sglist_seg segs[3];
int error, rcount;
+ mtx_lock(&sc->vtgpu_mtx);
+
sglist_init(&sg, 3, segs);
rcount = 1;
@@ -460,6 +638,7 @@
device_printf(sc->vtgpu_dev,
"Unable to append the request to the sglist: %d\n",
error);
+ mtx_unlock(&sc->vtgpu_mtx);
return (error);
}
if (req2 != NULL) {
@@ -468,6 +647,7 @@
device_printf(sc->vtgpu_dev,
"Unable to append the request to the sglist: %d\n",
error);
+ mtx_unlock(&sc->vtgpu_mtx);
return (error);
}
rcount++;
@@ -477,17 +657,21 @@
device_printf(sc->vtgpu_dev,
"Unable to append the response buffer to the sglist: %d\n",
error);
+ mtx_unlock(&sc->vtgpu_mtx);
return (error);
}
error = virtqueue_enqueue(sc->vtgpu_ctrl_vq, resp, &sg, rcount, 1);
if (error != 0) {
device_printf(sc->vtgpu_dev, "Enqueue failed: %d\n", error);
+ mtx_unlock(&sc->vtgpu_mtx);
return (error);
}
virtqueue_notify(sc->vtgpu_ctrl_vq);
virtqueue_poll(sc->vtgpu_ctrl_vq, NULL);
+ mtx_unlock(&sc->vtgpu_mtx);
+
return (0);
}
@@ -527,6 +711,18 @@
le32toh(s.resp.pmodes[i].r.width);
sc->vtgpu_fb_info.fb_height =
le32toh(s.resp.pmodes[i].r.height);
+
+ if (vtgpu_width_override > 0 && vtgpu_height_override > 0) {
+ device_printf(sc->vtgpu_dev,
+ "overriding resolution %dx%d -> %dx%d\n",
+ sc->vtgpu_fb_info.fb_width,
+ sc->vtgpu_fb_info.fb_height,
+ vtgpu_width_override,
+ vtgpu_height_override);
+ sc->vtgpu_fb_info.fb_width = vtgpu_width_override;
+ sc->vtgpu_fb_info.fb_height = vtgpu_height_override;
+ }
+
/* 32 bits per pixel */
sc->vtgpu_fb_info.fb_bpp = 32;
sc->vtgpu_fb_info.fb_depth = 32;
diff --git a/sys/dev/vt/vt_core.c b/sys/dev/vt/vt_core.c
--- a/sys/dev/vt/vt_core.c
+++ b/sys/dev/vt/vt_core.c
@@ -3408,7 +3408,7 @@
* it is bad idea to replace KMS driver with generic VGA one.
*/
if (drv->vd_priority <= main_vd->vd_driver->vd_priority) {
- printf("VT: Driver priority %d too low. Current %d\n ",
+ printf("VT: Driver priority %d too low. Current %d\n",
drv->vd_priority, main_vd->vd_driver->vd_priority);
return (EEXIST);
}

File Metadata

Mime Type
text/plain
Expires
Fri, Sep 11, 2:42 PM (4 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38711555
Default Alt Text
D55012.id178333.diff (10 KB)

Event Timeline