Skip to content

Commit f036c34

Browse files
committed
ethport: match GTP-U with RAW item on i40e
1 parent ca0be44 commit f036c34

File tree

9 files changed

+128
-52
lines changed

9 files changed

+128
-52
lines changed

csrc/dpdk/ethdev.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ typedef enum EthFlowFlags {
2929
*/
3030
EthFlowFlagsVxRaw = RTE_BIT32(8),
3131

32-
/**
33-
* @brief How to generate GTP-U flow item.
34-
* 0 = prefer @c RTE_FLOW_ITEM_TYPE_GTPU .
35-
* 1 = prefer @c RTE_FLOW_ITEM_TYPE_GTP .
36-
*/
37-
EthFlowFlagsGtp = RTE_BIT32(12),
32+
/** @brief Bitmask to indicate how to generate GTP-U flow items. */
33+
EthFlowFlagsGtpMask = RTE_GENMASK32(13, 12),
34+
35+
/** @brief Generate GTP-U flow item as @c RTE_FLOW_ITEM_TYPE_GTPU . */
36+
EthFlowFlagsGtpGtpu = RTE_SHIFT_VAL32(0, 12),
37+
/** @brief Generate GTP-U flow item as @c RTE_FLOW_ITEM_TYPE_GTP . */
38+
EthFlowFlagsGtpGtp = RTE_SHIFT_VAL32(1, 12),
39+
/** @brief Generate GTP-U flow item as @c RTE_FLOW_ITEM_TYPE_RAW . */
40+
EthFlowFlagsGtpRaw = RTE_SHIFT_VAL32(2, 12),
3841

3942
/**
4043
* @brief How to generate actions when RSS is used.

csrc/ethface/flowdef.c

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,21 @@ AppendItem(EthFlowDef* flow, size_t* i, enum rte_flow_item_type typ, const void*
1717
NDNDPDK_ASSERT(*i < RTE_DIM(flow->pattern));
1818
}
1919

20+
__attribute__((nonnull)) static inline void
21+
PrepareRawItem(EthFlowDef* flow, int32_t offset, uint16_t length, const void* spec,
22+
const void* mask) {
23+
NDNDPDK_ASSERT(length <= sizeof(flow->rawSpecBuf));
24+
memmove(flow->rawSpecBuf, spec, length);
25+
memmove(flow->rawMaskBuf, mask, length);
26+
27+
flow->rawSpec.relative = 1;
28+
flow->rawSpec.offset = offset;
29+
flow->rawSpec.length = length;
30+
flow->rawSpec.pattern = flow->rawSpecBuf;
31+
flow->rawMask = rte_flow_item_raw_mask;
32+
flow->rawMask.pattern = flow->rawMaskBuf;
33+
}
34+
2035
__attribute__((nonnull)) static inline void
2136
PrepareVxlan(const EthLocator* loc, struct rte_vxlan_hdr* vxlanSpec,
2237
struct rte_vxlan_hdr* vxlanMask, struct rte_ether_hdr* innerEthSpec,
@@ -101,15 +116,7 @@ GeneratePattern(EthFlowDef* flow, size_t specLen[], const EthLocator* loc, EthLo
101116
} __rte_aligned(2) spec = {0}, mask = {0};
102117
PrepareVxlan(loc, &spec.vxlan, &mask.vxlan, &spec.eth, &mask.eth);
103118
static_assert(sizeof(spec) == 4 + 16 + 2, "");
104-
rte_mov16(flow->rawSpecBuf, RTE_PTR_ADD(&spec, 4));
105-
rte_mov16(flow->rawMaskBuf, RTE_PTR_ADD(&mask, 4));
106-
107-
flow->rawSpec.relative = 1;
108-
flow->rawSpec.offset = 4;
109-
flow->rawSpec.length = 16;
110-
flow->rawSpec.pattern = flow->rawSpecBuf;
111-
flow->rawMask = rte_flow_item_raw_mask;
112-
flow->rawMask.pattern = flow->rawMaskBuf;
119+
PrepareRawItem(flow, 4, 16, RTE_PTR_ADD(&spec, 4), RTE_PTR_ADD(&mask, 4));
113120
APPEND(RAW, raw);
114121
} else {
115122
PrepareVxlan(loc, &flow->vxlanSpec.hdr, &flow->vxlanMask.hdr, &flow->innerEthSpec.hdr,
@@ -120,15 +127,36 @@ GeneratePattern(EthFlowDef* flow, size_t specLen[], const EthLocator* loc, EthLo
120127
break;
121128
}
122129
case 'G': {
123-
EthGtpHdr spec = {0};
130+
EthGtpHdr spec = {0}, mask = {0};
124131
PutGtpHdr((uint8_t*)&spec, true, loc->ulTEID, loc->ulQFI);
125-
flow->gtpSpec.hdr = spec.hdr;
126-
MASK(flow->gtpMask.hdr.teid);
127132

128-
if (flowFlags & EthFlowFlagsGtp) {
129-
APPEND(GTP, gtp);
130-
} else {
131-
APPEND(GTPU, gtp);
133+
switch (flowFlags & EthFlowFlagsGtpMask) {
134+
case EthFlowFlagsGtpGtpu: {
135+
APPEND(GTPU, gtp);
136+
goto FILL_GTP_ITEM;
137+
}
138+
case EthFlowFlagsGtpGtp: {
139+
APPEND(GTP, gtp);
140+
FILL_GTP_ITEM:
141+
flow->gtpSpec.hdr = spec.hdr;
142+
MASK(flow->gtpMask.hdr.teid);
143+
break;
144+
}
145+
case EthFlowFlagsGtpRaw: {
146+
// In i40e driver, RAW item can have up to I40E_FDIR_MAX_FLEX_LEN=16 uint16 words, of
147+
// which up to I40E_FDIR_BITMASK_NUM_WORD=2 words may have a "bit mask" i.e. mask other
148+
// than 0000 and FFFF, see i40e_flow_store_flex_mask(). We use bit masks on the first and
149+
// eighth words. mask.hdr.ver is unmasked because masking it seems to cause packet loss.
150+
mask.hdr.pt = 1;
151+
mask.hdr.e = 1;
152+
MASK(mask.hdr.msg_type);
153+
MASK(mask.hdr.teid);
154+
mask.psc.qfi = 0b111111;
155+
static_assert(sizeof(spec) == 16);
156+
PrepareRawItem(flow, 0, 16, &spec, &mask);
157+
APPEND(RAW, raw);
158+
break;
159+
}
132160
}
133161
break;
134162
}
@@ -215,8 +243,12 @@ __attribute__((nonnull)) static inline void
215243
PrintDef(const EthFlowDef* flow, size_t specLen[]) {
216244
for (int i = 0; i >= 0; ++i) {
217245
const struct rte_flow_item* item = &flow->pattern[i];
218-
char b16Spec[Base16_BufferSize(64)] = {'-', 0};
219-
char b16Mask[Base16_BufferSize(64)] = {'-', 0};
246+
enum {
247+
b16BufOctets = 64,
248+
b16BufSize = Base16_BufferSize(b16BufOctets),
249+
};
250+
char b16Spec[b16BufSize] = {'-', 0};
251+
char b16Mask[b16BufSize] = {'-', 0};
220252
if (item->spec != NULL && item->mask != NULL) {
221253
NDNDPDK_ASSERT(specLen[i] <= 64);
222254
Base16_Encode(b16Spec, sizeof(b16Spec), item->spec, specLen[i]);
@@ -227,25 +259,29 @@ PrintDef(const EthFlowDef* flow, size_t specLen[]) {
227259
(const void*)(uintptr_t)item->type, NULL) <= 0) {
228260
typeName = "-";
229261
}
230-
N_LOGD("^ pattern index=%d type=%d~%s spec=%s mask=%s", i, (int)item->type, typeName, b16Spec,
231-
b16Mask);
232262
switch (item->type) {
233263
case RTE_FLOW_ITEM_TYPE_END:
234264
i = -2; // break loop
235265
break;
236266
case RTE_FLOW_ITEM_TYPE_RAW: {
237267
const struct rte_flow_item_raw* spec = item->spec;
238268
const struct rte_flow_item_raw* mask = item->mask;
239-
Base16_Encode(b16Spec, sizeof(b16Spec), spec->pattern, spec->length);
240-
Base16_Encode(b16Mask, sizeof(b16Mask), mask->pattern, spec->length);
241-
N_LOGD("^ pattern-raw index=%d relative=%" PRIu32 " offset=%" PRId32
242-
" spec.pattern=%s mask.pattern=%s",
243-
i, spec->relative, spec->offset, b16Spec, b16Mask);
269+
NDNDPDK_ASSERT(sizeof(*spec) + 1 + spec->length <= b16BufOctets);
270+
int b16Offset = 2 * sizeof(*spec);
271+
b16Spec[b16Offset] = '+';
272+
b16Mask[b16Offset] = '+';
273+
++b16Offset;
274+
Base16_Encode(RTE_PTR_ADD(b16Spec, b16Offset), sizeof(b16Spec) - b16Offset, spec->pattern,
275+
spec->length);
276+
Base16_Encode(RTE_PTR_ADD(b16Mask, b16Offset), sizeof(b16Mask) - b16Offset, mask->pattern,
277+
spec->length);
244278
break;
245279
}
246280
default:
247281
break;
248282
}
283+
N_LOGD("^ pattern index=%d type=%d~%s spec=%s mask=%s", i, (int)item->type, typeName, b16Spec,
284+
b16Mask);
249285
}
250286

251287
for (int i = 0;; ++i) {

docs/face.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ Example commands:
6868
dpdk-devbind.py --status-dev net
6969

7070
# change kernel driver binding (only needed for some NICs, see DPDK docs on what driver to use)
71-
sudo dpdk-devbind.py -b uio_pci_generic 04:00.0
71+
sudo dpdk-devbind.py -b vfio-pci 04:00.0
7272

7373
# create an Ethernet port with PCI driver, enable RxFlow with 16 queues
7474
ndndpdk-ctrl create-eth-port --pci 04:00.0 --mtu 1500 --rx-flow 16

docs/nics.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ The VXLAN item in `pattern_vxlan_[12]` patterns are unusable because the parser
231231
GTP-U tunnel face is supported through `pattern_fdir_ipv4_gtpu`.
232232
This pattern relies on [Dynamic Device Personalization (DDP)](https://www.intel.com/content/www/us/en/developer/articles/technical/dynamic-device-personalization-for-intel-ethernet-700-series.html) feature.
233233
You must manually download the [GTPv1 DDP profile](https://downloadcenter.intel.com/download/27587) and place it at `/lib/firmware/intel/i40e/ddp/gtp.pkg`.
234+
To use this in Docker, you also need to bind-mount the directory into the container with `--mount type=bind,source=/lib/firmware/intel/i40e/ddp,target=/lib/firmware/intel/i40e/ddp,readonly=true` flag.
234235
If the profile is found, you would see "upload DDP package success" log message during Ethernet port creation.
235236
Without the profile, GTP-U face creation on RxFlow fails with "GTP is not supported by default" error.
236237
During NDN-DPDK service shutdown, a profile rollback will be attempted.

dpdk/ethdev/ddp.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ import (
2020

2121
// DdpProfile represents a Dynamic Device Personalization profile.
2222
type DdpProfile struct {
23-
pkg []byte
24-
info C.struct_rte_pmd_i40e_profile_info
25-
zapPkg zap.Field
23+
pkg []byte
24+
info C.struct_rte_pmd_i40e_profile_info
25+
protocols []string
26+
zapPkg zap.Field
2627
}
2728

2829
func (dp *DdpProfile) process(dev EthDev, buf []byte, op C.enum_rte_pmd_i40e_package_op, act string) error {
@@ -75,19 +76,41 @@ func OpenDdpProfile(name string) (dp *DdpProfile, e error) {
7576
return nil, e
7677
}
7778

78-
if res := C.rte_pmd_i40e_get_ddp_info((*C.uint8_t)(&dp.pkg[0]), C.uint32_t(len(dp.pkg)),
79-
(*C.uint8_t)(unsafe.Pointer(&dp.info)), C.uint32_t(unsafe.Sizeof(dp.info)),
80-
C.RTE_PMD_I40E_PKG_INFO_GLOBAL_HEADER); res != 0 {
81-
e = eal.MakeErrno(res)
82-
logEntry.Warn("parse DDP profile error", zap.Error(e))
83-
return nil, fmt.Errorf("rte_pmd_i40e_get_ddp_info %w", e)
79+
if e := getDdpInfo(dp.pkg, C.RTE_PMD_I40E_PKG_INFO_GLOBAL_HEADER, &dp.info, 1); e != nil {
80+
return nil, e
81+
}
82+
83+
var nProtocols uint32
84+
if e := getDdpInfo(dp.pkg, C.RTE_PMD_I40E_PKG_INFO_PROTOCOL_NUM, &nProtocols, 1); e != nil {
85+
return nil, e
86+
}
87+
88+
protoInfos := make([]C.struct_rte_pmd_i40e_proto_info, nProtocols)
89+
if nProtocols > 0 {
90+
if e := getDdpInfo(dp.pkg, C.RTE_PMD_I40E_PKG_INFO_PROTOCOL_LIST, unsafe.SliceData(protoInfos), len(protoInfos)); e != nil {
91+
return nil, e
92+
}
93+
}
94+
95+
for _, pcType := range protoInfos {
96+
dp.protocols = append(dp.protocols, C.GoString(unsafe.SliceData(pcType.name[:])))
8497
}
8598

8699
dp.zapPkg = zap.Dict("pkg",
87100
zap.Uint32("track-id", uint32(dp.info.track_id)),
88101
zap.String("name", C.GoString((*C.char)(unsafe.Pointer(&dp.info.name[0])))),
89102
zap.String("version", fmt.Sprintf("%d.%d.%d.%d",
90103
dp.info.version.major, dp.info.version.minor, dp.info.version.update, dp.info.version.draft)),
104+
zap.Strings("protocols", dp.protocols),
91105
)
92106
return dp, nil
93107
}
108+
109+
func getDdpInfo[T any](pkg []byte, typ C.enum_rte_pmd_i40e_package_info, info *T, count int) error {
110+
res := C.rte_pmd_i40e_get_ddp_info((*C.uint8_t)(unsafe.SliceData(pkg)), C.uint32_t(len(pkg)),
111+
(*C.uint8_t)(unsafe.Pointer(info)), C.uint32_t(unsafe.Sizeof(*info)*uintptr(count)), typ)
112+
if res != 0 {
113+
return fmt.Errorf("rte_pmd_i40e_get_ddp_info(%d) error %w", typ, eal.MakeErrno(res))
114+
}
115+
return nil
116+
}

dpdk/ethdev/info.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"encoding/json"
99
"fmt"
1010
"reflect"
11+
"slices"
1112
"unsafe"
1213

1314
"github.com/usnistgov/ndn-dpdk/core/cptr"
@@ -111,13 +112,24 @@ func (info DevInfo) FlowFlags() (flags uint32) {
111112
case DriverAfPacket, DriverTAP, DriverXDP, DriverRing, DriverMemif:
112113
flags |= C.EthFlowFlagsDisabled
113114
case DriverMlx5:
114-
flags |= C.EthFlowFlagsGtp
115+
flags |= C.EthFlowFlagsGtpGtp
115116
case DriverI40e:
116117
flags |= C.EthFlowFlagsPassthruArp | C.EthFlowFlagsVxRaw | C.EthFlowFlagsRssUnmarked | C.EthFlowFlagsEtherUnmarked
118+
flags |= C.EthFlowFlagsGtpRaw // further updated by DdpProfile.UpdateFlowFlags
117119
}
118120
return
119121
}
120122

123+
// UpdateFlowFlags adjust flow flags after loading a profile.
124+
func (dp *DdpProfile) UpdateFlowFlags(flags *uint32) {
125+
// If the GTPv1 DDP package is loaded, flow must use GTPU item, RAW item will not match.
126+
// If the GTPv1 DDP package is not loaded, GTPU item cannot be used, flow may use RAW item.
127+
if slices.Contains(dp.protocols, "GTPU") {
128+
*flags &= ^uint32(C.EthFlowFlagsGtpMask)
129+
*flags |= C.EthFlowFlagsGtpGtpu
130+
}
131+
}
132+
121133
// MarshalJSON implements json.Marshaler interface.
122134
func (info DevInfo) MarshalJSON() ([]byte, error) {
123135
return infoJSON(info, info.DevInfoC)

iface/ethport/port.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ type Port struct {
7979
logger *zap.Logger
8080
dev ethdev.EthDev
8181
devInfo ethdev.DevInfo
82+
flowFlags uint32
8283
ddpRollback func() error
8384
faces map[iface.ID]*Face
8485
rxBouncePool *pktmbuf.Pool
@@ -157,7 +158,10 @@ func (port *Port) startDev(nRxQueues int, promisc bool) (e error) {
157158

158159
if port.devInfo.Driver() == ethdev.DriverI40e {
159160
if dp, e := ethdev.OpenDdpProfile("gtp"); e == nil {
160-
port.ddpRollback, _ = dp.Upload(port.dev)
161+
port.ddpRollback, e = dp.Upload(port.dev)
162+
if e == nil {
163+
dp.UpdateFlowFlags(&port.flowFlags)
164+
}
161165
}
162166
}
163167

@@ -215,6 +219,7 @@ func New(cfg Config) (port *Port, e error) {
215219
devInfo: cfg.EthDev.DevInfo(),
216220
faces: map[iface.ID]*Face{},
217221
}
222+
port.flowFlags = port.devInfo.FlowFlags()
218223
switch port.devInfo.Driver() {
219224
case ethdev.DriverXDP:
220225
if port.rxBouncePool, e = pktmbuf.NewPool(pktmbuf.PoolConfig{

iface/ethport/rxflow.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ func destroyFlow(face *Face) error {
6868
}
6969

7070
type rxFlow struct {
71-
flowFlags C.EthFlowFlags
7271
availQueues []uint16
7372
}
7473

@@ -101,10 +100,9 @@ func (impl *rxFlow) setIsolate(port *Port, enable bool) error {
101100

102101
func (impl *rxFlow) Init(port *Port) error {
103102
*impl = rxFlow{}
104-
impl.flowFlags = C.EthFlowFlags(port.devInfo.FlowFlags())
105103
needPromisc := false
106104
if e := impl.setIsolate(port, true); e == nil {
107-
impl.flowFlags |= C.EthFlowFlagsIsolated
105+
port.flowFlags |= C.EthFlowFlagsIsolated
108106
} else {
109107
port.logger.Info("flow isolate mode unavailable", zap.Error(e))
110108
needPromisc = true
@@ -136,7 +134,7 @@ func (impl *rxFlow) Start(face *Face) error {
136134
}
137135

138136
queues := impl.availQueues[:nRxQueues]
139-
if e := setupFlow(face, queues, impl.flowFlags, zap.WarnLevel); e != nil {
137+
if e := setupFlow(face, queues, C.EthFlowFlags(face.port.flowFlags), zap.WarnLevel); e != nil {
140138
return e
141139
}
142140

iface/ethport/rxtable.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ import (
1919
)
2020

2121
type rxTable struct {
22-
rxt *rxgTable
23-
flowFlags C.EthFlowFlags
22+
rxt *rxgTable
2423
}
2524

2625
func (rxTable) String() string {
@@ -36,13 +35,12 @@ func (impl *rxTable) Init(port *Port) error {
3635
return e
3736
}
3837
impl.rxt = newRxgTable(port)
39-
impl.flowFlags = C.EthFlowFlags(port.devInfo.FlowFlags())
4038
return nil
4139
}
4240

4341
func (impl *rxTable) Start(face *Face) error {
44-
if impl.flowFlags&C.EthFlowFlagsDisabled == 0 {
45-
setupFlow(face, []uint16{0}, impl.flowFlags, zap.InfoLevel)
42+
if flowFlags := face.port.flowFlags; flowFlags&C.EthFlowFlagsDisabled == 0 {
43+
setupFlow(face, []uint16{0}, C.EthFlowFlags(flowFlags), zap.InfoLevel)
4644
}
4745

4846
if face.loc.Scheme() == SchemePassthru {

0 commit comments

Comments
 (0)