at v5.10 169 lines 6.2 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2007 Oracle. All rights reserved. 4 */ 5 6#include <asm/unaligned.h> 7 8#include "ctree.h" 9 10static bool check_setget_bounds(const struct extent_buffer *eb, 11 const void *ptr, unsigned off, int size) 12{ 13 const unsigned long member_offset = (unsigned long)ptr + off; 14 15 if (member_offset > eb->len) { 16 btrfs_warn(eb->fs_info, 17 "bad eb member start: ptr 0x%lx start %llu member offset %lu size %d", 18 (unsigned long)ptr, eb->start, member_offset, size); 19 return false; 20 } 21 if (member_offset + size > eb->len) { 22 btrfs_warn(eb->fs_info, 23 "bad eb member end: ptr 0x%lx start %llu member offset %lu size %d", 24 (unsigned long)ptr, eb->start, member_offset, size); 25 return false; 26 } 27 28 return true; 29} 30 31/* 32 * Macro templates that define helpers to read/write extent buffer data of a 33 * given size, that are also used via ctree.h for access to item members by 34 * specialized helpers. 35 * 36 * Generic helpers: 37 * - btrfs_set_8 (for 8/16/32/64) 38 * - btrfs_get_8 (for 8/16/32/64) 39 * 40 * Generic helpers with a token (cached address of the most recently accessed 41 * page): 42 * - btrfs_set_token_8 (for 8/16/32/64) 43 * - btrfs_get_token_8 (for 8/16/32/64) 44 * 45 * The set/get functions handle data spanning two pages transparently, in case 46 * metadata block size is larger than page. Every pointer to metadata items is 47 * an offset into the extent buffer page array, cast to a specific type. This 48 * gives us all the type checking. 49 * 50 * The extent buffer pages stored in the array pages do not form a contiguous 51 * phyusical range, but the API functions assume the linear offset to the range 52 * from 0 to metadata node size. 53 */ 54 55#define DEFINE_BTRFS_SETGET_BITS(bits) \ 56u##bits btrfs_get_token_##bits(struct btrfs_map_token *token, \ 57 const void *ptr, unsigned long off) \ 58{ \ 59 const unsigned long member_offset = (unsigned long)ptr + off; \ 60 const unsigned long idx = member_offset >> PAGE_SHIFT; \ 61 const unsigned long oip = offset_in_page(member_offset); \ 62 const int size = sizeof(u##bits); \ 63 u8 lebytes[sizeof(u##bits)]; \ 64 const int part = PAGE_SIZE - oip; \ 65 \ 66 ASSERT(token); \ 67 ASSERT(token->kaddr); \ 68 ASSERT(check_setget_bounds(token->eb, ptr, off, size)); \ 69 if (token->offset <= member_offset && \ 70 member_offset + size <= token->offset + PAGE_SIZE) { \ 71 return get_unaligned_le##bits(token->kaddr + oip); \ 72 } \ 73 token->kaddr = page_address(token->eb->pages[idx]); \ 74 token->offset = idx << PAGE_SHIFT; \ 75 if (oip + size <= PAGE_SIZE) \ 76 return get_unaligned_le##bits(token->kaddr + oip); \ 77 \ 78 memcpy(lebytes, token->kaddr + oip, part); \ 79 token->kaddr = page_address(token->eb->pages[idx + 1]); \ 80 token->offset = (idx + 1) << PAGE_SHIFT; \ 81 memcpy(lebytes + part, token->kaddr, size - part); \ 82 return get_unaligned_le##bits(lebytes); \ 83} \ 84u##bits btrfs_get_##bits(const struct extent_buffer *eb, \ 85 const void *ptr, unsigned long off) \ 86{ \ 87 const unsigned long member_offset = (unsigned long)ptr + off; \ 88 const unsigned long oip = offset_in_page(member_offset); \ 89 const unsigned long idx = member_offset >> PAGE_SHIFT; \ 90 char *kaddr = page_address(eb->pages[idx]); \ 91 const int size = sizeof(u##bits); \ 92 const int part = PAGE_SIZE - oip; \ 93 u8 lebytes[sizeof(u##bits)]; \ 94 \ 95 ASSERT(check_setget_bounds(eb, ptr, off, size)); \ 96 if (oip + size <= PAGE_SIZE) \ 97 return get_unaligned_le##bits(kaddr + oip); \ 98 \ 99 memcpy(lebytes, kaddr + oip, part); \ 100 kaddr = page_address(eb->pages[idx + 1]); \ 101 memcpy(lebytes + part, kaddr, size - part); \ 102 return get_unaligned_le##bits(lebytes); \ 103} \ 104void btrfs_set_token_##bits(struct btrfs_map_token *token, \ 105 const void *ptr, unsigned long off, \ 106 u##bits val) \ 107{ \ 108 const unsigned long member_offset = (unsigned long)ptr + off; \ 109 const unsigned long idx = member_offset >> PAGE_SHIFT; \ 110 const unsigned long oip = offset_in_page(member_offset); \ 111 const int size = sizeof(u##bits); \ 112 u8 lebytes[sizeof(u##bits)]; \ 113 const int part = PAGE_SIZE - oip; \ 114 \ 115 ASSERT(token); \ 116 ASSERT(token->kaddr); \ 117 ASSERT(check_setget_bounds(token->eb, ptr, off, size)); \ 118 if (token->offset <= member_offset && \ 119 member_offset + size <= token->offset + PAGE_SIZE) { \ 120 put_unaligned_le##bits(val, token->kaddr + oip); \ 121 return; \ 122 } \ 123 token->kaddr = page_address(token->eb->pages[idx]); \ 124 token->offset = idx << PAGE_SHIFT; \ 125 if (oip + size <= PAGE_SIZE) { \ 126 put_unaligned_le##bits(val, token->kaddr + oip); \ 127 return; \ 128 } \ 129 put_unaligned_le##bits(val, lebytes); \ 130 memcpy(token->kaddr + oip, lebytes, part); \ 131 token->kaddr = page_address(token->eb->pages[idx + 1]); \ 132 token->offset = (idx + 1) << PAGE_SHIFT; \ 133 memcpy(token->kaddr, lebytes + part, size - part); \ 134} \ 135void btrfs_set_##bits(const struct extent_buffer *eb, void *ptr, \ 136 unsigned long off, u##bits val) \ 137{ \ 138 const unsigned long member_offset = (unsigned long)ptr + off; \ 139 const unsigned long oip = offset_in_page(member_offset); \ 140 const unsigned long idx = member_offset >> PAGE_SHIFT; \ 141 char *kaddr = page_address(eb->pages[idx]); \ 142 const int size = sizeof(u##bits); \ 143 const int part = PAGE_SIZE - oip; \ 144 u8 lebytes[sizeof(u##bits)]; \ 145 \ 146 ASSERT(check_setget_bounds(eb, ptr, off, size)); \ 147 if (oip + size <= PAGE_SIZE) { \ 148 put_unaligned_le##bits(val, kaddr + oip); \ 149 return; \ 150 } \ 151 \ 152 put_unaligned_le##bits(val, lebytes); \ 153 memcpy(kaddr + oip, lebytes, part); \ 154 kaddr = page_address(eb->pages[idx + 1]); \ 155 memcpy(kaddr, lebytes + part, size - part); \ 156} 157 158DEFINE_BTRFS_SETGET_BITS(8) 159DEFINE_BTRFS_SETGET_BITS(16) 160DEFINE_BTRFS_SETGET_BITS(32) 161DEFINE_BTRFS_SETGET_BITS(64) 162 163void btrfs_node_key(const struct extent_buffer *eb, 164 struct btrfs_disk_key *disk_key, int nr) 165{ 166 unsigned long ptr = btrfs_node_key_ptr_offset(nr); 167 read_eb_member(eb, (struct btrfs_key_ptr *)ptr, 168 struct btrfs_key_ptr, key, disk_key); 169}