Ensō 0.4.6
Software API reference
Loading...
Searching...
No Matches
pcie.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2022, Carnegie Mellon University
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted (subject to the limitations in the disclaimer
6 * below) provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * * Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
20 * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
21 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
22 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
40#include "pcie.h"
41
42#include <arpa/inet.h>
43#include <enso/config.h>
44#include <enso/consts.h>
45#include <enso/helpers.h>
46#include <immintrin.h>
47#include <sched.h>
48#include <string.h>
49#include <sys/mman.h>
50#include <time.h>
51#include <unistd.h>
52
53#include <algorithm>
54#include <cassert>
55#include <cerrno>
56#include <cstdint>
57#include <cstdlib>
58#include <ctime>
59#include <iomanip>
60#include <iostream>
61#include <limits>
62#include <stdexcept>
63
64// Automatically points to the device backend configured at compile time.
65#include <dev_backend.h>
66
67namespace enso {
68
69static _enso_always_inline void try_clflush([[maybe_unused]] void* addr) {
70#ifdef __CLFLUSHOPT__
71 _mm_clflushopt(addr);
72#endif
73}
74
75int notification_buf_init(uint32_t bdf, int32_t bar,
76 struct NotificationBufPair* notification_buf_pair,
77 const std::string& huge_page_prefix) {
78 DevBackend* fpga_dev = DevBackend::Create(bdf, bar);
79 if (unlikely(fpga_dev == nullptr)) {
80 std::cerr << "Could not create device" << std::endl;
81 return -1;
82 }
83 notification_buf_pair->fpga_dev = fpga_dev;
84
85 int notif_pipe_id = fpga_dev->AllocateNotifBuf();
86
87 if (notif_pipe_id < 0) {
88 std::cerr << "Could not allocate notification buffer" << std::endl;
89 return -1;
90 }
91
92 notification_buf_pair->id = notif_pipe_id;
93
94 void* uio_mmap_bar2_addr =
95 fpga_dev->uio_mmap((1 << 12) * (kMaxNbFlows + kMaxNbApps), 2);
96 if (uio_mmap_bar2_addr == MAP_FAILED) {
97 std::cerr << "Could not get mmap uio memory!" << std::endl;
98 return -1;
99 }
100
101 notification_buf_pair->uio_mmap_bar2_addr = uio_mmap_bar2_addr;
102
103 // Register associated with the notification buffer. Notification buffer
104 // registers come after the enso pipe ones, that's why we use kMaxNbFlows
105 // as an offset.
106 volatile struct QueueRegs* notification_buf_pair_regs =
107 (struct QueueRegs*)((uint8_t*)uio_mmap_bar2_addr +
108 (notif_pipe_id + kMaxNbFlows) * kMemorySpacePerQueue);
109
110 // Make sure the notification buffer is disabled.
111 DevBackend::mmio_write32(&notification_buf_pair_regs->rx_mem_low, 0);
112 DevBackend::mmio_write32(&notification_buf_pair_regs->rx_mem_high, 0);
113 while (DevBackend::mmio_read32(&notification_buf_pair_regs->rx_mem_low) != 0)
114 continue;
115
116 while (DevBackend::mmio_read32(&notification_buf_pair_regs->rx_mem_high) != 0)
117 continue;
118
119 DevBackend::mmio_write32(&notification_buf_pair_regs->rx_tail, 0);
120 while (DevBackend::mmio_read32(&notification_buf_pair_regs->rx_tail) != 0)
121 continue;
122
123 DevBackend::mmio_write32(&notification_buf_pair_regs->rx_head, 0);
124 while (DevBackend::mmio_read32(&notification_buf_pair_regs->rx_head) != 0)
125 continue;
126
127 std::string huge_page_path = huge_page_prefix +
128 std::string(kHugePageNotifBufPathPrefix) +
129 std::to_string(notification_buf_pair->id);
130
131 notification_buf_pair->regs = (struct QueueRegs*)notification_buf_pair_regs;
132 notification_buf_pair->rx_buf =
133 (struct RxNotification*)get_huge_page(huge_page_path);
134 if (notification_buf_pair->rx_buf == NULL) {
135 std::cerr << "Could not get huge page" << std::endl;
136 return -1;
137 }
138
139 memset(notification_buf_pair->rx_buf, 0, kNotificationBufSize * 64);
140
141 // Use first half of the huge page for RX and second half for TX.
142 notification_buf_pair->tx_buf =
143 (struct TxNotification*)((uint64_t)notification_buf_pair->rx_buf +
145
146 memset(notification_buf_pair->tx_buf, 0, kNotificationBufSize * 64);
147
148 uint64_t phys_addr =
149 fpga_dev->ConvertVirtAddrToDevAddr(notification_buf_pair->rx_buf);
150
151 notification_buf_pair->rx_head_ptr =
152 (uint32_t*)&notification_buf_pair_regs->rx_head;
153 notification_buf_pair->tx_tail_ptr =
154 (uint32_t*)&notification_buf_pair_regs->tx_tail;
155
156 notification_buf_pair->rx_head =
157 DevBackend::mmio_read32(notification_buf_pair->rx_head_ptr);
158
159 // Preserve TX DSC tail and make head have the same value.
160 notification_buf_pair->tx_tail =
161 DevBackend::mmio_read32(notification_buf_pair->tx_tail_ptr);
162
163 notification_buf_pair->tx_head = notification_buf_pair->tx_tail;
164
165 DevBackend::mmio_write32(&notification_buf_pair_regs->tx_head,
166 notification_buf_pair->tx_head);
167
168 notification_buf_pair->pending_rx_pipe_tails = (uint32_t*)malloc(
169 sizeof(*(notification_buf_pair->pending_rx_pipe_tails)) * kMaxNbFlows);
170 if (notification_buf_pair->pending_rx_pipe_tails == NULL) {
171 std::cerr << "Could not allocate memory" << std::endl;
172 return -1;
173 }
174 memset(notification_buf_pair->pending_rx_pipe_tails, 0, kMaxNbFlows);
175
176 notification_buf_pair->wrap_tracker =
177 (uint8_t*)malloc(kNotificationBufSize / 8);
178 if (notification_buf_pair->wrap_tracker == NULL) {
179 std::cerr << "Could not allocate memory" << std::endl;
180 return -1;
181 }
182 memset(notification_buf_pair->wrap_tracker, 0, kNotificationBufSize / 8);
183
184 notification_buf_pair->next_rx_pipe_ids =
185 (enso_pipe_id_t*)malloc(kNotificationBufSize * sizeof(enso_pipe_id_t));
186 if (notification_buf_pair->next_rx_pipe_ids == NULL) {
187 std::cerr << "Could not allocate memory" << std::endl;
188 return -1;
189 }
190
191 notification_buf_pair->next_rx_ids_head = 0;
192 notification_buf_pair->next_rx_ids_tail = 0;
193 notification_buf_pair->tx_full_cnt = 0;
194 notification_buf_pair->nb_unreported_completions = 0;
195 notification_buf_pair->huge_page_prefix = huge_page_prefix;
196
197 // Setting the address enables the queue. Do this last.
198 // Use first half of the huge page for RX and second half for TX.
199 DevBackend::mmio_write32(&notification_buf_pair_regs->rx_mem_low,
200 (uint32_t)phys_addr);
201 DevBackend::mmio_write32(&notification_buf_pair_regs->rx_mem_high,
202 (uint32_t)(phys_addr >> 32));
203
204 phys_addr += kAlignedDscBufPairSize / 2;
205
206 DevBackend::mmio_write32(&notification_buf_pair_regs->tx_mem_low,
207 (uint32_t)phys_addr);
208 DevBackend::mmio_write32(&notification_buf_pair_regs->tx_mem_high,
209 (uint32_t)(phys_addr >> 32));
210
211 return 0;
212}
213
214int enso_pipe_init(struct RxEnsoPipeInternal* enso_pipe,
215 struct NotificationBufPair* notification_buf_pair,
216 bool fallback) {
217 void* uio_mmap_bar2_addr = notification_buf_pair->uio_mmap_bar2_addr;
218 DevBackend* fpga_dev =
219 static_cast<DevBackend*>(notification_buf_pair->fpga_dev);
220
221 int enso_pipe_id = fpga_dev->AllocatePipe(fallback);
222
223 if (enso_pipe_id < 0) {
224 std::cerr << "Could not allocate pipe" << std::endl;
225 return -1;
226 }
227
228 // Register associated with the enso pipe.
229 volatile struct QueueRegs* enso_pipe_regs =
230 (struct QueueRegs*)((uint8_t*)uio_mmap_bar2_addr +
231 enso_pipe_id * kMemorySpacePerQueue);
232 enso_pipe->regs = (struct QueueRegs*)enso_pipe_regs;
233
234 // Make sure the queue is disabled.
235 DevBackend::mmio_write32(&enso_pipe_regs->rx_mem_low, 0);
236 DevBackend::mmio_write32(&enso_pipe_regs->rx_mem_high, 0);
237 while (DevBackend::mmio_read32(&enso_pipe_regs->rx_mem_low) != 0 ||
238 DevBackend::mmio_read32(&enso_pipe_regs->rx_mem_high) != 0)
239 continue;
240
241 // Make sure head and tail start at zero.
242 DevBackend::mmio_write32(&enso_pipe_regs->rx_tail, 0);
243 while (DevBackend::mmio_read32(&enso_pipe_regs->rx_tail) != 0) continue;
244
245 DevBackend::mmio_write32(&enso_pipe_regs->rx_head, 0);
246 while (DevBackend::mmio_read32(&enso_pipe_regs->rx_head) != 0) continue;
247
248 std::string huge_page_path = notification_buf_pair->huge_page_prefix +
249 std::string(kHugePageRxPipePathPrefix) +
250 std::to_string(enso_pipe_id);
251
252 enso_pipe->buf = (uint32_t*)get_huge_page(huge_page_path, 0, true);
253 if (enso_pipe->buf == NULL) {
254 std::cerr << "Could not get huge page" << std::endl;
255 return -1;
256 }
257 uint64_t phys_addr = fpga_dev->ConvertVirtAddrToDevAddr(enso_pipe->buf);
258
259 enso_pipe->buf_phys_addr = phys_addr;
260 enso_pipe->phys_buf_offset = phys_addr - (uint64_t)(enso_pipe->buf);
261
262 enso_pipe->id = enso_pipe_id;
263 enso_pipe->buf_head_ptr = (uint32_t*)&enso_pipe_regs->rx_head;
264 enso_pipe->rx_head = 0;
265 enso_pipe->rx_tail = 0;
266 enso_pipe->huge_page_prefix = notification_buf_pair->huge_page_prefix;
267
268 // Make sure the last tail matches the current head.
269 notification_buf_pair->pending_rx_pipe_tails[enso_pipe->id] =
270 enso_pipe->rx_head;
271
272 // Setting the address enables the queue. Do this last.
273 // The least significant bits in rx_mem_low are used to keep the notification
274 // buffer ID. Therefore we add `notification_buf_pair->id` to the address.
275 DevBackend::mmio_write32(&enso_pipe_regs->rx_mem_low,
276 (uint32_t)phys_addr + notification_buf_pair->id);
277 DevBackend::mmio_write32(&enso_pipe_regs->rx_mem_high,
278 (uint32_t)(phys_addr >> 32));
279
280 update_fallback_queues_config(notification_buf_pair);
281
282 return enso_pipe_id;
283}
284
285int dma_init(struct NotificationBufPair* notification_buf_pair,
286 struct RxEnsoPipeInternal* enso_pipe, uint32_t bdf, int32_t bar,
287 const std::string& huge_page_prefix, bool fallback) {
288 printf("Running with NOTIFICATION_BUF_SIZE: %i\n", kNotificationBufSize);
289 printf("Running with ENSO_PIPE_SIZE: %i\n", kEnsoPipeSize);
290
291 int16_t core_id = sched_getcpu();
292 if (core_id < 0) {
293 std::cerr << "Could not get CPU id" << std::endl;
294 return -1;
295 }
296
297 // Set notification buffer only for the first socket.
298 if (notification_buf_pair->ref_cnt == 0) {
299 int ret = notification_buf_init(bdf, bar, notification_buf_pair,
300 huge_page_prefix);
301 if (ret != 0) {
302 return ret;
303 }
304 }
305
306 ++(notification_buf_pair->ref_cnt);
307
308 return enso_pipe_init(enso_pipe, notification_buf_pair, fallback);
309}
310
311static _enso_always_inline uint16_t
312__get_new_tails(struct NotificationBufPair* notification_buf_pair) {
313 struct RxNotification* notification_buf = notification_buf_pair->rx_buf;
314 uint32_t notification_buf_head = notification_buf_pair->rx_head;
315 uint16_t nb_consumed_notifications = 0;
316
317 uint16_t next_rx_ids_tail = notification_buf_pair->next_rx_ids_tail;
318
319 for (uint16_t i = 0; i < kBatchSize; ++i) {
320 struct RxNotification* cur_notification =
321 notification_buf + notification_buf_head;
322
323 // Check if the next notification was updated by the NIC.
324 if (cur_notification->signal == 0) {
325 break;
326 }
327
328 cur_notification->signal = 0;
329 notification_buf_head = (notification_buf_head + 1) % kNotificationBufSize;
330
331 enso_pipe_id_t enso_pipe_id = cur_notification->queue_id;
332 notification_buf_pair->pending_rx_pipe_tails[enso_pipe_id] =
333 (uint32_t)cur_notification->tail;
334
335 notification_buf_pair->next_rx_pipe_ids[next_rx_ids_tail] = enso_pipe_id;
336 next_rx_ids_tail = (next_rx_ids_tail + 1) % kNotificationBufSize;
337
338 ++nb_consumed_notifications;
339 }
340
341 notification_buf_pair->next_rx_ids_tail = next_rx_ids_tail;
342
343 if (likely(nb_consumed_notifications > 0)) {
344 // Update notification buffer head.
345 DevBackend::mmio_write32(notification_buf_pair->rx_head_ptr,
346 notification_buf_head);
347 notification_buf_pair->rx_head = notification_buf_head;
348 }
349
350 return nb_consumed_notifications;
351}
352
353uint16_t get_new_tails(struct NotificationBufPair* notification_buf_pair) {
354 return __get_new_tails(notification_buf_pair);
355}
356
357static _enso_always_inline uint32_t
358__consume_queue(struct RxEnsoPipeInternal* enso_pipe,
359 struct NotificationBufPair* notification_buf_pair, void** buf,
360 bool peek = false) {
361 uint32_t* enso_pipe_buf = enso_pipe->buf;
362 uint32_t enso_pipe_head = enso_pipe->rx_tail;
363 int queue_id = enso_pipe->id;
364
365 *buf = &enso_pipe_buf[enso_pipe_head * 16];
366
367 uint32_t enso_pipe_tail =
368 notification_buf_pair->pending_rx_pipe_tails[queue_id];
369
370 if (enso_pipe_tail == enso_pipe_head) {
371 return 0;
372 }
373
374 uint32_t flit_aligned_size =
375 ((enso_pipe_tail - enso_pipe_head) % ENSO_PIPE_SIZE) * 64;
376
377 if (!peek) {
378 enso_pipe_head = (enso_pipe_head + flit_aligned_size / 64) % ENSO_PIPE_SIZE;
379 enso_pipe->rx_tail = enso_pipe_head;
380 }
381
382 return flit_aligned_size;
383}
384
386 struct RxEnsoPipeInternal* enso_pipe,
387 struct NotificationBufPair* notification_buf_pair, void** buf) {
388 return __consume_queue(enso_pipe, notification_buf_pair, buf);
389}
390
392 struct RxEnsoPipeInternal* enso_pipe,
393 struct NotificationBufPair* notification_buf_pair, void** buf) {
394 return __consume_queue(enso_pipe, notification_buf_pair, buf, true);
395}
396
397static _enso_always_inline int32_t
398__get_next_enso_pipe_id(struct NotificationBufPair* notification_buf_pair) {
399 // Consume up to a batch of notifications at a time. If the number of consumed
400 // notifications is the same as the number of pending notifications, we are
401 // done processing the last batch and can get the next one. Using batches here
402 // performs **significantly** better compared to always fetching the latest
403 // notification.
404 uint16_t next_rx_ids_head = notification_buf_pair->next_rx_ids_head;
405 uint16_t next_rx_ids_tail = notification_buf_pair->next_rx_ids_tail;
406
407 if (next_rx_ids_head == next_rx_ids_tail) {
408 uint16_t nb_consumed_notifications = __get_new_tails(notification_buf_pair);
409 if (unlikely(nb_consumed_notifications == 0)) {
410 return -1;
411 }
412 }
413
414 enso_pipe_id_t enso_pipe_id =
415 notification_buf_pair->next_rx_pipe_ids[next_rx_ids_head];
416
417 notification_buf_pair->next_rx_ids_head =
418 (next_rx_ids_head + 1) % kNotificationBufSize;
419
420 return enso_pipe_id;
421}
422
424 struct NotificationBufPair* notification_buf_pair) {
425 return __get_next_enso_pipe_id(notification_buf_pair);
426}
427
428// Return next batch among all open sockets.
429uint32_t get_next_batch(struct NotificationBufPair* notification_buf_pair,
430 struct SocketInternal* socket_entries,
431 int* enso_pipe_id, void** buf) {
432 int32_t __enso_pipe_id = __get_next_enso_pipe_id(notification_buf_pair);
433
434 if (unlikely(__enso_pipe_id == -1)) {
435 return 0;
436 }
437
438 *enso_pipe_id = __enso_pipe_id;
439
440 struct SocketInternal* socket_entry = &socket_entries[__enso_pipe_id];
441 struct RxEnsoPipeInternal* enso_pipe = &socket_entry->enso_pipe;
442
443 return __consume_queue(enso_pipe, notification_buf_pair, buf);
444}
445
446void advance_pipe(struct RxEnsoPipeInternal* enso_pipe, size_t len) {
447 uint32_t rx_pkt_head = enso_pipe->rx_head;
448 uint32_t nb_flits = ((uint64_t)len - 1) / 64 + 1;
449 rx_pkt_head = (rx_pkt_head + nb_flits) % ENSO_PIPE_SIZE;
450
451 DevBackend::mmio_write32(enso_pipe->buf_head_ptr, rx_pkt_head);
452 enso_pipe->rx_head = rx_pkt_head;
453}
454
455void fully_advance_pipe(struct RxEnsoPipeInternal* enso_pipe) {
456 DevBackend::mmio_write32(enso_pipe->buf_head_ptr, enso_pipe->rx_tail);
457 enso_pipe->rx_head = enso_pipe->rx_tail;
458}
459
460void prefetch_pipe(struct RxEnsoPipeInternal* enso_pipe) {
461 DevBackend::mmio_write32(enso_pipe->buf_head_ptr, enso_pipe->rx_head);
462}
463
464static _enso_always_inline uint32_t
465__send_to_queue(struct NotificationBufPair* notification_buf_pair,
466 uint64_t phys_addr, uint32_t len) {
467 struct TxNotification* tx_buf = notification_buf_pair->tx_buf;
468 uint32_t tx_tail = notification_buf_pair->tx_tail;
469 uint32_t missing_bytes = len;
470
471 uint64_t transf_addr = phys_addr;
472 uint64_t hugepage_mask = ~((uint64_t)kBufPageSize - 1);
473 uint64_t hugepage_base_addr = transf_addr & hugepage_mask;
474 uint64_t hugepage_boundary = hugepage_base_addr + kBufPageSize;
475
476 while (missing_bytes > 0) {
477 uint32_t free_slots =
478 (notification_buf_pair->tx_head - tx_tail - 1) % kNotificationBufSize;
479
480 // Block until we can send.
481 while (unlikely(free_slots == 0)) {
482 ++notification_buf_pair->tx_full_cnt;
483 update_tx_head(notification_buf_pair);
484 free_slots =
485 (notification_buf_pair->tx_head - tx_tail - 1) % kNotificationBufSize;
486 }
487
488 struct TxNotification* tx_notification = tx_buf + tx_tail;
489 uint32_t req_length = std::min(missing_bytes, (uint32_t)kMaxTransferLen);
490 uint32_t missing_bytes_in_page = hugepage_boundary - transf_addr;
491 req_length = std::min(req_length, missing_bytes_in_page);
492
493 // If the transmission needs to be split among multiple requests, we
494 // need to set a bit in the wrap tracker.
495 uint8_t wrap_tracker_mask = (missing_bytes > req_length) << (tx_tail & 0x7);
496 notification_buf_pair->wrap_tracker[tx_tail / 8] |= wrap_tracker_mask;
497
498 tx_notification->length = req_length;
499 tx_notification->signal = 1;
500 tx_notification->phys_addr = transf_addr;
501
502 uint64_t huge_page_offset = (transf_addr + req_length) % kBufPageSize;
503 transf_addr = hugepage_base_addr + huge_page_offset;
504
505 tx_tail = (tx_tail + 1) % kNotificationBufSize;
506 missing_bytes -= req_length;
507 }
508
509 notification_buf_pair->tx_tail = tx_tail;
510 DevBackend::mmio_write32(notification_buf_pair->tx_tail_ptr, tx_tail);
511
512 return len;
513}
514
515uint32_t send_to_queue(struct NotificationBufPair* notification_buf_pair,
516 uint64_t phys_addr, uint32_t len) {
517 return __send_to_queue(notification_buf_pair, phys_addr, len);
518}
519
521 struct NotificationBufPair* notification_buf_pair) {
522 uint32_t completions;
523 update_tx_head(notification_buf_pair);
524 completions = notification_buf_pair->nb_unreported_completions;
525 notification_buf_pair->nb_unreported_completions = 0;
526
527 return completions;
528}
529
530void update_tx_head(struct NotificationBufPair* notification_buf_pair) {
531 struct TxNotification* tx_buf = notification_buf_pair->tx_buf;
532 uint32_t head = notification_buf_pair->tx_head;
533 uint32_t tail = notification_buf_pair->tx_tail;
534
535 if (head == tail) {
536 return;
537 }
538
539 // Advance pointer for pkt queues that were already sent.
540 for (uint16_t i = 0; i < kBatchSize; ++i) {
541 if (head == tail) {
542 break;
543 }
544 struct TxNotification* tx_notification = tx_buf + head;
545
546 // Notification has not yet been consumed by hardware.
547 if (tx_notification->signal != 0) {
548 break;
549 }
550
551 // Requests that wrap around need two notifications but should only signal
552 // a single completion notification. Therefore, we only increment
553 // `nb_unreported_completions` in the second notification.
554 // TODO(sadok): If we implement the logic to have two notifications in the
555 // same cache line, we can get rid of `wrap_tracker` and instead check
556 // for two notifications.
557 uint8_t wrap_tracker_mask = 1 << (head & 0x7);
558 uint8_t no_wrap =
559 !(notification_buf_pair->wrap_tracker[head / 8] & wrap_tracker_mask);
560 notification_buf_pair->nb_unreported_completions += no_wrap;
561 notification_buf_pair->wrap_tracker[head / 8] &= ~wrap_tracker_mask;
562
563 head = (head + 1) % kNotificationBufSize;
564 }
565
566 notification_buf_pair->tx_head = head;
567}
568
569int send_config(struct NotificationBufPair* notification_buf_pair,
570 struct TxNotification* config_notification) {
571 struct TxNotification* tx_buf = notification_buf_pair->tx_buf;
572 uint32_t tx_tail = notification_buf_pair->tx_tail;
573 uint32_t free_slots =
574 (notification_buf_pair->tx_head - tx_tail - 1) % kNotificationBufSize;
575
576 // Make sure it's a config notification.
577 if (config_notification->signal < 2) {
578 return -1;
579 }
580
581 // Block until we can send.
582 while (unlikely(free_slots == 0)) {
583 ++notification_buf_pair->tx_full_cnt;
584 update_tx_head(notification_buf_pair);
585 free_slots =
586 (notification_buf_pair->tx_head - tx_tail - 1) % kNotificationBufSize;
587 }
588
589 struct TxNotification* tx_notification = tx_buf + tx_tail;
590 *tx_notification = *config_notification;
591
592 tx_tail = (tx_tail + 1) % kNotificationBufSize;
593 notification_buf_pair->tx_tail = tx_tail;
594 DevBackend::mmio_write32(notification_buf_pair->tx_tail_ptr, tx_tail);
595
596 // Wait for request to be consumed.
597 uint32_t nb_unreported_completions =
598 notification_buf_pair->nb_unreported_completions;
599 while (notification_buf_pair->nb_unreported_completions ==
600 nb_unreported_completions) {
601 update_tx_head(notification_buf_pair);
602 }
603 notification_buf_pair->nb_unreported_completions = nb_unreported_completions;
604
605 return 0;
606}
607
608int get_nb_fallback_queues(struct NotificationBufPair* notification_buf_pair) {
609 DevBackend* fpga_dev =
610 static_cast<DevBackend*>(notification_buf_pair->fpga_dev);
611 return fpga_dev->GetNbFallbackQueues();
612}
613
614int set_round_robin_status(struct NotificationBufPair* notification_buf_pair,
615 bool round_robin) {
616 DevBackend* fpga_dev =
617 static_cast<DevBackend*>(notification_buf_pair->fpga_dev);
618 return fpga_dev->SetRrStatus(round_robin);
619}
620
621int get_round_robin_status(struct NotificationBufPair* notification_buf_pair) {
622 DevBackend* fpga_dev =
623 static_cast<DevBackend*>(notification_buf_pair->fpga_dev);
624 return fpga_dev->GetRrStatus();
625}
626
628 struct NotificationBufPair* notification_buf_pair, void* virt_addr) {
629 DevBackend* fpga_dev =
630 static_cast<DevBackend*>(notification_buf_pair->fpga_dev);
631 uint64_t dev_addr = fpga_dev->ConvertVirtAddrToDevAddr(virt_addr);
632 return dev_addr;
633}
634
635void notification_buf_free(struct NotificationBufPair* notification_buf_pair) {
636 DevBackend* fpga_dev =
637 static_cast<DevBackend*>(notification_buf_pair->fpga_dev);
638
639 fpga_dev->FreeNotifBuf(notification_buf_pair->id);
640
641 DevBackend::mmio_write32(&notification_buf_pair->regs->rx_mem_low, 0);
642 DevBackend::mmio_write32(&notification_buf_pair->regs->rx_mem_high, 0);
643 DevBackend::mmio_write32(&notification_buf_pair->regs->tx_mem_low, 0);
644 DevBackend::mmio_write32(&notification_buf_pair->regs->tx_mem_high, 0);
645
646 munmap(notification_buf_pair->rx_buf, kAlignedDscBufPairSize);
647
648 std::string huge_page_path = notification_buf_pair->huge_page_prefix +
649 std::string(kHugePageNotifBufPathPrefix) +
650 std::to_string(notification_buf_pair->id);
651
652 unlink(huge_page_path.c_str());
653
654 free(notification_buf_pair->pending_rx_pipe_tails);
655 free(notification_buf_pair->wrap_tracker);
656 free(notification_buf_pair->next_rx_pipe_ids);
657
658 delete fpga_dev;
659}
660
661void enso_pipe_free(struct NotificationBufPair* notification_buf_pair,
662 struct RxEnsoPipeInternal* enso_pipe,
663 enso_pipe_id_t enso_pipe_id) {
664 DevBackend* fpga_dev =
665 static_cast<DevBackend*>(notification_buf_pair->fpga_dev);
666
667 DevBackend::mmio_write32(&enso_pipe->regs->rx_mem_low, 0);
668 DevBackend::mmio_write32(&enso_pipe->regs->rx_mem_high, 0);
669
670 if (enso_pipe->buf) {
671 munmap(enso_pipe->buf, kBufPageSize);
672 std::string huge_page_path = enso_pipe->huge_page_prefix +
673 std::string(kHugePageRxPipePathPrefix) +
674 std::to_string(enso_pipe_id);
675 unlink(huge_page_path.c_str());
676 enso_pipe->buf = nullptr;
677 }
678
679 fpga_dev->FreePipe(enso_pipe_id);
680
681 update_fallback_queues_config(notification_buf_pair);
682}
683
684int dma_finish(struct SocketInternal* socket_entry) {
685 struct NotificationBufPair* notification_buf_pair =
686 socket_entry->notification_buf_pair;
687
688 struct RxEnsoPipeInternal* enso_pipe = &socket_entry->enso_pipe;
689
690 enso_pipe_id_t enso_pipe_id = enso_pipe->id;
691
692 if (notification_buf_pair->ref_cnt == 0) {
693 return -1;
694 }
695
696 enso_pipe_free(notification_buf_pair, enso_pipe, enso_pipe_id);
697
698 if (notification_buf_pair->ref_cnt == 1) {
699 notification_buf_free(notification_buf_pair);
700 }
701
702 --(notification_buf_pair->ref_cnt);
703
704 return 0;
705}
706
707uint32_t get_enso_pipe_id_from_socket(struct SocketInternal* socket_entry) {
708 return (uint32_t)socket_entry->enso_pipe.id;
709}
710
711void print_stats(struct SocketInternal* socket_entry, bool print_global) {
712 struct NotificationBufPair* notification_buf_pair =
713 socket_entry->notification_buf_pair;
714
715 if (print_global) {
716 printf("TX notification buffer full counter: %lu\n\n",
717 notification_buf_pair->tx_full_cnt);
718 printf("Dsc RX head: %d\n", notification_buf_pair->rx_head);
719 printf("Dsc TX tail: %d\n", notification_buf_pair->tx_tail);
720 printf("Dsc TX head: %d\n\n", notification_buf_pair->tx_head);
721 }
722
723 printf("Pkt RX tail: %d\n", socket_entry->enso_pipe.rx_tail);
724 printf("Pkt RX head: %d\n", socket_entry->enso_pipe.rx_head);
725}
726
727} // namespace enso
Functions to configure the data plane.
int update_fallback_queues_config(struct NotificationBufPair *notification_buf_pair)
Update the device's fallback queues configuration.
Definition: config.cpp:216
Constants used throughout the codebase. Some of these constants need to be kept in sync with the hard...
constexpr uint32_t kAlignedDscBufPairSize
Definition: consts.h:103
Miscellaneous helper functions.
void * get_huge_page(const std::string &path, size_t size=0, bool mirror=false)
Definition: ixy_helpers.cpp:82
int notification_buf_init(uint32_t bdf, int32_t bar, struct NotificationBufPair *notification_buf_pair, const std::string &huge_page_prefix)
Initializes the notification buffer pair.
Definition: pcie.cpp:75
void prefetch_pipe(struct RxEnsoPipeInternal *enso_pipe)
Prefetches a given Enso Pipe.
Definition: pcie.cpp:460
void update_tx_head(struct NotificationBufPair *notification_buf_pair)
Updates the tx head and the number of TX completions.
Definition: pcie.cpp:530
uint16_t get_new_tails(struct NotificationBufPair *notification_buf_pair)
Gets latest tails for the pipes associated with the given notification buffer.
Definition: pcie.cpp:353
void print_stats(struct SocketInternal *socket_entry, bool print_global)
Prints statistics for a given socket.
Definition: pcie.cpp:711
void fully_advance_pipe(struct RxEnsoPipeInternal *enso_pipe)
Frees all the received bytes in the buffer associated with the socket_entry socket.
Definition: pcie.cpp:455
uint32_t get_next_batch(struct NotificationBufPair *notification_buf_pair, struct SocketInternal *socket_entries, int *enso_pipe_id, void **buf)
Get next batch of data from the next available Enso Pipe.
Definition: pcie.cpp:429
uint32_t get_next_batch_from_queue(struct RxEnsoPipeInternal *enso_pipe, struct NotificationBufPair *notification_buf_pair, void **buf)
Gets the next batch of data from the given Enso Pipe.
Definition: pcie.cpp:385
int set_round_robin_status(struct NotificationBufPair *notification_buf_pair, bool round_robin)
Sets the round robin status for the device.
Definition: pcie.cpp:614
int32_t get_next_enso_pipe_id(struct NotificationBufPair *notification_buf_pair)
Get next Enso Pipe with pending data.
Definition: pcie.cpp:423
uint64_t get_dev_addr_from_virt_addr(struct NotificationBufPair *notification_buf_pair, void *virt_addr)
Converts an address in the application's virtual address space to an address that can be used by the ...
Definition: pcie.cpp:627
void notification_buf_free(struct NotificationBufPair *notification_buf_pair)
Frees the notification buffer pair.
Definition: pcie.cpp:635
int send_config(struct NotificationBufPair *notification_buf_pair, struct TxNotification *config_notification)
Sends configuration to the NIC.
Definition: pcie.cpp:569
uint32_t peek_next_batch_from_queue(struct RxEnsoPipeInternal *enso_pipe, struct NotificationBufPair *notification_buf_pair, void **buf)
Gets the next batch of data from the given Enso Pipe without consuming it. So the next call to get_ne...
Definition: pcie.cpp:391
uint32_t send_to_queue(struct NotificationBufPair *notification_buf_pair, uint64_t phys_addr, uint32_t len)
Sends data through a given queue.
Definition: pcie.cpp:515
uint32_t get_unreported_completions(struct NotificationBufPair *notification_buf_pair)
Returns the number of transmission requests that were completed since the last call to this function.
Definition: pcie.cpp:520
int get_nb_fallback_queues(struct NotificationBufPair *notification_buf_pair)
Get number of fallback queues currently in use.
Definition: pcie.cpp:608
int dma_init(struct NotificationBufPair *notification_buf_pair, struct RxEnsoPipeInternal *enso_pipe, uint32_t bdf, int32_t bar, const std::string &huge_page_prefix, bool fallback)
Initializes an enso pipe and the notification buffer if needed.
Definition: pcie.cpp:285
int get_round_robin_status(struct NotificationBufPair *notification_buf_pair)
Gets the round robin status for the device.
Definition: pcie.cpp:621
void advance_pipe(struct RxEnsoPipeInternal *enso_pipe, size_t len)
Frees the next len bytes in the buffer associated with the socket_entry socket. If len is greater tha...
Definition: pcie.cpp:446
void enso_pipe_free(struct NotificationBufPair *notification_buf_pair, struct RxEnsoPipeInternal *enso_pipe, enso_pipe_id_t enso_pipe_id)
Frees the Enso Pipe.
Definition: pcie.cpp:661
int dma_finish(struct SocketInternal *socket_entry)
Frees the notification buffer and all pipes.
Definition: pcie.cpp:684
int enso_pipe_init(struct RxEnsoPipeInternal *enso_pipe, struct NotificationBufPair *notification_buf_pair, bool fallback)
Initializes an Enso Pipe.
Definition: pcie.cpp:214
uint32_t get_enso_pipe_id_from_socket(struct SocketInternal *socket_entry)
Gets the Enso Pipe ID associated with a given socket.
Definition: pcie.cpp:707
Functions to initialize and interface directly with the PCIe device.