18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci#include <linux/ceph/ceph_debug.h> 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/math64.h> 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/ceph/striper.h> 98c2ecf20Sopenharmony_ci#include <linux/ceph/types.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * Map a file extent to a stripe unit within an object. 138c2ecf20Sopenharmony_ci * Fill in objno, offset into object, and object extent length (i.e. the 148c2ecf20Sopenharmony_ci * number of bytes mapped, less than or equal to @l->stripe_unit). 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Example for stripe_count = 3, stripes_per_object = 4: 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * blockno | 0 3 6 9 | 1 4 7 10 | 2 5 8 11 | 12 15 18 21 | 13 16 19 198c2ecf20Sopenharmony_ci * stripeno | 0 1 2 3 | 0 1 2 3 | 0 1 2 3 | 4 5 6 7 | 4 5 6 208c2ecf20Sopenharmony_ci * stripepos | 0 | 1 | 2 | 0 | 1 218c2ecf20Sopenharmony_ci * objno | 0 | 1 | 2 | 3 | 4 228c2ecf20Sopenharmony_ci * objsetno | 0 | 1 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_civoid ceph_calc_file_object_mapping(struct ceph_file_layout *l, 258c2ecf20Sopenharmony_ci u64 off, u64 len, 268c2ecf20Sopenharmony_ci u64 *objno, u64 *objoff, u32 *xlen) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci u32 stripes_per_object = l->object_size / l->stripe_unit; 298c2ecf20Sopenharmony_ci u64 blockno; /* which su in the file (i.e. globally) */ 308c2ecf20Sopenharmony_ci u32 blockoff; /* offset into su */ 318c2ecf20Sopenharmony_ci u64 stripeno; /* which stripe */ 328c2ecf20Sopenharmony_ci u32 stripepos; /* which su in the stripe, 338c2ecf20Sopenharmony_ci which object in the object set */ 348c2ecf20Sopenharmony_ci u64 objsetno; /* which object set */ 358c2ecf20Sopenharmony_ci u32 objsetpos; /* which stripe in the object set */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci blockno = div_u64_rem(off, l->stripe_unit, &blockoff); 388c2ecf20Sopenharmony_ci stripeno = div_u64_rem(blockno, l->stripe_count, &stripepos); 398c2ecf20Sopenharmony_ci objsetno = div_u64_rem(stripeno, stripes_per_object, &objsetpos); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci *objno = objsetno * l->stripe_count + stripepos; 428c2ecf20Sopenharmony_ci *objoff = objsetpos * l->stripe_unit + blockoff; 438c2ecf20Sopenharmony_ci *xlen = min_t(u64, len, l->stripe_unit - blockoff); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ceph_calc_file_object_mapping); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * Return the last extent with given objno (@object_extents is sorted 498c2ecf20Sopenharmony_ci * by objno). If not found, return NULL and set @add_pos so that the 508c2ecf20Sopenharmony_ci * new extent can be added with list_add(add_pos, new_ex). 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistatic struct ceph_object_extent * 538c2ecf20Sopenharmony_cilookup_last(struct list_head *object_extents, u64 objno, 548c2ecf20Sopenharmony_ci struct list_head **add_pos) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct list_head *pos; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci list_for_each_prev(pos, object_extents) { 598c2ecf20Sopenharmony_ci struct ceph_object_extent *ex = 608c2ecf20Sopenharmony_ci list_entry(pos, typeof(*ex), oe_item); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (ex->oe_objno == objno) 638c2ecf20Sopenharmony_ci return ex; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (ex->oe_objno < objno) 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci *add_pos = pos; 708c2ecf20Sopenharmony_ci return NULL; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic struct ceph_object_extent * 748c2ecf20Sopenharmony_cilookup_containing(struct list_head *object_extents, u64 objno, 758c2ecf20Sopenharmony_ci u64 objoff, u32 xlen) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct ceph_object_extent *ex; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci list_for_each_entry(ex, object_extents, oe_item) { 808c2ecf20Sopenharmony_ci if (ex->oe_objno == objno && 818c2ecf20Sopenharmony_ci ex->oe_off <= objoff && 828c2ecf20Sopenharmony_ci ex->oe_off + ex->oe_len >= objoff + xlen) /* paranoia */ 838c2ecf20Sopenharmony_ci return ex; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (ex->oe_objno > objno) 868c2ecf20Sopenharmony_ci break; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return NULL; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * Map a file extent to a sorted list of object extents. 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * We want only one (or as few as possible) object extents per object. 968c2ecf20Sopenharmony_ci * Adjacent object extents will be merged together, each returned object 978c2ecf20Sopenharmony_ci * extent may reverse map to multiple different file extents. 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * Call @alloc_fn for each new object extent and @action_fn for each 1008c2ecf20Sopenharmony_ci * mapped stripe unit, whether it was merged into an already allocated 1018c2ecf20Sopenharmony_ci * object extent or started a new object extent. 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * Newly allocated object extents are added to @object_extents. 1048c2ecf20Sopenharmony_ci * To keep @object_extents sorted, successive calls to this function 1058c2ecf20Sopenharmony_ci * must map successive file extents (i.e. the list of file extents that 1068c2ecf20Sopenharmony_ci * are mapped using the same @object_extents must be sorted). 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci * The caller is responsible for @object_extents. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ciint ceph_file_to_extents(struct ceph_file_layout *l, u64 off, u64 len, 1118c2ecf20Sopenharmony_ci struct list_head *object_extents, 1128c2ecf20Sopenharmony_ci struct ceph_object_extent *alloc_fn(void *arg), 1138c2ecf20Sopenharmony_ci void *alloc_arg, 1148c2ecf20Sopenharmony_ci ceph_object_extent_fn_t action_fn, 1158c2ecf20Sopenharmony_ci void *action_arg) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct ceph_object_extent *last_ex, *ex; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci while (len) { 1208c2ecf20Sopenharmony_ci struct list_head *add_pos = NULL; 1218c2ecf20Sopenharmony_ci u64 objno, objoff; 1228c2ecf20Sopenharmony_ci u32 xlen; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci ceph_calc_file_object_mapping(l, off, len, &objno, &objoff, 1258c2ecf20Sopenharmony_ci &xlen); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci last_ex = lookup_last(object_extents, objno, &add_pos); 1288c2ecf20Sopenharmony_ci if (!last_ex || last_ex->oe_off + last_ex->oe_len != objoff) { 1298c2ecf20Sopenharmony_ci ex = alloc_fn(alloc_arg); 1308c2ecf20Sopenharmony_ci if (!ex) 1318c2ecf20Sopenharmony_ci return -ENOMEM; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ex->oe_objno = objno; 1348c2ecf20Sopenharmony_ci ex->oe_off = objoff; 1358c2ecf20Sopenharmony_ci ex->oe_len = xlen; 1368c2ecf20Sopenharmony_ci if (action_fn) 1378c2ecf20Sopenharmony_ci action_fn(ex, xlen, action_arg); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!last_ex) 1408c2ecf20Sopenharmony_ci list_add(&ex->oe_item, add_pos); 1418c2ecf20Sopenharmony_ci else 1428c2ecf20Sopenharmony_ci list_add(&ex->oe_item, &last_ex->oe_item); 1438c2ecf20Sopenharmony_ci } else { 1448c2ecf20Sopenharmony_ci last_ex->oe_len += xlen; 1458c2ecf20Sopenharmony_ci if (action_fn) 1468c2ecf20Sopenharmony_ci action_fn(last_ex, xlen, action_arg); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci off += xlen; 1508c2ecf20Sopenharmony_ci len -= xlen; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci for (last_ex = list_first_entry(object_extents, typeof(*ex), oe_item), 1548c2ecf20Sopenharmony_ci ex = list_next_entry(last_ex, oe_item); 1558c2ecf20Sopenharmony_ci &ex->oe_item != object_extents; 1568c2ecf20Sopenharmony_ci last_ex = ex, ex = list_next_entry(ex, oe_item)) { 1578c2ecf20Sopenharmony_ci if (last_ex->oe_objno > ex->oe_objno || 1588c2ecf20Sopenharmony_ci (last_ex->oe_objno == ex->oe_objno && 1598c2ecf20Sopenharmony_ci last_ex->oe_off + last_ex->oe_len >= ex->oe_off)) { 1608c2ecf20Sopenharmony_ci WARN(1, "%s: object_extents list not sorted!\n", 1618c2ecf20Sopenharmony_ci __func__); 1628c2ecf20Sopenharmony_ci return -EINVAL; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ceph_file_to_extents); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/* 1718c2ecf20Sopenharmony_ci * A stripped down, non-allocating version of ceph_file_to_extents(), 1728c2ecf20Sopenharmony_ci * for when @object_extents is already populated. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ciint ceph_iterate_extents(struct ceph_file_layout *l, u64 off, u64 len, 1758c2ecf20Sopenharmony_ci struct list_head *object_extents, 1768c2ecf20Sopenharmony_ci ceph_object_extent_fn_t action_fn, 1778c2ecf20Sopenharmony_ci void *action_arg) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci while (len) { 1808c2ecf20Sopenharmony_ci struct ceph_object_extent *ex; 1818c2ecf20Sopenharmony_ci u64 objno, objoff; 1828c2ecf20Sopenharmony_ci u32 xlen; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ceph_calc_file_object_mapping(l, off, len, &objno, &objoff, 1858c2ecf20Sopenharmony_ci &xlen); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci ex = lookup_containing(object_extents, objno, objoff, xlen); 1888c2ecf20Sopenharmony_ci if (!ex) { 1898c2ecf20Sopenharmony_ci WARN(1, "%s: objno %llu %llu~%u not found!\n", 1908c2ecf20Sopenharmony_ci __func__, objno, objoff, xlen); 1918c2ecf20Sopenharmony_ci return -EINVAL; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci action_fn(ex, xlen, action_arg); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci off += xlen; 1978c2ecf20Sopenharmony_ci len -= xlen; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ceph_iterate_extents); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/* 2058c2ecf20Sopenharmony_ci * Reverse map an object extent to a sorted list of file extents. 2068c2ecf20Sopenharmony_ci * 2078c2ecf20Sopenharmony_ci * On success, the caller is responsible for: 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * kfree(file_extents) 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ciint ceph_extent_to_file(struct ceph_file_layout *l, 2128c2ecf20Sopenharmony_ci u64 objno, u64 objoff, u64 objlen, 2138c2ecf20Sopenharmony_ci struct ceph_file_extent **file_extents, 2148c2ecf20Sopenharmony_ci u32 *num_file_extents) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci u32 stripes_per_object = l->object_size / l->stripe_unit; 2178c2ecf20Sopenharmony_ci u64 blockno; /* which su */ 2188c2ecf20Sopenharmony_ci u32 blockoff; /* offset into su */ 2198c2ecf20Sopenharmony_ci u64 stripeno; /* which stripe */ 2208c2ecf20Sopenharmony_ci u32 stripepos; /* which su in the stripe, 2218c2ecf20Sopenharmony_ci which object in the object set */ 2228c2ecf20Sopenharmony_ci u64 objsetno; /* which object set */ 2238c2ecf20Sopenharmony_ci u32 i = 0; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (!objlen) { 2268c2ecf20Sopenharmony_ci *file_extents = NULL; 2278c2ecf20Sopenharmony_ci *num_file_extents = 0; 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci *num_file_extents = DIV_ROUND_UP_ULL(objoff + objlen, l->stripe_unit) - 2328c2ecf20Sopenharmony_ci DIV_ROUND_DOWN_ULL(objoff, l->stripe_unit); 2338c2ecf20Sopenharmony_ci *file_extents = kmalloc_array(*num_file_extents, sizeof(**file_extents), 2348c2ecf20Sopenharmony_ci GFP_NOIO); 2358c2ecf20Sopenharmony_ci if (!*file_extents) 2368c2ecf20Sopenharmony_ci return -ENOMEM; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci div_u64_rem(objoff, l->stripe_unit, &blockoff); 2398c2ecf20Sopenharmony_ci while (objlen) { 2408c2ecf20Sopenharmony_ci u64 off, len; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci objsetno = div_u64_rem(objno, l->stripe_count, &stripepos); 2438c2ecf20Sopenharmony_ci stripeno = div_u64(objoff, l->stripe_unit) + 2448c2ecf20Sopenharmony_ci objsetno * stripes_per_object; 2458c2ecf20Sopenharmony_ci blockno = stripeno * l->stripe_count + stripepos; 2468c2ecf20Sopenharmony_ci off = blockno * l->stripe_unit + blockoff; 2478c2ecf20Sopenharmony_ci len = min_t(u64, objlen, l->stripe_unit - blockoff); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci (*file_extents)[i].fe_off = off; 2508c2ecf20Sopenharmony_ci (*file_extents)[i].fe_len = len; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci blockoff = 0; 2538c2ecf20Sopenharmony_ci objoff += len; 2548c2ecf20Sopenharmony_ci objlen -= len; 2558c2ecf20Sopenharmony_ci i++; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci BUG_ON(i != *num_file_extents); 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ceph_extent_to_file); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ciu64 ceph_get_num_objects(struct ceph_file_layout *l, u64 size) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci u64 period = (u64)l->stripe_count * l->object_size; 2668c2ecf20Sopenharmony_ci u64 num_periods = DIV64_U64_ROUND_UP(size, period); 2678c2ecf20Sopenharmony_ci u64 remainder_bytes; 2688c2ecf20Sopenharmony_ci u64 remainder_objs = 0; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci div64_u64_rem(size, period, &remainder_bytes); 2718c2ecf20Sopenharmony_ci if (remainder_bytes > 0 && 2728c2ecf20Sopenharmony_ci remainder_bytes < (u64)l->stripe_count * l->stripe_unit) 2738c2ecf20Sopenharmony_ci remainder_objs = l->stripe_count - 2748c2ecf20Sopenharmony_ci DIV_ROUND_UP_ULL(remainder_bytes, l->stripe_unit); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return num_periods * l->stripe_count - remainder_objs; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ceph_get_num_objects); 279