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