162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2007 Oracle. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <asm/unaligned.h> 762306a36Sopenharmony_ci#include "messages.h" 862306a36Sopenharmony_ci#include "ctree.h" 962306a36Sopenharmony_ci#include "accessors.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic bool check_setget_bounds(const struct extent_buffer *eb, 1262306a36Sopenharmony_ci const void *ptr, unsigned off, int size) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci const unsigned long member_offset = (unsigned long)ptr + off; 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci if (unlikely(member_offset + size > eb->len)) { 1762306a36Sopenharmony_ci btrfs_warn(eb->fs_info, 1862306a36Sopenharmony_ci "bad eb member %s: ptr 0x%lx start %llu member offset %lu size %d", 1962306a36Sopenharmony_ci (member_offset > eb->len ? "start" : "end"), 2062306a36Sopenharmony_ci (unsigned long)ptr, eb->start, member_offset, size); 2162306a36Sopenharmony_ci return false; 2262306a36Sopenharmony_ci } 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci return true; 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_civoid btrfs_init_map_token(struct btrfs_map_token *token, struct extent_buffer *eb) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci token->eb = eb; 3062306a36Sopenharmony_ci token->kaddr = page_address(eb->pages[0]); 3162306a36Sopenharmony_ci token->offset = 0; 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Macro templates that define helpers to read/write extent buffer data of a 3662306a36Sopenharmony_ci * given size, that are also used via ctree.h for access to item members by 3762306a36Sopenharmony_ci * specialized helpers. 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * Generic helpers: 4062306a36Sopenharmony_ci * - btrfs_set_8 (for 8/16/32/64) 4162306a36Sopenharmony_ci * - btrfs_get_8 (for 8/16/32/64) 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Generic helpers with a token (cached address of the most recently accessed 4462306a36Sopenharmony_ci * page): 4562306a36Sopenharmony_ci * - btrfs_set_token_8 (for 8/16/32/64) 4662306a36Sopenharmony_ci * - btrfs_get_token_8 (for 8/16/32/64) 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * The set/get functions handle data spanning two pages transparently, in case 4962306a36Sopenharmony_ci * metadata block size is larger than page. Every pointer to metadata items is 5062306a36Sopenharmony_ci * an offset into the extent buffer page array, cast to a specific type. This 5162306a36Sopenharmony_ci * gives us all the type checking. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * The extent buffer pages stored in the array pages do not form a contiguous 5462306a36Sopenharmony_ci * phyusical range, but the API functions assume the linear offset to the range 5562306a36Sopenharmony_ci * from 0 to metadata node size. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define DEFINE_BTRFS_SETGET_BITS(bits) \ 5962306a36Sopenharmony_ciu##bits btrfs_get_token_##bits(struct btrfs_map_token *token, \ 6062306a36Sopenharmony_ci const void *ptr, unsigned long off) \ 6162306a36Sopenharmony_ci{ \ 6262306a36Sopenharmony_ci const unsigned long member_offset = (unsigned long)ptr + off; \ 6362306a36Sopenharmony_ci const unsigned long idx = get_eb_page_index(member_offset); \ 6462306a36Sopenharmony_ci const unsigned long oip = get_eb_offset_in_page(token->eb, \ 6562306a36Sopenharmony_ci member_offset); \ 6662306a36Sopenharmony_ci const int size = sizeof(u##bits); \ 6762306a36Sopenharmony_ci u8 lebytes[sizeof(u##bits)]; \ 6862306a36Sopenharmony_ci const int part = PAGE_SIZE - oip; \ 6962306a36Sopenharmony_ci \ 7062306a36Sopenharmony_ci ASSERT(token); \ 7162306a36Sopenharmony_ci ASSERT(token->kaddr); \ 7262306a36Sopenharmony_ci ASSERT(check_setget_bounds(token->eb, ptr, off, size)); \ 7362306a36Sopenharmony_ci if (token->offset <= member_offset && \ 7462306a36Sopenharmony_ci member_offset + size <= token->offset + PAGE_SIZE) { \ 7562306a36Sopenharmony_ci return get_unaligned_le##bits(token->kaddr + oip); \ 7662306a36Sopenharmony_ci } \ 7762306a36Sopenharmony_ci token->kaddr = page_address(token->eb->pages[idx]); \ 7862306a36Sopenharmony_ci token->offset = idx << PAGE_SHIFT; \ 7962306a36Sopenharmony_ci if (INLINE_EXTENT_BUFFER_PAGES == 1 || oip + size <= PAGE_SIZE ) \ 8062306a36Sopenharmony_ci return get_unaligned_le##bits(token->kaddr + oip); \ 8162306a36Sopenharmony_ci \ 8262306a36Sopenharmony_ci memcpy(lebytes, token->kaddr + oip, part); \ 8362306a36Sopenharmony_ci token->kaddr = page_address(token->eb->pages[idx + 1]); \ 8462306a36Sopenharmony_ci token->offset = (idx + 1) << PAGE_SHIFT; \ 8562306a36Sopenharmony_ci memcpy(lebytes + part, token->kaddr, size - part); \ 8662306a36Sopenharmony_ci return get_unaligned_le##bits(lebytes); \ 8762306a36Sopenharmony_ci} \ 8862306a36Sopenharmony_ciu##bits btrfs_get_##bits(const struct extent_buffer *eb, \ 8962306a36Sopenharmony_ci const void *ptr, unsigned long off) \ 9062306a36Sopenharmony_ci{ \ 9162306a36Sopenharmony_ci const unsigned long member_offset = (unsigned long)ptr + off; \ 9262306a36Sopenharmony_ci const unsigned long oip = get_eb_offset_in_page(eb, member_offset); \ 9362306a36Sopenharmony_ci const unsigned long idx = get_eb_page_index(member_offset); \ 9462306a36Sopenharmony_ci char *kaddr = page_address(eb->pages[idx]); \ 9562306a36Sopenharmony_ci const int size = sizeof(u##bits); \ 9662306a36Sopenharmony_ci const int part = PAGE_SIZE - oip; \ 9762306a36Sopenharmony_ci u8 lebytes[sizeof(u##bits)]; \ 9862306a36Sopenharmony_ci \ 9962306a36Sopenharmony_ci ASSERT(check_setget_bounds(eb, ptr, off, size)); \ 10062306a36Sopenharmony_ci if (INLINE_EXTENT_BUFFER_PAGES == 1 || oip + size <= PAGE_SIZE) \ 10162306a36Sopenharmony_ci return get_unaligned_le##bits(kaddr + oip); \ 10262306a36Sopenharmony_ci \ 10362306a36Sopenharmony_ci memcpy(lebytes, kaddr + oip, part); \ 10462306a36Sopenharmony_ci kaddr = page_address(eb->pages[idx + 1]); \ 10562306a36Sopenharmony_ci memcpy(lebytes + part, kaddr, size - part); \ 10662306a36Sopenharmony_ci return get_unaligned_le##bits(lebytes); \ 10762306a36Sopenharmony_ci} \ 10862306a36Sopenharmony_civoid btrfs_set_token_##bits(struct btrfs_map_token *token, \ 10962306a36Sopenharmony_ci const void *ptr, unsigned long off, \ 11062306a36Sopenharmony_ci u##bits val) \ 11162306a36Sopenharmony_ci{ \ 11262306a36Sopenharmony_ci const unsigned long member_offset = (unsigned long)ptr + off; \ 11362306a36Sopenharmony_ci const unsigned long idx = get_eb_page_index(member_offset); \ 11462306a36Sopenharmony_ci const unsigned long oip = get_eb_offset_in_page(token->eb, \ 11562306a36Sopenharmony_ci member_offset); \ 11662306a36Sopenharmony_ci const int size = sizeof(u##bits); \ 11762306a36Sopenharmony_ci u8 lebytes[sizeof(u##bits)]; \ 11862306a36Sopenharmony_ci const int part = PAGE_SIZE - oip; \ 11962306a36Sopenharmony_ci \ 12062306a36Sopenharmony_ci ASSERT(token); \ 12162306a36Sopenharmony_ci ASSERT(token->kaddr); \ 12262306a36Sopenharmony_ci ASSERT(check_setget_bounds(token->eb, ptr, off, size)); \ 12362306a36Sopenharmony_ci if (token->offset <= member_offset && \ 12462306a36Sopenharmony_ci member_offset + size <= token->offset + PAGE_SIZE) { \ 12562306a36Sopenharmony_ci put_unaligned_le##bits(val, token->kaddr + oip); \ 12662306a36Sopenharmony_ci return; \ 12762306a36Sopenharmony_ci } \ 12862306a36Sopenharmony_ci token->kaddr = page_address(token->eb->pages[idx]); \ 12962306a36Sopenharmony_ci token->offset = idx << PAGE_SHIFT; \ 13062306a36Sopenharmony_ci if (INLINE_EXTENT_BUFFER_PAGES == 1 || oip + size <= PAGE_SIZE) { \ 13162306a36Sopenharmony_ci put_unaligned_le##bits(val, token->kaddr + oip); \ 13262306a36Sopenharmony_ci return; \ 13362306a36Sopenharmony_ci } \ 13462306a36Sopenharmony_ci put_unaligned_le##bits(val, lebytes); \ 13562306a36Sopenharmony_ci memcpy(token->kaddr + oip, lebytes, part); \ 13662306a36Sopenharmony_ci token->kaddr = page_address(token->eb->pages[idx + 1]); \ 13762306a36Sopenharmony_ci token->offset = (idx + 1) << PAGE_SHIFT; \ 13862306a36Sopenharmony_ci memcpy(token->kaddr, lebytes + part, size - part); \ 13962306a36Sopenharmony_ci} \ 14062306a36Sopenharmony_civoid btrfs_set_##bits(const struct extent_buffer *eb, void *ptr, \ 14162306a36Sopenharmony_ci unsigned long off, u##bits val) \ 14262306a36Sopenharmony_ci{ \ 14362306a36Sopenharmony_ci const unsigned long member_offset = (unsigned long)ptr + off; \ 14462306a36Sopenharmony_ci const unsigned long oip = get_eb_offset_in_page(eb, member_offset); \ 14562306a36Sopenharmony_ci const unsigned long idx = get_eb_page_index(member_offset); \ 14662306a36Sopenharmony_ci char *kaddr = page_address(eb->pages[idx]); \ 14762306a36Sopenharmony_ci const int size = sizeof(u##bits); \ 14862306a36Sopenharmony_ci const int part = PAGE_SIZE - oip; \ 14962306a36Sopenharmony_ci u8 lebytes[sizeof(u##bits)]; \ 15062306a36Sopenharmony_ci \ 15162306a36Sopenharmony_ci ASSERT(check_setget_bounds(eb, ptr, off, size)); \ 15262306a36Sopenharmony_ci if (INLINE_EXTENT_BUFFER_PAGES == 1 || oip + size <= PAGE_SIZE) { \ 15362306a36Sopenharmony_ci put_unaligned_le##bits(val, kaddr + oip); \ 15462306a36Sopenharmony_ci return; \ 15562306a36Sopenharmony_ci } \ 15662306a36Sopenharmony_ci \ 15762306a36Sopenharmony_ci put_unaligned_le##bits(val, lebytes); \ 15862306a36Sopenharmony_ci memcpy(kaddr + oip, lebytes, part); \ 15962306a36Sopenharmony_ci kaddr = page_address(eb->pages[idx + 1]); \ 16062306a36Sopenharmony_ci memcpy(kaddr, lebytes + part, size - part); \ 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciDEFINE_BTRFS_SETGET_BITS(8) 16462306a36Sopenharmony_ciDEFINE_BTRFS_SETGET_BITS(16) 16562306a36Sopenharmony_ciDEFINE_BTRFS_SETGET_BITS(32) 16662306a36Sopenharmony_ciDEFINE_BTRFS_SETGET_BITS(64) 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_civoid btrfs_node_key(const struct extent_buffer *eb, 16962306a36Sopenharmony_ci struct btrfs_disk_key *disk_key, int nr) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci unsigned long ptr = btrfs_node_key_ptr_offset(eb, nr); 17262306a36Sopenharmony_ci read_eb_member(eb, (struct btrfs_key_ptr *)ptr, 17362306a36Sopenharmony_ci struct btrfs_key_ptr, key, disk_key); 17462306a36Sopenharmony_ci} 175