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/pci.h> 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include <linux/file.h> 962306a36Sopenharmony_ci#include <misc/cxl.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/mount.h> 1262306a36Sopenharmony_ci#include <linux/pseudo_fs.h> 1362306a36Sopenharmony_ci#include <linux/sched/mm.h> 1462306a36Sopenharmony_ci#include <linux/mmu_context.h> 1562306a36Sopenharmony_ci#include <linux/irqdomain.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "cxl.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * Since we want to track memory mappings to be able to force-unmap 2162306a36Sopenharmony_ci * when the AFU is no longer reachable, we need an inode. For devices 2262306a36Sopenharmony_ci * opened through the cxl user API, this is not a problem, but a 2362306a36Sopenharmony_ci * userland process can also get a cxl fd through the cxl_get_fd() 2462306a36Sopenharmony_ci * API, which is used by the cxlflash driver. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Therefore we implement our own simple pseudo-filesystem and inode 2762306a36Sopenharmony_ci * allocator. We don't use the anonymous inode, as we need the 2862306a36Sopenharmony_ci * meta-data associated with it (address_space) and it is shared by 2962306a36Sopenharmony_ci * other drivers/processes, so it could lead to cxl unmapping VMAs 3062306a36Sopenharmony_ci * from random processes. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define CXL_PSEUDO_FS_MAGIC 0x1697697f 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int cxl_fs_cnt; 3662306a36Sopenharmony_cistatic struct vfsmount *cxl_vfs_mount; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int cxl_fs_init_fs_context(struct fs_context *fc) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci return init_pseudo(fc, CXL_PSEUDO_FS_MAGIC) ? 0 : -ENOMEM; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct file_system_type cxl_fs_type = { 4462306a36Sopenharmony_ci .name = "cxl", 4562306a36Sopenharmony_ci .owner = THIS_MODULE, 4662306a36Sopenharmony_ci .init_fs_context = cxl_fs_init_fs_context, 4762306a36Sopenharmony_ci .kill_sb = kill_anon_super, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_civoid cxl_release_mapping(struct cxl_context *ctx) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci if (ctx->kernelapi && ctx->mapping) 5462306a36Sopenharmony_ci simple_release_fs(&cxl_vfs_mount, &cxl_fs_cnt); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct file *cxl_getfile(const char *name, 5862306a36Sopenharmony_ci const struct file_operations *fops, 5962306a36Sopenharmony_ci void *priv, int flags) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct file *file; 6262306a36Sopenharmony_ci struct inode *inode; 6362306a36Sopenharmony_ci int rc; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* strongly inspired by anon_inode_getfile() */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (fops->owner && !try_module_get(fops->owner)) 6862306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci rc = simple_pin_fs(&cxl_fs_type, &cxl_vfs_mount, &cxl_fs_cnt); 7162306a36Sopenharmony_ci if (rc < 0) { 7262306a36Sopenharmony_ci pr_err("Cannot mount cxl pseudo filesystem: %d\n", rc); 7362306a36Sopenharmony_ci file = ERR_PTR(rc); 7462306a36Sopenharmony_ci goto err_module; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci inode = alloc_anon_inode(cxl_vfs_mount->mnt_sb); 7862306a36Sopenharmony_ci if (IS_ERR(inode)) { 7962306a36Sopenharmony_ci file = ERR_CAST(inode); 8062306a36Sopenharmony_ci goto err_fs; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci file = alloc_file_pseudo(inode, cxl_vfs_mount, name, 8462306a36Sopenharmony_ci flags & (O_ACCMODE | O_NONBLOCK), fops); 8562306a36Sopenharmony_ci if (IS_ERR(file)) 8662306a36Sopenharmony_ci goto err_inode; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci file->private_data = priv; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return file; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cierr_inode: 9362306a36Sopenharmony_ci iput(inode); 9462306a36Sopenharmony_cierr_fs: 9562306a36Sopenharmony_ci simple_release_fs(&cxl_vfs_mount, &cxl_fs_cnt); 9662306a36Sopenharmony_cierr_module: 9762306a36Sopenharmony_ci module_put(fops->owner); 9862306a36Sopenharmony_ci return file; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistruct cxl_context *cxl_dev_context_init(struct pci_dev *dev) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct cxl_afu *afu; 10462306a36Sopenharmony_ci struct cxl_context *ctx; 10562306a36Sopenharmony_ci int rc; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci afu = cxl_pci_to_afu(dev); 10862306a36Sopenharmony_ci if (IS_ERR(afu)) 10962306a36Sopenharmony_ci return ERR_CAST(afu); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci ctx = cxl_context_alloc(); 11262306a36Sopenharmony_ci if (!ctx) 11362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci ctx->kernelapi = true; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Make it a slave context. We can promote it later? */ 11862306a36Sopenharmony_ci rc = cxl_context_init(ctx, afu, false); 11962306a36Sopenharmony_ci if (rc) 12062306a36Sopenharmony_ci goto err_ctx; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return ctx; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cierr_ctx: 12562306a36Sopenharmony_ci kfree(ctx); 12662306a36Sopenharmony_ci return ERR_PTR(rc); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_dev_context_init); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistruct cxl_context *cxl_get_context(struct pci_dev *dev) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci return dev->dev.archdata.cxl_ctx; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_get_context); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciint cxl_release_context(struct cxl_context *ctx) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci if (ctx->status >= STARTED) 13962306a36Sopenharmony_ci return -EBUSY; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci cxl_context_free(ctx); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_release_context); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic irq_hw_number_t cxl_find_afu_irq(struct cxl_context *ctx, int num) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci __u16 range; 15062306a36Sopenharmony_ci int r; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci for (r = 0; r < CXL_IRQ_RANGES; r++) { 15362306a36Sopenharmony_ci range = ctx->irqs.range[r]; 15462306a36Sopenharmony_ci if (num < range) { 15562306a36Sopenharmony_ci return ctx->irqs.offset[r] + num; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci num -= range; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciint cxl_set_priv(struct cxl_context *ctx, void *priv) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci if (!ctx) 16662306a36Sopenharmony_ci return -EINVAL; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ctx->priv = priv; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_set_priv); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_civoid *cxl_get_priv(struct cxl_context *ctx) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci if (!ctx) 17762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return ctx->priv; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_get_priv); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ciint cxl_allocate_afu_irqs(struct cxl_context *ctx, int num) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci int res; 18662306a36Sopenharmony_ci irq_hw_number_t hwirq; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (num == 0) 18962306a36Sopenharmony_ci num = ctx->afu->pp_irqs; 19062306a36Sopenharmony_ci res = afu_allocate_irqs(ctx, num); 19162306a36Sopenharmony_ci if (res) 19262306a36Sopenharmony_ci return res; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_HVMODE)) { 19562306a36Sopenharmony_ci /* In a guest, the PSL interrupt is not multiplexed. It was 19662306a36Sopenharmony_ci * allocated above, and we need to set its handler 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci hwirq = cxl_find_afu_irq(ctx, 0); 19962306a36Sopenharmony_ci if (hwirq) 20062306a36Sopenharmony_ci cxl_map_irq(ctx->afu->adapter, hwirq, cxl_ops->psl_interrupt, ctx, "psl"); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (ctx->status == STARTED) { 20462306a36Sopenharmony_ci if (cxl_ops->update_ivtes) 20562306a36Sopenharmony_ci cxl_ops->update_ivtes(ctx); 20662306a36Sopenharmony_ci else WARN(1, "BUG: cxl_allocate_afu_irqs must be called prior to starting the context on this platform\n"); 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return res; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_allocate_afu_irqs); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_civoid cxl_free_afu_irqs(struct cxl_context *ctx) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci irq_hw_number_t hwirq; 21662306a36Sopenharmony_ci unsigned int virq; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_HVMODE)) { 21962306a36Sopenharmony_ci hwirq = cxl_find_afu_irq(ctx, 0); 22062306a36Sopenharmony_ci if (hwirq) { 22162306a36Sopenharmony_ci virq = irq_find_mapping(NULL, hwirq); 22262306a36Sopenharmony_ci if (virq) 22362306a36Sopenharmony_ci cxl_unmap_irq(virq, ctx); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci afu_irq_name_free(ctx); 22762306a36Sopenharmony_ci cxl_ops->release_irq_ranges(&ctx->irqs, ctx->afu->adapter); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_free_afu_irqs); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ciint cxl_map_afu_irq(struct cxl_context *ctx, int num, 23262306a36Sopenharmony_ci irq_handler_t handler, void *cookie, char *name) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci irq_hw_number_t hwirq; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* 23762306a36Sopenharmony_ci * Find interrupt we are to register. 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_ci hwirq = cxl_find_afu_irq(ctx, num); 24062306a36Sopenharmony_ci if (!hwirq) 24162306a36Sopenharmony_ci return -ENOENT; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return cxl_map_irq(ctx->afu->adapter, hwirq, handler, cookie, name); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_map_afu_irq); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_civoid cxl_unmap_afu_irq(struct cxl_context *ctx, int num, void *cookie) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci irq_hw_number_t hwirq; 25062306a36Sopenharmony_ci unsigned int virq; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci hwirq = cxl_find_afu_irq(ctx, num); 25362306a36Sopenharmony_ci if (!hwirq) 25462306a36Sopenharmony_ci return; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci virq = irq_find_mapping(NULL, hwirq); 25762306a36Sopenharmony_ci if (virq) 25862306a36Sopenharmony_ci cxl_unmap_irq(virq, cookie); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_unmap_afu_irq); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * Start a context 26462306a36Sopenharmony_ci * Code here similar to afu_ioctl_start_work(). 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ciint cxl_start_context(struct cxl_context *ctx, u64 wed, 26762306a36Sopenharmony_ci struct task_struct *task) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci int rc = 0; 27062306a36Sopenharmony_ci bool kernel = true; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci pr_devel("%s: pe: %i\n", __func__, ctx->pe); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci mutex_lock(&ctx->status_mutex); 27562306a36Sopenharmony_ci if (ctx->status == STARTED) 27662306a36Sopenharmony_ci goto out; /* already started */ 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* 27962306a36Sopenharmony_ci * Increment the mapped context count for adapter. This also checks 28062306a36Sopenharmony_ci * if adapter_context_lock is taken. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci rc = cxl_adapter_context_get(ctx->afu->adapter); 28362306a36Sopenharmony_ci if (rc) 28462306a36Sopenharmony_ci goto out; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (task) { 28762306a36Sopenharmony_ci ctx->pid = get_task_pid(task, PIDTYPE_PID); 28862306a36Sopenharmony_ci kernel = false; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* acquire a reference to the task's mm */ 29162306a36Sopenharmony_ci ctx->mm = get_task_mm(current); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* ensure this mm_struct can't be freed */ 29462306a36Sopenharmony_ci cxl_context_mm_count_get(ctx); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (ctx->mm) { 29762306a36Sopenharmony_ci /* decrement the use count from above */ 29862306a36Sopenharmony_ci mmput(ctx->mm); 29962306a36Sopenharmony_ci /* make TLBIs for this context global */ 30062306a36Sopenharmony_ci mm_context_add_copro(ctx->mm); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* 30562306a36Sopenharmony_ci * Increment driver use count. Enables global TLBIs for hash 30662306a36Sopenharmony_ci * and callbacks to handle the segment table 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci cxl_ctx_get(); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* See the comment in afu_ioctl_start_work() */ 31162306a36Sopenharmony_ci smp_mb(); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if ((rc = cxl_ops->attach_process(ctx, kernel, wed, 0))) { 31462306a36Sopenharmony_ci put_pid(ctx->pid); 31562306a36Sopenharmony_ci ctx->pid = NULL; 31662306a36Sopenharmony_ci cxl_adapter_context_put(ctx->afu->adapter); 31762306a36Sopenharmony_ci cxl_ctx_put(); 31862306a36Sopenharmony_ci if (task) { 31962306a36Sopenharmony_ci cxl_context_mm_count_put(ctx); 32062306a36Sopenharmony_ci if (ctx->mm) 32162306a36Sopenharmony_ci mm_context_remove_copro(ctx->mm); 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci goto out; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ctx->status = STARTED; 32762306a36Sopenharmony_ciout: 32862306a36Sopenharmony_ci mutex_unlock(&ctx->status_mutex); 32962306a36Sopenharmony_ci return rc; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_start_context); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ciint cxl_process_element(struct cxl_context *ctx) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci return ctx->external_pe; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_process_element); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* Stop a context. Returns 0 on success, otherwise -Errno */ 34062306a36Sopenharmony_ciint cxl_stop_context(struct cxl_context *ctx) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci return __detach_context(ctx); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_stop_context); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_civoid cxl_set_master(struct cxl_context *ctx) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci ctx->master = true; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_set_master); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/* wrappers around afu_* file ops which are EXPORTED */ 35362306a36Sopenharmony_ciint cxl_fd_open(struct inode *inode, struct file *file) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci return afu_open(inode, file); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_fd_open); 35862306a36Sopenharmony_ciint cxl_fd_release(struct inode *inode, struct file *file) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci return afu_release(inode, file); 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_fd_release); 36362306a36Sopenharmony_cilong cxl_fd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci return afu_ioctl(file, cmd, arg); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_fd_ioctl); 36862306a36Sopenharmony_ciint cxl_fd_mmap(struct file *file, struct vm_area_struct *vm) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci return afu_mmap(file, vm); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_fd_mmap); 37362306a36Sopenharmony_ci__poll_t cxl_fd_poll(struct file *file, struct poll_table_struct *poll) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci return afu_poll(file, poll); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_fd_poll); 37862306a36Sopenharmony_cissize_t cxl_fd_read(struct file *file, char __user *buf, size_t count, 37962306a36Sopenharmony_ci loff_t *off) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci return afu_read(file, buf, count, off); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_fd_read); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci#define PATCH_FOPS(NAME) if (!fops->NAME) fops->NAME = afu_fops.NAME 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* Get a struct file and fd for a context and attach the ops */ 38862306a36Sopenharmony_cistruct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops, 38962306a36Sopenharmony_ci int *fd) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct file *file; 39262306a36Sopenharmony_ci int rc, flags, fdtmp; 39362306a36Sopenharmony_ci char *name = NULL; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* only allow one per context */ 39662306a36Sopenharmony_ci if (ctx->mapping) 39762306a36Sopenharmony_ci return ERR_PTR(-EEXIST); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci flags = O_RDWR | O_CLOEXEC; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* This code is similar to anon_inode_getfd() */ 40262306a36Sopenharmony_ci rc = get_unused_fd_flags(flags); 40362306a36Sopenharmony_ci if (rc < 0) 40462306a36Sopenharmony_ci return ERR_PTR(rc); 40562306a36Sopenharmony_ci fdtmp = rc; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* 40862306a36Sopenharmony_ci * Patch the file ops. Needs to be careful that this is rentrant safe. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci if (fops) { 41162306a36Sopenharmony_ci PATCH_FOPS(open); 41262306a36Sopenharmony_ci PATCH_FOPS(poll); 41362306a36Sopenharmony_ci PATCH_FOPS(read); 41462306a36Sopenharmony_ci PATCH_FOPS(release); 41562306a36Sopenharmony_ci PATCH_FOPS(unlocked_ioctl); 41662306a36Sopenharmony_ci PATCH_FOPS(compat_ioctl); 41762306a36Sopenharmony_ci PATCH_FOPS(mmap); 41862306a36Sopenharmony_ci } else /* use default ops */ 41962306a36Sopenharmony_ci fops = (struct file_operations *)&afu_fops; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "cxl:%d", ctx->pe); 42262306a36Sopenharmony_ci file = cxl_getfile(name, fops, ctx, flags); 42362306a36Sopenharmony_ci kfree(name); 42462306a36Sopenharmony_ci if (IS_ERR(file)) 42562306a36Sopenharmony_ci goto err_fd; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci cxl_context_set_mapping(ctx, file->f_mapping); 42862306a36Sopenharmony_ci *fd = fdtmp; 42962306a36Sopenharmony_ci return file; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cierr_fd: 43262306a36Sopenharmony_ci put_unused_fd(fdtmp); 43362306a36Sopenharmony_ci return NULL; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_get_fd); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistruct cxl_context *cxl_fops_get_context(struct file *file) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci return file->private_data; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_fops_get_context); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_civoid cxl_set_driver_ops(struct cxl_context *ctx, 44462306a36Sopenharmony_ci struct cxl_afu_driver_ops *ops) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci WARN_ON(!ops->fetch_event || !ops->event_delivered); 44762306a36Sopenharmony_ci atomic_set(&ctx->afu_driver_events, 0); 44862306a36Sopenharmony_ci ctx->afu_driver_ops = ops; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_set_driver_ops); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_civoid cxl_context_events_pending(struct cxl_context *ctx, 45362306a36Sopenharmony_ci unsigned int new_events) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci atomic_add(new_events, &ctx->afu_driver_events); 45662306a36Sopenharmony_ci wake_up_all(&ctx->wq); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_context_events_pending); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ciint cxl_start_work(struct cxl_context *ctx, 46162306a36Sopenharmony_ci struct cxl_ioctl_start_work *work) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci int rc; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* code taken from afu_ioctl_start_work */ 46662306a36Sopenharmony_ci if (!(work->flags & CXL_START_WORK_NUM_IRQS)) 46762306a36Sopenharmony_ci work->num_interrupts = ctx->afu->pp_irqs; 46862306a36Sopenharmony_ci else if ((work->num_interrupts < ctx->afu->pp_irqs) || 46962306a36Sopenharmony_ci (work->num_interrupts > ctx->afu->irqs_max)) { 47062306a36Sopenharmony_ci return -EINVAL; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci rc = afu_register_irqs(ctx, work->num_interrupts); 47462306a36Sopenharmony_ci if (rc) 47562306a36Sopenharmony_ci return rc; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci rc = cxl_start_context(ctx, work->work_element_descriptor, current); 47862306a36Sopenharmony_ci if (rc < 0) { 47962306a36Sopenharmony_ci afu_release_irqs(ctx, ctx); 48062306a36Sopenharmony_ci return rc; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return 0; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_start_work); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_civoid __iomem *cxl_psa_map(struct cxl_context *ctx) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci if (ctx->status != STARTED) 49062306a36Sopenharmony_ci return NULL; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci pr_devel("%s: psn_phys%llx size:%llx\n", 49362306a36Sopenharmony_ci __func__, ctx->psn_phys, ctx->psn_size); 49462306a36Sopenharmony_ci return ioremap(ctx->psn_phys, ctx->psn_size); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_psa_map); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_civoid cxl_psa_unmap(void __iomem *addr) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci iounmap(addr); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_psa_unmap); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ciint cxl_afu_reset(struct cxl_context *ctx) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct cxl_afu *afu = ctx->afu; 50762306a36Sopenharmony_ci int rc; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci rc = cxl_ops->afu_reset(afu); 51062306a36Sopenharmony_ci if (rc) 51162306a36Sopenharmony_ci return rc; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return cxl_ops->afu_check_and_enable(afu); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_afu_reset); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_civoid cxl_perst_reloads_same_image(struct cxl_afu *afu, 51862306a36Sopenharmony_ci bool perst_reloads_same_image) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci afu->adapter->perst_same_image = perst_reloads_same_image; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_perst_reloads_same_image); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cissize_t cxl_read_adapter_vpd(struct pci_dev *dev, void *buf, size_t count) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct cxl_afu *afu = cxl_pci_to_afu(dev); 52762306a36Sopenharmony_ci if (IS_ERR(afu)) 52862306a36Sopenharmony_ci return -ENODEV; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return cxl_ops->read_adapter_vpd(afu->adapter, buf, count); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_read_adapter_vpd); 533