Re: [RFC][PATCH] Oops messages transfer using QR codes

From: Laura Abbott
Date: Mon Nov 02 2015 - 13:56:14 EST


On 10/30/2015 08:03 AM, Murtaza Alexandru wrote:
This patch is continued from this previous RFC:
https://lkml.org/lkml/2014/3/17/525

If the config option for this feature is enabled, then when an Oops is
in progress, the printk() calls' strings are buffered. When the Oops
finishes, the buffer is split into multiple packets, compressed, encoded
into QRs and then displayed in console. The compression is done using
DEFLATE (from lib/zlib/). The user can issue commands writing to the sysfs.
For scanning and automatic reporting Bug Koops (Android application) is
needed [0].

Problems with the previous patch:
1. Even if the specifications says you can encode up to 2980 bytes into a
QR code, in reality, you can only encode about 200 bytes if you want to
scan it with a smartphone. Using DEFLATE, we can compress 300 bytes so that
the output is less than 200 bytes almost all the time. From what I saw, an
Oops message is on average about 1.7kb. So the conclusion is: one QR is not
enough.

2. If there are multiple Oopses occurring on different CPUs there is no way
to draw both QRs. Even if we would take not to overlap them, we would be
left with even less space to draw which results in less info we are able
to send.

3. Another problem in the previous RFC was that there is no way (or at
least I didn't figured it out) of knowing if what we write in the
framebuffer actually gets to the screen and also there is no simple way
to guarantee that what we draw is always on top of the text.


Proposed solution for the above stated problems:
When we finish capturing an Oops, split the message into multiple chunks,
compress them and encode into some format so we will be able to get the
initial message again no matter in what order we scan the QRs. For drawing,
we are going to make a kthread and continuously draw. This will guarantee
the QR is always on top. Also this will enable rendering an "animation".

The way data is encoded into a packet is described here [1] but TL;DR:
We are 100% sure the QR code is scanned correctly so we don't need error
correction, we are only in a 1-way "communication" so we have to run the
animation indefinitely anyway. The things we care about is being able
to distinguish between different messages, for one message to be able
to reorder the packets and sanity check of the message (check if the user
is not scanning normal QRs). For more detailed information and explanation
please see [1].

For this patch to work properly you need to have framebuffer enabled.
Anyway, if you do not have one, ASCII art will be used as a fallback.

From BugKoops to QR module communication we have to use the user (tell him
what to do). For user to QR module we use sysfs. Current implementation
supports stop, pause, resume, clear queue and print packets ids, print
messages ids, delete packet, delete message, which are all useful in
case you want to speed up the scanning process a little bit.

Current issues:
* screen flush and console acquisition is forced with a printk
* not tested for Oops messages issued from several CPUs
* all values and data presented here are empirical and finer tuning should
be done

I hope this will give an impulse to the community to search a way
and extend this for panics too, which would be much more useful.


I'm glad to see someone continuing on this work. For the next version, can
you break it up for ease of review? I'd say put the lib/qr/ addition as one
patch and then the print_oops as a second patch.


<snip>
diff --git a/include/linux/qrencode.h b/include/linux/qrencode.h
new file mode 100644
index 0000000..112c4bf
--- /dev/null
+++ b/include/linux/qrencode.h
@@ -0,0 +1,504 @@
+/**
+ * qrencode - QR Code encoder
+ *
+ * Copyright (C) 2006-2012 Kentaro Fukuchi <kentaro@xxxxxxxxxxx>
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+/** \mainpage
+ * Libqrencode is a library for encoding data in a QR Code symbol, a kind of 2D
+ * symbology.
+ *
+ * \section encoding Encoding
+ *
+ * There are two methods to encode data: <b>encoding a string/data</b> or
+ * <b>encoding a structured data</b>.
+ *
+ * \subsection encoding-string Encoding a string/data
+ * You can encode a string by calling qrcode_encode_string().
+ * The given string is parsed automatically and encoded. If you want to encode
+ * data that can be represented as a C string style (NUL terminated), you can
+ * simply use this way.
+ *
+ * If the input data contains Kanji (Shift-JIS) characters and you want to
+ * encode them as Kanji in QR Code, you should give QR_MODE_KANJI as a hint.
+ * Otherwise, all of non-alphanumeric characters are encoded as 8 bit data.
+ * If you want to encode a whole string in 8 bit mode, you can use
+ * qrcode_encode_string_8bit() instead.
+ *
+ * Please note that a C string can not contain NUL characters. If your data
+ * contains NUL, you must use qrcode_encode_data().
+ *
+ * \subsection encoding-input Encoding a structured data
+ * You can construct a structured input data manually. If the structure of the
+ * input data is known, you can use this way.
+ * At first, create a ::qrinput object by qrinput_new(). Then add input data
+ * to the qrinput object by qrinput_append(). Finally call qrcode_encode_input()
+ * to encode the qrinput data.
+ * You can reuse the qrinput data again to encode it in other symbols with
+ * different parameters.
+ *
+ * \section result Result
+ * The encoded symbol is resulted as a ::qrcode object. It will contain
+ * its version number, width of the symbol and an array represents the symbol.
+ * See ::qrcode for the details. You can free the object by qrcode_free().
+ *
+ * Please note that the version of the result may be larger than specified.
+ * In such cases, the input data would be too large to be encoded in a
+ * symbol of the specified version.
+ *
+ * \section structured Structured append
+ * Libqrencode can generate "Structured-appended" symbols that enables to split
+ * a large data set into mulitple QR codes. A QR code reader concatenates
+ * multiple QR code symbols into a string.
+ * Just like qrcode_encode_string(), you can use
+ * qrcode_encode_string_structured() to generate structured-appended symbols.
+ * This functions returns an instance of ::qrcode_list. The returned list is a
+ * singly-linked list of qrcode: you
+ * can retrieve each QR code in this way:
+ *
+ * \code
+ * qrcode_list *qrcodes;
+ * qrcode_list *entry;
+ * qrcode *qrcode;
+ *
+ * qrcodes = qrcode_encode_string_structured(...);
+ * entry = qrcodes;
+ * while(entry != NULL) {
+ * qrcode = entry->code;
+ * // do something
+ * entry = entry->next;
+ * }
+ * qrcode_list_free(entry);
+ * \endcode
+ *
+ * Instead of using auto-parsing functions, you can construct your own
+ * structured input. At first, instantiate an object of ::qrinput_struct
+ * by calling qrinput_struct_new(). This object can hold multiple ::qrinput,
+ * and one QR code is generated for a ::qrinput.
+ * qrinput_struct_append_input() appends a ::qrinput to a ::qrinput_struct
+ * object. In order to generate structured-appended symbols, it is required to
+ * embed headers to each symbol. You can use
+ * qrinput_struct_insert_structured_append_headers() to insert appropriate
+ * headers to each symbol. You should call this function just once before
+ * encoding symbols.
+ */
+
+#ifndef __QRENCODE_H__
+#define __QRENCODE_H__
+
+#if defined(__cplusplus)
+extern "C" {
+#endif

Go ahead and drop any __cplusplus wrappers.

<snip>

--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -24,6 +24,8 @@
#include <linux/init.h>
#include <linux/nmi.h>

+#include <linux/print_oops.h>
+
#define PANIC_TIMER_STEP 100
#define PANIC_BLINK_SPD 18

@@ -401,6 +403,9 @@ void print_oops_end_marker(void)
{
init_oops_id();
pr_warn("---[ end trace %016llx ]---\n", (unsigned long long)oops_id);
+#ifdef CONFIG_QR_OOPS
+ print_qr_err();
+#endif

The preferred style is to not have #ifdef in functions if it can be
avoided. #ifdef in the header file for print_qr_err would be better.

}

/*
diff --git a/kernel/print_oops.c b/kernel/print_oops.c
new file mode 100644
index 0000000..e367228
--- /dev/null
+++ b/kernel/print_oops.c
@@ -0,0 +1,693 @@
+/*
+ *
+ * Copyright (C) 2015 Murtaza Alexandru <alexandru.murtaza@xxxxxxxxx>
+ * Copyright (C) 2014 Teodora Baluta <teobaluta@xxxxxxxxx>
+ * Copyright (C) 2014 Levente Kurusa <levex@xxxxxxxxx>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ */
+#include <linux/print_oops.h>
+#include <linux/kdebug.h>
+#include <linux/bug.h>
+#include <linux/qrencode.h>
+#include <linux/fb.h>
+#include <linux/zlib.h>
+#include <linux/vmalloc.h>
+#include <linux/semaphore.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/time.h>
+#include <linux/list.h>
+#include <linux/jiffies.h>
+
+#define COMPR_LEVEL 9
+#define QQQ_WHITE 0x0F
+#define QQQ_BLACK 0x00
+
+static int qr_oops_cmd;
+core_param(qr_oops_cmd, qr_oops_cmd, int, 0644);
+
+static int qr_oops_param0;
+core_param(qr_oops_param0, qr_oops_param0, int, 0644);
+
+static int qr_oops_param1;
+core_param(qr_oops_param1, qr_oops_param1, int, 0644);
+
+static char qr_buffer[MESSAGE_BUFSIZE];
+static int buf_pos;
+
+static DEFINE_MUTEX(compr_mutex);
+static DEFINE_MUTEX(qr_list_mutex);


I gave it a quick test with just a 'echo c > /proc/sysrq-trigger'
and got a scheduling while atomic warning in addition to not seeing a
QR code. I don't think waking up a thread on panic is the correct approach
here. Can you elaborate more on what problem you were trying to solve by
adding a thread?

+static struct z_stream_s stream;
+
+static int compr_init(void)
+{
+ size_t size = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL),
+ zlib_inflate_workspacesize());
+
+ stream.workspace = vmalloc(size);
+ if (!stream.workspace)
+ return -ENOMEM;
+ return 0;
+}
+
+static void compr_exit(void)
+{
+ vfree(stream.workspace);
+}
+
+static int compress(void *in, void *out, size_t inlen, size_t outlen)
+{
+ int ret;
+
+ ret = compr_init();
+ if (ret != 0)
+ goto error;
+
+ mutex_lock(&compr_mutex);
+ ret = zlib_deflateInit(&stream, COMPR_LEVEL);
+ if (ret != Z_OK) {
+ pr_emerg("qr_compress: zlib_deflateInit failed with ret = %d\n",
+ ret);
+ goto error;
+ }
+
+ stream.next_in = in;
+ stream.avail_in = inlen;
+ stream.total_in = 0;
+ stream.next_out = out;
+ stream.avail_out = outlen;
+ stream.total_out = 0;
+
+ ret = zlib_deflate(&stream, Z_FINISH);
+ if (ret != Z_STREAM_END) {
+ pr_emerg("qr_compress: zlib_deflate failed with ret = %d\n",
+ ret);
+ goto error;
+ }
+
+ ret = zlib_deflateEnd(&stream);
+ if (ret != Z_OK) {
+ pr_emerg("qr_compress: zlib_deflateEnd failed with ret = %d\n",
+ ret);
+ goto error;
+ }
+
+ if (stream.total_out >= stream.total_in)
+ pr_emerg("qr_compress: total_out > total_in\n");
+
+ ret = stream.total_out;
+error:
+ mutex_unlock(&compr_mutex);
+ return ret;
+}
+
+static inline int compute_w(struct fb_info *info, int qrw)
+{
+ int xres = info->var.xres;
+ int yres = info->var.yres;
+ int minxy = (xres < yres) ? xres : yres;
+ int ret = minxy - minxy / 4;
+
+ return ret / qrw;
+}
+
+static inline void draw_qr(struct fb_info *info, struct qrcode *qr,
+ int pos_x, int pos_y,
+ int cell_width, int cell_height,
+ int border)
+{
+ int i, j;
+ int is_black;
+ struct fb_fillrect rect;
+
+ rect.width = cell_width;
+ rect.height = cell_height;
+ rect.rop = 0;
+
+ if (border) {
+ /* Print borders: */
+ rect.color = QQQ_WHITE;
+ for (i = 0; i < qr->width + 2; i++) {
+ /* Top */
+ rect.dx = 0 + pos_x;
+ rect.dy = i * cell_height + pos_y;
+ cfb_fillrect(info, &rect);
+
+ /* Bottom */
+ rect.dx = (qr->width + 1) * cell_width + pos_x;
+ rect.dy = i * cell_height + pos_y;
+ cfb_fillrect(info, &rect);
+
+ /* Left */
+ rect.dx = i * cell_width + pos_x;
+ rect.dy = pos_y;
+ cfb_fillrect(info, &rect);
+
+ /* Right */
+ rect.dx = i * cell_width + pos_x;
+ rect.dy = (qr->width + 1) * cell_height + pos_y;
+ cfb_fillrect(info, &rect);
+ }
+ }
+
+ /* Print actual QR matrix: */
+ for (i = 0; i < qr->width; i++) {
+ for (j = 0; j < qr->width; j++) {
+ rect.dx = (j + 1) * cell_width + pos_x;
+ rect.dy = (i + 1) * cell_height + pos_y;
+ is_black = qr->data[i * qr->width + j] & 1;
+ rect.color = is_black ? QQQ_BLACK : QQQ_WHITE;
+ cfb_fillrect(info, &rect);
+ }
+ }
+}
+
+#define ASCII_BLACK " "
+#define ASCII_BLOCK "%c", 219
+#define ASCII_HALFBLOCK_TOP "%c", 223
+#define ASCII_HALFBLOCK_BOTTOM "%c", 220
+
+static inline int qr_is_black(struct qrcode *qr, int x, int y)
+{
+ if (x < 0 || y < 0 || x >= qr->width || y >= qr->width)
+ return 0;
+ return qr->data[x * qr->width + y] & 1;
+}
+
+static inline void draw_ascii_qr(struct qrcode *qr)
+{
+ int i, j;
+ int up, down;
+
+ /* Print actual QR matrix: */
+ for (i = -1; i < qr->width + 1; i += 2) {
+ for (j = -1; j < qr->width + 1; j++) {
+ up = 1 - qr_is_black(qr, i, j);
+ down = 1 - qr_is_black(qr, i + 1, j);
+ if (up)
+ if (down)
+ printk(ASCII_BLOCK);
+ else
+ printk(ASCII_HALFBLOCK_TOP);
+ else
+ if (down)
+ printk(ASCII_HALFBLOCK_BOTTOM);
+ else
+ printk(ASCII_BLACK);
+ }
+ printk("\n");
+ }
+}
+
+struct qr_chunk {
+ struct list_head list;
+
+ struct qrcode *qr;
+ int message_id;
+ int packet_id;
+};
+
+static LIST_HEAD(qr_list_head);
+
+static struct task_struct *qr_thread;
+
+#define BUFFER_SIZE (4 * 1024)
+
+static inline void qr_list_delete(struct qr_chunk *element)
+{
+ list_del(&element->list);
+ vfree(element);
+}
+
+static void qr_list_delete_message(int message_id)
+{
+ struct list_head *it, *n;
+ struct qr_chunk *qr_entry;
+
+ mutex_lock(&qr_list_mutex);
+ list_for_each_safe(it, n, &qr_list_head) {
+ qr_entry = list_entry(it, struct qr_chunk, list);
+ if (qr_entry->message_id == message_id)
+ qr_list_delete(qr_entry);
+ }
+ mutex_unlock(&qr_list_mutex);
+}
+
+static void qr_list_clear(void)
+{
+ struct list_head *it, *n;
+
+ mutex_lock(&qr_list_mutex);
+ list_for_each_safe(it, n, &qr_list_head)
+ qr_list_delete(list_entry(it, struct qr_chunk, list));
+ mutex_unlock(&qr_list_mutex);
+}
+
+static void qr_list_delete_packet(int message_id, int packet_id)
+{
+ struct list_head *it, *n;
+ struct qr_chunk *qr_entry;
+
+ mutex_lock(&qr_list_mutex);
+ list_for_each_safe(it, n, &qr_list_head) {
+ qr_entry = list_entry(it, struct qr_chunk, list);
+ if (qr_entry->message_id == message_id &&
+ qr_entry->packet_id == packet_id)
+ qr_list_delete(qr_entry);
+ }
+ mutex_unlock(&qr_list_mutex);
+}
+
+#define BK1_MAGIC_FIRSTBYTE 222
+#define BK1_MAGIC_SECONDBYTE 173
+
+#define BK1_ENCODE_NONE 0
+#define BK1_ENCODE_DEFLATE 1
+
+#define BK1_VERSION 0
+
+static void make_bk1_packet(char *start, int len, int message_id,
+ int packet_id, int packet_count)
+{
+ struct qr_chunk *element;
+ int header_size;
+ ssize_t packet_len;
+ char compr_packet_buffer[BUFFER_SIZE];
+ char checksum;
+
+ element = vmalloc(sizeof(*element));
+ element->message_id = message_id;
+ element->packet_id = packet_id;
+
+ header_size = 0;
+
+ checksum = BK1_VERSION ^ message_id ^ packet_count ^ packet_id ^
+ BK1_ENCODE_DEFLATE;
+
+ compr_packet_buffer[header_size] = BK1_MAGIC_FIRSTBYTE;
+ ++header_size;
+ compr_packet_buffer[header_size] = BK1_MAGIC_SECONDBYTE;
+ ++header_size;
+ compr_packet_buffer[header_size] = BK1_VERSION;
+ ++header_size;
+ compr_packet_buffer[header_size] = message_id;
+ ++header_size;
+ compr_packet_buffer[header_size] = packet_count;
+ ++header_size;
+ compr_packet_buffer[header_size] = packet_id;
+ ++header_size;
+ compr_packet_buffer[header_size] = BK1_ENCODE_DEFLATE;
+ ++header_size;
+ compr_packet_buffer[header_size] = checksum;
+ ++header_size;
+ compr_packet_buffer[header_size] = (len >> 8) & 0xFF;
+ ++header_size;
+ compr_packet_buffer[header_size] = len & 0xFF;
+ ++header_size;
+
+ packet_len = compress(start, compr_packet_buffer + header_size, len,
+ BUFFER_SIZE - header_size);
+ if (packet_len < 0) {
+ pr_emerg("QR: Compression of QR code failed packet_len=%zd\n",
+ packet_len);
+ goto ERROR;
+ }
+ compr_exit();
+
+ packet_len += header_size;
+
+ element->qr = qrcode_encode_data(packet_len, compr_packet_buffer, 0,
+ QR_ECLEVEL_H);
+ if (!element->qr) {
+ pr_emerg("QR: Failed to encode data as a QR code!\n");
+ goto ERROR;
+ }
+
+ mutex_lock(&qr_list_mutex);
+ list_add(&element->list, &qr_list_head);
+ mutex_unlock(&qr_list_mutex);
+
+ return;
+ERROR:
+ pr_emerg("QR: Failed to make QR message packet!\n");
+}
+
+struct qr_chunk *tar_slow, *tar_fast, *tar_current;
+int tar_step;
+
+static struct qr_chunk *list_circular_next_entry(struct qr_chunk *it)
+{
+ it = list_next_entry(it, list);
+ if (it == &qr_list_head)
+ it = list_next_entry(it, list);
+
+ return it;
+}
+
+static inline void tar_strategy_next_step(void)
+{
+ ++tar_step;
+ if (tar_step == 4)
+ tar_step = 0;
+
+ if (tar_step == 0) {
+ mutex_lock(&qr_list_mutex);
+ tar_slow = list_circular_next_entry(tar_slow);
+ mutex_unlock(&qr_list_mutex);
+ tar_current = tar_slow;
+ } else if (tar_step == 1) {
+ tar_current = tar_slow;
+ } else if (tar_step == 2) {
+ mutex_lock(&qr_list_mutex);
+ tar_fast = list_circular_next_entry(tar_fast);
+ mutex_unlock(&qr_list_mutex);
+ tar_current = tar_fast;
+ } else if (tar_step == 3) {
+ mutex_lock(&qr_list_mutex);
+ tar_fast = list_circular_next_entry(tar_fast);
+ mutex_unlock(&qr_list_mutex);
+ tar_current = tar_fast;
+ }
+}
+
+static inline void tar_strategy_init(void)
+{
+ tar_step = 0;
+ tar_slow = list_first_entry(&qr_list_head, struct qr_chunk, list);
+ tar_fast = list_first_entry(&qr_list_head, struct qr_chunk, list);
+ tar_current = list_first_entry(&qr_list_head, struct qr_chunk, list);
+}
+
+static inline struct qrcode *tar_strategy_get_qrcode(void)
+{
+ if (tar_current != &qr_list_head)
+ return tar_current->qr;
+ return 0;
+}
+
+#define MESSAGE_DEFAULT_PACKET_SIZE 300
+
+static DEFINE_MUTEX(message_count_mutex);
+
+static int message_count;
+
+static void make_bk1_message(void)
+{
+ int remaining_bytes;
+ int packet_length;
+ int left;
+ int packet_count;
+ int packet_id;
+ int message_id;
+
+ mutex_lock(&message_count_mutex);
+ ++message_count;
+ message_id = message_count;
+ mutex_unlock(&message_count_mutex);
+
+ packet_count = buf_pos / MESSAGE_DEFAULT_PACKET_SIZE;
+ if (buf_pos % MESSAGE_DEFAULT_PACKET_SIZE)
+ ++packet_count;
+
+ left = 0;
+ packet_id = 0;
+ while (left < buf_pos) {
+ remaining_bytes = buf_pos - left;
+ packet_length = MESSAGE_DEFAULT_PACKET_SIZE;
+ if (packet_length > remaining_bytes)
+ packet_length = remaining_bytes;
+ ++packet_id;
+ make_bk1_packet(qr_buffer + left, packet_length, message_id,
+ packet_id, packet_count);
+ left += packet_length;
+ }
+}
+
+static void print_messages(void)
+{
+ struct qr_chunk *it;
+ int last_message_id = -1;
+
+ pr_info("QR: ids of messages in queue: ");
+ list_for_each_entry(it, &qr_list_head, list)
+ if (it->message_id != last_message_id) {
+ last_message_id = it->message_id;
+ pr_cont("%d ", last_message_id);
+ }
+ pr_cont("\n");
+}
+
+static void print_packets(void)
+{
+ struct qr_chunk *it;
+
+ pr_info("QR: packets in queue <message, packet>: ");
+ list_for_each_entry(it, &qr_list_head, list)
+ pr_cont("<%d, %d> ", it->message_id, it->packet_id);
+ pr_cont("\n");
+}
+
+static void print_packets_by_msg(int message_id)
+{
+ struct qr_chunk *it;
+
+ pr_info("QR: packets in queue for message id %d: ", message_id);
+ list_for_each_entry(it, &qr_list_head, list)
+ if (it->message_id == message_id)
+ pr_cont("%d ", it->packet_id);
+ pr_cont("\n");
+}
+
+static struct fb_info *info;
+static struct fb_fillrect rect;
+static int qr_total_width;
+static int qr_offset_x;
+static int qr_offset_y;
+
+static void clear_last_qr(void)
+{
+ if (info) {
+ pr_emerg("QR: framebuffer clear\n");
+
+ console_lock();
+
+ rect.width = qr_total_width;
+ rect.height = qr_total_width;
+ rect.dx = qr_offset_x;
+ rect.dy = qr_offset_y;
+ rect.rop = 0;
+ rect.color = QQQ_BLACK;
+ cfb_fillrect(info, &rect);
+
+ console_unlock();
+ }
+}
+
+#define QR_OOPS_CMD_NOTHING 0
+#define QR_OOPS_CMD_PRINT_MESSAGES 1
+#define QR_OOPS_CMD_PRINT_PACKETS 2
+#define QR_OOPS_CMD_DELETE_MESSAGE 3
+#define QR_OOPS_CMD_DELETE_PACKET 4
+#define QR_OOPS_CMD_PAUSE 5
+#define QR_OOPS_CMD_RESUME 6
+#define QR_OOPS_CMD_CLEAR_QUEUE 7
+#define QR_OOPS_CMD_STOP 8
+#define QR_THREAD_TIME_STEP (750 * 1000)
+
+int qr_thread_func(void *data)
+{
+ struct qrcode *curr_qr = 0;
+ int w;
+ int last_time;
+ int time_accumulator;
+ int time_now;
+ int elapsed_time;
+ int paused = 0;
+ int changed = 0;
+ int cmd = 0;
+ int param0 = 0;
+ int param1 = 0;
+
+ qr_total_width = 0;
+ qr_offset_x = 0;
+ qr_offset_y = 0;
+
+ info = registered_fb[0];
+ if (!info)
+ pr_emerg("QR: Unable to get hand of a framebuffer!\n");
+
+ tar_strategy_init();
+
+ qr_oops_cmd = QR_OOPS_CMD_NOTHING;
+ qr_oops_param0 = 0;
+ qr_oops_param1 = 0;
+
+ last_time = jiffies_to_usecs(get_jiffies_64());
+ time_accumulator = 0;
+
+ while (true) {
+ msleep(100);
+
+ time_now = jiffies_to_usecs(get_jiffies_64());
+ elapsed_time = time_now - last_time;
+ last_time = time_now;
+
+ cmd = qr_oops_cmd;
+ qr_oops_cmd = QR_OOPS_CMD_NOTHING;
+ param0 = qr_oops_param0;
+ param1 = qr_oops_param1;
+
+ if (cmd != QR_OOPS_CMD_NOTHING) {
+ qr_oops_param0 = 0;
+ qr_oops_param1 = 0;
+ }
+
+ switch (cmd) {
+ case QR_OOPS_CMD_NOTHING:
+ break;
+ case QR_OOPS_CMD_PRINT_MESSAGES:
+ print_messages();
+ break;
+ case QR_OOPS_CMD_PRINT_PACKETS:
+ if (param0)
+ print_packets_by_msg(param0);
+ else
+ print_packets();
+ break;
+ case QR_OOPS_CMD_DELETE_MESSAGE:
+ qr_list_delete_message(param0);
+ tar_strategy_init();
+ break;
+ case QR_OOPS_CMD_DELETE_PACKET:
+ qr_list_delete_packet(param0, param1);
+ tar_strategy_init();
+ break;
+ case QR_OOPS_CMD_PAUSE:
+ if (!paused) {
+ paused = 1;
+ clear_last_qr();
+ }
+ break;
+ case QR_OOPS_CMD_RESUME:
+ paused = 0;
+ break;
+ case QR_OOPS_CMD_CLEAR_QUEUE:
+ qr_list_clear();
+ tar_strategy_init();
+ break;
+ case QR_OOPS_CMD_STOP:
+ /*
+ * Not implemented
+ */
+ break;
+ default:
+ pr_emerg("QR: invalid command: %d\n", qr_oops_cmd);
+ break;
+ }
+
+ if (paused)
+ continue;
+
+ changed = 0;
+ time_accumulator += elapsed_time;
+ if (time_accumulator > QR_THREAD_TIME_STEP) {
+ time_accumulator -= QR_THREAD_TIME_STEP;
+
+ tar_strategy_next_step();
+ if (curr_qr != tar_strategy_get_qrcode()) {
+ curr_qr = tar_strategy_get_qrcode();
+ changed = 1;
+ }
+
+ if (changed && info)
+ pr_emerg("QR: force console flush\n");
+ }
+
+ if (!curr_qr) {
+ if (changed)
+ clear_last_qr();
+ continue;
+ }
+
+ if (info) {
+ console_lock();
+
+ rect.width = qr_total_width;
+ rect.height = qr_total_width;
+ rect.dx = qr_offset_x;
+ rect.dy = qr_offset_y;
+ rect.rop = 0;
+ rect.color = QQQ_BLACK;
+ cfb_fillrect(info, &rect);
+
+ w = compute_w(info, curr_qr->width);
+ qr_total_width = (curr_qr->width + 2) * w;
+ qr_offset_x = info->var.xres - qr_total_width;
+ qr_offset_y = 0;
+
+ draw_qr(info, curr_qr, qr_offset_x, qr_offset_y,
+ w, w, 1);
+
+ console_unlock();
+ } else if (changed) {
+ draw_ascii_qr(curr_qr);
+ }
+ }
+
+ return 0;
+}
+
+int qr_thread_init(void)
+{
+ char qr_thread_name[] = "qr_message_thread";
+
+ qr_thread = kthread_create(qr_thread_func, NULL, qr_thread_name);
+ if (qr_thread)
+ wake_up_process(qr_thread);
+
+ return 0;
+}
+
+void qr_thread_cleanup(void)
+{
+ int ret;
+
+ ret = kthread_stop(qr_thread);
+ if (!ret)
+ pr_emerg("QR thread stopped");
+}
+
+void qr_append(char *text)
+{
+ size_t len;
+
+ len = strlen(text);
+ if (len + buf_pos >= MESSAGE_BUFSIZE - 1) {
+ len = MESSAGE_BUFSIZE - 1 - buf_pos;
+ qr_buffer[MESSAGE_BUFSIZE - 1] = '\0';
+ }
+ memcpy(&qr_buffer[buf_pos], text, len);
+ buf_pos += len;
+}
+
+void print_qr_err(void)
+{
+ make_bk1_message();
+
+ buf_pos = 0;
+
+ if (!qr_thread)
+ qr_thread_init();
+}
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 8f0324e..916f3ed 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -46,6 +46,7 @@
#include <linux/utsname.h>
#include <linux/ctype.h>
#include <linux/uio.h>
+#include <linux/print_oops.h>

