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:
committed by
Treehugger Robot
parent
0174bfe7fd
commit
b7e97995a6
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user