1
1
// SPDX-License-Identifier: GPL-2.0
2
2
#include <test_progs.h>
3
+ #include <network_helpers.h>
3
4
4
5
/* test_tailcall_1 checks basic functionality by patching multiple locations
5
6
* in a single program for a single tail call slot with nop->jmp, jmp->nop
@@ -472,6 +473,329 @@ static void test_tailcall_5(void)
472
473
bpf_object__close (obj );
473
474
}
474
475
476
+ /* test_tailcall_bpf2bpf_1 purpose is to make sure that tailcalls are working
477
+ * correctly in correlation with BPF subprograms
478
+ */
479
+ static void test_tailcall_bpf2bpf_1 (void )
480
+ {
481
+ int err , map_fd , prog_fd , main_fd , i ;
482
+ struct bpf_map * prog_array ;
483
+ struct bpf_program * prog ;
484
+ struct bpf_object * obj ;
485
+ __u32 retval , duration ;
486
+ char prog_name [32 ];
487
+
488
+ err = bpf_prog_load ("tailcall_bpf2bpf1.o" , BPF_PROG_TYPE_SCHED_CLS ,
489
+ & obj , & prog_fd );
490
+ if (CHECK_FAIL (err ))
491
+ return ;
492
+
493
+ prog = bpf_object__find_program_by_title (obj , "classifier" );
494
+ if (CHECK_FAIL (!prog ))
495
+ goto out ;
496
+
497
+ main_fd = bpf_program__fd (prog );
498
+ if (CHECK_FAIL (main_fd < 0 ))
499
+ goto out ;
500
+
501
+ prog_array = bpf_object__find_map_by_name (obj , "jmp_table" );
502
+ if (CHECK_FAIL (!prog_array ))
503
+ goto out ;
504
+
505
+ map_fd = bpf_map__fd (prog_array );
506
+ if (CHECK_FAIL (map_fd < 0 ))
507
+ goto out ;
508
+
509
+ /* nop -> jmp */
510
+ for (i = 0 ; i < bpf_map__def (prog_array )-> max_entries ; i ++ ) {
511
+ snprintf (prog_name , sizeof (prog_name ), "classifier/%i" , i );
512
+
513
+ prog = bpf_object__find_program_by_title (obj , prog_name );
514
+ if (CHECK_FAIL (!prog ))
515
+ goto out ;
516
+
517
+ prog_fd = bpf_program__fd (prog );
518
+ if (CHECK_FAIL (prog_fd < 0 ))
519
+ goto out ;
520
+
521
+ err = bpf_map_update_elem (map_fd , & i , & prog_fd , BPF_ANY );
522
+ if (CHECK_FAIL (err ))
523
+ goto out ;
524
+ }
525
+
526
+ err = bpf_prog_test_run (main_fd , 1 , & pkt_v4 , sizeof (pkt_v4 ), 0 ,
527
+ 0 , & retval , & duration );
528
+ CHECK (err || retval != 1 , "tailcall" ,
529
+ "err %d errno %d retval %d\n" , err , errno , retval );
530
+
531
+ /* jmp -> nop, call subprog that will do tailcall */
532
+ i = 1 ;
533
+ err = bpf_map_delete_elem (map_fd , & i );
534
+ if (CHECK_FAIL (err ))
535
+ goto out ;
536
+
537
+ err = bpf_prog_test_run (main_fd , 1 , & pkt_v4 , sizeof (pkt_v4 ), 0 ,
538
+ 0 , & retval , & duration );
539
+ CHECK (err || retval != 0 , "tailcall" , "err %d errno %d retval %d\n" ,
540
+ err , errno , retval );
541
+
542
+ /* make sure that subprog can access ctx and entry prog that
543
+ * called this subprog can properly return
544
+ */
545
+ i = 0 ;
546
+ err = bpf_map_delete_elem (map_fd , & i );
547
+ if (CHECK_FAIL (err ))
548
+ goto out ;
549
+
550
+ err = bpf_prog_test_run (main_fd , 1 , & pkt_v4 , sizeof (pkt_v4 ), 0 ,
551
+ 0 , & retval , & duration );
552
+ CHECK (err || retval != sizeof (pkt_v4 ) * 2 ,
553
+ "tailcall" , "err %d errno %d retval %d\n" ,
554
+ err , errno , retval );
555
+ out :
556
+ bpf_object__close (obj );
557
+ }
558
+
559
+ /* test_tailcall_bpf2bpf_2 checks that the count value of the tail call limit
560
+ * enforcement matches with expectations when tailcall is preceded with
561
+ * bpf2bpf call.
562
+ */
563
+ static void test_tailcall_bpf2bpf_2 (void )
564
+ {
565
+ int err , map_fd , prog_fd , main_fd , data_fd , i , val ;
566
+ struct bpf_map * prog_array , * data_map ;
567
+ struct bpf_program * prog ;
568
+ struct bpf_object * obj ;
569
+ __u32 retval , duration ;
570
+ char buff [128 ] = {};
571
+
572
+ err = bpf_prog_load ("tailcall_bpf2bpf2.o" , BPF_PROG_TYPE_SCHED_CLS ,
573
+ & obj , & prog_fd );
574
+ if (CHECK_FAIL (err ))
575
+ return ;
576
+
577
+ prog = bpf_object__find_program_by_title (obj , "classifier" );
578
+ if (CHECK_FAIL (!prog ))
579
+ goto out ;
580
+
581
+ main_fd = bpf_program__fd (prog );
582
+ if (CHECK_FAIL (main_fd < 0 ))
583
+ goto out ;
584
+
585
+ prog_array = bpf_object__find_map_by_name (obj , "jmp_table" );
586
+ if (CHECK_FAIL (!prog_array ))
587
+ goto out ;
588
+
589
+ map_fd = bpf_map__fd (prog_array );
590
+ if (CHECK_FAIL (map_fd < 0 ))
591
+ goto out ;
592
+
593
+ prog = bpf_object__find_program_by_title (obj , "classifier/0" );
594
+ if (CHECK_FAIL (!prog ))
595
+ goto out ;
596
+
597
+ prog_fd = bpf_program__fd (prog );
598
+ if (CHECK_FAIL (prog_fd < 0 ))
599
+ goto out ;
600
+
601
+ i = 0 ;
602
+ err = bpf_map_update_elem (map_fd , & i , & prog_fd , BPF_ANY );
603
+ if (CHECK_FAIL (err ))
604
+ goto out ;
605
+
606
+ err = bpf_prog_test_run (main_fd , 1 , buff , sizeof (buff ), 0 ,
607
+ & duration , & retval , NULL );
608
+ CHECK (err || retval != 1 , "tailcall" , "err %d errno %d retval %d\n" ,
609
+ err , errno , retval );
610
+
611
+ data_map = bpf_object__find_map_by_name (obj , "tailcall.bss" );
612
+ if (CHECK_FAIL (!data_map || !bpf_map__is_internal (data_map )))
613
+ return ;
614
+
615
+ data_fd = bpf_map__fd (data_map );
616
+ if (CHECK_FAIL (map_fd < 0 ))
617
+ return ;
618
+
619
+ i = 0 ;
620
+ err = bpf_map_lookup_elem (data_fd , & i , & val );
621
+ CHECK (err || val != 33 , "tailcall count" , "err %d errno %d count %d\n" ,
622
+ err , errno , val );
623
+
624
+ i = 0 ;
625
+ err = bpf_map_delete_elem (map_fd , & i );
626
+ if (CHECK_FAIL (err ))
627
+ goto out ;
628
+
629
+ err = bpf_prog_test_run (main_fd , 1 , buff , sizeof (buff ), 0 ,
630
+ & duration , & retval , NULL );
631
+ CHECK (err || retval != 0 , "tailcall" , "err %d errno %d retval %d\n" ,
632
+ err , errno , retval );
633
+ out :
634
+ bpf_object__close (obj );
635
+ }
636
+
637
+ /* test_tailcall_bpf2bpf_3 checks that non-trivial amount of stack (up to
638
+ * 256 bytes) can be used within bpf subprograms that have the tailcalls
639
+ * in them
640
+ */
641
+ static void test_tailcall_bpf2bpf_3 (void )
642
+ {
643
+ int err , map_fd , prog_fd , main_fd , i ;
644
+ struct bpf_map * prog_array ;
645
+ struct bpf_program * prog ;
646
+ struct bpf_object * obj ;
647
+ __u32 retval , duration ;
648
+ char prog_name [32 ];
649
+
650
+ err = bpf_prog_load ("tailcall_bpf2bpf3.o" , BPF_PROG_TYPE_SCHED_CLS ,
651
+ & obj , & prog_fd );
652
+ if (CHECK_FAIL (err ))
653
+ return ;
654
+
655
+ prog = bpf_object__find_program_by_title (obj , "classifier" );
656
+ if (CHECK_FAIL (!prog ))
657
+ goto out ;
658
+
659
+ main_fd = bpf_program__fd (prog );
660
+ if (CHECK_FAIL (main_fd < 0 ))
661
+ goto out ;
662
+
663
+ prog_array = bpf_object__find_map_by_name (obj , "jmp_table" );
664
+ if (CHECK_FAIL (!prog_array ))
665
+ goto out ;
666
+
667
+ map_fd = bpf_map__fd (prog_array );
668
+ if (CHECK_FAIL (map_fd < 0 ))
669
+ goto out ;
670
+
671
+ for (i = 0 ; i < bpf_map__def (prog_array )-> max_entries ; i ++ ) {
672
+ snprintf (prog_name , sizeof (prog_name ), "classifier/%i" , i );
673
+
674
+ prog = bpf_object__find_program_by_title (obj , prog_name );
675
+ if (CHECK_FAIL (!prog ))
676
+ goto out ;
677
+
678
+ prog_fd = bpf_program__fd (prog );
679
+ if (CHECK_FAIL (prog_fd < 0 ))
680
+ goto out ;
681
+
682
+ err = bpf_map_update_elem (map_fd , & i , & prog_fd , BPF_ANY );
683
+ if (CHECK_FAIL (err ))
684
+ goto out ;
685
+ }
686
+
687
+ err = bpf_prog_test_run (main_fd , 1 , & pkt_v4 , sizeof (pkt_v4 ), 0 ,
688
+ & duration , & retval , NULL );
689
+ CHECK (err || retval != sizeof (pkt_v4 ) * 3 ,
690
+ "tailcall" , "err %d errno %d retval %d\n" ,
691
+ err , errno , retval );
692
+
693
+ i = 1 ;
694
+ err = bpf_map_delete_elem (map_fd , & i );
695
+ if (CHECK_FAIL (err ))
696
+ goto out ;
697
+
698
+ err = bpf_prog_test_run (main_fd , 1 , & pkt_v4 , sizeof (pkt_v4 ), 0 ,
699
+ & duration , & retval , NULL );
700
+ CHECK (err || retval != sizeof (pkt_v4 ),
701
+ "tailcall" , "err %d errno %d retval %d\n" ,
702
+ err , errno , retval );
703
+
704
+ i = 0 ;
705
+ err = bpf_map_delete_elem (map_fd , & i );
706
+ if (CHECK_FAIL (err ))
707
+ goto out ;
708
+
709
+ err = bpf_prog_test_run (main_fd , 1 , & pkt_v4 , sizeof (pkt_v4 ), 0 ,
710
+ & duration , & retval , NULL );
711
+ CHECK (err || retval != sizeof (pkt_v4 ) * 2 ,
712
+ "tailcall" , "err %d errno %d retval %d\n" ,
713
+ err , errno , retval );
714
+ out :
715
+ bpf_object__close (obj );
716
+ }
717
+
718
+ /* test_tailcall_bpf2bpf_4 checks that tailcall counter is correctly preserved
719
+ * across tailcalls combined with bpf2bpf calls. for making sure that tailcall
720
+ * counter behaves correctly, bpf program will go through following flow:
721
+ *
722
+ * entry -> entry_subprog -> tailcall0 -> bpf_func0 -> subprog0 ->
723
+ * -> tailcall1 -> bpf_func1 -> subprog1 -> tailcall2 -> bpf_func2 ->
724
+ * subprog2 [here bump global counter] --------^
725
+ *
726
+ * We go through first two tailcalls and start counting from the subprog2 where
727
+ * the loop begins. At the end of the test make sure that the global counter is
728
+ * equal to 31, because tailcall counter includes the first two tailcalls
729
+ * whereas global counter is incremented only on loop presented on flow above.
730
+ */
731
+ static void test_tailcall_bpf2bpf_4 (void )
732
+ {
733
+ int err , map_fd , prog_fd , main_fd , data_fd , i , val ;
734
+ struct bpf_map * prog_array , * data_map ;
735
+ struct bpf_program * prog ;
736
+ struct bpf_object * obj ;
737
+ __u32 retval , duration ;
738
+ char prog_name [32 ];
739
+
740
+ err = bpf_prog_load ("tailcall_bpf2bpf4.o" , BPF_PROG_TYPE_SCHED_CLS ,
741
+ & obj , & prog_fd );
742
+ if (CHECK_FAIL (err ))
743
+ return ;
744
+
745
+ prog = bpf_object__find_program_by_title (obj , "classifier" );
746
+ if (CHECK_FAIL (!prog ))
747
+ goto out ;
748
+
749
+ main_fd = bpf_program__fd (prog );
750
+ if (CHECK_FAIL (main_fd < 0 ))
751
+ goto out ;
752
+
753
+ prog_array = bpf_object__find_map_by_name (obj , "jmp_table" );
754
+ if (CHECK_FAIL (!prog_array ))
755
+ goto out ;
756
+
757
+ map_fd = bpf_map__fd (prog_array );
758
+ if (CHECK_FAIL (map_fd < 0 ))
759
+ goto out ;
760
+
761
+ for (i = 0 ; i < bpf_map__def (prog_array )-> max_entries ; i ++ ) {
762
+ snprintf (prog_name , sizeof (prog_name ), "classifier/%i" , i );
763
+
764
+ prog = bpf_object__find_program_by_title (obj , prog_name );
765
+ if (CHECK_FAIL (!prog ))
766
+ goto out ;
767
+
768
+ prog_fd = bpf_program__fd (prog );
769
+ if (CHECK_FAIL (prog_fd < 0 ))
770
+ goto out ;
771
+
772
+ err = bpf_map_update_elem (map_fd , & i , & prog_fd , BPF_ANY );
773
+ if (CHECK_FAIL (err ))
774
+ goto out ;
775
+ }
776
+
777
+ err = bpf_prog_test_run (main_fd , 1 , & pkt_v4 , sizeof (pkt_v4 ), 0 ,
778
+ & duration , & retval , NULL );
779
+ CHECK (err || retval != sizeof (pkt_v4 ) * 3 , "tailcall" , "err %d errno %d retval %d\n" ,
780
+ err , errno , retval );
781
+
782
+ data_map = bpf_object__find_map_by_name (obj , "tailcall.bss" );
783
+ if (CHECK_FAIL (!data_map || !bpf_map__is_internal (data_map )))
784
+ return ;
785
+
786
+ data_fd = bpf_map__fd (data_map );
787
+ if (CHECK_FAIL (map_fd < 0 ))
788
+ return ;
789
+
790
+ i = 0 ;
791
+ err = bpf_map_lookup_elem (data_fd , & i , & val );
792
+ CHECK (err || val != 31 , "tailcall count" , "err %d errno %d count %d\n" ,
793
+ err , errno , val );
794
+
795
+ out :
796
+ bpf_object__close (obj );
797
+ }
798
+
475
799
void test_tailcalls (void )
476
800
{
477
801
if (test__start_subtest ("tailcall_1" ))
@@ -484,4 +808,12 @@ void test_tailcalls(void)
484
808
test_tailcall_4 ();
485
809
if (test__start_subtest ("tailcall_5" ))
486
810
test_tailcall_5 ();
811
+ if (test__start_subtest ("tailcall_bpf2bpf_1" ))
812
+ test_tailcall_bpf2bpf_1 ();
813
+ if (test__start_subtest ("tailcall_bpf2bpf_2" ))
814
+ test_tailcall_bpf2bpf_2 ();
815
+ if (test__start_subtest ("tailcall_bpf2bpf_3" ))
816
+ test_tailcall_bpf2bpf_3 ();
817
+ if (test__start_subtest ("tailcall_bpf2bpf_4" ))
818
+ test_tailcall_bpf2bpf_4 ();
487
819
}
0 commit comments