/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Virtio loopback device driver * * Copyright 2022-2024 Virtual Open Systems SAS. * * Authors: * Timos Ampelikiotis * Anna Panagopoulou * Alvise Rigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef __LOOPBACK_H__ #define __LOOPBACK_H__ #define DRIVER "LOOPBACK" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* MMIO includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* mmap includes */ #include #include #include #include #include /* max Minor devices */ #define MAX_DEV 1 #define MAX_PDEV 100 #define PDEV_TYPES 2 /* Define mmap elements limit */ #define MMAP_LIMIT 200 /* * The alignment to use between consumer and producer parts of vring. * Currently hardcoded to the page size. */ #define VIRTIO_MMIO_VRING_ALIGN PAGE_SIZE #define to_virtio_loopback_device(ptr) \ container_of(ptr, struct virtio_loopback_device, vdev) /* mmap functionality */ #ifndef VM_RESERVED #define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP) #endif /* IOCTL defines */ #define EFD_INIT _IOC(_IOC_WRITE, 'k', 1, sizeof(efd_data)) #define WAKEUP _IOC(_IOC_WRITE, 'k', 2, 0) #define START_LOOPBACK _IOC(_IOC_WRITE, 'k', 3, sizeof(struct virtio_device_info_struct)) #define IRQ _IOC(_IOC_WRITE, 'k', 4, sizeof(int)) #define SHARE_VQS _IOC(_IOC_WRITE, 'k', 5, sizeof(uint32_t)) #define SHARE_COM_STRUCT _IOC(_IOC_WRITE, 'k', 6, 0) #define SHARE_VQS_NOTIF _IOC(_IOC_WRITE, 'k', 7, sizeof(struct vq_notifier)) /* Data structures */ struct virtio_device_info_struct { unsigned long magic; unsigned long version; unsigned long device_id; unsigned long vendor; bool priority_enabled; unsigned long init_notif_credits; unsigned long priority_group; }; struct virtio_neg { uint64_t notification; uint64_t data; uint64_t size; bool read; atomic_t done; }; struct share_mmap { uint64_t pfn; uint64_t vm_start; uint32_t size; uint32_t uid; struct page *page; }; struct mmap_data { int mmap_index; bool share_communication_struct; bool share_vqs; struct share_mmap share_mmap_list[MMAP_LIMIT]; int cur_ram_idx; uint64_t sum_pgfaults; }; struct vq_notifier { uint32_t vq_index; int notifier_fd; int pid; }; /* vq related data */ struct vq_data { uint32_t vq_index; uint64_t vq_pfns[16]; uint64_t vq_pfn; struct eventfd_ctx *vq_notifiers[16]; bool vq_notifiers_enabled; }; /* Data describing each device private status */ struct device_data { /* Info needed for adapter ops */ struct mmap_info *info; /* Waitqueue for the adapter */ wait_queue_head_t wq; struct mutex read_write_lock; struct eventfd_ctx *efd_ctx; /* * If this variable is true then read/write should wait * the adapter to unlock this operation by sending an * eventfd. If it's equal to "false" then the operation * does not wait for adapter's confirmation. */ bool valid_eventfd; /* vq data */ struct vq_data vq_data; /* virtio device data */ struct virtio_device_info_struct *vdev_data; bool priority_enabled; uint32_t priority_group; atomic_t notif_credits; atomic_t avail_notifs; atomic_t avail_inters; uint64_t served_timestamp; }; /* Data describing each entry of the driver */ struct loopback_devices_array { /* Array of probed devices */ struct virtio_loopback_device *devices[MAX_PDEV]; /* list of the devices */ struct list_head virtio_devices_list; /* Number of available devices */ atomic_t device_num; /* Registration completion */ struct completion reg_vl_dev_completion[MAX_PDEV]; /* Counter for all devices pending notifications */ atomic_t highest_active_prior_notifs; atomic_t pending_notifs; atomic_t pending_inters; wait_queue_head_t wq_notifs_inters; /* Spin lock for removing the device */ spinlock_t running_lock; }; /* Data concealed in the file private pointer */ struct file_priv_data { /* Device needed data */ struct device_data *dev_data; /* mmap needed data */ struct mmap_data *mm_data; /* Device info! */ struct virtio_device_info_struct device_info; /* The vl_dev pointer for the irq */ struct virtio_loopback_device *vl_dev_irq; }; struct virtio_loopback_device { struct virtio_device vdev; struct platform_device *pdev; /* Corresponding data pointer */ struct device_data *data; /* Status: -1 not initialized, 0 running, 1 paused */ int status; void __iomem *base; unsigned long version; /* A list of queues so we can dispatch IRQs */ spinlock_t lock; struct list_head virtqueues; /* Define workqueue for notifications */ struct workqueue_struct *notify_workqueue; /* Notify list and work struct */ spinlock_t notify_q_lock; struct list_head notify_list; struct work_struct notify_work; /* Notification waitqueue */ wait_queue_head_t wq_notifs_inters; }; struct virtio_loopback_vq_info { /* the actual virtqueue */ struct virtqueue *vq; /* the list node for the virtqueues list */ struct list_head node; }; struct virtio_loopback_device_node { /* the actual virtqueue */ uint32_t vq_index; atomic_t is_deleted; /* the list node for the virtqueues list */ struct list_head node; struct rcu_head rcu; }; /* Notify data*/ struct notify_data { uint32_t index; struct list_head list; }; /* Interrupt data*/ struct interrupt_data { uint32_t index; struct list_head list; }; /* Shared data structure between driver and user-space application */ struct mmap_info { void *data; int reference; }; /* * This structure holds the eventfds shared between the driver * and the user-space application. */ struct efd_data { int efd[2]; int pid; }; /* device data holder, this structure may be extended to hold additional data */ struct loopback_device_data { /*device Major number */ int dev_major; /* sysfs class structure */ struct class *class; struct cdev cdev; struct task_struct *notif_thread; }; /* Global variables */ extern struct platform_driver virtio_loopback_driver; /* Global functions */ int insert_entry_data(struct virtio_loopback_device *vl_dev, int id); int loopback_register_virtio_dev(struct virtio_loopback_device *vl_dev); bool register_interrupt(struct virtio_loopback_device *vl_dev, int irq); int notif_sched_func(void *data); bool add_dev_to_list(uint32_t array_dev_pos); void note_dev_deletion(struct virtio_loopback_device *vl_dev); extern struct loopback_devices_array loopback_devices; extern struct loopback_device_data loopback_data; #endif /* __LOOPBACK_H__ */