Skip to contentSkip to conversation

the random driver

PATCH Andrew Morton 5 participants 7 messages Nov 20, 2002 Random number driver

lore
Share & tools
lore
#0 lore raw

I'm seeing a context switch rate of 150/sec just writing a big file to disk at 20 megabytes/sec.

It is coming out of add_disk_randomness()'s invokation of batch_entropy_store().

That function is setting up deferred punt-to-process-context for every disk request, and has the potential to cause 1000 context switches per second. This is clearly excessive.

There is a 256 slot buffer in the random driver for this, and we are not using it at all effectively. I do intend to submit the below patch which will cause one context switch per 128 requests.

But this is a minimal fix. The batch_entropy_pool handling in there needs work.

a) It's racy. The head and tail pointers have no SMP protection and a race will cause it to dump 128 already-processed items back into the entropy pool.

b) It's weird. What's up with this?

        batch_entropy_pool[2*batch_head] = a;
        batch_entropy_pool[(2*batch_head) + 1] = b;

It should be an array of 2-element structures.

c) The ring-buffer handling is awkward. It shouldn't be masking the head and tail pointers to always remain within bounds.

A better technique is to allow these indices to wrap at 0xffffffff and only mask their values when you actually use them as a subscript. This allows you to distinguish the completely-full case from the completely-empty one. See LOG_BUF* in kernel/printk.c.

d) It's punting work up to process context which could be performed right there in interrupt context.

My suggestion, if anyone cares, is to convert the entropy pool into smaller per-cpu buffers, protected by local_irq_save() only. This way the global lock (which isn't there yet) only needs to be taken when a CPU is actually dumping its buffer.

drivers/char/random.c renamed from drivers/char/random.c~reduce-random-context-switch-rate +1 -1
@@ -663,7 +663,7 @@ void batch_entropy_store(u32 a, u32 b, i
 	batch_entropy_credit[batch_head] = num;
 
 	new = (batch_head+1) & (batch_max-1);
-	if (new != batch_tail) {
+	if ((new - batch_tail) >= batch_max / 2) {
 		/*
 		 * Schedule it for the next timer tick:
 		 */

_

#1 lore raw

Speaking of the random driver, did the entropy calculation fixes ever get merged? Those seemed important.

#3 lore raw
In reply to Andrew Morton

Hi Andrew,

Quoted 8 lines · Andrew Morton

c) The ring-buffer handling is awkward. It shouldn't be masking the head and tail pointers to always remain within bounds.

A better technique is to allow these indices to wrap at 0xffffffff and only mask their values when you actually use them as a subscript. This allows you to distinguish the completely-full case from the completely-empty one. See LOG_BUF* in kernel/printk.c.

Care to implement a generic ringbuffer in a header file, to avoid this kind of errors? I'm sure there are more (and even overflows on some implementations).

The gory implementation details of basic computer science algorithms seem to cause problems for many driver writers.

So the trend of abstracting these away in the kernel, if it costs no time and doesn't lead to ugly code, is right.

Regards

Ingo Oeser

Signature · 1 line
Science is what we can tell a computer. Art is everything else. --- D.E.Knuth
maintainer
#4 lore raw
In reply to Andrew Morton
Quoted 3 lines · Andrew Morton

a) It's racy. The head and tail pointers have no SMP protection and a race will cause it to dump 128 already-processed items back into the entropy pool.

Yeah, that's a real problem. The random driver was never adequately or locked for SMP case. We also have a problem on the output side; two processes that read from /dev/random at the same time can get the exact same value. This is **bad**, especially if it is being used for UUID generation or for session key generation.

The output side SMP locking is on my todo queue, but this week I'm in Atlanta dealing with IPSE Cat the the IETF meeting.... when I get back in Boston next week, I'll look at fixing this, but if someone wants to beat me to it, feel free....

Quoted 6 lines

b) It's weird. What's up with this?

        batch_entropy_pool[2*batch_head] = a;
        batch_entropy_pool[(2*batch_head) + 1] = b;

It should be an array of 2-element structures.

The entropy returned by the drivers is essentially just an arbitrary 64 bit value. It's treated as two 32 bit values so that we don't lose horribly given GCC's pathetic 64-bit code generator for the ia32 platform.

Quoted 2 lines

d) It's punting work up to process context which could be performed right there in interrupt context.

