FROMLIST: virt: gunyah: Enable demand paging

Tell resource manager to enable demand paging and wire vCPU faults to
provide the backing folio when a guestmemfd is bound to the faulting
access.

Bug: 338347082
Link: https://lore.kernel.org/all/20240222-gunyah-v17-28-1e9da6763d38@quicinc.com/
Change-Id: I4e6decdc34d8afc90e7c95e91b30526cf59d0914
Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
Signed-off-by: Elliot Berman <elliot.berman@oss.qualcomm.com>
This commit is contained in:
Elliot Berman
2024-02-22 15:16:51 -08:00
committed by Treehugger Robot
parent 0174bfe7fd
commit b7e97995a6
4 changed files with 159 additions and 8 deletions
+94
View File
@@ -891,3 +891,97 @@ int gunyah_gmem_reclaim_parcel(struct gunyah_vm *ghvm,
return 0;
}
int gunyah_gmem_setup_demand_paging(struct gunyah_vm *ghvm)
{
struct gunyah_rm_mem_entry *entries;
struct gunyah_gmem_binding *b;
unsigned long index = 0;
u32 count = 0, i;
int ret = 0;
down_read(&ghvm->bindings_lock);
mt_for_each(&ghvm->bindings, b, index, ULONG_MAX)
if (gunyah_guest_mem_is_lend(ghvm, b->flags))
count++;
if (!count)
goto out;
entries = kcalloc(count, sizeof(*entries), GFP_KERNEL);
if (!entries) {
ret = -ENOMEM;
goto out;
}
index = i = 0;
mt_for_each(&ghvm->bindings, b, index, ULONG_MAX) {
if (!gunyah_guest_mem_is_lend(ghvm, b->flags))
continue;
entries[i].phys_addr = cpu_to_le64(gunyah_gfn_to_gpa(b->gfn));
entries[i].size = cpu_to_le64(b->nr << PAGE_SHIFT);
if (++i == count)
break;
}
ret = gunyah_rm_vm_set_demand_paging(ghvm->rm, ghvm->vmid, i, entries);
kfree(entries);
out:
up_read(&ghvm->bindings_lock);
return ret;
}
int gunyah_gmem_demand_page(struct gunyah_vm *ghvm, u64 gpa, bool write)
{
unsigned long gfn = gunyah_gpa_to_gfn(gpa);
struct gunyah_gmem_binding *b;
struct folio *folio;
int ret;
down_read(&ghvm->bindings_lock);
b = mtree_load(&ghvm->bindings, gfn);
if (!b) {
ret = -ENOENT;
goto unlock;
}
if (write && !(b->flags & GUNYAH_MEM_ALLOW_WRITE)) {
ret = -EPERM;
goto unlock;
}
folio = gunyah_gmem_get_folio(file_inode(b->file),
gunyah_gfn_to_off(b, gfn));
if (!folio) {
ret = -ENOMEM;
pr_err_ratelimited("Failed to obtain memory for guest addr %016llx\n", gpa);
goto unlock;
}
/**
* the folio covers the requested guest address, but the folio may not
* start at the requested guest address. recompute the gfn based on the
* folio itself.
*/
gfn = gunyah_off_to_gfn(b, folio_index(folio));
filemap_invalidate_lock_shared(b->file->f_mapping);
ret = gunyah_vm_provide_folio(ghvm, folio, gfn,
!gunyah_guest_mem_is_lend(ghvm, b->flags),
!!(b->flags & GUNYAH_MEM_ALLOW_WRITE));
filemap_invalidate_unlock_shared(b->file->f_mapping);
if (ret) {
if (ret != -EAGAIN)
pr_err_ratelimited(
"Failed to provide folio for guest addr: %016llx: %d\n",
gpa, ret);
goto out;
}
out:
folio_unlock(folio);
folio_put(folio);
unlock:
up_read(&ghvm->bindings_lock);
return ret;
}
EXPORT_SYMBOL_GPL(gunyah_gmem_demand_page);
+33 -8
View File
@@ -89,29 +89,44 @@ static irqreturn_t gunyah_vcpu_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
static void gunyah_handle_page_fault(
static bool gunyah_handle_page_fault(
struct gunyah_vcpu *vcpu,
const struct gunyah_hypercall_vcpu_run_resp *vcpu_run_resp)
{
u64 addr = vcpu_run_resp->state_data[0];
bool write = !!vcpu_run_resp->state_data[1];
int ret = 0;
ret = gunyah_gmem_demand_page(vcpu->ghvm, addr, write);
if (!ret || ret == -EAGAIN)
return true;
vcpu->vcpu_run->page_fault.resume_action = GUNYAH_VCPU_RESUME_FAULT;
vcpu->vcpu_run->page_fault.attempt = 0;
vcpu->vcpu_run->page_fault.attempt = ret;
vcpu->vcpu_run->page_fault.phys_addr = addr;
vcpu->vcpu_run->exit_reason = GUNYAH_VCPU_EXIT_PAGE_FAULT;
return false;
}
static void
gunyah_handle_mmio(struct gunyah_vcpu *vcpu,
static bool
gunyah_handle_mmio(struct gunyah_vcpu *vcpu, unsigned long resume_data[3],
const struct gunyah_hypercall_vcpu_run_resp *vcpu_run_resp)
{
u64 addr = vcpu_run_resp->state_data[0],
len = vcpu_run_resp->state_data[1],
data = vcpu_run_resp->state_data[2];
int ret;
if (WARN_ON(len > sizeof(u64)))
len = sizeof(u64);
ret = gunyah_gmem_demand_page(vcpu->ghvm, addr,
vcpu->vcpu_run->mmio.is_write);
if (!ret || ret == -EAGAIN) {
resume_data[1] = GUNYAH_ADDRSPACE_VMMIO_ACTION_RETRY;
return true;
}
if (vcpu_run_resp->state == GUNYAH_VCPU_ADDRSPACE_VMMIO_READ) {
vcpu->vcpu_run->mmio.is_write = 0;
/* Record that we need to give vCPU user's supplied value next gunyah_vcpu_run() */
@@ -128,11 +143,15 @@ gunyah_handle_mmio(struct gunyah_vcpu *vcpu,
vcpu->mmio_addr = vcpu->vcpu_run->mmio.phys_addr = addr;
vcpu->vcpu_run->mmio.len = len;
vcpu->vcpu_run->exit_reason = GUNYAH_VCPU_EXIT_MMIO;
return false;
}
static int gunyah_handle_mmio_resume(struct gunyah_vcpu *vcpu,
unsigned long resume_data[3])
{
bool write = vcpu->state == GUNYAH_VCPU_RUN_STATE_MMIO_WRITE;
switch (vcpu->vcpu_run->mmio.resume_action) {
case GUNYAH_VCPU_RESUME_HANDLED:
if (vcpu->state == GUNYAH_VCPU_RUN_STATE_MMIO_READ) {
@@ -148,6 +167,8 @@ static int gunyah_handle_mmio_resume(struct gunyah_vcpu *vcpu,
resume_data[1] = GUNYAH_ADDRSPACE_VMMIO_ACTION_FAULT;
break;
case GUNYAH_VCPU_RESUME_RETRY:
/* userspace probably added a memory binding */
gunyah_gmem_demand_page(vcpu->ghvm, vcpu->mmio_addr, write);
resume_data[1] = GUNYAH_ADDRSPACE_VMMIO_ACTION_RETRY;
break;
default:
@@ -310,11 +331,15 @@ static int gunyah_vcpu_run(struct gunyah_vcpu *vcpu)
break;
case GUNYAH_VCPU_ADDRSPACE_VMMIO_READ:
case GUNYAH_VCPU_ADDRSPACE_VMMIO_WRITE:
gunyah_handle_mmio(vcpu, &vcpu_run_resp);
goto out;
if (!gunyah_handle_mmio(vcpu, resume_data,
&vcpu_run_resp))
goto out;
break;
case GUNYAH_VCPU_ADDRSPACE_PAGE_FAULT:
gunyah_handle_page_fault(vcpu, &vcpu_run_resp);
goto out;
if (!gunyah_handle_page_fault(vcpu,
&vcpu_run_resp))
goto out;
break;
default:
pr_warn_ratelimited(
"Unknown vCPU state: %llx\n",
+29
View File
@@ -474,6 +474,23 @@ static int gunyah_vm_start(struct gunyah_vm *ghvm)
goto err;
}
ret = gunyah_gmem_setup_demand_paging(ghvm);
if (ret) {
dev_warn(ghvm->parent,
"Failed to set up gmem demand paging: %d\n", ret);
goto err;
}
ret = gunyah_rm_vm_set_address_layout(
ghvm->rm, ghvm->vmid, GUNYAH_RM_RANGE_ID_IMAGE,
ghvm->dtb.parcel_start << PAGE_SHIFT,
ghvm->dtb.parcel_pages << PAGE_SHIFT);
if (ret) {
dev_warn(ghvm->parent,
"Failed to set location of DTB mem parcel: %d\n", ret);
goto err;
}
ret = gunyah_rm_vm_init(ghvm->rm, ghvm->vmid);
if (ret) {
ghvm->vm_status = GUNYAH_RM_VM_STATUS_INIT_FAILED;
@@ -501,8 +518,20 @@ static int gunyah_vm_start(struct gunyah_vm *ghvm)
gunyah_vm_add_resource(ghvm, ghrsc);
}
ret = gunyah_vm_parcel_to_paged(ghvm, &ghvm->dtb.parcel,
ghvm->dtb.parcel_start,
ghvm->dtb.parcel_pages);
if (ret)
goto err;
ret = gunyah_rm_vm_start(ghvm->rm, ghvm->vmid);
if (ret) {
/**
* need to rollback parcel_to_paged because RM is still
* tracking the parcel
*/
gunyah_vm_mm_erase_range(ghvm, ghvm->dtb.parcel_start,
ghvm->dtb.parcel_pages);
dev_warn(ghvm->parent, "Failed to start VM: %d\n", ret);
goto err;
}
+3
View File
@@ -199,4 +199,7 @@ int gunyah_gmem_reclaim_parcel(struct gunyah_vm *ghvm,
struct gunyah_rm_mem_parcel *parcel, u64 gfn,
u64 nr);
int gunyah_gmem_setup_demand_paging(struct gunyah_vm *ghvm);
int gunyah_gmem_demand_page(struct gunyah_vm *ghvm, u64 gpa, bool write);
#endif