/* Complete a previous send (backend --> guest) and enable the fd_read callback. */ static void netmap_send_completed(NetClientState *nc, ssize_t len) { NetmapState *s = DO_UPCAST(NetmapState, nc, nc); netmap_read_poll(s, true); }
/* Callback function that's called when the guest sends us data */ static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) { VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); ssize_t ret; struct session_op sess; struct crypt_op crypt; uint32_t sess_id; struct crypto_data *cr_data = (struct crypto_data*)buf; if (!vcon->chr) { /* If there's no backend, we can just say we consumed all data. */ return len; } switch (cr_data->cmd) { case CIOCGSESSION: sess = cr_data->op.sess; sess.key = cr_data->keyp; printf("cr_data->op.sess.ses: %u\n", cr_data->op.sess.ses); ret = ioctl(port->cr_info->cfd, CIOCGSESSION, &sess); if (ret) { perror("ioctl(CIOCGSESSION)"); return 1; } cr_data->op.sess.ses = sess.ses; printf("1cr_data->op.sess.ses: %u\n", cr_data->op.sess.ses); virtio_serial_write(port, (const uint8_t *)cr_data , sizeof(struct crypto_data)); break; case CIOCCRYPT: crypt = cr_data->op.crypt; crypt.src = cr_data->srcp; crypt.dst = cr_data->dstp; crypt.iv = cr_data->ivp; printf("cr_data->op.crypt.dstp[0]: %u\n", cr_data->dstp[0]); ret = ioctl(port->cr_info->cfd, CIOCCRYPT, &crypt); if (ret) { perror("ioctl(CIOCCRYPT)"); return 1; } memcpy(cr_data->dstp, crypt.dst, crypt.len); printf("1cr_data->op.crypt.dstp[0]: %u\n", cr_data->dstp[0]); virtio_serial_write(port, (const uint8_t *)cr_data , sizeof(struct crypto_data)); break; case CIOCFSESSION: sess_id = cr_data->op.sess_id; printf("0cr_data->op.sess_id: %d\n", cr_data->op.sess_id); ret = ioctl(port->cr_info->cfd, CIOCFSESSION, &sess_id); if (ret) { perror("ioctl(CIOCFSESSION)"); return 1; } cr_data->op.sess_id = sess_id; printf("1cr_data->op.sess_id: %d\n", cr_data->op.sess_id); virtio_serial_write(port, (const uint8_t *)cr_data , sizeof(struct crypto_data)); break; default: qemu_chr_fe_write(vcon->chr, buf, len); return -EINVAL; } if (ret < 0) { /* * Ideally we'd get a better error code than just -1, but * that's what the chardev interface gives us right now. If * we had a finer-grained message, like -EPIPE, we could close * this connection. Absent such error messages, the most we * can do is to return 0 here. * * This will prevent stray -1 values to go to * virtio-serial-bus.c and cause abort()s in * do_flush_queued_data(). */ ret = 0; } return ret; }
static void smc91c111_cleanup(VLANClientState *nc) { smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque; s->nic = NULL; }
static int spapr_vlan_can_receive(VLANClientState *nc) { VIOsPAPRVLANDevice *dev = DO_UPCAST(NICState, nc, nc)->opaque; return (dev->isopen && dev->rx_bufs > 0); }
/* Return a pointer to the data buffer. */ static uint8_t *scsi_get_buf(SCSIRequest *req) { SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); return r->buf; }
static uint32_t xen_pt_pci_read_config(PCIDevice *d, uint32_t addr, int len) { XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); uint32_t val = 0; XenPTRegGroup *reg_grp_entry = NULL; XenPTReg *reg_entry = NULL; int rc = 0; int emul_len = 0; uint32_t find_addr = addr; if (xen_pt_pci_config_access_check(d, addr, len)) { goto exit; } /* find register group entry */ reg_grp_entry = xen_pt_find_reg_grp(s, addr); if (reg_grp_entry) { /* check 0-Hardwired register group */ if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) { /* no need to emulate, just return 0 */ val = 0; goto exit; } } /* read I/O device register value */ rc = xen_host_pci_get_block(&s->real_device, addr, (uint8_t *)&val, len); if (rc < 0) { XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc); memset(&val, 0xff, len); } /* just return the I/O device register value for * passthrough type register group */ if (reg_grp_entry == NULL) { goto exit; } /* adjust the read value to appropriate CFC-CFF window */ val <<= (addr & 3) << 3; emul_len = len; /* loop around the guest requested size */ while (emul_len > 0) { /* find register entry to be emulated */ reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr); if (reg_entry) { XenPTRegInfo *reg = reg_entry->reg; uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); uint8_t *ptr_val = NULL; valid_mask <<= (find_addr - real_offset) << 3; ptr_val = (uint8_t *)&val + (real_offset & 3); /* do emulation based on register size */ switch (reg->size) { case 1: if (reg->u.b.read) { rc = reg->u.b.read(s, reg_entry, ptr_val, valid_mask); } break; case 2: if (reg->u.w.read) { rc = reg->u.w.read(s, reg_entry, (uint16_t *)ptr_val, valid_mask); } break; case 4: if (reg->u.dw.read) { rc = reg->u.dw.read(s, reg_entry, (uint32_t *)ptr_val, valid_mask); } break; } if (rc < 0) { xen_shutdown_fatal_error("Internal error: Invalid read " "emulation. (%s, rc: %d)\n", __func__, rc); return 0; } /* calculate next address to find */ emul_len -= reg->size; if (emul_len > 0) { find_addr = real_offset + reg->size; } } else { /* nothing to do with passthrough type register, * continue to find next byte */ emul_len--; find_addr++; } }
static void sysbus_esp_hard_reset(DeviceState *dev) { SysBusESPState *sysbus = DO_UPCAST(SysBusESPState, busdev.qdev, dev); esp_hard_reset(&sysbus->esp); }
static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size) { dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque; uint16_t data[10]; int packet_type; uint32_t available, address; int width, rx_len = size; uint32_t checksum; width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER | SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC); packet_type = receive_filter(s, buf, size); if (packet_type < 0) { DPRINTF("packet not for netcard\n"); return -1; } /* XXX: Check byte ordering */ /* Check for EOL */ if (s->regs[SONIC_LLFA] & 0x1) { /* Are we still in resource exhaustion? */ size = sizeof(uint16_t) * 1 * width; address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width; s->memory_rw(s->mem_opaque, address, (uint8_t*)data, size, 0); if (data[0 * width] & 0x1) { /* Still EOL ; stop reception */ return -1; } else { s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; } } /* Save current position */ s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1]; s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0]; /* Calculate the ethernet checksum */ #ifdef SONIC_CALCULATE_RXCRC checksum = cpu_to_le32(crc32(0, buf, rx_len)); #else checksum = 0; #endif /* Put packet into RBA */ DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]); address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]; s->memory_rw(s->mem_opaque, address, (uint8_t*)buf, rx_len, 1); address += rx_len; s->memory_rw(s->mem_opaque, address, (uint8_t*)&checksum, 4, 1); rx_len += 4; s->regs[SONIC_CRBA1] = address >> 16; s->regs[SONIC_CRBA0] = address & 0xffff; available = (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]; available -= rx_len / 2; s->regs[SONIC_RBWC1] = available >> 16; s->regs[SONIC_RBWC0] = available & 0xffff; /* Update status */ if (((s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]) < s->regs[SONIC_EOBC]) { s->regs[SONIC_RCR] |= SONIC_RCR_LPKT; } s->regs[SONIC_RCR] |= packet_type; s->regs[SONIC_RCR] |= SONIC_RCR_PRX; if (s->loopback_packet) { s->regs[SONIC_RCR] |= SONIC_RCR_LBK; s->loopback_packet = 0; } /* Write status to memory */ DPRINTF("Write status at %08x\n", (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]); data[0 * width] = s->regs[SONIC_RCR]; /* status */ data[1 * width] = rx_len; /* byte count */ data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */ data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */ data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */ size = sizeof(uint16_t) * 5 * width; s->memory_rw(s->mem_opaque, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], (uint8_t *)data, size, 1); /* Move to next descriptor */ size = sizeof(uint16_t) * width; s->memory_rw(s->mem_opaque, ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width, (uint8_t *)data, size, 0); s->regs[SONIC_LLFA] = data[0 * width]; if (s->regs[SONIC_LLFA] & 0x1) { /* EOL detected */ s->regs[SONIC_ISR] |= SONIC_ISR_RDE; } else { data[0 * width] = 0; /* in_use */ s->memory_rw(s->mem_opaque, ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width, (uint8_t *)data, size, 1); s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff); if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) { /* Read next RRA */ do_read_rra(s); } } /* Done */ dp8393x_update_irq(s); return size; }
void rtc_set_memory(ISADevice *dev, int addr, int val) { RTCState *s = DO_UPCAST(RTCState, dev, dev); if (addr >= 0 && addr <= 127) s->cmos_data[addr] = val; }
VHostNetState *tap_get_vhost_net(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP); return s->vhost_net; }
static void xlx_spi_reset(DeviceState *d) { xlx_spi_do_reset(DO_UPCAST(XilinxSPI, busdev.qdev, d)); }
int tap_get_fd(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP); return s->fd; }
static void tap_poll(NetClientState *nc, bool enable) { TAPState *s = DO_UPCAST(TAPState, nc, nc); tap_read_poll(s, enable); tap_write_poll(s, enable); }
static void tap_send_completed(NetClientState *nc, ssize_t len) { TAPState *s = DO_UPCAST(TAPState, nc, nc); tap_read_poll(s, true); }
VHostNetState *vhost_user_get_vhost_net(NetClientState *nc) { VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER); return s->vhost_net; }
void rtc_set_date(ISADevice *dev, const struct tm *tm) { RTCState *s = DO_UPCAST(RTCState, dev, dev); s->current_tm = *tm; rtc_copy_date(s); }
uint64_t vhost_user_get_acked_features(NetClientState *nc) { VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER); return s->acked_features; }
/* Callback function that's called when the guest sends us data */ static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) { VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); return qemu_chr_write(vcon->chr, buf, len); }
static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len) { XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); int index = 0; XenPTRegGroup *reg_grp_entry = NULL; int rc = 0; uint32_t read_val = 0; int emul_len = 0; XenPTReg *reg_entry = NULL; uint32_t find_addr = addr; XenPTRegInfo *reg = NULL; if (xen_pt_pci_config_access_check(d, addr, len)) { return; } XEN_PT_LOG_CONFIG(d, addr, val, len); /* check unused BAR register */ index = xen_pt_bar_offset_to_index(addr); if ((index >= 0) && (val > 0 && val < XEN_PT_BAR_ALLF) && (s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED)) { XEN_PT_WARN(d, "Guest attempt to set address to unused Base Address " "Register. (addr: 0x%02x, len: %d)\n", addr, len); } /* find register group entry */ reg_grp_entry = xen_pt_find_reg_grp(s, addr); if (reg_grp_entry) { /* check 0-Hardwired register group */ if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) { /* ignore silently */ XEN_PT_WARN(d, "Access to 0-Hardwired register. " "(addr: 0x%02x, len: %d)\n", addr, len); return; } } rc = xen_host_pci_get_block(&s->real_device, addr, (uint8_t *)&read_val, len); if (rc < 0) { XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc); memset(&read_val, 0xff, len); } /* pass directly to the real device for passthrough type register group */ if (reg_grp_entry == NULL) { goto out; } memory_region_transaction_begin(); pci_default_write_config(d, addr, val, len); /* adjust the read and write value to appropriate CFC-CFF window */ read_val <<= (addr & 3) << 3; val <<= (addr & 3) << 3; emul_len = len; /* loop around the guest requested size */ while (emul_len > 0) { /* find register entry to be emulated */ reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr); if (reg_entry) { reg = reg_entry->reg; uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); uint8_t *ptr_val = NULL; valid_mask <<= (find_addr - real_offset) << 3; ptr_val = (uint8_t *)&val + (real_offset & 3); /* do emulation based on register size */ switch (reg->size) { case 1: if (reg->u.b.write) { rc = reg->u.b.write(s, reg_entry, ptr_val, read_val >> ((real_offset & 3) << 3), valid_mask); } break; case 2: if (reg->u.w.write) { rc = reg->u.w.write(s, reg_entry, (uint16_t *)ptr_val, (read_val >> ((real_offset & 3) << 3)), valid_mask); } break; case 4: if (reg->u.dw.write) { rc = reg->u.dw.write(s, reg_entry, (uint32_t *)ptr_val, (read_val >> ((real_offset & 3) << 3)), valid_mask); } break; }
/* Callback function that's called when the guest closes the port */ static void guest_close(VirtIOSerialPort *port) { VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); qemu_chr_guest_close(vcon->chr); }
static void fw_cfg_reset(DeviceState *d) { FWCfgState *s = DO_UPCAST(FWCfgState, busdev.qdev, d); fw_cfg_select(s, 0); }
static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size) { TAPState *s = DO_UPCAST(TAPState, nc, nc); return tap_win32_write(s->handle, buf, size); }
static ssize_t spapr_vlan_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { VIOsPAPRDevice *sdev = DO_UPCAST(NICState, nc, nc)->opaque; VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev; vlan_bd_t rxq_bd = ldq_tce(sdev, dev->buf_list + VLAN_RXQ_BD_OFF); vlan_bd_t bd; int buf_ptr = dev->use_buf_ptr; uint64_t handle; uint8_t control; dprintf("spapr_vlan_receive() [%s] rx_bufs=%d\n", sdev->qdev.id, dev->rx_bufs); if (!dev->isopen) { return -1; } if (!dev->rx_bufs) { return -1; } do { buf_ptr += 8; if (buf_ptr >= SPAPR_VIO_TCE_PAGE_SIZE) { buf_ptr = VLAN_RX_BDS_OFF; } bd = ldq_tce(sdev, dev->buf_list + buf_ptr); dprintf("use_buf_ptr=%d bd=0x%016llx\n", buf_ptr, (unsigned long long)bd); } while ((!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) && (buf_ptr != dev->use_buf_ptr)); if (!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) { /* Failed to find a suitable buffer */ return -1; } /* Remove the buffer from the pool */ dev->rx_bufs--; dev->use_buf_ptr = buf_ptr; stq_tce(sdev, dev->buf_list + dev->use_buf_ptr, 0); dprintf("Found buffer: ptr=%d num=%d\n", dev->use_buf_ptr, dev->rx_bufs); /* Transfer the packet data */ if (spapr_tce_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) { return -1; } dprintf("spapr_vlan_receive: DMA write completed\n"); /* Update the receive queue */ control = VLAN_RXQC_TOGGLE | VLAN_RXQC_VALID; if (rxq_bd & VLAN_BD_TOGGLE) { control ^= VLAN_RXQC_TOGGLE; } handle = ldq_tce(sdev, VLAN_BD_ADDR(bd)); stq_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8, handle); stw_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 4, size); sth_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 2, 8); stb_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr, control); dprintf("wrote rxq entry (ptr=0x%llx): 0x%016llx 0x%016llx\n", (unsigned long long)dev->rxq_ptr, (unsigned long long)ldq_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr), (unsigned long long)ldq_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8)); dev->rxq_ptr += 16; if (dev->rxq_ptr >= VLAN_BD_LEN(rxq_bd)) { dev->rxq_ptr = 0; stq_tce(sdev, dev->buf_list + VLAN_RXQ_BD_OFF, rxq_bd ^ VLAN_BD_TOGGLE); } if (sdev->signal_state & 1) { qemu_irq_pulse(sdev->qirq); } return size; }
int ide_get_bios_chs_trans(BusState *bus, int unit) { return DO_UPCAST(IDEBus, qbus, bus)->ifs[unit].chs_trans; }
static void scsi_free_request(SCSIRequest *req) { SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); g_free(r->buf); }
static void lance_reset(DeviceState *dev) { SysBusPCNetState *d = DO_UPCAST(SysBusPCNetState, busdev.qdev, dev); pcnet_h_reset(&d->state); }
static ssize_t smc91c111_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque; int status; int packetsize; uint32_t crc; int packetnum; uint8_t *p; if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) return -1; /* Short packets are padded with zeros. Receiving a packet < 64 bytes long is considered an error condition. */ if (size < 64) packetsize = 64; else packetsize = (size & ~1); packetsize += 6; crc = (s->rcr & RCR_STRIP_CRC) == 0; if (crc) packetsize += 4; /* TODO: Flag overrun and receive errors. */ if (packetsize > 2048) return -1; packetnum = smc91c111_allocate_packet(s); if (packetnum == 0x80) return -1; s->rx_fifo[s->rx_fifo_len++] = packetnum; p = &s->data[packetnum][0]; /* ??? Multicast packets? */ status = 0; if (size > 1518) status |= RS_TOOLONG; if (size & 1) status |= RS_ODDFRAME; *(p++) = status & 0xff; *(p++) = status >> 8; *(p++) = packetsize & 0xff; *(p++) = packetsize >> 8; memcpy(p, buf, size & ~1); p += (size & ~1); /* Pad short packets. */ if (size < 64) { int pad; if (size & 1) *(p++) = buf[size - 1]; pad = 64 - size; memset(p, 0, pad); p += pad; size = 64; } /* It's not clear if the CRC should go before or after the last byte in odd sized packets. Linux disables the CRC, so that's no help. The pictures in the documentation show the CRC aligned on a 16-bit boundary before the last odd byte, so that's what we do. */ if (crc) { crc = crc32(~0, buf, size); *(p++) = crc & 0xff; crc >>= 8; *(p++) = crc & 0xff; crc >>= 8; *(p++) = crc & 0xff; crc >>= 8; *(p++) = crc & 0xff; crc >>= 8; } if (size & 1) { *(p++) = buf[size - 1]; *(p++) = 0x60; } else { *(p++) = 0; *(p++) = 0x40; } /* TODO: Raise early RX interrupt? */ s->int_level |= INT_RCV; smc91c111_update(s); return size; }
static void lance_cleanup(VLANClientState *nc) { PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque; pcnet_common_cleanup(d); }
static void eth_cleanup(NetClientState *nc) { struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque; s->nic = NULL; }
static ssize_t netmap_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) { NetmapState *s = DO_UPCAST(NetmapState, nc, nc); struct netmap_ring *ring = s->tx; uint32_t last; uint32_t idx; uint8_t *dst; int j; uint32_t i; if (unlikely(!ring)) { /* Drop the packet. */ return iov_size(iov, iovcnt); } last = i = ring->cur; if (nm_ring_space(ring) < iovcnt) { /* Not enough netmap slots. */ netmap_write_poll(s, true); return 0; } for (j = 0; j < iovcnt; j++) { int iov_frag_size = iov[j].iov_len; int offset = 0; int nm_frag_size; /* Split each iovec fragment over more netmap slots, if necessary. */ while (iov_frag_size) { nm_frag_size = MIN(iov_frag_size, ring->nr_buf_size); if (unlikely(nm_ring_empty(ring))) { /* We run out of netmap slots while splitting the iovec fragments. */ netmap_write_poll(s, true); return 0; } idx = ring->slot[i].buf_idx; dst = (uint8_t *)NETMAP_BUF(ring, idx); ring->slot[i].len = nm_frag_size; ring->slot[i].flags = NS_MOREFRAG; pkt_copy(iov[j].iov_base + offset, dst, nm_frag_size); last = i; i = nm_ring_next(ring, i); offset += nm_frag_size; iov_frag_size -= nm_frag_size; } } /* The last slot must not have NS_MOREFRAG set. */ ring->slot[last].flags &= ~NS_MOREFRAG; /* Now update ring->cur and ring->head. */ ring->cur = ring->head = i; ioctl(s->nmd->fd, NIOCTXSYNC, NULL); return iov_size(iov, iovcnt); }