#include <asm/uaccess.h>

@@ -1750,6 +1751,11 @@ asmlinkage int vprintk_emit(int facility, int level,
}
}

+#ifdef CONFIG_QR_OOPS
+ if (oops_in_progress)
+ qr_append(text);
+#endif
+
if (level == LOGLEVEL_DEFAULT)
level = default_message_loglevel;

@@ -1898,6 +1904,14 @@ asmlinkage __visible int printk(const char *fmt, ...)
va_list args;
int r;

+#ifdef CONFIG_KGDB_KDB
+ if (unlikely(kdb_trap_printk)) {
+ va_start(args, fmt);
+ r = vkdb_printf(KDB_MSGSRC_PRINTK, fmt, args);
+ va_end(args);
+ return r;
+ }
+#endif

Not quite sure what this is doing here, can you split this out into a
separate patch?

va_start(args, fmt);

/*
@@ -1910,7 +1924,6 @@ asmlinkage __visible int printk(const char *fmt, ...)
r = vprintk_func(fmt, args);

va_end(args);
-
return r;
}
EXPORT_SYMBOL(printk);
diff --git a/lib/Kconfig b/lib/Kconfig
index 2e491ac..c782919 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -492,6 +492,12 @@ config SIGNATURE
Digital signature verification. Currently only RSA is supported.
Implementation is done using GnuPG MPI library

+config QRLIB
+ bool "QR encoding library"
+ select ZLIB_DEFLATE
+ help
+ QR encoding library
+
#
# libfdt files, only selected if needed.
#
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index ab76b99..822fe7d 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -15,6 +15,23 @@ config PRINTK_TIME
The behavior is also controlled by the kernel command line
parameter printk.time=1. See Documentation/kernel-parameters.txt

+config QR_OOPS
+ bool "Display QR barcode for Oops messages for debugging purposes"
+ depends on PRINTK && FB
+ select QRLIB
+ select ZLIB_DEFLATE
+ select ZLIB_INFLATE
+ select FB_CFB_FILLRECT
+ select REED_SOLOMON
+ select REED_SOLOMON_ENC8
+ help
+ Selecting this option makes printk() calls to accumulate
+ the Oops messages in a buffer, compresses the message
+ and prints the OR to the frame buffer device.
+
+ This is an experimental feature at the moment.
+
+
config MESSAGE_LOGLEVEL_DEFAULT
int "Default message log level (1-7)"
range 1 7
diff --git a/lib/Makefile b/lib/Makefile
index 13a7c6a..97da07e 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -50,6 +50,9 @@ endif
obj-$(CONFIG_DEBUG_INFO_REDUCED) += debug_info.o
CFLAGS_debug_info.o += $(call cc-option, -femit-struct-debug-detailed=any)

+# QR lib
+obj-$(CONFIG_QRLIB) += qr/
+
obj-$(CONFIG_GENERIC_IOMAP) += iomap.o
obj-$(CONFIG_GENERIC_PCI_IOMAP) += pci_iomap.o
obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o
diff --git a/lib/qr/Makefile b/lib/qr/Makefile
new file mode 100644
index 0000000..d0c0f13
--- /dev/null
+++ b/lib/qr/Makefile
@@ -0,0 +1,6 @@
+#
+# QR encoding library
+#
+
+EXTRA_FLAGS = -g
+obj-$(CONFIG_QRLIB) = bitstream.o mask.o qrencode.o qrinput.o qrspec.o split.o
diff --git a/lib/qr/bitstream.c b/lib/qr/bitstream.c
new file mode 100644
index 0000000..a2f02e9
--- /dev/null
+++ b/lib/qr/bitstream.c
@@ -0,0 +1,203 @@
+/*
+ * bit_stream - storage of bits to which you can append
+ *
+ * Copyright (C) 2014 Levente Kurusa <levex@xxxxxxxxx>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include "bitstream.h"
+
+#define BITS_TO_BYTES(x) (((x) % 8) ? ((x) / 8 + 1) : ((x) / 8))

This function is defined a few places elsewhere in the kernel, it
might be worth it to pull it out to a generic header file (bitops.h?)

+
+u8 *__bit_stream_alloc_data(int len, gfp_t gfp)
+{
+ return kzalloc(BITS_TO_BYTES(len), gfp);
+}

This function doesn't help anything. Just put the kzalloc inline.

+
+struct bit_stream *bit_stream_allocate(int space, gfp_t gfp)
+{
+ struct bit_stream *bstr;
+ u8 *bitmap;
+
+ /* XXX */
+ gfp = GFP_ATOMIC;

