Skip to content

Commit

Permalink
Merge pull request torvalds#241 from M1cha/pull-fs
Browse files Browse the repository at this point in the history
add support for adding/removing virtio devices during runtime
  • Loading branch information
tavip authored Sep 21, 2016
2 parents 6bce11d + 46dc6da commit e8301b1
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 27 deletions.
2 changes: 1 addition & 1 deletion arch/lkl/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ endif
LDFLAGS_vmlinux += -r
LKL_ENTRY_POINTS := lkl_start_kernel lkl_sys_halt lkl_syscall lkl_trigger_irq \
lkl_get_free_irq lkl_put_irq lkl_create_syscall_thread \
lkl_stop_syscall_thread
lkl_stop_syscall_thread lkl_is_running

core-y += arch/lkl/kernel/
core-y += arch/lkl/mm/
Expand Down
3 changes: 1 addition & 2 deletions arch/lkl/include/asm/unistd.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#include <uapi/asm/unistd.h>

#define __NR_create_syscall_thread __NR_arch_specific_syscall

__SYSCALL(__NR_create_syscall_thread, sys_create_syscall_thread)
__SYSCALL(__NR_virtio_mmio_device_add, sys_virtio_mmio_device_add)

#define __SC_ASCII(t, a) #t "," #a

Expand Down
5 changes: 5 additions & 0 deletions arch/lkl/include/uapi/asm/host_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,9 @@ int lkl_start_kernel(struct lkl_host_operations *lkl_ops,
unsigned long mem_size,
const char *cmd_line, ...);

/**
* lkl_is_running - returns 1 if the kernel is currently running
*/
int lkl_is_running(void);

#endif
3 changes: 3 additions & 0 deletions arch/lkl/include/uapi/asm/unistd.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
#endif

#include <asm-generic/unistd.h>

#define __NR_create_syscall_thread (__NR_arch_specific_syscall + 0)
#define __NR_virtio_mmio_device_add (__NR_arch_specific_syscall + 1)
8 changes: 8 additions & 0 deletions arch/lkl/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ static void *idle_sem;
static void *init_sem;
static void *halt_sem;
static bool halt;
static int is_running;
void (*pm_power_off)(void) = NULL;
static unsigned long mem_size;

Expand Down Expand Up @@ -84,6 +85,7 @@ int __init lkl_start_kernel(struct lkl_host_operations *ops,

lkl_ops->sem_down(init_sem);

is_running = 1;
return 0;

out_free_idle_sem:
Expand All @@ -95,6 +97,11 @@ int __init lkl_start_kernel(struct lkl_host_operations *ops,
return ret;
}

int lkl_is_running(void)
{
return is_running;
}

void machine_halt(void)
{
halt = true;
Expand Down Expand Up @@ -146,6 +153,7 @@ void arch_cpu_idle(void)
/* Shutdown the clockevents source. */
tick_suspend_local();

is_running = false;
lkl_ops->sem_up(halt_sem);
lkl_ops->thread_exit();
}
Expand Down
50 changes: 50 additions & 0 deletions arch/lkl/kernel/syscalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
#include <linux/task_work.h>
#include <linux/syscalls.h>
#include <linux/kthread.h>
#include <linux/platform_device.h>
#include <asm/host_ops.h>
#include <asm/syscalls.h>
#include <asm/syscalls_32.h>

struct syscall_thread_data;
static asmlinkage long sys_create_syscall_thread(struct syscall_thread_data *);
static asmlinkage long sys_virtio_mmio_device_add(long base, long size,
unsigned int irq);

typedef long (*syscall_handler_t)(long arg1, ...);

Expand Down Expand Up @@ -425,3 +428,50 @@ void free_initial_syscall_thread(void)
* allocated in the LKL init routine. */
lkl_ops->sem_free(default_syscall_thread_data.mutex);
}

SYSCALL_DEFINE3(virtio_mmio_device_add, long, base, long, size, unsigned int,
irq)
{
struct platform_device *pdev;
int ret;

struct resource res[] = {
[0] = {
.start = base,
.end = base + size - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = irq,
.end = irq,
.flags = IORESOURCE_IRQ,
},
};

pdev = platform_device_alloc("virtio-mmio", PLATFORM_DEVID_AUTO);
if (!pdev) {
dev_err(&pdev->dev, "%s: Unable to device alloc for virtio-mmio\n", __func__);
return -ENOMEM;
}

ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
if (ret) {
dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n", __func__, pdev->name, pdev->id);
goto exit_device_put;
}

ret = platform_device_add(pdev);
if (ret < 0) {
dev_err(&pdev->dev, "%s: Unable to add %s%d\n", __func__, pdev->name, pdev->id);
goto exit_release_pdev;
}

return pdev->id;

exit_release_pdev:
platform_device_del(pdev);
exit_device_put:
platform_device_put(pdev);

return ret;
}
4 changes: 1 addition & 3 deletions tools/lkl/include/lkl.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ struct lkl_disk {
/**
* lkl_disk_add - add a new disk
*
* Must be called before calling lkl_start_kernel.
*
* @disk - the host disk handle
* @returns a disk id (0 is valid) or a strictly negative value in case of error
*/
Expand All @@ -111,7 +109,7 @@ int lkl_disk_add(struct lkl_disk *disk);
*
* @disk - the host disk handle
*/
void lkl_disk_remove(struct lkl_disk disk);
int lkl_disk_remove(struct lkl_disk disk);

/**
* lkl_mount_dev - mount a disk
Expand Down
2 changes: 1 addition & 1 deletion tools/lkl/include/lkl_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ extern struct lkl_host_operations lkl_host_ops;
*/
int lkl_printf(const char *fmt, ...);

extern char lkl_virtio_devs[256];
extern char lkl_virtio_devs[4096];

#ifdef CONFIG_AUTO_LKL_POSIX_HOST
#include <sys/uio.h>
Expand Down
80 changes: 77 additions & 3 deletions tools/lkl/lib/fs.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <lkl_host.h>

#include "virtio.h"

#define MAX_FSTYPE_LEN 50
int lkl_mount_fs(char *fstype)
{
Expand Down Expand Up @@ -29,19 +32,90 @@ int lkl_mount_fs(char *fstype)
return 0;
}

static uint32_t new_encode_dev(unsigned int major, unsigned int minor)
{
return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
}

static int startswith(const char *str, const char *pre)
{
return strncmp(pre, str, strlen(pre)) == 0;
}

static char *get_node_with_prefix(const char *path, const char *prefix,
int *ret)
{
struct lkl_dir *dir = NULL;
struct lkl_linux_dirent64 *dirent;
char *result = NULL;

dir = lkl_opendir(path, ret);
if (!dir)
return NULL;

while ((dirent = lkl_readdir(dir))) {
if (startswith(dirent->d_name, prefix)) {
result = strdup(dirent->d_name);
break;
}
}
lkl_closedir(dir);

if (!result)
*ret = -LKL_ENOENT;

return result;
}

static long get_virtio_blkdev(int disk_id)
{
char sysfs_path[] = "/sysfs/block/vda/dev";
char sysfs_path[LKL_PATH_MAX];
int sysfs_path_len = 0;
char buf[16] = { 0, };
long fd, ret;
int major, minor;
int opendir_ret;
char *virtio_name = NULL;
char *disk_name = NULL;

if (disk_id < 0)
return -LKL_EINVAL;

ret = lkl_mount_fs("sysfs");
if (ret < 0)
return ret;

sysfs_path[strlen("/sysfs/block/vd")] += disk_id;
if ((uint32_t) disk_id >= virtio_get_num_bootdevs())
ret = snprintf(sysfs_path, sizeof(sysfs_path), "/sysfs/devices/platform/virtio-mmio.%d.auto",
disk_id - virtio_get_num_bootdevs());
else
ret = snprintf(sysfs_path, sizeof(sysfs_path), "/sysfs/devices/virtio-mmio-cmdline/virtio-mmio.%d",
disk_id);
if (ret < 0 || (size_t) ret >= sizeof(sysfs_path))
return -LKL_ENOMEM;
sysfs_path_len += ret;

virtio_name = get_node_with_prefix(sysfs_path, "virtio", &opendir_ret);
if (!virtio_name)
return (long)opendir_ret;

ret = snprintf(sysfs_path + sysfs_path_len, sizeof(sysfs_path) - sysfs_path_len, "/%s/block",
virtio_name);
free(virtio_name);
if (ret < 0 || (size_t) ret >= sizeof(sysfs_path) - sysfs_path_len)
return -LKL_ENOMEM;
sysfs_path_len += ret;

disk_name = get_node_with_prefix(sysfs_path, "vd", &opendir_ret);
if (!disk_name)
return (long)opendir_ret;

ret = snprintf(sysfs_path + sysfs_path_len, sizeof(sysfs_path) - sysfs_path_len, "/%s/dev",
disk_name);
free(disk_name);
if (ret < 0 || (size_t) ret >= sizeof(sysfs_path) - sysfs_path_len)
return -LKL_ENOMEM;
sysfs_path_len += ret;

fd = lkl_sys_open(sysfs_path, LKL_O_RDONLY, 0);
if (fd < 0)
Expand All @@ -62,7 +136,7 @@ static long get_virtio_blkdev(int disk_id)
goto out_close;
}

ret = LKL_MKDEV(major, minor);
ret = new_encode_dev(major, minor);

out_close:
lkl_sys_close(fd);
Expand Down
82 changes: 71 additions & 11 deletions tools/lkl/lib/virtio.c
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,9 @@ static const struct lkl_iomem_ops virtio_ops = {
.write = virtio_write,
};

char lkl_virtio_devs[256];
char lkl_virtio_devs[4096];
static char *devs = lkl_virtio_devs;
static uint32_t lkl_num_virtio_boot_devs;

void virtio_set_queue_max_merge_len(struct virtio_dev *dev, int q, int len)
{
Expand All @@ -522,6 +523,7 @@ int virtio_dev_setup(struct virtio_dev *dev, int queues, int num_max)
int avail, mmio_size;
int i;
int num_bytes;
int ret;

dev->irq = lkl_get_free_irq("virtio");
if (dev->irq < 0)
Expand All @@ -544,24 +546,82 @@ int virtio_dev_setup(struct virtio_dev *dev, int queues, int num_max)
return -LKL_ENOMEM;
}

avail = sizeof(lkl_virtio_devs) - (devs - lkl_virtio_devs);
num_bytes = snprintf(devs, avail, " virtio_mmio.device=%d@0x%lx:%d",
mmio_size, (uintptr_t)dev->base, dev->irq);
if (num_bytes < 0 || num_bytes >= avail) {
lkl_put_irq(dev->irq, "virtio");
unregister_iomem(dev->base);
lkl_host_ops.mem_free(dev->queue);
return -LKL_ENOMEM;
if (!lkl_is_running()) {
avail = sizeof(lkl_virtio_devs) - (devs - lkl_virtio_devs);
num_bytes = snprintf(devs, avail, " virtio_mmio.device=%d@0x%lx:%d",
mmio_size, (uintptr_t) dev->base, dev->irq);
if (num_bytes < 0 || num_bytes >= avail) {
lkl_put_irq(dev->irq, "virtio");
unregister_iomem(dev->base);
lkl_host_ops.mem_free(dev->queue);
return -LKL_ENOMEM;
}
devs += num_bytes;
dev->virtio_mmio_id = lkl_num_virtio_boot_devs++;
} else {
ret =
lkl_sys_virtio_mmio_device_add((long)dev->base, mmio_size,
dev->irq);
if (ret < 0) {
lkl_printf("can't register mmio device\n");
return -1;
}
dev->virtio_mmio_id = lkl_num_virtio_boot_devs + ret;
}
devs += num_bytes;

return 0;
}

void virtio_dev_cleanup(struct virtio_dev *dev)
int virtio_dev_cleanup(struct virtio_dev *dev)
{
char devname[100];
long fd, ret;
long mount_ret;

if (!lkl_is_running())
goto skip_unbind;

mount_ret = lkl_mount_fs("sysfs");
if (mount_ret < 0)
return mount_ret;

if (dev->virtio_mmio_id >= virtio_get_num_bootdevs())
ret = snprintf(devname, sizeof(devname), "virtio-mmio.%d.auto",
dev->virtio_mmio_id - virtio_get_num_bootdevs());
else
ret = snprintf(devname, sizeof(devname), "virtio-mmio.%d",
dev->virtio_mmio_id);
if (ret < 0 || (size_t) ret >= sizeof(devname))
return -LKL_ENOMEM;

fd = lkl_sys_open("/sysfs/bus/platform/drivers/virtio-mmio/unbind",
LKL_O_WRONLY, 0);
if (fd < 0)
return fd;

ret = lkl_sys_write(fd, devname, strlen(devname));
if (ret < 0)
return ret;

ret = lkl_sys_close(fd);
if (ret < 0)
return ret;

if (mount_ret == 0) {
ret = lkl_sys_umount("/sysfs", 0);
if (ret < 0)
return ret;
}

skip_unbind:
lkl_put_irq(dev->irq, "virtio");
unregister_iomem(dev->base);
lkl_host_ops.mem_free(dev->queue);

return 0;
}

uint32_t virtio_get_num_bootdevs(void)
{
return lkl_num_virtio_boot_devs;
}
Loading

0 comments on commit e8301b1

Please sign in to comment.