Re: synchronization in ltt-usertrace
Mathieu Desnoyers <[email protected]>
| Newsgroups | gmane.linux.kernel.tracing |
|---|---|
| Message-ID | <20070211204303.GA5261@Krystal> |
I made a mistake in my attempt to correct the ltt-usertrace-fast tracing:
there was still the following race window : if a signal came between the
offset read and the signal disabling, it could cause a second pass in
the cmpxchg loop which would overwrite the old mask value. When
reenabling signals, it would put back a signal mask that has signals
disabled, which is utterly wrong.
The following patch addresses this issue. It will be in the newer
ltt-usertrace version.
Mathieu
Index: ltt-usertrace-fast.h
===================================================================
--- ltt-usertrace-fast.h (revision 2355)
+++ ltt-usertrace-fast.h (working copy)
@@ -367,6 +367,7 @@
int commit_count, reserve_count;
int ret;
sigset_t oldset, set;
+ int signals_disabled = 0;
do {
offset_old = atomic_read(<t_buf->offset);
@@ -418,12 +419,14 @@
* Signals are kept disabled to make sure we win the cmpxchg. */
/* Disable signals */
- ret = sigfillset(&set);
- if(ret) perror("LTT Error in sigfillset\n");
-
- ret = pthread_sigmask(SIG_BLOCK, &set, &oldset);
- if(ret) perror("LTT Error in pthread_sigmask\n");
-
+ if(!signals_disabled) {
+ ret = sigfillset(&set);
+ if(ret) perror("LTT Error in sigfillset\n");
+
+ ret = pthread_sigmask(SIG_BLOCK, &set, &oldset);
+ if(ret) perror("LTT Error in pthread_sigmask\n");
+ signals_disabled = 1;
+ }
sem_wait(<t_buf->writer_sem);
}
@@ -547,7 +550,7 @@
* sem_wait does in fact win the cmpxchg for the offset. We only call
* these system calls on buffer boundaries because of their performance
* cost. */
- if(reserve_commit_diff == 0) {
+ if(signals_disabled) {
ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
if(ret) perror("LTT Error in pthread_sigmask\n");
}
* Mathieu Desnoyers ([email protected]) wrote:
> Hrm, my solution works, but adds systems calls on the fast path, which
> is precisely what this function shouldn't do.
>
> Here is a new approach : I only disable signals and sem_wait in :
>
> > reserve_slot
> > new subbuffer
> > next subbuffer is not corrupted
>
> within the loop. Later, in the if(begin_switch) outside the cmpxchg
> loop, I reenable them. It makes sure no signal will come in my way while
> still not doing a system call on the fast path.
>
> Here is the patch, applied on top of the previous one :
>
>
> Index: ltt/ltt-usertrace-fast.h
> ===================================================================
> --- ltt/ltt-usertrace-fast.h (revision 2354)
> +++ ltt/ltt-usertrace-fast.h (working copy)
> @@ -368,15 +368,6 @@
> int ret;
> sigset_t oldset, set;
>
> - /* sem_wait is not signal safe. Disable signals around it. */
> -
> - /* Disable signals */
> - ret = sigfillset(&set);
> - if(ret) perror("LTT Error in sigfillset\n");
> -
> - ret = pthread_sigmask(SIG_BLOCK, &set, &oldset);
> - if(ret) perror("LTT Error in pthread_sigmask\n");
> -
> do {
> offset_old = atomic_read(<t_buf->offset);
> offset_begin = offset_old;
> @@ -416,16 +407,26 @@
> ltt_buf)])
> - atomic_read(<t_buf->commit_count[SUBBUF_INDEX(offset_begin,
> ltt_buf)]);
> +
> if(reserve_commit_diff == 0) {
> /* Next buffer not corrupted. */
> //if((SUBBUF_TRUNC(offset_begin, ltt_buf)
> // - SUBBUF_TRUNC(atomic_read(<t_buf->consumed), ltt_buf))
> // >= ltt_buf->alloc_size) {
> {
> + /* sem_wait is not signal safe. Disable signals around it.
> + * Signals are kept disabled to make sure we win the cmpxchg. */
> +
> + /* Disable signals */
> + ret = sigfillset(&set);
> + if(ret) perror("LTT Error in sigfillset\n");
> +
> + ret = pthread_sigmask(SIG_BLOCK, &set, &oldset);
> + if(ret) perror("LTT Error in pthread_sigmask\n");
> +
> sem_wait(<t_buf->writer_sem);
>
> }
> -
> /* go on with the write */
>
> //} else {
> @@ -435,7 +436,8 @@
> } else {
> /* Next subbuffer corrupted. Force pushing reader even in normal
> * mode. It's safe to write in this new subbuffer. */
> - sem_post(<t_buf->writer_sem);
> + /* No sem_post is required because we fall through without doing a
> + * sem_wait. */
> }
> size = ltt_get_header_size(trace, ltt_buf->start + offset_begin,
> before_hdr_pad, after_hdr_pad, header_size) + data_size;
> @@ -443,6 +445,10 @@
> /* Event too big for subbuffers, report error, don't complete
> * the sub-buffer switch. */
> atomic_inc(<t_buf->events_lost);
> + if(reserve_commit_diff == 0) {
> + ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
> + if(ret) perror("LTT Error in pthread_sigmask\n");
> + }
> return NULL;
> } else {
> /* We just made a successful buffer switch and the event fits in the
> @@ -463,10 +469,6 @@
> } while(atomic_cmpxchg(<t_buf->offset, offset_old, offset_end)
> != offset_old);
>
> - /* Enable signals */
> - ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
> - if(ret) perror("LTT Error in pthread_sigmask\n");
> -
> /* Push the reader if necessary */
> do {
> consumed_old = atomic_read(<t_buf->consumed);
> @@ -541,6 +543,14 @@
> }
>
> if(begin_switch) {
> + /* Enable signals : this is what guaranteed that same reserve which did the
> + * sem_wait does in fact win the cmpxchg for the offset. We only call
> + * these system calls on buffer boundaries because of their performance
> + * cost. */
> + if(reserve_commit_diff == 0) {
> + ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
> + if(ret) perror("LTT Error in pthread_sigmask\n");
> + }
> /* New sub-buffer */
> /* This code can be executed unordered : writers may already have written
> to the sub-buffer before this code gets executed, caution. */
>
>
> Regards,
>
> Mathieu
>
>
>
> * Mathieu Desnoyers ([email protected]) wrote:
> > Hi Naoya,
> >
> > Very good catch :)
> >
> > writer_sem is a counter, always positive, which indicates the number of
> > free subbuffers available for writing. Here is how it is used :
> >
> > init writer_sem to the number of subbuffers (indicates the number of
> > subbuffers free for writing)
> >
> > in the writer :
> > reserve_slot
> > new subbuffer
> > next subbuffer is not corrupted
> > wait for a free subbuffer (sem_wait(<t_buf->writer_sem);)
> > next subbuffer is corrupted
> > free the subbuffer ourself (sem_post(<t_buf->writer_sem);)
> >
> > in the reader :
> > ltt_buffer_put
> > if we have been pushed by the writer because it was corrupted
> > return error
> > else, we just successfully read a subbuffer
> > indicate that the subbuffer is free (sem_post)
> >
> >
> > What you pointed out in ltt-usertrace ltt_reserve_slot() is correct :
> > there is a case where it's not signal-safe.
> >
> > To correct it, I propose to disable signals around the cmpxchg loop,
> > like this :
> >
> >
> > Index: ltt-usertrace-fast.h
> > ===================================================================
> > --- ltt-usertrace-fast.h (revision 2076)
> > +++ ltt-usertrace-fast.h (working copy)
> > @@ -366,7 +366,17 @@
> > int consumed_old, consumed_new;
> > int commit_count, reserve_count;
> > int ret;
> > + sigset_t oldset, set;
> >
> > + /* sem_wait is not signal safe. Disable signals around it. */
> > +
> > + /* Disable signals */
> > + ret = sigfillset(&set);
> > + if(ret) perror("LTT Error in sigfillset\n");
> > +
> > + ret = pthread_sigmask(SIG_BLOCK, &set, &oldset);
> > + if(ret) perror("LTT Error in pthread_sigmask\n");
> > +
> > do {
> > offset_old = atomic_read(<t_buf->offset);
> > offset_begin = offset_old;
> > @@ -411,22 +421,9 @@
> > //if((SUBBUF_TRUNC(offset_begin, ltt_buf)
> > // - SUBBUF_TRUNC(atomic_read(<t_buf->consumed), ltt_buf))
> > // >= ltt_buf->alloc_size) {
> > - /* sem_wait is not signal safe. Disable signals around it. */
> > {
> > - sigset_t oldset, set;
> > -
> > - /* Disable signals */
> > - ret = sigfillset(&set);
> > - if(ret) perror("LTT Error in sigfillset\n");
> > -
> > - ret = pthread_sigmask(SIG_BLOCK, &set, &oldset);
> > - if(ret) perror("LTT Error in pthread_sigmask\n");
> > -
> > sem_wait(<t_buf->writer_sem);
> >
> > - /* Enable signals */
> > - ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
> > - if(ret) perror("LTT Error in pthread_sigmask\n");
> > }
> >
> > /* go on with the write */
> > @@ -466,6 +463,9 @@
> > } while(atomic_cmpxchg(<t_buf->offset, offset_old, offset_end)
> > != offset_old);
> >
> > + /* Enable signals */
> > + ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
> > + if(ret) perror("LTT Error in pthread_sigmask\n");
> >
> > /* Push the reader if necessary */
> > do {
> >
> > What do you think of this fix ?
> >
> > Regards,
> >
> > Mathieu
> >
> >
> > * Naoya Maruyama ([email protected]) wrote:
> > > Hello,
> > >
> > > I'm currently walking through the usertrace code, and am confused by a
> > > couple of code sections of the "fast" tracer.
> > >
> > > My question is the use of a semaphore in ltt_reserve_slot function in
> > > ltt-usertrae/ltt/ltt-usertrace-fast.h. I don't fully understand the
> > > entire code yet, but I'm thinking that it's not signal-safe in the
> > > following condition. I would appreciate if anyone could tell me
> > > whether my understanding is correct or not.
> > >
> > > 1. Let's say the writer has just called reserve_slot and the
> > > reservation starts at a new subbuffer.
> > >
> > > 2. Assuming the subbuffer is not corrupted, the writer will call
> > > sem_wait.
> > >
> > > 3. The write successfully returns from sem_wait, but is preempted by a
> > > signal after the signals being re-enabled, but before the cmpxchg.
> > >
> > > 4. The handler for the signal could call reserve_slot, find the new
> > > slot starts at the beginning of the next subbuffer, because the writer
> > > does not yet update the value of offset field.
> > >
> > > So, if the above steps happened, sem_wait would be called twice, even
> > > though the writer and its signal handler would eventually use the same
> > > subbuffer. If my understanding is correct, the semaphore is
> > > initialized to the number of the subbuffers. If it's true, it wouldn't
> > > make sense to call sem_wait multiple times for a single subbuffer,
> > > right?
> > >
> > > BTW, in the same function (reserve_slot in ltt-usertrace-fast.h), I
> > > don't yet understand why sem_post is called when the next subbuffer is
> > > corrupted, which might explain why I don't understand the call to
> > > sem_wait.
> > >
> > > Thanks very much in advance,
> > >
> > > Naoya Maruyama
> > > _______________________________________________
> > > Ltt-dev mailing list
> > > [email protected]
> > > http://listserv.shafik.org/mailman/listinfo/ltt-dev
> > >
> >
> > --
> > OpenPGP public key: http://krystal.dyndns.org:8080/key/compudj.gpg
> > Key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
> > _______________________________________________
> > Ltt-dev mailing list
> > [email protected]
> > http://listserv.shafik.org/mailman/listinfo/ltt-dev
> >
>
> --
> OpenPGP public key: http://krystal.dyndns.org:8080/key/compudj.gpg
> Key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
> _______________________________________________
> Ltt-dev mailing list
> [email protected]
> http://listserv.shafik.org/mailman/listinfo/ltt-dev
>
--
Mathieu Desnoyers
Computer Engineering Ph.D. Candidate, École Polytechnique de Montréal
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68