18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright(c) 2020 Cornelis Networks, Inc. 38c2ecf20Sopenharmony_ci * Copyright(c) 2015-2020 Intel Corporation. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 68c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 118c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 128c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 158c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 168c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 178c2ecf20Sopenharmony_ci * General Public License for more details. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * BSD LICENSE 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 228c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 238c2ecf20Sopenharmony_ci * are met: 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above copyright 268c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 278c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above copyright 288c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 298c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 308c2ecf20Sopenharmony_ci * distribution. 318c2ecf20Sopenharmony_ci * - Neither the name of Intel Corporation nor the names of its 328c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 338c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 368c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 378c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 388c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 398c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 408c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 418c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 428c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 438c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 448c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 458c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci#include <linux/poll.h> 498c2ecf20Sopenharmony_ci#include <linux/cdev.h> 508c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 518c2ecf20Sopenharmony_ci#include <linux/io.h> 528c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 538c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#include <rdma/ib.h> 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#include "hfi.h" 588c2ecf20Sopenharmony_ci#include "pio.h" 598c2ecf20Sopenharmony_ci#include "device.h" 608c2ecf20Sopenharmony_ci#include "common.h" 618c2ecf20Sopenharmony_ci#include "trace.h" 628c2ecf20Sopenharmony_ci#include "mmu_rb.h" 638c2ecf20Sopenharmony_ci#include "user_sdma.h" 648c2ecf20Sopenharmony_ci#include "user_exp_rcv.h" 658c2ecf20Sopenharmony_ci#include "aspm.h" 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#undef pr_fmt 688c2ecf20Sopenharmony_ci#define pr_fmt(fmt) DRIVER_NAME ": " fmt 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define SEND_CTXT_HALT_TIMEOUT 1000 /* msecs */ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * File operation functions 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_cistatic int hfi1_file_open(struct inode *inode, struct file *fp); 768c2ecf20Sopenharmony_cistatic int hfi1_file_close(struct inode *inode, struct file *fp); 778c2ecf20Sopenharmony_cistatic ssize_t hfi1_write_iter(struct kiocb *kiocb, struct iov_iter *from); 788c2ecf20Sopenharmony_cistatic __poll_t hfi1_poll(struct file *fp, struct poll_table_struct *pt); 798c2ecf20Sopenharmony_cistatic int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic u64 kvirt_to_phys(void *addr); 828c2ecf20Sopenharmony_cistatic int assign_ctxt(struct hfi1_filedata *fd, unsigned long arg, u32 len); 838c2ecf20Sopenharmony_cistatic void init_subctxts(struct hfi1_ctxtdata *uctxt, 848c2ecf20Sopenharmony_ci const struct hfi1_user_info *uinfo); 858c2ecf20Sopenharmony_cistatic int init_user_ctxt(struct hfi1_filedata *fd, 868c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt); 878c2ecf20Sopenharmony_cistatic void user_init(struct hfi1_ctxtdata *uctxt); 888c2ecf20Sopenharmony_cistatic int get_ctxt_info(struct hfi1_filedata *fd, unsigned long arg, u32 len); 898c2ecf20Sopenharmony_cistatic int get_base_info(struct hfi1_filedata *fd, unsigned long arg, u32 len); 908c2ecf20Sopenharmony_cistatic int user_exp_rcv_setup(struct hfi1_filedata *fd, unsigned long arg, 918c2ecf20Sopenharmony_ci u32 len); 928c2ecf20Sopenharmony_cistatic int user_exp_rcv_clear(struct hfi1_filedata *fd, unsigned long arg, 938c2ecf20Sopenharmony_ci u32 len); 948c2ecf20Sopenharmony_cistatic int user_exp_rcv_invalid(struct hfi1_filedata *fd, unsigned long arg, 958c2ecf20Sopenharmony_ci u32 len); 968c2ecf20Sopenharmony_cistatic int setup_base_ctxt(struct hfi1_filedata *fd, 978c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt); 988c2ecf20Sopenharmony_cistatic int setup_subctxt(struct hfi1_ctxtdata *uctxt); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int find_sub_ctxt(struct hfi1_filedata *fd, 1018c2ecf20Sopenharmony_ci const struct hfi1_user_info *uinfo); 1028c2ecf20Sopenharmony_cistatic int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd, 1038c2ecf20Sopenharmony_ci struct hfi1_user_info *uinfo, 1048c2ecf20Sopenharmony_ci struct hfi1_ctxtdata **cd); 1058c2ecf20Sopenharmony_cistatic void deallocate_ctxt(struct hfi1_ctxtdata *uctxt); 1068c2ecf20Sopenharmony_cistatic __poll_t poll_urgent(struct file *fp, struct poll_table_struct *pt); 1078c2ecf20Sopenharmony_cistatic __poll_t poll_next(struct file *fp, struct poll_table_struct *pt); 1088c2ecf20Sopenharmony_cistatic int user_event_ack(struct hfi1_ctxtdata *uctxt, u16 subctxt, 1098c2ecf20Sopenharmony_ci unsigned long arg); 1108c2ecf20Sopenharmony_cistatic int set_ctxt_pkey(struct hfi1_ctxtdata *uctxt, unsigned long arg); 1118c2ecf20Sopenharmony_cistatic int ctxt_reset(struct hfi1_ctxtdata *uctxt); 1128c2ecf20Sopenharmony_cistatic int manage_rcvq(struct hfi1_ctxtdata *uctxt, u16 subctxt, 1138c2ecf20Sopenharmony_ci unsigned long arg); 1148c2ecf20Sopenharmony_cistatic vm_fault_t vma_fault(struct vm_fault *vmf); 1158c2ecf20Sopenharmony_cistatic long hfi1_file_ioctl(struct file *fp, unsigned int cmd, 1168c2ecf20Sopenharmony_ci unsigned long arg); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic const struct file_operations hfi1_file_ops = { 1198c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1208c2ecf20Sopenharmony_ci .write_iter = hfi1_write_iter, 1218c2ecf20Sopenharmony_ci .open = hfi1_file_open, 1228c2ecf20Sopenharmony_ci .release = hfi1_file_close, 1238c2ecf20Sopenharmony_ci .unlocked_ioctl = hfi1_file_ioctl, 1248c2ecf20Sopenharmony_ci .poll = hfi1_poll, 1258c2ecf20Sopenharmony_ci .mmap = hfi1_file_mmap, 1268c2ecf20Sopenharmony_ci .llseek = noop_llseek, 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic const struct vm_operations_struct vm_ops = { 1308c2ecf20Sopenharmony_ci .fault = vma_fault, 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* 1348c2ecf20Sopenharmony_ci * Types of memories mapped into user processes' space 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_cienum mmap_types { 1378c2ecf20Sopenharmony_ci PIO_BUFS = 1, 1388c2ecf20Sopenharmony_ci PIO_BUFS_SOP, 1398c2ecf20Sopenharmony_ci PIO_CRED, 1408c2ecf20Sopenharmony_ci RCV_HDRQ, 1418c2ecf20Sopenharmony_ci RCV_EGRBUF, 1428c2ecf20Sopenharmony_ci UREGS, 1438c2ecf20Sopenharmony_ci EVENTS, 1448c2ecf20Sopenharmony_ci STATUS, 1458c2ecf20Sopenharmony_ci RTAIL, 1468c2ecf20Sopenharmony_ci SUBCTXT_UREGS, 1478c2ecf20Sopenharmony_ci SUBCTXT_RCV_HDRQ, 1488c2ecf20Sopenharmony_ci SUBCTXT_EGRBUF, 1498c2ecf20Sopenharmony_ci SDMA_COMP 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* 1538c2ecf20Sopenharmony_ci * Masks and offsets defining the mmap tokens 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci#define HFI1_MMAP_OFFSET_MASK 0xfffULL 1568c2ecf20Sopenharmony_ci#define HFI1_MMAP_OFFSET_SHIFT 0 1578c2ecf20Sopenharmony_ci#define HFI1_MMAP_SUBCTXT_MASK 0xfULL 1588c2ecf20Sopenharmony_ci#define HFI1_MMAP_SUBCTXT_SHIFT 12 1598c2ecf20Sopenharmony_ci#define HFI1_MMAP_CTXT_MASK 0xffULL 1608c2ecf20Sopenharmony_ci#define HFI1_MMAP_CTXT_SHIFT 16 1618c2ecf20Sopenharmony_ci#define HFI1_MMAP_TYPE_MASK 0xfULL 1628c2ecf20Sopenharmony_ci#define HFI1_MMAP_TYPE_SHIFT 24 1638c2ecf20Sopenharmony_ci#define HFI1_MMAP_MAGIC_MASK 0xffffffffULL 1648c2ecf20Sopenharmony_ci#define HFI1_MMAP_MAGIC_SHIFT 32 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci#define HFI1_MMAP_MAGIC 0xdabbad00 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci#define HFI1_MMAP_TOKEN_SET(field, val) \ 1698c2ecf20Sopenharmony_ci (((val) & HFI1_MMAP_##field##_MASK) << HFI1_MMAP_##field##_SHIFT) 1708c2ecf20Sopenharmony_ci#define HFI1_MMAP_TOKEN_GET(field, token) \ 1718c2ecf20Sopenharmony_ci (((token) >> HFI1_MMAP_##field##_SHIFT) & HFI1_MMAP_##field##_MASK) 1728c2ecf20Sopenharmony_ci#define HFI1_MMAP_TOKEN(type, ctxt, subctxt, addr) \ 1738c2ecf20Sopenharmony_ci (HFI1_MMAP_TOKEN_SET(MAGIC, HFI1_MMAP_MAGIC) | \ 1748c2ecf20Sopenharmony_ci HFI1_MMAP_TOKEN_SET(TYPE, type) | \ 1758c2ecf20Sopenharmony_ci HFI1_MMAP_TOKEN_SET(CTXT, ctxt) | \ 1768c2ecf20Sopenharmony_ci HFI1_MMAP_TOKEN_SET(SUBCTXT, subctxt) | \ 1778c2ecf20Sopenharmony_ci HFI1_MMAP_TOKEN_SET(OFFSET, (offset_in_page(addr)))) 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#define dbg(fmt, ...) \ 1808c2ecf20Sopenharmony_ci pr_info(fmt, ##__VA_ARGS__) 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic inline int is_valid_mmap(u64 token) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci return (HFI1_MMAP_TOKEN_GET(MAGIC, token) == HFI1_MMAP_MAGIC); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int hfi1_file_open(struct inode *inode, struct file *fp) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct hfi1_filedata *fd; 1908c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = container_of(inode->i_cdev, 1918c2ecf20Sopenharmony_ci struct hfi1_devdata, 1928c2ecf20Sopenharmony_ci user_cdev); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (!((dd->flags & HFI1_PRESENT) && dd->kregbase1)) 1958c2ecf20Sopenharmony_ci return -EINVAL; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (!atomic_inc_not_zero(&dd->user_refcount)) 1988c2ecf20Sopenharmony_ci return -ENXIO; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* The real work is performed later in assign_ctxt() */ 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci fd = kzalloc(sizeof(*fd), GFP_KERNEL); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (!fd || init_srcu_struct(&fd->pq_srcu)) 2058c2ecf20Sopenharmony_ci goto nomem; 2068c2ecf20Sopenharmony_ci spin_lock_init(&fd->pq_rcu_lock); 2078c2ecf20Sopenharmony_ci spin_lock_init(&fd->tid_lock); 2088c2ecf20Sopenharmony_ci spin_lock_init(&fd->invalid_lock); 2098c2ecf20Sopenharmony_ci fd->rec_cpu_num = -1; /* no cpu affinity by default */ 2108c2ecf20Sopenharmony_ci fd->dd = dd; 2118c2ecf20Sopenharmony_ci fp->private_data = fd; 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_cinomem: 2148c2ecf20Sopenharmony_ci kfree(fd); 2158c2ecf20Sopenharmony_ci fp->private_data = NULL; 2168c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&dd->user_refcount)) 2178c2ecf20Sopenharmony_ci complete(&dd->user_comp); 2188c2ecf20Sopenharmony_ci return -ENOMEM; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic long hfi1_file_ioctl(struct file *fp, unsigned int cmd, 2228c2ecf20Sopenharmony_ci unsigned long arg) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct hfi1_filedata *fd = fp->private_data; 2258c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt = fd->uctxt; 2268c2ecf20Sopenharmony_ci int ret = 0; 2278c2ecf20Sopenharmony_ci int uval = 0; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci hfi1_cdbg(IOCTL, "IOCTL recv: 0x%x", cmd); 2308c2ecf20Sopenharmony_ci if (cmd != HFI1_IOCTL_ASSIGN_CTXT && 2318c2ecf20Sopenharmony_ci cmd != HFI1_IOCTL_GET_VERS && 2328c2ecf20Sopenharmony_ci !uctxt) 2338c2ecf20Sopenharmony_ci return -EINVAL; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci switch (cmd) { 2368c2ecf20Sopenharmony_ci case HFI1_IOCTL_ASSIGN_CTXT: 2378c2ecf20Sopenharmony_ci ret = assign_ctxt(fd, arg, _IOC_SIZE(cmd)); 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci case HFI1_IOCTL_CTXT_INFO: 2418c2ecf20Sopenharmony_ci ret = get_ctxt_info(fd, arg, _IOC_SIZE(cmd)); 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci case HFI1_IOCTL_USER_INFO: 2458c2ecf20Sopenharmony_ci ret = get_base_info(fd, arg, _IOC_SIZE(cmd)); 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci case HFI1_IOCTL_CREDIT_UPD: 2498c2ecf20Sopenharmony_ci if (uctxt) 2508c2ecf20Sopenharmony_ci sc_return_credits(uctxt->sc); 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci case HFI1_IOCTL_TID_UPDATE: 2548c2ecf20Sopenharmony_ci ret = user_exp_rcv_setup(fd, arg, _IOC_SIZE(cmd)); 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci case HFI1_IOCTL_TID_FREE: 2588c2ecf20Sopenharmony_ci ret = user_exp_rcv_clear(fd, arg, _IOC_SIZE(cmd)); 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci case HFI1_IOCTL_TID_INVAL_READ: 2628c2ecf20Sopenharmony_ci ret = user_exp_rcv_invalid(fd, arg, _IOC_SIZE(cmd)); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci case HFI1_IOCTL_RECV_CTRL: 2668c2ecf20Sopenharmony_ci ret = manage_rcvq(uctxt, fd->subctxt, arg); 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci case HFI1_IOCTL_POLL_TYPE: 2708c2ecf20Sopenharmony_ci if (get_user(uval, (int __user *)arg)) 2718c2ecf20Sopenharmony_ci return -EFAULT; 2728c2ecf20Sopenharmony_ci uctxt->poll_type = (typeof(uctxt->poll_type))uval; 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci case HFI1_IOCTL_ACK_EVENT: 2768c2ecf20Sopenharmony_ci ret = user_event_ack(uctxt, fd->subctxt, arg); 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci case HFI1_IOCTL_SET_PKEY: 2808c2ecf20Sopenharmony_ci ret = set_ctxt_pkey(uctxt, arg); 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci case HFI1_IOCTL_CTXT_RESET: 2848c2ecf20Sopenharmony_ci ret = ctxt_reset(uctxt); 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci case HFI1_IOCTL_GET_VERS: 2888c2ecf20Sopenharmony_ci uval = HFI1_USER_SWVERSION; 2898c2ecf20Sopenharmony_ci if (put_user(uval, (int __user *)arg)) 2908c2ecf20Sopenharmony_ci return -EFAULT; 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci default: 2948c2ecf20Sopenharmony_ci return -EINVAL; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return ret; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic ssize_t hfi1_write_iter(struct kiocb *kiocb, struct iov_iter *from) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct hfi1_filedata *fd = kiocb->ki_filp->private_data; 3038c2ecf20Sopenharmony_ci struct hfi1_user_sdma_pkt_q *pq; 3048c2ecf20Sopenharmony_ci struct hfi1_user_sdma_comp_q *cq = fd->cq; 3058c2ecf20Sopenharmony_ci int done = 0, reqs = 0; 3068c2ecf20Sopenharmony_ci unsigned long dim = from->nr_segs; 3078c2ecf20Sopenharmony_ci int idx; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (!HFI1_CAP_IS_KSET(SDMA)) 3108c2ecf20Sopenharmony_ci return -EINVAL; 3118c2ecf20Sopenharmony_ci idx = srcu_read_lock(&fd->pq_srcu); 3128c2ecf20Sopenharmony_ci pq = srcu_dereference(fd->pq, &fd->pq_srcu); 3138c2ecf20Sopenharmony_ci if (!cq || !pq) { 3148c2ecf20Sopenharmony_ci srcu_read_unlock(&fd->pq_srcu, idx); 3158c2ecf20Sopenharmony_ci return -EIO; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (!iter_is_iovec(from) || !dim) { 3198c2ecf20Sopenharmony_ci srcu_read_unlock(&fd->pq_srcu, idx); 3208c2ecf20Sopenharmony_ci return -EINVAL; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci trace_hfi1_sdma_request(fd->dd, fd->uctxt->ctxt, fd->subctxt, dim); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (atomic_read(&pq->n_reqs) == pq->n_max_reqs) { 3268c2ecf20Sopenharmony_ci srcu_read_unlock(&fd->pq_srcu, idx); 3278c2ecf20Sopenharmony_ci return -ENOSPC; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci while (dim) { 3318c2ecf20Sopenharmony_ci int ret; 3328c2ecf20Sopenharmony_ci unsigned long count = 0; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ret = hfi1_user_sdma_process_request( 3358c2ecf20Sopenharmony_ci fd, (struct iovec *)(from->iov + done), 3368c2ecf20Sopenharmony_ci dim, &count); 3378c2ecf20Sopenharmony_ci if (ret) { 3388c2ecf20Sopenharmony_ci reqs = ret; 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci dim -= count; 3428c2ecf20Sopenharmony_ci done += count; 3438c2ecf20Sopenharmony_ci reqs++; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci srcu_read_unlock(&fd->pq_srcu, idx); 3478c2ecf20Sopenharmony_ci return reqs; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct hfi1_filedata *fd = fp->private_data; 3538c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt = fd->uctxt; 3548c2ecf20Sopenharmony_ci struct hfi1_devdata *dd; 3558c2ecf20Sopenharmony_ci unsigned long flags; 3568c2ecf20Sopenharmony_ci u64 token = vma->vm_pgoff << PAGE_SHIFT, 3578c2ecf20Sopenharmony_ci memaddr = 0; 3588c2ecf20Sopenharmony_ci void *memvirt = NULL; 3598c2ecf20Sopenharmony_ci u8 subctxt, mapio = 0, vmf = 0, type; 3608c2ecf20Sopenharmony_ci ssize_t memlen = 0; 3618c2ecf20Sopenharmony_ci int ret = 0; 3628c2ecf20Sopenharmony_ci u16 ctxt; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (!is_valid_mmap(token) || !uctxt || 3658c2ecf20Sopenharmony_ci !(vma->vm_flags & VM_SHARED)) { 3668c2ecf20Sopenharmony_ci ret = -EINVAL; 3678c2ecf20Sopenharmony_ci goto done; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci dd = uctxt->dd; 3708c2ecf20Sopenharmony_ci ctxt = HFI1_MMAP_TOKEN_GET(CTXT, token); 3718c2ecf20Sopenharmony_ci subctxt = HFI1_MMAP_TOKEN_GET(SUBCTXT, token); 3728c2ecf20Sopenharmony_ci type = HFI1_MMAP_TOKEN_GET(TYPE, token); 3738c2ecf20Sopenharmony_ci if (ctxt != uctxt->ctxt || subctxt != fd->subctxt) { 3748c2ecf20Sopenharmony_ci ret = -EINVAL; 3758c2ecf20Sopenharmony_ci goto done; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci flags = vma->vm_flags; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci switch (type) { 3818c2ecf20Sopenharmony_ci case PIO_BUFS: 3828c2ecf20Sopenharmony_ci case PIO_BUFS_SOP: 3838c2ecf20Sopenharmony_ci memaddr = ((dd->physaddr + TXE_PIO_SEND) + 3848c2ecf20Sopenharmony_ci /* chip pio base */ 3858c2ecf20Sopenharmony_ci (uctxt->sc->hw_context * BIT(16))) + 3868c2ecf20Sopenharmony_ci /* 64K PIO space / ctxt */ 3878c2ecf20Sopenharmony_ci (type == PIO_BUFS_SOP ? 3888c2ecf20Sopenharmony_ci (TXE_PIO_SIZE / 2) : 0); /* sop? */ 3898c2ecf20Sopenharmony_ci /* 3908c2ecf20Sopenharmony_ci * Map only the amount allocated to the context, not the 3918c2ecf20Sopenharmony_ci * entire available context's PIO space. 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_ci memlen = PAGE_ALIGN(uctxt->sc->credits * PIO_BLOCK_SIZE); 3948c2ecf20Sopenharmony_ci flags &= ~VM_MAYREAD; 3958c2ecf20Sopenharmony_ci flags |= VM_DONTCOPY | VM_DONTEXPAND; 3968c2ecf20Sopenharmony_ci vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); 3978c2ecf20Sopenharmony_ci mapio = 1; 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci case PIO_CRED: 4008c2ecf20Sopenharmony_ci if (flags & VM_WRITE) { 4018c2ecf20Sopenharmony_ci ret = -EPERM; 4028c2ecf20Sopenharmony_ci goto done; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * The credit return location for this context could be on the 4068c2ecf20Sopenharmony_ci * second or third page allocated for credit returns (if number 4078c2ecf20Sopenharmony_ci * of enabled contexts > 64 and 128 respectively). 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci memvirt = dd->cr_base[uctxt->numa_id].va; 4108c2ecf20Sopenharmony_ci memaddr = virt_to_phys(memvirt) + 4118c2ecf20Sopenharmony_ci (((u64)uctxt->sc->hw_free - 4128c2ecf20Sopenharmony_ci (u64)dd->cr_base[uctxt->numa_id].va) & PAGE_MASK); 4138c2ecf20Sopenharmony_ci memlen = PAGE_SIZE; 4148c2ecf20Sopenharmony_ci flags &= ~VM_MAYWRITE; 4158c2ecf20Sopenharmony_ci flags |= VM_DONTCOPY | VM_DONTEXPAND; 4168c2ecf20Sopenharmony_ci /* 4178c2ecf20Sopenharmony_ci * The driver has already allocated memory for credit 4188c2ecf20Sopenharmony_ci * returns and programmed it into the chip. Has that 4198c2ecf20Sopenharmony_ci * memory been flagged as non-cached? 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_ci /* vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); */ 4228c2ecf20Sopenharmony_ci mapio = 1; 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci case RCV_HDRQ: 4258c2ecf20Sopenharmony_ci memlen = rcvhdrq_size(uctxt); 4268c2ecf20Sopenharmony_ci memvirt = uctxt->rcvhdrq; 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci case RCV_EGRBUF: { 4298c2ecf20Sopenharmony_ci unsigned long addr; 4308c2ecf20Sopenharmony_ci int i; 4318c2ecf20Sopenharmony_ci /* 4328c2ecf20Sopenharmony_ci * The RcvEgr buffer need to be handled differently 4338c2ecf20Sopenharmony_ci * as multiple non-contiguous pages need to be mapped 4348c2ecf20Sopenharmony_ci * into the user process. 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_ci memlen = uctxt->egrbufs.size; 4378c2ecf20Sopenharmony_ci if ((vma->vm_end - vma->vm_start) != memlen) { 4388c2ecf20Sopenharmony_ci dd_dev_err(dd, "Eager buffer map size invalid (%lu != %lu)\n", 4398c2ecf20Sopenharmony_ci (vma->vm_end - vma->vm_start), memlen); 4408c2ecf20Sopenharmony_ci ret = -EINVAL; 4418c2ecf20Sopenharmony_ci goto done; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_WRITE) { 4448c2ecf20Sopenharmony_ci ret = -EPERM; 4458c2ecf20Sopenharmony_ci goto done; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci vma->vm_flags &= ~VM_MAYWRITE; 4488c2ecf20Sopenharmony_ci addr = vma->vm_start; 4498c2ecf20Sopenharmony_ci for (i = 0 ; i < uctxt->egrbufs.numbufs; i++) { 4508c2ecf20Sopenharmony_ci memlen = uctxt->egrbufs.buffers[i].len; 4518c2ecf20Sopenharmony_ci memvirt = uctxt->egrbufs.buffers[i].addr; 4528c2ecf20Sopenharmony_ci ret = remap_pfn_range( 4538c2ecf20Sopenharmony_ci vma, addr, 4548c2ecf20Sopenharmony_ci /* 4558c2ecf20Sopenharmony_ci * virt_to_pfn() does the same, but 4568c2ecf20Sopenharmony_ci * it's not available on x86_64 4578c2ecf20Sopenharmony_ci * when CONFIG_MMU is enabled. 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_ci PFN_DOWN(__pa(memvirt)), 4608c2ecf20Sopenharmony_ci memlen, 4618c2ecf20Sopenharmony_ci vma->vm_page_prot); 4628c2ecf20Sopenharmony_ci if (ret < 0) 4638c2ecf20Sopenharmony_ci goto done; 4648c2ecf20Sopenharmony_ci addr += memlen; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci ret = 0; 4678c2ecf20Sopenharmony_ci goto done; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci case UREGS: 4708c2ecf20Sopenharmony_ci /* 4718c2ecf20Sopenharmony_ci * Map only the page that contains this context's user 4728c2ecf20Sopenharmony_ci * registers. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci memaddr = (unsigned long) 4758c2ecf20Sopenharmony_ci (dd->physaddr + RXE_PER_CONTEXT_USER) 4768c2ecf20Sopenharmony_ci + (uctxt->ctxt * RXE_PER_CONTEXT_SIZE); 4778c2ecf20Sopenharmony_ci /* 4788c2ecf20Sopenharmony_ci * TidFlow table is on the same page as the rest of the 4798c2ecf20Sopenharmony_ci * user registers. 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ci memlen = PAGE_SIZE; 4828c2ecf20Sopenharmony_ci flags |= VM_DONTCOPY | VM_DONTEXPAND; 4838c2ecf20Sopenharmony_ci vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 4848c2ecf20Sopenharmony_ci mapio = 1; 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci case EVENTS: 4878c2ecf20Sopenharmony_ci /* 4888c2ecf20Sopenharmony_ci * Use the page where this context's flags are. User level 4898c2ecf20Sopenharmony_ci * knows where it's own bitmap is within the page. 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_ci memaddr = (unsigned long) 4928c2ecf20Sopenharmony_ci (dd->events + uctxt_offset(uctxt)) & PAGE_MASK; 4938c2ecf20Sopenharmony_ci memlen = PAGE_SIZE; 4948c2ecf20Sopenharmony_ci /* 4958c2ecf20Sopenharmony_ci * v3.7 removes VM_RESERVED but the effect is kept by 4968c2ecf20Sopenharmony_ci * using VM_IO. 4978c2ecf20Sopenharmony_ci */ 4988c2ecf20Sopenharmony_ci flags |= VM_IO | VM_DONTEXPAND; 4998c2ecf20Sopenharmony_ci vmf = 1; 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci case STATUS: 5028c2ecf20Sopenharmony_ci if (flags & VM_WRITE) { 5038c2ecf20Sopenharmony_ci ret = -EPERM; 5048c2ecf20Sopenharmony_ci goto done; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci memaddr = kvirt_to_phys((void *)dd->status); 5078c2ecf20Sopenharmony_ci memlen = PAGE_SIZE; 5088c2ecf20Sopenharmony_ci flags |= VM_IO | VM_DONTEXPAND; 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci case RTAIL: 5118c2ecf20Sopenharmony_ci if (!HFI1_CAP_IS_USET(DMA_RTAIL)) { 5128c2ecf20Sopenharmony_ci /* 5138c2ecf20Sopenharmony_ci * If the memory allocation failed, the context alloc 5148c2ecf20Sopenharmony_ci * also would have failed, so we would never get here 5158c2ecf20Sopenharmony_ci */ 5168c2ecf20Sopenharmony_ci ret = -EINVAL; 5178c2ecf20Sopenharmony_ci goto done; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci if ((flags & VM_WRITE) || !hfi1_rcvhdrtail_kvaddr(uctxt)) { 5208c2ecf20Sopenharmony_ci ret = -EPERM; 5218c2ecf20Sopenharmony_ci goto done; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci memlen = PAGE_SIZE; 5248c2ecf20Sopenharmony_ci memvirt = (void *)hfi1_rcvhdrtail_kvaddr(uctxt); 5258c2ecf20Sopenharmony_ci flags &= ~VM_MAYWRITE; 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci case SUBCTXT_UREGS: 5288c2ecf20Sopenharmony_ci memaddr = (u64)uctxt->subctxt_uregbase; 5298c2ecf20Sopenharmony_ci memlen = PAGE_SIZE; 5308c2ecf20Sopenharmony_ci flags |= VM_IO | VM_DONTEXPAND; 5318c2ecf20Sopenharmony_ci vmf = 1; 5328c2ecf20Sopenharmony_ci break; 5338c2ecf20Sopenharmony_ci case SUBCTXT_RCV_HDRQ: 5348c2ecf20Sopenharmony_ci memaddr = (u64)uctxt->subctxt_rcvhdr_base; 5358c2ecf20Sopenharmony_ci memlen = rcvhdrq_size(uctxt) * uctxt->subctxt_cnt; 5368c2ecf20Sopenharmony_ci flags |= VM_IO | VM_DONTEXPAND; 5378c2ecf20Sopenharmony_ci vmf = 1; 5388c2ecf20Sopenharmony_ci break; 5398c2ecf20Sopenharmony_ci case SUBCTXT_EGRBUF: 5408c2ecf20Sopenharmony_ci memaddr = (u64)uctxt->subctxt_rcvegrbuf; 5418c2ecf20Sopenharmony_ci memlen = uctxt->egrbufs.size * uctxt->subctxt_cnt; 5428c2ecf20Sopenharmony_ci flags |= VM_IO | VM_DONTEXPAND; 5438c2ecf20Sopenharmony_ci flags &= ~VM_MAYWRITE; 5448c2ecf20Sopenharmony_ci vmf = 1; 5458c2ecf20Sopenharmony_ci break; 5468c2ecf20Sopenharmony_ci case SDMA_COMP: { 5478c2ecf20Sopenharmony_ci struct hfi1_user_sdma_comp_q *cq = fd->cq; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (!cq) { 5508c2ecf20Sopenharmony_ci ret = -EFAULT; 5518c2ecf20Sopenharmony_ci goto done; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci memaddr = (u64)cq->comps; 5548c2ecf20Sopenharmony_ci memlen = PAGE_ALIGN(sizeof(*cq->comps) * cq->nentries); 5558c2ecf20Sopenharmony_ci flags |= VM_IO | VM_DONTEXPAND; 5568c2ecf20Sopenharmony_ci vmf = 1; 5578c2ecf20Sopenharmony_ci break; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci default: 5608c2ecf20Sopenharmony_ci ret = -EINVAL; 5618c2ecf20Sopenharmony_ci break; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if ((vma->vm_end - vma->vm_start) != memlen) { 5658c2ecf20Sopenharmony_ci hfi1_cdbg(PROC, "%u:%u Memory size mismatch %lu:%lu", 5668c2ecf20Sopenharmony_ci uctxt->ctxt, fd->subctxt, 5678c2ecf20Sopenharmony_ci (vma->vm_end - vma->vm_start), memlen); 5688c2ecf20Sopenharmony_ci ret = -EINVAL; 5698c2ecf20Sopenharmony_ci goto done; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci vma->vm_flags = flags; 5738c2ecf20Sopenharmony_ci hfi1_cdbg(PROC, 5748c2ecf20Sopenharmony_ci "%u:%u type:%u io/vf:%d/%d, addr:0x%llx, len:%lu(%lu), flags:0x%lx\n", 5758c2ecf20Sopenharmony_ci ctxt, subctxt, type, mapio, vmf, memaddr, memlen, 5768c2ecf20Sopenharmony_ci vma->vm_end - vma->vm_start, vma->vm_flags); 5778c2ecf20Sopenharmony_ci if (vmf) { 5788c2ecf20Sopenharmony_ci vma->vm_pgoff = PFN_DOWN(memaddr); 5798c2ecf20Sopenharmony_ci vma->vm_ops = &vm_ops; 5808c2ecf20Sopenharmony_ci ret = 0; 5818c2ecf20Sopenharmony_ci } else if (mapio) { 5828c2ecf20Sopenharmony_ci ret = io_remap_pfn_range(vma, vma->vm_start, 5838c2ecf20Sopenharmony_ci PFN_DOWN(memaddr), 5848c2ecf20Sopenharmony_ci memlen, 5858c2ecf20Sopenharmony_ci vma->vm_page_prot); 5868c2ecf20Sopenharmony_ci } else if (memvirt) { 5878c2ecf20Sopenharmony_ci ret = remap_pfn_range(vma, vma->vm_start, 5888c2ecf20Sopenharmony_ci PFN_DOWN(__pa(memvirt)), 5898c2ecf20Sopenharmony_ci memlen, 5908c2ecf20Sopenharmony_ci vma->vm_page_prot); 5918c2ecf20Sopenharmony_ci } else { 5928c2ecf20Sopenharmony_ci ret = remap_pfn_range(vma, vma->vm_start, 5938c2ecf20Sopenharmony_ci PFN_DOWN(memaddr), 5948c2ecf20Sopenharmony_ci memlen, 5958c2ecf20Sopenharmony_ci vma->vm_page_prot); 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_cidone: 5988c2ecf20Sopenharmony_ci return ret; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci/* 6028c2ecf20Sopenharmony_ci * Local (non-chip) user memory is not mapped right away but as it is 6038c2ecf20Sopenharmony_ci * accessed by the user-level code. 6048c2ecf20Sopenharmony_ci */ 6058c2ecf20Sopenharmony_cistatic vm_fault_t vma_fault(struct vm_fault *vmf) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct page *page; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci page = vmalloc_to_page((void *)(vmf->pgoff << PAGE_SHIFT)); 6108c2ecf20Sopenharmony_ci if (!page) 6118c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci get_page(page); 6148c2ecf20Sopenharmony_ci vmf->page = page; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci return 0; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic __poll_t hfi1_poll(struct file *fp, struct poll_table_struct *pt) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt; 6228c2ecf20Sopenharmony_ci __poll_t pollflag; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci uctxt = ((struct hfi1_filedata *)fp->private_data)->uctxt; 6258c2ecf20Sopenharmony_ci if (!uctxt) 6268c2ecf20Sopenharmony_ci pollflag = EPOLLERR; 6278c2ecf20Sopenharmony_ci else if (uctxt->poll_type == HFI1_POLL_TYPE_URGENT) 6288c2ecf20Sopenharmony_ci pollflag = poll_urgent(fp, pt); 6298c2ecf20Sopenharmony_ci else if (uctxt->poll_type == HFI1_POLL_TYPE_ANYRCV) 6308c2ecf20Sopenharmony_ci pollflag = poll_next(fp, pt); 6318c2ecf20Sopenharmony_ci else /* invalid */ 6328c2ecf20Sopenharmony_ci pollflag = EPOLLERR; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci return pollflag; 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic int hfi1_file_close(struct inode *inode, struct file *fp) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci struct hfi1_filedata *fdata = fp->private_data; 6408c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt = fdata->uctxt; 6418c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = container_of(inode->i_cdev, 6428c2ecf20Sopenharmony_ci struct hfi1_devdata, 6438c2ecf20Sopenharmony_ci user_cdev); 6448c2ecf20Sopenharmony_ci unsigned long flags, *ev; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci fp->private_data = NULL; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (!uctxt) 6498c2ecf20Sopenharmony_ci goto done; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci hfi1_cdbg(PROC, "closing ctxt %u:%u", uctxt->ctxt, fdata->subctxt); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci flush_wc(); 6548c2ecf20Sopenharmony_ci /* drain user sdma queue */ 6558c2ecf20Sopenharmony_ci hfi1_user_sdma_free_queues(fdata, uctxt); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* release the cpu */ 6588c2ecf20Sopenharmony_ci hfi1_put_proc_affinity(fdata->rec_cpu_num); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* clean up rcv side */ 6618c2ecf20Sopenharmony_ci hfi1_user_exp_rcv_free(fdata); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* 6648c2ecf20Sopenharmony_ci * fdata->uctxt is used in the above cleanup. It is not ready to be 6658c2ecf20Sopenharmony_ci * removed until here. 6668c2ecf20Sopenharmony_ci */ 6678c2ecf20Sopenharmony_ci fdata->uctxt = NULL; 6688c2ecf20Sopenharmony_ci hfi1_rcd_put(uctxt); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* 6718c2ecf20Sopenharmony_ci * Clear any left over, unhandled events so the next process that 6728c2ecf20Sopenharmony_ci * gets this context doesn't get confused. 6738c2ecf20Sopenharmony_ci */ 6748c2ecf20Sopenharmony_ci ev = dd->events + uctxt_offset(uctxt) + fdata->subctxt; 6758c2ecf20Sopenharmony_ci *ev = 0; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci spin_lock_irqsave(&dd->uctxt_lock, flags); 6788c2ecf20Sopenharmony_ci __clear_bit(fdata->subctxt, uctxt->in_use_ctxts); 6798c2ecf20Sopenharmony_ci if (!bitmap_empty(uctxt->in_use_ctxts, HFI1_MAX_SHARED_CTXTS)) { 6808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dd->uctxt_lock, flags); 6818c2ecf20Sopenharmony_ci goto done; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dd->uctxt_lock, flags); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* 6868c2ecf20Sopenharmony_ci * Disable receive context and interrupt available, reset all 6878c2ecf20Sopenharmony_ci * RcvCtxtCtrl bits to default values. 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_ci hfi1_rcvctrl(dd, HFI1_RCVCTRL_CTXT_DIS | 6908c2ecf20Sopenharmony_ci HFI1_RCVCTRL_TIDFLOW_DIS | 6918c2ecf20Sopenharmony_ci HFI1_RCVCTRL_INTRAVAIL_DIS | 6928c2ecf20Sopenharmony_ci HFI1_RCVCTRL_TAILUPD_DIS | 6938c2ecf20Sopenharmony_ci HFI1_RCVCTRL_ONE_PKT_EGR_DIS | 6948c2ecf20Sopenharmony_ci HFI1_RCVCTRL_NO_RHQ_DROP_DIS | 6958c2ecf20Sopenharmony_ci HFI1_RCVCTRL_NO_EGR_DROP_DIS | 6968c2ecf20Sopenharmony_ci HFI1_RCVCTRL_URGENT_DIS, uctxt); 6978c2ecf20Sopenharmony_ci /* Clear the context's J_KEY */ 6988c2ecf20Sopenharmony_ci hfi1_clear_ctxt_jkey(dd, uctxt); 6998c2ecf20Sopenharmony_ci /* 7008c2ecf20Sopenharmony_ci * If a send context is allocated, reset context integrity 7018c2ecf20Sopenharmony_ci * checks to default and disable the send context. 7028c2ecf20Sopenharmony_ci */ 7038c2ecf20Sopenharmony_ci if (uctxt->sc) { 7048c2ecf20Sopenharmony_ci sc_disable(uctxt->sc); 7058c2ecf20Sopenharmony_ci set_pio_integrity(uctxt->sc); 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci hfi1_free_ctxt_rcv_groups(uctxt); 7098c2ecf20Sopenharmony_ci hfi1_clear_ctxt_pkey(dd, uctxt); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci uctxt->event_flags = 0; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci deallocate_ctxt(uctxt); 7148c2ecf20Sopenharmony_cidone: 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&dd->user_refcount)) 7178c2ecf20Sopenharmony_ci complete(&dd->user_comp); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci cleanup_srcu_struct(&fdata->pq_srcu); 7208c2ecf20Sopenharmony_ci kfree(fdata); 7218c2ecf20Sopenharmony_ci return 0; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci/* 7258c2ecf20Sopenharmony_ci * Convert kernel *virtual* addresses to physical addresses. 7268c2ecf20Sopenharmony_ci * This is used to vmalloc'ed addresses. 7278c2ecf20Sopenharmony_ci */ 7288c2ecf20Sopenharmony_cistatic u64 kvirt_to_phys(void *addr) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci struct page *page; 7318c2ecf20Sopenharmony_ci u64 paddr = 0; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci page = vmalloc_to_page(addr); 7348c2ecf20Sopenharmony_ci if (page) 7358c2ecf20Sopenharmony_ci paddr = page_to_pfn(page) << PAGE_SHIFT; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci return paddr; 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci/** 7418c2ecf20Sopenharmony_ci * complete_subctxt 7428c2ecf20Sopenharmony_ci * @fd: valid filedata pointer 7438c2ecf20Sopenharmony_ci * 7448c2ecf20Sopenharmony_ci * Sub-context info can only be set up after the base context 7458c2ecf20Sopenharmony_ci * has been completed. This is indicated by the clearing of the 7468c2ecf20Sopenharmony_ci * HFI1_CTXT_BASE_UINIT bit. 7478c2ecf20Sopenharmony_ci * 7488c2ecf20Sopenharmony_ci * Wait for the bit to be cleared, and then complete the subcontext 7498c2ecf20Sopenharmony_ci * initialization. 7508c2ecf20Sopenharmony_ci * 7518c2ecf20Sopenharmony_ci */ 7528c2ecf20Sopenharmony_cistatic int complete_subctxt(struct hfi1_filedata *fd) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci int ret; 7558c2ecf20Sopenharmony_ci unsigned long flags; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci /* 7588c2ecf20Sopenharmony_ci * sub-context info can only be set up after the base context 7598c2ecf20Sopenharmony_ci * has been completed. 7608c2ecf20Sopenharmony_ci */ 7618c2ecf20Sopenharmony_ci ret = wait_event_interruptible( 7628c2ecf20Sopenharmony_ci fd->uctxt->wait, 7638c2ecf20Sopenharmony_ci !test_bit(HFI1_CTXT_BASE_UNINIT, &fd->uctxt->event_flags)); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci if (test_bit(HFI1_CTXT_BASE_FAILED, &fd->uctxt->event_flags)) 7668c2ecf20Sopenharmony_ci ret = -ENOMEM; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci /* Finish the sub-context init */ 7698c2ecf20Sopenharmony_ci if (!ret) { 7708c2ecf20Sopenharmony_ci fd->rec_cpu_num = hfi1_get_proc_affinity(fd->uctxt->numa_id); 7718c2ecf20Sopenharmony_ci ret = init_user_ctxt(fd, fd->uctxt); 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci if (ret) { 7758c2ecf20Sopenharmony_ci spin_lock_irqsave(&fd->dd->uctxt_lock, flags); 7768c2ecf20Sopenharmony_ci __clear_bit(fd->subctxt, fd->uctxt->in_use_ctxts); 7778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fd->dd->uctxt_lock, flags); 7788c2ecf20Sopenharmony_ci hfi1_rcd_put(fd->uctxt); 7798c2ecf20Sopenharmony_ci fd->uctxt = NULL; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci return ret; 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic int assign_ctxt(struct hfi1_filedata *fd, unsigned long arg, u32 len) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci int ret; 7888c2ecf20Sopenharmony_ci unsigned int swmajor; 7898c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt = NULL; 7908c2ecf20Sopenharmony_ci struct hfi1_user_info uinfo; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci if (fd->uctxt) 7938c2ecf20Sopenharmony_ci return -EINVAL; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (sizeof(uinfo) != len) 7968c2ecf20Sopenharmony_ci return -EINVAL; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (copy_from_user(&uinfo, (void __user *)arg, sizeof(uinfo))) 7998c2ecf20Sopenharmony_ci return -EFAULT; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci swmajor = uinfo.userversion >> 16; 8028c2ecf20Sopenharmony_ci if (swmajor != HFI1_USER_SWMAJOR) 8038c2ecf20Sopenharmony_ci return -ENODEV; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (uinfo.subctxt_cnt > HFI1_MAX_SHARED_CTXTS) 8068c2ecf20Sopenharmony_ci return -EINVAL; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* 8098c2ecf20Sopenharmony_ci * Acquire the mutex to protect against multiple creations of what 8108c2ecf20Sopenharmony_ci * could be a shared base context. 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_ci mutex_lock(&hfi1_mutex); 8138c2ecf20Sopenharmony_ci /* 8148c2ecf20Sopenharmony_ci * Get a sub context if available (fd->uctxt will be set). 8158c2ecf20Sopenharmony_ci * ret < 0 error, 0 no context, 1 sub-context found 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_ci ret = find_sub_ctxt(fd, &uinfo); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci /* 8208c2ecf20Sopenharmony_ci * Allocate a base context if context sharing is not required or a 8218c2ecf20Sopenharmony_ci * sub context wasn't found. 8228c2ecf20Sopenharmony_ci */ 8238c2ecf20Sopenharmony_ci if (!ret) 8248c2ecf20Sopenharmony_ci ret = allocate_ctxt(fd, fd->dd, &uinfo, &uctxt); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci mutex_unlock(&hfi1_mutex); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* Depending on the context type, finish the appropriate init */ 8298c2ecf20Sopenharmony_ci switch (ret) { 8308c2ecf20Sopenharmony_ci case 0: 8318c2ecf20Sopenharmony_ci ret = setup_base_ctxt(fd, uctxt); 8328c2ecf20Sopenharmony_ci if (ret) 8338c2ecf20Sopenharmony_ci deallocate_ctxt(uctxt); 8348c2ecf20Sopenharmony_ci break; 8358c2ecf20Sopenharmony_ci case 1: 8368c2ecf20Sopenharmony_ci ret = complete_subctxt(fd); 8378c2ecf20Sopenharmony_ci break; 8388c2ecf20Sopenharmony_ci default: 8398c2ecf20Sopenharmony_ci break; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci return ret; 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci/** 8468c2ecf20Sopenharmony_ci * match_ctxt 8478c2ecf20Sopenharmony_ci * @fd: valid filedata pointer 8488c2ecf20Sopenharmony_ci * @uinfo: user info to compare base context with 8498c2ecf20Sopenharmony_ci * @uctxt: context to compare uinfo to. 8508c2ecf20Sopenharmony_ci * 8518c2ecf20Sopenharmony_ci * Compare the given context with the given information to see if it 8528c2ecf20Sopenharmony_ci * can be used for a sub context. 8538c2ecf20Sopenharmony_ci */ 8548c2ecf20Sopenharmony_cistatic int match_ctxt(struct hfi1_filedata *fd, 8558c2ecf20Sopenharmony_ci const struct hfi1_user_info *uinfo, 8568c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = fd->dd; 8598c2ecf20Sopenharmony_ci unsigned long flags; 8608c2ecf20Sopenharmony_ci u16 subctxt; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* Skip dynamically allocated kernel contexts */ 8638c2ecf20Sopenharmony_ci if (uctxt->sc && (uctxt->sc->type == SC_KERNEL)) 8648c2ecf20Sopenharmony_ci return 0; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci /* Skip ctxt if it doesn't match the requested one */ 8678c2ecf20Sopenharmony_ci if (memcmp(uctxt->uuid, uinfo->uuid, sizeof(uctxt->uuid)) || 8688c2ecf20Sopenharmony_ci uctxt->jkey != generate_jkey(current_uid()) || 8698c2ecf20Sopenharmony_ci uctxt->subctxt_id != uinfo->subctxt_id || 8708c2ecf20Sopenharmony_ci uctxt->subctxt_cnt != uinfo->subctxt_cnt) 8718c2ecf20Sopenharmony_ci return 0; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* Verify the sharing process matches the base */ 8748c2ecf20Sopenharmony_ci if (uctxt->userversion != uinfo->userversion) 8758c2ecf20Sopenharmony_ci return -EINVAL; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci /* Find an unused sub context */ 8788c2ecf20Sopenharmony_ci spin_lock_irqsave(&dd->uctxt_lock, flags); 8798c2ecf20Sopenharmony_ci if (bitmap_empty(uctxt->in_use_ctxts, HFI1_MAX_SHARED_CTXTS)) { 8808c2ecf20Sopenharmony_ci /* context is being closed, do not use */ 8818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dd->uctxt_lock, flags); 8828c2ecf20Sopenharmony_ci return 0; 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci subctxt = find_first_zero_bit(uctxt->in_use_ctxts, 8868c2ecf20Sopenharmony_ci HFI1_MAX_SHARED_CTXTS); 8878c2ecf20Sopenharmony_ci if (subctxt >= uctxt->subctxt_cnt) { 8888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dd->uctxt_lock, flags); 8898c2ecf20Sopenharmony_ci return -EBUSY; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci fd->subctxt = subctxt; 8938c2ecf20Sopenharmony_ci __set_bit(fd->subctxt, uctxt->in_use_ctxts); 8948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dd->uctxt_lock, flags); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci fd->uctxt = uctxt; 8978c2ecf20Sopenharmony_ci hfi1_rcd_get(uctxt); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return 1; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci/** 9038c2ecf20Sopenharmony_ci * find_sub_ctxt 9048c2ecf20Sopenharmony_ci * @fd: valid filedata pointer 9058c2ecf20Sopenharmony_ci * @uinfo: matching info to use to find a possible context to share. 9068c2ecf20Sopenharmony_ci * 9078c2ecf20Sopenharmony_ci * The hfi1_mutex must be held when this function is called. It is 9088c2ecf20Sopenharmony_ci * necessary to ensure serialized creation of shared contexts. 9098c2ecf20Sopenharmony_ci * 9108c2ecf20Sopenharmony_ci * Return: 9118c2ecf20Sopenharmony_ci * 0 No sub-context found 9128c2ecf20Sopenharmony_ci * 1 Subcontext found and allocated 9138c2ecf20Sopenharmony_ci * errno EINVAL (incorrect parameters) 9148c2ecf20Sopenharmony_ci * EBUSY (all sub contexts in use) 9158c2ecf20Sopenharmony_ci */ 9168c2ecf20Sopenharmony_cistatic int find_sub_ctxt(struct hfi1_filedata *fd, 9178c2ecf20Sopenharmony_ci const struct hfi1_user_info *uinfo) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt; 9208c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = fd->dd; 9218c2ecf20Sopenharmony_ci u16 i; 9228c2ecf20Sopenharmony_ci int ret; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (!uinfo->subctxt_cnt) 9258c2ecf20Sopenharmony_ci return 0; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci for (i = dd->first_dyn_alloc_ctxt; i < dd->num_rcv_contexts; i++) { 9288c2ecf20Sopenharmony_ci uctxt = hfi1_rcd_get_by_index(dd, i); 9298c2ecf20Sopenharmony_ci if (uctxt) { 9308c2ecf20Sopenharmony_ci ret = match_ctxt(fd, uinfo, uctxt); 9318c2ecf20Sopenharmony_ci hfi1_rcd_put(uctxt); 9328c2ecf20Sopenharmony_ci /* value of != 0 will return */ 9338c2ecf20Sopenharmony_ci if (ret) 9348c2ecf20Sopenharmony_ci return ret; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci return 0; 9398c2ecf20Sopenharmony_ci} 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_cistatic int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd, 9428c2ecf20Sopenharmony_ci struct hfi1_user_info *uinfo, 9438c2ecf20Sopenharmony_ci struct hfi1_ctxtdata **rcd) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt; 9468c2ecf20Sopenharmony_ci int ret, numa; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if (dd->flags & HFI1_FROZEN) { 9498c2ecf20Sopenharmony_ci /* 9508c2ecf20Sopenharmony_ci * Pick an error that is unique from all other errors 9518c2ecf20Sopenharmony_ci * that are returned so the user process knows that 9528c2ecf20Sopenharmony_ci * it tried to allocate while the SPC was frozen. It 9538c2ecf20Sopenharmony_ci * it should be able to retry with success in a short 9548c2ecf20Sopenharmony_ci * while. 9558c2ecf20Sopenharmony_ci */ 9568c2ecf20Sopenharmony_ci return -EIO; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (!dd->freectxts) 9608c2ecf20Sopenharmony_ci return -EBUSY; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* 9638c2ecf20Sopenharmony_ci * If we don't have a NUMA node requested, preference is towards 9648c2ecf20Sopenharmony_ci * device NUMA node. 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_ci fd->rec_cpu_num = hfi1_get_proc_affinity(dd->node); 9678c2ecf20Sopenharmony_ci if (fd->rec_cpu_num != -1) 9688c2ecf20Sopenharmony_ci numa = cpu_to_node(fd->rec_cpu_num); 9698c2ecf20Sopenharmony_ci else 9708c2ecf20Sopenharmony_ci numa = numa_node_id(); 9718c2ecf20Sopenharmony_ci ret = hfi1_create_ctxtdata(dd->pport, numa, &uctxt); 9728c2ecf20Sopenharmony_ci if (ret < 0) { 9738c2ecf20Sopenharmony_ci dd_dev_err(dd, "user ctxtdata allocation failed\n"); 9748c2ecf20Sopenharmony_ci return ret; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci hfi1_cdbg(PROC, "[%u:%u] pid %u assigned to CPU %d (NUMA %u)", 9778c2ecf20Sopenharmony_ci uctxt->ctxt, fd->subctxt, current->pid, fd->rec_cpu_num, 9788c2ecf20Sopenharmony_ci uctxt->numa_id); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* 9818c2ecf20Sopenharmony_ci * Allocate and enable a PIO send context. 9828c2ecf20Sopenharmony_ci */ 9838c2ecf20Sopenharmony_ci uctxt->sc = sc_alloc(dd, SC_USER, uctxt->rcvhdrqentsize, dd->node); 9848c2ecf20Sopenharmony_ci if (!uctxt->sc) { 9858c2ecf20Sopenharmony_ci ret = -ENOMEM; 9868c2ecf20Sopenharmony_ci goto ctxdata_free; 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci hfi1_cdbg(PROC, "allocated send context %u(%u)\n", uctxt->sc->sw_index, 9898c2ecf20Sopenharmony_ci uctxt->sc->hw_context); 9908c2ecf20Sopenharmony_ci ret = sc_enable(uctxt->sc); 9918c2ecf20Sopenharmony_ci if (ret) 9928c2ecf20Sopenharmony_ci goto ctxdata_free; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci /* 9958c2ecf20Sopenharmony_ci * Setup sub context information if the user-level has requested 9968c2ecf20Sopenharmony_ci * sub contexts. 9978c2ecf20Sopenharmony_ci * This has to be done here so the rest of the sub-contexts find the 9988c2ecf20Sopenharmony_ci * proper base context. 9998c2ecf20Sopenharmony_ci * NOTE: _set_bit() can be used here because the context creation is 10008c2ecf20Sopenharmony_ci * protected by the mutex (rather than the spin_lock), and will be the 10018c2ecf20Sopenharmony_ci * very first instance of this context. 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_ci __set_bit(0, uctxt->in_use_ctxts); 10048c2ecf20Sopenharmony_ci if (uinfo->subctxt_cnt) 10058c2ecf20Sopenharmony_ci init_subctxts(uctxt, uinfo); 10068c2ecf20Sopenharmony_ci uctxt->userversion = uinfo->userversion; 10078c2ecf20Sopenharmony_ci uctxt->flags = hfi1_cap_mask; /* save current flag state */ 10088c2ecf20Sopenharmony_ci init_waitqueue_head(&uctxt->wait); 10098c2ecf20Sopenharmony_ci strlcpy(uctxt->comm, current->comm, sizeof(uctxt->comm)); 10108c2ecf20Sopenharmony_ci memcpy(uctxt->uuid, uinfo->uuid, sizeof(uctxt->uuid)); 10118c2ecf20Sopenharmony_ci uctxt->jkey = generate_jkey(current_uid()); 10128c2ecf20Sopenharmony_ci hfi1_stats.sps_ctxts++; 10138c2ecf20Sopenharmony_ci /* 10148c2ecf20Sopenharmony_ci * Disable ASPM when there are open user/PSM contexts to avoid 10158c2ecf20Sopenharmony_ci * issues with ASPM L1 exit latency 10168c2ecf20Sopenharmony_ci */ 10178c2ecf20Sopenharmony_ci if (dd->freectxts-- == dd->num_user_contexts) 10188c2ecf20Sopenharmony_ci aspm_disable_all(dd); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci *rcd = uctxt; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci return 0; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_cictxdata_free: 10258c2ecf20Sopenharmony_ci hfi1_free_ctxt(uctxt); 10268c2ecf20Sopenharmony_ci return ret; 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cistatic void deallocate_ctxt(struct hfi1_ctxtdata *uctxt) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci mutex_lock(&hfi1_mutex); 10328c2ecf20Sopenharmony_ci hfi1_stats.sps_ctxts--; 10338c2ecf20Sopenharmony_ci if (++uctxt->dd->freectxts == uctxt->dd->num_user_contexts) 10348c2ecf20Sopenharmony_ci aspm_enable_all(uctxt->dd); 10358c2ecf20Sopenharmony_ci mutex_unlock(&hfi1_mutex); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci hfi1_free_ctxt(uctxt); 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cistatic void init_subctxts(struct hfi1_ctxtdata *uctxt, 10418c2ecf20Sopenharmony_ci const struct hfi1_user_info *uinfo) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci uctxt->subctxt_cnt = uinfo->subctxt_cnt; 10448c2ecf20Sopenharmony_ci uctxt->subctxt_id = uinfo->subctxt_id; 10458c2ecf20Sopenharmony_ci set_bit(HFI1_CTXT_BASE_UNINIT, &uctxt->event_flags); 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_cistatic int setup_subctxt(struct hfi1_ctxtdata *uctxt) 10498c2ecf20Sopenharmony_ci{ 10508c2ecf20Sopenharmony_ci int ret = 0; 10518c2ecf20Sopenharmony_ci u16 num_subctxts = uctxt->subctxt_cnt; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci uctxt->subctxt_uregbase = vmalloc_user(PAGE_SIZE); 10548c2ecf20Sopenharmony_ci if (!uctxt->subctxt_uregbase) 10558c2ecf20Sopenharmony_ci return -ENOMEM; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* We can take the size of the RcvHdr Queue from the master */ 10588c2ecf20Sopenharmony_ci uctxt->subctxt_rcvhdr_base = vmalloc_user(rcvhdrq_size(uctxt) * 10598c2ecf20Sopenharmony_ci num_subctxts); 10608c2ecf20Sopenharmony_ci if (!uctxt->subctxt_rcvhdr_base) { 10618c2ecf20Sopenharmony_ci ret = -ENOMEM; 10628c2ecf20Sopenharmony_ci goto bail_ureg; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci uctxt->subctxt_rcvegrbuf = vmalloc_user(uctxt->egrbufs.size * 10668c2ecf20Sopenharmony_ci num_subctxts); 10678c2ecf20Sopenharmony_ci if (!uctxt->subctxt_rcvegrbuf) { 10688c2ecf20Sopenharmony_ci ret = -ENOMEM; 10698c2ecf20Sopenharmony_ci goto bail_rhdr; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci return 0; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_cibail_rhdr: 10758c2ecf20Sopenharmony_ci vfree(uctxt->subctxt_rcvhdr_base); 10768c2ecf20Sopenharmony_ci uctxt->subctxt_rcvhdr_base = NULL; 10778c2ecf20Sopenharmony_cibail_ureg: 10788c2ecf20Sopenharmony_ci vfree(uctxt->subctxt_uregbase); 10798c2ecf20Sopenharmony_ci uctxt->subctxt_uregbase = NULL; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci return ret; 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistatic void user_init(struct hfi1_ctxtdata *uctxt) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci unsigned int rcvctrl_ops = 0; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* initialize poll variables... */ 10898c2ecf20Sopenharmony_ci uctxt->urgent = 0; 10908c2ecf20Sopenharmony_ci uctxt->urgent_poll = 0; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci /* 10938c2ecf20Sopenharmony_ci * Now enable the ctxt for receive. 10948c2ecf20Sopenharmony_ci * For chips that are set to DMA the tail register to memory 10958c2ecf20Sopenharmony_ci * when they change (and when the update bit transitions from 10968c2ecf20Sopenharmony_ci * 0 to 1. So for those chips, we turn it off and then back on. 10978c2ecf20Sopenharmony_ci * This will (very briefly) affect any other open ctxts, but the 10988c2ecf20Sopenharmony_ci * duration is very short, and therefore isn't an issue. We 10998c2ecf20Sopenharmony_ci * explicitly set the in-memory tail copy to 0 beforehand, so we 11008c2ecf20Sopenharmony_ci * don't have to wait to be sure the DMA update has happened 11018c2ecf20Sopenharmony_ci * (chip resets head/tail to 0 on transition to enable). 11028c2ecf20Sopenharmony_ci */ 11038c2ecf20Sopenharmony_ci if (hfi1_rcvhdrtail_kvaddr(uctxt)) 11048c2ecf20Sopenharmony_ci clear_rcvhdrtail(uctxt); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci /* Setup J_KEY before enabling the context */ 11078c2ecf20Sopenharmony_ci hfi1_set_ctxt_jkey(uctxt->dd, uctxt, uctxt->jkey); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci rcvctrl_ops = HFI1_RCVCTRL_CTXT_ENB; 11108c2ecf20Sopenharmony_ci rcvctrl_ops |= HFI1_RCVCTRL_URGENT_ENB; 11118c2ecf20Sopenharmony_ci if (HFI1_CAP_UGET_MASK(uctxt->flags, HDRSUPP)) 11128c2ecf20Sopenharmony_ci rcvctrl_ops |= HFI1_RCVCTRL_TIDFLOW_ENB; 11138c2ecf20Sopenharmony_ci /* 11148c2ecf20Sopenharmony_ci * Ignore the bit in the flags for now until proper 11158c2ecf20Sopenharmony_ci * support for multiple packet per rcv array entry is 11168c2ecf20Sopenharmony_ci * added. 11178c2ecf20Sopenharmony_ci */ 11188c2ecf20Sopenharmony_ci if (!HFI1_CAP_UGET_MASK(uctxt->flags, MULTI_PKT_EGR)) 11198c2ecf20Sopenharmony_ci rcvctrl_ops |= HFI1_RCVCTRL_ONE_PKT_EGR_ENB; 11208c2ecf20Sopenharmony_ci if (HFI1_CAP_UGET_MASK(uctxt->flags, NODROP_EGR_FULL)) 11218c2ecf20Sopenharmony_ci rcvctrl_ops |= HFI1_RCVCTRL_NO_EGR_DROP_ENB; 11228c2ecf20Sopenharmony_ci if (HFI1_CAP_UGET_MASK(uctxt->flags, NODROP_RHQ_FULL)) 11238c2ecf20Sopenharmony_ci rcvctrl_ops |= HFI1_RCVCTRL_NO_RHQ_DROP_ENB; 11248c2ecf20Sopenharmony_ci /* 11258c2ecf20Sopenharmony_ci * The RcvCtxtCtrl.TailUpd bit has to be explicitly written. 11268c2ecf20Sopenharmony_ci * We can't rely on the correct value to be set from prior 11278c2ecf20Sopenharmony_ci * uses of the chip or ctxt. Therefore, add the rcvctrl op 11288c2ecf20Sopenharmony_ci * for both cases. 11298c2ecf20Sopenharmony_ci */ 11308c2ecf20Sopenharmony_ci if (HFI1_CAP_UGET_MASK(uctxt->flags, DMA_RTAIL)) 11318c2ecf20Sopenharmony_ci rcvctrl_ops |= HFI1_RCVCTRL_TAILUPD_ENB; 11328c2ecf20Sopenharmony_ci else 11338c2ecf20Sopenharmony_ci rcvctrl_ops |= HFI1_RCVCTRL_TAILUPD_DIS; 11348c2ecf20Sopenharmony_ci hfi1_rcvctrl(uctxt->dd, rcvctrl_ops, uctxt); 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_cistatic int get_ctxt_info(struct hfi1_filedata *fd, unsigned long arg, u32 len) 11388c2ecf20Sopenharmony_ci{ 11398c2ecf20Sopenharmony_ci struct hfi1_ctxt_info cinfo; 11408c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt = fd->uctxt; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci if (sizeof(cinfo) != len) 11438c2ecf20Sopenharmony_ci return -EINVAL; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci memset(&cinfo, 0, sizeof(cinfo)); 11468c2ecf20Sopenharmony_ci cinfo.runtime_flags = (((uctxt->flags >> HFI1_CAP_MISC_SHIFT) & 11478c2ecf20Sopenharmony_ci HFI1_CAP_MISC_MASK) << HFI1_CAP_USER_SHIFT) | 11488c2ecf20Sopenharmony_ci HFI1_CAP_UGET_MASK(uctxt->flags, MASK) | 11498c2ecf20Sopenharmony_ci HFI1_CAP_KGET_MASK(uctxt->flags, K2U); 11508c2ecf20Sopenharmony_ci /* adjust flag if this fd is not able to cache */ 11518c2ecf20Sopenharmony_ci if (!fd->use_mn) 11528c2ecf20Sopenharmony_ci cinfo.runtime_flags |= HFI1_CAP_TID_UNMAP; /* no caching */ 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci cinfo.num_active = hfi1_count_active_units(); 11558c2ecf20Sopenharmony_ci cinfo.unit = uctxt->dd->unit; 11568c2ecf20Sopenharmony_ci cinfo.ctxt = uctxt->ctxt; 11578c2ecf20Sopenharmony_ci cinfo.subctxt = fd->subctxt; 11588c2ecf20Sopenharmony_ci cinfo.rcvtids = roundup(uctxt->egrbufs.alloced, 11598c2ecf20Sopenharmony_ci uctxt->dd->rcv_entries.group_size) + 11608c2ecf20Sopenharmony_ci uctxt->expected_count; 11618c2ecf20Sopenharmony_ci cinfo.credits = uctxt->sc->credits; 11628c2ecf20Sopenharmony_ci cinfo.numa_node = uctxt->numa_id; 11638c2ecf20Sopenharmony_ci cinfo.rec_cpu = fd->rec_cpu_num; 11648c2ecf20Sopenharmony_ci cinfo.send_ctxt = uctxt->sc->hw_context; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci cinfo.egrtids = uctxt->egrbufs.alloced; 11678c2ecf20Sopenharmony_ci cinfo.rcvhdrq_cnt = get_hdrq_cnt(uctxt); 11688c2ecf20Sopenharmony_ci cinfo.rcvhdrq_entsize = get_hdrqentsize(uctxt) << 2; 11698c2ecf20Sopenharmony_ci cinfo.sdma_ring_size = fd->cq->nentries; 11708c2ecf20Sopenharmony_ci cinfo.rcvegr_size = uctxt->egrbufs.rcvtid_size; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci trace_hfi1_ctxt_info(uctxt->dd, uctxt->ctxt, fd->subctxt, &cinfo); 11738c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &cinfo, len)) 11748c2ecf20Sopenharmony_ci return -EFAULT; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci return 0; 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cistatic int init_user_ctxt(struct hfi1_filedata *fd, 11808c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci int ret; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci ret = hfi1_user_sdma_alloc_queues(uctxt, fd); 11858c2ecf20Sopenharmony_ci if (ret) 11868c2ecf20Sopenharmony_ci return ret; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci ret = hfi1_user_exp_rcv_init(fd, uctxt); 11898c2ecf20Sopenharmony_ci if (ret) 11908c2ecf20Sopenharmony_ci hfi1_user_sdma_free_queues(fd, uctxt); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci return ret; 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic int setup_base_ctxt(struct hfi1_filedata *fd, 11968c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = uctxt->dd; 11998c2ecf20Sopenharmony_ci int ret = 0; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci hfi1_init_ctxt(uctxt->sc); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci /* Now allocate the RcvHdr queue and eager buffers. */ 12048c2ecf20Sopenharmony_ci ret = hfi1_create_rcvhdrq(dd, uctxt); 12058c2ecf20Sopenharmony_ci if (ret) 12068c2ecf20Sopenharmony_ci goto done; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci ret = hfi1_setup_eagerbufs(uctxt); 12098c2ecf20Sopenharmony_ci if (ret) 12108c2ecf20Sopenharmony_ci goto done; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci /* If sub-contexts are enabled, do the appropriate setup */ 12138c2ecf20Sopenharmony_ci if (uctxt->subctxt_cnt) 12148c2ecf20Sopenharmony_ci ret = setup_subctxt(uctxt); 12158c2ecf20Sopenharmony_ci if (ret) 12168c2ecf20Sopenharmony_ci goto done; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci ret = hfi1_alloc_ctxt_rcv_groups(uctxt); 12198c2ecf20Sopenharmony_ci if (ret) 12208c2ecf20Sopenharmony_ci goto done; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci ret = init_user_ctxt(fd, uctxt); 12238c2ecf20Sopenharmony_ci if (ret) { 12248c2ecf20Sopenharmony_ci hfi1_free_ctxt_rcv_groups(uctxt); 12258c2ecf20Sopenharmony_ci goto done; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci user_init(uctxt); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci /* Now that the context is set up, the fd can get a reference. */ 12318c2ecf20Sopenharmony_ci fd->uctxt = uctxt; 12328c2ecf20Sopenharmony_ci hfi1_rcd_get(uctxt); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cidone: 12358c2ecf20Sopenharmony_ci if (uctxt->subctxt_cnt) { 12368c2ecf20Sopenharmony_ci /* 12378c2ecf20Sopenharmony_ci * On error, set the failed bit so sub-contexts will clean up 12388c2ecf20Sopenharmony_ci * correctly. 12398c2ecf20Sopenharmony_ci */ 12408c2ecf20Sopenharmony_ci if (ret) 12418c2ecf20Sopenharmony_ci set_bit(HFI1_CTXT_BASE_FAILED, &uctxt->event_flags); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci /* 12448c2ecf20Sopenharmony_ci * Base context is done (successfully or not), notify anybody 12458c2ecf20Sopenharmony_ci * using a sub-context that is waiting for this completion. 12468c2ecf20Sopenharmony_ci */ 12478c2ecf20Sopenharmony_ci clear_bit(HFI1_CTXT_BASE_UNINIT, &uctxt->event_flags); 12488c2ecf20Sopenharmony_ci wake_up(&uctxt->wait); 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci return ret; 12528c2ecf20Sopenharmony_ci} 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_cistatic int get_base_info(struct hfi1_filedata *fd, unsigned long arg, u32 len) 12558c2ecf20Sopenharmony_ci{ 12568c2ecf20Sopenharmony_ci struct hfi1_base_info binfo; 12578c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt = fd->uctxt; 12588c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = uctxt->dd; 12598c2ecf20Sopenharmony_ci unsigned offset; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci trace_hfi1_uctxtdata(uctxt->dd, uctxt, fd->subctxt); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci if (sizeof(binfo) != len) 12648c2ecf20Sopenharmony_ci return -EINVAL; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci memset(&binfo, 0, sizeof(binfo)); 12678c2ecf20Sopenharmony_ci binfo.hw_version = dd->revision; 12688c2ecf20Sopenharmony_ci binfo.sw_version = HFI1_KERN_SWVERSION; 12698c2ecf20Sopenharmony_ci binfo.bthqp = RVT_KDETH_QP_PREFIX; 12708c2ecf20Sopenharmony_ci binfo.jkey = uctxt->jkey; 12718c2ecf20Sopenharmony_ci /* 12728c2ecf20Sopenharmony_ci * If more than 64 contexts are enabled the allocated credit 12738c2ecf20Sopenharmony_ci * return will span two or three contiguous pages. Since we only 12748c2ecf20Sopenharmony_ci * map the page containing the context's credit return address, 12758c2ecf20Sopenharmony_ci * we need to calculate the offset in the proper page. 12768c2ecf20Sopenharmony_ci */ 12778c2ecf20Sopenharmony_ci offset = ((u64)uctxt->sc->hw_free - 12788c2ecf20Sopenharmony_ci (u64)dd->cr_base[uctxt->numa_id].va) % PAGE_SIZE; 12798c2ecf20Sopenharmony_ci binfo.sc_credits_addr = HFI1_MMAP_TOKEN(PIO_CRED, uctxt->ctxt, 12808c2ecf20Sopenharmony_ci fd->subctxt, offset); 12818c2ecf20Sopenharmony_ci binfo.pio_bufbase = HFI1_MMAP_TOKEN(PIO_BUFS, uctxt->ctxt, 12828c2ecf20Sopenharmony_ci fd->subctxt, 12838c2ecf20Sopenharmony_ci uctxt->sc->base_addr); 12848c2ecf20Sopenharmony_ci binfo.pio_bufbase_sop = HFI1_MMAP_TOKEN(PIO_BUFS_SOP, 12858c2ecf20Sopenharmony_ci uctxt->ctxt, 12868c2ecf20Sopenharmony_ci fd->subctxt, 12878c2ecf20Sopenharmony_ci uctxt->sc->base_addr); 12888c2ecf20Sopenharmony_ci binfo.rcvhdr_bufbase = HFI1_MMAP_TOKEN(RCV_HDRQ, uctxt->ctxt, 12898c2ecf20Sopenharmony_ci fd->subctxt, 12908c2ecf20Sopenharmony_ci uctxt->rcvhdrq); 12918c2ecf20Sopenharmony_ci binfo.rcvegr_bufbase = HFI1_MMAP_TOKEN(RCV_EGRBUF, uctxt->ctxt, 12928c2ecf20Sopenharmony_ci fd->subctxt, 12938c2ecf20Sopenharmony_ci uctxt->egrbufs.rcvtids[0].dma); 12948c2ecf20Sopenharmony_ci binfo.sdma_comp_bufbase = HFI1_MMAP_TOKEN(SDMA_COMP, uctxt->ctxt, 12958c2ecf20Sopenharmony_ci fd->subctxt, 0); 12968c2ecf20Sopenharmony_ci /* 12978c2ecf20Sopenharmony_ci * user regs are at 12988c2ecf20Sopenharmony_ci * (RXE_PER_CONTEXT_USER + (ctxt * RXE_PER_CONTEXT_SIZE)) 12998c2ecf20Sopenharmony_ci */ 13008c2ecf20Sopenharmony_ci binfo.user_regbase = HFI1_MMAP_TOKEN(UREGS, uctxt->ctxt, 13018c2ecf20Sopenharmony_ci fd->subctxt, 0); 13028c2ecf20Sopenharmony_ci offset = offset_in_page((uctxt_offset(uctxt) + fd->subctxt) * 13038c2ecf20Sopenharmony_ci sizeof(*dd->events)); 13048c2ecf20Sopenharmony_ci binfo.events_bufbase = HFI1_MMAP_TOKEN(EVENTS, uctxt->ctxt, 13058c2ecf20Sopenharmony_ci fd->subctxt, 13068c2ecf20Sopenharmony_ci offset); 13078c2ecf20Sopenharmony_ci binfo.status_bufbase = HFI1_MMAP_TOKEN(STATUS, uctxt->ctxt, 13088c2ecf20Sopenharmony_ci fd->subctxt, 13098c2ecf20Sopenharmony_ci dd->status); 13108c2ecf20Sopenharmony_ci if (HFI1_CAP_IS_USET(DMA_RTAIL)) 13118c2ecf20Sopenharmony_ci binfo.rcvhdrtail_base = HFI1_MMAP_TOKEN(RTAIL, uctxt->ctxt, 13128c2ecf20Sopenharmony_ci fd->subctxt, 0); 13138c2ecf20Sopenharmony_ci if (uctxt->subctxt_cnt) { 13148c2ecf20Sopenharmony_ci binfo.subctxt_uregbase = HFI1_MMAP_TOKEN(SUBCTXT_UREGS, 13158c2ecf20Sopenharmony_ci uctxt->ctxt, 13168c2ecf20Sopenharmony_ci fd->subctxt, 0); 13178c2ecf20Sopenharmony_ci binfo.subctxt_rcvhdrbuf = HFI1_MMAP_TOKEN(SUBCTXT_RCV_HDRQ, 13188c2ecf20Sopenharmony_ci uctxt->ctxt, 13198c2ecf20Sopenharmony_ci fd->subctxt, 0); 13208c2ecf20Sopenharmony_ci binfo.subctxt_rcvegrbuf = HFI1_MMAP_TOKEN(SUBCTXT_EGRBUF, 13218c2ecf20Sopenharmony_ci uctxt->ctxt, 13228c2ecf20Sopenharmony_ci fd->subctxt, 0); 13238c2ecf20Sopenharmony_ci } 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &binfo, len)) 13268c2ecf20Sopenharmony_ci return -EFAULT; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci return 0; 13298c2ecf20Sopenharmony_ci} 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci/** 13328c2ecf20Sopenharmony_ci * user_exp_rcv_setup - Set up the given tid rcv list 13338c2ecf20Sopenharmony_ci * @fd: file data of the current driver instance 13348c2ecf20Sopenharmony_ci * @arg: ioctl argumnent for user space information 13358c2ecf20Sopenharmony_ci * @len: length of data structure associated with ioctl command 13368c2ecf20Sopenharmony_ci * 13378c2ecf20Sopenharmony_ci * Wrapper to validate ioctl information before doing _rcv_setup. 13388c2ecf20Sopenharmony_ci * 13398c2ecf20Sopenharmony_ci */ 13408c2ecf20Sopenharmony_cistatic int user_exp_rcv_setup(struct hfi1_filedata *fd, unsigned long arg, 13418c2ecf20Sopenharmony_ci u32 len) 13428c2ecf20Sopenharmony_ci{ 13438c2ecf20Sopenharmony_ci int ret; 13448c2ecf20Sopenharmony_ci unsigned long addr; 13458c2ecf20Sopenharmony_ci struct hfi1_tid_info tinfo; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci if (sizeof(tinfo) != len) 13488c2ecf20Sopenharmony_ci return -EINVAL; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci if (copy_from_user(&tinfo, (void __user *)arg, (sizeof(tinfo)))) 13518c2ecf20Sopenharmony_ci return -EFAULT; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci ret = hfi1_user_exp_rcv_setup(fd, &tinfo); 13548c2ecf20Sopenharmony_ci if (!ret) { 13558c2ecf20Sopenharmony_ci /* 13568c2ecf20Sopenharmony_ci * Copy the number of tidlist entries we used 13578c2ecf20Sopenharmony_ci * and the length of the buffer we registered. 13588c2ecf20Sopenharmony_ci */ 13598c2ecf20Sopenharmony_ci addr = arg + offsetof(struct hfi1_tid_info, tidcnt); 13608c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)addr, &tinfo.tidcnt, 13618c2ecf20Sopenharmony_ci sizeof(tinfo.tidcnt))) 13628c2ecf20Sopenharmony_ci ret = -EFAULT; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci addr = arg + offsetof(struct hfi1_tid_info, length); 13658c2ecf20Sopenharmony_ci if (!ret && copy_to_user((void __user *)addr, &tinfo.length, 13668c2ecf20Sopenharmony_ci sizeof(tinfo.length))) 13678c2ecf20Sopenharmony_ci ret = -EFAULT; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci if (ret) 13708c2ecf20Sopenharmony_ci hfi1_user_exp_rcv_invalid(fd, &tinfo); 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci return ret; 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci/** 13778c2ecf20Sopenharmony_ci * user_exp_rcv_clear - Clear the given tid rcv list 13788c2ecf20Sopenharmony_ci * @fd: file data of the current driver instance 13798c2ecf20Sopenharmony_ci * @arg: ioctl argumnent for user space information 13808c2ecf20Sopenharmony_ci * @len: length of data structure associated with ioctl command 13818c2ecf20Sopenharmony_ci * 13828c2ecf20Sopenharmony_ci * The hfi1_user_exp_rcv_clear() can be called from the error path. Because 13838c2ecf20Sopenharmony_ci * of this, we need to use this wrapper to copy the user space information 13848c2ecf20Sopenharmony_ci * before doing the clear. 13858c2ecf20Sopenharmony_ci */ 13868c2ecf20Sopenharmony_cistatic int user_exp_rcv_clear(struct hfi1_filedata *fd, unsigned long arg, 13878c2ecf20Sopenharmony_ci u32 len) 13888c2ecf20Sopenharmony_ci{ 13898c2ecf20Sopenharmony_ci int ret; 13908c2ecf20Sopenharmony_ci unsigned long addr; 13918c2ecf20Sopenharmony_ci struct hfi1_tid_info tinfo; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci if (sizeof(tinfo) != len) 13948c2ecf20Sopenharmony_ci return -EINVAL; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci if (copy_from_user(&tinfo, (void __user *)arg, (sizeof(tinfo)))) 13978c2ecf20Sopenharmony_ci return -EFAULT; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci ret = hfi1_user_exp_rcv_clear(fd, &tinfo); 14008c2ecf20Sopenharmony_ci if (!ret) { 14018c2ecf20Sopenharmony_ci addr = arg + offsetof(struct hfi1_tid_info, tidcnt); 14028c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)addr, &tinfo.tidcnt, 14038c2ecf20Sopenharmony_ci sizeof(tinfo.tidcnt))) 14048c2ecf20Sopenharmony_ci return -EFAULT; 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci return ret; 14088c2ecf20Sopenharmony_ci} 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci/** 14118c2ecf20Sopenharmony_ci * user_exp_rcv_invalid - Invalidate the given tid rcv list 14128c2ecf20Sopenharmony_ci * @fd: file data of the current driver instance 14138c2ecf20Sopenharmony_ci * @arg: ioctl argumnent for user space information 14148c2ecf20Sopenharmony_ci * @len: length of data structure associated with ioctl command 14158c2ecf20Sopenharmony_ci * 14168c2ecf20Sopenharmony_ci * Wrapper to validate ioctl information before doing _rcv_invalid. 14178c2ecf20Sopenharmony_ci * 14188c2ecf20Sopenharmony_ci */ 14198c2ecf20Sopenharmony_cistatic int user_exp_rcv_invalid(struct hfi1_filedata *fd, unsigned long arg, 14208c2ecf20Sopenharmony_ci u32 len) 14218c2ecf20Sopenharmony_ci{ 14228c2ecf20Sopenharmony_ci int ret; 14238c2ecf20Sopenharmony_ci unsigned long addr; 14248c2ecf20Sopenharmony_ci struct hfi1_tid_info tinfo; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci if (sizeof(tinfo) != len) 14278c2ecf20Sopenharmony_ci return -EINVAL; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci if (!fd->invalid_tids) 14308c2ecf20Sopenharmony_ci return -EINVAL; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci if (copy_from_user(&tinfo, (void __user *)arg, (sizeof(tinfo)))) 14338c2ecf20Sopenharmony_ci return -EFAULT; 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci ret = hfi1_user_exp_rcv_invalid(fd, &tinfo); 14368c2ecf20Sopenharmony_ci if (ret) 14378c2ecf20Sopenharmony_ci return ret; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci addr = arg + offsetof(struct hfi1_tid_info, tidcnt); 14408c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)addr, &tinfo.tidcnt, 14418c2ecf20Sopenharmony_ci sizeof(tinfo.tidcnt))) 14428c2ecf20Sopenharmony_ci ret = -EFAULT; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci return ret; 14458c2ecf20Sopenharmony_ci} 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_cistatic __poll_t poll_urgent(struct file *fp, 14488c2ecf20Sopenharmony_ci struct poll_table_struct *pt) 14498c2ecf20Sopenharmony_ci{ 14508c2ecf20Sopenharmony_ci struct hfi1_filedata *fd = fp->private_data; 14518c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt = fd->uctxt; 14528c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = uctxt->dd; 14538c2ecf20Sopenharmony_ci __poll_t pollflag; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci poll_wait(fp, &uctxt->wait, pt); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci spin_lock_irq(&dd->uctxt_lock); 14588c2ecf20Sopenharmony_ci if (uctxt->urgent != uctxt->urgent_poll) { 14598c2ecf20Sopenharmony_ci pollflag = EPOLLIN | EPOLLRDNORM; 14608c2ecf20Sopenharmony_ci uctxt->urgent_poll = uctxt->urgent; 14618c2ecf20Sopenharmony_ci } else { 14628c2ecf20Sopenharmony_ci pollflag = 0; 14638c2ecf20Sopenharmony_ci set_bit(HFI1_CTXT_WAITING_URG, &uctxt->event_flags); 14648c2ecf20Sopenharmony_ci } 14658c2ecf20Sopenharmony_ci spin_unlock_irq(&dd->uctxt_lock); 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci return pollflag; 14688c2ecf20Sopenharmony_ci} 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_cistatic __poll_t poll_next(struct file *fp, 14718c2ecf20Sopenharmony_ci struct poll_table_struct *pt) 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci struct hfi1_filedata *fd = fp->private_data; 14748c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt = fd->uctxt; 14758c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = uctxt->dd; 14768c2ecf20Sopenharmony_ci __poll_t pollflag; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci poll_wait(fp, &uctxt->wait, pt); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci spin_lock_irq(&dd->uctxt_lock); 14818c2ecf20Sopenharmony_ci if (hdrqempty(uctxt)) { 14828c2ecf20Sopenharmony_ci set_bit(HFI1_CTXT_WAITING_RCV, &uctxt->event_flags); 14838c2ecf20Sopenharmony_ci hfi1_rcvctrl(dd, HFI1_RCVCTRL_INTRAVAIL_ENB, uctxt); 14848c2ecf20Sopenharmony_ci pollflag = 0; 14858c2ecf20Sopenharmony_ci } else { 14868c2ecf20Sopenharmony_ci pollflag = EPOLLIN | EPOLLRDNORM; 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci spin_unlock_irq(&dd->uctxt_lock); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci return pollflag; 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci/* 14948c2ecf20Sopenharmony_ci * Find all user contexts in use, and set the specified bit in their 14958c2ecf20Sopenharmony_ci * event mask. 14968c2ecf20Sopenharmony_ci * See also find_ctxt() for a similar use, that is specific to send buffers. 14978c2ecf20Sopenharmony_ci */ 14988c2ecf20Sopenharmony_ciint hfi1_set_uevent_bits(struct hfi1_pportdata *ppd, const int evtbit) 14998c2ecf20Sopenharmony_ci{ 15008c2ecf20Sopenharmony_ci struct hfi1_ctxtdata *uctxt; 15018c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = ppd->dd; 15028c2ecf20Sopenharmony_ci u16 ctxt; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci if (!dd->events) 15058c2ecf20Sopenharmony_ci return -EINVAL; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci for (ctxt = dd->first_dyn_alloc_ctxt; ctxt < dd->num_rcv_contexts; 15088c2ecf20Sopenharmony_ci ctxt++) { 15098c2ecf20Sopenharmony_ci uctxt = hfi1_rcd_get_by_index(dd, ctxt); 15108c2ecf20Sopenharmony_ci if (uctxt) { 15118c2ecf20Sopenharmony_ci unsigned long *evs; 15128c2ecf20Sopenharmony_ci int i; 15138c2ecf20Sopenharmony_ci /* 15148c2ecf20Sopenharmony_ci * subctxt_cnt is 0 if not shared, so do base 15158c2ecf20Sopenharmony_ci * separately, first, then remaining subctxt, if any 15168c2ecf20Sopenharmony_ci */ 15178c2ecf20Sopenharmony_ci evs = dd->events + uctxt_offset(uctxt); 15188c2ecf20Sopenharmony_ci set_bit(evtbit, evs); 15198c2ecf20Sopenharmony_ci for (i = 1; i < uctxt->subctxt_cnt; i++) 15208c2ecf20Sopenharmony_ci set_bit(evtbit, evs + i); 15218c2ecf20Sopenharmony_ci hfi1_rcd_put(uctxt); 15228c2ecf20Sopenharmony_ci } 15238c2ecf20Sopenharmony_ci } 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci return 0; 15268c2ecf20Sopenharmony_ci} 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci/** 15298c2ecf20Sopenharmony_ci * manage_rcvq - manage a context's receive queue 15308c2ecf20Sopenharmony_ci * @uctxt: the context 15318c2ecf20Sopenharmony_ci * @subctxt: the sub-context 15328c2ecf20Sopenharmony_ci * @start_stop: action to carry out 15338c2ecf20Sopenharmony_ci * 15348c2ecf20Sopenharmony_ci * start_stop == 0 disables receive on the context, for use in queue 15358c2ecf20Sopenharmony_ci * overflow conditions. start_stop==1 re-enables, to be used to 15368c2ecf20Sopenharmony_ci * re-init the software copy of the head register 15378c2ecf20Sopenharmony_ci */ 15388c2ecf20Sopenharmony_cistatic int manage_rcvq(struct hfi1_ctxtdata *uctxt, u16 subctxt, 15398c2ecf20Sopenharmony_ci unsigned long arg) 15408c2ecf20Sopenharmony_ci{ 15418c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = uctxt->dd; 15428c2ecf20Sopenharmony_ci unsigned int rcvctrl_op; 15438c2ecf20Sopenharmony_ci int start_stop; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci if (subctxt) 15468c2ecf20Sopenharmony_ci return 0; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci if (get_user(start_stop, (int __user *)arg)) 15498c2ecf20Sopenharmony_ci return -EFAULT; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci /* atomically clear receive enable ctxt. */ 15528c2ecf20Sopenharmony_ci if (start_stop) { 15538c2ecf20Sopenharmony_ci /* 15548c2ecf20Sopenharmony_ci * On enable, force in-memory copy of the tail register to 15558c2ecf20Sopenharmony_ci * 0, so that protocol code doesn't have to worry about 15568c2ecf20Sopenharmony_ci * whether or not the chip has yet updated the in-memory 15578c2ecf20Sopenharmony_ci * copy or not on return from the system call. The chip 15588c2ecf20Sopenharmony_ci * always resets it's tail register back to 0 on a 15598c2ecf20Sopenharmony_ci * transition from disabled to enabled. 15608c2ecf20Sopenharmony_ci */ 15618c2ecf20Sopenharmony_ci if (hfi1_rcvhdrtail_kvaddr(uctxt)) 15628c2ecf20Sopenharmony_ci clear_rcvhdrtail(uctxt); 15638c2ecf20Sopenharmony_ci rcvctrl_op = HFI1_RCVCTRL_CTXT_ENB; 15648c2ecf20Sopenharmony_ci } else { 15658c2ecf20Sopenharmony_ci rcvctrl_op = HFI1_RCVCTRL_CTXT_DIS; 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci hfi1_rcvctrl(dd, rcvctrl_op, uctxt); 15688c2ecf20Sopenharmony_ci /* always; new head should be equal to new tail; see above */ 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci return 0; 15718c2ecf20Sopenharmony_ci} 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci/* 15748c2ecf20Sopenharmony_ci * clear the event notifier events for this context. 15758c2ecf20Sopenharmony_ci * User process then performs actions appropriate to bit having been 15768c2ecf20Sopenharmony_ci * set, if desired, and checks again in future. 15778c2ecf20Sopenharmony_ci */ 15788c2ecf20Sopenharmony_cistatic int user_event_ack(struct hfi1_ctxtdata *uctxt, u16 subctxt, 15798c2ecf20Sopenharmony_ci unsigned long arg) 15808c2ecf20Sopenharmony_ci{ 15818c2ecf20Sopenharmony_ci int i; 15828c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = uctxt->dd; 15838c2ecf20Sopenharmony_ci unsigned long *evs; 15848c2ecf20Sopenharmony_ci unsigned long events; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci if (!dd->events) 15878c2ecf20Sopenharmony_ci return 0; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci if (get_user(events, (unsigned long __user *)arg)) 15908c2ecf20Sopenharmony_ci return -EFAULT; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci evs = dd->events + uctxt_offset(uctxt) + subctxt; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci for (i = 0; i <= _HFI1_MAX_EVENT_BIT; i++) { 15958c2ecf20Sopenharmony_ci if (!test_bit(i, &events)) 15968c2ecf20Sopenharmony_ci continue; 15978c2ecf20Sopenharmony_ci clear_bit(i, evs); 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci return 0; 16008c2ecf20Sopenharmony_ci} 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_cistatic int set_ctxt_pkey(struct hfi1_ctxtdata *uctxt, unsigned long arg) 16038c2ecf20Sopenharmony_ci{ 16048c2ecf20Sopenharmony_ci int i; 16058c2ecf20Sopenharmony_ci struct hfi1_pportdata *ppd = uctxt->ppd; 16068c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = uctxt->dd; 16078c2ecf20Sopenharmony_ci u16 pkey; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci if (!HFI1_CAP_IS_USET(PKEY_CHECK)) 16108c2ecf20Sopenharmony_ci return -EPERM; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if (get_user(pkey, (u16 __user *)arg)) 16138c2ecf20Sopenharmony_ci return -EFAULT; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci if (pkey == LIM_MGMT_P_KEY || pkey == FULL_MGMT_P_KEY) 16168c2ecf20Sopenharmony_ci return -EINVAL; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ppd->pkeys); i++) 16198c2ecf20Sopenharmony_ci if (pkey == ppd->pkeys[i]) 16208c2ecf20Sopenharmony_ci return hfi1_set_ctxt_pkey(dd, uctxt, pkey); 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci return -ENOENT; 16238c2ecf20Sopenharmony_ci} 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci/** 16268c2ecf20Sopenharmony_ci * ctxt_reset - Reset the user context 16278c2ecf20Sopenharmony_ci * @uctxt: valid user context 16288c2ecf20Sopenharmony_ci */ 16298c2ecf20Sopenharmony_cistatic int ctxt_reset(struct hfi1_ctxtdata *uctxt) 16308c2ecf20Sopenharmony_ci{ 16318c2ecf20Sopenharmony_ci struct send_context *sc; 16328c2ecf20Sopenharmony_ci struct hfi1_devdata *dd; 16338c2ecf20Sopenharmony_ci int ret = 0; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci if (!uctxt || !uctxt->dd || !uctxt->sc) 16368c2ecf20Sopenharmony_ci return -EINVAL; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci /* 16398c2ecf20Sopenharmony_ci * There is no protection here. User level has to guarantee that 16408c2ecf20Sopenharmony_ci * no one will be writing to the send context while it is being 16418c2ecf20Sopenharmony_ci * re-initialized. If user level breaks that guarantee, it will 16428c2ecf20Sopenharmony_ci * break it's own context and no one else's. 16438c2ecf20Sopenharmony_ci */ 16448c2ecf20Sopenharmony_ci dd = uctxt->dd; 16458c2ecf20Sopenharmony_ci sc = uctxt->sc; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci /* 16488c2ecf20Sopenharmony_ci * Wait until the interrupt handler has marked the context as 16498c2ecf20Sopenharmony_ci * halted or frozen. Report error if we time out. 16508c2ecf20Sopenharmony_ci */ 16518c2ecf20Sopenharmony_ci wait_event_interruptible_timeout( 16528c2ecf20Sopenharmony_ci sc->halt_wait, (sc->flags & SCF_HALTED), 16538c2ecf20Sopenharmony_ci msecs_to_jiffies(SEND_CTXT_HALT_TIMEOUT)); 16548c2ecf20Sopenharmony_ci if (!(sc->flags & SCF_HALTED)) 16558c2ecf20Sopenharmony_ci return -ENOLCK; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci /* 16588c2ecf20Sopenharmony_ci * If the send context was halted due to a Freeze, wait until the 16598c2ecf20Sopenharmony_ci * device has been "unfrozen" before resetting the context. 16608c2ecf20Sopenharmony_ci */ 16618c2ecf20Sopenharmony_ci if (sc->flags & SCF_FROZEN) { 16628c2ecf20Sopenharmony_ci wait_event_interruptible_timeout( 16638c2ecf20Sopenharmony_ci dd->event_queue, 16648c2ecf20Sopenharmony_ci !(READ_ONCE(dd->flags) & HFI1_FROZEN), 16658c2ecf20Sopenharmony_ci msecs_to_jiffies(SEND_CTXT_HALT_TIMEOUT)); 16668c2ecf20Sopenharmony_ci if (dd->flags & HFI1_FROZEN) 16678c2ecf20Sopenharmony_ci return -ENOLCK; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci if (dd->flags & HFI1_FORCED_FREEZE) 16708c2ecf20Sopenharmony_ci /* 16718c2ecf20Sopenharmony_ci * Don't allow context reset if we are into 16728c2ecf20Sopenharmony_ci * forced freeze 16738c2ecf20Sopenharmony_ci */ 16748c2ecf20Sopenharmony_ci return -ENODEV; 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci sc_disable(sc); 16778c2ecf20Sopenharmony_ci ret = sc_enable(sc); 16788c2ecf20Sopenharmony_ci hfi1_rcvctrl(dd, HFI1_RCVCTRL_CTXT_ENB, uctxt); 16798c2ecf20Sopenharmony_ci } else { 16808c2ecf20Sopenharmony_ci ret = sc_restart(sc); 16818c2ecf20Sopenharmony_ci } 16828c2ecf20Sopenharmony_ci if (!ret) 16838c2ecf20Sopenharmony_ci sc_return_credits(sc); 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci return ret; 16868c2ecf20Sopenharmony_ci} 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_cistatic void user_remove(struct hfi1_devdata *dd) 16898c2ecf20Sopenharmony_ci{ 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci hfi1_cdev_cleanup(&dd->user_cdev, &dd->user_device); 16928c2ecf20Sopenharmony_ci} 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistatic int user_add(struct hfi1_devdata *dd) 16958c2ecf20Sopenharmony_ci{ 16968c2ecf20Sopenharmony_ci char name[10]; 16978c2ecf20Sopenharmony_ci int ret; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "%s_%d", class_name(), dd->unit); 17008c2ecf20Sopenharmony_ci ret = hfi1_cdev_init(dd->unit, name, &hfi1_file_ops, 17018c2ecf20Sopenharmony_ci &dd->user_cdev, &dd->user_device, 17028c2ecf20Sopenharmony_ci true, &dd->verbs_dev.rdi.ibdev.dev.kobj); 17038c2ecf20Sopenharmony_ci if (ret) 17048c2ecf20Sopenharmony_ci user_remove(dd); 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci return ret; 17078c2ecf20Sopenharmony_ci} 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci/* 17108c2ecf20Sopenharmony_ci * Create per-unit files in /dev 17118c2ecf20Sopenharmony_ci */ 17128c2ecf20Sopenharmony_ciint hfi1_device_create(struct hfi1_devdata *dd) 17138c2ecf20Sopenharmony_ci{ 17148c2ecf20Sopenharmony_ci return user_add(dd); 17158c2ecf20Sopenharmony_ci} 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci/* 17188c2ecf20Sopenharmony_ci * Remove per-unit files in /dev 17198c2ecf20Sopenharmony_ci * void, core kernel returns no errors for this stuff 17208c2ecf20Sopenharmony_ci */ 17218c2ecf20Sopenharmony_civoid hfi1_device_remove(struct hfi1_devdata *dd) 17228c2ecf20Sopenharmony_ci{ 17238c2ecf20Sopenharmony_ci user_remove(dd); 17248c2ecf20Sopenharmony_ci} 1725