162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2014 IBM Corp.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/bitmap.h>
962306a36Sopenharmony_ci#include <linux/sched.h>
1062306a36Sopenharmony_ci#include <linux/pid.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/mm.h>
1362306a36Sopenharmony_ci#include <linux/debugfs.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/idr.h>
1662306a36Sopenharmony_ci#include <linux/sched/mm.h>
1762306a36Sopenharmony_ci#include <linux/mmu_context.h>
1862306a36Sopenharmony_ci#include <asm/cputable.h>
1962306a36Sopenharmony_ci#include <asm/current.h>
2062306a36Sopenharmony_ci#include <asm/copro.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "cxl.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci * Allocates space for a CXL context.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_cistruct cxl_context *cxl_context_alloc(void)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	return kzalloc(sizeof(struct cxl_context), GFP_KERNEL);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Initialises a CXL context.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_ciint cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	int i;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	ctx->afu = afu;
4062306a36Sopenharmony_ci	ctx->master = master;
4162306a36Sopenharmony_ci	ctx->pid = NULL; /* Set in start work ioctl */
4262306a36Sopenharmony_ci	mutex_init(&ctx->mapping_lock);
4362306a36Sopenharmony_ci	ctx->mapping = NULL;
4462306a36Sopenharmony_ci	ctx->tidr = 0;
4562306a36Sopenharmony_ci	ctx->assign_tidr = false;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (cxl_is_power8()) {
4862306a36Sopenharmony_ci		spin_lock_init(&ctx->sste_lock);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci		/*
5162306a36Sopenharmony_ci		 * Allocate the segment table before we put it in the IDR so that we
5262306a36Sopenharmony_ci		 * can always access it when dereferenced from IDR. For the same
5362306a36Sopenharmony_ci		 * reason, the segment table is only destroyed after the context is
5462306a36Sopenharmony_ci		 * removed from the IDR.  Access to this in the IOCTL is protected by
5562306a36Sopenharmony_ci		 * Linux filesystem semantics (can't IOCTL until open is complete).
5662306a36Sopenharmony_ci		 */
5762306a36Sopenharmony_ci		i = cxl_alloc_sst(ctx);
5862306a36Sopenharmony_ci		if (i)
5962306a36Sopenharmony_ci			return i;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	INIT_WORK(&ctx->fault_work, cxl_handle_fault);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	init_waitqueue_head(&ctx->wq);
6562306a36Sopenharmony_ci	spin_lock_init(&ctx->lock);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	ctx->irq_bitmap = NULL;
6862306a36Sopenharmony_ci	ctx->pending_irq = false;
6962306a36Sopenharmony_ci	ctx->pending_fault = false;
7062306a36Sopenharmony_ci	ctx->pending_afu_err = false;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->irq_names);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/*
7562306a36Sopenharmony_ci	 * When we have to destroy all contexts in cxl_context_detach_all() we
7662306a36Sopenharmony_ci	 * end up with afu_release_irqs() called from inside a
7762306a36Sopenharmony_ci	 * idr_for_each_entry(). Hence we need to make sure that anything
7862306a36Sopenharmony_ci	 * dereferenced from this IDR is ok before we allocate the IDR here.
7962306a36Sopenharmony_ci	 * This clears out the IRQ ranges to ensure this.
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	for (i = 0; i < CXL_IRQ_RANGES; i++)
8262306a36Sopenharmony_ci		ctx->irqs.range[i] = 0;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	mutex_init(&ctx->status_mutex);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	ctx->status = OPENED;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/*
8962306a36Sopenharmony_ci	 * Allocating IDR! We better make sure everything's setup that
9062306a36Sopenharmony_ci	 * dereferences from it.
9162306a36Sopenharmony_ci	 */
9262306a36Sopenharmony_ci	mutex_lock(&afu->contexts_lock);
9362306a36Sopenharmony_ci	idr_preload(GFP_KERNEL);
9462306a36Sopenharmony_ci	i = idr_alloc(&ctx->afu->contexts_idr, ctx, 0,
9562306a36Sopenharmony_ci		      ctx->afu->num_procs, GFP_NOWAIT);
9662306a36Sopenharmony_ci	idr_preload_end();
9762306a36Sopenharmony_ci	mutex_unlock(&afu->contexts_lock);
9862306a36Sopenharmony_ci	if (i < 0)
9962306a36Sopenharmony_ci		return i;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	ctx->pe = i;
10262306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_HVMODE)) {
10362306a36Sopenharmony_ci		ctx->elem = &ctx->afu->native->spa[i];
10462306a36Sopenharmony_ci		ctx->external_pe = ctx->pe;
10562306a36Sopenharmony_ci	} else {
10662306a36Sopenharmony_ci		ctx->external_pe = -1; /* assigned when attaching */
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci	ctx->pe_inserted = false;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/*
11162306a36Sopenharmony_ci	 * take a ref on the afu so that it stays alive at-least till
11262306a36Sopenharmony_ci	 * this context is reclaimed inside reclaim_ctx.
11362306a36Sopenharmony_ci	 */
11462306a36Sopenharmony_ci	cxl_afu_get(afu);
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_civoid cxl_context_set_mapping(struct cxl_context *ctx,
11962306a36Sopenharmony_ci			struct address_space *mapping)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
12262306a36Sopenharmony_ci	ctx->mapping = mapping;
12362306a36Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic vm_fault_t cxl_mmap_fault(struct vm_fault *vmf)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct vm_area_struct *vma = vmf->vma;
12962306a36Sopenharmony_ci	struct cxl_context *ctx = vma->vm_file->private_data;
13062306a36Sopenharmony_ci	u64 area, offset;
13162306a36Sopenharmony_ci	vm_fault_t ret;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	offset = vmf->pgoff << PAGE_SHIFT;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	pr_devel("%s: pe: %i address: 0x%lx offset: 0x%llx\n",
13662306a36Sopenharmony_ci			__func__, ctx->pe, vmf->address, offset);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (ctx->afu->current_mode == CXL_MODE_DEDICATED) {
13962306a36Sopenharmony_ci		area = ctx->afu->psn_phys;
14062306a36Sopenharmony_ci		if (offset >= ctx->afu->adapter->ps_size)
14162306a36Sopenharmony_ci			return VM_FAULT_SIGBUS;
14262306a36Sopenharmony_ci	} else {
14362306a36Sopenharmony_ci		area = ctx->psn_phys;
14462306a36Sopenharmony_ci		if (offset >= ctx->psn_size)
14562306a36Sopenharmony_ci			return VM_FAULT_SIGBUS;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	mutex_lock(&ctx->status_mutex);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (ctx->status != STARTED) {
15162306a36Sopenharmony_ci		mutex_unlock(&ctx->status_mutex);
15262306a36Sopenharmony_ci		pr_devel("%s: Context not started, failing problem state access\n", __func__);
15362306a36Sopenharmony_ci		if (ctx->mmio_err_ff) {
15462306a36Sopenharmony_ci			if (!ctx->ff_page) {
15562306a36Sopenharmony_ci				ctx->ff_page = alloc_page(GFP_USER);
15662306a36Sopenharmony_ci				if (!ctx->ff_page)
15762306a36Sopenharmony_ci					return VM_FAULT_OOM;
15862306a36Sopenharmony_ci				memset(page_address(ctx->ff_page), 0xff, PAGE_SIZE);
15962306a36Sopenharmony_ci			}
16062306a36Sopenharmony_ci			get_page(ctx->ff_page);
16162306a36Sopenharmony_ci			vmf->page = ctx->ff_page;
16262306a36Sopenharmony_ci			vma->vm_page_prot = pgprot_cached(vma->vm_page_prot);
16362306a36Sopenharmony_ci			return 0;
16462306a36Sopenharmony_ci		}
16562306a36Sopenharmony_ci		return VM_FAULT_SIGBUS;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	ret = vmf_insert_pfn(vma, vmf->address, (area + offset) >> PAGE_SHIFT);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	mutex_unlock(&ctx->status_mutex);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return ret;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic const struct vm_operations_struct cxl_mmap_vmops = {
17662306a36Sopenharmony_ci	.fault = cxl_mmap_fault,
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/*
18062306a36Sopenharmony_ci * Map a per-context mmio space into the given vma.
18162306a36Sopenharmony_ci */
18262306a36Sopenharmony_ciint cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	u64 start = vma->vm_pgoff << PAGE_SHIFT;
18562306a36Sopenharmony_ci	u64 len = vma->vm_end - vma->vm_start;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (ctx->afu->current_mode == CXL_MODE_DEDICATED) {
18862306a36Sopenharmony_ci		if (start + len > ctx->afu->adapter->ps_size)
18962306a36Sopenharmony_ci			return -EINVAL;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		if (cxl_is_power9()) {
19262306a36Sopenharmony_ci			/*
19362306a36Sopenharmony_ci			 * Make sure there is a valid problem state
19462306a36Sopenharmony_ci			 * area space for this AFU.
19562306a36Sopenharmony_ci			 */
19662306a36Sopenharmony_ci			if (ctx->master && !ctx->afu->psa) {
19762306a36Sopenharmony_ci				pr_devel("AFU doesn't support mmio space\n");
19862306a36Sopenharmony_ci				return -EINVAL;
19962306a36Sopenharmony_ci			}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci			/* Can't mmap until the AFU is enabled */
20262306a36Sopenharmony_ci			if (!ctx->afu->enabled)
20362306a36Sopenharmony_ci				return -EBUSY;
20462306a36Sopenharmony_ci		}
20562306a36Sopenharmony_ci	} else {
20662306a36Sopenharmony_ci		if (start + len > ctx->psn_size)
20762306a36Sopenharmony_ci			return -EINVAL;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		/* Make sure there is a valid per process space for this AFU */
21062306a36Sopenharmony_ci		if ((ctx->master && !ctx->afu->psa) || (!ctx->afu->pp_psa)) {
21162306a36Sopenharmony_ci			pr_devel("AFU doesn't support mmio space\n");
21262306a36Sopenharmony_ci			return -EINVAL;
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		/* Can't mmap until the AFU is enabled */
21662306a36Sopenharmony_ci		if (!ctx->afu->enabled)
21762306a36Sopenharmony_ci			return -EBUSY;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	pr_devel("%s: mmio physical: %llx pe: %i master:%i\n", __func__,
22162306a36Sopenharmony_ci		 ctx->psn_phys, ctx->pe , ctx->master);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	vm_flags_set(vma, VM_IO | VM_PFNMAP);
22462306a36Sopenharmony_ci	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
22562306a36Sopenharmony_ci	vma->vm_ops = &cxl_mmap_vmops;
22662306a36Sopenharmony_ci	return 0;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci/*
23062306a36Sopenharmony_ci * Detach a context from the hardware. This disables interrupts and doesn't
23162306a36Sopenharmony_ci * return until all outstanding interrupts for this context have completed. The
23262306a36Sopenharmony_ci * hardware should no longer access *ctx after this has returned.
23362306a36Sopenharmony_ci */
23462306a36Sopenharmony_ciint __detach_context(struct cxl_context *ctx)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	enum cxl_context_status status;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	mutex_lock(&ctx->status_mutex);
23962306a36Sopenharmony_ci	status = ctx->status;
24062306a36Sopenharmony_ci	ctx->status = CLOSED;
24162306a36Sopenharmony_ci	mutex_unlock(&ctx->status_mutex);
24262306a36Sopenharmony_ci	if (status != STARTED)
24362306a36Sopenharmony_ci		return -EBUSY;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Only warn if we detached while the link was OK.
24662306a36Sopenharmony_ci	 * If detach fails when hw is down, we don't care.
24762306a36Sopenharmony_ci	 */
24862306a36Sopenharmony_ci	WARN_ON(cxl_ops->detach_process(ctx) &&
24962306a36Sopenharmony_ci		cxl_ops->link_ok(ctx->afu->adapter, ctx->afu));
25062306a36Sopenharmony_ci	flush_work(&ctx->fault_work); /* Only needed for dedicated process */
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/*
25362306a36Sopenharmony_ci	 * Wait until no further interrupts are presented by the PSL
25462306a36Sopenharmony_ci	 * for this context.
25562306a36Sopenharmony_ci	 */
25662306a36Sopenharmony_ci	if (cxl_ops->irq_wait)
25762306a36Sopenharmony_ci		cxl_ops->irq_wait(ctx);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* release the reference to the group leader and mm handling pid */
26062306a36Sopenharmony_ci	put_pid(ctx->pid);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	cxl_ctx_put();
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* Decrease the attached context count on the adapter */
26562306a36Sopenharmony_ci	cxl_adapter_context_put(ctx->afu->adapter);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/* Decrease the mm count on the context */
26862306a36Sopenharmony_ci	cxl_context_mm_count_put(ctx);
26962306a36Sopenharmony_ci	if (ctx->mm)
27062306a36Sopenharmony_ci		mm_context_remove_copro(ctx->mm);
27162306a36Sopenharmony_ci	ctx->mm = NULL;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return 0;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/*
27762306a36Sopenharmony_ci * Detach the given context from the AFU. This doesn't actually
27862306a36Sopenharmony_ci * free the context but it should stop the context running in hardware
27962306a36Sopenharmony_ci * (ie. prevent this context from generating any further interrupts
28062306a36Sopenharmony_ci * so that it can be freed).
28162306a36Sopenharmony_ci */
28262306a36Sopenharmony_civoid cxl_context_detach(struct cxl_context *ctx)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	int rc;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	rc = __detach_context(ctx);
28762306a36Sopenharmony_ci	if (rc)
28862306a36Sopenharmony_ci		return;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	afu_release_irqs(ctx, ctx);
29162306a36Sopenharmony_ci	wake_up_all(&ctx->wq);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/*
29562306a36Sopenharmony_ci * Detach all contexts on the given AFU.
29662306a36Sopenharmony_ci */
29762306a36Sopenharmony_civoid cxl_context_detach_all(struct cxl_afu *afu)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct cxl_context *ctx;
30062306a36Sopenharmony_ci	int tmp;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	mutex_lock(&afu->contexts_lock);
30362306a36Sopenharmony_ci	idr_for_each_entry(&afu->contexts_idr, ctx, tmp) {
30462306a36Sopenharmony_ci		/*
30562306a36Sopenharmony_ci		 * Anything done in here needs to be setup before the IDR is
30662306a36Sopenharmony_ci		 * created and torn down after the IDR removed
30762306a36Sopenharmony_ci		 */
30862306a36Sopenharmony_ci		cxl_context_detach(ctx);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		/*
31162306a36Sopenharmony_ci		 * We are force detaching - remove any active PSA mappings so
31262306a36Sopenharmony_ci		 * userspace cannot interfere with the card if it comes back.
31362306a36Sopenharmony_ci		 * Easiest way to exercise this is to unbind and rebind the
31462306a36Sopenharmony_ci		 * driver via sysfs while it is in use.
31562306a36Sopenharmony_ci		 */
31662306a36Sopenharmony_ci		mutex_lock(&ctx->mapping_lock);
31762306a36Sopenharmony_ci		if (ctx->mapping)
31862306a36Sopenharmony_ci			unmap_mapping_range(ctx->mapping, 0, 0, 1);
31962306a36Sopenharmony_ci		mutex_unlock(&ctx->mapping_lock);
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci	mutex_unlock(&afu->contexts_lock);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic void reclaim_ctx(struct rcu_head *rcu)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct cxl_context *ctx = container_of(rcu, struct cxl_context, rcu);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (cxl_is_power8())
32962306a36Sopenharmony_ci		free_page((u64)ctx->sstp);
33062306a36Sopenharmony_ci	if (ctx->ff_page)
33162306a36Sopenharmony_ci		__free_page(ctx->ff_page);
33262306a36Sopenharmony_ci	ctx->sstp = NULL;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	bitmap_free(ctx->irq_bitmap);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* Drop ref to the afu device taken during cxl_context_init */
33762306a36Sopenharmony_ci	cxl_afu_put(ctx->afu);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	kfree(ctx);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_civoid cxl_context_free(struct cxl_context *ctx)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	if (ctx->kernelapi && ctx->mapping)
34562306a36Sopenharmony_ci		cxl_release_mapping(ctx);
34662306a36Sopenharmony_ci	mutex_lock(&ctx->afu->contexts_lock);
34762306a36Sopenharmony_ci	idr_remove(&ctx->afu->contexts_idr, ctx->pe);
34862306a36Sopenharmony_ci	mutex_unlock(&ctx->afu->contexts_lock);
34962306a36Sopenharmony_ci	call_rcu(&ctx->rcu, reclaim_ctx);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_civoid cxl_context_mm_count_get(struct cxl_context *ctx)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	if (ctx->mm)
35562306a36Sopenharmony_ci		mmgrab(ctx->mm);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_civoid cxl_context_mm_count_put(struct cxl_context *ctx)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	if (ctx->mm)
36162306a36Sopenharmony_ci		mmdrop(ctx->mm);
36262306a36Sopenharmony_ci}
363