ANDROID: gunyah: Add CMA bindings support
Allow VMMs to add a binding backed by CMA. Extend the generic Gunyah mem APIs to handle bindings backed by CMA. Bug: 399219478 Change-Id: I1f71641420c8c30f4dfad021d92f70ef6ffdfb29 Signed-off-by: Prakruthi Deepak Heragu <quic_pheragu@quicinc.com>
This commit is contained in:
committed by
Todd Kjos
parent
1aec1ec259
commit
8e3a62bf00
@@ -898,6 +898,19 @@ static long gunyah_vm_ioctl(struct file *filp, unsigned int cmd,
|
||||
gunyah_gpa_to_gfn(range.size) - 1);
|
||||
break;
|
||||
}
|
||||
case GH_VM_ANDROID_MAP_CMA_MEM: {
|
||||
struct gunyah_map_cma_mem_args cma_mem;
|
||||
|
||||
/* only allow owner task to add memory */
|
||||
if (ghvm->mm_s != current->mm)
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&cma_mem, argp, sizeof(cma_mem)))
|
||||
return -EFAULT;
|
||||
|
||||
r = gunyah_vm_binding_cma_alloc(ghvm, &cma_mem);
|
||||
break;
|
||||
}
|
||||
case GUNYAH_VM_SET_BOOT_CONTEXT: {
|
||||
struct gunyah_vm_boot_context boot_ctx;
|
||||
|
||||
|
||||
@@ -27,8 +27,15 @@ struct gunyah_vm_parcel {
|
||||
};
|
||||
|
||||
struct gunyah_vm_binding {
|
||||
enum { VM_MEM_USER } mem_type;
|
||||
u64 userspace_addr;
|
||||
enum { VM_MEM_USER, VM_MEM_CMA } mem_type;
|
||||
union {
|
||||
u64 userspace_addr;
|
||||
struct {
|
||||
struct file *file;
|
||||
u32 fd;
|
||||
u64 offset;
|
||||
} cma;
|
||||
};
|
||||
struct gunyah_vm_parcel *vm_parcel;
|
||||
enum { VM_MEM_SHARE, VM_MEM_LEND } share_type;
|
||||
u64 guest_phys_addr;
|
||||
@@ -233,6 +240,15 @@ int gunyah_setup_demand_paging(struct gunyah_vm *ghvm, u64 start_gfn,
|
||||
#ifdef CONFIG_DMA_CMA
|
||||
int gunyah_cma_mem_init(void);
|
||||
void gunyah_cma_mem_exit(void);
|
||||
int gunyah_vm_binding_cma_alloc(struct gunyah_vm *ghvm,
|
||||
struct gunyah_map_cma_mem_args *cma_map);
|
||||
int gunyah_cma_share_parcel(struct gunyah_vm *ghvm,
|
||||
struct gunyah_vm_parcel *parcel,
|
||||
struct gunyah_vm_binding *b,
|
||||
u64 *gfn, u64 *nr);
|
||||
int gunyah_cma_reclaim_parcel(struct gunyah_vm *ghvm,
|
||||
struct gunyah_vm_parcel *parcel,
|
||||
struct gunyah_vm_binding *b);
|
||||
#else
|
||||
static inline int gunyah_cma_mem_init(void)
|
||||
{
|
||||
@@ -241,6 +257,24 @@ static inline int gunyah_cma_mem_init(void)
|
||||
static inline void gunyah_cma_mem_exit(void)
|
||||
{
|
||||
}
|
||||
static inline int gunyah_vm_binding_cma_alloc(struct gunyah_vm *ghvm,
|
||||
struct gunyah_map_cma_mem_args *cma_map)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
static inline int gunyah_cma_share_parcel(struct gunyah_vm *ghvm,
|
||||
struct gunyah_vm_parcel *parcel,
|
||||
struct gunyah_vm_binding *b,
|
||||
u64 *gfn, u64 *nr)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int gunyah_cma_reclaim_parcel(struct gunyah_vm *ghvm,
|
||||
struct gunyah_vm_parcel *parcel,
|
||||
struct gunyah_vm_binding *b)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -112,6 +112,144 @@ static const struct file_operations gunyah_cma_fops = {
|
||||
.release = gunyah_cma_release,
|
||||
};
|
||||
|
||||
int gunyah_cma_reclaim_parcel(struct gunyah_vm *ghvm, struct gunyah_vm_parcel *vm_parcel,
|
||||
struct gunyah_vm_binding *b)
|
||||
{
|
||||
struct gunyah_rm_mem_parcel *parcel = &vm_parcel->parcel;
|
||||
int ret;
|
||||
|
||||
if (parcel->mem_handle == GUNYAH_MEM_HANDLE_INVAL)
|
||||
return 0;
|
||||
|
||||
ret = gunyah_rm_mem_reclaim(ghvm->rm, parcel);
|
||||
if (ret) {
|
||||
dev_err(ghvm->parent, "Failed to reclaim parcel: %d\n",
|
||||
ret);
|
||||
/* We can't reclaim the pages -- hold onto the pages
|
||||
* forever because we don't know what state the memory
|
||||
* is in
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
parcel->mem_handle = GUNYAH_MEM_HANDLE_INVAL;
|
||||
kfree(parcel->mem_entries);
|
||||
kfree(parcel->acl_entries);
|
||||
vm_parcel->start = 0;
|
||||
vm_parcel->pages = 0;
|
||||
b->vm_parcel = NULL;
|
||||
fput(b->cma.file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gunyah_cma_share_parcel(struct gunyah_vm *ghvm, struct gunyah_vm_parcel *vm_parcel,
|
||||
struct gunyah_vm_binding *b, u64 *gfn, u64 *nr)
|
||||
{
|
||||
struct gunyah_rm_mem_parcel *parcel = &vm_parcel->parcel;
|
||||
unsigned long offset;
|
||||
struct gunyah_cma *cma;
|
||||
struct file *file;
|
||||
int ret;
|
||||
|
||||
if ((*nr << PAGE_SHIFT) > b->size)
|
||||
return -EINVAL;
|
||||
|
||||
file = fget(b->cma.fd);
|
||||
if (!file)
|
||||
return -EINVAL;
|
||||
|
||||
if (file->f_op != &gunyah_cma_fops) {
|
||||
fput(file);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cma = file->private_data;
|
||||
b->cma.file = file;
|
||||
|
||||
parcel->n_mem_entries = 1;
|
||||
parcel->mem_entries = kcalloc(parcel->n_mem_entries, sizeof(parcel->mem_entries[0]),
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
if (!parcel->mem_entries) {
|
||||
fput(file);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
offset = gunyah_gfn_to_gpa(*gfn) - b->guest_phys_addr;
|
||||
parcel->mem_entries[0].size = cpu_to_le64(*nr << PAGE_SHIFT);
|
||||
parcel->mem_entries[0].phys_addr =
|
||||
cpu_to_le64(page_to_phys(cma->page + b->cma.offset + offset));
|
||||
|
||||
ret = gunyah_rm_mem_share(ghvm->rm, parcel);
|
||||
if (ret)
|
||||
goto free_mem_entries;
|
||||
|
||||
vm_parcel->start = *gfn;
|
||||
vm_parcel->pages = *nr;
|
||||
b->vm_parcel = vm_parcel;
|
||||
return ret;
|
||||
|
||||
free_mem_entries:
|
||||
kfree(parcel->mem_entries);
|
||||
parcel->mem_entries = NULL;
|
||||
parcel->n_mem_entries = 0;
|
||||
fput(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gunyah_vm_binding_cma_alloc(struct gunyah_vm *ghvm,
|
||||
struct gunyah_map_cma_mem_args *cma_map)
|
||||
{
|
||||
struct gunyah_vm_binding *binding;
|
||||
struct file *file;
|
||||
loff_t max_size;
|
||||
int ret = 0;
|
||||
|
||||
if (!cma_map->size || !PAGE_ALIGNED(cma_map->size) ||
|
||||
!PAGE_ALIGNED(cma_map->guest_addr))
|
||||
return -EINVAL;
|
||||
|
||||
if (overflows_type(cma_map->guest_addr + cma_map->size, u64))
|
||||
return -EOVERFLOW;
|
||||
|
||||
file = fget(cma_map->guest_mem_fd);
|
||||
max_size = i_size_read(file_inode(file));
|
||||
if (cma_map->offset + cma_map->size > max_size) {
|
||||
fput(file);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
fput(file);
|
||||
|
||||
binding = kzalloc(sizeof(*binding), GFP_KERNEL_ACCOUNT);
|
||||
if (!binding)
|
||||
return -ENOMEM;
|
||||
|
||||
binding->mem_type = VM_MEM_CMA;
|
||||
binding->cma.fd = cma_map->guest_mem_fd;
|
||||
binding->cma.offset = cma_map->offset;
|
||||
binding->guest_phys_addr = cma_map->guest_addr;
|
||||
binding->label = cma_map->label;
|
||||
binding->size = cma_map->size;
|
||||
binding->flags = cma_map->flags;
|
||||
binding->vm_parcel = NULL;
|
||||
|
||||
if (binding->flags & GUNYAH_MEM_FORCE_LEND)
|
||||
binding->share_type = VM_MEM_LEND;
|
||||
else
|
||||
binding->share_type = VM_MEM_SHARE;
|
||||
|
||||
down_write(&ghvm->bindings_lock);
|
||||
ret = mtree_insert_range(&ghvm->bindings,
|
||||
gunyah_gpa_to_gfn(binding->guest_phys_addr),
|
||||
gunyah_gpa_to_gfn(binding->guest_phys_addr + cma_map->size - 1),
|
||||
binding, GFP_KERNEL);
|
||||
|
||||
if (ret != 0)
|
||||
kfree(binding);
|
||||
|
||||
up_write(&ghvm->bindings_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long gunyah_cma_create_mem_fd(struct gunyah_cma *cma)
|
||||
{
|
||||
unsigned long flags = 0;
|
||||
|
||||
@@ -496,7 +496,13 @@ int gunyah_demand_page(struct gunyah_vm *ghvm, u64 gpa, bool write)
|
||||
ret = -ENOENT;
|
||||
goto unlock;
|
||||
}
|
||||
ret = gunyah_gup_demand_page(ghvm, b, gpa, write);
|
||||
|
||||
if (b->mem_type == VM_MEM_CMA) {
|
||||
dev_warn(ghvm->parent, "Demand paging of CMA mem not supported\n");
|
||||
ret = -EOPNOTSUPP;
|
||||
} else {
|
||||
ret = gunyah_gup_demand_page(ghvm, b, gpa, write);
|
||||
}
|
||||
|
||||
unlock:
|
||||
up_read(&ghvm->bindings_lock);
|
||||
@@ -651,10 +657,18 @@ int gunyah_share_parcel(struct gunyah_vm *ghvm, struct gunyah_vm_parcel *vm_parc
|
||||
parcel->acl_entries[1].perms = GUNYAH_RM_ACL_R | GUNYAH_RM_ACL_W | GUNYAH_RM_ACL_X;
|
||||
}
|
||||
|
||||
ret = gunyah_gup_share_parcel(ghvm, vm_parcel, b, gfn, nr);
|
||||
if (ret) {
|
||||
dev_warn(ghvm->parent, "Failed to share GUP memory: %d\n", ret);
|
||||
goto free_acl;
|
||||
if (b->mem_type == VM_MEM_CMA) {
|
||||
ret = gunyah_cma_share_parcel(ghvm, vm_parcel, b, gfn, nr);
|
||||
if (ret) {
|
||||
dev_warn(ghvm->parent, "Failed to share CMA memory: %d\n", ret);
|
||||
goto free_acl;
|
||||
}
|
||||
} else {
|
||||
ret = gunyah_gup_share_parcel(ghvm, vm_parcel, b, gfn, nr);
|
||||
if (ret) {
|
||||
dev_warn(ghvm->parent, "Failed to share GUP memory: %d\n", ret);
|
||||
goto free_acl;
|
||||
}
|
||||
}
|
||||
goto unlock;
|
||||
|
||||
@@ -742,7 +756,11 @@ static int gunyah_reclaim_parcel(struct gunyah_vm *ghvm,
|
||||
ret = -ENOENT;
|
||||
goto unlock;
|
||||
}
|
||||
ret = gunyah_gup_reclaim_parcel(ghvm, vm_parcel, b);
|
||||
|
||||
if (b->mem_type == VM_MEM_CMA)
|
||||
ret = gunyah_cma_reclaim_parcel(ghvm, vm_parcel, b);
|
||||
else
|
||||
ret = gunyah_gup_reclaim_parcel(ghvm, vm_parcel, b);
|
||||
|
||||
unlock:
|
||||
up_write(&ghvm->bindings_lock);
|
||||
|
||||
@@ -402,4 +402,22 @@ struct gunyah_address_range {
|
||||
*/
|
||||
#define GH_ANDROID_CREATE_CMA_MEM_FD _IO(GH_ANDROID_IOCTL_TYPE, 0x14)
|
||||
|
||||
/**
|
||||
* struct gunyah_map_cma_mem_args - Description to provide CMA based guest memory into a VM
|
||||
* @guest_addr: Location in guest address space to place the memory
|
||||
* @flags: See &enum gunyah_map_flags.
|
||||
* @guest_mem_fd: File descriptor created by GH_ANDROID_CREATE_CMA_MEM_FD
|
||||
* @offset: Offset into the guest memory file
|
||||
* @size: Size of the region to be mapped.
|
||||
*/
|
||||
struct gunyah_map_cma_mem_args {
|
||||
__u32 label;
|
||||
__u64 guest_addr;
|
||||
__u32 flags;
|
||||
__u32 guest_mem_fd;
|
||||
__u64 offset;
|
||||
__u64 size;
|
||||
};
|
||||
|
||||
#define GH_VM_ANDROID_MAP_CMA_MEM _IOW(GH_ANDROID_IOCTL_TYPE, 0x15, struct gunyah_map_cma_mem_args)
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user