spdk使用vhost-user实现虚拟机磁盘访问的c代码

以下是一个使用SPDK和vhost-user实现虚拟机磁盘访问的C代码案例。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define VHOST_SCSI_CTRLR_MAX_DEVS 16

static char g_vhost_if_name[IFNAMSIZ];
static char g_ctrlr_name[VHOST_SCSI_CTRLR_MAX_DEVS][256];
static int g_num_ctrlr_devs;
static struct spdk_nvme_ctrlr *g_nvme_ctrlr;
static struct spdk_nvme_ns *g_nvme_ns;

static void *
vhost_start(void *arg)
{
    int ret;
    int vhost_fd, tun_fd;
    struct ifreq ifr;
    char tun_name[IFNAMSIZ];
    struct vhost_scsi_ctrlr *ctrlr;
    struct vhost_scsi_dev *devs[VHOST_SCSI_CTRLR_MAX_DEVS];

    memset(g_vhost_if_name, 0, sizeof(g_vhost_if_name));
    memset(g_ctrlr_name, 0, sizeof(g_ctrlr_name));

    /* Create a vhost interface */
    vhost_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (vhost_fd == -1) {
        perror("socket");
        return NULL;
    }

    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, "vhost0", IFNAMSIZ - 1);
    if (ioctl(vhost_fd, SIOCGIFINDEX, &ifr) == -1) {
        perror("ioctl");
        close(vhost_fd);
        return NULL;
    }

    ret = bind(vhost_fd, (struct sockaddr *)&(struct sockaddr_un){
            .sun_family = AF_UNIX,
            .sun_path = "/tmp/vhost.0",
    }, sizeof(struct sockaddr_un));
    if (ret == -1) {
        perror("bind");
        close(vhost_fd);
        return NULL;
    }

    ret = listen(vhost_fd, 1);
    if (ret == -1) {
        perror("listen");
        close(vhost_fd);
        return NULL;
    }

    /* Create a TUN interface */
    tun_fd = open("/dev/net/tun", O_RDWR);
    if (tun_fd == -1) {
        perror("open");
        return NULL;
    }

    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
    strncpy(ifr.ifr_name, "vhost-scsi", IFNAMSIZ - 1);
    ret = ioctl(tun_fd, TUNSETIFF, &ifr);
    if (ret == -1) {
        perror("ioctl");
        close(tun_fd);
        return NULL;
    }

    strncpy(g_vhost_if_name, ifr.ifr_name, IFNAMSIZ - 1);

    /* Create a vhost SCSI controller */
    ctrlr = vhost_scsi_ctrlr_construct(g_vhost_if_name, 0);
    if (!ctrlr) {
        fprintf(stderr, "Failed to create vhost SCSI controller\n");
        close(tun_fd);
        return NULL;
    }

    /* Attach the TUN interface to the vhost SCSI controller */
    ret = vhost_scsi_ctrlr_add_dev(ctrlr, tun_fd, 0);
    if (ret == -1) {
        fprintf(stderr, "Failed to add vhost SCSI device\n");
        close(tun_fd);
        return NULL;
    }

    devs[0] = vhost_scsi_dev_construct("vhost-scsi.0");
    if (!devs[0]) {
        fprintf(stderr, "Failed to create vhost SCSI device\n");
        close(tun_fd);
        return NULL;
    }

    /* Map a NVMe namespace to the vhost SCSI device */
    ret = vhost_scsi_dev_add_lun(devs[0], g_nvme_ns);
    if (ret != 0) {
        fprintf(stderr, "Failed to add NVMe namespace to vhost SCSI device\n");
        close(tun_fd);
        return NULL;
    }

    ret = vhost_scsi_ctrlr_add_dev(ctrlr, tun_fd, 1);
    if (ret == -1) {
        fprintf(stderr, "Failed to add vhost SCSI device\n");
        close(tun_fd);
        return NULL;
    }

    devs[1] = vhost_scsi_dev_construct("vhost-scsi.1");
    if (!devs[1]) {
        fprintf(stderr, "Failed to create vhost SCSI device\n");
        close(tun_fd);
        return NULL;
    }

    /* Map a NVMe namespace to the vhost SCSI device */
    ret = vhost_scsi_dev_add_lun(devs[1], g_nvme_ns);
    if (ret != 0) {
        fprintf(stderr, "Failed to add NVMe namespace to vhost SCSI device\n");
        close(tun_fd);
        return NULL;
    }

    /* Start the vhost SCSI controller */
    ret = vhost_scsi_ctrlr_start(ctrlr, &devs[0], g_num_ctrlr_devs);
    if (ret != 0) {
        fprintf(stderr, "Failed to start vhost SCSI controller\n");
        close(tun_fd);
        return NULL;
    }

    /* Run the vhost loop */
    vhost_user_start(vhost_fd);

    return NULL;
}

static void
ctrl_c_handler(int signum)
{
    spdk_nvme_detach(g_nvme_ctrlr);
    exit(1);
}

int main(int argc, char **argv)
{
    int ret;
    pthread_t vhost_thread;

    if (argc < 3) {
        fprintf(stderr, "Usage: %s nvme_device_name num_ctrlr_devs\n", argv[0]);
        exit(1);
    }

    strncpy(g_ctrlr_name[0], "vhost-scsi.0", sizeof(g_ctrlr_name[0]));
    strncpy(g_ctrlr_name[1], "vhost-scsi.1", sizeof(g_ctrlr_name[1]));

    g_num_ctrlr_devs = atoi(argv[2]);

    spdk_env_opts opts = {};

    spdk_env_init(&opts);

    signal(SIGINT, ctrl_c_handler);

    /* Connect to the NVMe device */
    ret = spdk_nvme_connect(NULL, argv[1], NULL, NULL, &g_nvme_ctrlr);
    if (ret != 0) {
        fprintf(stderr, "Failed to connect to NVMe device\n");
        return 1;
    }

    /* Open the first namespace on the NVMe device */
    g_nvme_ns = spdk_nvme_ctrlr_get_ns(g_nvme_ctrlr, 1);

    /* Start the vhost thread */
    pthread_create(&vhost_thread, NULL, vhost_start, NULL);

    /* Wait for the vhost thread to exit */
    pthread_join(vhost_thread, NULL);

    return 0;
}

该代码使用SPDK和vhost-user实现了一个简单的虚拟机磁盘访问方案。它连接到一个NVMe设备,并将其映射到两个vhost SCSI设备上。然后,它启动一个vhost线程来处理与虚拟机的通信。在vhost循环中,我们可以看到如何通过vhost_scsi_dev_add_lun将NVMe命名空间映射到vhost SCSI设备上。

在main函数中,我们还使用pthread_create来创建一个线程,该线程将在后台运行并处理vhost通信。最后,我们使用pthread_join等待线程退出。

你可能感兴趣的:(网络虚拟化,spdk,DPDK)