e357e0bab397e7e13d6128b99b04c2c784407c46
[platform/kernel/linux-starfive.git] / fs / btrfs / struct-funcs.c
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
10 static inline u8 get_unaligned_le8(const void *p)
11 {
12        return *(u8 *)p;
13 }
14
15 static inline void put_unaligned_le8(u8 val, void *p)
16 {
17        *(u8 *)p = val;
18 }
19
20 static bool check_setget_bounds(const struct extent_buffer *eb,
21                                 const void *ptr, unsigned off, int size)
22 {
23         const unsigned long member_offset = (unsigned long)ptr + off;
24
25         if (member_offset > eb->len) {
26                 btrfs_warn(eb->fs_info,
27         "bad eb member start: ptr 0x%lx start %llu member offset %lu size %d",
28                         (unsigned long)ptr, eb->start, member_offset, size);
29                 return false;
30         }
31         if (member_offset + size > eb->len) {
32                 btrfs_warn(eb->fs_info,
33         "bad eb member end: ptr 0x%lx start %llu member offset %lu size %d",
34                         (unsigned long)ptr, eb->start, member_offset, size);
35                 return false;
36         }
37
38         return true;
39 }
40
41 /*
42  * this is some deeply nasty code.
43  *
44  * The end result is that anyone who #includes ctree.h gets a
45  * declaration for the btrfs_set_foo functions and btrfs_foo functions,
46  * which are wrappers of btrfs_set_token_#bits functions and
47  * btrfs_get_token_#bits functions, which are defined in this file.
48  *
49  * These setget functions do all the extent_buffer related mapping
50  * required to efficiently read and write specific fields in the extent
51  * buffers.  Every pointer to metadata items in btrfs is really just
52  * an unsigned long offset into the extent buffer which has been
53  * cast to a specific type.  This gives us all the gcc type checking.
54  *
55  * The extent buffer api is used to do the page spanning work required to
56  * have a metadata blocksize different from the page size.
57  *
58  * There are 2 variants defined, one with a token pointer and one without.
59  */
60
61 #define DEFINE_BTRFS_SETGET_BITS(bits)                                  \
62 u##bits btrfs_get_token_##bits(struct btrfs_map_token *token,           \
63                                const void *ptr, unsigned long off)      \
64 {                                                                       \
65         const unsigned long member_offset = (unsigned long)ptr + off;   \
66         const unsigned long idx = member_offset >> PAGE_SHIFT;          \
67         const unsigned long oip = offset_in_page(member_offset);        \
68         const int size = sizeof(u##bits);                               \
69         __le##bits leres;                                               \
70                                                                         \
71         ASSERT(token);                                                  \
72         ASSERT(token->kaddr);                                           \
73         ASSERT(check_setget_bounds(token->eb, ptr, off, size));         \
74         if (token->offset <= member_offset &&                           \
75             member_offset + size <= token->offset + PAGE_SIZE) {        \
76                 return get_unaligned_le##bits(token->kaddr + oip);      \
77         }                                                               \
78         if (oip + size <= PAGE_SIZE) {                                  \
79                 token->kaddr = page_address(token->eb->pages[idx]);     \
80                 token->offset = idx << PAGE_SHIFT;                      \
81                 return get_unaligned_le##bits(token->kaddr + oip);      \
82         }                                                               \
83         token->kaddr = page_address(token->eb->pages[idx + 1]);         \
84         token->offset = (idx + 1) << PAGE_SHIFT;                        \
85         read_extent_buffer(token->eb, &leres, member_offset, size);     \
86         return le##bits##_to_cpu(leres);                                \
87 }                                                                       \
88 u##bits btrfs_get_##bits(const struct extent_buffer *eb,                \
89                          const void *ptr, unsigned long off)            \
90 {                                                                       \
91         const unsigned long member_offset = (unsigned long)ptr + off;   \
92         const unsigned long oip = offset_in_page(member_offset);        \
93         const int size = sizeof(u##bits);                               \
94         __le##bits leres;                                               \
95                                                                         \
96         ASSERT(check_setget_bounds(eb, ptr, off, size));                \
97         if (oip + size <= PAGE_SIZE) {                                  \
98                 const unsigned long idx = member_offset >> PAGE_SHIFT;  \
99                 const char *kaddr = page_address(eb->pages[idx]);       \
100                 return get_unaligned_le##bits(kaddr + oip);             \
101         }                                                               \
102         read_extent_buffer(eb, &leres, member_offset, size);            \
103         return le##bits##_to_cpu(leres);                                \
104 }                                                                       \
105 void btrfs_set_token_##bits(struct btrfs_map_token *token,              \
106                             const void *ptr, unsigned long off,         \
107                             u##bits val)                                \
108 {                                                                       \
109         unsigned long part_offset = (unsigned long)ptr;                 \
110         unsigned long offset = part_offset + off;                       \
111         void *p;                                                        \
112         int err;                                                        \
113         char *kaddr;                                                    \
114         unsigned long map_start;                                        \
115         unsigned long map_len;                                          \
116         int size = sizeof(u##bits);                                     \
117                                                                         \
118         ASSERT(token);                                                  \
119         ASSERT(token->kaddr);                                           \
120         ASSERT(check_setget_bounds(token->eb, ptr, off, size));         \
121         if (token->offset <= offset &&                                  \
122            (token->offset + PAGE_SIZE >= offset + size)) {      \
123                 kaddr = token->kaddr;                                   \
124                 p = kaddr + part_offset - token->offset;                \
125                 put_unaligned_le##bits(val, p + off);                   \
126                 return;                                                 \
127         }                                                               \
128         err = map_private_extent_buffer(token->eb, offset, size,        \
129                         &kaddr, &map_start, &map_len);                  \
130         if (err) {                                                      \
131                 __le##bits val2;                                        \
132                                                                         \
133                 val2 = cpu_to_le##bits(val);                            \
134                 write_extent_buffer(token->eb, &val2, offset, size);    \
135                 return;                                                 \
136         }                                                               \
137         p = kaddr + part_offset - map_start;                            \
138         put_unaligned_le##bits(val, p + off);                           \
139         token->kaddr = kaddr;                                           \
140         token->offset = map_start;                                      \
141 }                                                                       \
142 void btrfs_set_##bits(struct extent_buffer *eb, void *ptr,              \
143                       unsigned long off, u##bits val)                   \
144 {                                                                       \
145         unsigned long part_offset = (unsigned long)ptr;                 \
146         unsigned long offset = part_offset + off;                       \
147         void *p;                                                        \
148         int err;                                                        \
149         char *kaddr;                                                    \
150         unsigned long map_start;                                        \
151         unsigned long map_len;                                          \
152         int size = sizeof(u##bits);                                     \
153                                                                         \
154         ASSERT(check_setget_bounds(eb, ptr, off, size));                \
155         err = map_private_extent_buffer(eb, offset, size,               \
156                         &kaddr, &map_start, &map_len);                  \
157         if (err) {                                                      \
158                 __le##bits val2;                                        \
159                                                                         \
160                 val2 = cpu_to_le##bits(val);                            \
161                 write_extent_buffer(eb, &val2, offset, size);           \
162                 return;                                                 \
163         }                                                               \
164         p = kaddr + part_offset - map_start;                            \
165         put_unaligned_le##bits(val, p + off);                           \
166 }
167
168 DEFINE_BTRFS_SETGET_BITS(8)
169 DEFINE_BTRFS_SETGET_BITS(16)
170 DEFINE_BTRFS_SETGET_BITS(32)
171 DEFINE_BTRFS_SETGET_BITS(64)
172
173 void btrfs_node_key(const struct extent_buffer *eb,
174                     struct btrfs_disk_key *disk_key, int nr)
175 {
176         unsigned long ptr = btrfs_node_key_ptr_offset(nr);
177         read_eb_member(eb, (struct btrfs_key_ptr *)ptr,
178                        struct btrfs_key_ptr, key, disk_key);
179 }