From ad574b7e10eca3564d915a211e2b1476dded9364 Mon Sep 17 00:00:00 2001 From: Zvonko Kaiser <zkaiser@nvidia.com> Date: Thu, 4 Jul 2024 17:06:39 +0000 Subject: [PATCH 1/4] dragonball: Add patches for 6.1.x Ported the 5.10 patchs to 6.1.x Signed-off-by: Zvonko Kaiser <zkaiser@nvidia.com> --- .../0001-upcall-establish-upcall-server.patch | 522 ++++++++++++++++++ ...roduce-device-manager-upcall-service.patch | 330 +++++++++++ ...otplug-hot-unplug-into-device-manage.patch | 301 ++++++++++ ...o-mmio-hotplug-hot-unplug-into-devic.patch | 417 ++++++++++++++ ...l-devmgr-suppots-cpu-hotplug-on-arm6.patch | 163 ++++++ ...msi-control-msi-irq-number-activated.patch | 66 +++ ...date-bringup_nonboot_cpus-parameters.patch | 139 +++++ ...l-add-pci-hotplug-hot-unplug-support.patch | 173 ++++++ 8 files changed, 2111 insertions(+) create mode 100644 tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0001-upcall-establish-upcall-server.patch create mode 100644 tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0002-upcall-introduce-device-manager-upcall-service.patch create mode 100644 tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0003-upcall-add-cpu-hotplug-hot-unplug-into-device-manage.patch create mode 100644 tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0004-upcall-add-virtio-mmio-hotplug-hot-unplug-into-devic.patch create mode 100644 tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0005-upcall-dragonball-devmgr-suppots-cpu-hotplug-on-arm6.patch create mode 100644 tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0006-msi-control-msi-irq-number-activated.patch create mode 100644 tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0007-smp-update-bringup_nonboot_cpus-parameters.patch create mode 100644 tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0008-upcall-add-pci-hotplug-hot-unplug-support.patch diff --git a/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0001-upcall-establish-upcall-server.patch b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0001-upcall-establish-upcall-server.patch new file mode 100644 index 0000000000..52da543824 --- /dev/null +++ b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0001-upcall-establish-upcall-server.patch @@ -0,0 +1,522 @@ +From 691186a091ecfc1777531a61594b88394d384cff Mon Sep 17 00:00:00 2001 +From: Chao Wu <chaowu@linux.alibaba.com> +Date: Wed, 9 Nov 2022 11:38:36 +0800 +Subject: [PATCH 1/4] upcall: establish upcall server + +Upcall is a direct communication tool between hypervisor and guest. This +patch introduces the server side in the upcall system. +At the start of the upcall server, A kthread `db-vsock-srv` will be +created. In this kthread, a vsock listener is established upon specific +port(currently that port is 0xDB, DB refers to Dragonball). After socket +is created, it will start accepting the connection from the client side. +If the connection is established, upcall server will try to get cmd from +the client and that cmd could determine which upcall service will handle +the request from the client. + +Besides, different service needs to be registered into upcall server so +that it could handle the request from the client. There is a +`register_db_vsock_service` in this commit provided for every service to +register service into service_entry list during initialization and we will +introduce device manager service in the following commits. + +Signed-off-by: Liu Jiang <gerry@linux.alibaba.com> +Signed-off-by: Zizheng Bian <zizheng.bian@linux.alibaba.com> +Signed-off-by: Chao Wu <chaowu@linux.alibaba.com> +Signed-off-by: WangYu <WangYu@linux.alibaba.com> +Signed-off-by: Xingjun Liu <xingjun.liu@linux.alibaba.com> +--- + drivers/misc/Kconfig | 1 + + drivers/misc/Makefile | 1 + + drivers/misc/dragonball/Kconfig | 21 ++ + drivers/misc/dragonball/Makefile | 6 + + drivers/misc/dragonball/upcall_srv/Kconfig | 14 + + drivers/misc/dragonball/upcall_srv/Makefile | 13 + + .../upcall_srv/dragonball_upcall_srv.c | 323 ++++++++++++++++++ + include/dragonball/upcall_srv.h | 42 +++ + 8 files changed, 421 insertions(+) + create mode 100644 drivers/misc/dragonball/Kconfig + create mode 100644 drivers/misc/dragonball/Makefile + create mode 100644 drivers/misc/dragonball/upcall_srv/Kconfig + create mode 100644 drivers/misc/dragonball/upcall_srv/Makefile + create mode 100644 drivers/misc/dragonball/upcall_srv/dragonball_upcall_srv.c + create mode 100644 include/dragonball/upcall_srv.h + +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 0cef98319..297d896f6 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -515,4 +515,5 @@ source "drivers/misc/habanalabs/Kconfig" + source "drivers/misc/uacce/Kconfig" + source "drivers/misc/pvpanic/Kconfig" + source "drivers/misc/mchp_pci1xxxx/Kconfig" ++source "drivers/misc/dragonball/Kconfig" + endmenu + +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index ac9b3e757..6ef1ddc50 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -62,3 +62,4 @@ obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o + obj-$(CONFIG_OPEN_DICE) += open-dice.o + obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/ + obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o ++obj-$(CONFIG_DRAGONBALL_DRIVERS) += dragonball/ + +diff --git a/drivers/misc/dragonball/Kconfig b/drivers/misc/dragonball/Kconfig +new file mode 100644 +index 000000000000..f81be3721908 +--- /dev/null ++++ b/drivers/misc/dragonball/Kconfig +@@ -0,0 +1,21 @@ ++# ++# Alibaba Dragonball Secure Container Runtime Drivers ++# ++ ++menuconfig DRAGONBALL_DRIVERS ++ bool "Alibaba Dragonball Secure Container Runtime Drivers" ++ depends on X86_64 || ARM64 ++ default n ++ help ++ Alibaba Dragonball is a secure container runtime with an embedded micro-vmm ++ to securely isolate container workloads. ++ ++ Say Y here to get to see options for various misc drivers to support the ++ Alibaba Dragonball secure container runtime. This option alone does not ++ add any kernel code. ++ ++ If unsure, say N. ++ ++if DRAGONBALL_DRIVERS ++source "drivers/misc/dragonball/upcall_srv/Kconfig" ++endif # DRAGONBALL_DRIVERS +diff --git a/drivers/misc/dragonball/Makefile b/drivers/misc/dragonball/Makefile +new file mode 100644 +index 000000000000..b7bd86d73ade +--- /dev/null ++++ b/drivers/misc/dragonball/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# Makefile for Dragonball misc drivers ++# ++ ++obj-$(CONFIG_DRAGONBALL_UPCALL_SRV) += upcall_srv/ +diff --git a/drivers/misc/dragonball/upcall_srv/Kconfig b/drivers/misc/dragonball/upcall_srv/Kconfig +new file mode 100644 +index 000000000000..b00bf1f8637d +--- /dev/null ++++ b/drivers/misc/dragonball/upcall_srv/Kconfig +@@ -0,0 +1,14 @@ ++# ++# Alibaba Dragonball Secure Container Runtime Drivers for vsock ++# ++ ++config DRAGONBALL_UPCALL_SRV ++ bool "Dragonball in-kernel Virtual Sockets Server" ++ depends on VIRTIO_VSOCKETS ++ default y ++ help ++ This configure implements an in-kernel vsock server to dispatch Dragonball ++ requests to registered service handlers, based on the reliable Virtual ++ Sockets communication channels between guest and host/vmm. ++ ++ If unsure, say N. +diff --git a/drivers/misc/dragonball/upcall_srv/Makefile b/drivers/misc/dragonball/upcall_srv/Makefile +new file mode 100644 +index 000000000000..4102e6c7edef +--- /dev/null ++++ b/drivers/misc/dragonball/upcall_srv/Makefile +@@ -0,0 +1,13 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# Makefile for the in-kernel vsock server. ++# ++# Copyright (C) 2022 Alibaba Cloud, Inc ++# ++# 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. ++# ++ ++obj-$(CONFIG_DRAGONBALL_UPCALL_SRV) += dragonball_upcall_srv.o +diff --git a/drivers/misc/dragonball/upcall_srv/dragonball_upcall_srv.c b/drivers/misc/dragonball/upcall_srv/dragonball_upcall_srv.c +new file mode 100644 +index 000000000000..1670bd8597f0 +--- /dev/null ++++ b/drivers/misc/dragonball/upcall_srv/dragonball_upcall_srv.c +@@ -0,0 +1,323 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * drivers/misc/dragonball/upcall_srv/dragonball_upcall_srv.c ++ * Dragonball upcall server ++ * ++ * Copyright (C) 2022 Alibaba Cloud, Inc ++ * ++ * 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. ++ * ++ */ ++ ++#define pr_fmt(fmt) "db-upcall-srv: " fmt ++ ++#include <linux/kthread.h> ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/net.h> ++#include <linux/vm_sockets.h> ++#include <net/net_namespace.h> ++#include <net/sock.h> ++#include <dragonball/upcall_srv.h> ++ ++struct db_conn_info { ++ struct work_struct work; ++ struct socket *sock; ++}; ++ ++struct db_service_entry { ++ char cmd; ++ db_vsock_svc_handler_t handler; ++ struct list_head list; ++}; ++ ++/* Protects registered command. */ ++static DEFINE_RWLOCK(db_service_lock); ++static LIST_HEAD(db_service_list); ++ ++static struct task_struct *db_service_task; ++static unsigned int db_server_port = DB_SERVER_PORT; ++ ++struct socket *db_create_vsock_listener(unsigned int port) ++{ ++ struct socket *sock; ++ int ret = 0; ++ ++ union { ++ struct sockaddr sa; ++ struct sockaddr_vm svm; ++ } addr = { ++ .svm = { ++ .svm_family = AF_VSOCK, ++ .svm_port = port, ++ .svm_cid = VMADDR_CID_ANY, ++ } ++ }; ++ ++ ret = sock_create_kern(&init_net, AF_VSOCK, SOCK_STREAM, 0, &sock); ++ if (ret) { ++ pr_err("Server vsock create failed, err: %d\n", ret); ++ return ERR_PTR(ret); ++ } ++ ++ ret = sock->ops->bind(sock, &addr.sa, sizeof(addr.svm)); ++ if (ret) { ++ pr_err("Server vsock bind failed, err: %d\n", ret); ++ goto err; ++ } ++ ret = sock->ops->listen(sock, 10); ++ if (ret < 0) { ++ pr_err("Server vsock listen error: %d\n", ret); ++ goto err; ++ } ++ ++ return sock; ++err: ++ sock_release(sock); ++ return ERR_PTR(ret); ++} ++EXPORT_SYMBOL_GPL(db_create_vsock_listener); ++ ++int db_vsock_sendmsg(struct socket *sock, char *buf, size_t len) ++{ ++ struct kvec vec; ++ struct msghdr msgh; ++ ++ vec.iov_base = buf; ++ vec.iov_len = len; ++ memset(&msgh, 0, sizeof(msgh)); ++ ++ return kernel_sendmsg(sock, &msgh, &vec, 1, len); ++} ++EXPORT_SYMBOL_GPL(db_vsock_sendmsg); ++ ++int db_vsock_recvmsg(struct socket *sock, char *buf, size_t len, int flags) ++{ ++ struct kvec vec; ++ struct msghdr msgh; ++ ++ memset(&vec, 0, sizeof(vec)); ++ memset(&msgh, 0, sizeof(msgh)); ++ vec.iov_base = buf; ++ vec.iov_len = len; ++ ++ return kernel_recvmsg(sock, &msgh, &vec, 1, len, flags); ++} ++EXPORT_SYMBOL_GPL(db_vsock_recvmsg); ++ ++static int db_vsock_recvcmd(struct socket *cli_socket, char *cmd) ++{ ++ int ret; ++ char rcv; ++ long timeout; ++ struct kvec vec; ++ struct msghdr msg; ++ ++ memset(&vec, 0, sizeof(vec)); ++ memset(&msg, 0, sizeof(msg)); ++ vec.iov_base = &rcv; ++ vec.iov_len = 1; ++ ++ timeout = cli_socket->sk->sk_rcvtimeo; ++ cli_socket->sk->sk_rcvtimeo = DB_INIT_TIMEOUT * HZ; ++ ret = kernel_recvmsg(cli_socket, &msg, &vec, 1, 1, 0); ++ cli_socket->sk->sk_rcvtimeo = timeout; ++ *cmd = rcv; ++ ++ return ret; ++} ++ ++/* ++ * The workqueue handler for vsock work_struct. ++ * ++ * Each worker-pool bound to an actual CPU implements concurrency management ++ * by hooking into the scheduler. The worker-pool is notified whenever an ++ * active worker wakes up or sleeps and keeps track of the number of the ++ * currently runnable workers. Generally, work items are not expected to hog ++ * a CPU and consume many cycles. That means maintaining just enough concurrency ++ * to prevent work processing from stalling should be optimal. ++ * ++ * So it's OK to sleep in a workqueue handler, it won't cause too many worker ++ * threads. ++ */ ++static void db_conn_service(struct work_struct *work) ++{ ++ struct db_conn_info *conn_info = ++ container_of(work, struct db_conn_info, work); ++ struct db_service_entry *service_entry; ++ int len, ret = -1; ++ char cmd; ++ ++ len = db_vsock_recvcmd(conn_info->sock, &cmd); ++ if (len <= 0) ++ goto recv_failed; ++ ++ read_lock(&db_service_lock); ++ list_for_each_entry(service_entry, &db_service_list, list) { ++ if (cmd == service_entry->cmd) { ++ ret = service_entry->handler(conn_info->sock); ++ break; ++ } ++ } ++ read_unlock(&db_service_lock); ++ ++recv_failed: ++ if (ret) { ++ sock_release(conn_info->sock); ++ pr_info("Client connection closed, error code: %d\n", ret); ++ } ++ kfree(conn_info); ++} ++ ++static int db_create_cli_conn(struct socket *sock) ++{ ++ struct db_conn_info *conn; ++ ++ conn = kmalloc(sizeof(*conn), GFP_KERNEL); ++ if (!conn) ++ return -ENOMEM; ++ ++ conn->sock = sock; ++ INIT_WORK(&conn->work, db_conn_service); ++ schedule_work(&conn->work); ++ ++ return 0; ++} ++ ++static int db_vsock_server(void *data) ++{ ++ struct socket *sock; ++ int err; ++ ++ sock = db_create_vsock_listener(db_server_port); ++ if (IS_ERR(sock)) { ++ err = PTR_ERR(sock); ++ pr_err("Init server err: %d\n", err); ++ return err; ++ } ++ ++ while (!kthread_should_stop()) { ++ struct socket *conn; ++ ++ conn = sock_alloc(); ++ if (!conn) ++ return -ENOMEM; ++ ++ conn->type = sock->type; ++ conn->ops = sock->ops; ++ ++ /* 0:propotal 1:kernel */ ++ err = sock->ops->accept(sock, conn, 0, 1); ++ if (err < 0) { ++ pr_err("Server accept err: %d\n", err); ++ sock_release(conn); ++ continue; ++ } ++ ++ err = db_create_cli_conn(conn); ++ if (err) ++ pr_err("Create client connetion err: %d\n", err); ++ } ++ ++ return 0; ++} ++ ++static int db_create_service(void) ++{ ++ struct task_struct *service; ++ int rc = 0; ++ ++ service = kthread_create(db_vsock_server, NULL, "db-vsock-srv"); ++ if (IS_ERR(service)) { ++ rc = PTR_ERR(service); ++ pr_err("Server task create failed, err: %d\n", rc); ++ } else { ++ db_service_task = service; ++ wake_up_process(service); ++ } ++ return rc; ++} ++ ++static int db_vsock_srv_cmdline_set(const char *device, ++ const struct kernel_param *kp) ++{ ++ unsigned int port = 0; ++ int processed, consumed = 0; ++ ++ /* Get "@<port>" */ ++ processed = sscanf(device, "@%u%n", &port, &consumed); ++ if (processed < 1 || device[consumed] || port == 0 || port > 1024) { ++ pr_err("Using @<port> format and port range (0, 1024].\n"); ++ return -EINVAL; ++ } ++ ++ db_server_port = port; ++ return 0; ++} ++ ++static const struct kernel_param_ops db_vsock_srv_cmdline_param_ops = { ++ .set = db_vsock_srv_cmdline_set, ++}; ++ ++device_param_cb(port, &db_vsock_srv_cmdline_param_ops, NULL, 0400); ++ ++int register_db_vsock_service(const char cmd, db_vsock_svc_handler_t handler) ++{ ++ int rc = -EEXIST; ++ struct db_service_entry *service_entry; ++ ++ write_lock(&db_service_lock); ++ list_for_each_entry(service_entry, &db_service_list, list) { ++ if (cmd == service_entry->cmd) { ++ rc = -EEXIST; ++ goto out; ++ } ++ } ++ ++ service_entry = kzalloc(sizeof(*service_entry), GFP_KERNEL); ++ if (!service_entry) { ++ rc = -ENOMEM; ++ goto out; ++ } ++ service_entry->cmd = cmd; ++ service_entry->handler = handler; ++ list_add_tail(&service_entry->list, &db_service_list); ++ rc = 0; ++out: ++ write_unlock(&db_service_lock); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(register_db_vsock_service); ++ ++int unregister_db_vsock_service(const char cmd) ++{ ++ int rc = -EEXIST; ++ struct db_service_entry *service_entry, *n; ++ ++ write_lock(&db_service_lock); ++ list_for_each_entry_safe(service_entry, n, &db_service_list, list) { ++ if (cmd == service_entry->cmd) { ++ list_del(&service_entry->list); ++ rc = 0; ++ break; ++ } ++ } ++ write_unlock(&db_service_lock); ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(unregister_db_vsock_service); ++ ++static int __init db_vsock_srv_init(void) ++{ ++ return db_create_service(); ++} ++ ++late_initcall(db_vsock_srv_init); ++ ++MODULE_AUTHOR("Alibaba, Inc."); ++MODULE_DESCRIPTION("Dragonball vsock server"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/dragonball/upcall_srv.h b/include/dragonball/upcall_srv.h +new file mode 100644 +index 000000000000..1c733982cc30 +--- /dev/null ++++ b/include/dragonball/upcall_srv.h +@@ -0,0 +1,42 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * db_upcall_srv.h Virtual Sockets Server for Dragonball ++ * ++ * Copyright (C) 2022 Alibaba Cloud, Inc ++ * ++ * 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. ++ */ ++ ++#ifndef _DB_UPCALL_SRV_H ++#define _DB_UPCALL_SRV_H ++ ++#include <linux/workqueue.h> ++#include <linux/net.h> ++ ++/* Vsock port to listen for incoming connections. */ ++#define DB_SERVER_PORT 0xDB ++#define DB_RECVBUF_SIZE 0x400 ++#define DB_INIT_TIMEOUT 10 ++ ++/* ++ * Vsock service handler to handle new incoming connections. ++ * ++ * Return: ++ * 0: on success and the callback takes ownership of the sock. ++ * !0: on failure and the callback should keep the sock as is. ++ */ ++typedef int (*db_vsock_svc_handler_t) (struct socket *sock); ++ ++extern int register_db_vsock_service(const char cmd, ++ db_vsock_svc_handler_t handler); ++extern int unregister_db_vsock_service(const char cmd); ++ ++extern struct socket *db_create_vsock_listener(unsigned int port); ++extern int db_vsock_sendmsg(struct socket *sock, char *buf, size_t len); ++extern int db_vsock_recvmsg(struct socket *sock, char *buf, size_t len, ++ int flags); ++ ++#endif /* _DB_UPCALL_SRV_H */ +-- +2.19.1.6.gb485710b + diff --git a/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0002-upcall-introduce-device-manager-upcall-service.patch b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0002-upcall-introduce-device-manager-upcall-service.patch new file mode 100644 index 0000000000..821deb373b --- /dev/null +++ b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0002-upcall-introduce-device-manager-upcall-service.patch @@ -0,0 +1,330 @@ +From 66e67cc3ebbebc9d138fff084c487d6b9d21f60c Mon Sep 17 00:00:00 2001 +From: Chao Wu <chaowu@linux.alibaba.com> +Date: Mon, 21 Nov 2022 19:19:26 +0800 +Subject: [PATCH 2/4] upcall: introduce device manager upcall service + +Different services are registered into upcall server to handle the +request from the client side. This commit introduces devic manager +upcall service and when new message gets into upcall server, cmd `d` is +used for identifying the device manager service. + +After a request is sent to device manager service, db_devmgr_handler +will start handle the request. A kthread `db_devmgr_server` will be +created and it will send CONNECT message to the client side to notify +the client start sending message for device management operations. +`db_devmgr_process` will be used for determining which device operations +will be triggered through msg_type. `get_action` will find out the +action for dealing with the operation and `action` fn will execute the +actual device management operation in the device manager service. + +Signed-off-by: Liu Jiang <gerry@linux.alibaba.com> +Signed-off-by: Zizheng Bian <zizheng.bian@linux.alibaba.com> +Signed-off-by: Chao Wu <chaowu@linux.alibaba.com> +Signed-off-by: WangYu <WangYu@linux.alibaba.com> +Signed-off-by: Xingjun Liu <xingjun.liu@linux.alibaba.com> +--- + drivers/misc/dragonball/upcall_srv/Kconfig | 12 + + drivers/misc/dragonball/upcall_srv/Makefile | 1 + + .../upcall_srv/dragonball_device_manager.c | 235 ++++++++++++++++++ + include/dragonball/device_manager.h | 18 ++ + 4 files changed, 266 insertions(+) + create mode 100644 drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c + create mode 100644 include/dragonball/device_manager.h + +diff --git a/drivers/misc/dragonball/upcall_srv/Kconfig b/drivers/misc/dragonball/upcall_srv/Kconfig +index b00bf1f8637d..6554a9741c00 100644 +--- a/drivers/misc/dragonball/upcall_srv/Kconfig ++++ b/drivers/misc/dragonball/upcall_srv/Kconfig +@@ -12,3 +12,15 @@ config DRAGONBALL_UPCALL_SRV + Sockets communication channels between guest and host/vmm. + + If unsure, say N. ++ ++config DRAGONBALL_DEVICE_MANAGER ++ bool "Vsock Service to Handle Dragonball Device Management Requests" ++ depends on DRAGONBALL_UPCALL_SRV ++ depends on VIRTIO_VSOCKETS ++ default y ++ help ++ This configure implements a vsock service to handle Dragonball device ++ management requests, such as getting device information, hot-plugging ++ devices etc. ++ ++ If unsure, say N. +diff --git a/drivers/misc/dragonball/upcall_srv/Makefile b/drivers/misc/dragonball/upcall_srv/Makefile +index 4102e6c7edef..409c0c11e2e6 100644 +--- a/drivers/misc/dragonball/upcall_srv/Makefile ++++ b/drivers/misc/dragonball/upcall_srv/Makefile +@@ -11,3 +11,4 @@ + # + + obj-$(CONFIG_DRAGONBALL_UPCALL_SRV) += dragonball_upcall_srv.o ++obj-$(CONFIG_DRAGONBALL_DEVICE_MANAGER) += dragonball_device_manager.o +diff --git a/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c b/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c +new file mode 100644 +index 000000000000..ebcb6ef74285 +--- /dev/null ++++ b/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c +@@ -0,0 +1,235 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * drivers/misc/dragonball/vsock_srv/dragonball_device_manager.c ++ * vsock service for device management. ++ * ++ * Copyright (C) 2022 Alibaba Cloud, Inc ++ * ++ * 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. ++ * ++ */ ++ ++#define pr_fmt(fmt) "db-dev-mgr: " fmt ++ ++#include <linux/kthread.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <linux/virtio_mmio.h> ++#include <linux/cpu.h> ++#include <linux/cpumask.h> ++#include <linux/cpuhotplug.h> ++#include <asm/cpu.h> ++#include <dragonball/upcall_srv.h> ++#include <dragonball/device_manager.h> ++#ifdef CONFIG_ARM64 ++#include <linux/irqdomain.h> ++#include <linux/irq.h> ++#endif ++#include <linux/percpu.h> ++#include <linux/device.h> ++#include <asm/numa.h> ++ ++/* ++ * Following designs are adopted to simplify implementation: ++ * 1) fix size messages with padding to ease receiving logic. ++ * 2) binary encoding instead of string encoding because it's on the same host. ++ * 3) synchronous communication in ping-pong mode, one in-fly request at most. ++ * 4) do not support module unloading ++ */ ++ ++/* These definitions are synchronized with dragonball */ ++#define DEV_MGR_MSG_SIZE 0x400 ++#define DEVMGR_CMD_BYTE 'd' ++#define DEVMGR_MAGIC_VERSION 0x444D0100 /* 'DM' + Version 1.0 */ ++#define SHARED_IRQ_NO 5 ++ ++/* Type of request and reply messages. */ ++enum devmgr_msg_type { ++ CONNECT = 0x00000000, ++ ADD_CPU = 0x00000001, ++ DEL_CPU = 0x00000002, ++ ADD_MEM = 0x00000003, ++ DEL_MEM = 0x00000004, ++ ADD_MMIO = 0x00000005, ++ DEL_MMIO = 0x00000006, ++ ADD_PCI = 0x00000007, ++ DEL_PCI = 0x00000008, ++}; ++ ++struct devmgr_msg_header { ++ /* magic version for identifying upcall */ ++ uint32_t magic_version; ++ /* size of the upcall message */ ++ uint32_t msg_size; ++ /* type for the message to identify its usage */ ++ uint32_t msg_type; ++ /* flag for extra information */ ++ uint32_t msg_flags; ++}; ++ ++struct devmgr_req { ++ struct devmgr_msg_header msg_header; ++ union { ++ char pad[DEV_MGR_MSG_SIZE - sizeof(struct devmgr_msg_header)]; ++ } msg_load; ++}; ++ ++struct devmgr_reply { ++ struct devmgr_msg_header msg_header; ++ /* ++ * if ret is 0, it means the operation is successful. ++ * if ret is not 0, return value will be error code. ++ */ ++ int32_t ret; ++ union { ++ char pad[DEV_MGR_MSG_SIZE - sizeof(struct devmgr_msg_header) - sizeof(int32_t)]; ++ } msg_load; ++}; ++ ++struct task_res { ++ struct task_struct *task; ++ struct socket *sock; ++ struct devmgr_req req; ++ struct devmgr_reply reply; ++}; ++ ++typedef int (*action_route_t) (struct devmgr_req *req, ++ struct devmgr_reply *rep); ++ ++static void _fill_msg_header(struct devmgr_msg_header *msg, uint32_t msg_size, ++ uint32_t msg_type, uint32_t msg_flags) ++{ ++ msg->magic_version = DEVMGR_MAGIC_VERSION; ++ msg->msg_size = msg_size; ++ msg->msg_type = msg_type; ++ msg->msg_flags = msg_flags; ++} ++ ++static struct { ++ enum devmgr_msg_type cmd; ++ action_route_t fn; ++} opt_map[] = { ++}; ++ ++static action_route_t get_action(struct devmgr_req *req) ++{ ++ int i; ++ action_route_t action = NULL; ++ int size_opt = ARRAY_SIZE(opt_map); ++ ++ for (i = 0; i < size_opt; i++) { ++ if (opt_map[i].cmd == req->msg_header.msg_type) { ++ action = opt_map[i].fn; ++ break; ++ } ++ } ++ return action; ++} ++ ++static void db_devmgr_process(struct devmgr_req *req, ++ struct devmgr_reply *rep) ++{ ++ int err; ++ action_route_t action; ++ struct devmgr_msg_header *req_mh = &req->msg_header; ++ struct devmgr_msg_header *rep_mh = &rep->msg_header; ++ ++ if (req_mh->magic_version != DEVMGR_MAGIC_VERSION) { ++ _fill_msg_header(rep_mh, 0, req->msg_header.msg_type, 0); ++ return; ++ } ++ ++ action = get_action(req); ++ if (action == NULL) { ++ pr_err("db_devmgr_process : Not found valid command"); ++ rep->ret = -1; ++ _fill_msg_header(rep_mh, 0, req->msg_header.msg_type, 0); ++ return; ++ } ++ ++ err = action(req, rep); ++ if (err) { ++ pr_err("db_devmgr_process : Command run failed, err: %d", err); ++ rep->ret = err; ++ _fill_msg_header(rep_mh, 0, req->msg_header.msg_type, 0); ++ return; ++ } ++} ++ ++static int db_devmgr_server(void *data) ++{ ++ struct task_res *res = (struct task_res *)data; ++ struct devmgr_msg_header *rep_mh = &res->reply.msg_header; ++ int len; ++ ++ _fill_msg_header(rep_mh, 0, CONNECT, 0); ++ len = db_vsock_sendmsg(res->sock, (char *)&res->reply, DEV_MGR_MSG_SIZE); ++ if (len <= 0) { ++ pr_err("db_devmgr_server : Server send message failed, err: %d", len); ++ sock_release(res->sock); ++ kfree(res); ++ return len; ++ } ++ ++ while (!kthread_should_stop()) { ++ len = db_vsock_recvmsg(res->sock, (char *)&res->req, ++ DEV_MGR_MSG_SIZE, 0); ++ if (len <= 0) ++ break; ++ ++ /* The result(OK or Error) will fill into res->reply field */ ++ db_devmgr_process(&res->req, &res->reply); ++ ++ len = db_vsock_sendmsg(res->sock, (char *)&res->reply, ++ DEV_MGR_MSG_SIZE); ++ if (len <= 0) ++ break; ++ } ++ ++ /* TODO: check who shutdown the socket, receiving or sending. */ ++ sock_release(res->sock); ++ kfree(res); ++ return 0; ++} ++ ++static int db_devmgr_handler(struct socket *sock) ++{ ++ struct task_res *res; ++ struct task_struct *conn_task; ++ ++ /* TODO: ensure singleton, only one server exists */ ++ res = kzalloc(sizeof(*res), GFP_KERNEL); ++ if (!res) ++ return -ENOMEM; ++ ++ res->sock = sock; ++ conn_task = kthread_create(db_devmgr_server, res, "db_dev_mgr"); ++ if (IS_ERR(conn_task)) { ++ pr_err("db_devmgr_handler : Client process thread create failed, err: %d", ++ (int)PTR_ERR(conn_task)); ++ goto failed; ++ } else { ++ res->task = conn_task; ++ wake_up_process(conn_task); ++ } ++ ++ return 0; ++failed: ++ kfree(res); ++ return PTR_ERR(conn_task); ++} ++ ++static int __init db_device_manager_init(void) ++{ ++ return register_db_vsock_service(DEVMGR_CMD_BYTE, db_devmgr_handler); ++} ++ ++late_initcall(db_device_manager_init); ++ ++MODULE_AUTHOR("Alibaba, Inc."); ++MODULE_DESCRIPTION("Dragonball Device Manager"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/dragonball/device_manager.h b/include/dragonball/device_manager.h +new file mode 100644 +index 000000000000..a1713e9f026d +--- /dev/null ++++ b/include/dragonball/device_manager.h +@@ -0,0 +1,18 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * device_manager.h Device Manager for Dragonball ++ * ++ * Copyright (C) 2022 Alibaba Cloud, Inc ++ * ++ * 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. ++ */ ++ ++#ifndef _DB_DEVICE_MANAGER_H ++#define _DB_DEVICE_MANAGER_H ++ ++#include <linux/device.h> ++ ++#endif /* _DB_DEVICE_MANAGER_H */ +-- +2.19.1.6.gb485710b + diff --git a/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0003-upcall-add-cpu-hotplug-hot-unplug-into-device-manage.patch b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0003-upcall-add-cpu-hotplug-hot-unplug-into-device-manage.patch new file mode 100644 index 0000000000..0d1097b650 --- /dev/null +++ b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0003-upcall-add-cpu-hotplug-hot-unplug-into-device-manage.patch @@ -0,0 +1,301 @@ +From 901fb71c77144b170465611617f0f25099d8a780 Mon Sep 17 00:00:00 2001 +From: Chao Wu <chaowu@linux.alibaba.com> +Date: Mon, 21 Nov 2022 19:44:50 +0800 +Subject: [PATCH 3/4] upcall: add cpu hotplug/hot-unplug into device manager + service + +Add cpu hotplug and hot-unplug support into device manager. In the +`devmgr_req` message, `msg_type` ADD_CPU in `msg_header` will trigger +`add_cpu_dev` action and DEL_CPU will trigger `del_cpu_dev` action, and +we use `apic_ids` and `count` delivered in `cpu_dev_info` to notify +which and how many cpus will be hotplugged / hot-unplugged. + +`add_cpu_dev` and `del_cpu_dev` will eventually trigger `add_cpu_upcall` +and `del_cpu_upcall` to trigger the cpu hotplug / hot-unplug process in +the kernel. After the cpu hotplug / hot-unplug process, +`cpu_event_notification` will generate device manager reply to the +client side. + +Signed-off-by: Liu Jiang <gerry@linux.alibaba.com> +Signed-off-by: Zizheng Bian <zizheng.bian@linux.alibaba.com> +Signed-off-by: Chao Wu <chaowu@linux.alibaba.com> +Signed-off-by: WangYu <WangYu@linux.alibaba.com> +Signed-off-by: Xingjun Liu <xingjun.liu@linux.alibaba.com> +--- + drivers/misc/dragonball/upcall_srv/Kconfig | 11 + + .../upcall_srv/dragonball_device_manager.c | 219 ++++++++++++++++++ + 2 files changed, 230 insertions(+) + +diff --git a/drivers/misc/dragonball/upcall_srv/Kconfig b/drivers/misc/dragonball/upcall_srv/Kconfig +index 6554a9741c00..b237882a2928 100644 +--- a/drivers/misc/dragonball/upcall_srv/Kconfig ++++ b/drivers/misc/dragonball/upcall_srv/Kconfig +@@ -24,3 +24,14 @@ config DRAGONBALL_DEVICE_MANAGER + devices etc. + + If unsure, say N. ++ ++config DRAGONBALL_HOTPLUG_CPU ++ bool "CPU hotplug/hotunplug support" ++ depends on DRAGONBALL_DEVICE_MANAGER ++ default y ++ help ++ This configure implements a vCPU hotplug/hotunplug support, vmm ++ should send hotplug request by vsock which follow special data ++ structure with command and parameter to hot-pluging an vCPU. ++ ++ If unsure, say N. +diff --git a/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c b/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c +index ebcb6ef74285..210ef5d6c9d5 100644 +--- a/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c ++++ b/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c +@@ -75,9 +75,20 @@ struct devmgr_req { + struct devmgr_msg_header msg_header; + union { + char pad[DEV_MGR_MSG_SIZE - sizeof(struct devmgr_msg_header)]; ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) ++ struct { ++ uint8_t count; ++ uint8_t apic_ver; ++ uint8_t apic_ids[256]; ++ } cpu_dev_info; ++#endif + } msg_load; + }; + ++struct cpu_dev_reply_info { ++ uint32_t apic_index; ++}; ++ + struct devmgr_reply { + struct devmgr_msg_header msg_header; + /* +@@ -87,6 +98,9 @@ struct devmgr_reply { + int32_t ret; + union { + char pad[DEV_MGR_MSG_SIZE - sizeof(struct devmgr_msg_header) - sizeof(int32_t)]; ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) ++ struct cpu_dev_reply_info cpu_dev_info; ++#endif + } msg_load; + }; + +@@ -109,10 +123,215 @@ static void _fill_msg_header(struct devmgr_msg_header *msg, uint32_t msg_size, + msg->msg_flags = msg_flags; + } + ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) && defined(CONFIG_X86_64) ++static int get_cpu_id(int apic_id) ++{ ++ int i; ++ ++ for (i = 0; i < num_processors; i++) { ++ if (cpu_physical_id(i) == apic_id) ++ return i; ++ } ++ return -1; ++} ++ ++/** ++ * Return the first failed hotplug index of the apic_ids to dragonball. ++ * If it is not equal to the count of all hotplug needed vcpus, ++ * we will rollback the vcpus from apics_ids[0] to apic_ids[i-1] in dragonball. ++ */ ++static void cpu_event_notification( ++ uint8_t apic_ids_index, ++ int ret, ++ uint32_t action_type, ++ struct devmgr_reply *rep) ++{ ++ pr_info("cpu event notification: apic ids index %d", apic_ids_index); ++ rep->msg_load.cpu_dev_info.apic_index = apic_ids_index; ++ rep->ret = ret; ++ _fill_msg_header(&rep->msg_header, ++ sizeof(struct cpu_dev_reply_info), action_type, 0); ++} ++#endif ++ ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) && defined(CONFIG_X86_64) ++static int add_cpu_upcall(int apic_id, uint8_t apic_ver) ++{ ++ int cpu_id, node_id; ++ int ret; ++ ++ pr_info("adding vcpu apic_id %d", apic_id); ++ ++ /** ++ * Get the mutex lock for hotplug and cpu update and cpu write lock. ++ * So that other threads won't influence the hotplug process. ++ */ ++ lock_device_hotplug(); ++ cpu_maps_update_begin(); ++ cpu_hotplug_begin(); ++ ++ cpu_id = generic_processor_info(apic_id, apic_ver); ++ if (cpu_id < 0) { ++ pr_err("cpu (apic id %d) cannot be added, generic processor info failed", apic_id); ++ ret = -EINVAL; ++ goto rollback_generic_cpu; ++ } ++ ++ /* update numa mapping for hot-plugged cpus. */ ++ node_id = numa_cpu_node(cpu_id); ++ if (node_id != NUMA_NO_NODE) ++ numa_set_node(cpu_id, node_id); ++ ++ ret = arch_register_cpu(cpu_id); ++ if (ret) { ++ pr_err("cpu %d cannot be added, register cpu failed %d", cpu_id, ret); ++ goto rollback_register_cpu; ++ } ++ ++ cpu_hotplug_done(); ++ cpu_maps_update_done(); ++ unlock_device_hotplug(); ++ ++ ret = add_cpu(cpu_id); ++ if (ret) { ++ pr_err("cpu %d cannot be added, cpu up failed: %d", cpu_id, ret); ++ goto rollback_cpu_up; ++ } ++ return ret; ++ ++rollback_cpu_up: ++ arch_unregister_cpu(cpu_id); ++ set_cpu_present(cpu_id, false); ++ per_cpu(x86_cpu_to_apicid, cpu_id) = -1; ++ num_processors--; ++ return ret; ++ ++rollback_register_cpu: ++ set_cpu_present(cpu_id, false); ++ per_cpu(x86_cpu_to_apicid, cpu_id) = -1; ++ num_processors--; ++rollback_generic_cpu: ++ cpu_hotplug_done(); ++ cpu_maps_update_done(); ++ unlock_device_hotplug(); ++ return ret; ++} ++ ++static int del_cpu_upcall(int apic_id) ++{ ++ int cpu_id = get_cpu_id(apic_id); ++ int ret; ++ ++ if (cpu_id == 0) { ++ pr_err("cannot del bootstrap processor."); ++ return -EINVAL; ++ } ++ pr_info("deleting vcpu %d", cpu_id); ++ ret = remove_cpu(cpu_id); ++ if (ret) { ++ pr_err("del vcpu failed, err: %d", ret); ++ return ret; ++ } ++ ++ lock_device_hotplug(); ++ cpu_maps_update_begin(); ++ cpu_hotplug_begin(); ++ ++ arch_unregister_cpu(cpu_id); ++ set_cpu_present(cpu_id, false); ++ per_cpu(x86_cpu_to_apicid, cpu_id) = -1; ++ num_processors--; ++ ++ cpu_hotplug_done(); ++ cpu_maps_update_done(); ++ unlock_device_hotplug(); ++ ++ return ret; ++} ++ ++static int add_cpu_dev(struct devmgr_req *req, ++ struct devmgr_reply *rep) ++{ ++ int ret; ++ uint8_t i; ++ int apic_id; ++ ++ uint8_t count = req->msg_load.cpu_dev_info.count; ++ uint8_t apic_ver = req->msg_load.cpu_dev_info.apic_ver; ++ uint8_t *apic_ids = req->msg_load.cpu_dev_info.apic_ids; ++ ++ pr_info("add vcpu number: %d", count); ++ ++ for (i = 0; i < count; ++i) { ++ apic_id = apic_ids[i]; ++ if (get_cpu_id(apic_id) != -1) { ++ pr_err("cpu cannot be added: apci_id %d is already been used.", apic_id); ++ ret = -EINVAL; ++ return ret; ++ } ++ } ++ ++ for (i = 0; i < count; ++i) { ++ apic_id = apic_ids[i]; ++ ret = add_cpu_upcall(apic_id, apic_ver); ++ if (ret != 0) ++ break; ++ } ++ ++ if (!ret) ++ cpu_event_notification(i, ret, ADD_CPU, rep); ++ return ret; ++} ++ ++static int del_cpu_dev(struct devmgr_req *req, ++ struct devmgr_reply *rep) ++{ ++ int ret; ++ uint8_t i; ++ int cpu_id; ++ ++ uint8_t count = req->msg_load.cpu_dev_info.count; ++ uint8_t *apic_ids = req->msg_load.cpu_dev_info.apic_ids; ++ ++ pr_info("del vcpu number : %d", count); ++ ++ if (count >= num_processors) { ++ pr_err("cpu del parameter check error: cannot remove all vcpus"); ++ ret = -EINVAL; ++ cpu_event_notification(0, ret, DEL_CPU, rep); ++ return ret; ++ } ++ ++ for (i = 0; i < count; ++i) { ++ cpu_id = get_cpu_id(apic_ids[i]); ++ if (!cpu_possible(cpu_id)) { ++ pr_err("cpu %d cannot be deleted: cpu not possible", cpu_id); ++ ret = -EINVAL; ++ cpu_event_notification(0, ret, DEL_CPU, rep); ++ return ret; ++ } ++ } ++ ++ for (i = 0; i < count; ++i) { ++ ret = del_cpu_upcall(apic_ids[i]); ++ if (ret != 0) ++ break; ++ } ++ ++ if (!ret) ++ cpu_event_notification(i, ret, DEL_CPU, rep); ++ return ret; ++} ++#endif ++ + static struct { + enum devmgr_msg_type cmd; + action_route_t fn; + } opt_map[] = { ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) && defined(CONFIG_X86_64) ++ {ADD_CPU, add_cpu_dev}, ++ {DEL_CPU, del_cpu_dev}, ++#endif + }; + + static action_route_t get_action(struct devmgr_req *req) +-- +2.19.1.6.gb485710b + diff --git a/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0004-upcall-add-virtio-mmio-hotplug-hot-unplug-into-devic.patch b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0004-upcall-add-virtio-mmio-hotplug-hot-unplug-into-devic.patch new file mode 100644 index 0000000000..105f7a7bf0 --- /dev/null +++ b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0004-upcall-add-virtio-mmio-hotplug-hot-unplug-into-devic.patch @@ -0,0 +1,417 @@ +From 90ced3463137076b9df2200b5c6ad720660c6bfc Mon Sep 17 00:00:00 2001 +From: Chao Wu <chaowu@linux.alibaba.com> +Date: Wed, 23 Nov 2022 19:23:47 +0800 +Subject: [PATCH 4/4] upcall: add virtio-mmio hotplug/hot-unplug into device + manager service + +Add virtio-mmio hotplug/hot-unplug support into device manager. In the +`devmgr_req` message, `msg_type` ADD_MMIO in `msg_header` will trigger +`add_mmio_dev` action and DEL_MMIO will trigger `del_mmio_dev` action, +and we use `mmio_base`, `mmio_size` and `mmio_irq` delivered in +`add_mmio_dev` to notify how to hotplug the virtio-mmio device + +Also `virtio_mmio_add_device` and `virtio_mmio_del_device` are +introduced under /drivers/virtio/virtio_mmio.c, and we extract +`vm_add_device` from `vm_cmdline_set` to help hotplug virtio-mmio +device. + +Signed-off-by: Liu Jiang <gerry@linux.alibaba.com> +Signed-off-by: Zizheng Bian <zizheng.bian@linux.alibaba.com> +Signed-off-by: Chao Wu <chaowu@linux.alibaba.com> +Signed-off-by: WangYu <WangYu@linux.alibaba.com> +Signed-off-by: Xingjun Liu <xingjun.liu@linux.alibaba.com> +--- + drivers/misc/dragonball/upcall_srv/Kconfig | 12 ++ + .../upcall_srv/dragonball_device_manager.c | 112 ++++++++++++++ + drivers/virtio/Kconfig | 14 ++ + drivers/virtio/virtio_mmio.c | 138 +++++++++++++++--- + include/dragonball/device_manager.h | 5 + + 5 files changed, 259 insertions(+), 22 deletions(-) + +diff --git a/drivers/misc/dragonball/upcall_srv/Kconfig b/drivers/misc/dragonball/upcall_srv/Kconfig +index b237882a2928..fc83f03c2edd 100644 +--- a/drivers/misc/dragonball/upcall_srv/Kconfig ++++ b/drivers/misc/dragonball/upcall_srv/Kconfig +@@ -25,6 +25,18 @@ config DRAGONBALL_DEVICE_MANAGER + + If unsure, say N. + ++config DRAGONBALL_HOTPLUG_VIRTIO_MMIO ++ bool "Virtio-MMIO device hotplug/hotunplug support" ++ depends on DRAGONBALL_DEVICE_MANAGER ++ default y ++ help ++ This configure implements a Virtio-MMIO device hotplug/hotunplug ++ support, vmm should send hotplug request by vsock which follow ++ special data structure with command and parameter to hot-pluging ++ an MMIO device. ++ ++ If unsure, say N. ++ + config DRAGONBALL_HOTPLUG_CPU + bool "CPU hotplug/hotunplug support" + depends on DRAGONBALL_DEVICE_MANAGER +diff --git a/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c b/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c +index 210ef5d6c9d5..5a95b2ba63e8 100644 +--- a/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c ++++ b/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c +@@ -75,6 +75,13 @@ struct devmgr_req { + struct devmgr_msg_header msg_header; + union { + char pad[DEV_MGR_MSG_SIZE - sizeof(struct devmgr_msg_header)]; ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_VIRTIO_MMIO) ++ struct { ++ uint64_t mmio_base; ++ uint64_t mmio_size; ++ uint32_t mmio_irq; ++ } add_mmio_dev; ++#endif + #if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) + struct { + uint8_t count; +@@ -98,6 +105,10 @@ struct devmgr_reply { + int32_t ret; + union { + char pad[DEV_MGR_MSG_SIZE - sizeof(struct devmgr_msg_header) - sizeof(int32_t)]; ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_VIRTIO_MMIO) ++ struct { ++ } add_mmio_dev; ++#endif + #if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) + struct cpu_dev_reply_info cpu_dev_info; + #endif +@@ -114,6 +125,62 @@ struct task_res { + typedef int (*action_route_t) (struct devmgr_req *req, + struct devmgr_reply *rep); + ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_VIRTIO_MMIO) ++#ifdef CONFIG_ARM64 ++static uint32_t get_device_virq(uint32_t pin) ++{ ++ uint32_t virq; ++ struct device_node *node; ++ struct irq_fwspec dummy_fwspec = { ++ .param_count = 3, ++ .param = {0, 0, IRQ_TYPE_EDGE_RISING} ++ }; ++ ++ node = of_find_node_by_name(NULL, "intc"); ++ if (!node) { ++ pr_err("interrupt controller device node not found."); ++ return 0; ++ } ++ dummy_fwspec.param[1] = pin; ++ dummy_fwspec.fwnode = of_node_to_fwnode(node); ++ virq = irq_create_fwspec_mapping(&dummy_fwspec); ++ of_node_put(node); ++ return virq; ++} ++#elif defined(CONFIG_X86_64) ++static inline uint32_t get_device_virq(uint32_t irq) ++{ ++ return irq; ++} ++#endif ++ ++static int get_dev_resource(struct devmgr_req *req, struct resource *res) ++{ ++ uint64_t base = req->msg_load.add_mmio_dev.mmio_base; ++ uint64_t size = req->msg_load.add_mmio_dev.mmio_size; ++ uint32_t irq = req->msg_load.add_mmio_dev.mmio_irq; ++ uint32_t virq; ++ ++ if (req->msg_header.msg_size != sizeof(req->msg_load.add_mmio_dev)) ++ return -EINVAL; ++ ++ res[0].flags = IORESOURCE_MEM; ++ res[0].start = base; ++ res[0].end = base + size - 1; ++ res[1].flags = IORESOURCE_IRQ; ++ virq = get_device_virq(irq); ++ if (!virq) ++ return -EINVAL; ++ res[1].start = res[1].end = virq; ++ ++ /* detect the irq sharing mode */ ++ if (irq == SHARED_IRQ_NO) ++ res[1].flags |= IORESOURCE_IRQ_SHAREABLE; ++ ++ return 0; ++} ++#endif ++ + static void _fill_msg_header(struct devmgr_msg_header *msg, uint32_t msg_size, + uint32_t msg_type, uint32_t msg_flags) + { +@@ -154,6 +221,47 @@ static void cpu_event_notification( + } + #endif + ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_VIRTIO_MMIO) ++static int add_mmio_dev(struct devmgr_req *req, ++ struct devmgr_reply *rep) ++{ ++ int ret; ++ struct resource res[2] = {}; ++ struct devmgr_msg_header *rep_mh = &rep->msg_header; ++ ++ ret = get_dev_resource(req, res); ++ if (ret) ++ return ret; ++ ++ ret = virtio_mmio_add_device(res, ARRAY_SIZE(res)); ++ if (!ret) { ++ rep->ret = ret; ++ _fill_msg_header(rep_mh, 0, ADD_MMIO, 0); ++ } ++ return ret; ++} ++ ++static int del_mmio_dev(struct devmgr_req *req, ++ struct devmgr_reply *rep) ++{ ++ int ret; ++ struct resource res[2] = {}; ++ struct devmgr_msg_header *rep_mh = &rep->msg_header; ++ ++ ret = get_dev_resource(req, res); ++ if (ret) ++ return ret; ++ ++ ret = virtio_mmio_del_device(res, ARRAY_SIZE(res)); ++ if (!ret) { ++ rep->ret = ret; ++ _fill_msg_header(rep_mh, 0, DEL_MMIO, 0); ++ } ++ return ret; ++} ++#endif ++ ++ + #if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) && defined(CONFIG_X86_64) + static int add_cpu_upcall(int apic_id, uint8_t apic_ver) + { +@@ -328,6 +436,10 @@ static struct { + enum devmgr_msg_type cmd; + action_route_t fn; + } opt_map[] = { ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_VIRTIO_MMIO) ++ {ADD_MMIO, add_mmio_dev}, ++ {DEL_MMIO, del_mmio_dev}, ++#endif + #if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) && defined(CONFIG_X86_64) + {ADD_CPU, add_cpu_dev}, + {DEL_CPU, del_cpu_dev}, + +diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig +index 0a53a6123..f599e80a5 100644 +--- a/drivers/virtio/Kconfig ++++ b/drivers/virtio/Kconfig +@@ -173,4 +173,18 @@ config VIRTIO_DMA_SHARED_BUFFER + This option adds a flavor of dma buffers that are backed by + virtio resources. + ++config VIRTIO_MMIO_DRAGONBALL ++ bool "Enable features for Dragonball virtio MMIO devcies" ++ default n ++ depends on VIRTIO_MMIO ++ depends on X86_64 || ARM64 ++ select X86_PLATFORM_MSI ++ select VIRTIO_MMIO_MSI ++ help ++ The Dragonball VMM implements several optimizations for MMIO virtio ++ devices. This option enables support of those optimization features: ++ - virtio-mmio hotplug through upcall ++ ++ If unsure, say N ++ + endif # VIRTIO_MENU +diff --git a/include/dragonball/device_manager.h b/include/dragonball/device_manager.h +index a1713e9f026d..785761c47f97 100644 +--- a/include/dragonball/device_manager.h ++++ b/include/dragonball/device_manager.h +@@ -15,4 +15,9 @@ + + #include <linux/device.h> + ++#if defined(CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES) ++int virtio_mmio_add_device(struct resource *resources, size_t res_size); ++int virtio_mmio_del_device(struct resource *resources, size_t res_size); ++#endif ++ + #endif /* _DB_DEVICE_MANAGER_H */ + +diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c +index de1a08108..2d13dd6e3 100644 +--- a/drivers/virtio/virtio_mmio.c ++++ b/drivers/virtio/virtio_mmio.c +@@ -700,16 +700,41 @@ static struct device vm_cmdline_parent = { + static int vm_cmdline_parent_registered; + static int vm_cmdline_id; + ++static int vm_add_device(struct resource *resources, size_t res_size) ++{ ++ int err; ++ struct platform_device *pdev; ++ ++ if (!vm_cmdline_parent_registered) { ++ err = device_register(&vm_cmdline_parent); ++ if (err) { ++ pr_err("Failed to register parent device!\n"); ++ return err; ++ } ++ vm_cmdline_parent_registered = 1; ++ } ++ ++ pr_info("Registering device virtio-mmio.%d at 0x%llx-0x%llx, IRQ %d.\n", ++ vm_cmdline_id, ++ (unsigned long long)resources[0].start, ++ (unsigned long long)resources[0].end, ++ (int)resources[1].start); ++ ++ pdev = platform_device_register_resndata(&vm_cmdline_parent, ++ "virtio-mmio", vm_cmdline_id++, ++ resources, res_size, NULL, 0); ++ ++ return PTR_ERR_OR_ZERO(pdev); ++} ++ + static int vm_cmdline_set(const char *device, + const struct kernel_param *kp) + { +- int err; + struct resource resources[2] = {}; + char *str; + long long base, size; + unsigned int irq; + int processed, consumed = 0; +- struct platform_device *pdev; + + /* Consume "size" part of the command line parameter */ + size = memparse(device, &str); +@@ -734,27 +759,7 @@ static int vm_cmdline_set(const char *device, + resources[1].flags = IORESOURCE_IRQ; + resources[1].start = resources[1].end = irq; + +- if (!vm_cmdline_parent_registered) { +- err = device_register(&vm_cmdline_parent); +- if (err) { +- put_device(&vm_cmdline_parent); +- pr_err("Failed to register parent device!\n"); +- return err; +- } +- vm_cmdline_parent_registered = 1; +- } +- +- pr_info("Registering device virtio-mmio.%d at 0x%llx-0x%llx, IRQ %d.\n", +- vm_cmdline_id, +- (unsigned long long)resources[0].start, +- (unsigned long long)resources[0].end, +- (int)resources[1].start); +- +- pdev = platform_device_register_resndata(&vm_cmdline_parent, +- "virtio-mmio", vm_cmdline_id++, +- resources, ARRAY_SIZE(resources), NULL, 0); +- +- return PTR_ERR_OR_ZERO(pdev); ++ return vm_add_device(resources, ARRAY_SIZE(resources)); + } + + static int vm_cmdline_get_device(struct device *dev, void *data) +@@ -804,6 +809,94 @@ static void vm_unregister_cmdline_devices(void) + } + } + ++#ifdef CONFIG_DRAGONBALL_DEVICE_MANAGER ++static int vm_match_device(struct device *dev, void *data) ++{ ++ struct resource *resource = (struct resource *)data; ++ struct platform_device *pdev = to_platform_device(dev); ++ ++ if ((pdev->resource[0].start == resource[0].start) && ++ (pdev->resource[0].end == resource[0].end) && ++ (pdev->resource[1].start == resource[1].start)) ++ return 1; ++ return 0; ++} ++ ++static struct device *vm_find_device(struct resource *res) ++{ ++ return device_find_child(&vm_cmdline_parent, res, vm_match_device); ++} ++ ++static int vm_device_overlap(struct device *dev, void *data) ++{ ++ struct resource *res = (struct resource *)data; ++ struct platform_device *pdev = to_platform_device(dev); ++ ++ /* Detect IRQ number conflicts except shared IRQs. */ ++ if (!(res[1].flags & IORESOURCE_IRQ_SHAREABLE) && ++ (pdev->resource[1].start == res[1].start)) { ++ return 1; ++ } ++ ++ /* Detect device MMIO addresses overlapping */ ++ if ((pdev->resource[0].start < res[0].end) && ++ (pdev->resource[0].end > res[0].start)) { ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static struct device *vm_detect_resource(struct resource *res) ++{ ++ /* return NULL if no resource overlapped */ ++ return device_find_child(&vm_cmdline_parent, res, vm_device_overlap); ++} ++ ++int virtio_mmio_add_device(struct resource *resources, size_t res_size) ++{ ++ int err; ++ struct device *dev; ++ ++ if (res_size < 2 || !resources) ++ return -EINVAL; ++ ++ dev = vm_detect_resource(resources); ++ if (dev) { ++ put_device(dev); ++ return -EEXIST; ++ } ++ ++ lock_device_hotplug(); ++ err = vm_add_device(resources, res_size); ++ unlock_device_hotplug(); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(virtio_mmio_add_device); ++ ++int virtio_mmio_del_device(struct resource *resources, size_t res_size) ++{ ++ int ret; ++ struct device *dev; ++ ++ if (res_size < 2 || !resources) ++ return -EINVAL; ++ ++ dev = vm_find_device(resources); ++ if (!dev) ++ return -ENODEV; ++ ++ put_device(dev); ++ lock_device_hotplug(); ++ ret = vm_unregister_cmdline_device(dev, NULL); ++ unlock_device_hotplug(); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(virtio_mmio_del_device); ++#endif /* CONFIG_DRAGONBALL_DEVICE_MANAGER */ ++ + #else + + static void vm_unregister_cmdline_devices(void) +-- +2.19.1.6.gb485710b diff --git a/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0005-upcall-dragonball-devmgr-suppots-cpu-hotplug-on-arm6.patch b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0005-upcall-dragonball-devmgr-suppots-cpu-hotplug-on-arm6.patch new file mode 100644 index 0000000000..74dcc732e5 --- /dev/null +++ b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0005-upcall-dragonball-devmgr-suppots-cpu-hotplug-on-arm6.patch @@ -0,0 +1,163 @@ +From 16e3b3da9fb8b79b006d8c9d1f68b2dec9980d72 Mon Sep 17 00:00:00 2001 +Message-Id: <16e3b3da9fb8b79b006d8c9d1f68b2dec9980d72.1685428663.git.jiyunxue@linux.alibaba.com> +From: xuejun-xj <jiyunxue@linux.alibaba.com> +Date: Wed, 10 May 2023 13:55:43 +0800 +Subject: [PATCH 1/3] upcall: dragonball-devmgr suppots cpu hotplug on arm64 + +Enable vcpuhotplug feature on aarch64 in guest kernel. It communicates +with dragonball by using upcall. This commit does these changes: + +1. Wraps x86 related fields with CONFIG_X86_64. +2. Add "cpu_event_notification" for arm64. +3. Add "add_cpu_dev" and "del_cpu_dev" for arm64. + +Signed-off-by: xuejun-xj <jiyunxue@linux.alibaba.com> +Reviewed-by : Chao Wu <chaowu@linux.alibaba.com> +Reviewed-by: Zizheng Bian <zizheng.bian@linux.alibaba.com> +Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com> +--- + .../upcall_srv/dragonball_device_manager.c | 84 ++++++++++++++++++- + 1 file changed, 81 insertions(+), 3 deletions(-) + +diff --git a/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c b/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c +index 5a95b2ba63e8..088d38623b8d 100644 +--- a/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c ++++ b/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c +@@ -85,15 +85,21 @@ struct devmgr_req { + #if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) + struct { + uint8_t count; ++#ifdef CONFIG_X86_64 + uint8_t apic_ver; + uint8_t apic_ids[256]; ++#endif + } cpu_dev_info; + #endif + } msg_load; + }; + + struct cpu_dev_reply_info { ++#if defined(CONFIG_X86_64) + uint32_t apic_index; ++#elif defined(CONFIG_ARM64) ++ uint32_t cpu_id; ++#endif + }; + + struct devmgr_reply { +@@ -190,7 +196,8 @@ static void _fill_msg_header(struct devmgr_msg_header *msg, uint32_t msg_size, + msg->msg_flags = msg_flags; + } + +-#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) && defined(CONFIG_X86_64) ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) ++#if defined(CONFIG_X86_64) + static int get_cpu_id(int apic_id) + { + int i; +@@ -219,6 +226,24 @@ static void cpu_event_notification( + _fill_msg_header(&rep->msg_header, + sizeof(struct cpu_dev_reply_info), action_type, 0); + } ++#elif defined(CONFIG_ARM64) ++/** ++ * Return the first failed hotplug index of the cpu_id to dragonball. ++ * If hotplug/hotunplug succeeds, it will equals to the expected cpu count. ++ */ ++static void cpu_event_notification( ++ uint8_t cpu_id, ++ int ret, ++ uint32_t action_type, ++ struct devmgr_reply *rep) ++{ ++ pr_info("cpu event notification: cpu_id %d\n", cpu_id); ++ rep->msg_load.cpu_dev_info.cpu_id = cpu_id; ++ rep->ret = ret; ++ _fill_msg_header(&rep->msg_header, ++ sizeof(struct cpu_dev_reply_info), action_type, 0); ++} ++#endif + #endif + + #if defined(CONFIG_DRAGONBALL_HOTPLUG_VIRTIO_MMIO) +@@ -262,7 +287,8 @@ static int del_mmio_dev(struct devmgr_req *req, + #endif + + +-#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) && defined(CONFIG_X86_64) ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) ++#if defined(CONFIG_X86_64) + static int add_cpu_upcall(int apic_id, uint8_t apic_ver) + { + int cpu_id, node_id; +@@ -430,6 +456,58 @@ static int del_cpu_dev(struct devmgr_req *req, + cpu_event_notification(i, ret, DEL_CPU, rep); + return ret; + } ++#elif defined(CONFIG_ARM64) ++static int add_cpu_dev(struct devmgr_req *req, struct devmgr_reply *rep) ++{ ++ int i, ret = 0; ++ unsigned int cpu_id, nr_online_cpus; ++ uint8_t count = req->msg_load.cpu_dev_info.count; ++ ++ nr_online_cpus = num_online_cpus(); ++ ++ pr_info("Current vcpu number: %d, Add vcpu number: %d\n", ++ nr_online_cpus, count); ++ ++ for (i = 0; i < count; ++i) { ++ cpu_id = nr_online_cpus + i; ++ ret = add_cpu(cpu_id); ++ if (ret != 0) ++ break; ++ } ++ ++ cpu_event_notification(nr_online_cpus + i, ret, ADD_CPU, rep); ++ return ret; ++} ++ ++static int del_cpu_dev(struct devmgr_req *req, struct devmgr_reply *rep) ++{ ++ int i, ret = 0; ++ unsigned int cpu_id, nr_online_cpus; ++ uint8_t count = req->msg_load.cpu_dev_info.count; ++ ++ nr_online_cpus = num_online_cpus(); ++ ++ pr_info("Current vcpu number: %d, Delete vcpu number: %d\n", ++ nr_online_cpus, count); ++ ++ if (count >= nr_online_cpus) { ++ pr_err("cpu del parameter check error: cannot remove all vcpus\n"); ++ ret = -EINVAL; ++ cpu_event_notification(0, ret, DEL_CPU, rep); ++ return ret; ++ } ++ ++ for (i = 0; i < count; ++i) { ++ cpu_id = nr_online_cpus - i - 1; ++ ret = remove_cpu(cpu_id); ++ if (ret != 0) ++ break; ++ } ++ ++ cpu_event_notification(nr_online_cpus - i, ret, DEL_CPU, rep); ++ return ret; ++} ++#endif + #endif + + static struct { +@@ -440,7 +518,7 @@ static struct { + {ADD_MMIO, add_mmio_dev}, + {DEL_MMIO, del_mmio_dev}, + #endif +-#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) && defined(CONFIG_X86_64) ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) + {ADD_CPU, add_cpu_dev}, + {DEL_CPU, del_cpu_dev}, + #endif +-- +2.28.0 + diff --git a/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0006-msi-control-msi-irq-number-activated.patch b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0006-msi-control-msi-irq-number-activated.patch new file mode 100644 index 0000000000..e19983952c --- /dev/null +++ b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0006-msi-control-msi-irq-number-activated.patch @@ -0,0 +1,66 @@ +From 6e07ca77fe7b5c15e0e98d9e86294c7dd2553a5a Mon Sep 17 00:00:00 2001 +Message-Id: <6e07ca77fe7b5c15e0e98d9e86294c7dd2553a5a.1685428663.git.jiyunxue@linux.alibaba.com> +In-Reply-To: <16e3b3da9fb8b79b006d8c9d1f68b2dec9980d72.1685428663.git.jiyunxue@linux.alibaba.com> +References: <16e3b3da9fb8b79b006d8c9d1f68b2dec9980d72.1685428663.git.jiyunxue@linux.alibaba.com> +From: xuejun-xj <jiyunxue@linux.alibaba.com> +Date: Wed, 10 May 2023 14:51:40 +0800 +Subject: [PATCH 2/3] msi: control msi irq number activated + +When passthroughing pci device, kernel will initialize and activate +(max_cpu_count+1) msi irq. However, in vcpu hotplugging situation, +because of vgic, max_cpu_count may be greater than online_cpu_count. +Those offline cpus will also be activated by kernel, which cause failure +of passthroughing pci device. + +To solve this problem, this patch add a function +"check_affinity_mask_online" to check if msi_desc->affinity contains +online cpus. If current cpu is offline, it will continue the for loop to +skip activating related irq. + +Signed-off-by: xuejun-xj <jiyunxue@linux.alibaba.com> +Reviewed-by: Shuo Tan <shuo.tan@linux.alibaba.com> +Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com> +--- + kernel/irq/msi.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c +index 77e513e2e..3a35011ce 100644 +--- a/kernel/irq/msi.c ++++ b/kernel/irq/msi.c +@@ -850,6 +850,23 @@ static int msi_init_virq(struct irq_domain *domain, int virq, unsigned int vflag + return 0; + } + ++/* This function is used for check whether the cpu affinity belongs to the ++ * online cpus. When we passthrough the nvme devices, the kernel will allocate ++ * maxcpus+1 MSI irqs and then activate them. In vcpu hotplug situations, it ++ * may happen that kernel activates the offline cpus when bootcpus < maxcpus. ++ * To avoid this conflict, this function check the affinities. ++ */ ++static inline bool check_affinity_mask_online(struct irq_affinity_desc *affinity) ++{ ++ int cpu; ++ ++ for_each_cpu(cpu, &affinity->mask) ++ if (cpu_online(cpu)) ++ return true; ++ ++ return false; ++} ++ + int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, + int nvec) + { +@@ -897,6 +914,9 @@ int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, + return msi_handle_pci_fail(domain, desc, allocated); + + for (i = 0; i < desc->nvec_used; i++) { ++ if (desc->affinity ++ && !check_affinity_mask_online(desc->affinity)) ++ continue; + irq_set_msi_desc_off(virq, i, desc); + irq_debugfs_copy_devname(virq + i, dev); + ret = msi_init_virq(domain, virq + i, vflags); +-- +2.28.0 diff --git a/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0007-smp-update-bringup_nonboot_cpus-parameters.patch b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0007-smp-update-bringup_nonboot_cpus-parameters.patch new file mode 100644 index 0000000000..0c13ff81fd --- /dev/null +++ b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0007-smp-update-bringup_nonboot_cpus-parameters.patch @@ -0,0 +1,139 @@ +From a05086142be13d43c7fc92500bcb870a2f37e485 Mon Sep 17 00:00:00 2001 +Message-Id: <a05086142be13d43c7fc92500bcb870a2f37e485.1685428663.git.jiyunxue@linux.alibaba.com> +In-Reply-To: <16e3b3da9fb8b79b006d8c9d1f68b2dec9980d72.1685428663.git.jiyunxue@linux.alibaba.com> +References: <16e3b3da9fb8b79b006d8c9d1f68b2dec9980d72.1685428663.git.jiyunxue@linux.alibaba.com> +From: xuejun-xj <jiyunxue@linux.alibaba.com> +Date: Tue, 23 May 2023 09:43:02 +0800 +Subject: [PATCH 3/3] smp: update bringup_nonboot_cpus parameters + +On aarch64, kvm doesn't allow vmm to call KVM_CREATE_VCPU ioctls after +vm has already started, which is caused by vgic_initialized check in +kvm_arch_vcpu_precreate() function. Therefore, to support vcpu hotplug +feature on aarch64, all the vcpus should be created and configured ready +for start at booting procedure. + +To solve the problem, dragonball will add a property in each cpu node, +called "boot-onlined". This property indicates whether this cpu should +be onlined at first boot. It has two values: 0 and 1. 0 means offline, +while 1 means online. + +This commit also add a helper function called "of_get_cpu_boot_onlined", +which parse the cpu node and get the value of boot-onlined property. +Then update the global variable "boot_onlined_cpu". + +When kernel calling smp_init(), bringup_nonboot_cpus will start all the +other cpus except cpu0. The activated cpu number equals setup_max_cpus. +In vcpu hotplug scenario, vmm will create all the vcpufd before vm is +initialized, while activating only a few vcpus at first boot. The +setup_max_cpus variable will be initialized as all vcpu count. This +cause that the other cpus cannot find enough cpu threads, and they will +wait for 5 seconds each cpu. + +Therefore, we use boot_onlined_cpu instead of setup_max_cpus to give +"bringup_nonboot_cpus" correct cpu number it needs. + +Signed-off-by: xuejun-xj <jiyunxue@linux.alibaba.com> +--- + .../devicetree/bindings/arm/cpus.yaml | 11 +++++++++ + arch/arm64/kernel/smp.c | 24 +++++++++++++++++++ + kernel/smp.c | 10 +++++++- + 3 files changed, 44 insertions(+), 1 deletion(-) + +diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml +index 14cd727d3c4b..691bb352d842 100644 +--- a/Documentation/devicetree/bindings/arm/cpus.yaml ++++ b/Documentation/devicetree/bindings/arm/cpus.yaml +@@ -316,6 +316,17 @@ properties: + formed by encoding the target CPU id into the low bits of the + physical start address it should jump to. + ++ boot-onlined: ++ $ref: '/schemas/types.yaml#/definitions/uint32' ++ description: | ++ The boot-onlined property is an optional u32 value that indicates ++ whether the cpu device should be activated at first boot. This is ++ useful in vcpu hotplug scenario to pass correct value of activated ++ cpu number. ++ ++ This property has two values: 0 and 1. 1 means the cpu should be ++ activated while 0 means it shouldn't. ++ + if: + # If the enable-method property contains one of those values + properties: +diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c +index d323621d1..e0708c9ac 100644 +--- a/arch/arm64/kernel/smp.c ++++ b/arch/arm64/kernel/smp.c +@@ -620,6 +620,28 @@ static void __init acpi_parse_and_init_cpus(void) + #define acpi_parse_and_init_cpus(...) do { } while (0) + #endif + ++ ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) && defined(CONFIG_ARM64) ++extern unsigned int boot_onlined_cpu; ++static void __init of_get_cpu_boot_onlined(struct device_node *dn) ++{ ++ unsigned int boot_onlined; ++ int r; ++ ++ r = of_property_read_u32(dn, "boot-onlined", &boot_onlined); ++ if (r) { ++ pr_err("%pOF: missing boot-onlined property\n", dn); ++ return; ++ } ++ /* ++ * Property boot-onlined has two values: 0 and 1. ++ * 0 means offline, and 1 means online. ++ * Here just count the number of boot_onlined_cpu. ++ */ ++ boot_onlined_cpu += boot_onlined; ++} ++#endif ++ + /* + * Enumerate the possible CPU set from the device tree and build the + * cpu logical map array containing MPIDR values related to logical +@@ -630,6 +652,9 @@ static void __init of_parse_and_init_cpus(void) + struct device_node *dn; + + for_each_of_cpu_node(dn) { ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) && defined(CONFIG_ARM64) ++ of_get_cpu_boot_onlined(dn); ++#endif + u64 hwid = of_get_cpu_hwid(dn, 0); + + if (hwid & ~MPIDR_HWID_BITMASK) +diff --git a/kernel/smp.c b/kernel/smp.c +index 25240fb2df94..567615b9a008 100644 +--- a/kernel/smp.c ++++ b/kernel/smp.c +@@ -801,17 +801,25 @@ void __init setup_nr_cpu_ids(void) + nr_cpu_ids = find_last_bit(cpumask_bits(cpu_possible_mask),NR_CPUS) + 1; + } + ++/* Setup number of CPUs to activate */ ++unsigned int boot_onlined_cpu = 0; ++ + /* Called by boot processor to activate the rest. */ + void __init smp_init(void) + { + int num_nodes, num_cpus; ++ int num_onlined_cpu = setup_max_cpus; + + idle_threads_init(); + cpuhp_threads_init(); + + pr_info("Bringing up secondary CPUs ...\n"); + +- bringup_nonboot_cpus(setup_max_cpus); ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) && defined(CONFIG_ARM64) ++ if (boot_onlined_cpu != 0) ++ num_onlined_cpu = boot_onlined_cpu; ++#endif ++ bringup_nonboot_cpus(num_onlined_cpu); + + num_nodes = num_online_nodes(); + num_cpus = num_online_cpus(); +-- +2.28.0 diff --git a/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0008-upcall-add-pci-hotplug-hot-unplug-support.patch b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0008-upcall-add-pci-hotplug-hot-unplug-support.patch new file mode 100644 index 0000000000..d4171e64a2 --- /dev/null +++ b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0008-upcall-add-pci-hotplug-hot-unplug-support.patch @@ -0,0 +1,173 @@ +From 4ed40d8ce3793129ba9c0b7b663a5e137aceb70c Mon Sep 17 00:00:00 2001 +From: Chao Wu <chaowu@linux.alibaba.com> +Date: Wed, 27 Dec 2023 14:43:47 +0800 +Subject: [PATCH] upcall: add pci hotplug / hot-unplug support + +add two new upcall functions add_pci_dev and del_pci_dev, mainly for hotplugging +and hot-unplugging pci device in the guest kernel through the upcall server. + +Users could implement upcall client side with add_pci or del_pci command and trigger +those commands in the hypervisor side. + +As always, Dragonball hypervisor will implement the client side to do pci hotplug and +hot-unplug as an example + +Signed-off-by: Gerry Liu <gerry@linux.alibaba.com> +Signed-off-by: Helin Guo <helinguo@linux.alibaba.com> +Signed-off-by: Chao Wu <chaowu@linux.alibaba.com> +--- + drivers/misc/dragonball/upcall_srv/Kconfig | 11 +++ + .../upcall_srv/dragonball_device_manager.c | 90 +++++++++++++++++++ + 2 files changed, 101 insertions(+) + +diff --git a/drivers/misc/dragonball/upcall_srv/Kconfig b/drivers/misc/dragonball/upcall_srv/Kconfig +index fc83f03c2edd..19a6ca957ea6 100644 +--- a/drivers/misc/dragonball/upcall_srv/Kconfig ++++ b/drivers/misc/dragonball/upcall_srv/Kconfig +@@ -47,3 +47,14 @@ config DRAGONBALL_HOTPLUG_CPU + structure with command and parameter to hot-pluging an vCPU. + + If unsure, say N. ++ ++config DRAGONBALL_HOTPLUG_PCI ++ bool "PCI hotplug/hotunplug support" ++ depends on DRAGONBALL_DEVICE_MANAGER ++ default y ++ help ++ This configure implements a PCI hotplug/hotunplug support, vmm ++ should send hotplug request by vsock which follow special data ++ structure with command and parameter to hot-pluging a PCI device. ++ ++ If unsure, say N. +diff --git a/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c b/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c +index 088d38623b8d..3544afefa2a9 100644 +--- a/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c ++++ b/drivers/misc/dragonball/upcall_srv/dragonball_device_manager.c +@@ -22,6 +22,7 @@ + #include <linux/cpu.h> + #include <linux/cpumask.h> + #include <linux/cpuhotplug.h> ++#include <linux/pci.h> + #include <asm/cpu.h> + #include <dragonball/upcall_srv.h> + #include <dragonball/device_manager.h> +@@ -90,6 +91,12 @@ struct devmgr_req { + uint8_t apic_ids[256]; + #endif + } cpu_dev_info; ++#endif ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_PCI) ++ struct { ++ uint8_t busno; ++ uint8_t devfn; ++ } pci_dev_info; + #endif + } msg_load; + }; +@@ -117,6 +124,9 @@ struct devmgr_reply { + #endif + #if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) + struct cpu_dev_reply_info cpu_dev_info; ++#endif ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_PCI) ++ struct {} pci_dev_info; + #endif + } msg_load; + }; +@@ -286,6 +296,82 @@ static int del_mmio_dev(struct devmgr_req *req, + } + #endif + ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_PCI) ++static int add_pci_dev(struct devmgr_req *req, ++ struct devmgr_reply *rep) ++{ ++ int ret = 0; ++ struct devmgr_msg_header *rep_mh = &rep->msg_header; ++ uint8_t busno = req->msg_load.pci_dev_info.busno; ++ uint8_t devfn = req->msg_load.pci_dev_info.devfn; ++ struct pci_bus *bus; ++ struct pci_dev *dev; ++ ++ pr_info("add pci device of busno: %02x, devfn: %02x\n", busno, devfn); ++ ++ pci_lock_rescan_remove(); ++ ++ /* It is similar to pci_rescan_bus */ ++ ++ bus = pci_find_bus(0, busno); ++ if (!bus) { ++ pr_err("Could not find PCI bus for busno %02x\n", busno); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ pci_scan_slot(bus, devfn); ++ dev = pci_get_slot(bus, devfn); ++ if (!dev) { ++ pr_err("Could not find PCI device for slot %02x\n", devfn); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ pci_bus_claim_resources(bus); ++ ++ pci_bus_add_devices(bus); ++ ++ pci_dev_put(dev); ++ ++out: ++ pci_unlock_rescan_remove(); ++ if (!ret) ++ _fill_msg_header(rep_mh, 0, ADD_PCI, 0); ++ return ret; ++} ++ ++static int del_pci_dev(struct devmgr_req *req, ++ struct devmgr_reply *rep) ++{ ++ int ret = 0; ++ struct devmgr_msg_header *rep_mh = &rep->msg_header; ++ uint8_t busno = req->msg_load.pci_dev_info.busno; ++ uint8_t devfn = req->msg_load.pci_dev_info.devfn; ++ struct pci_dev *dev; ++ ++ pr_info("remove pci device of busno: %02x, devfn: %02x\n", busno, devfn); ++ ++ pci_lock_rescan_remove(); ++ ++ dev = pci_get_domain_bus_and_slot(0, busno, devfn); ++ ++ if (!dev) { ++ pr_err("Could not find PCI device for slot %02x\n", devfn); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ pci_stop_and_remove_bus_device(dev); ++ ++ pci_dev_put(dev); ++out: ++ pci_unlock_rescan_remove(); ++ if (!ret) ++ _fill_msg_header(rep_mh, 0, DEL_PCI, 0); ++ return ret; ++} ++#endif + + #if defined(CONFIG_DRAGONBALL_HOTPLUG_CPU) + #if defined(CONFIG_X86_64) +@@ -522,6 +608,10 @@ static struct { + {ADD_CPU, add_cpu_dev}, + {DEL_CPU, del_cpu_dev}, + #endif ++#if defined(CONFIG_DRAGONBALL_HOTPLUG_PCI) ++ {ADD_PCI, add_pci_dev}, ++ {DEL_PCI, del_pci_dev}, ++#endif + }; + + static action_route_t get_action(struct devmgr_req *req) +-- +2.31.1 + From 26446d1edb012908c21f3768fa143bce11fd1181 Mon Sep 17 00:00:00 2001 From: Zvonko Kaiser <zkaiser@nvidia.com> Date: Thu, 4 Jul 2024 17:08:43 +0000 Subject: [PATCH 2/4] dragonball: Update patches After v5.14 there is no cpu_hotplug_begin function now cpus_write_lock same for cpu_hotplug_done = cpus_write_unlock Signed-off-by: Zvonko Kaiser <zkaiser@nvidia.com> --- ...add-cpu-hotplug-hot-unplug-into-device-manage.patch | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0003-upcall-add-cpu-hotplug-hot-unplug-into-device-manage.patch b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0003-upcall-add-cpu-hotplug-hot-unplug-into-device-manage.patch index 0d1097b650..4e57335331 100644 --- a/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0003-upcall-add-cpu-hotplug-hot-unplug-into-device-manage.patch +++ b/tools/packaging/kernel/patches/6.1.x/dragonball-experimental/0003-upcall-add-cpu-hotplug-hot-unplug-into-device-manage.patch @@ -129,7 +129,7 @@ index ebcb6ef74285..210ef5d6c9d5 100644 + */ + lock_device_hotplug(); + cpu_maps_update_begin(); -+ cpu_hotplug_begin(); ++ cpus_write_lock(); + + cpu_id = generic_processor_info(apic_id, apic_ver); + if (cpu_id < 0) { @@ -149,7 +149,7 @@ index ebcb6ef74285..210ef5d6c9d5 100644 + goto rollback_register_cpu; + } + -+ cpu_hotplug_done(); ++ cpus_write_unlock(); + cpu_maps_update_done(); + unlock_device_hotplug(); + @@ -172,7 +172,7 @@ index ebcb6ef74285..210ef5d6c9d5 100644 + per_cpu(x86_cpu_to_apicid, cpu_id) = -1; + num_processors--; +rollback_generic_cpu: -+ cpu_hotplug_done(); ++ cpus_write_unlock(); + cpu_maps_update_done(); + unlock_device_hotplug(); + return ret; @@ -196,14 +196,14 @@ index ebcb6ef74285..210ef5d6c9d5 100644 + + lock_device_hotplug(); + cpu_maps_update_begin(); -+ cpu_hotplug_begin(); ++ cpus_write_lock(); + + arch_unregister_cpu(cpu_id); + set_cpu_present(cpu_id, false); + per_cpu(x86_cpu_to_apicid, cpu_id) = -1; + num_processors--; + -+ cpu_hotplug_done(); ++ cpus_write_unlock(); + cpu_maps_update_done(); + unlock_device_hotplug(); + From cfbca4fe0d360b64904662082bdfa95861367be6 Mon Sep 17 00:00:00 2001 From: Zvonko Kaiser <zkaiser@nvidia.com> Date: Thu, 4 Jul 2024 17:09:20 +0000 Subject: [PATCH 3/4] dragonball: Update versions Use the latest guest kernel that we use for all other VMMs Signed-off-by: Zvonko Kaiser <zkaiser@nvidia.com> --- versions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.yaml b/versions.yaml index bef777cd65..9e3210772f 100644 --- a/versions.yaml +++ b/versions.yaml @@ -208,7 +208,7 @@ assets: kernel-dragonball-experimental: description: "Linux kernel with Dragonball VMM optimizations like upcall" url: "https://cdn.kernel.org/pub/linux/kernel/v5.x/" - version: "v5.10.25" + version: "v6.1.62" externals: description: "Third-party projects used by the system" From 7990d3a154757550ecc49333d7921ca6ae2e35ae Mon Sep 17 00:00:00 2001 From: Zvonko Kaiser <zkaiser@nvidia.com> Date: Thu, 4 Jul 2024 17:17:29 +0000 Subject: [PATCH 4/4] dragonball: Update kata config version Mandatory update Signed-off-by: Zvonko Kaiser <zkaiser@nvidia.com> --- tools/packaging/kernel/kata_config_version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/packaging/kernel/kata_config_version b/tools/packaging/kernel/kata_config_version index 6a4573e805..405e2afe8e 100644 --- a/tools/packaging/kernel/kata_config_version +++ b/tools/packaging/kernel/kata_config_version @@ -1 +1 @@ -133 +134