drm: smmu: fix smmu map failure

In concurrency test, smmu map failure appears in a low possibility.
This is caused by virtual address conflict. Display driver calls
different smmu map APIs to do map with the same domain, while the
virtual address allocation happens separately, one in dmp-mapping.c,
and another one is in msm_gem_vam.c. This could not gurantee the
virtual addresses by different callers are different in the same
mmu domain, if they are the same, the virtual address will possibily
be mapped twice, hence conflict happens.

The change is to fix this:
1. Remove the map calling for early splash buffer from msm_smmu_map.
2. Introduce one new map API early_splash_map/unmap, dedicated for
memory mapping for early splash usercase.

CRs-Fixed: 2198827
Change-Id: I8e632f1971998a14c362b5bf0ae9a5522a5a1bff
Signed-off-by: Guchun Chen <guchunc@codeaurora.org>
This commit is contained in:
Guchun Chen 2018-03-09 20:48:57 +08:00
parent 1707cb3ff1
commit e453eb66bc
3 changed files with 61 additions and 24 deletions

View file

@ -46,6 +46,10 @@ struct msm_mmu_funcs {
void (*destroy)(struct msm_mmu *mmu);
void (*enable)(struct msm_mmu *mmu);
void (*disable)(struct msm_mmu *mmu);
int (*early_splash_map)(struct msm_mmu *mmu, uint64_t iova,
struct sg_table *sgt, u32 flags);
void (*early_splash_unmap)(struct msm_mmu *mmu, uint64_t iova,
struct sg_table *sgt);
int (*set_property)(struct msm_mmu *mmu,
enum iommu_attr attr, void *data);
};

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@ -120,21 +120,11 @@ static int msm_smmu_map(struct msm_mmu *mmu, uint64_t iova,
{
struct msm_smmu *smmu = to_msm_smmu(mmu);
struct msm_smmu_client *client = msm_smmu_to_client(smmu);
struct iommu_domain *domain;
int ret;
if (!client || !sgt)
return -EINVAL;
if (iova != 0) {
if (!client->mmu_mapping || !client->mmu_mapping->domain)
return -EINVAL;
domain = client->mmu_mapping->domain;
return iommu_map_sg(domain, iova, sgt->sgl,
sgt->nents, flags);
} else {
if (priv)
ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl,
sgt->nents, DMA_BIDIRECTIONAL, priv);
@ -143,7 +133,6 @@ static int msm_smmu_map(struct msm_mmu *mmu, uint64_t iova,
DMA_BIDIRECTIONAL);
return (ret != sgt->nents) ? -ENOMEM : 0;
}
}
static void msm_smmu_unmap(struct msm_mmu *mmu, uint64_t iova,
@ -160,6 +149,47 @@ static void msm_smmu_unmap(struct msm_mmu *mmu, uint64_t iova,
DMA_BIDIRECTIONAL);
}
static int msm_smmu_early_splash_map(struct msm_mmu *mmu, uint64_t iova,
struct sg_table *sgt, u32 flags)
{
struct msm_smmu *smmu = to_msm_smmu(mmu);
struct msm_smmu_client *client = msm_smmu_to_client(smmu);
struct iommu_domain *domain;
if (!client || !sgt)
return -EINVAL;
if (!client->mmu_mapping || !client->mmu_mapping->domain)
return -EINVAL;
domain = client->mmu_mapping->domain;
return iommu_map_sg(domain, iova, sgt->sgl, sgt->nents, flags);
}
static void msm_smmu_early_splash_unmap(struct msm_mmu *mmu, uint64_t iova,
struct sg_table *sgt)
{
struct msm_smmu *smmu = to_msm_smmu(mmu);
struct msm_smmu_client *client = msm_smmu_to_client(smmu);
struct iommu_domain *domain;
struct scatterlist *sg;
size_t len = 0;
int unmapped, i = 0;
if (!client || !client->mmu_mapping || !client->mmu_mapping->domain)
return;
domain = client->mmu_mapping->domain;
for_each_sg(sgt->sgl, sg, sgt->nents, i)
len += sg->length;
unmapped = iommu_unmap(domain, iova, len);
if (unmapped < len)
DRM_ERROR("could not unmap iova@%llx\n", iova);
}
static void msm_smmu_destroy(struct msm_mmu *mmu)
{
struct msm_smmu *smmu = to_msm_smmu(mmu);
@ -199,6 +229,8 @@ static const struct msm_mmu_funcs funcs = {
.map = msm_smmu_map,
.unmap = msm_smmu_unmap,
.destroy = msm_smmu_destroy,
.early_splash_map = msm_smmu_early_splash_map,
.early_splash_unmap = msm_smmu_early_splash_unmap,
.set_property = msm_smmu_set_property,
};

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, The Linux Foundation. All rights reserved.
* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -301,8 +301,8 @@ static int _sde_splash_free_resource(struct msm_mmu *mmu,
return -EINVAL;
if (mmu->funcs && mmu->funcs->unmap)
mmu->funcs->unmap(mmu, sinfo->splash_mem_paddr[conn],
msm_obj->sgt, NULL);
mmu->funcs->early_splash_unmap(mmu,
sinfo->splash_mem_paddr[conn], msm_obj->sgt);
_sde_splash_free_bootup_memory_to_system(sinfo->splash_mem_paddr[conn],
sinfo->splash_mem_size[conn]);
@ -489,8 +489,9 @@ int sde_splash_smmu_map(struct drm_device *dev, struct msm_mmu *mmu,
msm_obj = to_msm_bo(sinfo->obj[i]);
if (mmu->funcs && mmu->funcs->map) {
ret = mmu->funcs->map(mmu, sinfo->splash_mem_paddr[i],
msm_obj->sgt, IOMMU_READ | IOMMU_NOEXEC, NULL);
ret = mmu->funcs->early_splash_map(mmu,
sinfo->splash_mem_paddr[i], msm_obj->sgt,
IOMMU_READ | IOMMU_NOEXEC);
if (!ret) {
SDE_ERROR("Map blk %d @%pK failed.\n",