The idea was to trying to pacify the soft realtime nazi's that are stressing out over every single microsecond of interrupt latency. Realistically, it's about dozen memory memory cache misses, so it's not *that* bad. Originally though the batched work was being done in a bottom-half handler, so there wasn't a process context switch overhead. So perhaps we should rethink the design decision of deffering the work in the interests of reducing interrupt latency.

Quoted 4 lines

My suggestion, if anyone cares, is to convert the entropy pool into smaller per-cpu buffers, protected by local_irq_save() only. This way the global lock (which isn't there yet) only needs to be taken when a CPU is actually dumping its buffer.

Yeah, that's probably what we should do.

- Ted

#5 lore raw
Quoted 11 lines · Theodore Ts'o
Quoted 3 lines · Andrew Morton

a) It's racy. The head and tail pointers have no SMP protection and a race will cause it to dump 128 already-processed items back into the entropy pool.

Yeah, that's a real problem. The random driver was never adequately or locked for SMP case. We also have a problem on the output side; two processes that read from /dev/random at the same time can get the exact same value. This is **bad**, especially if it is being used for UUID generation or for session key generation.

It was pointed out (alleged?) to me that the lack of input-side locking is a feature - if the SMP race hits, it adds unpredicatability.

Quoted 6 lines

...

Quoted 5 lines

b) It's weird. What's up with this?

        batch_entropy_pool[2*batch_head] = a;
        batch_entropy_pool[(2*batch_head) + 1] = b;
Quoted 6 lines
Quoted 1 line

It should be an array of 2-element structures.

The entropy returned by the drivers is essentially just an arbitrary 64 bit value. It's treated as two 32 bit values so that we don't lose horribly given GCC's pathetic 64-bit code generator for the ia32 platform.

heh, I see. Presumably u64 loads and stores would be OK though?

Quoted 10 lines
Quoted 2 lines

d) It's punting work up to process context which could be performed right there in interrupt context.

The idea was to trying to pacify the soft realtime nazi's that are stressing out over every single microsecond of interrupt latency. Realistically, it's about dozen memory memory cache misses, so it's not *that* bad. Originally though the batched work was being done in a bottom-half handler, so there wasn't a process context switch overhead. So perhaps we should rethink the design decision of deffering the work in the interests of reducing interrupt latency.

That would suit. If you go this way, the batching is probably detrimental - it would increase peak latencies. Could do the work direct in the interrupt handler or schedule a softirq.

I think what bit us in 2.5 was the HZ=1000 change - with HZ=100 the context switch rate would be lower. But yes, using a workqueue here seems inappropriate.

The whole idea of scheduling the work on the calling CPU is a little inappropriate in this case. I have one CPU working hard and three idle. Yet the deferred work and all the context switching is being performed on the busy CPU. hmm.

#6 lore raw
In reply to Andrew Morton
Quoted 6 lines · Andrew Morton

I'm seeing a context switch rate of 150/sec just writing a big file to disk at 20 megabytes/sec.

It is coming out of add_disk_randomness()'s invokation of batch_entropy_store().

First off, there's very little unobservable randomness in those disk ops, especially in a file/webserver context, so the value of add_disk_randomness is questionable.

Quoted 8 lines

That function is setting up deferred punt-to-process-context for every disk request, and has the potential to cause 1000 context switches per second. This is clearly excessive.

There is a 256 slot buffer in the random driver for this, and we are not using it at all effectively. I do intend to submit the below patch which will cause one context switch per 128 requests.

Done that.

Quoted 6 lines

But this is a minimal fix. The batch_entropy_pool handling in there needs work.

a) It's racy. The head and tail pointers have no SMP protection and a race will cause it to dump 128 already-processed items back into the entropy pool.

I have a rewrite that fails safe and is lockless. But this is nothing compared to the completely broken entropy accounting in xfer_secondary_pool. Try this: cat /dev/random, wait for it to block, and then tap your mouse.

Quoted 2 lines

d) It's punting work up to process context which could be performed right there in interrupt context.

Disagree. Did you look at the mixing function? It'll dirty a large chunk of cache.

Signature · 1 line
 "Love the dolphins," she advised him. "Write by W.A.S.T.E.." 
#2 lore raw
In reply to Aaron Lehmann
Quoted 2 lines · Aaron Lehmann

Speaking of the random driver, did the entropy calculation fixes ever get merged? Those seemed important.

Nope. I don't have a working 2.5 at the moment, will resubmit them when the feature freeze firms up a bit.

Signature · 1 line
 "Love the dolphins," she advised him. "Write by W.A.S.T.E.."