On (19/11/28 02:58), John Ogness wrote:
> +/* Given @blk_lpos, copy an expected @len of data into the provided buffer. 
> */
> +static bool copy_data(struct prb_data_ring *data_ring,
> +                   struct prb_data_blk_lpos *blk_lpos, u16 len, char *buf,
> +                   unsigned int buf_size)
> +{
> +     unsigned long data_size;
> +     char *data;
> +
> +     /* Caller might not want the data. */
> +     if (!buf || !buf_size)
> +             return true;
> +
> +     data = get_data(data_ring, blk_lpos, &data_size);
> +     if (!data)
> +             return false;
> +
> +     /* Actual cannot be less than expected. */
> +     if (WARN_ON_ONCE(data_size < len))
> +             return false;
> +
> +     data_size = min_t(u16, buf_size, len);
> +
> +     if (!WARN_ON_ONCE(!data_size))
> +             memcpy(&buf[0], data, data_size);
> +     return true;
> +}
> +
> +/*
> + * Read the record @id and verify that it is committed and has the sequence
> + * number @seq.
> + *
> + * Error return values:
> + * -EINVAL: The record @seq does not exist.
> + * -ENOENT: The record @seq exists, but its data is not available. This is a
> + *          valid record, so readers should continue with the next seq.
> + */
> +static int desc_read_committed(struct prb_desc_ring *desc_ring, u32 id,
> +                            u64 seq, struct prb_desc *desc)
> +{
> +     enum desc_state d_state;
> +
> +     d_state = desc_read(desc_ring, id, desc);
> +     if (desc->info.seq != seq)
> +             return -EINVAL;
> +     else if (d_state == desc_reusable)
> +             return -ENOENT;
> +     else if (d_state != desc_committed)
> +             return -EINVAL;
> +
> +     return 0;
> +}
> +
> +/*
> + * Copy the ringbuffer data from the record with @seq to the provided
> + * @r buffer. On success, 0 is returned.
> + *
> + * See desc_read_committed() for error return values.
> + */
> +static int prb_read(struct printk_ringbuffer *rb, u64 seq,
> +                 struct printk_record *r)
> +{
> +     struct prb_desc_ring *desc_ring = &rb->desc_ring;
> +     struct prb_desc *rdesc = to_desc(desc_ring, seq);
> +     atomic_t *state_var = &rdesc->state_var;
> +     struct prb_desc desc;
> +     int err;
> +     u32 id;
> +
> +     /* Get a reliable local copy of the descriptor and check validity. */
> +     id = DESC_ID(atomic_read(state_var));
> +     err = desc_read_committed(desc_ring, id, seq, &desc);
> +     if (err)
> +             return err;
> +
> +     /* If requested, copy meta data. */
> +     if (r->info)
> +             memcpy(r->info, &desc.info, sizeof(*(r->info)));

I wonder if those WARN_ON-s will trigger false positive sometimes.

A theoretical case.

What if reader gets preempted/interrupted in the middle of
desc_read_committed()->desc_read()->memcpy(). The context which interrupts
the reader recycles the descriptor and pushes new data. Suppose that
reader was interrupted right after it copied ->info.seq and ->info.text_len.
So the first desc_read_committed() will pass - we have matching ->seq
and committed state. copy_data(), however, most likely, will generate
WARNs. The final desc_read_committed() will notice that local copy
of desc was in non-consistent state and everything is fine, but we have
WARNs in the log buffer now.

        -ss

_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

Reply via email to