Skip to content

Commit 7b5b913

Browse files
committed
Implement crude head pointer writeback
1 parent ddf8037 commit 7b5b913

File tree

1 file changed

+57
-5
lines changed

1 file changed

+57
-5
lines changed

Diff for: src/driver/ixgbe.c

+57-5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ struct ixgbe_tx_queue {
3939
uint16_t clean_index;
4040
// position to insert packets for transmission
4141
uint16_t tx_index;
42+
// head writeback pointer
43+
uint32_t* head_pointer;
4244
// virtual addresses to map descriptors back to their mbuf for freeing
4345
void* virtual_addresses[];
4446
};
@@ -195,13 +197,23 @@ static void init_tx(struct ixgbe_device* dev) {
195197
// there are no defines for this in ixgbe_type.h for some reason
196198
// pthresh: 6:0, hthresh: 14:8, wthresh: 22:16
197199
txdctl &= ~(0x3F | (0x3F << 8) | (0x3F << 16)); // clear bits
198-
txdctl |= (36 | (8 << 8) | (4 << 16)); // from DPDK
200+
txdctl |= (36 | (8 << 8) | (0 << 16)); // from DPDK
199201
set_reg32(dev->addr, IXGBE_TXDCTL(i), txdctl);
200202

201203
// private data for the driver, 0-initialized
202204
struct ixgbe_tx_queue* queue = ((struct ixgbe_tx_queue*)(dev->tx_queues)) + i;
203205
queue->num_entries = NUM_TX_QUEUE_ENTRIES;
204206
queue->descriptors = (union ixgbe_adv_tx_desc*) mem.virt;
207+
208+
mem = memory_allocate_dma(4, true);
209+
queue->head_pointer = mem.virt;
210+
info("virt %p phy %lx", mem.virt, mem.phy);
211+
212+
// Set writeback head pointer address
213+
set_reg32(dev, IXGBE_TDWBAL(i), (uint32_t) (1 | mem.phy));
214+
set_reg32(dev, IXGBE_TDWBAH(i), (mem.phy >> 32));
215+
debug("TDWBAL %x", get_reg32(dev, IXGBE_TDWBAL(i)));
216+
debug("TDWBAH %x", get_reg32(dev, IXGBE_TDWBAH(i)));
205217
}
206218
// final step: enable DMA
207219
set_reg32(dev->addr, IXGBE_DMATXCTL, IXGBE_DMATXCTL_TE);
@@ -415,12 +427,18 @@ uint32_t ixgbe_tx_batch(struct ixy_device* ixy, uint16_t queue_id, struct pkt_bu
415427
uint16_t clean_index = queue->clean_index; // next descriptor to clean up
416428
uint16_t cur_index = queue->tx_index; // next descriptor to use for tx
417429

430+
//uint32_t iio_head = get_reg32(dev, IXGBE_TDH(queue_id));
431+
uint32_t ptr_head = *queue->head_pointer;
432+
//debug("TDH %u, head pointer: %u", iio_head, ptr_head);
433+
418434
// step 1: clean up descriptors that were sent out by the hardware and return them to the mempool
419435
// start by reading step 2 which is done first for each packet
420436
// cleaning up must be done in batches for performance reasons, so this is unfortunately somewhat complicated
421-
while (true) {
437+
//while (true) {
422438
// figure out how many descriptors can be cleaned up
423-
int32_t cleanable = cur_index - clean_index; // cur is always ahead of clean (invariant of our queue)
439+
//int32_t cleanable = cur_index - clean_index; // cur is always ahead of clean (invariant of our queue)
440+
/*
441+
int32_t cleanable = *queue->head_pointer - clean_index;
424442
if (cleanable < 0) { // handle wrap-around
425443
cleanable = queue->num_entries + cleanable;
426444
}
@@ -433,8 +451,33 @@ uint32_t ixgbe_tx_batch(struct ixy_device* ixy, uint16_t queue_id, struct pkt_bu
433451
if (cleanup_to >= queue->num_entries) {
434452
cleanup_to -= queue->num_entries;
435453
}
454+
*/
455+
456+
int32_t cleanup_to = ptr_head;
457+
int32_t i = clean_index;
458+
// Ring empty
459+
if (clean_index == cleanup_to) {
460+
goto skip_clean;
461+
}
462+
cleanup_to--;
463+
if (cleanup_to < 0) {
464+
cleanup_to += queue->num_entries;
465+
}
466+
//debug("cleaning from %i to %i", clean_index, cleanup_to);
467+
while (true) {
468+
struct pkt_buf* buf = queue->virtual_addresses[i];
469+
pkt_buf_free(buf);
470+
if (i == cleanup_to) {
471+
break;
472+
}
473+
i = wrap_ring(i, queue->num_entries);
474+
}
475+
clean_index = wrap_ring(cleanup_to, queue->num_entries);
476+
477+
/*
436478
volatile union ixgbe_adv_tx_desc* txd = queue->descriptors + cleanup_to;
437479
uint32_t status = txd->wb.status;
480+
438481
// hardware sets this flag as soon as it's sent out, we can give back all bufs in the batch back to the mempool
439482
if (status & IXGBE_ADVTXD_STAT_DD) {
440483
int32_t i = clean_index;
@@ -453,8 +496,10 @@ uint32_t ixgbe_tx_batch(struct ixy_device* ixy, uint16_t queue_id, struct pkt_bu
453496
// the queue forever if you stop transmitting, but that's not a real concern
454497
break;
455498
}
456-
}
499+
*/
500+
//}
457501
queue->clean_index = clean_index;
502+
skip_clean:;
458503

459504
// step 2: send out as many of our packets as possible
460505
uint32_t sent;
@@ -473,7 +518,13 @@ uint32_t ixgbe_tx_batch(struct ixy_device* ixy, uint16_t queue_id, struct pkt_bu
473518
txd->read.buffer_addr = buf->buf_addr_phy + offsetof(struct pkt_buf, data);
474519
// always the same flags: one buffer (EOP), advanced data descriptor, CRC offload, data length
475520
txd->read.cmd_type_len =
476-
IXGBE_ADVTXD_DCMD_EOP | IXGBE_ADVTXD_DCMD_RS | IXGBE_ADVTXD_DCMD_IFCS | IXGBE_ADVTXD_DCMD_DEXT | IXGBE_ADVTXD_DTYP_DATA | buf->size;
521+
IXGBE_ADVTXD_DCMD_EOP | /*IXGBE_ADVTXD_DCMD_RS |*/ IXGBE_ADVTXD_DCMD_IFCS | IXGBE_ADVTXD_DCMD_DEXT | IXGBE_ADVTXD_DTYP_DATA | buf->size;
522+
523+
// RS bit signals the NIC head pointer updates
524+
// Implement someting more fancy. I did not find an upper limit for gaps, but sent packets can't be cleaned until one with the bit set passes.
525+
if (sent == num_bufs - 1) {
526+
txd->read.cmd_type_len |= IXGBE_ADVTXD_DCMD_RS;
527+
}
477528
// no fancy offloading stuff - only the total payload length
478529
// implement offloading flags here:
479530
// * ip checksum offloading is trivial: just set the offset
@@ -487,3 +538,4 @@ uint32_t ixgbe_tx_batch(struct ixy_device* ixy, uint16_t queue_id, struct pkt_bu
487538
return sent;
488539
}
489540

541+

0 commit comments

Comments
 (0)