@@ -76,6 +76,8 @@ torture_param(int, verbose_batched, 0, "Batch verbose debugging printk()s");
76
76
// Wait until there are multiple CPUs before starting test.
77
77
torture_param (int , holdoff , IS_BUILTIN (CONFIG_RCU_REF_SCALE_TEST ) ? 10 : 0 ,
78
78
"Holdoff time before test start (s)" );
79
+ // Number of typesafe_lookup structures, that is, the degree of concurrency.
80
+ torture_param (long , lookup_instances , 0 , "Number of typesafe_lookup structures." );
79
81
// Number of loops per experiment, all readers execute operations concurrently.
80
82
torture_param (long , loops , 10000 , "Number of loops per experiment." );
81
83
// Number of readers, with -1 defaulting to about 75% of the CPUs.
@@ -526,6 +528,237 @@ static struct ref_scale_ops clock_ops = {
526
528
.name = "clock"
527
529
};
528
530
531
+ ////////////////////////////////////////////////////////////////////////
532
+ //
533
+ // Methods leveraging SLAB_TYPESAFE_BY_RCU.
534
+ //
535
+
536
+ // Item to look up in a typesafe manner. Array of pointers to these.
537
+ struct refscale_typesafe {
538
+ atomic_t rts_refctr ; // Used by all flavors
539
+ spinlock_t rts_lock ;
540
+ seqlock_t rts_seqlock ;
541
+ unsigned int a ;
542
+ unsigned int b ;
543
+ };
544
+
545
+ static struct kmem_cache * typesafe_kmem_cachep ;
546
+ static struct refscale_typesafe * * rtsarray ;
547
+ static long rtsarray_size ;
548
+ static DEFINE_TORTURE_RANDOM_PERCPU (refscale_rand );
549
+ static bool (* rts_acquire )(struct refscale_typesafe * rtsp , unsigned int * start );
550
+ static bool (* rts_release )(struct refscale_typesafe * rtsp , unsigned int start );
551
+
552
+ // Conditionally acquire an explicit in-structure reference count.
553
+ static bool typesafe_ref_acquire (struct refscale_typesafe * rtsp , unsigned int * start )
554
+ {
555
+ return atomic_inc_not_zero (& rtsp -> rts_refctr );
556
+ }
557
+
558
+ // Unconditionally release an explicit in-structure reference count.
559
+ static bool typesafe_ref_release (struct refscale_typesafe * rtsp , unsigned int start )
560
+ {
561
+ if (!atomic_dec_return (& rtsp -> rts_refctr )) {
562
+ WRITE_ONCE (rtsp -> a , rtsp -> a + 1 );
563
+ kmem_cache_free (typesafe_kmem_cachep , rtsp );
564
+ }
565
+ return true;
566
+ }
567
+
568
+ // Unconditionally acquire an explicit in-structure spinlock.
569
+ static bool typesafe_lock_acquire (struct refscale_typesafe * rtsp , unsigned int * start )
570
+ {
571
+ spin_lock (& rtsp -> rts_lock );
572
+ return true;
573
+ }
574
+
575
+ // Unconditionally release an explicit in-structure spinlock.
576
+ static bool typesafe_lock_release (struct refscale_typesafe * rtsp , unsigned int start )
577
+ {
578
+ spin_unlock (& rtsp -> rts_lock );
579
+ return true;
580
+ }
581
+
582
+ // Unconditionally acquire an explicit in-structure sequence lock.
583
+ static bool typesafe_seqlock_acquire (struct refscale_typesafe * rtsp , unsigned int * start )
584
+ {
585
+ * start = read_seqbegin (& rtsp -> rts_seqlock );
586
+ return true;
587
+ }
588
+
589
+ // Conditionally release an explicit in-structure sequence lock. Return
590
+ // true if this release was successful, that is, if no retry is required.
591
+ static bool typesafe_seqlock_release (struct refscale_typesafe * rtsp , unsigned int start )
592
+ {
593
+ return !read_seqretry (& rtsp -> rts_seqlock , start );
594
+ }
595
+
596
+ // Do a read-side critical section with the specified delay in
597
+ // microseconds and nanoseconds inserted so as to increase probability
598
+ // of failure.
599
+ static void typesafe_delay_section (const int nloops , const int udl , const int ndl )
600
+ {
601
+ unsigned int a ;
602
+ unsigned int b ;
603
+ int i ;
604
+ long idx ;
605
+ struct refscale_typesafe * rtsp ;
606
+ unsigned int start ;
607
+
608
+ for (i = nloops ; i >= 0 ; i -- ) {
609
+ preempt_disable ();
610
+ idx = torture_random (this_cpu_ptr (& refscale_rand )) % rtsarray_size ;
611
+ preempt_enable ();
612
+ retry :
613
+ rcu_read_lock ();
614
+ rtsp = rcu_dereference (rtsarray [idx ]);
615
+ a = READ_ONCE (rtsp -> a );
616
+ if (!rts_acquire (rtsp , & start )) {
617
+ rcu_read_unlock ();
618
+ goto retry ;
619
+ }
620
+ if (a != READ_ONCE (rtsp -> a )) {
621
+ (void )rts_release (rtsp , start );
622
+ rcu_read_unlock ();
623
+ goto retry ;
624
+ }
625
+ un_delay (udl , ndl );
626
+ // Remember, seqlock read-side release can fail.
627
+ if (!rts_release (rtsp , start )) {
628
+ rcu_read_unlock ();
629
+ goto retry ;
630
+ }
631
+ b = READ_ONCE (rtsp -> a );
632
+ WARN_ONCE (a != b , "Re-read of ->a changed from %u to %u.\n" , a , b );
633
+ b = rtsp -> b ;
634
+ rcu_read_unlock ();
635
+ WARN_ON_ONCE (a * a != b );
636
+ }
637
+ }
638
+
639
+ // Because the acquisition and release methods are expensive, there
640
+ // is no point in optimizing away the un_delay() function's two checks.
641
+ // Thus simply define typesafe_read_section() as a simple wrapper around
642
+ // typesafe_delay_section().
643
+ static void typesafe_read_section (const int nloops )
644
+ {
645
+ typesafe_delay_section (nloops , 0 , 0 );
646
+ }
647
+
648
+ // Allocate and initialize one refscale_typesafe structure.
649
+ static struct refscale_typesafe * typesafe_alloc_one (void )
650
+ {
651
+ struct refscale_typesafe * rtsp ;
652
+
653
+ rtsp = kmem_cache_alloc (typesafe_kmem_cachep , GFP_KERNEL );
654
+ if (!rtsp )
655
+ return NULL ;
656
+ atomic_set (& rtsp -> rts_refctr , 1 );
657
+ WRITE_ONCE (rtsp -> a , rtsp -> a + 1 );
658
+ WRITE_ONCE (rtsp -> b , rtsp -> a * rtsp -> a );
659
+ return rtsp ;
660
+ }
661
+
662
+ // Slab-allocator constructor for refscale_typesafe structures created
663
+ // out of a new slab of system memory.
664
+ static void refscale_typesafe_ctor (void * rtsp_in )
665
+ {
666
+ struct refscale_typesafe * rtsp = rtsp_in ;
667
+
668
+ spin_lock_init (& rtsp -> rts_lock );
669
+ seqlock_init (& rtsp -> rts_seqlock );
670
+ preempt_disable ();
671
+ rtsp -> a = torture_random (this_cpu_ptr (& refscale_rand ));
672
+ preempt_enable ();
673
+ }
674
+
675
+ static struct ref_scale_ops typesafe_ref_ops ;
676
+ static struct ref_scale_ops typesafe_lock_ops ;
677
+ static struct ref_scale_ops typesafe_seqlock_ops ;
678
+
679
+ // Initialize for a typesafe test.
680
+ static bool typesafe_init (void )
681
+ {
682
+ long idx ;
683
+ long si = lookup_instances ;
684
+
685
+ typesafe_kmem_cachep = kmem_cache_create ("refscale_typesafe" ,
686
+ sizeof (struct refscale_typesafe ), sizeof (void * ),
687
+ SLAB_TYPESAFE_BY_RCU , refscale_typesafe_ctor );
688
+ if (!typesafe_kmem_cachep )
689
+ return false;
690
+ if (si < 0 )
691
+ si = - si * nr_cpu_ids ;
692
+ else if (si == 0 )
693
+ si = nr_cpu_ids ;
694
+ rtsarray_size = si ;
695
+ rtsarray = kcalloc (si , sizeof (* rtsarray ), GFP_KERNEL );
696
+ if (!rtsarray )
697
+ return false;
698
+ for (idx = 0 ; idx < rtsarray_size ; idx ++ ) {
699
+ rtsarray [idx ] = typesafe_alloc_one ();
700
+ if (!rtsarray [idx ])
701
+ return false;
702
+ }
703
+ if (cur_ops == & typesafe_ref_ops ) {
704
+ rts_acquire = typesafe_ref_acquire ;
705
+ rts_release = typesafe_ref_release ;
706
+ } else if (cur_ops == & typesafe_lock_ops ) {
707
+ rts_acquire = typesafe_lock_acquire ;
708
+ rts_release = typesafe_lock_release ;
709
+ } else if (cur_ops == & typesafe_seqlock_ops ) {
710
+ rts_acquire = typesafe_seqlock_acquire ;
711
+ rts_release = typesafe_seqlock_release ;
712
+ } else {
713
+ WARN_ON_ONCE (1 );
714
+ return false;
715
+ }
716
+ return true;
717
+ }
718
+
719
+ // Clean up after a typesafe test.
720
+ static void typesafe_cleanup (void )
721
+ {
722
+ long idx ;
723
+
724
+ if (rtsarray ) {
725
+ for (idx = 0 ; idx < rtsarray_size ; idx ++ )
726
+ kmem_cache_free (typesafe_kmem_cachep , rtsarray [idx ]);
727
+ kfree (rtsarray );
728
+ rtsarray = NULL ;
729
+ rtsarray_size = 0 ;
730
+ }
731
+ kmem_cache_destroy (typesafe_kmem_cachep );
732
+ typesafe_kmem_cachep = NULL ;
733
+ rts_acquire = NULL ;
734
+ rts_release = NULL ;
735
+ }
736
+
737
+ // The typesafe_init() function distinguishes these structures by address.
738
+ static struct ref_scale_ops typesafe_ref_ops = {
739
+ .init = typesafe_init ,
740
+ .cleanup = typesafe_cleanup ,
741
+ .readsection = typesafe_read_section ,
742
+ .delaysection = typesafe_delay_section ,
743
+ .name = "typesafe_ref"
744
+ };
745
+
746
+ static struct ref_scale_ops typesafe_lock_ops = {
747
+ .init = typesafe_init ,
748
+ .cleanup = typesafe_cleanup ,
749
+ .readsection = typesafe_read_section ,
750
+ .delaysection = typesafe_delay_section ,
751
+ .name = "typesafe_lock"
752
+ };
753
+
754
+ static struct ref_scale_ops typesafe_seqlock_ops = {
755
+ .init = typesafe_init ,
756
+ .cleanup = typesafe_cleanup ,
757
+ .readsection = typesafe_read_section ,
758
+ .delaysection = typesafe_delay_section ,
759
+ .name = "typesafe_seqlock"
760
+ };
761
+
529
762
static void rcu_scale_one_reader (void )
530
763
{
531
764
if (readdelay <= 0 )
@@ -815,6 +1048,7 @@ ref_scale_init(void)
815
1048
static struct ref_scale_ops * scale_ops [] = {
816
1049
& rcu_ops , & srcu_ops , RCU_TRACE_OPS RCU_TASKS_OPS & refcnt_ops , & rwlock_ops ,
817
1050
& rwsem_ops , & lock_ops , & lock_irq_ops , & acqrel_ops , & clock_ops ,
1051
+ & typesafe_ref_ops , & typesafe_lock_ops , & typesafe_seqlock_ops ,
818
1052
};
819
1053
820
1054
if (!torture_init_begin (scale_type , verbose ))
0 commit comments