162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/ceph/ceph_debug.h> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/math64.h> 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/ceph/striper.h> 962306a36Sopenharmony_ci#include <linux/ceph/types.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * Map a file extent to a stripe unit within an object. 1362306a36Sopenharmony_ci * Fill in objno, offset into object, and object extent length (i.e. the 1462306a36Sopenharmony_ci * number of bytes mapped, less than or equal to @l->stripe_unit). 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Example for stripe_count = 3, stripes_per_object = 4: 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * blockno | 0 3 6 9 | 1 4 7 10 | 2 5 8 11 | 12 15 18 21 | 13 16 19 1962306a36Sopenharmony_ci * stripeno | 0 1 2 3 | 0 1 2 3 | 0 1 2 3 | 4 5 6 7 | 4 5 6 2062306a36Sopenharmony_ci * stripepos | 0 | 1 | 2 | 0 | 1 2162306a36Sopenharmony_ci * objno | 0 | 1 | 2 | 3 | 4 2262306a36Sopenharmony_ci * objsetno | 0 | 1 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_civoid ceph_calc_file_object_mapping(struct ceph_file_layout *l, 2562306a36Sopenharmony_ci u64 off, u64 len, 2662306a36Sopenharmony_ci u64 *objno, u64 *objoff, u32 *xlen) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci u32 stripes_per_object = l->object_size / l->stripe_unit; 2962306a36Sopenharmony_ci u64 blockno; /* which su in the file (i.e. globally) */ 3062306a36Sopenharmony_ci u32 blockoff; /* offset into su */ 3162306a36Sopenharmony_ci u64 stripeno; /* which stripe */ 3262306a36Sopenharmony_ci u32 stripepos; /* which su in the stripe, 3362306a36Sopenharmony_ci which object in the object set */ 3462306a36Sopenharmony_ci u64 objsetno; /* which object set */ 3562306a36Sopenharmony_ci u32 objsetpos; /* which stripe in the object set */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci blockno = div_u64_rem(off, l->stripe_unit, &blockoff); 3862306a36Sopenharmony_ci stripeno = div_u64_rem(blockno, l->stripe_count, &stripepos); 3962306a36Sopenharmony_ci objsetno = div_u64_rem(stripeno, stripes_per_object, &objsetpos); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci *objno = objsetno * l->stripe_count + stripepos; 4262306a36Sopenharmony_ci *objoff = objsetpos * l->stripe_unit + blockoff; 4362306a36Sopenharmony_ci *xlen = min_t(u64, len, l->stripe_unit - blockoff); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ciEXPORT_SYMBOL(ceph_calc_file_object_mapping); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * Return the last extent with given objno (@object_extents is sorted 4962306a36Sopenharmony_ci * by objno). If not found, return NULL and set @add_pos so that the 5062306a36Sopenharmony_ci * new extent can be added with list_add(add_pos, new_ex). 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistatic struct ceph_object_extent * 5362306a36Sopenharmony_cilookup_last(struct list_head *object_extents, u64 objno, 5462306a36Sopenharmony_ci struct list_head **add_pos) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct list_head *pos; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci list_for_each_prev(pos, object_extents) { 5962306a36Sopenharmony_ci struct ceph_object_extent *ex = 6062306a36Sopenharmony_ci list_entry(pos, typeof(*ex), oe_item); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (ex->oe_objno == objno) 6362306a36Sopenharmony_ci return ex; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (ex->oe_objno < objno) 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci *add_pos = pos; 7062306a36Sopenharmony_ci return NULL; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic struct ceph_object_extent * 7462306a36Sopenharmony_cilookup_containing(struct list_head *object_extents, u64 objno, 7562306a36Sopenharmony_ci u64 objoff, u32 xlen) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct ceph_object_extent *ex; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci list_for_each_entry(ex, object_extents, oe_item) { 8062306a36Sopenharmony_ci if (ex->oe_objno == objno && 8162306a36Sopenharmony_ci ex->oe_off <= objoff && 8262306a36Sopenharmony_ci ex->oe_off + ex->oe_len >= objoff + xlen) /* paranoia */ 8362306a36Sopenharmony_ci return ex; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (ex->oe_objno > objno) 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return NULL; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * Map a file extent to a sorted list of object extents. 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * We want only one (or as few as possible) object extents per object. 9662306a36Sopenharmony_ci * Adjacent object extents will be merged together, each returned object 9762306a36Sopenharmony_ci * extent may reverse map to multiple different file extents. 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * Call @alloc_fn for each new object extent and @action_fn for each 10062306a36Sopenharmony_ci * mapped stripe unit, whether it was merged into an already allocated 10162306a36Sopenharmony_ci * object extent or started a new object extent. 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * Newly allocated object extents are added to @object_extents. 10462306a36Sopenharmony_ci * To keep @object_extents sorted, successive calls to this function 10562306a36Sopenharmony_ci * must map successive file extents (i.e. the list of file extents that 10662306a36Sopenharmony_ci * are mapped using the same @object_extents must be sorted). 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * The caller is responsible for @object_extents. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ciint ceph_file_to_extents(struct ceph_file_layout *l, u64 off, u64 len, 11162306a36Sopenharmony_ci struct list_head *object_extents, 11262306a36Sopenharmony_ci struct ceph_object_extent *alloc_fn(void *arg), 11362306a36Sopenharmony_ci void *alloc_arg, 11462306a36Sopenharmony_ci ceph_object_extent_fn_t action_fn, 11562306a36Sopenharmony_ci void *action_arg) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct ceph_object_extent *last_ex, *ex; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci while (len) { 12062306a36Sopenharmony_ci struct list_head *add_pos = NULL; 12162306a36Sopenharmony_ci u64 objno, objoff; 12262306a36Sopenharmony_ci u32 xlen; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci ceph_calc_file_object_mapping(l, off, len, &objno, &objoff, 12562306a36Sopenharmony_ci &xlen); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci last_ex = lookup_last(object_extents, objno, &add_pos); 12862306a36Sopenharmony_ci if (!last_ex || last_ex->oe_off + last_ex->oe_len != objoff) { 12962306a36Sopenharmony_ci ex = alloc_fn(alloc_arg); 13062306a36Sopenharmony_ci if (!ex) 13162306a36Sopenharmony_ci return -ENOMEM; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci ex->oe_objno = objno; 13462306a36Sopenharmony_ci ex->oe_off = objoff; 13562306a36Sopenharmony_ci ex->oe_len = xlen; 13662306a36Sopenharmony_ci if (action_fn) 13762306a36Sopenharmony_ci action_fn(ex, xlen, action_arg); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (!last_ex) 14062306a36Sopenharmony_ci list_add(&ex->oe_item, add_pos); 14162306a36Sopenharmony_ci else 14262306a36Sopenharmony_ci list_add(&ex->oe_item, &last_ex->oe_item); 14362306a36Sopenharmony_ci } else { 14462306a36Sopenharmony_ci last_ex->oe_len += xlen; 14562306a36Sopenharmony_ci if (action_fn) 14662306a36Sopenharmony_ci action_fn(last_ex, xlen, action_arg); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci off += xlen; 15062306a36Sopenharmony_ci len -= xlen; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci for (last_ex = list_first_entry(object_extents, typeof(*ex), oe_item), 15462306a36Sopenharmony_ci ex = list_next_entry(last_ex, oe_item); 15562306a36Sopenharmony_ci &ex->oe_item != object_extents; 15662306a36Sopenharmony_ci last_ex = ex, ex = list_next_entry(ex, oe_item)) { 15762306a36Sopenharmony_ci if (last_ex->oe_objno > ex->oe_objno || 15862306a36Sopenharmony_ci (last_ex->oe_objno == ex->oe_objno && 15962306a36Sopenharmony_ci last_ex->oe_off + last_ex->oe_len >= ex->oe_off)) { 16062306a36Sopenharmony_ci WARN(1, "%s: object_extents list not sorted!\n", 16162306a36Sopenharmony_ci __func__); 16262306a36Sopenharmony_ci return -EINVAL; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ciEXPORT_SYMBOL(ceph_file_to_extents); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * A stripped down, non-allocating version of ceph_file_to_extents(), 17262306a36Sopenharmony_ci * for when @object_extents is already populated. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ciint ceph_iterate_extents(struct ceph_file_layout *l, u64 off, u64 len, 17562306a36Sopenharmony_ci struct list_head *object_extents, 17662306a36Sopenharmony_ci ceph_object_extent_fn_t action_fn, 17762306a36Sopenharmony_ci void *action_arg) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci while (len) { 18062306a36Sopenharmony_ci struct ceph_object_extent *ex; 18162306a36Sopenharmony_ci u64 objno, objoff; 18262306a36Sopenharmony_ci u32 xlen; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci ceph_calc_file_object_mapping(l, off, len, &objno, &objoff, 18562306a36Sopenharmony_ci &xlen); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci ex = lookup_containing(object_extents, objno, objoff, xlen); 18862306a36Sopenharmony_ci if (!ex) { 18962306a36Sopenharmony_ci WARN(1, "%s: objno %llu %llu~%u not found!\n", 19062306a36Sopenharmony_ci __func__, objno, objoff, xlen); 19162306a36Sopenharmony_ci return -EINVAL; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci action_fn(ex, xlen, action_arg); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci off += xlen; 19762306a36Sopenharmony_ci len -= xlen; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ciEXPORT_SYMBOL(ceph_iterate_extents); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* 20562306a36Sopenharmony_ci * Reverse map an object extent to a sorted list of file extents. 20662306a36Sopenharmony_ci * 20762306a36Sopenharmony_ci * On success, the caller is responsible for: 20862306a36Sopenharmony_ci * 20962306a36Sopenharmony_ci * kfree(file_extents) 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ciint ceph_extent_to_file(struct ceph_file_layout *l, 21262306a36Sopenharmony_ci u64 objno, u64 objoff, u64 objlen, 21362306a36Sopenharmony_ci struct ceph_file_extent **file_extents, 21462306a36Sopenharmony_ci u32 *num_file_extents) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci u32 stripes_per_object = l->object_size / l->stripe_unit; 21762306a36Sopenharmony_ci u64 blockno; /* which su */ 21862306a36Sopenharmony_ci u32 blockoff; /* offset into su */ 21962306a36Sopenharmony_ci u64 stripeno; /* which stripe */ 22062306a36Sopenharmony_ci u32 stripepos; /* which su in the stripe, 22162306a36Sopenharmony_ci which object in the object set */ 22262306a36Sopenharmony_ci u64 objsetno; /* which object set */ 22362306a36Sopenharmony_ci u32 i = 0; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (!objlen) { 22662306a36Sopenharmony_ci *file_extents = NULL; 22762306a36Sopenharmony_ci *num_file_extents = 0; 22862306a36Sopenharmony_ci return 0; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci *num_file_extents = DIV_ROUND_UP_ULL(objoff + objlen, l->stripe_unit) - 23262306a36Sopenharmony_ci DIV_ROUND_DOWN_ULL(objoff, l->stripe_unit); 23362306a36Sopenharmony_ci *file_extents = kmalloc_array(*num_file_extents, sizeof(**file_extents), 23462306a36Sopenharmony_ci GFP_NOIO); 23562306a36Sopenharmony_ci if (!*file_extents) 23662306a36Sopenharmony_ci return -ENOMEM; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci div_u64_rem(objoff, l->stripe_unit, &blockoff); 23962306a36Sopenharmony_ci while (objlen) { 24062306a36Sopenharmony_ci u64 off, len; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci objsetno = div_u64_rem(objno, l->stripe_count, &stripepos); 24362306a36Sopenharmony_ci stripeno = div_u64(objoff, l->stripe_unit) + 24462306a36Sopenharmony_ci objsetno * stripes_per_object; 24562306a36Sopenharmony_ci blockno = stripeno * l->stripe_count + stripepos; 24662306a36Sopenharmony_ci off = blockno * l->stripe_unit + blockoff; 24762306a36Sopenharmony_ci len = min_t(u64, objlen, l->stripe_unit - blockoff); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci (*file_extents)[i].fe_off = off; 25062306a36Sopenharmony_ci (*file_extents)[i].fe_len = len; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci blockoff = 0; 25362306a36Sopenharmony_ci objoff += len; 25462306a36Sopenharmony_ci objlen -= len; 25562306a36Sopenharmony_ci i++; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci BUG_ON(i != *num_file_extents); 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ciEXPORT_SYMBOL(ceph_extent_to_file); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ciu64 ceph_get_num_objects(struct ceph_file_layout *l, u64 size) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci u64 period = (u64)l->stripe_count * l->object_size; 26662306a36Sopenharmony_ci u64 num_periods = DIV64_U64_ROUND_UP(size, period); 26762306a36Sopenharmony_ci u64 remainder_bytes; 26862306a36Sopenharmony_ci u64 remainder_objs = 0; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci div64_u64_rem(size, period, &remainder_bytes); 27162306a36Sopenharmony_ci if (remainder_bytes > 0 && 27262306a36Sopenharmony_ci remainder_bytes < (u64)l->stripe_count * l->stripe_unit) 27362306a36Sopenharmony_ci remainder_objs = l->stripe_count - 27462306a36Sopenharmony_ci DIV_ROUND_UP_ULL(remainder_bytes, l->stripe_unit); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return num_periods * l->stripe_count - remainder_objs; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ciEXPORT_SYMBOL(ceph_get_num_objects); 279