lkml.org 
[lkml]   [2015]   [Nov]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
SubjectRe: [RFC][PATCH] Oops messages transfer using QR codes
Date
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



\
 
 \ /
  Last update: 2015-11-02 20:21    [W:0.093 / U:0.440 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site