@@ -453,9 +453,158 @@ static u16 ocelot_vlan_unaware_pvid(struct ocelot *ocelot,
453
453
return VLAN_N_VID - bridge_num - 1 ;
454
454
}
455
455
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
+
456
605
/* 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 )
459
608
{
460
609
struct ocelot_port * ocelot_port = ocelot -> ports [port ];
461
610
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,
475
624
* happens automatically), but also 802.1p traffic which gets
476
625
* classified to VLAN 0, but that is always in our RX filter, so it
477
626
* 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.
478
631
*/
479
632
if (!pvid_vlan && ocelot_port -> vlan_aware )
480
633
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 ;
482
636
483
637
ocelot_rmw_gix (ocelot , val ,
484
638
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 ,
486
641
ANA_PORT_DROP_CFG , port );
642
+
643
+ return ocelot_update_vlan_reclassify_rule (ocelot , port );
487
644
}
488
645
489
646
static 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,
631
788
ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M ,
632
789
ANA_PORT_VLAN_CFG , port );
633
790
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
+
635
795
ocelot_port_manage_port_tag (ocelot , port );
636
796
637
797
return 0 ;
@@ -684,9 +844,12 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
684
844
return err ;
685
845
686
846
/* 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
+ }
690
853
691
854
/* Untagged egress vlan clasification */
692
855
ocelot_port_manage_port_tag (ocelot , port );
@@ -712,8 +875,11 @@ int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
712
875
return err ;
713
876
714
877
/* 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
+ }
717
883
718
884
/* Egress */
719
885
ocelot_port_manage_port_tag (ocelot , port );
@@ -2607,7 +2773,7 @@ int ocelot_port_set_default_prio(struct ocelot *ocelot, int port, u8 prio)
2607
2773
ANA_PORT_QOS_CFG ,
2608
2774
port );
2609
2775
2610
- return 0 ;
2776
+ return ocelot_update_vlan_reclassify_rule ( ocelot , port ) ;
2611
2777
}
2612
2778
EXPORT_SYMBOL_GPL (ocelot_port_set_default_prio );
2613
2779
0 commit comments