common/sfc_efx/base: support setting PPORT in match spec
[dpdk.git] / drivers / common / sfc_efx / base / efx_mae.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  *
3  * Copyright(c) 2019 Xilinx, Inc. All rights reserved.
4  * All rights reserved.
5  */
6
7 #include "efx.h"
8 #include "efx_impl.h"
9
10
11 #if EFSYS_OPT_MAE
12
13 static  __checkReturn                   efx_rc_t
14 efx_mae_get_capabilities(
15         __in                            efx_nic_t *enp)
16 {
17         efx_mcdi_req_t req;
18         EFX_MCDI_DECLARE_BUF(payload,
19             MC_CMD_MAE_GET_CAPS_IN_LEN,
20             MC_CMD_MAE_GET_CAPS_OUT_LEN);
21         struct efx_mae_s *maep = enp->en_maep;
22         efx_rc_t rc;
23
24         req.emr_cmd = MC_CMD_MAE_GET_CAPS;
25         req.emr_in_buf = payload;
26         req.emr_in_length = MC_CMD_MAE_GET_CAPS_IN_LEN;
27         req.emr_out_buf = payload;
28         req.emr_out_length = MC_CMD_MAE_GET_CAPS_OUT_LEN;
29
30         efx_mcdi_execute(enp, &req);
31
32         if (req.emr_rc != 0) {
33                 rc = req.emr_rc;
34                 goto fail1;
35         }
36
37         if (req.emr_out_length_used < MC_CMD_MAE_GET_CAPS_OUT_LEN) {
38                 rc = EMSGSIZE;
39                 goto fail2;
40         }
41
42         maep->em_max_n_action_prios =
43             MCDI_OUT_DWORD(req, MAE_GET_CAPS_OUT_ACTION_PRIOS);
44
45         maep->em_max_nfields =
46             MCDI_OUT_DWORD(req, MAE_GET_CAPS_OUT_MATCH_FIELD_COUNT);
47
48         return (0);
49
50 fail2:
51         EFSYS_PROBE(fail2);
52 fail1:
53         EFSYS_PROBE1(fail1, efx_rc_t, rc);
54         return (rc);
55 }
56
57 static  __checkReturn                   efx_rc_t
58 efx_mae_get_action_rule_caps(
59         __in                            efx_nic_t *enp,
60         __in                            unsigned int field_ncaps,
61         __out_ecount(field_ncaps)       efx_mae_field_cap_t *field_caps)
62 {
63         efx_mcdi_req_t req;
64         EFX_MCDI_DECLARE_BUF(payload,
65             MC_CMD_MAE_GET_AR_CAPS_IN_LEN,
66             MC_CMD_MAE_GET_AR_CAPS_OUT_LENMAX_MCDI2);
67         unsigned int mcdi_field_ncaps;
68         unsigned int i;
69         efx_rc_t rc;
70
71         if (MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(field_ncaps) >
72             MC_CMD_MAE_GET_AR_CAPS_OUT_LENMAX_MCDI2) {
73                 rc = EINVAL;
74                 goto fail1;
75         }
76
77         req.emr_cmd = MC_CMD_MAE_GET_AR_CAPS;
78         req.emr_in_buf = payload;
79         req.emr_in_length = MC_CMD_MAE_GET_AR_CAPS_IN_LEN;
80         req.emr_out_buf = payload;
81         req.emr_out_length = MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(field_ncaps);
82
83         efx_mcdi_execute(enp, &req);
84
85         if (req.emr_rc != 0) {
86                 rc = req.emr_rc;
87                 goto fail2;
88         }
89
90         mcdi_field_ncaps = MCDI_OUT_DWORD(req, MAE_GET_OR_CAPS_OUT_COUNT);
91
92         if (req.emr_out_length_used <
93             MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(mcdi_field_ncaps)) {
94                 rc = EMSGSIZE;
95                 goto fail3;
96         }
97
98         if (mcdi_field_ncaps > field_ncaps) {
99                 rc = EMSGSIZE;
100                 goto fail4;
101         }
102
103         for (i = 0; i < mcdi_field_ncaps; ++i) {
104                 uint32_t match_flag;
105                 uint32_t mask_flag;
106
107                 field_caps[i].emfc_support = MCDI_OUT_INDEXED_DWORD_FIELD(req,
108                     MAE_GET_AR_CAPS_OUT_FIELD_FLAGS, i,
109                     MAE_FIELD_FLAGS_SUPPORT_STATUS);
110
111                 match_flag = MCDI_OUT_INDEXED_DWORD_FIELD(req,
112                     MAE_GET_AR_CAPS_OUT_FIELD_FLAGS, i,
113                     MAE_FIELD_FLAGS_MATCH_AFFECTS_CLASS);
114
115                 field_caps[i].emfc_match_affects_class =
116                     (match_flag != 0) ? B_TRUE : B_FALSE;
117
118                 mask_flag = MCDI_OUT_INDEXED_DWORD_FIELD(req,
119                     MAE_GET_AR_CAPS_OUT_FIELD_FLAGS, i,
120                     MAE_FIELD_FLAGS_MASK_AFFECTS_CLASS);
121
122                 field_caps[i].emfc_mask_affects_class =
123                     (mask_flag != 0) ? B_TRUE : B_FALSE;
124         }
125
126         return (0);
127
128 fail4:
129         EFSYS_PROBE(fail4);
130 fail3:
131         EFSYS_PROBE(fail3);
132 fail2:
133         EFSYS_PROBE(fail2);
134 fail1:
135         EFSYS_PROBE1(fail1, efx_rc_t, rc);
136         return (rc);
137 }
138
139         __checkReturn                   efx_rc_t
140 efx_mae_init(
141         __in                            efx_nic_t *enp)
142 {
143         const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
144         efx_mae_field_cap_t *ar_fcaps;
145         size_t ar_fcaps_size;
146         efx_mae_t *maep;
147         efx_rc_t rc;
148
149         if (encp->enc_mae_supported == B_FALSE) {
150                 rc = ENOTSUP;
151                 goto fail1;
152         }
153
154         EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*maep), maep);
155         if (maep == NULL) {
156                 rc = ENOMEM;
157                 goto fail2;
158         }
159
160         enp->en_maep = maep;
161
162         rc = efx_mae_get_capabilities(enp);
163         if (rc != 0)
164                 goto fail3;
165
166         ar_fcaps_size = maep->em_max_nfields * sizeof (*ar_fcaps);
167         EFSYS_KMEM_ALLOC(enp->en_esip, ar_fcaps_size, ar_fcaps);
168         if (ar_fcaps == NULL) {
169                 rc = ENOMEM;
170                 goto fail4;
171         }
172
173         maep->em_action_rule_field_caps_size = ar_fcaps_size;
174         maep->em_action_rule_field_caps = ar_fcaps;
175
176         rc = efx_mae_get_action_rule_caps(enp, maep->em_max_nfields, ar_fcaps);
177         if (rc != 0)
178                 goto fail5;
179
180         return (0);
181
182 fail5:
183         EFSYS_PROBE(fail5);
184         EFSYS_KMEM_FREE(enp->en_esip, ar_fcaps_size, ar_fcaps);
185 fail4:
186         EFSYS_PROBE(fail4);
187 fail3:
188         EFSYS_PROBE(fail3);
189         EFSYS_KMEM_FREE(enp->en_esip, sizeof (struct efx_mae_s), enp->en_maep);
190         enp->en_maep = NULL;
191 fail2:
192         EFSYS_PROBE(fail2);
193 fail1:
194         EFSYS_PROBE1(fail1, efx_rc_t, rc);
195         return (rc);
196 }
197
198                                         void
199 efx_mae_fini(
200         __in                            efx_nic_t *enp)
201 {
202         const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
203         efx_mae_t *maep = enp->en_maep;
204
205         if (encp->enc_mae_supported == B_FALSE)
206                 return;
207
208         EFSYS_KMEM_FREE(enp->en_esip, maep->em_action_rule_field_caps_size,
209             maep->em_action_rule_field_caps);
210         EFSYS_KMEM_FREE(enp->en_esip, sizeof (*maep), maep);
211         enp->en_maep = NULL;
212 }
213
214         __checkReturn                   efx_rc_t
215 efx_mae_get_limits(
216         __in                            efx_nic_t *enp,
217         __out                           efx_mae_limits_t *emlp)
218 {
219         const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
220         struct efx_mae_s *maep = enp->en_maep;
221         efx_rc_t rc;
222
223         if (encp->enc_mae_supported == B_FALSE) {
224                 rc = ENOTSUP;
225                 goto fail1;
226         }
227
228         emlp->eml_max_n_action_prios = maep->em_max_n_action_prios;
229
230         return (0);
231
232 fail1:
233         EFSYS_PROBE1(fail1, efx_rc_t, rc);
234         return (rc);
235 }
236
237         __checkReturn                   efx_rc_t
238 efx_mae_match_spec_init(
239         __in                            efx_nic_t *enp,
240         __in                            efx_mae_rule_type_t type,
241         __in                            uint32_t prio,
242         __out                           efx_mae_match_spec_t **specp)
243 {
244         efx_mae_match_spec_t *spec;
245         efx_rc_t rc;
246
247         switch (type) {
248         case EFX_MAE_RULE_ACTION:
249                 break;
250         default:
251                 rc = ENOTSUP;
252                 goto fail1;
253         }
254
255         EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*spec), spec);
256         if (spec == NULL) {
257                 rc = ENOMEM;
258                 goto fail2;
259         }
260
261         spec->emms_type = type;
262         spec->emms_prio = prio;
263
264         *specp = spec;
265
266         return (0);
267
268 fail2:
269         EFSYS_PROBE(fail2);
270 fail1:
271         EFSYS_PROBE1(fail1, efx_rc_t, rc);
272         return (rc);
273 }
274
275                                         void
276 efx_mae_match_spec_fini(
277         __in                            efx_nic_t *enp,
278         __in                            efx_mae_match_spec_t *spec)
279 {
280         EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), spec);
281 }
282
283 /* Named identifiers which are valid indices to efx_mae_field_cap_t */
284 typedef enum efx_mae_field_cap_id_e {
285         EFX_MAE_FIELD_ID_INGRESS_MPORT_SELECTOR = MAE_FIELD_INGRESS_PORT,
286
287         EFX_MAE_FIELD_CAP_NIDS
288 } efx_mae_field_cap_id_t;
289
290 typedef enum efx_mae_field_endianness_e {
291         EFX_MAE_FIELD_LE = 0,
292         EFX_MAE_FIELD_BE,
293
294         EFX_MAE_FIELD_ENDIANNESS_NTYPES
295 } efx_mae_field_endianness_t;
296
297 /*
298  * The following structure is a means to describe an MAE field.
299  * The information in it is meant to be used internally by
300  * APIs for addressing a given field in a mask-value pairs
301  * structure and for validation purposes.
302  */
303 typedef struct efx_mae_mv_desc_s {
304         efx_mae_field_cap_id_t          emmd_field_cap_id;
305
306         size_t                          emmd_value_size;
307         size_t                          emmd_value_offset;
308         size_t                          emmd_mask_size;
309         size_t                          emmd_mask_offset;
310
311         efx_mae_field_endianness_t      emmd_endianness;
312 } efx_mae_mv_desc_t;
313
314 /* Indices to this array are provided by efx_mae_field_id_t */
315 static const efx_mae_mv_desc_t __efx_mae_action_rule_mv_desc_set[] = {
316 #define EFX_MAE_MV_DESC(_name, _endianness)                             \
317         [EFX_MAE_FIELD_##_name] =                                       \
318         {                                                               \
319                 EFX_MAE_FIELD_ID_##_name,                               \
320                 MAE_FIELD_MASK_VALUE_PAIRS_##_name##_LEN,               \
321                 MAE_FIELD_MASK_VALUE_PAIRS_##_name##_OFST,              \
322                 MAE_FIELD_MASK_VALUE_PAIRS_##_name##_MASK_LEN,          \
323                 MAE_FIELD_MASK_VALUE_PAIRS_##_name##_MASK_OFST,         \
324                 _endianness                                             \
325         }
326
327         EFX_MAE_MV_DESC(INGRESS_MPORT_SELECTOR, EFX_MAE_FIELD_LE),
328
329 #undef EFX_MAE_MV_DESC
330 };
331
332         __checkReturn                   efx_rc_t
333 efx_mae_mport_by_phy_port(
334         __in                            uint32_t phy_port,
335         __out                           efx_mport_sel_t *mportp)
336 {
337         efx_dword_t dword;
338         efx_rc_t rc;
339
340         if (phy_port > EFX_MASK32(MAE_MPORT_SELECTOR_PPORT_ID)) {
341                 rc = EINVAL;
342                 goto fail1;
343         }
344
345         EFX_POPULATE_DWORD_2(dword,
346             MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_PPORT,
347             MAE_MPORT_SELECTOR_PPORT_ID, phy_port);
348
349         memset(mportp, 0, sizeof (*mportp));
350         mportp->sel = dword.ed_u32[0];
351
352         return (0);
353
354 fail1:
355         EFSYS_PROBE1(fail1, efx_rc_t, rc);
356         return (rc);
357 }
358
359         __checkReturn                   efx_rc_t
360 efx_mae_match_spec_field_set(
361         __in                            efx_mae_match_spec_t *spec,
362         __in                            efx_mae_field_id_t field_id,
363         __in                            size_t value_size,
364         __in_bcount(value_size)         const uint8_t *value,
365         __in                            size_t mask_size,
366         __in_bcount(mask_size)          const uint8_t *mask)
367 {
368         const efx_mae_mv_desc_t *descp;
369         uint8_t *mvp;
370         efx_rc_t rc;
371
372         if (field_id >= EFX_MAE_FIELD_NIDS) {
373                 rc = EINVAL;
374                 goto fail1;
375         }
376
377         switch (spec->emms_type) {
378         case EFX_MAE_RULE_ACTION:
379                 descp = &__efx_mae_action_rule_mv_desc_set[field_id];
380                 mvp = spec->emms_mask_value_pairs.action;
381                 break;
382         default:
383                 rc = ENOTSUP;
384                 goto fail2;
385         }
386
387         if (value_size != descp->emmd_value_size) {
388                 rc = EINVAL;
389                 goto fail3;
390         }
391
392         if (mask_size != descp->emmd_mask_size) {
393                 rc = EINVAL;
394                 goto fail4;
395         }
396
397         if (descp->emmd_endianness == EFX_MAE_FIELD_BE) {
398                 /*
399                  * The mask/value are in network (big endian) order.
400                  * The MCDI request field is also big endian.
401                  */
402                 memcpy(mvp + descp->emmd_value_offset, value, value_size);
403                 memcpy(mvp + descp->emmd_mask_offset, mask, mask_size);
404         } else {
405                 efx_dword_t dword;
406
407                 /*
408                  * The mask/value are in host byte order.
409                  * The MCDI request field is little endian.
410                  */
411                 switch (value_size) {
412                 case 4:
413                         EFX_POPULATE_DWORD_1(dword,
414                             EFX_DWORD_0, *(const uint32_t *)value);
415
416                         memcpy(mvp + descp->emmd_value_offset,
417                             &dword, sizeof (dword));
418                         break;
419                 default:
420                         EFSYS_ASSERT(B_FALSE);
421                 }
422
423                 switch (mask_size) {
424                 case 4:
425                         EFX_POPULATE_DWORD_1(dword,
426                             EFX_DWORD_0, *(const uint32_t *)mask);
427
428                         memcpy(mvp + descp->emmd_mask_offset,
429                             &dword, sizeof (dword));
430                         break;
431                 default:
432                         EFSYS_ASSERT(B_FALSE);
433                 }
434         }
435
436         return (0);
437
438 fail4:
439         EFSYS_PROBE(fail4);
440 fail3:
441         EFSYS_PROBE(fail3);
442 fail2:
443         EFSYS_PROBE(fail2);
444 fail1:
445         EFSYS_PROBE1(fail1, efx_rc_t, rc);
446         return (rc);
447 }
448
449         __checkReturn                   efx_rc_t
450 efx_mae_match_spec_mport_set(
451         __in                            efx_mae_match_spec_t *spec,
452         __in                            const efx_mport_sel_t *valuep,
453         __in_opt                        const efx_mport_sel_t *maskp)
454 {
455         uint32_t full_mask = UINT32_MAX;
456         const uint8_t *vp;
457         const uint8_t *mp;
458         efx_rc_t rc;
459
460         if (valuep == NULL) {
461                 rc = EINVAL;
462                 goto fail1;
463         }
464
465         vp = (const uint8_t *)&valuep->sel;
466         if (maskp != NULL)
467                 mp = (const uint8_t *)&maskp->sel;
468         else
469                 mp = (const uint8_t *)&full_mask;
470
471         rc = efx_mae_match_spec_field_set(spec,
472             EFX_MAE_FIELD_INGRESS_MPORT_SELECTOR,
473             sizeof (valuep->sel), vp, sizeof (maskp->sel), mp);
474         if (rc != 0)
475                 goto fail2;
476
477         return (0);
478
479 fail2:
480         EFSYS_PROBE(fail2);
481 fail1:
482         EFSYS_PROBE1(fail1, efx_rc_t, rc);
483         return (rc);
484 }
485
486 #define EFX_MASK_BIT_IS_SET(_mask, _mask_page_nbits, _bit)              \
487             ((_mask)[(_bit) / (_mask_page_nbits)] &                     \
488                     (1ULL << ((_bit) & ((_mask_page_nbits) - 1))))
489
490 static inline                           boolean_t
491 efx_mask_is_prefix(
492         __in                            size_t mask_nbytes,
493         __in_bcount(mask_nbytes)        const uint8_t *maskp)
494 {
495         boolean_t prev_bit_is_set = B_TRUE;
496         unsigned int i;
497
498         for (i = 0; i < 8 * mask_nbytes; ++i) {
499                 boolean_t bit_is_set = EFX_MASK_BIT_IS_SET(maskp, 8, i);
500
501                 if (!prev_bit_is_set && bit_is_set)
502                         return B_FALSE;
503
504                 prev_bit_is_set = bit_is_set;
505         }
506
507         return B_TRUE;
508 }
509
510 static inline                           boolean_t
511 efx_mask_is_all_ones(
512         __in                            size_t mask_nbytes,
513         __in_bcount(mask_nbytes)        const uint8_t *maskp)
514 {
515         unsigned int i;
516         uint8_t t = ~0;
517
518         for (i = 0; i < mask_nbytes; ++i)
519                 t &= maskp[i];
520
521         return (t == (uint8_t)(~0));
522 }
523
524 static inline                           boolean_t
525 efx_mask_is_all_zeros(
526         __in                            size_t mask_nbytes,
527         __in_bcount(mask_nbytes)        const uint8_t *maskp)
528 {
529         unsigned int i;
530         uint8_t t = 0;
531
532         for (i = 0; i < mask_nbytes; ++i)
533                 t |= maskp[i];
534
535         return (t == 0);
536 }
537
538         __checkReturn                   boolean_t
539 efx_mae_match_spec_is_valid(
540         __in                            efx_nic_t *enp,
541         __in                            const efx_mae_match_spec_t *spec)
542 {
543         efx_mae_t *maep = enp->en_maep;
544         unsigned int field_ncaps = maep->em_max_nfields;
545         const efx_mae_field_cap_t *field_caps;
546         const efx_mae_mv_desc_t *desc_setp;
547         unsigned int desc_set_nentries;
548         boolean_t is_valid = B_TRUE;
549         efx_mae_field_id_t field_id;
550         const uint8_t *mvp;
551
552         switch (spec->emms_type) {
553         case EFX_MAE_RULE_ACTION:
554                 field_caps = maep->em_action_rule_field_caps;
555                 desc_setp = __efx_mae_action_rule_mv_desc_set;
556                 desc_set_nentries =
557                     EFX_ARRAY_SIZE(__efx_mae_action_rule_mv_desc_set);
558                 mvp = spec->emms_mask_value_pairs.action;
559                 break;
560         default:
561                 return (B_FALSE);
562         }
563
564         if (field_caps == NULL)
565                 return (B_FALSE);
566
567         for (field_id = 0; field_id < desc_set_nentries; ++field_id) {
568                 const efx_mae_mv_desc_t *descp = &desc_setp[field_id];
569                 efx_mae_field_cap_id_t field_cap_id = descp->emmd_field_cap_id;
570                 const uint8_t *m_buf = mvp + descp->emmd_mask_offset;
571                 size_t m_size = descp->emmd_mask_size;
572
573                 if (m_size == 0)
574                         continue; /* Skip array gap */
575
576                 if (field_cap_id >= field_ncaps)
577                         break;
578
579                 switch (field_caps[field_cap_id].emfc_support) {
580                 case MAE_FIELD_SUPPORTED_MATCH_MASK:
581                         is_valid = B_TRUE;
582                         break;
583                 case MAE_FIELD_SUPPORTED_MATCH_PREFIX:
584                         is_valid = efx_mask_is_prefix(m_size, m_buf);
585                         break;
586                 case MAE_FIELD_SUPPORTED_MATCH_OPTIONAL:
587                         is_valid = (efx_mask_is_all_ones(m_size, m_buf) ||
588                             efx_mask_is_all_zeros(m_size, m_buf));
589                         break;
590                 case MAE_FIELD_SUPPORTED_MATCH_ALWAYS:
591                         is_valid = efx_mask_is_all_ones(m_size, m_buf);
592                         break;
593                 case MAE_FIELD_SUPPORTED_MATCH_NEVER:
594                 case MAE_FIELD_UNSUPPORTED:
595                 default:
596                         is_valid = efx_mask_is_all_zeros(m_size, m_buf);
597                         break;
598                 }
599
600                 if (is_valid == B_FALSE)
601                         break;
602         }
603
604         return (is_valid);
605 }
606
607         __checkReturn                   efx_rc_t
608 efx_mae_action_set_spec_init(
609         __in                            efx_nic_t *enp,
610         __out                           efx_mae_actions_t **specp)
611 {
612         efx_mae_actions_t *spec;
613         efx_rc_t rc;
614
615         EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*spec), spec);
616         if (spec == NULL) {
617                 rc = ENOMEM;
618                 goto fail1;
619         }
620
621         *specp = spec;
622
623         return (0);
624
625 fail1:
626         EFSYS_PROBE1(fail1, efx_rc_t, rc);
627         return (rc);
628 }
629
630                                         void
631 efx_mae_action_set_spec_fini(
632         __in                            efx_nic_t *enp,
633         __in                            efx_mae_actions_t *spec)
634 {
635         EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), spec);
636 }
637
638         __checkReturn                   boolean_t
639 efx_mae_action_set_specs_equal(
640         __in                            const efx_mae_actions_t *left,
641         __in                            const efx_mae_actions_t *right)
642 {
643         return ((memcmp(left, right, sizeof (*left)) == 0) ? B_TRUE : B_FALSE);
644 }
645
646         __checkReturn                   efx_rc_t
647 efx_mae_match_specs_class_cmp(
648         __in                            efx_nic_t *enp,
649         __in                            const efx_mae_match_spec_t *left,
650         __in                            const efx_mae_match_spec_t *right,
651         __out                           boolean_t *have_same_classp)
652 {
653         efx_mae_t *maep = enp->en_maep;
654         unsigned int field_ncaps = maep->em_max_nfields;
655         const efx_mae_field_cap_t *field_caps;
656         const efx_mae_mv_desc_t *desc_setp;
657         unsigned int desc_set_nentries;
658         boolean_t have_same_class = B_TRUE;
659         efx_mae_field_id_t field_id;
660         const uint8_t *mvpl;
661         const uint8_t *mvpr;
662         efx_rc_t rc;
663
664         switch (left->emms_type) {
665         case EFX_MAE_RULE_ACTION:
666                 field_caps = maep->em_action_rule_field_caps;
667                 desc_setp = __efx_mae_action_rule_mv_desc_set;
668                 desc_set_nentries =
669                     EFX_ARRAY_SIZE(__efx_mae_action_rule_mv_desc_set);
670                 mvpl = left->emms_mask_value_pairs.action;
671                 mvpr = right->emms_mask_value_pairs.action;
672                 break;
673         default:
674                 rc = ENOTSUP;
675                 goto fail1;
676         }
677
678         if (field_caps == NULL) {
679                 rc = EAGAIN;
680                 goto fail2;
681         }
682
683         if (left->emms_type != right->emms_type ||
684             left->emms_prio != right->emms_prio) {
685                 /*
686                  * Rules of different types can never map to the same class.
687                  *
688                  * The FW can support some set of match criteria for one
689                  * priority and not support the very same set for
690                  * another priority. Thus, two rules which have
691                  * different priorities can never map to
692                  * the same class.
693                  */
694                 *have_same_classp = B_FALSE;
695                 return (0);
696         }
697
698         for (field_id = 0; field_id < desc_set_nentries; ++field_id) {
699                 const efx_mae_mv_desc_t *descp = &desc_setp[field_id];
700                 efx_mae_field_cap_id_t field_cap_id = descp->emmd_field_cap_id;
701
702                 if (descp->emmd_mask_size == 0)
703                         continue; /* Skip array gap */
704
705                 if (field_cap_id >= field_ncaps)
706                         break;
707
708                 if (field_caps[field_cap_id].emfc_mask_affects_class) {
709                         const uint8_t *lmaskp = mvpl + descp->emmd_mask_offset;
710                         const uint8_t *rmaskp = mvpr + descp->emmd_mask_offset;
711                         size_t mask_size = descp->emmd_mask_size;
712
713                         if (memcmp(lmaskp, rmaskp, mask_size) != 0) {
714                                 have_same_class = B_FALSE;
715                                 break;
716                         }
717                 }
718
719                 if (field_caps[field_cap_id].emfc_match_affects_class) {
720                         const uint8_t *lvalp = mvpl + descp->emmd_value_offset;
721                         const uint8_t *rvalp = mvpr + descp->emmd_value_offset;
722                         size_t value_size = descp->emmd_value_size;
723
724                         if (memcmp(lvalp, rvalp, value_size) != 0) {
725                                 have_same_class = B_FALSE;
726                                 break;
727                         }
728                 }
729         }
730
731         *have_same_classp = have_same_class;
732
733         return (0);
734
735 fail2:
736         EFSYS_PROBE(fail2);
737 fail1:
738         EFSYS_PROBE1(fail1, efx_rc_t, rc);
739         return (rc);
740 }
741
742 #endif /* EFSYS_OPT_MAE */