accessors.c 6.7 KB

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