/** * Copyright (c) 2021, Systems Group, ETH Zurich * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "fpga_dev.h" #include "fpga_sysfs.h" int fpga_major = FPGA_MAJOR; struct class *fpga_class = NULL; /** * @brief Fops * */ struct file_operations fpga_fops = { .owner = THIS_MODULE, .open = fpga_open, .release = fpga_release, .mmap = fpga_mmap, .unlocked_ioctl = fpga_ioctl, }; /** * @brief Sysfs * */ static struct kobj_attribute kobj_attr_ip_q0 = __ATTR(cyt_attr_ip_q0, 0664, cyt_attr_ip_q0_show, cyt_attr_ip_q0_store); static struct kobj_attribute kobj_attr_ip_q1 = __ATTR(cyt_attr_ip_q1, 0664, cyt_attr_ip_q1_show, cyt_attr_ip_q1_store); static struct kobj_attribute kobj_attr_mac_q0 = __ATTR(cyt_attr_mac_q0, 0664, cyt_attr_mac_q0_show, cyt_attr_mac_q0_store); static struct kobj_attribute kobj_attr_mac_q1 = __ATTR(cyt_attr_mac_q1, 0664, cyt_attr_mac_q1_show, cyt_attr_mac_q1_store); static struct kobj_attribute kobj_attr_nstats_q0 = __ATTR_RO(cyt_attr_nstats_q0); static struct kobj_attribute kobj_attr_nstats_q1 = __ATTR_RO(cyt_attr_nstats_q1); static struct kobj_attribute kobj_attr_xstats = __ATTR_RO(cyt_attr_xstats); static struct kobj_attribute kobj_attr_cnfg = __ATTR_RO(cyt_attr_cnfg); static struct kobj_attribute kobj_attr_eost = __ATTR(cyt_attr_eost, 0664, cyt_attr_eost_show, cyt_attr_eost_store); static struct attribute *attrs[] = { &kobj_attr_ip_q0.attr, &kobj_attr_ip_q1.attr, &kobj_attr_mac_q0.attr, &kobj_attr_mac_q1.attr, &kobj_attr_nstats_q0.attr, &kobj_attr_nstats_q1.attr, &kobj_attr_xstats.attr, &kobj_attr_cnfg.attr, &kobj_attr_eost.attr, NULL, }; static struct attribute_group attr_group = { .attrs = attrs, }; /** * @brief Read static configuration * */ int read_static_config(struct bus_drvdata *d) { long tmp; int ret_val = 0; // probe d->probe = d->fpga_stat_cnfg->probe; pr_info("deployment id %08x\n", d->probe); // channels and regions d->n_fpga_chan = d->fpga_stat_cnfg->n_chan; d->n_fpga_reg = d->fpga_stat_cnfg->n_regions; pr_info("detected %d virtual FPGA regions, %d FPGA channels\n", d->n_fpga_reg, d->n_fpga_chan); // flags d->en_avx = (d->fpga_stat_cnfg->ctrl_cnfg & EN_AVX_MASK) >> EN_AVX_SHFT; d->en_bypass = (d->fpga_stat_cnfg->ctrl_cnfg & EN_BPSS_MASK) >> EN_BPSS_SHFT; d->en_tlbf = (d->fpga_stat_cnfg->ctrl_cnfg & EN_TLBF_MASK) >> EN_TLBF_SHFT; d->en_wb = (d->fpga_stat_cnfg->ctrl_cnfg & EN_WB_MASK) >> EN_WB_SHFT; pr_info("enabled AVX %d, enabled bypass %d, enabled tlb fast %d, enabled writeback %d\n", d->en_avx, d->en_bypass, d->en_tlbf, d->en_wb); // mmu d->stlb_order = kzalloc(sizeof(struct tlb_order), GFP_KERNEL); BUG_ON(!d->stlb_order); d->stlb_order->hugepage = false; d->stlb_order->key_size = (d->fpga_stat_cnfg->ctrl_cnfg & TLB_S_ORDER_MASK) >> TLB_S_ORDER_SHFT; d->stlb_order->assoc = (d->fpga_stat_cnfg->ctrl_cnfg & TLB_S_ASSOC_MASK) >> TLB_S_ASSOC_SHFT; d->stlb_order->page_shift = (d->fpga_stat_cnfg->ctrl_cnfg & TLB_S_PG_SHFT_MASK) >> TLB_S_PG_SHFT_SHFT; BUG_ON(d->stlb_order->page_shift != PAGE_SHIFT); d->stlb_order->page_size = PAGE_SIZE; d->stlb_order->page_mask = PAGE_MASK; d->stlb_order->key_mask = (1 << d->stlb_order->key_size) - 1; d->stlb_order->tag_size = TLB_VADDR_RANGE - d->stlb_order->page_shift - d->stlb_order->key_size; d->stlb_order->tag_mask = (1 << d->stlb_order->tag_size) - 1; d->stlb_order->phy_size = TLB_PADDR_RANGE - d->stlb_order->page_shift; d->stlb_order->phy_mask = (1 << d->stlb_order->phy_size) - 1; pr_info("sTLB order %d, sTLB assoc %d, sTLB page size %lld\n", d->stlb_order->key_size, d->stlb_order->assoc, d->stlb_order->page_size); d->ltlb_order = kzalloc(sizeof(struct tlb_order), GFP_KERNEL); BUG_ON(!d->ltlb_order); d->ltlb_order->hugepage = true; d->ltlb_order->key_size = (d->fpga_stat_cnfg->ctrl_cnfg & TLB_L_ORDER_MASK) >> TLB_L_ORDER_SHFT; d->ltlb_order->assoc = (d->fpga_stat_cnfg->ctrl_cnfg & TLB_L_ASSOC_MASK) >> TLB_L_ASSOC_SHFT; d->ltlb_order->page_shift = (d->fpga_stat_cnfg->ctrl_cnfg & TLB_L_PG_SHFT_MASK) >> TLB_L_PG_SHFT_SHFT; d->ltlb_order->page_size = 1 << d->ltlb_order->page_shift; d->ltlb_order->page_mask = (~(d->ltlb_order->page_size - 1)); d->ltlb_order->key_mask = (1 << d->ltlb_order->key_size) - 1; d->ltlb_order->tag_size = TLB_VADDR_RANGE - d->ltlb_order->page_shift - d->ltlb_order->key_size; d->ltlb_order->tag_mask = (1 << d->ltlb_order->tag_size) - 1; d->ltlb_order->phy_size = TLB_PADDR_RANGE - d->ltlb_order->page_shift ; d->ltlb_order->phy_mask = (1 << d->ltlb_order->phy_size) - 1; pr_info("lTLB order %d, lTLB assoc %d, lTLB page size %lld\n", d->ltlb_order->key_size, d->ltlb_order->assoc, d->ltlb_order->page_size); // mem d->en_strm = (d->fpga_stat_cnfg->mem_cnfg & EN_STRM_MASK) >> EN_STRM_SHFT; d->en_mem = (d->fpga_stat_cnfg->mem_cnfg & EN_MEM_MASK) >> EN_MEM_SHFT; pr_info("enabled host streams %d, enabled card streams (mem) %d\n", d->en_strm, d->en_mem); // pr d->en_pr = (d->fpga_stat_cnfg->pr_cnfg & EN_PR_MASK) >> EN_PR_SHFT; pr_info("enabled PR %d\n", d->en_pr); // set eost if(d->en_pr) { d->eost = eost; d->fpga_stat_cnfg->pr_eost = eost; pr_info("set EOST [clks] %lld\n", d->eost); } // network d->en_rdma_0 = (d->fpga_stat_cnfg->rdma_cnfg & EN_RDMA_0_MASK) >> EN_RDMA_0_SHFT; d->en_rdma_1 = (d->fpga_stat_cnfg->rdma_cnfg & EN_RDMA_1_MASK) >> EN_RDMA_1_SHFT; pr_info("enabled RDMA on QSFP0 %d, enabled RDMA on QSFP1 %d\n", d->en_rdma_0, d->en_rdma_1); d->en_tcp_0 = (d->fpga_stat_cnfg->tcp_cnfg & EN_TCP_0_MASK) >> EN_TCP_0_SHFT; d->en_tcp_1 = (d->fpga_stat_cnfg->tcp_cnfg & EN_TCP_1_MASK) >> EN_TCP_1_SHFT; pr_info("enabled TCP/IP on QSFP0 %d, enabled TCP/IP on QSFP1 %d\n", d->en_tcp_0, d->en_tcp_1); // set ip and mac d->en_net_0 = d->en_rdma_0 | d->en_tcp_0; if(d->en_net_0) { ret_val = kstrtol(ip_addr_q0, 16, &tmp); d->net_0_ip_addr = (uint64_t) tmp; ret_val = kstrtol(mac_addr_q0, 16, &tmp); d->net_0_mac_addr = (uint64_t) tmp; d->fpga_stat_cnfg->net_0_ip = d->net_0_ip_addr; d->fpga_stat_cnfg->net_0_mac = d->net_0_mac_addr; pr_info("set QSFP0 ip %08x, mac %012llx\n", d->net_0_ip_addr, d->net_0_mac_addr); } d->en_net_1 = d->en_rdma_1 | d->en_tcp_1; if(d->en_net_1) { ret_val = kstrtol(ip_addr_q1, 16, &tmp); d->net_1_ip_addr = (uint64_t) tmp; ret_val = kstrtol(mac_addr_q1, 16, &tmp); d->net_1_mac_addr = (uint64_t) tmp; d->fpga_stat_cnfg->net_1_ip = d->net_1_ip_addr; d->fpga_stat_cnfg->net_1_mac = d->net_1_mac_addr; pr_info("set QSFP1 ip %08x, mac %012llx\n", d->net_1_ip_addr, d->net_1_mac_addr); } // lowspeed ctrl d->fpga_stat_cnfg->lspeed_cnfg = EN_LOWSPEED; return ret_val; } /** * @brief Allocate FPGA memory resources * */ int alloc_card_resources(struct bus_drvdata *d) { int ret_val = 0; int i; // init chunks card d->num_free_lchunks = N_LARGE_CHUNKS; d->num_free_schunks = N_SMALL_CHUNKS; d->lchunks = vzalloc(N_LARGE_CHUNKS * sizeof(struct chunk)); if (!d->lchunks) { pr_err("memory region for cmem structs not obtained\n"); goto err_alloc_lchunks; // ERR_ALLOC_LCHUNKS } d->schunks = vzalloc(N_SMALL_CHUNKS * sizeof(struct chunk)); if (!d->schunks) { pr_err("memory region for cmem structs not obtained\n"); goto err_alloc_schunks; // ERR_ALLOC_SCHUNKS } for (i = 0; i < N_LARGE_CHUNKS - 1; i++) { d->lchunks[i].used = false; d->lchunks[i].id = i; d->lchunks[i].next = &d->lchunks[i + 1]; } for (i = 0; i < N_SMALL_CHUNKS - 1; i++) { d->schunks[i].used = false; d->schunks[i].id = i; d->schunks[i].next = &d->schunks[i + 1]; } d->lalloc = &d->lchunks[0]; d->salloc = &d->schunks[0]; goto end; err_alloc_schunks: vfree(d->lchunks); err_alloc_lchunks: ret_val = -ENOMEM; end: return ret_val; } /** * @brief Free card memory resources * */ void free_card_resources(struct bus_drvdata *d) { // free card memory structs vfree(d->schunks); vfree(d->lchunks); pr_info("card resources deallocated\n"); } /** * @brief Initialize spin locks * */ void init_spin_locks(struct bus_drvdata *d) { // initialize spinlocks spin_lock_init(&d->card_l_lock); spin_lock_init(&d->card_s_lock); spin_lock_init(&d->prc.lock); spin_lock_init(&d->stat_lock); spin_lock_init(&d->prc_lock); spin_lock_init(&d->tlb_lock); } static struct kobj_type cyt_kobj_type = { .sysfs_ops = &kobj_sysfs_ops, }; /** * @brief Create sysfs entry * */ int create_sysfs_entry(struct bus_drvdata *d) { int ret_val = 0; pr_info("creating sysfs entry - coyote_cnfg\n"); ret_val = kobject_init_and_add(&d->cyt_kobj, &cyt_kobj_type, kernel_kobj, "coyote_cnfg"); if(ret_val) { return -ENOMEM; } ret_val = sysfs_create_group(&d->cyt_kobj, &attr_group); if(ret_val) { return -ENODEV; } return ret_val; } /** * @brief Remove sysfs entry * */ void remove_sysfs_entry(struct bus_drvdata *d) { sysfs_remove_group(&d->cyt_kobj, &attr_group); kobject_put(&d->cyt_kobj); } /** * @brief Init char devices * */ int init_char_devices(struct bus_drvdata *d, dev_t dev) { int ret_val = 0; ret_val = alloc_chrdev_region(&dev, 0, d->n_fpga_reg, DEV_NAME); fpga_major = MAJOR(dev); if (ret_val) { pr_err("failed to register virtual FPGA devices"); goto end; } pr_info("virtual FPGA device regions allocated, major number %d\n", fpga_major); // create device class fpga_class = class_create(THIS_MODULE, DEV_NAME); // virtual FPGA devices d->fpga_dev = kmalloc(d->n_fpga_reg * sizeof(struct fpga_dev), GFP_KERNEL); if (!d->fpga_dev) { pr_err("could not allocate memory for fpga devices\n"); goto err_char_mem; // ERR_CHAR_MEM } memset(d->fpga_dev, 0, d->n_fpga_reg * sizeof(struct fpga_dev)); pr_info("allocated memory for fpga devices\n"); goto end; err_char_mem: unregister_chrdev_region(dev, d->n_fpga_reg); ret_val = -ENOMEM; end: return ret_val; } /** * @brief Delete char devices * */ void free_char_devices(struct bus_drvdata *d) { // free virtual FPGA memory kfree(d->fpga_dev); pr_info("virtual FPGA device memory freed\n"); // remove class class_destroy(fpga_class); pr_info("fpga class deleted\n"); // remove char devices unregister_chrdev_region(MKDEV(fpga_major, 0), d->n_fpga_reg); pr_info("char devices unregistered\n"); } /** * @brief Initialize vFPGAs * */ int init_fpga_devices(struct bus_drvdata *d) { int ret_val = 0; int i, j; int devno; for (i = 0; i < d->n_fpga_reg; i++) { // ID d->fpga_dev[i].id = i; // PCI device d->fpga_dev[i].pd = d; d->fpga_dev[i].prc = &d->prc; // physical if(cyt_arch == CYT_ARCH_PCI) { d->fpga_dev[i].fpga_phys_addr_ctrl = d->bar_phys_addr[BAR_FPGA_CONFIG] + FPGA_CTRL_OFFS + i * FPGA_CTRL_SIZE; d->fpga_dev[i].fpga_phys_addr_ctrl_avx = d->bar_phys_addr[BAR_FPGA_CONFIG] + FPGA_CTRL_CNFG_AVX_OFFS + i * FPGA_CTRL_CNFG_AVX_SIZE; } else if(cyt_arch == CYT_ARCH_ECI) { d->fpga_dev[i].fpga_phys_addr_ctrl = d->io_phys_addr + FPGA_CTRL_OFFS + i*FPGA_CTRL_SIZE; } // MMU control region d->fpga_dev[i].fpga_lTlb = ioremap(d->fpga_dev[i].fpga_phys_addr_ctrl + FPGA_CTRL_LTLB_OFFS, FPGA_CTRL_LTLB_SIZE); d->fpga_dev[i].fpga_sTlb = ioremap(d->fpga_dev[i].fpga_phys_addr_ctrl + FPGA_CTRL_STLB_OFFS, FPGA_CTRL_STLB_SIZE); // FPGA engine control d->fpga_dev[i].fpga_cnfg = ioremap(d->fpga_dev[i].fpga_phys_addr_ctrl + FPGA_CTRL_CNFG_OFFS, FPGA_CTRL_CNFG_SIZE); // FPGA engine control AVX if(cyt_arch == CYT_ARCH_PCI) { d->fpga_dev[i].fpga_cnfg_avx = ioremap(d->fpga_dev[i].fpga_phys_addr_ctrl_avx, FPGA_CTRL_CNFG_AVX_SIZE); } // init chunks pid d->fpga_dev[i].num_free_pid_chunks = N_CPID_MAX; d->fpga_dev[i].pid_chunks = vzalloc(N_CPID_MAX * sizeof(struct chunk)); if (!d->fpga_dev[i].pid_chunks) { pr_err("memory region for pid chunks not obtained\n"); goto err_alloc_pid_chunks; // ERR_ALLOC_PID_CHUNKS } d->fpga_dev[i].pid_array = vzalloc(N_CPID_MAX * sizeof(pid_t)); if (!d->fpga_dev[i].pid_array) { pr_err("memory region for pid array not obtained\n"); goto err_alloc_pid_array; // ERR_ALLOC_PID_ARRAY } for (j = 0; j < N_CPID_MAX - 1; j++) { d->fpga_dev[i].pid_chunks[j].used = false; d->fpga_dev[i].pid_chunks[j].id = j; d->fpga_dev[i].pid_chunks[j].next = &d->fpga_dev[i].pid_chunks[j + 1]; } d->fpga_dev[i].pid_alloc = &d->fpga_dev[i].pid_chunks[0]; // initialize device spinlock spin_lock_init(&d->fpga_dev[i].lock); spin_lock_init(&d->fpga_dev[i].card_pid_lock); // writeback setup if(d->en_wb) { d->fpga_dev[i].wb_addr_virt = dma_alloc_coherent(&d->pci_dev->dev, WB_SIZE, &d->fpga_dev[i].wb_phys_addr, GFP_ATOMIC); if(!d->fpga_dev[i].wb_addr_virt) { pr_err("failed to allocate writeback memory\n"); goto err_wb; } if(cyt_arch == CYT_ARCH_PCI && d->en_avx) { for(j = 0; j < WB_BLOCKS; j++) d->fpga_dev[i].fpga_cnfg_avx->wback[j] = d->fpga_dev[i].wb_phys_addr + j*(N_CPID_MAX * sizeof(uint32_t)); } else { for(j = 0; j < WB_BLOCKS; j++) d->fpga_dev[i].fpga_cnfg->wback[j] = d->fpga_dev[i].wb_phys_addr + j*(N_CPID_MAX * sizeof(uint32_t)); } pr_info("allocated memory for descriptor writeback, vaddr %llx, paddr %llx", (uint64_t)d->fpga_dev[i].wb_addr_virt, d->fpga_dev[i].wb_phys_addr); } // create device devno = MKDEV(fpga_major, i); device_create(fpga_class, NULL, devno, NULL, DEV_NAME "%d", i); pr_info("virtual FPGA device %d created\n", i); // add device cdev_init(&d->fpga_dev[i].cdev, &fpga_fops); d->fpga_dev[i].cdev.owner = THIS_MODULE; d->fpga_dev[i].cdev.ops = &fpga_fops; // Init hash hash_init(pid_cpid_map[i]); hash_init(user_lbuff_map[i]); hash_init(user_sbuff_map[i]); ret_val = cdev_add(&d->fpga_dev[i].cdev, devno, 1); if (ret_val) { pr_err("could not create a virtual FPGA device %d\n", i); goto err_char_reg; } } pr_info("all virtual FPGA devices added\n"); goto end; err_char_reg: for (j = 0; j < i; j++) { device_destroy(fpga_class, MKDEV(fpga_major, j)); cdev_del(&d->fpga_dev[j].cdev); } err_wb: if(d->en_wb) { for (j = 0; j < i; j++) { set_memory_wb((uint64_t)d->fpga_dev[j].wb_addr_virt, N_WB_PAGES); dma_free_coherent(&d->pci_dev->dev, WB_SIZE, d->fpga_dev[j].wb_addr_virt, d->fpga_dev[j].wb_phys_addr); } } vfree(d->fpga_dev[i].pid_array); err_alloc_pid_array: for (j = 0; j < i; j++) { vfree(d->fpga_dev[j].pid_array); } vfree(d->fpga_dev[i].pid_chunks); err_alloc_pid_chunks: for (j = 0; j < i; j++) { vfree(d->fpga_dev[j].pid_chunks); } ret_val = -ENOMEM; end: return ret_val; } /** * @brief Delete vFPGAs * */ void free_fpga_devices(struct bus_drvdata *d) { int i; for(i = 0; i < d->n_fpga_reg; i++) { device_destroy(fpga_class, MKDEV(fpga_major, i)); cdev_del(&d->fpga_dev[i].cdev); if(d->en_wb) { set_memory_wb((uint64_t)d->fpga_dev[i].wb_addr_virt, N_WB_PAGES); dma_free_coherent(&d->pci_dev->dev, WB_SIZE, d->fpga_dev[i].wb_addr_virt, d->fpga_dev[i].wb_phys_addr); } vfree(d->fpga_dev[i].pid_array); vfree(d->fpga_dev[i].pid_chunks); } pr_info("vFPGAs deleted\n"); }