summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSuzanne Candanedo <suzanne.candanedo@arm.com>2022-10-06 20:03:48 +0100
committerJack Diver <diverj@google.com>2022-10-07 11:24:40 +0000
commitfb2e7ed2859a7cb88c50dc0d357bb27837aaa843 (patch)
treebf181dc170af55f341d94cf4a5c4ec4a3edd1e1e
parent348355041f171b55a3b93f52821158689ae38b1c (diff)
downloadgpu-fb2e7ed2859a7cb88c50dc0d357bb27837aaa843.tar.gz
mali_kbase: MIDCET-4220 Patch for GPUSWERRATA-1430
This patch is a fix for: - SW Errata: 2715151 - CVE: CVE-2022-36449 This patch fixes potential use-after-free after userbuf un-pin Bug: 251397485 Provenance: https://code.ipdelivery.arm.com/c/GPU/mali-ddk/+/4611 Change-Id: I89aae381705466ca5971485d5b3b4ef48bc229d3 Signed-off-by: Jack Diver <diverj@google.com>
-rw-r--r--mali_kbase/jm/mali_kbase_jm_defs.h18
-rw-r--r--mali_kbase/mali_kbase_defs.h15
-rw-r--r--mali_kbase/mali_kbase_jd.c90
-rw-r--r--mali_kbase/mali_kbase_mem.c181
-rw-r--r--mali_kbase/mali_kbase_mem.h28
-rw-r--r--mali_kbase/mali_kbase_mem_linux.c4
-rw-r--r--mali_kbase/mali_kbase_softjobs.c3
7 files changed, 185 insertions, 154 deletions
diff --git a/mali_kbase/jm/mali_kbase_jm_defs.h b/mali_kbase/jm/mali_kbase_jm_defs.h
index 3c4d6b2..c9b9ea0 100644
--- a/mali_kbase/jm/mali_kbase_jm_defs.h
+++ b/mali_kbase/jm/mali_kbase_jm_defs.h
@@ -361,19 +361,6 @@ enum kbase_atom_exit_protected_state {
};
/**
- * struct kbase_ext_res - Contains the info for external resources referred
- * by an atom, which have been mapped on GPU side.
- * @gpu_address: Start address of the memory region allocated for
- * the resource from GPU virtual address space.
- * @alloc: pointer to physical pages tracking object, set on
- * mapping the external resource on GPU side.
- */
-struct kbase_ext_res {
- u64 gpu_address;
- struct kbase_mem_phy_alloc *alloc;
-};
-
-/**
* struct kbase_jd_atom - object representing the atom, containing the complete
* state and attributes of an atom.
* @work: work item for the bottom half processing of the atom,
@@ -406,7 +393,8 @@ struct kbase_ext_res {
* each allocation is read in order to enforce an
* overall physical memory usage limit.
* @nr_extres: number of external resources referenced by the atom.
- * @extres: pointer to the location containing info about
+ * @extres: Pointer to @nr_extres VA regions containing the external
+ * resource allocation and other information.
* @nr_extres external resources referenced by the atom.
* @device_nr: indicates the coregroup with which the atom is
* associated, when
@@ -536,7 +524,7 @@ struct kbase_jd_atom {
#endif /* MALI_JIT_PRESSURE_LIMIT_BASE */
u16 nr_extres;
- struct kbase_ext_res *extres;
+ struct kbase_va_region **extres;
u32 device_nr;
u64 jc;
diff --git a/mali_kbase/mali_kbase_defs.h b/mali_kbase/mali_kbase_defs.h
index ba63e1a..df373cb 100644
--- a/mali_kbase/mali_kbase_defs.h
+++ b/mali_kbase/mali_kbase_defs.h
@@ -1970,17 +1970,15 @@ struct kbasep_gwt_list_element {
* to a @kbase_context.
* @ext_res_node: List head for adding the metadata to a
* @kbase_context.
- * @alloc: The physical memory allocation structure
- * which is mapped.
- * @gpu_addr: The GPU virtual address the resource is
- * mapped to.
+ * @reg: External resource information, containing
+ * the corresponding VA region
* @ref: Reference count.
*
* External resources can be mapped into multiple contexts as well as the same
* context multiple times.
- * As kbase_va_region itself isn't refcounted we can't attach our extra
- * information to it as it could be removed under our feet leaving external
- * resources pinned.
+ * As kbase_va_region is refcounted, we guarantee that it will be available
+ * for the duration of the external resource, meaning it is sufficient to use
+ * it to rederive any additional data, like the GPU address.
* This metadata structure binds a single external resource to a single
* context, ensuring that per context mapping is tracked separately so it can
* be overridden when needed and abuses by the application (freeing the resource
@@ -1988,8 +1986,7 @@ struct kbasep_gwt_list_element {
*/
struct kbase_ctx_ext_res_meta {
struct list_head ext_res_node;
- struct kbase_mem_phy_alloc *alloc;
- u64 gpu_addr;
+ struct kbase_va_region *reg;
u32 ref;
};
diff --git a/mali_kbase/mali_kbase_jd.c b/mali_kbase/mali_kbase_jd.c
index 96c475a..79c8ebb 100644
--- a/mali_kbase/mali_kbase_jd.c
+++ b/mali_kbase/mali_kbase_jd.c
@@ -185,13 +185,7 @@ static void kbase_jd_post_external_resources(struct kbase_jd_atom *katom)
res_no = katom->nr_extres;
while (res_no-- > 0) {
- struct kbase_mem_phy_alloc *alloc = katom->extres[res_no].alloc;
- struct kbase_va_region *reg;
-
- reg = kbase_region_tracker_find_region_base_address(
- katom->kctx,
- katom->extres[res_no].gpu_address);
- kbase_unmap_external_resource(katom->kctx, reg, alloc);
+ kbase_unmap_external_resource(katom->kctx, katom->extres[res_no]);
}
kfree(katom->extres);
katom->extres = NULL;
@@ -207,7 +201,7 @@ static void kbase_jd_post_external_resources(struct kbase_jd_atom *katom)
static int kbase_jd_pre_external_resources(struct kbase_jd_atom *katom, const struct base_jd_atom *user_atom)
{
- int err_ret_val = -EINVAL;
+ int err = -EINVAL;
u32 res_no;
#ifdef CONFIG_MALI_DMA_FENCE
struct kbase_dma_fence_resv_info info = {
@@ -240,21 +234,10 @@ static int kbase_jd_pre_external_resources(struct kbase_jd_atom *katom, const st
if (!katom->extres)
return -ENOMEM;
- /* copy user buffer to the end of our real buffer.
- * Make sure the struct sizes haven't changed in a way
- * we don't support
- */
- BUILD_BUG_ON(sizeof(*input_extres) > sizeof(*katom->extres));
- input_extres = (struct base_external_resource *)
- (((unsigned char *)katom->extres) +
- (sizeof(*katom->extres) - sizeof(*input_extres)) *
- katom->nr_extres);
-
- if (copy_from_user(input_extres,
- get_compat_pointer(katom->kctx, user_atom->extres_list),
- sizeof(*input_extres) * katom->nr_extres) != 0) {
- err_ret_val = -EINVAL;
- goto early_err_out;
+ input_extres = kmalloc_array(katom->nr_extres, sizeof(*input_extres), GFP_KERNEL);
+ if (!input_extres) {
+ err = -ENOMEM;
+ goto failed_input_alloc;
}
#ifdef CONFIG_MALI_DMA_FENCE
@@ -268,40 +251,45 @@ static int kbase_jd_pre_external_resources(struct kbase_jd_atom *katom, const st
#endif
GFP_KERNEL);
if (!info.resv_objs) {
- err_ret_val = -ENOMEM;
- goto early_err_out;
+ err = -ENOMEM;
+ goto failed_input_copy;
}
info.dma_fence_excl_bitmap =
kcalloc(BITS_TO_LONGS(katom->nr_extres),
sizeof(unsigned long), GFP_KERNEL);
if (!info.dma_fence_excl_bitmap) {
- err_ret_val = -ENOMEM;
- goto early_err_out;
+ err = -ENOMEM;
+ goto failed_input_copy;
}
}
#endif /* CONFIG_MALI_DMA_FENCE */
+ if (copy_from_user(input_extres,
+ get_compat_pointer(katom->kctx, user_atom->extres_list),
+ sizeof(*input_extres) * katom->nr_extres) != 0) {
+ err = -EINVAL;
+ goto failed_input_copy;
+ }
+
/* Take the processes mmap lock */
down_read(kbase_mem_get_process_mmap_lock());
/* need to keep the GPU VM locked while we set up UMM buffers */
kbase_gpu_vm_lock(katom->kctx);
for (res_no = 0; res_no < katom->nr_extres; res_no++) {
- struct base_external_resource *res = &input_extres[res_no];
+ struct base_external_resource *user_res = &input_extres[res_no];
struct kbase_va_region *reg;
- struct kbase_mem_phy_alloc *alloc;
#ifdef CONFIG_MALI_DMA_FENCE
bool exclusive;
- exclusive = (res->ext_resource & BASE_EXT_RES_ACCESS_EXCLUSIVE)
+ exclusive = (user_res->ext_resource & BASE_EXT_RES_ACCESS_EXCLUSIVE)
? true : false;
#endif
reg = kbase_region_tracker_find_region_enclosing_address(
- katom->kctx,
- res->ext_resource & ~BASE_EXT_RES_ACCESS_EXCLUSIVE);
+ katom->kctx, user_res->ext_resource & ~BASE_EXT_RES_ACCESS_EXCLUSIVE);
/* did we find a matching region object? */
- if (kbase_is_region_invalid_or_free(reg)) {
+ if (unlikely(kbase_is_region_invalid_or_free(reg))) {
/* roll back */
goto failed_loop;
}
@@ -311,12 +299,9 @@ static int kbase_jd_pre_external_resources(struct kbase_jd_atom *katom, const st
katom->atom_flags |= KBASE_KATOM_FLAG_PROTECTED;
}
- alloc = kbase_map_external_resource(katom->kctx, reg,
- current->mm);
- if (!alloc) {
- err_ret_val = -EINVAL;
+ err = kbase_map_external_resource(katom->kctx, reg, current->mm);
+ if (err)
goto failed_loop;
- }
#ifdef CONFIG_MALI_DMA_FENCE
if (implicit_sync &&
@@ -332,15 +317,7 @@ static int kbase_jd_pre_external_resources(struct kbase_jd_atom *katom, const st
exclusive);
}
#endif /* CONFIG_MALI_DMA_FENCE */
-
- /* finish with updating out array with the data we found */
- /* NOTE: It is important that this is the last thing we do (or
- * at least not before the first write) as we overwrite elements
- * as we loop and could be overwriting ourself, so no writes
- * until the last read for an element.
- */
- katom->extres[res_no].gpu_address = reg->start_pfn << PAGE_SHIFT; /* save the start_pfn (as an address, not pfn) to use fast lookup later */
- katom->extres[res_no].alloc = alloc;
+ katom->extres[res_no] = reg;
}
/* successfully parsed the extres array */
/* drop the vm lock now */
@@ -363,12 +340,13 @@ static int kbase_jd_pre_external_resources(struct kbase_jd_atom *katom, const st
kfree(info.dma_fence_excl_bitmap);
}
#endif /* CONFIG_MALI_DMA_FENCE */
+ /* Free the buffer holding data from userspace */
+ kfree(input_extres);
/* all done OK */
return 0;
/* error handling section */
-
#ifdef CONFIG_MALI_DMA_FENCE
failed_dma_fence_setup:
/* Lock the processes mmap lock */
@@ -378,19 +356,23 @@ failed_dma_fence_setup:
kbase_gpu_vm_lock(katom->kctx);
#endif
- failed_loop:
- /* undo the loop work */
+failed_loop:
+ /* undo the loop work. We are guaranteed to have access to the VA region
+ * as we hold a reference to it until it's unmapped
+ */
while (res_no-- > 0) {
- struct kbase_mem_phy_alloc *alloc = katom->extres[res_no].alloc;
+ struct kbase_va_region *reg = katom->extres[res_no];
- kbase_unmap_external_resource(katom->kctx, NULL, alloc);
+ kbase_unmap_external_resource(katom->kctx, reg);
}
kbase_gpu_vm_unlock(katom->kctx);
/* Release the processes mmap lock */
up_read(kbase_mem_get_process_mmap_lock());
- early_err_out:
+failed_input_copy:
+ kfree(input_extres);
+failed_input_alloc:
kfree(katom->extres);
katom->extres = NULL;
#ifdef CONFIG_MALI_DMA_FENCE
@@ -399,7 +381,7 @@ failed_dma_fence_setup:
kfree(info.dma_fence_excl_bitmap);
}
#endif
- return err_ret_val;
+ return err;
}
static inline void jd_resolve_dep(struct list_head *out_list,
diff --git a/mali_kbase/mali_kbase_mem.c b/mali_kbase/mali_kbase_mem.c
index 6562f01..66280fd 100644
--- a/mali_kbase/mali_kbase_mem.c
+++ b/mali_kbase/mali_kbase_mem.c
@@ -1814,8 +1814,8 @@ bad_insert:
KBASE_EXPORT_TEST_API(kbase_gpu_mmap);
-static void kbase_jd_user_buf_unmap(struct kbase_context *kctx,
- struct kbase_mem_phy_alloc *alloc, bool writeable);
+static void kbase_jd_user_buf_unmap(struct kbase_context *kctx, struct kbase_mem_phy_alloc *alloc,
+ struct kbase_va_region *reg, bool writeable);
int kbase_gpu_munmap(struct kbase_context *kctx, struct kbase_va_region *reg)
{
@@ -1873,21 +1873,19 @@ int kbase_gpu_munmap(struct kbase_context *kctx, struct kbase_va_region *reg)
*/
break;
case KBASE_MEM_TYPE_IMPORTED_USER_BUF: {
- struct kbase_alloc_import_user_buf *user_buf =
- &reg->gpu_alloc->imported.user_buf;
-
- if (user_buf->current_mapping_usage_count & PINNED_ON_IMPORT) {
- user_buf->current_mapping_usage_count &=
- ~PINNED_ON_IMPORT;
-
- /* The allocation could still have active mappings. */
- if (user_buf->current_mapping_usage_count == 0) {
- kbase_jd_user_buf_unmap(kctx, reg->gpu_alloc,
- (reg->flags & (KBASE_REG_CPU_WR |
- KBASE_REG_GPU_WR)));
- }
+ struct kbase_alloc_import_user_buf *user_buf = &reg->gpu_alloc->imported.user_buf;
+
+ if (user_buf->current_mapping_usage_count & PINNED_ON_IMPORT) {
+ user_buf->current_mapping_usage_count &= ~PINNED_ON_IMPORT;
+
+ /* The allocation could still have active mappings. */
+ if (user_buf->current_mapping_usage_count == 0) {
+ kbase_jd_user_buf_unmap(kctx, reg->gpu_alloc, reg,
+ (reg->flags &
+ (KBASE_REG_CPU_WR | KBASE_REG_GPU_WR)));
}
}
+ }
fallthrough;
default:
kbase_mem_phy_alloc_gpu_unmapped(reg->gpu_alloc);
@@ -3064,6 +3062,13 @@ KBASE_EXPORT_TEST_API(kbase_free_phy_pages_helper_locked);
/**
* kbase_jd_user_buf_unpin_pages - Release the pinned pages of a user buffer.
* @alloc: The allocation for the imported user buffer.
+ *
+ * This must only be called when terminating an alloc, when its refcount
+ * (number of users) has become 0. This also ensures it is only called once all
+ * CPU mappings have been closed.
+ *
+ * Instead call kbase_jd_user_buf_unmap() if you need to unpin pages on active
+ * allocations
*/
static void kbase_jd_user_buf_unpin_pages(struct kbase_mem_phy_alloc *alloc);
#endif
@@ -4772,7 +4777,23 @@ void kbase_unpin_user_buf_page(struct page *page)
#if MALI_USE_CSF
static void kbase_jd_user_buf_unpin_pages(struct kbase_mem_phy_alloc *alloc)
{
- if (alloc->nents) {
+ /* In CSF builds, we keep pages pinned until the last reference is
+ * released on the alloc. A refcount of 0 also means we can be sure
+ * that all CPU mappings have been closed on this alloc, and no more
+ * mappings of it will be created.
+ *
+ * Further, the WARN() below captures the restriction that this
+ * function will not handle anything other than the alloc termination
+ * path, because the caller of kbase_mem_phy_alloc_put() is not
+ * required to hold the kctx's reg_lock, and so we could not handle
+ * removing an existing CPU mapping here.
+ *
+ * Refer to this function's kernel-doc comments for alternatives for
+ * unpinning a User buffer.
+ */
+
+ if (alloc->nents && !WARN(kref_read(&alloc->kref) != 0,
+ "must only be called on terminating an allocation")) {
struct page **pages = alloc->imported.user_buf.pages;
long i;
@@ -4780,6 +4801,8 @@ static void kbase_jd_user_buf_unpin_pages(struct kbase_mem_phy_alloc *alloc)
for (i = 0; i < alloc->nents; i++)
kbase_unpin_user_buf_page(pages[i]);
+
+ alloc->nents = 0;
}
}
#endif
@@ -4795,6 +4818,8 @@ int kbase_jd_user_buf_pin_pages(struct kbase_context *kctx,
long i;
int write;
+ lockdep_assert_held(&kctx->reg_lock);
+
if (WARN_ON(alloc->type != KBASE_MEM_TYPE_IMPORTED_USER_BUF))
return -EINVAL;
@@ -4836,6 +4861,9 @@ KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE
return pinned_pages;
if (pinned_pages != alloc->imported.user_buf.nr_pages) {
+ /* Above code already ensures there will not have been a CPU
+ * mapping by ensuring alloc->nents is 0
+ */
for (i = 0; i < pinned_pages; i++)
kbase_unpin_user_buf_page(pages[i]);
return -ENOMEM;
@@ -4849,7 +4877,8 @@ KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE
static int kbase_jd_user_buf_map(struct kbase_context *kctx,
struct kbase_va_region *reg)
{
- long pinned_pages;
+ int err;
+ long pinned_pages = 0;
struct kbase_mem_phy_alloc *alloc;
struct page **pages;
struct tagged_addr *pa;
@@ -4859,13 +4888,15 @@ static int kbase_jd_user_buf_map(struct kbase_context *kctx,
unsigned long offset;
unsigned long local_size;
unsigned long gwt_mask = ~0;
- int err = kbase_jd_user_buf_pin_pages(kctx, reg);
-
/* Calls to this function are inherently asynchronous, with respect to
* MMU operations.
*/
const enum kbase_caller_mmu_sync_info mmu_sync_info = CALLER_MMU_ASYNC;
+ lockdep_assert_held(&kctx->reg_lock);
+
+ err = kbase_jd_user_buf_pin_pages(kctx, reg);
+
if (err)
return err;
@@ -4918,7 +4949,13 @@ unwind:
PAGE_SIZE, DMA_BIDIRECTIONAL);
}
- while (++i < pinned_pages) {
+ /* The user buffer could already have been previously pinned before
+ * entering this function, and hence there could potentially be CPU
+ * mappings of it
+ */
+ kbase_mem_shrink_cpu_mapping(kctx, reg, 0, pinned_pages);
+
+ for (i = 0; i < pinned_pages; i++) {
kbase_unpin_user_buf_page(pages[i]);
pages[i] = NULL;
}
@@ -4930,15 +4967,24 @@ unwind:
* GPUs, which implies that a call to kbase_jd_user_buf_pin_pages() will NOT
* have a corresponding call to kbase_jd_user_buf_unpin_pages().
*/
-static void kbase_jd_user_buf_unmap(struct kbase_context *kctx,
- struct kbase_mem_phy_alloc *alloc, bool writeable)
+static void kbase_jd_user_buf_unmap(struct kbase_context *kctx, struct kbase_mem_phy_alloc *alloc,
+ struct kbase_va_region *reg, bool writeable)
{
long i;
struct page **pages;
unsigned long size = alloc->imported.user_buf.size;
+ lockdep_assert_held(&kctx->reg_lock);
+
KBASE_DEBUG_ASSERT(alloc->type == KBASE_MEM_TYPE_IMPORTED_USER_BUF);
pages = alloc->imported.user_buf.pages;
+
+#if !MALI_USE_CSF
+ kbase_mem_shrink_cpu_mapping(kctx, reg, 0, alloc->nents);
+#else
+ CSTD_UNUSED(reg);
+#endif
+
for (i = 0; i < alloc->imported.user_buf.nr_pages; i++) {
unsigned long local_size;
dma_addr_t dma_addr = alloc->imported.user_buf.dma_addrs[i];
@@ -5000,11 +5046,11 @@ int kbase_mem_copy_to_pinned_user_pages(struct page **dest_pages,
return 0;
}
-struct kbase_mem_phy_alloc *kbase_map_external_resource(
- struct kbase_context *kctx, struct kbase_va_region *reg,
- struct mm_struct *locked_mm)
+int kbase_map_external_resource(struct kbase_context *kctx, struct kbase_va_region *reg,
+ struct mm_struct *locked_mm)
{
- int err;
+ int err = 0;
+ struct kbase_mem_phy_alloc *alloc = reg->gpu_alloc;
lockdep_assert_held(&kctx->reg_lock);
@@ -5013,7 +5059,7 @@ struct kbase_mem_phy_alloc *kbase_map_external_resource(
case KBASE_MEM_TYPE_IMPORTED_USER_BUF: {
if ((reg->gpu_alloc->imported.user_buf.mm != locked_mm) &&
(!reg->gpu_alloc->nents))
- goto exit;
+ return -EINVAL;
reg->gpu_alloc->imported.user_buf.current_mapping_usage_count++;
if (reg->gpu_alloc->imported.user_buf
@@ -5021,7 +5067,7 @@ struct kbase_mem_phy_alloc *kbase_map_external_resource(
err = kbase_jd_user_buf_map(kctx, reg);
if (err) {
reg->gpu_alloc->imported.user_buf.current_mapping_usage_count--;
- goto exit;
+ return err;
}
}
}
@@ -5029,21 +5075,29 @@ struct kbase_mem_phy_alloc *kbase_map_external_resource(
case KBASE_MEM_TYPE_IMPORTED_UMM: {
err = kbase_mem_umm_map(kctx, reg);
if (err)
- goto exit;
+ return err;
break;
}
default:
- goto exit;
+ WARN(1, "Invalid external resource GPU allocation type (%x) on mapping",
+ alloc->type);
+ return -EINVAL;
}
- return kbase_mem_phy_alloc_get(reg->gpu_alloc);
-exit:
- return NULL;
+ kbase_va_region_alloc_get(kctx, reg);
+ kbase_mem_phy_alloc_get(alloc);
+ return err;
}
-void kbase_unmap_external_resource(struct kbase_context *kctx,
- struct kbase_va_region *reg, struct kbase_mem_phy_alloc *alloc)
+void kbase_unmap_external_resource(struct kbase_context *kctx, struct kbase_va_region *reg)
{
+ /* gpu_alloc was used in kbase_map_external_resources, so we need to use it for the
+ * unmapping operation.
+ */
+ struct kbase_mem_phy_alloc *alloc = reg->gpu_alloc;
+
+ lockdep_assert_held(&kctx->reg_lock);
+
switch (alloc->type) {
case KBASE_MEM_TYPE_IMPORTED_UMM: {
kbase_mem_umm_unmap(kctx, reg, alloc);
@@ -5055,26 +5109,31 @@ void kbase_unmap_external_resource(struct kbase_context *kctx,
if (alloc->imported.user_buf.current_mapping_usage_count == 0) {
bool writeable = true;
- if (!kbase_is_region_invalid_or_free(reg) &&
- reg->gpu_alloc == alloc)
- kbase_mmu_teardown_pages(
- kctx->kbdev,
- &kctx->mmu,
- reg->start_pfn,
- kbase_reg_current_backed_size(reg),
- kctx->as_nr);
+ if (!kbase_is_region_invalid_or_free(reg)) {
+ kbase_mmu_teardown_pages(kctx->kbdev, &kctx->mmu, reg->start_pfn,
+ kbase_reg_current_backed_size(reg),
+ kctx->as_nr);
+ }
- if (reg && ((reg->flags & (KBASE_REG_CPU_WR | KBASE_REG_GPU_WR)) == 0))
+ if ((reg->flags & (KBASE_REG_CPU_WR | KBASE_REG_GPU_WR)) == 0)
writeable = false;
- kbase_jd_user_buf_unmap(kctx, alloc, writeable);
+ kbase_jd_user_buf_unmap(kctx, alloc, reg, writeable);
+ }
}
- }
break;
default:
- break;
+ WARN(1, "Invalid external resource GPU allocation type (%x) on unmapping",
+ alloc->type);
+ return;
}
kbase_mem_phy_alloc_put(alloc);
+ kbase_va_region_alloc_put(kctx, reg);
+}
+
+static inline u64 kbasep_get_va_gpu_addr(struct kbase_va_region *reg)
+{
+ return reg->start_pfn << PAGE_SHIFT;
}
struct kbase_ctx_ext_res_meta *kbase_sticky_resource_acquire(
@@ -5090,7 +5149,7 @@ struct kbase_ctx_ext_res_meta *kbase_sticky_resource_acquire(
* metadata which matches the region which is being acquired.
*/
list_for_each_entry(walker, &kctx->ext_res_meta_head, ext_res_node) {
- if (walker->gpu_addr == gpu_addr) {
+ if (kbasep_get_va_gpu_addr(walker->reg) == gpu_addr) {
meta = walker;
meta->ref++;
break;
@@ -5102,8 +5161,7 @@ struct kbase_ctx_ext_res_meta *kbase_sticky_resource_acquire(
struct kbase_va_region *reg;
/* Find the region */
- reg = kbase_region_tracker_find_region_enclosing_address(
- kctx, gpu_addr);
+ reg = kbase_region_tracker_find_region_enclosing_address(kctx, gpu_addr);
if (kbase_is_region_invalid_or_free(reg))
goto failed;
@@ -5111,18 +5169,18 @@ struct kbase_ctx_ext_res_meta *kbase_sticky_resource_acquire(
meta = kzalloc(sizeof(*meta), GFP_KERNEL);
if (!meta)
goto failed;
-
/*
* Fill in the metadata object and acquire a reference
* for the physical resource.
*/
- meta->alloc = kbase_map_external_resource(kctx, reg, NULL);
- meta->ref = 1;
+ meta->reg = reg;
- if (!meta->alloc)
+ /* Map the external resource to the GPU allocation of the region
+ * and acquire the reference to the VA region
+ */
+ if (kbase_map_external_resource(kctx, meta->reg, NULL))
goto fail_map;
-
- meta->gpu_addr = reg->start_pfn << PAGE_SHIFT;
+ meta->ref = 1;
list_add(&meta->ext_res_node, &kctx->ext_res_meta_head);
}
@@ -5147,7 +5205,7 @@ find_sticky_resource_meta(struct kbase_context *kctx, u64 gpu_addr)
* metadata which matches the region which is being released.
*/
list_for_each_entry(walker, &kctx->ext_res_meta_head, ext_res_node)
- if (walker->gpu_addr == gpu_addr)
+ if (kbasep_get_va_gpu_addr(walker->reg) == gpu_addr)
return walker;
return NULL;
@@ -5156,14 +5214,7 @@ find_sticky_resource_meta(struct kbase_context *kctx, u64 gpu_addr)
static void release_sticky_resource_meta(struct kbase_context *kctx,
struct kbase_ctx_ext_res_meta *meta)
{
- struct kbase_va_region *reg;
-
- /* Drop the physical memory reference and free the metadata. */
- reg = kbase_region_tracker_find_region_enclosing_address(
- kctx,
- meta->gpu_addr);
-
- kbase_unmap_external_resource(kctx, reg, meta->alloc);
+ kbase_unmap_external_resource(kctx, meta->reg);
list_del(&meta->ext_res_node);
kfree(meta);
}
diff --git a/mali_kbase/mali_kbase_mem.h b/mali_kbase/mali_kbase_mem.h
index 5f4e85e..1c7169b 100644
--- a/mali_kbase/mali_kbase_mem.h
+++ b/mali_kbase/mali_kbase_mem.h
@@ -1870,28 +1870,36 @@ bool kbase_has_exec_va_zone(struct kbase_context *kctx);
/**
* kbase_map_external_resource - Map an external resource to the GPU.
* @kctx: kbase context.
- * @reg: The region to map.
+ * @reg: External resource to map.
* @locked_mm: The mm_struct which has been locked for this operation.
*
- * Return: The physical allocation which backs the region on success or NULL
- * on failure.
+ * On successful mapping, the VA region and the gpu_alloc refcounts will be
+ * increased, making it safe to use and store both values directly.
+ *
+ * Return: Zero on success, or negative error code.
*/
-struct kbase_mem_phy_alloc *kbase_map_external_resource(
- struct kbase_context *kctx, struct kbase_va_region *reg,
- struct mm_struct *locked_mm);
+int kbase_map_external_resource(struct kbase_context *kctx, struct kbase_va_region *reg,
+ struct mm_struct *locked_mm);
/**
* kbase_unmap_external_resource - Unmap an external resource from the GPU.
* @kctx: kbase context.
- * @reg: The region to unmap or NULL if it has already been released.
- * @alloc: The physical allocation being unmapped.
+ * @reg: VA region corresponding to external resource
+ *
+ * On successful unmapping, the VA region and the gpu_alloc refcounts will
+ * be decreased. If the refcount reaches zero, both @reg and the corresponding
+ * allocation may be freed, so using them after returning from this function
+ * requires the caller to explicitly check their state.
*/
-void kbase_unmap_external_resource(struct kbase_context *kctx,
- struct kbase_va_region *reg, struct kbase_mem_phy_alloc *alloc);
+void kbase_unmap_external_resource(struct kbase_context *kctx, struct kbase_va_region *reg);
/**
* kbase_unpin_user_buf_page - Unpin a page of a user buffer.
* @page: page to unpin
+ *
+ * The caller must have ensured that there are no CPU mappings for @page (as
+ * might be created from the struct kbase_mem_phy_alloc that tracks @page), and
+ * that userspace will not be able to recreate the CPU mappings again.
*/
void kbase_unpin_user_buf_page(struct page *page);
diff --git a/mali_kbase/mali_kbase_mem_linux.c b/mali_kbase/mali_kbase_mem_linux.c
index 39db1f6..c482662 100644
--- a/mali_kbase/mali_kbase_mem_linux.c
+++ b/mali_kbase/mali_kbase_mem_linux.c
@@ -1762,6 +1762,10 @@ unwind_dma_map:
}
fault_mismatch:
if (pages) {
+ /* In this case, the region was not yet in the region tracker,
+ * and so there are no CPU mappings to remove before we unpin
+ * the page
+ */
for (i = 0; i < faulted_pages; i++)
kbase_unpin_user_buf_page(pages[i]);
}
diff --git a/mali_kbase/mali_kbase_softjobs.c b/mali_kbase/mali_kbase_softjobs.c
index 611a3b6..ae3b9ad 100644
--- a/mali_kbase/mali_kbase_softjobs.c
+++ b/mali_kbase/mali_kbase_softjobs.c
@@ -1486,10 +1486,11 @@ static void kbase_ext_res_process(struct kbase_jd_atom *katom, bool map)
if (!kbase_sticky_resource_acquire(katom->kctx,
gpu_addr))
goto failed_loop;
- } else
+ } else {
if (!kbase_sticky_resource_release_force(katom->kctx, NULL,
gpu_addr))
failed = true;
+ }
}
/*