Coyote/driver/fpga_dev.c

511 lines
18 KiB
C

/**
* 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");
}