Разгрузка хранилища DPU/IPU SPDK в пользовательском режиме vfio (vfio_user)
Разгрузка хранилища DPU/IPU SPDK в пользовательском режиме vfio (vfio_user)

термин

IPU: инфраструктурные процессоры (IPU), инфраструктурные процессоры (аппаратные карты), такие как обработка/выгрузка хранилища на IPU.

Краткое введение

использоватьIPUудалитьnvme_targetизссылка Архитектура следующая:

Блочные устройства SPDK могут быть локальными NVMeSSD/удаленными (Nvmeof, iscsi, ceph, daos и т.д.):

График разработки Nvme_target в SPDK:

  • Поддержка nvmf_target в 2016 году (поддерживает только RDMA)
  • Поддержка транспортного уровня vfio-user (программная эмуляция диска Nvme) в 2020 г.

СПДК ИПУ К8С сочетает в себе:

VFIO-USERКраткое введение

VFIO-USER — это протокол, который позволяет эмулировать устройства в отдельном процессе вне монитора виртуальной машины (VMM). ▪ Спецификация VFIO-USER в первую очередь основана на интерфейсе ioctl VFIO Linux и реализует его как сообщения, отправляемые через сокеты домена UNIX. ▪ VFIO-USER состоит из двух частей: • Клиент VFIO-USER работает в VMM или приложении. • Сервер VFIO-USER используется для эмуляции устройств в отдельном процессе.

фон

Для представления паравиртуализированных устройств виртуальным машинам исторически требовалось наличие специализированных драйверов в гостевой операционной системе. Наиболее распространенными примерами являются virtio-blk или virtio-scsi. Эти устройства могут быть созданы с использованием операционной системы хост-системы (например, KVM может эмулировать устройства virtio-blk и virtio-scsi для гостевого использования) или как отдельные процессы пользовательского пространства (протокол vhost-user может подключаться к этим целям, обычно предоставляемый СПДК). Однако в настоящее время только Linux поставляется со встроенными драйверами virtio-blk и virtio-scsi. В BIOS часто отсутствуют доступные драйверы, поэтому загрузка с этих устройств невозможна, тогда как операционные системы, такие как Windows, требуют отдельной установки драйверов. В этом докладе мы представим новый стандартизированный протокол, используемый виртуальными машинами для взаимодействия с другими процессами, который можно использовать для эмуляции любого устройства PCI, называемый vfio-user. QEMU будет поддерживать этот протокол в следующем выпуске. Затем мы опишем, как SPDK использует этот новый протокол для представления гостям паравиртуализированных устройств NVMe, позволяя гостевому BIOS и гостевой операционной системе загружать существующие драйверы NVMe без изменений для использования этих дисков. Затем мы завершим статью некоторыми тестами, которые демонстрируют чрезвычайно низкие накладные расходы этой виртуализации.

VFIO-USER: новый протокол виртуализации, 4 мая 2021 г. • Бен Уокер Мы рады объявить о поддержке NVMe поверх vfio-user — технологии, которая позволяет SPDK представлять полностью эмулированные устройства NVMe в виртуальных машинах. Виртуальные машины могут использовать существующие драйверы NVMe для взаимодействия с устройством, а данные могут эффективно передаваться в SPDK и обратно с использованием общей памяти. Другими словами, он аналогичен vhost-user, но способен эмулировать устройства NVMe вместо устройств virtio-blk или virtio-scsi. Сообщество QEMU в настоящее время занимается стандартизацией и поддержкой vfio-user. Проект спецификации был согласован всеми участвующими сторонами и быстро дорабатывается. Сам протокол способен эмулировать любое физическое устройство, не только NVMe, но пока что эмуляция NVMe-устройств с помощью SPDK — первый и основной потребитель нового интерфейса. Nutanix выпустила удобную библиотеку, которая помогает реализовать серверы для протокола, и SPDK использует это в своей реализации. Эмуляция устройств NVMe реализована с использованием существующих целевых объектов SPDK NVMe-oF, рассматривая vfio-user как «транспорт» общей памяти точно так же, как он поддерживает TCP, RDMA, PCIe и Fibre Channel. Спецификация NVMe-oF уже требует обширной эмуляции устройств NVMe, поэтому почти вся логика, необходимая для эмуляции физического твердотельного накопителя NVMe, уже существует. На самом деле это так же просто, как добавить новый транспортный плагин для пользователя vfio. Хотя этот транспорт официально не является частью спецификации NVMe-oF, система подключаемых модулей транспорта настроена на возможность использования пользовательских транспортов. Эти транспортные плагины могут даже существовать в общих библиотеках вне репозитория SPDK и быть обнаружены во время выполнения, хотя транспорт vfio-user встроен в сам SPDK. Мы воодушевлены этим достижением в области технологий виртуализации. Эмулируя физическое устройство NVMe, любая операционная система с драйвером NVMe (то есть все операционные системы) может взаимодействовать с этим устройством. Это особенно важно для Windows — больше не нужно загружать драйвер virtio! Это также позволяет виртуальным машинам загружаться с эмулируемых устройств NVMe, поскольку UEFI BIOS включает драйверы NVMe. В долгосрочной перспективе мы абсолютно уверены, что пользователь vfio сможет завоевать долю рынка не только в индустрии хранения данных, но также в сетевых и других областях. Мы ожидаем, что его производительность будет превосходной — по крайней мере, такой же, как у virtio-blk, но, вероятно, лучше — и мы будем более чем рады поделиться результатами тестов, как только они станут доступны.

