|  | /* | 
|  | * Copyright (C) 2007 Oracle.  All rights reserved. | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU General Public | 
|  | * License v2 as published by the Free Software Foundation. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU General Public | 
|  | * License along with this program; if not, write to the | 
|  | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | 
|  | * Boston, MA 021110-1307, USA. | 
|  | */ | 
|  |  | 
|  | #include <linux/highmem.h> | 
|  | #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 wappers 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(struct extent_buffer *eb, 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_CACHE_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,			\ | 
|  | 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_CACHE_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(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); | 
|  | } |