I'm guessing this is atomic because it's not possible to determine
the gfp flags?

+
+ bstr = kzalloc(sizeof(*bstr), gfp);
+ if (!bstr)
+ return NULL;
+
+ bstr->gfp = gfp;
+
+ if (space == 0)
+ return bstr;
+
+ bitmap = __bit_stream_alloc_data(space, gfp);
+ if (!bitmap) {
+ kfree(bstr);
+ return NULL;
+ }
+
+ bstr->data = bitmap;
+ bstr->space = space;
+ bstr->length = 0;
+ return bstr;
+}
+
+struct bit_stream *bit_stream_new(void)
+{
+ return bit_stream_allocate(128, GFP_ATOMIC);
+}
+
+void bit_stream_free(struct bit_stream *bstr)
+{
+ if (!bstr)
+ return;
+ kfree(bstr->data);
+ kfree(bstr);
+}
+
+int bit_stream_resize(struct bit_stream *bstr, int nspace)
+{
+ unsigned char *data;
+
+ if (nspace == 0)
+ return -EINVAL;
+
+ if (bstr->length >= nspace)
+ return -ENOSPC;
+
+ data = kzalloc(BITS_TO_BYTES(nspace), bstr->gfp);
+ if (!data)
+ return -ENOMEM;
+
+ if (bstr->data) {
+ memcpy(data, bstr->data, BITS_TO_BYTES(bstr->length));
+ kfree(bstr->data);
+ }
+
+ bstr->data = data;
+ bstr->space = nspace;
+ return 0;
+}
+
+int __bit_stream_get_bit(struct bit_stream *bstr, int no)
+{
+ if (WARN_ON(no > bstr->length))
+ return 0;
+
+ return (bstr->data[no / 8] & (1 << (no % 8))) ? 1 : 0;
+}
+
+int __bit_stream_append_bit(struct bit_stream *bstr, u8 bit)
+{
+ int rc;
+
+ if (!bstr->data || bstr->length + 1 >= bstr->space) {
+ rc = bit_stream_resize(bstr, bstr->space + 256);
+ if (rc)
+ return rc;
+ }
+
+ if (bit != 0)
+ bstr->data[bstr->length / 8] |= (1UL << (bstr->length % 8));
+ else
+ bstr->data[bstr->length / 8] &= ~(1UL << (bstr->length % 8));
+
+ bstr->length++;
+
+ return 0;
+}
+
+int bit_stream_append_bytes(struct bit_stream *bstr, int bytes, u8 *data)
+{
+ int rc;
+ int i, j;
+ unsigned char mask;
+
+ for (i = 0; i < bytes; i++) {
+ mask = 0x80;
+ for (j = 0; j < 8; j++) {
+ rc = __bit_stream_append_bit(bstr, data[i] & mask);
+ if (rc)
+ return rc;
+ mask = mask >> 1;
+ }
+ }
+
+ return 0;
+}
+
+int bit_stream_append_num(struct bit_stream *bstr, int bits, int num)
+{
+ int rc;
+ int i;
+ unsigned int mask;
+
+ mask = 1 << (bits - 1);
+ for (i = 0; i < bits; i++) {
+ rc = __bit_stream_append_bit(bstr, num & mask);
+ if (rc)
+ return rc;
+ mask = mask >> 1;
+ }
+
+ return 0;
+}
+
+int bit_stream_append(struct bit_stream *dst, struct bit_stream *src)
+{
+ int rc, i;
+
+ for (i = 0; i < src->length; i++) {
+ rc = __bit_stream_append_bit(dst, __bit_stream_get_bit(src, i));
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+unsigned char *bit_stream_to_byte(struct bit_stream *bstr)
+{
+ unsigned char *data, v;
+ int i, j, size, bytes, p;
+
+ data = kzalloc((bstr->length + 7) / 8, bstr->gfp);
+ if (!data)
+ return NULL;
+ size = bit_stream_size(bstr);
+ bytes = size / 8;
+ p = 0;
+ for (i = 0; i < bytes; i++) {
+ v = 0;
+ for (j = 0; j < 8; j++) {
+ v = v << 1;
+ v |= __bit_stream_get_bit(bstr, p);
+ p++;
+ }
+ data[i] = v;
+ }
+ if (size & 7) {
+ v = 0;
+ for (j = 0; j < (size & 7); j++) {
+ v = v << 1;
+ v |= __bit_stream_get_bit(bstr, p);
+ p++;
+ }
+ data[bytes] = v;
+ }
+
+ return data;
+}

<snip>

diff --git a/lib/qr/qrinput.c b/lib/qr/qrinput.c
new file mode 100644
index 0000000..a7d9779
--- /dev/null
+++ b/lib/qr/qrinput.c
@@ -0,0 +1,1515 @@
+/*
+ * qrencode - QR Code encoder
+ *
+ * Input data chunk class
+ * Copyright (C) 2014 Levente Kurusa <levex@xxxxxxxxx>
+ * Copyright (C) 2006-2011 Kentaro Fukuchi <kentaro@xxxxxxxxxxx>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <linux/qrencode.h>
+#include "qrspec.h"
+#include "bitstream.h"
+#include "qrinput.h"
+
+/******************************************************************************
+ * Utilities
+ *****************************************************************************/
+int qrinput_is_splittable_mode(enum qrencode_mode mode)
+{
+ return mode >= QR_MODE_NUM;
+}
+
+/******************************************************************************
+ * Entry of input data
+ *****************************************************************************/
+
+static
+struct qrinput_list *qrinput_list_new_entry(enum qrencode_mode mode,
+ int size,
+ const unsigned char *data)
+{
+ struct qrinput_list *entry;
+
+ if (qrinput_check(mode, size, data))
+ return NULL;
+
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return NULL;
+
+ entry->mode = mode;
+ entry->size = size;
+ if (size > 0) {
+ entry->data = kmalloc(size, GFP_ATOMIC);
+ if (!entry->data) {
+ kfree(entry);
+ return NULL;
+ }
+ memcpy(entry->data, data, size);
+ }
+ entry->bstream = NULL;
+ entry->next = NULL;
+
+ return entry;
+}
+
+static void qrinput_list_free_entry(struct qrinput_list *entry)
+{
+ if (!entry) {
+ kfree(entry->data);
+ bit_stream_free(entry->bstream);
+ kfree(entry);
+ }
+}
+
+static struct qrinput_list *qrinput_list_dup(struct qrinput_list *entry)
+{
+ struct qrinput_list *n;
+
+ n = kmalloc(sizeof(*n), GFP_ATOMIC);
+ if (!n)
+ return NULL;
+
+ n->mode = entry->mode;
+ n->size = entry->size;
+ n->data = kmalloc(n->size, GFP_ATOMIC);
+ if (!n->data) {
+ kfree(n);
+ return NULL;
+ }
+ memcpy(n->data, entry->data, entry->size);
+ n->bstream = NULL;
+ n->next = NULL;
+
+ return n;
+}
+
+/******************************************************************************
+ * Input Data
+ *****************************************************************************/
+
+struct qrinput *qrinput_new(void)
+{
+ return qrinput_new2(0, QR_ECLEVEL_L);
+}
+
+struct qrinput *qrinput_new2(int version, enum qrec_level level)
+{
+ struct qrinput *input;
+
+ if (version < 0 || version > QRSPEC_VERSION_MAX || level > QR_ECLEVEL_H)
+ return NULL;
+
+ input = kmalloc(sizeof(*input), GFP_ATOMIC);
+ if (!input)
+ return NULL;
+
+ input->head = NULL;
+ input->tail = NULL;
+ input->version = version;
+ input->level = level;
+ input->fnc1 = 0;
+
+ return input;
+}
+
+int qrinput_get_version(struct qrinput *input)
+{
+ return input->version;
+}
+
+int qrinput_set_version(struct qrinput *input, int version)
+{
+ if (version < 0 || version > QRSPEC_VERSION_MAX)
+ return -1;

This and a bunch of the other functions need to return actual
error codes and not just -1

<snip>

diff --git a/lib/qr/split.c b/lib/qr/split.c
new file mode 100644
index 0000000..922f9a0
--- /dev/null
+++ b/lib/qr/split.c
@@ -0,0 +1,283 @@
+/*
+ * qrencode - QR Code encoder
+ *
+ * Input data splitter.
+ * Copyright (C) 2006-2011 Kentaro Fukuchi <kentaro@xxxxxxxxxxx>
+ *
+ * The following data / specifications are taken from
+ * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
+ * or
+ * "Automatic identification and data capture techniques --
+ * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <linux/qrencode.h>
+#include "qrinput.h"
+#include "qrspec.h"
+#include "split.h"
+
+#define isdigit(__c__) ((unsigned char)((signed char)(__c__) - '0') < 10)
+#define isalnum(__c__) (qrinput_look_an_table(__c__) >= 0)

These are already defined in ctypes.h do, do they need to be redefined?


Thanks,
Laura

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/