Общая архитектура libvfio-user

SPDK + VFIO-USER + QEMU Испытательная архитектура

компоненты

клиентский компонент:

Серверкомпоненты:

Схема PCI устройства эмуляции virtio

Язык кода:javascript
копировать
/* virtio device layout:
 *
 * region 1: MSI-X Table
 * region 2: MSI-X PBA
 * region 4: virtio modern memory 64bits BAR
 *     Common configuration          0x0    - 0x1000
 *     ISR access                    0x1000 - 0x2000
 *     Device specific configuration 0x2000 - 0x3000
 *     Notifications                 0x3000 - 0x4000
 */
#define VIRTIO_PCI_COMMON_CFG_OFFSET    (0x0)
#define VIRTIO_PCI_COMMON_CFG_LENGTH    (0x1000)
#define VIRTIO_PCI_ISR_ACCESS_OFFSET    (VIRTIO_PCI_COMMON_CFG_OFFSET + VIRTIO_PCI_COMMON_CFG_LENGTH)
#define VIRTIO_PCI_ISR_ACCESS_LENGTH    (0x1000)
#define VIRTIO_PCI_SPECIFIC_CFG_OFFSET    (VIRTIO_PCI_ISR_ACCESS_OFFSET + VIRTIO_PCI_ISR_ACCESS_LENGTH)
#define VIRTIO_PCI_SPECIFIC_CFG_LENGTH    (0x1000)
#define VIRTIO_PCI_NOTIFICATIONS_OFFSET    (VIRTIO_PCI_SPECIFIC_CFG_OFFSET + VIRTIO_PCI_SPECIFIC_CFG_LENGTH)
#define VIRTIO_PCI_NOTIFICATIONS_LENGTH    (0x1000)
​
#define VIRTIO_PCI_BAR4_LENGTH        (VIRTIO_PCI_NOTIFICATIONS_OFFSET + VIRTIO_PCI_NOTIFICATIONS_LENGTH)

Анализ исходного кода

Язык кода:javascript
копировать
Целевая сторона SPDK создает моделируемое устройство nvme и конечную точку устройства vfio в пользовательском режиме, Затем QEMU запускает виртуальную машину, указав тип устройства vfio-user-pci.
1. build/bin/spdk_tgt
2. scripts/rpc.py bdev_malloc_create -b malloc0 $((512)) 512
3. scripts/rpc.py vfu_virtio_create_blk_endpoint vfu.0 --bdev-name malloc0 \
--cpumask=0x1 --num-queues=2 \
--qsize=256 --packed-ring
4. Start QEMU with '-device vfio-user-pci,socket=/spdk/vfu.0'

Создание процесса конечной точки блочного устройства VFU

