From d255d94ea935d327e4e811023b05908307b52246 Mon Sep 17 00:00:00 2001 From: "hyesoo.yu" Date: Mon, 26 Jun 2017 18:35:26 +0900 Subject: [PATCH] [COMMON] g2d: add priority setting All contexts have priority. The requested task inherits the priority of the context. Updating the priority of the context always succeeds, but EBUSY could be returned if other tasks is running or queued with a priority lower than its own. If the priority of the context is lower than the priority of the other context, the driver doesn't accept lower context's request, It is called S/W preemption. Change-Id: I82650f516ac019eb77f853a948b7f11cfe0578ae Signed-off-by: hyesoo.yu --- drivers/gpu/exynos/g2d/g2d.h | 12 +++++ drivers/gpu/exynos/g2d/g2d_drv.c | 78 ++++++++++++++++++++++++++++++- drivers/gpu/exynos/g2d/g2d_task.c | 4 +- drivers/gpu/exynos/g2d/g2d_task.h | 3 +- drivers/gpu/exynos/g2d/g2d_uapi.h | 1 + 5 files changed, 95 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/exynos/g2d/g2d.h b/drivers/gpu/exynos/g2d/g2d.h index b6b86f793e47..22fac19314ae 100644 --- a/drivers/gpu/exynos/g2d/g2d.h +++ b/drivers/gpu/exynos/g2d/g2d.h @@ -21,6 +21,15 @@ struct g2d_task; /* defined in g2d_task.h */ +enum g2d_priority { + G2D_LOW_PRIORITY, + G2D_MEDIUM_PRIORITY, + G2D_DEFAULT_PRIORITY = G2D_MEDIUM_PRIORITY, + G2D_HIGH_PRIORITY, + G2D_HIGHEST_PRIORITY, + G2D_PRIORITY_END +}; + /* * G2D_DEVICE_STATE_SUSPEND should be treated under g2d_dev->lock_task held * because it should be consistent with the state of all tasks attached to @@ -56,11 +65,14 @@ struct g2d_device { struct dentry *debug_root; struct dentry *debug; struct dentry *debug_logs; + + atomic_t prior_stats[G2D_PRIORITY_END]; }; struct g2d_context { struct g2d_device *g2d_dev; struct shared_buffer_info *hwfc_info; + u32 priority; }; int g2d_device_run(struct g2d_device *g2d_dev, struct g2d_task *task); diff --git a/drivers/gpu/exynos/g2d/g2d_drv.c b/drivers/gpu/exynos/g2d/g2d_drv.c index 8c7a98491c17..18cdb5b9581f 100644 --- a/drivers/gpu/exynos/g2d/g2d_drv.c +++ b/drivers/gpu/exynos/g2d/g2d_drv.c @@ -33,6 +33,38 @@ #define MODULE_NAME "exynos-g2d" +static int g2d_update_priority(struct g2d_context *ctx, + enum g2d_priority priority) +{ + struct g2d_device *g2d_dev = ctx->g2d_dev; + struct g2d_task *task; + unsigned long flags; + + if (ctx->priority == priority) + return 0; + + atomic_dec(&g2d_dev->prior_stats[ctx->priority]); + atomic_inc(&g2d_dev->prior_stats[priority]); + ctx->priority = priority; + + /* + * check lower priority task in use, and return EBUSY + * for higher priority to avoid waiting lower task completion + */ + spin_lock_irqsave(&g2d_dev->lock_task, flags); + + for (task = g2d_dev->tasks; task != NULL; task = task->next) { + if (!is_task_state_idle(task) && (task->priority < priority)) { + spin_unlock_irqrestore(&g2d_dev->lock_task, flags); + return -EBUSY; + } + } + + spin_unlock_irqrestore(&g2d_dev->lock_task, flags); + + return 0; +} + void g2d_hw_timeout_handler(unsigned long arg) { struct g2d_task *task = (struct g2d_task *)arg; @@ -215,12 +247,18 @@ static int g2d_open(struct inode *inode, struct file *filp) g2d_ctx->g2d_dev = g2d_dev; + g2d_ctx->priority = G2D_DEFAULT_PRIORITY; + atomic_inc(&g2d_dev->prior_stats[g2d_ctx->priority]); + return 0; } static int g2d_release(struct inode *inode, struct file *filp) { struct g2d_context *g2d_ctx = filp->private_data; + struct g2d_device *g2d_dev = g2d_ctx->g2d_dev; + + atomic_dec(&g2d_dev->prior_stats[g2d_ctx->priority]); if (g2d_ctx->hwfc_info) { int i; @@ -248,6 +286,20 @@ static long g2d_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) (struct g2d_task_data __user *)arg; struct g2d_task_data data; struct g2d_task *task; + int i; + + /* + * A process that has lower priority is not allowed + * to execute and simply returns -EBUSY + */ + for (i = ctx->priority + 1; i < G2D_PRIORITY_END; i++) { + if (atomic_read(&g2d_dev->prior_stats[i]) > 0) { + ret = -EBUSY; + break; + } + } + if (ret) + break; if (copy_from_user(&data, uptr, sizeof(data))) { dev_err(g2d_dev->dev, @@ -285,7 +337,7 @@ static long g2d_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } } - task = g2d_get_free_task(g2d_dev, IS_HWFC(data.flags)); + task = g2d_get_free_task(g2d_dev, ctx, IS_HWFC(data.flags)); if (task == NULL) { ret = -EBUSY; break; @@ -306,6 +358,30 @@ static long g2d_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (!(task->flags & G2D_FLAG_NONBLOCK)) ret = g2d_wait_put_user(g2d_dev, task, uptr, data.flags); + + break; + } + case G2D_IOC_PRIORITY: + { + enum g2d_priority data; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { + dev_err(g2d_dev->dev, + "%s: Failed to get priority\n", __func__); + ret = -EFAULT; + break; + } + + if ((data < G2D_LOW_PRIORITY) || (data >= G2D_PRIORITY_END)) { + dev_err(g2d_dev->dev, + "%s: Wrong priority %u\n", __func__, data); + ret = -EINVAL; + break; + } + + ret = g2d_update_priority(ctx, data); + + break; } } diff --git a/drivers/gpu/exynos/g2d/g2d_task.c b/drivers/gpu/exynos/g2d/g2d_task.c index fd10e4b61915..af335925e7f2 100644 --- a/drivers/gpu/exynos/g2d/g2d_task.c +++ b/drivers/gpu/exynos/g2d/g2d_task.c @@ -346,7 +346,8 @@ void g2d_fence_callback(struct dma_fence *fence, struct dma_fence_cb *cb) spin_unlock_irqrestore(&layer->task->fence_timeout_lock, flags); } -struct g2d_task *g2d_get_free_task(struct g2d_device *g2d_dev, bool hwfc) +struct g2d_task *g2d_get_free_task(struct g2d_device *g2d_dev, + struct g2d_context *g2d_ctx, bool hwfc) { struct g2d_task *task; struct list_head *taskfree; @@ -369,6 +370,7 @@ struct g2d_task *g2d_get_free_task(struct g2d_device *g2d_dev, bool hwfc) INIT_WORK(&task->work, g2d_task_schedule_work); init_task_state(task); + task->priority = g2d_ctx->priority; g2d_init_commands(task); diff --git a/drivers/gpu/exynos/g2d/g2d_task.h b/drivers/gpu/exynos/g2d/g2d_task.h index 4386b6614d27..43e925b626cc 100644 --- a/drivers/gpu/exynos/g2d/g2d_task.h +++ b/drivers/gpu/exynos/g2d/g2d_task.h @@ -174,7 +174,8 @@ struct g2d_task *g2d_get_active_task_from_id(struct g2d_device *g2d_dev, void g2d_destroy_tasks(struct g2d_device *g2d_dev); int g2d_create_tasks(struct g2d_device *g2d_dev); -struct g2d_task *g2d_get_free_task(struct g2d_device *g2d_dev, bool hwfc); +struct g2d_task *g2d_get_free_task(struct g2d_device *g2d_dev, + struct g2d_context *g2d_ctx, bool hwfc); void g2d_put_free_task(struct g2d_device *g2d_dev, struct g2d_task *task); void g2d_start_task(struct g2d_task *task); diff --git a/drivers/gpu/exynos/g2d/g2d_uapi.h b/drivers/gpu/exynos/g2d/g2d_uapi.h index 29ce32ead6a2..4b1806c7101a 100644 --- a/drivers/gpu/exynos/g2d/g2d_uapi.h +++ b/drivers/gpu/exynos/g2d/g2d_uapi.h @@ -210,6 +210,7 @@ struct g2d_task_data { }; #define G2D_IOC_PROCESS _IOWR('M', 4, struct g2d_task_data) +#define G2D_IOC_PRIORITY _IOR('M', 5, int32_t) #endif /* _G2D_UAPI_H_ */ -- 2.20.1