2019-03-08 03:46:10 +03:00
# include "bcachefs.h"
2020-06-16 02:53:46 +03:00
# include "btree_cache.h"
2019-03-08 03:46:10 +03:00
# include "btree_iter.h"
# include "btree_key_cache.h"
# include "btree_locking.h"
# include "btree_update.h"
# include "error.h"
# include "journal.h"
# include "journal_reclaim.h"
# include "trace.h"
2020-11-13 01:19:47 +03:00
# include <linux/sched/mm.h>
2020-11-18 22:09:33 +03:00
static struct kmem_cache * bch2_key_cache ;
2019-03-08 03:46:10 +03:00
static int bch2_btree_key_cache_cmp_fn ( struct rhashtable_compare_arg * arg ,
const void * obj )
{
const struct bkey_cached * ck = obj ;
const struct bkey_cached_key * key = arg - > key ;
return cmp_int ( ck - > key . btree_id , key - > btree_id ) ? :
2021-03-05 00:20:16 +03:00
bpos_cmp ( ck - > key . pos , key - > pos ) ;
2019-03-08 03:46:10 +03:00
}
static const struct rhashtable_params bch2_btree_key_cache_params = {
. head_offset = offsetof ( struct bkey_cached , hash ) ,
. key_offset = offsetof ( struct bkey_cached , key ) ,
. key_len = sizeof ( struct bkey_cached_key ) ,
. obj_cmpfn = bch2_btree_key_cache_cmp_fn ,
} ;
__flatten
2019-09-23 02:10:21 +03:00
inline struct bkey_cached *
bch2_btree_key_cache_find ( struct bch_fs * c , enum btree_id btree_id , struct bpos pos )
2019-03-08 03:46:10 +03:00
{
struct bkey_cached_key key = {
. btree_id = btree_id ,
. pos = pos ,
} ;
return rhashtable_lookup_fast ( & c - > btree_key_cache . table , & key ,
bch2_btree_key_cache_params ) ;
}
static bool bkey_cached_lock_for_evict ( struct bkey_cached * ck )
{
if ( ! six_trylock_intent ( & ck - > c . lock ) )
return false ;
if ( ! six_trylock_write ( & ck - > c . lock ) ) {
six_unlock_intent ( & ck - > c . lock ) ;
return false ;
}
if ( test_bit ( BKEY_CACHED_DIRTY , & ck - > flags ) ) {
six_unlock_write ( & ck - > c . lock ) ;
six_unlock_intent ( & ck - > c . lock ) ;
return false ;
}
return true ;
}
static void bkey_cached_evict ( struct btree_key_cache * c ,
struct bkey_cached * ck )
{
BUG_ON ( rhashtable_remove_fast ( & c - > table , & ck - > hash ,
bch2_btree_key_cache_params ) ) ;
memset ( & ck - > key , ~ 0 , sizeof ( ck - > key ) ) ;
2020-11-09 21:01:52 +03:00
2021-03-25 06:37:33 +03:00
atomic_long_dec ( & c - > nr_keys ) ;
2019-03-08 03:46:10 +03:00
}
2020-11-13 01:19:47 +03:00
static void bkey_cached_free ( struct btree_key_cache * bc ,
2019-03-08 03:46:10 +03:00
struct bkey_cached * ck )
{
2020-11-13 01:19:47 +03:00
struct bch_fs * c = container_of ( bc , struct bch_fs , btree_key_cache ) ;
2020-11-19 23:38:27 +03:00
BUG_ON ( test_bit ( BKEY_CACHED_DIRTY , & ck - > flags ) ) ;
2020-11-13 01:19:47 +03:00
ck - > btree_trans_barrier_seq =
start_poll_synchronize_srcu ( & c - > btree_trans_barrier ) ;
2020-11-19 23:38:27 +03:00
list_move_tail ( & ck - > list , & bc - > freed ) ;
bc - > nr_freed + + ;
2019-03-08 03:46:10 +03:00
kfree ( ck - > k ) ;
ck - > k = NULL ;
ck - > u64s = 0 ;
six_unlock_write ( & ck - > c . lock ) ;
six_unlock_intent ( & ck - > c . lock ) ;
}
static struct bkey_cached *
bkey_cached_alloc ( struct btree_key_cache * c )
{
struct bkey_cached * ck ;
2020-11-19 23:38:27 +03:00
ck = kmem_cache_alloc ( bch2_key_cache , GFP_NOFS | __GFP_ZERO ) ;
if ( likely ( ck ) ) {
INIT_LIST_HEAD ( & ck - > list ) ;
six_lock_init ( & ck - > c . lock ) ;
lockdep_set_novalidate_class ( & ck - > c . lock ) ;
BUG_ON ( ! six_trylock_intent ( & ck - > c . lock ) ) ;
BUG_ON ( ! six_trylock_write ( & ck - > c . lock ) ) ;
return ck ;
}
2019-03-08 03:46:10 +03:00
2021-03-25 06:37:33 +03:00
return NULL ;
}
static struct bkey_cached *
bkey_cached_reuse ( struct btree_key_cache * c )
{
struct bucket_table * tbl ;
struct rhash_head * pos ;
struct bkey_cached * ck ;
unsigned i ;
mutex_lock ( & c - > lock ) ;
list_for_each_entry_reverse ( ck , & c - > freed , list )
2019-03-08 03:46:10 +03:00
if ( bkey_cached_lock_for_evict ( ck ) ) {
2021-03-25 06:37:33 +03:00
c - > nr_freed - - ;
list_del ( & ck - > list ) ;
mutex_unlock ( & c - > lock ) ;
2019-03-08 03:46:10 +03:00
return ck ;
}
2021-03-25 06:37:33 +03:00
mutex_unlock ( & c - > lock ) ;
rcu_read_lock ( ) ;
tbl = rht_dereference_rcu ( c - > table . tbl , & c - > table ) ;
for ( i = 0 ; i < tbl - > size ; i + + )
rht_for_each_entry_rcu ( ck , pos , tbl , i , hash ) {
if ( ! test_bit ( BKEY_CACHED_DIRTY , & ck - > flags ) & &
bkey_cached_lock_for_evict ( ck ) ) {
bkey_cached_evict ( c , ck ) ;
rcu_read_unlock ( ) ;
return ck ;
}
}
rcu_read_unlock ( ) ;
2019-03-08 03:46:10 +03:00
2020-11-19 23:38:27 +03:00
return NULL ;
2019-03-08 03:46:10 +03:00
}
static struct bkey_cached *
btree_key_cache_create ( struct btree_key_cache * c ,
enum btree_id btree_id ,
struct bpos pos )
{
struct bkey_cached * ck ;
2021-03-25 06:37:33 +03:00
bool was_new = true ;
2019-03-08 03:46:10 +03:00
ck = bkey_cached_alloc ( c ) ;
2021-03-25 06:37:33 +03:00
if ( unlikely ( ! ck ) ) {
ck = bkey_cached_reuse ( c ) ;
if ( unlikely ( ! ck ) )
return ERR_PTR ( - ENOMEM ) ;
was_new = false ;
}
2019-03-08 03:46:10 +03:00
ck - > c . level = 0 ;
ck - > c . btree_id = btree_id ;
ck - > key . btree_id = btree_id ;
ck - > key . pos = pos ;
ck - > valid = false ;
2020-11-19 23:38:27 +03:00
ck - > flags = 1U < < BKEY_CACHED_ACCESSED ;
2019-03-08 03:46:10 +03:00
2021-03-25 06:37:33 +03:00
if ( unlikely ( rhashtable_lookup_insert_fast ( & c - > table ,
2019-03-08 03:46:10 +03:00
& ck - > hash ,
2021-03-25 06:37:33 +03:00
bch2_btree_key_cache_params ) ) ) {
2019-03-08 03:46:10 +03:00
/* We raced with another fill: */
2021-03-25 06:37:33 +03:00
if ( likely ( was_new ) ) {
six_unlock_write ( & ck - > c . lock ) ;
six_unlock_intent ( & ck - > c . lock ) ;
kfree ( ck ) ;
} else {
mutex_lock ( & c - > lock ) ;
bkey_cached_free ( c , ck ) ;
mutex_unlock ( & c - > lock ) ;
}
2019-03-08 03:46:10 +03:00
return NULL ;
}
2021-03-25 06:37:33 +03:00
atomic_long_inc ( & c - > nr_keys ) ;
2020-11-09 21:01:52 +03:00
2019-03-08 03:46:10 +03:00
six_unlock_write ( & ck - > c . lock ) ;
return ck ;
}
static int btree_key_cache_fill ( struct btree_trans * trans ,
struct btree_iter * ck_iter ,
struct bkey_cached * ck )
{
struct btree_iter * iter ;
struct bkey_s_c k ;
unsigned new_u64s = 0 ;
struct bkey_i * new_k = NULL ;
int ret ;
iter = bch2_trans_get_iter ( trans , ck - > key . btree_id ,
ck - > key . pos , BTREE_ITER_SLOTS ) ;
k = bch2_btree_iter_peek_slot ( iter ) ;
ret = bkey_err ( k ) ;
2021-03-20 05:54:18 +03:00
if ( ret )
goto err ;
2019-03-08 03:46:10 +03:00
if ( ! bch2_btree_node_relock ( ck_iter , 0 ) ) {
trace_transaction_restart_ip ( trans - > ip , _THIS_IP_ ) ;
2021-03-20 05:54:18 +03:00
ret = - EINTR ;
goto err ;
2019-03-08 03:46:10 +03:00
}
if ( k . k - > u64s > ck - > u64s ) {
new_u64s = roundup_pow_of_two ( k . k - > u64s ) ;
new_k = kmalloc ( new_u64s * sizeof ( u64 ) , GFP_NOFS ) ;
if ( ! new_k ) {
2021-03-20 05:54:18 +03:00
ret = - ENOMEM ;
goto err ;
2019-03-08 03:46:10 +03:00
}
}
bch2_btree_node_lock_write ( ck_iter - > l [ 0 ] . b , ck_iter ) ;
if ( new_k ) {
kfree ( ck - > k ) ;
ck - > u64s = new_u64s ;
ck - > k = new_k ;
}
bkey_reassemble ( ck - > k , k ) ;
ck - > valid = true ;
bch2_btree_node_unlock_write ( ck_iter - > l [ 0 ] . b , ck_iter ) ;
/* We're not likely to need this iterator again: */
2021-03-20 05:54:18 +03:00
set_btree_iter_dontneed ( trans , iter ) ;
err :
bch2_trans_iter_put ( trans , iter ) ;
return ret ;
2019-03-08 03:46:10 +03:00
}
static int bkey_cached_check_fn ( struct six_lock * lock , void * p )
{
struct bkey_cached * ck = container_of ( lock , struct bkey_cached , c . lock ) ;
const struct btree_iter * iter = p ;
return ck - > key . btree_id = = iter - > btree_id & &
2021-03-05 00:20:16 +03:00
! bpos_cmp ( ck - > key . pos , iter - > pos ) ? 0 : - 1 ;
2019-03-08 03:46:10 +03:00
}
2020-11-06 09:34:41 +03:00
__flatten
2019-03-08 03:46:10 +03:00
int bch2_btree_iter_traverse_cached ( struct btree_iter * iter )
{
struct btree_trans * trans = iter - > trans ;
struct bch_fs * c = trans - > c ;
struct bkey_cached * ck ;
int ret = 0 ;
BUG_ON ( iter - > level ) ;
if ( btree_node_locked ( iter , 0 ) ) {
ck = ( void * ) iter - > l [ 0 ] . b ;
goto fill ;
}
retry :
2019-09-23 02:10:21 +03:00
ck = bch2_btree_key_cache_find ( c , iter - > btree_id , iter - > pos ) ;
2019-03-08 03:46:10 +03:00
if ( ! ck ) {
if ( iter - > flags & BTREE_ITER_CACHED_NOCREATE ) {
iter - > l [ 0 ] . b = NULL ;
return 0 ;
}
ck = btree_key_cache_create ( & c - > btree_key_cache ,
iter - > btree_id , iter - > pos ) ;
ret = PTR_ERR_OR_ZERO ( ck ) ;
if ( ret )
goto err ;
if ( ! ck )
goto retry ;
mark_btree_node_locked ( iter , 0 , SIX_LOCK_intent ) ;
iter - > locks_want = 1 ;
} else {
enum six_lock_type lock_want = __btree_lock_want ( iter , 0 ) ;
if ( ! btree_node_lock ( ( void * ) ck , iter - > pos , 0 , iter , lock_want ,
2020-10-28 21:17:46 +03:00
bkey_cached_check_fn , iter , _THIS_IP_ ) ) {
2019-03-08 03:46:10 +03:00
if ( ck - > key . btree_id ! = iter - > btree_id | |
2021-03-05 00:20:16 +03:00
bpos_cmp ( ck - > key . pos , iter - > pos ) ) {
2019-03-08 03:46:10 +03:00
goto retry ;
}
trace_transaction_restart_ip ( trans - > ip , _THIS_IP_ ) ;
ret = - EINTR ;
goto err ;
}
if ( ck - > key . btree_id ! = iter - > btree_id | |
2021-03-05 00:20:16 +03:00
bpos_cmp ( ck - > key . pos , iter - > pos ) ) {
2019-03-08 03:46:10 +03:00
six_unlock_type ( & ck - > c . lock , lock_want ) ;
goto retry ;
}
mark_btree_node_locked ( iter , 0 , lock_want ) ;
}
iter - > l [ 0 ] . lock_seq = ck - > c . lock . state . seq ;
iter - > l [ 0 ] . b = ( void * ) ck ;
fill :
if ( ! ck - > valid & & ! ( iter - > flags & BTREE_ITER_CACHED_NOFILL ) ) {
if ( ! btree_node_intent_locked ( iter , 0 ) )
bch2_btree_iter_upgrade ( iter , 1 ) ;
if ( ! btree_node_intent_locked ( iter , 0 ) ) {
trace_transaction_restart_ip ( trans - > ip , _THIS_IP_ ) ;
ret = - EINTR ;
goto err ;
}
ret = btree_key_cache_fill ( trans , iter , ck ) ;
if ( ret )
goto err ;
}
2020-11-19 23:38:27 +03:00
if ( ! test_bit ( BKEY_CACHED_ACCESSED , & ck - > flags ) )
set_bit ( BKEY_CACHED_ACCESSED , & ck - > flags ) ;
2019-03-08 03:46:10 +03:00
iter - > uptodate = BTREE_ITER_NEED_PEEK ;
2021-03-09 01:09:13 +03:00
if ( ! ( iter - > flags & BTREE_ITER_INTENT ) )
bch2_btree_iter_downgrade ( iter ) ;
else if ( ! iter - > locks_want ) {
if ( ! __bch2_btree_iter_upgrade ( iter , 1 ) )
ret = - EINTR ;
}
2019-03-08 03:46:10 +03:00
return ret ;
err :
if ( ret ! = - EINTR ) {
btree_node_unlock ( iter , 0 ) ;
iter - > flags | = BTREE_ITER_ERROR ;
iter - > l [ 0 ] . b = BTREE_ITER_NO_NODE_ERROR ;
}
return ret ;
}
static int btree_key_cache_flush_pos ( struct btree_trans * trans ,
struct bkey_cached_key key ,
u64 journal_seq ,
2021-04-03 23:24:13 +03:00
unsigned commit_flags ,
2019-03-08 03:46:10 +03:00
bool evict )
{
struct bch_fs * c = trans - > c ;
struct journal * j = & c - > journal ;
struct btree_iter * c_iter = NULL , * b_iter = NULL ;
2020-12-03 21:09:08 +03:00
struct bkey_cached * ck = NULL ;
2019-03-08 03:46:10 +03:00
int ret ;
b_iter = bch2_trans_get_iter ( trans , key . btree_id , key . pos ,
BTREE_ITER_SLOTS |
BTREE_ITER_INTENT ) ;
c_iter = bch2_trans_get_iter ( trans , key . btree_id , key . pos ,
BTREE_ITER_CACHED |
BTREE_ITER_CACHED_NOFILL |
BTREE_ITER_CACHED_NOCREATE |
BTREE_ITER_INTENT ) ;
retry :
ret = bch2_btree_iter_traverse ( c_iter ) ;
if ( ret )
goto err ;
ck = ( void * ) c_iter - > l [ 0 ] . b ;
if ( ! ck | |
( journal_seq & & ck - > journal . seq ! = journal_seq ) )
goto out ;
if ( ! test_bit ( BKEY_CACHED_DIRTY , & ck - > flags ) ) {
if ( ! evict )
goto out ;
goto evict ;
}
ret = bch2_btree_iter_traverse ( b_iter ) ? :
bch2_trans_update ( trans , b_iter , ck - > k , BTREE_TRIGGER_NORUN ) ? :
bch2_trans_commit ( trans , NULL , NULL ,
BTREE_INSERT_NOUNLOCK |
BTREE_INSERT_NOCHECK_RW |
BTREE_INSERT_NOFAIL |
2021-04-03 23:24:13 +03:00
( ck - > journal . seq = = journal_last_seq ( j )
? BTREE_INSERT_JOURNAL_RESERVED
: 0 ) |
commit_flags ) ;
2019-03-08 03:46:10 +03:00
err :
if ( ret = = - EINTR )
goto retry ;
2021-04-03 23:24:13 +03:00
if ( ret = = - EAGAIN )
goto out ;
2020-11-30 10:07:38 +03:00
if ( ret ) {
bch2_fs_fatal_err_on ( ! bch2_journal_error ( j ) , c ,
" error flushing key cache: %i " , ret ) ;
2019-03-08 03:46:10 +03:00
goto out ;
2020-11-30 10:07:38 +03:00
}
2019-03-08 03:46:10 +03:00
bch2_journal_pin_drop ( j , & ck - > journal ) ;
bch2_journal_preres_put ( j , & ck - > res ) ;
2021-03-25 06:37:33 +03:00
BUG_ON ( ! btree_node_locked ( c_iter , 0 ) ) ;
2019-03-08 03:46:10 +03:00
if ( ! evict ) {
2020-11-09 21:01:52 +03:00
if ( test_bit ( BKEY_CACHED_DIRTY , & ck - > flags ) ) {
clear_bit ( BKEY_CACHED_DIRTY , & ck - > flags ) ;
2021-03-25 06:37:33 +03:00
atomic_long_dec ( & c - > btree_key_cache . nr_dirty ) ;
2020-11-09 21:01:52 +03:00
}
2019-03-08 03:46:10 +03:00
} else {
evict :
BUG_ON ( ! btree_node_intent_locked ( c_iter , 0 ) ) ;
mark_btree_node_unlocked ( c_iter , 0 ) ;
c_iter - > l [ 0 ] . b = NULL ;
six_lock_write ( & ck - > c . lock , NULL , NULL ) ;
2020-11-09 21:01:52 +03:00
if ( test_bit ( BKEY_CACHED_DIRTY , & ck - > flags ) ) {
clear_bit ( BKEY_CACHED_DIRTY , & ck - > flags ) ;
2021-03-25 06:37:33 +03:00
atomic_long_dec ( & c - > btree_key_cache . nr_dirty ) ;
2020-11-09 21:01:52 +03:00
}
2019-03-08 03:46:10 +03:00
bkey_cached_evict ( & c - > btree_key_cache , ck ) ;
2021-03-25 06:37:33 +03:00
mutex_lock ( & c - > btree_key_cache . lock ) ;
2019-03-08 03:46:10 +03:00
bkey_cached_free ( & c - > btree_key_cache , ck ) ;
mutex_unlock ( & c - > btree_key_cache . lock ) ;
}
out :
bch2_trans_iter_put ( trans , b_iter ) ;
bch2_trans_iter_put ( trans , c_iter ) ;
return ret ;
}
2021-04-03 23:24:13 +03:00
static int btree_key_cache_journal_flush ( struct journal * j ,
struct journal_entry_pin * pin ,
u64 seq )
2019-03-08 03:46:10 +03:00
{
struct bch_fs * c = container_of ( j , struct bch_fs , journal ) ;
struct bkey_cached * ck =
container_of ( pin , struct bkey_cached , journal ) ;
struct bkey_cached_key key ;
struct btree_trans trans ;
2021-04-03 23:24:13 +03:00
int ret = 0 ;
2019-03-08 03:46:10 +03:00
2020-11-13 01:19:47 +03:00
int srcu_idx = srcu_read_lock ( & c - > btree_trans_barrier ) ;
2019-03-08 03:46:10 +03:00
six_lock_read ( & ck - > c . lock , NULL , NULL ) ;
key = ck - > key ;
if ( ck - > journal . seq ! = seq | |
! test_bit ( BKEY_CACHED_DIRTY , & ck - > flags ) ) {
six_unlock_read ( & ck - > c . lock ) ;
2020-11-13 01:19:47 +03:00
goto unlock ;
2019-03-08 03:46:10 +03:00
}
six_unlock_read ( & ck - > c . lock ) ;
bch2_trans_init ( & trans , c , 0 , 0 ) ;
2021-04-03 23:24:13 +03:00
ret = btree_key_cache_flush_pos ( & trans , key , seq ,
BTREE_INSERT_JOURNAL_RECLAIM , false ) ;
2019-03-08 03:46:10 +03:00
bch2_trans_exit ( & trans ) ;
2020-11-13 01:19:47 +03:00
unlock :
srcu_read_unlock ( & c - > btree_trans_barrier , srcu_idx ) ;
2021-04-03 23:24:13 +03:00
return ret ;
2019-03-08 03:46:10 +03:00
}
/*
* Flush and evict a key from the key cache :
*/
int bch2_btree_key_cache_flush ( struct btree_trans * trans ,
enum btree_id id , struct bpos pos )
{
struct bch_fs * c = trans - > c ;
struct bkey_cached_key key = { id , pos } ;
/* Fastpath - assume it won't be found: */
2019-09-23 02:10:21 +03:00
if ( ! bch2_btree_key_cache_find ( c , id , pos ) )
2019-03-08 03:46:10 +03:00
return 0 ;
2021-04-03 23:24:13 +03:00
return btree_key_cache_flush_pos ( trans , key , 0 , 0 , true ) ;
2019-03-08 03:46:10 +03:00
}
bool bch2_btree_insert_key_cached ( struct btree_trans * trans ,
struct btree_iter * iter ,
struct bkey_i * insert )
{
struct bch_fs * c = trans - > c ;
struct bkey_cached * ck = ( void * ) iter - > l [ 0 ] . b ;
2020-11-20 03:54:40 +03:00
bool kick_reclaim = false ;
2019-03-08 03:46:10 +03:00
BUG_ON ( insert - > u64s > ck - > u64s ) ;
if ( likely ( ! ( trans - > flags & BTREE_INSERT_JOURNAL_REPLAY ) ) ) {
int difference ;
BUG_ON ( jset_u64s ( insert - > u64s ) > trans - > journal_preres . u64s ) ;
difference = jset_u64s ( insert - > u64s ) - ck - > res . u64s ;
if ( difference > 0 ) {
trans - > journal_preres . u64s - = difference ;
ck - > res . u64s + = difference ;
}
}
bkey_copy ( ck - > k , insert ) ;
ck - > valid = true ;
if ( ! test_bit ( BKEY_CACHED_DIRTY , & ck - > flags ) ) {
set_bit ( BKEY_CACHED_DIRTY , & ck - > flags ) ;
2021-03-25 06:37:33 +03:00
atomic_long_inc ( & c - > btree_key_cache . nr_dirty ) ;
2020-11-20 03:54:40 +03:00
if ( bch2_nr_btree_keys_need_flush ( c ) )
kick_reclaim = true ;
2019-03-08 03:46:10 +03:00
}
bch2_journal_pin_update ( & c - > journal , trans - > journal_res . seq ,
& ck - > journal , btree_key_cache_journal_flush ) ;
2020-11-20 03:54:40 +03:00
if ( kick_reclaim )
2020-11-20 04:55:33 +03:00
journal_reclaim_kick ( & c - > journal ) ;
2019-03-08 03:46:10 +03:00
return true ;
}
# ifdef CONFIG_BCACHEFS_DEBUG
void bch2_btree_key_cache_verify_clean ( struct btree_trans * trans ,
enum btree_id id , struct bpos pos )
{
2019-09-23 02:10:21 +03:00
BUG_ON ( bch2_btree_key_cache_find ( trans - > c , id , pos ) ) ;
2019-03-08 03:46:10 +03:00
}
# endif
2020-11-13 01:19:47 +03:00
static unsigned long bch2_btree_key_cache_scan ( struct shrinker * shrink ,
struct shrink_control * sc )
{
struct bch_fs * c = container_of ( shrink , struct bch_fs ,
btree_key_cache . shrink ) ;
struct btree_key_cache * bc = & c - > btree_key_cache ;
2021-03-25 06:37:33 +03:00
struct bucket_table * tbl ;
2020-11-13 01:19:47 +03:00
struct bkey_cached * ck , * t ;
size_t scanned = 0 , freed = 0 , nr = sc - > nr_to_scan ;
2021-03-25 06:37:33 +03:00
unsigned start , flags ;
int srcu_idx ;
2020-11-13 01:19:47 +03:00
/* Return -1 if we can't do anything right now */
if ( sc - > gfp_mask & __GFP_FS )
mutex_lock ( & bc - > lock ) ;
else if ( ! mutex_trylock ( & bc - > lock ) )
return - 1 ;
2021-03-25 06:37:33 +03:00
srcu_idx = srcu_read_lock ( & c - > btree_trans_barrier ) ;
2020-11-13 01:19:47 +03:00
flags = memalloc_nofs_save ( ) ;
2020-11-19 23:38:27 +03:00
/*
* Newest freed entries are at the end of the list - once we hit one
* that ' s too new to be freed , we can bail out :
*/
2020-11-13 01:19:47 +03:00
list_for_each_entry_safe ( ck , t , & bc - > freed , list ) {
2020-11-19 23:38:27 +03:00
if ( ! poll_state_synchronize_srcu ( & c - > btree_trans_barrier ,
ck - > btree_trans_barrier_seq ) )
break ;
2020-11-13 01:19:47 +03:00
2020-11-19 23:38:27 +03:00
list_del ( & ck - > list ) ;
kmem_cache_free ( bch2_key_cache , ck ) ;
bc - > nr_freed - - ;
scanned + + ;
freed + + ;
2020-11-13 01:19:47 +03:00
}
2020-11-19 23:38:27 +03:00
if ( scanned > = nr )
goto out ;
2020-11-13 01:19:47 +03:00
2021-03-25 06:37:33 +03:00
rcu_read_lock ( ) ;
tbl = rht_dereference_rcu ( bc - > table . tbl , & bc - > table ) ;
if ( bc - > shrink_iter > = tbl - > size )
bc - > shrink_iter = 0 ;
start = bc - > shrink_iter ;
2020-11-13 01:19:47 +03:00
2021-03-25 06:37:33 +03:00
do {
struct rhash_head * pos , * next ;
pos = rht_ptr_rcu ( rht_bucket ( tbl , bc - > shrink_iter ) ) ;
while ( ! rht_is_a_nulls ( pos ) ) {
next = rht_dereference_bucket_rcu ( pos - > next , tbl , bc - > shrink_iter ) ;
ck = container_of ( pos , struct bkey_cached , hash ) ;
if ( test_bit ( BKEY_CACHED_DIRTY , & ck - > flags ) )
goto next ;
if ( test_bit ( BKEY_CACHED_ACCESSED , & ck - > flags ) )
clear_bit ( BKEY_CACHED_ACCESSED , & ck - > flags ) ;
else if ( bkey_cached_lock_for_evict ( ck ) ) {
bkey_cached_evict ( bc , ck ) ;
bkey_cached_free ( bc , ck ) ;
}
scanned + + ;
if ( scanned > = nr )
break ;
next :
pos = next ;
2020-11-13 01:19:47 +03:00
}
2021-03-25 06:37:33 +03:00
bc - > shrink_iter + + ;
if ( bc - > shrink_iter > = tbl - > size )
bc - > shrink_iter = 0 ;
} while ( scanned < nr & & bc - > shrink_iter ! = start ) ;
rcu_read_unlock ( ) ;
2020-11-13 01:19:47 +03:00
out :
memalloc_nofs_restore ( flags ) ;
2021-03-25 06:37:33 +03:00
srcu_read_unlock ( & c - > btree_trans_barrier , srcu_idx ) ;
2020-11-13 01:19:47 +03:00
mutex_unlock ( & bc - > lock ) ;
return freed ;
}
static unsigned long bch2_btree_key_cache_count ( struct shrinker * shrink ,
struct shrink_control * sc )
{
struct bch_fs * c = container_of ( shrink , struct bch_fs ,
btree_key_cache . shrink ) ;
struct btree_key_cache * bc = & c - > btree_key_cache ;
2021-03-25 06:37:33 +03:00
return atomic_long_read ( & bc - > nr_keys ) ;
2020-11-13 01:19:47 +03:00
}
2020-11-09 21:01:52 +03:00
void bch2_fs_btree_key_cache_exit ( struct btree_key_cache * bc )
2019-03-08 03:46:10 +03:00
{
2020-11-09 21:01:52 +03:00
struct bch_fs * c = container_of ( bc , struct bch_fs , btree_key_cache ) ;
2021-03-25 06:37:33 +03:00
struct bucket_table * tbl ;
2019-03-08 03:46:10 +03:00
struct bkey_cached * ck , * n ;
2021-03-25 06:37:33 +03:00
struct rhash_head * pos ;
unsigned i ;
2019-03-08 03:46:10 +03:00
2020-11-13 01:19:47 +03:00
if ( bc - > shrink . list . next )
unregister_shrinker ( & bc - > shrink ) ;
2020-11-09 21:01:52 +03:00
mutex_lock ( & bc - > lock ) ;
2021-03-25 06:37:33 +03:00
rcu_read_lock ( ) ;
tbl = rht_dereference_rcu ( bc - > table . tbl , & bc - > table ) ;
for ( i = 0 ; i < tbl - > size ; i + + )
rht_for_each_entry_rcu ( ck , pos , tbl , i , hash ) {
bkey_cached_evict ( bc , ck ) ;
list_add ( & ck - > list , & bc - > freed ) ;
}
rcu_read_unlock ( ) ;
list_for_each_entry_safe ( ck , n , & bc - > freed , list ) {
2020-12-14 00:12:04 +03:00
cond_resched ( ) ;
2020-11-12 01:47:39 +03:00
bch2_journal_pin_drop ( & c - > journal , & ck - > journal ) ;
bch2_journal_preres_put ( & c - > journal , & ck - > res ) ;
2020-11-18 22:09:33 +03:00
list_del ( & ck - > list ) ;
2021-03-25 06:37:33 +03:00
kfree ( ck - > k ) ;
2020-11-18 22:09:33 +03:00
kmem_cache_free ( bch2_key_cache , ck ) ;
2019-03-08 03:46:10 +03:00
}
2020-11-09 21:01:52 +03:00
2021-03-25 06:37:33 +03:00
BUG_ON ( atomic_long_read ( & bc - > nr_dirty ) & & ! bch2_journal_error ( & c - > journal ) ) ;
BUG_ON ( atomic_long_read ( & bc - > nr_keys ) ) ;
2020-12-14 00:12:04 +03:00
2020-11-09 21:01:52 +03:00
mutex_unlock ( & bc - > lock ) ;
2019-03-08 03:46:10 +03:00
2020-11-30 07:48:20 +03:00
if ( bc - > table_init_done )
rhashtable_destroy ( & bc - > table ) ;
2019-03-08 03:46:10 +03:00
}
void bch2_fs_btree_key_cache_init_early ( struct btree_key_cache * c )
{
mutex_init ( & c - > lock ) ;
INIT_LIST_HEAD ( & c - > freed ) ;
}
2020-11-13 01:19:47 +03:00
int bch2_fs_btree_key_cache_init ( struct btree_key_cache * bc )
2019-03-08 03:46:10 +03:00
{
2020-11-13 01:19:47 +03:00
struct bch_fs * c = container_of ( bc , struct bch_fs , btree_key_cache ) ;
2020-11-30 07:48:20 +03:00
int ret ;
2020-11-13 01:19:47 +03:00
2021-04-05 08:23:55 +03:00
ret = rhashtable_init ( & bc - > table , & bch2_btree_key_cache_params ) ;
2020-11-30 07:48:20 +03:00
if ( ret )
return ret ;
bc - > table_init_done = true ;
2021-04-05 08:23:55 +03:00
bc - > shrink . seeks = 1 ;
bc - > shrink . count_objects = bch2_btree_key_cache_count ;
bc - > shrink . scan_objects = bch2_btree_key_cache_scan ;
return register_shrinker ( & bc - > shrink , " %s/btree_key_cache " , c - > name ) ;
2019-03-08 03:46:10 +03:00
}
2020-06-16 02:53:46 +03:00
void bch2_btree_key_cache_to_text ( struct printbuf * out , struct btree_key_cache * c )
{
2020-11-19 23:38:27 +03:00
pr_buf ( out , " nr_freed: \t %zu \n " , c - > nr_freed ) ;
2021-03-25 06:37:33 +03:00
pr_buf ( out , " nr_keys: \t %zu \n " , atomic_long_read ( & c - > nr_keys ) ) ;
pr_buf ( out , " nr_dirty: \t %zu \n " , atomic_long_read ( & c - > nr_dirty ) ) ;
2020-06-16 02:53:46 +03:00
}
2020-11-18 22:09:33 +03:00
void bch2_btree_key_cache_exit ( void )
{
if ( bch2_key_cache )
kmem_cache_destroy ( bch2_key_cache ) ;
}
int __init bch2_btree_key_cache_init ( void )
{
bch2_key_cache = KMEM_CACHE ( bkey_cached , 0 ) ;
if ( ! bch2_key_cache )
return - ENOMEM ;
return 0 ;
}