Язык кода:javascript
копировать
Выполнить RPC:
SPDK_RPC_REGISTER("vfu_virtio_create_blk_endpoint", rpc_vfu_virtio_create_blk_endpoint, SPDK_RPC_RUNTIME)
rpc_vfu_virtio_create_blk_endpoint
    spdk_vfu_create_endpoint(req.name, req.cpumask, "virtio_blk")
        vfu_parse_core_mask
        spdk_vfu_get_endpoint_by_name(endpoint_name)
            TAILQ_FOREACH_SAFE(endpoint, &g_endpoint, link, tmp)
        ops = tgt_get_pci_device_ops(dev_type_name)
            TAILQ_FOREACH_SAFE(pci_ops, &g_pci_device_ops, link, tmp) -> отглобальная таблица действий Искать в        basename = tgt_get_base_path() -> g_endpoint_path_dirname
        endpoint->endpoint_ctx = ops->init(endpoint, basename, endpoint_name) -> vfu_virtio_blk_endpoint_init
        tgt_endpoint_realize(endpoint)
            ret = endpoint->ops.get_device_info(endpoint, &pci_dev) -> vfu_virtio_blk_get_device_info
                vfu_virtio_get_device_info(&blk_endpoint->virtio, device_info)
                    memcpy(device_info, &vfu_virtio_device_info, sizeof(*device_info))
                    device_info->regions[VFU_PCI_DEV_BAR4_REGION_IDX].fd = virtio_endpoint->devmem_fd
                device_info->id.did = PCI_DEVICE_ID_VIRTIO_BLK_MODERN
            endpoint->vfu_ctx = vfu_create_ctx
                vfu_ctx = calloc(1, sizeof(vfu_ctx_t))
                vfu_ctx->tran = &tran_sock_ops
                vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_ERR_IRQ, 1) -> vfu_ctx->irq_count[type] = count
                vfu_ctx->tran->init(vfu_ctx) -> tran_sock_init
            vfu_pci_init(endpoint->vfu_ctx, VFU_PCI_TYPE_EXPRESS, PCI_HEADER_TYPE_NORMAL, 0)
                cfg_space = calloc(1, size)
                vfu_ctx->pci.config_space = cfg_space
                vfu_ctx->reg_info[VFU_PCI_DEV_CFG_REGION_IDX].size = size
            vfu_pci_set_id
            vfu_pci_set_class
            cap_size = endpoint->ops.get_vendor_capability(endpoint, buf, 256, vendor_cap_idx)
            cap_offset = vfu_pci_add_capability(endpoint->vfu_ctx, 0, 0, vendor_cap)
            vfu_setup_region(endpoint->vfu_ctx, region_idx, region->len, region->access_cb, region->flags, region->nr_sparse_mmaps ? sparse_mmap : NULL, region->nr_sparse_mmaps, region->fd, region->offset)
                copyin_mmap_areas(reg, mmap_areas, nr_mmap_areas)
                    memcpy(reg_info->mmap_areas, mmap_areas, size)
            vfu_setup_device_dma(endpoint->vfu_ctx, tgt_memory_region_add_cb, tgt_memory_region_remove_cb)
                vfu_ctx->dma = dma_controller_create(vfu_ctx, MAX_DMA_REGIONS, MAX_DMA_SIZE)
                    dma = malloc(offsetof(dma_controller_t, regions) +
            vfu_setup_device_reset_cb(endpoint->vfu_ctx, tgt_device_reset_cb)
            vfu_setup_device_quiesce_cb(endpoint->vfu_ctx, tgt_device_quiesce_cb)
            vfu_setup_device_nr_irqs
            vfu_realize_ctx
            endpoint->pci_config_space = vfu_pci_get_config_space(endpoint->vfu_ctx)
            init_pci_config_space
                p->hdr.bars[0].raw = 0x0
                p->hdr.intr.ipin = ipin
                ...
        endpoint->thread = spdk_thread_create(endpoint_name, &cpumask)
        spdk_thread_send_msg(endpoint->thread, tgt_endpoint_start_thread, endpoint)
            endpoint->accept_poller = SPDK_POLLER_REGISTER(tgt_accept_poller, endpoint, 1000) -> Служба получает запрос протокола vfio-user.
                vfu_attach_ctx -> tran_sock_attach
                    ts->conn_fd = accept(ts->listen_fd, NULL, NULL)
                    ret = tran_negotiate(vfu_ctx, &ts->client_cmd_socket_fd) -> switch ctx
                endpoint->vfu_ctx_poller = SPDK_POLLER_REGISTER(tgt_vfu_ctx_poller, endpoint, 1000)
                    vfu_run_ctx(vfu_ctx)
                    do
                        err = get_request(vfu_ctx, &msg)
                        handle_request(vfu_ctx, msg)
                            switch (msg->hdr.cmd) -> Обрабатывается в соответствии с различными типами протоколов
                            case VFIO_USER_DMA_MAP
                                handle_dma_map(vfu_ctx, msg, msg->in.iov.iov_base)
                            ...
                endpoint->ops.attach_device(endpoint)
    vfu_virtio_blk_add_bdev(req.name, req.bdev_name, req.num_queues, req.qsize, req.packed_ring)
        spdk_bdev_open_ext(bdev_name, true, bdev_event_cb, blk_endpoint, &blk_endpoint->bdev_desc) -> open and set bdev_desc -> ассоциацияbdev
        virtio_blk_update_config(&blk_endpoint->blk_cfg, blk_endpoint->bdev, blk_endpoint->virtio.num_queues)
            blk_cfg->blk_size = spdk_bdev_get_block_size(bdev)

Таблица глобальных операций g_pci_device_ops связана с vfu_virtio_blk_ops.

Язык кода:javascript
копировать
__attribute__((constructor)) _vfu_virtio_blk_pci_model_register
    spdk_vfu_register_endpoint_ops(&vfu_virtio_blk_ops)
        TAILQ_INSERT_TAIL(&g_pci_device_ops, pci_ops, link)
​
struct spdk_vfu_endpoint_ops vfu_virtio_blk_ops = {
    .name = "virtio_blk",
    .init = vfu_virtio_blk_endpoint_init,
        vfu_virtio_endpoint_setup(&blk_endpoint->virtio, endpoint, basename, endpoint_name, &virtio_blk_ops)
            snprintf(path, PATH_MAX, "%s%s_bar4", basename, endpoint_name)
            open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)
            ftruncate(virtio_endpoint->devmem_fd, VIRTIO_PCI_BAR4_LENGTH)
            virtio_endpoint->doorbells = mmap(NULL, VIRTIO_PCI_NOTIFICATIONS_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, virtio_endpoint->devmem_fd, VIRTIO_PCI_NOTIFICATIONS_OFFSET)
            virtio_endpoint->virtio_ops = *ops
            virtio_endpoint->num_queues = VIRTIO_DEV_MAX_VQS -> 64
            virtio_endpoint->qsize = VIRTIO_VQ_DEFAULT_SIZE -> 128
    .get_device_info = vfu_virtio_blk_get_device_info,
    .get_vendor_capability = vfu_virtio_get_vendor_capability,
    .post_memory_add = vfu_virtio_post_memory_add,
        virtio_dev_map_vq(dev, &dev->vqs[i])
            phys_addr = ((((uint64_t)vq->desc_hi) << 32) | vq->desc_lo)
            vfu_virtio_map_q(dev, &vq->desc, phys_addr, len)
                addr = spdk_vfu_map_one(virtio_endpoint->endpoint, phys_addr, len, mapping->sg, &mapping->iov, PROT_READ | PROT_WRITE)
            vfu_virtio_map_q(dev, &vq->avail, phys_addr, len)
            vfu_virtio_map_q(dev, &vq->used, phys_addr, len)
    .pre_memory_remove = vfu_virtio_pre_memory_remove,
    .reset_device = vfu_virtio_pci_reset_cb,
    .quiesce_device = vfu_virtio_quiesce_cb,
    .destruct = vfu_virtio_blk_endpoint_destruct,
    .attach_device = vfu_virtio_attach_device,
        for (j = 0; j <= vq->qsize; j++)
            req = vfu_virtio_vq_alloc_req(virtio_endpoint, vq)
                endpoint->virtio_ops.alloc_req(endpoint, vq)
            req->indirect_sg = virtio_req_to_sg_t(req, VIRTIO_DEV_MAX_IOVS)
            req->dev = dev
            STAILQ_INSERT_TAIL(&vq->free_reqs, req, link)
    .detach_device = vfu_virtio_detach_device,
};

