i40e_lan.cc 24.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
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/* determine if we should sample a ptp rx timestamp for this packet */
bool lan_queue_rx::ptp_rx_sample(const void *data, size_t len) {
  if (!(dev.regs.prtsyn_ctl_1 & I40E_PRTTSYN_CTL1_TSYNENA_MASK))
    return false;

  uint8_t tsyntype = (dev.regs.prtsyn_ctl_1 & I40E_PRTTSYN_CTL1_TSYNTYPE_MASK)
      >> I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT;
  uint8_t udp_ena = (dev.regs.prtsyn_ctl_1 & I40E_PRTTSYN_CTL1_UDP_ENA_MASK)
      >> I40E_PRTTSYN_CTL1_UDP_ENA_SHIFT;
  uint8_t v1messtype0 = (dev.regs.prtsyn_ctl_1 &
        I40E_PRTTSYN_CTL1_V1MESSTYPE0_MASK)
      >> I40E_PRTTSYN_CTL1_V1MESSTYPE0_SHIFT;
  uint8_t v1messtype1 = (dev.regs.prtsyn_ctl_1 &
        I40E_PRTTSYN_CTL1_V1MESSTYPE1_MASK)
      >> I40E_PRTTSYN_CTL1_V1MESSTYPE1_SHIFT;
  uint8_t v2messtype0 = (dev.regs.prtsyn_ctl_1 &
        I40E_PRTTSYN_CTL1_V2MESSTYPE0_MASK)
      >> I40E_PRTTSYN_CTL1_V2MESSTYPE0_SHIFT;
  uint8_t v2messtype1 = (dev.regs.prtsyn_ctl_1 &
        I40E_PRTTSYN_CTL1_V2MESSTYPE1_MASK)
      >> I40E_PRTTSYN_CTL1_V2MESSTYPE1_SHIFT;

  const headers::pkt_udp *udp =
      reinterpret_cast<const headers::pkt_udp *>(data);
  const void *ptp_start;
  bool is_udp = false;

  // TODO(antoinek): ipv6
  if (udp->eth.type == htons(ETH_TYPE_IP) && udp->ip.proto == IP_PROTO_UDP) {
    // no udp packet types enabled
    if (tsyntype == 0)
      return false;

    uint16_t port = ntohs(udp->udp.dest);
    if (!(port == 0x013F && (udp_ena == 1 || udp_ena == 3)) &&
        !(port  == 0x0140 && (udp_ena == 2 || udp_ena == 3)))
      return false;

    is_udp = true;
    ptp_start = udp + 1;
  } else if (udp->eth.type == htons(ETH_TYPE_PTP)) {
    if (tsyntype == 1)
      return false;

    ptp_start = &udp->ip;
  } else {
    return false;
  }

  const headers::ptp_v1_hdr *v1 =
      reinterpret_cast<const headers::ptp_v1_hdr *>(ptp_start);
  const headers::ptp_v2_hdr *v2 =
      reinterpret_cast<const headers::ptp_v2_hdr *>(ptp_start);

  /* handle ptp v1 */
  if (v1->version_ptp == 1) {
    /* check if v1 is allowed*/
    if (tsyntype != 1)
      return false;

    if (v1messtype0 == 0xff || v1messtype1 == 0xff)
      return true;
    if (v1->msg_type == v1messtype0 || v1->msg_type == v1messtype1)
      return true;

    return false;
  } else if (v2->version_ptp == 2) {
    /* check if no v1 packets allowed*/
    if (tsyntype == 1)
      return false;
    if (tsyntype == 0 && is_udp)
      return false;
    if (tsyntype == 3)
      return v2->msg_type < 8;

    if (v2messtype0 == 0xf)
      return true;

    if (v2->msg_type == v2messtype0 || v2->msg_type == v2messtype1)
      return true;
    return false;
  }

  return false;
}

394
395
396
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;
397

398
399
  if (!enabled)
    return;
400

401
  if (dcache.size() < num_descs) {
402
#ifdef DEBUG_LAN
403
404
    log << " not enough rx descs (" << num_descs << ", dropping packet"
        << logger::endl;
405
#endif
406
407
    return;
  }
