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