Received: by 2002:a25:7ec1:0:0:0:0:0 with SMTP id z184csp6235086ybc; Wed, 27 Nov 2019 17:54:50 -0800 (PST) X-Google-Smtp-Source: APXvYqzAWUmn3WY3pKutPv+0xwpJSK1//T0vRA8FZVerux5zioNOqdJYJdxnjOvFtacVoEOcG5h4 X-Received: by 2002:a17:906:8697:: with SMTP id g23mr53533243ejx.177.1574906090168; Wed, 27 Nov 2019 17:54:50 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1574906090; cv=none; d=google.com; s=arc-20160816; b=fIw6bov390WcK3f23sXUDb2uhQP6zBa+Ku2JV8hwH4igffjIEjG0QjsKN/oObR10yD XDWtB6bB6t/+TzBB1b074SjoN4LIJEUW5Cq6vJDahGZDevGPQq2Vr8qWQ9b9QNHDFKXd vbNeYXp6p/E4wkc60zULZwxbRySr33UtziNm+6zyCEMV3eMI1Ugv1FeRJ/VYbTWcKZBj w2PPAURzMRLR0CSE/dtcC0P9bRJrsFpRS1nl0TSYqBxpaejpHVnUT52oNgzR76zD7tog e/DORh9iaxUltv46UvxbCpBVnSebvPxgyNUUm1/dBamQocngQhsUopyo6mGLBLdbYKyb 6VDQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=zOMrWAz7d85fQsPlURvII19nkQWvrip7ecSjqP3/RpE=; b=ZrmhsTMJyqkn9zddj7ojtCDdqFWOJA4FvKXJ4/LF06yZ8wYWhwB1y9nhmLg2a95sgM S4xERgoHEJQYNs+3b5FS/DcYYoI6X/t1y6HpCUUoQ02eDNQPppkf1LJitiRhDQLzM0vK PwmwFGp0I0ku782cidOEHR8e35uP20Tx1FkSAMgfxFaG73HV2AIelV0j5haMnlLXBkVH rTMyB69pAnmeJPekPFdr8otISNy1nwdIka+w/JBEPts3G/dPI6Saxkz/7WECcMP94rsP 7hY/S5OfG9P2wUR7JG3KSGrsFkmxPiuXOq7yZUgVSpGn2QhJESFbjxP3wBQfFhmFkKnD ZMiw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id d12si10801811ejt.334.2019.11.27.17.54.26; Wed, 27 Nov 2019 17:54:50 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728031AbfK1BxV (ORCPT + 99 others); Wed, 27 Nov 2019 20:53:21 -0500 Received: from Galois.linutronix.de ([193.142.43.55]:45760 "EHLO Galois.linutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727906AbfK1BxF (ORCPT ); Wed, 27 Nov 2019 20:53:05 -0500 Received: from [5.158.153.53] (helo=g2noscherz.lab.linutronix.de.) by Galois.linutronix.de with esmtpsa (TLS1.2:DHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.80) (envelope-from ) id 1ia8zK-00083b-AJ; Thu, 28 Nov 2019 02:52:58 +0100 From: John Ogness To: linux-kernel@vger.kernel.org Cc: Peter Zijlstra , Petr Mladek , Sergey Senozhatsky , Steven Rostedt , Linus Torvalds , Greg Kroah-Hartman , Andrea Parri , Thomas Gleixner , Sergey Senozhatsky , Brendan Higgins , kexec@lists.infradead.org Subject: [RFC PATCH v5 2/3] printk-rb: new printk ringbuffer implementation (reader) Date: Thu, 28 Nov 2019 02:58:34 +0106 Message-Id: <20191128015235.12940-3-john.ogness@linutronix.de> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20191128015235.12940-1-john.ogness@linutronix.de> References: <20191128015235.12940-1-john.ogness@linutronix.de> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Linutronix-Spam-Score: -1.0 X-Linutronix-Spam-Level: - X-Linutronix-Spam-Status: No , -1.0 points, 5.0 required, ALL_TRUSTED=-1,SHORTCIRCUIT=-0.0001 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add the reader implementation for the new ringbuffer. Signed-off-by: John Ogness --- kernel/printk/printk_ringbuffer.c | 234 ++++++++++++++++++++++++++++++ kernel/printk/printk_ringbuffer.h | 12 +- 2 files changed, 245 insertions(+), 1 deletion(-) diff --git a/kernel/printk/printk_ringbuffer.c b/kernel/printk/printk_ringbuffer.c index 09c32e52fd40..f85762713583 100644 --- a/kernel/printk/printk_ringbuffer.c +++ b/kernel/printk/printk_ringbuffer.c @@ -674,3 +674,237 @@ void prb_commit(struct prb_reserved_entry *e) local_irq_restore(e->irqflags); } EXPORT_SYMBOL(prb_commit); + +/* + * Given @blk_lpos, return a pointer to the raw data from the data block + * and calculate the size of the data part. A NULL pointer is returned + * if @blk_lpos specifies values that could never be legal. + * + * This function (used by readers) performs strict validation on the lpos + * values to possibly detect bugs in the writer code. A WARN_ON_ONCE() is + * triggered if an internal error is detected. + */ +static char *get_data(struct prb_data_ring *data_ring, + struct prb_data_blk_lpos *blk_lpos, + unsigned long *data_size) +{ + struct prb_data_block *db; + + if (blk_lpos->begin == INVALID_LPOS && + blk_lpos->next == INVALID_LPOS) { + /* descriptor without a data block */ + return NULL; + } else if (DATA_WRAPS(data_ring, blk_lpos->begin) == + DATA_WRAPS(data_ring, blk_lpos->next)) { + /* regular data block */ + if (WARN_ON_ONCE(blk_lpos->next <= blk_lpos->begin)) + return NULL; + db = to_block(data_ring, blk_lpos->begin); + *data_size = blk_lpos->next - blk_lpos->begin; + + } else if ((DATA_WRAPS(data_ring, blk_lpos->begin) + 1 == + DATA_WRAPS(data_ring, blk_lpos->next)) || + ((DATA_WRAPS(data_ring, blk_lpos->begin) == + DATA_WRAPS(data_ring, -1UL)) && + (DATA_WRAPS(data_ring, blk_lpos->next) == 0))) { + /* wrapping data block */ + db = to_block(data_ring, 0); + *data_size = DATA_INDEX(data_ring, blk_lpos->next); + + } else { + WARN_ON_ONCE(1); + return NULL; + } + + /* A valid data block will always be aligned to the ID size. */ + if (WARN_ON_ONCE(blk_lpos->begin != + ALIGN(blk_lpos->begin, sizeof(db->id))) || + WARN_ON_ONCE(blk_lpos->next != + ALIGN(blk_lpos->next, sizeof(db->id)))) { + return NULL; + } + + /* A valid data block will always have at least an ID. */ + if (WARN_ON_ONCE(*data_size < sizeof(db->id))) + return NULL; + + /* Subtract descriptor ID space from size. */ + *data_size -= sizeof(db->id); + + return &db->data[0]; +} + +/* 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))); + + /* + * Load/copy text data. If it fails, this is a + * data-less descriptor. + */ + if (!copy_data(&rb->text_data_ring, &desc.text_blk_lpos, + desc.info.text_len, r->text_buf, r->text_buf_size)) { + return -ENOENT; + } + + /* + * Load/copy dict data. Although this should not fail, dict data + * is not important. So if it fails, modify the copied meta data + * to report that there is no dict data, thus silently dropping + * the dict data. + */ + if (!copy_data(&rb->dict_data_ring, &desc.dict_blk_lpos, + desc.info.dict_len, r->dict_buf, r->dict_buf_size)) { + if (r->info) + r->info->dict_len = 0; + } + + /* Re-check real descriptor validity. */ + return desc_read_committed(desc_ring, id, seq, &desc); +} + +/* Get the sequence number of the tail descriptor. */ +static u64 get_desc_tail_seq(struct printk_ringbuffer *rb) +{ + struct prb_desc_ring *desc_ring = &rb->desc_ring; + enum desc_state d_state; + struct prb_desc desc; + u32 id; + + do { + id = atomic_read(&rb->desc_ring.tail_id); + d_state = desc_read(desc_ring, id, &desc); + + /* + * This loop will not be infinite because the tail is + * _always_ in the committed or reusable state. + */ + } while (d_state != desc_committed && d_state != desc_reusable); + + return desc.info.seq; +} + +/** + * prb_read_valid() - Non-blocking read of a requested record or (if gone) + * the next available record. + * + * @rb: The ringbuffer to read from. + * @seq: The sequence number of the record to read. + * @r: The record data buffer to store the read record to. + * + * This is the public function available to readers to read a record. + * + * The reader provides the @info, @text_buf, @dict_buf buffers of @r to be + * filled in. + * + * Context: Any context. + * Return: true if a record was read, otherwise false. + * + * On success, the reader must check r->info.seq to see which record was + * actually read. This allows the reader to detect dropped records. + * + * Failure means @seq refers to a not yet written record. + */ +bool prb_read_valid(struct printk_ringbuffer *rb, u64 seq, + struct printk_record *r) +{ + u64 tail_seq; + int err; + + while ((err = prb_read(rb, seq, r))) { + tail_seq = get_desc_tail_seq(rb); + + if (seq < tail_seq) { + /* + * Behind the tail. Catch up and try again. This + * can happen for -ENOENT and -EINVAL cases. + */ + seq = tail_seq; + + } else if (err == -ENOENT) { + /* Record exists, but no data available. Skip. */ + seq++; + + } else { + /* Non-existent/non-committed record. Must stop. */ + return false; + } + } + + return true; +} +EXPORT_SYMBOL(prb_read_valid); diff --git a/kernel/printk/printk_ringbuffer.h b/kernel/printk/printk_ringbuffer.h index b6f06e5edc1b..3c0eaa165a5f 100644 --- a/kernel/printk/printk_ringbuffer.h +++ b/kernel/printk/printk_ringbuffer.h @@ -17,11 +17,17 @@ struct printk_info { }; /* - * A structure providing the buffers, used by writers. + * A structure providing the buffers, used by writers and readers. * * Writers: * The writer sets @text_buf_size and @dict_buf_size before calling * prb_reserve(). On success, prb_reserve() sets @info, @text_buf, @dict_buf. + * + * Readers: + * The reader sets all fields before calling prb_read_valid(). Note that + * the reader provides the @info, @text_buf, @dict_buf buffers. On success, + * the struct pointed to by @info will be filled and the char arrays pointed + * to by @text_buf and @dict_buf will be filled with text and dict data. */ struct printk_record { struct printk_info *info; @@ -236,4 +242,8 @@ bool prb_reserve(struct prb_reserved_entry *e, struct printk_ringbuffer *rb, struct printk_record *r); void prb_commit(struct prb_reserved_entry *e); +/* Reader Interface */ +bool prb_read_valid(struct printk_ringbuffer *rb, u64 seq, + struct printk_record *r); + #endif /* _KERNEL_PRINTK_RINGBUFFER_H */ -- 2.20.1