diff options
Diffstat (limited to 'kernel/rcu/refscale.c')
| -rw-r--r-- | kernel/rcu/refscale.c | 250 | 
1 files changed, 245 insertions, 5 deletions
diff --git a/kernel/rcu/refscale.c b/kernel/rcu/refscale.c index 435c884c02b5..afa3e1a2f690 100644 --- a/kernel/rcu/refscale.c +++ b/kernel/rcu/refscale.c @@ -76,6 +76,8 @@ torture_param(int, verbose_batched, 0, "Batch verbose debugging printk()s");  // Wait until there are multiple CPUs before starting test.  torture_param(int, holdoff, IS_BUILTIN(CONFIG_RCU_REF_SCALE_TEST) ? 10 : 0,  	      "Holdoff time before test start (s)"); +// Number of typesafe_lookup structures, that is, the degree of concurrency. +torture_param(long, lookup_instances, 0, "Number of typesafe_lookup structures.");  // Number of loops per experiment, all readers execute operations concurrently.  torture_param(long, loops, 10000, "Number of loops per experiment.");  // Number of readers, with -1 defaulting to about 75% of the CPUs. @@ -124,7 +126,7 @@ static int exp_idx;  // Operations vector for selecting different types of tests.  struct ref_scale_ops { -	void (*init)(void); +	bool (*init)(void);  	void (*cleanup)(void);  	void (*readsection)(const int nloops);  	void (*delaysection)(const int nloops, const int udl, const int ndl); @@ -162,8 +164,9 @@ static void ref_rcu_delay_section(const int nloops, const int udl, const int ndl  	}  } -static void rcu_sync_scale_init(void) +static bool rcu_sync_scale_init(void)  { +	return true;  }  static struct ref_scale_ops rcu_ops = { @@ -315,9 +318,10 @@ static struct ref_scale_ops refcnt_ops = {  // Definitions for rwlock  static rwlock_t test_rwlock; -static void ref_rwlock_init(void) +static bool ref_rwlock_init(void)  {  	rwlock_init(&test_rwlock); +	return true;  }  static void ref_rwlock_section(const int nloops) @@ -351,9 +355,10 @@ static struct ref_scale_ops rwlock_ops = {  // Definitions for rwsem  static struct rw_semaphore test_rwsem; -static void ref_rwsem_init(void) +static bool ref_rwsem_init(void)  {  	init_rwsem(&test_rwsem); +	return true;  }  static void ref_rwsem_section(const int nloops) @@ -523,6 +528,237 @@ static struct ref_scale_ops clock_ops = {  	.name		= "clock"  }; +//////////////////////////////////////////////////////////////////////// +// +// Methods leveraging SLAB_TYPESAFE_BY_RCU. +// + +// Item to look up in a typesafe manner.  Array of pointers to these. +struct refscale_typesafe { +	atomic_t rts_refctr;  // Used by all flavors +	spinlock_t rts_lock; +	seqlock_t rts_seqlock; +	unsigned int a; +	unsigned int b; +}; + +static struct kmem_cache *typesafe_kmem_cachep; +static struct refscale_typesafe **rtsarray; +static long rtsarray_size; +static DEFINE_TORTURE_RANDOM_PERCPU(refscale_rand); +static bool (*rts_acquire)(struct refscale_typesafe *rtsp, unsigned int *start); +static bool (*rts_release)(struct refscale_typesafe *rtsp, unsigned int start); + +// Conditionally acquire an explicit in-structure reference count. +static bool typesafe_ref_acquire(struct refscale_typesafe *rtsp, unsigned int *start) +{ +	return atomic_inc_not_zero(&rtsp->rts_refctr); +} + +// Unconditionally release an explicit in-structure reference count. +static bool typesafe_ref_release(struct refscale_typesafe *rtsp, unsigned int start) +{ +	if (!atomic_dec_return(&rtsp->rts_refctr)) { +		WRITE_ONCE(rtsp->a, rtsp->a + 1); +		kmem_cache_free(typesafe_kmem_cachep, rtsp); +	} +	return true; +} + +// Unconditionally acquire an explicit in-structure spinlock. +static bool typesafe_lock_acquire(struct refscale_typesafe *rtsp, unsigned int *start) +{ +	spin_lock(&rtsp->rts_lock); +	return true; +} + +// Unconditionally release an explicit in-structure spinlock. +static bool typesafe_lock_release(struct refscale_typesafe *rtsp, unsigned int start) +{ +	spin_unlock(&rtsp->rts_lock); +	return true; +} + +// Unconditionally acquire an explicit in-structure sequence lock. +static bool typesafe_seqlock_acquire(struct refscale_typesafe *rtsp, unsigned int *start) +{ +	*start = read_seqbegin(&rtsp->rts_seqlock); +	return true; +} + +// Conditionally release an explicit in-structure sequence lock.  Return +// true if this release was successful, that is, if no retry is required. +static bool typesafe_seqlock_release(struct refscale_typesafe *rtsp, unsigned int start) +{ +	return !read_seqretry(&rtsp->rts_seqlock, start); +} + +// Do a read-side critical section with the specified delay in +// microseconds and nanoseconds inserted so as to increase probability +// of failure. +static void typesafe_delay_section(const int nloops, const int udl, const int ndl) +{ +	unsigned int a; +	unsigned int b; +	int i; +	long idx; +	struct refscale_typesafe *rtsp; +	unsigned int start; + +	for (i = nloops; i >= 0; i--) { +		preempt_disable(); +		idx = torture_random(this_cpu_ptr(&refscale_rand)) % rtsarray_size; +		preempt_enable(); +retry: +		rcu_read_lock(); +		rtsp = rcu_dereference(rtsarray[idx]); +		a = READ_ONCE(rtsp->a); +		if (!rts_acquire(rtsp, &start)) { +			rcu_read_unlock(); +			goto retry; +		} +		if (a != READ_ONCE(rtsp->a)) { +			(void)rts_release(rtsp, start); +			rcu_read_unlock(); +			goto retry; +		} +		un_delay(udl, ndl); +		// Remember, seqlock read-side release can fail. +		if (!rts_release(rtsp, start)) { +			rcu_read_unlock(); +			goto retry; +		} +		b = READ_ONCE(rtsp->a); +		WARN_ONCE(a != b, "Re-read of ->a changed from %u to %u.\n", a, b); +		b = rtsp->b; +		rcu_read_unlock(); +		WARN_ON_ONCE(a * a != b); +	} +} + +// Because the acquisition and release methods are expensive, there +// is no point in optimizing away the un_delay() function's two checks. +// Thus simply define typesafe_read_section() as a simple wrapper around +// typesafe_delay_section(). +static void typesafe_read_section(const int nloops) +{ +	typesafe_delay_section(nloops, 0, 0); +} + +// Allocate and initialize one refscale_typesafe structure. +static struct refscale_typesafe *typesafe_alloc_one(void) +{ +	struct refscale_typesafe *rtsp; + +	rtsp = kmem_cache_alloc(typesafe_kmem_cachep, GFP_KERNEL); +	if (!rtsp) +		return NULL; +	atomic_set(&rtsp->rts_refctr, 1); +	WRITE_ONCE(rtsp->a, rtsp->a + 1); +	WRITE_ONCE(rtsp->b, rtsp->a * rtsp->a); +	return rtsp; +} + +// Slab-allocator constructor for refscale_typesafe structures created +// out of a new slab of system memory. +static void refscale_typesafe_ctor(void *rtsp_in) +{ +	struct refscale_typesafe *rtsp = rtsp_in; + +	spin_lock_init(&rtsp->rts_lock); +	seqlock_init(&rtsp->rts_seqlock); +	preempt_disable(); +	rtsp->a = torture_random(this_cpu_ptr(&refscale_rand)); +	preempt_enable(); +} + +static struct ref_scale_ops typesafe_ref_ops; +static struct ref_scale_ops typesafe_lock_ops; +static struct ref_scale_ops typesafe_seqlock_ops; + +// Initialize for a typesafe test. +static bool typesafe_init(void) +{ +	long idx; +	long si = lookup_instances; + +	typesafe_kmem_cachep = kmem_cache_create("refscale_typesafe", +						 sizeof(struct refscale_typesafe), sizeof(void *), +						 SLAB_TYPESAFE_BY_RCU, refscale_typesafe_ctor); +	if (!typesafe_kmem_cachep) +		return false; +	if (si < 0) +		si = -si * nr_cpu_ids; +	else if (si == 0) +		si = nr_cpu_ids; +	rtsarray_size = si; +	rtsarray = kcalloc(si, sizeof(*rtsarray), GFP_KERNEL); +	if (!rtsarray) +		return false; +	for (idx = 0; idx < rtsarray_size; idx++) { +		rtsarray[idx] = typesafe_alloc_one(); +		if (!rtsarray[idx]) +			return false; +	} +	if (cur_ops == &typesafe_ref_ops) { +		rts_acquire = typesafe_ref_acquire; +		rts_release = typesafe_ref_release; +	} else if (cur_ops == &typesafe_lock_ops) { +		rts_acquire = typesafe_lock_acquire; +		rts_release = typesafe_lock_release; +	} else if (cur_ops == &typesafe_seqlock_ops) { +		rts_acquire = typesafe_seqlock_acquire; +		rts_release = typesafe_seqlock_release; +	} else { +		WARN_ON_ONCE(1); +		return false; +	} +	return true; +} + +// Clean up after a typesafe test. +static void typesafe_cleanup(void) +{ +	long idx; + +	if (rtsarray) { +		for (idx = 0; idx < rtsarray_size; idx++) +			kmem_cache_free(typesafe_kmem_cachep, rtsarray[idx]); +		kfree(rtsarray); +		rtsarray = NULL; +		rtsarray_size = 0; +	} +	kmem_cache_destroy(typesafe_kmem_cachep); +	typesafe_kmem_cachep = NULL; +	rts_acquire = NULL; +	rts_release = NULL; +} + +// The typesafe_init() function distinguishes these structures by address. +static struct ref_scale_ops typesafe_ref_ops = { +	.init		= typesafe_init, +	.cleanup	= typesafe_cleanup, +	.readsection	= typesafe_read_section, +	.delaysection	= typesafe_delay_section, +	.name		= "typesafe_ref" +}; + +static struct ref_scale_ops typesafe_lock_ops = { +	.init		= typesafe_init, +	.cleanup	= typesafe_cleanup, +	.readsection	= typesafe_read_section, +	.delaysection	= typesafe_delay_section, +	.name		= "typesafe_lock" +}; + +static struct ref_scale_ops typesafe_seqlock_ops = { +	.init		= typesafe_init, +	.cleanup	= typesafe_cleanup, +	.readsection	= typesafe_read_section, +	.delaysection	= typesafe_delay_section, +	.name		= "typesafe_seqlock" +}; +  static void rcu_scale_one_reader(void)  {  	if (readdelay <= 0) @@ -812,6 +1048,7 @@ ref_scale_init(void)  	static struct ref_scale_ops *scale_ops[] = {  		&rcu_ops, &srcu_ops, RCU_TRACE_OPS RCU_TASKS_OPS &refcnt_ops, &rwlock_ops,  		&rwsem_ops, &lock_ops, &lock_irq_ops, &acqrel_ops, &clock_ops, +		&typesafe_ref_ops, &typesafe_lock_ops, &typesafe_seqlock_ops,  	};  	if (!torture_init_begin(scale_type, verbose)) @@ -833,7 +1070,10 @@ ref_scale_init(void)  		goto unwind;  	}  	if (cur_ops->init) -		cur_ops->init(); +		if (!cur_ops->init()) { +			firsterr = -EUCLEAN; +			goto unwind; +		}  	ref_scale_print_module_parms(cur_ops, "Start of test");  | 
