From: John Ogness <john.ogness@linutronix.de> To: Petr Mladek <pmladek@suse.com> Cc: Peter Zijlstra <peterz@infradead.org>, Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com>, Sergey Senozhatsky <sergey.senozhatsky@gmail.com>, Steven Rostedt <rostedt@goodmis.org>, Linus Torvalds <torvalds@linux-foundation.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Andrea Parri <parri.andrea@gmail.com>, Thomas Gleixner <tglx@linutronix.de>, kexec@lists.infradead.org, linux-kernel@vger.kernel.org, Paul McKenney <paulmck@kernel.org> Subject: Re: redundant check in make_data_reusable(): was [PATCH v2 2/3] printk: add lockless buffer Date: Wed, 10 Jun 2020 16:56:53 +0200 [thread overview] Message-ID: <87d067otoq.fsf@vostro.fn.ogness.net> (raw) In-Reply-To: <87o8prp6bi.fsf@vostro.fn.ogness.net> (John Ogness's message of "Wed, 10 Jun 2020 12:24:01 +0200") On 2020-06-10, Petr Mladek <pmladek@suse.com> wrote: >> +static bool data_make_reusable(struct printk_ringbuffer *rb, >> + struct prb_data_ring *data_ring, >> + unsigned long lpos_begin, >> + unsigned long lpos_end, >> + unsigned long *lpos_out) >> +{ >> + struct prb_desc_ring *desc_ring = &rb->desc_ring; >> + struct prb_data_blk_lpos *blk_lpos; >> + struct prb_data_block *blk; >> + unsigned long tail_lpos; >> + enum desc_state d_state; >> + struct prb_desc desc; >> + unsigned long id; >> + >> + /* >> + * Using the provided @data_ring, point @blk_lpos to the correct >> + * blk_lpos within the local copy of the descriptor. >> + */ >> + if (data_ring == &rb->text_data_ring) >> + blk_lpos = &desc.text_blk_lpos; >> + else >> + blk_lpos = &desc.dict_blk_lpos; >> + >> + /* Loop until @lpos_begin has advanced to or beyond @lpos_end. */ >> + while ((lpos_end - lpos_begin) - 1 < DATA_SIZE(data_ring)) { >> + blk = to_block(data_ring, lpos_begin); >> + id = READ_ONCE(blk->id); /* LMM(data_make_reusable:A) */ >> + >> + /* >> + * Guarantee the block ID is loaded before checking the tail >> + * lpos. The loaded block ID can only be considered valid if >> + * the tail lpos has not overtaken @lpos_begin. This pairs >> + * with data_alloc:A. >> + * >> + * Memory barrier involvement: >> + * >> + * If data_make_reusable:A reads from data_alloc:B, then >> + * data_make_reusable:C reads from data_push_tail:D. >> + * >> + * Relies on: >> + * >> + * MB from data_push_tail:D to data_alloc:B >> + * matching >> + * RMB from data_make_reusable:A to data_make_reusable:C >> + * >> + * Note: data_push_tail:D and data_alloc:B can be different >> + * CPUs. However, the data_alloc:B CPU (which performs >> + * the full memory barrier) must have previously seen >> + * data_push_tail:D. >> + */ >> + smp_rmb(); /* LMM(data_make_reusable:B) */ >> + >> + tail_lpos = atomic_long_read(&data_ring->tail_lpos >> + ); /* LMM(data_make_reusable:C) */ >> + >> + /* >> + * If @lpos_begin has fallen behind the tail lpos, the read >> + * block ID cannot be trusted. Fast forward @lpos_begin to the >> + * tail lpos and try again. >> + */ >> + if (lpos_begin - tail_lpos >= DATA_SIZE(data_ring)) { >> + lpos_begin = tail_lpos; >> + continue; >> + } >> + >> + d_state = desc_read(desc_ring, id, >> + &desc); /* LMM(data_make_reusable:D) */ >> + >> + switch (d_state) { >> + case desc_miss: >> + return false; >> + case desc_reserved: >> + return false; >> + case desc_committed: >> + /* >> + * This data block is invalid if the descriptor >> + * does not point back to it. >> + */ > > Here again the comments describe what the check does but not why. > I would write something like: > > /* > * The block might have already been > * reused. Make sure that the descriptor really > * points back to the checked lpos. It covers > * both situations. Random data might point to > * a valid descriptor just by chance. Or the block > * has been already reused by another descriptor. > */ Originally this check was needed because the descriptor would be read even if there was a data race reading the ID from the data block. Validating the lpos value was a kind of protection against reading random data that by chance yielded an ID of a committed/reusable descriptor. However, after you pointed out that this check was not enough, the code now re-checks the data tail to make sure that no data race happened. So actually it is not possible that a descriptor in the committed/reusable state will point anywhere else. We know the ID is not random garbage or recycled, so the state can be trusted. I recommend to either remove this sanity check (for committed and reusable) or at least change it to: WARN_ON_ONCE(blk_lpos->begin != lpos_begin); Or can you see any possibility of this case? >> + if (blk_lpos->begin != lpos_begin) >> + return false; >> + desc_make_reusable(desc_ring, id); >> + break; >> + case desc_reusable: >> + /* >> + * This data block is invalid if the descriptor >> + * does not point back to it. >> + */ >> + if (blk_lpos->begin != lpos_begin) >> + return false; >> + break; >> + } >> + >> + /* Advance @lpos_begin to the next data block. */ >> + lpos_begin = blk_lpos->next; >> + } John Ogness
WARNING: multiple messages have this Message-ID (diff)
From: John Ogness <john.ogness@linutronix.de> To: Petr Mladek <pmladek@suse.com> Cc: Andrea Parri <parri.andrea@gmail.com>, Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com>, Paul McKenney <paulmck@kernel.org>, Peter Zijlstra <peterz@infradead.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, kexec@lists.infradead.org, linux-kernel@vger.kernel.org, Steven Rostedt <rostedt@goodmis.org>, Sergey Senozhatsky <sergey.senozhatsky@gmail.com>, Thomas Gleixner <tglx@linutronix.de>, Linus Torvalds <torvalds@linux-foundation.org> Subject: Re: redundant check in make_data_reusable(): was [PATCH v2 2/3] printk: add lockless buffer Date: Wed, 10 Jun 2020 16:56:53 +0200 [thread overview] Message-ID: <87d067otoq.fsf@vostro.fn.ogness.net> (raw) In-Reply-To: <87o8prp6bi.fsf@vostro.fn.ogness.net> (John Ogness's message of "Wed, 10 Jun 2020 12:24:01 +0200") On 2020-06-10, Petr Mladek <pmladek@suse.com> wrote: >> +static bool data_make_reusable(struct printk_ringbuffer *rb, >> + struct prb_data_ring *data_ring, >> + unsigned long lpos_begin, >> + unsigned long lpos_end, >> + unsigned long *lpos_out) >> +{ >> + struct prb_desc_ring *desc_ring = &rb->desc_ring; >> + struct prb_data_blk_lpos *blk_lpos; >> + struct prb_data_block *blk; >> + unsigned long tail_lpos; >> + enum desc_state d_state; >> + struct prb_desc desc; >> + unsigned long id; >> + >> + /* >> + * Using the provided @data_ring, point @blk_lpos to the correct >> + * blk_lpos within the local copy of the descriptor. >> + */ >> + if (data_ring == &rb->text_data_ring) >> + blk_lpos = &desc.text_blk_lpos; >> + else >> + blk_lpos = &desc.dict_blk_lpos; >> + >> + /* Loop until @lpos_begin has advanced to or beyond @lpos_end. */ >> + while ((lpos_end - lpos_begin) - 1 < DATA_SIZE(data_ring)) { >> + blk = to_block(data_ring, lpos_begin); >> + id = READ_ONCE(blk->id); /* LMM(data_make_reusable:A) */ >> + >> + /* >> + * Guarantee the block ID is loaded before checking the tail >> + * lpos. The loaded block ID can only be considered valid if >> + * the tail lpos has not overtaken @lpos_begin. This pairs >> + * with data_alloc:A. >> + * >> + * Memory barrier involvement: >> + * >> + * If data_make_reusable:A reads from data_alloc:B, then >> + * data_make_reusable:C reads from data_push_tail:D. >> + * >> + * Relies on: >> + * >> + * MB from data_push_tail:D to data_alloc:B >> + * matching >> + * RMB from data_make_reusable:A to data_make_reusable:C >> + * >> + * Note: data_push_tail:D and data_alloc:B can be different >> + * CPUs. However, the data_alloc:B CPU (which performs >> + * the full memory barrier) must have previously seen >> + * data_push_tail:D. >> + */ >> + smp_rmb(); /* LMM(data_make_reusable:B) */ >> + >> + tail_lpos = atomic_long_read(&data_ring->tail_lpos >> + ); /* LMM(data_make_reusable:C) */ >> + >> + /* >> + * If @lpos_begin has fallen behind the tail lpos, the read >> + * block ID cannot be trusted. Fast forward @lpos_begin to the >> + * tail lpos and try again. >> + */ >> + if (lpos_begin - tail_lpos >= DATA_SIZE(data_ring)) { >> + lpos_begin = tail_lpos; >> + continue; >> + } >> + >> + d_state = desc_read(desc_ring, id, >> + &desc); /* LMM(data_make_reusable:D) */ >> + >> + switch (d_state) { >> + case desc_miss: >> + return false; >> + case desc_reserved: >> + return false; >> + case desc_committed: >> + /* >> + * This data block is invalid if the descriptor >> + * does not point back to it. >> + */ > > Here again the comments describe what the check does but not why. > I would write something like: > > /* > * The block might have already been > * reused. Make sure that the descriptor really > * points back to the checked lpos. It covers > * both situations. Random data might point to > * a valid descriptor just by chance. Or the block > * has been already reused by another descriptor. > */ Originally this check was needed because the descriptor would be read even if there was a data race reading the ID from the data block. Validating the lpos value was a kind of protection against reading random data that by chance yielded an ID of a committed/reusable descriptor. However, after you pointed out that this check was not enough, the code now re-checks the data tail to make sure that no data race happened. So actually it is not possible that a descriptor in the committed/reusable state will point anywhere else. We know the ID is not random garbage or recycled, so the state can be trusted. I recommend to either remove this sanity check (for committed and reusable) or at least change it to: WARN_ON_ONCE(blk_lpos->begin != lpos_begin); Or can you see any possibility of this case? >> + if (blk_lpos->begin != lpos_begin) >> + return false; >> + desc_make_reusable(desc_ring, id); >> + break; >> + case desc_reusable: >> + /* >> + * This data block is invalid if the descriptor >> + * does not point back to it. >> + */ >> + if (blk_lpos->begin != lpos_begin) >> + return false; >> + break; >> + } >> + >> + /* Advance @lpos_begin to the next data block. */ >> + lpos_begin = blk_lpos->next; >> + } John Ogness _______________________________________________ kexec mailing list kexec@lists.infradead.org http://lists.infradead.org/mailman/listinfo/kexec
next prev parent reply other threads:[~2020-06-10 14:57 UTC|newest] Thread overview: 61+ messages / expand[flat|nested] mbox.gz Atom feed top 2020-05-01 9:40 [PATCH v2 0/3] printk: replace ringbuffer John Ogness 2020-05-01 9:40 ` John Ogness 2020-05-01 9:40 ` [PATCH v2 1/3] crash: add VMCOREINFO macro for anonymous structs John Ogness 2020-05-01 9:40 ` John Ogness 2020-06-03 10:16 ` Petr Mladek 2020-06-03 10:16 ` Petr Mladek 2020-05-01 9:40 ` [PATCH v2 2/3] printk: add lockless buffer John Ogness 2020-05-01 9:40 ` John Ogness 2020-05-18 13:03 ` John Ogness 2020-05-18 17:22 ` Linus Torvalds 2020-05-18 17:22 ` Linus Torvalds 2020-05-19 20:34 ` John Ogness 2020-05-19 20:34 ` John Ogness 2020-06-09 7:10 ` blk->id read race: was: " Petr Mladek 2020-06-09 7:10 ` Petr Mladek 2020-06-09 14:18 ` John Ogness 2020-06-09 14:18 ` John Ogness 2020-06-10 8:42 ` Petr Mladek 2020-06-10 8:42 ` Petr Mladek 2020-06-10 13:55 ` John Ogness 2020-06-10 13:55 ` John Ogness 2020-06-09 9:31 ` redundant check in make_data_reusable(): was " Petr Mladek 2020-06-09 9:31 ` Petr Mladek 2020-06-09 14:48 ` John Ogness 2020-06-09 14:48 ` John Ogness 2020-06-10 9:38 ` Petr Mladek 2020-06-10 9:38 ` Petr Mladek 2020-06-10 10:24 ` John Ogness 2020-06-10 10:24 ` John Ogness 2020-06-10 14:56 ` John Ogness [this message] 2020-06-10 14:56 ` John Ogness 2020-06-11 19:51 ` John Ogness 2020-06-11 19:51 ` John Ogness 2020-06-11 13:55 ` Petr Mladek 2020-06-11 13:55 ` Petr Mladek 2020-06-11 20:25 ` John Ogness 2020-06-11 20:25 ` John Ogness 2020-06-09 9:48 ` Full barrier in data_push_tail(): " Petr Mladek 2020-06-09 9:48 ` Petr Mladek 2020-06-09 15:03 ` John Ogness 2020-06-09 15:03 ` John Ogness 2020-06-09 11:37 ` Barrier before pushing desc_ring tail: " Petr Mladek 2020-06-09 11:37 ` Petr Mladek 2020-06-09 15:56 ` John Ogness 2020-06-09 15:56 ` John Ogness 2020-06-11 12:01 ` Petr Mladek 2020-06-11 12:01 ` Petr Mladek 2020-06-11 23:06 ` John Ogness 2020-06-11 23:06 ` John Ogness 2020-06-09 14:38 ` data_ring head_lpos and tail_lpos synchronization: " Petr Mladek 2020-06-09 14:38 ` Petr Mladek 2020-06-10 7:53 ` John Ogness 2020-06-10 7:53 ` John Ogness 2020-05-01 9:40 ` [PATCH v2 3/3] printk: use the lockless ringbuffer John Ogness 2020-05-01 9:40 ` John Ogness 2020-05-06 14:50 ` John Ogness 2020-05-06 14:50 ` John Ogness 2020-05-13 12:05 ` [PATCH v2 0/3] printk: replace ringbuffer Prarit Bhargava 2020-05-13 12:05 ` Prarit Bhargava 2020-05-15 10:24 ` Sergey Senozhatsky 2020-05-15 10:24 ` Sergey Senozhatsky
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=87d067otoq.fsf@vostro.fn.ogness.net \ --to=john.ogness@linutronix.de \ --cc=gregkh@linuxfoundation.org \ --cc=kexec@lists.infradead.org \ --cc=linux-kernel@vger.kernel.org \ --cc=parri.andrea@gmail.com \ --cc=paulmck@kernel.org \ --cc=peterz@infradead.org \ --cc=pmladek@suse.com \ --cc=rostedt@goodmis.org \ --cc=sergey.senozhatsky.work@gmail.com \ --cc=sergey.senozhatsky@gmail.com \ --cc=tglx@linutronix.de \ --cc=torvalds@linux-foundation.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.