Порядок обработки ввода-вывода:

Язык кода:c
копировать
virtio blk OP Тип операции ввода-вывода:
#define VIRTIO_BLK_T_IN 0   // операция чтения, Считать данные блочного устройства в VIRTIO_VQ (поставить в очередь)
#define VIRTIO_BLK_T_OUT 1  // операция записи, ВИРТИО Данные в VQ (удалить из очереди), и записать на блочное устройство
#define VIRTIO_BLK_T_FLUSH 4
#define VIRTIO_BLK_T_GET_ID 8
#define VIRTIO_BLK_T_GET_LIFETIME 10
#define VIRTIO_BLK_T_DISCARD 11
#define VIRTIO_BLK_T_WRITE_ZEROES 13
#define VIRTIO_BLK_T_SECURE_ERASE 14


vfu_virito_dev_process_split_ring
    virtio_endpoint->virtio_ops.exec_request(virtio_endpoint, vq, req)

vfu_virtio_blk_vring_poll
    struct vfu_virtio_dev *dev = blk_endpoint->virtio.dev
    count += vfu_virito_dev_process_packed_ring(dev, vq)
        req = vfu_virtio_dev_get_req(virtio_endpoint, vq) -> init req
        virtio_dev_packed_iovs_setup(dev, vq, vq->last_avail_idx, desc, req)
            if (virtio_vring_packed_desc_is_indirect(current_desc))
                desc_table = virtio_vring_packed_desc_to_iov(dev, current_desc, req->indirect_sg, req->indirect_iov)
                    spdk_vfu_map_one(virtio_endpoint->endpoint, desc->addr, desc->len, sg, iov, PROT_READ | PROT_WRITE)
                        vfu_addr_to_sgl(endpoint->vfu_ctx, (void *)(uintptr_t)addr, len, sg, 1, prot) -> Получает диапазон физических адресов гостя и заполняет массив записей разброса/сбора, которые можно индивидуально сопоставить с виртуальной памятью программы. Из-за ограничений в способе отображения памяти один линейный диапазон гостевых физических адресов может потребоваться разделить на несколько областей разброса/сбора. , должен быть вызван перед использованием этой функции vfu_setup_device_dma()
                        vfu_sgl_get(endpoint->vfu_ctx, sg, iov, 1, 0)
            virtio_vring_packed_desc_to_iov(dev, desc, virtio_req_to_sg_t(req, req->iovcnt), &req->iovs[req->iovcnt])
        virtio_endpoint->virtio_ops.exec_request(virtio_endpoint, vq, req)

