2023-08-04 01:18:21 +03:00
// SPDX-License-Identifier: GPL-2.0
# ifndef NO_BCACHEFS_FS
# include "bcachefs.h"
# include "btree_iter.h"
# include "extents.h"
# include "fs-io.h"
# include "fs-io-pagecache.h"
# include "subvolume.h"
# include <linux/pagevec.h>
# include <linux/writeback.h>
int bch2_filemap_get_contig_folios_d ( struct address_space * mapping ,
loff_t start , u64 end ,
2023-11-11 20:30:19 +03:00
fgf_t fgp_flags , gfp_t gfp ,
2023-09-13 01:41:22 +03:00
folios * fs )
2023-08-04 01:18:21 +03:00
{
struct folio * f ;
u64 pos = start ;
int ret = 0 ;
while ( pos < end ) {
if ( ( u64 ) pos > = ( u64 ) start + ( 1ULL < < 20 ) )
fgp_flags & = ~ FGP_CREAT ;
2023-09-13 01:41:22 +03:00
ret = darray_make_room_gfp ( fs , 1 , gfp & GFP_KERNEL ) ;
2023-08-04 01:18:21 +03:00
if ( ret )
break ;
f = __filemap_get_folio ( mapping , pos > > PAGE_SHIFT , fgp_flags , gfp ) ;
if ( IS_ERR_OR_NULL ( f ) )
break ;
2023-09-13 01:41:22 +03:00
BUG_ON ( fs - > nr & & folio_pos ( f ) ! = pos ) ;
2023-08-04 01:18:21 +03:00
pos = folio_end_pos ( f ) ;
2023-09-13 01:41:22 +03:00
darray_push ( fs , f ) ;
2023-08-04 01:18:21 +03:00
}
2023-09-13 01:41:22 +03:00
if ( ! fs - > nr & & ! ret & & ( fgp_flags & FGP_CREAT ) )
2023-08-04 01:18:21 +03:00
ret = - ENOMEM ;
2023-09-13 01:41:22 +03:00
return fs - > nr ? 0 : ret ;
2023-08-04 01:18:21 +03:00
}
/* pagecache_block must be held */
int bch2_write_invalidate_inode_pages_range ( struct address_space * mapping ,
loff_t start , loff_t end )
{
int ret ;
/*
* XXX : the way this is currently implemented , we can spin if a process
* is continually redirtying a specific page
*/
do {
if ( ! mapping - > nrpages )
return 0 ;
ret = filemap_write_and_wait_range ( mapping , start , end ) ;
if ( ret )
break ;
if ( ! mapping - > nrpages )
return 0 ;
ret = invalidate_inode_pages2_range ( mapping ,
start > > PAGE_SHIFT ,
end > > PAGE_SHIFT ) ;
} while ( ret = = - EBUSY ) ;
return ret ;
}
2023-09-13 01:41:22 +03:00
#if 0
/* Useful for debug tracing: */
2023-08-04 01:18:21 +03:00
static const char * const bch2_folio_sector_states [ ] = {
# define x(n) #n,
BCH_FOLIO_SECTOR_STATE ( )
# undef x
NULL
} ;
2023-09-13 01:41:22 +03:00
# endif
2023-08-04 01:18:21 +03:00
static inline enum bch_folio_sector_state
folio_sector_dirty ( enum bch_folio_sector_state state )
{
switch ( state ) {
case SECTOR_unallocated :
return SECTOR_dirty ;
case SECTOR_reserved :
return SECTOR_dirty_reserved ;
default :
return state ;
}
}
static inline enum bch_folio_sector_state
folio_sector_undirty ( enum bch_folio_sector_state state )
{
switch ( state ) {
case SECTOR_dirty :
return SECTOR_unallocated ;
case SECTOR_dirty_reserved :
return SECTOR_reserved ;
default :
return state ;
}
}
static inline enum bch_folio_sector_state
folio_sector_reserve ( enum bch_folio_sector_state state )
{
switch ( state ) {
case SECTOR_unallocated :
return SECTOR_reserved ;
case SECTOR_dirty :
return SECTOR_dirty_reserved ;
default :
return state ;
}
}
/* for newly allocated folios: */
struct bch_folio * __bch2_folio_create ( struct folio * folio , gfp_t gfp )
{
struct bch_folio * s ;
s = kzalloc ( sizeof ( * s ) +
sizeof ( struct bch_folio_sector ) *
folio_sectors ( folio ) , gfp ) ;
if ( ! s )
return NULL ;
spin_lock_init ( & s - > lock ) ;
folio_attach_private ( folio , s ) ;
return s ;
}
struct bch_folio * bch2_folio_create ( struct folio * folio , gfp_t gfp )
{
return bch2_folio ( folio ) ? : __bch2_folio_create ( folio , gfp ) ;
}
static unsigned bkey_to_sector_state ( struct bkey_s_c k )
{
if ( bkey_extent_is_reservation ( k ) )
return SECTOR_reserved ;
if ( bkey_extent_is_allocation ( k . k ) )
return SECTOR_allocated ;
return SECTOR_unallocated ;
}
static void __bch2_folio_set ( struct folio * folio ,
unsigned pg_offset , unsigned pg_len ,
unsigned nr_ptrs , unsigned state )
{
struct bch_folio * s = bch2_folio ( folio ) ;
unsigned i , sectors = folio_sectors ( folio ) ;
BUG_ON ( pg_offset > = sectors ) ;
BUG_ON ( pg_offset + pg_len > sectors ) ;
spin_lock ( & s - > lock ) ;
for ( i = pg_offset ; i < pg_offset + pg_len ; i + + ) {
s - > s [ i ] . nr_replicas = nr_ptrs ;
bch2_folio_sector_set ( folio , s , i , state ) ;
}
if ( i = = sectors )
s - > uptodate = true ;
spin_unlock ( & s - > lock ) ;
}
/*
* Initialize bch_folio state ( allocated / unallocated , nr_replicas ) from the
* extents btree :
*/
int bch2_folio_set ( struct bch_fs * c , subvol_inum inum ,
2023-09-13 01:41:22 +03:00
struct folio * * fs , unsigned nr_folios )
2023-08-04 01:18:21 +03:00
{
2023-09-13 00:16:02 +03:00
struct btree_trans * trans ;
2023-08-04 01:18:21 +03:00
struct btree_iter iter ;
struct bkey_s_c k ;
struct bch_folio * s ;
2023-09-13 01:41:22 +03:00
u64 offset = folio_sector ( fs [ 0 ] ) ;
2023-08-04 01:18:21 +03:00
unsigned folio_idx ;
u32 snapshot ;
bool need_set = false ;
int ret ;
for ( folio_idx = 0 ; folio_idx < nr_folios ; folio_idx + + ) {
2023-09-13 01:41:22 +03:00
s = bch2_folio_create ( fs [ folio_idx ] , GFP_KERNEL ) ;
2023-08-04 01:18:21 +03:00
if ( ! s )
return - ENOMEM ;
need_set | = ! s - > uptodate ;
}
if ( ! need_set )
return 0 ;
folio_idx = 0 ;
2023-09-13 00:16:02 +03:00
trans = bch2_trans_get ( c ) ;
2023-08-04 01:18:21 +03:00
retry :
2023-09-13 00:16:02 +03:00
bch2_trans_begin ( trans ) ;
2023-08-04 01:18:21 +03:00
2023-09-13 00:16:02 +03:00
ret = bch2_subvolume_get_snapshot ( trans , inum . subvol , & snapshot ) ;
2023-08-04 01:18:21 +03:00
if ( ret )
goto err ;
2023-09-13 00:16:02 +03:00
for_each_btree_key_norestart ( trans , iter , BTREE_ID_extents ,
2023-08-04 01:18:21 +03:00
SPOS ( inum . inum , offset , snapshot ) ,
BTREE_ITER_SLOTS , k , ret ) {
unsigned nr_ptrs = bch2_bkey_nr_ptrs_fully_allocated ( k ) ;
unsigned state = bkey_to_sector_state ( k ) ;
while ( folio_idx < nr_folios ) {
2023-09-13 01:41:22 +03:00
struct folio * folio = fs [ folio_idx ] ;
2023-08-04 01:18:21 +03:00
u64 folio_start = folio_sector ( folio ) ;
u64 folio_end = folio_end_sector ( folio ) ;
unsigned folio_offset = max ( bkey_start_offset ( k . k ) , folio_start ) -
folio_start ;
unsigned folio_len = min ( k . k - > p . offset , folio_end ) -
folio_offset - folio_start ;
BUG_ON ( k . k - > p . offset < folio_start ) ;
BUG_ON ( bkey_start_offset ( k . k ) > folio_end ) ;
if ( ! bch2_folio ( folio ) - > uptodate )
__bch2_folio_set ( folio , folio_offset , folio_len , nr_ptrs , state ) ;
if ( k . k - > p . offset < folio_end )
break ;
folio_idx + + ;
}
if ( folio_idx = = nr_folios )
break ;
}
offset = iter . pos . offset ;
2023-09-13 00:16:02 +03:00
bch2_trans_iter_exit ( trans , & iter ) ;
2023-08-04 01:18:21 +03:00
err :
if ( bch2_err_matches ( ret , BCH_ERR_transaction_restart ) )
goto retry ;
2023-09-13 00:16:02 +03:00
bch2_trans_put ( trans ) ;
2023-08-04 01:18:21 +03:00
return ret ;
}
void bch2_bio_page_state_set ( struct bio * bio , struct bkey_s_c k )
{
struct bvec_iter iter ;
struct folio_vec fv ;
unsigned nr_ptrs = k . k - > type = = KEY_TYPE_reflink_v
? 0 : bch2_bkey_nr_ptrs_fully_allocated ( k ) ;
unsigned state = bkey_to_sector_state ( k ) ;
bio_for_each_folio ( fv , bio , iter )
__bch2_folio_set ( fv . fv_folio ,
fv . fv_offset > > 9 ,
fv . fv_len > > 9 ,
nr_ptrs , state ) ;
}
void bch2_mark_pagecache_unallocated ( struct bch_inode_info * inode ,
u64 start , u64 end )
{
pgoff_t index = start > > PAGE_SECTORS_SHIFT ;
pgoff_t end_index = ( end - 1 ) > > PAGE_SECTORS_SHIFT ;
struct folio_batch fbatch ;
unsigned i , j ;
if ( end < = start )
return ;
folio_batch_init ( & fbatch ) ;
while ( filemap_get_folios ( inode - > v . i_mapping ,
& index , end_index , & fbatch ) ) {
for ( i = 0 ; i < folio_batch_count ( & fbatch ) ; i + + ) {
struct folio * folio = fbatch . folios [ i ] ;
u64 folio_start = folio_sector ( folio ) ;
u64 folio_end = folio_end_sector ( folio ) ;
unsigned folio_offset = max ( start , folio_start ) - folio_start ;
unsigned folio_len = min ( end , folio_end ) - folio_offset - folio_start ;
struct bch_folio * s ;
BUG_ON ( end < = folio_start ) ;
folio_lock ( folio ) ;
s = bch2_folio ( folio ) ;
if ( s ) {
spin_lock ( & s - > lock ) ;
for ( j = folio_offset ; j < folio_offset + folio_len ; j + + )
s - > s [ j ] . nr_replicas = 0 ;
spin_unlock ( & s - > lock ) ;
}
folio_unlock ( folio ) ;
}
folio_batch_release ( & fbatch ) ;
cond_resched ( ) ;
}
}
2024-01-16 04:37:23 +03:00
int bch2_mark_pagecache_reserved ( struct bch_inode_info * inode ,
u64 * start , u64 end ,
bool nonblocking )
2023-08-04 01:18:21 +03:00
{
struct bch_fs * c = inode - > v . i_sb - > s_fs_info ;
2024-01-16 04:37:23 +03:00
pgoff_t index = * start > > PAGE_SECTORS_SHIFT ;
2023-08-04 01:18:21 +03:00
pgoff_t end_index = ( end - 1 ) > > PAGE_SECTORS_SHIFT ;
struct folio_batch fbatch ;
s64 i_sectors_delta = 0 ;
2024-01-16 04:37:23 +03:00
int ret = 0 ;
2023-08-04 01:18:21 +03:00
2024-01-16 04:37:23 +03:00
if ( end < = * start )
return 0 ;
2023-08-04 01:18:21 +03:00
folio_batch_init ( & fbatch ) ;
while ( filemap_get_folios ( inode - > v . i_mapping ,
& index , end_index , & fbatch ) ) {
2024-01-16 04:37:23 +03:00
for ( unsigned i = 0 ; i < folio_batch_count ( & fbatch ) ; i + + ) {
2023-08-04 01:18:21 +03:00
struct folio * folio = fbatch . folios [ i ] ;
2024-01-16 04:37:23 +03:00
if ( ! nonblocking )
folio_lock ( folio ) ;
else if ( ! folio_trylock ( folio ) ) {
folio_batch_release ( & fbatch ) ;
ret = - EAGAIN ;
break ;
}
2023-08-04 01:18:21 +03:00
u64 folio_start = folio_sector ( folio ) ;
u64 folio_end = folio_end_sector ( folio ) ;
BUG_ON ( end < = folio_start ) ;
2024-01-16 04:37:23 +03:00
* start = min ( end , folio_end ) ;
2023-08-04 01:18:21 +03:00
2024-01-16 04:37:23 +03:00
struct bch_folio * s = bch2_folio ( folio ) ;
2023-08-04 01:18:21 +03:00
if ( s ) {
2024-01-16 04:37:23 +03:00
unsigned folio_offset = max ( * start , folio_start ) - folio_start ;
unsigned folio_len = min ( end , folio_end ) - folio_offset - folio_start ;
2023-08-04 01:18:21 +03:00
spin_lock ( & s - > lock ) ;
2024-01-16 04:37:23 +03:00
for ( unsigned j = folio_offset ; j < folio_offset + folio_len ; j + + ) {
2023-08-04 01:18:21 +03:00
i_sectors_delta - = s - > s [ j ] . state = = SECTOR_dirty ;
bch2_folio_sector_set ( folio , s , j ,
folio_sector_reserve ( s - > s [ j ] . state ) ) ;
}
spin_unlock ( & s - > lock ) ;
}
folio_unlock ( folio ) ;
}
folio_batch_release ( & fbatch ) ;
cond_resched ( ) ;
}
bch2_i_sectors_acct ( c , inode , NULL , i_sectors_delta ) ;
2024-01-16 04:37:23 +03:00
return ret ;
2023-08-04 01:18:21 +03:00
}
static inline unsigned sectors_to_reserve ( struct bch_folio_sector * s ,
unsigned nr_replicas )
{
return max ( 0 , ( int ) nr_replicas -
s - > nr_replicas -
s - > replicas_reserved ) ;
}
int bch2_get_folio_disk_reservation ( struct bch_fs * c ,
struct bch_inode_info * inode ,
struct folio * folio , bool check_enospc )
{
struct bch_folio * s = bch2_folio_create ( folio , 0 ) ;
unsigned nr_replicas = inode_nr_replicas ( c , inode ) ;
struct disk_reservation disk_res = { 0 } ;
unsigned i , sectors = folio_sectors ( folio ) , disk_res_sectors = 0 ;
int ret ;
if ( ! s )
return - ENOMEM ;
for ( i = 0 ; i < sectors ; i + + )
disk_res_sectors + = sectors_to_reserve ( & s - > s [ i ] , nr_replicas ) ;
if ( ! disk_res_sectors )
return 0 ;
ret = bch2_disk_reservation_get ( c , & disk_res ,
disk_res_sectors , 1 ,
! check_enospc
? BCH_DISK_RESERVATION_NOFAIL
: 0 ) ;
if ( unlikely ( ret ) )
return ret ;
for ( i = 0 ; i < sectors ; i + + )
s - > s [ i ] . replicas_reserved + =
sectors_to_reserve ( & s - > s [ i ] , nr_replicas ) ;
return 0 ;
}
void bch2_folio_reservation_put ( struct bch_fs * c ,
struct bch_inode_info * inode ,
struct bch2_folio_reservation * res )
{
bch2_disk_reservation_put ( c , & res - > disk ) ;
bch2_quota_reservation_put ( c , inode , & res - > quota ) ;
}
int bch2_folio_reservation_get ( struct bch_fs * c ,
struct bch_inode_info * inode ,
struct folio * folio ,
struct bch2_folio_reservation * res ,
unsigned offset , unsigned len )
{
struct bch_folio * s = bch2_folio_create ( folio , 0 ) ;
unsigned i , disk_sectors = 0 , quota_sectors = 0 ;
int ret ;
if ( ! s )
return - ENOMEM ;
BUG_ON ( ! s - > uptodate ) ;
for ( i = round_down ( offset , block_bytes ( c ) ) > > 9 ;
i < round_up ( offset + len , block_bytes ( c ) ) > > 9 ;
i + + ) {
disk_sectors + = sectors_to_reserve ( & s - > s [ i ] ,
res - > disk . nr_replicas ) ;
quota_sectors + = s - > s [ i ] . state = = SECTOR_unallocated ;
}
if ( disk_sectors ) {
ret = bch2_disk_reservation_add ( c , & res - > disk , disk_sectors , 0 ) ;
if ( unlikely ( ret ) )
return ret ;
}
if ( quota_sectors ) {
ret = bch2_quota_reservation_add ( c , inode , & res - > quota ,
quota_sectors , true ) ;
if ( unlikely ( ret ) ) {
struct disk_reservation tmp = {
. sectors = disk_sectors
} ;
bch2_disk_reservation_put ( c , & tmp ) ;
res - > disk . sectors - = disk_sectors ;
return ret ;
}
}
return 0 ;
}
static void bch2_clear_folio_bits ( struct folio * folio )
{
struct bch_inode_info * inode = to_bch_ei ( folio - > mapping - > host ) ;
struct bch_fs * c = inode - > v . i_sb - > s_fs_info ;
struct bch_folio * s = bch2_folio ( folio ) ;
struct disk_reservation disk_res = { 0 } ;
int i , sectors = folio_sectors ( folio ) , dirty_sectors = 0 ;
if ( ! s )
return ;
EBUG_ON ( ! folio_test_locked ( folio ) ) ;
EBUG_ON ( folio_test_writeback ( folio ) ) ;
for ( i = 0 ; i < sectors ; i + + ) {
disk_res . sectors + = s - > s [ i ] . replicas_reserved ;
s - > s [ i ] . replicas_reserved = 0 ;
dirty_sectors - = s - > s [ i ] . state = = SECTOR_dirty ;
bch2_folio_sector_set ( folio , s , i , folio_sector_undirty ( s - > s [ i ] . state ) ) ;
}
bch2_disk_reservation_put ( c , & disk_res ) ;
bch2_i_sectors_acct ( c , inode , NULL , dirty_sectors ) ;
bch2_folio_release ( folio ) ;
}
void bch2_set_folio_dirty ( struct bch_fs * c ,
struct bch_inode_info * inode ,
struct folio * folio ,
struct bch2_folio_reservation * res ,
unsigned offset , unsigned len )
{
struct bch_folio * s = bch2_folio ( folio ) ;
unsigned i , dirty_sectors = 0 ;
WARN_ON ( ( u64 ) folio_pos ( folio ) + offset + len >
round_up ( ( u64 ) i_size_read ( & inode - > v ) , block_bytes ( c ) ) ) ;
BUG_ON ( ! s - > uptodate ) ;
spin_lock ( & s - > lock ) ;
for ( i = round_down ( offset , block_bytes ( c ) ) > > 9 ;
i < round_up ( offset + len , block_bytes ( c ) ) > > 9 ;
i + + ) {
unsigned sectors = sectors_to_reserve ( & s - > s [ i ] ,
res - > disk . nr_replicas ) ;
/*
* This can happen if we race with the error path in
* bch2_writepage_io_done ( ) :
*/
sectors = min_t ( unsigned , sectors , res - > disk . sectors ) ;
s - > s [ i ] . replicas_reserved + = sectors ;
res - > disk . sectors - = sectors ;
dirty_sectors + = s - > s [ i ] . state = = SECTOR_unallocated ;
bch2_folio_sector_set ( folio , s , i , folio_sector_dirty ( s - > s [ i ] . state ) ) ;
}
spin_unlock ( & s - > lock ) ;
bch2_i_sectors_acct ( c , inode , & res - > quota , dirty_sectors ) ;
if ( ! folio_test_dirty ( folio ) )
filemap_dirty_folio ( inode - > v . i_mapping , folio ) ;
}
vm_fault_t bch2_page_fault ( struct vm_fault * vmf )
{
struct file * file = vmf - > vma - > vm_file ;
struct address_space * mapping = file - > f_mapping ;
struct address_space * fdm = faults_disabled_mapping ( ) ;
struct bch_inode_info * inode = file_bch_inode ( file ) ;
vm_fault_t ret ;
if ( fdm = = mapping )
return VM_FAULT_SIGBUS ;
/* Lock ordering: */
if ( fdm > mapping ) {
struct bch_inode_info * fdm_host = to_bch_ei ( fdm - > host ) ;
if ( bch2_pagecache_add_tryget ( inode ) )
goto got_lock ;
bch2_pagecache_block_put ( fdm_host ) ;
bch2_pagecache_add_get ( inode ) ;
bch2_pagecache_add_put ( inode ) ;
bch2_pagecache_block_get ( fdm_host ) ;
/* Signal that lock has been dropped: */
set_fdm_dropped_locks ( ) ;
return VM_FAULT_SIGBUS ;
}
bch2_pagecache_add_get ( inode ) ;
got_lock :
ret = filemap_fault ( vmf ) ;
bch2_pagecache_add_put ( inode ) ;
return ret ;
}
vm_fault_t bch2_page_mkwrite ( struct vm_fault * vmf )
{
struct folio * folio = page_folio ( vmf - > page ) ;
struct file * file = vmf - > vma - > vm_file ;
struct bch_inode_info * inode = file_bch_inode ( file ) ;
struct address_space * mapping = file - > f_mapping ;
struct bch_fs * c = inode - > v . i_sb - > s_fs_info ;
struct bch2_folio_reservation res ;
unsigned len ;
loff_t isize ;
vm_fault_t ret ;
bch2_folio_reservation_init ( c , inode , & res ) ;
sb_start_pagefault ( inode - > v . i_sb ) ;
file_update_time ( file ) ;
/*
* Not strictly necessary , but helps avoid dio writes livelocking in
* bch2_write_invalidate_inode_pages_range ( ) - can drop this if / when we get
* a bch2_write_invalidate_inode_pages_range ( ) that works without dropping
* page lock before invalidating page
*/
bch2_pagecache_add_get ( inode ) ;
folio_lock ( folio ) ;
isize = i_size_read ( & inode - > v ) ;
if ( folio - > mapping ! = mapping | | folio_pos ( folio ) > = isize ) {
folio_unlock ( folio ) ;
ret = VM_FAULT_NOPAGE ;
goto out ;
}
len = min_t ( loff_t , folio_size ( folio ) , isize - folio_pos ( folio ) ) ;
if ( bch2_folio_set ( c , inode_inum ( inode ) , & folio , 1 ) ? :
bch2_folio_reservation_get ( c , inode , folio , & res , 0 , len ) ) {
folio_unlock ( folio ) ;
ret = VM_FAULT_SIGBUS ;
goto out ;
}
bch2_set_folio_dirty ( c , inode , folio , & res , 0 , len ) ;
bch2_folio_reservation_put ( c , inode , & res ) ;
folio_wait_stable ( folio ) ;
ret = VM_FAULT_LOCKED ;
out :
bch2_pagecache_add_put ( inode ) ;
sb_end_pagefault ( inode - > v . i_sb ) ;
return ret ;
}
void bch2_invalidate_folio ( struct folio * folio , size_t offset , size_t length )
{
if ( offset | | length < folio_size ( folio ) )
return ;
bch2_clear_folio_bits ( folio ) ;
}
bool bch2_release_folio ( struct folio * folio , gfp_t gfp_mask )
{
if ( folio_test_dirty ( folio ) | | folio_test_writeback ( folio ) )
return false ;
bch2_clear_folio_bits ( folio ) ;
return true ;
}
/* fseek: */
static int folio_data_offset ( struct folio * folio , loff_t pos ,
unsigned min_replicas )
{
struct bch_folio * s = bch2_folio ( folio ) ;
unsigned i , sectors = folio_sectors ( folio ) ;
if ( s )
for ( i = folio_pos_to_s ( folio , pos ) ; i < sectors ; i + + )
if ( s - > s [ i ] . state > = SECTOR_dirty & &
s - > s [ i ] . nr_replicas + s - > s [ i ] . replicas_reserved > = min_replicas )
return i < < SECTOR_SHIFT ;
return - 1 ;
}
loff_t bch2_seek_pagecache_data ( struct inode * vinode ,
loff_t start_offset ,
loff_t end_offset ,
unsigned min_replicas ,
bool nonblock )
{
struct folio_batch fbatch ;
pgoff_t start_index = start_offset > > PAGE_SHIFT ;
pgoff_t end_index = end_offset > > PAGE_SHIFT ;
pgoff_t index = start_index ;
unsigned i ;
loff_t ret ;
int offset ;
folio_batch_init ( & fbatch ) ;
while ( filemap_get_folios ( vinode - > i_mapping ,
& index , end_index , & fbatch ) ) {
for ( i = 0 ; i < folio_batch_count ( & fbatch ) ; i + + ) {
struct folio * folio = fbatch . folios [ i ] ;
if ( ! nonblock ) {
folio_lock ( folio ) ;
} else if ( ! folio_trylock ( folio ) ) {
folio_batch_release ( & fbatch ) ;
return - EAGAIN ;
}
offset = folio_data_offset ( folio ,
max ( folio_pos ( folio ) , start_offset ) ,
min_replicas ) ;
if ( offset > = 0 ) {
ret = clamp ( folio_pos ( folio ) + offset ,
start_offset , end_offset ) ;
folio_unlock ( folio ) ;
folio_batch_release ( & fbatch ) ;
return ret ;
}
folio_unlock ( folio ) ;
}
folio_batch_release ( & fbatch ) ;
cond_resched ( ) ;
}
return end_offset ;
}
2023-08-14 17:49:42 +03:00
/*
* Search for a hole in a folio .
*
* The filemap layer returns - ENOENT if no folio exists , so reuse the same error
* code to indicate a pagecache hole exists at the returned offset . Otherwise
* return 0 if the folio is filled with data , or an error code . This function
* can return - EAGAIN if nonblock is specified .
*/
2023-08-04 01:18:21 +03:00
static int folio_hole_offset ( struct address_space * mapping , loff_t * offset ,
unsigned min_replicas , bool nonblock )
{
struct folio * folio ;
struct bch_folio * s ;
unsigned i , sectors ;
2023-08-14 17:49:42 +03:00
int ret = - ENOENT ;
2023-08-04 01:18:21 +03:00
folio = __filemap_get_folio ( mapping , * offset > > PAGE_SHIFT ,
FGP_LOCK | ( nonblock ? FGP_NOWAIT : 0 ) , 0 ) ;
2023-08-14 17:49:42 +03:00
if ( IS_ERR ( folio ) )
return PTR_ERR ( folio ) ;
2023-08-04 01:18:21 +03:00
s = bch2_folio ( folio ) ;
if ( ! s )
goto unlock ;
sectors = folio_sectors ( folio ) ;
for ( i = folio_pos_to_s ( folio , * offset ) ; i < sectors ; i + + )
if ( s - > s [ i ] . state < SECTOR_dirty | |
s - > s [ i ] . nr_replicas + s - > s [ i ] . replicas_reserved < min_replicas ) {
* offset = max ( * offset ,
folio_pos ( folio ) + ( i < < SECTOR_SHIFT ) ) ;
goto unlock ;
}
* offset = folio_end_pos ( folio ) ;
2023-08-14 17:49:42 +03:00
ret = 0 ;
2023-08-04 01:18:21 +03:00
unlock :
folio_unlock ( folio ) ;
folio_put ( folio ) ;
return ret ;
}
loff_t bch2_seek_pagecache_hole ( struct inode * vinode ,
loff_t start_offset ,
loff_t end_offset ,
unsigned min_replicas ,
bool nonblock )
{
struct address_space * mapping = vinode - > i_mapping ;
loff_t offset = start_offset ;
2023-08-14 17:49:42 +03:00
loff_t ret = 0 ;
2023-08-04 01:18:21 +03:00
2023-08-14 17:49:42 +03:00
while ( ! ret & & offset < end_offset )
ret = folio_hole_offset ( mapping , & offset , min_replicas , nonblock ) ;
2023-08-04 01:18:21 +03:00
2023-08-14 17:49:42 +03:00
if ( ret & & ret ! = - ENOENT )
return ret ;
2023-08-04 01:18:21 +03:00
return min ( offset , end_offset ) ;
}
int bch2_clamp_data_hole ( struct inode * inode ,
u64 * hole_start ,
u64 * hole_end ,
unsigned min_replicas ,
bool nonblock )
{
loff_t ret ;
ret = bch2_seek_pagecache_hole ( inode ,
* hole_start < < 9 , * hole_end < < 9 , min_replicas , nonblock ) > > 9 ;
if ( ret < 0 )
return ret ;
* hole_start = ret ;
if ( * hole_start = = * hole_end )
return 0 ;
ret = bch2_seek_pagecache_data ( inode ,
* hole_start < < 9 , * hole_end < < 9 , min_replicas , nonblock ) > > 9 ;
if ( ret < 0 )
return ret ;
* hole_end = ret ;
return 0 ;
}
# endif /* NO_BCACHEFS_FS */