2018-02-02 17:23:22 +03:00
/* SPDX-License-Identifier: GPL-2.0 */
# include <linux/ceph/ceph_debug.h>
# include <linux/math64.h>
# include <linux/slab.h>
# include <linux/ceph/striper.h>
# include <linux/ceph/types.h>
2018-02-17 12:41:20 +03:00
/*
* Map a file extent to a stripe unit within an object .
* Fill in objno , offset into object , and object extent length ( i . e . the
* number of bytes mapped , less than or equal to @ l - > stripe_unit ) .
*
* Example for stripe_count = 3 , stripes_per_object = 4 :
*
* blockno | 0 3 6 9 | 1 4 7 10 | 2 5 8 11 | 12 15 18 21 | 13 16 19
* stripeno | 0 1 2 3 | 0 1 2 3 | 0 1 2 3 | 4 5 6 7 | 4 5 6
* stripepos | 0 | 1 | 2 | 0 | 1
* objno | 0 | 1 | 2 | 3 | 4
* objsetno | 0 | 1
*/
void ceph_calc_file_object_mapping ( struct ceph_file_layout * l ,
u64 off , u64 len ,
u64 * objno , u64 * objoff , u32 * xlen )
{
u32 stripes_per_object = l - > object_size / l - > stripe_unit ;
u64 blockno ; /* which su in the file (i.e. globally) */
u32 blockoff ; /* offset into su */
u64 stripeno ; /* which stripe */
u32 stripepos ; /* which su in the stripe,
which object in the object set */
u64 objsetno ; /* which object set */
u32 objsetpos ; /* which stripe in the object set */
blockno = div_u64_rem ( off , l - > stripe_unit , & blockoff ) ;
stripeno = div_u64_rem ( blockno , l - > stripe_count , & stripepos ) ;
objsetno = div_u64_rem ( stripeno , stripes_per_object , & objsetpos ) ;
* objno = objsetno * l - > stripe_count + stripepos ;
* objoff = objsetpos * l - > stripe_unit + blockoff ;
* xlen = min_t ( u64 , len , l - > stripe_unit - blockoff ) ;
}
EXPORT_SYMBOL ( ceph_calc_file_object_mapping ) ;
2018-02-02 17:23:22 +03:00
/*
* Return the last extent with given objno ( @ object_extents is sorted
* by objno ) . If not found , return NULL and set @ add_pos so that the
* new extent can be added with list_add ( add_pos , new_ex ) .
*/
static struct ceph_object_extent *
lookup_last ( struct list_head * object_extents , u64 objno ,
struct list_head * * add_pos )
{
struct list_head * pos ;
list_for_each_prev ( pos , object_extents ) {
struct ceph_object_extent * ex =
list_entry ( pos , typeof ( * ex ) , oe_item ) ;
if ( ex - > oe_objno = = objno )
return ex ;
if ( ex - > oe_objno < objno )
break ;
}
* add_pos = pos ;
return NULL ;
}
static struct ceph_object_extent *
lookup_containing ( struct list_head * object_extents , u64 objno ,
u64 objoff , u32 xlen )
{
struct ceph_object_extent * ex ;
list_for_each_entry ( ex , object_extents , oe_item ) {
if ( ex - > oe_objno = = objno & &
ex - > oe_off < = objoff & &
ex - > oe_off + ex - > oe_len > = objoff + xlen ) /* paranoia */
return ex ;
if ( ex - > oe_objno > objno )
break ;
}
return NULL ;
}
/*
* Map a file extent to a sorted list of object extents .
*
* We want only one ( or as few as possible ) object extents per object .
* Adjacent object extents will be merged together , each returned object
* extent may reverse map to multiple different file extents .
*
* Call @ alloc_fn for each new object extent and @ action_fn for each
* mapped stripe unit , whether it was merged into an already allocated
* object extent or started a new object extent .
*
* Newly allocated object extents are added to @ object_extents .
* To keep @ object_extents sorted , successive calls to this function
* must map successive file extents ( i . e . the list of file extents that
* are mapped using the same @ object_extents must be sorted ) .
*
* The caller is responsible for @ object_extents .
*/
int ceph_file_to_extents ( struct ceph_file_layout * l , u64 off , u64 len ,
struct list_head * object_extents ,
struct ceph_object_extent * alloc_fn ( void * arg ) ,
void * alloc_arg ,
ceph_object_extent_fn_t action_fn ,
void * action_arg )
{
struct ceph_object_extent * last_ex , * ex ;
while ( len ) {
struct list_head * add_pos = NULL ;
u64 objno , objoff ;
u32 xlen ;
ceph_calc_file_object_mapping ( l , off , len , & objno , & objoff ,
& xlen ) ;
last_ex = lookup_last ( object_extents , objno , & add_pos ) ;
if ( ! last_ex | | last_ex - > oe_off + last_ex - > oe_len ! = objoff ) {
ex = alloc_fn ( alloc_arg ) ;
if ( ! ex )
return - ENOMEM ;
ex - > oe_objno = objno ;
ex - > oe_off = objoff ;
ex - > oe_len = xlen ;
if ( action_fn )
action_fn ( ex , xlen , action_arg ) ;
if ( ! last_ex )
list_add ( & ex - > oe_item , add_pos ) ;
else
list_add ( & ex - > oe_item , & last_ex - > oe_item ) ;
} else {
last_ex - > oe_len + = xlen ;
if ( action_fn )
action_fn ( last_ex , xlen , action_arg ) ;
}
off + = xlen ;
len - = xlen ;
}
for ( last_ex = list_first_entry ( object_extents , typeof ( * ex ) , oe_item ) ,
ex = list_next_entry ( last_ex , oe_item ) ;
& ex - > oe_item ! = object_extents ;
last_ex = ex , ex = list_next_entry ( ex , oe_item ) ) {
if ( last_ex - > oe_objno > ex - > oe_objno | |
( last_ex - > oe_objno = = ex - > oe_objno & &
last_ex - > oe_off + last_ex - > oe_len > = ex - > oe_off ) ) {
WARN ( 1 , " %s: object_extents list not sorted! \n " ,
__func__ ) ;
return - EINVAL ;
}
}
return 0 ;
}
EXPORT_SYMBOL ( ceph_file_to_extents ) ;
/*
* A stripped down , non - allocating version of ceph_file_to_extents ( ) ,
* for when @ object_extents is already populated .
*/
int ceph_iterate_extents ( struct ceph_file_layout * l , u64 off , u64 len ,
struct list_head * object_extents ,
ceph_object_extent_fn_t action_fn ,
void * action_arg )
{
while ( len ) {
struct ceph_object_extent * ex ;
u64 objno , objoff ;
u32 xlen ;
ceph_calc_file_object_mapping ( l , off , len , & objno , & objoff ,
& xlen ) ;
ex = lookup_containing ( object_extents , objno , objoff , xlen ) ;
if ( ! ex ) {
WARN ( 1 , " %s: objno %llu %llu~%u not found! \n " ,
__func__ , objno , objoff , xlen ) ;
return - EINVAL ;
}
action_fn ( ex , xlen , action_arg ) ;
off + = xlen ;
len - = xlen ;
}
return 0 ;
}
EXPORT_SYMBOL ( ceph_iterate_extents ) ;
/*
* Reverse map an object extent to a sorted list of file extents .
*
* On success , the caller is responsible for :
*
* kfree ( file_extents )
*/
int ceph_extent_to_file ( struct ceph_file_layout * l ,
u64 objno , u64 objoff , u64 objlen ,
struct ceph_file_extent * * file_extents ,
u32 * num_file_extents )
{
u32 stripes_per_object = l - > object_size / l - > stripe_unit ;
u64 blockno ; /* which su */
u32 blockoff ; /* offset into su */
u64 stripeno ; /* which stripe */
u32 stripepos ; /* which su in the stripe,
which object in the object set */
u64 objsetno ; /* which object set */
u32 i = 0 ;
if ( ! objlen ) {
* file_extents = NULL ;
* num_file_extents = 0 ;
return 0 ;
}
* num_file_extents = DIV_ROUND_UP_ULL ( objoff + objlen , l - > stripe_unit ) -
DIV_ROUND_DOWN_ULL ( objoff , l - > stripe_unit ) ;
* file_extents = kmalloc_array ( * num_file_extents , sizeof ( * * file_extents ) ,
GFP_NOIO ) ;
if ( ! * file_extents )
return - ENOMEM ;
div_u64_rem ( objoff , l - > stripe_unit , & blockoff ) ;
while ( objlen ) {
u64 off , len ;
objsetno = div_u64_rem ( objno , l - > stripe_count , & stripepos ) ;
stripeno = div_u64 ( objoff , l - > stripe_unit ) +
objsetno * stripes_per_object ;
blockno = stripeno * l - > stripe_count + stripepos ;
off = blockno * l - > stripe_unit + blockoff ;
len = min_t ( u64 , objlen , l - > stripe_unit - blockoff ) ;
( * file_extents ) [ i ] . fe_off = off ;
( * file_extents ) [ i ] . fe_len = len ;
blockoff = 0 ;
objoff + = len ;
objlen - = len ;
i + + ;
}
BUG_ON ( i ! = * num_file_extents ) ;
return 0 ;
}
EXPORT_SYMBOL ( ceph_extent_to_file ) ;
2019-06-05 20:25:11 +03:00
u64 ceph_get_num_objects ( struct ceph_file_layout * l , u64 size )
{
u64 period = ( u64 ) l - > stripe_count * l - > object_size ;
u64 num_periods = DIV64_U64_ROUND_UP ( size , period ) ;
u64 remainder_bytes ;
u64 remainder_objs = 0 ;
div64_u64_rem ( size , period , & remainder_bytes ) ;
if ( remainder_bytes > 0 & &
remainder_bytes < ( u64 ) l - > stripe_count * l - > stripe_unit )
remainder_objs = l - > stripe_count -
DIV_ROUND_UP_ULL ( remainder_bytes , l - > stripe_unit ) ;
return num_periods * l - > stripe_count - remainder_objs ;
}
EXPORT_SYMBOL ( ceph_get_num_objects ) ;