12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2f204e0b8SIan Munsie /*
3f204e0b8SIan Munsie * Copyright 2014 IBM Corp.
4f204e0b8SIan Munsie */
5f204e0b8SIan Munsie
6f204e0b8SIan Munsie #include <linux/spinlock.h>
7f204e0b8SIan Munsie #include <linux/module.h>
8f204e0b8SIan Munsie #include <linux/export.h>
9f204e0b8SIan Munsie #include <linux/kernel.h>
10f204e0b8SIan Munsie #include <linux/bitmap.h>
11174cd4b1SIngo Molnar #include <linux/sched/signal.h>
12f204e0b8SIan Munsie #include <linux/poll.h>
13f204e0b8SIan Munsie #include <linux/pid.h>
14f204e0b8SIan Munsie #include <linux/fs.h>
15f204e0b8SIan Munsie #include <linux/mm.h>
16f204e0b8SIan Munsie #include <linux/slab.h>
176dd2d234SChristophe Lombard #include <linux/sched/mm.h>
1803b8abedSFrederic Barrat #include <linux/mmu_context.h>
19f204e0b8SIan Munsie #include <asm/cputable.h>
20f204e0b8SIan Munsie #include <asm/current.h>
21f204e0b8SIan Munsie #include <asm/copro.h>
22f204e0b8SIan Munsie
23f204e0b8SIan Munsie #include "cxl.h"
249bcf28cdSIan Munsie #include "trace.h"
25f204e0b8SIan Munsie
26f204e0b8SIan Munsie #define CXL_NUM_MINORS 256 /* Total to reserve */
27f204e0b8SIan Munsie
28f204e0b8SIan Munsie #define CXL_AFU_MINOR_D(afu) (CXL_CARD_MINOR(afu->adapter) + 1 + (3 * afu->slice))
29f204e0b8SIan Munsie #define CXL_AFU_MINOR_M(afu) (CXL_AFU_MINOR_D(afu) + 1)
30f204e0b8SIan Munsie #define CXL_AFU_MINOR_S(afu) (CXL_AFU_MINOR_D(afu) + 2)
31f204e0b8SIan Munsie #define CXL_AFU_MKDEV_D(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_D(afu))
32f204e0b8SIan Munsie #define CXL_AFU_MKDEV_M(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_M(afu))
33f204e0b8SIan Munsie #define CXL_AFU_MKDEV_S(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_S(afu))
34f204e0b8SIan Munsie
35f204e0b8SIan Munsie #define CXL_DEVT_AFU(dev) ((MINOR(dev) % CXL_DEV_MINORS - 1) / 3)
36f204e0b8SIan Munsie
37f204e0b8SIan Munsie #define CXL_DEVT_IS_CARD(dev) (MINOR(dev) % CXL_DEV_MINORS == 0)
38f204e0b8SIan Munsie
39f204e0b8SIan Munsie static dev_t cxl_dev;
40f204e0b8SIan Munsie
__afu_open(struct inode * inode,struct file * file,bool master)41f204e0b8SIan Munsie static int __afu_open(struct inode *inode, struct file *file, bool master)
42f204e0b8SIan Munsie {
43f204e0b8SIan Munsie struct cxl *adapter;
44f204e0b8SIan Munsie struct cxl_afu *afu;
45f204e0b8SIan Munsie struct cxl_context *ctx;
46f204e0b8SIan Munsie int adapter_num = CXL_DEVT_ADAPTER(inode->i_rdev);
47f204e0b8SIan Munsie int slice = CXL_DEVT_AFU(inode->i_rdev);
48f204e0b8SIan Munsie int rc = -ENODEV;
49f204e0b8SIan Munsie
50f204e0b8SIan Munsie pr_devel("afu_open afu%i.%i\n", slice, adapter_num);
51f204e0b8SIan Munsie
52f204e0b8SIan Munsie if (!(adapter = get_cxl_adapter(adapter_num)))
53f204e0b8SIan Munsie return -ENODEV;
54f204e0b8SIan Munsie
55f204e0b8SIan Munsie if (slice > adapter->slices)
56f204e0b8SIan Munsie goto err_put_adapter;
57f204e0b8SIan Munsie
58f204e0b8SIan Munsie spin_lock(&adapter->afu_list_lock);
59f204e0b8SIan Munsie if (!(afu = adapter->afu[slice])) {
60f204e0b8SIan Munsie spin_unlock(&adapter->afu_list_lock);
61f204e0b8SIan Munsie goto err_put_adapter;
62f204e0b8SIan Munsie }
631b5df59eSVaibhav Jain
641b5df59eSVaibhav Jain /*
651b5df59eSVaibhav Jain * taking a ref to the afu so that it doesn't go away
661b5df59eSVaibhav Jain * for rest of the function. This ref is released before
671b5df59eSVaibhav Jain * we return.
681b5df59eSVaibhav Jain */
691b5df59eSVaibhav Jain cxl_afu_get(afu);
70f204e0b8SIan Munsie spin_unlock(&adapter->afu_list_lock);
71f204e0b8SIan Munsie
72f204e0b8SIan Munsie if (!afu->current_mode)
73f204e0b8SIan Munsie goto err_put_afu;
74f204e0b8SIan Munsie
750d400f77SChristophe Lombard if (!cxl_ops->link_ok(adapter, afu)) {
760b3f9c75SDaniel Axtens rc = -EIO;
770b3f9c75SDaniel Axtens goto err_put_afu;
780b3f9c75SDaniel Axtens }
790b3f9c75SDaniel Axtens
80f204e0b8SIan Munsie if (!(ctx = cxl_context_alloc())) {
81f204e0b8SIan Munsie rc = -ENOMEM;
82f204e0b8SIan Munsie goto err_put_afu;
83f204e0b8SIan Munsie }
84f204e0b8SIan Munsie
85bdecf76eSFrederic Barrat rc = cxl_context_init(ctx, afu, master);
86bdecf76eSFrederic Barrat if (rc)
87f204e0b8SIan Munsie goto err_put_afu;
88f204e0b8SIan Munsie
89bdecf76eSFrederic Barrat cxl_context_set_mapping(ctx, inode->i_mapping);
90bdecf76eSFrederic Barrat
91f204e0b8SIan Munsie pr_devel("afu_open pe: %i\n", ctx->pe);
92f204e0b8SIan Munsie file->private_data = ctx;
93f204e0b8SIan Munsie
941b5df59eSVaibhav Jain /* indicate success */
951b5df59eSVaibhav Jain rc = 0;
96f204e0b8SIan Munsie
97f204e0b8SIan Munsie err_put_afu:
981b5df59eSVaibhav Jain /* release the ref taken earlier */
991b5df59eSVaibhav Jain cxl_afu_put(afu);
100f204e0b8SIan Munsie err_put_adapter:
101f204e0b8SIan Munsie put_device(&adapter->dev);
102f204e0b8SIan Munsie return rc;
103f204e0b8SIan Munsie }
1040520336aSMichael Neuling
afu_open(struct inode * inode,struct file * file)1050520336aSMichael Neuling int afu_open(struct inode *inode, struct file *file)
106f204e0b8SIan Munsie {
107f204e0b8SIan Munsie return __afu_open(inode, file, false);
108f204e0b8SIan Munsie }
109f204e0b8SIan Munsie
afu_master_open(struct inode * inode,struct file * file)110f204e0b8SIan Munsie static int afu_master_open(struct inode *inode, struct file *file)
111f204e0b8SIan Munsie {
112f204e0b8SIan Munsie return __afu_open(inode, file, true);
113f204e0b8SIan Munsie }
114f204e0b8SIan Munsie
afu_release(struct inode * inode,struct file * file)1150520336aSMichael Neuling int afu_release(struct inode *inode, struct file *file)
116f204e0b8SIan Munsie {
117f204e0b8SIan Munsie struct cxl_context *ctx = file->private_data;
118f204e0b8SIan Munsie
119f204e0b8SIan Munsie pr_devel("%s: closing cxl file descriptor. pe: %i\n",
120f204e0b8SIan Munsie __func__, ctx->pe);
121f204e0b8SIan Munsie cxl_context_detach(ctx);
122f204e0b8SIan Munsie
1235f81b95fSAndrew Donnellan
1245f81b95fSAndrew Donnellan /*
1255f81b95fSAndrew Donnellan * Delete the context's mapping pointer, unless it's created by the
1265f81b95fSAndrew Donnellan * kernel API, in which case leave it so it can be freed by reclaim_ctx()
1275f81b95fSAndrew Donnellan */
1285f81b95fSAndrew Donnellan if (!ctx->kernelapi) {
129b123429eSIan Munsie mutex_lock(&ctx->mapping_lock);
130b123429eSIan Munsie ctx->mapping = NULL;
131b123429eSIan Munsie mutex_unlock(&ctx->mapping_lock);
1325f81b95fSAndrew Donnellan }
133b123429eSIan Munsie
134f204e0b8SIan Munsie /*
135f204e0b8SIan Munsie * At this this point all bottom halfs have finished and we should be
136f204e0b8SIan Munsie * getting no more IRQs from the hardware for this context. Once it's
137f204e0b8SIan Munsie * removed from the IDR (and RCU synchronised) it's safe to free the
138f204e0b8SIan Munsie * sstp and context.
139f204e0b8SIan Munsie */
140f204e0b8SIan Munsie cxl_context_free(ctx);
141f204e0b8SIan Munsie
142f204e0b8SIan Munsie return 0;
143f204e0b8SIan Munsie }
144f204e0b8SIan Munsie
afu_ioctl_start_work(struct cxl_context * ctx,struct cxl_ioctl_start_work __user * uwork)145f204e0b8SIan Munsie static long afu_ioctl_start_work(struct cxl_context *ctx,
146f204e0b8SIan Munsie struct cxl_ioctl_start_work __user *uwork)
147f204e0b8SIan Munsie {
148f204e0b8SIan Munsie struct cxl_ioctl_start_work work;
149f204e0b8SIan Munsie u64 amr = 0;
150f204e0b8SIan Munsie int rc;
151f204e0b8SIan Munsie
152f204e0b8SIan Munsie pr_devel("%s: pe: %i\n", __func__, ctx->pe);
153f204e0b8SIan Munsie
1540712dc7eSIan Munsie /* Do this outside the status_mutex to avoid a circular dependency with
1550712dc7eSIan Munsie * the locking in cxl_mmap_fault() */
156cec422c1SFrederic Barrat if (copy_from_user(&work, uwork, sizeof(work)))
157cec422c1SFrederic Barrat return -EFAULT;
158f204e0b8SIan Munsie
1590712dc7eSIan Munsie mutex_lock(&ctx->status_mutex);
1600712dc7eSIan Munsie if (ctx->status != OPENED) {
1610712dc7eSIan Munsie rc = -EIO;
1620712dc7eSIan Munsie goto out;
1630712dc7eSIan Munsie }
1640712dc7eSIan Munsie
165f204e0b8SIan Munsie /*
166f204e0b8SIan Munsie * if any of the reserved fields are set or any of the unused
167f204e0b8SIan Munsie * flags are set it's invalid
168f204e0b8SIan Munsie */
169f204e0b8SIan Munsie if (work.reserved1 || work.reserved2 || work.reserved3 ||
170b1db5513SChristophe Lombard work.reserved4 || work.reserved5 ||
171f204e0b8SIan Munsie (work.flags & ~CXL_START_WORK_ALL)) {
172f204e0b8SIan Munsie rc = -EINVAL;
173f204e0b8SIan Munsie goto out;
174f204e0b8SIan Munsie }
175f204e0b8SIan Munsie
176f204e0b8SIan Munsie if (!(work.flags & CXL_START_WORK_NUM_IRQS))
177f204e0b8SIan Munsie work.num_interrupts = ctx->afu->pp_irqs;
178f204e0b8SIan Munsie else if ((work.num_interrupts < ctx->afu->pp_irqs) ||
179f204e0b8SIan Munsie (work.num_interrupts > ctx->afu->irqs_max)) {
180f204e0b8SIan Munsie rc = -EINVAL;
181f204e0b8SIan Munsie goto out;
182f204e0b8SIan Munsie }
183b1db5513SChristophe Lombard
184f204e0b8SIan Munsie if ((rc = afu_register_irqs(ctx, work.num_interrupts)))
185f204e0b8SIan Munsie goto out;
186f204e0b8SIan Munsie
187f204e0b8SIan Munsie if (work.flags & CXL_START_WORK_AMR)
188f204e0b8SIan Munsie amr = work.amr & mfspr(SPRN_UAMOR);
189f204e0b8SIan Munsie
190b1db5513SChristophe Lombard if (work.flags & CXL_START_WORK_TID)
191b1db5513SChristophe Lombard ctx->assign_tidr = true;
192b1db5513SChristophe Lombard
193d9232a3dSIan Munsie ctx->mmio_err_ff = !!(work.flags & CXL_START_WORK_ERR_FF);
194d9232a3dSIan Munsie
195f204e0b8SIan Munsie /*
196a05b82d5SVaibhav Jain * Increment the mapped context count for adapter. This also checks
197a05b82d5SVaibhav Jain * if adapter_context_lock is taken.
198a05b82d5SVaibhav Jain */
199a05b82d5SVaibhav Jain rc = cxl_adapter_context_get(ctx->afu->adapter);
200a05b82d5SVaibhav Jain if (rc) {
201a05b82d5SVaibhav Jain afu_release_irqs(ctx, ctx);
202a05b82d5SVaibhav Jain goto out;
203a05b82d5SVaibhav Jain }
204a05b82d5SVaibhav Jain
205a05b82d5SVaibhav Jain /*
206f204e0b8SIan Munsie * We grab the PID here and not in the file open to allow for the case
207f204e0b8SIan Munsie * where a process (master, some daemon, etc) has opened the chardev on
208f204e0b8SIan Munsie * behalf of another process, so the AFU's mm gets bound to the process
209f204e0b8SIan Munsie * that performs this ioctl and not the process that opened the file.
2107b8ad495SVaibhav Jain * Also we grab the PID of the group leader so that if the task that
2117b8ad495SVaibhav Jain * has performed the attach operation exits the mm context of the
2127b8ad495SVaibhav Jain * process is still accessible.
213f204e0b8SIan Munsie */
2147b8ad495SVaibhav Jain ctx->pid = get_task_pid(current, PIDTYPE_PID);
215f204e0b8SIan Munsie
2166dd2d234SChristophe Lombard /* acquire a reference to the task's mm */
2176dd2d234SChristophe Lombard ctx->mm = get_task_mm(current);
2186dd2d234SChristophe Lombard
2196dd2d234SChristophe Lombard /* ensure this mm_struct can't be freed */
2206dd2d234SChristophe Lombard cxl_context_mm_count_get(ctx);
2216dd2d234SChristophe Lombard
22203b8abedSFrederic Barrat if (ctx->mm) {
22303b8abedSFrederic Barrat /* decrement the use count from above */
2246dd2d234SChristophe Lombard mmput(ctx->mm);
22503b8abedSFrederic Barrat /* make TLBIs for this context global */
22603b8abedSFrederic Barrat mm_context_add_copro(ctx->mm);
22703b8abedSFrederic Barrat }
22870b565bbSVaibhav Jain
229197267d0SFrederic Barrat /*
230197267d0SFrederic Barrat * Increment driver use count. Enables global TLBIs for hash
231197267d0SFrederic Barrat * and callbacks to handle the segment table
232197267d0SFrederic Barrat */
233197267d0SFrederic Barrat cxl_ctx_get();
234197267d0SFrederic Barrat
23503b8abedSFrederic Barrat /*
23603b8abedSFrederic Barrat * A barrier is needed to make sure all TLBIs are global
23703b8abedSFrederic Barrat * before we attach and the context starts being used by the
23803b8abedSFrederic Barrat * adapter.
23903b8abedSFrederic Barrat *
24003b8abedSFrederic Barrat * Needed after mm_context_add_copro() for radix and
24103b8abedSFrederic Barrat * cxl_ctx_get() for hash/p8.
24203b8abedSFrederic Barrat *
24303b8abedSFrederic Barrat * The barrier should really be mb(), since it involves a
24403b8abedSFrederic Barrat * device. However, it's only useful when we have local
24503b8abedSFrederic Barrat * vs. global TLBIs, i.e SMP=y. So keep smp_mb().
24603b8abedSFrederic Barrat */
24703b8abedSFrederic Barrat smp_mb();
24803b8abedSFrederic Barrat
2499bcf28cdSIan Munsie trace_cxl_attach(ctx, work.work_element_descriptor, work.num_interrupts, amr);
2509bcf28cdSIan Munsie
2515be587b1SFrederic Barrat if ((rc = cxl_ops->attach_process(ctx, false, work.work_element_descriptor,
252456295e2SIan Munsie amr))) {
2536428832aSMichael Neuling afu_release_irqs(ctx, ctx);
25470b565bbSVaibhav Jain cxl_adapter_context_put(ctx->afu->adapter);
255a05b82d5SVaibhav Jain put_pid(ctx->pid);
2566dd2d234SChristophe Lombard ctx->pid = NULL;
257197267d0SFrederic Barrat cxl_ctx_put();
2586dd2d234SChristophe Lombard cxl_context_mm_count_put(ctx);
25903b8abedSFrederic Barrat if (ctx->mm)
26003b8abedSFrederic Barrat mm_context_remove_copro(ctx->mm);
261f204e0b8SIan Munsie goto out;
262456295e2SIan Munsie }
263f204e0b8SIan Munsie
264f204e0b8SIan Munsie rc = 0;
265b1db5513SChristophe Lombard if (work.flags & CXL_START_WORK_TID) {
266b1db5513SChristophe Lombard work.tid = ctx->tidr;
267b1db5513SChristophe Lombard if (copy_to_user(uwork, &work, sizeof(work)))
268b1db5513SChristophe Lombard rc = -EFAULT;
269b1db5513SChristophe Lombard }
270b1db5513SChristophe Lombard
271b1db5513SChristophe Lombard ctx->status = STARTED;
272b1db5513SChristophe Lombard
273f204e0b8SIan Munsie out:
274f204e0b8SIan Munsie mutex_unlock(&ctx->status_mutex);
275f204e0b8SIan Munsie return rc;
276f204e0b8SIan Munsie }
2775be587b1SFrederic Barrat
afu_ioctl_process_element(struct cxl_context * ctx,int __user * upe)278f204e0b8SIan Munsie static long afu_ioctl_process_element(struct cxl_context *ctx,
279f204e0b8SIan Munsie int __user *upe)
280f204e0b8SIan Munsie {
281f204e0b8SIan Munsie pr_devel("%s: pe: %i\n", __func__, ctx->pe);
282f204e0b8SIan Munsie
28314baf4d9SChristophe Lombard if (copy_to_user(upe, &ctx->external_pe, sizeof(__u32)))
284f204e0b8SIan Munsie return -EFAULT;
285f204e0b8SIan Munsie
286f204e0b8SIan Munsie return 0;
287f204e0b8SIan Munsie }
288f204e0b8SIan Munsie
afu_ioctl_get_afu_id(struct cxl_context * ctx,struct cxl_afu_id __user * upafuid)28927d4dc71SVaibhav Jain static long afu_ioctl_get_afu_id(struct cxl_context *ctx,
29027d4dc71SVaibhav Jain struct cxl_afu_id __user *upafuid)
29127d4dc71SVaibhav Jain {
29227d4dc71SVaibhav Jain struct cxl_afu_id afuid = { 0 };
29327d4dc71SVaibhav Jain
29427d4dc71SVaibhav Jain afuid.card_id = ctx->afu->adapter->adapter_num;
29527d4dc71SVaibhav Jain afuid.afu_offset = ctx->afu->slice;
29627d4dc71SVaibhav Jain afuid.afu_mode = ctx->afu->current_mode;
29727d4dc71SVaibhav Jain
29827d4dc71SVaibhav Jain /* set the flag bit in case the afu is a slave */
29927d4dc71SVaibhav Jain if (ctx->afu->current_mode == CXL_MODE_DIRECTED && !ctx->master)
30027d4dc71SVaibhav Jain afuid.flags |= CXL_AFUID_FLAG_SLAVE;
30127d4dc71SVaibhav Jain
30227d4dc71SVaibhav Jain if (copy_to_user(upafuid, &afuid, sizeof(afuid)))
30327d4dc71SVaibhav Jain return -EFAULT;
30427d4dc71SVaibhav Jain
30527d4dc71SVaibhav Jain return 0;
30627d4dc71SVaibhav Jain }
30727d4dc71SVaibhav Jain
afu_ioctl(struct file * file,unsigned int cmd,unsigned long arg)3080520336aSMichael Neuling long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
309f204e0b8SIan Munsie {
310f204e0b8SIan Munsie struct cxl_context *ctx = file->private_data;
311f204e0b8SIan Munsie
312f204e0b8SIan Munsie if (ctx->status == CLOSED)
313f204e0b8SIan Munsie return -EIO;
314f204e0b8SIan Munsie
3150d400f77SChristophe Lombard if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu))
3160b3f9c75SDaniel Axtens return -EIO;
3170b3f9c75SDaniel Axtens
318f204e0b8SIan Munsie pr_devel("afu_ioctl\n");
319f204e0b8SIan Munsie switch (cmd) {
320f204e0b8SIan Munsie case CXL_IOCTL_START_WORK:
321f204e0b8SIan Munsie return afu_ioctl_start_work(ctx, (struct cxl_ioctl_start_work __user *)arg);
322f204e0b8SIan Munsie case CXL_IOCTL_GET_PROCESS_ELEMENT:
323f204e0b8SIan Munsie return afu_ioctl_process_element(ctx, (__u32 __user *)arg);
32427d4dc71SVaibhav Jain case CXL_IOCTL_GET_AFU_ID:
32527d4dc71SVaibhav Jain return afu_ioctl_get_afu_id(ctx, (struct cxl_afu_id __user *)
32627d4dc71SVaibhav Jain arg);
327f204e0b8SIan Munsie }
328f204e0b8SIan Munsie return -EINVAL;
329f204e0b8SIan Munsie }
330f204e0b8SIan Munsie
afu_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)3313d6b040eSDaniel Axtens static long afu_compat_ioctl(struct file *file, unsigned int cmd,
332f204e0b8SIan Munsie unsigned long arg)
333f204e0b8SIan Munsie {
334f204e0b8SIan Munsie return afu_ioctl(file, cmd, arg);
335f204e0b8SIan Munsie }
336f204e0b8SIan Munsie
afu_mmap(struct file * file,struct vm_area_struct * vm)3370520336aSMichael Neuling int afu_mmap(struct file *file, struct vm_area_struct *vm)
338f204e0b8SIan Munsie {
339f204e0b8SIan Munsie struct cxl_context *ctx = file->private_data;
340f204e0b8SIan Munsie
341f204e0b8SIan Munsie /* AFU must be started before we can MMIO */
342f204e0b8SIan Munsie if (ctx->status != STARTED)
343f204e0b8SIan Munsie return -EIO;
344f204e0b8SIan Munsie
3450d400f77SChristophe Lombard if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu))
3460b3f9c75SDaniel Axtens return -EIO;
3470b3f9c75SDaniel Axtens
348f204e0b8SIan Munsie return cxl_context_iomap(ctx, vm);
349f204e0b8SIan Munsie }
350f204e0b8SIan Munsie
ctx_event_pending(struct cxl_context * ctx)351b810253bSPhilippe Bergheaud static inline bool ctx_event_pending(struct cxl_context *ctx)
352b810253bSPhilippe Bergheaud {
353b810253bSPhilippe Bergheaud if (ctx->pending_irq || ctx->pending_fault || ctx->pending_afu_err)
354b810253bSPhilippe Bergheaud return true;
355b810253bSPhilippe Bergheaud
356b810253bSPhilippe Bergheaud if (ctx->afu_driver_ops && atomic_read(&ctx->afu_driver_events))
357b810253bSPhilippe Bergheaud return true;
358b810253bSPhilippe Bergheaud
359b810253bSPhilippe Bergheaud return false;
360b810253bSPhilippe Bergheaud }
361b810253bSPhilippe Bergheaud
afu_poll(struct file * file,struct poll_table_struct * poll)362afc9a42bSAl Viro __poll_t afu_poll(struct file *file, struct poll_table_struct *poll)
363f204e0b8SIan Munsie {
364f204e0b8SIan Munsie struct cxl_context *ctx = file->private_data;
365afc9a42bSAl Viro __poll_t mask = 0;
366f204e0b8SIan Munsie unsigned long flags;
367f204e0b8SIan Munsie
368f204e0b8SIan Munsie
369f204e0b8SIan Munsie poll_wait(file, &ctx->wq, poll);
370f204e0b8SIan Munsie
371f204e0b8SIan Munsie pr_devel("afu_poll wait done pe: %i\n", ctx->pe);
372f204e0b8SIan Munsie
373f204e0b8SIan Munsie spin_lock_irqsave(&ctx->lock, flags);
374b810253bSPhilippe Bergheaud if (ctx_event_pending(ctx))
375a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM;
376f204e0b8SIan Munsie else if (ctx->status == CLOSED)
377f204e0b8SIan Munsie /* Only error on closed when there are no futher events pending
378f204e0b8SIan Munsie */
379a9a08845SLinus Torvalds mask |= EPOLLERR;
380f204e0b8SIan Munsie spin_unlock_irqrestore(&ctx->lock, flags);
381f204e0b8SIan Munsie
382f204e0b8SIan Munsie pr_devel("afu_poll pe: %i returning %#x\n", ctx->pe, mask);
383f204e0b8SIan Munsie
384f204e0b8SIan Munsie return mask;
385f204e0b8SIan Munsie }
386f204e0b8SIan Munsie
afu_driver_event_copy(struct cxl_context * ctx,char __user * buf,struct cxl_event * event,struct cxl_event_afu_driver_reserved * pl)387b810253bSPhilippe Bergheaud static ssize_t afu_driver_event_copy(struct cxl_context *ctx,
388b810253bSPhilippe Bergheaud char __user *buf,
389b810253bSPhilippe Bergheaud struct cxl_event *event,
390b810253bSPhilippe Bergheaud struct cxl_event_afu_driver_reserved *pl)
391f204e0b8SIan Munsie {
392b810253bSPhilippe Bergheaud /* Check event */
393b810253bSPhilippe Bergheaud if (!pl) {
394b810253bSPhilippe Bergheaud ctx->afu_driver_ops->event_delivered(ctx, pl, -EINVAL);
395b810253bSPhilippe Bergheaud return -EFAULT;
396b810253bSPhilippe Bergheaud }
397b810253bSPhilippe Bergheaud
398b810253bSPhilippe Bergheaud /* Check event size */
399b810253bSPhilippe Bergheaud event->header.size += pl->data_size;
400b810253bSPhilippe Bergheaud if (event->header.size > CXL_READ_MIN_SIZE) {
401b810253bSPhilippe Bergheaud ctx->afu_driver_ops->event_delivered(ctx, pl, -EINVAL);
402b810253bSPhilippe Bergheaud return -EFAULT;
403b810253bSPhilippe Bergheaud }
404b810253bSPhilippe Bergheaud
405b810253bSPhilippe Bergheaud /* Copy event header */
406b810253bSPhilippe Bergheaud if (copy_to_user(buf, event, sizeof(struct cxl_event_header))) {
407b810253bSPhilippe Bergheaud ctx->afu_driver_ops->event_delivered(ctx, pl, -EFAULT);
408b810253bSPhilippe Bergheaud return -EFAULT;
409b810253bSPhilippe Bergheaud }
410b810253bSPhilippe Bergheaud
411b810253bSPhilippe Bergheaud /* Copy event data */
412b810253bSPhilippe Bergheaud buf += sizeof(struct cxl_event_header);
413b810253bSPhilippe Bergheaud if (copy_to_user(buf, &pl->data, pl->data_size)) {
414b810253bSPhilippe Bergheaud ctx->afu_driver_ops->event_delivered(ctx, pl, -EFAULT);
415b810253bSPhilippe Bergheaud return -EFAULT;
416b810253bSPhilippe Bergheaud }
417b810253bSPhilippe Bergheaud
418b810253bSPhilippe Bergheaud ctx->afu_driver_ops->event_delivered(ctx, pl, 0); /* Success */
419b810253bSPhilippe Bergheaud return event->header.size;
420f204e0b8SIan Munsie }
421f204e0b8SIan Munsie
afu_read(struct file * file,char __user * buf,size_t count,loff_t * off)4220520336aSMichael Neuling ssize_t afu_read(struct file *file, char __user *buf, size_t count,
423f204e0b8SIan Munsie loff_t *off)
424f204e0b8SIan Munsie {
425f204e0b8SIan Munsie struct cxl_context *ctx = file->private_data;
426b810253bSPhilippe Bergheaud struct cxl_event_afu_driver_reserved *pl = NULL;
427f204e0b8SIan Munsie struct cxl_event event;
428f204e0b8SIan Munsie unsigned long flags;
429d53ba6b3SIan Munsie int rc;
430f204e0b8SIan Munsie DEFINE_WAIT(wait);
431f204e0b8SIan Munsie
4320d400f77SChristophe Lombard if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu))
4330b3f9c75SDaniel Axtens return -EIO;
4340b3f9c75SDaniel Axtens
435f204e0b8SIan Munsie if (count < CXL_READ_MIN_SIZE)
436f204e0b8SIan Munsie return -EINVAL;
437f204e0b8SIan Munsie
438f204e0b8SIan Munsie spin_lock_irqsave(&ctx->lock, flags);
439f204e0b8SIan Munsie
440f204e0b8SIan Munsie for (;;) {
441f204e0b8SIan Munsie prepare_to_wait(&ctx->wq, &wait, TASK_INTERRUPTIBLE);
442b810253bSPhilippe Bergheaud if (ctx_event_pending(ctx) || (ctx->status == CLOSED))
443f204e0b8SIan Munsie break;
444f204e0b8SIan Munsie
4450d400f77SChristophe Lombard if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu)) {
4460b3f9c75SDaniel Axtens rc = -EIO;
4470b3f9c75SDaniel Axtens goto out;
4480b3f9c75SDaniel Axtens }
4490b3f9c75SDaniel Axtens
450d53ba6b3SIan Munsie if (file->f_flags & O_NONBLOCK) {
451d53ba6b3SIan Munsie rc = -EAGAIN;
452d53ba6b3SIan Munsie goto out;
453d53ba6b3SIan Munsie }
454d53ba6b3SIan Munsie
455d53ba6b3SIan Munsie if (signal_pending(current)) {
456d53ba6b3SIan Munsie rc = -ERESTARTSYS;
457d53ba6b3SIan Munsie goto out;
458d53ba6b3SIan Munsie }
459d53ba6b3SIan Munsie
460f204e0b8SIan Munsie spin_unlock_irqrestore(&ctx->lock, flags);
461f204e0b8SIan Munsie pr_devel("afu_read going to sleep...\n");
462f204e0b8SIan Munsie schedule();
463f204e0b8SIan Munsie pr_devel("afu_read woken up\n");
464f204e0b8SIan Munsie spin_lock_irqsave(&ctx->lock, flags);
465f204e0b8SIan Munsie }
466f204e0b8SIan Munsie
467f204e0b8SIan Munsie finish_wait(&ctx->wq, &wait);
468f204e0b8SIan Munsie
469f204e0b8SIan Munsie memset(&event, 0, sizeof(event));
470f204e0b8SIan Munsie event.header.process_element = ctx->pe;
471f204e0b8SIan Munsie event.header.size = sizeof(struct cxl_event_header);
472b810253bSPhilippe Bergheaud if (ctx->afu_driver_ops && atomic_read(&ctx->afu_driver_events)) {
473b810253bSPhilippe Bergheaud pr_devel("afu_read delivering AFU driver specific event\n");
474b810253bSPhilippe Bergheaud pl = ctx->afu_driver_ops->fetch_event(ctx);
475b810253bSPhilippe Bergheaud atomic_dec(&ctx->afu_driver_events);
476b810253bSPhilippe Bergheaud event.header.type = CXL_EVENT_AFU_DRIVER;
477b810253bSPhilippe Bergheaud } else if (ctx->pending_irq) {
478f204e0b8SIan Munsie pr_devel("afu_read delivering AFU interrupt\n");
479f204e0b8SIan Munsie event.header.size += sizeof(struct cxl_event_afu_interrupt);
480f204e0b8SIan Munsie event.header.type = CXL_EVENT_AFU_INTERRUPT;
481f204e0b8SIan Munsie event.irq.irq = find_first_bit(ctx->irq_bitmap, ctx->irq_count) + 1;
482f204e0b8SIan Munsie clear_bit(event.irq.irq - 1, ctx->irq_bitmap);
483f204e0b8SIan Munsie if (bitmap_empty(ctx->irq_bitmap, ctx->irq_count))
484f204e0b8SIan Munsie ctx->pending_irq = false;
485f204e0b8SIan Munsie } else if (ctx->pending_fault) {
486f204e0b8SIan Munsie pr_devel("afu_read delivering data storage fault\n");
487f204e0b8SIan Munsie event.header.size += sizeof(struct cxl_event_data_storage);
488f204e0b8SIan Munsie event.header.type = CXL_EVENT_DATA_STORAGE;
489f204e0b8SIan Munsie event.fault.addr = ctx->fault_addr;
490f204e0b8SIan Munsie event.fault.dsisr = ctx->fault_dsisr;
491f204e0b8SIan Munsie ctx->pending_fault = false;
492f204e0b8SIan Munsie } else if (ctx->pending_afu_err) {
493f204e0b8SIan Munsie pr_devel("afu_read delivering afu error\n");
494f204e0b8SIan Munsie event.header.size += sizeof(struct cxl_event_afu_error);
495f204e0b8SIan Munsie event.header.type = CXL_EVENT_AFU_ERROR;
496f204e0b8SIan Munsie event.afu_error.error = ctx->afu_err;
497f204e0b8SIan Munsie ctx->pending_afu_err = false;
498f204e0b8SIan Munsie } else if (ctx->status == CLOSED) {
499f204e0b8SIan Munsie pr_devel("afu_read fatal error\n");
500f204e0b8SIan Munsie spin_unlock_irqrestore(&ctx->lock, flags);
501f204e0b8SIan Munsie return -EIO;
502f204e0b8SIan Munsie } else
503f204e0b8SIan Munsie WARN(1, "afu_read must be buggy\n");
504f204e0b8SIan Munsie
505f204e0b8SIan Munsie spin_unlock_irqrestore(&ctx->lock, flags);
506f204e0b8SIan Munsie
507b810253bSPhilippe Bergheaud if (event.header.type == CXL_EVENT_AFU_DRIVER)
508b810253bSPhilippe Bergheaud return afu_driver_event_copy(ctx, buf, &event, pl);
509b810253bSPhilippe Bergheaud
510f204e0b8SIan Munsie if (copy_to_user(buf, &event, event.header.size))
511f204e0b8SIan Munsie return -EFAULT;
512f204e0b8SIan Munsie return event.header.size;
513d53ba6b3SIan Munsie
514d53ba6b3SIan Munsie out:
515d53ba6b3SIan Munsie finish_wait(&ctx->wq, &wait);
516d53ba6b3SIan Munsie spin_unlock_irqrestore(&ctx->lock, flags);
517d53ba6b3SIan Munsie return rc;
518f204e0b8SIan Munsie }
519f204e0b8SIan Munsie
5200520336aSMichael Neuling /*
5210520336aSMichael Neuling * Note: if this is updated, we need to update api.c to patch the new ones in
5220520336aSMichael Neuling * too
5230520336aSMichael Neuling */
5240520336aSMichael Neuling const struct file_operations afu_fops = {
525f204e0b8SIan Munsie .owner = THIS_MODULE,
526f204e0b8SIan Munsie .open = afu_open,
527f204e0b8SIan Munsie .poll = afu_poll,
528f204e0b8SIan Munsie .read = afu_read,
529f204e0b8SIan Munsie .release = afu_release,
530f204e0b8SIan Munsie .unlocked_ioctl = afu_ioctl,
531f204e0b8SIan Munsie .compat_ioctl = afu_compat_ioctl,
532f204e0b8SIan Munsie .mmap = afu_mmap,
533f204e0b8SIan Munsie };
534f204e0b8SIan Munsie
5353d6b040eSDaniel Axtens static const struct file_operations afu_master_fops = {
536f204e0b8SIan Munsie .owner = THIS_MODULE,
537f204e0b8SIan Munsie .open = afu_master_open,
538f204e0b8SIan Munsie .poll = afu_poll,
539f204e0b8SIan Munsie .read = afu_read,
540f204e0b8SIan Munsie .release = afu_release,
541f204e0b8SIan Munsie .unlocked_ioctl = afu_ioctl,
542f204e0b8SIan Munsie .compat_ioctl = afu_compat_ioctl,
543f204e0b8SIan Munsie .mmap = afu_mmap,
544f204e0b8SIan Munsie };
545f204e0b8SIan Munsie
546f204e0b8SIan Munsie
cxl_devnode(const struct device * dev,umode_t * mode)547fb12940fSGreg Kroah-Hartman static char *cxl_devnode(const struct device *dev, umode_t *mode)
548f204e0b8SIan Munsie {
549594ff7d0SChristophe Lombard if (cpu_has_feature(CPU_FTR_HVMODE) &&
550594ff7d0SChristophe Lombard CXL_DEVT_IS_CARD(dev->devt)) {
551f204e0b8SIan Munsie /*
552f204e0b8SIan Munsie * These minor numbers will eventually be used to program the
553f204e0b8SIan Munsie * PSL and AFUs once we have dynamic reprogramming support
554f204e0b8SIan Munsie */
555f204e0b8SIan Munsie return NULL;
556f204e0b8SIan Munsie }
557f204e0b8SIan Munsie return kasprintf(GFP_KERNEL, "cxl/%s", dev_name(dev));
558f204e0b8SIan Munsie }
559f204e0b8SIan Munsie
560*d2231926SGreg Kroah-Hartman static const struct class cxl_class = {
561*d2231926SGreg Kroah-Hartman .name = "cxl",
562*d2231926SGreg Kroah-Hartman .devnode = cxl_devnode,
563*d2231926SGreg Kroah-Hartman };
564f204e0b8SIan Munsie
cxl_add_chardev(struct cxl_afu * afu,dev_t devt,struct cdev * cdev,struct device ** chardev,char * postfix,char * desc,const struct file_operations * fops)565f204e0b8SIan Munsie static int cxl_add_chardev(struct cxl_afu *afu, dev_t devt, struct cdev *cdev,
566f204e0b8SIan Munsie struct device **chardev, char *postfix, char *desc,
567f204e0b8SIan Munsie const struct file_operations *fops)
568f204e0b8SIan Munsie {
569f204e0b8SIan Munsie struct device *dev;
570f204e0b8SIan Munsie int rc;
571f204e0b8SIan Munsie
572f204e0b8SIan Munsie cdev_init(cdev, fops);
573da9db711SChristophe JAILLET rc = cdev_add(cdev, devt, 1);
574da9db711SChristophe JAILLET if (rc) {
575f204e0b8SIan Munsie dev_err(&afu->dev, "Unable to add %s chardev: %i\n", desc, rc);
576f204e0b8SIan Munsie return rc;
577f204e0b8SIan Munsie }
578f204e0b8SIan Munsie
579*d2231926SGreg Kroah-Hartman dev = device_create(&cxl_class, &afu->dev, devt, afu,
580f204e0b8SIan Munsie "afu%i.%i%s", afu->adapter->adapter_num, afu->slice, postfix);
581f204e0b8SIan Munsie if (IS_ERR(dev)) {
582f204e0b8SIan Munsie rc = PTR_ERR(dev);
583da9db711SChristophe JAILLET dev_err(&afu->dev, "Unable to create %s chardev in sysfs: %i\n", desc, rc);
584f204e0b8SIan Munsie goto err;
585f204e0b8SIan Munsie }
586f204e0b8SIan Munsie
587f204e0b8SIan Munsie *chardev = dev;
588f204e0b8SIan Munsie
589f204e0b8SIan Munsie return 0;
590f204e0b8SIan Munsie err:
591f204e0b8SIan Munsie cdev_del(cdev);
592f204e0b8SIan Munsie return rc;
593f204e0b8SIan Munsie }
594f204e0b8SIan Munsie
cxl_chardev_d_afu_add(struct cxl_afu * afu)595f204e0b8SIan Munsie int cxl_chardev_d_afu_add(struct cxl_afu *afu)
596f204e0b8SIan Munsie {
597f204e0b8SIan Munsie return cxl_add_chardev(afu, CXL_AFU_MKDEV_D(afu), &afu->afu_cdev_d,
598f204e0b8SIan Munsie &afu->chardev_d, "d", "dedicated",
599f204e0b8SIan Munsie &afu_master_fops); /* Uses master fops */
600f204e0b8SIan Munsie }
601f204e0b8SIan Munsie
cxl_chardev_m_afu_add(struct cxl_afu * afu)602f204e0b8SIan Munsie int cxl_chardev_m_afu_add(struct cxl_afu *afu)
603f204e0b8SIan Munsie {
604f204e0b8SIan Munsie return cxl_add_chardev(afu, CXL_AFU_MKDEV_M(afu), &afu->afu_cdev_m,
605f204e0b8SIan Munsie &afu->chardev_m, "m", "master",
606f204e0b8SIan Munsie &afu_master_fops);
607f204e0b8SIan Munsie }
608f204e0b8SIan Munsie
cxl_chardev_s_afu_add(struct cxl_afu * afu)609f204e0b8SIan Munsie int cxl_chardev_s_afu_add(struct cxl_afu *afu)
610f204e0b8SIan Munsie {
611f204e0b8SIan Munsie return cxl_add_chardev(afu, CXL_AFU_MKDEV_S(afu), &afu->afu_cdev_s,
612f204e0b8SIan Munsie &afu->chardev_s, "s", "shared",
613f204e0b8SIan Munsie &afu_fops);
614f204e0b8SIan Munsie }
615f204e0b8SIan Munsie
cxl_chardev_afu_remove(struct cxl_afu * afu)616f204e0b8SIan Munsie void cxl_chardev_afu_remove(struct cxl_afu *afu)
617f204e0b8SIan Munsie {
618f204e0b8SIan Munsie if (afu->chardev_d) {
619f204e0b8SIan Munsie cdev_del(&afu->afu_cdev_d);
620f204e0b8SIan Munsie device_unregister(afu->chardev_d);
621f204e0b8SIan Munsie afu->chardev_d = NULL;
622f204e0b8SIan Munsie }
623f204e0b8SIan Munsie if (afu->chardev_m) {
624f204e0b8SIan Munsie cdev_del(&afu->afu_cdev_m);
625f204e0b8SIan Munsie device_unregister(afu->chardev_m);
626f204e0b8SIan Munsie afu->chardev_m = NULL;
627f204e0b8SIan Munsie }
628f204e0b8SIan Munsie if (afu->chardev_s) {
629f204e0b8SIan Munsie cdev_del(&afu->afu_cdev_s);
630f204e0b8SIan Munsie device_unregister(afu->chardev_s);
631f204e0b8SIan Munsie afu->chardev_s = NULL;
632f204e0b8SIan Munsie }
633f204e0b8SIan Munsie }
634f204e0b8SIan Munsie
cxl_register_afu(struct cxl_afu * afu)635f204e0b8SIan Munsie int cxl_register_afu(struct cxl_afu *afu)
636f204e0b8SIan Munsie {
637*d2231926SGreg Kroah-Hartman afu->dev.class = &cxl_class;
638f204e0b8SIan Munsie
639f204e0b8SIan Munsie return device_register(&afu->dev);
640f204e0b8SIan Munsie }
641f204e0b8SIan Munsie
cxl_register_adapter(struct cxl * adapter)642f204e0b8SIan Munsie int cxl_register_adapter(struct cxl *adapter)
643f204e0b8SIan Munsie {
644*d2231926SGreg Kroah-Hartman adapter->dev.class = &cxl_class;
645f204e0b8SIan Munsie
646f204e0b8SIan Munsie /*
647f204e0b8SIan Munsie * Future: When we support dynamically reprogramming the PSL & AFU we
648f204e0b8SIan Munsie * will expose the interface to do that via a chardev:
649f204e0b8SIan Munsie * adapter->dev.devt = CXL_CARD_MKDEV(adapter);
650f204e0b8SIan Munsie */
651f204e0b8SIan Munsie
652f204e0b8SIan Munsie return device_register(&adapter->dev);
653f204e0b8SIan Munsie }
654f204e0b8SIan Munsie
cxl_get_dev(void)655594ff7d0SChristophe Lombard dev_t cxl_get_dev(void)
656594ff7d0SChristophe Lombard {
657594ff7d0SChristophe Lombard return cxl_dev;
658594ff7d0SChristophe Lombard }
659594ff7d0SChristophe Lombard
cxl_file_init(void)660f204e0b8SIan Munsie int __init cxl_file_init(void)
661f204e0b8SIan Munsie {
662f204e0b8SIan Munsie int rc;
663f204e0b8SIan Munsie
664f204e0b8SIan Munsie /*
665f204e0b8SIan Munsie * If these change we really need to update API. Either change some
666f204e0b8SIan Munsie * flags or update API version number CXL_API_VERSION.
667f204e0b8SIan Munsie */
668b810253bSPhilippe Bergheaud BUILD_BUG_ON(CXL_API_VERSION != 3);
669f204e0b8SIan Munsie BUILD_BUG_ON(sizeof(struct cxl_ioctl_start_work) != 64);
670f204e0b8SIan Munsie BUILD_BUG_ON(sizeof(struct cxl_event_header) != 8);
671f204e0b8SIan Munsie BUILD_BUG_ON(sizeof(struct cxl_event_afu_interrupt) != 8);
672f204e0b8SIan Munsie BUILD_BUG_ON(sizeof(struct cxl_event_data_storage) != 32);
673f204e0b8SIan Munsie BUILD_BUG_ON(sizeof(struct cxl_event_afu_error) != 16);
674f204e0b8SIan Munsie
675f204e0b8SIan Munsie if ((rc = alloc_chrdev_region(&cxl_dev, 0, CXL_NUM_MINORS, "cxl"))) {
676f204e0b8SIan Munsie pr_err("Unable to allocate CXL major number: %i\n", rc);
677f204e0b8SIan Munsie return rc;
678f204e0b8SIan Munsie }
679f204e0b8SIan Munsie
680f204e0b8SIan Munsie pr_devel("CXL device allocated, MAJOR %i\n", MAJOR(cxl_dev));
681f204e0b8SIan Munsie
682*d2231926SGreg Kroah-Hartman rc = class_register(&cxl_class);
683*d2231926SGreg Kroah-Hartman if (rc) {
684f204e0b8SIan Munsie pr_err("Unable to create CXL class\n");
685f204e0b8SIan Munsie goto err;
686f204e0b8SIan Munsie }
687f204e0b8SIan Munsie
688f204e0b8SIan Munsie return 0;
689f204e0b8SIan Munsie
690f204e0b8SIan Munsie err:
691f204e0b8SIan Munsie unregister_chrdev_region(cxl_dev, CXL_NUM_MINORS);
692f204e0b8SIan Munsie return rc;
693f204e0b8SIan Munsie }
694f204e0b8SIan Munsie
cxl_file_exit(void)695f204e0b8SIan Munsie void cxl_file_exit(void)
696f204e0b8SIan Munsie {
697f204e0b8SIan Munsie unregister_chrdev_region(cxl_dev, CXL_NUM_MINORS);
698*d2231926SGreg Kroah-Hartman class_unregister(&cxl_class);
699f204e0b8SIan Munsie }
700