1
0
mirror of https://github.com/corundum/corundum.git synced 2025-01-16 08:12:53 +08:00

Improve ioctl implementation to support arbitrary number of regions

This commit is contained in:
Alex Forencich 2022-03-26 00:24:02 -07:00
parent 2babcdd16e
commit 6daf1171b5
5 changed files with 357 additions and 81 deletions

View File

@ -54,39 +54,52 @@ static int mqnic_release(struct inode *inode, struct file *file)
return 0;
}
static int mqnic_map_registers(struct mqnic_dev *mqnic, struct vm_area_struct *vma)
{
size_t map_size = vma->vm_end - vma->vm_start;
int ret;
if (map_size > mqnic->hw_regs_size) {
dev_err(mqnic->dev, "%s: Tried to map registers region with wrong size %lu (expected <= %llu)",
__func__, vma->vm_end - vma->vm_start, mqnic->hw_regs_size);
return -EINVAL;
}
ret = remap_pfn_range(vma, vma->vm_start, mqnic->hw_regs_phys >> PAGE_SHIFT,
map_size, pgprot_noncached(vma->vm_page_prot));
if (ret)
dev_err(mqnic->dev, "%s: remap_pfn_range failed for registers region", __func__);
else
dev_dbg(mqnic->dev, "%s: Mapped registers region at phys: 0x%pap, virt: 0x%p",
__func__, &mqnic->hw_regs_phys, (void *)vma->vm_start);
return ret;
}
static int mqnic_mmap(struct file *file, struct vm_area_struct *vma)
{
struct miscdevice *miscdev = file->private_data;
struct mqnic_dev *mqnic = container_of(miscdev, struct mqnic_dev, misc_dev);
int index;
u64 pgoff, req_len, req_start;
if (vma->vm_pgoff == 0)
return mqnic_map_registers(mqnic, vma);
index = vma->vm_pgoff >> (40 - PAGE_SHIFT);
req_len = vma->vm_end - vma->vm_start;
pgoff = vma->vm_pgoff & ((1U << (40 - PAGE_SHIFT)) - 1);
req_start = pgoff << PAGE_SHIFT;
if (vma->vm_end < vma->vm_start)
return -EINVAL;
if ((vma->vm_flags & VM_SHARED) == 0)
return -EINVAL;
switch (index) {
case 0:
if (req_start + req_len > mqnic->hw_regs_size)
return -EINVAL;
return io_remap_pfn_range(vma, vma->vm_start,
(mqnic->hw_regs_phys >> PAGE_SHIFT) + pgoff,
req_len, pgprot_noncached(vma->vm_page_prot));
case 1:
if (req_start + req_len > mqnic->app_hw_regs_size)
return -EINVAL;
return io_remap_pfn_range(vma, vma->vm_start,
(mqnic->app_hw_regs_phys >> PAGE_SHIFT) + pgoff,
req_len, pgprot_noncached(vma->vm_page_prot));
case 2:
if (req_start + req_len > mqnic->ram_hw_regs_size)
return -EINVAL;
return io_remap_pfn_range(vma, vma->vm_start,
(mqnic->ram_hw_regs_phys >> PAGE_SHIFT) + pgoff,
req_len, pgprot_noncached(vma->vm_page_prot));
default:
dev_err(mqnic->dev, "%s: Tried to map an unknown region at page offset 0x%lx",
__func__, vma->vm_pgoff);
return -EINVAL;
}
dev_err(mqnic->dev, "%s: Tried to map an unknown region at page offset %lu",
__func__, vma->vm_pgoff);
return -EINVAL;
}
@ -94,29 +107,90 @@ static long mqnic_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct miscdevice *miscdev = file->private_data;
struct mqnic_dev *mqnic = container_of(miscdev, struct mqnic_dev, misc_dev);
size_t minsz;
if (_IOC_TYPE(cmd) != MQNIC_IOCTL_TYPE)
return -ENOTTY;
if (cmd == MQNIC_IOCTL_GET_API_VERSION) {
// Get API version
return MQNIC_IOCTL_API_VERSION;
} else if (cmd == MQNIC_IOCTL_GET_DEVICE_INFO) {
// Get device information
struct mqnic_ioctl_device_info info;
switch (cmd) {
case MQNIC_IOCTL_INFO:
{
struct mqnic_ioctl_info ctl;
minsz = offsetofend(struct mqnic_ioctl_device_info, num_irqs);
ctl.fw_id = mqnic->fw_id;
ctl.fw_ver = mqnic->fw_ver;
ctl.board_id = mqnic->board_id;
ctl.board_ver = mqnic->board_ver;
ctl.regs_size = mqnic->hw_regs_size;
if (copy_from_user(&info, (void __user *)arg, minsz))
return -EFAULT;
if (copy_to_user((void __user *)arg, &ctl, sizeof(ctl)) != 0)
return -EFAULT;
if (info.argsz < minsz)
return -EINVAL;
return 0;
info.flags = 0;
info.fw_id = mqnic->fw_id;
info.fw_ver = mqnic->fw_ver;
info.board_id = mqnic->board_id;
info.board_ver = mqnic->board_ver;
info.build_date = mqnic->build_date;
info.git_hash = mqnic->git_hash;
info.rel_info = mqnic->rel_info;
info.num_regions = 3;
info.num_irqs = 0;
return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
} else if (cmd == MQNIC_IOCTL_GET_REGION_INFO) {
// Get region information
struct mqnic_ioctl_region_info info;
minsz = offsetofend(struct mqnic_ioctl_region_info, name);
if (copy_from_user(&info, (void __user *)arg, minsz))
return -EFAULT;
if (info.argsz < minsz)
return -EINVAL;
info.flags = 0;
info.type = MQNIC_REGION_TYPE_UNIMPLEMENTED;
info.next = 0;
info.child = 0;
info.size = 0;
info.offset = ((u64)info.index) << 40;
info.name[0] = 0;
switch (info.index) {
case 0:
info.type = MQNIC_REGION_TYPE_NIC_CTRL;
info.next = 1;
info.child = 0;
info.size = mqnic->hw_regs_size;
info.offset = ((u64)info.index) << 40;
strlcpy(info.name, "ctrl", sizeof(info.name));
break;
case 1:
info.type = MQNIC_REGION_TYPE_APP_CTRL;
info.next = 2;
info.child = 0;
info.size = mqnic->app_hw_regs_size;
info.offset = ((u64)info.index) << 40;
strlcpy(info.name, "app", sizeof(info.name));
break;
case 2:
info.type = MQNIC_REGION_TYPE_RAM;
info.next = 3;
info.child = 0;
info.size = mqnic->ram_hw_regs_size;
info.offset = ((u64)info.index) << 40;
strlcpy(info.name, "ram", sizeof(info.name));
break;
default:
return -EINVAL;
}
default:
return -ENOTTY;
return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
}
return -EINVAL;
}
const struct file_operations mqnic_fops = {

View File

@ -38,16 +38,52 @@
#include <linux/types.h>
#define MQNIC_IOCTL_API_VERSION 0
#define MQNIC_IOCTL_TYPE 0x88
#define MQNIC_IOCTL_BASE 0xC0
#define MQNIC_IOCTL_INFO _IOR(MQNIC_IOCTL_TYPE, 0xf0, struct mqnic_ioctl_info)
enum {
MQNIC_REGION_TYPE_UNIMPLEMENTED = 0x00000000,
MQNIC_REGION_TYPE_CTRL = 0x00001000,
MQNIC_REGION_TYPE_NIC_CTRL = 0x00001001,
MQNIC_REGION_TYPE_APP_CTRL = 0x00001002,
MQNIC_REGION_TYPE_RAM = 0x00002000
};
struct mqnic_ioctl_info {
// get API version
#define MQNIC_IOCTL_GET_API_VERSION _IO(MQNIC_IOCTL_TYPE, MQNIC_IOCTL_BASE + 0)
// get device information
struct mqnic_ioctl_device_info {
__u32 argsz;
__u32 flags;
__u32 fw_id;
__u32 fw_ver;
__u32 board_id;
__u32 board_ver;
size_t regs_size;
__u32 build_date;
__u32 git_hash;
__u32 rel_info;
__u32 num_regions;
__u32 num_irqs;
};
#define MQNIC_IOCTL_GET_DEVICE_INFO _IO(MQNIC_IOCTL_TYPE, MQNIC_IOCTL_BASE + 1)
// get region information
struct mqnic_ioctl_region_info {
__u32 argsz;
__u32 flags;
__u32 index;
__u32 type;
__u32 next;
__u32 child;
__u64 size;
__u64 offset;
__u8 name[32];
};
#define MQNIC_IOCTL_GET_REGION_INFO _IO(MQNIC_IOCTL_TYPE, MQNIC_IOCTL_BASE + 2)
#endif /* MQNIC_IOCTL_H */

View File

@ -106,6 +106,12 @@ int main(int argc, char *argv[])
printf("PCIe ID: %s\n", ptr+1);
}
printf("Control region size: %lu\n", dev->regs_size);
if (dev->app_regs_size)
printf("Application region size: %lu\n", dev->app_regs_size);
if (dev->ram_size)
printf("RAM region size: %lu\n", dev->ram_size);
printf("Device-level register blocks:\n");
for (struct reg_block *rb = dev->rb_list; rb->type && rb->version; rb++)
printf(" type 0x%08x (v %d.%d.%d.%d)\n", rb->type, rb->version >> 24,

View File

@ -75,26 +75,6 @@ static int mqnic_try_open(struct mqnic *dev, const char *fmt, ...)
goto fail_open;
}
if (fstat(dev->fd, &st))
{
perror("fstat failed");
goto fail_fstat;
}
dev->regs_size = st.st_size;
if (dev->regs_size == 0)
{
struct mqnic_ioctl_info info;
if (ioctl(dev->fd, MQNIC_IOCTL_INFO, &info) != 0)
{
perror("MQNIC_IOCTL_INFO ioctl failed");
goto fail_ioctl;
}
dev->regs_size = info.regs_size;
}
// determine sysfs path of PCIe device
// first, try to find via miscdevice
ptr = strrchr(dev->device_path, '/');
@ -123,27 +103,173 @@ static int mqnic_try_open(struct mqnic *dev, const char *fmt, ...)
dev->pci_device_path[0] = 0;
}
// map registers
dev->regs = (volatile uint8_t *)mmap(NULL, dev->regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, 0);
if (dev->regs == MAP_FAILED)
if (fstat(dev->fd, &st))
{
perror("mmap regs failed");
goto fail_mmap_regs;
perror("fstat failed");
goto fail_fstat;
}
if (dev->pci_device_path[0] && mqnic_reg_read32(dev->regs, 4) == 0xffffffff)
dev->regs_size = st.st_size;
if (dev->regs_size == 0)
{
// if we were given a PCIe resource, then we may need to enable the device
snprintf(path, sizeof(path), "%s/enable", dev->pci_device_path);
// miscdevice
off_t regs_offset = 0;
off_t app_regs_offset = 0;
off_t ram_offset = 0;
if (access(path, W_OK) == 0)
if (ioctl(dev->fd, MQNIC_IOCTL_GET_API_VERSION, 0) != MQNIC_IOCTL_API_VERSION)
{
FILE *fp = fopen(path, "w");
fprintf(stderr, "Error: unknown API version\n");
goto fail_ioctl;
}
if (fp)
struct mqnic_ioctl_device_info device_info;
device_info.argsz = sizeof(device_info);
device_info.flags = 0;
if (ioctl(dev->fd, MQNIC_IOCTL_GET_DEVICE_INFO, &device_info) != 0)
{
perror("MQNIC_IOCTL_GET_DEVICE_INFO ioctl failed");
goto fail_ioctl;
}
struct mqnic_ioctl_region_info region_info;
region_info.argsz = sizeof(region_info);
region_info.flags = 0;
region_info.index = 0;
for (region_info.index = 0; region_info.index < device_info.num_regions; region_info.index++)
{
if (ioctl(dev->fd, MQNIC_IOCTL_GET_REGION_INFO, &region_info) != 0)
{
fputc('1', fp);
fclose(fp);
perror("MQNIC_IOCTL_GET_REGION_INFO ioctl failed");
goto fail_ioctl;
}
switch (region_info.type) {
case MQNIC_REGION_TYPE_NIC_CTRL:
regs_offset = region_info.offset;
dev->regs_size = region_info.size;
break;
case MQNIC_REGION_TYPE_APP_CTRL:
app_regs_offset = region_info.offset;
dev->app_regs_size = region_info.size;
break;
case MQNIC_REGION_TYPE_RAM:
ram_offset = region_info.offset;
dev->ram_size = region_info.size;
break;
default:
break;
}
}
// map registers
dev->regs = (volatile uint8_t *)mmap(NULL, dev->regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, regs_offset);
if (dev->regs == MAP_FAILED)
{
perror("mmap regs failed");
goto fail_mmap_regs;
}
// map application section registers
if (dev->app_regs_size)
{
dev->app_regs = (volatile uint8_t *)mmap(NULL, dev->app_regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, app_regs_offset);
if (dev->app_regs == MAP_FAILED)
{
perror("mmap app regs failed");
goto fail_mmap_regs;
}
}
// map RAM
if (dev->ram_size)
{
dev->ram = (volatile uint8_t *)mmap(NULL, dev->ram_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, ram_offset);
if (dev->ram == MAP_FAILED)
{
perror("mmap RAM failed");
goto fail_mmap_regs;
}
}
}
else
{
// PCIe resource
// map registers
dev->regs = (volatile uint8_t *)mmap(NULL, dev->regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, 0);
if (dev->regs == MAP_FAILED)
{
perror("mmap regs failed");
goto fail_mmap_regs;
}
if (dev->pci_device_path[0])
{
// map application section registers
snprintf(path, sizeof(path), "%s/resource2", dev->pci_device_path);
dev->app_fd = open(path, O_RDWR);
if (dev->app_fd >= 0)
{
if (fstat(dev->app_fd, &st))
{
perror("fstat failed");
goto fail_fstat;
}
dev->app_regs_size = st.st_size;
dev->app_regs = (volatile uint8_t *)mmap(NULL, dev->app_regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->app_fd, 0);
if (dev->app_regs == MAP_FAILED)
{
perror("mmap app regs failed");
goto fail_mmap_regs;
}
}
// map RAM
snprintf(path, sizeof(path), "%s/resource4", dev->pci_device_path);
dev->ram_fd = open(path, O_RDWR);
if (dev->ram_fd >= 0)
{
if (fstat(dev->ram_fd, &st))
{
perror("fstat failed");
goto fail_fstat;
}
dev->ram_size = st.st_size;
dev->ram = (volatile uint8_t *)mmap(NULL, dev->ram_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->ram_fd, 0);
if (dev->ram == MAP_FAILED)
{
perror("mmap RAM failed");
goto fail_mmap_regs;
}
}
if (dev->pci_device_path[0] && mqnic_reg_read32(dev->regs, 4) == 0xffffffff)
{
// if we were given a PCIe resource, then we may need to enable the device
snprintf(path, sizeof(path), "%s/enable", dev->pci_device_path);
if (access(path, W_OK) == 0)
{
FILE *fp = fopen(path, "w");
if (fp)
{
fputc('1', fp);
fclose(fp);
}
}
}
}
}
@ -177,11 +303,24 @@ fail_enum:
if (dev->rb_list)
free_reg_block_list(dev->rb_list);
fail_reset:
munmap((void *)dev->regs, dev->regs_size);
fail_mmap_regs:
if (dev->ram)
munmap((void *)dev->ram, dev->ram_size);
dev->ram = NULL;
if (dev->app_regs)
munmap((void *)dev->app_regs, dev->app_regs_size);
dev->app_regs = NULL;
if (dev->regs)
munmap((void *)dev->regs, dev->regs_size);
dev->regs = NULL;
fail_ioctl:
fail_fstat:
close(dev->fd);
dev->fd = -1;
close(dev->app_fd);
dev->app_fd = -1;
close(dev->ram_fd);
dev->ram_fd = -1;
fail_open:
return -1;
}
@ -227,6 +366,10 @@ struct mqnic *mqnic_open(const char *dev_name)
goto fail_alloc;
}
dev->fd = -1;
dev->app_fd = -1;
dev->ram_fd = -1;
// absolute path
if (mqnic_try_open(dev, "%s", dev_name) == 0)
goto open;
@ -322,8 +465,17 @@ void mqnic_close(struct mqnic *dev)
if (dev->rb_list)
free_reg_block_list(dev->rb_list);
munmap((void *)dev->regs, dev->regs_size);
if (dev->ram)
munmap((void *)dev->ram, dev->ram_size);
if (dev->app_regs)
munmap((void *)dev->app_regs, dev->app_regs_size);
if (dev->regs)
munmap((void *)dev->regs, dev->regs_size);
close(dev->fd);
close(dev->app_fd);
close(dev->ram_fd);
free(dev);
}

View File

@ -125,10 +125,18 @@ struct mqnic_if {
struct mqnic {
int fd;
int app_fd;
int ram_fd;
size_t regs_size;
volatile uint8_t *regs;
size_t app_regs_size;
volatile uint8_t *app_regs;
size_t ram_size;
volatile uint8_t *ram;
struct reg_block *rb_list;
struct reg_block *fw_id_rb;
struct reg_block *if_rb;