i40e_lan.cc 20.8 KB
Newer Older
Antoine Kaufmann's avatar
Antoine Kaufmann committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
 * Copyright 2021 Max Planck Institute for Software Systems, and
 * National University of Singapore
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

25
#include <arpa/inet.h>
26
27
#include <stdlib.h>
#include <string.h>
28

29
30
31
#include <cassert>
#include <iostream>

Antoine Kaufmann's avatar
Antoine Kaufmann committed
32
#include "sims/nic/i40e_bm/headers.h"
33
34
#include "sims/nic/i40e_bm/i40e_base_wrapper.h"
#include "sims/nic/i40e_bm/i40e_bm.h"
35

36
namespace i40e {
37
38

lan::lan(i40e_bm &dev_, size_t num_qs_)
Antoine Kaufmann's avatar
Antoine Kaufmann committed
39
40
41
    : dev(dev_),
      log("lan", dev_.runner_),
      rss_kc(dev_.regs.pfqf_hkey),
42
      num_qs(num_qs_) {
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  rxqs = new lan_queue_rx *[num_qs];
  txqs = new lan_queue_tx *[num_qs];

  for (size_t i = 0; i < num_qs; i++) {
    rxqs[i] =
        new lan_queue_rx(*this, dev.regs.qrx_tail[i], i, dev.regs.qrx_ena[i],
                         dev.regs.glhmc_lanrxbase[0], dev.regs.qint_rqctl[i]);
    txqs[i] =
        new lan_queue_tx(*this, dev.regs.qtx_tail[i], i, dev.regs.qtx_ena[i],
                         dev.regs.glhmc_lantxbase[0], dev.regs.qint_tqctl[i]);
  }
}

void lan::reset() {
  rss_kc.set_dirty();
  for (size_t i = 0; i < num_qs; i++) {
    rxqs[i]->reset();
    txqs[i]->reset();
  }
}

void lan::qena_updated(uint16_t idx, bool rx) {
  uint32_t &reg = (rx ? dev.regs.qrx_ena[idx] : dev.regs.qtx_ena[idx]);
66
#ifdef DEBUG_LAN
67
68
  log << " qena updated idx=" << idx << " rx=" << rx << " reg=" << reg
      << logger::endl;
69
#endif
70
71
  lan_queue_base &q = (rx ? static_cast<lan_queue_base &>(*rxqs[idx])
                          : static_cast<lan_queue_base &>(*txqs[idx]));
72

73
74
75
76
77
  if ((reg & I40E_QRX_ENA_QENA_REQ_MASK) && !q.is_enabled()) {
    q.enable();
  } else if (!(reg & I40E_QRX_ENA_QENA_REQ_MASK) && q.is_enabled()) {
    q.disable();
  }
78
79
}

80
void lan::tail_updated(uint16_t idx, bool rx) {
81
#ifdef DEBUG_LAN
82
  log << " tail updated idx=" << idx << " rx=" << rx << logger::endl;
83
#endif
84

85
86
  lan_queue_base &q = (rx ? static_cast<lan_queue_base &>(*rxqs[idx])
                          : static_cast<lan_queue_base &>(*txqs[idx]));
87

88
89
  if (q.is_enabled())
    q.reg_updated();
90
91
}

92
93
void lan::rss_key_updated() {
  rss_kc.set_dirty();
Antoine Kaufmann's avatar
Antoine Kaufmann committed
94
95
96
}

bool lan::rss_steering(const void *data, size_t len, uint16_t &queue,
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
                       uint32_t &hash) {
  hash = 0;

  const headers::pkt_tcp *tcp =
      reinterpret_cast<const headers::pkt_tcp *>(data);
  const headers::pkt_udp *udp =
      reinterpret_cast<const headers::pkt_udp *>(data);

  // should actually determine packet type and mask with enabled packet types
  // TODO(antoinek): ipv6
  if (tcp->eth.type == htons(ETH_TYPE_IP) && tcp->ip.proto == IP_PROTO_TCP) {
    hash = rss_kc.hash_ipv4(ntohl(tcp->ip.src), ntohl(tcp->ip.dest),
                            ntohs(tcp->tcp.src), ntohs(tcp->tcp.dest));
  } else if (udp->eth.type == htons(ETH_TYPE_IP) &&
             udp->ip.proto == IP_PROTO_UDP) {
    hash = rss_kc.hash_ipv4(ntohl(udp->ip.src), ntohl(udp->ip.dest),
                            ntohs(udp->udp.src), ntohs(udp->udp.dest));
  } else if (udp->eth.type == htons(ETH_TYPE_IP)) {
    hash = rss_kc.hash_ipv4(ntohl(udp->ip.src), ntohl(udp->ip.dest), 0, 0);
  } else {
    return false;
  }

  uint16_t luts =
      (!(dev.regs.pfqf_ctl_0 & I40E_PFQF_CTL_0_HASHLUTSIZE_MASK) ? 128 : 512);
  uint16_t idx = hash % luts;
  queue = (dev.regs.pfqf_hlut[idx / 4] >> (8 * (idx % 4))) & 0x3f;
124
#ifdef DEBUG_LAN
125
  log << "  q=" << queue << " h=" << hash << " i=" << idx << logger::endl;
126
#endif
127
  return true;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
128
129
}

130
void lan::packet_received(const void *data, size_t len) {
131
#ifdef DEBUG_LAN
132
  log << " packet received len=" << len << logger::endl;
133
#endif
134

135
136
137
138
  uint32_t hash = 0;
  uint16_t queue = 0;
  rss_steering(data, len, queue, hash);
  rxqs[queue]->packet_received(data, len, hash);
139
140
}

141
lan_queue_base::lan_queue_base(lan &lanmgr_, const std::string &qtype,
142
143
144
                               uint32_t &reg_tail_, size_t idx_,
                               uint32_t &reg_ena_, uint32_t &fpm_basereg_,
                               uint32_t &reg_intqctl_, uint16_t ctx_size_)
145
146
    : queue_base(qtype + std::to_string(idx_), reg_dummy_head, reg_tail_,
                 lanmgr_.dev),
147
148
149
150
151
152
153
154
      lanmgr(lanmgr_),
      enabling(false),
      idx(idx_),
      reg_ena(reg_ena_),
      fpm_basereg(fpm_basereg_),
      reg_intqctl(reg_intqctl_),
      ctx_size(ctx_size_) {
  ctx = new uint8_t[ctx_size_];
155
156
}

157
158
159
void lan_queue_base::reset() {
  enabling = false;
  queue_base::reset();
160
161
}

162
163
164
void lan_queue_base::enable() {
  if (enabling || enabled)
    return;
165

166
#ifdef DEBUG_LAN
167
  log << " lan enabling queue " << idx << logger::endl;
168
#endif
169
  enabling = true;
170

171
  qctx_fetch *qf = new qctx_fetch(*this);
172
173
  qf->write_ = false;
  qf->dma_addr_ = ((fpm_basereg & I40E_GLHMC_LANTXBASE_FPMLANTXBASE_MASK) >>
174
175
                   I40E_GLHMC_LANTXBASE_FPMLANTXBASE_SHIFT) *
                  512;
176
177
178
  qf->dma_addr_ += ctx_size * idx;
  qf->len_ = ctx_size;
  qf->data_ = ctx;
179

180
  lanmgr.dev.hmc.issue_mem_op(*qf);
181
182
}

183
void lan_queue_base::ctx_fetched() {
184
#ifdef DEBUG_LAN
185
  log << " lan ctx fetched " << idx << logger::endl;
186
#endif
187

188
  initialize();
189

190
191
192
  enabling = false;
  enabled = true;
  reg_ena |= I40E_QRX_ENA_QENA_STAT_MASK;
193

194
  reg_updated();
195
196
}

197
void lan_queue_base::disable() {
198
#ifdef DEBUG_LAN
199
  log << " lan disabling queue " << idx << logger::endl;
200
#endif
201
202
203
  enabled = false;
  // TODO(antoinek): write back
  reg_ena &= ~I40E_QRX_ENA_QENA_STAT_MASK;
204
205
}

206
207
208
void lan_queue_base::interrupt() {
  uint32_t qctl = reg_intqctl;
  uint32_t gctl = lanmgr.dev.regs.pfint_dyn_ctl0;
209
#ifdef DEBUG_LAN
210
  log << " interrupt qctl=" << qctl << " gctl=" << gctl << logger::endl;
211
#endif
212

213
214
215
216
  uint16_t msix_idx = (qctl & I40E_QINT_TQCTL_MSIX_INDX_MASK) >>
                      I40E_QINT_TQCTL_MSIX_INDX_SHIFT;
  uint8_t msix0_idx = (qctl & I40E_QINT_TQCTL_MSIX0_INDX_MASK) >>
                      I40E_QINT_TQCTL_MSIX0_INDX_SHIFT;
217

218
219
220
  bool cause_ena = !!(qctl & I40E_QINT_TQCTL_CAUSE_ENA_MASK) &&
                   !!(gctl & I40E_PFINT_DYN_CTL0_INTENA_MASK);
  if (!cause_ena) {
221
#ifdef DEBUG_LAN
222
    log << " interrupt cause disabled" << logger::endl;
223
#endif
224
225
    return;
  }
226

227
  if (msix_idx == 0) {
228
#ifdef DEBUG_LAN
229
    log << "   setting int0.qidx=" << msix0_idx << logger::endl;
230
#endif
231
232
233
234
    lanmgr.dev.regs.pfint_icr0 |=
        I40E_PFINT_ICR0_INTEVENT_MASK |
        (1 << (I40E_PFINT_ICR0_QUEUE_0_SHIFT + msix0_idx));
  }
235

236
237
  uint8_t itr =
      (qctl & I40E_QINT_TQCTL_ITR_INDX_MASK) >> I40E_QINT_TQCTL_ITR_INDX_SHIFT;
238
  lanmgr.dev.SignalInterrupt(msix_idx, itr);
239
240
}

241
lan_queue_base::qctx_fetch::qctx_fetch(lan_queue_base &lq_) : lq(lq_) {
242
243
}

244
245
246
void lan_queue_base::qctx_fetch::done() {
  lq.ctx_fetched();
  delete this;
247
248
249
}

lan_queue_rx::lan_queue_rx(lan &lanmgr_, uint32_t &reg_tail_, size_t idx_,
250
251
                           uint32_t &reg_ena_, uint32_t &reg_fpmbase_,
                           uint32_t &reg_intqctl_)
252
    : lan_queue_base(lanmgr_, "rxq", reg_tail_, idx_, reg_ena_, reg_fpmbase_,
253
254
255
256
                     reg_intqctl_, 32) {
  // use larger value for initialization
  desc_len = 32;
  ctxs_init();
257
258
}

259
260
261
void lan_queue_rx::reset() {
  dcache.clear();
  queue_base::reset();
262
263
}

264
void lan_queue_rx::initialize() {
265
#ifdef DEBUG_LAN
266
  log << " initialize()" << logger::endl;
267
#endif
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
  uint8_t *ctx_p = reinterpret_cast<uint8_t *>(ctx);

  uint16_t *head_p = reinterpret_cast<uint16_t *>(ctx_p + 0);
  uint64_t *base_p = reinterpret_cast<uint64_t *>(ctx_p + 4);
  uint16_t *qlen_p = reinterpret_cast<uint16_t *>(ctx_p + 11);
  uint16_t *dbsz_p = reinterpret_cast<uint16_t *>(ctx_p + 12);
  uint16_t *hbsz_p = reinterpret_cast<uint16_t *>(ctx_p + 13);
  uint32_t *rxmax_p = reinterpret_cast<uint32_t *>(ctx_p + 21);

  reg_dummy_head = (*head_p) & ((1 << 13) - 1);

  base = ((*base_p) & ((1ULL << 57) - 1)) * 128;
  len = (*qlen_p >> 1) & ((1 << 13) - 1);

  dbuff_size = (((*dbsz_p) >> 6) & ((1 << 7) - 1)) * 128;
  hbuff_size = (((*hbsz_p) >> 5) & ((1 << 5) - 1)) * 64;
  uint8_t dtype = ((*hbsz_p) >> 10) & ((1 << 2) - 1);
  bool longdesc = !!(((*hbsz_p) >> 12) & 0x1);
  desc_len = (longdesc ? 32 : 16);
  crc_strip = !!(((*hbsz_p) >> 13) & 0x1);
  rxmax = (((*rxmax_p) >> 6) & ((1 << 14) - 1)) * 128;

  if (!longdesc) {
    log << "lan_queue_rx::initialize: currently only 32B descs "
           " supported"
        << logger::endl;
    abort();
  }
  if (dtype != 0) {
    log << "lan_queue_rx::initialize: no header split supported"
        << logger::endl;
    abort();
  }
301

302
#ifdef DEBUG_LAN
303
304
305
306
  log << "  head=" << reg_dummy_head << " base=" << base << " len=" << len
      << " dbsz=" << dbuff_size << " hbsz=" << hbuff_size
      << " dtype=" << (unsigned)dtype << " longdesc=" << longdesc
      << " crcstrip=" << crc_strip << " rxmax=" << rxmax << logger::endl;
307
#endif
308
309
}

310
311
queue_base::desc_ctx &lan_queue_rx::desc_ctx_create() {
  return *new rx_desc_ctx(*this);
312
313
}

314
315
316
void lan_queue_rx::packet_received(const void *data, size_t pktlen,
                                   uint32_t h) {
  size_t num_descs = (pktlen + dbuff_size - 1) / dbuff_size;
317

318
319
  if (!enabled)
    return;
320

321
  if (dcache.size() < num_descs) {
322
#ifdef DEBUG_LAN
323
324
    log << " not enough rx descs (" << num_descs << ", dropping packet"
        << logger::endl;
325
#endif
326
327
    return;
  }
328

329
330
  for (size_t i = 0; i < num_descs; i++) {
    rx_desc_ctx &ctx = *dcache.front();
331

332
#ifdef DEBUG_LAN
333
334
    log << " packet part=" << i << " received didx=" << ctx.index
        << " cnt=" << dcache.size() << logger::endl;
335
#endif
336
337
338
339
340
341
342
343
    dcache.pop_front();

    const uint8_t *buf = (const uint8_t *)data + (dbuff_size * i);
    if (i == num_descs - 1) {
      // last packet
      ctx.packet_received(buf, pktlen - dbuff_size * i, true);
    } else {
      ctx.packet_received(buf, dbuff_size, false);
344
    }
345
  }
346
347
}

348
lan_queue_rx::rx_desc_ctx::rx_desc_ctx(lan_queue_rx &queue_)
349
    : desc_ctx(queue_), rq(queue_) {
350
351
}

352
353
void lan_queue_rx::rx_desc_ctx::data_written(uint64_t addr, size_t len) {
  processed();
354
}
355

356
357
void lan_queue_rx::rx_desc_ctx::process() {
  rq.dcache.push_back(this);
358
}
359

360
361
362
363
void lan_queue_rx::rx_desc_ctx::packet_received(const void *data, size_t pktlen,
                                                bool last) {
  union i40e_32byte_rx_desc *rxd =
      reinterpret_cast<union i40e_32byte_rx_desc *>(desc);
364

365
  uint64_t addr = rxd->read.pkt_addr;
366

367
368
369
  memset(rxd, 0, sizeof(*rxd));
  rxd->wb.qword1.status_error_len |= (1 << I40E_RX_DESC_STATUS_DD_SHIFT);
  rxd->wb.qword1.status_error_len |= (pktlen << I40E_RXD_QW1_LENGTH_PBUF_SHIFT);
370

371
372
373
374
375
376
  if (last) {
    rxd->wb.qword1.status_error_len |= (1 << I40E_RX_DESC_STATUS_EOF_SHIFT);
    // TODO(antoinek): only if checksums are correct
    rxd->wb.qword1.status_error_len |= (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT);
  }
  data_write(addr, pktlen, data);
377
378
}

379
lan_queue_tx::lan_queue_tx(lan &lanmgr_, uint32_t &reg_tail_, size_t idx_,
380
381
                           uint32_t &reg_ena_, uint32_t &reg_fpmbase_,
                           uint32_t &reg_intqctl)
382
    : lan_queue_base(lanmgr_, "txq", reg_tail_, idx_, reg_ena_, reg_fpmbase_,
383
384
385
                     reg_intqctl, 128) {
  desc_len = 16;
  ctxs_init();
386
387
}

388
389
390
391
392
void lan_queue_tx::reset() {
  tso_off = 0;
  tso_len = 0;
  ready_segments.clear();
  queue_base::reset();
Antoine Kaufmann's avatar
Antoine Kaufmann committed
393
394
}

395
void lan_queue_tx::initialize() {
396
#ifdef DEBUG_LAN
397
  log << " initialize()" << logger::endl;
398
#endif
399
  uint8_t *ctx_p = reinterpret_cast<uint8_t *>(ctx);
400

401
402
403
404
  uint16_t *head_p = reinterpret_cast<uint16_t *>(ctx_p + 0);
  uint64_t *base_p = reinterpret_cast<uint64_t *>(ctx_p + 4);
  uint16_t *hwb_qlen_p = reinterpret_cast<uint16_t *>(ctx_p + 20);
  uint64_t *hwb_addr_p = reinterpret_cast<uint64_t *>(ctx_p + 24);
405

406
  reg_dummy_head = (*head_p) & ((1 << 13) - 1);
407

408
409
  base = ((*base_p) & ((1ULL << 57) - 1)) * 128;
  len = ((*hwb_qlen_p) >> 1) & ((1 << 13) - 1);
410

411
412
  hwb = !!(*hwb_qlen_p & (1 << 0));
  hwb_addr = *hwb_addr_p;
413

414
#ifdef DEBUG_LAN
415
416
  log << "  head=" << reg_dummy_head << " base=" << base << " len=" << len
      << " hwb=" << hwb << " hwb_addr=" << hwb_addr << logger::endl;
417
#endif
418
}
419

420
421
queue_base::desc_ctx &lan_queue_tx::desc_ctx_create() {
  return *new tx_desc_ctx(*this);
422
}
423

424
void lan_queue_tx::do_writeback(uint32_t first_idx, uint32_t first_pos,
425
426
427
428
429
430
431
                                uint32_t cnt) {
  if (!hwb) {
    // if head index writeback is disabled we need to write descriptor back
    lan_queue_base::do_writeback(first_idx, first_pos, cnt);
  } else {
    // else we just need to write the index back
    dma_hwb *dma = new dma_hwb(*this, first_pos, cnt, (first_idx + cnt) % len);
432
    dma->dma_addr_ = hwb_addr;
433

434
#ifdef DEBUG_LAN
435
    log << " hwb=" << *((uint32_t *)dma->data_) << logger::endl;
436
#endif
437
    dev.runner_->IssueDma(*dma);
438
  }
439
440
}

441
442
443
444
445
446
447
448
449
bool lan_queue_tx::trigger_tx_packet() {
  size_t n = ready_segments.size();
  size_t d_skip = 0, dcnt;
  bool eop = false;
  uint64_t d1;
  uint32_t iipt, l4t, pkt_len, total_len = 0, data_limit;
  bool tso = false;
  uint32_t tso_mss = 0, tso_paylen = 0;
  uint16_t maclen = 0, iplen = 0, l4len = 0;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
450

451
452
453
  // abort if no queued up descriptors
  if (n == 0)
    return false;
454

Antoine Kaufmann's avatar
Antoine Kaufmann committed
455
#ifdef DEBUG_LAN
456
457
458
  log << "trigger_tx_packet(n=" << n
      << ", firstidx=" << ready_segments.at(0)->index << ")" << logger::endl;
  log << "  tso_off=" << tso_off << " tso_len=" << tso_len << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
459
460
#endif

461
462
463
464
465
466
467
468
  // check if we have a context descriptor first
  tx_desc_ctx *rd = ready_segments.at(0);
  uint8_t dtype = (rd->d->cmd_type_offset_bsz & I40E_TXD_QW1_DTYPE_MASK) >>
                  I40E_TXD_QW1_DTYPE_SHIFT;
  if (dtype == I40E_TX_DESC_DTYPE_CONTEXT) {
    struct i40e_tx_context_desc *ctxd =
        reinterpret_cast<struct i40e_tx_context_desc *>(rd->d);
    d1 = ctxd->type_cmd_tso_mss;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
469

470
471
472
473
    uint16_t cmd =
        ((d1 & I40E_TXD_CTX_QW1_CMD_MASK) >> I40E_TXD_CTX_QW1_CMD_SHIFT);
    tso = !!(cmd & I40E_TX_CTX_DESC_TSO);
    tso_mss = (d1 & I40E_TXD_CTX_QW1_MSS_MASK) >> I40E_TXD_CTX_QW1_MSS_SHIFT;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
474
475

#ifdef DEBUG_LAN
476
    log << "  tso=" << tso << " mss=" << tso_mss << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
477
478
#endif

479
480
    d_skip = 1;
  }
481

482
483
484
485
  // find EOP descriptor
  for (dcnt = d_skip; dcnt < n && !eop; dcnt++) {
    tx_desc_ctx *rd = ready_segments.at(dcnt);
    d1 = rd->d->cmd_type_offset_bsz;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
486

487
#ifdef DEBUG_LAN
488
    log << " data fetched didx=" << rd->index << " d1=" << d1 << logger::endl;
489
#endif
490

491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
    dtype = (d1 & I40E_TXD_QW1_DTYPE_MASK) >> I40E_TXD_QW1_DTYPE_SHIFT;
    if (dtype != I40E_TX_DESC_DTYPE_DATA) {
      log << "trigger tx desc is not a data descriptor idx=" << rd->index
          << " d1=" << d1 << logger::endl;
      abort();
    }

    uint16_t cmd = (d1 & I40E_TXD_QW1_CMD_MASK) >> I40E_TXD_QW1_CMD_SHIFT;
    eop = (cmd & I40E_TX_DESC_CMD_EOP);
    iipt = cmd & (I40E_TX_DESC_CMD_IIPT_MASK);
    l4t = (cmd & I40E_TX_DESC_CMD_L4T_EOFT_MASK);

    if (eop) {
      uint32_t off =
          (d1 & I40E_TXD_QW1_OFFSET_MASK) >> I40E_TXD_QW1_OFFSET_SHIFT;
      maclen = ((off & I40E_TXD_QW1_MACLEN_MASK) >>
                I40E_TX_DESC_LENGTH_MACLEN_SHIFT) *
               2;
      iplen =
          ((off & I40E_TXD_QW1_IPLEN_MASK) >> I40E_TX_DESC_LENGTH_IPLEN_SHIFT) *
          4;
      l4len = ((off & I40E_TXD_QW1_L4LEN_MASK) >>
               I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT) *
              4;
    }

    pkt_len =
        (d1 & I40E_TXD_QW1_TX_BUF_SZ_MASK) >> I40E_TXD_QW1_TX_BUF_SZ_SHIFT;
    total_len += pkt_len;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
520
521

#ifdef DEBUG_LAN
522
    log << "    eop=" << eop << " len=" << pkt_len << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
523
#endif
524
  }
525

526
527
528
  // Unit not completely fetched yet
  if (!eop)
    return false;
529

530
531
532
533
534
  if (tso) {
    if (tso_off == 0)
      data_limit = maclen + iplen + l4len + tso_mss;
    else
      data_limit = tso_off + tso_mss;
535

536
537
538
539
540
541
542
543
    if (data_limit > total_len) {
      data_limit = total_len;
    }
  } else {
    if (total_len > MTU) {
      log << "    packet is longer (" << total_len << ") than MTU (" << MTU
          << ")" << logger::endl;
      abort();
544
    }
545
546
    data_limit = total_len;
  }
Antoine Kaufmann's avatar
Antoine Kaufmann committed
547

548
#ifdef DEBUG_LAN
549
550
551
  log << "    iipt=" << iipt << " l4t=" << l4t << " maclen=" << maclen
      << " iplen=" << iplen << " l4len=" << l4len << " total_len=" << total_len
      << " data_limit=" << data_limit << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
552

553
#else
554
  (void)iipt;
555
#endif
556

557
558
559
560
561
562
563
  // copy data for this segment
  uint32_t off = 0;
  for (dcnt = d_skip; dcnt < n && off < data_limit; dcnt++) {
    tx_desc_ctx *rd = ready_segments.at(dcnt);
    d1 = rd->d->cmd_type_offset_bsz;
    uint16_t pkt_len =
        (d1 & I40E_TXD_QW1_TX_BUF_SZ_MASK) >> I40E_TXD_QW1_TX_BUF_SZ_SHIFT;
564

565
566
567
568
569
    if (off <= tso_off && off + pkt_len > tso_off) {
      uint32_t start = tso_off;
      uint32_t end = off + pkt_len;
      if (end > data_limit)
        end = data_limit;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
570
571

#ifdef DEBUG_LAN
572
573
574
      log << "    copying data from off=" << off << " idx=" << rd->index
          << " start=" << start << " end=" << end << " tso_len=" << tso_len
          << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
575
576
#endif

577
578
579
580
      memcpy(pktbuf + tso_len, (uint8_t *)rd->data + (start - off),
             end - start);
      tso_off = end;
      tso_len += end - start;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
581
582
    }

583
584
585
586
    off += pkt_len;
  }

  assert(tso_len <= MTU);
Antoine Kaufmann's avatar
Antoine Kaufmann committed
587

588
  if (!tso) {
Antoine Kaufmann's avatar
Antoine Kaufmann committed
589
#ifdef DEBUG_LAN
590
    log << "    normal non-tso packet" << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
591
592
#endif

593
594
595
    if (l4t == I40E_TX_DESC_CMD_L4T_EOFT_TCP) {
      uint16_t tcp_off = maclen + iplen;
      xsum_tcp(pktbuf + tcp_off, tso_len - tcp_off);
596
597
598
    } else if (l4t == I40E_TX_DESC_CMD_L4T_EOFT_UDP) {
      uint16_t udp_off = maclen + iplen;
      xsum_udp(pktbuf + udp_off, tso_len - udp_off);
599
    }
Antoine Kaufmann's avatar
Antoine Kaufmann committed
600

601
    dev.runner_->EthSend(pktbuf, tso_len);
602
  } else {
Antoine Kaufmann's avatar
Antoine Kaufmann committed
603
#ifdef DEBUG_LAN
604
605
    log << "    tso packet off=" << tso_off << " len=" << tso_len
        << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
606
607
#endif

608
609
    // TSO gets hairier
    uint16_t hdrlen = maclen + iplen + l4len;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
610

611
612
613
614
    // calculate payload size
    tso_paylen = tso_len - hdrlen;
    if (tso_paylen > tso_mss)
      tso_paylen = tso_mss;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
615

616
    xsum_tcpip_tso(pktbuf + maclen, iplen, l4len, tso_paylen);
Antoine Kaufmann's avatar
Antoine Kaufmann committed
617

618
    dev.runner_->EthSend(pktbuf, tso_len);
Antoine Kaufmann's avatar
Antoine Kaufmann committed
619

620
    tso_postupdate_header(pktbuf + maclen, iplen, l4len, tso_paylen);
Antoine Kaufmann's avatar
Antoine Kaufmann committed
621

622
623
624
625
    // not done yet with this TSO unit
    if (tso && tso_off < total_len) {
      tso_len = hdrlen;
      return true;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
626
    }
627
  }
Antoine Kaufmann's avatar
Antoine Kaufmann committed
628
629

#ifdef DEBUG_LAN
630
  log << "    unit done" << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
631
#endif
632
633
634
635
  while (dcnt-- > 0) {
    ready_segments.front()->processed();
    ready_segments.pop_front();
  }
636

637
638
  tso_len = 0;
  tso_off = 0;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
639

640
  return true;
641
642
}

643
644
645
void lan_queue_tx::trigger_tx() {
  while (trigger_tx_packet()) {
  }
646
647
648
}

lan_queue_tx::tx_desc_ctx::tx_desc_ctx(lan_queue_tx &queue_)
649
650
    : desc_ctx(queue_), tq(queue_) {
  d = reinterpret_cast<struct i40e_tx_desc *>(desc);
651
652
}

653
654
void lan_queue_tx::tx_desc_ctx::prepare() {
  uint64_t d1 = d->cmd_type_offset_bsz;
655

656
#ifdef DEBUG_LAN
657
  queue.log << " desc fetched didx=" << index << " d1=" << d1 << logger::endl;
658
#endif
659

660
661
662
663
  uint8_t dtype = (d1 & I40E_TXD_QW1_DTYPE_MASK) >> I40E_TXD_QW1_DTYPE_SHIFT;
  if (dtype == I40E_TX_DESC_DTYPE_DATA) {
    uint16_t len =
        (d1 & I40E_TXD_QW1_TX_BUF_SZ_MASK) >> I40E_TXD_QW1_TX_BUF_SZ_SHIFT;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
664

665
#ifdef DEBUG_LAN
666
667
    queue.log << "  bufaddr=" << d->buffer_addr << " len=" << len
              << logger::endl;
668
#endif
Antoine Kaufmann's avatar
Antoine Kaufmann committed
669

670
671
    data_fetch(d->buffer_addr, len);
  } else if (dtype == I40E_TX_DESC_DTYPE_CONTEXT) {
Antoine Kaufmann's avatar
Antoine Kaufmann committed
672
#ifdef DEBUG_LAN
673
674
675
676
677
    struct i40e_tx_context_desc *ctxd =
        reinterpret_cast<struct i40e_tx_context_desc *>(d);
    queue.log << "  context descriptor: tp=" << ctxd->tunneling_params
              << " l2t=" << ctxd->l2tag2 << " tctm=" << ctxd->type_cmd_tso_mss
              << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
678
#endif
Antoine Kaufmann's avatar
Antoine Kaufmann committed
679

680
681
682
683
684
    prepared();
  } else {
    queue.log << "txq: only support context & data descriptors" << logger::endl;
    abort();
  }
685
686
}

687
688
689
void lan_queue_tx::tx_desc_ctx::process() {
  tq.ready_segments.push_back(this);
  tq.trigger_tx();
690
691
}

692
693
694
695
void lan_queue_tx::tx_desc_ctx::processed() {
  d->cmd_type_offset_bsz = I40E_TX_DESC_DTYPE_DESC_DONE
                           << I40E_TXD_QW1_DTYPE_SHIFT;
  desc_ctx::processed();
696
697
}

698
lan_queue_tx::dma_hwb::dma_hwb(lan_queue_tx &queue_, uint32_t pos_,
699
700
                               uint32_t cnt_, uint32_t nh_)
    : queue(queue_), pos(pos_), cnt(cnt_), next_head(nh_) {
701
702
703
  data_ = &next_head;
  len_ = 4;
  write_ = true;
704
705
}

706
lan_queue_tx::dma_hwb::~dma_hwb() {
707
708
}

709
void lan_queue_tx::dma_hwb::done() {
710
#ifdef DEBUG_LAN
711
  queue.log << " tx head written back" << logger::endl;
712
#endif
713
714
715
  queue.writeback_done(pos, cnt);
  queue.trigger();
  delete this;
716
}
717
}  // namespace i40e