2023-10-25 22:51:16 +03:00
// SPDX-License-Identifier: GPL-2.0
# include "bcachefs.h"
# include "sb-errors.h"
# include "super-io.h"
static const char * const bch2_sb_error_strs [ ] = {
# define x(t, n, ...) [n] = #t,
BCH_SB_ERRS ( )
NULL
} ;
static void bch2_sb_error_id_to_text ( struct printbuf * out , enum bch_sb_error_id id )
{
if ( id < BCH_SB_ERR_MAX )
prt_str ( out , bch2_sb_error_strs [ id ] ) ;
else
prt_printf ( out , " (unknown error %u) " , id ) ;
}
static inline unsigned bch2_sb_field_errors_nr_entries ( struct bch_sb_field_errors * e )
{
return e
? ( bch2_sb_field_bytes ( & e - > field ) - sizeof ( * e ) ) / sizeof ( e - > entries [ 0 ] )
: 0 ;
}
static inline unsigned bch2_sb_field_errors_u64s ( unsigned nr )
{
return ( sizeof ( struct bch_sb_field_errors ) +
sizeof ( struct bch_sb_field_error_entry ) * nr ) / sizeof ( u64 ) ;
}
static int bch2_sb_errors_validate ( struct bch_sb * sb , struct bch_sb_field * f ,
struct printbuf * err )
{
struct bch_sb_field_errors * e = field_to_type ( f , errors ) ;
unsigned i , nr = bch2_sb_field_errors_nr_entries ( e ) ;
for ( i = 0 ; i < nr ; i + + ) {
if ( ! BCH_SB_ERROR_ENTRY_NR ( & e - > entries [ i ] ) ) {
prt_printf ( err , " entry with count 0 (id " ) ;
bch2_sb_error_id_to_text ( err , BCH_SB_ERROR_ENTRY_ID ( & e - > entries [ i ] ) ) ;
prt_printf ( err , " ) " ) ;
return - BCH_ERR_invalid_sb_errors ;
}
if ( i + 1 < nr & &
BCH_SB_ERROR_ENTRY_ID ( & e - > entries [ i ] ) > =
BCH_SB_ERROR_ENTRY_ID ( & e - > entries [ i + 1 ] ) ) {
prt_printf ( err , " entries out of order " ) ;
return - BCH_ERR_invalid_sb_errors ;
}
}
return 0 ;
}
static void bch2_sb_errors_to_text ( struct printbuf * out , struct bch_sb * sb ,
struct bch_sb_field * f )
{
struct bch_sb_field_errors * e = field_to_type ( f , errors ) ;
unsigned i , nr = bch2_sb_field_errors_nr_entries ( e ) ;
if ( out - > nr_tabstops < = 1 )
printbuf_tabstop_push ( out , 16 ) ;
for ( i = 0 ; i < nr ; i + + ) {
bch2_sb_error_id_to_text ( out , BCH_SB_ERROR_ENTRY_ID ( & e - > entries [ i ] ) ) ;
prt_tab ( out ) ;
prt_u64 ( out , BCH_SB_ERROR_ENTRY_NR ( & e - > entries [ i ] ) ) ;
prt_tab ( out ) ;
2023-11-01 06:43:47 +03:00
bch2_prt_datetime ( out , le64_to_cpu ( e - > entries [ i ] . last_error_time ) ) ;
2023-10-25 22:51:16 +03:00
prt_newline ( out ) ;
}
}
const struct bch_sb_field_ops bch_sb_field_ops_errors = {
. validate = bch2_sb_errors_validate ,
. to_text = bch2_sb_errors_to_text ,
} ;
void bch2_sb_error_count ( struct bch_fs * c , enum bch_sb_error_id err )
{
bch_sb_errors_cpu * e = & c - > fsck_error_counts ;
struct bch_sb_error_entry_cpu n = {
. id = err ,
. nr = 1 ,
. last_error_time = ktime_get_real_seconds ( )
} ;
unsigned i ;
mutex_lock ( & c - > fsck_error_counts_lock ) ;
for ( i = 0 ; i < e - > nr ; i + + ) {
if ( err = = e - > data [ i ] . id ) {
e - > data [ i ] . nr + + ;
e - > data [ i ] . last_error_time = n . last_error_time ;
goto out ;
}
if ( err < e - > data [ i ] . id )
break ;
}
if ( darray_make_room ( e , 1 ) )
goto out ;
darray_insert_item ( e , i , n ) ;
out :
mutex_unlock ( & c - > fsck_error_counts_lock ) ;
}
void bch2_sb_errors_from_cpu ( struct bch_fs * c )
{
bch_sb_errors_cpu * src = & c - > fsck_error_counts ;
struct bch_sb_field_errors * dst =
bch2_sb_field_resize ( & c - > disk_sb , errors ,
bch2_sb_field_errors_u64s ( src - > nr ) ) ;
unsigned i ;
if ( ! dst )
return ;
for ( i = 0 ; i < src - > nr ; i + + ) {
SET_BCH_SB_ERROR_ENTRY_ID ( & dst - > entries [ i ] , src - > data [ i ] . id ) ;
SET_BCH_SB_ERROR_ENTRY_NR ( & dst - > entries [ i ] , src - > data [ i ] . nr ) ;
dst - > entries [ i ] . last_error_time = cpu_to_le64 ( src - > data [ i ] . last_error_time ) ;
}
}
static int bch2_sb_errors_to_cpu ( struct bch_fs * c )
{
struct bch_sb_field_errors * src = bch2_sb_field_get ( c - > disk_sb . sb , errors ) ;
bch_sb_errors_cpu * dst = & c - > fsck_error_counts ;
unsigned i , nr = bch2_sb_field_errors_nr_entries ( src ) ;
int ret ;
if ( ! nr )
return 0 ;
mutex_lock ( & c - > fsck_error_counts_lock ) ;
ret = darray_make_room ( dst , nr ) ;
if ( ret )
goto err ;
dst - > nr = nr ;
for ( i = 0 ; i < nr ; i + + ) {
dst - > data [ i ] . id = BCH_SB_ERROR_ENTRY_ID ( & src - > entries [ i ] ) ;
dst - > data [ i ] . nr = BCH_SB_ERROR_ENTRY_NR ( & src - > entries [ i ] ) ;
dst - > data [ i ] . last_error_time = le64_to_cpu ( src - > entries [ i ] . last_error_time ) ;
}
err :
mutex_unlock ( & c - > fsck_error_counts_lock ) ;
return ret ;
}
void bch2_fs_sb_errors_exit ( struct bch_fs * c )
{
darray_exit ( & c - > fsck_error_counts ) ;
}
void bch2_fs_sb_errors_init_early ( struct bch_fs * c )
{
mutex_init ( & c - > fsck_error_counts_lock ) ;
darray_init ( & c - > fsck_error_counts ) ;
}
int bch2_fs_sb_errors_init ( struct bch_fs * c )
{
return bch2_sb_errors_to_cpu ( c ) ;
}