[COMMON] g2d: add priority setting
authorhyesoo.yu <hyesoo.yu@samsung.com>
Mon, 26 Jun 2017 09:35:26 +0000 (18:35 +0900)
committerSeungchul Kim <sc377.kim@samsung.com>
Mon, 28 May 2018 05:27:26 +0000 (14:27 +0900)
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 <hyesoo.yu@samsung.com>
drivers/gpu/exynos/g2d/g2d.h
drivers/gpu/exynos/g2d/g2d_drv.c
drivers/gpu/exynos/g2d/g2d_task.c
drivers/gpu/exynos/g2d/g2d_task.h
drivers/gpu/exynos/g2d/g2d_uapi.h

index b6b86f793e47c16d2a882b2b240bfffcbd0af11a..22fac19314ae13c3a3e18d26f24fab1717d11697 100644 (file)
 
 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);
index 8c7a98491c179b323abc7213d1510bb2adea7fba..18cdb5b9581fd79396346c4fab610699b1aa8df4 100644 (file)
 
 #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;
        }
        }
 
index fd10e4b619159a77f73ee41994176c157be63be6..af335925e7f2e172e625bac35a8e58c2fa44a5fe 100644 (file)
@@ -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);
 
index 4386b6614d27975f213cb5dd3d4a68decfaf6acd..43e925b626cc1787f1742340ea825eb37d781773 100644 (file)
@@ -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);
index 29ce32ead6a2e83a0ccca0723f9ab2c4c1edb009..4b1806c7101a681811d3c904644facb3ef36e8c4 100644 (file)
@@ -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_ */