408

409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
  // sample ptp rx timestamp if enabled, packet matches conditions, and a free
  // rx timestamp register can be found.
  int rxtime_id = -1;
  if (ptp_rx_sample(data, pktlen)) {
    for (int i = 0; i < 4; i++) {
      if (!dev.regs.prtsyn_rxtime_lock[i]) {
        dev.regs.prtsyn_rxtime[i] = dev.ptp.phc_read();
        dev.regs.prtsyn_rxtime_lock[i] = true;
        dev.regs.prtsyn_stat_1 |= 1 << (I40E_PRTTSYN_STAT_1_RXT0_SHIFT + i);
        rxtime_id = i;
        break;
      }
    }
  }

424
425
  for (size_t i = 0; i < num_descs; i++) {
    rx_desc_ctx &ctx = *dcache.front();
426

427
#ifdef DEBUG_LAN
428
429
    log << " packet part=" << i << " received didx=" << ctx.index
        << " cnt=" << dcache.size() << logger::endl;
430
#endif
431
432
433
434
435
    dcache.pop_front();

    const uint8_t *buf = (const uint8_t *)data + (dbuff_size * i);
    if (i == num_descs - 1) {
      // last packet
436
      ctx.packet_received(buf, pktlen - dbuff_size * i, true, rxtime_id);
437
    } else {
438
      ctx.packet_received(buf, dbuff_size, false, -1);
439
    }
440
  }
441
442
}

443
lan_queue_rx::rx_desc_ctx::rx_desc_ctx(lan_queue_rx &queue_)
444
    : desc_ctx(queue_), rq(queue_) {
445
446
}

447
448
void lan_queue_rx::rx_desc_ctx::data_written(uint64_t addr, size_t len) {
  processed();
449
}
450

451
452
void lan_queue_rx::rx_desc_ctx::process() {
  rq.dcache.push_back(this);
453
}
454

455
void lan_queue_rx::rx_desc_ctx::packet_received(const void *data, size_t pktlen,
456
                                                bool last, int rxtime_id) {
457
458
459
  // 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);
460

461
  uint64_t addr = rxd->read.pkt_addr;
462

463
  memset(rxd, 0, sizeof(*rxd));
464
465
466
467
  // 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));

468
469
  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);
470

471
472
473
474
  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);
475
476
477
478
479
480
481
482
483

    // if an rx timestamp was assigned, need to report ts register id in
    // descriptor
    if (rxtime_id >= 0) {
      rxd->wb.qword1.status_error_len |=
        ((uint32_t) rxtime_id) << I40E_RX_DESC_STATUS_TSYNINDX_SHIFT;
      rxd->wb.qword1.status_error_len |=
        (1 << I40E_RX_DESC_STATUS_TSYNVALID_SHIFT);
    }
484
485
  }
  data_write(addr, pktlen, data);
486
487
}

488
lan_queue_tx::lan_queue_tx(lan &lanmgr_, uint32_t &reg_tail_, size_t idx_,
489
490
                           uint32_t &reg_ena_, uint32_t &reg_fpmbase_,
                           uint32_t &reg_intqctl)
491
    : lan_queue_base(lanmgr_, "txq", reg_tail_, idx_, reg_ena_, reg_fpmbase_,
492
493
494
                     reg_intqctl, 128) {
  desc_len = 16;
  ctxs_init();
495
496
}

497
498
499
500
501
void lan_queue_tx::reset() {
  tso_off = 0;
  tso_len = 0;
  ready_segments.clear();
  queue_base::reset();
Antoine Kaufmann's avatar
Antoine Kaufmann committed
502
503
}

504
void lan_queue_tx::initialize() {
505
#ifdef DEBUG_LAN
506
  log << " initialize()" << logger::endl;
507
#endif
508
  uint8_t *ctx_p = reinterpret_cast<uint8_t *>(ctx);
509

510
511
512
513
  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);
514

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

517
518
  base = ((*base_p) & ((1ULL << 57) - 1)) * 128;
  len = ((*hwb_qlen_p) >> 1) & ((1 << 13) - 1);
519

