arch/s390/kvm/dat.c

Source file repositories/reference/linux-study-clean/arch/s390/kvm/dat.c

File Facts

System
Linux kernel
Corpus path
arch/s390/kvm/dat.c
Extension
.c
Size
35555 bytes
Lines
1323
Domain
Architecture Layer
Bucket
arch/s390
Inferred role
Architecture Layer: implementation source
Status
source implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

struct slot_priv {
	unsigned long token;
	struct kvm_s390_mmu_cache *mc;
};

static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
{
	struct slot_priv *p = walk->priv;
	union crste dummy = { .val = p->token };
	union pte new_pte, pte = READ_ONCE(*ptep);

	new_pte = _PTE_TOK(dummy.tok.type, dummy.tok.par);

	/* Table entry already in the desired state. */
	if (pte.val == new_pte.val)
		return 0;

	dat_ptep_xchg(ptep, new_pte, gfn, walk->asce, false);
	return 0;
}

static long _dat_slot_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
{
	union crste new_crste, crste = READ_ONCE(*crstep);
	struct slot_priv *p = walk->priv;

	new_crste.val = p->token;
	new_crste.h.tt = crste.h.tt;

	/* Table entry already in the desired state. */
	if (crste.val == new_crste.val)
		return 0;

	/* This table entry needs to be updated. */
	if (walk->start <= gfn && walk->end >= next) {
		if (!dat_crstep_xchg_atomic(crstep, crste, new_crste, gfn, walk->asce))
			return -EINVAL;
		/* A lower level table was present, needs to be freed. */
		if (!crste.h.fc && !crste.h.i) {
			if (is_pmd(crste))
				dat_free_pt(dereference_pmd(crste.pmd));
			else
				dat_free_level(dereference_crste(crste), true);
		}
		return 0;
	}

	/* A lower level table is present, things will handled there. */
	if (!crste.h.fc && !crste.h.i)
		return 0;
	/* Split (install a lower level table), and handle things there. */
	return dat_split_crste(p->mc, crstep, gfn, walk->asce, false);
}

static const struct dat_walk_ops dat_slot_ops = {
	.pte_entry = _dat_slot_pte,
	.crste_ops = { _dat_slot_crste, _dat_slot_crste, _dat_slot_crste, _dat_slot_crste, },
};

int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end,
		 u16 type, u16 param)
{
	struct slot_priv priv = {
		.token = _CRSTE_TOK(0, type, param).val,
		.mc = mc,
	};

	return _dat_walk_gfn_range(start, end, asce, &dat_slot_ops,
				   DAT_WALK_IGN_HOLES | DAT_WALK_ANY, &priv);
}

static void pgste_set_unlock_multiple(union pte *first, int n, union pgste *pgstes)
{
	int i;

	for (i = 0; i < n; i++) {
		if (!pgstes[i].pcl)
			break;
		pgste_set_unlock(first + i, pgstes[i]);
	}
}

static bool pgste_get_trylock_multiple(union pte *first, int n, union pgste *pgstes)
{
	int i;

	for (i = 0; i < n; i++) {
		if (!pgste_get_trylock(first + i, pgstes + i))
			break;
	}

Annotation

Implementation Notes