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:
Prakruthi Deepak Heragu
2025-02-24 11:49:10 -08:00
committed by Todd Kjos
parent 1aec1ec259
commit 8e3a62bf00
5 changed files with 229 additions and 8 deletions
+13
View File
@@ -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;
+36 -2
View File
@@ -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
+138
View File
@@ -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;
+24 -6
View File
@@ -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);
+18
View File
@@ -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