2017-03-16 22:18:50 -08:00
/* SPDX-License-Identifier: GPL-2.0 */
# ifndef _BCACHEFS_STR_HASH_H
# define _BCACHEFS_STR_HASH_H
# include "btree_iter.h"
# include "btree_update.h"
# include "checksum.h"
# include "error.h"
# include "inode.h"
# include "siphash.h"
# include "super.h"
# include <linux/crc32c.h>
# include <crypto/hash.h>
# include <crypto/sha2.h>
2019-10-04 15:58:43 -04:00
static inline enum bch_str_hash_type
bch2_str_hash_opt_to_type ( struct bch_fs * c , enum bch_str_hash_opts opt )
{
switch ( opt ) {
case BCH_STR_HASH_OPT_CRC32C :
return BCH_STR_HASH_CRC32C ;
case BCH_STR_HASH_OPT_CRC64 :
return BCH_STR_HASH_CRC64 ;
case BCH_STR_HASH_OPT_SIPHASH :
2019-12-28 20:17:06 -05:00
return c - > sb . features & ( 1ULL < < BCH_FEATURE_new_siphash )
2019-10-04 15:58:43 -04:00
? BCH_STR_HASH_SIPHASH
: BCH_STR_HASH_SIPHASH_OLD ;
default :
BUG ( ) ;
}
}
2017-03-16 22:18:50 -08:00
struct bch_hash_info {
u8 type ;
union {
__le64 crc_key ;
SIPHASH_KEY siphash_key ;
} ;
} ;
static inline struct bch_hash_info
2019-10-04 15:58:43 -04:00
bch2_hash_info_init ( struct bch_fs * c , const struct bch_inode_unpacked * bi )
2017-03-16 22:18:50 -08:00
{
/* XXX ick */
struct bch_hash_info info = {
. type = ( bi - > bi_flags > > INODE_STR_HASH_OFFSET ) &
2019-10-04 15:58:43 -04:00
~ ( ~ 0U < < INODE_STR_HASH_BITS ) ,
. crc_key = bi - > bi_hash_seed ,
2017-03-16 22:18:50 -08:00
} ;
2019-10-04 15:58:43 -04:00
if ( unlikely ( info . type = = BCH_STR_HASH_SIPHASH_OLD ) ) {
2017-03-16 22:18:50 -08:00
SHASH_DESC_ON_STACK ( desc , c - > sha256 ) ;
u8 digest [ SHA256_DIGEST_SIZE ] ;
desc - > tfm = c - > sha256 ;
crypto_shash_digest ( desc , ( void * ) & bi - > bi_hash_seed ,
sizeof ( bi - > bi_hash_seed ) , digest ) ;
memcpy ( & info . siphash_key , digest , sizeof ( info . siphash_key ) ) ;
}
return info ;
}
struct bch_str_hash_ctx {
union {
u32 crc32c ;
u64 crc64 ;
SIPHASH_CTX siphash ;
} ;
} ;
static inline void bch2_str_hash_init ( struct bch_str_hash_ctx * ctx ,
const struct bch_hash_info * info )
{
switch ( info - > type ) {
case BCH_STR_HASH_CRC32C :
ctx - > crc32c = crc32c ( ~ 0 , & info - > crc_key , sizeof ( info - > crc_key ) ) ;
break ;
case BCH_STR_HASH_CRC64 :
2019-07-03 19:36:39 -04:00
ctx - > crc64 = crc64_be ( ~ 0 , & info - > crc_key , sizeof ( info - > crc_key ) ) ;
2017-03-16 22:18:50 -08:00
break ;
2019-10-04 15:58:43 -04:00
case BCH_STR_HASH_SIPHASH_OLD :
2017-03-16 22:18:50 -08:00
case BCH_STR_HASH_SIPHASH :
SipHash24_Init ( & ctx - > siphash , & info - > siphash_key ) ;
break ;
default :
BUG ( ) ;
}
}
static inline void bch2_str_hash_update ( struct bch_str_hash_ctx * ctx ,
const struct bch_hash_info * info ,
const void * data , size_t len )
{
switch ( info - > type ) {
case BCH_STR_HASH_CRC32C :
ctx - > crc32c = crc32c ( ctx - > crc32c , data , len ) ;
break ;
case BCH_STR_HASH_CRC64 :
2019-07-03 19:36:39 -04:00
ctx - > crc64 = crc64_be ( ctx - > crc64 , data , len ) ;
2017-03-16 22:18:50 -08:00
break ;
2019-10-04 15:58:43 -04:00
case BCH_STR_HASH_SIPHASH_OLD :
2017-03-16 22:18:50 -08:00
case BCH_STR_HASH_SIPHASH :
SipHash24_Update ( & ctx - > siphash , data , len ) ;
break ;
default :
BUG ( ) ;
}
}
static inline u64 bch2_str_hash_end ( struct bch_str_hash_ctx * ctx ,
const struct bch_hash_info * info )
{
switch ( info - > type ) {
case BCH_STR_HASH_CRC32C :
return ctx - > crc32c ;
case BCH_STR_HASH_CRC64 :
return ctx - > crc64 > > 1 ;
2019-10-04 15:58:43 -04:00
case BCH_STR_HASH_SIPHASH_OLD :
2017-03-16 22:18:50 -08:00
case BCH_STR_HASH_SIPHASH :
return SipHash24_End ( & ctx - > siphash ) > > 1 ;
default :
BUG ( ) ;
}
}
struct bch_hash_desc {
enum btree_id btree_id ;
u8 key_type ;
u64 ( * hash_key ) ( const struct bch_hash_info * , const void * ) ;
u64 ( * hash_bkey ) ( const struct bch_hash_info * , struct bkey_s_c ) ;
bool ( * cmp_key ) ( struct bkey_s_c , const void * ) ;
bool ( * cmp_bkey ) ( struct bkey_s_c , struct bkey_s_c ) ;
} ;
2019-02-20 14:18:44 -05:00
static __always_inline struct btree_iter *
2017-03-16 22:18:50 -08:00
bch2_hash_lookup ( struct btree_trans * trans ,
const struct bch_hash_desc desc ,
const struct bch_hash_info * info ,
u64 inode , const void * key ,
unsigned flags )
{
struct btree_iter * iter ;
struct bkey_s_c k ;
2019-04-17 15:49:28 -04:00
int ret ;
2017-03-16 22:18:50 -08:00
2019-04-17 15:49:28 -04:00
for_each_btree_key ( trans , iter , desc . btree_id ,
POS ( inode , desc . hash_key ( info , key ) ) ,
BTREE_ITER_SLOTS | flags , k , ret ) {
2017-03-16 22:18:50 -08:00
if ( iter - > pos . inode ! = inode )
break ;
if ( k . k - > type = = desc . key_type ) {
if ( ! desc . cmp_key ( k , key ) )
return iter ;
2018-11-01 15:10:01 -04:00
} else if ( k . k - > type = = KEY_TYPE_whiteout ) {
2017-03-16 22:18:50 -08:00
;
} else {
/* hole, not found */
break ;
}
}
2020-02-26 15:39:46 -05:00
bch2_trans_iter_put ( trans , iter ) ;
2017-03-16 22:18:50 -08:00
2019-04-17 15:49:28 -04:00
return ERR_PTR ( ret ? : - ENOENT ) ;
2017-03-16 22:18:50 -08:00
}
2019-02-20 14:18:44 -05:00
static __always_inline struct btree_iter *
2017-03-16 22:18:50 -08:00
bch2_hash_hole ( struct btree_trans * trans ,
const struct bch_hash_desc desc ,
const struct bch_hash_info * info ,
u64 inode , const void * key )
{
struct btree_iter * iter ;
struct bkey_s_c k ;
2019-04-17 15:49:28 -04:00
int ret ;
2017-03-16 22:18:50 -08:00
2019-04-17 15:49:28 -04:00
for_each_btree_key ( trans , iter , desc . btree_id ,
POS ( inode , desc . hash_key ( info , key ) ) ,
BTREE_ITER_SLOTS | BTREE_ITER_INTENT , k , ret ) {
2017-03-16 22:18:50 -08:00
if ( iter - > pos . inode ! = inode )
break ;
if ( k . k - > type ! = desc . key_type )
return iter ;
}
2020-02-26 15:39:46 -05:00
iter - > flags | = BTREE_ITER_KEEP_UNTIL_COMMIT ;
bch2_trans_iter_put ( trans , iter ) ;
2019-04-17 15:49:28 -04:00
return ERR_PTR ( ret ? : - ENOSPC ) ;
2017-03-16 22:18:50 -08:00
}
2019-02-20 14:18:44 -05:00
static __always_inline
int bch2_hash_needs_whiteout ( struct btree_trans * trans ,
const struct bch_hash_desc desc ,
const struct bch_hash_info * info ,
struct btree_iter * start )
2017-03-16 22:18:50 -08:00
{
struct btree_iter * iter ;
struct bkey_s_c k ;
2019-09-25 15:57:56 -04:00
int ret ;
2017-03-16 22:18:50 -08:00
iter = bch2_trans_copy_iter ( trans , start ) ;
if ( IS_ERR ( iter ) )
return PTR_ERR ( iter ) ;
bch2_btree_iter_next_slot ( iter ) ;
2019-09-25 15:57:56 -04:00
for_each_btree_key_continue ( iter , BTREE_ITER_SLOTS , k , ret ) {
2017-03-16 22:18:50 -08:00
if ( k . k - > type ! = desc . key_type & &
2018-11-01 15:10:01 -04:00
k . k - > type ! = KEY_TYPE_whiteout )
2019-03-25 15:10:15 -04:00
break ;
2017-03-16 22:18:50 -08:00
if ( k . k - > type = = desc . key_type & &
2019-03-25 15:10:15 -04:00
desc . hash_bkey ( info , k ) < = start - > pos . offset ) {
2019-09-26 22:21:39 -04:00
iter - > flags | = BTREE_ITER_KEEP_UNTIL_COMMIT ;
ret = 1 ;
break ;
2019-03-25 15:10:15 -04:00
}
2017-03-16 22:18:50 -08:00
}
2019-03-25 15:10:15 -04:00
2019-09-26 22:21:39 -04:00
bch2_trans_iter_put ( trans , iter ) ;
2019-09-25 15:57:56 -04:00
return ret ;
2017-03-16 22:18:50 -08:00
}
2019-02-20 14:18:44 -05:00
static __always_inline
2019-03-13 20:49:16 -04:00
int bch2_hash_set ( struct btree_trans * trans ,
const struct bch_hash_desc desc ,
const struct bch_hash_info * info ,
u64 inode , struct bkey_i * insert , int flags )
2017-03-16 22:18:50 -08:00
{
struct btree_iter * iter , * slot = NULL ;
struct bkey_s_c k ;
2019-03-25 15:10:15 -04:00
bool found = false ;
2019-04-17 15:49:28 -04:00
int ret ;
2017-03-16 22:18:50 -08:00
2019-04-17 15:49:28 -04:00
for_each_btree_key ( trans , iter , desc . btree_id ,
POS ( inode , desc . hash_bkey ( info , bkey_i_to_s_c ( insert ) ) ) ,
BTREE_ITER_SLOTS | BTREE_ITER_INTENT , k , ret ) {
2017-03-16 22:18:50 -08:00
if ( iter - > pos . inode ! = inode )
break ;
if ( k . k - > type = = desc . key_type ) {
if ( ! desc . cmp_bkey ( k , bkey_i_to_s_c ( insert ) ) )
goto found ;
/* hash collision: */
continue ;
}
if ( ! slot & &
! ( flags & BCH_HASH_SET_MUST_REPLACE ) ) {
slot = bch2_trans_copy_iter ( trans , iter ) ;
if ( IS_ERR ( slot ) )
return PTR_ERR ( slot ) ;
}
2018-11-01 15:10:01 -04:00
if ( k . k - > type ! = KEY_TYPE_whiteout )
2017-03-16 22:18:50 -08:00
goto not_found ;
}
2019-09-26 22:21:39 -04:00
if ( ! ret )
ret = - ENOSPC ;
out :
2020-02-18 14:27:10 -05:00
bch2_trans_iter_put ( trans , slot ) ;
bch2_trans_iter_put ( trans , iter ) ;
2017-03-16 22:18:50 -08:00
2019-09-26 22:21:39 -04:00
return ret ;
2017-03-16 22:18:50 -08:00
found :
2019-03-25 15:10:15 -04:00
found = true ;
not_found :
if ( ! found & & ( flags & BCH_HASH_SET_MUST_REPLACE ) ) {
ret = - ENOENT ;
} else if ( found & & ( flags & BCH_HASH_SET_MUST_CREATE ) ) {
ret = - EEXIST ;
} else {
2019-09-26 22:21:39 -04:00
if ( ! found & & slot )
swap ( iter , slot ) ;
2019-03-25 15:10:15 -04:00
insert - > k . p = iter - > pos ;
2019-12-31 16:17:42 -05:00
bch2_trans_update ( trans , iter , insert , 0 ) ;
2019-03-25 15:10:15 -04:00
}
2017-03-16 22:18:50 -08:00
2019-09-26 22:21:39 -04:00
goto out ;
2017-03-16 22:18:50 -08:00
}
2019-02-20 14:18:44 -05:00
static __always_inline
int bch2_hash_delete_at ( struct btree_trans * trans ,
const struct bch_hash_desc desc ,
const struct bch_hash_info * info ,
struct btree_iter * iter )
2017-03-16 22:18:50 -08:00
{
struct bkey_i * delete ;
int ret ;
ret = bch2_hash_needs_whiteout ( trans , desc , info , iter ) ;
if ( ret < 0 )
return ret ;
delete = bch2_trans_kmalloc ( trans , sizeof ( * delete ) ) ;
if ( IS_ERR ( delete ) )
return PTR_ERR ( delete ) ;
bkey_init ( & delete - > k ) ;
delete - > k . p = iter - > pos ;
2018-11-01 15:10:01 -04:00
delete - > k . type = ret ? KEY_TYPE_whiteout : KEY_TYPE_deleted ;
2017-03-16 22:18:50 -08:00
2019-12-31 16:17:42 -05:00
bch2_trans_update ( trans , iter , delete , 0 ) ;
2017-03-16 22:18:50 -08:00
return 0 ;
}
2019-02-20 14:18:44 -05:00
static __always_inline
int bch2_hash_delete ( struct btree_trans * trans ,
const struct bch_hash_desc desc ,
const struct bch_hash_info * info ,
u64 inode , const void * key )
2017-03-16 22:18:50 -08:00
{
struct btree_iter * iter ;
2020-01-27 17:47:07 -05:00
int ret ;
2017-03-16 22:18:50 -08:00
iter = bch2_hash_lookup ( trans , desc , info , inode , key ,
BTREE_ITER_INTENT ) ;
if ( IS_ERR ( iter ) )
return PTR_ERR ( iter ) ;
2020-01-27 17:47:07 -05:00
ret = bch2_hash_delete_at ( trans , desc , info , iter ) ;
bch2_trans_iter_put ( trans , iter ) ;
return ret ;
2017-03-16 22:18:50 -08:00
}
# endif /* _BCACHEFS_STR_HASH_H */