gpu: nvgpu: implement poll() for semaphores
Konsta Holtta [Wed, 10 Sep 2014 14:23:31 +0000 (17:23 +0300)]
Add poll interface and control ioctls for waiting for GPU job completion
via semaphores.

Poll on a gk20a channel file waits for events from pending semaphore
interrupts (stalling) of that channel. New ioctls enable and disable the
events, and clear a single interrupt event so that next poll doesn't
wake up for it again.

Bug 1528781

Change-Id: I5c6238966b5d0900c8ab263c6a7f8f2611901f33
Signed-off-by: Konsta Holtta <kholtta@nvidia.com>
Reviewed-on: http://git-master/r/497750
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
Tested-by: Terje Bergstrom <tbergstrom@nvidia.com>

drivers/gpu/nvgpu/gk20a/channel_gk20a.c
drivers/gpu/nvgpu/gk20a/channel_gk20a.h
drivers/gpu/nvgpu/gk20a/gk20a.c
drivers/gpu/nvgpu/gk20a/gr_gk20a.c
include/linux/nvhost_ioctl.h

index 1e71c1c..2c6013c 100644 (file)
@@ -773,6 +773,10 @@ struct channel_gk20a *gk20a_open_new_channel(struct gk20a *g)
        init_waitqueue_head(&ch->semaphore_wq);
        init_waitqueue_head(&ch->submit_wq);
 
+       mutex_init(&ch->poll_events.lock);
+       ch->poll_events.events_enabled = false;
+       ch->poll_events.num_pending_events = 0;
+
        return ch;
 }
 
@@ -1891,6 +1895,119 @@ notif_clean_up:
        return ret;
 }
 
+/* poll events for semaphores */
+
+static void gk20a_channel_events_enable(struct channel_gk20a_poll_events *ev)
+{
+       gk20a_dbg_fn("");
+
+       mutex_lock(&ev->lock);
+
+       ev->events_enabled = true;
+       ev->num_pending_events = 0;
+
+       mutex_unlock(&ev->lock);
+}
+
+static void gk20a_channel_events_disable(struct channel_gk20a_poll_events *ev)
+{
+       gk20a_dbg_fn("");
+
+       mutex_lock(&ev->lock);
+
+       ev->events_enabled = false;
+       ev->num_pending_events = 0;
+
+       mutex_unlock(&ev->lock);
+}
+
+static void gk20a_channel_events_clear(struct channel_gk20a_poll_events *ev)
+{
+       gk20a_dbg_fn("");
+
+       mutex_lock(&ev->lock);
+
+       if (ev->events_enabled &&
+                       ev->num_pending_events > 0)
+               ev->num_pending_events--;
+
+       mutex_unlock(&ev->lock);
+}
+
+static int gk20a_channel_events_ctrl(struct channel_gk20a *ch,
+                         struct nvhost_channel_events_ctrl_args *args)
+{
+       int ret = 0;
+
+       gk20a_dbg(gpu_dbg_fn | gpu_dbg_info,
+                       "channel events ctrl cmd %d", args->cmd);
+
+       switch (args->cmd) {
+       case NVHOST_IOCTL_CHANNEL_EVENTS_CTRL_CMD_ENABLE:
+               gk20a_channel_events_enable(&ch->poll_events);
+               break;
+
+       case NVHOST_IOCTL_CHANNEL_EVENTS_CTRL_CMD_DISABLE:
+               gk20a_channel_events_disable(&ch->poll_events);
+               break;
+
+       case NVHOST_IOCTL_CHANNEL_EVENTS_CTRL_CMD_CLEAR:
+               gk20a_channel_events_clear(&ch->poll_events);
+               break;
+
+       default:
+               gk20a_err(dev_from_gk20a(ch->g),
+                          "unrecognized channel events ctrl cmd: 0x%x",
+                          args->cmd);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+void gk20a_channel_event(struct channel_gk20a *ch)
+{
+       mutex_lock(&ch->poll_events.lock);
+
+       if (ch->poll_events.events_enabled) {
+               gk20a_dbg_info("posting event on channel id %d",
+                               ch->hw_chid);
+               gk20a_dbg_info("%d channel events pending",
+                               ch->poll_events.num_pending_events);
+
+               ch->poll_events.num_pending_events++;
+               /* not waking up here, caller does that */
+       }
+
+       mutex_unlock(&ch->poll_events.lock);
+}
+
+unsigned int gk20a_channel_poll(struct file *filep, poll_table *wait)
+{
+       unsigned int mask = 0;
+       struct channel_gk20a *ch = filep->private_data;
+
+       gk20a_dbg(gpu_dbg_fn | gpu_dbg_info, "");
+
+       poll_wait(filep, &ch->semaphore_wq, wait);
+
+       mutex_lock(&ch->poll_events.lock);
+
+       if (ch->poll_events.events_enabled &&
+                       ch->poll_events.num_pending_events > 0) {
+               gk20a_dbg_info("found pending event on channel id %d",
+                               ch->hw_chid);
+               gk20a_dbg_info("%d channel events pending",
+                               ch->poll_events.num_pending_events);
+               mask = (POLLPRI | POLLIN);
+       }
+
+       mutex_unlock(&ch->poll_events.lock);
+
+       return mask;
+}
+
 static int gk20a_channel_set_priority(struct channel_gk20a *ch,
                u32 priority)
 {
@@ -2314,6 +2431,10 @@ long gk20a_channel_ioctl(struct file *filp,
                err = gk20a_fifo_force_reset_ch(ch, true);
                gk20a_idle(dev);
                break;
+       case NVHOST_IOCTL_CHANNEL_EVENTS_CTRL:
+               err = gk20a_channel_events_ctrl(ch,
+                          (struct nvhost_channel_events_ctrl_args *)buf);
+               break;
        default:
                dev_err(&dev->dev, "unrecognized ioctl cmd: 0x%x", cmd);
                err = -ENOTTY;
index 37ca824..bb9f314 100644 (file)
@@ -26,6 +26,8 @@
 #include <linux/wait.h>
 #include <linux/mutex.h>
 #include <linux/nvhost_ioctl.h>
+#include <linux/poll.h>
+
 struct gk20a;
 struct gr_gk20a;
 struct dbg_session_gk20a;
@@ -74,6 +76,12 @@ struct channel_gk20a_job {
        struct list_head list;
 };
 
+struct channel_gk20a_poll_events {
+       struct mutex lock;
+       bool events_enabled;
+       int num_pending_events;
+};
+
 /* this is the priv element of struct nvhost_channel */
 struct channel_gk20a {
        struct gk20a *g;
@@ -148,6 +156,9 @@ struct channel_gk20a {
 #ifdef CONFIG_TEGRA_GR_VIRTUALIZATION
        u64 virt_ctx;
 #endif
+
+       /* event support */
+       struct channel_gk20a_poll_events poll_events;
 };
 
 static inline bool gk20a_channel_as_bound(struct channel_gk20a *ch)
@@ -180,6 +191,8 @@ long gk20a_channel_ioctl(struct file *filp,
 int gk20a_channel_release(struct inode *inode, struct file *filp);
 struct channel_gk20a *gk20a_get_channel_from_file(int fd);
 void gk20a_channel_update(struct channel_gk20a *c, int nr_completed);
+unsigned int gk20a_channel_poll(struct file *filep, poll_table *wait);
+void gk20a_channel_event(struct channel_gk20a *ch);
 
 void gk20a_init_channel(struct gpu_ops *gops);
 
index d16bba1..f7916b3 100644 (file)
@@ -101,6 +101,7 @@ static const struct file_operations gk20a_channel_ops = {
        .compat_ioctl = gk20a_channel_ioctl,
 #endif
        .unlocked_ioctl = gk20a_channel_ioctl,
+       .poll = gk20a_channel_poll,
 };
 
 static const struct file_operations gk20a_ctrl_ops = {
index f5c3bd6..db655c3 100644 (file)
@@ -5075,6 +5075,7 @@ static int gk20a_gr_handle_semaphore_pending(struct gk20a *g,
        struct fifo_gk20a *f = &g->fifo;
        struct channel_gk20a *ch = &f->channel[isr_data->chid];
 
+       gk20a_channel_event(ch);
        wake_up(&ch->semaphore_wq);
 
        return 0;
index ae627f3..51d5f25 100644 (file)
@@ -335,6 +335,17 @@ struct nvhost_set_ctxswitch_args {
        __u32 pad;
 };
 
+/* Enable/disable/clear event notifications */
+struct nvhost_channel_events_ctrl_args {
+       __u32 cmd; /* in */
+       __u32 _pad0[1];
+};
+
+/* valid event ctrl values */
+#define NVHOST_IOCTL_CHANNEL_EVENTS_CTRL_CMD_DISABLE 0
+#define NVHOST_IOCTL_CHANNEL_EVENTS_CTRL_CMD_ENABLE  1
+#define NVHOST_IOCTL_CHANNEL_EVENTS_CTRL_CMD_CLEAR   2
+
 #define NVHOST_IOCTL_CHANNEL_GET_SYNCPOINTS    \
        _IOR(NVHOST_IOCTL_MAGIC, 2, struct nvhost_get_param_args)
 #define NVHOST_IOCTL_CHANNEL_GET_WAITBASES     \
@@ -407,9 +418,11 @@ struct nvhost_set_ctxswitch_args {
        _IO(NVHOST_IOCTL_MAGIC,  115)
 #define NVHOST_IOCTL_CHANNEL_FORCE_RESET       \
        _IO(NVHOST_IOCTL_MAGIC,  116)
+#define NVHOST_IOCTL_CHANNEL_EVENTS_CTRL       \
+       _IOW(NVHOST_IOCTL_MAGIC,  117, struct nvhost_channel_events_ctrl_args)
 
 #define NVHOST_IOCTL_CHANNEL_LAST      \
-       _IOC_NR(NVHOST_IOCTL_CHANNEL_FORCE_RESET)
+       _IOC_NR(NVHOST_IOCTL_CHANNEL_EVENTS_CTRL)
 #define NVHOST_IOCTL_CHANNEL_MAX_ARG_SIZE sizeof(struct nvhost_submit_args)
 
 struct nvhost_ctrl_syncpt_read_args {