virt/kvm/pfncache.c

Source file repositories/reference/linux-study-clean/virt/kvm/pfncache.c

File Facts

System
Linux kernel
Corpus path
virt/kvm/pfncache.c
Extension
.c
Size
12125 bytes
Lines
485
Domain
Kernel Services
Bucket
virt
Inferred role
Kernel Services: implementation source
Status
source implementation candidate

Why This File Exists

Shared kernel service surface used by multiple subsystems, including helpers, cryptography, virtualization support, and async I/O infrastructure.

Dependency Surface

Detected Declarations

Annotated Snippet

if (new_pfn != KVM_PFN_ERR_FAULT) {
			/*
			 * Keep the mapping if the previous iteration reused
			 * the existing mapping and didn't create a new one.
			 */
			if (new_khva != old_khva)
				gpc_unmap(new_pfn, new_khva);

			kvm_release_page_unused(page);

			cond_resched();
		}

		new_pfn = hva_to_pfn(&kfp);
		if (is_error_noslot_pfn(new_pfn))
			goto out_error;

		/*
		 * Obtain a new kernel mapping if KVM itself will access the
		 * pfn.  Note, kmap() and memremap() can both sleep, so this
		 * too must be done outside of gpc->lock!
		 */
		if (new_pfn == gpc->pfn)
			new_khva = old_khva;
		else
			new_khva = gpc_map(new_pfn);

		if (!new_khva) {
			kvm_release_page_unused(page);
			goto out_error;
		}

		write_lock_irq(&gpc->lock);

		/*
		 * Other tasks must wait for _this_ refresh to complete before
		 * attempting to refresh.
		 */
		WARN_ON_ONCE(gpc->valid);
	} while (mmu_notifier_retry_cache(gpc->kvm, mmu_seq));

	gpc->valid = true;
	gpc->pfn = new_pfn;
	gpc->khva = new_khva + offset_in_page(gpc->uhva);

	/*
	 * Put the reference to the _new_ page.  The page is now tracked by the
	 * cache and can be safely migrated, swapped, etc... as the cache will
	 * invalidate any mappings in response to relevant mmu_notifier events.
	 */
	kvm_release_page_clean(page);

	return 0;

out_error:
	write_lock_irq(&gpc->lock);

	return -EFAULT;
}

static int __kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long uhva)
{
	unsigned long page_offset;
	bool unmap_old = false;
	unsigned long old_uhva;
	kvm_pfn_t old_pfn;
	bool hva_change = false;
	void *old_khva;
	int ret;

	/* Either gpa or uhva must be valid, but not both */
	if (WARN_ON_ONCE(kvm_is_error_gpa(gpa) == kvm_is_error_hva(uhva)))
		return -EINVAL;

	lockdep_assert_held(&gpc->refresh_lock);

	write_lock_irq(&gpc->lock);

	if (!gpc->active) {
		ret = -EINVAL;
		goto out_unlock;
	}

	old_pfn = gpc->pfn;
	old_khva = (void *)PAGE_ALIGN_DOWN((uintptr_t)gpc->khva);
	old_uhva = PAGE_ALIGN_DOWN(gpc->uhva);

	if (kvm_is_error_gpa(gpa)) {
		page_offset = offset_in_page(uhva);

Annotation

Implementation Notes