struct vfu_virtio_ops virtio_blk_ops = {
	.get_device_features = virtio_blk_get_supported_features,
	.alloc_req = virtio_blk_alloc_req,
        blk_req = calloc(1, sizeof(*blk_req) + dma_sg_size() * (VIRTIO_DEV_MAX_IOVS + 1))
	.free_req = virtio_blk_free_req,
	.exec_request = virtio_blk_process_req, -> Выполнить запрос ввода-вывода
        iov = &req->iovs[0]
        hdr = iov->iov_base
        case VIRTIO_BLK_T_IN -> читать
            spdk_bdev_readv(blk_endpoint->bdev_desc, blk_endpoint->io_channel, &req->iovs[1], iovcnt, hdr->sector * 512, payload_len, blk_request_complete_cb, blk_req)
        case VIRTIO_BLK_T_OUT -> Писать
            spdk_bdev_writev(blk_endpoint->bdev_desc, blk_endpoint->io_channel, &req->iovs[1], iovcnt, hdr->sector * 512, payload_len, blk_request_complete_cb, blk_req)
        ...
        case VIRTIO_BLK_T_FLUSH
            spdk_bdev_flush(blk_endpoint->bdev_desc, blk_endpoint->io_channel, 0, flush_bytes, blk_request_complete_cb, blk_req)
	.get_config = virtio_blk_get_device_specific_config,
	.start_device = virtio_blk_start,
        blk_endpoint->io_channel = spdk_bdev_get_io_channel(blk_endpoint->bdev_desc)
        blk_endpoint->ring_poller = SPDK_POLLER_REGISTER(vfu_virtio_blk_vring_poll, blk_endpoint, 0)
	.stop_device = virtio_blk_stop,
};

