[PATCH v3 8/8] drm/etnaviv: implement per-process address spaces on MMUv2

Guido Günther agx at sigxcpu.org
Wed Aug 14 10:00:05 UTC 2019


Hi,
On Fri, Aug 09, 2019 at 02:04:24PM +0200, Lucas Stach wrote:
> This builds on top of the MMU contexts introduced earlier. Instead of having
> one context per GPU core, each GPU client receives its own context.
> 
> On MMUv1 this still means a single shared pagetable set is used by all
> clients, but on MMUv2 there is now a distinct set of pagetables for each
> client. As the command fetch is also translated via the MMU on MMUv2 the
> kernel command ringbuffer is mapped into each of the client pagetables.
> 
> As the MMU context switch is a bit of a heavy operation, due to the needed
> cache and TLB flushing, this patch implements a lazy way of switching the
> MMU context. The kernel does not have its own MMU context, but reuses the
> last client context for all of its operations. This has some visible impact,
> as the GPU can now only be started once a client has submitted some work and
> we got the client MMU context assigned. Also the MMU context has a different
> lifetime than the general client context, as the GPU might still execute the
> kernel command buffer in the context of a client even after the client has
> completed all GPU work and has been terminated. Only when the GPU is runtime
> suspended or switches to another clients MMU context is the old context
> freed up.
> 
> Signed-off-by: Lucas Stach <l.stach at pengutronix.de>

Reviewed-by: Guido Günther <agx at sigxcpu.org> 

> ---
> v3: Don't call etnaviv_cmdbuf_suballoc_unmap when mapping failed.
> ---
>  drivers/gpu/drm/etnaviv/etnaviv_buffer.c     |  64 ++++++++---
>  drivers/gpu/drm/etnaviv/etnaviv_drv.c        |  38 ++++++-
>  drivers/gpu/drm/etnaviv/etnaviv_drv.h        |   6 +-
>  drivers/gpu/drm/etnaviv/etnaviv_dump.c       |   4 +-
>  drivers/gpu/drm/etnaviv/etnaviv_gem.c        |   7 +-
>  drivers/gpu/drm/etnaviv/etnaviv_gem.h        |   4 +-
>  drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c |  11 +-
>  drivers/gpu/drm/etnaviv/etnaviv_gpu.c        | 105 ++++++++-----------
>  drivers/gpu/drm/etnaviv/etnaviv_gpu.h        |   4 -
>  drivers/gpu/drm/etnaviv/etnaviv_iommu.c      |  10 +-
>  drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c   |  17 ++-
>  drivers/gpu/drm/etnaviv/etnaviv_mmu.c        |  42 ++++++--
>  drivers/gpu/drm/etnaviv/etnaviv_mmu.h        |  11 +-
>  13 files changed, 208 insertions(+), 115 deletions(-)
> 
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c
> index 4324b098689f..876a035ee1a2 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c
> @@ -118,7 +118,8 @@ static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
>  	u32 *ptr = buf->vaddr + off;
>  
>  	dev_info(gpu->dev, "virt %p phys 0x%08x free 0x%08x\n",
> -			ptr, etnaviv_cmdbuf_get_va(buf, &gpu->cmdbuf_mapping) +
> +			ptr, etnaviv_cmdbuf_get_va(buf,
> +			&gpu->mmu_context->cmdbuf_mapping) +
>  			off, size - len * 4 - off);
>  
>  	print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
> @@ -152,7 +153,8 @@ static u32 etnaviv_buffer_reserve(struct etnaviv_gpu *gpu,
>  	if (buffer->user_size + cmd_dwords * sizeof(u64) > buffer->size)
>  		buffer->user_size = 0;
>  
> -	return etnaviv_cmdbuf_get_va(buffer, &gpu->cmdbuf_mapping) +
> +	return etnaviv_cmdbuf_get_va(buffer,
> +				     &gpu->mmu_context->cmdbuf_mapping) +
>  	       buffer->user_size;
>  }
>  
> @@ -166,7 +168,8 @@ u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
>  	buffer->user_size = 0;
>  
>  	CMD_WAIT(buffer);
> -	CMD_LINK(buffer, 2, etnaviv_cmdbuf_get_va(buffer, &gpu->cmdbuf_mapping)
> +	CMD_LINK(buffer, 2,
> +		 etnaviv_cmdbuf_get_va(buffer, &gpu->mmu_context->cmdbuf_mapping)
>  		 + buffer->user_size - 4);
>  
>  	return buffer->user_size / 8;
> @@ -293,7 +296,8 @@ void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event)
>  
>  	/* Append waitlink */
>  	CMD_WAIT(buffer);
> -	CMD_LINK(buffer, 2, etnaviv_cmdbuf_get_va(buffer, &gpu->cmdbuf_mapping)
> +	CMD_LINK(buffer, 2,
> +		 etnaviv_cmdbuf_get_va(buffer, &gpu->mmu_context->cmdbuf_mapping)
>  		 + buffer->user_size - 4);
>  
>  	/*
> @@ -308,7 +312,8 @@ void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event)
>  
>  /* Append a command buffer to the ring buffer. */
>  void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
> -	unsigned int event, struct etnaviv_cmdbuf *cmdbuf)
> +	struct etnaviv_iommu_context *mmu_context, unsigned int event,
> +	struct etnaviv_cmdbuf *cmdbuf)
>  {
>  	struct etnaviv_cmdbuf *buffer = &gpu->buffer;
>  	unsigned int waitlink_offset = buffer->user_size - 16;
> @@ -317,17 +322,19 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
>  	bool switch_context = gpu->exec_state != exec_state;
>  	unsigned int new_flush_seq = READ_ONCE(gpu->mmu_context->flush_seq);
>  	bool need_flush = gpu->flush_seq != new_flush_seq;
> +	bool switch_mmu_context = gpu->mmu_context != mmu_context;
>  
>  	lockdep_assert_held(&gpu->lock);
>  
>  	if (drm_debug & DRM_UT_DRIVER)
>  		etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
>  
> -	link_target = etnaviv_cmdbuf_get_va(cmdbuf, &gpu->cmdbuf_mapping);
> +	link_target = etnaviv_cmdbuf_get_va(cmdbuf,
> +					    &gpu->mmu_context->cmdbuf_mapping);
>  	link_dwords = cmdbuf->size / 8;
>  
>  	/*
> -	 * If we need maintanence prior to submitting this buffer, we will
> +	 * If we need maintenance prior to submitting this buffer, we will
>  	 * need to append a mmu flush load state, followed by a new
>  	 * link to this buffer - a total of four additional words.
>  	 */
> @@ -349,7 +356,24 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
>  		if (switch_context)
>  			extra_dwords += 4;
>  
> +		/* PTA load command */
> +		if (switch_mmu_context && gpu->sec_mode == ETNA_SEC_KERNEL)
> +			extra_dwords += 1;
> +
>  		target = etnaviv_buffer_reserve(gpu, buffer, extra_dwords);
> +		/*
> +		 * Switch MMU context if necessary. Must be done after the
> +		 * link target has been calculated, as the jump forward in the
> +		 * kernel ring still uses the last active MMU context before
> +		 * the switch.
> +		 */
> +		if (switch_mmu_context) {
> +			struct etnaviv_iommu_context *old_context = gpu->mmu_context;
> +
> +			etnaviv_iommu_context_get(mmu_context);
> +			gpu->mmu_context = mmu_context;
> +			etnaviv_iommu_context_put(old_context);
> +		}
>  
>  		if (need_flush) {
>  			/* Add the MMU flush */
> @@ -361,10 +385,23 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
>  					       VIVS_GL_FLUSH_MMU_FLUSH_PEMMU |
>  					       VIVS_GL_FLUSH_MMU_FLUSH_UNK4);
>  			} else {
> +				u32 flush = VIVS_MMUv2_CONFIGURATION_MODE_MASK |
> +					    VIVS_MMUv2_CONFIGURATION_FLUSH_FLUSH;
> +
> +				if (switch_mmu_context &&
> +				    gpu->sec_mode == ETNA_SEC_KERNEL) {
> +					unsigned short id =
> +						etnaviv_iommuv2_get_pta_id(gpu->mmu_context);
> +					CMD_LOAD_STATE(buffer,
> +						VIVS_MMUv2_PTA_CONFIG,
> +						VIVS_MMUv2_PTA_CONFIG_INDEX(id));
> +				}
> +
> +				if (gpu->sec_mode == ETNA_SEC_NONE)
> +					flush |= etnaviv_iommuv2_get_mtlb_addr(gpu->mmu_context);
> +
>  				CMD_LOAD_STATE(buffer, VIVS_MMUv2_CONFIGURATION,
> -					VIVS_MMUv2_CONFIGURATION_MODE_MASK |
> -					VIVS_MMUv2_CONFIGURATION_ADDRESS_MASK |
> -					VIVS_MMUv2_CONFIGURATION_FLUSH_FLUSH);
> +					       flush);
>  				CMD_SEM(buffer, SYNC_RECIPIENT_FE,
>  					SYNC_RECIPIENT_PE);
>  				CMD_STALL(buffer, SYNC_RECIPIENT_FE,
> @@ -380,6 +417,8 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
>  		}
>  
>  		/* And the link to the submitted buffer */
> +		link_target = etnaviv_cmdbuf_get_va(cmdbuf,
> +					&gpu->mmu_context->cmdbuf_mapping);
>  		CMD_LINK(buffer, link_dwords, link_target);
>  
>  		/* Update the link target to point to above instructions */
> @@ -416,13 +455,14 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
>  	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
>  		       VIVS_GL_EVENT_FROM_PE);
>  	CMD_WAIT(buffer);
> -	CMD_LINK(buffer, 2, etnaviv_cmdbuf_get_va(buffer, &gpu->cmdbuf_mapping)
> +	CMD_LINK(buffer, 2,
> +		 etnaviv_cmdbuf_get_va(buffer, &gpu->mmu_context->cmdbuf_mapping)
>  		 + buffer->user_size - 4);
>  
>  	if (drm_debug & DRM_UT_DRIVER)
>  		pr_info("stream link to 0x%08x @ 0x%08x %p\n",
>  			return_target,
> -			etnaviv_cmdbuf_get_va(cmdbuf, &gpu->cmdbuf_mapping),
> +			etnaviv_cmdbuf_get_va(cmdbuf, &gpu->mmu_context->cmdbuf_mapping),
>  			cmdbuf->vaddr);
>  
>  	if (drm_debug & DRM_UT_DRIVER) {
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
> index eb0c23fe979a..80f1edcbbea0 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
> @@ -50,12 +50,19 @@ static int etnaviv_open(struct drm_device *dev, struct drm_file *file)
>  {
>  	struct etnaviv_drm_private *priv = dev->dev_private;
>  	struct etnaviv_file_private *ctx;
> -	int i;
> +	int ret, i;
>  
>  	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
>  	if (!ctx)
>  		return -ENOMEM;
>  
> +	ctx->mmu = etnaviv_iommu_context_init(priv->mmu_global,
> +					      priv->cmdbuf_suballoc);
> +	if (!ctx->mmu) {
> +		ret = -ENOMEM;
> +		goto out_free;
> +	}
> +
>  	for (i = 0; i < ETNA_MAX_PIPES; i++) {
>  		struct etnaviv_gpu *gpu = priv->gpu[i];
>  		struct drm_sched_rq *rq;
> @@ -70,6 +77,10 @@ static int etnaviv_open(struct drm_device *dev, struct drm_file *file)
>  	file->driver_priv = ctx;
>  
>  	return 0;
> +
> +out_free:
> +	kfree(ctx);
> +	return ret;
>  }
>  
>  static void etnaviv_postclose(struct drm_device *dev, struct drm_file *file)
> @@ -85,6 +96,8 @@ static void etnaviv_postclose(struct drm_device *dev, struct drm_file *file)
>  			drm_sched_entity_destroy(&ctx->sched_entity[i]);
>  	}
>  
> +	etnaviv_iommu_context_put(ctx->mmu);
> +
>  	kfree(ctx);
>  }
>  
> @@ -116,12 +129,29 @@ static int etnaviv_mm_show(struct drm_device *dev, struct seq_file *m)
>  static int etnaviv_mmu_show(struct etnaviv_gpu *gpu, struct seq_file *m)
>  {
>  	struct drm_printer p = drm_seq_file_printer(m);
> +	struct etnaviv_iommu_context *mmu_context;
>  
>  	seq_printf(m, "Active Objects (%s):\n", dev_name(gpu->dev));
>  
> -	mutex_lock(&gpu->mmu_context->lock);
> -	drm_mm_print(&gpu->mmu_context->mm, &p);
> -	mutex_unlock(&gpu->mmu_context->lock);
> +	/*
> +	 * Lock the GPU to avoid a MMU context switch just now and elevate
> +	 * the refcount of the current context to avoid it disappearing from
> +	 * under our feet.
> +	 */
> +	mutex_lock(&gpu->lock);
> +	mmu_context = gpu->mmu_context;
> +	if (mmu_context)
> +		etnaviv_iommu_context_get(mmu_context);
> +	mutex_unlock(&gpu->lock);
> +
> +	if (!mmu_context)
> +		return 0;
> +
> +	mutex_lock(&mmu_context->lock);
> +	drm_mm_print(&mmu_context->mm, &p);
> +	mutex_unlock(&mmu_context->lock);
> +
> +	etnaviv_iommu_context_put(mmu_context);
>  
>  	return 0;
>  }
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h
> index 5f8db08f1c17..a488cfdb6bbf 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h
> @@ -25,10 +25,7 @@ struct etnaviv_gem_submit;
>  struct etnaviv_iommu_global;
>  
>  struct etnaviv_file_private {
> -	/*
> -	 * When per-context address spaces are supported we'd keep track of
> -	 * the context's page-tables here.
> -	 */
> +	struct etnaviv_iommu_context	*mmu;
>  	struct drm_sched_entity		sched_entity[ETNA_MAX_PIPES];
>  };
>  
> @@ -75,6 +72,7 @@ u16 etnaviv_buffer_config_pta(struct etnaviv_gpu *gpu, unsigned short id);
>  void etnaviv_buffer_end(struct etnaviv_gpu *gpu);
>  void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event);
>  void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
> +	struct etnaviv_iommu_context *mmu,
>  	unsigned int event, struct etnaviv_cmdbuf *cmdbuf);
>  void etnaviv_validate_init(void);
>  bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu,
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_dump.c b/drivers/gpu/drm/etnaviv/etnaviv_dump.c
> index 7e6791517693..698db540972c 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_dump.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_dump.c
> @@ -173,12 +173,12 @@ void etnaviv_core_dump(struct etnaviv_gem_submit *submit)
>  	etnaviv_core_dump_mem(&iter, ETDUMP_BUF_RING, gpu->buffer.vaddr,
>  			      gpu->buffer.size,
>  			      etnaviv_cmdbuf_get_va(&gpu->buffer,
> -						    &gpu->cmdbuf_mapping));
> +					&gpu->mmu_context->cmdbuf_mapping));
>  
>  	etnaviv_core_dump_mem(&iter, ETDUMP_BUF_CMD,
>  			      submit->cmdbuf.vaddr, submit->cmdbuf.size,
>  			      etnaviv_cmdbuf_get_va(&submit->cmdbuf,
> -						    &gpu->cmdbuf_mapping));
> +					&gpu->mmu_context->cmdbuf_mapping));
>  
>  	/* Reserve space for the bomap */
>  	if (n_bomap_pages) {
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
> index 04c8170f76cd..e79f6ef3659a 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
> @@ -248,8 +248,7 @@ void etnaviv_gem_mapping_unreference(struct etnaviv_vram_mapping *mapping)
>  }
>  
>  struct etnaviv_vram_mapping *etnaviv_gem_mapping_get(
> -	struct drm_gem_object *obj, struct etnaviv_gpu *gpu,
> -	struct etnaviv_iommu_context *mmu_context)
> +	struct drm_gem_object *obj, struct etnaviv_iommu_context *mmu_context)
>  {
>  	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
>  	struct etnaviv_vram_mapping *mapping;
> @@ -308,8 +307,8 @@ struct etnaviv_vram_mapping *etnaviv_gem_mapping_get(
>  	mapping->context = mmu_context;
>  	mapping->use = 1;
>  
> -	ret = etnaviv_iommu_map_gem(mmu_context, etnaviv_obj, gpu->memory_base,
> -				    mapping);
> +	ret = etnaviv_iommu_map_gem(mmu_context, etnaviv_obj,
> +				    mmu_context->global->memory_base, mapping);
>  	if (ret < 0)
>  		kfree(mapping);
>  	else
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h
> index f342560b5938..1e11659a8842 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h
> @@ -93,6 +93,7 @@ struct etnaviv_gem_submit {
>  	struct kref refcount;
>  	struct etnaviv_file_private *ctx;
>  	struct etnaviv_gpu *gpu;
> +	struct etnaviv_iommu_context *mmu_context, *prev_mmu_context;
>  	struct dma_fence *out_fence, *in_fence;
>  	int out_fence_id;
>  	struct list_head node; /* GPU active submit list */
> @@ -119,8 +120,7 @@ struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *obj);
>  void etnaviv_gem_put_pages(struct etnaviv_gem_object *obj);
>  
>  struct etnaviv_vram_mapping *etnaviv_gem_mapping_get(
> -	struct drm_gem_object *obj, struct etnaviv_gpu *gpu,
> -	struct etnaviv_iommu_context *mmu_context);
> +	struct drm_gem_object *obj, struct etnaviv_iommu_context *mmu_context);
>  void etnaviv_gem_mapping_unreference(struct etnaviv_vram_mapping *mapping);
>  
>  #endif /* __ETNAVIV_GEM_H__ */
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
> index 7929d258daf8..16e7d371a7ef 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
> @@ -224,8 +224,7 @@ static int submit_pin_objects(struct etnaviv_gem_submit *submit)
>  		struct etnaviv_vram_mapping *mapping;
>  
>  		mapping = etnaviv_gem_mapping_get(&etnaviv_obj->base,
> -						  submit->gpu,
> -						  submit->gpu->mmu_context);
> +						  submit->mmu_context);
>  		if (IS_ERR(mapping)) {
>  			ret = PTR_ERR(mapping);
>  			break;
> @@ -362,6 +361,12 @@ static void submit_cleanup(struct kref *kref)
>  	if (submit->cmdbuf.suballoc)
>  		etnaviv_cmdbuf_free(&submit->cmdbuf);
>  
> +	if (submit->mmu_context)
> +		etnaviv_iommu_context_put(submit->mmu_context);
> +
> +	if (submit->prev_mmu_context)
> +		etnaviv_iommu_context_put(submit->prev_mmu_context);
> +
>  	for (i = 0; i < submit->nr_bos; i++) {
>  		struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
>  
> @@ -503,6 +508,8 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
>  		goto err_submit_objects;
>  
>  	submit->ctx = file->driver_priv;
> +	etnaviv_iommu_context_get(submit->ctx->mmu);
> +	submit->mmu_context = submit->ctx->mmu;
>  	submit->exec_state = args->exec_state;
>  	submit->flags = args->flags;
>  
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
> index 3af72a4127b0..d8a83ebfce47 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
> @@ -602,7 +602,8 @@ void etnaviv_gpu_start_fe(struct etnaviv_gpu *gpu, u32 address, u16 prefetch)
>  
>  static void etnaviv_gpu_start_fe_idleloop(struct etnaviv_gpu *gpu)
>  {
> -	u32 address = etnaviv_cmdbuf_get_va(&gpu->buffer, &gpu->cmdbuf_mapping);
> +	u32 address = etnaviv_cmdbuf_get_va(&gpu->buffer,
> +				&gpu->mmu_context->cmdbuf_mapping);
>  	u16 prefetch;
>  
>  	/* setup the MMU */
> @@ -693,8 +694,6 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
>  	etnaviv_gpu_setup_pulse_eater(gpu);
>  
>  	gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U);
> -
> -	etnaviv_gpu_start_fe_idleloop(gpu);
>  }
>  
>  int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
> @@ -724,28 +723,6 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
>  		goto fail;
>  	}
>  
> -	/*
> -	 * Set the GPU linear window to be at the end of the DMA window, where
> -	 * the CMA area is likely to reside. This ensures that we are able to
> -	 * map the command buffers while having the linear window overlap as
> -	 * much RAM as possible, so we can optimize mappings for other buffers.
> -	 *
> -	 * For 3D cores only do this if MC2.0 is present, as with MC1.0 it leads
> -	 * to different views of the memory on the individual engines.
> -	 */
> -	if (!(gpu->identity.features & chipFeatures_PIPE_3D) ||
> -	    (gpu->identity.minor_features0 & chipMinorFeatures0_MC20)) {
> -		u32 dma_mask = (u32)dma_get_required_mask(gpu->dev);
> -		if (dma_mask < PHYS_OFFSET + SZ_2G)
> -			gpu->memory_base = PHYS_OFFSET;
> -		else
> -			gpu->memory_base = dma_mask - SZ_2G + 1;
> -	} else if (PHYS_OFFSET >= SZ_2G) {
> -		dev_info(gpu->dev, "Need to move linear window on MC1.0, disabling TS\n");
> -		gpu->memory_base = PHYS_OFFSET;
> -		gpu->identity.features &= ~chipFeatures_FAST_CLEAR;
> -	}
> -
>  	/*
>  	 * On cores with security features supported, we claim control over the
>  	 * security states.
> @@ -764,20 +741,26 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
>  	if (ret)
>  		goto fail;
>  
> -	gpu->mmu_context = etnaviv_iommu_context_init(priv->mmu_global);
> -	if (IS_ERR(gpu->mmu_context)) {
> -		dev_err(gpu->dev, "Failed to instantiate GPU IOMMU\n");
> -		ret = PTR_ERR(gpu->mmu_context);
> -		goto iommu_global_fini;
> -	}
> -
> -	ret = etnaviv_cmdbuf_suballoc_map(priv->cmdbuf_suballoc,
> -					  gpu->mmu_context,
> -					  &gpu->cmdbuf_mapping,
> -					  gpu->memory_base);
> -	if (ret) {
> -		dev_err(gpu->dev, "failed to map cmdbuf suballoc\n");
> -		goto destroy_iommu;
> +	/*
> +	 * Set the GPU linear window to be at the end of the DMA window, where
> +	 * the CMA area is likely to reside. This ensures that we are able to
> +	 * map the command buffers while having the linear window overlap as
> +	 * much RAM as possible, so we can optimize mappings for other buffers.
> +	 *
> +	 * For 3D cores only do this if MC2.0 is present, as with MC1.0 it leads
> +	 * to different views of the memory on the individual engines.
> +	 */
> +	if (!(gpu->identity.features & chipFeatures_PIPE_3D) ||
> +	    (gpu->identity.minor_features0 & chipMinorFeatures0_MC20)) {
> +		u32 dma_mask = (u32)dma_get_required_mask(gpu->dev);
> +		if (dma_mask < PHYS_OFFSET + SZ_2G)
> +			priv->mmu_global->memory_base = PHYS_OFFSET;
> +		else
> +			priv->mmu_global->memory_base = dma_mask - SZ_2G + 1;
> +	} else if (PHYS_OFFSET >= SZ_2G) {
> +		dev_info(gpu->dev, "Need to move linear window on MC1.0, disabling TS\n");
> +		priv->mmu_global->memory_base = PHYS_OFFSET;
> +		gpu->identity.features &= ~chipFeatures_FAST_CLEAR;
>  	}
>  
>  	/* Create buffer: */
> @@ -785,15 +768,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
>  				  PAGE_SIZE);
>  	if (ret) {
>  		dev_err(gpu->dev, "could not create command buffer\n");
> -		goto unmap_suballoc;
> -	}
> -
> -	if (!(gpu->identity.minor_features1 & chipMinorFeatures1_MMU_VERSION) &&
> -	    etnaviv_cmdbuf_get_va(&gpu->buffer, &gpu->cmdbuf_mapping) > 0x80000000) {
> -		ret = -EINVAL;
> -		dev_err(gpu->dev,
> -			"command buffer outside valid memory window\n");
> -		goto free_buffer;
> +		goto fail;
>  	}
>  
>  	/* Setup event management */
> @@ -816,14 +791,6 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
>  
>  	return 0;
>  
> -free_buffer:
> -	etnaviv_cmdbuf_free(&gpu->buffer);
> -unmap_suballoc:
> -	etnaviv_cmdbuf_suballoc_unmap(gpu->mmu_context, &gpu->cmdbuf_mapping);
> -destroy_iommu:
> -	etnaviv_iommu_context_put(gpu->mmu_context);
> -iommu_global_fini:
> -	etnaviv_iommu_global_fini(gpu);
>  fail:
>  	pm_runtime_mark_last_busy(gpu->dev);
>  	pm_runtime_put_autosuspend(gpu->dev);
> @@ -1017,6 +984,7 @@ void etnaviv_gpu_recover_hang(struct etnaviv_gpu *gpu)
>  
>  	etnaviv_gpu_hw_init(gpu);
>  	gpu->exec_state = -1;
> +	gpu->mmu_context = NULL;
>  
>  	mutex_unlock(&gpu->lock);
>  	pm_runtime_mark_last_busy(gpu->dev);
> @@ -1323,6 +1291,15 @@ struct dma_fence *etnaviv_gpu_submit(struct etnaviv_gem_submit *submit)
>  		goto out_unlock;
>  	}
>  
> +	if (!gpu->mmu_context) {
> +		etnaviv_iommu_context_get(submit->mmu_context);
> +		gpu->mmu_context = submit->mmu_context;
> +		etnaviv_gpu_start_fe_idleloop(gpu);
> +	} else {
> +		etnaviv_iommu_context_get(gpu->mmu_context);
> +		submit->prev_mmu_context = gpu->mmu_context;
> +	}
> +
>  	if (submit->nr_pmrs) {
>  		gpu->event[event[1]].sync_point = &sync_point_perfmon_sample_pre;
>  		kref_get(&submit->refcount);
> @@ -1332,8 +1309,8 @@ struct dma_fence *etnaviv_gpu_submit(struct etnaviv_gem_submit *submit)
>  
>  	gpu->event[event[0]].fence = gpu_fence;
>  	submit->cmdbuf.user_size = submit->cmdbuf.size - 8;
> -	etnaviv_buffer_queue(gpu, submit->exec_state, event[0],
> -			     &submit->cmdbuf);
> +	etnaviv_buffer_queue(gpu, submit->exec_state, submit->mmu_context,
> +			     event[0], &submit->cmdbuf);
>  
>  	if (submit->nr_pmrs) {
>  		gpu->event[event[2]].sync_point = &sync_point_perfmon_sample_post;
> @@ -1535,7 +1512,7 @@ int etnaviv_gpu_wait_idle(struct etnaviv_gpu *gpu, unsigned int timeout_ms)
>  
>  static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
>  {
> -	if (gpu->initialized) {
> +	if (gpu->initialized && gpu->mmu_context) {
>  		/* Replace the last WAIT with END */
>  		mutex_lock(&gpu->lock);
>  		etnaviv_buffer_end(gpu);
> @@ -1547,8 +1524,13 @@ static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
>  		 * we fail, just warn and continue.
>  		 */
>  		etnaviv_gpu_wait_idle(gpu, 100);
> +
> +		etnaviv_iommu_context_put(gpu->mmu_context);
> +		gpu->mmu_context = NULL;
>  	}
>  
> +	gpu->exec_state = -1;
> +
>  	return etnaviv_gpu_clk_disable(gpu);
>  }
>  
> @@ -1564,8 +1546,6 @@ static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu)
>  	etnaviv_gpu_update_clock(gpu);
>  	etnaviv_gpu_hw_init(gpu);
>  
> -	gpu->exec_state = -1;
> -
>  	mutex_unlock(&gpu->lock);
>  
>  	return 0;
> @@ -1696,9 +1676,6 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
>  
>  	if (gpu->initialized) {
>  		etnaviv_cmdbuf_free(&gpu->buffer);
> -		etnaviv_cmdbuf_suballoc_unmap(gpu->mmu_context,
> -					      &gpu->cmdbuf_mapping);
> -		etnaviv_iommu_context_put(gpu->mmu_context);
>  		etnaviv_iommu_global_fini(gpu);
>  		gpu->initialized = false;
>  	}
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h
> index c0bd6018d53b..8f9bd4edc96a 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h
> @@ -103,13 +103,9 @@ struct etnaviv_gpu {
>  	bool initialized;
>  
>  	/* 'ring'-buffer: */
> -	struct etnaviv_vram_mapping cmdbuf_mapping;
>  	struct etnaviv_cmdbuf buffer;
>  	int exec_state;
>  
> -	/* bus base address of memory  */
> -	u32 memory_base;
> -
>  	/* event management: */
>  	DECLARE_BITMAP(event_bitmap, ETNA_NR_EVENTS);
>  	struct etnaviv_event event[ETNA_NR_EVENTS];
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu.c
> index a2f1ff151822..aac8dbf3ea56 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu.c
> @@ -93,11 +93,11 @@ static void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu,
>  	u32 pgtable;
>  
>  	/* set base addresses */
> -	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, gpu->memory_base);
> -	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, gpu->memory_base);
> -	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, gpu->memory_base);
> -	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, gpu->memory_base);
> -	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, gpu->memory_base);
> +	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, context->global->memory_base);
> +	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, context->global->memory_base);
> +	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, context->global->memory_base);
> +	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, context->global->memory_base);
> +	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, context->global->memory_base);
>  
>  	/* set page table address in MC */
>  	pgtable = (u32)v1_context->pgtable_dma;
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c
> index 5ca2077c148d..043111a1d60c 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c
> @@ -206,7 +206,7 @@ static void etnaviv_iommuv2_restore_sec(struct etnaviv_gpu *gpu,
>  		  VIVS_MMUv2_SAFE_ADDRESS_CONFIG_SEC_SAFE_ADDR_HIGH(
>  		  upper_32_bits(context->global->bad_page_dma)));
>  
> -	context->global->v2.pta_cpu[0] = v2_context->mtlb_dma |
> +	context->global->v2.pta_cpu[v2_context->id] = v2_context->mtlb_dma |
>  				 	 VIVS_MMUv2_CONFIGURATION_MODE_MODE4_K;
>  
>  	/* trigger a PTA load through the FE */
> @@ -218,6 +218,19 @@ static void etnaviv_iommuv2_restore_sec(struct etnaviv_gpu *gpu,
>  	gpu_write(gpu, VIVS_MMUv2_SEC_CONTROL, VIVS_MMUv2_SEC_CONTROL_ENABLE);
>  }
>  
> +u32 etnaviv_iommuv2_get_mtlb_addr(struct etnaviv_iommu_context *context)
> +{
> +	struct etnaviv_iommuv2_context *v2_context = to_v2_context(context);
> +
> +	return v2_context->mtlb_dma;
> +}
> +
> +unsigned short etnaviv_iommuv2_get_pta_id(struct etnaviv_iommu_context *context)
> +{
> +	struct etnaviv_iommuv2_context *v2_context = to_v2_context(context);
> +
> +	return v2_context->id;
> +}
>  static void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu,
>  				    struct etnaviv_iommu_context *context)
>  {
> @@ -272,6 +285,8 @@ etnaviv_iommuv2_context_alloc(struct etnaviv_iommu_global *global)
>  	memset32(v2_context->mtlb_cpu, MMUv2_PTE_EXCEPTION,
>  		 MMUv2_MAX_STLB_ENTRIES);
>  
> +	global->v2.pta_cpu[v2_context->id] = v2_context->mtlb_dma;
> +
>  	context = &v2_context->base;
>  	context->global = global;
>  	kref_init(&context->refcount);
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
> index 2f64eef773ed..82822e30bf30 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
> @@ -290,6 +290,8 @@ static void etnaviv_iommu_context_free(struct kref *kref)
>  	struct etnaviv_iommu_context *context =
>  		container_of(kref, struct etnaviv_iommu_context, refcount);
>  
> +	etnaviv_cmdbuf_suballoc_unmap(context, &context->cmdbuf_mapping);
> +
>  	context->global->ops->free(context);
>  }
>  void etnaviv_iommu_context_put(struct etnaviv_iommu_context *context)
> @@ -298,12 +300,28 @@ void etnaviv_iommu_context_put(struct etnaviv_iommu_context *context)
>  }
>  
>  struct etnaviv_iommu_context *
> -etnaviv_iommu_context_init(struct etnaviv_iommu_global *global)
> +etnaviv_iommu_context_init(struct etnaviv_iommu_global *global,
> +			   struct etnaviv_cmdbuf_suballoc *suballoc)
>  {
> +	struct etnaviv_iommu_context *ctx;
> +	int ret;
> +
>  	if (global->version == ETNAVIV_IOMMU_V1)
> -		return etnaviv_iommuv1_context_alloc(global);
> +		ctx = etnaviv_iommuv1_context_alloc(global);
>  	else
> -		return etnaviv_iommuv2_context_alloc(global);
> +		ctx = etnaviv_iommuv2_context_alloc(global);
> +
> +	if (!ctx)
> +		return NULL;
> +
> +	ret = etnaviv_cmdbuf_suballoc_map(suballoc, ctx, &ctx->cmdbuf_mapping,
> +					  global->memory_base);
> +	if (ret) {
> +		global->ops->free(ctx);
> +		return NULL;
> +	}
> +
> +	return ctx;
>  }
>  
>  void etnaviv_iommu_restore(struct etnaviv_gpu *gpu,
> @@ -319,6 +337,12 @@ int etnaviv_iommu_get_suballoc_va(struct etnaviv_iommu_context *context,
>  {
>  	mutex_lock(&context->lock);
>  
> +	if (mapping->use > 0) {
> +		mapping->use++;
> +		mutex_unlock(&context->lock);
> +		return 0;
> +	}
> +
>  	/*
>  	 * For MMUv1 we don't add the suballoc region to the pagetables, as
>  	 * those GPUs can only work with cmdbufs accessed through the linear
> @@ -340,7 +364,6 @@ int etnaviv_iommu_get_suballoc_va(struct etnaviv_iommu_context *context,
>  		mapping->iova = node->start;
>  		ret = etnaviv_context_map(context, node->start, paddr, size,
>  					  ETNAVIV_PROT_READ);
> -
>  		if (ret < 0) {
>  			drm_mm_remove_node(node);
>  			mutex_unlock(&context->lock);
> @@ -363,15 +386,14 @@ void etnaviv_iommu_put_suballoc_va(struct etnaviv_iommu_context *context,
>  {
>  	struct drm_mm_node *node = &mapping->vram_node;
>  
> -	if (!mapping->use)
> -		return;
> -
> -	mapping->use = 0;
> +	mutex_lock(&context->lock);
> +	mapping->use--;
>  
> -	if (context->global->version == ETNAVIV_IOMMU_V1)
> +	if (mapping->use > 0 || context->global->version == ETNAVIV_IOMMU_V1) {
> +		mutex_unlock(&context->lock);
>  		return;
> +	}
>  
> -	mutex_lock(&context->lock);
>  	etnaviv_context_unmap(context, node->start, node->size);
>  	drm_mm_remove_node(node);
>  	mutex_unlock(&context->lock);
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.h b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h
> index 4438d66db6ab..c01491a6c4d8 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.h
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h
> @@ -47,6 +47,8 @@ struct etnaviv_iommu_global {
>  	void *bad_page_cpu;
>  	dma_addr_t bad_page_dma;
>  
> +	u32 memory_base;
> +
>  	/*
>  	 * This union holds members needed by either MMUv1 or MMUv2, which
>  	 * can not exist at the same time.
> @@ -74,6 +76,9 @@ struct etnaviv_iommu_context {
>  	struct list_head mappings;
>  	struct drm_mm mm;
>  	unsigned int flush_seq;
> +
> +	/* Not part of the context, but needs to have the same lifetime */
> +	struct etnaviv_vram_mapping cmdbuf_mapping;
>  };
>  
>  int etnaviv_iommu_global_init(struct etnaviv_gpu *gpu);
> @@ -98,7 +103,8 @@ size_t etnaviv_iommu_dump_size(struct etnaviv_iommu_context *ctx);
>  void etnaviv_iommu_dump(struct etnaviv_iommu_context *ctx, void *buf);
>  
>  struct etnaviv_iommu_context *
> -etnaviv_iommu_context_init(struct etnaviv_iommu_global *global);
> +etnaviv_iommu_context_init(struct etnaviv_iommu_global *global,
> +			   struct etnaviv_cmdbuf_suballoc *suballoc);
>  static inline void etnaviv_iommu_context_get(struct etnaviv_iommu_context *ctx)
>  {
>  	kref_get(&ctx->refcount);
> @@ -112,4 +118,7 @@ etnaviv_iommuv1_context_alloc(struct etnaviv_iommu_global *global);
>  struct etnaviv_iommu_context *
>  etnaviv_iommuv2_context_alloc(struct etnaviv_iommu_global *global);
>  
> +u32 etnaviv_iommuv2_get_mtlb_addr(struct etnaviv_iommu_context *context);
> +unsigned short etnaviv_iommuv2_get_pta_id(struct etnaviv_iommu_context *context);
> +
>  #endif /* __ETNAVIV_MMU_H__ */
> -- 
> 2.20.1
> 
> _______________________________________________
> etnaviv mailing list
> etnaviv at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/etnaviv


More information about the dri-devel mailing list