void *vb2_ion_private_alloc(void *alloc_ctx, size_t size) { struct vb2_ion_context *ctx = alloc_ctx; struct vb2_ion_buf *buf; int flags = ion_heapflag(ctx->flags); int ret = 0; buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) { pr_err("%s error: fail to kzalloc size(%d)\n", __func__, sizeof(*buf)); return ERR_PTR(-ENOMEM); } size = PAGE_ALIGN(size); buf->handle = ion_alloc(ctx->client, size, ctx->alignment, flags, flags); if (IS_ERR(buf->handle)) { ret = -ENOMEM; goto err_alloc; } buf->cookie.sgt = ion_sg_table(ctx->client, buf->handle); buf->ctx = ctx; buf->size = size; buf->kva = ion_map_kernel(ctx->client, buf->handle); if (IS_ERR(buf->kva)) { ret = PTR_ERR(buf->kva); buf->kva = NULL; goto err_map_kernel; } return &buf->cookie; err_map_kernel: ion_free(ctx->client, buf->handle); err_alloc: kfree(buf); pr_err("%s: Error occured while allocating\n", __func__); return ERR_PTR(ret); }
/*! ****************************************************************************** @Function ImportPages ******************************************************************************/ static IMG_RESULT ImportPages( SYSMEM_Heap *heap, SYSDEVU_sInfo *sysdev, IMG_UINT32 ui32Size, SYSMEMU_sPages *psPages, SYS_eMemAttrib eMemAttrib, IMG_INT32 buff_fd, IMG_UINT64 *pPhyAddrs, IMG_VOID *priv, IMG_BOOL kernelMapped ) { size_t numPages = (ui32Size + HOST_MMU_PAGE_SIZE - 1)/HOST_MMU_PAGE_SIZE; struct ion_handle *ionHandle; IMG_RESULT result = IMG_ERROR_FATAL; unsigned pg_i = 0; struct ion_client *pIONcl; DEBUG_REPORT(REPORT_MODULE_SYSMEM, "Importing buff_fd %d of size %u", buff_fd, ui32Size); pIONcl = get_ion_client(); if (!pIONcl) goto exitFailGetClient; ionHandle = ion_import_dma_buf(pIONcl, buff_fd); if (IS_ERR(ionHandle)) { REPORT(REPORT_MODULE_SYSMEM, REPORT_ERR, "Error obtaining handle from fd %d", buff_fd); result = IMG_ERROR_FATAL; goto exitFailImportFD; } psPages->pvImplData = ionHandle; #if defined(ION_SYSTEM_HEAP) { struct scatterlist *psScattLs, *psScattLsAux; struct sg_table *psSgTable; psSgTable = ion_sg_table(pIONcl, ionHandle); if (psSgTable == NULL) { REPORT(REPORT_MODULE_SYSMEM, REPORT_ERR, "Error obtaining sg table"); result = IMG_ERROR_FATAL; goto exitFailMap; } psScattLs = psSgTable->sgl; if (psScattLs == NULL) { REPORT(REPORT_MODULE_SYSMEM, REPORT_ERR, "Error obtaining scatter list"); result = IMG_ERROR_FATAL; goto exitFailMap; } // Get physical addresses from scatter list for (psScattLsAux = psScattLs; psScattLsAux; psScattLsAux = sg_next(psScattLsAux)) { int offset; dma_addr_t chunkBase = sg_phys(psScattLsAux); for (offset = 0; offset < psScattLsAux->length; offset += PAGE_SIZE, ++pg_i) { if (pg_i >= numPages) break; pPhyAddrs[pg_i] = chunkBase + offset; } if (pg_i >= numPages) break; } if (kernelMapped) psPages->pvCpuKmAddr = ion_map_kernel(pIONcl, ionHandle); } #else { int offset; ion_phys_addr_t physaddr; size_t len = 0; result = ion_phys(pIONcl, ionHandle, &physaddr, &len); if(result) { IMG_ASSERT(!"ion_phys failed"); result = IMG_ERROR_FATAL; goto exitFailMap; } for (offset = 0; pg_i < numPages; offset += PAGE_SIZE, ++pg_i) { if (pg_i >= numPages) break; pPhyAddrs[pg_i] = physaddr + offset; } if (kernelMapped) psPages->pvCpuKmAddr = SYSMEMU_CpuPAddrToCpuKmAddr(heap->memId, physaddr); } #endif { size_t physAddrArrSize = numPages * sizeof(psPages->ppaPhysAddr[0]); size_t phy_i; psPages->ppaPhysAddr = IMG_BIGORSMALL_ALLOC(physAddrArrSize); IMG_ASSERT(psPages->ppaPhysAddr != IMG_NULL); if (psPages->ppaPhysAddr == IMG_NULL) { return IMG_ERROR_OUT_OF_MEMORY; } for (phy_i = 0; phy_i < numPages; ++phy_i) psPages->ppaPhysAddr[phy_i] = pPhyAddrs[phy_i]; } if (kernelMapped && psPages->pvCpuKmAddr == NULL) { REPORT(REPORT_MODULE_SYSMEM, REPORT_ERR, "Error mapping to kernel address"); result = IMG_ERROR_FATAL; goto exitFailMapKernel; } result = IMG_SUCCESS; exitFailMapKernel: exitFailMap: exitFailImportFD: exitFailGetClient: return result; }
/***************************************************************************** @Function AllocPages ******************************************************************************/ static IMG_RESULT AllocPages( SYSMEM_Heap * heap, IMG_UINT32 ui32Size, SYSMEMU_sPages * psPages, SYS_eMemAttrib eMemAttrib ) { IMG_UINT32 Res; struct ion_handle * ion_handle; unsigned allocFlags; struct ion_client * ion_client; IMG_UINT64 * pCpuPhysAddrs; size_t numPages; size_t physAddrArrSize; ion_client = (struct ion_client *)heap->priv; if ( (eMemAttrib & SYS_MEMATTRIB_WRITECOMBINE) || (eMemAttrib & SYS_MEMATTRIB_UNCACHED)) { allocFlags = 0; } else { allocFlags = ION_FLAG_CACHED; } if (eMemAttrib == SYS_MEMATTRIB_UNCACHED) REPORT(REPORT_MODULE_SYSMEM, REPORT_WARNING, "Purely uncached memory is not supported by ION"); // PAGE_SIZE aligment, heap depends on platform ion_handle = ion_alloc(ion_client, ui32Size, PAGE_SIZE, ION_HEAP_SYSTEM_MASK, allocFlags); if (!ion_handle) { REPORT(REPORT_MODULE_SYSMEM, REPORT_ERR, "Error allocating %u bytes from ion", ui32Size); Res = IMG_ERROR_OUT_OF_MEMORY; goto errAlloc; } /* Find out physical addresses in the mappable region */ numPages = (ui32Size + HOST_MMU_PAGE_SIZE - 1)/HOST_MMU_PAGE_SIZE; physAddrArrSize = sizeof *pCpuPhysAddrs * numPages; pCpuPhysAddrs = IMG_BIGORSMALL_ALLOC(physAddrArrSize); if (!pCpuPhysAddrs) { Res = IMG_ERROR_OUT_OF_MEMORY; goto errPhysArrAlloc; } { struct scatterlist *psScattLs, *psScattLsAux; struct sg_table *psSgTable; size_t pg_i = 0; psSgTable = ion_sg_table(ion_client, ion_handle); if (psSgTable == NULL) { REPORT(REPORT_MODULE_SYSMEM, REPORT_ERR, "Error obtaining sg table"); Res = IMG_ERROR_FATAL; goto errGetPhys; } psScattLs = psSgTable->sgl; if (psScattLs == NULL) { REPORT(REPORT_MODULE_SYSMEM, REPORT_ERR, "Error obtaining scatter list"); Res = IMG_ERROR_FATAL; goto errGetPhys; } // Get physical addresses from scatter list for (psScattLsAux = psScattLs; psScattLsAux; psScattLsAux = sg_next(psScattLsAux)) { int offset; dma_addr_t chunkBase = sg_phys(psScattLsAux); for (offset = 0; offset < psScattLsAux->length; offset += PAGE_SIZE, ++pg_i) { if (pg_i >= numPages) break; //pCpuPhysAddrs[pg_i] = dma_map_page(NULL, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); pCpuPhysAddrs[pg_i] = chunkBase + offset; } if (pg_i >= numPages) break; } } // Set pointer to physical address in structure psPages->ppaPhysAddr = pCpuPhysAddrs; DEBUG_REPORT(REPORT_MODULE_SYSMEM, "%s region of size %u phys 0x%llx", __FUNCTION__, ui32Size, psPages->ppaPhysAddr[0]); Res = SYSBRGU_CreateMappableRegion(psPages->ppaPhysAddr[0], ui32Size, eMemAttrib, psPages, &psPages->hRegHandle); if (Res != IMG_SUCCESS) { REPORT(REPORT_MODULE_SYSMEM, REPORT_ERR, "Error %u in SYSBRGU_CreateMappableRegion", Res); goto errCreateMapRegion; } psPages->pvImplData = ion_handle; return IMG_SUCCESS; errCreateMapRegion: errGetPhys: IMG_BIGORSMALL_FREE(numPages*sizeof(*pCpuPhysAddrs), pCpuPhysAddrs); errPhysArrAlloc: ion_unmap_kernel(ion_client, ion_handle); ion_free(ion_client, ion_handle); errAlloc: return Res; }
PVRSRV_ERROR IonImportBufferAndAcquirePhysAddr(IMG_HANDLE hIonDev, IMG_UINT32 ui32NumFDs, IMG_INT32 *pai32BufferFDs, IMG_UINT32 *pui32PageCount, IMG_SYS_PHYADDR **ppsSysPhysAddr, IMG_PVOID *ppvKernAddr0, IMG_HANDLE *phPriv, IMG_HANDLE *phUnique) { struct scatterlist *psTemp, *psScatterList[MAX_IMPORT_ION_FDS] = {}; PVRSRV_ERROR eError = PVRSRV_ERROR_OUT_OF_MEMORY; struct ion_client *psIonClient = hIonDev; IMG_UINT32 i, k, ui32PageCount = 0; ION_IMPORT_DATA *psImportData; if(ui32NumFDs > MAX_IMPORT_ION_FDS) { printk(KERN_ERR "%s: More ion export fds passed in than supported " "(%d provided, %d max)", __func__, ui32NumFDs, MAX_IMPORT_ION_FDS); return PVRSRV_ERROR_INVALID_PARAMS; } psImportData = kzalloc(sizeof(ION_IMPORT_DATA), GFP_KERNEL); if (psImportData == NULL) { goto exitFailKMallocImportData; } /* Set up import data for free call */ psImportData->psIonClient = psIonClient; psImportData->ui32NumIonHandles = ui32NumFDs; for(i = 0; i < ui32NumFDs; i++) { int fd = (int)pai32BufferFDs[i]; struct sg_table *psSgTable; psImportData->apsIonHandle[i] = ion_import_dma_buf(psIonClient, fd); if (psImportData->apsIonHandle[i] == IMG_NULL) { eError = PVRSRV_ERROR_BAD_MAPPING; goto exitFailImport; } psSgTable = ion_sg_table(psIonClient, psImportData->apsIonHandle[i]); psScatterList[i] = psSgTable->sgl; if (psScatterList[i] == NULL) { eError = PVRSRV_ERROR_INVALID_PARAMS; goto exitFailImport; } /* Although all heaps will provide an sg_table, the tables cannot * always be trusted because sg_lists are just pointers to "struct * page" values, and some memory e.g. carveout may not have valid * "struct page" values. In particular, on ARM, carveout is * generally reserved with memblock_remove(), which leaves the * "struct page" entries uninitialized when SPARSEMEM is enabled. * The effect of this is that page_to_pfn(pfn_to_page(pfn)) != pfn. * * There's more discussion on this mailing list thread: * http://lists.linaro.org/pipermail/linaro-mm-sig/2012-August/002440.html * * If the heap this buffer comes from implements ->phys(), it's * probably a contiguous allocator. If the phys() function is * implemented, we'll use it to check sg_table->sgl[0]. If we find * they don't agree, we'll assume phys() is more reliable and use * that. * * Some heaps out there will implement phys() even though they are * not for physically contiguous allocations (so the sg_table must * be used). Therefore use the sg_table if the phys() and first * sg_table entry match. This should be reliable because for most * contiguous allocators, the sg_table should be a single span * from 'start' to 'start+size'. * * Also, ion prints out an error message if the heap doesn't implement * ->phys(), which we want to avoid, so only use ->phys() if the * sg_table contains a single span and therefore could plausibly * be a contiguous allocator. */ if(!sg_next(psScatterList[i])) { ion_phys_addr_t sPhyAddr; size_t sLength; if(!ion_phys(psIonClient, psImportData->apsIonHandle[i], &sPhyAddr, &sLength)) { BUG_ON(sLength & ~PAGE_MASK); if(sg_phys(psScatterList[i]) != sPhyAddr) { psScatterList[i] = IMG_NULL; ui32PageCount += sLength / PAGE_SIZE; } } } for(psTemp = psScatterList[i]; psTemp; psTemp = sg_next(psTemp)) { IMG_UINT32 j; for (j = 0; j < psTemp->length; j += PAGE_SIZE) { ui32PageCount++; } } } BUG_ON(ui32PageCount == 0); psImportData->psSysPhysAddr = kmalloc(sizeof(IMG_SYS_PHYADDR) * ui32PageCount, GFP_KERNEL); if (psImportData->psSysPhysAddr == NULL) { goto exitFailImport; } for(i = 0, k = 0; i < ui32NumFDs; i++) { if(psScatterList[i]) { for(psTemp = psScatterList[i]; psTemp; psTemp = sg_next(psTemp)) { IMG_UINT32 j; for (j = 0; j < psTemp->length; j += PAGE_SIZE) { psImportData->psSysPhysAddr[k].uiAddr = sg_phys(psTemp) + j; k++; } } } else { ion_phys_addr_t sPhyAddr; size_t sLength, j; ion_phys(psIonClient, psImportData->apsIonHandle[i], &sPhyAddr, &sLength); for(j = 0; j < sLength; j += PAGE_SIZE) { psImportData->psSysPhysAddr[k].uiAddr = sPhyAddr + j; k++; } } } *pui32PageCount = ui32PageCount; *ppsSysPhysAddr = psImportData->psSysPhysAddr; #if defined(PDUMP) if(ui32NumFDs == 1) { IMG_PVOID pvKernAddr0; pvKernAddr0 = ion_map_kernel(psIonClient, psImportData->apsIonHandle[0]); if (IS_ERR(pvKernAddr0)) { pvKernAddr0 = IMG_NULL; } psImportData->pvKernAddr0 = pvKernAddr0; *ppvKernAddr0 = pvKernAddr0; } else #endif /* defined(PDUMP) */ { *ppvKernAddr0 = NULL; } *phPriv = psImportData; *phUnique = (IMG_HANDLE)psImportData->psSysPhysAddr[0].uiAddr; return PVRSRV_OK; exitFailImport: for(i = 0; psImportData->apsIonHandle[i] != NULL; i++) { ion_free(psIonClient, psImportData->apsIonHandle[i]); } kfree(psImportData); exitFailKMallocImportData: return eError; }