pax_global_header00006660000000000000000000000064132443463730014523gustar00rootroot0000000000000052 comment=4d1a278d8b90f439f3e5dadfe02e7d8c566eecc5 compsize-1.1/000077500000000000000000000000001324434637300132155ustar00rootroot00000000000000compsize-1.1/.gitignore000066400000000000000000000000201324434637300151750ustar00rootroot00000000000000compsize *.o *~ compsize-1.1/LICENSE000066400000000000000000000003601324434637300142210ustar00rootroot00000000000000This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. compsize-1.1/Makefile000066400000000000000000000007441324434637300146620ustar00rootroot00000000000000PREFIX ?= / CC ?= gcc CFLAGS ?= -Wall -std=gnu90 all: compsize debug: CFLAGS += -Wall -DDEBUG -g debug: compsize compsize.o: compsize.c $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $^ radix-tree.o: radix-tree.c $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $^ compsize: compsize.o radix-tree.o $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ clean: rm -f compsize rm -f *.o install: install -Dm755 compsize $(PREFIX)/usr/bin/compsize gzip -9n $(PREFIX)/usr/share/man/man8/compsize.8.gz compsize-1.1/README.md000066400000000000000000000030721324434637300144760ustar00rootroot00000000000000# compsize compsize takes a list of files (given as arguments) on a btrfs filesystem and measures used compression types and effective compression ratio, producing a report such as: ``` [~]$ compsize /home Processed 140058 files, 133128 regular extents (196786 refs), 80886 inline. Type Perc Disk Usage Uncompressed Referenced TOTAL 93% 14G 15G 12G none 100% 13G 13G 10G zlib 41% 628M 1.4G 1.4G zstd 28% 42M 148M 148M ``` A directory has no extents but has a (recursive) list of files. A non-regular file is silently ignored. As it makes no sense to talk about compression ratio of a partial extent, every referenced extent is counted whole, exactly once -- no matter if you use only a few bytes of a 1GB extent or reflink it a thousand times. Thus, the uncompressed size will not match the number given by **tar** or **du**. On the other hand, the space _used_ should be accurate (although obviously it can be shared with files outside our set). The fields are: * Type: compression algorithm used * Perc: disk usage/uncompressed -- ie, effective compression ratio * Disk Usage: blocks actually used on the disk * Uncompressed: extents before compression * Referenced: apparent size of files (minus holes) The ioctl used requires root. # Installation: Besides regular C toolchain, you need btrfs userspace headers, usually installed as part of btrfs-progs; SuSE ships them inside libbtrfs-devel. Required kernel: 3.16, btrfs-progs: 3.18 (untested!). compsize-1.1/compsize.8000066400000000000000000000051641324434637300151450ustar00rootroot00000000000000.TH compsize 8 2017-09-04 btrfs btrfs .SH NAME compsize \- calculate compression ratio of a set of files on btrfs .SH SYNOPSIS .B compsize .I file-or-dir [ .I file-or-dir \&... ] .SH DESCRIPTION .B compsize takes a list of files on a btrfs filesystem (recursing directories) and measures used compression types and the effective compression ratio. .P As talking about compression ratio for a partial extent doesn't quite make any sense, every used extent is considered in its entirety. Every extent is also counted exactly once, even if it's reflinked multiple times. .P The program gives a report similar to: .br Processed 90319 files. .br Type Perc Disk Usage Uncompressed Referenced .br TOTAL 79% 1.4G 1.8G 1.9G .br none 100% 1.0G 1.0G 1.0G .br lzo 53% 446M 833M 843M .P The fields above are: .TP .B Type compression algorithm .TP .B Perc disk usage/uncompressed (compression ratio) .TP .B Disk Usage blocks on the disk; this is what storing these files actually costs you (save for RAID considerations) .TP .B Uncompressed uncompressed extents; what you would need without compression \- includes deduplication savings and pinned extent waste .TP .B Referenced apparent file sizes (sans holes); this is what a traditional filesystem that supports holes and efficient tail packing, or .IR "tar -S" , would need to store these files .P Let's see this on an example: a file 128K big is stored as a single extent A which compressed to a single 4K page. It then receives a write of 32K at offset 32K, which also compressed to a single 4K page, stored as extent B. .P The file now appears as: .br +-------+-------+---------------+ .br extent A | used | waste | used | .br +-------+-------+---------------+ .br extent B | used | .br +-------+ .P The "waste" inside extent A can't be gotten rid until the whole extent is rewritten (for example by \fIdefrag\fR). If compressed, the whole extent needs to be read every time that part of the file is being read, thus the "waste" is still required. .P In this case, we have: \fBDisk Usage\fR: 8KB, \fBUncompressed\fR: 160K, \fBReferenced\fR: 128K. .SH OPTIONS .TP .BR -b / --bytes Show raw byte counts rather than human-friendly sizes. .TP .BR -x / --one-file-system Skip files and directories on different file systems. .SH CAVEATS Recently written files may show as not taking any space until they're actually allocated and compressed; this happens once they're synced or on natural writeout, typically on the order of 30 seconds. .P The ioctls used by this program require root. compsize-1.1/compsize.c000066400000000000000000000267321324434637300152240ustar00rootroot00000000000000#define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "radix-tree.h" #include "endianness.h" #if defined(DEBUG) #define DPRINTF(fmt, args...) fprintf(stderr, fmt, ##args) #else #define DPRINTF(fmt, args...) #endif // We recognize yet-unknown compression types (u8). #define MAX_ENTRIES 256 #ifndef SZ_16M // old kernel headers #define SZ_16M 16777216 #endif struct btrfs_sv2_args { struct btrfs_ioctl_search_key key; uint64_t buf_size; uint8_t buf[SZ_16M]; // hardcoded kernel's limit }; struct workspace { uint64_t disk[MAX_ENTRIES]; uint64_t uncomp[MAX_ENTRIES]; uint64_t refd[MAX_ENTRIES]; uint64_t disk_all; uint64_t uncomp_all; uint64_t refd_all; uint64_t nfiles; uint64_t nextents, nrefs, ninline; struct radix_tree_root seen_extents; }; static const char *comp_types[MAX_ENTRIES] = { "none", "zlib", "lzo", "zstd" }; static int opt_bytes = 0; static int opt_one_fs = 0; static void die(const char *txt, ...) __attribute__((format (printf, 1, 2))); static void die(const char *txt, ...) { va_list ap; va_start(ap, txt); vfprintf(stderr, txt, ap); va_end(ap); exit(1); } static uint64_t get_u64(const void *mem) { typedef struct __attribute__((__packed__)) { uint64_t v; } u64_unal; uint64_t bad_endian = ((u64_unal*)mem)->v; return htole64(bad_endian); } static uint32_t get_u32(const void *mem) { typedef struct __attribute__((__packed__)) { uint32_t v; } u32_unal; uint32_t bad_endian = ((u32_unal*)mem)->v; return htole32(bad_endian); } static void init_sv2_args(ino_t st_ino, struct btrfs_sv2_args *sv2_args) { sv2_args->key.tree_id = 0; sv2_args->key.max_objectid = st_ino; sv2_args->key.min_objectid = st_ino; sv2_args->key.min_offset = 0; sv2_args->key.max_offset = -1; sv2_args->key.min_transid = 0; sv2_args->key.max_transid = -1; // Only search for EXTENT_DATA_KEY sv2_args->key.min_type = BTRFS_EXTENT_DATA_KEY; sv2_args->key.max_type = BTRFS_EXTENT_DATA_KEY; sv2_args->key.nr_items = -1; sv2_args->buf_size = sizeof(sv2_args->buf); } static inline int is_hole(uint64_t disk_bytenr) { return disk_bytenr == 0; } static inline int is_inline_data(uint8_t type) { return type == 0; } static void parse_file_extent_item(uint8_t *bp, uint32_t hlen, struct workspace *ws) { struct btrfs_file_extent_item *ei; uint64_t disk_num_bytes, ram_bytes, disk_bytenr, num_bytes; uint32_t inline_header_sz; uint8_t comp_type; DPRINTF("len=%u\n", hlen); ei = (struct btrfs_file_extent_item *) bp; ram_bytes = get_u64(&ei->ram_bytes); comp_type = ei->compression; if (is_inline_data(ei->type)) { inline_header_sz = sizeof(*ei); inline_header_sz -= sizeof(ei->disk_bytenr); inline_header_sz -= sizeof(ei->disk_num_bytes); inline_header_sz -= sizeof(ei->offset); inline_header_sz -= sizeof(ei->num_bytes); disk_num_bytes = hlen-inline_header_sz; DPRINTF("inline: ram_bytes=%lu compression=%u disk_num_bytes=%lu\n", ram_bytes, comp_type, disk_num_bytes); ws->disk[comp_type] += disk_num_bytes; ws->uncomp[comp_type] += ram_bytes; ws->refd[comp_type] += ram_bytes; ws->ninline++; return; } if (hlen != sizeof(*ei)) die("Regular extent's header not 53 bytes (%u) long?!?\n", hlen); disk_num_bytes = get_u64(&ei->disk_num_bytes); disk_bytenr = get_u64(&ei->disk_bytenr); num_bytes = get_u64(&ei->num_bytes); if (is_hole(disk_bytenr)) return; DPRINTF("regular: ram_bytes=%lu compression=%u disk_num_bytes=%lu disk_bytenr=%lu\n", ram_bytes, comp_type, disk_num_bytes, disk_bytenr); if (!IS_ALIGNED(disk_bytenr, 1 << 12)) die("Extent not 4K-aligned at %"PRIu64"?!?\n", disk_bytenr); disk_bytenr >>= 12; radix_tree_preload(GFP_KERNEL); if (radix_tree_insert(&ws->seen_extents, disk_bytenr, (void *)disk_bytenr) == 0) { ws->disk[comp_type] += disk_num_bytes; ws->uncomp[comp_type] += ram_bytes; ws->nextents++; } radix_tree_preload_end(); ws->refd[comp_type] += num_bytes; ws->nrefs++; } static void do_file(int fd, ino_t st_ino, struct workspace *ws) { static struct btrfs_sv2_args sv2_args; struct btrfs_ioctl_search_header *head; uint32_t nr_items, hlen; uint8_t *bp; DPRINTF("inode = %" PRIu64"\n", st_ino); ws->nfiles++; init_sv2_args(st_ino, &sv2_args); again: if (ioctl(fd, BTRFS_IOC_TREE_SEARCH_V2, &sv2_args)) { if (errno == ENOTTY) die("Not btrfs (or SEARCH_V2 unsupported).\n"); else die("SEARCH_V2: %m\n"); } nr_items = sv2_args.key.nr_items; DPRINTF("nr_items = %u\n", nr_items); bp = sv2_args.buf; for (; nr_items > 0; nr_items--, bp += hlen) { head = (struct btrfs_ioctl_search_header*)bp; hlen = get_u32(&head->len); DPRINTF("{ transid=%lu objectid=%lu offset=%lu type=%u len=%u }\n", get_u64(&head->transid), get_u64(&head->objectid), get_u64(&head->offset), get_u32(&head->type), hlen); bp += sizeof(*head); parse_file_extent_item(bp, hlen, ws); } // Will be exactly 197379 (16MB/85) on overflow, but let's play it safe. // In theory, we're supposed to retry until getting 0, but RTFK says // there are no short reads (just running out of buffer space), so we // avoid having to search twice. if (sv2_args.key.nr_items > 16384) { sv2_args.key.nr_items = -1; sv2_args.key.min_offset = get_u64(&head->offset) + 1; goto again; } } static void do_recursive_search(const char *path, struct workspace *ws, const dev_t *dev) { int fd; int path_size; char *fn; DIR *dir; struct dirent *de; struct stat st; fd = open(path, O_RDONLY|O_NOFOLLOW|O_NOCTTY|O_NONBLOCK|__O_LARGEFILE); if (fd == -1) { if (errno == ELOOP // symlink || errno == ENXIO // some device nodes || errno == ENODEV // /dev/ptmx || errno == ENOMEDIUM// more device nodes || errno == ENOENT) // something just deleted return; else die("open(\"%s\"): %m\n", path); } DPRINTF("%s\n", path); if (fstat(fd, &st)) die("stat(\"%s\"): %m\n", path); if (opt_one_fs && dev != NULL && *dev != st.st_dev) return; if (S_ISDIR(st.st_mode)) { dir = fdopendir(fd); if (!dir) die("opendir(\"%s\"): %m\n", path); path_size = 2; // slash + \0; path_size += strlen(path) + NAME_MAX; fn = (char *) malloc(path_size); if (!fn) die("Out of memory.\n"); while(1) { de = readdir(dir); if (!de) break; if (de->d_type != DT_DIR && de->d_type != DT_REG && de->d_type != DT_UNKNOWN) { continue; } if (!strcmp(de->d_name, ".")) continue; if (!strcmp(de->d_name, "..")) continue; snprintf(fn, path_size, "%s/%s", path, de->d_name); do_recursive_search(fn, ws, &st.st_dev); } free(fn); closedir(dir); } if (S_ISREG(st.st_mode)) do_file(fd, st.st_ino, ws); close(fd); } #define HB 24 /* size of buffers */ static void human_bytes(uint64_t x, char *output) { static const char *units = "BKMGTPE"; int u = 0; if (opt_bytes) return (void)snprintf(output, HB, "%"PRIu64, x); while (x >= 10240) u++, x>>=10; if (x >= 1024) snprintf(output, HB, " %"PRIu64".%"PRIu64"%c", x>>10, x*10/1024%10, units[u+1]); else snprintf(output, HB, "%4"PRIu64"%c", x, units[u]); } static void print_table(const char *type, const char *percentage, const char *disk_usage, const char *uncomp_usage, const char *refd_usage) { printf("%-10s %-8s %-12s %-12s %-12s\n", type, percentage, disk_usage, uncomp_usage, refd_usage); } static void parse_options(int argc, char **argv) { static const char *short_options = "bx"; static struct option long_options[] = { {"bytes", 0, 0, 'b'}, {"one-file-system", 0, 0, 'x'}, {0}, }; while (1) { switch (getopt_long(argc, argv, short_options, long_options, 0)) { case 'b': opt_bytes = 1; break; case 'x': opt_one_fs = 1; break; case -1: return; default: exit(1); } } } int main(int argc, char **argv) { char perc[8], disk_usage[HB], uncomp_usage[HB], refd_usage[HB]; struct workspace *ws; uint32_t percentage; int t; ws = (struct workspace *) calloc(sizeof(*ws), 1); parse_options(argc, argv); if (optind >= argc) { fprintf(stderr, "Usage: compsize file-or-dir1 [file-or-dir2 ...]\n"); return 1; } radix_tree_init(); INIT_RADIX_TREE(&ws->seen_extents, 0); for (; argv[optind]; optind++) do_recursive_search(argv[optind], ws, NULL); for (t=0; tuncomp_all += ws->uncomp[t]; ws->disk_all += ws->disk[t]; ws->refd_all += ws->refd[t]; } if (!ws->uncomp_all) { if (!ws->nfiles) fprintf(stderr, "No files.\n"); else fprintf(stderr, "All empty or still-delalloced files.\n"); return 1; } if (ws->nfiles > 1) { printf("Processed %"PRIu64" files, %"PRIu64" regular extents " "(%"PRIu64" refs), %"PRIu64" inline.\n", ws->nfiles, ws->nextents, ws->nrefs, ws->ninline); } print_table("Type", "Perc", "Disk Usage", "Uncompressed", "Referenced"); percentage = ws->disk_all*100/ws->uncomp_all; snprintf(perc, sizeof(perc), "%3u%%", percentage); human_bytes(ws->disk_all, disk_usage); human_bytes(ws->uncomp_all, uncomp_usage); human_bytes(ws->refd_all, refd_usage); print_table("TOTAL", perc, disk_usage, uncomp_usage, refd_usage); for (t=0; tuncomp[t]) continue; const char *ct = comp_types[t]; char unkn_comp[12]; percentage = ws->disk[t]*100/ws->uncomp[t]; snprintf(perc, sizeof(perc), "%3u%%", percentage); human_bytes(ws->disk[t], disk_usage); human_bytes(ws->uncomp[t], uncomp_usage); human_bytes(ws->refd[t], refd_usage); if (!ct) { snprintf(unkn_comp, sizeof(unkn_comp), "?%u", t); ct = unkn_comp; } print_table(ct, perc, disk_usage, uncomp_usage, refd_usage); } free(ws); return 0; } compsize-1.1/endianness.h000066400000000000000000000026401324434637300155170ustar00rootroot00000000000000#include #include #ifdef __ANDROID__ #include #endif #ifdef __sun # include # define LITTLE_ENDIAN 1234 # define BIG_ENDIAN 4321 # ifdef _LITTLE_ENDIAN # define BYTE_ORDER LITTLE_ENDIAN # else # define BYTE_ORDER BIG_ENDIAN # endif #endif #ifndef htole32 #if BYTE_ORDER == LITTLE_ENDIAN #define htole32(x) (x) #else #if BYTE_ORDER == BIG_ENDIAN #define htole32(x) \ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) #else #error No endianness given. #endif #endif #endif #ifndef htole64 #if BYTE_ORDER == LITTLE_ENDIAN #define htole64(x) (x) #else #if BYTE_ORDER == BIG_ENDIAN #define htole64(x) \ ( (((x) & 0xff00000000000000ull) >> 56) \ | (((x) & 0x00ff000000000000ull) >> 40) \ | (((x) & 0x0000ff0000000000ull) >> 24) \ | (((x) & 0x000000ff00000000ull) >> 8) \ | (((x) & 0x00000000ff000000ull) << 8) \ | (((x) & 0x0000000000ff0000ull) << 24) \ | (((x) & 0x000000000000ff00ull) << 40) \ | (((x) & 0x00000000000000ffull) << 56)) #else #error No endianness given. #endif #endif #endif compsize-1.1/kerncompat.h000066400000000000000000000242631324434637300155400ustar00rootroot00000000000000/* * 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. */ #ifndef __KERNCOMPAT_H__ #define __KERNCOMPAT_H__ #include #include #include #include #include #include #include #include #include #include #include #ifndef __GLIBC__ #ifndef BTRFS_DISABLE_BACKTRACE #define BTRFS_DISABLE_BACKTRACE #endif #define __always_inline __inline __attribute__ ((__always_inline__)) #endif #ifndef BTRFS_DISABLE_BACKTRACE #include #endif #define ptr_to_u64(x) ((u64)(uintptr_t)x) #define u64_to_ptr(x) ((void *)(uintptr_t)x) #ifndef READ #define READ 0 #define WRITE 1 #define READA 2 #endif #define gfp_t int #define get_cpu_var(p) (p) #define __get_cpu_var(p) (p) #define BITS_PER_BYTE 8 #define BITS_PER_LONG (__SIZEOF_LONG__ * BITS_PER_BYTE) #define __GFP_BITS_SHIFT 20 #define __GFP_BITS_MASK ((int)((1 << __GFP_BITS_SHIFT) - 1)) #define GFP_KERNEL 0 #define GFP_NOFS 0 #define __read_mostly #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #ifndef ULONG_MAX #define ULONG_MAX (~0UL) #endif #define __token_glue(a,b,c) ___token_glue(a,b,c) #define ___token_glue(a,b,c) a ## b ## c #ifdef DEBUG_BUILD_CHECKS #define BUILD_ASSERT(x) extern int __token_glue(compile_time_assert_,__LINE__,__COUNTER__)[1-2*!(x)] __attribute__((unused)) #else #define BUILD_ASSERT(x) #endif #ifndef BTRFS_DISABLE_BACKTRACE #define MAX_BACKTRACE 16 static inline void print_trace(void) { void *array[MAX_BACKTRACE]; int size; size = backtrace(array, MAX_BACKTRACE); backtrace_symbols_fd(array, size, 2); } #endif static inline void warning_trace(const char *assertion, const char *filename, const char *func, unsigned line, long val) { if (!val) return; fprintf(stderr, "%s:%d: %s: Warning: assertion `%s` failed, value %ld\n", filename, line, func, assertion, val); #ifndef BTRFS_DISABLE_BACKTRACE print_trace(); #endif } static inline void bugon_trace(const char *assertion, const char *filename, const char *func, unsigned line, long val) { if (!val) return; fprintf(stderr, "%s:%d: %s: BUG_ON `%s` triggered, value %ld\n", filename, line, func, assertion, val); #ifndef BTRFS_DISABLE_BACKTRACE print_trace(); #endif abort(); exit(1); } #ifdef __CHECKER__ #define __force __attribute__((force)) #define __bitwise__ __attribute__((bitwise)) #else #define __force #define __bitwise__ #endif #ifndef __CHECKER__ /* * Since we're using primitive definitions from kernel-space, we need to * define __KERNEL__ so that system header files know which definitions * to use. */ #define __KERNEL__ #include typedef __u32 u32; typedef __u64 u64; typedef __u16 u16; typedef __u8 u8; typedef __s64 s64; typedef __s32 s32; /* * Continuing to define __KERNEL__ breaks others parts of the code, so * we can just undefine it now that we have the correct headers... */ #undef __KERNEL__ #else typedef unsigned int u32; typedef unsigned int __u32; typedef unsigned long long u64; typedef unsigned char u8; typedef unsigned short u16; typedef long long s64; typedef int s32; #endif struct vma_shared { int prio_tree_node; }; struct vm_area_struct { unsigned long vm_pgoff; unsigned long vm_start; unsigned long vm_end; struct vma_shared shared; }; struct page { unsigned long index; }; struct mutex { unsigned long lock; }; #define mutex_init(m) \ do { \ (m)->lock = 1; \ } while (0) static inline void mutex_lock(struct mutex *m) { m->lock--; } static inline void mutex_unlock(struct mutex *m) { m->lock++; } static inline int mutex_is_locked(struct mutex *m) { return (m->lock != 1); } #define cond_resched() do { } while (0) #define preempt_enable() do { } while (0) #define preempt_disable() do { } while (0) #define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) #define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) #ifndef __attribute_const__ #define __attribute_const__ __attribute__((__const__)) #endif /** * __set_bit - Set a bit in memory * @nr: the bit to set * @addr: the address to start counting from * * Unlike set_bit(), this function is non-atomic and may be reordered. * If it's called on the same region of memory simultaneously, the effect * may be that only one operation succeeds. */ static inline void __set_bit(int nr, volatile unsigned long *addr) { unsigned long mask = BITOP_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); *p |= mask; } static inline void __clear_bit(int nr, volatile unsigned long *addr) { unsigned long mask = BITOP_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); *p &= ~mask; } /** * test_bit - Determine whether a bit is set * @nr: bit number to test * @addr: Address to start counting from */ static inline int test_bit(int nr, const volatile unsigned long *addr) { return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); } /* * error pointer */ #define MAX_ERRNO 4095 #define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO) static inline void *ERR_PTR(long error) { return (void *) error; } static inline long PTR_ERR(const void *ptr) { return (long) ptr; } static inline int IS_ERR(const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); } static inline int IS_ERR_OR_NULL(const void *ptr) { return !ptr || IS_ERR(ptr); } /* * This looks more complex than it should be. But we need to * get the type for the ~ right in round_down (it needs to be * as wide as the result!), and we want to evaluate the macro * arguments just once each. */ #define __round_mask(x, y) ((__typeof__(x))((y)-1)) #define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) #define round_down(x, y) ((x) & ~__round_mask(x, y)) /* * printk */ #define printk(fmt, args...) fprintf(stderr, fmt, ##args) #define KERN_CRIT "" #define KERN_ERR "" /* * kmalloc/kfree */ #define kmalloc(x, y) malloc(x) #define kzalloc(x, y) calloc(1, x) #define kstrdup(x, y) strdup(x) #define kfree(x) free(x) #define vmalloc(x) malloc(x) #define vfree(x) free(x) #ifndef BTRFS_DISABLE_BACKTRACE static inline void assert_trace(const char *assertion, const char *filename, const char *func, unsigned line, long val) { if (val) return; fprintf(stderr, "%s:%d: %s: Assertion `%s` failed, value %ld\n", filename, line, func, assertion, val); #ifndef BTRFS_DISABLE_BACKTRACE print_trace(); #endif abort(); exit(1); } #define ASSERT(c) assert_trace(#c, __FILE__, __func__, __LINE__, (long)(c)) #else #define ASSERT(c) assert(c) #endif #define BUG_ON(c) bugon_trace(#c, __FILE__, __func__, __LINE__, (long)(c)) #define BUG() BUG_ON(1) #define WARN_ON(c) warning_trace(#c, __FILE__, __func__, __LINE__, (long)(c)) #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #ifndef __bitwise #ifdef __CHECKER__ #define __bitwise __bitwise__ #else #define __bitwise #endif /* __CHECKER__ */ #endif /* __bitwise */ /* Alignment check */ #define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0) static inline int is_power_of_2(unsigned long n) { return (n != 0 && ((n & (n - 1)) == 0)); } typedef u16 __bitwise __le16; typedef u16 __bitwise __be16; typedef u32 __bitwise __le32; typedef u32 __bitwise __be32; typedef u64 __bitwise __le64; typedef u64 __bitwise __be64; /* Macros to generate set/get funcs for the struct fields * assume there is a lefoo_to_cpu for every type, so lets make a simple * one for u8: */ #define le8_to_cpu(v) (v) #define cpu_to_le8(v) (v) #define __le8 u8 #if __BYTE_ORDER == __BIG_ENDIAN #define cpu_to_le64(x) ((__force __le64)(u64)(bswap_64(x))) #define le64_to_cpu(x) ((__force u64)(__le64)(bswap_64(x))) #define cpu_to_le32(x) ((__force __le32)(u32)(bswap_32(x))) #define le32_to_cpu(x) ((__force u32)(__le32)(bswap_32(x))) #define cpu_to_le16(x) ((__force __le16)(u16)(bswap_16(x))) #define le16_to_cpu(x) ((__force u16)(__le16)(bswap_16(x))) #else #define cpu_to_le64(x) ((__force __le64)(u64)(x)) #define le64_to_cpu(x) ((__force u64)(__le64)(x)) #define cpu_to_le32(x) ((__force __le32)(u32)(x)) #define le32_to_cpu(x) ((__force u32)(__le32)(x)) #define cpu_to_le16(x) ((__force __le16)(u16)(x)) #define le16_to_cpu(x) ((__force u16)(__le16)(x)) #endif struct __una_u16 { __le16 x; } __attribute__((__packed__)); struct __una_u32 { __le32 x; } __attribute__((__packed__)); struct __una_u64 { __le64 x; } __attribute__((__packed__)); #define get_unaligned_le8(p) (*((u8 *)(p))) #define get_unaligned_8(p) (*((u8 *)(p))) #define put_unaligned_le8(val,p) ((*((u8 *)(p))) = (val)) #define put_unaligned_8(val,p) ((*((u8 *)(p))) = (val)) #define get_unaligned_le16(p) le16_to_cpu(((const struct __una_u16 *)(p))->x) #define get_unaligned_16(p) (((const struct __una_u16 *)(p))->x) #define put_unaligned_le16(val,p) (((struct __una_u16 *)(p))->x = cpu_to_le16(val)) #define put_unaligned_16(val,p) (((struct __una_u16 *)(p))->x = (val)) #define get_unaligned_le32(p) le32_to_cpu(((const struct __una_u32 *)(p))->x) #define get_unaligned_32(p) (((const struct __una_u32 *)(p))->x) #define put_unaligned_le32(val,p) (((struct __una_u32 *)(p))->x = cpu_to_le32(val)) #define put_unaligned_32(val,p) (((struct __una_u32 *)(p))->x = (val)) #define get_unaligned_le64(p) le64_to_cpu(((const struct __una_u64 *)(p))->x) #define get_unaligned_64(p) (((const struct __una_u64 *)(p))->x) #define put_unaligned_le64(val,p) (((struct __una_u64 *)(p))->x = cpu_to_le64(val)) #define put_unaligned_64(val,p) (((struct __una_u64 *)(p))->x = (val)) #ifndef true #define true 1 #define false 0 #endif #ifndef noinline #define noinline #endif #endif compsize-1.1/radix-tree.c000066400000000000000000000503171324434637300154330ustar00rootroot00000000000000/* * 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. */ /* * Copyright (C) 2001 Momchil Velikov * Portions Copyright (C) 2001 Christoph Hellwig * Copyright (C) 2005 SGI, Christoph Lameter * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "kerncompat.h" #include "radix-tree.h" #ifdef __KERNEL__ #define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6) #else #define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */ #endif #define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT) #define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1) #define RADIX_TREE_TAG_LONGS \ ((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG) struct radix_tree_node { unsigned int count; void *slots[RADIX_TREE_MAP_SIZE]; unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS]; }; struct radix_tree_path { struct radix_tree_node *node; int offset; }; #define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long)) #define RADIX_TREE_MAX_PATH (RADIX_TREE_INDEX_BITS/RADIX_TREE_MAP_SHIFT + 2) static unsigned long height_to_maxindex[RADIX_TREE_MAX_PATH] __read_mostly; /* * Per-cpu pool of preloaded nodes */ struct radix_tree_preload { int nr; struct radix_tree_node *nodes[RADIX_TREE_MAX_PATH]; }; static struct radix_tree_preload radix_tree_preloads = { 0, }; static int internal_nodes = 0; /* * This assumes that the caller has performed appropriate preallocation, and * that the caller has pinned this thread of control to the current CPU. */ static struct radix_tree_node * radix_tree_node_alloc(struct radix_tree_root *root) { struct radix_tree_node *ret; ret = malloc(sizeof(struct radix_tree_node)); if (ret) { memset(ret, 0, sizeof(struct radix_tree_node)); internal_nodes++; } return ret; } static inline void radix_tree_node_free(struct radix_tree_node *node) { internal_nodes--; free(node); } /* * Load up this CPU's radix_tree_node buffer with sufficient objects to * ensure that the addition of a single element in the tree cannot fail. On * success, return zero, with preemption disabled. On error, return -ENOMEM * with preemption not disabled. */ int radix_tree_preload(gfp_t gfp_mask) { struct radix_tree_preload *rtp; struct radix_tree_node *node; int ret = -ENOMEM; preempt_disable(); rtp = &__get_cpu_var(radix_tree_preloads); while (rtp->nr < ARRAY_SIZE(rtp->nodes)) { preempt_enable(); node = radix_tree_node_alloc(NULL); if (node == NULL) goto out; preempt_disable(); rtp = &__get_cpu_var(radix_tree_preloads); if (rtp->nr < ARRAY_SIZE(rtp->nodes)) rtp->nodes[rtp->nr++] = node; else radix_tree_node_free(node); } ret = 0; out: return ret; } static inline void tag_set(struct radix_tree_node *node, unsigned int tag, int offset) { __set_bit(offset, node->tags[tag]); } static inline void tag_clear(struct radix_tree_node *node, unsigned int tag, int offset) { __clear_bit(offset, node->tags[tag]); } static inline int tag_get(struct radix_tree_node *node, unsigned int tag, int offset) { return test_bit(offset, node->tags[tag]); } static inline void root_tag_set(struct radix_tree_root *root, unsigned int tag) { root->gfp_mask |= (__force gfp_t)(1 << (tag + __GFP_BITS_SHIFT)); } static inline void root_tag_clear(struct radix_tree_root *root, unsigned int tag) { root->gfp_mask &= (__force gfp_t)~(1 << (tag + __GFP_BITS_SHIFT)); } static inline void root_tag_clear_all(struct radix_tree_root *root) { root->gfp_mask &= __GFP_BITS_MASK; } static inline int root_tag_get(struct radix_tree_root *root, unsigned int tag) { return (__force unsigned)root->gfp_mask & (1 << (tag + __GFP_BITS_SHIFT)); } /* * Returns 1 if any slot in the node has this tag set. * Otherwise returns 0. */ static inline int any_tag_set(struct radix_tree_node *node, unsigned int tag) { int idx; for (idx = 0; idx < RADIX_TREE_TAG_LONGS; idx++) { if (node->tags[tag][idx]) return 1; } return 0; } /* * Return the maximum key which can be store into a * radix tree with height HEIGHT. */ static inline unsigned long radix_tree_maxindex(unsigned int height) { return height_to_maxindex[height]; } /* * Extend a radix tree so it can store key @index. */ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index) { struct radix_tree_node *node; unsigned int height; int tag; /* Figure out what the height should be. */ height = root->height + 1; while (index > radix_tree_maxindex(height)) height++; if (root->rnode == NULL) { root->height = height; goto out; } do { if (!(node = radix_tree_node_alloc(root))) return -ENOMEM; /* Increase the height. */ node->slots[0] = root->rnode; /* Propagate the aggregated tag info into the new root */ for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { if (root_tag_get(root, tag)) tag_set(node, tag, 0); } node->count = 1; root->rnode = node; root->height++; } while (height > root->height); out: return 0; } /** * radix_tree_insert - insert into a radix tree * @root: radix tree root * @index: index key * @item: item to insert * * Insert an item into the radix tree at position @index. */ int radix_tree_insert(struct radix_tree_root *root, unsigned long index, void *item) { struct radix_tree_node *node = NULL, *slot; unsigned int height, shift; int offset; int error; /* Make sure the tree is high enough. */ if (index > radix_tree_maxindex(root->height)) { error = radix_tree_extend(root, index); if (error) return error; } slot = root->rnode; height = root->height; shift = (height-1) * RADIX_TREE_MAP_SHIFT; offset = 0; /* uninitialised var warning */ while (height > 0) { if (slot == NULL) { /* Have to add a child node. */ if (!(slot = radix_tree_node_alloc(root))) return -ENOMEM; if (node) { node->slots[offset] = slot; node->count++; } else root->rnode = slot; } /* Go a level down */ offset = (index >> shift) & RADIX_TREE_MAP_MASK; node = slot; slot = node->slots[offset]; shift -= RADIX_TREE_MAP_SHIFT; height--; } if (slot != NULL) return -EEXIST; if (node) { node->count++; node->slots[offset] = item; BUG_ON(tag_get(node, 0, offset)); BUG_ON(tag_get(node, 1, offset)); } else { root->rnode = item; BUG_ON(root_tag_get(root, 0)); BUG_ON(root_tag_get(root, 1)); } return 0; } static inline void **__lookup_slot(struct radix_tree_root *root, unsigned long index) { unsigned int height, shift; struct radix_tree_node **slot; height = root->height; if (index > radix_tree_maxindex(height)) return NULL; if (height == 0 && root->rnode) return (void *)&root->rnode; shift = (height-1) * RADIX_TREE_MAP_SHIFT; slot = &root->rnode; while (height > 0) { if (*slot == NULL) return NULL; slot = (struct radix_tree_node **) ((*slot)->slots + ((index >> shift) & RADIX_TREE_MAP_MASK)); shift -= RADIX_TREE_MAP_SHIFT; height--; } return (void **)slot; } /** * radix_tree_lookup_slot - lookup a slot in a radix tree * @root: radix tree root * @index: index key * * Lookup the slot corresponding to the position @index in the radix tree * @root. This is useful for update-if-exists operations. */ void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index) { return __lookup_slot(root, index); } /** * radix_tree_lookup - perform lookup operation on a radix tree * @root: radix tree root * @index: index key * * Lookup the item at the position @index in the radix tree @root. */ void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index) { void **slot; slot = __lookup_slot(root, index); return slot != NULL ? *slot : NULL; } /** * radix_tree_tag_set - set a tag on a radix tree node * @root: radix tree root * @index: index key * @tag: tag index * * Set the search tag (which must be < RADIX_TREE_MAX_TAGS) * corresponding to @index in the radix tree. From * the root all the way down to the leaf node. * * Returns the address of the tagged item. Setting a tag on a not-present * item is a bug. */ void *radix_tree_tag_set(struct radix_tree_root *root, unsigned long index, unsigned int tag) { unsigned int height, shift; struct radix_tree_node *slot; height = root->height; BUG_ON(index > radix_tree_maxindex(height)); slot = root->rnode; shift = (height - 1) * RADIX_TREE_MAP_SHIFT; while (height > 0) { int offset; offset = (index >> shift) & RADIX_TREE_MAP_MASK; if (!tag_get(slot, tag, offset)) tag_set(slot, tag, offset); slot = slot->slots[offset]; BUG_ON(slot == NULL); shift -= RADIX_TREE_MAP_SHIFT; height--; } /* set the root's tag bit */ if (slot && !root_tag_get(root, tag)) root_tag_set(root, tag); return slot; } /** * radix_tree_tag_clear - clear a tag on a radix tree node * @root: radix tree root * @index: index key * @tag: tag index * * Clear the search tag (which must be < RADIX_TREE_MAX_TAGS) * corresponding to @index in the radix tree. If * this causes the leaf node to have no tags set then clear the tag in the * next-to-leaf node, etc. * * Returns the address of the tagged item on success, else NULL. ie: * has the same return value and semantics as radix_tree_lookup(). */ void *radix_tree_tag_clear(struct radix_tree_root *root, unsigned long index, unsigned int tag) { struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path; struct radix_tree_node *slot = NULL; unsigned int height, shift; height = root->height; if (index > radix_tree_maxindex(height)) goto out; shift = (height - 1) * RADIX_TREE_MAP_SHIFT; pathp->node = NULL; slot = root->rnode; while (height > 0) { int offset; if (slot == NULL) goto out; offset = (index >> shift) & RADIX_TREE_MAP_MASK; pathp[1].offset = offset; pathp[1].node = slot; slot = slot->slots[offset]; pathp++; shift -= RADIX_TREE_MAP_SHIFT; height--; } if (slot == NULL) goto out; while (pathp->node) { if (!tag_get(pathp->node, tag, pathp->offset)) goto out; tag_clear(pathp->node, tag, pathp->offset); if (any_tag_set(pathp->node, tag)) goto out; pathp--; } /* clear the root's tag bit */ if (root_tag_get(root, tag)) root_tag_clear(root, tag); out: return slot; } #ifndef __KERNEL__ /* Only the test harness uses this at present */ /** * radix_tree_tag_get - get a tag on a radix tree node * @root: radix tree root * @index: index key * @tag: tag index (< RADIX_TREE_MAX_TAGS) * * Return values: * * 0: tag not present or not set * 1: tag set */ int radix_tree_tag_get(struct radix_tree_root *root, unsigned long index, unsigned int tag) { unsigned int height, shift; struct radix_tree_node *slot; int saw_unset_tag = 0; height = root->height; if (index > radix_tree_maxindex(height)) return 0; /* check the root's tag bit */ if (!root_tag_get(root, tag)) return 0; if (height == 0) return 1; shift = (height - 1) * RADIX_TREE_MAP_SHIFT; slot = root->rnode; for ( ; ; ) { int offset; if (slot == NULL) return 0; offset = (index >> shift) & RADIX_TREE_MAP_MASK; /* * This is just a debug check. Later, we can bale as soon as * we see an unset tag. */ if (!tag_get(slot, tag, offset)) saw_unset_tag = 1; if (height == 1) { int ret = tag_get(slot, tag, offset); BUG_ON(ret && saw_unset_tag); return !!ret; } slot = slot->slots[offset]; shift -= RADIX_TREE_MAP_SHIFT; height--; } } #endif static unsigned int __lookup(struct radix_tree_root *root, void **results, unsigned long index, unsigned int max_items, unsigned long *next_index) { unsigned int nr_found = 0; unsigned int shift, height; struct radix_tree_node *slot; unsigned long i; height = root->height; if (height == 0) { if (root->rnode && index == 0) results[nr_found++] = root->rnode; goto out; } shift = (height-1) * RADIX_TREE_MAP_SHIFT; slot = root->rnode; for ( ; height > 1; height--) { for (i = (index >> shift) & RADIX_TREE_MAP_MASK ; i < RADIX_TREE_MAP_SIZE; i++) { if (slot->slots[i] != NULL) break; index &= ~((1UL << shift) - 1); index += 1UL << shift; if (index == 0) goto out; /* 32-bit wraparound */ } if (i == RADIX_TREE_MAP_SIZE) goto out; shift -= RADIX_TREE_MAP_SHIFT; slot = slot->slots[i]; } /* Bottom level: grab some items */ for (i = index & RADIX_TREE_MAP_MASK; i < RADIX_TREE_MAP_SIZE; i++) { index++; if (slot->slots[i]) { results[nr_found++] = slot->slots[i]; if (nr_found == max_items) goto out; } } out: *next_index = index; return nr_found; } /** * radix_tree_gang_lookup - perform multiple lookup on a radix tree * @root: radix tree root * @results: where the results of the lookup are placed * @first_index: start the lookup from this key * @max_items: place up to this many items at *results * * Performs an index-ascending scan of the tree for present items. Places * them at *@results and returns the number of items which were placed at * *@results. * * The implementation is naive. */ unsigned int radix_tree_gang_lookup(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items) { const unsigned long max_index = radix_tree_maxindex(root->height); unsigned long cur_index = first_index; unsigned int ret = 0; while (ret < max_items) { unsigned int nr_found; unsigned long next_index; /* Index of next search */ if (cur_index > max_index) break; nr_found = __lookup(root, results + ret, cur_index, max_items - ret, &next_index); ret += nr_found; if (next_index == 0) break; cur_index = next_index; } return ret; } /* * FIXME: the two tag_get()s here should use find_next_bit() instead of * open-coding the search. */ static unsigned int __lookup_tag(struct radix_tree_root *root, void **results, unsigned long index, unsigned int max_items, unsigned long *next_index, unsigned int tag) { unsigned int nr_found = 0; unsigned int shift; unsigned int height = root->height; struct radix_tree_node *slot; if (height == 0) { if (root->rnode && index == 0) results[nr_found++] = root->rnode; goto out; } shift = (height - 1) * RADIX_TREE_MAP_SHIFT; slot = root->rnode; do { unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK; for ( ; i < RADIX_TREE_MAP_SIZE; i++) { if (tag_get(slot, tag, i)) { BUG_ON(slot->slots[i] == NULL); break; } index &= ~((1UL << shift) - 1); index += 1UL << shift; if (index == 0) goto out; /* 32-bit wraparound */ } if (i == RADIX_TREE_MAP_SIZE) goto out; height--; if (height == 0) { /* Bottom level: grab some items */ unsigned long j = index & RADIX_TREE_MAP_MASK; for ( ; j < RADIX_TREE_MAP_SIZE; j++) { index++; if (tag_get(slot, tag, j)) { BUG_ON(slot->slots[j] == NULL); results[nr_found++] = slot->slots[j]; if (nr_found == max_items) goto out; } } } shift -= RADIX_TREE_MAP_SHIFT; slot = slot->slots[i]; } while (height > 0); out: *next_index = index; return nr_found; } /** * radix_tree_gang_lookup_tag - perform multiple lookup on a radix tree * based on a tag * @root: radix tree root * @results: where the results of the lookup are placed * @first_index: start the lookup from this key * @max_items: place up to this many items at *results * @tag: the tag index (< RADIX_TREE_MAX_TAGS) * * Performs an index-ascending scan of the tree for present items which * have the tag indexed by @tag set. Places the items at *@results and * returns the number of items which were placed at *@results. */ unsigned int radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items, unsigned int tag) { const unsigned long max_index = radix_tree_maxindex(root->height); unsigned long cur_index = first_index; unsigned int ret = 0; /* check the root's tag bit */ if (!root_tag_get(root, tag)) return 0; while (ret < max_items) { unsigned int nr_found; unsigned long next_index; /* Index of next search */ if (cur_index > max_index) break; nr_found = __lookup_tag(root, results + ret, cur_index, max_items - ret, &next_index, tag); ret += nr_found; if (next_index == 0) break; cur_index = next_index; } return ret; } /** * radix_tree_shrink - shrink height of a radix tree to minimal * @root radix tree root */ static inline void radix_tree_shrink(struct radix_tree_root *root) { /* try to shrink tree height */ while (root->height > 0 && root->rnode->count == 1 && root->rnode->slots[0]) { struct radix_tree_node *to_free = root->rnode; root->rnode = to_free->slots[0]; root->height--; /* must only free zeroed nodes into the slab */ tag_clear(to_free, 0, 0); tag_clear(to_free, 1, 0); to_free->slots[0] = NULL; to_free->count = 0; radix_tree_node_free(to_free); } } /** * radix_tree_delete - delete an item from a radix tree * @root: radix tree root * @index: index key * * Remove the item at @index from the radix tree rooted at @root. * * Returns the address of the deleted item, or NULL if it was not present. */ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) { struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path; struct radix_tree_node *slot = NULL; unsigned int height, shift; int tag; int offset; height = root->height; if (index > radix_tree_maxindex(height)) goto out; slot = root->rnode; if (height == 0 && root->rnode) { root_tag_clear_all(root); root->rnode = NULL; goto out; } shift = (height - 1) * RADIX_TREE_MAP_SHIFT; pathp->node = NULL; do { if (slot == NULL) goto out; pathp++; offset = (index >> shift) & RADIX_TREE_MAP_MASK; pathp->offset = offset; pathp->node = slot; slot = slot->slots[offset]; shift -= RADIX_TREE_MAP_SHIFT; height--; } while (height > 0); if (slot == NULL) goto out; /* * Clear all tags associated with the just-deleted item */ for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { if (tag_get(pathp->node, tag, pathp->offset)) radix_tree_tag_clear(root, index, tag); } /* Now free the nodes we do not need anymore */ while (pathp->node) { pathp->node->slots[pathp->offset] = NULL; pathp->node->count--; if (pathp->node->count) { if (pathp->node == root->rnode) radix_tree_shrink(root); goto out; } /* Node with zero slots in use so free it */ radix_tree_node_free(pathp->node); pathp--; } root_tag_clear_all(root); root->height = 0; root->rnode = NULL; out: return slot; } /** * radix_tree_tagged - test whether any items in the tree are tagged * @root: radix tree root * @tag: tag to test */ int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag) { return root_tag_get(root, tag); } static unsigned long __maxindex(unsigned int height) { unsigned int tmp = height * RADIX_TREE_MAP_SHIFT; unsigned long index = ~0UL; if (tmp < RADIX_TREE_INDEX_BITS) index = (index >> (RADIX_TREE_INDEX_BITS - tmp - 1)) >> 1; return index; } static void radix_tree_init_maxindex(void) { unsigned int i; for (i = 0; i < ARRAY_SIZE(height_to_maxindex); i++) height_to_maxindex[i] = __maxindex(i); } void radix_tree_init(void) { radix_tree_init_maxindex(); } compsize-1.1/radix-tree.h000066400000000000000000000064171324434637300154420ustar00rootroot00000000000000/* * 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. */ /* * Copyright (C) 2001 Momchil Velikov * Portions Copyright (C) 2001 Christoph Hellwig * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _LINUX_RADIX_TREE_H #define _LINUX_RADIX_TREE_H #if BTRFS_FLAT_INCLUDES #include "kerncompat.h" #else #include #endif /* BTRFS_FLAT_INCLUDES */ #define RADIX_TREE_MAX_TAGS 2 /* root tags are stored in gfp_mask, shifted by __GFP_BITS_SHIFT */ struct radix_tree_root { unsigned int height; gfp_t gfp_mask; struct radix_tree_node *rnode; }; #define RADIX_TREE_INIT(mask) { \ .height = 0, \ .gfp_mask = (mask), \ .rnode = NULL, \ } #define RADIX_TREE(name, mask) \ struct radix_tree_root name = RADIX_TREE_INIT(mask) #define INIT_RADIX_TREE(root, mask) \ do { \ (root)->height = 0; \ (root)->gfp_mask = (mask); \ (root)->rnode = NULL; \ } while (0) int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); void *radix_tree_lookup(struct radix_tree_root *, unsigned long); void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); void *radix_tree_delete(struct radix_tree_root *, unsigned long); unsigned int radix_tree_gang_lookup(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items); int radix_tree_preload(gfp_t gfp_mask); void radix_tree_init(void); void *radix_tree_tag_set(struct radix_tree_root *root, unsigned long index, unsigned int tag); void *radix_tree_tag_clear(struct radix_tree_root *root, unsigned long index, unsigned int tag); int radix_tree_tag_get(struct radix_tree_root *root, unsigned long index, unsigned int tag); unsigned int radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items, unsigned int tag); int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag); static inline void radix_tree_preload_end(void) { preempt_enable(); } #endif /* _LINUX_RADIX_TREE_H */