|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (C) 2007 Oracle.  All rights reserved. | 
|  | */ | 
|  |  | 
|  | #include <asm/unaligned.h> | 
|  |  | 
|  | #include "ctree.h" | 
|  |  | 
|  | static inline u8 get_unaligned_le8(const void *p) | 
|  | { | 
|  | return *(u8 *)p; | 
|  | } | 
|  |  | 
|  | static inline void put_unaligned_le8(u8 val, void *p) | 
|  | { | 
|  | *(u8 *)p = val; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * this is some deeply nasty code. | 
|  | * | 
|  | * The end result is that anyone who #includes ctree.h gets a | 
|  | * declaration for the btrfs_set_foo functions and btrfs_foo functions, | 
|  | * which are wrappers of btrfs_set_token_#bits functions and | 
|  | * btrfs_get_token_#bits functions, which are defined in this file. | 
|  | * | 
|  | * These setget functions do all the extent_buffer related mapping | 
|  | * required to efficiently read and write specific fields in the extent | 
|  | * buffers.  Every pointer to metadata items in btrfs is really just | 
|  | * an unsigned long offset into the extent buffer which has been | 
|  | * cast to a specific type.  This gives us all the gcc type checking. | 
|  | * | 
|  | * The extent buffer api is used to do the page spanning work required to | 
|  | * have a metadata blocksize different from the page size. | 
|  | */ | 
|  |  | 
|  | #define DEFINE_BTRFS_SETGET_BITS(bits)					\ | 
|  | u##bits btrfs_get_token_##bits(const struct extent_buffer *eb,		\ | 
|  | const void *ptr, unsigned long off,	\ | 
|  | struct btrfs_map_token *token)		\ | 
|  | {									\ | 
|  | unsigned long part_offset = (unsigned long)ptr;			\ | 
|  | unsigned long offset = part_offset + off;			\ | 
|  | void *p;							\ | 
|  | int err;							\ | 
|  | char *kaddr;							\ | 
|  | unsigned long map_start;					\ | 
|  | unsigned long map_len;						\ | 
|  | int size = sizeof(u##bits);					\ | 
|  | u##bits res;							\ | 
|  | \ | 
|  | if (token && token->kaddr && token->offset <= offset &&		\ | 
|  | token->eb == eb &&						\ | 
|  | (token->offset + PAGE_SIZE >= offset + size)) {	\ | 
|  | kaddr = token->kaddr;					\ | 
|  | p = kaddr + part_offset - token->offset;		\ | 
|  | res = get_unaligned_le##bits(p + off);			\ | 
|  | return res;						\ | 
|  | }								\ | 
|  | err = map_private_extent_buffer(eb, offset, size,		\ | 
|  | &kaddr, &map_start, &map_len);	\ | 
|  | if (err) {							\ | 
|  | __le##bits leres;					\ | 
|  | \ | 
|  | read_extent_buffer(eb, &leres, offset, size);		\ | 
|  | return le##bits##_to_cpu(leres);			\ | 
|  | }								\ | 
|  | p = kaddr + part_offset - map_start;				\ | 
|  | res = get_unaligned_le##bits(p + off);				\ | 
|  | if (token) {							\ | 
|  | token->kaddr = kaddr;					\ | 
|  | token->offset = map_start;				\ | 
|  | token->eb = eb;						\ | 
|  | }								\ | 
|  | return res;							\ | 
|  | }									\ | 
|  | void btrfs_set_token_##bits(struct extent_buffer *eb,			\ | 
|  | const void *ptr, unsigned long off,		\ | 
|  | u##bits val,				\ | 
|  | struct btrfs_map_token *token)		\ | 
|  | {									\ | 
|  | unsigned long part_offset = (unsigned long)ptr;			\ | 
|  | unsigned long offset = part_offset + off;			\ | 
|  | void *p;							\ | 
|  | int err;							\ | 
|  | char *kaddr;							\ | 
|  | unsigned long map_start;					\ | 
|  | unsigned long map_len;						\ | 
|  | int size = sizeof(u##bits);					\ | 
|  | \ | 
|  | if (token && token->kaddr && token->offset <= offset &&		\ | 
|  | token->eb == eb &&						\ | 
|  | (token->offset + PAGE_SIZE >= offset + size)) {	\ | 
|  | kaddr = token->kaddr;					\ | 
|  | p = kaddr + part_offset - token->offset;		\ | 
|  | put_unaligned_le##bits(val, p + off);			\ | 
|  | return;							\ | 
|  | }								\ | 
|  | err = map_private_extent_buffer(eb, offset, size,		\ | 
|  | &kaddr, &map_start, &map_len);			\ | 
|  | if (err) {							\ | 
|  | __le##bits val2;					\ | 
|  | \ | 
|  | val2 = cpu_to_le##bits(val);				\ | 
|  | write_extent_buffer(eb, &val2, offset, size);		\ | 
|  | return;							\ | 
|  | }								\ | 
|  | p = kaddr + part_offset - map_start;				\ | 
|  | put_unaligned_le##bits(val, p + off);				\ | 
|  | if (token) {							\ | 
|  | token->kaddr = kaddr;					\ | 
|  | token->offset = map_start;				\ | 
|  | token->eb = eb;						\ | 
|  | }								\ | 
|  | } | 
|  |  | 
|  | DEFINE_BTRFS_SETGET_BITS(8) | 
|  | DEFINE_BTRFS_SETGET_BITS(16) | 
|  | DEFINE_BTRFS_SETGET_BITS(32) | 
|  | DEFINE_BTRFS_SETGET_BITS(64) | 
|  |  | 
|  | void btrfs_node_key(const struct extent_buffer *eb, | 
|  | struct btrfs_disk_key *disk_key, int nr) | 
|  | { | 
|  | unsigned long ptr = btrfs_node_key_ptr_offset(nr); | 
|  | read_eb_member(eb, (struct btrfs_key_ptr *)ptr, | 
|  | struct btrfs_key_ptr, key, disk_key); | 
|  | } |