520
521
  hwb = !!(*hwb_qlen_p & (1 << 0));
  hwb_addr = *hwb_addr_p;
522

523
#ifdef DEBUG_LAN
524
525
  log << "  head=" << reg_dummy_head << " base=" << base << " len=" << len
      << " hwb=" << hwb << " hwb_addr=" << hwb_addr << logger::endl;
526
#endif
527
}
528

529
530
queue_base::desc_ctx &lan_queue_tx::desc_ctx_create() {
  return *new tx_desc_ctx(*this);
531
}
532

533
void lan_queue_tx::do_writeback(uint32_t first_idx, uint32_t first_pos,
534
535
536
537
538
539
540
                                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);
541
    dma->dma_addr_ = hwb_addr;
542

543
#ifdef DEBUG_LAN
544
    log << " hwb=" << *((uint32_t *)dma->data_) << logger::endl;
545
#endif
546
    dev.runner_->IssueDma(*dma);
547
  }
548
549
}

550
551
552
553
554
555
556
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;
557
  bool tsyn = false;
558
559
  uint32_t tso_mss = 0, tso_paylen = 0;
  uint16_t maclen = 0, iplen = 0, l4len = 0;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
560

561
562
563
  // abort if no queued up descriptors
  if (n == 0)
    return false;
564

Antoine Kaufmann's avatar
Antoine Kaufmann committed
565
#ifdef DEBUG_LAN
566
567
568
  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
569
570
#endif

571
572
573
574
575
576
577
578
  // 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
579

580
581
582
583
    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;
584
    tsyn = !!(cmd & I40E_TX_CTX_DESC_TSYN);
Antoine Kaufmann's avatar
Antoine Kaufmann committed
585
586

#ifdef DEBUG_LAN
587
    log << "  tso=" << tso << " mss=" << tso_mss << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
588
589
#endif

590
591
    d_skip = 1;
  }
592

593
594
595
596
  // 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
597

598
#ifdef DEBUG_LAN
599
    log << " data fetched didx=" << rd->index << " d1=" << d1 << logger::endl;
600
#endif
601

602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
    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
631
632

#ifdef DEBUG_LAN
633
    log << "    eop=" << eop << " len=" << pkt_len << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
634
#endif
635
  }
636

637
638
639
  // Unit not completely fetched yet
  if (!eop)
    return false;
640

641
642
643
644
645
  if (tso) {
    if (tso_off == 0)
      data_limit = maclen + iplen + l4len + tso_mss;
    else
      data_limit = tso_off + tso_mss;
646

647
648
649
650
651
652
653
654
    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();
655
    }
656
657
    data_limit = total_len;
  }
Antoine Kaufmann's avatar
Antoine Kaufmann committed
658

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

664
#else
665
  (void)iipt;
666
#endif
667

668
669
670
671
672
673
674
  // 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;
675

676
677
678
679
680
    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
681
682

#ifdef DEBUG_LAN
683
684
685
      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
686
687
#endif

688
689
690
691
      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
692
693
    }

694
695
696
697
    off += pkt_len;
  }

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

699
  if (!tso) {
Antoine Kaufmann's avatar
Antoine Kaufmann committed
700
#ifdef DEBUG_LAN
701
    log << "    normal non-tso packet" << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
702
703
#endif

704
705
706
    if (l4t == I40E_TX_DESC_CMD_L4T_EOFT_TCP) {
      uint16_t tcp_off = maclen + iplen;
      xsum_tcp(pktbuf + tcp_off, tso_len - tcp_off);
707
708
709
    } 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);
710
    }
Antoine Kaufmann's avatar
Antoine Kaufmann committed
711

712
    dev.runner_->EthSend(pktbuf, tso_len);
713
  } else {
Antoine Kaufmann's avatar
Antoine Kaufmann committed
714
#ifdef DEBUG_LAN
715
716
    log << "    tso packet off=" << tso_off << " len=" << tso_len
        << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
717
718
#endif

719
720
    // TSO gets hairier
    uint16_t hdrlen = maclen + iplen + l4len;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
721

722
723
724
725
    // calculate payload size
    tso_paylen = tso_len - hdrlen;
    if (tso_paylen > tso_mss)
      tso_paylen = tso_mss;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
726

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

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

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

733
734
735
736
    // 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
737
    }
