soc: hab: refine codes to support multi import

Make HAB clients have chance to import the
same export id to fd and uva before unimport
happens.

Change-Id: I59fdc701dcdc086e58865fa216b10f9fe6a45e29
Signed-off-by: Yajun Li <yajunl@codeaurora.org>
This commit is contained in:
Yajun Li 2018-09-18 19:16:22 +08:00 committed by Gerrit - the friendly Code Review server
parent 3b8fc0b7a3
commit 4d4b4c61ab

View file

@ -21,18 +21,14 @@ struct pages_list {
struct page **pages;
long npages;
uint64_t index; /* for mmap first call */
int kernel;
void *kva;
void *uva;
int refcntk;
int refcntu;
uint32_t userflags;
struct file *filp_owner;
struct file *filp_mapper;
struct dma_buf *dmabuf;
int32_t export_id;
int32_t vcid;
struct physical_channel *pchan;
struct kref refcount;
};
struct importer_context {
@ -42,6 +38,118 @@ struct importer_context {
rwlock_t implist_lock;
};
static struct pages_list *pages_list_create(
void *imp_ctx,
struct export_desc *exp,
uint32_t userflags)
{
struct page **pages;
struct compressed_pfns *pfn_table =
(struct compressed_pfns *)exp->payload;
struct pages_list *pglist;
unsigned long pfn;
int i, j, k = 0, size;
if (!pfn_table)
return ERR_PTR(-EINVAL);
size = exp->payload_count * sizeof(struct page *);
pages = kmalloc(size, GFP_KERNEL);
if (!pages)
return ERR_PTR(-ENOMEM);
pglist = kzalloc(sizeof(*pglist), GFP_KERNEL);
if (!pglist) {
kfree(pages);
return ERR_PTR(-ENOMEM);
}
pfn = pfn_table->first_pfn;
for (i = 0; i < pfn_table->nregions; i++) {
for (j = 0; j < pfn_table->region[i].size; j++) {
pages[k] = pfn_to_page(pfn+j);
k++;
}
pfn += pfn_table->region[i].size + pfn_table->region[i].space;
}
pglist->pages = pages;
pglist->npages = exp->payload_count;
pglist->userflags = userflags;
pglist->export_id = exp->export_id;
pglist->vcid = exp->vcid_remote;
pglist->pchan = exp->pchan;
kref_init(&pglist->refcount);
return pglist;
}
static void pages_list_destroy(struct kref *refcount)
{
struct pages_list *pglist = container_of(refcount,
struct pages_list, refcount);
if (pglist->kva)
vunmap(pglist->kva);
kfree(pglist->pages);
kfree(pglist);
}
static void pages_list_get(struct pages_list *pglist)
{
kref_get(&pglist->refcount);
}
static int pages_list_put(struct pages_list *pglist)
{
return kref_put(&pglist->refcount, pages_list_destroy);
}
static struct pages_list *pages_list_lookup(
struct importer_context *imp_ctx,
uint32_t export_id, struct physical_channel *pchan)
{
struct pages_list *pglist, *tmp;
read_lock(&imp_ctx->implist_lock);
list_for_each_entry_safe(pglist, tmp, &imp_ctx->imp_list, list) {
if (pglist->export_id == export_id &&
pglist->pchan == pchan) {
pages_list_get(pglist);
read_unlock(&imp_ctx->implist_lock);
return pglist;
}
}
read_unlock(&imp_ctx->implist_lock);
return NULL;
}
static void pages_list_add(struct importer_context *imp_ctx,
struct pages_list *pglist)
{
pages_list_get(pglist);
write_lock(&imp_ctx->implist_lock);
list_add_tail(&pglist->list, &imp_ctx->imp_list);
imp_ctx->cnt++;
write_unlock(&imp_ctx->implist_lock);
}
static void pages_list_remove(struct importer_context *imp_ctx,
struct pages_list *pglist)
{
write_lock(&imp_ctx->implist_lock);
list_del(&pglist->list);
imp_ctx->cnt--;
write_unlock(&imp_ctx->implist_lock);
pages_list_put(pglist);
}
void *habmm_hyp_allocate_grantable(int page_count,
uint32_t *sizebytes)
{
@ -192,9 +300,12 @@ static int habmem_get_dma_pages_from_fd(int32_t fd,
for (j = 0; j < (s->length >> PAGE_SHIFT); j++) {
pages[rc] = nth_page(page, j);
rc++;
if (WARN_ON(rc >= page_count))
if (rc >= page_count)
break;
}
if (rc >= page_count)
break;
}
err:
@ -320,16 +431,8 @@ void habmem_imp_hyp_close(void *imp_ctx, int kernel)
if (!priv)
return;
list_for_each_entry_safe(pglist, pglist_tmp, &priv->imp_list, list) {
if (kernel && pglist->kva)
vunmap(pglist->kva);
list_del(&pglist->list);
priv->cnt--;
kfree(pglist->pages);
kfree(pglist);
}
list_for_each_entry_safe(pglist, pglist_tmp, &priv->imp_list, list)
pages_list_remove(priv, pglist);
kfree(priv);
}
@ -406,10 +509,19 @@ static int hab_map_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
static void hab_map_open(struct vm_area_struct *vma)
{
struct pages_list *pglist =
(struct pages_list *)vma->vm_private_data;
pages_list_get(pglist);
}
static void hab_map_close(struct vm_area_struct *vma)
{
struct pages_list *pglist =
(struct pages_list *)vma->vm_private_data;
pages_list_put(pglist);
vma->vm_private_data = NULL;
}
static const struct vm_operations_struct habmem_vm_ops = {
@ -418,6 +530,51 @@ static const struct vm_operations_struct habmem_vm_ops = {
.close = hab_map_close,
};
static int hab_buffer_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct pages_list *pglist = vma->vm_private_data;
pgoff_t page_offset;
int ret;
page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >>
PAGE_SHIFT;
if (page_offset > pglist->npages)
return VM_FAULT_SIGBUS;
ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address,
pglist->pages[page_offset]);
switch (ret) {
case 0:
return VM_FAULT_NOPAGE;
case -ENOMEM:
return VM_FAULT_OOM;
case -EBUSY:
return VM_FAULT_RETRY;
case -EFAULT:
case -EINVAL:
return VM_FAULT_SIGBUS;
default:
WARN_ON(1);
return VM_FAULT_SIGBUS;
}
}
static void hab_buffer_open(struct vm_area_struct *vma)
{
}
static void hab_buffer_close(struct vm_area_struct *vma)
{
}
static const struct vm_operations_struct hab_buffer_vm_ops = {
.fault = hab_buffer_fault,
.open = hab_buffer_open,
.close = hab_buffer_close,
};
static int hab_mem_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{
struct pages_list *pglist = dmabuf->priv;
@ -431,7 +588,7 @@ static int hab_mem_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_ops = &habmem_vm_ops;
vma->vm_ops = &hab_buffer_vm_ops;
vma->vm_private_data = pglist;
vma->vm_flags |= VM_MIXEDMAP;
@ -440,6 +597,9 @@ static int hab_mem_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
static void hab_mem_dma_buf_release(struct dma_buf *dmabuf)
{
struct pages_list *pglist = dmabuf->priv;
pages_list_put(pglist);
}
static void *hab_mem_dma_buf_kmap(struct dma_buf *dmabuf,
@ -470,82 +630,50 @@ static int habmem_imp_hyp_map_fd(void *imp_ctx,
uint32_t userflags,
int32_t *pfd)
{
struct page **pages;
struct compressed_pfns *pfn_table =
(struct compressed_pfns *)exp->payload;
struct pages_list *pglist;
struct importer_context *priv = imp_ctx;
unsigned long pfn;
int i, j, k = 0;
pgprot_t prot = PAGE_KERNEL;
int32_t fd, size;
int32_t fd = -1;
int ret;
struct dma_buf *dmabuf;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
if (!pfn_table || !priv)
if (!priv)
return -EINVAL;
size = exp->payload_count * sizeof(struct page *);
pages = kmalloc(size, GFP_KERNEL);
if (!pages)
return -ENOMEM;
pglist = kzalloc(sizeof(*pglist), GFP_KERNEL);
if (!pglist) {
kfree(pages);
return -ENOMEM;
}
pglist = pages_list_lookup(priv, exp->export_id, exp->pchan);
if (pglist)
goto buffer_ready;
pfn = pfn_table->first_pfn;
for (i = 0; i < pfn_table->nregions; i++) {
for (j = 0; j < pfn_table->region[i].size; j++) {
pages[k] = pfn_to_page(pfn+j);
k++;
}
pfn += pfn_table->region[i].size + pfn_table->region[i].space;
}
pglist = pages_list_create(imp_ctx, exp, userflags);
if (IS_ERR(pglist))
return PTR_ERR(pglist);
pglist->pages = pages;
pglist->npages = exp->payload_count;
pglist->kernel = 0;
pglist->index = 0;
pglist->refcntk = pglist->refcntu = 0;
pglist->userflags = userflags;
pglist->export_id = exp->export_id;
pglist->vcid = exp->vcid_remote;
pglist->pchan = exp->pchan;
if (!(userflags & HABMM_IMPORT_FLAGS_CACHED))
prot = pgprot_writecombine(prot);
pages_list_add(priv, pglist);
buffer_ready:
exp_info.ops = &dma_buf_ops;
exp_info.size = exp->payload_count << PAGE_SHIFT;
exp_info.size = pglist->npages << PAGE_SHIFT;
exp_info.flags = O_RDWR;
exp_info.priv = pglist;
pglist->dmabuf = dma_buf_export(&exp_info);
if (IS_ERR(pglist->dmabuf)) {
ret = PTR_ERR(pglist->dmabuf);
kfree(pages);
kfree(pglist);
return ret;
dmabuf = dma_buf_export(&exp_info);
if (IS_ERR(dmabuf)) {
pr_err("export to dmabuf failed\n");
ret = PTR_ERR(dmabuf);
goto proc_end;
}
pages_list_get(pglist);
fd = dma_buf_fd(pglist->dmabuf, O_CLOEXEC);
fd = dma_buf_fd(dmabuf, O_CLOEXEC);
if (fd < 0) {
dma_buf_put(pglist->dmabuf);
kfree(pages);
kfree(pglist);
return -EINVAL;
pr_err("dma buf to fd failed\n");
dma_buf_put(dmabuf);
ret = -EINVAL;
goto proc_end;
}
pglist->refcntk++;
write_lock(&priv->implist_lock);
list_add_tail(&pglist->list, &priv->imp_list);
priv->cnt++;
write_unlock(&priv->implist_lock);
proc_end:
*pfd = fd;
pages_list_put(pglist);
return 0;
}
@ -554,68 +682,45 @@ static int habmem_imp_hyp_map_kva(void *imp_ctx,
uint32_t userflags,
void **pkva)
{
struct page **pages;
struct compressed_pfns *pfn_table =
(struct compressed_pfns *)exp->payload;
struct pages_list *pglist;
struct importer_context *priv = imp_ctx;
unsigned long pfn;
int i, j, k = 0, size;
pgprot_t prot = PAGE_KERNEL;
if (!pfn_table || !priv)
if (!priv)
return -EINVAL;
size = exp->payload_count * sizeof(struct page *);
pages = kmalloc(size, GFP_KERNEL);
if (!pages)
return -ENOMEM;
pglist = kzalloc(sizeof(*pglist), GFP_KERNEL);
if (!pglist) {
kfree(pages);
return -ENOMEM;
}
pfn = pfn_table->first_pfn;
for (i = 0; i < pfn_table->nregions; i++) {
for (j = 0; j < pfn_table->region[i].size; j++) {
pages[k] = pfn_to_page(pfn+j);
k++;
}
pfn += pfn_table->region[i].size + pfn_table->region[i].space;
}
pglist = pages_list_lookup(priv, exp->export_id, exp->pchan);
if (pglist)
goto buffer_ready;
pglist->pages = pages;
pglist->npages = exp->payload_count;
pglist->kernel = 1;
pglist->refcntk = pglist->refcntu = 0;
pglist->userflags = userflags;
pglist->export_id = exp->export_id;
pglist->vcid = exp->vcid_remote;
pglist->pchan = exp->pchan;
pglist = pages_list_create(imp_ctx, exp, userflags);
if (IS_ERR(pglist))
return PTR_ERR(pglist);
pages_list_add(priv, pglist);
buffer_ready:
if (pglist->kva)
goto pro_end;
if (pglist->userflags != userflags) {
pr_info("exp %d: userflags: 0x%x -> 0x%x\n",
exp->export_id, pglist->userflags, userflags);
pglist->userflags = userflags;
}
if (!(userflags & HABMM_IMPORT_FLAGS_CACHED))
prot = pgprot_writecombine(prot);
pglist->kva = vmap(pglist->pages, pglist->npages, VM_MAP, prot);
if (pglist->kva == NULL) {
kfree(pages);
pr_err("%ld pages vmap failed\n", pglist->npages);
kfree(pglist);
return -ENOMEM;
}
pr_debug("%ld pages vmap pass, return %p\n",
pglist->npages, pglist->kva);
pglist->refcntk++;
write_lock(&priv->implist_lock);
list_add_tail(&pglist->list, &priv->imp_list);
priv->cnt++;
write_unlock(&priv->implist_lock);
pro_end:
*pkva = pglist->kva;
pages_list_put(pglist);
return 0;
}
@ -624,52 +729,31 @@ static int habmem_imp_hyp_map_uva(void *imp_ctx,
uint32_t userflags,
uint64_t *index)
{
struct page **pages;
struct compressed_pfns *pfn_table =
(struct compressed_pfns *)exp->payload;
struct pages_list *pglist;
struct importer_context *priv = imp_ctx;
unsigned long pfn;
int i, j, k = 0, size;
if (!pfn_table || !priv)
if (!priv)
return -EINVAL;
size = exp->payload_count * sizeof(struct page *);
pages = kmalloc(size, GFP_KERNEL);
if (!pages)
return -ENOMEM;
pglist = kzalloc(sizeof(*pglist), GFP_KERNEL);
if (!pglist) {
kfree(pages);
return -ENOMEM;
}
pglist = pages_list_lookup(priv, exp->export_id, exp->pchan);
if (pglist)
goto buffer_ready;
pfn = pfn_table->first_pfn;
for (i = 0; i < pfn_table->nregions; i++) {
for (j = 0; j < pfn_table->region[i].size; j++) {
pages[k] = pfn_to_page(pfn+j);
k++;
}
pfn += pfn_table->region[i].size + pfn_table->region[i].space;
}
pglist = pages_list_create(imp_ctx, exp, userflags);
if (IS_ERR(pglist))
return PTR_ERR(pglist);
pglist->pages = pages;
pglist->npages = exp->payload_count;
pglist->index = page_to_phys(pages[0]) >> PAGE_SHIFT;
pglist->refcntk = pglist->refcntu = 0;
pglist->userflags = userflags;
pglist->export_id = exp->export_id;
pglist->vcid = exp->vcid_remote;
pglist->pchan = exp->pchan;
pages_list_add(priv, pglist);
write_lock(&priv->implist_lock);
list_add_tail(&pglist->list, &priv->imp_list);
priv->cnt++;
write_unlock(&priv->implist_lock);
buffer_ready:
if (pglist->index)
goto proc_end;
pglist->index = page_to_phys(pglist->pages[0]) >> PAGE_SHIFT;
proc_end:
*index = pglist->index << PAGE_SHIFT;
pages_list_put(pglist);
return 0;
}
@ -697,37 +781,17 @@ int habmem_imp_hyp_map(void *imp_ctx, struct hab_import *param,
int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp, int kernel)
{
struct importer_context *priv = imp_ctx;
struct pages_list *pglist, *tmp;
int found = 0;
struct pages_list *pglist;
write_lock(&priv->implist_lock);
list_for_each_entry_safe(pglist, tmp, &priv->imp_list, list) {
if (pglist->export_id == exp->export_id &&
pglist->pchan == exp->pchan) {
found = 1;
list_del(&pglist->list);
priv->cnt--;
break;
}
}
write_unlock(&priv->implist_lock);
if (!found) {
pglist = pages_list_lookup(priv, exp->export_id, exp->pchan);
if (!pglist) {
pr_err("failed to find export id %u\n", exp->export_id);
return -EINVAL;
}
pr_debug("detach pglist %p, kernel %d, list cnt %d\n",
pglist, pglist->kernel, priv->cnt);
pages_list_remove(priv, pglist);
if (pglist->kva)
vunmap(pglist->kva);
if (pglist->dmabuf)
dma_buf_put(pglist->dmabuf);
kfree(pglist->pages);
kfree(pglist);
pages_list_put(pglist);
return 0;
}
@ -739,12 +803,14 @@ int habmem_imp_hyp_mmap(struct file *filp, struct vm_area_struct *vma)
long length = vma->vm_end - vma->vm_start;
struct pages_list *pglist;
int bfound = 0;
int ret = 0;
read_lock(&imp_ctx->implist_lock);
list_for_each_entry(pglist, &imp_ctx->imp_list, list) {
if ((pglist->index == vma->vm_pgoff) &&
((length <= pglist->npages * PAGE_SIZE))) {
bfound = 1;
pages_list_get(pglist);
break;
}
}
@ -758,7 +824,8 @@ int habmem_imp_hyp_mmap(struct file *filp, struct vm_area_struct *vma)
if (length > pglist->npages * PAGE_SIZE) {
pr_err("Error vma length %ld not matching page list %ld\n",
length, pglist->npages * PAGE_SIZE);
return -EINVAL;
ret = -EINVAL;
goto proc_end;
}
vma->vm_ops = &habmem_vm_ops;
@ -769,6 +836,10 @@ int habmem_imp_hyp_mmap(struct file *filp, struct vm_area_struct *vma)
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
return 0;
proc_end:
pages_list_put(pglist);
return ret;
}
int habmm_imp_hyp_map_check(void *imp_ctx, struct export_desc *exp)
@ -777,15 +848,11 @@ int habmm_imp_hyp_map_check(void *imp_ctx, struct export_desc *exp)
struct pages_list *pglist;
int found = 0;
read_lock(&priv->implist_lock);
list_for_each_entry(pglist, &priv->imp_list, list) {
if (pglist->export_id == exp->export_id &&
pglist->pchan == exp->pchan) {
found = 1;
break;
}
pglist = pages_list_lookup(priv, exp->export_id, exp->pchan);
if (pglist) {
found = 1;
pages_list_put(pglist);
}
read_unlock(&priv->implist_lock);
return found;
}