Messages in this thread Patch in this message | | | From | Laura Abbott <> | Subject | Re: [RFC][PATCH] Oops messages transfer using QR codes | Date | Mon, 2 Nov 2015 10:56:00 -0800 |
| |
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@fukuchi.org> > + * 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@intel.com> > + * Copyright (C) 2014 Teodora Baluta <teobaluta@gmail.com> > + * Copyright (C) 2014 Levente Kurusa <levex@linux.com> > + * > + * 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@linux.com> > + * > + * 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@linux.com> > + * Copyright (C) 2006-2011 Kentaro Fukuchi <kentaro@fukuchi.org> > + * > + * 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@fukuchi.org> > + * > + * 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
| |