738
  }
Antoine Kaufmann's avatar
Antoine Kaufmann committed
739

740
741
742
743
744
745
746
747
748
749
750
751
752
  // PTP transmit timestamping
  if (tsyn) {
    dev.regs.prtsyn_txtime = dev.ptp.phc_read();

    lanmgr.dev.regs.prtsyn_stat_0 |= I40E_PRTTSYN_STAT_0_TXTIME_MASK;
    if ((dev.regs.prtsyn_ctl_0 & I40E_PRTTSYN_CTL0_TXTIME_INT_ENA_MASK) &&
        (lanmgr.dev.regs.pfint_icr0_ena & I40E_PFINT_ICR0_ENA_TIMESYNC_MASK)) {
      lanmgr.dev.regs.pfint_icr0 |=
        I40E_PFINT_ICR0_INTEVENT_MASK | I40E_PFINT_ICR0_TIMESYNC_MASK;
      lanmgr.dev.SignalInterrupt(0, 0);
    }
  }

Antoine Kaufmann's avatar
Antoine Kaufmann committed
753
#ifdef DEBUG_LAN
754
  log << "    unit done" << logger::endl;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
755
#endif
756
757
758
759
  while (dcnt-- > 0) {
    ready_segments.front()->processed();
    ready_segments.pop_front();
  }
760

761
762
  tso_len = 0;
  tso_off = 0;
Antoine Kaufmann's avatar
Antoine Kaufmann committed
763

764
  return true;
765
766
}

767
768
769
void lan_queue_tx::trigger_tx() {
  while (trigger_tx_packet()) {
  }
770
771
772
}

lan_queue_tx::tx_desc_ctx::tx_desc_ctx(lan_queue_tx &queue_)
773
774
    : desc_ctx(queue_), tq(queue_) {
  d = reinterpret_cast<struct i40e_tx_desc *>(desc);
775
776
}

777
778
void lan_queue_tx::tx_desc_ctx::prepare() {
  uint64_t d1 = d->cmd_type_offset_bsz;
779

780
#ifdef DEBUG_LAN
781
  queue.log << " desc fetched didx=" << index << " d1=" << d1 << logger::endl;
782
#endif
783

784
785
786
787
  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
788

789
#ifdef DEBUG_LAN
790
791
    queue.log << "  bufaddr=" << d->buffer_addr << " len=" << len
              << logger::endl;
792
#endif
Antoine Kaufmann's avatar
Antoine Kaufmann committed
793

794
795
    data_fetch(d->buffer_addr, len);
  } else if (dtype == I40E_TX_DESC_DTYPE_CONTEXT) {
Antoine Kaufmann's avatar
Antoine Kaufmann committed
796
#ifdef DEBUG_LAN
797
798
799
800
801
    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
802
#endif
Antoine Kaufmann's avatar
Antoine Kaufmann committed
803

804
805
806
807
808
    prepared();
  } else {
    queue.log << "txq: only support context & data descriptors" << logger::endl;
    abort();
  }
809
810
}

811
812
813
void lan_queue_tx::tx_desc_ctx::process() {
  tq.ready_segments.push_back(this);
  tq.trigger_tx();
814
815
}

816
817
818
819
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();
820
821
}

822
lan_queue_tx::dma_hwb::dma_hwb(lan_queue_tx &queue_, uint32_t pos_,
823
824
                               uint32_t cnt_, uint32_t nh_)
    : queue(queue_), pos(pos_), cnt(cnt_), next_head(nh_) {
825
826
827
  data_ = &next_head;
  len_ = 4;
  write_ = true;
828
829
}

830
lan_queue_tx::dma_hwb::~dma_hwb() {
831
832
}

833
void lan_queue_tx::dma_hwb::done() {
834
#ifdef DEBUG_LAN
835
  queue.log << " tx head written back" << logger::endl;
836
#endif
837
838
839
  queue.writeback_done(pos, cnt);
  queue.trigger();
  delete this;
840
}
841
}  // namespace i40e