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
    : dev(dev_),
40
      log("lan", dev_),
Antoine Kaufmann's avatar
Antoine Kaufmann committed
41
      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
  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 (dtype != 0) {
    log << "lan_queue_rx::initialize: no header split supported"
        << logger::endl;
    abort();
  }
295

296
#ifdef DEBUG_LAN
297
298
299
300
  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;
301
#endif
302
303
}

304
305
queue_base::desc_ctx &lan_queue_rx::desc_ctx_create() {
  return *new rx_desc_ctx(*this);
306
307
}

308
309
310
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;
311

312
313
  if (!enabled)
    return;
314

315
  if (dcache.size() < num_descs) {
316
#ifdef DEBUG_LAN
317
318
    log << " not enough rx descs (" << num_descs << ", dropping packet"
        << logger::endl;
319
#endif
320
321
    return;
  }
322

323
324
  for (size_t i = 0; i < num_descs; i++) {
    rx_desc_ctx &ctx = *dcache.front();
325

326
#ifdef DEBUG_LAN
327
328
    log << " packet part=" << i << " received didx=" << ctx.index
        << " cnt=" << dcache.size() << logger::endl;
329
#endif
330
331
332
333
334
335
336
337
    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);
338
    }
339
  }
340
341
}

342
lan_queue_rx::rx_desc_ctx::rx_desc_ctx(lan_queue_rx &queue_)
343
    : desc_ctx(queue_), rq(queue_) {
344
345
}

346
347
void lan_queue_rx::rx_desc_ctx::data_written(uint64_t addr, size_t len) {
  processed();
348
}
349

350
351
void lan_queue_rx::rx_desc_ctx::process() {
  rq.dcache.push_back(this);
352
}
353

354
355
void lan_queue_rx::rx_desc_ctx::packet_received(const void *data, size_t pktlen,
                                                bool last) {
356
357
358
  // we only use fields in the lowest 16b anyways, even if set to 32b
  union i40e_16byte_rx_desc *rxd =
      reinterpret_cast<union i40e_16byte_rx_desc *>(desc);
359

360
  uint64_t addr = rxd->read.pkt_addr;
361

362
  memset(rxd, 0, sizeof(*rxd));
363
364
365
366
  // for 32b descriptors need to ensure to zero later fields
  if (desc_len > sizeof(*rxd))
    memset((uint8_t *)desc + sizeof(*rxd), 0, desc_len - sizeof(*rxd));

367
368
  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);
369

370
371
372
373
374
375
  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);
376
377
}

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

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

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

400
401
402
403
  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);
404

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

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

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

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

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

423
void lan_queue_tx::do_writeback(uint32_t first_idx, uint32_t first_pos,
424
425
426
427
428
429
430
                                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);
431
    dma->dma_addr_ = hwb_addr;
432

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

440
441
442
443
444
445
446
447
448
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
449

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

Antoine Kaufmann's avatar
Antoine Kaufmann committed
454
#ifdef DEBUG_LAN
455
456
457
  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
458
459
#endif

460
461
462
463
464
465
466
467
  // 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
468

469
470
471
472
    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
473
474

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

478
479
    d_skip = 1;
  }
480

481
482
483
484
  // 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
485

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

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
    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
519
520

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

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

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

535
536
537
538
539
540
541
542
    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();
543
    }
544
545
    data_limit = total_len;
  }
Antoine Kaufmann's avatar
Antoine Kaufmann committed
546

547
#ifdef DEBUG_LAN
548
549
550
  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
551

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

556
557
558
559
560
561
562
  // 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;
563

564
565
566
567
568
    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
569
570

#ifdef DEBUG_LAN
571
572
573
      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
574
575
#endif

576
577
578
579
      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
580
581
    }

582
583
584
585
    off += pkt_len;
  }

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

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

592
593
594
    if (l4t == I40E_TX_DESC_CMD_L4T_EOFT_TCP) {
      uint16_t tcp_off = maclen + iplen;
      xsum_tcp(pktbuf + tcp_off, tso_len - tcp_off);
595
596
597
    } 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);
598
    }
Antoine Kaufmann's avatar
Antoine Kaufmann committed
599

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

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

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

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

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

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

621
622
623
624
    // 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
625
    }
626
  }
Antoine Kaufmann's avatar
Antoine Kaufmann committed
627
628

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

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

639
  return true;
640
641
}

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

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

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

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

659
660
661
662
  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
663

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

669
670
    data_fetch(d->buffer_addr, len);
  } else if (dtype == I40E_TX_DESC_DTYPE_CONTEXT) {
Antoine Kaufmann's avatar
Antoine Kaufmann committed
671
#ifdef DEBUG_LAN
672
673
674
675
676
    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
677
#endif
Antoine Kaufmann's avatar
Antoine Kaufmann committed
678

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

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

691
692
693
694
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();
695
696
}

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

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

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