сервер и клиент libvfio-user Анализ исходного кода

Язык кода:javascript
копировать
Запустите сервер:
server.c -> main
    vfu_create_ctx
        vfu_ctx->tran = &tran_sock_ops
        vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_ERR_IRQ, 1)
        vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_REQ_IRQ, 1)
        vfu_ctx->tran->init(vfu_ctx)
    vfu_setup_log(vfu_ctx, _log, verbose ? LOG_DEBUG : LOG_ERR)
    vfu_pci_init(vfu_ctx, VFU_PCI_TYPE_CONVENTIONAL, PCI_HEADER_TYPE_NORMAL, 0)
        vfu_pci_config_space_t *cfg_space
        case VFU_PCI_TYPE_PCI_X_1
            size = PCI_CFG_SPACE_SIZE -> 256
        cfg_space = calloc(1, size)
    vfu_pci_set_id(vfu_ctx, 0xdead, 0xbeef, 0xcafe, 0xbabe)
    vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR0_REGION_IDX, sizeof(time_t), &bar0_access, VFU_REGION_FLAG_RW, NULL, 0, -1, 0)
        reg->cb = cb
    umask(0022)
    tmpfd = mkstemp(template)
    unlink(template)
    ftruncate(tmpfd, server_data.bar1_size)
    server_data.bar1 = mmap(NULL, server_data.bar1_size, PROT_READ | PROT_WRITE, MAP_SHARED, tmpfd, 0)
    vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR1_REGION_IDX, server_data.bar1_size, &bar1_access, VFU_REGION_FLAG_RW, bar1_mmap_areas, 2, tmpfd, 0)
        copyin_mmap_areas(reg, mmap_areas, nr_mmap_areas)
            reg_info->mmap_areas = malloc(size)
            memcpy(reg_info->mmap_areas, mmap_areas, size)
    vfu_setup_device_migration_callbacks(vfu_ctx, &migr_callbacks)
        vfu_ctx->migration = init_migration(callbacks, &ret)
            migr->pgsize = sysconf(_SC_PAGESIZE)
            migr->callbacks = *callbacks
    vfu_setup_device_reset_cb(vfu_ctx, &device_reset)
    vfu_setup_device_dma(vfu_ctx, &dma_register, &dma_unregister)
        vfu_ctx->dma = dma_controller_create(vfu_ctx, MAX_DMA_REGIONS, MAX_DMA_SIZE)
            dma = malloc(offsetof(dma_controller_t, regions) + max_regions * sizeof(dma->regions[0]))
            memset(dma->regions, 0, max_regions * sizeof(dma->regions[0]))
            dma->dirty_pgsize = 0
    vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_INTX_IRQ, 1)
    vfu_realize_ctx(vfu_ctx)
        vfu_ctx->pci.config_space = calloc(1, cfg_reg->size)
    vfu_attach_ctx(vfu_ctx) -> vfu_ctx->tran->attach(vfu_ctx)
    do
        vfu_run_ctx(vfu_ctx)
            do
                err = get_request(vfu_ctx, &msg)
                    should_exec_command(vfu_ctx, msg->hdr.cmd)
                handle_request(vfu_ctx, msg)
                    handle_region_access(vfu_ctx, msg)
                    region_access
                        ret = pci_config_space_access(vfu_ctx, buf, count, offset, is_write)
                    do_reply
        if (ret == -1 && errno == EINTR)
            ret = vfu_irq_trigger(vfu_ctx, 0)
                eventfd_write(vfu_ctx->irqs->efds[subindex], val)
            do_dma_io(vfu_ctx, &server_data, 1, false)
                sg = alloca(dma_sg_size())
                vfu_addr_to_sgl
                    dma_addr_to_sgl(vfu_ctx->dma, dma_addr, len, sgl, max_nr_sgs, prot) -> Получите линейный DMA адрес охватывает и возвращает подходящий DMA из sg список。Из-за метода отображения памятиизпредел,Может возникнуть необходимость преобразовать один линейный DMA Диапазоны адресов разделены на несколько областей сбора и разброса.
                        dma_init_sg
                            sg->dma_addr = region->info.iova.iov_base
                            sg->offset = dma_addr - region->info.iova.iov_base
                        _dma_addr_sg_split
                            dma_init_sg
                vfu_sgl_write
                    vfu_dma_transfer
                        memcpy(rbuf + sizeof(*dma_req), data + count, dma_req->count)
                        ret = vfu_ctx->tran->send_msg(vfu_ctx, msg_id++, VFIO_USER_DMA_WRITE, rbuf,
                vfu_sgl_mark_dirty(vfu_ctx, sg, 1)
                    dma_sgl_mark_dirty(vfu_ctx->dma, sgl, cnt)
                vfu_sgl_put(vfu_ctx, sg, &iov, 1)
                crc1 = rte_hash_crc(buf, sizeof(buf), 0)
            do_dma_io(vfu_ctx, &server_data, 0, true)
​
​
​
Обратный вызов миграции:
const vfu_migration_callbacks_t migr_callbacks = {
    .version = VFU_MIGR_CALLBACKS_VERS,
    .transition = &migration_device_state_transition,
    .read_data = &migration_read_data,
    .write_data = &migration_write_data
        memcpy(server_data->bar1 + write_start, buf, length_in_bar1)
        memcpy((char *)&server_data->bar0 + write_start, buf + length_in_bar1, length_in_bar0)
};
​
​
Набор функций передачи сокета:
struct transport_ops tran_sock_ops = {
    .init = tran_sock_init,
        ts->listen_fd = socket(AF_UNIX, SOCK_STREAM, 0) -> bind -> listen
    .get_poll_fd = tran_sock_get_poll_fd,
    .attach = tran_sock_attach,
        ts->conn_fd = accept(ts->listen_fd, NULL, NULL)
        tran_negotiate(vfu_ctx, &ts->client_cmd_socket_fd)
            recv_version(vfu_ctx, &msg_id, &client_version, &twin_socket_supported)
                vfu_ctx->tran->recv_msg(vfu_ctx, &msg)
    .get_request_header = tran_sock_get_request_header,
    .recv_body = tran_sock_recv_body,
    .reply = tran_sock_reply,
    .recv_msg = tran_sock_recv_msg,
        tran_sock_recv_alloc(ts->conn_fd, &msg->hdr, false, NULL, &msg->in.iov.iov_base, &msg->in.iov.iov_len)
            tran_sock_recv(sock, hdr, is_reply, msg_id, NULL, NULL) -> tran_sock_recv_fds
                get_msg(hdr, sizeof(*hdr), fds, nr_fds, sock, 0)
                    recvmsg(sock_fd, &msg, sock_flags)
            recv(sock, data, len, MSG_WAITALL)
    .send_msg = tran_sock_send_msg,
        maybe_print_cmd_collision_warning
        tran_sock_msg(fd, msg_id, cmd, send_data, send_len, hdr, recv_data, recv_len) -> tran_sock_msg_fds
            tran_sock_msg_iovec
    .detach = tran_sock_detach,
    .fini = tran_sock_fini
};
​
​
​
Запуск клиента:
client.c -> main
    void *dirty_pages = malloc(dirty_pages_size) -> 24B
    dirty_pages_control = (void *)(dirty_pages_feature + 1)
    sock = init_sock(argv[optind]) -> sock -> connect
    negotiate(sock, &server_max_fds, &server_max_data_xfer_size, &pgsize)
        send_version(sock)
        recv_version(sock, server_max_fds, server_max_data_xfer_size, pgsize)
    ret = access_region(sock, 0xdeadbeef, false, 0, &ret, sizeof(ret))
        op = VFIO_USER_REGION_READ
        tran_sock_msg_iovec -> tran_sock_send_iovec
    get_device_info(sock, &client_dev_info)
    get_device_regions_info(sock, &client_dev_info)
        get_device_region_info
            do_get_device_region_info
    send_device_reset(sock)
    map_dma_regions(sock, dma_regions, nr_dma_regions)
        tran_sock_msg_iovec(sock, 0x1234 + i, VFIO_USER_DMA_MAP,
    irq_fd = configure_irqs(sock) -> VFIO_USER_DEVICE_GET_IRQ_INFO -> VFIO_USER_DEVICE_SET_IRQS
    access_bar0(sock, &t)
    wait_for_irq(irq_fd) -> read(irq_fd, &val, sizeof(val)
    handle_dma_io(sock, dma_regions, nr_dma_regions)
        handle_dma_write
            c = pwrite(dma_regions[i].fd, data, dma_access.count, offset)
            tran_sock_send(sock, msg_id, true, VFIO_USER_DMA_WRITE, &dma_access, sizeof(dma_access))
        handle_dma_read
    get_dirty_bitmap
    nr_iters = migrate_from
    migrate_to
        fork()
        ret = execvp(_argv[0] , _argv)
        sock = init_sock(sock_path) -> reconnect to new server
        set_migration_state(sock, device_state)
        write_migr_data -> tran_sock_msg_iovec(sock, msg_id--, VFIO_USER_MIG_DATA_WRITE,
        dst_crc = rte_hash_crc(buf, bar1_size, 0)
            init_val = rte_hash_crc_8byte(*(const uint64_t *)pd, init_val) -> crc32c_2words(data, init_val)
                term1 = CRC32_UPD(crc, 7) -> static const uint32_t crc32c_tables[8][256]
    map_dma_regions
    configure_irqs
    wait_for_irq
    handle_dma_io
​
​
​
Обработка отображения DMA:
handle_dma_map
    dma_controller_add_region -> MOCK_DEFINE(dma_controller_add_region)
        region->info.iova.iov_base = (void *)dma_addr
        dma_map_region(dma, region)
            mmap_len = ROUND_UP(region->info.iova.iov_len, region->info.page_size) -> 4096
            mmap_base = mmap(NULL, mmap_len, region->info.prot, MAP_SHARED, region->fd, offset)
            madvise(mmap_base, mmap_len, MADV_DONTDUMP)
            region->info.vaddr = mmap_base + (region->offset - offset)
    vfu_ctx->dma_register(vfu_ctx, &vfu_ctx->dma->regions[ret].info) -> dma_register(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info)
        struct server_data *server_data = vfu_get_private(vfu_ctx)
        server_data->regions[idx].iova = info->iova
                                               
Онлайн-сбор статуса миграции:
/* Analogous to enum vfio_device_mig_state */
enum vfio_user_device_mig_state {
    VFIO_USER_DEVICE_STATE_ERROR = 0,
    VFIO_USER_DEVICE_STATE_STOP = 1,
    VFIO_USER_DEVICE_STATE_RUNNING = 2,
    VFIO_USER_DEVICE_STATE_STOP_COPY = 3,
    VFIO_USER_DEVICE_STATE_RESUMING = 4,
    VFIO_USER_DEVICE_STATE_RUNNING_P2P = 5,
    VFIO_USER_DEVICE_STATE_PRE_COPY = 6,
    VFIO_USER_DEVICE_STATE_PRE_COPY_P2P = 7,
    VFIO_USER_DEVICE_NUM_STATES = 8,
};

Инициализация сервера

Процесс прямого доступа к памяти

Посетите пространство БАР

Инициализация клиента

Чтение конфигурационного пространства

прерывать процесс

ссылка

SPDK IPU OFFLOAD NVME: https://www.sniadeveloper.org/sites/default/files/SDC/2022/pdfs/SNIA-SDC22-Harris-SPDK-and-Infrastructure-Offload.pdf

libvfio-user git repo: https://github.com/nutanix/libvfio-user.git

Сяобин (ssbandjl)

блог: https://cloud.tencent.com/developer/user/5060293/articles | https://logread.cn | https://blog.csdn.net/ssbandjl | https://www.zhihu.com/people/ssbandjl/posts

https://chattoyou.cn(Подавать жалобы/сообщение)

столбец ДПУ

https://cloud.tencent.com/developer/column/101987

Технические друзья: Добро пожаловать в DPU/SmartNIC/Uninstall/Network.,хранилищеускоряться/Интересуются такими технологиями, как изоляция безопасностииз Друзья присоединяйтесьТехнология ДПУКоммуникационная группа

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose