Commit 088880dd authored by Lucas Stach's avatar Lucas Stach

drm/etnaviv: implement softpin

With softpin we allow the userspace to take control over the GPU virtual
address space. The new capability is relected by a bump of the minor DRM
version. There are a few restrictions for userspace to take into
account:

1. The kernel reserves a bit of the address space to implement zero page
faulting and mapping of the kernel internal ring buffer. Userspace can
query the kernel for the first usable GPU VM address via
ETNAVIV_PARAM_SOFTPIN_START_ADDR.

2. We only allow softpin on GPUs, which implement proper process
separation via PPAS. If softpin is not available the softpin start
address will be set to ~0.

3. Softpin is all or nothing. A submit using softpin must not use any
address fixups via relocs.
Signed-off-by: default avatarLucas Stach <l.stach@pengutronix.de>
Reviewed-by: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
Reviewed-by: default avatarGuido Günther <agx@sigxcpu.org>
parent 17eae23b
...@@ -44,6 +44,7 @@ etnaviv_cmdbuf_suballoc_new(struct device *dev) ...@@ -44,6 +44,7 @@ etnaviv_cmdbuf_suballoc_new(struct device *dev)
mutex_init(&suballoc->lock); mutex_init(&suballoc->lock);
init_waitqueue_head(&suballoc->free_event); init_waitqueue_head(&suballoc->free_event);
BUILD_BUG_ON(ETNAVIV_SOFTPIN_START_ADDRESS < SUBALLOC_SIZE);
suballoc->vaddr = dma_alloc_wc(dev, SUBALLOC_SIZE, suballoc->vaddr = dma_alloc_wc(dev, SUBALLOC_SIZE,
&suballoc->paddr, GFP_KERNEL); &suballoc->paddr, GFP_KERNEL);
if (!suballoc->vaddr) { if (!suballoc->vaddr) {
......
...@@ -529,7 +529,7 @@ static struct drm_driver etnaviv_drm_driver = { ...@@ -529,7 +529,7 @@ static struct drm_driver etnaviv_drm_driver = {
.desc = "etnaviv DRM", .desc = "etnaviv DRM",
.date = "20151214", .date = "20151214",
.major = 1, .major = 1,
.minor = 2, .minor = 3,
}; };
/* /*
......
...@@ -24,6 +24,8 @@ struct etnaviv_gem_object; ...@@ -24,6 +24,8 @@ struct etnaviv_gem_object;
struct etnaviv_gem_submit; struct etnaviv_gem_submit;
struct etnaviv_iommu_global; struct etnaviv_iommu_global;
#define ETNAVIV_SOFTPIN_START_ADDRESS SZ_4M /* must be >= SUBALLOC_SIZE */
struct etnaviv_file_private { struct etnaviv_file_private {
struct etnaviv_iommu_context *mmu; struct etnaviv_iommu_context *mmu;
struct drm_sched_entity sched_entity[ETNA_MAX_PIPES]; struct drm_sched_entity sched_entity[ETNA_MAX_PIPES];
......
...@@ -248,7 +248,8 @@ void etnaviv_gem_mapping_unreference(struct etnaviv_vram_mapping *mapping) ...@@ -248,7 +248,8 @@ void etnaviv_gem_mapping_unreference(struct etnaviv_vram_mapping *mapping)
} }
struct etnaviv_vram_mapping *etnaviv_gem_mapping_get( struct etnaviv_vram_mapping *etnaviv_gem_mapping_get(
struct drm_gem_object *obj, struct etnaviv_iommu_context *mmu_context) struct drm_gem_object *obj, struct etnaviv_iommu_context *mmu_context,
u64 va)
{ {
struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
struct etnaviv_vram_mapping *mapping; struct etnaviv_vram_mapping *mapping;
...@@ -309,7 +310,7 @@ struct etnaviv_vram_mapping *etnaviv_gem_mapping_get( ...@@ -309,7 +310,7 @@ struct etnaviv_vram_mapping *etnaviv_gem_mapping_get(
ret = etnaviv_iommu_map_gem(mmu_context, etnaviv_obj, ret = etnaviv_iommu_map_gem(mmu_context, etnaviv_obj,
mmu_context->global->memory_base, mmu_context->global->memory_base,
mapping, 0); mapping, va);
if (ret < 0) { if (ret < 0) {
etnaviv_iommu_context_put(mmu_context); etnaviv_iommu_context_put(mmu_context);
kfree(mapping); kfree(mapping);
......
...@@ -77,6 +77,7 @@ static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj) ...@@ -77,6 +77,7 @@ static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj)
struct etnaviv_gem_submit_bo { struct etnaviv_gem_submit_bo {
u32 flags; u32 flags;
u64 va;
struct etnaviv_gem_object *obj; struct etnaviv_gem_object *obj;
struct etnaviv_vram_mapping *mapping; struct etnaviv_vram_mapping *mapping;
struct dma_fence *excl; struct dma_fence *excl;
......
...@@ -72,6 +72,14 @@ static int submit_lookup_objects(struct etnaviv_gem_submit *submit, ...@@ -72,6 +72,14 @@ static int submit_lookup_objects(struct etnaviv_gem_submit *submit,
} }
submit->bos[i].flags = bo->flags; submit->bos[i].flags = bo->flags;
if (submit->flags & ETNA_SUBMIT_SOFTPIN) {
if (bo->presumed < ETNAVIV_SOFTPIN_START_ADDRESS) {
DRM_ERROR("invalid softpin address\n");
ret = -EINVAL;
goto out_unlock;
}
submit->bos[i].va = bo->presumed;
}
/* normally use drm_gem_object_lookup(), but for bulk lookup /* normally use drm_gem_object_lookup(), but for bulk lookup
* all under single table_lock just hit object_idr directly: * all under single table_lock just hit object_idr directly:
...@@ -224,11 +232,17 @@ static int submit_pin_objects(struct etnaviv_gem_submit *submit) ...@@ -224,11 +232,17 @@ static int submit_pin_objects(struct etnaviv_gem_submit *submit)
struct etnaviv_vram_mapping *mapping; struct etnaviv_vram_mapping *mapping;
mapping = etnaviv_gem_mapping_get(&etnaviv_obj->base, mapping = etnaviv_gem_mapping_get(&etnaviv_obj->base,
submit->mmu_context); submit->mmu_context,
submit->bos[i].va);
if (IS_ERR(mapping)) { if (IS_ERR(mapping)) {
ret = PTR_ERR(mapping); ret = PTR_ERR(mapping);
break; break;
} }
if ((submit->flags & ETNA_SUBMIT_SOFTPIN) &&
submit->bos[i].va != mapping->iova)
return -EINVAL;
atomic_inc(&etnaviv_obj->gpu_active); atomic_inc(&etnaviv_obj->gpu_active);
submit->bos[i].flags |= BO_PINNED; submit->bos[i].flags |= BO_PINNED;
...@@ -261,6 +275,10 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream, ...@@ -261,6 +275,10 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream,
u32 *ptr = stream; u32 *ptr = stream;
int ret; int ret;
/* Submits using softpin don't blend with relocs */
if ((submit->flags & ETNA_SUBMIT_SOFTPIN) && nr_relocs != 0)
return -EINVAL;
for (i = 0; i < nr_relocs; i++) { for (i = 0; i < nr_relocs; i++) {
const struct drm_etnaviv_gem_submit_reloc *r = relocs + i; const struct drm_etnaviv_gem_submit_reloc *r = relocs + i;
struct etnaviv_gem_submit_bo *bo; struct etnaviv_gem_submit_bo *bo;
...@@ -445,6 +463,12 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, ...@@ -445,6 +463,12 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
return -EINVAL; return -EINVAL;
} }
if ((args->flags & ETNA_SUBMIT_SOFTPIN) &&
priv->mmu_global->version != ETNAVIV_IOMMU_V2) {
DRM_ERROR("softpin requested on incompatible MMU\n");
return -EINVAL;
}
/* /*
* Copy the command submission and bo array to kernel space in * Copy the command submission and bo array to kernel space in
* one go, and do this outside of any locks. * one go, and do this outside of any locks.
......
...@@ -42,6 +42,8 @@ static const struct platform_device_id gpu_ids[] = { ...@@ -42,6 +42,8 @@ static const struct platform_device_id gpu_ids[] = {
int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value) int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value)
{ {
struct etnaviv_drm_private *priv = gpu->drm->dev_private;
switch (param) { switch (param) {
case ETNAVIV_PARAM_GPU_MODEL: case ETNAVIV_PARAM_GPU_MODEL:
*value = gpu->identity.model; *value = gpu->identity.model;
...@@ -147,6 +149,13 @@ int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value) ...@@ -147,6 +149,13 @@ int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value)
*value = gpu->identity.varyings_count; *value = gpu->identity.varyings_count;
break; break;
case ETNAVIV_PARAM_SOFTPIN_START_ADDR:
if (priv->mmu_global->version == ETNAVIV_IOMMU_V2)
*value = ETNAVIV_SOFTPIN_START_ADDRESS;
else
*value = ~0ULL;
break;
default: default:
DBG("%s: invalid param: %u", dev_name(gpu->dev), param); DBG("%s: invalid param: %u", dev_name(gpu->dev), param);
return -EINVAL; return -EINVAL;
......
...@@ -73,6 +73,7 @@ struct drm_etnaviv_timespec { ...@@ -73,6 +73,7 @@ struct drm_etnaviv_timespec {
#define ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT 0x18 #define ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT 0x18
#define ETNAVIV_PARAM_GPU_NUM_CONSTANTS 0x19 #define ETNAVIV_PARAM_GPU_NUM_CONSTANTS 0x19
#define ETNAVIV_PARAM_GPU_NUM_VARYINGS 0x1a #define ETNAVIV_PARAM_GPU_NUM_VARYINGS 0x1a
#define ETNAVIV_PARAM_SOFTPIN_START_ADDR 0x1b
#define ETNA_MAX_PIPES 4 #define ETNA_MAX_PIPES 4
...@@ -148,6 +149,11 @@ struct drm_etnaviv_gem_submit_reloc { ...@@ -148,6 +149,11 @@ struct drm_etnaviv_gem_submit_reloc {
* then patching the cmdstream for this entry is skipped. This can * then patching the cmdstream for this entry is skipped. This can
* avoid kernel needing to map/access the cmdstream bo in the common * avoid kernel needing to map/access the cmdstream bo in the common
* case. * case.
* If the submit is a softpin submit (ETNA_SUBMIT_SOFTPIN) the 'presumed'
* field is interpreted as the fixed location to map the bo into the gpu
* virtual address space. If the kernel is unable to map the buffer at
* this location the submit will fail. This means userspace is responsible
* for the whole gpu virtual address management.
*/ */
#define ETNA_SUBMIT_BO_READ 0x0001 #define ETNA_SUBMIT_BO_READ 0x0001
#define ETNA_SUBMIT_BO_WRITE 0x0002 #define ETNA_SUBMIT_BO_WRITE 0x0002
...@@ -177,9 +183,11 @@ struct drm_etnaviv_gem_submit_pmr { ...@@ -177,9 +183,11 @@ struct drm_etnaviv_gem_submit_pmr {
#define ETNA_SUBMIT_NO_IMPLICIT 0x0001 #define ETNA_SUBMIT_NO_IMPLICIT 0x0001
#define ETNA_SUBMIT_FENCE_FD_IN 0x0002 #define ETNA_SUBMIT_FENCE_FD_IN 0x0002
#define ETNA_SUBMIT_FENCE_FD_OUT 0x0004 #define ETNA_SUBMIT_FENCE_FD_OUT 0x0004
#define ETNA_SUBMIT_SOFTPIN 0x0008
#define ETNA_SUBMIT_FLAGS (ETNA_SUBMIT_NO_IMPLICIT | \ #define ETNA_SUBMIT_FLAGS (ETNA_SUBMIT_NO_IMPLICIT | \
ETNA_SUBMIT_FENCE_FD_IN | \ ETNA_SUBMIT_FENCE_FD_IN | \
ETNA_SUBMIT_FENCE_FD_OUT) ETNA_SUBMIT_FENCE_FD_OUT| \
ETNA_SUBMIT_SOFTPIN)
#define ETNA_PIPE_3D 0x00 #define ETNA_PIPE_3D 0x00
#define ETNA_PIPE_2D 0x01 #define ETNA_PIPE_2D 0x01
#define ETNA_PIPE_VG 0x02 #define ETNA_PIPE_VG 0x02
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment