2019-05-30 02:57:50 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2007-05-29 16:31:42 +04:00
/*
* MTD Oops / Panic logger
*
2010-08-08 23:58:20 +04:00
* Copyright © 2007 Nokia Corporation . All rights reserved .
2007-05-29 16:31:42 +04:00
*
* Author : Richard Purdie < rpurdie @ openedhand . com >
*/
2022-10-10 07:55:47 +03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2007-05-29 16:31:42 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/console.h>
# include <linux/vmalloc.h>
# include <linux/workqueue.h>
# include <linux/sched.h>
# include <linux/wait.h>
2008-02-06 13:17:50 +03:00
# include <linux/delay.h>
2008-02-07 13:50:57 +03:00
# include <linux/interrupt.h>
2022-04-25 19:09:27 +03:00
# include <linux/timekeeping.h>
2007-05-29 16:31:42 +04:00
# include <linux/mtd/mtd.h>
2009-11-03 16:19:03 +03:00
# include <linux/kmsg_dump.h>
2007-05-29 16:31:42 +04:00
2009-11-03 09:08:41 +03:00
/* Maximum MTD partition size */
# define MTDOOPS_MAX_MTD_SIZE (8 * 1024 * 1024)
2009-10-29 15:41:19 +03:00
static unsigned long record_size = 4096 ;
module_param ( record_size , ulong , 0400 ) ;
MODULE_PARM_DESC ( record_size ,
" record size for MTD OOPS pages in bytes (default 4096) " ) ;
2007-05-29 16:31:42 +04:00
2009-11-03 16:19:03 +03:00
static char mtddev [ 80 ] ;
module_param_string ( mtddev , mtddev , 80 , 0400 ) ;
MODULE_PARM_DESC ( mtddev ,
" name or index number of the MTD device to use " ) ;
static int dump_oops = 1 ;
module_param ( dump_oops , int , 0600 ) ;
MODULE_PARM_DESC ( dump_oops ,
" set to 1 to dump oopses, 0 to only dump panics (default 1) " ) ;
2022-04-25 19:09:27 +03:00
# define MTDOOPS_KERNMSG_MAGIC_v1 0x5d005d00 /* Original */
# define MTDOOPS_KERNMSG_MAGIC_v2 0x5d005e00 /* Adds the timestamp */
2022-04-22 02:42:43 +03:00
struct mtdoops_hdr {
u32 seq ;
u32 magic ;
2022-04-25 19:09:27 +03:00
ktime_t timestamp ;
2022-04-22 02:42:43 +03:00
} __packed ;
2008-04-19 00:44:11 +04:00
static struct mtdoops_context {
2009-11-03 16:19:03 +03:00
struct kmsg_dumper dump ;
2007-05-29 16:31:42 +04:00
int mtd_index ;
2008-01-29 14:27:11 +03:00
struct work_struct work_erase ;
struct work_struct work_write ;
2007-05-29 16:31:42 +04:00
struct mtd_info * mtd ;
int oops_pages ;
int nextpage ;
int nextcount ;
2009-10-29 15:41:11 +03:00
unsigned long * oops_page_used ;
2007-05-29 16:31:42 +04:00
2021-03-03 13:15:15 +03:00
unsigned long oops_buf_busy ;
2007-05-29 16:31:42 +04:00
void * oops_buf ;
} oops_cxt ;
2009-10-29 15:41:11 +03:00
static void mark_page_used ( struct mtdoops_context * cxt , int page )
{
set_bit ( page , cxt - > oops_page_used ) ;
}
static void mark_page_unused ( struct mtdoops_context * cxt , int page )
{
clear_bit ( page , cxt - > oops_page_used ) ;
}
static int page_is_used ( struct mtdoops_context * cxt , int page )
{
return test_bit ( page , cxt - > oops_page_used ) ;
}
static int mtdoops_erase_block ( struct mtdoops_context * cxt , int offset )
2007-05-29 16:31:42 +04:00
{
2009-10-29 15:41:11 +03:00
struct mtd_info * mtd = cxt - > mtd ;
u32 start_page_offset = mtd_div_by_eb ( offset , mtd ) * mtd - > erasesize ;
2009-10-29 15:41:19 +03:00
u32 start_page = start_page_offset / record_size ;
u32 erase_pages = mtd - > erasesize / record_size ;
2007-05-29 16:31:42 +04:00
struct erase_info erase ;
int ret ;
2009-10-29 15:41:11 +03:00
int page ;
2007-05-29 16:31:42 +04:00
erase . addr = offset ;
2008-01-29 13:25:55 +03:00
erase . len = mtd - > erasesize ;
2007-05-29 16:31:42 +04:00
2011-12-23 17:25:39 +04:00
ret = mtd_erase ( mtd , & erase ) ;
2007-05-29 16:31:42 +04:00
if ( ret ) {
2022-10-10 07:55:47 +03:00
pr_warn ( " erase of region [0x%llx, 0x%llx] on \" %s \" failed \n " ,
( unsigned long long ) erase . addr ,
( unsigned long long ) erase . len , mtddev ) ;
2007-05-29 16:31:42 +04:00
return ret ;
}
2009-10-29 15:41:11 +03:00
/* Mark pages as unused */
for ( page = start_page ; page < start_page + erase_pages ; page + + )
mark_page_unused ( cxt , page ) ;
2007-05-29 16:31:42 +04:00
return 0 ;
}
2022-10-10 07:55:48 +03:00
static void mtdoops_erase ( struct mtdoops_context * cxt )
2007-05-29 16:31:42 +04:00
{
struct mtd_info * mtd = cxt - > mtd ;
int i = 0 , j , ret , mod ;
/* We were unregistered */
if ( ! mtd )
return ;
2009-10-29 15:41:19 +03:00
mod = ( cxt - > nextpage * record_size ) % mtd - > erasesize ;
2007-05-29 16:31:42 +04:00
if ( mod ! = 0 ) {
2009-10-29 15:41:19 +03:00
cxt - > nextpage = cxt - > nextpage + ( ( mtd - > erasesize - mod ) / record_size ) ;
2008-07-26 12:17:41 +04:00
if ( cxt - > nextpage > = cxt - > oops_pages )
2007-05-29 16:31:42 +04:00
cxt - > nextpage = 0 ;
}
2012-05-12 00:30:33 +04:00
while ( ( ret = mtd_block_isbad ( mtd , cxt - > nextpage * record_size ) ) > 0 ) {
2007-05-29 16:31:42 +04:00
badblock :
2022-10-10 07:55:47 +03:00
pr_warn ( " bad block at %08lx \n " ,
cxt - > nextpage * record_size ) ;
2007-05-29 16:31:42 +04:00
i + + ;
2009-10-29 15:41:19 +03:00
cxt - > nextpage = cxt - > nextpage + ( mtd - > erasesize / record_size ) ;
2008-07-26 12:17:41 +04:00
if ( cxt - > nextpage > = cxt - > oops_pages )
2007-05-29 16:31:42 +04:00
cxt - > nextpage = 0 ;
2009-10-29 15:41:19 +03:00
if ( i = = cxt - > oops_pages / ( mtd - > erasesize / record_size ) ) {
2022-10-10 07:55:47 +03:00
pr_err ( " all blocks bad! \n " ) ;
2007-05-29 16:31:42 +04:00
return ;
}
}
2012-05-12 00:30:33 +04:00
if ( ret < 0 ) {
2022-10-10 07:55:47 +03:00
pr_err ( " mtd_block_isbad failed, aborting \n " ) ;
2012-05-12 00:30:33 +04:00
return ;
}
2007-05-29 16:31:42 +04:00
for ( j = 0 , ret = - 1 ; ( j < 3 ) & & ( ret < 0 ) ; j + + )
2009-10-29 15:41:19 +03:00
ret = mtdoops_erase_block ( cxt , cxt - > nextpage * record_size ) ;
2007-05-29 16:31:42 +04:00
2008-01-29 14:27:09 +03:00
if ( ret > = 0 ) {
2022-10-10 07:55:47 +03:00
pr_debug ( " ready %d, %d \n " ,
cxt - > nextpage , cxt - > nextcount ) ;
2008-01-29 14:27:09 +03:00
return ;
2007-05-29 16:31:42 +04:00
}
2012-02-03 13:23:57 +04:00
if ( ret = = - EIO ) {
2011-12-23 21:37:38 +04:00
ret = mtd_block_markbad ( mtd , cxt - > nextpage * record_size ) ;
2012-02-03 13:23:57 +04:00
if ( ret < 0 & & ret ! = - EOPNOTSUPP ) {
2022-10-10 07:55:47 +03:00
pr_err ( " block_markbad failed, aborting \n " ) ;
2008-01-29 14:27:09 +03:00
return ;
}
}
goto badblock ;
2007-05-29 16:31:42 +04:00
}
2022-10-10 07:55:48 +03:00
/* Scheduled work - when we can't proceed without erasing a block */
static void mtdoops_workfunc_erase ( struct work_struct * work )
{
struct mtdoops_context * cxt =
container_of ( work , struct mtdoops_context , work_erase ) ;
mtdoops_erase ( cxt ) ;
}
2022-10-10 07:55:49 +03:00
static void mtdoops_inc_counter ( struct mtdoops_context * cxt , int panic )
2022-10-10 07:55:48 +03:00
{
cxt - > nextpage + + ;
if ( cxt - > nextpage > = cxt - > oops_pages )
cxt - > nextpage = 0 ;
cxt - > nextcount + + ;
if ( cxt - > nextcount = = 0xffffffff )
cxt - > nextcount = 0 ;
if ( page_is_used ( cxt , cxt - > nextpage ) ) {
2022-10-10 07:55:49 +03:00
pr_debug ( " not ready %d, %d (erase %s) \n " ,
cxt - > nextpage , cxt - > nextcount ,
panic ? " immediately " : " scheduled " ) ;
if ( panic ) {
/* In case of panic, erase immediately */
mtdoops_erase ( cxt ) ;
} else {
/* Otherwise, schedule work to erase it "nicely" */
schedule_work ( & cxt - > work_erase ) ;
}
} else {
pr_debug ( " ready %d, %d (no erase) \n " ,
cxt - > nextpage , cxt - > nextcount ) ;
2022-10-10 07:55:48 +03:00
}
}
2008-02-06 13:17:50 +03:00
static void mtdoops_write ( struct mtdoops_context * cxt , int panic )
2007-05-29 16:31:42 +04:00
{
2008-01-29 14:27:11 +03:00
struct mtd_info * mtd = cxt - > mtd ;
size_t retlen ;
2022-04-22 02:42:43 +03:00
struct mtdoops_hdr * hdr ;
2008-01-29 14:27:11 +03:00
int ret ;
2007-05-29 16:31:42 +04:00
2021-03-03 13:15:15 +03:00
if ( test_and_set_bit ( 0 , & cxt - > oops_buf_busy ) )
return ;
2009-11-03 16:19:03 +03:00
/* Add mtdoops header to the buffer */
2022-04-22 02:42:43 +03:00
hdr = ( struct mtdoops_hdr * ) cxt - > oops_buf ;
hdr - > seq = cxt - > nextcount ;
2022-04-25 19:09:27 +03:00
hdr - > magic = MTDOOPS_KERNMSG_MAGIC_v2 ;
hdr - > timestamp = ktime_get_real ( ) ;
2008-01-29 14:27:11 +03:00
2011-12-28 19:27:18 +04:00
if ( panic ) {
2011-12-23 20:03:17 +04:00
ret = mtd_panic_write ( mtd , cxt - > nextpage * record_size ,
record_size , & retlen , cxt - > oops_buf ) ;
2011-12-28 19:27:18 +04:00
if ( ret = = - EOPNOTSUPP ) {
2022-10-10 07:55:47 +03:00
pr_err ( " Cannot write from panic without panic_write \n " ) ;
2021-03-03 13:15:15 +03:00
goto out ;
2011-12-28 19:27:18 +04:00
}
} else
2011-12-23 19:35:41 +04:00
ret = mtd_write ( mtd , cxt - > nextpage * record_size ,
record_size , & retlen , cxt - > oops_buf ) ;
2008-01-29 14:27:11 +03:00
2009-10-29 15:41:19 +03:00
if ( retlen ! = record_size | | ret < 0 )
2022-10-10 07:55:47 +03:00
pr_err ( " write failure at %ld (%td of %ld written), error %d \n " ,
2009-10-29 15:41:19 +03:00
cxt - > nextpage * record_size , retlen , record_size , ret ) ;
2009-10-29 15:41:11 +03:00
mark_page_used ( cxt , cxt - > nextpage ) ;
2009-11-03 16:19:03 +03:00
memset ( cxt - > oops_buf , 0xff , record_size ) ;
2008-01-29 14:27:11 +03:00
2022-10-10 07:55:49 +03:00
mtdoops_inc_counter ( cxt , panic ) ;
2021-03-03 13:15:15 +03:00
out :
clear_bit ( 0 , & cxt - > oops_buf_busy ) ;
2008-02-06 13:17:50 +03:00
}
static void mtdoops_workfunc_write ( struct work_struct * work )
{
struct mtdoops_context * cxt =
container_of ( work , struct mtdoops_context , work_write ) ;
mtdoops_write ( cxt , 0 ) ;
2009-10-11 14:40:40 +04:00
}
2007-05-29 16:31:42 +04:00
2008-01-29 14:27:11 +03:00
static void find_next_position ( struct mtdoops_context * cxt )
2007-05-29 16:31:42 +04:00
{
struct mtd_info * mtd = cxt - > mtd ;
2022-04-22 02:42:43 +03:00
struct mtdoops_hdr hdr ;
2008-01-29 14:27:09 +03:00
int ret , page , maxpos = 0 ;
2022-04-22 02:42:43 +03:00
u32 maxcount = 0xffffffff ;
2007-05-29 16:31:42 +04:00
size_t retlen ;
for ( page = 0 ; page < cxt - > oops_pages ; page + + ) {
2012-02-03 13:23:57 +04:00
if ( mtd_block_isbad ( mtd , page * record_size ) )
2011-12-02 17:07:17 +04:00
continue ;
2009-10-29 15:41:11 +03:00
/* Assume the page is used */
mark_page_used ( cxt , page ) ;
2022-04-22 02:42:43 +03:00
ret = mtd_read ( mtd , page * record_size , sizeof ( hdr ) ,
& retlen , ( u_char * ) & hdr ) ;
if ( retlen ! = sizeof ( hdr ) | |
2011-09-21 05:34:25 +04:00
( ret < 0 & & ! mtd_is_bitflip ( ret ) ) ) {
2022-10-10 07:55:47 +03:00
pr_err ( " read failure at %ld (%zu of %zu read), err %d \n " ,
2022-04-22 02:42:43 +03:00
page * record_size , retlen , sizeof ( hdr ) , ret ) ;
2008-01-29 14:27:09 +03:00
continue ;
}
2022-04-22 02:42:43 +03:00
if ( hdr . seq = = 0xffffffff & & hdr . magic = = 0xffffffff )
2009-10-29 15:41:11 +03:00
mark_page_unused ( cxt , page ) ;
2022-04-25 19:09:27 +03:00
if ( hdr . seq = = 0xffffffff | |
( hdr . magic ! = MTDOOPS_KERNMSG_MAGIC_v1 & &
hdr . magic ! = MTDOOPS_KERNMSG_MAGIC_v2 ) )
2007-05-29 16:31:42 +04:00
continue ;
if ( maxcount = = 0xffffffff ) {
2022-04-22 02:42:43 +03:00
maxcount = hdr . seq ;
2007-05-29 16:31:42 +04:00
maxpos = page ;
2022-04-22 02:42:43 +03:00
} else if ( hdr . seq < 0x40000000 & & maxcount > 0xc0000000 ) {
maxcount = hdr . seq ;
2007-05-29 16:31:42 +04:00
maxpos = page ;
2022-04-22 02:42:43 +03:00
} else if ( hdr . seq > maxcount & & hdr . seq < 0xc0000000 ) {
maxcount = hdr . seq ;
2007-05-29 16:31:42 +04:00
maxpos = page ;
2022-04-22 02:42:43 +03:00
} else if ( hdr . seq > maxcount & & hdr . seq > 0xc0000000
2009-10-11 14:40:40 +04:00
& & maxcount > 0x80000000 ) {
2022-04-22 02:42:43 +03:00
maxcount = hdr . seq ;
2007-05-29 16:31:42 +04:00
maxpos = page ;
}
}
if ( maxcount = = 0xffffffff ) {
2012-10-19 19:29:33 +04:00
cxt - > nextpage = cxt - > oops_pages - 1 ;
cxt - > nextcount = 0 ;
}
else {
cxt - > nextpage = maxpos ;
cxt - > nextcount = maxcount ;
2007-05-29 16:31:42 +04:00
}
2022-10-10 07:55:49 +03:00
mtdoops_inc_counter ( cxt , 0 ) ;
2007-05-29 16:31:42 +04:00
}
2009-11-03 16:19:03 +03:00
static void mtdoops_do_dump ( struct kmsg_dumper * dumper ,
2012-06-15 16:07:51 +04:00
enum kmsg_dump_reason reason )
2009-11-03 16:19:03 +03:00
{
struct mtdoops_context * cxt = container_of ( dumper ,
struct mtdoops_context , dump ) ;
2021-03-03 13:15:25 +03:00
struct kmsg_dump_iter iter ;
2011-01-13 03:59:29 +03:00
2009-11-03 16:19:03 +03:00
/* Only dump oopses if dump_oops is set */
if ( reason = = KMSG_DUMP_OOPS & & ! dump_oops )
return ;
2021-03-03 13:15:25 +03:00
kmsg_dump_rewind ( & iter ) ;
2021-03-03 13:15:15 +03:00
if ( test_and_set_bit ( 0 , & cxt - > oops_buf_busy ) )
return ;
2022-04-22 02:42:43 +03:00
kmsg_dump_get_buffer ( & iter , true ,
cxt - > oops_buf + sizeof ( struct mtdoops_hdr ) ,
record_size - sizeof ( struct mtdoops_hdr ) , NULL ) ;
2021-03-03 13:15:15 +03:00
clear_bit ( 0 , & cxt - > oops_buf_busy ) ;
2009-11-03 16:19:03 +03:00
2020-09-03 06:42:17 +03:00
if ( reason ! = KMSG_DUMP_OOPS ) {
/* Panics must be written immediately */
2011-12-28 19:27:18 +04:00
mtdoops_write ( cxt , 1 ) ;
2020-09-03 06:42:17 +03:00
} else {
/* For other cases, schedule work to write it "nicely" */
schedule_work ( & cxt - > work_write ) ;
}
2009-11-03 16:19:03 +03:00
}
2007-05-29 16:31:42 +04:00
static void mtdoops_notify_add ( struct mtd_info * mtd )
{
struct mtdoops_context * cxt = & oops_cxt ;
2009-11-03 16:19:03 +03:00
u64 mtdoops_pages = div_u64 ( mtd - > size , record_size ) ;
int err ;
2007-05-29 16:31:42 +04:00
2009-11-03 16:19:03 +03:00
if ( ! strcmp ( mtd - > name , mtddev ) )
2009-02-16 19:21:35 +03:00
cxt - > mtd_index = mtd - > index ;
2009-10-11 14:40:40 +04:00
if ( mtd - > index ! = cxt - > mtd_index | | cxt - > mtd_index < 0 )
2007-05-29 16:31:42 +04:00
return ;
2009-10-11 14:40:40 +04:00
if ( mtd - > size < mtd - > erasesize * 2 ) {
2022-10-10 07:55:47 +03:00
pr_err ( " MTD partition %d not big enough for mtdoops \n " ,
2009-10-11 14:40:40 +04:00
mtd - > index ) ;
2007-05-29 16:31:42 +04:00
return ;
}
2009-10-29 15:41:19 +03:00
if ( mtd - > erasesize < record_size ) {
2022-10-10 07:55:47 +03:00
pr_err ( " eraseblock size of MTD partition %d too small \n " ,
2009-10-11 14:40:40 +04:00
mtd - > index ) ;
2008-01-29 13:25:55 +03:00
return ;
}
2009-11-03 09:08:41 +03:00
if ( mtd - > size > MTDOOPS_MAX_MTD_SIZE ) {
2022-10-10 07:55:47 +03:00
pr_err ( " mtd%d is too large (limit is %d MiB) \n " ,
2009-11-03 09:08:41 +03:00
mtd - > index , MTDOOPS_MAX_MTD_SIZE / 1024 / 1024 ) ;
return ;
}
2009-10-29 15:41:11 +03:00
/* oops_page_used is a bit field */
treewide: Use array_size() in vmalloc()
The vmalloc() function has no 2-factor argument form, so multiplication
factors need to be wrapped in array_size(). This patch replaces cases of:
vmalloc(a * b)
with:
vmalloc(array_size(a, b))
as well as handling cases of:
vmalloc(a * b * c)
with:
vmalloc(array3_size(a, b, c))
This does, however, attempt to ignore constant size factors like:
vmalloc(4 * 1024)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
vmalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
vmalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
vmalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
vmalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
vmalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
vmalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
vmalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
vmalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
vmalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
vmalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
vmalloc(
- sizeof(TYPE) * (COUNT_ID)
+ array_size(COUNT_ID, sizeof(TYPE))
, ...)
|
vmalloc(
- sizeof(TYPE) * COUNT_ID
+ array_size(COUNT_ID, sizeof(TYPE))
, ...)
|
vmalloc(
- sizeof(TYPE) * (COUNT_CONST)
+ array_size(COUNT_CONST, sizeof(TYPE))
, ...)
|
vmalloc(
- sizeof(TYPE) * COUNT_CONST
+ array_size(COUNT_CONST, sizeof(TYPE))
, ...)
|
vmalloc(
- sizeof(THING) * (COUNT_ID)
+ array_size(COUNT_ID, sizeof(THING))
, ...)
|
vmalloc(
- sizeof(THING) * COUNT_ID
+ array_size(COUNT_ID, sizeof(THING))
, ...)
|
vmalloc(
- sizeof(THING) * (COUNT_CONST)
+ array_size(COUNT_CONST, sizeof(THING))
, ...)
|
vmalloc(
- sizeof(THING) * COUNT_CONST
+ array_size(COUNT_CONST, sizeof(THING))
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
vmalloc(
- SIZE * COUNT
+ array_size(COUNT, SIZE)
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
vmalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
vmalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
vmalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
vmalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
vmalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
vmalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
vmalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
vmalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
vmalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
vmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
vmalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
vmalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
vmalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
vmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
vmalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vmalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vmalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vmalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vmalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vmalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vmalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vmalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
vmalloc(C1 * C2 * C3, ...)
|
vmalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants.
@@
expression E1, E2;
constant C1, C2;
@@
(
vmalloc(C1 * C2, ...)
|
vmalloc(
- E1 * E2
+ array_size(E1, E2)
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:27:11 +03:00
cxt - > oops_page_used =
vmalloc ( array_size ( sizeof ( unsigned long ) ,
DIV_ROUND_UP ( mtdoops_pages ,
BITS_PER_LONG ) ) ) ;
2009-10-29 15:41:11 +03:00
if ( ! cxt - > oops_page_used ) {
2022-10-10 07:55:47 +03:00
pr_err ( " could not allocate page array \n " ) ;
2009-11-03 16:19:03 +03:00
return ;
}
2012-06-15 16:07:51 +04:00
cxt - > dump . max_reason = KMSG_DUMP_OOPS ;
2009-11-03 16:19:03 +03:00
cxt - > dump . dump = mtdoops_do_dump ;
err = kmsg_dump_register ( & cxt - > dump ) ;
if ( err ) {
2022-10-10 07:55:47 +03:00
pr_err ( " registering kmsg dumper failed, error %d \n " , err ) ;
2009-11-03 16:19:03 +03:00
vfree ( cxt - > oops_page_used ) ;
cxt - > oops_page_used = NULL ;
2009-10-29 15:41:11 +03:00
return ;
}
2007-05-29 16:31:42 +04:00
2009-11-03 09:08:41 +03:00
cxt - > mtd = mtd ;
2009-10-29 15:41:19 +03:00
cxt - > oops_pages = ( int ) mtd - > size / record_size ;
2008-01-29 14:27:11 +03:00
find_next_position ( cxt ) ;
2022-10-10 07:55:47 +03:00
pr_info ( " Attached to MTD device %d \n " , mtd - > index ) ;
2007-05-29 16:31:42 +04:00
}
static void mtdoops_notify_remove ( struct mtd_info * mtd )
{
struct mtdoops_context * cxt = & oops_cxt ;
2009-10-11 14:40:40 +04:00
if ( mtd - > index ! = cxt - > mtd_index | | cxt - > mtd_index < 0 )
2007-05-29 16:31:42 +04:00
return ;
2009-11-03 16:19:03 +03:00
if ( kmsg_dump_unregister ( & cxt - > dump ) < 0 )
2022-10-10 07:55:47 +03:00
pr_warn ( " could not unregister kmsg_dumper \n " ) ;
2009-11-03 16:19:03 +03:00
2007-05-29 16:31:42 +04:00
cxt - > mtd = NULL ;
2012-08-21 01:51:24 +04:00
flush_work ( & cxt - > work_erase ) ;
flush_work ( & cxt - > work_write ) ;
2007-05-29 16:31:42 +04:00
}
static struct mtd_notifier mtdoops_notifier = {
. add = mtdoops_notify_add ,
. remove = mtdoops_notify_remove ,
} ;
2009-11-03 16:19:03 +03:00
static int __init mtdoops_init ( void )
2007-05-29 16:31:42 +04:00
{
struct mtdoops_context * cxt = & oops_cxt ;
2009-11-03 16:19:03 +03:00
int mtd_index ;
char * endp ;
2007-05-29 16:31:42 +04:00
2009-11-03 16:19:03 +03:00
if ( strlen ( mtddev ) = = 0 ) {
2022-10-10 07:55:47 +03:00
pr_err ( " mtd device (mtddev=name/number) must be supplied \n " ) ;
2009-11-03 16:19:03 +03:00
return - EINVAL ;
}
2009-10-29 15:41:19 +03:00
if ( ( record_size & 4095 ) ! = 0 ) {
2022-10-10 07:55:47 +03:00
pr_err ( " record_size must be a multiple of 4096 \n " ) ;
2009-10-29 15:41:19 +03:00
return - EINVAL ;
}
if ( record_size < 4096 ) {
2022-10-10 07:55:47 +03:00
pr_err ( " record_size must be over 4096 bytes \n " ) ;
2009-10-29 15:41:19 +03:00
return - EINVAL ;
}
2009-11-03 16:19:03 +03:00
/* Setup the MTD device to use */
2007-05-29 16:31:42 +04:00
cxt - > mtd_index = - 1 ;
2009-11-03 16:19:03 +03:00
mtd_index = simple_strtoul ( mtddev , & endp , 0 ) ;
if ( * endp = = ' \0 ' )
cxt - > mtd_index = mtd_index ;
2009-10-29 15:41:19 +03:00
cxt - > oops_buf = vmalloc ( record_size ) ;
2021-06-10 05:12:01 +03:00
if ( ! cxt - > oops_buf )
2007-05-29 16:31:42 +04:00
return - ENOMEM ;
2009-11-03 16:19:03 +03:00
memset ( cxt - > oops_buf , 0xff , record_size ) ;
2021-03-03 13:15:15 +03:00
cxt - > oops_buf_busy = 0 ;
2007-05-29 16:31:42 +04:00
2008-01-29 14:27:11 +03:00
INIT_WORK ( & cxt - > work_erase , mtdoops_workfunc_erase ) ;
INIT_WORK ( & cxt - > work_write , mtdoops_workfunc_write ) ;
2007-05-29 16:31:42 +04:00
register_mtd_user ( & mtdoops_notifier ) ;
return 0 ;
}
2009-11-03 16:19:03 +03:00
static void __exit mtdoops_exit ( void )
2007-05-29 16:31:42 +04:00
{
struct mtdoops_context * cxt = & oops_cxt ;
unregister_mtd_user ( & mtdoops_notifier ) ;
vfree ( cxt - > oops_buf ) ;
2009-10-29 15:41:11 +03:00
vfree ( cxt - > oops_page_used ) ;
2007-05-29 16:31:42 +04:00
}
2009-11-03 16:19:03 +03:00
module_init ( mtdoops_init ) ;
module_exit ( mtdoops_exit ) ;
2007-05-29 16:31:42 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Richard Purdie <rpurdie@openedhand.com> " ) ;
MODULE_DESCRIPTION ( " MTD Oops/Panic console logger/driver " ) ;