nvnet: Fix ringbuffer index reset, multi-desc frames, IRQ status bits

This commit is contained in:
Matt Borgerson 2020-06-19 04:21:22 -07:00 committed by mborgerson
parent cd9888c0c0
commit 7f4b9d5480
1 changed files with 125 additions and 32 deletions

View File

@ -240,6 +240,7 @@ enum {
#define RX_NIC_BUFSIZE (DEFAULT_MTU + 64)
/* even more slack */
#define RX_ALLOC_BUFSIZE (DEFAULT_MTU + 128)
#define TX_ALLOC_BUFSIZE (DEFAULT_MTU + 128)
#define OOM_REFILL (1 + HZ / 20)
#define POLL_WAIT (1 + HZ / 100)
@ -286,17 +287,21 @@ typedef struct NvNetState {
uint8_t tx_ring_size;
uint8_t rx_ring_index;
uint8_t rx_ring_size;
uint8_t txrx_dma_buf[RX_ALLOC_BUFSIZE];
uint8_t tx_dma_buf[TX_ALLOC_BUFSIZE];
uint32_t tx_dma_buf_offset;
uint8_t rx_dma_buf[RX_ALLOC_BUFSIZE];
FILE *packet_dump_file;
char *packet_dump_path;
} NvNetState;
#pragma pack(1)
struct RingDesc {
uint32_t packet_buffer;
uint16_t length;
uint16_t flags;
};
#pragma pack()
/*******************************************************************************
* Helper Macros
@ -360,6 +365,7 @@ static void nvnet_hex_dump(NvNetState *s, const uint8_t *buf, int size);
#ifdef DEBUG
static const char *nvnet_get_reg_name(hwaddr addr);
static const char *nvnet_get_mii_reg_name(uint8_t reg);
static void nvnet_dump_ring_descriptors(NvNetState *s);
#endif
/*******************************************************************************
@ -373,8 +379,10 @@ static void nvnet_update_irq(NvNetState *s)
{
PCIDevice *d = PCI_DEVICE(s);
if (nvnet_get_reg(s, NvRegIrqMask, 4) &&
nvnet_get_reg(s, NvRegIrqStatus, 4)) {
uint32_t irq_mask = nvnet_get_reg(s, NvRegIrqMask, 4);
uint32_t irq_status = nvnet_get_reg(s, NvRegIrqStatus, 4);
if (irq_mask & irq_status) {
NVNET_DPRINTF("Asserting IRQ\n");
pci_irq_assert(d);
} else {
@ -557,6 +565,9 @@ static void nvnet_mmio_write(void *opaque, hwaddr addr,
case NvRegTxRxControl:
if (val == NVREG_TXRXCTL_KICK) {
NVNET_DPRINTF("NvRegTxRxControl = NVREG_TXRXCTL_KICK!\n");
#ifdef DEBUG
nvnet_dump_ring_descriptors(s);
#endif
nvnet_dma_packet_from_guest(s);
}
@ -565,7 +576,14 @@ static void nvnet_mmio_write(void *opaque, hwaddr addr,
break;
}
if (val & NVREG_TXRXCTL_RESET) {
s->tx_ring_index = 0;
s->rx_ring_index = 0;
s->tx_dma_buf_offset = 0;
}
if (val & NVREG_TXRXCTL_BIT1) {
// FIXME
nvnet_set_reg(s, NvRegIrqStatus, 0, 4);
break;
} else if (val == 0) {
@ -641,23 +659,31 @@ static ssize_t nvnet_receive_iov(NetClientState *nc,
NVNET_DPRINTF("nvnet: Packet received!\n");
if (size > sizeof(s->txrx_dma_buf)) {
if (size > sizeof(s->rx_dma_buf)) {
NVNET_DPRINTF("nvnet_receive_iov packet too large!\n");
assert(0);
return -1;
}
iov_to_buf(iov, iovcnt, 0, s->txrx_dma_buf, size);
nvnet_hex_dump(s, s->txrx_dma_buf, size);
return nvnet_dma_packet_to_guest(s, s->txrx_dma_buf, size);
iov_to_buf(iov, iovcnt, 0, s->rx_dma_buf, size);
#ifdef DEBUG
nvnet_hex_dump(s, s->rx_dma_buf, size);
#endif
return nvnet_dma_packet_to_guest(s, s->rx_dma_buf, size);
}
static ssize_t nvnet_dma_packet_to_guest(NvNetState *s,
const uint8_t *buf, size_t size)
{
PCIDevice *d = PCI_DEVICE(s);
struct RingDesc desc;
int i;
bool did_receive = false;
nvnet_set_reg(s, NvRegTxRxControl,
nvnet_get_reg(s, NvRegTxRxControl, 4) & ~NVREG_TXRXCTL_IDLE,
4);
for (i = 0; i < s->rx_ring_size; i++) {
/* Read current ring descriptor */
@ -665,18 +691,20 @@ static ssize_t nvnet_dma_packet_to_guest(NvNetState *s,
dma_addr_t rx_ring_addr = nvnet_get_reg(s, NvRegRxRingPhysAddr, 4);
rx_ring_addr += s->rx_ring_index * sizeof(desc);
pci_dma_read(d, rx_ring_addr, &desc, sizeof(desc));
NVNET_DPRINTF("Looking at ring descriptor %d (0x%llx): ",
NVNET_DPRINTF("RX: Looking at ring descriptor %d (0x%llx): ",
s->rx_ring_index, rx_ring_addr);
NVNET_DPRINTF("Buffer: 0x%x, ", desc.packet_buffer);
NVNET_DPRINTF("Length: 0x%x, ", desc.length);
NVNET_DPRINTF("Flags: 0x%x\n", desc.flags);
s->rx_ring_index += 1;
if (!(desc.flags & NV_RX_AVAIL) || !(desc.length >= size)) {
continue;
if (!(desc.flags & NV_RX_AVAIL)) {
break;
}
assert((desc.length+1) >= size); // FIXME
s->rx_ring_index += 1;
/* Transfer packet from device to memory */
NVNET_DPRINTF("Transferring packet, size 0x%zx, to memory at 0x%x\n",
size, desc.packet_buffer);
@ -692,14 +720,24 @@ static ssize_t nvnet_dma_packet_to_guest(NvNetState *s,
/* Trigger interrupt */
NVNET_DPRINTF("Triggering interrupt\n");
nvnet_set_reg(s, NvRegIrqStatus, NVREG_IRQSTAT_BIT1, 4);
uint32_t irq_status = nvnet_get_reg(s, NvRegIrqStatus, 4);
nvnet_set_reg(s, NvRegIrqStatus, irq_status | NVREG_IRQSTAT_BIT1, 4);
nvnet_update_irq(s);
return size;
did_receive = true;
break;
}
/* Could not find free buffer, or packet too large. */
NVNET_DPRINTF("Could not find free buffer!\n");
return -1;
nvnet_set_reg(s, NvRegTxRxControl,
nvnet_get_reg(s, NvRegTxRxControl, 4) | NVREG_TXRXCTL_IDLE,
4);
if (did_receive) {
return size;
} else {
/* Could not find free buffer, or packet too large. */
NVNET_DPRINTF("Could not find free buffer!\n");
return -1;
}
}
static ssize_t nvnet_dma_packet_from_guest(NvNetState *s)
@ -707,8 +745,12 @@ static ssize_t nvnet_dma_packet_from_guest(NvNetState *s)
PCIDevice *d = PCI_DEVICE(s);
struct RingDesc desc;
bool is_last_packet;
int i;
bool packet_sent = false;
int i;
nvnet_set_reg(s, NvRegTxRxControl,
nvnet_get_reg(s, NvRegTxRxControl, 4) & ~NVREG_TXRXCTL_IDLE,
4);
for (i = 0; i < s->tx_ring_size; i++) {
/* Read ring descriptor */
@ -716,27 +758,34 @@ static ssize_t nvnet_dma_packet_from_guest(NvNetState *s)
dma_addr_t tx_ring_addr = nvnet_get_reg(s, NvRegTxRingPhysAddr, 4);
tx_ring_addr += s->tx_ring_index * sizeof(desc);
pci_dma_read(d, tx_ring_addr, &desc, sizeof(desc));
NVNET_DPRINTF("Looking at ring desc %d (%llx): ",
NVNET_DPRINTF("TX: Looking at ring desc %d (%llx): ",
s->tx_ring_index, tx_ring_addr);
NVNET_DPRINTF("Buffer: 0x%x, ", desc.packet_buffer);
NVNET_DPRINTF("Length: 0x%x, ", desc.length);
NVNET_DPRINTF("Flags: 0x%x\n", desc.flags);
s->tx_ring_index += 1;
if (!(desc.flags & NV_TX_VALID)) {
continue;
break;
}
s->tx_ring_index += 1;
/* Transfer packet from guest memory */
NVNET_DPRINTF("Sending packet...\n");
assert((s->tx_dma_buf_offset + desc.length + 1) <= sizeof(s->tx_dma_buf));
pci_dma_read(d, desc.packet_buffer,
s->txrx_dma_buf, desc.length + 1);
nvnet_send_packet(s, s->txrx_dma_buf, desc.length + 1);
packet_sent = true;
&s->tx_dma_buf[s->tx_dma_buf_offset],
desc.length + 1);
s->tx_dma_buf_offset += desc.length + 1;
/* Update descriptor */
is_last_packet = desc.flags & NV_TX_LASTPACKET;
if (is_last_packet) {
NVNET_DPRINTF("Sending packet...\n");
nvnet_send_packet(s, s->tx_dma_buf, s->tx_dma_buf_offset);
s->tx_dma_buf_offset = 0;
packet_sent = true;
}
desc.flags &= ~(NV_TX_VALID | NV_TX_RETRYERROR | NV_TX_DEFERRED |
NV_TX_CARRIERLOST | NV_TX_LATECOLLISION | NV_TX_UNDERFLOW |
NV_TX_ERROR);
@ -744,7 +793,7 @@ static ssize_t nvnet_dma_packet_from_guest(NvNetState *s)
pci_dma_write(d, tx_ring_addr, &desc, sizeof(desc));
if (is_last_packet) {
NVNET_DPRINTF(" -- Last packet\n");
// FIXME
break;
}
}
@ -752,10 +801,15 @@ static ssize_t nvnet_dma_packet_from_guest(NvNetState *s)
/* Trigger interrupt */
if (packet_sent) {
NVNET_DPRINTF("Triggering interrupt\n");
nvnet_set_reg(s, NvRegIrqStatus, NVREG_IRQSTAT_BIT4, 4);
uint32_t irq_status = nvnet_get_reg(s, NvRegIrqStatus, 4);
nvnet_set_reg(s, NvRegIrqStatus, irq_status | NVREG_IRQSTAT_BIT4, 4);
nvnet_update_irq(s);
}
nvnet_set_reg(s, NvRegTxRxControl,
nvnet_get_reg(s, NvRegTxRxControl, 4) | NVREG_TXRXCTL_IDLE,
4);
return 0;
}
@ -871,6 +925,16 @@ static void nvnet_reset(void *opaque)
if (qemu_get_queue(s->nic)->link_down) {
nvnet_link_down(s);
}
memset(&s->regs, 0, sizeof(s->regs));
memset(&s->phy_regs, 0, sizeof(s->phy_regs));
s->tx_ring_index = 0;
s->tx_ring_size = 0;
s->rx_ring_index = 0;
s->rx_ring_size = 0;
memset(&s->tx_dma_buf, 0, sizeof(s->tx_dma_buf));
s->tx_dma_buf_offset = 0;
memset(&s->rx_dma_buf, 0, sizeof(s->rx_dma_buf));
}
static void qdev_nvnet_reset(DeviceState *dev)
@ -973,10 +1037,7 @@ static const char *nvnet_get_reg_name(hwaddr addr)
default: return "Unknown";
}
}
#endif
#ifdef DEBUG
/*
* Get PHY register name.
*/
@ -992,6 +1053,39 @@ static const char *nvnet_get_mii_reg_name(uint8_t reg)
default: return "Unknown";
}
}
static void nvnet_dump_ring_descriptors(NvNetState *s)
{
struct RingDesc desc;
PCIDevice *d = PCI_DEVICE(s);
NVNET_DPRINTF("------------------------------------------------\n");
for (int i = 0; i < s->tx_ring_size; i++) {
/* Read ring descriptor */
dma_addr_t tx_ring_addr = nvnet_get_reg(s, NvRegTxRingPhysAddr, 4);
tx_ring_addr += i * sizeof(desc);
pci_dma_read(d, tx_ring_addr, &desc, sizeof(desc));
NVNET_DPRINTF("TX: Dumping ring desc %d (%llx): ",
i, tx_ring_addr);
NVNET_DPRINTF("Buffer: 0x%x, ", desc.packet_buffer);
NVNET_DPRINTF("Length: 0x%x, ", desc.length);
NVNET_DPRINTF("Flags: 0x%x\n", desc.flags);
}
NVNET_DPRINTF("------------------------------------------------\n");
for (int i = 0; i < s->rx_ring_size; i++) {
/* Read ring descriptor */
dma_addr_t rx_ring_addr = nvnet_get_reg(s, NvRegRxRingPhysAddr, 4);
rx_ring_addr += i * sizeof(desc);
pci_dma_read(d, rx_ring_addr, &desc, sizeof(desc));
NVNET_DPRINTF("RX: Dumping ring desc %d (%llx): ",
i, rx_ring_addr);
NVNET_DPRINTF("Buffer: 0x%x, ", desc.packet_buffer);
NVNET_DPRINTF("Length: 0x%x, ", desc.length);
NVNET_DPRINTF("Flags: 0x%x\n", desc.flags);
}
NVNET_DPRINTF("------------------------------------------------\n");
}
#endif
/*******************************************************************************
@ -1010,7 +1104,6 @@ static const VMStateDescription vmstate_nvnet = {
VMSTATE_UINT8(tx_ring_size, NvNetState),
VMSTATE_UINT8(rx_ring_index, NvNetState),
VMSTATE_UINT8(rx_ring_size, NvNetState),
VMSTATE_UINT8_ARRAY(txrx_dma_buf, NvNetState, RX_ALLOC_BUFSIZE),
VMSTATE_END_OF_LIST()
},
};