diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index 38cbde9..0d01810 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -15,6 +15,7 @@ config DRM_MSM
 	select SND_SOC_HDMI_CODEC if SND_SOC
 	select SYNC_FILE
 	select PM_OPP
+	select DRM_SCHED
 	default y
 	help
 	  DRM/KMS driver for MSM/snapdragon.
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index cd40c05..71ed921 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -60,7 +60,8 @@ msm-y := \
 	msm_perf.o \
 	msm_rd.o \
 	msm_ringbuffer.o \
-	msm_submitqueue.o
+	msm_submitqueue.o \
+	msm_sched.o
 
 msm-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o
 
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index b2da1fb..e461a9c 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -213,8 +213,7 @@ struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
 int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv);
 int msm_gem_sync_object(struct drm_gem_object *obj,
 		struct msm_fence_context *fctx, bool exclusive);
-void msm_gem_move_to_active(struct drm_gem_object *obj,
-		struct msm_gpu *gpu, bool exclusive, struct dma_fence *fence);
+void msm_gem_move_to_active(struct drm_gem_object *obj, struct msm_gpu *gpu);
 void msm_gem_move_to_inactive(struct drm_gem_object *obj);
 int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, ktime_t *timeout);
 int msm_gem_cpu_fini(struct drm_gem_object *obj);
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index f583bb4..7a12f30 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -663,16 +663,12 @@ int msm_gem_sync_object(struct drm_gem_object *obj,
 	return 0;
 }
 
-void msm_gem_move_to_active(struct drm_gem_object *obj,
-		struct msm_gpu *gpu, bool exclusive, struct dma_fence *fence)
+void msm_gem_move_to_active(struct drm_gem_object *obj, struct msm_gpu *gpu)
 {
 	struct msm_gem_object *msm_obj = to_msm_bo(obj);
 	WARN_ON(msm_obj->madv != MSM_MADV_WILLNEED);
 	msm_obj->gpu = gpu;
-	if (exclusive)
-		reservation_object_add_excl_fence(msm_obj->resv, fence);
-	else
-		reservation_object_add_shared_fence(msm_obj->resv, fence);
+
 	list_del_init(&msm_obj->mm_list);
 	list_add_tail(&msm_obj->mm_list, &gpu->active_list);
 }
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index cae3aa5..8c6269f 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -20,6 +20,7 @@
 
 #include <linux/kref.h>
 #include <linux/reservation.h>
+#include <drm/gpu_scheduler.h>
 #include "msm_drv.h"
 
 /* Additional internal-use only BO flags: */
@@ -136,6 +137,7 @@ enum msm_gem_lock {
  * lasts for the duration of the submit-ioctl.
  */
 struct msm_gem_submit {
+	struct drm_sched_job sched_job;
 	struct drm_device *dev;
 	struct msm_file_private *ctx;
 	struct msm_gpu *gpu;
@@ -144,6 +146,7 @@ struct msm_gem_submit {
 	struct ww_acquire_ctx ticket;
 	uint32_t seqno;		/* Sequence number of the submit on the ring */
 	struct dma_fence *hw_fence;
+	struct dma_fence *in_fence, *out_fence;
 	int out_fence_id;
 	struct msm_gpu_submitqueue *queue;
 	struct pid *pid;    /* submitting process */
@@ -162,6 +165,9 @@ struct msm_gem_submit {
 		uint32_t flags;
 		struct msm_gem_object *obj;
 		uint64_t iova;
+		struct dma_fence *excl;
+		uint32_t nr_shared;
+		struct dma_fence **shared;
 	} bos[0];
 };
 
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index 7931c2a..dff945c 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -65,23 +65,37 @@ void msm_gem_submit_free(struct msm_gem_submit *submit)
 {
 	int i;
 
+	mutex_lock(&submit->ring->fence_idr_lock);
 	idr_remove(&submit->ring->fence_idr, submit->out_fence_id);
-
-	dma_fence_put(submit->hw_fence);
+	mutex_unlock(&submit->ring->fence_idr_lock);
 
 	for (i = 0; i < submit->nr_bos; i++) {
+		int j;
+
 		struct msm_gem_object *msm_obj = submit->bos[i].obj;
 
 		if (submit->bos[i].flags & BO_PINNED)
 			msm_gem_put_iova(&msm_obj->base, submit->gpu->aspace);
 
-		drm_gem_object_put(&msm_obj->base);
+		drm_gem_object_put_unlocked(&msm_obj->base);
+
+		/*
+		 * While we are at it, clear the saved exclusive and shared
+		 * fences if any
+		 */
+		dma_fence_put(submit->bos[i].excl);
+
+		for (j = 0; j < submit->bos[i].nr_shared; j++)
+			dma_fence_put(submit->bos[i].shared[j]);
+
+		kfree(submit->bos[i].shared);
 	}
 
-	list_del(&submit->node);
 	put_pid(submit->pid);
 	msm_submitqueue_put(submit->queue);
 
+	dma_fence_put(submit->in_fence);
+	dma_fence_put(submit->out_fence);
 	kfree(submit);
 }
 
@@ -238,6 +252,27 @@ static int submit_lock_objects(struct msm_gem_submit *submit)
 	return ret;
 }
 
+static void submit_attach_fences_and_unlock(struct msm_gem_submit *submit)
+{
+	int i;
+
+	for (i = 0; i < submit->nr_bos; i++) {
+		struct msm_gem_object *msm_obj = submit->bos[i].obj;
+
+		if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
+			reservation_object_add_excl_fence(msm_obj->resv,
+					submit->out_fence);
+		else
+			reservation_object_add_shared_fence(msm_obj->resv,
+					submit->out_fence);
+
+		if (submit->bos[i].flags & BO_LOCKED) {
+			ww_mutex_unlock(&msm_obj->resv->lock);
+			submit->bos[i].flags &= ~BO_LOCKED;
+		}
+	}
+}
+
 static int submit_fence_sync(struct msm_gem_submit *submit, bool no_implicit)
 {
 	int i, ret = 0;
@@ -260,10 +295,24 @@ static int submit_fence_sync(struct msm_gem_submit *submit, bool no_implicit)
 		if (no_implicit)
 			continue;
 
-		ret = msm_gem_sync_object(&msm_obj->base, submit->ring->fctx,
-			write);
-		if (ret)
-			break;
+		if (write) {
+			/*
+			 * Save the per buffer shared and exclusive fences for
+			 * the scheduler thread to wait on.
+			 *
+			 * Note: The memory allocated for the storing the
+			 * shared fences will be released when the scheduler
+			 * is done waiting on all the saved fences.
+			 */
+			ret = reservation_object_get_fences_rcu(msm_obj->resv,
+					&submit->bos[i].excl,
+					&submit->bos[i].nr_shared,
+					&submit->bos[i].shared);
+			if (ret)
+				return ret;
+		} else
+			submit->bos[i].excl =
+				reservation_object_get_excl_rcu(msm_obj->resv);
 	}
 
 	return ret;
@@ -425,7 +474,6 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
 	struct msm_file_private *ctx = file->driver_priv;
 	struct msm_gem_submit *submit;
 	struct msm_gpu *gpu = priv->gpu;
-	struct dma_fence *in_fence = NULL;
 	struct sync_file *sync_file = NULL;
 	struct msm_gpu_submitqueue *queue;
 	struct msm_ringbuffer *ring;
@@ -457,32 +505,11 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
 
 	ring = gpu->rb[queue->prio];
 
-	if (args->flags & MSM_SUBMIT_FENCE_FD_IN) {
-		in_fence = sync_file_get_fence(args->fence_fd);
-
-		if (!in_fence)
-			return -EINVAL;
-
-		/*
-		 * Wait if the fence is from a foreign context, or if the fence
-		 * array contains any fence from a foreign context.
-		 */
-		if (!dma_fence_match_context(in_fence, ring->fctx->context)) {
-			ret = dma_fence_wait(in_fence, true);
-			if (ret)
-				return ret;
-		}
-	}
-
-	ret = mutex_lock_interruptible(&dev->struct_mutex);
-	if (ret)
-		return ret;
-
 	if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
 		out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
 		if (out_fence_fd < 0) {
 			ret = out_fence_fd;
-			goto out_unlock;
+			goto out_err;
 		}
 	}
 
@@ -490,7 +517,16 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
 			args->nr_cmds, ctx);
 	if (!submit) {
 		ret = -ENOMEM;
-		goto out_unlock;
+		goto out_err;
+	}
+
+	if (args->flags & MSM_SUBMIT_FENCE_FD_IN) {
+		submit->in_fence = sync_file_get_fence(args->fence_fd);
+
+		if (!submit->in_fence) {
+			ret = -EINVAL;
+			goto out;
+		}
 	}
 
 	if (args->flags & MSM_SUBMIT_SUDO)
@@ -573,28 +609,16 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
 
 	submit->nr_cmds = i;
 
-	msm_gpu_submit(submit);
-	if (IS_ERR(submit->hw_fence)) {
-		ret = PTR_ERR(submit->hw_fence);
-		submit->hw_fence = NULL;
+	ret = msm_sched_job_init(&submit->sched_job);
+	if (ret)
 		goto out;
-	}
 
-	/*
-	 * No protection should be okay here since this is protected by the big
-	 * GPU lock.
-	 */
-	submit->out_fence_id = idr_alloc_cyclic(&ring->fence_idr,
-			submit->hw_fence, 0, INT_MAX, GFP_KERNEL);
-	if (submit->out_fence_id < 0) {
-		ret = -ENOMEM;
-		goto out;
-	}
+	submit_attach_fences_and_unlock(submit);
 
 	args->fence = submit->out_fence_id;
 
 	if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
-		sync_file = sync_file_create(submit->hw_fence);
+		sync_file = sync_file_create(submit->out_fence);
 		if (!sync_file) {
 			ret = -ENOMEM;
 			goto out;
@@ -604,14 +628,22 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
 	}
 
 out:
-	if (in_fence)
-		dma_fence_put(in_fence);
+	/*
+	 * Clean up the submit bits and pieces which are not needed beyond this
+	 * function context
+	 */
 	submit_cleanup(submit);
-	if (ret)
+
+	if (!ret)
+		/*
+		 * If we reached here, then all is well. Push the job to the
+		 * scheduler
+		 */
+		msm_sched_push_job(submit);
+	else
 		msm_gem_submit_free(submit);
-out_unlock:
+out_err:
 	if (ret && (out_fence_fd >= 0))
 		put_unused_fd(out_fence_fd);
-	mutex_unlock(&dev->struct_mutex);
 	return ret;
 }
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index cd5fe49..481a55c 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -295,12 +295,17 @@ static void update_fences(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
 find_submit(struct msm_ringbuffer *ring, uint32_t fence)
 {
 	struct msm_gem_submit *submit;
+	unsigned long flags;
 
-	WARN_ON(!mutex_is_locked(&ring->gpu->dev->struct_mutex));
+	spin_lock_irqsave(&ring->lock, flags);
 
 	list_for_each_entry(submit, &ring->submits, node)
-		if (submit->seqno == fence)
+		if (submit->seqno == fence) {
+			spin_unlock_irqrestore(&ring->lock, flags);
 			return submit;
+		}
+
+	spin_unlock_irqrestore(&ring->lock, flags);
 
 	return NULL;
 }
@@ -316,6 +321,12 @@ static void recover_worker(struct work_struct *work)
 	struct msm_ringbuffer *cur_ring = gpu->funcs->active_ring(gpu);
 	int i;
 
+	submit = find_submit(cur_ring, cur_ring->memptrs->fence + 1);
+	return msm_sched_gpu_recovery(gpu, submit);
+
+	/*
+	 * The unused code below will be removed in a subsequent patch
+	 */
 	mutex_lock(&dev->struct_mutex);
 
 	dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
@@ -591,11 +602,36 @@ static void retire_worker(struct work_struct *work)
 	mutex_unlock(&dev->struct_mutex);
 }
 
-/* call from irq handler to schedule work to retire bo's */
+static void signal_hw_fences(struct msm_ringbuffer *ring)
+{
+	unsigned long flags;
+	struct msm_gem_submit *submit, *tmp;
+
+	spin_lock_irqsave(&ring->lock, flags);
+
+	list_for_each_entry_safe(submit, tmp, &ring->submits, node) {
+		if (submit->seqno > ring->memptrs->fence)
+			break;
+
+		list_del(&submit->node);
+
+		dma_fence_signal(submit->hw_fence);
+	}
+
+	spin_unlock_irqrestore(&ring->lock, flags);
+}
+
+/*
+ * Called from the IRQ context to signal hardware fences for the completed
+ * submits
+ */
 void msm_gpu_retire(struct msm_gpu *gpu)
 {
-	struct msm_drm_private *priv = gpu->dev->dev_private;
-	queue_work(priv->wq, &gpu->retire_work);
+	int i;
+
+	for (i = 0; i < gpu->nr_rings; i++)
+		signal_hw_fences(gpu->rb[i]);
+
 	update_sw_cntrs(gpu);
 }
 
@@ -606,25 +642,28 @@ struct dma_fence *msm_gpu_submit(struct msm_gem_submit *submit)
 	struct drm_device *dev = gpu->dev;
 	struct msm_drm_private *priv = dev->dev_private;
 	struct msm_ringbuffer *ring = submit->ring;
+	unsigned long flags;
 	int i;
 
-	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
-
 	submit->hw_fence = msm_fence_alloc(ring->fctx);
 	if (IS_ERR(submit->hw_fence))
 		return submit->hw_fence;
 
-	pm_runtime_get_sync(&gpu->pdev->dev);
-
-	msm_gpu_hw_init(gpu);
+	spin_lock_irqsave(&ring->lock, flags);
+	list_add_tail(&submit->node, &ring->submits);
+	spin_unlock_irqrestore(&ring->lock, flags);
 
 	submit->seqno = ++ring->seqno;
 
-	list_add_tail(&submit->node, &ring->submits);
+	update_sw_cntrs(gpu);
 
-	msm_rd_dump_submit(priv->rd, submit, NULL);
+	mutex_lock(&dev->struct_mutex);
 
-	update_sw_cntrs(gpu);
+	pm_runtime_get_sync(&gpu->pdev->dev);
+
+	msm_gpu_hw_init(gpu);
+
+	msm_rd_dump_submit(priv->rd, submit, NULL);
 
 	for (i = 0; i < submit->nr_bos; i++) {
 		struct msm_gem_object *msm_obj = submit->bos[i].obj;
@@ -634,16 +673,13 @@ struct dma_fence *msm_gpu_submit(struct msm_gem_submit *submit)
 		 */
 		WARN_ON(is_active(msm_obj) && (msm_obj->gpu != gpu));
 
-		if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
-			msm_gem_move_to_active(&msm_obj->base, gpu, true, submit->hw_fence);
-		else if (submit->bos[i].flags & MSM_SUBMIT_BO_READ)
-			msm_gem_move_to_active(&msm_obj->base, gpu, false, submit->hw_fence);
+		msm_gem_move_to_active(&msm_obj->base, gpu);
 	}
 
 	gpu->funcs->submit(gpu, submit, submit->ctx);
 	priv->lastctx = submit->ctx;
 
-	hangcheck_timer_reset(gpu);
+	mutex_unlock(&dev->struct_mutex);
 
 	return submit->hw_fence;
 }
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index dd55979..3bd1920 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -173,6 +173,8 @@ struct msm_gpu_submitqueue {
 	int faults;
 	struct list_head node;
 	struct kref ref;
+	struct msm_gpu *gpu;
+	struct drm_sched_entity sched_entity;
 };
 
 static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c
index 0889766..ef2bf10 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.c
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.c
@@ -56,9 +56,14 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id,
 
 	snprintf(ring->name, sizeof(ring->name), "msm-gpu-ring-%d", ring->id);
 
+	if (msm_sched_init(&ring->sched, ring->name) != 0) {
+		ret = -EINVAL;
+		goto fail;
+	}
 	ring->fctx = msm_fence_context_alloc(gpu->dev, ring->name);
 
 	idr_init(&ring->fence_idr);
+	mutex_init(&ring->fence_idr_lock);
 
 	return ring;
 
@@ -74,6 +79,7 @@ void msm_ringbuffer_destroy(struct msm_ringbuffer *ring)
 
 	msm_fence_context_free(ring->fctx);
 
+	msm_sched_cleanup(&ring->sched);
 	if (ring->bo) {
 		msm_gem_put_iova(ring->bo, ring->gpu->aspace);
 		msm_gem_put_vaddr(ring->bo);
@@ -81,6 +87,7 @@ void msm_ringbuffer_destroy(struct msm_ringbuffer *ring)
 	}
 
 	idr_destroy(&ring->fence_idr);
+	mutex_destroy(&ring->fence_idr_lock);
 
 	kfree(ring);
 }
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h b/drivers/gpu/drm/msm/msm_ringbuffer.h
index 523373b..10ae4a8 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.h
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.h
@@ -19,6 +19,7 @@
 #define __MSM_RINGBUFFER_H__
 
 #include "msm_drv.h"
+#include "msm_sched.h"
 
 #define rbmemptr(ring, member)  \
 	((ring)->memptrs_iova + offsetof(struct msm_rbmemptrs, member))
@@ -42,7 +43,9 @@ struct msm_ringbuffer {
 	uint64_t memptrs_iova;
 	struct msm_fence_context *fctx;
 	struct idr fence_idr;
+	struct mutex fence_idr_lock;
 	spinlock_t lock;
+	struct drm_gpu_scheduler sched;
 };
 
 struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id,
diff --git a/drivers/gpu/drm/msm/msm_sched.c b/drivers/gpu/drm/msm/msm_sched.c
new file mode 100644
index 0000000..8b805ce
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_sched.c
@@ -0,0 +1,313 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
+
+#include "msm_gpu.h"
+#include "msm_gem.h"
+#include "msm_sched.h"
+
+#include <linux/string_helpers.h>
+#include <linux/kthread.h>
+
+struct msm_gem_submit *to_msm_gem_submit(struct drm_sched_job *sched_job)
+{
+	return container_of(sched_job, struct msm_gem_submit, sched_job);
+}
+
+/*
+ * Go through the bo's one by one and return the previously saved shared and
+ * exclusive fences. If the scheduler gets the fence, then it takes care of
+ * releasing the reference on the fence.
+ */
+static struct dma_fence *msm_sched_dependency(struct drm_sched_job *sched_job,
+		struct drm_sched_entity *entity)
+{
+	struct msm_gem_submit *submit = to_msm_gem_submit(sched_job);
+	struct dma_fence *fence;
+	int i;
+
+	if (submit->in_fence) {
+		fence = submit->in_fence;
+		submit->in_fence = NULL;
+
+		if (!dma_fence_is_signaled(fence))
+			return fence;
+
+		dma_fence_put(fence);
+	}
+
+	/* Return the previously saved shared and exclusive fences if any */
+	for (i = 0; i < submit->nr_bos; i++) {
+		int j;
+
+		if (submit->bos[i].excl) {
+			fence = submit->bos[i].excl;
+			submit->bos[i].excl = NULL;
+
+			if (!dma_fence_is_signaled(fence))
+				return fence;
+
+			dma_fence_put(fence);
+		}
+
+		for (j = 0; j < submit->bos[i].nr_shared; j++) {
+			if (!submit->bos[i].shared[j])
+				continue;
+
+			fence = submit->bos[i].shared[j];
+			submit->bos[i].shared[j] = NULL;
+
+			if (!dma_fence_is_signaled(fence))
+				return fence;
+
+			dma_fence_put(fence);
+		}
+
+		kfree(submit->bos[i].shared);
+		submit->bos[i].nr_shared = 0;
+		submit->bos[i].shared = NULL;
+	}
+
+	return NULL;
+}
+
+static struct dma_fence *msm_sched_run_job(struct drm_sched_job *sched_job)
+{
+	struct msm_gem_submit *submit = to_msm_gem_submit(sched_job);
+
+	return !sched_job->s_fence->finished.error ?
+		msm_gpu_submit(submit) : NULL;
+}
+
+static void dump_bad_submit(struct msm_gem_submit *submit)
+{
+	struct msm_gpu *gpu = submit->gpu;
+	struct drm_device *dev = gpu->dev;
+	struct msm_drm_private *priv = dev->dev_private;
+	struct task_struct *task;
+	char task_name[32] = {0};
+
+	rcu_read_lock();
+	task = pid_task(submit->pid, PIDTYPE_PID);
+	if (task) {
+		char *cmd;
+
+		cmd = kstrdup_quotable_cmdline(task, GFP_KERNEL);
+
+		dev_err(&gpu->pdev->dev, "%s: offending task: %s (%s)\n",
+				gpu->name, task->comm, cmd);
+
+		snprintf(task_name, sizeof(task_name),
+				"offending task: %s (%s)", task->comm, cmd);
+
+		kfree(cmd);
+	}
+	rcu_read_unlock();
+
+	mutex_lock(&gpu->dev->struct_mutex);
+	msm_rd_dump_submit(priv->hangrd, submit, task_name);
+	mutex_unlock(&gpu->dev->struct_mutex);
+}
+
+void msm_sched_gpu_recovery(struct msm_gpu *gpu, struct msm_gem_submit *submit)
+{
+	int i;
+	static atomic_t gpu_recovery;
+
+	/* Check if a GPU recovery is already in progress */
+	if (!(atomic_cmpxchg(&gpu_recovery, 0, 1) == 0))
+		return;
+
+	/*
+	 * Pause the schedulers so we don't get new requests while the recovery
+	 * is in progress
+	 */
+	for (i = 0; i < gpu->nr_rings; i++)
+		kthread_park(gpu->rb[i]->sched.thread);
+
+	/*
+	 * Disable interrupts so we don't get interrupted while the recovery is
+	 * in progress
+	 */
+	disable_irq(gpu->irq);
+
+	dev_err(&gpu->pdev->dev, "%s: hangcheck recover!\n", gpu->name);
+
+	if (submit)
+		dump_bad_submit(submit);
+
+	/*
+	 * For each ring, we do the following
+	 * a) Inform the scheduler to drop the hardware fence for the
+	 * submissions on its mirror list
+	 * b) The submit(job) list on the ring is not useful anymore
+	 * as we are going for a gpu reset, so we empty the submit list
+	 */
+	for (i = 0; i < gpu->nr_rings; i++) {
+		struct msm_gem_submit *job, *tmp;
+		unsigned long flags;
+		struct msm_ringbuffer *ring = gpu->rb[i];
+
+		/* a) Release the hardware fences */
+		drm_sched_hw_job_reset(&ring->sched,
+				(submit && submit->ring == ring) ?
+				&submit->sched_job : NULL);
+
+		/* b) Free up the ring submit list */
+		spin_lock_irqsave(&ring->lock, flags);
+
+		list_for_each_entry_safe(job, tmp, &ring->submits, node)
+			list_del(&job->node);
+
+		spin_unlock_irqrestore(&ring->lock, flags);
+	}
+
+	/* Power cycle and reset the GPU back to init state */
+	mutex_lock(&gpu->dev->struct_mutex);
+
+	pm_runtime_get_sync(&gpu->pdev->dev);
+	gpu->funcs->recover(gpu);
+	pm_runtime_put_sync(&gpu->pdev->dev);
+
+	mutex_unlock(&gpu->dev->struct_mutex);
+
+	/* Re-enable the interrupts once the gpu reset is complete */
+	enable_irq(gpu->irq);
+
+	/*
+	 * The GPU is hopefully back in good shape, now request the schedulers
+	 * to replay its mirror list, starting with the scheduler on the highest
+	 * priority ring
+	 */
+	for (i = 0; i < gpu->nr_rings; i++) {
+		drm_sched_job_recovery(&gpu->rb[i]->sched);
+		kthread_unpark(gpu->rb[i]->sched.thread);
+	}
+
+	atomic_set(&gpu_recovery, 0);
+}
+
+static void msm_sched_timedout_job(struct drm_sched_job *bad_job)
+{
+	struct msm_gem_submit *submit = to_msm_gem_submit(bad_job);
+	struct msm_gpu *gpu = submit->gpu;
+	struct msm_ringbuffer *ring = submit->ring;
+
+	/*
+	 * If this submission completed in the mean time, then the timeout is
+	 * spurious
+	 */
+	if (submit->seqno <= submit->ring->memptrs->fence)
+		return;
+
+	dev_err(&gpu->pdev->dev, "%s: hangcheck detected gpu lockup rb %d!\n",
+			gpu->name, ring->id);
+	dev_err(&gpu->pdev->dev, "%s:     completed fence: %u\n",
+			gpu->name, ring->memptrs->fence);
+	dev_err(&gpu->pdev->dev, "%s:     submitted fence: %u\n",
+			gpu->name, ring->seqno);
+
+	msm_sched_gpu_recovery(gpu, submit);
+}
+
+static void msm_sched_free_job(struct drm_sched_job *sched_job)
+{
+	struct msm_gem_submit *submit = to_msm_gem_submit(sched_job);
+	struct msm_gpu *gpu = submit->gpu;
+	int i;
+
+	mutex_lock(&gpu->dev->struct_mutex);
+
+	for (i = 0; i < submit->nr_bos; i++) {
+		struct msm_gem_object *msm_obj = submit->bos[i].obj;
+
+		msm_gem_move_to_inactive(&msm_obj->base);
+	}
+
+	mutex_unlock(&gpu->dev->struct_mutex);
+
+	pm_runtime_mark_last_busy(&gpu->pdev->dev);
+	pm_runtime_put_autosuspend(&gpu->pdev->dev);
+
+	msm_gem_submit_free(submit);
+}
+
+static const struct drm_sched_backend_ops msm_sched_ops = {
+	.dependency = msm_sched_dependency,
+	.run_job = msm_sched_run_job,
+	.timedout_job = msm_sched_timedout_job,
+	.free_job = msm_sched_free_job,
+};
+
+int msm_sched_job_init(struct drm_sched_job *sched_job)
+{
+	int ret;
+	struct msm_gem_submit *submit = to_msm_gem_submit(sched_job);
+	struct msm_ringbuffer *ring = submit->ring;
+
+	ret = drm_sched_job_init(sched_job, &ring->sched,
+			&submit->queue->sched_entity, submit->ctx);
+	if (ret)
+		return ret;
+
+	submit->out_fence = dma_fence_get(&submit->sched_job.s_fence->finished);
+
+	mutex_lock(&ring->fence_idr_lock);
+	submit->out_fence_id = idr_alloc_cyclic(&ring->fence_idr,
+						submit->out_fence, 0, INT_MAX,
+						GFP_KERNEL);
+	mutex_unlock(&ring->fence_idr_lock);
+
+	if (submit->out_fence_id < 0) {
+		/*
+		 * TODO: The scheduler's finished fence leaks here since the
+		 * job will not be pushed to the queue. Need to update scheduler
+		 * to fix this cleanly(?)
+		 */
+		dma_fence_put(submit->out_fence);
+		submit->out_fence = NULL;
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+void msm_sched_push_job(struct msm_gem_submit *submit)
+{
+	return drm_sched_entity_push_job(&submit->sched_job,
+			&submit->queue->sched_entity);
+}
+
+int msm_sched_init(struct drm_gpu_scheduler *sched, const char *name)
+{
+	return drm_sched_init(sched, &msm_sched_ops, 4, 0,
+			msecs_to_jiffies(500), name);
+}
+
+void msm_sched_cleanup(struct drm_gpu_scheduler *sched)
+{
+	drm_sched_fini(sched);
+}
+
+/*
+ * Create a new entity on the schedulers normal priority runqueue. For now we
+ * choose to always use the normal run queue priority, but in future its
+ * possible with some extension to the msm drm interface, to create the
+ * submitqueue(entities) of different priorities on the same ring, thereby
+ * allowing to priortize and schedule submitqueues belonging to the same ring
+ */
+int msm_sched_entity_init(struct msm_gpu *gpu,
+		struct drm_sched_entity *sched_entity, int prio)
+{
+	struct drm_gpu_scheduler *sched = &gpu->rb[prio]->sched;
+
+	return drm_sched_entity_init(sched, sched_entity,
+			&sched->sched_rq[DRM_SCHED_PRIORITY_NORMAL], NULL);
+}
+
+void msm_sched_entity_cleanup(struct msm_gpu *gpu,
+		struct drm_sched_entity *sched_entity, int prio)
+{
+	struct drm_gpu_scheduler *sched = &gpu->rb[prio]->sched;
+
+	drm_sched_entity_fini(sched, sched_entity);
+}
diff --git a/drivers/gpu/drm/msm/msm_sched.h b/drivers/gpu/drm/msm/msm_sched.h
new file mode 100644
index 0000000..6ab2728
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_sched.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
+
+#ifndef __MSM_SCHED_H__
+#define __MSM_SCHED_H__
+
+#include <drm/gpu_scheduler.h>
+
+int msm_sched_init(struct drm_gpu_scheduler *sched, const char *name);
+void msm_sched_cleanup(struct drm_gpu_scheduler *sched);
+int msm_sched_entity_init(struct msm_gpu *gpu, struct drm_sched_entity *queue,
+		int prio);
+void msm_sched_entity_cleanup(struct msm_gpu *gpu,
+		struct drm_sched_entity *queue, int prio);
+int msm_sched_job_init(struct drm_sched_job *sched_job);
+void msm_sched_push_job(struct msm_gem_submit *submit);
+void msm_sched_gpu_recovery(struct msm_gpu *gpu, struct msm_gem_submit *submit);
+#endif /* __MSM_SCHED_H__ */
diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c
index 325da44..b6eb13e 100644
--- a/drivers/gpu/drm/msm/msm_submitqueue.c
+++ b/drivers/gpu/drm/msm/msm_submitqueue.c
@@ -19,6 +19,7 @@ void msm_submitqueue_destroy(struct kref *kref)
 	struct msm_gpu_submitqueue *queue = container_of(kref,
 		struct msm_gpu_submitqueue, ref);
 
+	msm_sched_entity_cleanup(queue->gpu, &queue->sched_entity, queue->prio);
 	kfree(queue);
 }
 
@@ -65,6 +66,7 @@ int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
 {
 	struct msm_drm_private *priv = drm->dev_private;
 	struct msm_gpu_submitqueue *queue;
+	struct msm_gpu *gpu = priv->gpu;
 
 	if (!ctx)
 		return -ENODEV;
@@ -77,13 +79,16 @@ int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
 	kref_init(&queue->ref);
 	queue->flags = flags;
 
-	if (priv->gpu) {
-		if (prio >= priv->gpu->nr_rings) {
-			kfree(queue);
-			return -EINVAL;
-		}
+	if (gpu) {
+		if (prio >= gpu->nr_rings)
+			goto fail;
+
+		if (msm_sched_entity_init(priv->gpu, &queue->sched_entity,
+					prio))
+			goto fail;
 
 		queue->prio = prio;
+		queue->gpu = gpu;
 	}
 
 	write_lock(&ctx->queuelock);
@@ -98,6 +103,9 @@ int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
 	write_unlock(&ctx->queuelock);
 
 	return 0;
+fail:
+	kfree(queue);
+	return -EINVAL;
 }
 
 int msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx)
