@@ -453,9 +453,158 @@ static u16 ocelot_vlan_unaware_pvid(struct ocelot *ocelot,
453453 return VLAN_N_VID - bridge_num - 1 ;
454454}
455455
456+ /**
457+ * ocelot_update_vlan_reclassify_rule() - Make switch aware only to bridge VLAN TPID
458+ *
459+ * @ocelot: Switch private data structure
460+ * @port: Index of ingress port
461+ *
462+ * IEEE 802.1Q-2018 clauses "5.5 C-VLAN component conformance" and "5.6 S-VLAN
463+ * component conformance" suggest that a C-VLAN component should only recognize
464+ * and filter on C-Tags, and an S-VLAN component should only recognize and
465+ * process based on C-Tags.
466+ *
467+ * In Linux, as per commit 1a0b20b25732 ("Merge branch 'bridge-next'"), C-VLAN
468+ * components are largely represented by a bridge with vlan_protocol 802.1Q,
469+ * and S-VLAN components by a bridge with vlan_protocol 802.1ad.
470+ *
471+ * Currently the driver only offloads vlan_protocol 802.1Q, but the hardware
472+ * design is non-conformant, because the switch assigns each frame to a VLAN
473+ * based on an entirely different question, as detailed in figure "Basic VLAN
474+ * Classification Flow" from its manual and reproduced below.
475+ *
476+ * Set TAG_TYPE, PCP, DEI, VID to port-default values in VLAN_CFG register
477+ * if VLAN_AWARE_ENA[port] and frame has outer tag then:
478+ * if VLAN_INNER_TAG_ENA[port] and frame has inner tag then:
479+ * TAG_TYPE = (Frame.InnerTPID <> 0x8100)
480+ * Set PCP, DEI, VID to values from inner VLAN header
481+ * else:
482+ * TAG_TYPE = (Frame.OuterTPID <> 0x8100)
483+ * Set PCP, DEI, VID to values from outer VLAN header
484+ * if VID == 0 then:
485+ * VID = VLAN_CFG.VLAN_VID
486+ *
487+ * Summarized, the switch will recognize both 802.1Q and 802.1ad TPIDs as VLAN
488+ * "with equal rights", and just set the TAG_TYPE bit to 0 (if 802.1Q) or to 1
489+ * (if 802.1ad). It will classify based on whichever of the tags is "outer", no
490+ * matter what TPID that may have (or "inner", if VLAN_INNER_TAG_ENA[port]).
491+ *
492+ * In the VLAN Table, the TAG_TYPE information is not accessible - just the
493+ * classified VID is - so it is as if each VLAN Table entry is for 2 VLANs:
494+ * C-VLAN X, and S-VLAN X.
495+ *
496+ * Whereas the Linux bridge behavior is to only filter on frames with a TPID
497+ * equal to the vlan_protocol, and treat everything else as VLAN-untagged.
498+ *
499+ * Consider an ingress packet tagged with 802.1ad VID=3 and 802.1Q VID=5,
500+ * received on a bridge vlan_filtering=1 vlan_protocol=802.1Q port. This frame
501+ * should be treated as 802.1Q-untagged, and classified to the PVID of that
502+ * bridge port. Not to VID=3, and not to VID=5.
503+ *
504+ * The VCAP IS1 TCAM has everything we need to overwrite the choices made in
505+ * the basic VLAN classification pipeline: it can match on TAG_TYPE in the key,
506+ * and it can modify the classified VID in the action. Thus, for each port
507+ * under a vlan_filtering bridge, we can insert a rule in VCAP IS1 lookup 0 to
508+ * match on 802.1ad tagged frames and modify their classified VID to the 802.1Q
509+ * PVID of the port. This effectively makes it appear to the outside world as
510+ * if those packets were processed as VLAN-untagged.
511+ *
512+ * The rule needs to be updated each time the bridge PVID changes, and needs
513+ * to be deleted if the bridge PVID is deleted, or if the port becomes
514+ * VLAN-unaware.
515+ */
516+ static int ocelot_update_vlan_reclassify_rule (struct ocelot * ocelot , int port )
517+ {
518+ unsigned long cookie = OCELOT_VCAP_IS1_VLAN_RECLASSIFY (ocelot , port );
519+ struct ocelot_vcap_block * block_vcap_is1 = & ocelot -> block [VCAP_IS1 ];
520+ struct ocelot_port * ocelot_port = ocelot -> ports [port ];
521+ const struct ocelot_bridge_vlan * pvid_vlan ;
522+ struct ocelot_vcap_filter * filter ;
523+ int err , val , pcp , dei ;
524+ bool vid_replace_ena ;
525+ u16 vid ;
526+
527+ pvid_vlan = ocelot_port -> pvid_vlan ;
528+ vid_replace_ena = ocelot_port -> vlan_aware && pvid_vlan ;
529+
530+ filter = ocelot_vcap_block_find_filter_by_id (block_vcap_is1 , cookie ,
531+ false);
532+ if (!vid_replace_ena ) {
533+ /* If the reclassification filter doesn't need to exist, delete
534+ * it if it was previously installed, and exit doing nothing
535+ * otherwise.
536+ */
537+ if (filter )
538+ return ocelot_vcap_filter_del (ocelot , filter );
539+
540+ return 0 ;
541+ }
542+
543+ /* The reclassification rule must apply. See if it already exists
544+ * or if it must be created.
545+ */
546+
547+ /* Treating as VLAN-untagged means using as classified VID equal to
548+ * the bridge PVID, and PCP/DEI set to the port default QoS values.
549+ */
550+ vid = pvid_vlan -> vid ;
551+ val = ocelot_read_gix (ocelot , ANA_PORT_QOS_CFG , port );
552+ pcp = ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL_X (val );
553+ dei = !!(val & ANA_PORT_QOS_CFG_DP_DEFAULT_VAL );
554+
555+ if (filter ) {
556+ bool changed = false;
557+
558+ /* Filter exists, just update it */
559+ if (filter -> action .vid != vid ) {
560+ filter -> action .vid = vid ;
561+ changed = true;
562+ }
563+ if (filter -> action .pcp != pcp ) {
564+ filter -> action .pcp = pcp ;
565+ changed = true;
566+ }
567+ if (filter -> action .dei != dei ) {
568+ filter -> action .dei = dei ;
569+ changed = true;
570+ }
571+
572+ if (!changed )
573+ return 0 ;
574+
575+ return ocelot_vcap_filter_replace (ocelot , filter );
576+ }
577+
578+ /* Filter doesn't exist, create it */
579+ filter = kzalloc (sizeof (* filter ), GFP_KERNEL );
580+ if (!filter )
581+ return - ENOMEM ;
582+
583+ filter -> key_type = OCELOT_VCAP_KEY_ANY ;
584+ filter -> ingress_port_mask = BIT (port );
585+ filter -> vlan .tpid = OCELOT_VCAP_BIT_1 ;
586+ filter -> prio = 1 ;
587+ filter -> id .cookie = cookie ;
588+ filter -> id .tc_offload = false;
589+ filter -> block_id = VCAP_IS1 ;
590+ filter -> type = OCELOT_VCAP_FILTER_OFFLOAD ;
591+ filter -> lookup = 0 ;
592+ filter -> action .vid_replace_ena = true;
593+ filter -> action .pcp_dei_ena = true;
594+ filter -> action .vid = vid ;
595+ filter -> action .pcp = pcp ;
596+ filter -> action .dei = dei ;
597+
598+ err = ocelot_vcap_filter_add (ocelot , filter , NULL );
599+ if (err )
600+ kfree (filter );
601+
602+ return err ;
603+ }
604+
456605/* Default vlan to clasify for untagged frames (may be zero) */
457- static void ocelot_port_set_pvid (struct ocelot * ocelot , int port ,
458- const struct ocelot_bridge_vlan * pvid_vlan )
606+ static int ocelot_port_set_pvid (struct ocelot * ocelot , int port ,
607+ const struct ocelot_bridge_vlan * pvid_vlan )
459608{
460609 struct ocelot_port * ocelot_port = ocelot -> ports [port ];
461610 u16 pvid = ocelot_vlan_unaware_pvid (ocelot , ocelot_port -> bridge );
@@ -475,15 +624,23 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
475624 * happens automatically), but also 802.1p traffic which gets
476625 * classified to VLAN 0, but that is always in our RX filter, so it
477626 * would get accepted were it not for this setting.
627+ *
628+ * Also, we only support the bridge 802.1Q VLAN protocol, so
629+ * 802.1ad-tagged frames (carrying S-Tags) should be considered
630+ * 802.1Q-untagged, and also dropped.
478631 */
479632 if (!pvid_vlan && ocelot_port -> vlan_aware )
480633 val = ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
481- ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA ;
634+ ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA |
635+ ANA_PORT_DROP_CFG_DROP_S_TAGGED_ENA ;
482636
483637 ocelot_rmw_gix (ocelot , val ,
484638 ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
485- ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA ,
639+ ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA |
640+ ANA_PORT_DROP_CFG_DROP_S_TAGGED_ENA ,
486641 ANA_PORT_DROP_CFG , port );
642+
643+ return ocelot_update_vlan_reclassify_rule (ocelot , port );
487644}
488645
489646static struct ocelot_bridge_vlan * ocelot_bridge_vlan_find (struct ocelot * ocelot ,
@@ -631,7 +788,10 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
631788 ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M ,
632789 ANA_PORT_VLAN_CFG , port );
633790
634- ocelot_port_set_pvid (ocelot , port , ocelot_port -> pvid_vlan );
791+ err = ocelot_port_set_pvid (ocelot , port , ocelot_port -> pvid_vlan );
792+ if (err )
793+ return err ;
794+
635795 ocelot_port_manage_port_tag (ocelot , port );
636796
637797 return 0 ;
@@ -684,9 +844,12 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
684844 return err ;
685845
686846 /* Default ingress vlan classification */
687- if (pvid )
688- ocelot_port_set_pvid (ocelot , port ,
689- ocelot_bridge_vlan_find (ocelot , vid ));
847+ if (pvid ) {
848+ err = ocelot_port_set_pvid (ocelot , port ,
849+ ocelot_bridge_vlan_find (ocelot , vid ));
850+ if (err )
851+ return err ;
852+ }
690853
691854 /* Untagged egress vlan clasification */
692855 ocelot_port_manage_port_tag (ocelot , port );
@@ -712,8 +875,11 @@ int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
712875 return err ;
713876
714877 /* Ingress */
715- if (del_pvid )
716- ocelot_port_set_pvid (ocelot , port , NULL );
878+ if (del_pvid ) {
879+ err = ocelot_port_set_pvid (ocelot , port , NULL );
880+ if (err )
881+ return err ;
882+ }
717883
718884 /* Egress */
719885 ocelot_port_manage_port_tag (ocelot , port );
@@ -2607,7 +2773,7 @@ int ocelot_port_set_default_prio(struct ocelot *ocelot, int port, u8 prio)
26072773 ANA_PORT_QOS_CFG ,
26082774 port );
26092775
2610- return 0 ;
2776+ return ocelot_update_vlan_reclassify_rule ( ocelot , port ) ;
26112777}
26122778EXPORT_SYMBOL_GPL (ocelot_port_set_default_prio );
26132779
0 commit comments