[PATCH 1/3] drm/qxl: add delayed fb operations
Daniel Vetter
daniel at ffwll.ch
Tue Jul 23 22:36:07 PDT 2013
On Wed, Jul 24, 2013 at 4:00 AM, Dave Airlie <airlied at gmail.com> wrote:
> From: Dave Airlie <airlied at redhat.com>
>
> Due to the nature of qxl hw we cannot queue operations while in an irq
> context, so we queue these operations as best we can until atomic allocations
> fail, and dequeue them later in a work queue.
>
> Daniel looked over the locking on the list and agrees it should be sufficent.
>
> The atomic allocs use no warn, as the last thing we want if we haven't memory
> to allocate space for a printk in an irq context is more printks.
>
> Signed-off-by: Dave Airlie <airlied at redhat.com>
Hm, I've thought that GFP_ATOMIC already implies that you get no
warning, so the __GFP_NOWARN looks redundant. But tbh I haven't
checked the allocator code ...
-Daniel
> ---
> drivers/gpu/drm/qxl/qxl_drv.h | 1 +
> drivers/gpu/drm/qxl/qxl_fb.c | 184 +++++++++++++++++++++++++++++++++++++-----
> 2 files changed, 164 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h
> index aacb791..6a4106f 100644
> --- a/drivers/gpu/drm/qxl/qxl_drv.h
> +++ b/drivers/gpu/drm/qxl/qxl_drv.h
> @@ -314,6 +314,7 @@ struct qxl_device {
> struct workqueue_struct *gc_queue;
> struct work_struct gc_work;
>
> + struct work_struct fb_work;
> };
>
> /* forward declaration for QXL_INFO_IO */
> diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
> index 76f39d8..88722f2 100644
> --- a/drivers/gpu/drm/qxl/qxl_fb.c
> +++ b/drivers/gpu/drm/qxl/qxl_fb.c
> @@ -37,12 +37,29 @@
>
> #define QXL_DIRTY_DELAY (HZ / 30)
>
> +#define QXL_FB_OP_FILLRECT 1
> +#define QXL_FB_OP_COPYAREA 2
> +#define QXL_FB_OP_IMAGEBLIT 3
> +
> +struct qxl_fb_op {
> + struct list_head head;
> + int op_type;
> + union {
> + struct fb_fillrect fr;
> + struct fb_copyarea ca;
> + struct fb_image ib;
> + } op;
> + void *img_data;
> +};
> +
> struct qxl_fbdev {
> struct drm_fb_helper helper;
> struct qxl_framebuffer qfb;
> struct list_head fbdev_list;
> struct qxl_device *qdev;
>
> + spinlock_t delayed_ops_lock;
> + struct list_head delayed_ops;
> void *shadow;
> int size;
>
> @@ -164,8 +181,69 @@ static struct fb_deferred_io qxl_defio = {
> .deferred_io = qxl_deferred_io,
> };
>
> -static void qxl_fb_fillrect(struct fb_info *info,
> - const struct fb_fillrect *fb_rect)
> +static void qxl_fb_delayed_fillrect(struct qxl_fbdev *qfbdev,
> + const struct fb_fillrect *fb_rect)
> +{
> + struct qxl_fb_op *op;
> + unsigned long flags;
> +
> + op = kmalloc(sizeof(struct qxl_fb_op), GFP_ATOMIC | __GFP_NOWARN);
> + if (!op)
> + return;
> +
> + op->op.fr = *fb_rect;
> + op->img_data = NULL;
> + op->op_type = QXL_FB_OP_FILLRECT;
> +
> + spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags);
> + list_add_tail(&op->head, &qfbdev->delayed_ops);
> + spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags);
> +}
> +
> +static void qxl_fb_delayed_copyarea(struct qxl_fbdev *qfbdev,
> + const struct fb_copyarea *fb_copy)
> +{
> + struct qxl_fb_op *op;
> + unsigned long flags;
> +
> + op = kmalloc(sizeof(struct qxl_fb_op), GFP_ATOMIC | __GFP_NOWARN);
> + if (!op)
> + return;
> +
> + op->op.ca = *fb_copy;
> + op->img_data = NULL;
> + op->op_type = QXL_FB_OP_COPYAREA;
> +
> + spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags);
> + list_add_tail(&op->head, &qfbdev->delayed_ops);
> + spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags);
> +}
> +
> +static void qxl_fb_delayed_imageblit(struct qxl_fbdev *qfbdev,
> + const struct fb_image *fb_image)
> +{
> + struct qxl_fb_op *op;
> + unsigned long flags;
> + uint32_t size = fb_image->width * fb_image->height * (fb_image->depth >= 8 ? fb_image->depth / 8 : 1);
> +
> + op = kmalloc(sizeof(struct qxl_fb_op) + size, GFP_ATOMIC | __GFP_NOWARN);
> + if (!op)
> + return;
> +
> + op->op.ib = *fb_image;
> + op->img_data = (void *)(op + 1);
> + op->op_type = QXL_FB_OP_IMAGEBLIT;
> +
> + memcpy(op->img_data, fb_image->data, size);
> +
> + op->op.ib.data = op->img_data;
> + spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags);
> + list_add_tail(&op->head, &qfbdev->delayed_ops);
> + spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags);
> +}
> +
> +static void qxl_fb_fillrect_internal(struct fb_info *info,
> + const struct fb_fillrect *fb_rect)
> {
> struct qxl_fbdev *qfbdev = info->par;
> struct qxl_device *qdev = qfbdev->qdev;
> @@ -203,17 +281,28 @@ static void qxl_fb_fillrect(struct fb_info *info,
> qxl_draw_fill_rec.rect = rect;
> qxl_draw_fill_rec.color = color;
> qxl_draw_fill_rec.rop = rop;
> +
> + qxl_draw_fill(&qxl_draw_fill_rec);
> +}
> +
> +static void qxl_fb_fillrect(struct fb_info *info,
> + const struct fb_fillrect *fb_rect)
> +{
> + struct qxl_fbdev *qfbdev = info->par;
> + struct qxl_device *qdev = qfbdev->qdev;
> +
> if (!drm_can_sleep()) {
> - qxl_io_log(qdev,
> - "%s: TODO use RCU, mysterious locks with spin_lock\n",
> - __func__);
> + qxl_fb_delayed_fillrect(qfbdev, fb_rect);
> + schedule_work(&qdev->fb_work);
> return;
> }
> - qxl_draw_fill(&qxl_draw_fill_rec);
> + /* make sure any previous work is done */
> + flush_work(&qdev->fb_work);
> + qxl_fb_fillrect_internal(info, fb_rect);
> }
>
> -static void qxl_fb_copyarea(struct fb_info *info,
> - const struct fb_copyarea *region)
> +static void qxl_fb_copyarea_internal(struct fb_info *info,
> + const struct fb_copyarea *region)
> {
> struct qxl_fbdev *qfbdev = info->par;
>
> @@ -223,37 +312,89 @@ static void qxl_fb_copyarea(struct fb_info *info,
> region->dx, region->dy);
> }
>
> +static void qxl_fb_copyarea(struct fb_info *info,
> + const struct fb_copyarea *region)
> +{
> + struct qxl_fbdev *qfbdev = info->par;
> + struct qxl_device *qdev = qfbdev->qdev;
> +
> + if (!drm_can_sleep()) {
> + qxl_fb_delayed_copyarea(qfbdev, region);
> + schedule_work(&qdev->fb_work);
> + return;
> + }
> + /* make sure any previous work is done */
> + flush_work(&qdev->fb_work);
> + qxl_fb_copyarea_internal(info, region);
> +}
> +
> static void qxl_fb_imageblit_safe(struct qxl_fb_image *qxl_fb_image)
> {
> qxl_draw_opaque_fb(qxl_fb_image, 0);
> }
>
> +static void qxl_fb_imageblit_internal(struct fb_info *info,
> + const struct fb_image *image)
> +{
> + struct qxl_fbdev *qfbdev = info->par;
> + struct qxl_fb_image qxl_fb_image;
> +
> + /* ensure proper order rendering operations - TODO: must do this
> + * for everything. */
> + qxl_fb_image_init(&qxl_fb_image, qfbdev->qdev, info, image);
> + qxl_fb_imageblit_safe(&qxl_fb_image);
> +}
> +
> static void qxl_fb_imageblit(struct fb_info *info,
> const struct fb_image *image)
> {
> struct qxl_fbdev *qfbdev = info->par;
> struct qxl_device *qdev = qfbdev->qdev;
> - struct qxl_fb_image qxl_fb_image;
>
> if (!drm_can_sleep()) {
> - /* we cannot do any ttm_bo allocation since that will fail on
> - * ioremap_wc..__get_vm_area_node, so queue the work item
> - * instead This can happen from printk inside an interrupt
> - * context, i.e.: smp_apic_timer_interrupt..check_cpu_stall */
> - qxl_io_log(qdev,
> - "%s: TODO use RCU, mysterious locks with spin_lock\n",
> - __func__);
> + qxl_fb_delayed_imageblit(qfbdev, image);
> + schedule_work(&qdev->fb_work);
> return;
> }
> + /* make sure any previous work is done */
> + flush_work(&qdev->fb_work);
> + qxl_fb_imageblit_internal(info, image);
> +}
>
> - /* ensure proper order of rendering operations - TODO: must do this
> - * for everything. */
> - qxl_fb_image_init(&qxl_fb_image, qfbdev->qdev, info, image);
> - qxl_fb_imageblit_safe(&qxl_fb_image);
> +static void qxl_fb_work(struct work_struct *work)
> +{
> + struct qxl_device *qdev = container_of(work, struct qxl_device, fb_work);
> + unsigned long flags;
> + struct qxl_fb_op *entry, *tmp;
> + struct qxl_fbdev *qfbdev = qdev->mode_info.qfbdev;
> +
> + /* since the irq context just adds entries to the end of the
> + list dropping the lock should be fine, as entry isn't modified
> + in the operation code */
> + spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags);
> + list_for_each_entry_safe(entry, tmp, &qfbdev->delayed_ops, head) {
> + spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags);
> + switch (entry->op_type) {
> + case QXL_FB_OP_FILLRECT:
> + qxl_fb_fillrect_internal(qfbdev->helper.fbdev, &entry->op.fr);
> + break;
> + case QXL_FB_OP_COPYAREA:
> + qxl_fb_copyarea_internal(qfbdev->helper.fbdev, &entry->op.ca);
> + break;
> + case QXL_FB_OP_IMAGEBLIT:
> + qxl_fb_imageblit_internal(qfbdev->helper.fbdev, &entry->op.ib);
> + break;
> + }
> + spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags);
> + list_del(&entry->head);
> + kfree(entry);
> + }
> + spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags);
> }
>
> int qxl_fb_init(struct qxl_device *qdev)
> {
> + INIT_WORK(&qdev->fb_work, qxl_fb_work);
> return 0;
> }
>
> @@ -536,7 +677,8 @@ int qxl_fbdev_init(struct qxl_device *qdev)
> qfbdev->qdev = qdev;
> qdev->mode_info.qfbdev = qfbdev;
> qfbdev->helper.funcs = &qxl_fb_helper_funcs;
> -
> + spin_lock_init(&qfbdev->delayed_ops_lock);
> + INIT_LIST_HEAD(&qfbdev->delayed_ops);
> ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
> qxl_num_crtc /* num_crtc - QXL supports just 1 */,
> QXLFB_CONN_LIMIT);
> --
> 1.8.3.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
More information about the dri-devel
mailing list